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 SapCoeffs {
pub abcd: Vec<Vec<(f32, f32, f32, f32)>>,
}
impl SapCoeffs {
pub fn identity(max_sfb_per_group: &[u32]) -> Self {
let abcd = max_sfb_per_group
.iter()
.map(|&m| vec![(1.0, 0.0, 0.0, 1.0); m as usize])
.collect();
Self { abcd }
}
}
pub fn extract_sap_abcd(info: &ChparamInfo, max_sfb_per_group: &[u32]) -> SapCoeffs {
let mode = info.mode();
let mut abcd: Vec<Vec<(f32, f32, f32, f32)>> = max_sfb_per_group
.iter()
.map(|&m| vec![(1.0, 0.0, 0.0, 1.0); m as usize])
.collect();
match mode {
SapMode::None | SapMode::Reserved => {
}
SapMode::MsUsed => {
for (g, &m) in max_sfb_per_group.iter().enumerate() {
let row = info.ms_used.get(g);
for (sfb, slot) in abcd[g].iter_mut().enumerate().take(m as usize) {
let used = row.and_then(|r| r.get(sfb).copied()).unwrap_or(false);
if used {
*slot = (1.0, 1.0, 1.0, -1.0);
}
}
}
}
SapMode::SapData => {
let Some(sd) = info.sap_data.as_ref() else {
return SapCoeffs { abcd };
};
let num_groups = max_sfb_per_group.len();
let mut alpha_q: Vec<Vec<i32>> = max_sfb_per_group
.iter()
.map(|&m| vec![0i32; m as usize])
.collect();
let mut max_sfb_prev = max_sfb_per_group.first().copied().unwrap_or(0);
for g in 0..num_groups {
let m = max_sfb_per_group[g] as usize;
let dp = sd.dpcm_alpha_q.get(g);
let used_row = sd.sap_coeff_used.get(g);
let mut sfb = 0usize;
while sfb < m {
let used = used_row.and_then(|r| r.get(sfb).copied()).unwrap_or(false);
if used {
if sfb % 2 == 1 {
alpha_q[g][sfb] =
alpha_q[g].get(sfb.saturating_sub(1)).copied().unwrap_or(0);
} else {
let delta = dp.and_then(|r| r.get(sfb).copied()).unwrap_or(0);
let code_delta = g != 0
&& max_sfb_per_group[g] == max_sfb_prev
&& sd.delta_code_time;
let prev = if code_delta {
alpha_q
.get(g.wrapping_sub(1))
.and_then(|r| r.get(sfb).copied())
.unwrap_or(0)
} else if sfb == 0 {
0
} else {
alpha_q[g].get(sfb - 2).copied().unwrap_or(0)
};
alpha_q[g][sfb] = prev + delta;
}
let sap_gain = alpha_q[g][sfb] as f32 * 0.1;
abcd[g][sfb] = (1.0 + sap_gain, 1.0, 1.0 - sap_gain, -1.0);
} else {
abcd[g][sfb] = (1.0, 0.0, 0.0, 1.0);
}
sfb += 1;
}
max_sfb_prev = max_sfb_per_group[g];
}
}
}
SapCoeffs { abcd }
}
pub type SapTable181Output = (Vec<f32>, Vec<f32>, Vec<f32>, Vec<f32>);
pub fn apply_sap_table_181(
a_spec: &[f32],
b_spec: &[f32],
s3_spec: &[f32],
s4_spec: &[f32],
chparam_pair: &[ChparamInfo; 2],
max_sfb_master: u32,
tl: u32,
) -> Option<SapTable181Output> {
let n = tl as usize;
if n == 0 || a_spec.len() < n || b_spec.len() < n || s3_spec.len() < n || s4_spec.len() < n {
return None;
}
let sfbo = crate::sfb_offset::sfb_offset_48(tl)?;
let coeffs0 = extract_sap_abcd(&chparam_pair[0], &[max_sfb_master]);
let coeffs1 = extract_sap_abcd(&chparam_pair[1], &[max_sfb_master]);
let abcd0: &[(f32, f32, f32, f32)] = coeffs0.abcd.first().map(|v| v.as_slice()).unwrap_or(&[]);
let abcd1: &[(f32, f32, f32, f32)] = coeffs1.abcd.first().map(|v| v.as_slice()).unwrap_or(&[]);
let max_sfb = max_sfb_master as usize;
let mut l_spec = vec![0.0f32; n];
let mut r_spec = vec![0.0f32; n];
let mut ls_spec = vec![0.0f32; n];
let mut rs_spec = vec![0.0f32; n];
let usable0 = abcd0.len().min(max_sfb);
let usable1 = abcd1.len().min(max_sfb);
let usable = usable0.min(usable1);
for sfb in 0..usable {
let lo = sfbo[sfb] as usize;
let hi = sfbo[sfb + 1] as usize;
let hi = hi.min(n);
let (a0, b0, c0, d0) = abcd0[sfb];
let (a1, b1, c1, d1) = abcd1[sfb];
for k in lo..hi {
let a = a_spec[k];
let b = b_spec[k];
let s3 = s3_spec[k];
let s4 = s4_spec[k];
l_spec[k] = a0 * a + b0 * s3;
r_spec[k] = a1 * b + b1 * s4;
ls_spec[k] = c0 * a + d0 * s3;
rs_spec[k] = c1 * b + d1 * s4;
}
}
let unmixed_start = sfbo.get(usable).copied().map(|v| v as usize).unwrap_or(n);
let unmixed_lo = unmixed_start.min(n);
if unmixed_lo < n {
l_spec[unmixed_lo..n].copy_from_slice(&a_spec[unmixed_lo..n]);
r_spec[unmixed_lo..n].copy_from_slice(&b_spec[unmixed_lo..n]);
}
Some((l_spec, r_spec, ls_spec, rs_spec))
}
pub type SapTable181EncodeOutput = (Vec<f32>, Vec<f32>, Vec<f32>, Vec<f32>);
pub fn invert_sap_table_181(
l_spec: &[f32],
r_spec: &[f32],
ls_spec: &[f32],
rs_spec: &[f32],
chparam_pair: &[ChparamInfo; 2],
max_sfb_master: u32,
tl: u32,
) -> Option<SapTable181EncodeOutput> {
let n = tl as usize;
if n == 0 || l_spec.len() < n || r_spec.len() < n || ls_spec.len() < n || rs_spec.len() < n {
return None;
}
let sfbo = crate::sfb_offset::sfb_offset_48(tl)?;
let coeffs0 = extract_sap_abcd(&chparam_pair[0], &[max_sfb_master]);
let coeffs1 = extract_sap_abcd(&chparam_pair[1], &[max_sfb_master]);
let abcd0: &[(f32, f32, f32, f32)] = coeffs0.abcd.first().map(|v| v.as_slice()).unwrap_or(&[]);
let abcd1: &[(f32, f32, f32, f32)] = coeffs1.abcd.first().map(|v| v.as_slice()).unwrap_or(&[]);
let max_sfb = max_sfb_master as usize;
let mut a_spec = vec![0.0f32; n];
let mut b_spec = vec![0.0f32; n];
let mut s3_spec = vec![0.0f32; n];
let mut s4_spec = vec![0.0f32; n];
let usable0 = abcd0.len().min(max_sfb);
let usable1 = abcd1.len().min(max_sfb);
let usable = usable0.min(usable1);
for sfb in 0..usable {
let lo = sfbo[sfb] as usize;
let hi = sfbo[sfb + 1] as usize;
let hi = hi.min(n);
let (a0, b0, c0, d0) = abcd0[sfb];
let (a1, b1, c1, d1) = abcd1[sfb];
let det0 = a0 * d0 - b0 * c0;
let det1 = a1 * d1 - b1 * c1;
if det0 == 0.0 || det1 == 0.0 {
continue;
}
let inv0 = 1.0 / det0;
let inv1 = 1.0 / det1;
for k in lo..hi {
let l = l_spec[k];
let ls = ls_spec[k];
let r = r_spec[k];
let rs = rs_spec[k];
a_spec[k] = inv0 * (d0 * l - b0 * ls);
s3_spec[k] = inv0 * (-c0 * l + a0 * ls);
b_spec[k] = inv1 * (d1 * r - b1 * rs);
s4_spec[k] = inv1 * (-c1 * r + a1 * rs);
}
}
let unmixed_start = sfbo.get(usable).copied().map(|v| v as usize).unwrap_or(n);
let unmixed_lo = unmixed_start.min(n);
if unmixed_lo < n {
a_spec[unmixed_lo..n].copy_from_slice(&l_spec[unmixed_lo..n]);
b_spec[unmixed_lo..n].copy_from_slice(&r_spec[unmixed_lo..n]);
}
Some((a_spec, b_spec, s3_spec, s4_spec))
}
pub fn build_chparam_info_ms_used(ms_used_per_group: Vec<Vec<bool>>) -> ChparamInfo {
ChparamInfo {
sap_mode: 1,
ms_used: ms_used_per_group,
sap_data: None,
}
}
pub fn build_chparam_info_sap_data_from_alpha_q(
alpha_q_per_group: &[Vec<i32>],
sap_coeff_used_per_group: &[Vec<bool>],
delta_code_time: bool,
max_sfb_per_group: &[u32],
) -> ChparamInfo {
let num_groups = max_sfb_per_group.len();
let mut sap_coeff_used: Vec<Vec<bool>> = Vec::with_capacity(num_groups);
let mut dpcm_alpha_q: Vec<Vec<i32>> = Vec::with_capacity(num_groups);
let mut all_set = true;
let mut max_sfb_prev: u32 = max_sfb_per_group.first().copied().unwrap_or(0);
for (g, &m) in max_sfb_per_group.iter().enumerate() {
let m = m as usize;
let used_row = sap_coeff_used_per_group.get(g);
let alpha_row = alpha_q_per_group.get(g);
let prev_alpha_row = if g == 0 {
None
} else {
alpha_q_per_group.get(g - 1)
};
let mut used_out: Vec<bool> = vec![false; m];
let mut dpcm_out: Vec<i32> = vec![0i32; m];
let mut sfb = 0usize;
while sfb < m {
let used = used_row.and_then(|r| r.get(sfb).copied()).unwrap_or(false);
used_out[sfb] = used;
if sfb + 1 < m {
used_out[sfb + 1] = used;
}
if !used {
all_set = false;
}
if used && sfb % 2 == 0 {
let code_delta = g != 0 && max_sfb_per_group[g] == max_sfb_prev && delta_code_time;
let prev = if code_delta {
prev_alpha_row
.and_then(|r| r.get(sfb).copied())
.unwrap_or(0)
} else if sfb == 0 {
0
} else {
alpha_row
.and_then(|r| r.get(sfb.wrapping_sub(2)).copied())
.unwrap_or(0)
};
let cur = alpha_row.and_then(|r| r.get(sfb).copied()).unwrap_or(0);
dpcm_out[sfb] = cur - prev;
}
sfb += 1;
}
sap_coeff_used.push(used_out);
dpcm_alpha_q.push(dpcm_out);
max_sfb_prev = max_sfb_per_group[g];
}
let dct_normalised = num_groups != 1 && delta_code_time;
ChparamInfo {
sap_mode: 3,
ms_used: vec![],
sap_data: Some(SapData {
sap_coeff_all: all_set && num_groups > 0,
sap_coeff_used,
delta_code_time: dct_normalised,
dpcm_alpha_q,
}),
}
}
pub fn build_chparam_info_none() -> ChparamInfo {
ChparamInfo {
sap_mode: 0,
ms_used: vec![],
sap_data: None,
}
}
pub fn select_ms_used_for_pair(
l_spec_per_group: &[Vec<f32>],
r_spec_per_group: &[Vec<f32>],
sfb_offset: &[u16],
max_sfb_per_group: &[u32],
) -> Vec<Vec<bool>> {
let mut out: Vec<Vec<bool>> = Vec::with_capacity(max_sfb_per_group.len());
for (g, &m) in max_sfb_per_group.iter().enumerate() {
let m_usize = m as usize;
let mut row = vec![false; m_usize];
let l = l_spec_per_group.get(g);
let r = r_spec_per_group.get(g);
let (Some(l), Some(r)) = (l, r) else {
out.push(row);
continue;
};
for (sfb, slot) in row.iter_mut().enumerate().take(m_usize) {
let Some(&lo) = sfb_offset.get(sfb) else {
break;
};
let Some(&hi) = sfb_offset.get(sfb + 1) else {
break;
};
let lo = lo as usize;
let hi = (hi as usize).min(l.len()).min(r.len());
if hi <= lo {
continue;
}
let mut e_l = 0.0f64;
let mut e_r = 0.0f64;
let mut cross = 0.0f64;
for k in lo..hi {
let lk = l[k] as f64;
let rk = r[k] as f64;
e_l += lk * lk;
e_r += rk * rk;
cross += lk * rk;
}
let sum = e_l + e_r;
let e_m = (sum + 2.0 * cross) * 0.25;
let e_s = (sum - 2.0 * cross) * 0.25;
let min_lr = e_l.min(e_r);
let min_ms = e_m.min(e_s);
*slot = min_ms < min_lr;
}
out.push(row);
}
out
}
pub type SapAlphaDecision = (Vec<Vec<i32>>, Vec<Vec<bool>>);
pub fn select_alpha_q_for_pair(
l_spec_per_group: &[Vec<f32>],
r_spec_per_group: &[Vec<f32>],
sfb_offset: &[u16],
max_sfb_per_group: &[u32],
) -> SapAlphaDecision {
let num_groups = max_sfb_per_group.len();
let mut alpha_q_out: Vec<Vec<i32>> = Vec::with_capacity(num_groups);
let mut used_out: Vec<Vec<bool>> = Vec::with_capacity(num_groups);
for (g, &m) in max_sfb_per_group.iter().enumerate() {
let m_usize = m as usize;
let mut alpha_row = vec![0i32; m_usize];
let mut used_row = vec![false; m_usize];
let (Some(l), Some(r)) = (l_spec_per_group.get(g), r_spec_per_group.get(g)) else {
alpha_q_out.push(alpha_row);
used_out.push(used_row);
continue;
};
let mut sfb = 0usize;
while sfb < m_usize {
let Some(&lo) = sfb_offset.get(sfb) else {
break;
};
let Some(&hi) = sfb_offset.get(sfb + 1) else {
break;
};
let lo = lo as usize;
let hi = (hi as usize).min(l.len()).min(r.len());
if hi > lo {
let mut e_m = 0.0f64; let mut cross_sm = 0.0f64; for k in lo..hi {
let lk = l[k] as f64;
let rk = r[k] as f64;
let mk = (lk + rk) * 0.5;
let sk = (lk - rk) * 0.5;
e_m += mk * mk;
cross_sm += sk * mk;
}
if e_m > 0.0 {
let g_star = cross_sm / e_m;
let aq = (g_star * 10.0).round().clamp(-60.0, 60.0) as i32;
if aq != 0 {
alpha_row[sfb] = aq;
used_row[sfb] = true;
if sfb + 1 < m_usize {
alpha_row[sfb + 1] = aq;
used_row[sfb + 1] = true;
}
}
}
}
sfb += 2;
}
alpha_q_out.push(alpha_row);
used_out.push(used_row);
}
(alpha_q_out, used_out)
}
#[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 cfg2_aspx_lr: Option<aspx::FiveXAspxTrailer>,
pub cfg2_aspx_ls_rs: Option<aspx::FiveXAspxTrailer>,
pub cfg2_aspx_centre: Option<aspx::FiveXAspxTrailer>,
pub cfg0_aspx_lr: Option<aspx::FiveXAspxTrailer>,
pub cfg0_aspx_ls_rs: Option<aspx::FiveXAspxTrailer>,
pub cfg0_aspx_centre: Option<aspx::FiveXAspxTrailer>,
pub cfg1_aspx_lr: Option<aspx::FiveXAspxTrailer>,
pub cfg1_aspx_ls_rs: Option<aspx::FiveXAspxTrailer>,
pub cfg1_aspx_centre: Option<aspx::FiveXAspxTrailer>,
pub cfg3_aspx_lr: Option<aspx::FiveXAspxTrailer>,
pub cfg3_aspx_ls_rs: Option<aspx::FiveXAspxTrailer>,
pub cfg3_aspx_centre: Option<aspx::FiveXAspxTrailer>,
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 acpl_1_residual_pair: [Option<(u32, Vec<f32>)>; 2],
pub acpl_1_residual_chparam: [Option<ChparamInfo>; 2],
pub acpl_1_residual_max_sfb_master: Option<u32>,
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>,
pub ssf_data_primary: Option<crate::ssf::SsfData>,
pub ssf_data_secondary: Option<crate::ssf::SsfData>,
}
#[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<()> {
parse_mono_audio_data_outer_stateful(br, tools, b_iframe, frame_len_base, None)
}
pub fn parse_mono_audio_data_outer_stateful(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
b_iframe: bool,
frame_len_base: u32,
ssf_states: Option<&mut [crate::ssf::SsfChannelState]>,
) -> 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::Ssf = frontend {
if let Some(cfg) = crate::ssf::SsfFrameConfig::from_frame_len_base(frame_len_base) {
let mut local = crate::ssf::SsfChannelState::new();
let state: &mut crate::ssf::SsfChannelState = match ssf_states {
Some(slice) if !slice.is_empty() => &mut slice[0],
_ => &mut local,
};
if let Ok(d) = crate::ssf::parse_ssf_data(br, b_iframe, &cfg, state) {
tools.ssf_data_primary = Some(d);
}
}
return Ok(());
}
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(())
}
struct AspxTrailerSnapshot {
framing_pri: Option<aspx::AspxFraming>,
framing_sec: Option<aspx::AspxFraming>,
balance: Option<bool>,
xover: Option<u8>,
delta_dir_pri: Option<aspx::AspxDeltaDir>,
delta_dir_sec: Option<aspx::AspxDeltaDir>,
qmode_pri: Option<aspx::AspxQuantStep>,
qmode_sec: Option<aspx::AspxQuantStep>,
frequency_tables: Option<aspx::AspxFrequencyTables>,
hfgen_1ch: Option<aspx::AspxHfgenIwc1Ch>,
hfgen_2ch: Option<aspx::AspxHfgenIwc2Ch>,
sig_pri: Option<Vec<aspx::AspxHuffEnv>>,
sig_sec: Option<Vec<aspx::AspxHuffEnv>>,
noise_pri: Option<Vec<aspx::AspxHuffEnv>>,
noise_sec: Option<Vec<aspx::AspxHuffEnv>>,
}
impl AspxTrailerSnapshot {
fn capture(tools: &mut SubstreamTools) -> Self {
Self {
framing_pri: tools.aspx_framing_primary.take(),
framing_sec: tools.aspx_framing_secondary.take(),
balance: tools.aspx_balance.take(),
xover: tools.aspx_xover_subband_offset.take(),
delta_dir_pri: tools.aspx_delta_dir_primary.take(),
delta_dir_sec: tools.aspx_delta_dir_secondary.take(),
qmode_pri: tools.aspx_qmode_env_primary.take(),
qmode_sec: tools.aspx_qmode_env_secondary.take(),
frequency_tables: tools.aspx_frequency_tables.take(),
hfgen_1ch: tools.aspx_hfgen_iwc_1ch.take(),
hfgen_2ch: tools.aspx_hfgen_iwc_2ch.take(),
sig_pri: tools.aspx_data_sig_primary.take(),
sig_sec: tools.aspx_data_sig_secondary.take(),
noise_pri: tools.aspx_data_noise_primary.take(),
noise_sec: tools.aspx_data_noise_secondary.take(),
}
}
fn restore(self, tools: &mut SubstreamTools) {
tools.aspx_framing_primary = self.framing_pri;
tools.aspx_framing_secondary = self.framing_sec;
tools.aspx_balance = self.balance;
tools.aspx_xover_subband_offset = self.xover;
tools.aspx_delta_dir_primary = self.delta_dir_pri;
tools.aspx_delta_dir_secondary = self.delta_dir_sec;
tools.aspx_qmode_env_primary = self.qmode_pri;
tools.aspx_qmode_env_secondary = self.qmode_sec;
tools.aspx_frequency_tables = self.frequency_tables;
tools.aspx_hfgen_iwc_1ch = self.hfgen_1ch;
tools.aspx_hfgen_iwc_2ch = self.hfgen_2ch;
tools.aspx_data_sig_primary = self.sig_pri;
tools.aspx_data_sig_secondary = self.sig_sec;
tools.aspx_data_noise_primary = self.noise_pri;
tools.aspx_data_noise_secondary = self.noise_sec;
}
}
pub(crate) fn capture_aspx_data_2ch_trailer(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
cfg: &aspx::AspxConfig,
b_iframe: bool,
frame_len_base: u32,
) -> Option<aspx::FiveXAspxTrailer> {
let snap = AspxTrailerSnapshot::capture(tools);
let parse_ok = parse_aspx_data_2ch_body(br, tools, cfg, b_iframe, frame_len_base).is_ok();
let trailer = if parse_ok {
let xover = tools.aspx_xover_subband_offset?;
let frequency_tables = tools.aspx_frequency_tables.clone()?;
let framing_pri = tools.aspx_framing_primary.clone()?;
let qmode_pri = tools.aspx_qmode_env_primary?;
let delta_dir_pri = tools.aspx_delta_dir_primary.clone()?;
let sig_pri = tools.aspx_data_sig_primary.clone().unwrap_or_default();
let noise_pri = tools.aspx_data_noise_primary.clone().unwrap_or_default();
let hfgen = tools.aspx_hfgen_iwc_2ch.clone();
let (ah_pri, tna_pri, ah_sec, tna_sec) = if let Some(h) = hfgen.as_ref() {
let ah_pri = h.add_harmonic.first().cloned();
let ah_sec = h.add_harmonic.get(1).cloned();
let tna_pri = h.tna_mode.first().cloned();
let tna_sec = h.tna_mode.get(1).cloned();
(ah_pri, tna_pri, ah_sec, tna_sec)
} else {
(None, None, None, None)
};
let primary = aspx::FiveXAspxChannelTrailer {
framing: framing_pri.clone(),
qmode_env: qmode_pri,
delta_dir: delta_dir_pri,
data_sig: sig_pri,
data_noise: noise_pri,
add_harmonic: ah_pri,
tna_mode: tna_pri,
};
let framing_sec = tools
.aspx_framing_secondary
.clone()
.unwrap_or_else(|| framing_pri.clone());
let qmode_sec = tools.aspx_qmode_env_secondary.unwrap_or(qmode_pri);
let delta_dir_sec = tools
.aspx_delta_dir_secondary
.clone()
.unwrap_or_else(|| tools.aspx_delta_dir_primary.clone().unwrap_or_default());
let sig_sec = tools.aspx_data_sig_secondary.clone().unwrap_or_default();
let noise_sec = tools.aspx_data_noise_secondary.clone().unwrap_or_default();
let secondary = aspx::FiveXAspxChannelTrailer {
framing: framing_sec,
qmode_env: qmode_sec,
delta_dir: delta_dir_sec,
data_sig: sig_sec,
data_noise: noise_sec,
add_harmonic: ah_sec,
tna_mode: tna_sec,
};
Some(aspx::FiveXAspxTrailer {
xover,
frequency_tables,
primary,
secondary: Some(secondary),
})
} else {
None
};
snap.restore(tools);
trailer
}
pub(crate) fn capture_aspx_data_1ch_trailer(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
cfg: &aspx::AspxConfig,
b_iframe: bool,
frame_len_base: u32,
) -> Option<aspx::FiveXAspxTrailer> {
let snap = AspxTrailerSnapshot::capture(tools);
let parse_ok = parse_aspx_data_1ch_body(br, tools, cfg, b_iframe, frame_len_base).is_ok();
let trailer = if parse_ok {
let xover = tools.aspx_xover_subband_offset?;
let frequency_tables = tools.aspx_frequency_tables.clone()?;
let framing = tools.aspx_framing_primary.clone()?;
let qmode = tools.aspx_qmode_env_primary?;
let delta_dir = tools.aspx_delta_dir_primary.clone()?;
let data_sig = tools.aspx_data_sig_primary.clone().unwrap_or_default();
let data_noise = tools.aspx_data_noise_primary.clone().unwrap_or_default();
let hfgen = tools.aspx_hfgen_iwc_1ch.clone();
let (add_harmonic, tna_mode) = if let Some(h) = hfgen.as_ref() {
(Some(h.add_harmonic.clone()), Some(h.tna_mode.clone()))
} else {
(None, None)
};
Some(aspx::FiveXAspxTrailer {
xover,
frequency_tables,
primary: aspx::FiveXAspxChannelTrailer {
framing,
qmode_env: qmode,
delta_dir,
data_sig,
data_noise,
add_harmonic,
tna_mode,
},
secondary: None,
})
} else {
None
};
snap.restore(tools);
trailer
}
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 mut scaled = asf_data::dequantise_and_scale(&qspec, &sf_gain, sfbo, max_sfb);
if let Some(snf_data) = snf {
let mut rng: u32 = 0x1234_5678; asf_data::inject_snf_noise(&mut scaled, &snf_data, sfbo, max_sfb, &mut rng);
}
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 {
parse_aspx_acpl1_mdct_body_stateful(br, tools, frame_len_base, None)
}
fn parse_aspx_acpl1_mdct_body_stateful(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
frame_len_base: u32,
ssf_states: Option<&mut [crate::ssf::SsfChannelState]>,
) -> 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 mut local_m = crate::ssf::SsfChannelState::new();
let mut local_s = crate::ssf::SsfChannelState::new();
let (state_m_ref, state_s_ref): (
&mut crate::ssf::SsfChannelState,
&mut crate::ssf::SsfChannelState,
) = match ssf_states {
Some(slice) if slice.len() >= 2 => {
let (a, rest) = slice.split_at_mut(1);
(&mut a[0], &mut rest[0])
}
Some(slice) if slice.len() == 1 => (&mut slice[0], &mut local_s),
_ => (&mut local_m, &mut local_s),
};
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) {
if let Some(cfg) = crate::ssf::SsfFrameConfig::from_frame_len_base(frame_len_base) {
match crate::ssf::parse_ssf_data(br, false, &cfg, state_m_ref) {
Ok(d) => tools.ssf_data_primary = Some(d),
Err(_) => return false,
}
} else {
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) {
if let Some(cfg) = crate::ssf::SsfFrameConfig::from_frame_len_base(frame_len_base) {
match crate::ssf::parse_ssf_data(br, false, &cfg, state_s_ref) {
Ok(d) => tools.ssf_data_secondary = Some(d),
Err(_) => return false,
}
} else {
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(crate) fn decode_asf_long_lfe_body_with_max_sfb_lfe(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
max_sfb_lfe: u32,
) -> Option<Vec<f32>> {
if !ti.b_long_frame {
return None;
}
decode_asf_long_mono_body_with_max_sfb(br, ti, max_sfb_lfe)
}
pub fn parse_stereo_audio_data_outer(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
b_iframe: bool,
frame_len_base: u32,
) -> Result<()> {
parse_stereo_audio_data_outer_stateful(br, tools, b_iframe, frame_len_base, None)
}
pub fn parse_stereo_audio_data_outer_stateful(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
b_iframe: bool,
frame_len_base: u32,
mut ssf_states: Option<&mut [crate::ssf::SsfChannelState]>,
) -> 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_stateful(
br,
tools,
frame_len_base,
ssf_states.as_deref_mut(),
),
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_stateful(
br,
tools,
frame_len_base,
ssf_states.as_deref_mut(),
);
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_stateful(br, tools, frame_len_base, ssf_states);
let _ = b_iframe; Ok(())
}
pub(crate) fn parse_stereo_data_body(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
frame_len_base: u32,
) -> bool {
parse_stereo_data_body_stateful(br, tools, frame_len_base, None)
}
pub(crate) fn parse_stereo_data_body_stateful(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
frame_len_base: u32,
ssf_states: Option<&mut [crate::ssf::SsfChannelState]>,
) -> 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;
let mut local_l = crate::ssf::SsfChannelState::new();
let mut local_r = crate::ssf::SsfChannelState::new();
let (state_l_ref, state_r_ref): (
&mut crate::ssf::SsfChannelState,
&mut crate::ssf::SsfChannelState,
) = match ssf_states {
Some(slice) if slice.len() >= 2 => {
let (a, rest) = slice.split_at_mut(1);
(&mut a[0], &mut rest[0])
}
Some(slice) if slice.len() == 1 => (&mut slice[0], &mut local_r),
_ => (&mut local_l, &mut local_r),
};
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;
}
} else if matches!(l, SpecFrontend::Ssf) {
if let Some(cfg) = crate::ssf::SsfFrameConfig::from_frame_len_base(frame_len_base) {
if let Ok(d) = crate::ssf::parse_ssf_data(br, false, &cfg, state_l_ref) {
tools.ssf_data_primary = Some(d);
} else {
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;
}
} else if matches!(r, SpecFrontend::Ssf) {
if let Some(cfg) = crate::ssf::SsfFrameConfig::from_frame_len_base(frame_len_base) {
if let Ok(d) = crate::ssf::parse_ssf_data(br, false, &cfg, state_r_ref) {
tools.ssf_data_secondary = Some(d);
} else {
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> {
walk_ac4_substream_stateful(substream_bytes, channels, b_iframe, frame_len_base, None)
}
pub fn walk_ac4_substream_stateful(
substream_bytes: &[u8],
channels: u16,
b_iframe: bool,
frame_len_base: u32,
mut ssf_states: Option<&mut [crate::ssf::SsfChannelState]>,
) -> 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_stateful(
&mut br,
&mut tools,
b_iframe,
frame_len_base,
ssf_states.as_deref_mut(),
)?,
2 => parse_stereo_audio_data_outer_stateful(
&mut br,
&mut tools,
b_iframe,
frame_len_base,
ssf_states,
)?,
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]);
}
#[test]
fn extract_sap_abcd_mode_zero_returns_identity() {
let cp = ChparamInfo {
sap_mode: 0,
..ChparamInfo::default()
};
let coeffs = extract_sap_abcd(&cp, &[4]);
assert_eq!(coeffs.abcd.len(), 1);
for q in &coeffs.abcd[0] {
assert_eq!(*q, (1.0, 0.0, 0.0, 1.0));
}
}
#[test]
fn extract_sap_abcd_mode_one_swaps_per_sfb_on_ms_used() {
let cp = ChparamInfo {
sap_mode: 1,
ms_used: vec![vec![true, false, true, false]],
sap_data: None,
};
let coeffs = extract_sap_abcd(&cp, &[4]);
assert_eq!(coeffs.abcd[0][0], (1.0, 1.0, 1.0, -1.0));
assert_eq!(coeffs.abcd[0][1], (1.0, 0.0, 0.0, 1.0));
assert_eq!(coeffs.abcd[0][2], (1.0, 1.0, 1.0, -1.0));
assert_eq!(coeffs.abcd[0][3], (1.0, 0.0, 0.0, 1.0));
}
#[test]
fn extract_sap_abcd_mode_three_pair_dpcm_decode() {
let cp = ChparamInfo {
sap_mode: 3,
ms_used: vec![],
sap_data: Some(SapData {
sap_coeff_all: true,
sap_coeff_used: vec![vec![true, true, true, true]],
delta_code_time: false,
dpcm_alpha_q: vec![vec![5, 0, 3, 0]],
}),
};
let coeffs = extract_sap_abcd(&cp, &[4]);
let row = &coeffs.abcd[0];
let (a, b, c, d) = row[0];
assert!((a - 1.5).abs() < 1e-6);
assert_eq!(b, 1.0);
assert!((c - 0.5).abs() < 1e-6);
assert_eq!(d, -1.0);
assert_eq!(row[1], row[0]);
let (a2, _b2, c2, _d2) = row[2];
assert!((a2 - 1.8).abs() < 1e-6);
assert!((c2 - 0.2).abs() < 1e-6);
assert_eq!(row[3], row[2]);
}
#[test]
fn extract_sap_abcd_mode_three_unused_bands_pass_through() {
let cp = ChparamInfo {
sap_mode: 3,
ms_used: vec![],
sap_data: Some(SapData {
sap_coeff_all: false,
sap_coeff_used: vec![vec![false, false]],
delta_code_time: false,
dpcm_alpha_q: vec![vec![10, 0]],
}),
};
let coeffs = extract_sap_abcd(&cp, &[2]);
assert_eq!(coeffs.abcd[0][0], (1.0, 0.0, 0.0, 1.0));
assert_eq!(coeffs.abcd[0][1], (1.0, 0.0, 0.0, 1.0));
}
#[test]
fn extract_sap_abcd_mode_three_delta_code_time_cross_group() {
let cp = ChparamInfo {
sap_mode: 3,
ms_used: vec![],
sap_data: Some(SapData {
sap_coeff_all: true,
sap_coeff_used: vec![vec![true, true], vec![true, true]],
delta_code_time: true,
dpcm_alpha_q: vec![vec![4, 0], vec![2, 0]],
}),
};
let coeffs = extract_sap_abcd(&cp, &[2, 2]);
let (a, _, c, _) = coeffs.abcd[1][0];
assert!((a - 1.6).abs() < 1e-6);
assert!((c - 0.4).abs() < 1e-6);
}
#[test]
fn apply_sap_table_181_identity_passthrough() {
let tl = 256u32;
let n = tl as usize;
let max_sfb_master = 4u32;
let a_spec: Vec<f32> = (0..n).map(|i| 0.10 + 1e-3 * i as f32).collect();
let b_spec: Vec<f32> = (0..n).map(|i| 0.20 + 1e-3 * i as f32).collect();
let s3_spec: Vec<f32> = (0..n).map(|i| 0.30 + 1e-3 * i as f32).collect();
let s4_spec: Vec<f32> = (0..n).map(|i| 0.40 + 1e-3 * i as f32).collect();
let cp_id = ChparamInfo {
sap_mode: 0,
ms_used: vec![],
sap_data: None,
};
let pair = [cp_id.clone(), cp_id];
let (l, r, ls, rs) = apply_sap_table_181(
&a_spec,
&b_spec,
&s3_spec,
&s4_spec,
&pair,
max_sfb_master,
tl,
)
.expect("identity SAP must produce outputs");
assert_eq!(l.len(), n);
for k in 0..n {
assert!(
(l[k] - a_spec[k]).abs() < 1e-6,
"L[{k}] = {} expected {}",
l[k],
a_spec[k]
);
assert!((r[k] - b_spec[k]).abs() < 1e-6);
}
let sfbo = crate::sfb_offset::sfb_offset_48(tl).unwrap();
let sap_hi = sfbo[max_sfb_master as usize] as usize;
for k in 0..sap_hi {
assert!((ls[k] - s3_spec[k]).abs() < 1e-6);
assert!((rs[k] - s4_spec[k]).abs() < 1e-6);
}
for k in sap_hi..n {
assert_eq!(ls[k], 0.0);
assert_eq!(rs[k], 0.0);
}
}
#[test]
fn apply_sap_table_181_ms_used_mixing() {
let tl = 256u32;
let n = tl as usize;
let max_sfb_master = 2u32;
let a_spec = vec![1.0_f32; n];
let b_spec = vec![2.0_f32; n];
let s3_spec = vec![3.0_f32; n];
let s4_spec = vec![4.0_f32; n];
let cp = ChparamInfo {
sap_mode: 1,
ms_used: vec![vec![true, true]],
sap_data: None,
};
let pair = [cp.clone(), cp];
let (l, r, ls, rs) = apply_sap_table_181(
&a_spec,
&b_spec,
&s3_spec,
&s4_spec,
&pair,
max_sfb_master,
tl,
)
.unwrap();
let sfbo = crate::sfb_offset::sfb_offset_48(tl).unwrap();
let sap_hi = sfbo[max_sfb_master as usize] as usize;
for k in 0..sap_hi {
assert!((l[k] - 4.0).abs() < 1e-6); assert!((r[k] - 6.0).abs() < 1e-6); assert!((ls[k] - (-2.0)).abs() < 1e-6); assert!((rs[k] - (-2.0)).abs() < 1e-6); }
for k in sap_hi..n {
assert_eq!(l[k], 1.0);
assert_eq!(r[k], 2.0);
assert_eq!(ls[k], 0.0);
assert_eq!(rs[k], 0.0);
}
}
#[test]
fn apply_sap_table_181_missing_sfb_table_returns_none() {
let tl = 100u32;
let n = tl as usize;
let cp = ChparamInfo::default();
let pair = [cp.clone(), cp];
let zero = vec![0.0f32; n];
let out = apply_sap_table_181(&zero, &zero, &zero, &zero, &pair, 1, tl);
assert!(out.is_none());
}
#[test]
fn invert_sap_table_181_identity_passthrough() {
let tl = 256u32;
let n = tl as usize;
let max_sfb_master = 4u32;
let l_spec: Vec<f32> = (0..n).map(|i| 0.10 + 1e-3 * i as f32).collect();
let r_spec: Vec<f32> = (0..n).map(|i| 0.20 + 1e-3 * i as f32).collect();
let ls_spec: Vec<f32> = (0..n).map(|i| 0.30 + 1e-3 * i as f32).collect();
let rs_spec: Vec<f32> = (0..n).map(|i| 0.40 + 1e-3 * i as f32).collect();
let cp_id = ChparamInfo {
sap_mode: 0,
ms_used: vec![],
sap_data: None,
};
let pair = [cp_id.clone(), cp_id];
let (a, b, s3, s4) = invert_sap_table_181(
&l_spec,
&r_spec,
&ls_spec,
&rs_spec,
&pair,
max_sfb_master,
tl,
)
.expect("identity inverse must produce outputs");
assert_eq!(a.len(), n);
let sfbo = crate::sfb_offset::sfb_offset_48(tl).unwrap();
let sap_hi = sfbo[max_sfb_master as usize] as usize;
for k in 0..sap_hi {
assert!((a[k] - l_spec[k]).abs() < 1e-6);
assert!((b[k] - r_spec[k]).abs() < 1e-6);
assert!((s3[k] - ls_spec[k]).abs() < 1e-6);
assert!((s4[k] - rs_spec[k]).abs() < 1e-6);
}
for k in sap_hi..n {
assert!((a[k] - l_spec[k]).abs() < 1e-6);
assert!((b[k] - r_spec[k]).abs() < 1e-6);
assert_eq!(s3[k], 0.0);
assert_eq!(s4[k], 0.0);
}
}
#[test]
fn invert_sap_table_181_ms_used_inverse() {
let tl = 256u32;
let n = tl as usize;
let max_sfb_master = 2u32;
let l_spec = vec![4.0_f32; n]; let r_spec = vec![6.0_f32; n]; let ls_spec = vec![-2.0_f32; n]; let rs_spec = vec![-2.0_f32; n]; let cp = ChparamInfo {
sap_mode: 1,
ms_used: vec![vec![true, true]],
sap_data: None,
};
let pair = [cp.clone(), cp];
let (a, b, s3, s4) = invert_sap_table_181(
&l_spec,
&r_spec,
&ls_spec,
&rs_spec,
&pair,
max_sfb_master,
tl,
)
.unwrap();
let sfbo = crate::sfb_offset::sfb_offset_48(tl).unwrap();
let sap_hi = sfbo[max_sfb_master as usize] as usize;
for k in 0..sap_hi {
assert!((a[k] - 1.0).abs() < 1e-6); assert!((s3[k] - 3.0).abs() < 1e-6); assert!((b[k] - 2.0).abs() < 1e-6); assert!((s4[k] - 4.0).abs() < 1e-6); }
for k in sap_hi..n {
assert!((a[k] - 4.0).abs() < 1e-6);
assert!((b[k] - 6.0).abs() < 1e-6);
assert_eq!(s3[k], 0.0);
assert_eq!(s4[k], 0.0);
}
}
#[test]
fn sap_table_181_roundtrip_identity() {
let tl = 256u32;
let n = tl as usize;
let max_sfb_master = 4u32;
let a0: Vec<f32> = (0..n).map(|i| 0.11 + 1e-3 * i as f32).collect();
let b0: Vec<f32> = (0..n).map(|i| 0.22 + 1e-3 * i as f32).collect();
let s30: Vec<f32> = (0..n).map(|i| 0.33 + 1e-3 * i as f32).collect();
let s40: Vec<f32> = (0..n).map(|i| 0.44 + 1e-3 * i as f32).collect();
let cp_id = ChparamInfo {
sap_mode: 0,
ms_used: vec![],
sap_data: None,
};
let pair = [cp_id.clone(), cp_id];
let (l, r, ls, rs) =
apply_sap_table_181(&a0, &b0, &s30, &s40, &pair, max_sfb_master, tl).unwrap();
let (a_r, b_r, s3_r, s4_r) =
invert_sap_table_181(&l, &r, &ls, &rs, &pair, max_sfb_master, tl).unwrap();
let sfbo = crate::sfb_offset::sfb_offset_48(tl).unwrap();
let sap_hi = sfbo[max_sfb_master as usize] as usize;
for k in 0..sap_hi {
assert!((a_r[k] - a0[k]).abs() < 1e-6);
assert!((b_r[k] - b0[k]).abs() < 1e-6);
assert!((s3_r[k] - s30[k]).abs() < 1e-6);
assert!((s4_r[k] - s40[k]).abs() < 1e-6);
}
for k in sap_hi..n {
assert!((a_r[k] - a0[k]).abs() < 1e-6);
assert!((b_r[k] - b0[k]).abs() < 1e-6);
assert_eq!(s3_r[k], 0.0);
assert_eq!(s4_r[k], 0.0);
}
}
#[test]
fn sap_table_181_roundtrip_ms_used() {
let tl = 256u32;
let n = tl as usize;
let max_sfb_master = 2u32;
let a0: Vec<f32> = (0..n).map(|i| 0.10 + 1e-3 * i as f32).collect();
let b0: Vec<f32> = (0..n).map(|i| 0.20 + 1e-3 * i as f32).collect();
let s30: Vec<f32> = (0..n).map(|i| 0.30 + 1e-3 * i as f32).collect();
let s40: Vec<f32> = (0..n).map(|i| 0.40 + 1e-3 * i as f32).collect();
let cp = ChparamInfo {
sap_mode: 1,
ms_used: vec![vec![true, true]],
sap_data: None,
};
let pair = [cp.clone(), cp];
let (l, r, ls, rs) =
apply_sap_table_181(&a0, &b0, &s30, &s40, &pair, max_sfb_master, tl).unwrap();
let (a_r, b_r, s3_r, s4_r) =
invert_sap_table_181(&l, &r, &ls, &rs, &pair, max_sfb_master, tl).unwrap();
let sfbo = crate::sfb_offset::sfb_offset_48(tl).unwrap();
let sap_hi = sfbo[max_sfb_master as usize] as usize;
for k in 0..sap_hi {
assert!((a_r[k] - a0[k]).abs() < 1e-5);
assert!((b_r[k] - b0[k]).abs() < 1e-5);
assert!((s3_r[k] - s30[k]).abs() < 1e-5);
assert!((s4_r[k] - s40[k]).abs() < 1e-5);
}
}
#[test]
fn invert_sap_table_181_missing_sfb_table_returns_none() {
let tl = 100u32;
let n = tl as usize;
let cp = ChparamInfo::default();
let pair = [cp.clone(), cp];
let zero = vec![0.0f32; n];
let out = invert_sap_table_181(&zero, &zero, &zero, &zero, &pair, 1, tl);
assert!(out.is_none());
}
#[test]
fn sap_coeffs_identity_helper() {
let coeffs = SapCoeffs::identity(&[3, 2]);
assert_eq!(coeffs.abcd.len(), 2);
for row in &coeffs.abcd {
for q in row {
assert_eq!(*q, (1.0, 0.0, 0.0, 1.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));
}
#[test]
fn mono_ssf_substream_walker_populates_ssf_data() {
let mut bw = BitWriter::new();
bw.write_u32(100, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 1);
bw.write_u32(1, 1);
bw.write_u32(0, 1); bw.write_u32(0, 3); bw.write_u32(0, 5); bw.write_u32(0, 1);
bw.write_u32(0, 5);
for _ in 0..(30 + 256) {
bw.write_bit(false);
}
bw.align_to_byte();
while bw.byte_len() < 128 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 1, true, 960).unwrap();
assert_eq!(info.tools.mono_mode, Some(MonoCodecMode::Simple));
assert_eq!(info.tools.spec_frontend_primary, Some(SpecFrontend::Ssf));
let ssf = info
.tools
.ssf_data_primary
.as_ref()
.expect("SSF walker should have populated ssf_data_primary");
assert_eq!(ssf.granules.len(), 1);
let g = &ssf.granules[0];
assert_eq!(g.num_bands, 12);
assert_eq!(g.n_mdct, 960);
assert!(g.b_iframe);
}
#[test]
fn walk_ac4_substream_stateful_persists_ssf_walker_state() {
let mut bw = BitWriter::new();
bw.write_u32(100, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 1);
bw.write_u32(1, 1);
bw.write_u32(0, 1); bw.write_u32(0, 3); bw.write_u32(0, 5); bw.write_u32(0, 1); bw.write_u32(0, 5); for _ in 0..(30 + 256) {
bw.write_bit(false);
}
bw.align_to_byte();
while bw.byte_len() < 128 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let mut state_bank = vec![crate::ssf::SsfChannelState::new()];
assert_eq!(state_bank[0].last_num_bands, 0);
assert!(state_bank[0].env_prev.is_empty());
let info =
walk_ac4_substream_stateful(&bytes, 1, true, 960, Some(&mut state_bank)).unwrap();
assert_eq!(state_bank[0].last_num_bands, 12);
assert_eq!(state_bank[0].last_n_mdct, 960);
assert_eq!(state_bank[0].env_prev.len(), 12);
assert!(info.tools.ssf_data_primary.is_some());
}
#[test]
fn build_chparam_info_ms_used_round_trips_through_extract_sap_abcd() {
let ms_used = vec![vec![true, false, true, true]];
let info = build_chparam_info_ms_used(ms_used.clone());
assert_eq!(info.sap_mode, 1);
assert_eq!(info.ms_used, ms_used);
assert!(info.sap_data.is_none());
let coeffs = extract_sap_abcd(&info, &[4]);
assert_eq!(coeffs.abcd[0][0], (1.0, 1.0, 1.0, -1.0));
assert_eq!(coeffs.abcd[0][1], (1.0, 0.0, 0.0, 1.0));
assert_eq!(coeffs.abcd[0][2], (1.0, 1.0, 1.0, -1.0));
assert_eq!(coeffs.abcd[0][3], (1.0, 1.0, 1.0, -1.0));
}
#[test]
fn build_chparam_info_ms_used_round_trips_through_bitstream() {
let ms_used = vec![vec![true, false, true, false, true]];
let info = build_chparam_info_ms_used(ms_used.clone());
let mut bw = BitWriter::new();
crate::encoder_asf::write_chparam_info(&mut bw, &info, &[5]);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let parsed = parse_chparam_info(&mut br, &[5]).unwrap();
assert_eq!(parsed.sap_mode, 1);
assert_eq!(parsed.ms_used, ms_used);
}
#[test]
fn build_chparam_info_sap_data_pair_major_round_trip() {
let alpha_q = vec![vec![5, 5, 8, 8]];
let used = vec![vec![true, true, true, true]];
let info = build_chparam_info_sap_data_from_alpha_q(&alpha_q, &used, false, &[4]);
assert_eq!(info.sap_mode, 3);
let sd = info.sap_data.as_ref().expect("sap_data populated");
assert!(sd.sap_coeff_all);
assert_eq!(sd.dpcm_alpha_q[0], vec![5, 0, 3, 0]);
let coeffs = extract_sap_abcd(&info, &[4]);
let row = &coeffs.abcd[0];
assert!((row[0].0 - 1.5).abs() < 1e-6);
assert_eq!(row[0].1, 1.0);
assert!((row[0].2 - 0.5).abs() < 1e-6);
assert_eq!(row[0].3, -1.0);
assert_eq!(row[1], row[0]); assert!((row[2].0 - 1.8).abs() < 1e-6);
assert!((row[2].2 - 0.2).abs() < 1e-6);
assert_eq!(row[3], row[2]);
}
#[test]
fn build_chparam_info_sap_data_unused_bands_pass_through() {
let alpha_q = vec![vec![10, 10]];
let used = vec![vec![false, false]];
let info = build_chparam_info_sap_data_from_alpha_q(&alpha_q, &used, false, &[2]);
let sd = info.sap_data.as_ref().expect("sap_data populated");
assert!(!sd.sap_coeff_all);
assert_eq!(sd.sap_coeff_used[0], vec![false, false]);
let coeffs = extract_sap_abcd(&info, &[2]);
assert_eq!(coeffs.abcd[0][0], (1.0, 0.0, 0.0, 1.0));
assert_eq!(coeffs.abcd[0][1], (1.0, 0.0, 0.0, 1.0));
}
#[test]
fn build_chparam_info_sap_data_delta_code_time_cross_group() {
let alpha_q = vec![vec![4, 4], vec![6, 6]];
let used = vec![vec![true, true], vec![true, true]];
let info = build_chparam_info_sap_data_from_alpha_q(&alpha_q, &used, true, &[2, 2]);
let sd = info.sap_data.as_ref().expect("sap_data populated");
assert!(sd.delta_code_time);
assert_eq!(sd.dpcm_alpha_q[0], vec![4, 0]);
assert_eq!(sd.dpcm_alpha_q[1], vec![2, 0]);
let coeffs = extract_sap_abcd(&info, &[2, 2]);
let (a, _, c, _) = coeffs.abcd[1][0];
assert!((a - 1.6).abs() < 1e-6);
assert!((c - 0.4).abs() < 1e-6);
}
#[test]
fn build_chparam_info_sap_data_single_group_drops_delta_code_time() {
let alpha_q = vec![vec![3, 3]];
let used = vec![vec![true, true]];
let info = build_chparam_info_sap_data_from_alpha_q(&alpha_q, &used, true, &[2]);
let sd = info.sap_data.as_ref().expect("sap_data populated");
assert!(!sd.delta_code_time);
}
#[test]
fn build_chparam_info_sap_data_round_trips_through_bitstream() {
let alpha_q = vec![vec![2, 2, 7, 7]];
let used = vec![vec![true, true, true, true]];
let info = build_chparam_info_sap_data_from_alpha_q(&alpha_q, &used, false, &[4]);
let mut bw = BitWriter::new();
crate::encoder_asf::write_chparam_info(&mut bw, &info, &[4]);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let parsed = parse_chparam_info(&mut br, &[4]).unwrap();
assert_eq!(parsed.sap_mode, 3);
let coeffs = extract_sap_abcd(&parsed, &[4]);
let (a0, _, c0, _) = coeffs.abcd[0][0];
assert!((a0 - 1.2).abs() < 1e-6);
assert!((c0 - 0.8).abs() < 1e-6);
let (a2, _, c2, _) = coeffs.abcd[0][2];
assert!((a2 - 1.7).abs() < 1e-6);
assert!((c2 - 0.3).abs() < 1e-6);
}
#[test]
fn build_chparam_info_none_round_trips_through_extract_and_bitstream() {
let info = build_chparam_info_none();
assert_eq!(info.sap_mode, 0);
assert!(info.ms_used.is_empty());
assert!(info.sap_data.is_none());
let coeffs = extract_sap_abcd(&info, &[3, 5]);
assert_eq!(coeffs.abcd.len(), 2);
for row in &coeffs.abcd {
for &(a, b, c, d) in row {
assert!((a - 1.0).abs() < 1e-6);
assert!(b.abs() < 1e-6);
assert!(c.abs() < 1e-6);
assert!((d - 1.0).abs() < 1e-6);
}
}
let mut bw = BitWriter::new();
crate::encoder_asf::write_chparam_info(&mut bw, &info, &[3, 5]);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let parsed = parse_chparam_info(&mut br, &[3, 5]).unwrap();
assert_eq!(parsed.sap_mode, 0);
assert_eq!(parsed.mode(), SapMode::None);
assert!(parsed.ms_used.is_empty());
assert!(parsed.sap_data.is_none());
}
#[test]
fn select_ms_used_for_pair_per_band_decision() {
let sfb_offset: [u16; 5] = [0, 4, 8, 12, 16];
let mut l = vec![1.0f32, 0.5, -0.7, 0.3];
let mut r = vec![1.0f32, 0.5, -0.7, 0.3];
l.extend_from_slice(&[0.4, 0.9, -0.2, 0.6]);
r.extend_from_slice(&[-0.4, -0.9, 0.2, -0.6]);
l.extend_from_slice(&[0.8, 0.1, -0.3, 0.5]);
r.extend_from_slice(&[0.0, 0.0, 0.0, 0.0]);
l.extend_from_slice(&[0.0, 0.0, 0.0, 0.0]);
r.extend_from_slice(&[0.0, 0.0, 0.0, 0.0]);
let l_per_group = vec![l];
let r_per_group = vec![r];
let decisions = select_ms_used_for_pair(&l_per_group, &r_per_group, &sfb_offset, &[4]);
assert_eq!(decisions.len(), 1);
assert_eq!(decisions[0].len(), 4);
assert!(decisions[0][0], "L == R band must pick M/S");
assert!(decisions[0][1], "L == -R band must pick M/S");
assert!(!decisions[0][2], "L-only band must stay L/R");
assert!(!decisions[0][3], "zero-energy band must stay L/R");
}
#[test]
fn select_ms_used_for_pair_round_trips_into_extract_sap_abcd() {
let sfb_offset: [u16; 5] = [0, 4, 8, 12, 16];
let l = vec![
1.0f32, 0.5, -0.7, 0.3, 0.4, 0.9, -0.2, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ];
let r = vec![
1.0f32, 0.5, -0.7, 0.3, -0.4, -0.9, 0.2, -0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ];
let decisions = select_ms_used_for_pair(&[l], &[r], &sfb_offset, &[4]);
let info = build_chparam_info_ms_used(decisions.clone());
let coeffs = extract_sap_abcd(&info, &[4]);
assert_eq!(coeffs.abcd[0].len(), 4);
for sfb in [0, 1] {
let (a, b, c, d) = coeffs.abcd[0][sfb];
assert!((a - 1.0).abs() < 1e-6);
assert!((b - 1.0).abs() < 1e-6);
assert!((c - 1.0).abs() < 1e-6);
assert!((d - (-1.0)).abs() < 1e-6);
}
for sfb in [2, 3] {
let (a, b, c, d) = coeffs.abcd[0][sfb];
assert!((a - 1.0).abs() < 1e-6);
assert!(b.abs() < 1e-6);
assert!(c.abs() < 1e-6);
assert!((d - 1.0).abs() < 1e-6);
}
}
#[test]
fn select_ms_used_for_pair_respects_per_group_bound() {
let sfb_offset: [u16; 5] = [0, 2, 4, 6, 8];
let l = vec![1.0f32, 0.5, 0.4, 0.9, 0.8, 0.1, -0.3, 0.5];
let r = vec![1.0f32, 0.5, 0.4, 0.9, 0.8, 0.1, -0.3, 0.5];
let decisions = select_ms_used_for_pair(&[l], &[r], &sfb_offset, &[2]);
assert_eq!(decisions.len(), 1);
assert_eq!(decisions[0].len(), 2);
assert!(decisions[0][0]);
assert!(decisions[0][1]);
}
#[test]
fn select_ms_used_for_pair_multi_group_independent() {
let sfb_offset: [u16; 3] = [0, 4, 8];
let g0_l = vec![1.0f32, 0.5, -0.7, 0.3, 0.4, 0.9, -0.2, 0.6];
let g0_r = vec![1.0f32, 0.5, -0.7, 0.3, 0.4, 0.9, -0.2, 0.6];
let g1_l = vec![0.0f32; 8];
let g1_r = vec![0.0f32; 8];
let decisions = select_ms_used_for_pair(&[g0_l, g1_l], &[g0_r, g1_r], &sfb_offset, &[2, 2]);
assert_eq!(decisions.len(), 2);
assert!(decisions[0][0]);
assert!(decisions[0][1]);
assert!(!decisions[1][0]);
assert!(!decisions[1][1]);
}
#[test]
fn select_alpha_q_for_pair_least_squares_projection() {
let sfb_offset: [u16; 5] = [0, 4, 8, 12, 16];
let l = vec![
1.0f32, 0.5, -0.7, 0.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ];
let r = vec![
0.0f32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.2, -0.4, 0.6, 0.0, 0.0, 0.0, 0.0, ];
let (alpha_q, used) = select_alpha_q_for_pair(&[l], &[r], &sfb_offset, &[4]);
assert_eq!(alpha_q.len(), 1);
assert_eq!(alpha_q[0].len(), 4);
assert_eq!(alpha_q[0][0], 10);
assert!(used[0][0]);
assert_eq!(alpha_q[0][1], 10);
assert!(used[0][1]);
assert_eq!(alpha_q[0][2], -10);
assert!(used[0][2]);
assert_eq!(alpha_q[0][3], -10);
assert!(used[0][3]);
}
#[test]
fn select_alpha_q_for_pair_clears_flag_when_no_prediction_benefit() {
let sfb_offset: [u16; 5] = [0, 4, 8, 12, 16];
let l = vec![
1.0f32, 0.5, -0.7, 0.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ];
let r = vec![
1.0f32, 0.5, -0.7, 0.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ];
let (alpha_q, used) = select_alpha_q_for_pair(&[l], &[r], &sfb_offset, &[4]);
assert_eq!(alpha_q[0][0], 0);
assert!(!used[0][0]);
assert!(!used[0][1]);
assert_eq!(alpha_q[0][2], 0);
assert!(!used[0][2]);
assert!(!used[0][3]);
}
#[test]
fn select_alpha_q_for_pair_round_trips_through_sap_data_builder() {
let sfb_offset: [u16; 5] = [0, 4, 8, 12, 16];
let l = vec![
1.0f32, 0.5, -0.7, 0.3, 0.0, 0.0, 0.0, 0.0, 0.9, 0.4, -0.2, 0.5, 0.0, 0.0, 0.0, 0.0, ];
let r = vec![
0.0f32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.9, 0.4, -0.2, 0.5, 0.0, 0.0, 0.0, 0.0, ];
let (alpha_q, used) = select_alpha_q_for_pair(&[l], &[r], &sfb_offset, &[4]);
let info = build_chparam_info_sap_data_from_alpha_q(&alpha_q, &used, false, &[4]);
let coeffs = extract_sap_abcd(&info, &[4]);
assert_eq!(coeffs.abcd[0].len(), 4);
for sfb in [0usize, 1] {
let (a, b, c, d) = coeffs.abcd[0][sfb];
assert!((a - 2.0).abs() < 1e-5, "sfb {sfb} a");
assert!((b - 1.0).abs() < 1e-5, "sfb {sfb} b");
assert!(c.abs() < 1e-5, "sfb {sfb} c");
assert!((d - (-1.0)).abs() < 1e-5, "sfb {sfb} d");
}
for sfb in [2usize, 3] {
let (a, b, c, d) = coeffs.abcd[0][sfb];
assert!((a - 1.0).abs() < 1e-5, "sfb {sfb} a");
assert!(b.abs() < 1e-5, "sfb {sfb} b");
assert!(c.abs() < 1e-5, "sfb {sfb} c");
assert!((d - 1.0).abs() < 1e-5, "sfb {sfb} d");
}
}
#[test]
fn select_alpha_q_for_pair_clamps_to_codebook_range() {
let sfb_offset: [u16; 2] = [0, 4];
let l = vec![11.0f32, 5.5, -3.3, 2.2];
let r = vec![-9.0f32, -4.5, 2.7, -1.8];
let (alpha_q, used) = select_alpha_q_for_pair(&[l], &[r], &sfb_offset, &[1]);
assert_eq!(alpha_q[0][0], 60);
assert!(used[0][0]);
}
#[test]
fn select_alpha_q_for_pair_multi_group_independent() {
let sfb_offset: [u16; 3] = [0, 4, 8];
let g0_l = vec![1.0f32, 0.5, -0.7, 0.3, 0.0, 0.0, 0.0, 0.0];
let g0_r = vec![0.0f32; 8];
let g1_l = vec![0.0f32; 8];
let g1_r = vec![0.0f32; 8];
let (alpha_q, used) =
select_alpha_q_for_pair(&[g0_l, g1_l], &[g0_r, g1_r], &sfb_offset, &[2, 2]);
assert_eq!(alpha_q.len(), 2);
assert_eq!(alpha_q[0][0], 10);
assert!(used[0][0]);
assert!(used[0][1]);
assert!(!used[1][0]);
assert!(!used[1][1]);
}
}