use oxideav_core::bits::BitWriter;
use crate::asf_data::AsfSections;
use crate::huffman::{
asf_hcb, Hcb, CB_DIM, HCB_SCALEFAC_CW, HCB_SCALEFAC_LEN, HCB_SNF_CW, HCB_SNF_LEN, UNSIGNED_CB,
};
pub const CB_QMAX: [u32; 12] = [0, 1, 1, 2, 2, 4, 4, 7, 7, 12, 12, 16];
pub const ALL_CB_IDS: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
pub fn quantise_coeff(coeff: f32, sf: i32) -> i32 {
let sf_gain = 2.0_f32.powf((sf as f32 - 100.0) * 0.25);
if sf_gain == 0.0 || !sf_gain.is_finite() {
return 0;
}
let mag = (coeff.abs() / sf_gain).powf(0.75);
let q = mag.round() as i32;
if coeff < 0.0 {
-q
} else {
q
}
}
pub fn pick_scalefactor_for_band(coeffs: &[f32], q_max: u32) -> (i32, Vec<i32>) {
if coeffs.is_empty() {
return (100, Vec::new());
}
let max_abs = coeffs.iter().fold(0.0_f32, |a, &c| a.max(c.abs()));
if max_abs <= 1e-12 {
return (100, vec![0i32; coeffs.len()]);
}
let q_max_43 = (q_max as f32).powf(4.0 / 3.0);
let sf_gain_min = max_abs / q_max_43;
let sf_f = 100.0 + 4.0 * sf_gain_min.log2();
let sf = sf_f.ceil() as i32;
let sf = sf.clamp(0, 255);
let mut q = vec![0i32; coeffs.len()];
for (i, &c) in coeffs.iter().enumerate() {
let qi = quantise_coeff(c, sf);
q[i] = qi.clamp(-(q_max as i32), q_max as i32);
}
(sf, q)
}
pub fn encode_pair(bw: &mut BitWriter, hcb: &Hcb, q: &[i32]) {
let dim = hcb.dim as usize;
debug_assert!(q.len() >= dim);
let cb_idx = if dim == 4 {
let i1 = (q[0] + hcb.cb_off) as u32 * hcb.cb_mod3;
let i2 = (q[1] + hcb.cb_off) as u32 * hcb.cb_mod2;
let i3 = (q[2] + hcb.cb_off) as u32 * hcb.cb_mod;
let i4 = (q[3] + hcb.cb_off) as u32;
i1 + i2 + i3 + i4
} else {
let i1 = (q[0] + hcb.cb_off) as u32 * hcb.cb_mod;
let i2 = (q[1] + hcb.cb_off) as u32;
i1 + i2
} as usize;
debug_assert!(
cb_idx < hcb.cw.len(),
"encode_pair: cb_idx {cb_idx} out of range for cb (len={})",
hcb.cw.len()
);
bw.write_u32(hcb.cw[cb_idx], hcb.len[cb_idx] as u32);
if hcb.unsigned {
for &qi in &q[..dim] {
if qi != 0 {
bw.write_u32(if qi < 0 { 1 } else { 0 }, 1);
}
}
}
}
pub 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);
}
pub fn single_section(max_sfb: u32, cb: u8) -> AsfSections {
AsfSections {
sect_cb: vec![cb],
sect_start: vec![0],
sect_end: vec![max_sfb as u16],
sfb_cb: vec![cb; max_sfb as usize],
num_sec: 1,
num_sec_lsf: 1,
}
}
pub fn bit_cost_for_band(hcb: &Hcb, q: &[i32], cb_id: u8) -> u32 {
let dim = hcb.dim as usize;
let unsig = hcb.unsigned;
let mut bits: u32 = 0;
let mut k = 0usize;
while k + dim <= q.len() {
let mut sym = [0i32; 4];
for t in 0..dim {
let qi = q[k + t];
let mag = qi.unsigned_abs() as i32;
let inline_mag = if cb_id == 11 && mag >= 16 { 16 } else { mag };
sym[t] = if unsig {
inline_mag
} else if qi < 0 {
-inline_mag
} else {
inline_mag
};
}
let cb_idx = if dim == 4 {
let i1 = (sym[0] + hcb.cb_off) as u32 * hcb.cb_mod3;
let i2 = (sym[1] + hcb.cb_off) as u32 * hcb.cb_mod2;
let i3 = (sym[2] + hcb.cb_off) as u32 * hcb.cb_mod;
let i4 = (sym[3] + hcb.cb_off) as u32;
(i1 + i2 + i3 + i4) as usize
} else {
let i1 = (sym[0] + hcb.cb_off) as u32 * hcb.cb_mod;
let i2 = (sym[1] + hcb.cb_off) as u32;
(i1 + i2) as usize
};
if cb_idx >= hcb.cw.len() {
return u32::MAX;
}
bits += hcb.len[cb_idx] as u32;
if unsig {
for t in 0..dim {
let qi = q[k + t];
let mag = qi.unsigned_abs() as i32;
let inline_mag = if cb_id == 11 && mag >= 16 { 16 } else { mag };
if inline_mag != 0 {
bits += 1;
}
}
}
if cb_id == 11 {
for &qi in &q[k..k + dim] {
let mag = qi.unsigned_abs();
if mag >= 16 {
let mut n_ext: u32 = 0;
while (1u32 << (n_ext + 4)) <= mag {
n_ext += 1;
}
n_ext = n_ext.saturating_sub(1);
bits += (n_ext + 1) + (n_ext + 4);
}
}
}
k += dim;
}
bits
}
fn quantise_band_for_cb(coeffs: &[f32], sf: i32, cb_id: u8) -> Vec<i32> {
let q_max = CB_QMAX[cb_id as usize] as i32;
let mut q = vec![0i32; coeffs.len()];
for (i, &c) in coeffs.iter().enumerate() {
let qi = quantise_coeff(c, sf);
q[i] = if cb_id == 11 {
qi.clamp(-256, 256)
} else {
qi.clamp(-q_max, q_max)
};
}
q
}
pub fn pick_best_codebook_for_band(coeffs: &[f32]) -> (u8, i32, Vec<i32>, u32) {
pick_best_codebook_for_band_with_q_target(coeffs, 12.0)
}
pub fn pick_best_codebook_for_band_with_q_target(
coeffs: &[f32],
q_target: f32,
) -> (u8, i32, Vec<i32>, u32) {
if coeffs.is_empty() {
return (0, 100, Vec::new(), 0);
}
let max_abs = coeffs.iter().fold(0.0_f32, |a, &c| a.max(c.abs()));
if max_abs <= 1e-12 {
return (0, 100, vec![0i32; coeffs.len()], 0);
}
let q_target_43 = q_target.powf(4.0 / 3.0);
let sf_gain_min = max_abs / q_target_43;
let sf_anchor_f = 100.0 + 4.0 * sf_gain_min.log2();
let sf_anchor = (sf_anchor_f.ceil() as i32).clamp(0, 255);
let natural_q = quantise_band_for_cb(coeffs, sf_anchor, 11);
let natural_max: u32 = natural_q
.iter()
.map(|&qi| qi.unsigned_abs())
.max()
.unwrap_or(0);
if natural_max == 0 {
return (0, 100, natural_q, 0);
}
let mut best_cb: u8 = 5;
let mut best_q: Vec<i32> = natural_q.clone();
let mut best_cost: u32 = u32::MAX;
for &cb_id in ALL_CB_IDS {
let hcb = match asf_hcb(cb_id as u32) {
Some(h) => h,
None => continue,
};
let dim = hcb.dim as usize;
if natural_q.len() % dim != 0 {
continue;
}
let q_max_cb = CB_QMAX[cb_id as usize];
if cb_id != 11 && natural_max > q_max_cb {
continue;
}
let cost = bit_cost_for_band(hcb, &natural_q, cb_id);
if cost < best_cost {
best_cost = cost;
best_cb = cb_id;
best_q = natural_q.clone();
}
}
(best_cb, sf_anchor, best_q, best_cost)
}
pub fn dp_optimise_sections(cost_band_cb: &[[u32; 12]], max_sections: u32) -> Vec<(u32, u32, u8)> {
let n = cost_band_cb.len();
if n == 0 {
return Vec::new();
}
let inf: u64 = u64::MAX / 4;
let mut best: Vec<u64> = vec![inf; n];
let mut pred: Vec<(i64, u8)> = vec![(-1, 0u8); n];
let mut sections_count: Vec<u32> = vec![0u32; n];
let big: u64 = u64::MAX / 2;
let mut prefix: Vec<[u64; 12]> = vec![[0u64; 12]; n + 1];
for cb in 0..12usize {
let mut acc: u64 = 0;
let mut blocked = false;
prefix[0][cb] = 0;
for b in 0..n {
if blocked {
prefix[b + 1][cb] = big;
continue;
}
let c = cost_band_cb[b][cb];
if c == u32::MAX {
blocked = true;
prefix[b + 1][cb] = big;
} else {
acc += c as u64;
prefix[b + 1][cb] = acc;
}
}
}
let band_sum = |start: usize, i: usize, cb: usize| -> Option<u64> {
if prefix[i + 1][cb] != big && prefix[start][cb] != big {
return Some(prefix[i + 1][cb] - prefix[start][cb]);
}
let mut s: u64 = 0;
for row in cost_band_cb.iter().take(i + 1).skip(start) {
let c = row[cb];
if c == u32::MAX {
return None;
}
s += c as u64;
}
Some(s)
};
for i in 0..n {
for start in 0..=i {
let len = (i - start + 1) as u32;
let overhead = section_overhead_bits(len) as u64;
let (prior_cost, prior_sections) = if start == 0 {
(0u64, 0u32)
} else {
if best[start - 1] >= inf {
continue;
}
(best[start - 1], sections_count[start - 1])
};
if prior_sections + 1 > max_sections {
continue;
}
for cb in 0..12usize {
let bs = match band_sum(start, i, cb) {
Some(v) => v,
None => continue,
};
let total = prior_cost + bs + overhead;
if total < best[i] {
best[i] = total;
pred[i] = (if start == 0 { -1 } else { (start - 1) as i64 }, cb as u8);
sections_count[i] = prior_sections + 1;
}
}
}
}
let mut sections: Vec<(u32, u32, u8)> = Vec::new();
let mut i: i64 = (n - 1) as i64;
while i >= 0 {
let (prev, cb) = pred[i as usize];
let start = (prev + 1) as u32;
let end = (i as u32) + 1;
sections.push((start, end, cb));
i = prev;
}
sections.reverse();
sections
}
#[inline]
pub fn section_overhead_bits(len: u32) -> u32 {
let base = len.saturating_sub(1);
let k = base / 7;
4 + 3 * (k + 1)
}
pub fn build_band_codebook_cost_table(natural_q_per_band: &[Vec<i32>]) -> Vec<[u32; 12]> {
let mut table: Vec<[u32; 12]> = Vec::with_capacity(natural_q_per_band.len());
for q in natural_q_per_band {
let mut row = [u32::MAX; 12];
let all_zero = q.iter().all(|&qi| qi == 0);
if all_zero {
row[0] = 0;
}
if q.is_empty() {
row[0] = 0;
table.push(row);
continue;
}
let nat_max: u32 = q.iter().map(|&qi| qi.unsigned_abs()).max().unwrap_or(0);
for cb_id in 1u8..=11 {
let hcb = match asf_hcb(cb_id as u32) {
Some(h) => h,
None => continue,
};
let dim = hcb.dim as usize;
if q.len() % dim != 0 {
continue;
}
if cb_id != 11 && nat_max > CB_QMAX[cb_id as usize] {
continue;
}
row[cb_id as usize] = bit_cost_for_band(hcb, q, cb_id);
}
table.push(row);
}
table
}
pub fn build_sections_from_dp(sections: &[(u32, u32, u8)], max_sfb: u32) -> AsfSections {
let mut sect_cb: Vec<u8> = Vec::with_capacity(sections.len());
let mut sect_start: Vec<u16> = Vec::with_capacity(sections.len());
let mut sect_end: Vec<u16> = Vec::with_capacity(sections.len());
let mut sfb_cb = vec![0u8; max_sfb as usize];
for &(s, e, cb) in sections {
sect_cb.push(cb);
sect_start.push(s as u16);
sect_end.push(e as u16);
for sfb in s..e {
if (sfb as usize) < sfb_cb.len() {
sfb_cb[sfb as usize] = cb;
}
}
}
let n = sect_cb.len() as u32;
AsfSections {
sect_cb,
sect_start,
sect_end,
sfb_cb,
num_sec: n,
num_sec_lsf: n,
}
}
pub fn compute_snf_dpcm_for_zero_quant_bands(
coeffs: &[f32],
sfb_offset: &[u16],
max_sfb: u32,
sfb_cb: &[u8],
max_quant_idx: &[u32],
) -> Option<Vec<i32>> {
let mut out = vec![0i32; max_sfb as usize];
let mut any_set = false;
for sfb in 0..max_sfb as usize {
let eligible = sfb_cb[sfb] == 0 || max_quant_idx[sfb] == 0;
if !eligible {
continue;
}
let a = sfb_offset[sfb] as usize;
let b = sfb_offset[sfb + 1] as usize;
if b <= a {
continue;
}
let band = &coeffs[a..b.min(coeffs.len())];
let n = band.len();
if n == 0 {
continue;
}
let mut sum_sq: f64 = 0.0;
for &c in band {
sum_sq += (c as f64) * (c as f64);
}
if sum_sq <= 1e-30 {
continue;
}
let rms = (sum_sq / n as f64).sqrt() as f32;
if rms <= 0.0 {
continue;
}
let idx_f = (4.0 * rms.log2() + 84.0) / 1.5;
let idx = (idx_f.round() as i32).clamp(1, (HCB_SNF_LEN.len() as i32) - 1);
out[sfb] = idx;
any_set = true;
}
if any_set {
Some(out)
} else {
None
}
}
pub fn write_snf_data(
bw: &mut BitWriter,
snf: Option<&[i32]>,
sfb_cb: &[u8],
max_quant_idx: &[u32],
max_sfb: u32,
) {
let snf = match snf {
Some(s) => s,
None => {
bw.write_u32(0, 1);
return;
}
};
bw.write_bit(true);
for sfb in 0..max_sfb as usize {
let cb = sfb_cb[sfb];
if cb != 0 && max_quant_idx[sfb] != 0 {
continue;
}
let idx = snf.get(sfb).copied().unwrap_or(0);
let idx_u = idx.clamp(0, (HCB_SNF_LEN.len() as i32) - 1) as usize;
bw.write_u32(HCB_SNF_CW[idx_u], HCB_SNF_LEN[idx_u] as u32);
}
}
pub fn build_sections_from_per_band_cb(per_band_cb: &[u8], max_sfb: u32) -> AsfSections {
debug_assert_eq!(per_band_cb.len(), max_sfb as usize);
let mut sect_cb: Vec<u8> = Vec::new();
let mut sect_start: Vec<u16> = Vec::new();
let mut sect_end: Vec<u16> = Vec::new();
let mut sfb: u32 = 0;
while sfb < max_sfb {
let cb = per_band_cb[sfb as usize];
let start = sfb;
while sfb < max_sfb && per_band_cb[sfb as usize] == cb {
sfb += 1;
}
sect_cb.push(cb);
sect_start.push(start as u16);
sect_end.push(sfb as u16);
}
let n = sect_cb.len() as u32;
AsfSections {
sect_cb,
sect_start,
sect_end,
sfb_cb: per_band_cb.to_vec(),
num_sec: n,
num_sec_lsf: n,
}
}
pub fn write_scalefac_data(
bw: &mut BitWriter,
sf_per_band: &[i32],
sfb_cb: &[u8],
max_quant_idx: &[u32],
max_sfb: u32,
) {
let mut reference: i32 = 100;
let mut found = false;
for sfb in 0..max_sfb as usize {
let cb = sfb_cb[sfb];
if cb != 0 && max_quant_idx[sfb] > 0 {
reference = sf_per_band[sfb].clamp(0, 255);
found = true;
break;
}
}
bw.write_u32(reference as u32, 8);
let mut running = reference;
let mut first = false;
for sfb in 0..max_sfb as usize {
let cb = sfb_cb[sfb];
if cb == 0 || max_quant_idx[sfb] == 0 {
continue;
}
if !first {
first = true;
if !found {
running = sf_per_band[sfb].clamp(0, 255);
}
continue;
}
let delta = sf_per_band[sfb].clamp(0, 255) - running;
let idx = (delta + 60).clamp(0, HCB_SCALEFAC_LEN.len() as i32 - 1) as usize;
bw.write_u32(HCB_SCALEFAC_CW[idx], HCB_SCALEFAC_LEN[idx] as u32);
running += idx as i32 - 60;
}
}
pub fn write_spectral_data_single_section(
bw: &mut BitWriter,
qspec: &[i32],
sfb_offset: &[u16],
max_sfb: u32,
cb: u32,
) {
let hcb = asf_hcb(cb).expect("encode_asf: invalid codebook id");
let dim = CB_DIM[cb as usize] as usize;
let _unsig = UNSIGNED_CB[cb as usize];
let end_bin = sfb_offset[max_sfb as usize] as usize;
debug_assert!(qspec.len() >= end_bin);
let mut k = 0usize;
while k + dim <= end_bin {
encode_pair(bw, hcb, &qspec[k..k + dim]);
k += dim;
}
}
pub fn write_section_data(bw: &mut BitWriter, sections: &AsfSections) {
for i in 0..sections.num_sec_lsf as usize {
let cb = sections.sect_cb[i];
let start = sections.sect_start[i] as u32;
let end = sections.sect_end[i] as u32;
let len = end - start;
bw.write_u32(cb as u32, 4);
write_sect_len_incr(bw, len, 3, 7);
}
}
pub fn write_chparam_info(
bw: &mut BitWriter,
info: &crate::asf::ChparamInfo,
max_sfb_per_group: &[u32],
) {
let sap_mode = info.sap_mode & 0b11;
bw.write_u32(sap_mode, 2);
match crate::asf::SapMode::from_u32(sap_mode) {
crate::asf::SapMode::None | crate::asf::SapMode::Reserved => {}
crate::asf::SapMode::MsUsed => {
for (g, &m) in max_sfb_per_group.iter().enumerate() {
let row = info.ms_used.get(g);
for sfb in 0..m as usize {
let bit = row.and_then(|r| r.get(sfb)).copied().unwrap_or(false);
bw.write_bit(bit);
}
}
}
crate::asf::SapMode::SapData => {
if let Some(sd) = info.sap_data.as_ref() {
write_sap_data(bw, sd, max_sfb_per_group);
} else {
let default = crate::asf::SapData::default();
write_sap_data(bw, &default, max_sfb_per_group);
}
}
}
}
pub fn write_sap_data(bw: &mut BitWriter, sd: &crate::asf::SapData, max_sfb_per_group: &[u32]) {
bw.write_bit(sd.sap_coeff_all);
let num_groups = max_sfb_per_group.len();
if !sd.sap_coeff_all {
for (g, &m) in max_sfb_per_group.iter().enumerate() {
let row = sd.sap_coeff_used.get(g);
let mut sfb = 0usize;
while sfb < m as usize {
let f = row.and_then(|r| r.get(sfb)).copied().unwrap_or(false);
bw.write_bit(f);
sfb += 2;
}
}
}
if num_groups != 1 {
bw.write_bit(sd.delta_code_time);
}
for (g, &m) in max_sfb_per_group.iter().enumerate() {
let pair_row = sd.sap_coeff_used.get(g);
let dpcm_row = sd.dpcm_alpha_q.get(g);
let mut sfb = 0usize;
while sfb < m as usize {
let pair_flag = if sd.sap_coeff_all {
true
} else {
pair_row.and_then(|r| r.get(sfb)).copied().unwrap_or(false)
};
if pair_flag {
let delta = dpcm_row.and_then(|r| r.get(sfb)).copied().unwrap_or(0);
let idx = (delta + 60).clamp(0, HCB_SCALEFAC_LEN.len() as i32 - 1) as usize;
bw.write_u32(HCB_SCALEFAC_CW[idx], HCB_SCALEFAC_LEN[idx] as u32);
}
sfb += 2;
}
}
}
pub fn write_spectral_data_sections(
bw: &mut BitWriter,
qspec: &[i32],
sfb_offset: &[u16],
sections: &AsfSections,
) {
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).expect("encode_asf: bad section cb id");
let dim = CB_DIM[cb as usize] as usize;
let unsig = UNSIGNED_CB[cb as usize];
let start_bin = sfb_offset[sections.sect_start[i] as usize] as usize;
let end_bin = sfb_offset[sections.sect_end[i] as usize] as usize;
let mut k = start_bin;
while k + dim <= end_bin {
let mut sym = [0i32; 4];
for t in 0..dim {
let qi = qspec[k + t];
let mag = qi.unsigned_abs() as i32;
let inline_mag = if cb == 11 && mag >= 16 { 16 } else { mag };
sym[t] = if unsig {
inline_mag
} else if qi < 0 {
-inline_mag
} else {
inline_mag
};
}
let cb_idx = if dim == 4 {
let i1 = (sym[0] + hcb.cb_off) as u32 * hcb.cb_mod3;
let i2 = (sym[1] + hcb.cb_off) as u32 * hcb.cb_mod2;
let i3 = (sym[2] + hcb.cb_off) as u32 * hcb.cb_mod;
let i4 = (sym[3] + hcb.cb_off) as u32;
(i1 + i2 + i3 + i4) as usize
} else {
let i1 = (sym[0] + hcb.cb_off) as u32 * hcb.cb_mod;
let i2 = (sym[1] + hcb.cb_off) as u32;
(i1 + i2) as usize
};
debug_assert!(
cb_idx < hcb.cw.len(),
"spec_data: cb_idx {cb_idx} out of range"
);
bw.write_u32(hcb.cw[cb_idx], hcb.len[cb_idx] as u32);
if unsig {
for t in 0..dim {
let qi = qspec[k + t];
let mag = qi.unsigned_abs() as i32;
let inline_mag = if cb == 11 && mag >= 16 { 16 } else { mag };
if inline_mag != 0 {
bw.write_u32(if qi < 0 { 1 } else { 0 }, 1);
}
}
}
if cb == 11 {
for t in 0..dim {
let mag = qspec[k + t].unsigned_abs();
if mag >= 16 {
let mut n_ext: u32 = 0;
while (1u32 << (n_ext + 4)) <= mag {
n_ext += 1;
}
n_ext = n_ext.saturating_sub(1);
let bits = n_ext + 4;
let ext = mag - (1u32 << bits);
for _ in 0..n_ext {
bw.write_u32(1, 1);
}
bw.write_u32(0, 1);
bw.write_u32(ext, bits);
}
}
}
k += dim;
}
}
}
pub fn build_mono_simple_asf_body_from_pcm_spectrum(
transform_length: u32,
max_sfb: u32,
coeffs: &[f32],
pad_target_bytes: usize,
) -> Vec<u8> {
let sfbo = crate::sfb_offset::sfb_offset_48(transform_length)
.expect("encoder: unsupported transform_length");
let end_bin = sfbo[max_sfb as usize] as usize;
let mut qspec = vec![0i32; end_bin];
let mut sf_per_band = vec![100i32; max_sfb as usize];
let mut max_quant_idx = vec![0u32; max_sfb as usize];
let mut natural_q_per_band: Vec<Vec<i32>> = Vec::with_capacity(max_sfb as usize);
for sfb in 0..max_sfb as usize {
let a = sfbo[sfb] as usize;
let b = sfbo[sfb + 1] as usize;
let band = &coeffs[a..b.min(coeffs.len())];
let (_cb_picked, sf, q, _cost) = pick_best_codebook_for_band(band);
sf_per_band[sfb] = sf;
let mut max_q: u32 = 0;
for (i, &qi) in q.iter().enumerate() {
qspec[a + i] = qi;
max_q = max_q.max(qi.unsigned_abs());
}
max_quant_idx[sfb] = max_q;
natural_q_per_band.push(q);
}
let cost_table = build_band_codebook_cost_table(&natural_q_per_band);
let dp_sections = dp_optimise_sections(&cost_table, 16);
let sections = build_sections_from_dp(&dp_sections, max_sfb);
let snf = compute_snf_dpcm_for_zero_quant_bands(
coeffs,
sfbo,
max_sfb,
§ions.sfb_cb,
&max_quant_idx,
);
let mut bw = BitWriter::new();
let audio_size = pad_target_bytes as u32;
bw.write_u32(audio_size & 0x7FFF, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 1);
bw.write_u32(0, 1);
bw.write_bit(true);
let (n_msfb_bits, _, _) =
crate::tables::n_msfb_bits_48(transform_length).expect("encoder: bad tl");
bw.write_u32(max_sfb, n_msfb_bits);
write_section_data(&mut bw, §ions);
write_spectral_data_sections(&mut bw, &qspec, sfbo, §ions);
write_scalefac_data(
&mut bw,
&sf_per_band,
§ions.sfb_cb,
&max_quant_idx,
max_sfb,
);
write_snf_data(
&mut bw,
snf.as_deref(),
§ions.sfb_cb,
&max_quant_idx,
max_sfb,
);
bw.align_to_byte();
while bw.byte_len() < pad_target_bytes {
bw.write_u32(0, 8);
}
let mut bytes = bw.finish();
if bytes.len() > pad_target_bytes {
bytes.truncate(pad_target_bytes);
}
bytes
}
pub fn build_stereo_simple_asf_split_body_from_pcm_spectra(
transform_length: u32,
max_sfb: u32,
coeffs_l: &[f32],
coeffs_r: &[f32],
pad_target_bytes: usize,
) -> Vec<u8> {
let sfbo = crate::sfb_offset::sfb_offset_48(transform_length)
.expect("encoder: unsupported transform_length");
let end_bin = sfbo[max_sfb as usize] as usize;
type StereoChannelAnalysis = (Vec<i32>, Vec<i32>, Vec<u32>, AsfSections, Option<Vec<i32>>);
let prepare_channel = |coeffs: &[f32]| -> StereoChannelAnalysis {
let mut qspec = vec![0i32; end_bin];
let mut sf_per_band = vec![100i32; max_sfb as usize];
let mut max_quant_idx = vec![0u32; max_sfb as usize];
let mut natural_q_per_band: Vec<Vec<i32>> = Vec::with_capacity(max_sfb as usize);
for sfb in 0..max_sfb as usize {
let a = sfbo[sfb] as usize;
let b = sfbo[sfb + 1] as usize;
let band = &coeffs[a..b.min(coeffs.len())];
let (_cb_picked, sf, q, _cost) = pick_best_codebook_for_band(band);
sf_per_band[sfb] = sf;
let mut max_q: u32 = 0;
for (i, &qi) in q.iter().enumerate() {
qspec[a + i] = qi;
max_q = max_q.max(qi.unsigned_abs());
}
max_quant_idx[sfb] = max_q;
natural_q_per_band.push(q);
}
let cost_table = build_band_codebook_cost_table(&natural_q_per_band);
let dp_sections = dp_optimise_sections(&cost_table, 16);
let sections = build_sections_from_dp(&dp_sections, max_sfb);
let snf = compute_snf_dpcm_for_zero_quant_bands(
coeffs,
sfbo,
max_sfb,
§ions.sfb_cb,
&max_quant_idx,
);
(qspec, sf_per_band, max_quant_idx, sections, snf)
};
let (qspec_l, sf_l, mqi_l, sections_l, snf_l) = prepare_channel(coeffs_l);
let (qspec_r, sf_r, mqi_r, sections_r, snf_r) = prepare_channel(coeffs_r);
let mut bw = BitWriter::new();
let audio_size = pad_target_bytes as u32;
bw.write_u32(audio_size & 0x7FFF, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 2);
bw.write_bit(false);
let (n_msfb_bits, _, _) =
crate::tables::n_msfb_bits_48(transform_length).expect("encoder: bad tl");
bw.write_u32(0, 1); bw.write_bit(true); bw.write_u32(max_sfb, n_msfb_bits); bw.write_u32(0, 1); bw.write_bit(true); bw.write_u32(max_sfb, n_msfb_bits);
write_section_data(&mut bw, §ions_l);
write_spectral_data_sections(&mut bw, &qspec_l, sfbo, §ions_l);
write_scalefac_data(&mut bw, &sf_l, §ions_l.sfb_cb, &mqi_l, max_sfb);
write_snf_data(
&mut bw,
snf_l.as_deref(),
§ions_l.sfb_cb,
&mqi_l,
max_sfb,
);
write_section_data(&mut bw, §ions_r);
write_spectral_data_sections(&mut bw, &qspec_r, sfbo, §ions_r);
write_scalefac_data(&mut bw, &sf_r, §ions_r.sfb_cb, &mqi_r, max_sfb);
write_snf_data(
&mut bw,
snf_r.as_deref(),
§ions_r.sfb_cb,
&mqi_r,
max_sfb,
);
bw.align_to_byte();
while bw.byte_len() < pad_target_bytes {
bw.write_u32(0, 8);
}
let mut bytes = bw.finish();
if bytes.len() > pad_target_bytes {
bytes.truncate(pad_target_bytes);
}
bytes
}
pub fn average_per_sfb_correlation(
transform_length: u32,
max_sfb: u32,
coeffs_l: &[f32],
coeffs_r: &[f32],
) -> f32 {
let sfbo = match crate::sfb_offset::sfb_offset_48(transform_length) {
Some(t) => t,
None => return 0.0,
};
let mut acc: f64 = 0.0;
let mut weight_total: f64 = 0.0;
for sfb in 0..max_sfb as usize {
let a = sfbo[sfb] as usize;
let b = sfbo[sfb + 1] as usize;
let bmax = b.min(coeffs_l.len()).min(coeffs_r.len());
if bmax <= a {
continue;
}
let mut s_lr: f64 = 0.0;
let mut s_ll: f64 = 0.0;
let mut s_rr: f64 = 0.0;
for k in a..bmax {
let l = coeffs_l[k] as f64;
let r = coeffs_r[k] as f64;
s_lr += l * r;
s_ll += l * l;
s_rr += r * r;
}
if s_ll <= 1e-20 || s_rr <= 1e-20 {
continue;
}
let denom = (s_ll * s_rr).sqrt();
if denom <= 1e-20 {
continue;
}
let rho = (s_lr / denom).clamp(-1.0, 1.0);
let w = denom;
acc += rho * w;
weight_total += w;
}
if weight_total <= 1e-20 {
return 0.0;
}
(acc / weight_total) as f32
}
pub fn build_stereo_simple_asf_joint_body_from_pcm_spectra(
transform_length: u32,
max_sfb: u32,
coeffs_l: &[f32],
coeffs_r: &[f32],
pad_target_bytes: usize,
) -> Vec<u8> {
let sfbo = crate::sfb_offset::sfb_offset_48(transform_length)
.expect("encoder: unsupported transform_length");
let end_bin = sfbo[max_sfb as usize] as usize;
let n_coeffs = coeffs_l.len().min(coeffs_r.len()).min(end_bin);
let mut spec_m = vec![0.0_f32; end_bin];
let mut spec_s = vec![0.0_f32; end_bin];
for k in 0..n_coeffs {
let l = coeffs_l[k];
let r = coeffs_r[k];
spec_m[k] = 0.5 * (l + r);
spec_s[k] = 0.5 * (l - r);
}
let mut q_pri = vec![0i32; end_bin];
let mut q_sec = vec![0i32; end_bin];
let mut sf_per_band = vec![100i32; max_sfb as usize];
let mut max_quant_idx = vec![0u32; max_sfb as usize];
let mut natural_q_pri_per_band: Vec<Vec<i32>> = Vec::with_capacity(max_sfb as usize);
let mut natural_q_sec_per_band: Vec<Vec<i32>> = Vec::with_capacity(max_sfb as usize);
let mut ms_used_per_band = vec![false; max_sfb as usize];
let frame_e_m: f64 = spec_m[..end_bin].iter().map(|&v| (v as f64).powi(2)).sum();
let frame_e_s: f64 = spec_s[..end_bin].iter().map(|&v| (v as f64).powi(2)).sum();
let frame_ratio = if frame_e_m > 1e-20 {
(frame_e_s / (frame_e_m + frame_e_s)) as f32
} else {
1.0
};
let matched_channels = frame_ratio < 0.15;
let frame_q_target_m: f32 = if matched_channels {
let max_q = 16.0_f32;
let min_q = 12.0_f32;
max_q - (max_q - min_q) * (frame_ratio / 0.15).min(1.0)
} else {
12.0
};
for sfb in 0..max_sfb as usize {
let a = sfbo[sfb] as usize;
let b = sfbo[sfb + 1] as usize;
let bmax = b.min(end_bin);
let band_l = &coeffs_l[a..bmax.min(coeffs_l.len())];
let band_r = &coeffs_r[a..bmax.min(coeffs_r.len())];
let band_m = &spec_m[a..bmax];
let band_s = &spec_s[a..bmax];
let (cb_l, sf_l, ql, cost_l) = pick_best_codebook_for_band(band_l);
let (cb_r, sf_r, qr, cost_r) = pick_best_codebook_for_band(band_r);
let (cb_m_base, sf_m_base, qm_base, cost_m_base) = pick_best_codebook_for_band(band_m);
let (cb_s, sf_s, qs, cost_s) = pick_best_codebook_for_band(band_s);
let ms_total = cost_m_base.saturating_add(cost_s);
let lr_total = cost_l.saturating_add(cost_r);
let use_ms = ms_total < lr_total;
ms_used_per_band[sfb] = use_ms;
let (cb_m, sf_m, qm, cost_m) = if use_ms && matched_channels {
pick_best_codebook_for_band_with_q_target(band_m, frame_q_target_m)
} else {
(cb_m_base, sf_m_base, qm_base, cost_m_base)
};
let _ = (cb_l, cb_r, cb_m, cb_s, ql, qr, qm, qs, cost_m);
let (sf_a, sf_b) = if use_ms { (sf_m, sf_s) } else { (sf_l, sf_r) };
let a_silent = if use_ms {
cost_m_base == 0 && cb_m_base == 0
} else {
cost_l == 0 && cb_l == 0
};
let b_silent = if use_ms {
cost_s == 0 && cb_s == 0
} else {
cost_r == 0 && cb_r == 0
};
let sf = match (a_silent, b_silent) {
(true, true) => 100,
(true, false) => sf_b,
(false, true) => sf_a,
(false, false) => sf_a.max(sf_b),
};
sf_per_band[sfb] = sf;
let coeff_a: Vec<f32> = if use_ms {
band_m.to_vec()
} else {
band_l.to_vec()
};
let coeff_b: Vec<f32> = if use_ms {
band_s.to_vec()
} else {
band_r.to_vec()
};
let q_a_re = quantise_band_for_cb(&coeff_a, sf, 11);
let q_b_re = quantise_band_for_cb(&coeff_b, sf, 11);
let mut mq: u32 = 0;
for (i, &qi) in q_a_re.iter().enumerate() {
if a + i < q_pri.len() {
q_pri[a + i] = qi;
}
mq = mq.max(qi.unsigned_abs());
}
for (i, &qi) in q_b_re.iter().enumerate() {
if a + i < q_sec.len() {
q_sec[a + i] = qi;
}
mq = mq.max(qi.unsigned_abs());
}
max_quant_idx[sfb] = mq;
natural_q_pri_per_band.push(q_a_re);
natural_q_sec_per_band.push(q_b_re);
}
let cost_table_pri = build_band_codebook_cost_table(&natural_q_pri_per_band);
let cost_table_sec = build_band_codebook_cost_table(&natural_q_sec_per_band);
let mut cost_table_joint: Vec<[u32; 12]> = Vec::with_capacity(natural_q_pri_per_band.len());
for sfb in 0..max_sfb as usize {
let mut row = [u32::MAX; 12];
let pri_zero = natural_q_pri_per_band[sfb].iter().all(|&qi| qi == 0);
let sec_zero = natural_q_sec_per_band[sfb].iter().all(|&qi| qi == 0);
if pri_zero && sec_zero {
row[0] = 0;
}
for cb_id in 1u8..=11 {
let p = cost_table_pri[sfb][cb_id as usize];
let s = cost_table_sec[sfb][cb_id as usize];
if p == u32::MAX || s == u32::MAX {
continue;
}
row[cb_id as usize] = p.saturating_add(s);
}
cost_table_joint.push(row);
}
let dp_sections = dp_optimise_sections(&cost_table_joint, 16);
let sections = build_sections_from_dp(&dp_sections, max_sfb);
let snf = compute_snf_dpcm_for_zero_quant_bands(
&spec_m,
sfbo,
max_sfb,
§ions.sfb_cb,
&max_quant_idx,
);
let mut bw = BitWriter::new();
let audio_size = pad_target_bytes as u32;
bw.write_u32(audio_size & 0x7FFF, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 2);
bw.write_bit(true);
bw.write_bit(true); let (n_msfb_bits, _, _) =
crate::tables::n_msfb_bits_48(transform_length).expect("encoder: bad tl");
bw.write_u32(max_sfb, n_msfb_bits);
write_section_data(&mut bw, §ions);
write_spectral_data_sections(&mut bw, &q_pri, sfbo, §ions);
write_spectral_data_sections(&mut bw, &q_sec, sfbo, §ions);
write_scalefac_data(
&mut bw,
&sf_per_band,
§ions.sfb_cb,
&max_quant_idx,
max_sfb,
);
for sfb in 0..max_sfb as usize {
let cb = sections.sfb_cb[sfb];
if cb == 0 || max_quant_idx[sfb] == 0 {
continue;
}
bw.write_bit(ms_used_per_band[sfb]);
}
write_snf_data(
&mut bw,
snf.as_deref(),
§ions.sfb_cb,
&max_quant_idx,
max_sfb,
);
bw.align_to_byte();
while bw.byte_len() < pad_target_bytes {
bw.write_u32(0, 8);
}
let mut bytes = bw.finish();
if bytes.len() > pad_target_bytes {
bytes.truncate(pad_target_bytes);
}
bytes
}
pub fn build_5_0_simple_asf_body_from_pcm_spectra(
transform_length: u32,
max_sfb: u32,
coeffs_per_channel: &[&[f32]; 5],
pad_target_bytes: usize,
) -> Vec<u8> {
let sfbo = crate::sfb_offset::sfb_offset_48(transform_length)
.expect("encoder: unsupported transform_length");
let end_bin = sfbo[max_sfb as usize] as usize;
type FiveZeroChannelAnalysis = (Vec<i32>, Vec<i32>, Vec<u32>, AsfSections, Option<Vec<i32>>);
let prepare_channel = |coeffs: &[f32]| -> FiveZeroChannelAnalysis {
let mut qspec = vec![0i32; end_bin];
let mut sf_per_band = vec![100i32; max_sfb as usize];
let mut max_quant_idx = vec![0u32; max_sfb as usize];
let mut natural_q_per_band: Vec<Vec<i32>> = Vec::with_capacity(max_sfb as usize);
for sfb in 0..max_sfb as usize {
let a = sfbo[sfb] as usize;
let b = sfbo[sfb + 1] as usize;
let band = &coeffs[a..b.min(coeffs.len())];
let (_cb_picked, sf, q, _cost) = pick_best_codebook_for_band(band);
sf_per_band[sfb] = sf;
let mut max_q: u32 = 0;
for (i, &qi) in q.iter().enumerate() {
qspec[a + i] = qi;
max_q = max_q.max(qi.unsigned_abs());
}
max_quant_idx[sfb] = max_q;
natural_q_per_band.push(q);
}
let cost_table = build_band_codebook_cost_table(&natural_q_per_band);
let dp_sections = dp_optimise_sections(&cost_table, 16);
let sections = build_sections_from_dp(&dp_sections, max_sfb);
let snf = compute_snf_dpcm_for_zero_quant_bands(
coeffs,
sfbo,
max_sfb,
§ions.sfb_cb,
&max_quant_idx,
);
(qspec, sf_per_band, max_quant_idx, sections, snf)
};
let analyses: [FiveZeroChannelAnalysis; 5] = [
prepare_channel(coeffs_per_channel[0]),
prepare_channel(coeffs_per_channel[1]),
prepare_channel(coeffs_per_channel[2]),
prepare_channel(coeffs_per_channel[3]),
prepare_channel(coeffs_per_channel[4]),
];
let mut bw = BitWriter::new();
let audio_size = pad_target_bytes as u32;
bw.write_u32(audio_size & 0x7FFF, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 3);
bw.write_u32(3, 2);
bw.write_bit(true);
let (n_msfb_bits, _, _) =
crate::tables::n_msfb_bits_48(transform_length).expect("encoder: bad tl");
bw.write_u32(max_sfb, n_msfb_bits);
bw.write_u32(0, 4);
for _ in 0..5 {
bw.write_u32(0, 2);
}
for analysis in &analyses {
let (qspec, sf_per_band, max_quant_idx, sections, snf) = analysis;
write_section_data(&mut bw, sections);
write_spectral_data_sections(&mut bw, qspec, sfbo, sections);
write_scalefac_data(
&mut bw,
sf_per_band,
§ions.sfb_cb,
max_quant_idx,
max_sfb,
);
write_snf_data(
&mut bw,
snf.as_deref(),
§ions.sfb_cb,
max_quant_idx,
max_sfb,
);
}
bw.align_to_byte();
while bw.byte_len() < pad_target_bytes {
bw.write_u32(0, 8);
}
let mut bytes = bw.finish();
if bytes.len() > pad_target_bytes {
bytes.truncate(pad_target_bytes);
}
bytes
}
pub fn build_5_1_simple_asf_body_from_pcm_spectra(
transform_length: u32,
max_sfb: u32,
max_sfb_lfe: u32,
coeffs_per_channel: &[&[f32]; 6],
pad_target_bytes: usize,
) -> Vec<u8> {
let sfbo = crate::sfb_offset::sfb_offset_48(transform_length)
.expect("encoder: unsupported transform_length");
let end_bin = sfbo[max_sfb as usize] as usize;
let end_bin_lfe = sfbo[max_sfb_lfe as usize] as usize;
type FiveOneChannelAnalysis = (Vec<i32>, Vec<i32>, Vec<u32>, AsfSections, Option<Vec<i32>>);
let prepare_channel = |coeffs: &[f32], max_sfb_use: u32| -> FiveOneChannelAnalysis {
let local_end = sfbo[max_sfb_use as usize] as usize;
let mut qspec = vec![0i32; local_end];
let mut sf_per_band = vec![100i32; max_sfb_use as usize];
let mut max_quant_idx = vec![0u32; max_sfb_use as usize];
let mut natural_q_per_band: Vec<Vec<i32>> = Vec::with_capacity(max_sfb_use as usize);
for sfb in 0..max_sfb_use as usize {
let a = sfbo[sfb] as usize;
let b = sfbo[sfb + 1] as usize;
let band = &coeffs[a..b.min(coeffs.len())];
let (_cb_picked, sf, q, _cost) = pick_best_codebook_for_band(band);
sf_per_band[sfb] = sf;
let mut max_q: u32 = 0;
for (i, &qi) in q.iter().enumerate() {
qspec[a + i] = qi;
max_q = max_q.max(qi.unsigned_abs());
}
max_quant_idx[sfb] = max_q;
natural_q_per_band.push(q);
}
let cost_table = build_band_codebook_cost_table(&natural_q_per_band);
let dp_sections = dp_optimise_sections(&cost_table, 16);
let sections = build_sections_from_dp(&dp_sections, max_sfb_use);
let snf = compute_snf_dpcm_for_zero_quant_bands(
coeffs,
sfbo,
max_sfb_use,
§ions.sfb_cb,
&max_quant_idx,
);
(qspec, sf_per_band, max_quant_idx, sections, snf)
};
let analyses: [FiveOneChannelAnalysis; 5] = [
prepare_channel(coeffs_per_channel[0], max_sfb),
prepare_channel(coeffs_per_channel[1], max_sfb),
prepare_channel(coeffs_per_channel[2], max_sfb),
prepare_channel(coeffs_per_channel[3], max_sfb),
prepare_channel(coeffs_per_channel[4], max_sfb),
];
let lfe_analysis = prepare_channel(coeffs_per_channel[5], max_sfb_lfe);
let _ = end_bin; let _ = end_bin_lfe;
let mut bw = BitWriter::new();
let audio_size = pad_target_bytes as u32;
bw.write_u32(audio_size & 0x7FFF, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 3);
bw.write_bit(true);
let (_n_msfb_bits, _, n_msfbl_bits) =
crate::tables::n_msfb_bits_48(transform_length).expect("encoder: bad tl");
assert!(
n_msfbl_bits > 0,
"encoder: LFE not permitted at transform_length = {transform_length}"
);
let n_msfbl_cap = (1u32 << n_msfbl_bits) - 1;
let max_sfb_lfe_clamped = max_sfb_lfe.min(n_msfbl_cap);
bw.write_u32(max_sfb_lfe_clamped, n_msfbl_bits);
{
let (qspec, sf_per_band, max_quant_idx, sections, snf) = &lfe_analysis;
write_section_data(&mut bw, sections);
write_spectral_data_sections(&mut bw, qspec, sfbo, sections);
write_scalefac_data(
&mut bw,
sf_per_band,
§ions.sfb_cb,
max_quant_idx,
max_sfb_lfe_clamped,
);
write_snf_data(
&mut bw,
snf.as_deref(),
§ions.sfb_cb,
max_quant_idx,
max_sfb_lfe_clamped,
);
}
bw.write_u32(3, 2);
bw.write_bit(true);
let (n_msfb_bits, _, _) =
crate::tables::n_msfb_bits_48(transform_length).expect("encoder: bad tl");
bw.write_u32(max_sfb, n_msfb_bits);
bw.write_u32(0, 4);
for _ in 0..5 {
bw.write_u32(0, 2);
}
for analysis in &analyses {
let (qspec, sf_per_band, max_quant_idx, sections, snf) = analysis;
write_section_data(&mut bw, sections);
write_spectral_data_sections(&mut bw, qspec, sfbo, sections);
write_scalefac_data(
&mut bw,
sf_per_band,
§ions.sfb_cb,
max_quant_idx,
max_sfb,
);
write_snf_data(
&mut bw,
snf.as_deref(),
§ions.sfb_cb,
max_quant_idx,
max_sfb,
);
}
bw.align_to_byte();
while bw.byte_len() < pad_target_bytes {
bw.write_u32(0, 8);
}
let mut bytes = bw.finish();
if bytes.len() > pad_target_bytes {
bytes.truncate(pad_target_bytes);
}
bytes
}
pub fn build_7_0_simple_asf_body_from_pcm_spectra(
transform_length: u32,
max_sfb: u32,
max_sfb_add: u32,
coeffs_per_channel: &[&[f32]; 7],
pad_target_bytes: usize,
) -> Vec<u8> {
let sfbo = crate::sfb_offset::sfb_offset_48(transform_length)
.expect("encoder: unsupported transform_length");
type SevenZeroChannelAnalysis = (Vec<i32>, Vec<i32>, Vec<u32>, AsfSections, Option<Vec<i32>>);
let prepare_channel = |coeffs: &[f32], max_sfb_use: u32| -> SevenZeroChannelAnalysis {
let local_end = sfbo[max_sfb_use as usize] as usize;
let mut qspec = vec![0i32; local_end];
let mut sf_per_band = vec![100i32; max_sfb_use as usize];
let mut max_quant_idx = vec![0u32; max_sfb_use as usize];
let mut natural_q_per_band: Vec<Vec<i32>> = Vec::with_capacity(max_sfb_use as usize);
for sfb in 0..max_sfb_use as usize {
let a = sfbo[sfb] as usize;
let b = sfbo[sfb + 1] as usize;
let band = &coeffs[a..b.min(coeffs.len())];
let (_cb_picked, sf, q, _cost) = pick_best_codebook_for_band(band);
sf_per_band[sfb] = sf;
let mut max_q: u32 = 0;
for (i, &qi) in q.iter().enumerate() {
qspec[a + i] = qi;
max_q = max_q.max(qi.unsigned_abs());
}
max_quant_idx[sfb] = max_q;
natural_q_per_band.push(q);
}
let cost_table = build_band_codebook_cost_table(&natural_q_per_band);
let dp_sections = dp_optimise_sections(&cost_table, 16);
let sections = build_sections_from_dp(&dp_sections, max_sfb_use);
let snf = compute_snf_dpcm_for_zero_quant_bands(
coeffs,
sfbo,
max_sfb_use,
§ions.sfb_cb,
&max_quant_idx,
);
(qspec, sf_per_band, max_quant_idx, sections, snf)
};
let analyses_five: [SevenZeroChannelAnalysis; 5] = [
prepare_channel(coeffs_per_channel[0], max_sfb),
prepare_channel(coeffs_per_channel[1], max_sfb),
prepare_channel(coeffs_per_channel[2], max_sfb),
prepare_channel(coeffs_per_channel[3], max_sfb),
prepare_channel(coeffs_per_channel[4], max_sfb),
];
let analyses_add: [SevenZeroChannelAnalysis; 2] = [
prepare_channel(coeffs_per_channel[5], max_sfb_add),
prepare_channel(coeffs_per_channel[6], max_sfb_add),
];
let mut bw = BitWriter::new();
let audio_size = pad_target_bytes as u32;
bw.write_u32(audio_size & 0x7FFF, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 2);
bw.write_u32(3, 2);
let (n_msfb_bits, _, _) =
crate::tables::n_msfb_bits_48(transform_length).expect("encoder: bad tl");
bw.write_bit(true);
bw.write_u32(max_sfb, n_msfb_bits);
bw.write_u32(0, 4);
for _ in 0..5 {
bw.write_u32(0, 2);
}
for analysis in &analyses_five {
let (qspec, sf_per_band, max_quant_idx, sections, snf) = analysis;
write_section_data(&mut bw, sections);
write_spectral_data_sections(&mut bw, qspec, sfbo, sections);
write_scalefac_data(
&mut bw,
sf_per_band,
§ions.sfb_cb,
max_quant_idx,
max_sfb,
);
write_snf_data(
&mut bw,
snf.as_deref(),
§ions.sfb_cb,
max_quant_idx,
max_sfb,
);
}
bw.write_bit(false);
bw.write_bit(true); bw.write_u32(max_sfb_add, n_msfb_bits);
bw.write_u32(0, 2);
for analysis in &analyses_add {
let (qspec, sf_per_band, max_quant_idx, sections, snf) = analysis;
write_section_data(&mut bw, sections);
write_spectral_data_sections(&mut bw, qspec, sfbo, sections);
write_scalefac_data(
&mut bw,
sf_per_band,
§ions.sfb_cb,
max_quant_idx,
max_sfb_add,
);
write_snf_data(
&mut bw,
snf.as_deref(),
§ions.sfb_cb,
max_quant_idx,
max_sfb_add,
);
}
bw.align_to_byte();
while bw.byte_len() < pad_target_bytes {
bw.write_u32(0, 8);
}
let mut bytes = bw.finish();
if bytes.len() > pad_target_bytes {
bytes.truncate(pad_target_bytes);
}
bytes
}
pub fn build_7_1_simple_asf_body_from_pcm_spectra(
transform_length: u32,
max_sfb: u32,
max_sfb_add: u32,
max_sfb_lfe: u32,
coeffs_per_channel: &[&[f32]; 8],
pad_target_bytes: usize,
) -> Vec<u8> {
let sfbo = crate::sfb_offset::sfb_offset_48(transform_length)
.expect("encoder: unsupported transform_length");
type SevenOneChannelAnalysis = (Vec<i32>, Vec<i32>, Vec<u32>, AsfSections, Option<Vec<i32>>);
let prepare_channel = |coeffs: &[f32], max_sfb_use: u32| -> SevenOneChannelAnalysis {
let local_end = sfbo[max_sfb_use as usize] as usize;
let mut qspec = vec![0i32; local_end];
let mut sf_per_band = vec![100i32; max_sfb_use as usize];
let mut max_quant_idx = vec![0u32; max_sfb_use as usize];
let mut natural_q_per_band: Vec<Vec<i32>> = Vec::with_capacity(max_sfb_use as usize);
for sfb in 0..max_sfb_use as usize {
let a = sfbo[sfb] as usize;
let b = sfbo[sfb + 1] as usize;
let band = &coeffs[a..b.min(coeffs.len())];
let (_cb_picked, sf, q, _cost) = pick_best_codebook_for_band(band);
sf_per_band[sfb] = sf;
let mut max_q: u32 = 0;
for (i, &qi) in q.iter().enumerate() {
qspec[a + i] = qi;
max_q = max_q.max(qi.unsigned_abs());
}
max_quant_idx[sfb] = max_q;
natural_q_per_band.push(q);
}
let cost_table = build_band_codebook_cost_table(&natural_q_per_band);
let dp_sections = dp_optimise_sections(&cost_table, 16);
let sections = build_sections_from_dp(&dp_sections, max_sfb_use);
let snf = compute_snf_dpcm_for_zero_quant_bands(
coeffs,
sfbo,
max_sfb_use,
§ions.sfb_cb,
&max_quant_idx,
);
(qspec, sf_per_band, max_quant_idx, sections, snf)
};
let analyses_five: [SevenOneChannelAnalysis; 5] = [
prepare_channel(coeffs_per_channel[0], max_sfb),
prepare_channel(coeffs_per_channel[1], max_sfb),
prepare_channel(coeffs_per_channel[2], max_sfb),
prepare_channel(coeffs_per_channel[3], max_sfb),
prepare_channel(coeffs_per_channel[4], max_sfb),
];
let analyses_add: [SevenOneChannelAnalysis; 2] = [
prepare_channel(coeffs_per_channel[5], max_sfb_add),
prepare_channel(coeffs_per_channel[6], max_sfb_add),
];
let lfe_analysis = prepare_channel(coeffs_per_channel[7], max_sfb_lfe);
let mut bw = BitWriter::new();
let audio_size = pad_target_bytes as u32;
bw.write_u32(audio_size & 0x7FFF, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 2);
bw.write_bit(true);
let (n_msfb_bits, _, n_msfbl_bits) =
crate::tables::n_msfb_bits_48(transform_length).expect("encoder: bad tl");
assert!(
n_msfbl_bits > 0,
"encoder: LFE not permitted at transform_length = {transform_length}"
);
let n_msfbl_cap = (1u32 << n_msfbl_bits) - 1;
let max_sfb_lfe_clamped = max_sfb_lfe.min(n_msfbl_cap);
bw.write_u32(max_sfb_lfe_clamped, n_msfbl_bits);
{
let (qspec, sf_per_band, max_quant_idx, sections, snf) = &lfe_analysis;
write_section_data(&mut bw, sections);
write_spectral_data_sections(&mut bw, qspec, sfbo, sections);
write_scalefac_data(
&mut bw,
sf_per_band,
§ions.sfb_cb,
max_quant_idx,
max_sfb_lfe_clamped,
);
write_snf_data(
&mut bw,
snf.as_deref(),
§ions.sfb_cb,
max_quant_idx,
max_sfb_lfe_clamped,
);
}
bw.write_u32(3, 2);
bw.write_bit(true);
bw.write_u32(max_sfb, n_msfb_bits);
bw.write_u32(0, 4);
for _ in 0..5 {
bw.write_u32(0, 2);
}
for analysis in &analyses_five {
let (qspec, sf_per_band, max_quant_idx, sections, snf) = analysis;
write_section_data(&mut bw, sections);
write_spectral_data_sections(&mut bw, qspec, sfbo, sections);
write_scalefac_data(
&mut bw,
sf_per_band,
§ions.sfb_cb,
max_quant_idx,
max_sfb,
);
write_snf_data(
&mut bw,
snf.as_deref(),
§ions.sfb_cb,
max_quant_idx,
max_sfb,
);
}
bw.write_bit(false);
bw.write_bit(true); bw.write_u32(max_sfb_add, n_msfb_bits);
bw.write_u32(0, 2);
for analysis in &analyses_add {
let (qspec, sf_per_band, max_quant_idx, sections, snf) = analysis;
write_section_data(&mut bw, sections);
write_spectral_data_sections(&mut bw, qspec, sfbo, sections);
write_scalefac_data(
&mut bw,
sf_per_band,
§ions.sfb_cb,
max_quant_idx,
max_sfb_add,
);
write_snf_data(
&mut bw,
snf.as_deref(),
§ions.sfb_cb,
max_quant_idx,
max_sfb_add,
);
}
bw.align_to_byte();
while bw.byte_len() < pad_target_bytes {
bw.write_u32(0, 8);
}
let mut bytes = bw.finish();
if bytes.len() > pad_target_bytes {
bytes.truncate(pad_target_bytes);
}
bytes
}
pub fn measure_greedy_vs_dp_bits(
transform_length: u32,
max_sfb: u32,
coeffs: &[f32],
) -> (u64, u64) {
let sfbo = crate::sfb_offset::sfb_offset_48(transform_length)
.expect("encoder: unsupported transform_length");
let mut natural_q_per_band: Vec<Vec<i32>> = Vec::with_capacity(max_sfb as usize);
let mut per_band_cb = vec![0u8; max_sfb as usize];
for sfb in 0..max_sfb as usize {
let a = sfbo[sfb] as usize;
let b = sfbo[sfb + 1] as usize;
let band = &coeffs[a..b.min(coeffs.len())];
let (cb, _sf, q, _cost) = pick_best_codebook_for_band(band);
per_band_cb[sfb] = cb;
natural_q_per_band.push(q);
}
let cost_table = build_band_codebook_cost_table(&natural_q_per_band);
let greedy = build_sections_from_per_band_cb(&per_band_cb, max_sfb);
let mut greedy_bits: u64 = 0;
for s in 0..greedy.num_sec as usize {
let cb = greedy.sect_cb[s] as usize;
let start = greedy.sect_start[s] as u32;
let end = greedy.sect_end[s] as u32;
greedy_bits += section_overhead_bits(end - start) as u64;
for b in start..end {
let c = cost_table[b as usize][cb];
greedy_bits += if c == u32::MAX { 0 } else { c as u64 };
}
}
let dp_sections = dp_optimise_sections(&cost_table, 16);
let mut dp_bits: u64 = 0;
for &(start, end, cb) in &dp_sections {
dp_bits += section_overhead_bits(end - start) as u64;
for b in start..end {
let c = cost_table[b as usize][cb as usize];
dp_bits += if c == u32::MAX { 0 } else { c as u64 };
}
}
(greedy_bits, dp_bits)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::asf_data::{
dequantise_and_scale, parse_asf_scalefac_data, parse_asf_section_data,
parse_asf_spectral_data,
};
use crate::huffman::HCB5;
use oxideav_core::bits::BitReader;
#[test]
fn quantise_then_dequantise_roundtrips_small_value() {
let q = quantise_coeff(32.0, 120);
assert_eq!(q, 1);
let q0 = quantise_coeff(0.0, 120);
assert_eq!(q0, 0);
let qn = quantise_coeff(-32.0, 120);
assert_eq!(qn, -1);
}
#[test]
fn pick_scalefactor_keeps_quantised_within_q_max() {
let band = vec![10.0_f32, -50.0, 0.0, 100.0];
let (_sf, q) = pick_scalefactor_for_band(&band, 4);
for &qi in &q {
assert!(qi.abs() <= 4, "q exceeded q_max=4: got {qi}");
}
}
#[test]
fn encode_pair_dim2_signed_roundtrips() {
let mut bw = BitWriter::new();
let q = [1i32, 0i32];
encode_pair(&mut bw, &HCB5, &q);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let cb_idx = crate::huffman::huff_decode(&mut br, HCB5.len, HCB5.cw).unwrap();
let mut out = [0i32; 4];
crate::huffman::split_qspec(&HCB5, cb_idx, &mut out);
assert_eq!(out[0], 1);
assert_eq!(out[1], 0);
}
#[test]
fn encode_pair_dim2_unsigned_emits_sign_bit() {
let mut bw = BitWriter::new();
let q = [2i32, 0i32];
let hcb = asf_hcb(7).unwrap();
encode_pair(&mut bw, hcb, &q);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let cb_idx = crate::huffman::huff_decode(&mut br, hcb.len, hcb.cw).unwrap();
let sign = br.read_u32(1).unwrap();
let mut out = [0i32; 4];
crate::huffman::split_qspec(hcb, cb_idx, &mut out);
let q1 = if sign == 1 { -out[0] } else { out[0] };
assert_eq!(q1, 2);
}
#[test]
fn build_body_roundtrips_through_asf_parser() {
let tl = 256u32;
let max_sfb = 5u32;
let sfbo = crate::sfb_offset::sfb_offset_48(tl).unwrap();
let end_bin = sfbo[max_sfb as usize] as usize;
let mut coeffs = vec![0.0_f32; end_bin];
coeffs[4] = 32.0; coeffs[5] = -32.0;
let body = build_mono_simple_asf_body_from_pcm_spectrum(tl, max_sfb, &coeffs, 80);
assert_eq!(body.len(), 80);
let mut br = BitReader::new(&body);
let _audio_size = br.read_u32(15).unwrap();
let _more = br.read_bit().unwrap();
br.align_to_byte();
let mono_mode = br.read_u32(1).unwrap();
assert_eq!(mono_mode, 0);
let frontend = br.read_u32(1).unwrap();
assert_eq!(frontend, 0);
let b_long = br.read_bit().unwrap();
assert!(b_long);
let (n_msfb_bits, _, _) = crate::tables::n_msfb_bits_48(tl).unwrap();
let parsed_max_sfb = br.read_u32(n_msfb_bits).unwrap();
assert_eq!(parsed_max_sfb, max_sfb);
let sections = parse_asf_section_data(&mut br, 0, tl, max_sfb).unwrap();
assert!(
sections.num_sec >= 1,
"expected at least one section, got {}",
sections.num_sec
);
let (qspec, mqi) = parse_asf_spectral_data(&mut br, §ions, sfbo, max_sfb).unwrap();
assert!(mqi[1] > 0, "expected non-zero max_quant_idx in band 1");
let sf_gain = parse_asf_scalefac_data(&mut br, §ions, &mqi, max_sfb, tl).unwrap();
let scaled = dequantise_and_scale(&qspec, &sf_gain, sfbo, max_sfb);
assert!(
scaled[4] > 0.0,
"expected positive reconstruction at bin 4, got {}",
scaled[4]
);
assert!(
scaled[5] < 0.0,
"expected negative reconstruction at bin 5, got {}",
scaled[5]
);
}
#[test]
fn optimiser_picks_real_codebook_for_low_magnitude_band() {
let band = vec![32.0_f32, -32.0, 0.0, 0.0];
let (cb, _sf, q, cost) = pick_best_codebook_for_band(&band);
assert!(
(1..=11).contains(&cb),
"expected a real codebook id, got {cb}"
);
assert!(cost < u32::MAX, "cost overflow");
assert_eq!(q.len(), band.len());
assert!(q[0] > 0, "expected positive q[0], got {}", q[0]);
assert!(q[1] < 0, "expected negative q[1], got {}", q[1]);
}
#[test]
fn optimiser_natural_max_at_q_target_12() {
let cases: Vec<(Vec<f32>, u32)> = vec![
(vec![1.0_f32, -1.0, 0.0, 0.0], 1),
(vec![5.0, -3.0, 1.0, 0.0], 1),
(vec![32.0, -32.0, 0.0, 0.0], 1),
(vec![100.0, -50.0, 10.0, 0.0], 1),
];
for (band, min_max) in cases {
let (_cb, _sf, q, cost) = pick_best_codebook_for_band(&band);
let nat_max = q.iter().map(|&qi| qi.unsigned_abs()).max().unwrap_or(0);
assert!(
nat_max >= min_max,
"expected natural_max ≥ {min_max} for {band:?}, got {nat_max} (q={q:?} cost={cost})"
);
}
}
#[test]
fn optimiser_picks_wide_codebook_for_high_magnitude_band() {
let band = vec![200.0_f32, -180.0, 150.0, -120.0];
let (cb, _sf, _q, cost) = pick_best_codebook_for_band(&band);
assert!(cost < u32::MAX);
assert!(
matches!(cb, 7..=11),
"expected a wide-q codebook for high magnitude band, picked cb {cb}"
);
}
#[test]
fn optimiser_picks_zero_codebook_for_silent_band() {
let band = vec![0.0_f32; 4];
let (cb, _sf, _q, cost) = pick_best_codebook_for_band(&band);
assert_eq!(cb, 0);
assert_eq!(cost, 0);
}
#[test]
fn bit_cost_hcb5_matches_codeword_length() {
let q = vec![1i32, 0i32];
let cost = bit_cost_for_band(asf_hcb(5).unwrap(), &q, 5);
assert_eq!(cost, crate::huffman::HCB5_LEN[49] as u32);
}
#[test]
fn bit_cost_hcb11_escape_for_q_16() {
let q = vec![16i32, 0i32];
let hcb = asf_hcb(11).unwrap();
let inline = vec![16i32, 0i32];
let inline_cost = bit_cost_for_band(hcb, &inline, 5); let total = bit_cost_for_band(hcb, &q, 11);
assert!(
total >= inline_cost + 5,
"HCB11 escape did not add at least 5 bits: total={total} inline={inline_cost}"
);
}
#[test]
fn build_sections_collapses_runs() {
let per_band = vec![3u8, 3, 3, 5, 5];
let secs = build_sections_from_per_band_cb(&per_band, 5);
assert_eq!(secs.num_sec, 2);
assert_eq!(secs.sect_cb, vec![3, 5]);
assert_eq!(secs.sect_start, vec![0, 3]);
assert_eq!(secs.sect_end, vec![3, 5]);
}
#[test]
fn section_overhead_bits_long_frame() {
for len in 1..=7u32 {
assert_eq!(section_overhead_bits(len), 7, "len={len}");
}
for len in 8..=14u32 {
assert_eq!(section_overhead_bits(len), 10, "len={len}");
}
assert_eq!(section_overhead_bits(15), 13);
assert_eq!(section_overhead_bits(21), 13);
assert_eq!(section_overhead_bits(22), 16);
}
#[test]
fn dp_optimise_sections_collapses_uniform_cost() {
let mut row = [u32::MAX; 12];
row[5] = 6;
let table = vec![row; 5];
let sections = dp_optimise_sections(&table, 16);
assert_eq!(sections.len(), 1);
assert_eq!(sections[0], (0, 5, 5));
}
#[test]
fn dp_optimise_sections_splits_when_split_is_cheaper() {
let mut t = vec![[u32::MAX; 12]; 4];
t[0][1] = 5;
t[0][11] = 50;
t[1][1] = 5;
t[1][11] = 50;
t[2][11] = 50;
t[3][11] = 50;
let sections = dp_optimise_sections(&t, 16);
assert_eq!(sections.len(), 2);
assert_eq!(sections[0], (0, 2, 1));
assert_eq!(sections[1], (2, 4, 11));
}
#[test]
fn dp_optimise_sections_caps_at_max_sections() {
let mut t = vec![[u32::MAX; 12]; 5];
for r in &mut t {
r[5] = 10;
}
let sections = dp_optimise_sections(&t, 1);
assert_eq!(sections.len(), 1);
assert_eq!(sections[0], (0, 5, 5));
}
#[test]
fn snf_emits_index_for_zero_quant_band_with_real_energy() {
let coeffs: Vec<f32> = vec![0.5, -0.5, 0.3, -0.4, 0.2, 0.0];
let sfb_offset: Vec<u16> = vec![0, 4, 6];
let max_sfb = 2u32;
let sfb_cb = vec![0u8, 0u8]; let mqi = vec![0u32, 0u32];
let snf =
compute_snf_dpcm_for_zero_quant_bands(&coeffs, &sfb_offset, max_sfb, &sfb_cb, &mqi);
let snf = snf.expect("expected SNF data for non-silent band");
assert!(
snf[0] >= 1 && snf[0] <= 21,
"expected SNF idx in [1,21], got {}",
snf[0]
);
}
#[test]
fn snf_returns_none_for_silent_input() {
let coeffs = vec![0.0_f32; 8];
let sfb_offset: Vec<u16> = vec![0, 4, 8];
let snf = compute_snf_dpcm_for_zero_quant_bands(
&coeffs,
&sfb_offset,
2,
&[0u8, 0u8],
&[0u32, 0u32],
);
assert!(snf.is_none());
}
#[test]
fn snf_skips_bands_with_quantised_content() {
let coeffs: Vec<f32> = vec![0.5, -0.5, 0.3, -0.4];
let sfb_offset: Vec<u16> = vec![0, 2, 4];
let snf = compute_snf_dpcm_for_zero_quant_bands(
&coeffs,
&sfb_offset,
2,
&[5u8, 0u8],
&[3u32, 0u32],
);
let snf = snf.expect("expected SNF for the silent band");
assert_eq!(snf[0], 0);
assert!(
snf[1] >= 1,
"expected SNF idx >= 1 for band 1, got {}",
snf[1]
);
}
#[test]
fn snf_round_trip_through_parser() {
use crate::asf_data::parse_asf_snf_data;
let max_sfb = 4u32;
let snf = vec![0i32, 0i32, 14, 16]; let sfb_cb = vec![5u8, 5u8, 0u8, 0u8];
let mqi = vec![3u32, 3u32, 0u32, 0u32];
let sections = AsfSections {
sect_cb: vec![5u8, 0u8],
sect_start: vec![0u16, 2u16],
sect_end: vec![2u16, 4u16],
sfb_cb: sfb_cb.clone(),
num_sec: 2,
num_sec_lsf: 2,
};
let mut bw = BitWriter::new();
write_snf_data(&mut bw, Some(&snf), &sfb_cb, &mqi, max_sfb);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let parsed = parse_asf_snf_data(&mut br, §ions, &mqi, max_sfb, 1920)
.expect("parse_asf_snf_data");
let parsed = parsed.expect("expected SNF data");
assert_eq!(parsed[2], 14);
assert_eq!(parsed[3], 16);
}
#[test]
fn dp_vs_greedy_dp_savings_on_alternating_pattern() {
let mut t = vec![[u32::MAX; 12]; 6];
for (i, row) in t.iter_mut().enumerate() {
row[11] = 12;
if i % 2 == 0 {
row[1] = 8;
}
}
let dp_sections = dp_optimise_sections(&t, 16);
let greedy_total: u64 = 6 * 7 + 8 * 3 + 12 * 3;
let mut dp_total: u64 = 0;
for &(s, e, cb) in &dp_sections {
dp_total += section_overhead_bits(e - s) as u64;
for b in s..e {
let c = t[b as usize][cb as usize];
dp_total += if c == u32::MAX { 0 } else { c as u64 };
}
}
assert!(
dp_total <= greedy_total,
"DP must beat greedy: dp={dp_total} greedy={greedy_total}"
);
assert!(
dp_total <= 79,
"DP did not collapse alternating pattern to single section: {dp_total}"
);
}
#[test]
fn dp_vs_greedy_dp_never_worse_for_noise_spectrum() {
let n = 1920usize;
let max_sfb = 50u32;
let mut s: u64 = 0xACE4u64;
let pcm: Vec<f32> = (0..n)
.map(|_| {
s = s
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let u = (s >> 33) as u32;
(u as f32 / (1u32 << 31) as f32 - 1.0) * 0.3
})
.collect();
let mut mdct = crate::encoder_mdct::EncoderMdctState::new(n as u32);
let _ = mdct.analyse_frame(&pcm);
let coeffs = mdct.analyse_frame(&pcm);
let (greedy, dp) = measure_greedy_vs_dp_bits(n as u32, max_sfb, &coeffs);
eprintln!(
"ROUND-50 DP-vs-greedy noise-fixture bits: greedy={greedy} dp={dp} (savings={})",
greedy.saturating_sub(dp)
);
assert!(
dp <= greedy,
"DP must not be worse than greedy: dp={dp} greedy={greedy}"
);
}
}