use oxideav_core::bits::BitReader;
use oxideav_core::{Error, Result};
use crate::asf_data;
use crate::aspx;
use crate::huffman::{huff_decode, HCB_SCALEFAC_CW, HCB_SCALEFAC_LEN};
use crate::sfb_offset;
use crate::tables;
use crate::toc::variable_bits;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MonoCodecMode {
Simple,
Aspx,
}
impl MonoCodecMode {
pub fn from_bit(v: u32) -> Self {
if v == 0 {
Self::Simple
} else {
Self::Aspx
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StereoCodecMode {
Simple,
Aspx,
AspxAcpl1,
AspxAcpl2,
}
impl StereoCodecMode {
pub fn from_u32(v: u32) -> Self {
match v & 0b11 {
0 => Self::Simple,
1 => Self::Aspx,
2 => Self::AspxAcpl1,
_ => Self::AspxAcpl2,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpecFrontend {
Asf,
Ssf,
}
impl SpecFrontend {
pub fn from_bit(v: u32) -> Self {
if v == 0 {
Self::Asf
} else {
Self::Ssf
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct AsfTransformInfo {
pub b_long_frame: bool,
pub transf_length: [u32; 2],
pub transform_length_0: u32,
pub transform_length_1: u32,
}
#[derive(Debug, Clone, Default)]
pub struct AsfPsyInfo {
pub b_different_framing: bool,
pub max_sfb_0: u32,
pub max_sfb_side_0: u32,
pub max_sfb_1: u32,
pub max_sfb_side_1: u32,
pub scale_factor_grouping: Vec<u8>,
pub num_windows: u32,
pub num_window_groups: u32,
}
pub fn n_grp_bits_ge_1536(tl0: u32, tl1: u32) -> u32 {
match (tl0 & 0b11, tl1 & 0b11) {
(0, 0) => 15,
(0, 1) => 10,
(0, 2) => 8,
(0, 3) => 7,
(1, 0) => 10,
(1, 1) => 7,
(1, 2) => 4,
(1, 3) => 3,
(2, 0) => 8,
(2, 1) => 4,
(2, 2) => 3,
(2, 3) => 1,
(3, 0) => 7,
(3, 1) => 3,
(3, 2) => 1,
(3, 3) => 1,
_ => 0,
}
}
pub fn n_grp_bits_lt_1536(frame_len_base: u32, tl: u32) -> u32 {
match (frame_len_base, tl & 0b11) {
(1024, 0) | (960, 0) | (768, 0) => 7,
(1024, 1) | (960, 1) | (768, 1) => 3,
(1024, 2) | (960, 2) | (768, 2) => 1,
(1024, 3) | (960, 3) | (768, 3) => 0,
(512, 0) | (384, 0) => 3,
(512, 1) | (384, 1) => 1,
(512, 2) | (384, 2) => 0,
_ => 0,
}
}
pub fn parse_asf_psy_info(
br: &mut BitReader<'_>,
transform_info: &AsfTransformInfo,
frame_len_base: u32,
b_dual_maxsfb: bool,
b_side_limited: bool,
) -> Result<AsfPsyInfo> {
let b_different_framing = frame_len_base >= 1536
&& !transform_info.b_long_frame
&& transform_info.transf_length[0] != transform_info.transf_length[1];
let mut info = AsfPsyInfo {
b_different_framing,
..AsfPsyInfo::default()
};
let (nm0, ns0, _nml0) = tables::n_msfb_bits_48(transform_info.transform_length_0)
.ok_or_else(|| Error::invalid("ac4: asf_psy_info: unsupported transform_length[0]"))?;
if b_side_limited {
info.max_sfb_0 = br.read_u32(ns0)?;
} else {
info.max_sfb_0 = br.read_u32(nm0)?;
if b_dual_maxsfb {
info.max_sfb_side_0 = br.read_u32(nm0)?;
}
}
if info.b_different_framing {
let (nm1, ns1, _) = tables::n_msfb_bits_48(transform_info.transform_length_1)
.ok_or_else(|| Error::invalid("ac4: asf_psy_info: unsupported transform_length[1]"))?;
if b_side_limited {
info.max_sfb_1 = br.read_u32(ns1)?;
} else {
info.max_sfb_1 = br.read_u32(nm1)?;
if b_dual_maxsfb {
info.max_sfb_side_1 = br.read_u32(nm1)?;
}
}
}
let n_grp_bits = if transform_info.b_long_frame {
0
} else if frame_len_base >= 1536 {
n_grp_bits_ge_1536(
transform_info.transf_length[0],
transform_info.transf_length[1],
)
} else {
n_grp_bits_lt_1536(frame_len_base, transform_info.transf_length[0])
};
let mut grouping = Vec::with_capacity(n_grp_bits as usize);
for _ in 0..n_grp_bits {
grouping.push(br.read_u32(1)? as u8);
}
info.scale_factor_grouping = grouping;
if transform_info.b_long_frame {
info.num_windows = 1;
info.num_window_groups = 1;
} else {
info.num_windows = n_grp_bits + 1;
info.num_window_groups = 1;
for &b in &info.scale_factor_grouping {
if b == 0 {
info.num_window_groups += 1;
}
}
if info.num_windows == 0 {
info.num_windows = 1;
}
}
Ok(info)
}
pub fn parse_asf_psy_info_lfe(
br: &mut BitReader<'_>,
transform_info: &AsfTransformInfo,
) -> Result<AsfPsyInfo> {
let mut info = AsfPsyInfo {
b_different_framing: false,
num_windows: 1,
num_window_groups: 1,
..AsfPsyInfo::default()
};
let (_nm0, _ns0, nml0) = tables::n_msfb_bits_48(transform_info.transform_length_0)
.ok_or_else(|| Error::invalid("ac4: asf_psy_info_lfe: unsupported transform_length"))?;
if nml0 == 0 {
return Err(Error::invalid(
"ac4: asf_psy_info_lfe: transform_length not permitted for LFE",
));
}
info.max_sfb_0 = br.read_u32(nml0)?;
Ok(info)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SapMode {
None,
MsUsed,
Reserved,
SapData,
}
impl SapMode {
pub fn from_u32(v: u32) -> Self {
match v & 0b11 {
0 => Self::None,
1 => Self::MsUsed,
2 => Self::Reserved,
_ => Self::SapData,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct SapData {
pub sap_coeff_all: bool,
pub sap_coeff_used: Vec<Vec<bool>>,
pub delta_code_time: bool,
pub dpcm_alpha_q: Vec<Vec<i32>>,
}
#[derive(Debug, Clone, Default)]
pub struct ChparamInfo {
pub sap_mode: u32,
pub ms_used: Vec<Vec<bool>>,
pub sap_data: Option<SapData>,
}
impl ChparamInfo {
pub fn mode(&self) -> SapMode {
SapMode::from_u32(self.sap_mode)
}
}
pub fn parse_chparam_info(
br: &mut BitReader<'_>,
max_sfb_per_group: &[u32],
) -> Result<ChparamInfo> {
let sap_mode = br.read_u32(2)?;
let mut info = ChparamInfo {
sap_mode,
..ChparamInfo::default()
};
match SapMode::from_u32(sap_mode) {
SapMode::None | SapMode::Reserved => {}
SapMode::MsUsed => {
let mut groups = Vec::with_capacity(max_sfb_per_group.len());
for &m in max_sfb_per_group {
let mut row = Vec::with_capacity(m as usize);
for _ in 0..m {
row.push(br.read_bit()?);
}
groups.push(row);
}
info.ms_used = groups;
}
SapMode::SapData => {
info.sap_data = Some(parse_sap_data(br, max_sfb_per_group)?);
}
}
Ok(info)
}
pub fn parse_sap_data(br: &mut BitReader<'_>, max_sfb_per_group: &[u32]) -> Result<SapData> {
let num_groups = max_sfb_per_group.len();
let sap_coeff_all = br.read_bit()?;
let mut sap_coeff_used = Vec::with_capacity(num_groups);
if !sap_coeff_all {
for &m in max_sfb_per_group {
let mut row = vec![false; m as usize];
let mut sfb = 0usize;
while sfb < m as usize {
let f = br.read_bit()?;
row[sfb] = f;
if sfb + 1 < m as usize {
row[sfb + 1] = f;
}
sfb += 2;
}
sap_coeff_used.push(row);
}
} else {
for &m in max_sfb_per_group {
sap_coeff_used.push(vec![true; m as usize]);
}
}
let delta_code_time = if num_groups != 1 {
br.read_bit()?
} else {
false
};
let mut dpcm_alpha_q = Vec::with_capacity(num_groups);
for g in 0..num_groups {
let m = max_sfb_per_group[g] as usize;
let mut row = vec![0i32; m];
let mut sfb = 0usize;
while sfb < m {
if sap_coeff_used[g][sfb] {
let raw = huff_decode(br, HCB_SCALEFAC_LEN, HCB_SCALEFAC_CW)?;
row[sfb] = raw as i32 - 60;
}
sfb += 2;
}
dpcm_alpha_q.push(row);
}
Ok(SapData {
sap_coeff_all,
sap_coeff_used,
delta_code_time,
dpcm_alpha_q,
})
}
#[derive(Debug, Clone, Default)]
pub struct SubstreamTools {
pub channel_mode_channels: u16,
pub mono_mode: Option<MonoCodecMode>,
pub stereo_mode: Option<StereoCodecMode>,
pub spec_frontend_primary: Option<SpecFrontend>,
pub spec_frontend_secondary: Option<SpecFrontend>,
pub transform_info_primary: Option<AsfTransformInfo>,
pub transform_info_secondary: Option<AsfTransformInfo>,
pub psy_info_primary: Option<AsfPsyInfo>,
pub psy_info_secondary: Option<AsfPsyInfo>,
pub mdct_stereo_proc: bool,
pub scaled_spec_primary: Option<Vec<f32>>,
pub scaled_spec_secondary: Option<Vec<f32>>,
pub ms_used: Option<Vec<bool>>,
pub aspx_config: Option<aspx::AspxConfig>,
pub companding: Option<aspx::CompandingControl>,
pub aspx_framing_primary: Option<aspx::AspxFraming>,
pub aspx_framing_secondary: Option<aspx::AspxFraming>,
pub aspx_balance: Option<bool>,
pub aspx_xover_subband_offset: Option<u8>,
pub aspx_delta_dir_primary: Option<aspx::AspxDeltaDir>,
pub aspx_delta_dir_secondary: Option<aspx::AspxDeltaDir>,
pub aspx_qmode_env_primary: Option<aspx::AspxQuantStep>,
pub aspx_qmode_env_secondary: Option<aspx::AspxQuantStep>,
pub aspx_frequency_tables: Option<aspx::AspxFrequencyTables>,
pub aspx_hfgen_iwc_1ch: Option<aspx::AspxHfgenIwc1Ch>,
pub aspx_hfgen_iwc_2ch: Option<aspx::AspxHfgenIwc2Ch>,
pub aspx_data_sig_primary: Option<Vec<aspx::AspxHuffEnv>>,
pub aspx_data_sig_secondary: Option<Vec<aspx::AspxHuffEnv>>,
pub aspx_data_noise_primary: Option<Vec<aspx::AspxHuffEnv>>,
pub aspx_data_noise_secondary: Option<Vec<aspx::AspxHuffEnv>>,
pub acpl_config_1ch_partial: Option<crate::acpl::AcplConfig1ch>,
pub acpl_config_1ch_full: Option<crate::acpl::AcplConfig1ch>,
pub acpl_data_1ch: Option<crate::acpl::AcplData1ch>,
pub chparam_info: Option<ChparamInfo>,
pub five_x_mode: Option<crate::mch::FiveXCodecMode>,
pub five_x_b_has_lfe: bool,
pub five_x_coding_config: Option<crate::mch::FiveXCodingConfig>,
pub lfe_mono_data: Option<crate::mch::MonoLfeData>,
pub b_2ch_mode: Option<bool>,
pub cfg0_centre_mono: Option<crate::mch::MonoLfeData>,
pub cfg2_back_mono: Option<crate::mch::MonoLfeData>,
pub two_channel_data: Vec<crate::mch::TwoChannelData>,
pub three_channel_data: Option<crate::mch::ThreeChannelData>,
pub four_channel_data: Option<crate::mch::FourChannelData>,
pub five_channel_data: Option<crate::mch::FiveChannelData>,
pub acpl_config_2ch: Option<crate::acpl::AcplConfig2ch>,
pub acpl_data_2ch: Option<crate::acpl::AcplData2ch>,
pub acpl_data_1ch_pair: [Option<crate::acpl::AcplData1ch>; 2],
pub seven_x_mode: Option<crate::mch::SevenXCodecMode>,
pub seven_x_b_has_lfe: bool,
pub seven_x_coding_config: Option<crate::mch::FiveXCodingConfig>,
pub seven_x_b_use_sap_add_ch: Option<bool>,
pub seven_x_add_chparam_info: Option<[ChparamInfo; 2]>,
pub seven_x_additional_channel_data: Option<crate::mch::TwoChannelData>,
}
#[derive(Debug, Clone, Default)]
pub struct Ac4SubstreamInfo {
pub audio_size: u32,
pub audio_data_offset: u32,
pub tools: SubstreamTools,
}
pub fn resolve_transf_length(frame_len_base: u32, b_long_frame: bool, idx: u32) -> u32 {
if b_long_frame {
return frame_len_base;
}
if frame_len_base >= 1536 {
match (frame_len_base, idx & 0b11) {
(2048, 0) => 128,
(2048, 1) => 256,
(2048, 2) => 512,
(2048, 3) => 1024,
(1920, 0) => 120,
(1920, 1) => 240,
(1920, 2) => 480,
(1920, 3) => 960,
(1536, 0) => 96,
(1536, 1) => 192,
(1536, 2) => 384,
(1536, 3) => 768,
_ => 0,
}
} else {
match (frame_len_base, idx & 0b11) {
(1024, 0) => 128,
(1024, 1) => 256,
(1024, 2) => 512,
(1024, 3) => 1024,
(960, 0) => 120,
(960, 1) => 240,
(960, 2) => 480,
(960, 3) => 960,
(768, 0) => 96,
(768, 1) => 192,
(768, 2) => 384,
(768, 3) => 768,
(512, 0) => 128,
(512, 1) => 256,
(512, 2) => 512,
(384, 0) => 96,
(384, 1) => 192,
(384, 2) => 384,
_ => 0,
}
}
}
pub fn parse_asf_transform_info(
br: &mut BitReader<'_>,
frame_len_base: u32,
) -> Result<AsfTransformInfo> {
if frame_len_base >= 1536 {
let b_long_frame = br.read_bit()?;
if b_long_frame {
let tl = resolve_transf_length(frame_len_base, true, 0);
Ok(AsfTransformInfo {
b_long_frame: true,
transf_length: [0, 0],
transform_length_0: tl,
transform_length_1: tl,
})
} else {
let t0 = br.read_u32(2)?;
let t1 = br.read_u32(2)?;
Ok(AsfTransformInfo {
b_long_frame: false,
transf_length: [t0, t1],
transform_length_0: resolve_transf_length(frame_len_base, false, t0),
transform_length_1: resolve_transf_length(frame_len_base, false, t1),
})
}
} else {
let t0 = br.read_u32(2)?;
let len = resolve_transf_length(frame_len_base, false, t0);
Ok(AsfTransformInfo {
b_long_frame: false,
transf_length: [t0, t0],
transform_length_0: len,
transform_length_1: len,
})
}
}
pub fn parse_mono_audio_data_outer(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
b_iframe: bool,
frame_len_base: u32,
) -> Result<()> {
let mode_bit = br.read_u32(1)?;
let mode = MonoCodecMode::from_bit(mode_bit);
tools.mono_mode = Some(mode);
if mode != MonoCodecMode::Simple {
if b_iframe {
tools.aspx_config = Some(aspx::parse_aspx_config(br)?);
}
tools.companding = Some(aspx::parse_companding_control(br, 1)?);
}
let sf_bit = br.read_u32(1)?;
let frontend = SpecFrontend::from_bit(sf_bit);
tools.spec_frontend_primary = Some(frontend);
if !b_iframe {
}
if let SpecFrontend::Asf = frontend {
let ti = parse_asf_transform_info(br, frame_len_base)?;
tools.transform_info_primary = Some(ti);
if let Ok(psy) = parse_asf_psy_info(br, &ti, frame_len_base, false, false) {
tools.psy_info_primary = Some(psy);
let mut body_ok = false;
let psy_ref = tools.psy_info_primary.as_ref().unwrap().clone();
if ti.b_long_frame && psy_ref.num_window_groups == 1 {
if let Some(scaled) = decode_asf_long_mono_body(br, &ti, &psy_ref) {
tools.scaled_spec_primary = Some(scaled);
body_ok = true;
}
} else if psy_ref.num_window_groups > 1 {
if let Some(scaled) = decode_asf_grouped_mono_body(br, &ti, &psy_ref) {
tools.scaled_spec_primary = Some(scaled);
body_ok = true;
}
}
if mode != MonoCodecMode::Simple && b_iframe && body_ok {
if let Some(cfg) = tools.aspx_config {
parse_aspx_data_1ch_body(br, tools, &cfg, b_iframe, frame_len_base)?;
}
}
}
}
Ok(())
}
pub(crate) fn parse_aspx_data_1ch_body(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
cfg: &aspx::AspxConfig,
b_iframe: bool,
frame_len_base: u32,
) -> Result<()> {
let xover = br.read_u32(3)? as u8;
tools.aspx_xover_subband_offset = Some(xover);
let nats = aspx::num_aspx_timeslots(frame_len_base);
let framing = aspx::parse_aspx_framing(br, cfg, b_iframe, nats > 8)?;
let qmode = if matches!(framing.int_class, aspx::AspxIntClass::FixFix) && framing.num_env == 1 {
aspx::AspxQuantStep::Fine
} else {
cfg.quant_mode_env
};
tools.aspx_qmode_env_primary = Some(qmode);
let dd = aspx::parse_aspx_delta_dir(br, &framing)?;
if let Ok(tables) = aspx::derive_aspx_frequency_tables(cfg, xover as u32) {
let hfgen = aspx::parse_aspx_hfgen_iwc_1ch(
br,
cfg.num_noise_sbgroups(),
tables.counts.num_sbg_sig_highres,
nats,
)?;
tools.aspx_hfgen_iwc_1ch = Some(hfgen);
let sig = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Signal,
framing.num_env,
&framing.freq_res,
qmode,
aspx::AspxStereoMode::Level,
&dd.sig_delta_dir,
tables.counts,
)?;
tools.aspx_data_sig_primary = Some(sig);
let noise = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Noise,
framing.num_noise,
&[],
aspx::AspxQuantStep::Fine,
aspx::AspxStereoMode::Level,
&dd.noise_delta_dir,
tables.counts,
)?;
tools.aspx_data_noise_primary = Some(noise);
tools.aspx_frequency_tables = Some(tables);
}
tools.aspx_delta_dir_primary = Some(dd);
tools.aspx_framing_primary = Some(framing);
Ok(())
}
pub(crate) fn parse_aspx_data_2ch_body(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
cfg: &aspx::AspxConfig,
b_iframe: bool,
frame_len_base: u32,
) -> Result<()> {
let xover = br.read_u32(3)? as u8;
tools.aspx_xover_subband_offset = Some(xover);
let nats = aspx::num_aspx_timeslots(frame_len_base);
let framing_ch0 = aspx::parse_aspx_framing(br, cfg, b_iframe, nats > 8)?;
let qmode_ch0 = if matches!(framing_ch0.int_class, aspx::AspxIntClass::FixFix)
&& framing_ch0.num_env == 1
{
aspx::AspxQuantStep::Fine
} else {
cfg.quant_mode_env
};
tools.aspx_qmode_env_primary = Some(qmode_ch0);
let balance = br.read_bit()?;
tools.aspx_balance = Some(balance);
let framing_ch1_ref;
if !balance {
let framing_ch1 = aspx::parse_aspx_framing(br, cfg, b_iframe, nats > 8)?;
let qmode_ch1 = if matches!(framing_ch1.int_class, aspx::AspxIntClass::FixFix)
&& framing_ch1.num_env == 1
{
aspx::AspxQuantStep::Fine
} else {
cfg.quant_mode_env
};
tools.aspx_qmode_env_secondary = Some(qmode_ch1);
tools.aspx_framing_secondary = Some(framing_ch1);
framing_ch1_ref = tools.aspx_framing_secondary.as_ref();
} else {
tools.aspx_qmode_env_secondary = Some(qmode_ch0);
framing_ch1_ref = Some(&framing_ch0);
}
let dd0 = aspx::parse_aspx_delta_dir(br, &framing_ch0)?;
let f_ch1 = framing_ch1_ref.unwrap_or(&framing_ch0);
let dd1 = aspx::parse_aspx_delta_dir(br, f_ch1)?;
if let Ok(tables) = aspx::derive_aspx_frequency_tables(cfg, xover as u32) {
let hfgen = aspx::parse_aspx_hfgen_iwc_2ch(
br,
balance,
cfg.num_noise_sbgroups(),
tables.counts.num_sbg_sig_highres,
nats,
)?;
tools.aspx_hfgen_iwc_2ch = Some(hfgen);
let qmode_ch1_effective = tools.aspx_qmode_env_secondary.unwrap_or(qmode_ch0);
let sig0 = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Signal,
framing_ch0.num_env,
&framing_ch0.freq_res,
qmode_ch0,
aspx::AspxStereoMode::Level,
&dd0.sig_delta_dir,
tables.counts,
)?;
tools.aspx_data_sig_primary = Some(sig0);
let sm_ch1 = if balance {
aspx::AspxStereoMode::Balance
} else {
aspx::AspxStereoMode::Level
};
let sig1 = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Signal,
f_ch1.num_env,
&f_ch1.freq_res,
qmode_ch1_effective,
sm_ch1,
&dd1.sig_delta_dir,
tables.counts,
)?;
tools.aspx_data_sig_secondary = Some(sig1);
let noise0 = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Noise,
framing_ch0.num_noise,
&[],
aspx::AspxQuantStep::Fine,
aspx::AspxStereoMode::Level,
&dd0.noise_delta_dir,
tables.counts,
)?;
tools.aspx_data_noise_primary = Some(noise0);
let noise1 = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Noise,
f_ch1.num_noise,
&[],
aspx::AspxQuantStep::Fine,
sm_ch1,
&dd1.noise_delta_dir,
tables.counts,
)?;
tools.aspx_data_noise_secondary = Some(noise1);
tools.aspx_frequency_tables = Some(tables);
}
tools.aspx_delta_dir_primary = Some(dd0);
tools.aspx_delta_dir_secondary = Some(dd1);
tools.aspx_framing_primary = Some(framing_ch0);
Ok(())
}
fn derive_per_group(ti: &AsfTransformInfo, psy: &AsfPsyInfo) -> (Vec<u32>, Vec<u32>, Vec<u32>) {
derive_per_group_with_max_sfb(ti, psy, psy.max_sfb_0, psy.max_sfb_1)
}
fn derive_per_group_with_max_sfb(
ti: &AsfTransformInfo,
psy: &AsfPsyInfo,
max_sfb_a: u32,
max_sfb_b: u32,
) -> (Vec<u32>, Vec<u32>, Vec<u32>) {
let n = psy.num_window_groups.max(1) as usize;
let mut tl_idx = Vec::with_capacity(n);
let mut tl = Vec::with_capacity(n);
let mut msfb = Vec::with_capacity(n);
if !psy.b_different_framing {
for _ in 0..n {
tl_idx.push(ti.transf_length[0]);
tl.push(ti.transform_length_0);
msfb.push(max_sfb_a);
}
return (tl_idx, tl, msfb);
}
let num_windows_0 = 1u32 << (3u32.saturating_sub(ti.transf_length[0]));
let mut wtg = Vec::with_capacity(psy.num_windows as usize);
wtg.push(0u32);
let mut g = 0u32;
let mut grouping = psy.scale_factor_grouping.clone();
if (num_windows_0 as usize) <= grouping.len() {
for i in (num_windows_0 as usize..grouping.len()).rev() {
grouping[i] = grouping[i - 1];
}
grouping[(num_windows_0 - 1) as usize] = 0;
}
for &b in &grouping {
if b == 0 {
g += 1;
}
wtg.push(g);
}
let split_group = if (num_windows_0 as usize) < wtg.len() {
wtg[num_windows_0 as usize]
} else {
psy.num_window_groups
};
for grp in 0..n as u32 {
if grp < split_group {
tl_idx.push(ti.transf_length[0]);
tl.push(ti.transform_length_0);
msfb.push(max_sfb_a);
} else {
tl_idx.push(ti.transf_length[1]);
tl.push(ti.transform_length_1);
msfb.push(max_sfb_b);
}
}
(tl_idx, tl, msfb)
}
pub(crate) fn decode_asf_grouped_mono_body_with_max_sfb(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
psy: &AsfPsyInfo,
max_sfb_a: u32,
max_sfb_b: u32,
) -> Option<Vec<f32>> {
if psy.num_window_groups <= 1 {
return None;
}
let (tl_idx_per_g, tl_per_g, max_sfb_per_g) =
derive_per_group_with_max_sfb(ti, psy, max_sfb_a, max_sfb_b);
let mut sfbo_per_g: Vec<&'static [u16]> = Vec::with_capacity(tl_per_g.len());
let mut max_sfb_capped: Vec<u32> = Vec::with_capacity(tl_per_g.len());
for g in 0..tl_per_g.len() {
let tl = tl_per_g[g];
let cap = tables::num_sfb_48(tl)?;
let m = max_sfb_per_g[g].min(cap);
if m == 0 {
return None;
}
max_sfb_capped.push(m);
sfbo_per_g.push(sfb_offset::sfb_offset_48(tl)?);
}
let sections =
asf_data::parse_asf_section_data_grouped(br, &tl_idx_per_g, &tl_per_g, &max_sfb_capped)
.ok()?;
let (qspec_per_g, mqi_per_g) =
asf_data::parse_asf_spectral_data_grouped(br, §ions, &sfbo_per_g, &max_sfb_capped)
.ok()?;
let sf_gain_per_g = asf_data::parse_asf_scalefac_data_grouped(
br,
§ions,
&mqi_per_g,
&max_sfb_capped,
&tl_per_g,
)
.ok()?;
let _snf =
asf_data::parse_asf_snf_data_grouped(br, §ions, &mqi_per_g, &max_sfb_capped, &tl_per_g)
.ok()?;
let mut out: Vec<f32> = Vec::new();
for g in 0..tl_per_g.len() {
let scaled = asf_data::dequantise_and_scale(
&qspec_per_g[g],
&sf_gain_per_g[g],
sfbo_per_g[g],
max_sfb_capped[g],
);
out.extend_from_slice(&scaled);
}
Some(out)
}
fn decode_asf_grouped_mono_body(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
psy: &AsfPsyInfo,
) -> Option<Vec<f32>> {
decode_asf_grouped_mono_body_with_max_sfb(br, ti, psy, psy.max_sfb_0, psy.max_sfb_1)
}
fn decode_asf_grouped_stereo_joint_body(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
psy: &AsfPsyInfo,
) -> Option<(Vec<f32>, Vec<f32>, Vec<bool>)> {
if psy.num_window_groups <= 1 {
return None;
}
let (tl_idx_per_g, tl_per_g, max_sfb_per_g) = derive_per_group(ti, psy);
let mut sfbo_per_g: Vec<&'static [u16]> = Vec::with_capacity(tl_per_g.len());
let mut max_sfb_capped: Vec<u32> = Vec::with_capacity(tl_per_g.len());
for g in 0..tl_per_g.len() {
let tl = tl_per_g[g];
let cap = tables::num_sfb_48(tl)?;
let m = max_sfb_per_g[g].min(cap);
if m == 0 {
return None;
}
max_sfb_capped.push(m);
sfbo_per_g.push(sfb_offset::sfb_offset_48(tl)?);
}
let sections =
asf_data::parse_asf_section_data_grouped(br, &tl_idx_per_g, &tl_per_g, &max_sfb_capped)
.ok()?;
let (q_l_per_g, mqi_l_per_g) =
asf_data::parse_asf_spectral_data_grouped(br, §ions, &sfbo_per_g, &max_sfb_capped)
.ok()?;
let (q_r_per_g, mqi_r_per_g) =
asf_data::parse_asf_spectral_data_grouped(br, §ions, &sfbo_per_g, &max_sfb_capped)
.ok()?;
let mut mqi_per_g: Vec<Vec<u32>> = Vec::with_capacity(tl_per_g.len());
for g in 0..tl_per_g.len() {
let v: Vec<u32> = mqi_l_per_g[g]
.iter()
.zip(mqi_r_per_g[g].iter())
.map(|(a, b)| (*a).max(*b))
.collect();
mqi_per_g.push(v);
}
let sf_gain_per_g = asf_data::parse_asf_scalefac_data_grouped(
br,
§ions,
&mqi_per_g,
&max_sfb_capped,
&tl_per_g,
)
.ok()?;
let mut ms_used_per_g: Vec<Vec<bool>> = Vec::with_capacity(tl_per_g.len());
for g in 0..tl_per_g.len() {
let max_sfb = max_sfb_capped[g];
let mut ms = vec![false; max_sfb as usize];
for sfb in 0..max_sfb as usize {
let cb = sections[g].sfb_cb[sfb];
if cb == 0 || mqi_per_g[g][sfb] == 0 {
continue;
}
ms[sfb] = br.read_bit().ok()?;
}
ms_used_per_g.push(ms);
}
let _snf =
asf_data::parse_asf_snf_data_grouped(br, §ions, &mqi_per_g, &max_sfb_capped, &tl_per_g)
.ok()?;
let mut scaled_l: Vec<f32> = Vec::new();
let mut scaled_r: Vec<f32> = Vec::new();
let mut ms_concat: Vec<bool> = Vec::new();
for g in 0..tl_per_g.len() {
let mut l = asf_data::dequantise_and_scale(
&q_l_per_g[g],
&sf_gain_per_g[g],
sfbo_per_g[g],
max_sfb_capped[g],
);
let mut r = asf_data::dequantise_and_scale(
&q_r_per_g[g],
&sf_gain_per_g[g],
sfbo_per_g[g],
max_sfb_capped[g],
);
for (sfb, &used) in ms_used_per_g[g].iter().enumerate() {
if !used {
continue;
}
let a = sfbo_per_g[g][sfb] as usize;
let b = sfbo_per_g[g][sfb + 1] as usize;
let bmax = b.min(l.len()).min(r.len());
for k in a..bmax {
let m = l[k];
let s = r[k];
l[k] = m + s;
r[k] = m - s;
}
}
scaled_l.extend_from_slice(&l);
scaled_r.extend_from_slice(&r);
ms_concat.extend_from_slice(&ms_used_per_g[g]);
}
Some((scaled_l, scaled_r, ms_concat))
}
fn decode_asf_long_mono_body(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
psy: &AsfPsyInfo,
) -> Option<Vec<f32>> {
let tl = ti.transform_length_0;
let tl_idx = ti.transf_length[0];
let max_sfb_cap = tables::num_sfb_48(tl)?;
let max_sfb = psy.max_sfb_0.min(max_sfb_cap);
if max_sfb == 0 {
return None;
}
let sfbo = sfb_offset::sfb_offset_48(tl)?;
let sections = asf_data::parse_asf_section_data(br, tl_idx, tl, max_sfb).ok()?;
let (qspec, mqi) = asf_data::parse_asf_spectral_data(br, §ions, sfbo, max_sfb).ok()?;
let sf_gain = asf_data::parse_asf_scalefac_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let _snf = asf_data::parse_asf_snf_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let scaled = asf_data::dequantise_and_scale(&qspec, &sf_gain, sfbo, max_sfb);
Some(scaled)
}
fn parse_aspx_acpl2_mdct_body(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
frame_len_base: u32,
) -> bool {
let sf_bit = match br.read_u32(1) {
Ok(v) => v,
Err(_) => return false,
};
let frontend = SpecFrontend::from_bit(sf_bit);
tools.spec_frontend_primary = Some(frontend);
if !matches!(frontend, SpecFrontend::Asf) {
return false;
}
let ti = match parse_asf_transform_info(br, frame_len_base) {
Ok(t) => t,
Err(_) => return false,
};
tools.transform_info_primary = Some(ti);
let psy = match parse_asf_psy_info(br, &ti, frame_len_base, false, false) {
Ok(p) => p,
Err(_) => return false,
};
tools.psy_info_primary = Some(psy.clone());
if ti.b_long_frame && psy.num_window_groups == 1 {
match decode_asf_long_mono_body(br, &ti, &psy) {
Some(s) => {
tools.scaled_spec_primary = Some(s);
true
}
None => false,
}
} else if psy.num_window_groups > 1 {
match decode_asf_grouped_mono_body(br, &ti, &psy) {
Some(s) => {
tools.scaled_spec_primary = Some(s);
true
}
None => false,
}
} else {
false
}
}
fn parse_aspx_acpl1_mdct_body(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
frame_len_base: u32,
) -> bool {
let b_mdct_stereo = match br.read_u32(1) {
Ok(v) => v != 0,
Err(_) => return false,
};
tools.mdct_stereo_proc = b_mdct_stereo;
if b_mdct_stereo {
tools.spec_frontend_primary = Some(SpecFrontend::Asf);
tools.spec_frontend_secondary = Some(SpecFrontend::Asf);
let ti = match parse_asf_transform_info(br, frame_len_base) {
Ok(t) => t,
Err(_) => return false,
};
tools.transform_info_primary = Some(ti);
tools.transform_info_secondary = Some(ti);
let psy = match parse_asf_psy_info(br, &ti, frame_len_base, true, false) {
Ok(p) => p,
Err(_) => return false,
};
tools.psy_info_primary = Some(psy.clone());
tools.psy_info_secondary = Some(psy.clone());
let max_sfb_g = psy.max_sfb_side_0.min(psy.max_sfb_0);
let cp = match parse_chparam_info(br, &[max_sfb_g]) {
Ok(c) => c,
Err(_) => return false,
};
if cp.mode() == SapMode::MsUsed && !cp.ms_used.is_empty() {
tools.ms_used = Some(cp.ms_used[0].clone());
}
tools.chparam_info = Some(cp);
let m_body = decode_asf_mono_body_for_max_sfb(br, &ti, &psy, psy.max_sfb_0);
let m_ok = m_body.is_some();
if let Some(s) = m_body {
tools.scaled_spec_primary = Some(s);
}
if !m_ok {
return false;
}
let s_body = decode_asf_mono_body_for_max_sfb(br, &ti, &psy, psy.max_sfb_side_0);
let s_ok = s_body.is_some();
if let Some(s) = s_body {
tools.scaled_spec_secondary = Some(s);
}
s_ok
} else {
let m_bit = match br.read_u32(1) {
Ok(v) => v,
Err(_) => return false,
};
let m_fe = SpecFrontend::from_bit(m_bit);
tools.spec_frontend_primary = Some(m_fe);
let mut ti_m: Option<AsfTransformInfo> = None;
if let SpecFrontend::Asf = m_fe {
let ti = match parse_asf_transform_info(br, frame_len_base) {
Ok(t) => t,
Err(_) => return false,
};
tools.transform_info_primary = Some(ti);
ti_m = Some(ti);
let psy = match parse_asf_psy_info(br, &ti, frame_len_base, false, false) {
Ok(p) => p,
Err(_) => return false,
};
tools.psy_info_primary = Some(psy);
}
let s_bit = match br.read_u32(1) {
Ok(v) => v,
Err(_) => return false,
};
let s_fe = SpecFrontend::from_bit(s_bit);
tools.spec_frontend_secondary = Some(s_fe);
let mut ti_s: Option<AsfTransformInfo> = None;
if let SpecFrontend::Asf = s_fe {
let ti = match parse_asf_transform_info(br, frame_len_base) {
Ok(t) => t,
Err(_) => return false,
};
tools.transform_info_secondary = Some(ti);
ti_s = Some(ti);
let psy = match parse_asf_psy_info(br, &ti, frame_len_base, false, true) {
Ok(p) => p,
Err(_) => return false,
};
tools.psy_info_secondary = Some(psy);
}
let psy_m_clone = tools.psy_info_primary.clone();
if let (Some(ti), Some(psy)) = (ti_m, psy_m_clone.as_ref()) {
match decode_asf_mono_body_dispatch(br, &ti, psy) {
Some(s) => tools.scaled_spec_primary = Some(s),
None => return false,
}
} else if matches!(m_fe, SpecFrontend::Ssf) {
return false;
}
let psy_s_clone = tools.psy_info_secondary.clone();
if let (Some(ti), Some(psy)) = (ti_s, psy_s_clone.as_ref()) {
match decode_asf_mono_body_dispatch(br, &ti, psy) {
Some(s) => tools.scaled_spec_secondary = Some(s),
None => return false,
}
} else if matches!(s_fe, SpecFrontend::Ssf) {
return false;
}
true
}
}
fn decode_asf_mono_body_dispatch(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
psy: &AsfPsyInfo,
) -> Option<Vec<f32>> {
if ti.b_long_frame && psy.num_window_groups == 1 {
decode_asf_long_mono_body(br, ti, psy)
} else if psy.num_window_groups > 1 {
decode_asf_grouped_mono_body(br, ti, psy)
} else {
None
}
}
fn decode_asf_mono_body_for_max_sfb(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
psy: &AsfPsyInfo,
max_sfb_in: u32,
) -> Option<Vec<f32>> {
if ti.b_long_frame && psy.num_window_groups == 1 {
decode_asf_long_mono_body_with_max_sfb(br, ti, max_sfb_in)
} else if psy.num_window_groups > 1 {
decode_asf_grouped_mono_body_with_max_sfb(br, ti, psy, max_sfb_in, max_sfb_in)
} else {
None
}
}
pub(crate) fn decode_asf_long_mono_body_with_max_sfb(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
max_sfb_in: u32,
) -> Option<Vec<f32>> {
let tl = ti.transform_length_0;
let tl_idx = ti.transf_length[0];
let max_sfb_cap = tables::num_sfb_48(tl)?;
let max_sfb = max_sfb_in.min(max_sfb_cap);
if max_sfb == 0 {
return None;
}
let sfbo = sfb_offset::sfb_offset_48(tl)?;
let sections = asf_data::parse_asf_section_data(br, tl_idx, tl, max_sfb).ok()?;
let (qspec, mqi) = asf_data::parse_asf_spectral_data(br, §ions, sfbo, max_sfb).ok()?;
let sf_gain = asf_data::parse_asf_scalefac_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let _snf = asf_data::parse_asf_snf_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let scaled = asf_data::dequantise_and_scale(&qspec, &sf_gain, sfbo, max_sfb);
Some(scaled)
}
pub fn parse_stereo_audio_data_outer(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
b_iframe: bool,
frame_len_base: u32,
) -> Result<()> {
let mode_bits = br.read_u32(2)?;
let mode = StereoCodecMode::from_u32(mode_bits);
tools.stereo_mode = Some(mode);
if mode != StereoCodecMode::Simple {
let mut acpl_cfg_active: Option<crate::acpl::AcplConfig1ch> = None;
if b_iframe {
tools.aspx_config = Some(aspx::parse_aspx_config(br)?);
match mode {
StereoCodecMode::AspxAcpl1 => {
let cfg =
crate::acpl::parse_acpl_config_1ch(br, crate::acpl::Acpl1chMode::Partial)?;
tools.acpl_config_1ch_partial = Some(cfg);
acpl_cfg_active = Some(cfg);
}
StereoCodecMode::AspxAcpl2 => {
let cfg =
crate::acpl::parse_acpl_config_1ch(br, crate::acpl::Acpl1chMode::Full)?;
tools.acpl_config_1ch_full = Some(cfg);
acpl_cfg_active = Some(cfg);
}
_ => {}
}
}
let nc = match mode {
StereoCodecMode::Aspx => 2,
_ => 1,
};
tools.companding = Some(aspx::parse_companding_control(br, nc)?);
if matches!(
mode,
StereoCodecMode::AspxAcpl1 | StereoCodecMode::AspxAcpl2
) {
let body_ok = match mode {
StereoCodecMode::AspxAcpl1 => parse_aspx_acpl1_mdct_body(br, tools, frame_len_base),
StereoCodecMode::AspxAcpl2 => parse_aspx_acpl2_mdct_body(br, tools, frame_len_base),
_ => false,
};
if b_iframe && body_ok {
if let Some(cfg) = tools.aspx_config {
if parse_aspx_data_1ch_body(br, tools, &cfg, b_iframe, frame_len_base).is_ok() {
if let Some(acfg) = acpl_cfg_active {
let start_band = if acfg.qmf_band == 0 {
0
} else {
crate::acpl::sb_to_pb(acfg.qmf_band as u32, acfg.num_param_bands)
};
if let Ok(d) = crate::acpl::parse_acpl_data_1ch(
br,
acfg.num_param_bands,
start_band,
acfg.quant_mode,
) {
tools.acpl_data_1ch = Some(d);
}
}
}
}
}
return Ok(());
}
if matches!(mode, StereoCodecMode::Aspx) {
let body_ok = parse_stereo_data_body(br, tools, frame_len_base);
if b_iframe && body_ok {
if let Some(cfg) = tools.aspx_config {
parse_aspx_data_2ch_body(br, tools, &cfg, b_iframe, frame_len_base)?;
}
}
}
return Ok(());
}
let _ = parse_stereo_data_body(br, tools, frame_len_base);
let _ = b_iframe; Ok(())
}
pub(crate) fn parse_stereo_data_body(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
frame_len_base: u32,
) -> bool {
let b_mdct_stereo = match br.read_bit() {
Ok(v) => v,
Err(_) => return false,
};
tools.mdct_stereo_proc = b_mdct_stereo;
if b_mdct_stereo {
tools.spec_frontend_primary = Some(SpecFrontend::Asf);
tools.spec_frontend_secondary = Some(SpecFrontend::Asf);
let ti = match parse_asf_transform_info(br, frame_len_base) {
Ok(t) => t,
Err(_) => return false,
};
tools.transform_info_primary = Some(ti);
tools.transform_info_secondary = Some(ti);
let psy = match parse_asf_psy_info(br, &ti, frame_len_base, false, false) {
Ok(p) => p,
Err(_) => return false,
};
tools.psy_info_primary = Some(psy.clone());
if ti.b_long_frame && psy.num_window_groups == 1 {
match decode_asf_long_stereo_joint_body(br, &ti, &psy) {
Some((l, r, ms)) => {
tools.scaled_spec_primary = Some(l);
tools.scaled_spec_secondary = Some(r);
tools.ms_used = Some(ms);
true
}
None => false,
}
} else if psy.num_window_groups > 1 {
match decode_asf_grouped_stereo_joint_body(br, &ti, &psy) {
Some((l, r, ms)) => {
tools.scaled_spec_primary = Some(l);
tools.scaled_spec_secondary = Some(r);
tools.ms_used = Some(ms);
true
}
None => false,
}
} else {
false
}
} else {
let l = match br.read_u32(1) {
Ok(v) => SpecFrontend::from_bit(v),
Err(_) => return false,
};
tools.spec_frontend_primary = Some(l);
let mut ti_l: Option<AsfTransformInfo> = None;
let mut psy_l: Option<AsfPsyInfo> = None;
if let SpecFrontend::Asf = l {
let ti = match parse_asf_transform_info(br, frame_len_base) {
Ok(t) => t,
Err(_) => return false,
};
tools.transform_info_primary = Some(ti);
ti_l = Some(ti);
if let Ok(psy) = parse_asf_psy_info(br, &ti, frame_len_base, false, false) {
tools.psy_info_primary = Some(psy.clone());
psy_l = Some(psy);
} else {
return false;
}
}
let r = match br.read_u32(1) {
Ok(v) => SpecFrontend::from_bit(v),
Err(_) => return false,
};
tools.spec_frontend_secondary = Some(r);
let mut ti_r: Option<AsfTransformInfo> = None;
let mut psy_r: Option<AsfPsyInfo> = None;
if let SpecFrontend::Asf = r {
let ti = match parse_asf_transform_info(br, frame_len_base) {
Ok(t) => t,
Err(_) => return false,
};
tools.transform_info_secondary = Some(ti);
ti_r = Some(ti);
if let Ok(psy) = parse_asf_psy_info(br, &ti, frame_len_base, false, false) {
tools.psy_info_secondary = Some(psy.clone());
psy_r = Some(psy);
} else {
return false;
}
}
let mut body_ok = true;
if let (Some(ti), Some(psy)) = (ti_l, psy_l.as_ref()) {
tools.scaled_spec_primary = decode_asf_mono_body_dispatch(br, &ti, psy);
if tools.scaled_spec_primary.is_none() {
body_ok = false;
}
}
if let (Some(ti), Some(psy)) = (ti_r, psy_r.as_ref()) {
tools.scaled_spec_secondary = decode_asf_mono_body_dispatch(br, &ti, psy);
if tools.scaled_spec_secondary.is_none() {
body_ok = false;
}
}
body_ok
}
}
fn decode_asf_long_stereo_joint_body(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
psy: &AsfPsyInfo,
) -> Option<(Vec<f32>, Vec<f32>, Vec<bool>)> {
let tl = ti.transform_length_0;
let tl_idx = ti.transf_length[0];
let max_sfb_cap = tables::num_sfb_48(tl)?;
let max_sfb = psy.max_sfb_0.min(max_sfb_cap);
if max_sfb == 0 {
return None;
}
let sfbo = sfb_offset::sfb_offset_48(tl)?;
let sections = asf_data::parse_asf_section_data(br, tl_idx, tl, max_sfb).ok()?;
let (q_l, mqi_l) = asf_data::parse_asf_spectral_data(br, §ions, sfbo, max_sfb).ok()?;
let (q_r, mqi_r) = asf_data::parse_asf_spectral_data(br, §ions, sfbo, max_sfb).ok()?;
let mqi: Vec<u32> = mqi_l
.iter()
.zip(mqi_r.iter())
.map(|(a, b)| (*a).max(*b))
.collect();
let sf_gain = asf_data::parse_asf_scalefac_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let mut ms_used = vec![false; max_sfb as usize];
for sfb in 0..max_sfb as usize {
let cb = sections.sfb_cb[sfb];
if cb == 0 || mqi[sfb] == 0 {
continue;
}
ms_used[sfb] = br.read_bit().ok()?;
}
let _ = asf_data::parse_asf_snf_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let mut scaled_l = asf_data::dequantise_and_scale(&q_l, &sf_gain, sfbo, max_sfb);
let mut scaled_r = asf_data::dequantise_and_scale(&q_r, &sf_gain, sfbo, max_sfb);
for (sfb, &used) in ms_used.iter().enumerate() {
if !used {
continue;
}
let a = sfbo[sfb] as usize;
let b = sfbo[sfb + 1] as usize;
let bmax = b.min(scaled_l.len()).min(scaled_r.len());
for k in a..bmax {
let m = scaled_l[k];
let s = scaled_r[k];
scaled_l[k] = m + s;
scaled_r[k] = m - s;
}
}
Some((scaled_l, scaled_r, ms_used))
}
pub fn walk_ac4_substream(
substream_bytes: &[u8],
channels: u16,
b_iframe: bool,
frame_len_base: u32,
) -> Result<Ac4SubstreamInfo> {
if substream_bytes.is_empty() {
return Err(Error::invalid("ac4: empty substream"));
}
let mut br = BitReader::new(substream_bytes);
let audio_size_short = br.read_u32(15)?;
let b_more_bits = br.read_bit()?;
let audio_size = if b_more_bits {
audio_size_short + (variable_bits(&mut br, 7)? << 15)
} else {
audio_size_short
};
br.align_to_byte();
let audio_data_offset = br.byte_position() as u32;
let mut tools = SubstreamTools {
channel_mode_channels: channels,
..Default::default()
};
match channels {
1 => parse_mono_audio_data_outer(&mut br, &mut tools, b_iframe, frame_len_base)?,
2 => parse_stereo_audio_data_outer(&mut br, &mut tools, b_iframe, frame_len_base)?,
5 => {
let _ = crate::mch::parse_5x_audio_data_outer(
&mut br,
&mut tools,
false,
b_iframe,
frame_len_base,
);
}
6 => {
let _ = crate::mch::parse_5x_audio_data_outer(
&mut br,
&mut tools,
true,
b_iframe,
frame_len_base,
);
}
7 => {
let _ = crate::mch::parse_7x_audio_data_outer(
&mut br,
&mut tools,
false,
b_iframe,
frame_len_base,
);
}
8 => {
let _ = crate::mch::parse_7x_audio_data_outer(
&mut br,
&mut tools,
true,
b_iframe,
frame_len_base,
);
}
_ => {}
}
Ok(Ac4SubstreamInfo {
audio_size,
audio_data_offset,
tools,
})
}
#[cfg(test)]
mod tests {
use super::*;
use oxideav_core::bits::BitWriter;
#[test]
fn resolve_transf_length_long_frame_table_99() {
assert_eq!(resolve_transf_length(2048, true, 0), 2048);
assert_eq!(resolve_transf_length(1920, true, 3), 1920);
}
#[test]
fn resolve_transf_length_table_100_rows() {
assert_eq!(resolve_transf_length(2048, false, 0), 128);
assert_eq!(resolve_transf_length(2048, false, 1), 256);
assert_eq!(resolve_transf_length(2048, false, 2), 512);
assert_eq!(resolve_transf_length(2048, false, 3), 1024);
assert_eq!(resolve_transf_length(1920, false, 2), 480);
assert_eq!(resolve_transf_length(1536, false, 1), 192);
}
#[test]
fn resolve_transf_length_table_103_rows() {
assert_eq!(resolve_transf_length(1024, false, 3), 1024);
assert_eq!(resolve_transf_length(960, false, 2), 480);
assert_eq!(resolve_transf_length(512, false, 0), 128);
assert_eq!(resolve_transf_length(384, false, 2), 384);
}
#[test]
fn asf_transform_info_long_frame_path() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ti = parse_asf_transform_info(&mut br, 1920).unwrap();
assert!(ti.b_long_frame);
assert_eq!(ti.transform_length_0, 1920);
assert_eq!(ti.transform_length_1, 1920);
}
#[test]
fn asf_transform_info_short_pair() {
let mut bw = BitWriter::new();
bw.write_bit(false); bw.write_u32(2, 2);
bw.write_u32(3, 2);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ti = parse_asf_transform_info(&mut br, 1920).unwrap();
assert!(!ti.b_long_frame);
assert_eq!(ti.transf_length, [2, 3]);
assert_eq!(ti.transform_length_0, 480);
assert_eq!(ti.transform_length_1, 960);
}
fn build_mono_substream() -> Vec<u8> {
let mut bw = BitWriter::new();
bw.write_u32(10, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 1);
bw.write_u32(0, 1);
bw.write_bit(true);
bw.write_u32(40, 6);
bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
bw.finish()
}
#[test]
fn walk_mono_asf_substream_extracts_tools() {
let bytes = build_mono_substream();
let info = walk_ac4_substream(&bytes, 1, true, 1920).unwrap();
assert_eq!(info.audio_size, 10);
assert_eq!(info.tools.mono_mode, Some(MonoCodecMode::Simple));
assert_eq!(info.tools.spec_frontend_primary, Some(SpecFrontend::Asf));
let ti = info.tools.transform_info_primary.unwrap();
assert!(ti.b_long_frame);
assert_eq!(ti.transform_length_0, 1920);
let psy = info.tools.psy_info_primary.as_ref().unwrap();
assert_eq!(psy.max_sfb_0, 40);
assert_eq!(psy.num_windows, 1);
assert_eq!(psy.num_window_groups, 1);
}
fn build_stereo_simple_substream() -> Vec<u8> {
let mut bw = BitWriter::new();
bw.write_u32(20, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 2);
bw.write_bit(false);
bw.write_u32(0, 1);
bw.write_bit(true); bw.write_u32(30, 6);
bw.write_u32(0, 1);
bw.write_bit(true);
bw.write_u32(32, 6);
bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
bw.finish()
}
#[test]
fn walk_stereo_simple_substream_extracts_two_frontends() {
let bytes = build_stereo_simple_substream();
let info = walk_ac4_substream(&bytes, 2, true, 1920).unwrap();
assert_eq!(info.audio_size, 20);
assert_eq!(info.tools.stereo_mode, Some(StereoCodecMode::Simple));
assert!(!info.tools.mdct_stereo_proc);
assert_eq!(info.tools.spec_frontend_primary, Some(SpecFrontend::Asf));
assert_eq!(info.tools.spec_frontend_secondary, Some(SpecFrontend::Asf));
assert!(info.tools.transform_info_primary.is_some());
assert!(info.tools.transform_info_secondary.is_some());
let psy_l = info.tools.psy_info_primary.as_ref().unwrap();
let psy_r = info.tools.psy_info_secondary.as_ref().unwrap();
assert_eq!(psy_l.max_sfb_0, 30);
assert_eq!(psy_r.max_sfb_0, 32);
}
#[test]
fn walk_stereo_mdct_joint_shares_transform_info() {
let mut bw = BitWriter::new();
bw.write_u32(20, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 2); bw.write_bit(true); bw.write_bit(true); bw.write_u32(25, 6); bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 2, true, 1920).unwrap();
assert!(info.tools.mdct_stereo_proc);
assert_eq!(info.tools.spec_frontend_primary, Some(SpecFrontend::Asf));
assert_eq!(info.tools.spec_frontend_secondary, Some(SpecFrontend::Asf));
assert_eq!(
info.tools
.transform_info_primary
.unwrap()
.transform_length_0,
info.tools
.transform_info_secondary
.unwrap()
.transform_length_0
);
let psy = info.tools.psy_info_primary.as_ref().unwrap();
assert_eq!(psy.max_sfb_0, 25);
}
#[test]
fn asf_psy_info_long_frame_path() {
let ti = AsfTransformInfo {
b_long_frame: true,
transf_length: [0, 0],
transform_length_0: 1920,
transform_length_1: 1920,
};
let mut bw = BitWriter::new();
bw.write_u32(50, 6); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let psy = parse_asf_psy_info(&mut br, &ti, 1920, false, false).unwrap();
assert!(!psy.b_different_framing);
assert_eq!(psy.max_sfb_0, 50);
assert_eq!(psy.num_windows, 1);
assert_eq!(psy.num_window_groups, 1);
assert!(psy.scale_factor_grouping.is_empty());
}
#[test]
fn asf_psy_info_short_pair_with_grouping() {
let ti = AsfTransformInfo {
b_long_frame: false,
transf_length: [2, 2],
transform_length_0: 480,
transform_length_1: 480,
};
let mut bw = BitWriter::new();
bw.write_u32(20, 6); bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(1, 1);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let psy = parse_asf_psy_info(&mut br, &ti, 1920, false, false).unwrap();
assert!(!psy.b_different_framing);
assert_eq!(psy.max_sfb_0, 20);
assert_eq!(psy.scale_factor_grouping, vec![1, 0, 1]);
assert_eq!(psy.num_windows, 4);
assert_eq!(psy.num_window_groups, 2);
}
#[test]
fn asf_psy_info_lfe_long_frame_2048_uses_3bit_max_sfb() {
let ti = AsfTransformInfo {
b_long_frame: true,
transf_length: [0, 0],
transform_length_0: 2048,
transform_length_1: 2048,
};
let mut bw = BitWriter::new();
bw.write_u32(7, 3); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let psy = parse_asf_psy_info_lfe(&mut br, &ti).unwrap();
assert_eq!(psy.max_sfb_0, 7);
assert_eq!(psy.num_windows, 1);
assert_eq!(psy.num_window_groups, 1);
assert_eq!(psy.max_sfb_1, 0);
assert!(psy.scale_factor_grouping.is_empty());
}
#[test]
fn asf_psy_info_lfe_long_frame_512_uses_2bit_max_sfb() {
let ti = AsfTransformInfo {
b_long_frame: true,
transf_length: [0, 0],
transform_length_0: 512,
transform_length_1: 512,
};
let mut bw = BitWriter::new();
bw.write_u32(3, 2); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let psy = parse_asf_psy_info_lfe(&mut br, &ti).unwrap();
assert_eq!(psy.max_sfb_0, 3);
}
#[test]
fn asf_psy_info_lfe_rejects_short_only_transforms() {
let ti = AsfTransformInfo {
b_long_frame: false,
transf_length: [2, 2],
transform_length_0: 480,
transform_length_1: 480,
};
let bytes = [0u8; 4];
let mut br = BitReader::new(&bytes);
let err = parse_asf_psy_info_lfe(&mut br, &ti).unwrap_err();
assert!(format!("{err}").contains("LFE"));
}
#[test]
fn asf_psy_info_lfe_rejects_unknown_transform() {
let ti = AsfTransformInfo {
b_long_frame: true,
transf_length: [0, 0],
transform_length_0: 999,
transform_length_1: 999,
};
let bytes = [0u8; 4];
let mut br = BitReader::new(&bytes);
let err = parse_asf_psy_info_lfe(&mut br, &ti).unwrap_err();
assert!(format!("{err}").contains("transform_length"));
}
#[test]
fn asf_psy_info_different_framing_path() {
let ti = AsfTransformInfo {
b_long_frame: false,
transf_length: [1, 2],
transform_length_0: 240,
transform_length_1: 480,
};
let mut bw = BitWriter::new();
bw.write_u32(10, 5); bw.write_u32(15, 6); bw.write_u32(0, 4);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let psy = parse_asf_psy_info(&mut br, &ti, 1920, false, false).unwrap();
assert!(psy.b_different_framing);
assert_eq!(psy.max_sfb_0, 10);
assert_eq!(psy.max_sfb_1, 15);
assert_eq!(psy.num_windows, 5);
}
#[test]
fn walk_mono_aspx_substream_parses_config_and_companding() {
let mut bw = BitWriter::new();
bw.write_u32(5, 15); bw.write_bit(false); bw.align_to_byte();
bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(3, 3);
bw.write_u32(1, 2);
bw.write_u32(1, 1);
bw.write_bit(true);
bw.write_bit(false);
bw.write_bit(true);
bw.write_u32(2, 2);
bw.write_u32(0, 1);
bw.write_u32(0, 2);
bw.write_bit(true);
bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 1, true, 1920).unwrap();
assert_eq!(info.tools.mono_mode, Some(MonoCodecMode::Aspx));
let cfg = info.tools.aspx_config.unwrap();
assert_eq!(cfg.start_freq, 3);
assert_eq!(cfg.stop_freq, 1);
assert_eq!(cfg.noise_sbg, 2);
assert_eq!(cfg.num_noise_sbgroups(), 3);
assert!(cfg.interpolation);
assert!(!cfg.preflat);
assert!(cfg.limiter);
assert!(cfg.signals_freq_res());
let cc = info.tools.companding.as_ref().unwrap();
assert_eq!(cc.compand_on, vec![true]);
assert!(cc.sync_flag.is_none());
assert!(cc.compand_avg.is_none());
}
#[test]
fn walk_stereo_aspx_substream_parses_config() {
let mut bw = BitWriter::new();
bw.write_u32(5, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0b01, 2); bw.write_u32(0, 15);
bw.write_bit(false);
bw.write_bit(true);
bw.write_bit(true);
bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 2, true, 1920).unwrap();
assert_eq!(info.tools.stereo_mode, Some(StereoCodecMode::Aspx));
let cfg = info.tools.aspx_config.unwrap();
assert_eq!(cfg.start_freq, 0);
let cc = info.tools.companding.as_ref().unwrap();
assert_eq!(cc.sync_flag, Some(false));
assert_eq!(cc.compand_on, vec![true, true]);
}
#[test]
fn walk_stereo_aspx_non_iframe_skips_config() {
let mut bw = BitWriter::new();
bw.write_u32(5, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0b01, 2); bw.write_bit(true); bw.write_bit(true); bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 2, false, 1920).unwrap();
assert_eq!(info.tools.stereo_mode, Some(StereoCodecMode::Aspx));
assert!(info.tools.aspx_config.is_none());
let cc = info.tools.companding.as_ref().unwrap();
assert_eq!(cc.sync_flag, Some(true));
assert_eq!(cc.compand_on, vec![true]);
}
fn write_sect_len_incr(bw: &mut BitWriter, sect_len: u32, n_sect_bits: u32, esc: u32) {
let base = sect_len.saturating_sub(1);
let k = base / esc;
let incr = base % esc;
for _ in 0..k {
bw.write_u32(esc, n_sect_bits);
}
bw.write_u32(incr, n_sect_bits);
}
#[test]
fn walk_mono_aspx_iframe_reads_framing_after_mono_data() {
use crate::huffman;
let mut bw = BitWriter::new();
bw.write_u32(200, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(1, 1);
bw.write_u32(0, 1); bw.write_u32(0, 3); bw.write_u32(0, 2); bw.write_u32(0, 1); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(0, 2); bw.write_u32(0, 1); bw.write_u32(3, 2); bw.write_bit(true);
bw.write_u32(0, 1);
bw.write_bit(true);
bw.write_u32(10, 6);
bw.write_u32(5, 4);
write_sect_len_incr(&mut bw, 10, 3, 7);
let sfbo = crate::sfb_offset::sfb_offset_48(1920).unwrap();
let end_line = sfbo[10] as u32;
let hcb = huffman::asf_hcb(5).unwrap();
let pairs = end_line / 2;
for _ in 0..pairs {
bw.write_u32(hcb.cw[40], hcb.len[40] as u32); }
bw.write_u32(120, 8);
bw.write_u32(0, 1);
bw.write_u32(3, 3);
bw.write_bit(false); bw.write_bit(true); bw.align_to_byte();
while bw.byte_len() < 220 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 1, true, 1920).unwrap();
assert_eq!(info.tools.mono_mode, Some(MonoCodecMode::Aspx));
assert!(info.tools.aspx_config.is_some());
assert!(info.tools.companding.is_some());
assert!(info.tools.transform_info_primary.is_some());
assert_eq!(info.tools.aspx_xover_subband_offset, Some(3));
let framing = info.tools.aspx_framing_primary.expect("framing");
assert_eq!(framing.int_class, aspx::AspxIntClass::FixFix);
assert_eq!(framing.num_env, 2);
assert_eq!(framing.num_noise, 2);
assert!(framing.freq_res.is_empty()); assert!(info.tools.aspx_framing_secondary.is_none());
assert!(info.tools.aspx_balance.is_none());
let dd = info.tools.aspx_delta_dir_primary.expect("delta dir");
assert_eq!(dd.sig_delta_dir, vec![false; 2]);
assert_eq!(dd.noise_delta_dir, vec![false; 2]);
assert_eq!(
info.tools.aspx_qmode_env_primary,
Some(aspx::AspxQuantStep::Fine)
);
}
#[test]
fn walk_mono_aspx_non_iframe_does_not_emit_framing() {
use crate::huffman;
let mut bw = BitWriter::new();
bw.write_u32(200, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(1, 1); bw.write_bit(true); bw.write_u32(0, 1); bw.write_bit(true); bw.write_u32(10, 6); bw.write_u32(5, 4); write_sect_len_incr(&mut bw, 10, 3, 7);
let sfbo = crate::sfb_offset::sfb_offset_48(1920).unwrap();
let end_line = sfbo[10] as u32;
let hcb = huffman::asf_hcb(5).unwrap();
for _ in 0..(end_line / 2) {
bw.write_u32(hcb.cw[40], hcb.len[40] as u32);
}
bw.write_u32(120, 8);
bw.write_u32(0, 1);
bw.align_to_byte();
while bw.byte_len() < 220 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 1, false, 1920).unwrap();
assert_eq!(info.tools.mono_mode, Some(MonoCodecMode::Aspx));
assert!(info.tools.aspx_config.is_none());
assert!(info.tools.aspx_framing_primary.is_none());
assert!(info.tools.aspx_xover_subband_offset.is_none());
}
#[test]
fn walk_mono_aspx_iframe_reads_ec_data_end_to_end() {
use crate::aspx::{
ASPX_HCB_ENV_LEVEL_15_DF, ASPX_HCB_ENV_LEVEL_15_F0, ASPX_HCB_NOISE_LEVEL_F0,
};
use crate::huffman;
let mut bw = BitWriter::new();
bw.write_u32(500, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(1, 1); bw.write_u32(0, 1); bw.write_u32(3, 3); bw.write_u32(3, 2); bw.write_u32(1, 1); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(0, 2); bw.write_u32(0, 1); bw.write_u32(3, 2); bw.write_bit(true);
bw.write_u32(0, 1); bw.write_bit(true); bw.write_u32(10, 6); bw.write_u32(5, 4);
write_sect_len_incr(&mut bw, 10, 3, 7);
let sfbo = crate::sfb_offset::sfb_offset_48(1920).unwrap();
let end_line = sfbo[10] as u32;
let hcb = huffman::asf_hcb(5).unwrap();
for _ in 0..(end_line / 2) {
bw.write_u32(hcb.cw[40], hcb.len[40] as u32);
}
bw.write_u32(120, 8); bw.write_u32(0, 1); bw.write_u32(7, 3); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false);
bw.write_bit(false);
bw.write_u32(0, 2);
bw.write_bit(false);
bw.write_bit(false);
bw.write_bit(false);
let f0_cb = &ASPX_HCB_ENV_LEVEL_15_F0;
let df_cb = &ASPX_HCB_ENV_LEVEL_15_DF;
bw.write_u32(f0_cb.cw[30], f0_cb.len[30] as u32);
bw.write_u32(df_cb.cw[70], df_cb.len[70] as u32);
bw.write_u32(df_cb.cw[70], df_cb.len[70] as u32);
let noise_f0 = &ASPX_HCB_NOISE_LEVEL_F0;
bw.write_u32(noise_f0.cw[6], noise_f0.len[6] as u32);
bw.align_to_byte();
while bw.byte_len() < 520 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 1, true, 1920).unwrap();
let tables = info.tools.aspx_frequency_tables.as_ref().expect("tables");
assert_eq!(tables.num_sbg_master, 10);
assert_eq!(tables.sba, 24);
assert_eq!(tables.sbz, 44);
assert_eq!(tables.counts.num_sbg_sig_highres, 3);
assert_eq!(tables.counts.num_sbg_sig_lowres, 2);
assert_eq!(tables.counts.num_sbg_noise, 1);
let hfgen = info.tools.aspx_hfgen_iwc_1ch.as_ref().expect("hfgen");
assert_eq!(hfgen.tna_mode, vec![0]);
assert!(!hfgen.ah_present);
assert!(!hfgen.fic_present);
assert!(!hfgen.tic_present);
let sig = info.tools.aspx_data_sig_primary.as_ref().expect("sig");
assert_eq!(sig.len(), 1);
assert_eq!(sig[0].values, vec![30, 0, 0]);
assert!(!sig[0].direction_time);
let noise = info.tools.aspx_data_noise_primary.as_ref().expect("noise");
assert_eq!(noise.len(), 1);
assert_eq!(noise[0].values, vec![6]);
assert!(!noise[0].direction_time);
}
#[test]
fn walk_stereo_aspx_acpl1_parses_acpl_config_partial() {
let mut bw = BitWriter::new();
bw.write_u32(5, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0b10, 2); bw.write_u32(0, 15); bw.write_u32(0b01, 2);
bw.write_u32(1, 1);
bw.write_u32(0b011, 3);
bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 2, true, 1920).unwrap();
assert_eq!(info.tools.stereo_mode, Some(StereoCodecMode::AspxAcpl1));
assert!(info.tools.aspx_config.is_some());
let acpl = info
.tools
.acpl_config_1ch_partial
.expect("acpl_config_1ch_partial should be set");
assert_eq!(acpl.num_param_bands_id, 1);
assert_eq!(acpl.num_param_bands, 12);
assert_eq!(acpl.quant_mode, crate::acpl::AcplQuantMode::Coarse);
assert_eq!(acpl.qmf_band, 4);
assert!(info.tools.acpl_config_1ch_full.is_none());
assert!(info.tools.companding.is_some());
assert!(info.tools.acpl_data_1ch.is_none());
}
#[test]
fn walk_stereo_aspx_acpl2_parses_acpl_config_full() {
let mut bw = BitWriter::new();
bw.write_u32(5, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0b11, 2); bw.write_u32(0, 15); bw.write_u32(0b10, 2);
bw.write_u32(0, 1);
bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 2, true, 1920).unwrap();
assert_eq!(info.tools.stereo_mode, Some(StereoCodecMode::AspxAcpl2));
assert!(info.tools.aspx_config.is_some());
let acpl = info
.tools
.acpl_config_1ch_full
.expect("acpl_config_1ch_full should be set");
assert_eq!(acpl.num_param_bands_id, 2);
assert_eq!(acpl.num_param_bands, 9);
assert_eq!(acpl.quant_mode, crate::acpl::AcplQuantMode::Fine);
assert_eq!(acpl.qmf_band, 0);
assert!(info.tools.acpl_config_1ch_partial.is_none());
assert!(info.tools.companding.is_some());
assert!(info.tools.acpl_data_1ch.is_none());
}
#[test]
fn chparam_info_sap_mode_zero_no_payload() {
let mut bw = BitWriter::new();
bw.write_u32(0, 2);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let cp = parse_chparam_info(&mut br, &[8]).unwrap();
assert_eq!(cp.sap_mode, 0);
assert_eq!(cp.mode(), SapMode::None);
assert!(cp.ms_used.is_empty());
assert!(cp.sap_data.is_none());
}
#[test]
fn chparam_info_sap_mode_one_walks_ms_used_per_group() {
let mut bw = BitWriter::new();
bw.write_u32(1, 2);
bw.write_bit(true);
bw.write_bit(false);
bw.write_bit(true);
bw.write_bit(true);
bw.write_bit(false);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let cp = parse_chparam_info(&mut br, &[5]).unwrap();
assert_eq!(cp.mode(), SapMode::MsUsed);
assert_eq!(cp.ms_used.len(), 1);
assert_eq!(cp.ms_used[0], vec![true, false, true, true, false]);
}
#[test]
fn chparam_info_sap_mode_three_walks_sap_data_pair_packed() {
use crate::huffman::{HCB_SCALEFAC_CW, HCB_SCALEFAC_LEN};
let mut bw = BitWriter::new();
bw.write_u32(3, 2); bw.write_bit(false); bw.write_bit(true); bw.write_bit(true); bw.write_u32(HCB_SCALEFAC_CW[60], HCB_SCALEFAC_LEN[60] as u32);
bw.write_u32(HCB_SCALEFAC_CW[61], HCB_SCALEFAC_LEN[61] as u32);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let cp = parse_chparam_info(&mut br, &[4]).unwrap();
assert_eq!(cp.mode(), SapMode::SapData);
let sd = cp.sap_data.expect("sap_data populated");
assert!(!sd.sap_coeff_all);
assert_eq!(sd.sap_coeff_used, vec![vec![true, true, true, true]]);
assert_eq!(sd.dpcm_alpha_q, vec![vec![0, 0, 1, 0]]);
}
#[test]
fn chparam_info_sap_mode_three_all_flag_skips_pair_array() {
use crate::huffman::{HCB_SCALEFAC_CW, HCB_SCALEFAC_LEN};
let mut bw = BitWriter::new();
bw.write_u32(3, 2);
bw.write_bit(true); bw.write_u32(HCB_SCALEFAC_CW[60], HCB_SCALEFAC_LEN[60] as u32);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let cp = parse_chparam_info(&mut br, &[2]).unwrap();
let sd = cp.sap_data.expect("sap_data populated");
assert!(sd.sap_coeff_all);
assert_eq!(sd.sap_coeff_used, vec![vec![true, true]]);
assert_eq!(sd.dpcm_alpha_q, vec![vec![0, 0]]);
}
#[test]
fn chparam_info_sap_mode_three_two_groups_emits_delta_code_time() {
use crate::huffman::{HCB_SCALEFAC_CW, HCB_SCALEFAC_LEN};
let mut bw = BitWriter::new();
bw.write_u32(3, 2);
bw.write_bit(true); bw.write_bit(true); bw.write_u32(HCB_SCALEFAC_CW[60], HCB_SCALEFAC_LEN[60] as u32);
bw.write_u32(HCB_SCALEFAC_CW[60], HCB_SCALEFAC_LEN[60] as u32);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let cp = parse_chparam_info(&mut br, &[2, 2]).unwrap();
let sd = cp.sap_data.expect("sap_data populated");
assert!(sd.sap_coeff_all);
assert!(sd.delta_code_time);
assert_eq!(sd.dpcm_alpha_q.len(), 2);
assert_eq!(sd.dpcm_alpha_q[0], vec![0, 0]);
assert_eq!(sd.dpcm_alpha_q[1], vec![0, 0]);
}
fn write_zero_grouped_sf_data_body_one_channel(
bw: &mut BitWriter,
max_sfb_per_group: &[u32],
transf_length_idx_per_group: &[u32],
) {
for g in 0..max_sfb_per_group.len() {
let max_sfb = max_sfb_per_group[g];
let tl_idx = transf_length_idx_per_group[g];
let (n_sect_bits, sect_esc_val) = if tl_idx <= 2 { (3, 7) } else { (5, 31) };
bw.write_u32(0, 4); let mut remaining = max_sfb.saturating_sub(1);
while remaining >= sect_esc_val {
bw.write_u32(sect_esc_val, n_sect_bits);
remaining -= sect_esc_val;
}
bw.write_u32(remaining, n_sect_bits);
}
bw.write_u32(120, 8);
bw.write_bit(false);
}
#[test]
fn decode_asf_grouped_mono_body_two_groups_equal_tl() {
let max_sfb = 6u32;
let tl_idx = 2u32; let mut bw = BitWriter::new();
write_zero_grouped_sf_data_body_one_channel(
&mut bw,
&[max_sfb, max_sfb],
&[tl_idx, tl_idx],
);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ti = AsfTransformInfo {
b_long_frame: false,
transf_length: [tl_idx, tl_idx],
transform_length_0: 480,
transform_length_1: 480,
};
let psy = AsfPsyInfo {
max_sfb_0: max_sfb,
num_windows: 2,
num_window_groups: 2,
scale_factor_grouping: vec![0],
..Default::default()
};
let scaled = decode_asf_grouped_mono_body(&mut br, &ti, &psy).expect("decodes");
let sfbo = sfb_offset::sfb_offset_48(480).unwrap();
let per_group_len = sfbo[max_sfb as usize] as usize;
assert_eq!(scaled.len(), per_group_len * 2);
assert!(scaled.iter().all(|&v| v == 0.0));
}
#[test]
fn decode_asf_grouped_mono_body_truncated_returns_none() {
let max_sfb = 5u32;
let tl_idx = 2u32;
let mut bw = BitWriter::new();
let (n_sect_bits, sect_esc_val) = (3, 7);
bw.write_u32(0, 4);
let mut remaining = max_sfb.saturating_sub(1);
while remaining >= sect_esc_val {
bw.write_u32(sect_esc_val, n_sect_bits);
remaining -= sect_esc_val;
}
bw.write_u32(remaining, n_sect_bits);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ti = AsfTransformInfo {
b_long_frame: false,
transf_length: [tl_idx, tl_idx],
transform_length_0: 480,
transform_length_1: 480,
};
let psy = AsfPsyInfo {
max_sfb_0: max_sfb,
num_windows: 3,
num_window_groups: 3,
scale_factor_grouping: vec![0, 0],
..Default::default()
};
let _ = decode_asf_grouped_mono_body(&mut br, &ti, &psy);
}
#[test]
fn walk_mono_simple_short_frame_two_groups_decodes_sf_data() {
let max_sfb = 5u32;
let mut bw = BitWriter::new();
bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(2, 2); bw.write_u32(2, 2); bw.write_u32(max_sfb, 6);
bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(1, 1);
write_zero_grouped_sf_data_body_one_channel(&mut bw, &[max_sfb, max_sfb], &[2, 2]);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_mono_audio_data_outer(&mut br, &mut tools, true, 1920).unwrap();
let psy = tools.psy_info_primary.as_ref().unwrap();
assert_eq!(psy.num_window_groups, 2);
let scaled = tools
.scaled_spec_primary
.as_ref()
.expect("grouped mono sf_data body decoded");
let sfbo = sfb_offset::sfb_offset_48(480).unwrap();
assert_eq!(scaled.len(), 2 * sfbo[max_sfb as usize] as usize);
assert!(scaled.iter().all(|&v| v == 0.0));
}
#[test]
fn parse_stereo_data_body_split_short_frame_two_groups_decodes_both_channels() {
let max_sfb = 4u32;
let mut bw = BitWriter::new();
bw.write_bit(false);
bw.write_bit(false);
bw.write_bit(false); bw.write_u32(2, 2);
bw.write_u32(2, 2);
bw.write_u32(max_sfb, 6);
bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(1, 1);
bw.write_bit(false);
bw.write_bit(false);
bw.write_u32(2, 2);
bw.write_u32(2, 2);
bw.write_u32(max_sfb, 6);
bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(1, 1);
write_zero_grouped_sf_data_body_one_channel(&mut bw, &[max_sfb, max_sfb], &[2, 2]);
write_zero_grouped_sf_data_body_one_channel(&mut bw, &[max_sfb, max_sfb], &[2, 2]);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
let ok = parse_stereo_data_body(&mut br, &mut tools, 1920);
assert!(ok, "stereo split short-frame body should parse");
let sfbo = sfb_offset::sfb_offset_48(480).unwrap();
let expected = 2 * sfbo[max_sfb as usize] as usize;
let l = tools.scaled_spec_primary.as_ref().unwrap();
let r = tools.scaled_spec_secondary.as_ref().unwrap();
assert_eq!(l.len(), expected);
assert_eq!(r.len(), expected);
assert!(l.iter().all(|&v| v == 0.0));
assert!(r.iter().all(|&v| v == 0.0));
}
#[test]
fn parse_stereo_data_body_joint_short_frame_two_groups_decodes_both_channels() {
let max_sfb = 4u32;
let mut bw = BitWriter::new();
bw.write_bit(true);
bw.write_bit(false);
bw.write_u32(2, 2);
bw.write_u32(2, 2);
bw.write_u32(max_sfb, 6);
bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(1, 1);
for _ in 0..2 {
bw.write_u32(0, 4);
let mut remaining = max_sfb.saturating_sub(1);
while remaining >= 7 {
bw.write_u32(7, 3);
remaining -= 7;
}
bw.write_u32(remaining, 3);
}
bw.write_u32(120, 8);
bw.write_bit(false);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
let ok = parse_stereo_data_body(&mut br, &mut tools, 1920);
assert!(ok, "stereo joint short-frame body should parse");
let sfbo = sfb_offset::sfb_offset_48(480).unwrap();
let expected = 2 * sfbo[max_sfb as usize] as usize;
let l = tools.scaled_spec_primary.as_ref().unwrap();
let r = tools.scaled_spec_secondary.as_ref().unwrap();
assert_eq!(l.len(), expected);
assert_eq!(r.len(), expected);
let ms = tools.ms_used.as_ref().unwrap();
assert_eq!(ms.len(), 2 * max_sfb as usize);
assert!(ms.iter().all(|&b| !b));
}
}