use oxideav_core::bits::BitReader;
use oxideav_core::{Error, Result};
use crate::huffman::{
asf_hcb, ext_decode, huff_decode, split_qspec, CB_DIM, HCB_SCALEFAC_CW, HCB_SCALEFAC_LEN,
HCB_SNF_CW, HCB_SNF_LEN, UNSIGNED_CB,
};
use crate::tables::num_sfb_48;
#[derive(Debug, Default, Clone)]
pub struct AsfSections {
pub sect_cb: Vec<u8>,
pub sect_start: Vec<u16>,
pub sect_end: Vec<u16>,
pub sfb_cb: Vec<u8>,
pub num_sec: u32,
pub num_sec_lsf: u32,
}
pub fn parse_asf_section_data(
br: &mut BitReader<'_>,
transf_length_idx: u32,
transform_length: u32,
max_sfb: u32,
) -> Result<AsfSections> {
let (n_sect_bits, sect_esc_val) = if transf_length_idx <= 2 {
(3u32, 7u32)
} else {
(5u32, 31u32)
};
let num_sfb = num_sfb_48(transform_length)
.ok_or_else(|| Error::invalid("ac4: asf_section_data: unsupported transform_length"))?;
let mut out = AsfSections {
sfb_cb: vec![0u8; max_sfb as usize],
..Default::default()
};
let mut k: u32 = 0;
while k < max_sfb {
let sect_cb = br.read_u32(4)? as u8;
let mut sect_len: u32 = 0;
loop {
let incr = br.read_u32(n_sect_bits)?;
sect_len += incr;
if incr != sect_esc_val {
break;
}
}
sect_len += 1;
let sect_start = k;
let mut sect_end = k + sect_len;
if sect_start < num_sfb && sect_end > num_sfb {
out.sect_cb.push(sect_cb);
out.sect_start.push(sect_start as u16);
out.sect_end.push(num_sfb as u16);
out.num_sec_lsf = out.sect_cb.len() as u32;
out.sect_cb.push(sect_cb);
out.sect_start.push(num_sfb as u16);
out.sect_end.push(sect_end as u16);
for sfb in sect_start..sect_end {
if (sfb as usize) < out.sfb_cb.len() {
out.sfb_cb[sfb as usize] = sect_cb;
}
}
k += sect_len;
continue;
}
if sect_end > max_sfb {
sect_end = max_sfb;
}
out.sect_cb.push(sect_cb);
out.sect_start.push(sect_start as u16);
out.sect_end.push(sect_end as u16);
for sfb in sect_start..sect_end {
if (sfb as usize) < out.sfb_cb.len() {
out.sfb_cb[sfb as usize] = sect_cb;
}
}
k += sect_len;
}
out.num_sec = out.sect_cb.len() as u32;
if out.num_sec_lsf == 0 {
out.num_sec_lsf = out.num_sec;
}
Ok(out)
}
pub fn parse_asf_spectral_data(
br: &mut BitReader<'_>,
sections: &AsfSections,
sfb_offset: &[u16],
max_sfb: u32,
) -> Result<(Vec<i32>, Vec<u32>)> {
let end_bin = sfb_offset[max_sfb as usize] as usize;
let mut quant_spec = vec![0i32; end_bin];
let mut max_quant_idx = vec![0u32; max_sfb as usize];
for i in 0..sections.num_sec_lsf as usize {
let cb = sections.sect_cb[i] as u32;
if cb == 0 || cb > 11 {
continue;
}
let hcb =
asf_hcb(cb).ok_or_else(|| Error::invalid("ac4: asf_spectral_data: bad codebook"))?;
let dim = CB_DIM[cb as usize];
let unsig = UNSIGNED_CB[cb as usize];
let sect_start_line = sfb_offset[sections.sect_start[i] as usize] as usize;
let sect_end_line = sfb_offset[sections.sect_end[i] as usize] as usize;
let mut k = sect_start_line;
let mut tmp = [0i32; 4];
while k < sect_end_line {
let cb_idx = huff_decode(br, hcb.len, hcb.cw)?;
split_qspec(hcb, cb_idx, &mut tmp);
let step = dim as usize;
for t in 0..step {
let mut q = tmp[t];
if unsig && q != 0 {
let s = br.read_u32(1)?;
if s == 1 {
q = -q;
}
}
if cb == 11 && q.unsigned_abs() == 16 {
let ext = ext_decode(br)?;
q = if q.is_negative() {
-(ext as i32)
} else {
ext as i32
};
}
if k + t < quant_spec.len() {
quant_spec[k + t] = q;
}
}
k += step;
}
}
for sfb in 0..max_sfb as usize {
let a = sfb_offset[sfb] as usize;
let b = sfb_offset[sfb + 1] as usize;
let mut m: u32 = 0;
for &q in &quant_spec[a..b.min(quant_spec.len())] {
m = m.max(q.unsigned_abs());
}
max_quant_idx[sfb] = m;
}
Ok((quant_spec, max_quant_idx))
}
pub fn parse_asf_scalefac_data(
br: &mut BitReader<'_>,
sections: &AsfSections,
max_quant_idx: &[u32],
max_sfb: u32,
transform_length: u32,
) -> Result<Vec<f32>> {
let num_sfb_lsf =
num_sfb_48(transform_length).ok_or_else(|| Error::invalid("ac4: scalefac: bad tl"))?;
let reference_scale_factor = br.read_u32(8)?;
let mut sf_gain = vec![0.0_f32; max_sfb as usize];
let mut scale_factor: i32 = reference_scale_factor as i32;
let mut first_scf_found = false;
let max_sfb_eff = max_sfb.min(num_sfb_lsf);
for sfb in 0..max_sfb_eff as usize {
let cb = sections.sfb_cb[sfb];
if cb == 0 || max_quant_idx[sfb] == 0 {
continue;
}
if first_scf_found {
let cw_idx = huff_decode(br, HCB_SCALEFAC_LEN, HCB_SCALEFAC_CW)?;
scale_factor += cw_idx as i32 - 60;
} else {
first_scf_found = true;
}
let sf = scale_factor;
let exp = (sf as f32 - 100.0) * 0.25;
sf_gain[sfb] = 2.0_f32.powf(exp);
}
Ok(sf_gain)
}
pub fn parse_asf_snf_data(
br: &mut BitReader<'_>,
sections: &AsfSections,
max_quant_idx: &[u32],
max_sfb: u32,
transform_length: u32,
) -> Result<Option<Vec<i32>>> {
let b_snf_data_exists = br.read_bit()?;
if !b_snf_data_exists {
return Ok(None);
}
let num_sfb_lsf =
num_sfb_48(transform_length).ok_or_else(|| Error::invalid("ac4: snf: bad tl"))?;
let mut dpcm_snf = vec![0i32; max_sfb as usize];
let max_sfb_eff = max_sfb.min(num_sfb_lsf);
for sfb in 0..max_sfb_eff as usize {
let cb = sections.sfb_cb[sfb];
if cb == 0 || max_quant_idx[sfb] == 0 {
let idx = huff_decode(br, HCB_SNF_LEN, HCB_SNF_CW)?;
dpcm_snf[sfb] = idx as i32;
}
}
Ok(Some(dpcm_snf))
}
pub fn parse_asf_section_data_grouped(
br: &mut BitReader<'_>,
transf_length_idx_per_group: &[u32],
transform_length_per_group: &[u32],
max_sfb_per_group: &[u32],
) -> Result<Vec<AsfSections>> {
let n = transf_length_idx_per_group.len();
if n == 0 || transform_length_per_group.len() != n || max_sfb_per_group.len() != n {
return Err(Error::invalid(
"ac4: asf_section_data_grouped: inconsistent per-group slice lengths",
));
}
let mut out = Vec::with_capacity(n);
for g in 0..n {
out.push(parse_asf_section_data(
br,
transf_length_idx_per_group[g],
transform_length_per_group[g],
max_sfb_per_group[g],
)?);
}
Ok(out)
}
#[allow(clippy::type_complexity)]
pub fn parse_asf_spectral_data_grouped(
br: &mut BitReader<'_>,
sections_per_group: &[AsfSections],
sfb_offset_per_group: &[&[u16]],
max_sfb_per_group: &[u32],
) -> Result<(Vec<Vec<i32>>, Vec<Vec<u32>>)> {
let n = sections_per_group.len();
if sfb_offset_per_group.len() != n || max_sfb_per_group.len() != n {
return Err(Error::invalid(
"ac4: asf_spectral_data_grouped: inconsistent per-group slice lengths",
));
}
let mut q_per_g = Vec::with_capacity(n);
let mut mqi_per_g = Vec::with_capacity(n);
for g in 0..n {
let (q, mqi) = parse_asf_spectral_data(
br,
§ions_per_group[g],
sfb_offset_per_group[g],
max_sfb_per_group[g],
)?;
q_per_g.push(q);
mqi_per_g.push(mqi);
}
Ok((q_per_g, mqi_per_g))
}
pub fn parse_asf_scalefac_data_grouped(
br: &mut BitReader<'_>,
sections_per_group: &[AsfSections],
max_quant_idx_per_group: &[Vec<u32>],
max_sfb_per_group: &[u32],
transform_length_per_group: &[u32],
) -> Result<Vec<Vec<f32>>> {
let n = sections_per_group.len();
if max_quant_idx_per_group.len() != n
|| max_sfb_per_group.len() != n
|| transform_length_per_group.len() != n
{
return Err(Error::invalid(
"ac4: asf_scalefac_data_grouped: inconsistent per-group slice lengths",
));
}
let reference_scale_factor = br.read_u32(8)?;
let mut scale_factor: i32 = reference_scale_factor as i32;
let mut first_scf_found = false;
let mut out = Vec::with_capacity(n);
for g in 0..n {
let max_sfb = max_sfb_per_group[g];
let tl = transform_length_per_group[g];
let num_sfb_lsf = num_sfb_48(tl)
.ok_or_else(|| Error::invalid("ac4: scalefac_grouped: bad transform_length"))?;
let max_sfb_eff = max_sfb.min(num_sfb_lsf);
let mqi = &max_quant_idx_per_group[g];
let sections = §ions_per_group[g];
let mut sf_gain = vec![0.0_f32; max_sfb as usize];
for sfb in 0..max_sfb_eff as usize {
let cb = sections.sfb_cb[sfb];
if cb == 0 || mqi[sfb] == 0 {
continue;
}
if first_scf_found {
let cw_idx = huff_decode(br, HCB_SCALEFAC_LEN, HCB_SCALEFAC_CW)?;
scale_factor += cw_idx as i32 - 60;
} else {
first_scf_found = true;
}
let sf = scale_factor;
let exp = (sf as f32 - 100.0) * 0.25;
sf_gain[sfb] = 2.0_f32.powf(exp);
}
out.push(sf_gain);
}
Ok(out)
}
pub fn parse_asf_snf_data_grouped(
br: &mut BitReader<'_>,
sections_per_group: &[AsfSections],
max_quant_idx_per_group: &[Vec<u32>],
max_sfb_per_group: &[u32],
transform_length_per_group: &[u32],
) -> Result<Option<Vec<Vec<i32>>>> {
let n = sections_per_group.len();
if max_quant_idx_per_group.len() != n
|| max_sfb_per_group.len() != n
|| transform_length_per_group.len() != n
{
return Err(Error::invalid(
"ac4: asf_snf_data_grouped: inconsistent per-group slice lengths",
));
}
let b_snf_data_exists = br.read_bit()?;
if !b_snf_data_exists {
return Ok(None);
}
let mut out = Vec::with_capacity(n);
for g in 0..n {
let max_sfb = max_sfb_per_group[g];
let tl = transform_length_per_group[g];
let num_sfb_lsf = num_sfb_48(tl)
.ok_or_else(|| Error::invalid("ac4: snf_grouped: bad transform_length"))?;
let max_sfb_eff = max_sfb.min(num_sfb_lsf);
let mqi = &max_quant_idx_per_group[g];
let sections = §ions_per_group[g];
let mut dpcm_snf = vec![0i32; max_sfb as usize];
for sfb in 0..max_sfb_eff as usize {
let cb = sections.sfb_cb[sfb];
if cb == 0 || mqi[sfb] == 0 {
let idx = huff_decode(br, HCB_SNF_LEN, HCB_SNF_CW)?;
dpcm_snf[sfb] = idx as i32;
}
}
out.push(dpcm_snf);
}
Ok(Some(out))
}
pub fn dequantise_and_scale(
quant_spec: &[i32],
sf_gain: &[f32],
sfb_offset: &[u16],
max_sfb: u32,
) -> Vec<f32> {
let end_bin = sfb_offset[max_sfb as usize] as usize;
let mut scaled = vec![0.0_f32; end_bin];
for sfb in 0..max_sfb as usize {
let gain = sf_gain[sfb];
if gain == 0.0 {
continue;
}
let a = sfb_offset[sfb] as usize;
let b = sfb_offset[sfb + 1] as usize;
for k in a..b.min(end_bin) {
let q = quant_spec[k];
let rec = (q.unsigned_abs() as f32).powf(4.0 / 3.0);
let rec_signed = if q < 0 { -rec } else { rec };
scaled[k] = gain * rec_signed;
}
}
scaled
}
pub fn inject_snf_noise(
scaled: &mut [f32],
snf_data: &[i32],
sfb_offset: &[u16],
max_sfb: u32,
rng_state: &mut u32,
) {
for sfb in 0..max_sfb as usize {
let idx = match snf_data.get(sfb) {
Some(&v) if v > 0 => v as u32,
_ => continue,
};
let snf_gain: f32 = 2.0_f32.powf((idx as f32 * 1.5 - 84.0) / 4.0);
let a = sfb_offset.get(sfb).copied().unwrap_or(0) as usize;
let b = sfb_offset.get(sfb + 1).copied().unwrap_or(0) as usize;
for k in a..b.min(scaled.len()) {
if scaled[k] == 0.0 {
*rng_state = rng_state.wrapping_mul(69069).wrapping_add(1);
let sign: f32 = if (*rng_state >> 15) & 1 == 0 {
1.0
} else {
-1.0
};
scaled[k] = sign * snf_gain;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use oxideav_core::bits::BitWriter;
fn encode_scalefac_idx(bw: &mut BitWriter, idx: usize) {
bw.write_u32(
crate::huffman::HCB_SCALEFAC_CW[idx],
crate::huffman::HCB_SCALEFAC_LEN[idx] as u32,
);
}
#[test]
fn section_parser_single_cb_zero_covers_all_bands() {
let mut bw = BitWriter::new();
bw.write_u32(0, 4); bw.write_u32(4, 3); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let s = parse_asf_section_data(&mut br, 0, 256, 5).unwrap();
assert_eq!(s.num_sec, 1);
assert_eq!(s.sect_cb, vec![0u8]);
assert_eq!(s.sect_start, vec![0u16]);
assert_eq!(s.sect_end, vec![5u16]);
assert_eq!(s.sfb_cb, vec![0u8; 5]);
}
#[test]
fn section_parser_two_sections_escape() {
let mut bw = BitWriter::new();
bw.write_u32(3, 4);
bw.write_u32(2, 3);
bw.write_u32(5, 4);
bw.write_u32(6, 3);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let s = parse_asf_section_data(&mut br, 0, 256, 10).unwrap();
assert_eq!(s.num_sec, 2);
assert_eq!(s.sect_cb, vec![3u8, 5u8]);
assert_eq!(s.sect_start, vec![0u16, 3u16]);
assert_eq!(s.sect_end, vec![3u16, 10u16]);
}
#[test]
fn spectral_parser_all_zero_section() {
let sections = AsfSections {
sect_cb: vec![0u8],
sect_start: vec![0u16],
sect_end: vec![2u16],
sfb_cb: vec![0u8; 2],
num_sec: 1,
num_sec_lsf: 1,
};
let mut bw = BitWriter::new();
bw.write_u32(0, 8); let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let sfb_offset = crate::sfb_offset::sfb_offset_48(256).unwrap();
let (qspec, mqi) = parse_asf_spectral_data(&mut br, §ions, sfb_offset, 2).unwrap();
let end_bin = sfb_offset[2] as usize;
assert_eq!(qspec.len(), end_bin);
assert!(qspec.iter().all(|&v| v == 0));
assert_eq!(mqi, vec![0u32, 0u32]);
}
#[test]
fn scalefac_parser_yields_gain_when_mqi_nonzero() {
let sections = AsfSections {
sfb_cb: vec![5u8, 5u8, 5u8],
..AsfSections::default()
};
let mqi = vec![0u32, 3u32, 0u32];
let mut bw = BitWriter::new();
bw.write_u32(120, 8); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let gains = parse_asf_scalefac_data(&mut br, §ions, &mqi, 3, 256).unwrap();
assert_eq!(gains[0], 0.0);
assert!((gains[1] - 32.0).abs() < 1e-3);
assert_eq!(gains[2], 0.0);
}
#[test]
fn scalefac_parser_reads_dpcm_for_subsequent_bands() {
let sections = AsfSections {
sfb_cb: vec![5u8, 5u8, 5u8],
..AsfSections::default()
};
let mqi = vec![3u32, 3u32, 3u32];
let mut bw = BitWriter::new();
bw.write_u32(120, 8);
encode_scalefac_idx(&mut bw, 60);
encode_scalefac_idx(&mut bw, 63);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let gains = parse_asf_scalefac_data(&mut br, §ions, &mqi, 3, 256).unwrap();
assert!((gains[0] - 32.0).abs() < 1e-2);
assert!((gains[1] - 32.0).abs() < 1e-2);
assert!((gains[2] - 2.0_f32.powf((123.0 - 100.0) / 4.0)).abs() < 1e-2);
}
#[test]
fn dequantise_scales_correctly() {
let qspec = vec![0i32, 0, 2, -2, 0, 0, 0, 0];
let sfb_offset = [0u16, 2, 4, 8];
let sf_gain = vec![0.0_f32, 1.0, 0.5];
let out = dequantise_and_scale(&qspec, &sf_gain, &sfb_offset, 3);
assert_eq!(out[0], 0.0);
assert_eq!(out[1], 0.0);
let exp_mag = 2.0_f32.powf(4.0 / 3.0);
assert!((out[2] - exp_mag).abs() < 1e-4);
assert!((out[3] + exp_mag).abs() < 1e-4);
assert!(out[4..].iter().all(|&v| v == 0.0));
}
#[test]
fn section_grouped_walks_two_groups_all_zero_sections() {
let mut bw = BitWriter::new();
for _ in 0..2 {
bw.write_u32(0, 4); bw.write_u32(4, 3); }
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let s = parse_asf_section_data_grouped(
&mut br,
&[0u32, 0u32],
&[256u32, 256u32],
&[5u32, 5u32],
)
.unwrap();
assert_eq!(s.len(), 2);
for sg in &s {
assert_eq!(sg.num_sec, 1);
assert_eq!(sg.sect_cb, vec![0u8]);
assert_eq!(sg.sect_start, vec![0u16]);
assert_eq!(sg.sect_end, vec![5u16]);
}
}
#[test]
fn scalefac_grouped_consumes_single_reference_then_no_dpcm_when_mqi_all_zero() {
let sections = vec![
AsfSections {
sfb_cb: vec![5u8, 5u8],
..AsfSections::default()
},
AsfSections {
sfb_cb: vec![5u8, 5u8],
..AsfSections::default()
},
];
let mqi_per_g = vec![vec![0u32, 0u32], vec![0u32, 0u32]];
let mut bw = BitWriter::new();
bw.write_u32(120, 8); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let gains_per_g = parse_asf_scalefac_data_grouped(
&mut br,
§ions,
&mqi_per_g,
&[2u32, 2u32],
&[256u32, 256u32],
)
.unwrap();
assert_eq!(gains_per_g.len(), 2);
for g in &gains_per_g {
assert_eq!(g.len(), 2);
assert!(g.iter().all(|&v| v == 0.0));
}
}
#[test]
fn scalefac_grouped_first_scf_found_carries_across_groups() {
let sections = vec![
AsfSections {
sfb_cb: vec![5u8, 5u8],
..AsfSections::default()
},
AsfSections {
sfb_cb: vec![5u8, 5u8],
..AsfSections::default()
},
];
let mqi_per_g = vec![vec![3u32, 0u32], vec![3u32, 0u32]];
let mut bw = BitWriter::new();
bw.write_u32(120, 8); bw.write_u32(
crate::huffman::HCB_SCALEFAC_CW[60],
crate::huffman::HCB_SCALEFAC_LEN[60] as u32,
);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let gains_per_g = parse_asf_scalefac_data_grouped(
&mut br,
§ions,
&mqi_per_g,
&[2u32, 2u32],
&[256u32, 256u32],
)
.unwrap();
assert!((gains_per_g[0][0] - 32.0).abs() < 1e-2);
assert!((gains_per_g[1][0] - 32.0).abs() < 1e-2);
}
#[test]
fn snf_grouped_single_gate_at_head_returns_none_when_absent() {
let sections = vec![AsfSections::default(); 3];
let mqi_per_g = vec![vec![0u32; 0]; 3];
let mut bw = BitWriter::new();
bw.write_bit(false); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let snf =
parse_asf_snf_data_grouped(&mut br, §ions, &mqi_per_g, &[0u32; 3], &[256u32; 3])
.unwrap();
assert!(snf.is_none());
}
#[test]
fn inject_snf_fills_zero_bins_only() {
let sfb_offset: Vec<u16> = vec![0, 4, 8];
let snf_data: Vec<i32> = vec![56, 0]; let mut scaled = vec![0.0_f32, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let mut rng: u32 = 1;
inject_snf_noise(&mut scaled, &snf_data, &sfb_offset, 2, &mut rng);
assert_eq!(scaled[1], 1.0);
let snf_gain = 2.0_f32.powf((56.0 * 1.5 - 84.0) / 4.0);
assert!((scaled[0].abs() - snf_gain).abs() < 1e-5);
assert!((scaled[2].abs() - snf_gain).abs() < 1e-5);
assert!((scaled[3].abs() - snf_gain).abs() < 1e-5);
for &s in &scaled[4..] {
assert_eq!(s, 0.0, "sfb1 bins must remain zero");
}
}
#[test]
fn inject_snf_gain_formula_idx56() {
let snf_gain = 2.0_f32.powf((56.0_f32 * 1.5 - 84.0) / 4.0);
assert!((snf_gain - 1.0).abs() < 1e-5, "idx=56 gain must be 1.0");
}
#[test]
fn inject_snf_lcg_state_advances() {
let sfb_offset: Vec<u16> = vec![0, 2];
let snf_data: Vec<i32> = vec![56];
let mut scaled = vec![0.0_f32; 2];
let mut rng: u32 = 0x1234_5678;
inject_snf_noise(&mut scaled, &snf_data, &sfb_offset, 1, &mut rng);
assert!((scaled[0].abs() - 1.0).abs() < 1e-5);
assert!((scaled[1].abs() - 1.0).abs() < 1e-5);
let mut expected_rng = 0x1234_5678u32;
expected_rng = expected_rng.wrapping_mul(69069).wrapping_add(1);
let sign0: f32 = if (expected_rng >> 15) & 1 == 0 {
1.0
} else {
-1.0
};
assert_eq!(scaled[0], sign0 * 1.0);
expected_rng = expected_rng.wrapping_mul(69069).wrapping_add(1);
let sign1: f32 = if (expected_rng >> 15) & 1 == 0 {
1.0
} else {
-1.0
};
assert_eq!(scaled[1], sign1 * 1.0);
}
}