use oxideav_core::bits::BitReader;
use oxideav_core::Result;
use crate::asf::{
decode_asf_long_lfe_body_with_max_sfb_lfe, decode_asf_long_mono_body_with_max_sfb,
parse_asf_psy_info, parse_asf_psy_info_lfe, parse_asf_transform_info, parse_chparam_info,
AsfPsyInfo, AsfTransformInfo, ChparamInfo, SubstreamTools,
};
use crate::tables;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FiveXCodecMode {
Simple,
Aspx,
AspxAcpl1,
AspxAcpl2,
AspxAcpl3,
Reserved(u8),
}
impl FiveXCodecMode {
pub fn from_u32(v: u32) -> Self {
match v & 0b111 {
0 => Self::Simple,
1 => Self::Aspx,
2 => Self::AspxAcpl1,
3 => Self::AspxAcpl2,
4 => Self::AspxAcpl3,
other => Self::Reserved(other as u8),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FiveXCodingConfig {
Cfg0Stereo2plusMono,
Cfg1ThreeStereo,
Cfg2FourMono,
Cfg3Five,
AcplLite2,
}
#[derive(Debug, Clone, Default)]
pub struct MonoLfeData {
pub b_lfe: bool,
pub spec_frontend_bit: u8,
pub transform_info: Option<AsfTransformInfo>,
pub psy_info: Option<AsfPsyInfo>,
pub scaled_spec: Option<Vec<f32>>,
}
#[derive(Debug, Clone, Default)]
pub struct ThreeChannelInfo {
pub chel_matsel: u8,
pub chparam: [ChparamInfo; 2],
}
#[derive(Debug, Clone, Default)]
pub struct FourChannelInfo {
pub chparam: [ChparamInfo; 4],
}
#[derive(Debug, Clone, Default)]
pub struct FiveChannelInfo {
pub chel_matsel: u8,
pub chparam: [ChparamInfo; 5],
}
#[derive(Debug, Clone, Default)]
pub struct TwoChannelData {
pub transform_info: Option<AsfTransformInfo>,
pub psy_info: Option<AsfPsyInfo>,
pub chparam: Option<ChparamInfo>,
pub scaled_spec_per_channel: Vec<Option<Vec<f32>>>,
}
#[derive(Debug, Clone, Default)]
pub struct ThreeChannelData {
pub transform_info: Option<AsfTransformInfo>,
pub psy_info: Option<AsfPsyInfo>,
pub info: Option<ThreeChannelInfo>,
pub scaled_spec_per_channel: Vec<Option<Vec<f32>>>,
}
#[derive(Debug, Clone, Default)]
pub struct FourChannelData {
pub transform_info: Option<AsfTransformInfo>,
pub psy_info: Option<AsfPsyInfo>,
pub info: Option<FourChannelInfo>,
pub scaled_spec_per_channel: Vec<Option<Vec<f32>>>,
}
#[derive(Debug, Clone, Default)]
pub struct FiveChannelData {
pub transform_info: Option<AsfTransformInfo>,
pub psy_info: Option<AsfPsyInfo>,
pub info: Option<FiveChannelInfo>,
pub scaled_spec_per_channel: Vec<Option<Vec<f32>>>,
}
pub fn parse_mono_data(
br: &mut BitReader<'_>,
b_lfe: bool,
frame_len_base: u32,
) -> Result<MonoLfeData> {
let mut out = MonoLfeData {
b_lfe,
..Default::default()
};
if !b_lfe {
out.spec_frontend_bit = br.read_u32(1)? as u8;
}
let ti = parse_asf_transform_info(br, frame_len_base)?;
out.transform_info = Some(ti);
let psy = if b_lfe {
parse_asf_psy_info_lfe(br, &ti)?
} else {
parse_asf_psy_info(br, &ti, frame_len_base, false, false)?
};
if !b_lfe && out.spec_frontend_bit == 0 {
if ti.b_long_frame && psy.num_window_groups == 1 {
if let Some(scaled) = decode_asf_long_mono_body_with_max_sfb(br, &ti, psy.max_sfb_0) {
out.scaled_spec = Some(scaled);
}
} else if psy.num_window_groups > 0 {
if let Some(scaled) = decode_asf_grouped_mono_body_with_max_sfb(
br,
&ti,
psy.max_sfb_0,
psy.num_window_groups,
) {
out.scaled_spec = Some(scaled);
}
}
} else if b_lfe {
if let Some(scaled) = decode_asf_long_lfe_body_with_max_sfb_lfe(br, &ti, psy.max_sfb_0) {
out.scaled_spec = Some(scaled);
}
}
out.psy_info = Some(psy);
Ok(out)
}
pub fn parse_two_channel_data(
br: &mut BitReader<'_>,
frame_len_base: u32,
) -> Result<TwoChannelData> {
let ti = parse_asf_transform_info(br, frame_len_base)?;
let psy = parse_asf_psy_info(br, &ti, frame_len_base, false, false)?;
let max_sfb_g = psy.max_sfb_0;
let chparam = parse_chparam_info(br, &[max_sfb_g])?;
let scaled = decode_mch_sf_data_channels(br, &ti, &psy, 2);
Ok(TwoChannelData {
transform_info: Some(ti),
psy_info: Some(psy),
chparam: Some(chparam),
scaled_spec_per_channel: scaled,
})
}
pub fn parse_three_channel_info(
br: &mut BitReader<'_>,
max_sfb_per_group: &[u32],
) -> Result<ThreeChannelInfo> {
let chel_matsel = br.read_u32(4)? as u8;
let cp0 = parse_chparam_info(br, max_sfb_per_group)?;
let cp1 = parse_chparam_info(br, max_sfb_per_group)?;
Ok(ThreeChannelInfo {
chel_matsel,
chparam: [cp0, cp1],
})
}
pub fn parse_four_channel_info(
br: &mut BitReader<'_>,
max_sfb_per_group: &[u32],
) -> Result<FourChannelInfo> {
let cp0 = parse_chparam_info(br, max_sfb_per_group)?;
let cp1 = parse_chparam_info(br, max_sfb_per_group)?;
let cp2 = parse_chparam_info(br, max_sfb_per_group)?;
let cp3 = parse_chparam_info(br, max_sfb_per_group)?;
Ok(FourChannelInfo {
chparam: [cp0, cp1, cp2, cp3],
})
}
pub fn parse_five_channel_info(
br: &mut BitReader<'_>,
max_sfb_per_group: &[u32],
) -> Result<FiveChannelInfo> {
let chel_matsel = br.read_u32(4)? as u8;
let cp0 = parse_chparam_info(br, max_sfb_per_group)?;
let cp1 = parse_chparam_info(br, max_sfb_per_group)?;
let cp2 = parse_chparam_info(br, max_sfb_per_group)?;
let cp3 = parse_chparam_info(br, max_sfb_per_group)?;
let cp4 = parse_chparam_info(br, max_sfb_per_group)?;
Ok(FiveChannelInfo {
chel_matsel,
chparam: [cp0, cp1, cp2, cp3, cp4],
})
}
pub fn parse_three_channel_data(
br: &mut BitReader<'_>,
frame_len_base: u32,
) -> Result<ThreeChannelData> {
let ti = parse_asf_transform_info(br, frame_len_base)?;
let psy = parse_asf_psy_info(br, &ti, frame_len_base, false, false)?;
let max_sfb_g = psy.max_sfb_0;
let info = parse_three_channel_info(br, &[max_sfb_g])?;
let scaled = decode_mch_sf_data_channels(br, &ti, &psy, 3);
Ok(ThreeChannelData {
transform_info: Some(ti),
psy_info: Some(psy),
info: Some(info),
scaled_spec_per_channel: scaled,
})
}
pub fn parse_four_channel_data(
br: &mut BitReader<'_>,
frame_len_base: u32,
) -> Result<FourChannelData> {
let ti = parse_asf_transform_info(br, frame_len_base)?;
let psy = parse_asf_psy_info(br, &ti, frame_len_base, false, false)?;
let max_sfb_g = psy.max_sfb_0;
let info = parse_four_channel_info(br, &[max_sfb_g])?;
let scaled = decode_mch_sf_data_channels(br, &ti, &psy, 4);
Ok(FourChannelData {
transform_info: Some(ti),
psy_info: Some(psy),
info: Some(info),
scaled_spec_per_channel: scaled,
})
}
pub fn parse_five_channel_data(
br: &mut BitReader<'_>,
frame_len_base: u32,
) -> Result<FiveChannelData> {
let ti = parse_asf_transform_info(br, frame_len_base)?;
let psy = parse_asf_psy_info(br, &ti, frame_len_base, false, false)?;
let max_sfb_g = psy.max_sfb_0;
let info = parse_five_channel_info(br, &[max_sfb_g])?;
let scaled = decode_mch_sf_data_channels(br, &ti, &psy, 5);
Ok(FiveChannelData {
transform_info: Some(ti),
psy_info: Some(psy),
info: Some(info),
scaled_spec_per_channel: scaled,
})
}
pub(crate) fn decode_mch_sf_data_channels(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
psy: &AsfPsyInfo,
n_channels: usize,
) -> Vec<Option<Vec<f32>>> {
let mut out = vec![None; n_channels];
if ti.b_long_frame && psy.num_window_groups == 1 {
for slot in out.iter_mut() {
match decode_asf_long_mono_body_with_max_sfb(br, ti, psy.max_sfb_0) {
Some(v) => *slot = Some(v),
None => break,
}
}
return out;
}
if psy.num_window_groups == 0 {
return out;
}
for slot in out.iter_mut() {
match decode_asf_grouped_mono_body_with_max_sfb(
br,
ti,
psy.max_sfb_0,
psy.num_window_groups,
) {
Some(v) => *slot = Some(v),
None => break,
}
}
out
}
fn decode_asf_grouped_mono_body_with_max_sfb(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
max_sfb_in: u32,
num_window_groups: u32,
) -> Option<Vec<f32>> {
let tl = ti.transform_length_0;
let tl_idx = ti.transf_length[0];
let max_sfb_cap = crate::tables::num_sfb_48(tl)?;
let max_sfb = max_sfb_in.min(max_sfb_cap);
if max_sfb == 0 {
return None;
}
let sfbo = crate::sfb_offset::sfb_offset_48(tl)?;
let per_group_len = sfbo[max_sfb as usize] as usize;
let total_len = per_group_len.checked_mul(num_window_groups as usize)?;
let mut out = Vec::with_capacity(total_len);
for _g in 0..num_window_groups {
let sections = crate::asf_data::parse_asf_section_data(br, tl_idx, tl, max_sfb).ok()?;
let (qspec, mqi) =
crate::asf_data::parse_asf_spectral_data(br, §ions, sfbo, max_sfb).ok()?;
let sf_gain =
crate::asf_data::parse_asf_scalefac_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let _snf = crate::asf_data::parse_asf_snf_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let scaled = crate::asf_data::dequantise_and_scale(&qspec, &sf_gain, sfbo, max_sfb);
out.extend_from_slice(&scaled);
}
Some(out)
}
pub fn parse_5x_audio_data_outer(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
b_has_lfe: bool,
b_iframe: bool,
frame_len_base: u32,
) -> Result<()> {
let mode_bits = br.read_u32(3)?;
let mode = FiveXCodecMode::from_u32(mode_bits);
tools.five_x_mode = Some(mode);
tools.five_x_b_has_lfe = b_has_lfe;
if b_iframe {
match mode {
FiveXCodecMode::Aspx
| FiveXCodecMode::AspxAcpl1
| FiveXCodecMode::AspxAcpl2
| FiveXCodecMode::AspxAcpl3 => {
tools.aspx_config = Some(crate::aspx::parse_aspx_config(br)?);
}
_ => {}
}
match mode {
FiveXCodecMode::AspxAcpl1 => {
let cfg =
crate::acpl::parse_acpl_config_1ch(br, crate::acpl::Acpl1chMode::Partial)?;
tools.acpl_config_1ch_partial = Some(cfg);
}
FiveXCodecMode::AspxAcpl2 => {
let cfg = crate::acpl::parse_acpl_config_1ch(br, crate::acpl::Acpl1chMode::Full)?;
tools.acpl_config_1ch_full = Some(cfg);
}
FiveXCodecMode::AspxAcpl3 => {
let cfg = crate::acpl::parse_acpl_config_2ch(br)?;
tools.acpl_config_2ch = Some(cfg);
}
_ => {}
}
}
if b_has_lfe {
let lfe = parse_mono_data(br, true, frame_len_base)?;
tools.lfe_mono_data = Some(lfe);
}
match mode {
FiveXCodecMode::Simple | FiveXCodecMode::Aspx => {
if matches!(mode, FiveXCodecMode::Aspx) {
tools.companding = Some(crate::aspx::parse_companding_control(br, 5)?);
}
let cc = br.read_u32(2)?;
let coding_cfg = match cc {
0 => FiveXCodingConfig::Cfg0Stereo2plusMono,
1 => FiveXCodingConfig::Cfg1ThreeStereo,
2 => FiveXCodingConfig::Cfg2FourMono,
_ => FiveXCodingConfig::Cfg3Five,
};
tools.five_x_coding_config = Some(coding_cfg);
match coding_cfg {
FiveXCodingConfig::Cfg0Stereo2plusMono => {
tools.b_2ch_mode = Some(br.read_bit()?);
tools.two_channel_data.clear();
tools
.two_channel_data
.push(parse_two_channel_data(br, frame_len_base)?);
tools
.two_channel_data
.push(parse_two_channel_data(br, frame_len_base)?);
tools.cfg0_centre_mono = Some(parse_mono_data(br, false, frame_len_base)?);
}
FiveXCodingConfig::Cfg1ThreeStereo => {
tools.three_channel_data = Some(parse_three_channel_data(br, frame_len_base)?);
tools.two_channel_data.clear();
tools
.two_channel_data
.push(parse_two_channel_data(br, frame_len_base)?);
}
FiveXCodingConfig::Cfg2FourMono => {
tools.four_channel_data = Some(parse_four_channel_data(br, frame_len_base)?);
tools.cfg2_back_mono = Some(parse_mono_data(br, false, frame_len_base)?);
}
FiveXCodingConfig::Cfg3Five => {
tools.five_channel_data = Some(parse_five_channel_data(br, frame_len_base)?);
}
FiveXCodingConfig::AcplLite2 => {
debug_assert!(
false,
"AcplLite2 unreachable from SIMPLE/ASPX 2-bit coding_config"
);
}
}
if matches!(mode, FiveXCodecMode::Aspx) && b_iframe && tools.aspx_config.is_some() {
let aspx_cfg = tools.aspx_config.unwrap();
let lr = crate::asf::capture_aspx_data_2ch_trailer(
br,
tools,
&aspx_cfg,
b_iframe,
frame_len_base,
);
let ls_rs = crate::asf::capture_aspx_data_2ch_trailer(
br,
tools,
&aspx_cfg,
b_iframe,
frame_len_base,
);
let centre = crate::asf::capture_aspx_data_1ch_trailer(
br,
tools,
&aspx_cfg,
b_iframe,
frame_len_base,
);
match coding_cfg {
FiveXCodingConfig::Cfg2FourMono => {
tools.cfg2_aspx_lr = lr;
tools.cfg2_aspx_ls_rs = ls_rs;
tools.cfg2_aspx_centre = centre;
}
FiveXCodingConfig::Cfg0Stereo2plusMono => {
tools.cfg0_aspx_lr = lr;
tools.cfg0_aspx_ls_rs = ls_rs;
tools.cfg0_aspx_centre = centre;
}
FiveXCodingConfig::Cfg1ThreeStereo => {
tools.cfg1_aspx_lr = lr;
tools.cfg1_aspx_ls_rs = ls_rs;
tools.cfg1_aspx_centre = centre;
}
FiveXCodingConfig::Cfg3Five => {
tools.cfg3_aspx_lr = lr;
tools.cfg3_aspx_ls_rs = ls_rs;
tools.cfg3_aspx_centre = centre;
}
FiveXCodingConfig::AcplLite2 => {
let _ = (lr, ls_rs, centre);
}
}
}
}
FiveXCodecMode::AspxAcpl1 | FiveXCodecMode::AspxAcpl2 => {
tools.companding = Some(crate::aspx::parse_companding_control(br, 3)?);
let cc = br.read_bit()?;
let coding_cfg = if cc {
FiveXCodingConfig::Cfg1ThreeStereo
} else {
FiveXCodingConfig::AcplLite2
};
tools.five_x_coding_config = Some(coding_cfg);
let _ = parse_aspx_acpl_1_2_inner_body(br, tools, mode, cc, b_iframe, frame_len_base);
}
FiveXCodecMode::AspxAcpl3 => {
tools.companding = Some(crate::aspx::parse_companding_control(br, 2)?);
let body_ok = crate::asf::parse_stereo_data_body(br, tools, frame_len_base);
if b_iframe && body_ok {
if let Some(cfg) = tools.aspx_config {
crate::asf::parse_aspx_data_2ch_body(
br,
tools,
&cfg,
b_iframe,
frame_len_base,
)?;
if let Some(acfg) = tools.acpl_config_2ch {
if let Ok(d) = crate::acpl::parse_acpl_data_2ch(
br,
acfg.num_param_bands,
0,
acfg.quant_mode_0,
acfg.quant_mode_1,
) {
tools.acpl_data_2ch = Some(d);
}
}
}
}
}
FiveXCodecMode::Reserved(_) => {}
}
Ok(())
}
fn parse_aspx_acpl_1_2_inner_body(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
mode: FiveXCodecMode,
coding_config_bit: bool,
b_iframe: bool,
frame_len_base: u32,
) -> Result<()> {
debug_assert!(matches!(
mode,
FiveXCodecMode::AspxAcpl1 | FiveXCodecMode::AspxAcpl2
));
let largest_tl: Option<u32> = if coding_config_bit {
match parse_three_channel_data(br, frame_len_base) {
Ok(d) => {
let tl = d.transform_info.as_ref().map(|ti| ti.transform_length_0);
tools.three_channel_data = Some(d);
tl
}
Err(_) => return Ok(()),
}
} else {
match parse_two_channel_data(br, frame_len_base) {
Ok(d) => {
let tl = d.transform_info.as_ref().map(|ti| ti.transform_length_0);
tools.two_channel_data.clear();
tools.two_channel_data.push(d);
tl
}
Err(_) => return Ok(()),
}
};
if matches!(mode, FiveXCodecMode::AspxAcpl1) {
let Some(tl) = largest_tl else {
return Ok(());
};
let Some((_n_msfb, n_side, _n_msfbl)) = tables::n_msfb_bits_48(tl) else {
return Ok(());
};
let Some(num_sfb_cap) = tables::num_sfb_48(tl) else {
return Ok(());
};
let max_sfb_master = match br.read_u32(n_side) {
Ok(v) => v.min(num_sfb_cap),
Err(_) => return Ok(()),
};
if max_sfb_master == 0 {
return Ok(());
}
let cp0 = match parse_chparam_info(br, &[max_sfb_master]) {
Ok(v) => v,
Err(_) => return Ok(()),
};
let cp1 = match parse_chparam_info(br, &[max_sfb_master]) {
Ok(v) => v,
Err(_) => return Ok(()),
};
let synth_ti = AsfTransformInfo {
b_long_frame: true,
transf_length: [0; 2],
transform_length_0: tl,
transform_length_1: tl,
};
let body0 = decode_asf_long_mono_body_with_max_sfb(br, &synth_ti, max_sfb_master);
let Some(b0) = body0 else { return Ok(()) };
let body1 = decode_asf_long_mono_body_with_max_sfb(br, &synth_ti, max_sfb_master);
let Some(b1) = body1 else { return Ok(()) };
tools.acpl_1_residual_pair[0] = Some((tl, b0));
tools.acpl_1_residual_pair[1] = Some((tl, b1));
tools.acpl_1_residual_chparam[0] = Some(cp0);
tools.acpl_1_residual_chparam[1] = Some(cp1);
tools.acpl_1_residual_max_sfb_master = Some(max_sfb_master);
}
if !coding_config_bit {
match parse_mono_data(br, false, frame_len_base) {
Ok(m) => tools.cfg0_centre_mono = Some(m),
Err(_) => return Ok(()),
}
}
if !b_iframe {
return Ok(());
}
let Some(aspx_cfg) = tools.aspx_config else {
return Ok(());
};
if crate::asf::parse_aspx_data_2ch_body(br, tools, &aspx_cfg, b_iframe, frame_len_base).is_err()
{
return Ok(());
}
if crate::asf::parse_aspx_data_1ch_body(br, tools, &aspx_cfg, b_iframe, frame_len_base).is_err()
{
return Ok(());
}
let acpl_cfg = match mode {
FiveXCodecMode::AspxAcpl1 => tools.acpl_config_1ch_partial,
FiveXCodecMode::AspxAcpl2 => tools.acpl_config_1ch_full,
_ => None,
};
let Some(acfg) = acpl_cfg else {
return Ok(());
};
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(d0) =
crate::acpl::parse_acpl_data_1ch(br, acfg.num_param_bands, start_band, acfg.quant_mode)
{
tools.acpl_data_1ch_pair[0] = Some(d0);
if let Ok(d1) =
crate::acpl::parse_acpl_data_1ch(br, acfg.num_param_bands, start_band, acfg.quant_mode)
{
tools.acpl_data_1ch_pair[1] = Some(d1);
}
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SevenXCodecMode {
Simple,
Aspx,
AspxAcpl1,
AspxAcpl2,
}
impl SevenXCodecMode {
pub fn from_u32(v: u32) -> Self {
match v & 0b11 {
0 => Self::Simple,
1 => Self::Aspx,
2 => Self::AspxAcpl1,
_ => Self::AspxAcpl2,
}
}
}
pub fn parse_7x_audio_data_outer(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
b_has_lfe: bool,
b_iframe: bool,
frame_len_base: u32,
) -> Result<()> {
let mode_bits = br.read_u32(2)?;
let mode = SevenXCodecMode::from_u32(mode_bits);
tools.seven_x_mode = Some(mode);
tools.seven_x_b_has_lfe = b_has_lfe;
if b_iframe {
if !matches!(mode, SevenXCodecMode::Simple) {
tools.aspx_config = Some(crate::aspx::parse_aspx_config(br)?);
}
match mode {
SevenXCodecMode::AspxAcpl1 => {
let cfg =
crate::acpl::parse_acpl_config_1ch(br, crate::acpl::Acpl1chMode::Partial)?;
tools.acpl_config_1ch_partial = Some(cfg);
}
SevenXCodecMode::AspxAcpl2 => {
let cfg = crate::acpl::parse_acpl_config_1ch(br, crate::acpl::Acpl1chMode::Full)?;
tools.acpl_config_1ch_full = Some(cfg);
}
_ => {}
}
}
if b_has_lfe {
let lfe = parse_mono_data(br, true, frame_len_base)?;
tools.lfe_mono_data = Some(lfe);
}
if matches!(
mode,
SevenXCodecMode::AspxAcpl1 | SevenXCodecMode::AspxAcpl2
) {
tools.companding = Some(crate::aspx::parse_companding_control(br, 5)?);
}
let cc = br.read_u32(2)?;
let coding_cfg = match cc {
0 => FiveXCodingConfig::Cfg0Stereo2plusMono,
1 => FiveXCodingConfig::Cfg1ThreeStereo,
2 => FiveXCodingConfig::Cfg2FourMono,
_ => FiveXCodingConfig::Cfg3Five,
};
tools.seven_x_coding_config = Some(coding_cfg);
let mut largest_tl: Option<u32> = None;
let update_largest = |tl: u32, slot: &mut Option<u32>| {
*slot = Some(slot.map_or(tl, |cur| cur.max(tl)));
};
let mut body_ok = true;
match coding_cfg {
FiveXCodingConfig::Cfg0Stereo2plusMono => {
tools.b_2ch_mode = match br.read_bit() {
Ok(b) => Some(b),
Err(_) => {
body_ok = false;
None
}
};
tools.two_channel_data.clear();
if body_ok {
match parse_two_channel_data(br, frame_len_base) {
Ok(d) => {
if let Some(ti) = d.transform_info.as_ref() {
update_largest(ti.transform_length_0, &mut largest_tl);
}
tools.two_channel_data.push(d);
}
Err(_) => body_ok = false,
}
}
if body_ok {
match parse_two_channel_data(br, frame_len_base) {
Ok(d) => {
if let Some(ti) = d.transform_info.as_ref() {
update_largest(ti.transform_length_0, &mut largest_tl);
}
tools.two_channel_data.push(d);
}
Err(_) => body_ok = false,
}
}
}
FiveXCodingConfig::Cfg1ThreeStereo => {
match parse_three_channel_data(br, frame_len_base) {
Ok(d) => {
if let Some(ti) = d.transform_info.as_ref() {
update_largest(ti.transform_length_0, &mut largest_tl);
}
tools.three_channel_data = Some(d);
}
Err(_) => body_ok = false,
}
if body_ok {
tools.two_channel_data.clear();
match parse_two_channel_data(br, frame_len_base) {
Ok(d) => {
if let Some(ti) = d.transform_info.as_ref() {
update_largest(ti.transform_length_0, &mut largest_tl);
}
tools.two_channel_data.push(d);
}
Err(_) => body_ok = false,
}
}
}
FiveXCodingConfig::Cfg2FourMono => {
match parse_four_channel_data(br, frame_len_base) {
Ok(d) => {
if let Some(ti) = d.transform_info.as_ref() {
update_largest(ti.transform_length_0, &mut largest_tl);
}
tools.four_channel_data = Some(d);
}
Err(_) => body_ok = false,
}
}
FiveXCodingConfig::Cfg3Five => match parse_five_channel_data(br, frame_len_base) {
Ok(d) => {
if let Some(ti) = d.transform_info.as_ref() {
update_largest(ti.transform_length_0, &mut largest_tl);
}
tools.five_channel_data = Some(d);
}
Err(_) => body_ok = false,
},
FiveXCodingConfig::AcplLite2 => {
debug_assert!(false, "AcplLite2 unreachable from 7.X 2-bit coding_config");
body_ok = false;
}
}
if !body_ok {
return Ok(());
}
if matches!(mode, SevenXCodecMode::Simple | SevenXCodecMode::Aspx) {
let b_use_sap_add_ch = match br.read_bit() {
Ok(b) => b,
Err(_) => return Ok(()),
};
tools.seven_x_b_use_sap_add_ch = Some(b_use_sap_add_ch);
if b_use_sap_add_ch {
let max_sfb_g = largest_tl.and_then(crate::tables::num_sfb_48).unwrap_or(63);
let cp0 = match parse_chparam_info(br, &[max_sfb_g]) {
Ok(c) => c,
Err(_) => return Ok(()),
};
let cp1 = match parse_chparam_info(br, &[max_sfb_g]) {
Ok(c) => c,
Err(_) => return Ok(()),
};
tools.seven_x_add_chparam_info = Some([cp0, cp1]);
}
match parse_two_channel_data(br, frame_len_base) {
Ok(d) => {
if let Some(ti) = d.transform_info.as_ref() {
update_largest(ti.transform_length_0, &mut largest_tl);
}
tools.seven_x_additional_channel_data = Some(d);
}
Err(_) => return Ok(()),
}
}
if matches!(mode, SevenXCodecMode::AspxAcpl1) {
let Some(tl) = largest_tl else {
return Ok(());
};
let Some((_n_msfb, n_side, _n_msfbl)) = tables::n_msfb_bits_48(tl) else {
return Ok(());
};
let Some(num_sfb_cap) = tables::num_sfb_48(tl) else {
return Ok(());
};
let max_sfb_master = match br.read_u32(n_side) {
Ok(v) => v.min(num_sfb_cap),
Err(_) => return Ok(()),
};
if max_sfb_master == 0 {
return Ok(());
}
let cp0 = match parse_chparam_info(br, &[max_sfb_master]) {
Ok(v) => v,
Err(_) => return Ok(()),
};
let cp1 = match parse_chparam_info(br, &[max_sfb_master]) {
Ok(v) => v,
Err(_) => return Ok(()),
};
let synth_ti = AsfTransformInfo {
b_long_frame: true,
transf_length: [0; 2],
transform_length_0: tl,
transform_length_1: tl,
};
let body0 = decode_asf_long_mono_body_with_max_sfb(br, &synth_ti, max_sfb_master);
let Some(b0) = body0 else { return Ok(()) };
let body1 = decode_asf_long_mono_body_with_max_sfb(br, &synth_ti, max_sfb_master);
let Some(b1) = body1 else { return Ok(()) };
tools.acpl_1_residual_pair[0] = Some((tl, b0));
tools.acpl_1_residual_pair[1] = Some((tl, b1));
tools.acpl_1_residual_chparam[0] = Some(cp0);
tools.acpl_1_residual_chparam[1] = Some(cp1);
tools.acpl_1_residual_max_sfb_master = Some(max_sfb_master);
}
if matches!(
coding_cfg,
FiveXCodingConfig::Cfg0Stereo2plusMono | FiveXCodingConfig::Cfg2FourMono
) {
match parse_mono_data(br, false, frame_len_base) {
Ok(m) => {
if matches!(coding_cfg, FiveXCodingConfig::Cfg0Stereo2plusMono) {
tools.cfg0_centre_mono = Some(m);
} else {
tools.cfg2_back_mono = Some(m);
}
}
Err(_) => return Ok(()),
}
}
if !b_iframe {
return Ok(());
}
let Some(aspx_cfg) = tools.aspx_config else {
return Ok(());
};
if !matches!(mode, SevenXCodecMode::Simple) {
if crate::asf::parse_aspx_data_2ch_body(br, tools, &aspx_cfg, b_iframe, frame_len_base)
.is_err()
{
return Ok(());
}
if crate::asf::parse_aspx_data_2ch_body(br, tools, &aspx_cfg, b_iframe, frame_len_base)
.is_err()
{
return Ok(());
}
if crate::asf::parse_aspx_data_1ch_body(br, tools, &aspx_cfg, b_iframe, frame_len_base)
.is_err()
{
return Ok(());
}
}
if matches!(mode, SevenXCodecMode::Aspx)
&& crate::asf::parse_aspx_data_2ch_body(br, tools, &aspx_cfg, b_iframe, frame_len_base)
.is_err()
{
return Ok(());
}
if matches!(
mode,
SevenXCodecMode::AspxAcpl1 | SevenXCodecMode::AspxAcpl2
) {
let acpl_cfg = match mode {
SevenXCodecMode::AspxAcpl1 => tools.acpl_config_1ch_partial,
SevenXCodecMode::AspxAcpl2 => tools.acpl_config_1ch_full,
_ => None,
};
let Some(acfg) = acpl_cfg else {
return Ok(());
};
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(d0) =
crate::acpl::parse_acpl_data_1ch(br, acfg.num_param_bands, start_band, acfg.quant_mode)
{
tools.acpl_data_1ch_pair[0] = Some(d0);
if let Ok(d1) = crate::acpl::parse_acpl_data_1ch(
br,
acfg.num_param_bands,
start_band,
acfg.quant_mode,
) {
tools.acpl_data_1ch_pair[1] = Some(d1);
}
}
}
Ok(())
}
pub fn n_msfbl_bits_48(transform_length: u32) -> Option<u32> {
tables::n_msfb_bits_48(transform_length)
.and_then(|(_n, _s, l)| if l == 0 { None } else { Some(l) })
}
#[cfg(test)]
mod tests {
use super::*;
use oxideav_core::bits::BitWriter;
fn write_zero_sf_data_body(bw: &mut BitWriter, max_sfb: u32, transf_length_idx: u32) {
let (n_sect_bits, sect_esc_val) = if transf_length_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 five_x_codec_mode_round_trip() {
assert_eq!(FiveXCodecMode::from_u32(0), FiveXCodecMode::Simple);
assert_eq!(FiveXCodecMode::from_u32(1), FiveXCodecMode::Aspx);
assert_eq!(FiveXCodecMode::from_u32(2), FiveXCodecMode::AspxAcpl1);
assert_eq!(FiveXCodecMode::from_u32(3), FiveXCodecMode::AspxAcpl2);
assert_eq!(FiveXCodecMode::from_u32(4), FiveXCodecMode::AspxAcpl3);
assert_eq!(FiveXCodecMode::from_u32(5), FiveXCodecMode::Reserved(5));
assert_eq!(FiveXCodecMode::from_u32(7), FiveXCodecMode::Reserved(7));
}
#[test]
fn n_msfbl_bits_48_known_rows() {
assert_eq!(n_msfbl_bits_48(2048), Some(3));
assert_eq!(n_msfbl_bits_48(1920), Some(3));
assert_eq!(n_msfbl_bits_48(1024), Some(2));
assert_eq!(n_msfbl_bits_48(384), Some(2));
assert_eq!(n_msfbl_bits_48(480), None);
assert_eq!(n_msfbl_bits_48(128), None);
}
#[test]
fn parse_mono_data_lfe_long_frame() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(5, 3); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let lfe = parse_mono_data(&mut br, true, 1920).unwrap();
assert!(lfe.b_lfe);
assert_eq!(lfe.spec_frontend_bit, 0);
let ti = lfe.transform_info.unwrap();
assert_eq!(ti.transform_length_0, 1920);
let psy = lfe.psy_info.unwrap();
assert_eq!(psy.max_sfb_0, 5);
assert_eq!(psy.num_windows, 1);
assert_eq!(psy.num_window_groups, 1);
assert!(psy.scale_factor_grouping.is_empty());
}
#[test]
fn parse_mono_data_lfe_rejects_short_only_transform() {
let mut bw = BitWriter::new();
bw.write_bit(false); bw.write_u32(2, 2); bw.write_u32(2, 2); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let err = parse_mono_data(&mut br, true, 1920).unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("LFE") || msg.contains("transform_length"),
"expected LFE-rejection error, got: {msg}"
);
}
#[test]
fn parse_mono_data_non_lfe_walks_sf_data_body() {
let mut bw = BitWriter::new();
bw.write_bit(false); bw.write_bit(true); bw.write_u32(8, 6); write_zero_sf_data_body(&mut bw, 8, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mono = parse_mono_data(&mut br, false, 1920).unwrap();
assert!(!mono.b_lfe);
assert_eq!(mono.spec_frontend_bit, 0);
let scaled = mono
.scaled_spec
.as_ref()
.expect("body walked into scaled_spec");
assert!(!scaled.is_empty(), "scaled spectrum must be non-empty");
assert!(
scaled.iter().all(|&v| v == 0.0),
"all-zero sf_data body must dequantise to all zeros"
);
}
#[test]
fn parse_mono_data_lfe_walks_sf_data_body() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(5, 3); write_zero_sf_data_body(&mut bw, 5, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let lfe = parse_mono_data(&mut br, true, 1920).unwrap();
assert!(lfe.b_lfe);
let scaled = lfe
.scaled_spec
.as_ref()
.expect("LFE body walked into scaled_spec");
assert!(!scaled.is_empty(), "LFE scaled spectrum must be non-empty");
assert!(
scaled.iter().all(|&v| v == 0.0),
"all-zero LFE sf_data body must dequantise to all zeros"
);
assert!(scaled.len() <= 1920);
}
#[test]
fn parse_mono_data_non_lfe_ssf_frontend_skips_body_walk() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_bit(true); bw.write_u32(8, 6); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mono = parse_mono_data(&mut br, false, 1920).unwrap();
assert_eq!(mono.spec_frontend_bit, 1);
assert!(
mono.scaled_spec.is_none(),
"SSF-frontend mono must skip the ASF body walk"
);
}
#[test]
fn parse_three_channel_info_reads_chel_matsel_and_two_chparam() {
let mut bw = BitWriter::new();
bw.write_u32(0b1010, 4);
bw.write_u32(0, 2); bw.write_u32(0, 2); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let info = parse_three_channel_info(&mut br, &[10]).unwrap();
assert_eq!(info.chel_matsel, 0b1010);
assert_eq!(info.chparam[0].sap_mode, 0);
assert_eq!(info.chparam[1].sap_mode, 0);
}
#[test]
fn parse_four_channel_info_reads_four_chparam() {
let mut bw = BitWriter::new();
for _ in 0..4 {
bw.write_u32(0, 2); }
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let info = parse_four_channel_info(&mut br, &[10]).unwrap();
assert!(info.chparam.iter().all(|c| c.sap_mode == 0));
}
#[test]
fn parse_five_channel_info_reads_chel_matsel_and_five_chparam() {
let mut bw = BitWriter::new();
bw.write_u32(0b0111, 4); for _ in 0..5 {
bw.write_u32(0, 2); }
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let info = parse_five_channel_info(&mut br, &[10]).unwrap();
assert_eq!(info.chel_matsel, 0b0111);
assert!(info.chparam.iter().all(|c| c.sap_mode == 0));
}
#[test]
fn parse_three_channel_data_outer_shell() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(12, 6); bw.write_u32(3, 4); bw.write_u32(0, 2); bw.write_u32(0, 2); for _ in 0..3 {
write_zero_sf_data_body(&mut bw, 12, 0);
}
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let d = parse_three_channel_data(&mut br, 1920).unwrap();
let psy = d.psy_info.unwrap();
assert_eq!(psy.max_sfb_0, 12);
let info = d.info.unwrap();
assert_eq!(info.chel_matsel, 3);
assert_eq!(d.scaled_spec_per_channel.len(), 3);
for ch in &d.scaled_spec_per_channel {
let v = ch.as_ref().expect("per-channel sf_data should decode");
assert!(v.iter().all(|&s| s == 0.0));
}
}
#[test]
fn parse_five_channel_data_outer_shell() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(20, 6); bw.write_u32(0xF, 4); for _ in 0..5 {
bw.write_u32(0, 2);
}
for _ in 0..5 {
write_zero_sf_data_body(&mut bw, 20, 0);
}
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let d = parse_five_channel_data(&mut br, 1920).unwrap();
let info = d.info.unwrap();
assert_eq!(info.chel_matsel, 0xF);
assert_eq!(info.chparam.len(), 5);
assert_eq!(d.scaled_spec_per_channel.len(), 5);
for ch in &d.scaled_spec_per_channel {
assert!(ch.is_some());
}
}
#[test]
fn parse_5x_outer_simple_cfg3_five_channel() {
let mut bw = BitWriter::new();
bw.write_u32(0, 3); bw.write_u32(3, 2); bw.write_bit(true); bw.write_u32(15, 6); bw.write_u32(0, 4); for _ in 0..5 {
bw.write_u32(0, 2); }
for _ in 0..5 {
write_zero_sf_data_body(&mut bw, 15, 0);
}
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(tools.five_x_mode, Some(FiveXCodecMode::Simple));
assert_eq!(
tools.five_x_coding_config,
Some(FiveXCodingConfig::Cfg3Five)
);
let d = tools.five_channel_data.as_ref().unwrap();
assert_eq!(d.psy_info.as_ref().unwrap().max_sfb_0, 15);
}
#[test]
fn parse_5x_outer_simple_with_lfe_walks_lfe_mono_data() {
let mut bw = BitWriter::new();
bw.write_u32(0, 3); bw.write_bit(true); bw.write_u32(4, 3); write_zero_sf_data_body(&mut bw, 4, 0); bw.write_u32(3, 2);
bw.write_bit(true);
bw.write_u32(10, 6);
bw.write_u32(0, 4);
for _ in 0..5 {
bw.write_u32(0, 2);
}
for _ in 0..5 {
write_zero_sf_data_body(&mut bw, 10, 0);
}
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, true, true, 1920).unwrap();
assert!(tools.five_x_b_has_lfe);
let lfe = tools.lfe_mono_data.as_ref().unwrap();
assert!(lfe.b_lfe);
assert_eq!(lfe.psy_info.as_ref().unwrap().max_sfb_0, 4);
assert!(
lfe.scaled_spec.is_some(),
"round 38: LFE body walks into scaled_spec"
);
let d = tools.five_channel_data.as_ref().unwrap();
assert_eq!(d.psy_info.as_ref().unwrap().max_sfb_0, 10);
}
#[test]
fn parse_two_channel_data_outer_walks_sf_info_plus_chparam() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(20, 6); bw.write_u32(0, 2); write_zero_sf_data_body(&mut bw, 20, 0);
write_zero_sf_data_body(&mut bw, 20, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let d = parse_two_channel_data(&mut br, 1920).unwrap();
assert_eq!(d.transform_info.as_ref().unwrap().transform_length_0, 1920);
assert_eq!(d.psy_info.as_ref().unwrap().max_sfb_0, 20);
assert_eq!(d.chparam.as_ref().unwrap().sap_mode, 0);
assert_eq!(d.scaled_spec_per_channel.len(), 2);
assert!(d.scaled_spec_per_channel.iter().all(|c| c.is_some()));
}
#[test]
fn parse_5x_outer_simple_cfg0_walks_pair_pair_centre() {
let mut bw = BitWriter::new();
bw.write_u32(0, 3); bw.write_u32(0, 2); bw.write_bit(true); bw.write_bit(true);
bw.write_u32(10, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 10, 0); write_zero_sf_data_body(&mut bw, 10, 0); bw.write_bit(true);
bw.write_u32(12, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 12, 0); write_zero_sf_data_body(&mut bw, 12, 0); bw.write_bit(false); bw.write_bit(true); bw.write_u32(8, 6); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(
tools.five_x_coding_config,
Some(FiveXCodingConfig::Cfg0Stereo2plusMono)
);
assert_eq!(tools.b_2ch_mode, Some(true));
assert_eq!(tools.two_channel_data.len(), 2);
assert_eq!(
tools.two_channel_data[0]
.psy_info
.as_ref()
.unwrap()
.max_sfb_0,
10
);
assert_eq!(
tools.two_channel_data[1]
.psy_info
.as_ref()
.unwrap()
.max_sfb_0,
12
);
let centre = tools.cfg0_centre_mono.as_ref().unwrap();
assert!(!centre.b_lfe);
assert_eq!(centre.spec_frontend_bit, 0);
assert_eq!(centre.psy_info.as_ref().unwrap().max_sfb_0, 8);
}
#[test]
fn parse_5x_outer_simple_cfg1_walks_three_plus_two() {
let mut bw = BitWriter::new();
bw.write_u32(0, 3); bw.write_u32(1, 2); bw.write_bit(true);
bw.write_u32(14, 6);
bw.write_u32(0, 4);
bw.write_u32(0, 2);
bw.write_u32(0, 2);
for _ in 0..3 {
write_zero_sf_data_body(&mut bw, 14, 0);
}
bw.write_bit(true);
bw.write_u32(18, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 18, 0);
write_zero_sf_data_body(&mut bw, 18, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(
tools.five_x_coding_config,
Some(FiveXCodingConfig::Cfg1ThreeStereo)
);
let three = tools.three_channel_data.as_ref().unwrap();
assert_eq!(three.psy_info.as_ref().unwrap().max_sfb_0, 14);
assert_eq!(tools.two_channel_data.len(), 1);
assert_eq!(
tools.two_channel_data[0]
.psy_info
.as_ref()
.unwrap()
.max_sfb_0,
18
);
}
#[test]
fn parse_5x_outer_simple_cfg2_walks_four_plus_mono() {
let mut bw = BitWriter::new();
bw.write_u32(0, 3); bw.write_u32(2, 2); bw.write_bit(true);
bw.write_u32(22, 6);
for _ in 0..4 {
bw.write_u32(0, 2);
}
for _ in 0..4 {
write_zero_sf_data_body(&mut bw, 22, 0);
}
bw.write_bit(false);
bw.write_bit(true);
bw.write_u32(7, 6);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(
tools.five_x_coding_config,
Some(FiveXCodingConfig::Cfg2FourMono)
);
let four = tools.four_channel_data.as_ref().unwrap();
assert_eq!(four.psy_info.as_ref().unwrap().max_sfb_0, 22);
let back = tools.cfg2_back_mono.as_ref().unwrap();
assert!(!back.b_lfe);
assert_eq!(back.psy_info.as_ref().unwrap().max_sfb_0, 7);
}
#[test]
fn parse_5x_outer_aspx_acpl3_reads_acpl_config_2ch_and_companding() {
let mut bw = BitWriter::new();
bw.write_u32(4, 3); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, false, 1920).unwrap();
assert_eq!(tools.five_x_mode, Some(FiveXCodecMode::AspxAcpl3));
assert!(tools.acpl_config_2ch.is_none());
assert!(tools.companding.is_some());
}
#[test]
fn decode_mch_sf_data_long_frame_all_zero_two_channels() {
let mut bw = BitWriter::new();
write_zero_sf_data_body(&mut bw, 8, 0);
write_zero_sf_data_body(&mut bw, 8, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ti = AsfTransformInfo {
b_long_frame: true,
transf_length: [0, 0],
transform_length_0: 1920,
transform_length_1: 1920,
};
let psy = AsfPsyInfo {
max_sfb_0: 8,
num_windows: 1,
num_window_groups: 1,
..Default::default()
};
let out = decode_mch_sf_data_channels(&mut br, &ti, &psy, 2);
assert_eq!(out.len(), 2);
let sfbo = crate::sfb_offset::sfb_offset_48(1920).unwrap();
let expected_len = sfbo[8] as usize;
for slot in &out {
let v = slot.as_ref().expect("each channel decodes");
assert_eq!(v.len(), expected_len);
assert!(v.iter().all(|&s| s == 0.0));
}
}
#[test]
fn decode_mch_sf_data_short_frame_garbage_returns_all_none() {
let bytes = [0xFFu8; 4];
let mut br = BitReader::new(&bytes);
let ti = AsfTransformInfo {
b_long_frame: false,
transf_length: [0, 0],
transform_length_0: 480,
transform_length_1: 480,
};
let psy = AsfPsyInfo {
max_sfb_0: 6,
num_windows: 4,
num_window_groups: 2,
..Default::default()
};
let out = decode_mch_sf_data_channels(&mut br, &ti, &psy, 5);
assert_eq!(out.len(), 5);
assert!(out.iter().all(|c| c.is_none()));
}
#[test]
fn parse_three_channel_data_decodes_three_sf_data_bodies() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(10, 6); bw.write_u32(1, 4); bw.write_u32(0, 2); bw.write_u32(0, 2); for _ in 0..3 {
write_zero_sf_data_body(&mut bw, 10, 0);
}
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let d = parse_three_channel_data(&mut br, 1920).unwrap();
assert_eq!(d.scaled_spec_per_channel.len(), 3);
let sfbo = crate::sfb_offset::sfb_offset_48(1920).unwrap();
let expected_len = sfbo[10] as usize;
for ch in &d.scaled_spec_per_channel {
let v = ch.as_ref().unwrap();
assert_eq!(v.len(), expected_len);
assert!(v.iter().all(|&s| s == 0.0));
}
}
#[test]
fn parse_four_and_five_channel_data_emit_correct_per_channel_counts() {
let mut bw = BitWriter::new();
bw.write_bit(true);
bw.write_u32(8, 6); for _ in 0..4 {
bw.write_u32(0, 2); }
for _ in 0..4 {
write_zero_sf_data_body(&mut bw, 8, 0);
}
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let d4 = parse_four_channel_data(&mut br, 1920).unwrap();
assert_eq!(d4.scaled_spec_per_channel.len(), 4);
assert_eq!(
d4.scaled_spec_per_channel
.iter()
.filter(|c| c.is_some())
.count(),
4
);
let mut bw = BitWriter::new();
bw.write_bit(true);
bw.write_u32(6, 6); bw.write_u32(0, 4); for _ in 0..5 {
bw.write_u32(0, 2);
}
for _ in 0..5 {
write_zero_sf_data_body(&mut bw, 6, 0);
}
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let d5 = parse_five_channel_data(&mut br, 1920).unwrap();
assert_eq!(d5.scaled_spec_per_channel.len(), 5);
assert!(d5.scaled_spec_per_channel.iter().all(|c| c.is_some()));
}
#[test]
fn parse_three_channel_data_truncated_sf_data_yields_partial_decode() {
let mut bw = BitWriter::new();
bw.write_bit(true);
bw.write_u32(12, 6);
bw.write_u32(2, 4);
bw.write_u32(0, 2);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 12, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let d = parse_three_channel_data(&mut br, 1920).unwrap();
assert_eq!(d.scaled_spec_per_channel.len(), 3);
assert!(d.scaled_spec_per_channel[0].is_some());
let some_count = d
.scaled_spec_per_channel
.iter()
.filter(|c| c.is_some())
.count();
assert!(some_count < 3, "expected at least one None slot");
}
#[test]
fn parse_two_channel_data_per_channel_lengths_match_sfb_offset() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(15, 6); bw.write_u32(0, 2); write_zero_sf_data_body(&mut bw, 15, 0);
write_zero_sf_data_body(&mut bw, 15, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let d = parse_two_channel_data(&mut br, 1920).unwrap();
let sfbo = crate::sfb_offset::sfb_offset_48(1920).unwrap();
let expected_len = sfbo[15] as usize;
assert_eq!(d.scaled_spec_per_channel.len(), 2);
for ch in &d.scaled_spec_per_channel {
let v = ch.as_ref().unwrap();
assert_eq!(v.len(), expected_len);
}
}
#[test]
fn decode_mch_sf_data_grouped_short_frame_two_groups_two_channels() {
let max_sfb = 8u32;
let tl_idx = 2u32; let mut bw = BitWriter::new();
write_zero_sf_data_body(&mut bw, max_sfb, tl_idx);
write_zero_sf_data_body(&mut bw, max_sfb, tl_idx);
write_zero_sf_data_body(&mut bw, max_sfb, tl_idx);
write_zero_sf_data_body(&mut bw, max_sfb, 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 out = decode_mch_sf_data_channels(&mut br, &ti, &psy, 2);
assert_eq!(out.len(), 2);
let sfbo = crate::sfb_offset::sfb_offset_48(480).unwrap();
let per_group_len = sfbo[max_sfb as usize] as usize;
let expected_total = per_group_len * 2; for slot in &out {
let v = slot.as_ref().expect("each channel decodes");
assert_eq!(v.len(), expected_total);
assert!(v.iter().all(|&s| s == 0.0));
}
}
#[test]
fn decode_mch_sf_data_grouped_three_groups_one_channel() {
let max_sfb = 6u32;
let tl_idx = 2u32;
let mut bw = BitWriter::new();
for _ in 0..3 {
write_zero_sf_data_body(&mut bw, max_sfb, 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: 3,
num_window_groups: 3,
scale_factor_grouping: vec![0, 0],
..Default::default()
};
let out = decode_mch_sf_data_channels(&mut br, &ti, &psy, 1);
assert_eq!(out.len(), 1);
let v = out[0].as_ref().expect("decode succeeds");
let sfbo = crate::sfb_offset::sfb_offset_48(480).unwrap();
assert_eq!(v.len(), 3 * sfbo[max_sfb as usize] as usize);
}
#[test]
fn parse_three_channel_data_grouped_short_frame_walks_per_group() {
let mut bw = BitWriter::new();
bw.write_bit(false); bw.write_u32(2, 2); bw.write_u32(2, 2); let max_sfb = 5u32;
bw.write_u32(max_sfb, 6); bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(1, 1);
bw.write_u32(0, 4);
bw.write_u32(0, 2);
bw.write_u32(0, 2);
for _ in 0..3 {
for _ in 0..2 {
write_zero_sf_data_body(&mut bw, max_sfb, 2);
}
}
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let d = parse_three_channel_data(&mut br, 1920).unwrap();
let psy = d.psy_info.as_ref().unwrap();
assert_eq!(psy.num_window_groups, 2);
assert!(!d.transform_info.as_ref().unwrap().b_long_frame);
assert_eq!(d.scaled_spec_per_channel.len(), 3);
let sfbo = crate::sfb_offset::sfb_offset_48(480).unwrap();
let expected_total = (sfbo[max_sfb as usize] as usize) * 2;
for ch in &d.scaled_spec_per_channel {
let v = ch.as_ref().expect("each channel decodes");
assert_eq!(v.len(), expected_total);
assert!(v.iter().all(|&s| s == 0.0));
}
}
#[test]
fn parse_two_channel_data_grouped_short_frame_walks_per_group() {
let mut bw = BitWriter::new();
bw.write_bit(false); bw.write_u32(2, 2); bw.write_u32(2, 2); let max_sfb = 4u32;
bw.write_u32(max_sfb, 6); bw.write_u32(0, 1);
bw.write_u32(0, 1);
bw.write_u32(0, 1);
bw.write_u32(0, 2);
for _ in 0..2 {
for _ in 0..4 {
write_zero_sf_data_body(&mut bw, max_sfb, 2);
}
}
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let d = parse_two_channel_data(&mut br, 1920).unwrap();
let psy = d.psy_info.as_ref().unwrap();
assert_eq!(psy.num_window_groups, 4);
assert_eq!(d.scaled_spec_per_channel.len(), 2);
let sfbo = crate::sfb_offset::sfb_offset_48(480).unwrap();
let expected_total = (sfbo[max_sfb as usize] as usize) * 4;
for ch in &d.scaled_spec_per_channel {
let v = ch.as_ref().expect("each channel decodes");
assert_eq!(v.len(), expected_total);
}
}
#[test]
fn decode_mch_sf_data_grouped_truncated_returns_none() {
let max_sfb = 6u32;
let tl_idx = 2u32;
let mut bw = BitWriter::new();
write_zero_sf_data_body(&mut bw, max_sfb, 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 out = decode_mch_sf_data_channels(&mut br, &ti, &psy, 1);
assert_eq!(out.len(), 1);
let _ = &out[0];
}
#[test]
fn parse_5x_aspx_acpl_3_non_iframe_leaves_acpl_data_2ch_none() {
let mut bw = BitWriter::new();
bw.write_u32(4, 3); bw.write_bit(false);
bw.write_bit(false);
bw.write_bit(false);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, false, 1920).unwrap();
assert_eq!(tools.five_x_mode, Some(FiveXCodecMode::AspxAcpl3));
assert!(tools.acpl_data_2ch.is_none());
assert!(tools.companding.is_some());
}
#[test]
fn parse_5x_aspx_acpl_3_iframe_parses_aspx_and_acpl_configs() {
let mut bw = BitWriter::new();
bw.write_u32(4, 3); bw.write_u32(0, 15);
bw.write_u32(0, 2);
bw.write_bit(false);
bw.write_bit(false);
bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.align_to_byte();
while bw.byte_len() < 256 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(tools.five_x_mode, Some(FiveXCodecMode::AspxAcpl3));
let cfg = tools.acpl_config_2ch.expect("acpl_config_2ch parsed");
assert_eq!(cfg.num_param_bands, 15);
}
fn write_companding_3_all_on(bw: &mut oxideav_core::bits::BitWriter) {
bw.write_bit(true); bw.write_bit(true); }
fn write_zero_aspx_config(bw: &mut oxideav_core::bits::BitWriter) {
bw.write_u32(0, 15);
}
fn write_acpl_config_1ch_partial(bw: &mut oxideav_core::bits::BitWriter) {
bw.write_u32(0, 2); bw.write_bit(false); bw.write_u32(0, 3); }
fn write_acpl_config_1ch_full(bw: &mut oxideav_core::bits::BitWriter) {
bw.write_u32(0, 2); bw.write_bit(false); }
#[test]
fn parse_5x_aspx_acpl_2_non_iframe_leaves_acpl_pair_none() {
let mut bw = BitWriter::new();
bw.write_u32(3, 3); write_companding_3_all_on(&mut bw);
bw.write_bit(false); bw.write_bit(true); bw.write_u32(8, 6); bw.write_u32(0, 2); write_zero_sf_data_body(&mut bw, 8, 0);
write_zero_sf_data_body(&mut bw, 8, 0);
bw.write_bit(false); bw.write_bit(true); bw.write_u32(8, 6); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, false, 1920).unwrap();
assert_eq!(tools.five_x_mode, Some(FiveXCodecMode::AspxAcpl2));
assert_eq!(
tools.five_x_coding_config,
Some(FiveXCodingConfig::AcplLite2)
);
assert_eq!(tools.two_channel_data.len(), 1);
assert!(tools.cfg0_centre_mono.is_some());
assert!(tools.acpl_data_1ch_pair[0].is_none());
assert!(tools.acpl_data_1ch_pair[1].is_none());
}
#[test]
fn parse_5x_aspx_acpl_1_non_iframe_walks_three_channel_data() {
let mut bw = BitWriter::new();
bw.write_u32(2, 3); write_companding_3_all_on(&mut bw);
bw.write_bit(true); bw.write_bit(true); bw.write_u32(10, 6); bw.write_u32(0, 4); bw.write_u32(0, 2); bw.write_u32(0, 2); for _ in 0..3 {
write_zero_sf_data_body(&mut bw, 10, 0);
}
bw.write_u32(8, 5); bw.write_u32(0, 2); bw.write_u32(0, 2); write_zero_sf_data_body(&mut bw, 8, 0); write_zero_sf_data_body(&mut bw, 8, 0); bw.align_to_byte();
while bw.byte_len() < 64 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, false, 1920).unwrap();
assert_eq!(tools.five_x_mode, Some(FiveXCodecMode::AspxAcpl1));
assert_eq!(
tools.five_x_coding_config,
Some(FiveXCodingConfig::Cfg1ThreeStereo)
);
let three = tools.three_channel_data.as_ref().expect("3ch parsed");
assert_eq!(three.psy_info.as_ref().unwrap().max_sfb_0, 10);
assert!(tools.acpl_data_1ch_pair[0].is_none());
assert!(tools.acpl_data_1ch_pair[1].is_none());
assert!(tools.acpl_1_residual_pair[0].is_some(), "sSMP,3 persisted");
assert!(tools.acpl_1_residual_pair[1].is_some(), "sSMP,4 persisted");
let (tl0, spec0) = tools.acpl_1_residual_pair[0].as_ref().unwrap();
assert_eq!(*tl0, 1920);
assert!(!spec0.is_empty(), "sSMP,3 spectrum is non-empty");
}
#[test]
fn parse_5x_aspx_acpl_2_iframe_parses_configs_and_three_channel() {
let mut bw = BitWriter::new();
bw.write_u32(3, 3); write_zero_aspx_config(&mut bw);
write_acpl_config_1ch_full(&mut bw);
write_companding_3_all_on(&mut bw);
bw.write_bit(true); bw.write_bit(true); bw.write_u32(10, 6); bw.write_u32(0, 4); bw.write_u32(0, 2); bw.write_u32(0, 2); for _ in 0..3 {
write_zero_sf_data_body(&mut bw, 10, 0);
}
bw.align_to_byte();
while bw.byte_len() < 256 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(tools.five_x_mode, Some(FiveXCodecMode::AspxAcpl2));
assert!(tools.aspx_config.is_some());
let cfg_full = tools.acpl_config_1ch_full.expect("FULL config parsed");
assert_eq!(cfg_full.num_param_bands, 15);
assert_eq!(cfg_full.qmf_band, 0); let three = tools.three_channel_data.as_ref().expect("3ch parsed");
assert_eq!(three.psy_info.as_ref().unwrap().max_sfb_0, 10);
}
#[test]
fn parse_5x_aspx_acpl_1_iframe_walks_residual_and_mono_trailer() {
let mut bw = BitWriter::new();
bw.write_u32(2, 3); write_zero_aspx_config(&mut bw);
write_acpl_config_1ch_partial(&mut bw);
write_companding_3_all_on(&mut bw);
bw.write_bit(false); bw.write_bit(true); bw.write_u32(12, 6); bw.write_u32(0, 2); write_zero_sf_data_body(&mut bw, 12, 0);
write_zero_sf_data_body(&mut bw, 12, 0);
bw.write_u32(6, 5); bw.write_u32(0, 2); bw.write_u32(0, 2); write_zero_sf_data_body(&mut bw, 6, 0);
write_zero_sf_data_body(&mut bw, 6, 0);
bw.write_bit(false); bw.write_bit(true); bw.write_u32(7, 6); bw.align_to_byte();
while bw.byte_len() < 256 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(tools.five_x_mode, Some(FiveXCodecMode::AspxAcpl1));
assert!(tools.aspx_config.is_some());
let cfg_partial = tools
.acpl_config_1ch_partial
.expect("PARTIAL config parsed");
assert_eq!(cfg_partial.num_param_bands, 15);
assert_eq!(cfg_partial.qmf_band, 1); assert_eq!(tools.two_channel_data.len(), 1);
assert_eq!(
tools.two_channel_data[0]
.psy_info
.as_ref()
.unwrap()
.max_sfb_0,
12
);
let centre = tools
.cfg0_centre_mono
.as_ref()
.expect("Cfg0 centre mono walked");
assert_eq!(centre.psy_info.as_ref().unwrap().max_sfb_0, 7);
}
#[test]
fn parse_5x_aspx_acpl_2_truncated_channel_data_bails() {
let mut bw = BitWriter::new();
bw.write_u32(3, 3); write_companding_3_all_on(&mut bw);
bw.write_bit(true); bw.write_bit(true);
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, false, 1920).unwrap();
assert_eq!(tools.five_x_mode, Some(FiveXCodecMode::AspxAcpl2));
assert!(tools.three_channel_data.is_none());
assert!(tools.acpl_data_1ch_pair[0].is_none());
assert!(tools.acpl_data_1ch_pair[1].is_none());
}
#[test]
fn parse_5x_aspx_acpl_1_iframe_zero_max_sfb_master_bails() {
let mut bw = BitWriter::new();
bw.write_u32(2, 3); write_zero_aspx_config(&mut bw);
write_acpl_config_1ch_partial(&mut bw);
write_companding_3_all_on(&mut bw);
bw.write_bit(true); bw.write_bit(true); bw.write_u32(10, 6); bw.write_u32(0, 4); bw.write_u32(0, 2);
bw.write_u32(0, 2);
for _ in 0..3 {
write_zero_sf_data_body(&mut bw, 10, 0);
}
bw.write_u32(0, 5);
bw.align_to_byte();
while bw.byte_len() < 64 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_5x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(tools.five_x_mode, Some(FiveXCodecMode::AspxAcpl1));
assert!(tools.three_channel_data.is_some());
assert!(tools.acpl_data_1ch_pair[0].is_none());
assert!(tools.acpl_data_1ch_pair[1].is_none());
}
fn write_companding_5_all_on(bw: &mut oxideav_core::bits::BitWriter) {
bw.write_bit(true); bw.write_bit(true); }
#[test]
fn seven_x_codec_mode_round_trip() {
assert_eq!(SevenXCodecMode::from_u32(0), SevenXCodecMode::Simple);
assert_eq!(SevenXCodecMode::from_u32(1), SevenXCodecMode::Aspx);
assert_eq!(SevenXCodecMode::from_u32(2), SevenXCodecMode::AspxAcpl1);
assert_eq!(SevenXCodecMode::from_u32(3), SevenXCodecMode::AspxAcpl2);
assert_eq!(SevenXCodecMode::from_u32(4), SevenXCodecMode::Simple);
}
#[test]
fn parse_7x_outer_simple_cfg3_no_sap_walks_full_body() {
let mut bw = BitWriter::new();
bw.write_u32(0, 2);
bw.write_u32(3, 2);
bw.write_bit(true); bw.write_u32(15, 6); bw.write_u32(0, 4); for _ in 0..5 {
bw.write_u32(0, 2); }
for _ in 0..5 {
write_zero_sf_data_body(&mut bw, 15, 0);
}
bw.write_bit(false);
bw.write_bit(true); bw.write_u32(10, 6); bw.write_u32(0, 2); write_zero_sf_data_body(&mut bw, 10, 0);
write_zero_sf_data_body(&mut bw, 10, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_7x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(tools.seven_x_mode, Some(SevenXCodecMode::Simple));
assert!(!tools.seven_x_b_has_lfe);
assert_eq!(
tools.seven_x_coding_config,
Some(FiveXCodingConfig::Cfg3Five)
);
let five = tools.five_channel_data.as_ref().expect("5ch parsed");
assert_eq!(five.psy_info.as_ref().unwrap().max_sfb_0, 15);
assert_eq!(tools.seven_x_b_use_sap_add_ch, Some(false));
assert!(tools.seven_x_add_chparam_info.is_none());
let add = tools
.seven_x_additional_channel_data
.as_ref()
.expect("additional 2ch parsed");
assert_eq!(add.psy_info.as_ref().unwrap().max_sfb_0, 10);
}
#[test]
fn parse_7x_outer_simple_71_walks_lfe_and_five_channel() {
let mut bw = BitWriter::new();
bw.write_u32(0, 2); bw.write_bit(true); bw.write_u32(4, 3); write_zero_sf_data_body(&mut bw, 4, 0); bw.write_u32(3, 2);
bw.write_bit(true); bw.write_u32(10, 6); bw.write_u32(0, 4);
for _ in 0..5 {
bw.write_u32(0, 2);
}
for _ in 0..5 {
write_zero_sf_data_body(&mut bw, 10, 0);
}
bw.write_bit(false); bw.write_bit(true); bw.write_u32(8, 6); bw.write_u32(0, 2); write_zero_sf_data_body(&mut bw, 8, 0);
write_zero_sf_data_body(&mut bw, 8, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_7x_audio_data_outer(&mut br, &mut tools, true, true, 1920).unwrap();
assert!(tools.seven_x_b_has_lfe);
let lfe = tools.lfe_mono_data.as_ref().expect("LFE walked");
assert!(lfe.b_lfe);
assert_eq!(lfe.psy_info.as_ref().unwrap().max_sfb_0, 4);
assert!(tools.five_channel_data.is_some());
assert!(tools.seven_x_additional_channel_data.is_some());
}
#[test]
fn parse_7x_outer_simple_cfg0_walks_two_pairs_then_centre_mono() {
let mut bw = BitWriter::new();
bw.write_u32(0, 2); bw.write_u32(0, 2);
bw.write_bit(false);
bw.write_bit(true);
bw.write_u32(12, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 12, 0);
write_zero_sf_data_body(&mut bw, 12, 0);
bw.write_bit(true);
bw.write_u32(12, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 12, 0);
write_zero_sf_data_body(&mut bw, 12, 0);
bw.write_bit(false); bw.write_bit(true);
bw.write_u32(10, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 10, 0);
write_zero_sf_data_body(&mut bw, 10, 0);
bw.write_bit(false); bw.write_bit(true); bw.write_u32(7, 6); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_7x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(tools.seven_x_mode, Some(SevenXCodecMode::Simple));
assert_eq!(
tools.seven_x_coding_config,
Some(FiveXCodingConfig::Cfg0Stereo2plusMono)
);
assert_eq!(tools.b_2ch_mode, Some(false));
assert_eq!(tools.two_channel_data.len(), 2);
let centre = tools.cfg0_centre_mono.as_ref().expect("centre mono walked");
assert_eq!(centre.psy_info.as_ref().unwrap().max_sfb_0, 7);
}
#[test]
fn parse_7x_outer_simple_cfg2_walks_four_then_back_mono() {
let mut bw = BitWriter::new();
bw.write_u32(0, 2); bw.write_u32(2, 2);
bw.write_bit(true); bw.write_u32(11, 6); for _ in 0..4 {
bw.write_u32(0, 2);
}
for _ in 0..4 {
write_zero_sf_data_body(&mut bw, 11, 0);
}
bw.write_bit(false); bw.write_bit(true);
bw.write_u32(9, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 9, 0);
write_zero_sf_data_body(&mut bw, 9, 0);
bw.write_bit(false);
bw.write_bit(true);
bw.write_u32(6, 6);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_7x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(
tools.seven_x_coding_config,
Some(FiveXCodingConfig::Cfg2FourMono)
);
assert!(tools.four_channel_data.is_some());
let back = tools.cfg2_back_mono.as_ref().expect("back mono walked");
assert_eq!(back.psy_info.as_ref().unwrap().max_sfb_0, 6);
}
#[test]
fn parse_7x_outer_simple_cfg1_no_mono_trailer() {
let mut bw = BitWriter::new();
bw.write_u32(0, 2); bw.write_u32(1, 2);
bw.write_bit(true);
bw.write_u32(10, 6);
bw.write_u32(0, 4); bw.write_u32(0, 2);
bw.write_u32(0, 2);
for _ in 0..3 {
write_zero_sf_data_body(&mut bw, 10, 0);
}
bw.write_bit(true);
bw.write_u32(10, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 10, 0);
write_zero_sf_data_body(&mut bw, 10, 0);
bw.write_bit(false); bw.write_bit(true);
bw.write_u32(8, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 8, 0);
write_zero_sf_data_body(&mut bw, 8, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_7x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(
tools.seven_x_coding_config,
Some(FiveXCodingConfig::Cfg1ThreeStereo)
);
assert!(tools.three_channel_data.is_some());
assert_eq!(tools.two_channel_data.len(), 1);
assert!(tools.cfg0_centre_mono.is_none());
assert!(tools.cfg2_back_mono.is_none());
assert!(tools.seven_x_additional_channel_data.is_some());
}
#[test]
fn parse_7x_outer_simple_with_sap_add_ch_populates_chparam_pair() {
let mut bw = BitWriter::new();
bw.write_u32(0, 2); bw.write_u32(3, 2); bw.write_bit(true);
bw.write_u32(12, 6);
bw.write_u32(0, 4);
for _ in 0..5 {
bw.write_u32(0, 2);
}
for _ in 0..5 {
write_zero_sf_data_body(&mut bw, 12, 0);
}
bw.write_bit(true); bw.write_u32(0, 2); bw.write_u32(0, 2); bw.write_bit(true);
bw.write_u32(8, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 8, 0);
write_zero_sf_data_body(&mut bw, 8, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_7x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(tools.seven_x_b_use_sap_add_ch, Some(true));
let pair = tools
.seven_x_add_chparam_info
.as_ref()
.expect("SAP chparam pair");
assert_eq!(pair[0].sap_mode, 0);
assert_eq!(pair[1].sap_mode, 0);
}
#[test]
fn parse_7x_aspx_acpl_2_non_iframe_walks_three_channel_no_addch() {
let mut bw = BitWriter::new();
bw.write_u32(3, 2); write_companding_5_all_on(&mut bw);
bw.write_u32(1, 2); bw.write_bit(true);
bw.write_u32(10, 6);
bw.write_u32(0, 4);
bw.write_u32(0, 2);
bw.write_u32(0, 2);
for _ in 0..3 {
write_zero_sf_data_body(&mut bw, 10, 0);
}
bw.write_bit(true);
bw.write_u32(10, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 10, 0);
write_zero_sf_data_body(&mut bw, 10, 0);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_7x_audio_data_outer(&mut br, &mut tools, false, false, 1920).unwrap();
assert_eq!(tools.seven_x_mode, Some(SevenXCodecMode::AspxAcpl2));
assert_eq!(
tools.seven_x_coding_config,
Some(FiveXCodingConfig::Cfg1ThreeStereo)
);
assert!(tools.three_channel_data.is_some());
assert_eq!(tools.two_channel_data.len(), 1);
assert!(tools.seven_x_additional_channel_data.is_none());
assert!(tools.seven_x_b_use_sap_add_ch.is_none());
assert!(tools.acpl_data_1ch_pair[0].is_none());
assert!(tools.acpl_data_1ch_pair[1].is_none());
}
#[test]
fn parse_7x_aspx_acpl_1_iframe_walks_residual_and_mono_trailer() {
let mut bw = BitWriter::new();
bw.write_u32(2, 2); write_zero_aspx_config(&mut bw);
write_acpl_config_1ch_partial(&mut bw);
write_companding_5_all_on(&mut bw);
bw.write_u32(0, 2); bw.write_bit(false); bw.write_bit(true);
bw.write_u32(12, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 12, 0);
write_zero_sf_data_body(&mut bw, 12, 0);
bw.write_bit(true);
bw.write_u32(12, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 12, 0);
write_zero_sf_data_body(&mut bw, 12, 0);
bw.write_u32(6, 5); bw.write_u32(0, 2);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 6, 0);
write_zero_sf_data_body(&mut bw, 6, 0);
bw.write_bit(false);
bw.write_bit(true);
bw.write_u32(7, 6);
bw.align_to_byte();
while bw.byte_len() < 256 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_7x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(tools.seven_x_mode, Some(SevenXCodecMode::AspxAcpl1));
assert!(tools.aspx_config.is_some());
let cfg_partial = tools
.acpl_config_1ch_partial
.expect("PARTIAL config parsed");
assert_eq!(cfg_partial.num_param_bands, 15);
assert_eq!(cfg_partial.qmf_band, 1);
assert_eq!(tools.two_channel_data.len(), 2);
let centre = tools.cfg0_centre_mono.as_ref().expect("centre mono walked");
assert_eq!(centre.psy_info.as_ref().unwrap().max_sfb_0, 7);
assert!(tools.seven_x_additional_channel_data.is_none());
assert!(tools.seven_x_b_use_sap_add_ch.is_none());
}
#[test]
fn parse_7x_aspx_acpl_1_iframe_zero_max_sfb_master_bails() {
let mut bw = BitWriter::new();
bw.write_u32(2, 2); write_zero_aspx_config(&mut bw);
write_acpl_config_1ch_partial(&mut bw);
write_companding_5_all_on(&mut bw);
bw.write_u32(1, 2); bw.write_bit(true);
bw.write_u32(10, 6);
bw.write_u32(0, 4);
bw.write_u32(0, 2);
bw.write_u32(0, 2);
for _ in 0..3 {
write_zero_sf_data_body(&mut bw, 10, 0);
}
bw.write_bit(true);
bw.write_u32(10, 6);
bw.write_u32(0, 2);
write_zero_sf_data_body(&mut bw, 10, 0);
write_zero_sf_data_body(&mut bw, 10, 0);
bw.write_u32(0, 5);
bw.align_to_byte();
while bw.byte_len() < 64 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_7x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(tools.seven_x_mode, Some(SevenXCodecMode::AspxAcpl1));
assert!(tools.three_channel_data.is_some());
assert!(tools.acpl_data_1ch_pair[0].is_none());
assert!(tools.acpl_data_1ch_pair[1].is_none());
}
#[test]
fn parse_7x_simple_truncated_five_channel_data_bails() {
let mut bw = BitWriter::new();
bw.write_u32(0, 2); bw.write_u32(3, 2); bw.write_bit(true); let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let mut tools = SubstreamTools::default();
parse_7x_audio_data_outer(&mut br, &mut tools, false, true, 1920).unwrap();
assert_eq!(tools.seven_x_mode, Some(SevenXCodecMode::Simple));
assert_eq!(
tools.seven_x_coding_config,
Some(FiveXCodingConfig::Cfg3Five)
);
assert!(tools.five_channel_data.is_none());
assert!(tools.seven_x_additional_channel_data.is_none());
}
}