#![allow(clippy::excessive_precision, clippy::needless_range_loop)]
use crate::acpl::{sb_to_pb, AcplHuffParam, AcplQuantMode};
use crate::qmf::NUM_QMF_SUBBANDS;
#[rustfmt::skip]
pub const ALPHA_DQ_FINE: [f32; 33] = [
-2.000000, -1.809375, -1.637500, -1.484375, -1.350000, -1.234375,
-1.137500, -1.059375, -1.000000, -0.940625, -0.862500, -0.765625,
-0.650000, -0.515625, -0.362500, -0.190625, 0.000000, 0.190625,
0.362500, 0.515625, 0.650000, 0.765625, 0.862500, 0.940625,
1.000000, 1.059375, 1.137500, 1.234375, 1.350000, 1.484375,
1.637500, 1.809375, 2.000000,
];
#[rustfmt::skip]
pub const IBETA_FINE: [u8; 33] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5,
6, 7, 8, 7, 6, 5, 4, 3, 2, 1, 0,
];
#[rustfmt::skip]
pub const ALPHA_DQ_COARSE: [f32; 17] = [
-2.000000, -1.637500, -1.350000, -1.137500, -1.000000, -0.862500,
-0.650000, -0.362500, 0.000000, 0.362500, 0.650000, 0.862500,
1.000000, 1.137500, 1.350000, 1.637500, 2.000000,
];
#[rustfmt::skip]
pub const IBETA_COARSE: [u8; 17] = [
0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1, 0,
];
#[rustfmt::skip]
pub const BETA_DQ_FINE: [[f32; 9]; 9] = [
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
[0.2375000, 0.2035449, 0.1729297, 0.1456543, 0.1217188, 0.1011230, 0.0838672, 0.0699512, 0.0593750],
[0.5500000, 0.4713672, 0.4004688, 0.3373047, 0.2818750, 0.2341797, 0.1942188, 0.1619922, 0.1375000],
[0.9375000, 0.8034668, 0.6826172, 0.5749512, 0.4804688, 0.3991699, 0.3310547, 0.2761230, 0.2343750],
[1.4000000, 1.1998440, 1.0193750, 0.8585938, 0.7175000, 0.5960938, 0.4943750, 0.4123438, 0.3500000],
[1.9375000, 1.6604980, 1.4107420, 1.1882319, 0.9929688, 0.8249512, 0.6841797, 0.5706543, 0.4843750],
[2.5500000, 2.1854300, 1.8567190, 1.5638670, 1.3068750, 1.0857420, 0.9004688, 0.7510547, 0.6375000],
[3.2375000, 2.7746389, 2.3573050, 1.9854980, 1.6592190, 1.3784670, 1.1432420, 0.9535449, 0.8093750],
[4.0000000, 3.4281249, 2.9124999, 2.4531250, 2.0500000, 1.7031250, 1.4125000, 1.1781250, 1.0000000],
];
#[rustfmt::skip]
pub const BETA_DQ_COARSE: [[f32; 5]; 5] = [
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
[0.5500000, 0.4004688, 0.2818750, 0.1942188, 0.1375000],
[1.4000000, 1.0193750, 0.7175000, 0.4943750, 0.3500000],
[2.5500000, 1.8567190, 1.3068750, 0.9004688, 0.6375000],
[4.0000000, 2.9124999, 2.0500000, 1.4125000, 1.0000000],
];
pub fn beta3_delta(qm: AcplQuantMode) -> f32 {
match qm {
AcplQuantMode::Fine => 0.125,
AcplQuantMode::Coarse => 0.25,
}
}
pub fn gamma_delta(qm: AcplQuantMode) -> f32 {
match qm {
AcplQuantMode::Fine => 1638.0 / 16384.0,
AcplQuantMode::Coarse => 3276.0 / 16384.0,
}
}
pub fn dequantize_alpha_index(qm: AcplQuantMode, alpha_q: i32) -> (f32, u8) {
match qm {
AcplQuantMode::Fine => {
let lane = (alpha_q + 16).clamp(0, 32) as usize;
(ALPHA_DQ_FINE[lane], IBETA_FINE[lane])
}
AcplQuantMode::Coarse => {
let lane = (alpha_q + 8).clamp(0, 16) as usize;
(ALPHA_DQ_COARSE[lane], IBETA_COARSE[lane])
}
}
}
pub fn dequantize_beta_index(qm: AcplQuantMode, beta_q: i32, ibeta: u8) -> f32 {
let mag = beta_q.unsigned_abs() as usize;
let val = match qm {
AcplQuantMode::Fine => {
let row = mag.min(8);
let col = (ibeta as usize).min(8);
BETA_DQ_FINE[row][col]
}
AcplQuantMode::Coarse => {
let row = mag.min(4);
let col = (ibeta as usize).min(4);
BETA_DQ_COARSE[row][col]
}
};
if beta_q < 0 {
-val
} else {
val
}
}
#[derive(Debug, Clone)]
pub struct AcplDiffState {
pub q_prev: Vec<i32>,
}
impl AcplDiffState {
pub fn new() -> Self {
Self { q_prev: Vec::new() }
}
pub fn carry(&mut self, row: &[i32]) {
self.q_prev.clear();
self.q_prev.extend_from_slice(row);
}
}
impl Default for AcplDiffState {
fn default() -> Self {
Self::new()
}
}
pub fn differential_decode(
params: &[AcplHuffParam],
num_bands: u32,
state: &mut AcplDiffState,
) -> Vec<Vec<i32>> {
let nb = num_bands as usize;
if state.q_prev.len() != nb {
state.q_prev = vec![0; nb];
}
let mut out = Vec::with_capacity(params.len());
for p in params {
let mut row = vec![0i32; nb];
if !p.direction_time {
if !p.values.is_empty() {
row[0] = p.values[0];
for i in 1..nb {
let d = p.values.get(i).copied().unwrap_or(0);
row[i] = row[i - 1] + d;
}
}
} else {
for i in 0..nb {
let d = p.values.get(i).copied().unwrap_or(0);
row[i] = state.q_prev[i] + d;
}
}
state.q_prev = row.clone();
out.push(row);
}
out
}
pub fn dequantize_alpha_beta(
alpha_q: &[Vec<i32>],
beta_q: &[Vec<i32>],
qm: AcplQuantMode,
) -> (Vec<Vec<f32>>, Vec<Vec<f32>>) {
debug_assert_eq!(alpha_q.len(), beta_q.len());
let mut alpha_dq = Vec::with_capacity(alpha_q.len());
let mut beta_dq = Vec::with_capacity(beta_q.len());
for ps in 0..alpha_q.len() {
let n = alpha_q[ps].len();
let mut a_row = vec![0.0f32; n];
let mut b_row = vec![0.0f32; n];
for i in 0..n {
let (a_val, ibeta) = dequantize_alpha_index(qm, alpha_q[ps][i]);
a_row[i] = a_val;
let bq = beta_q[ps].get(i).copied().unwrap_or(0);
b_row[i] = dequantize_beta_index(qm, bq, ibeta);
}
alpha_dq.push(a_row);
beta_dq.push(b_row);
}
(alpha_dq, beta_dq)
}
pub fn dequantize_beta3(beta3_q: &[Vec<i32>], qm: AcplQuantMode) -> Vec<Vec<f32>> {
let delta = beta3_delta(qm);
beta3_q
.iter()
.map(|row| row.iter().map(|&q| (q as f32) * delta).collect())
.collect()
}
pub fn dequantize_gamma(gamma_q: &[Vec<i32>], qm: AcplQuantMode) -> Vec<Vec<f32>> {
let delta = gamma_delta(qm);
gamma_q
.iter()
.map(|row| row.iter().map(|&q| (q as f32) * delta).collect())
.collect()
}
#[derive(Debug, Clone)]
pub struct InterpInputs<'a> {
pub by_pset: &'a [Vec<f32>],
pub prev: &'a [f32],
}
#[allow(clippy::too_many_arguments)]
pub fn interpolate(
inputs: &InterpInputs<'_>,
num_pset: u32,
sb: u32,
ts: u32,
num_ts: u32,
interp_steep: bool,
param_timeslots: &[u8],
) -> f32 {
let sb_idx = sb as usize;
if !interp_steep {
if num_pset == 1 {
let p = inputs.by_pset[0].get(sb_idx).copied().unwrap_or(0.0);
let prev = inputs.prev.get(sb_idx).copied().unwrap_or(0.0);
let delta = p - prev;
prev + ((ts + 1) as f32) * delta / (num_ts as f32)
} else {
let ts_2 = num_ts / 2;
let p0 = inputs.by_pset[0].get(sb_idx).copied().unwrap_or(0.0);
let p1 = inputs.by_pset[1].get(sb_idx).copied().unwrap_or(0.0);
if ts < ts_2 {
let prev = inputs.prev.get(sb_idx).copied().unwrap_or(0.0);
let delta = p0 - prev;
prev + ((ts + 1) as f32) * delta / (ts_2 as f32)
} else {
let delta = p1 - p0;
p0 + ((ts - ts_2 + 1) as f32) * delta / ((num_ts - ts_2) as f32)
}
}
} else {
if num_pset == 1 {
let bound = param_timeslots.first().copied().unwrap_or(0) as u32;
if ts < bound {
inputs.prev.get(sb_idx).copied().unwrap_or(0.0)
} else {
inputs.by_pset[0].get(sb_idx).copied().unwrap_or(0.0)
}
} else {
let bound0 = param_timeslots.first().copied().unwrap_or(0) as u32;
let bound1 = param_timeslots.get(1).copied().unwrap_or(0) as u32;
if ts < bound0 {
inputs.prev.get(sb_idx).copied().unwrap_or(0.0)
} else if ts < bound1 {
inputs.by_pset[0].get(sb_idx).copied().unwrap_or(0.0)
} else {
inputs.by_pset[1].get(sb_idx).copied().unwrap_or(0.0)
}
}
}
}
pub fn expand_pb_to_sb(pb_vals: &[Vec<f32>], num_param_bands: u32) -> Vec<Vec<f32>> {
pb_vals
.iter()
.map(|row| {
let mut out = vec![0.0f32; NUM_QMF_SUBBANDS];
for sb in 0..NUM_QMF_SUBBANDS {
let pb = sb_to_pb(sb as u32, num_param_bands) as usize;
out[sb] = row.get(pb).copied().unwrap_or(0.0);
}
out
})
.collect()
}
pub fn update_param_prev(prev: &mut Vec<f32>, last_set_sb: &[f32]) {
prev.clear();
prev.extend_from_slice(last_set_sb);
prev.resize(NUM_QMF_SUBBANDS, 0.0);
}
pub fn region_delay(sb: u32) -> usize {
match sb {
0..=6 => 7,
7..=22 => 10,
_ => 12,
}
}
pub fn region_filter_length(sb: u32) -> usize {
match sb {
0..=6 => 7,
7..=22 => 4,
_ => 2,
}
}
#[derive(Debug, Clone, Copy)]
pub enum DecorrelatorId {
D0,
D1,
D2,
}
#[rustfmt::skip]
pub const A_K0: [[f64; 8]; 3] = [
[1.0000, 0.5306, -0.4533, -0.6248, 0.0424, 0.4237, 0.4311, 0.1688],
[1.0000, -0.4178, 0.1082, -0.2368, -0.1014, -0.1052, -0.3528, 0.4665],
[1.0000, 0.4007, 0.4747, 0.2611, -0.1211, -0.4248, -0.2989, -0.1932],
];
#[rustfmt::skip]
pub const A_K1: [[f64; 5]; 3] = [
[1.0000, 0.5561, -0.3039, -0.5024, -0.1850],
[1.0000, 0.0425, 0.3235, -0.1556, 0.4958],
[1.0000, -0.4361, 0.0345, 0.5215, -0.4178],
];
#[rustfmt::skip]
pub const A_K2: [[f64; 3]; 3] = [
[1.0000, 0.5773, 0.3321],
[1.0000, 0.2327, -0.3901],
[1.0000, -0.6057, 0.3804],
];
pub fn region_coeffs(d: DecorrelatorId, sb: u32) -> &'static [f64] {
let col = match d {
DecorrelatorId::D0 => 0,
DecorrelatorId::D1 => 1,
DecorrelatorId::D2 => 2,
};
match sb {
0..=6 => &A_K0[col][..],
7..=22 => &A_K1[col][..],
_ => &A_K2[col][..],
}
}
#[derive(Debug, Clone)]
pub struct InputSignalModifier {
pub which: DecorrelatorId,
pub x_hist: Vec<Vec<(f32, f32)>>,
pub y_hist: Vec<Vec<(f32, f32)>>,
}
impl InputSignalModifier {
pub fn new(which: DecorrelatorId) -> Self {
let depth = 24usize;
Self {
which,
x_hist: vec![vec![(0.0, 0.0); depth]; NUM_QMF_SUBBANDS],
y_hist: vec![vec![(0.0, 0.0); depth]; NUM_QMF_SUBBANDS],
}
}
pub fn reset(&mut self) {
for h in &mut self.x_hist {
for v in h {
*v = (0.0, 0.0);
}
}
for h in &mut self.y_hist {
for v in h {
*v = (0.0, 0.0);
}
}
}
pub fn process_sample(&mut self, sb: u32, x_in: (f32, f32)) -> (f32, f32) {
let sb_idx = sb as usize;
let delay = region_delay(sb);
let filter_length = region_filter_length(sb);
let a = region_coeffs(self.which, sb);
let xh = &mut self.x_hist[sb_idx];
xh.rotate_right(1);
xh[0] = x_in;
let yh = &mut self.y_hist[sb_idx];
let b0 = a[filter_length];
let a0 = a[0];
let xd = if delay < xh.len() {
xh[delay]
} else {
(0.0, 0.0)
};
let mut y_re = (b0 / a0) * xd.0 as f64;
let mut y_im = (b0 / a0) * xd.1 as f64;
for i in 1..=filter_length {
let bi = a[filter_length - i];
let xi_idx = i + delay;
let xi = if xi_idx < xh.len() {
xh[xi_idx]
} else {
(0.0, 0.0)
};
let yi = if i - 1 < yh.len() {
yh[i - 1]
} else {
(0.0, 0.0)
};
let ai = a[i];
y_re += (bi * xi.0 as f64 - ai * yi.0 as f64) / a0;
y_im += (bi * xi.1 as f64 - ai * yi.1 as f64) / a0;
}
let y_out = (y_re as f32, y_im as f32);
yh.rotate_right(1);
yh[0] = y_out;
y_out
}
}
#[derive(Debug, Clone)]
pub struct Decorrelator {
pub d0: InputSignalModifier,
pub d1: InputSignalModifier,
pub d2: InputSignalModifier,
}
impl Decorrelator {
pub fn new() -> Self {
Self {
d0: InputSignalModifier::new(DecorrelatorId::D0),
d1: InputSignalModifier::new(DecorrelatorId::D1),
d2: InputSignalModifier::new(DecorrelatorId::D2),
}
}
}
impl Default for Decorrelator {
fn default() -> Self {
Self::new()
}
}
pub const DUCKER_ALPHA: f32 = 0.76592833836465;
pub const DUCKER_ALPHA_SMOOTH: f32 = 0.25;
pub const DUCKER_GAMMA: f32 = 1.5;
pub const DUCKER_EPSILON: f32 = 1.0e-9;
pub const ACPL_MAX_NUM_PARAM_BANDS: usize = 15;
#[derive(Debug, Clone)]
pub struct TransientDucker {
pub p_peak_decay_prev: [f32; ACPL_MAX_NUM_PARAM_BANDS],
pub p_smooth_prev: [f32; ACPL_MAX_NUM_PARAM_BANDS],
pub smooth_peak_diff_prev: [f32; ACPL_MAX_NUM_PARAM_BANDS],
}
impl TransientDucker {
pub fn new() -> Self {
Self {
p_peak_decay_prev: [0.0; ACPL_MAX_NUM_PARAM_BANDS],
p_smooth_prev: [0.0; ACPL_MAX_NUM_PARAM_BANDS],
smooth_peak_diff_prev: [0.0; ACPL_MAX_NUM_PARAM_BANDS],
}
}
pub fn reset(&mut self) {
*self = Self::new();
}
pub fn update(
&mut self,
p_energy: &[f32; ACPL_MAX_NUM_PARAM_BANDS],
) -> [f32; ACPL_MAX_NUM_PARAM_BANDS] {
let mut duck = [1.0f32; ACPL_MAX_NUM_PARAM_BANDS];
for pb in 0..ACPL_MAX_NUM_PARAM_BANDS {
let p_peak_decay = if DUCKER_ALPHA * self.p_peak_decay_prev[pb] < p_energy[pb] {
p_energy[pb]
} else {
DUCKER_ALPHA * self.p_peak_decay_prev[pb]
};
let smooth = (1.0 - DUCKER_ALPHA_SMOOTH) * self.p_smooth_prev[pb]
+ DUCKER_ALPHA_SMOOTH * p_energy[pb];
let smooth_peak_diff = (1.0 - DUCKER_ALPHA_SMOOTH) * self.smooth_peak_diff_prev[pb]
+ DUCKER_ALPHA_SMOOTH * (p_peak_decay - p_energy[pb]);
let g = if DUCKER_GAMMA * smooth_peak_diff > smooth {
smooth / (DUCKER_GAMMA * (smooth_peak_diff + DUCKER_EPSILON))
} else {
1.0
};
duck[pb] = g;
self.p_peak_decay_prev[pb] = p_peak_decay;
self.p_smooth_prev[pb] = smooth;
self.smooth_peak_diff_prev[pb] = smooth_peak_diff;
}
duck
}
}
impl Default for TransientDucker {
fn default() -> Self {
Self::new()
}
}
pub fn compute_p_energy(
x: &[(f32, f32); NUM_QMF_SUBBANDS],
num_param_bands: u32,
) -> [f32; ACPL_MAX_NUM_PARAM_BANDS] {
let mut e = [0.0f32; ACPL_MAX_NUM_PARAM_BANDS];
for sb in 0..NUM_QMF_SUBBANDS as u32 {
let pb = sb_to_pb(sb, num_param_bands) as usize;
let (re, im) = x[sb as usize];
e[pb] += re * re + im * im;
}
e
}
pub fn apply_transient_ducker(
x: &[(f32, f32); NUM_QMF_SUBBANDS],
duck_gain: &[f32; ACPL_MAX_NUM_PARAM_BANDS],
num_param_bands: u32,
) -> [(f32, f32); NUM_QMF_SUBBANDS] {
let mut out = *x;
for sb in 0..NUM_QMF_SUBBANDS as u32 {
let pb = sb_to_pb(sb, num_param_bands) as usize;
let g = duck_gain[pb];
out[sb as usize].0 *= g;
out[sb as usize].1 *= g;
}
out
}
#[derive(Debug, Clone)]
pub struct AcplCpeState {
pub alpha_prev_sb: Vec<f32>,
pub beta_prev_sb: Vec<f32>,
pub decorrelator: InputSignalModifier,
pub ducker: TransientDucker,
}
impl AcplCpeState {
pub fn new(which: DecorrelatorId) -> Self {
Self {
alpha_prev_sb: vec![0.0; NUM_QMF_SUBBANDS],
beta_prev_sb: vec![0.0; NUM_QMF_SUBBANDS],
decorrelator: InputSignalModifier::new(which),
ducker: TransientDucker::new(),
}
}
}
pub struct AcplCpeFrame<'a> {
pub x0: &'a [[(f32, f32); NUM_QMF_SUBBANDS]],
pub x1: Option<&'a [[(f32, f32); NUM_QMF_SUBBANDS]]>,
pub alpha_dq: &'a [Vec<f32>],
pub beta_dq: &'a [Vec<f32>],
pub num_param_bands: u32,
pub acpl_qmf_band: u32,
pub steep: bool,
pub param_timeslots: &'a [u8],
}
pub struct AcplCpeOutput {
pub z0: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>,
pub z1: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>,
}
pub fn acpl_module(
frame: &AcplCpeFrame<'_>,
y: &[[(f32, f32); NUM_QMF_SUBBANDS]],
) -> AcplCpeOutput {
let num_ts = frame.x0.len();
debug_assert_eq!(y.len(), num_ts);
let num_pset = frame.alpha_dq.len() as u32;
let alpha_sb = expand_pb_to_sb(frame.alpha_dq, frame.num_param_bands);
let beta_sb = expand_pb_to_sb(frame.beta_dq, frame.num_param_bands);
let prev_alpha = vec![0.0f32; NUM_QMF_SUBBANDS];
let prev_beta = vec![0.0f32; NUM_QMF_SUBBANDS];
let alpha_inp = InterpInputs {
by_pset: &alpha_sb,
prev: &prev_alpha,
};
let beta_inp = InterpInputs {
by_pset: &beta_sb,
prev: &prev_beta,
};
let zero_col = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
let mut z0_out = Vec::with_capacity(num_ts);
let mut z1_out = Vec::with_capacity(num_ts);
for ts in 0..num_ts {
let x0_col = frame.x0[ts];
let x1_col = match frame.x1 {
Some(matrix) => matrix[ts],
None => zero_col,
};
let y_col = y[ts];
let mut z0_col = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
let mut z1_col = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
for sb in 0..NUM_QMF_SUBBANDS as u32 {
let interp_a = interpolate(
&alpha_inp,
num_pset,
sb,
ts as u32,
num_ts as u32,
frame.steep,
frame.param_timeslots,
);
let interp_b = interpolate(
&beta_inp,
num_pset,
sb,
ts as u32,
num_ts as u32,
frame.steep,
frame.param_timeslots,
);
let sb_i = sb as usize;
if sb < frame.acpl_qmf_band {
let (x0r, x0i) = x0_col[sb_i];
let (x1r, x1i) = x1_col[sb_i];
z0_col[sb_i] = (0.5 * (x0r + x1r), 0.5 * (x0i + x1i));
z1_col[sb_i] = (0.5 * (x0r - x1r), 0.5 * (x0i - x1i));
} else {
let (x0r, x0i) = x0_col[sb_i];
let (yr, yi) = y_col[sb_i];
let plus_a = 1.0 + interp_a;
let minus_a = 1.0 - interp_a;
z0_col[sb_i] = (
0.5 * (x0r * plus_a + yr * interp_b),
0.5 * (x0i * plus_a + yi * interp_b),
);
z1_col[sb_i] = (
0.5 * (x0r * minus_a - yr * interp_b),
0.5 * (x0i * minus_a - yi * interp_b),
);
}
}
z0_out.push(z0_col);
z1_out.push(z1_col);
}
AcplCpeOutput {
z0: z0_out,
z1: z1_out,
}
}
pub fn run_pseudocode_115_pair(state: &mut AcplCpeState, frame: AcplCpeFrame<'_>) -> AcplCpeOutput {
let num_ts = frame.x0.len();
let mut x0in = Vec::with_capacity(num_ts);
for ts in 0..num_ts {
let mut col = frame.x0[ts];
for sb in 0..NUM_QMF_SUBBANDS {
col[sb].0 *= 2.0;
col[sb].1 *= 2.0;
}
x0in.push(col);
}
let mut u0 = Vec::with_capacity(num_ts);
for ts in 0..num_ts {
let mut col = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
for sb in 0..NUM_QMF_SUBBANDS as u32 {
col[sb as usize] = state.decorrelator.process_sample(sb, x0in[ts][sb as usize]);
}
u0.push(col);
}
let mut y0 = Vec::with_capacity(num_ts);
for ts in 0..num_ts {
let p_energy = compute_p_energy(&u0[ts], frame.num_param_bands);
let duck = state.ducker.update(&p_energy);
y0.push(apply_transient_ducker(
&u0[ts],
&duck,
frame.num_param_bands,
));
}
let mut x1in_owned: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>;
let inner_x1: Option<&[[(f32, f32); NUM_QMF_SUBBANDS]]> = if let Some(x1) = frame.x1 {
x1in_owned = Vec::with_capacity(num_ts);
for ts in 0..num_ts {
let mut col = x1[ts];
for sb in 0..NUM_QMF_SUBBANDS {
col[sb].0 *= 2.0;
col[sb].1 *= 2.0;
}
x1in_owned.push(col);
}
Some(x1in_owned.as_slice())
} else {
None
};
let inner_frame = AcplCpeFrame {
x0: &x0in,
x1: inner_x1,
alpha_dq: frame.alpha_dq,
beta_dq: frame.beta_dq,
num_param_bands: frame.num_param_bands,
acpl_qmf_band: frame.acpl_qmf_band,
steep: frame.steep,
param_timeslots: frame.param_timeslots,
};
let out = acpl_module(&inner_frame, &y0);
if !frame.alpha_dq.is_empty() {
let last_idx = frame.alpha_dq.len() - 1;
let alpha_sb = expand_pb_to_sb(frame.alpha_dq, frame.num_param_bands);
let beta_sb = expand_pb_to_sb(frame.beta_dq, frame.num_param_bands);
update_param_prev(&mut state.alpha_prev_sb, &alpha_sb[last_idx]);
update_param_prev(&mut state.beta_prev_sb, &beta_sb[last_idx]);
}
out
}
pub type AcplQmfMatrix = Vec<[(f32, f32); NUM_QMF_SUBBANDS]>;
#[allow(clippy::too_many_arguments)]
pub fn transform(
x0: &[[(f32, f32); NUM_QMF_SUBBANDS]],
x1: &[[(f32, f32); NUM_QMF_SUBBANDS]],
g1_pb: &[Vec<f32>],
g2_pb: &[Vec<f32>],
num_param_bands: u32,
prev_g1: &[f32],
prev_g2: &[f32],
steep: bool,
param_timeslots: &[u8],
) -> Vec<[(f32, f32); NUM_QMF_SUBBANDS]> {
let num_ts = x0.len();
debug_assert_eq!(x1.len(), num_ts);
let num_pset = g1_pb.len() as u32;
let g1_sb = expand_pb_to_sb(g1_pb, num_param_bands);
let g2_sb = expand_pb_to_sb(g2_pb, num_param_bands);
let g1_inp = InterpInputs {
by_pset: &g1_sb,
prev: prev_g1,
};
let g2_inp = InterpInputs {
by_pset: &g2_sb,
prev: prev_g2,
};
let mut out = Vec::with_capacity(num_ts);
for ts in 0..num_ts {
let mut v_col = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
for sb in 0..NUM_QMF_SUBBANDS as u32 {
let g1 = interpolate(
&g1_inp,
num_pset,
sb,
ts as u32,
num_ts as u32,
steep,
param_timeslots,
);
let g2 = interpolate(
&g2_inp,
num_pset,
sb,
ts as u32,
num_ts as u32,
steep,
param_timeslots,
);
let (x0r, x0i) = x0[ts][sb as usize];
let (x1r, x1i) = x1[ts][sb as usize];
v_col[sb as usize] = (x0r * g1 + x1r * g2, x0i * g1 + x1i * g2);
}
out.push(v_col);
}
out
}
#[allow(clippy::too_many_arguments)]
pub fn acpl_module2(
x0: &[[(f32, f32); NUM_QMF_SUBBANDS]],
x1: &[[(f32, f32); NUM_QMF_SUBBANDS]],
y: &[[(f32, f32); NUM_QMF_SUBBANDS]],
g1_pb: &[Vec<f32>],
g2_pb: &[Vec<f32>],
g1a_pb: &[Vec<f32>],
g2a_pb: &[Vec<f32>],
b_pb: &[Vec<f32>],
num_param_bands: u32,
steep: bool,
param_timeslots: &[u8],
) -> (AcplQmfMatrix, AcplQmfMatrix) {
let num_ts = x0.len();
debug_assert_eq!(x1.len(), num_ts);
debug_assert_eq!(y.len(), num_ts);
let num_pset = g1_pb.len() as u32;
let g1_sb = expand_pb_to_sb(g1_pb, num_param_bands);
let g2_sb = expand_pb_to_sb(g2_pb, num_param_bands);
let g1a_sb = expand_pb_to_sb(g1a_pb, num_param_bands);
let g2a_sb = expand_pb_to_sb(g2a_pb, num_param_bands);
let b_sb = expand_pb_to_sb(b_pb, num_param_bands);
let zero_prev = vec![0.0f32; NUM_QMF_SUBBANDS];
let g1_inp = InterpInputs {
by_pset: &g1_sb,
prev: &zero_prev,
};
let g2_inp = InterpInputs {
by_pset: &g2_sb,
prev: &zero_prev,
};
let g1a_inp = InterpInputs {
by_pset: &g1a_sb,
prev: &zero_prev,
};
let g2a_inp = InterpInputs {
by_pset: &g2a_sb,
prev: &zero_prev,
};
let b_inp = InterpInputs {
by_pset: &b_sb,
prev: &zero_prev,
};
let mut z0_out = Vec::with_capacity(num_ts);
let mut z1_out = Vec::with_capacity(num_ts);
for ts in 0..num_ts {
let mut z0_col = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
let mut z1_col = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
for sb in 0..NUM_QMF_SUBBANDS as u32 {
let g1 = interpolate(
&g1_inp,
num_pset,
sb,
ts as u32,
num_ts as u32,
steep,
param_timeslots,
);
let g2 = interpolate(
&g2_inp,
num_pset,
sb,
ts as u32,
num_ts as u32,
steep,
param_timeslots,
);
let g1a = interpolate(
&g1a_inp,
num_pset,
sb,
ts as u32,
num_ts as u32,
steep,
param_timeslots,
);
let g2a = interpolate(
&g2a_inp,
num_pset,
sb,
ts as u32,
num_ts as u32,
steep,
param_timeslots,
);
let b = interpolate(
&b_inp,
num_pset,
sb,
ts as u32,
num_ts as u32,
steep,
param_timeslots,
);
let sb_i = sb as usize;
let (x0r, x0i) = x0[ts][sb_i];
let (x1r, x1i) = x1[ts][sb_i];
let (yr, yi) = y[ts][sb_i];
z0_col[sb_i] = (
0.5 * (x0r * (g1 + g1a) + x1r * (g2 + g2a) + yr * b),
0.5 * (x0i * (g1 + g1a) + x1i * (g2 + g2a) + yi * b),
);
z1_col[sb_i] = (
0.5 * (x0r * (g1 - g1a) + x1r * (g2 - g2a) - yr * b),
0.5 * (x0i * (g1 - g1a) + x1i * (g2 - g2a) - yi * b),
);
}
z0_out.push(z0_col);
z1_out.push(z1_col);
}
(z0_out, z1_out)
}
#[allow(clippy::too_many_arguments)]
pub fn acpl_module3(
z0: &mut [[(f32, f32); NUM_QMF_SUBBANDS]],
z1: &mut [[(f32, f32); NUM_QMF_SUBBANDS]],
y2: &[[(f32, f32); NUM_QMF_SUBBANDS]],
b3_pb: &[Vec<f32>],
b3a_pb: &[Vec<f32>],
num_param_bands: u32,
steep: bool,
param_timeslots: &[u8],
) {
let num_ts = z0.len();
debug_assert_eq!(z1.len(), num_ts);
debug_assert_eq!(y2.len(), num_ts);
let num_pset = b3_pb.len() as u32;
let b3_sb = expand_pb_to_sb(b3_pb, num_param_bands);
let b3a_sb = expand_pb_to_sb(b3a_pb, num_param_bands);
let zero_prev = vec![0.0f32; NUM_QMF_SUBBANDS];
let b3_inp = InterpInputs {
by_pset: &b3_sb,
prev: &zero_prev,
};
let b3a_inp = InterpInputs {
by_pset: &b3a_sb,
prev: &zero_prev,
};
for ts in 0..num_ts {
for sb in 0..NUM_QMF_SUBBANDS as u32 {
let b3 = interpolate(
&b3_inp,
num_pset,
sb,
ts as u32,
num_ts as u32,
steep,
param_timeslots,
);
let b3a = interpolate(
&b3a_inp,
num_pset,
sb,
ts as u32,
num_ts as u32,
steep,
param_timeslots,
);
let sb_i = sb as usize;
let (yr, yi) = y2[ts][sb_i];
z0[ts][sb_i].0 += 0.25 * yr * (b3 + b3a);
z0[ts][sb_i].1 += 0.25 * yi * (b3 + b3a);
z1[ts][sb_i].0 += 0.25 * yr * (b3 - b3a);
z1[ts][sb_i].1 += 0.25 * yi * (b3 - b3a);
}
}
}
fn pb_matrix_mul(a: &[Vec<f32>], b: &[Vec<f32>]) -> Vec<Vec<f32>> {
debug_assert_eq!(a.len(), b.len());
a.iter()
.zip(b.iter())
.map(|(ar, br)| {
let n = ar.len().min(br.len());
let mut row = Vec::with_capacity(n);
for i in 0..n {
row.push(ar[i] * br[i]);
}
row
})
.collect()
}
fn pb_matrix_sum3(a: &[Vec<f32>], b: &[Vec<f32>], c: &[Vec<f32>]) -> Vec<Vec<f32>> {
debug_assert_eq!(a.len(), b.len());
debug_assert_eq!(a.len(), c.len());
a.iter()
.zip(b.iter())
.zip(c.iter())
.map(|((ar, br), cr)| {
let n = ar.len().min(br.len()).min(cr.len());
let mut row = Vec::with_capacity(n);
for i in 0..n {
row.push(ar[i] + br[i] + cr[i]);
}
row
})
.collect()
}
fn pb_matrix_scale(a: &[Vec<f32>], s: f32) -> Vec<Vec<f32>> {
a.iter()
.map(|row| row.iter().map(|&v| v * s).collect())
.collect()
}
#[derive(Debug, Clone)]
pub struct AcplMchState {
pub d0: InputSignalModifier,
pub d1: InputSignalModifier,
pub d2: InputSignalModifier,
pub ducker0: TransientDucker,
pub ducker1: TransientDucker,
pub ducker2: TransientDucker,
pub g1_prev_sb: Vec<f32>,
pub g2_prev_sb: Vec<f32>,
pub g3_prev_sb: Vec<f32>,
pub g4_prev_sb: Vec<f32>,
}
impl AcplMchState {
pub fn new() -> Self {
Self {
d0: InputSignalModifier::new(DecorrelatorId::D0),
d1: InputSignalModifier::new(DecorrelatorId::D1),
d2: InputSignalModifier::new(DecorrelatorId::D2),
ducker0: TransientDucker::new(),
ducker1: TransientDucker::new(),
ducker2: TransientDucker::new(),
g1_prev_sb: vec![0.0; NUM_QMF_SUBBANDS],
g2_prev_sb: vec![0.0; NUM_QMF_SUBBANDS],
g3_prev_sb: vec![0.0; NUM_QMF_SUBBANDS],
g4_prev_sb: vec![0.0; NUM_QMF_SUBBANDS],
}
}
}
impl Default for AcplMchState {
fn default() -> Self {
Self::new()
}
}
pub struct AcplMchFrame<'a> {
pub x0: &'a [[(f32, f32); NUM_QMF_SUBBANDS]],
pub x1: &'a [[(f32, f32); NUM_QMF_SUBBANDS]],
pub x2: &'a [[(f32, f32); NUM_QMF_SUBBANDS]],
pub alpha_1_dq: &'a [Vec<f32>],
pub alpha_2_dq: &'a [Vec<f32>],
pub beta_1_dq: &'a [Vec<f32>],
pub beta_2_dq: &'a [Vec<f32>],
pub beta_3_dq: &'a [Vec<f32>],
pub g1_dq: &'a [Vec<f32>],
pub g2_dq: &'a [Vec<f32>],
pub g3_dq: &'a [Vec<f32>],
pub g4_dq: &'a [Vec<f32>],
pub g5_dq: &'a [Vec<f32>],
pub g6_dq: &'a [Vec<f32>],
pub num_param_bands: u32,
pub steep: bool,
pub param_timeslots: &'a [u8],
}
pub struct AcplMchOutput {
pub z0: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>, pub z2: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>, pub z4: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>, pub z1: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>, pub z3: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>, }
pub fn run_pseudocode_118_5x(state: &mut AcplMchState, frame: AcplMchFrame<'_>) -> AcplMchOutput {
let num_ts = frame.x0.len();
debug_assert_eq!(frame.x1.len(), num_ts);
debug_assert_eq!(frame.x2.len(), num_ts);
let scale = 1.0 + 2.0 * (0.5f32).sqrt();
let scale_x = |x: &[[(f32, f32); NUM_QMF_SUBBANDS]]| -> Vec<[(f32, f32); NUM_QMF_SUBBANDS]> {
x.iter()
.map(|col| {
let mut out = *col;
for sb in 0..NUM_QMF_SUBBANDS {
out[sb].0 *= scale;
out[sb].1 *= scale;
}
out
})
.collect()
};
let x0in = scale_x(frame.x0);
let x1in = scale_x(frame.x1);
let g_sum_1 = pb_matrix_sum3(frame.g1_dq, frame.g3_dq, frame.g5_dq);
let g_sum_2 = pb_matrix_sum3(frame.g2_dq, frame.g4_dq, frame.g6_dq);
let v1 = transform(
&x0in,
&x1in,
frame.g1_dq,
frame.g2_dq,
frame.num_param_bands,
&state.g1_prev_sb,
&state.g2_prev_sb,
frame.steep,
frame.param_timeslots,
);
let v2 = transform(
&x0in,
&x1in,
frame.g3_dq,
frame.g4_dq,
frame.num_param_bands,
&state.g3_prev_sb,
&state.g4_prev_sb,
frame.steep,
frame.param_timeslots,
);
let v3 = transform(
&x0in,
&x1in,
&g_sum_1,
&g_sum_2,
frame.num_param_bands,
&vec![0.0; NUM_QMF_SUBBANDS],
&vec![0.0; NUM_QMF_SUBBANDS],
frame.steep,
frame.param_timeslots,
);
let mut u0 = Vec::with_capacity(num_ts);
let mut u1 = Vec::with_capacity(num_ts);
let mut u2 = Vec::with_capacity(num_ts);
for ts in 0..num_ts {
let mut col0 = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
let mut col1 = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
let mut col2 = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
for sb in 0..NUM_QMF_SUBBANDS as u32 {
col0[sb as usize] = state.d0.process_sample(sb, v1[ts][sb as usize]);
col1[sb as usize] = state.d1.process_sample(sb, v2[ts][sb as usize]);
col2[sb as usize] = state.d2.process_sample(sb, v3[ts][sb as usize]);
}
u0.push(col0);
u1.push(col1);
u2.push(col2);
}
let apply_ducker = |u: &[[(f32, f32); NUM_QMF_SUBBANDS]],
ducker: &mut TransientDucker|
-> Vec<[(f32, f32); NUM_QMF_SUBBANDS]> {
u.iter()
.map(|col| {
let p_energy = compute_p_energy(col, frame.num_param_bands);
let duck = ducker.update(&p_energy);
apply_transient_ducker(col, &duck, frame.num_param_bands)
})
.collect()
};
let y0 = apply_ducker(&u0, &mut state.ducker0);
let y1 = apply_ducker(&u1, &mut state.ducker1);
let y2 = apply_ducker(&u2, &mut state.ducker2);
let g1_a1 = pb_matrix_mul(frame.g1_dq, frame.alpha_1_dq);
let g2_a1 = pb_matrix_mul(frame.g2_dq, frame.alpha_1_dq);
let (mut z0, mut z1) = acpl_module2(
&x0in,
&x1in,
&y0,
frame.g1_dq,
frame.g2_dq,
&g1_a1,
&g2_a1,
frame.beta_1_dq,
frame.num_param_bands,
frame.steep,
frame.param_timeslots,
);
let g3_a2 = pb_matrix_mul(frame.g3_dq, frame.alpha_2_dq);
let g4_a2 = pb_matrix_mul(frame.g4_dq, frame.alpha_2_dq);
let (mut z2, mut z3) = acpl_module2(
&x0in,
&x1in,
&y1,
frame.g3_dq,
frame.g4_dq,
&g3_a2,
&g4_a2,
frame.beta_2_dq,
frame.num_param_bands,
frame.steep,
frame.param_timeslots,
);
let zero_y = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let zero_pb: Vec<Vec<f32>> = frame
.g5_dq
.iter()
.map(|row| vec![0.0f32; row.len()])
.collect();
let (mut z4, _z5) = acpl_module2(
&x0in,
&x1in,
&zero_y,
frame.g5_dq,
frame.g6_dq,
frame.g5_dq, frame.g6_dq, &zero_pb,
frame.num_param_bands,
frame.steep,
frame.param_timeslots,
);
let b3_a1 = pb_matrix_mul(frame.beta_3_dq, frame.alpha_1_dq);
acpl_module3(
&mut z0,
&mut z1,
&y2,
frame.beta_3_dq,
&b3_a1,
frame.num_param_bands,
frame.steep,
frame.param_timeslots,
);
let b3_a2 = pb_matrix_mul(frame.beta_3_dq, frame.alpha_2_dq);
acpl_module3(
&mut z2,
&mut z3,
&y2,
frame.beta_3_dq,
&b3_a2,
frame.num_param_bands,
frame.steep,
frame.param_timeslots,
);
let neg_b3 = pb_matrix_scale(frame.beta_3_dq, -1.0);
let neg_b3_a = pb_matrix_scale(frame.beta_3_dq, -1.0); let mut z5_dummy = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
acpl_module3(
&mut z4,
&mut z5_dummy,
&y2,
&neg_b3,
&neg_b3_a,
frame.num_param_bands,
frame.steep,
frame.param_timeslots,
);
let sq2 = (2.0f32).sqrt();
let scale_inplace = |z: &mut [[(f32, f32); NUM_QMF_SUBBANDS]], s: f32| {
for col in z.iter_mut() {
for sb in 0..NUM_QMF_SUBBANDS {
col[sb].0 *= s;
col[sb].1 *= s;
}
}
};
scale_inplace(&mut z1, sq2);
scale_inplace(&mut z3, sq2);
scale_inplace(&mut z4, sq2);
let update_prev = |prev: &mut Vec<f32>, src_pb: &[Vec<f32>]| {
if !src_pb.is_empty() {
let last = &src_pb[src_pb.len() - 1];
let sb = expand_pb_to_sb(std::slice::from_ref(last), frame.num_param_bands);
update_param_prev(prev, &sb[0]);
}
};
update_prev(&mut state.g1_prev_sb, frame.g1_dq);
update_prev(&mut state.g2_prev_sb, frame.g2_dq);
update_prev(&mut state.g3_prev_sb, frame.g3_dq);
update_prev(&mut state.g4_prev_sb, frame.g4_dq);
AcplMchOutput { z0, z2, z4, z1, z3 }
}
#[derive(Debug, Clone)]
pub struct Acpl5xPairState {
pub left_pair: AcplCpeState,
pub right_pair: AcplCpeState,
pub alpha1_diff: AcplDiffState,
pub beta1_diff: AcplDiffState,
pub alpha2_diff: AcplDiffState,
pub beta2_diff: AcplDiffState,
}
impl Acpl5xPairState {
pub fn new() -> Self {
Self {
left_pair: AcplCpeState::new(DecorrelatorId::D0),
right_pair: AcplCpeState::new(DecorrelatorId::D1),
alpha1_diff: AcplDiffState::new(),
beta1_diff: AcplDiffState::new(),
alpha2_diff: AcplDiffState::new(),
beta2_diff: AcplDiffState::new(),
}
}
}
impl Default for Acpl5xPairState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Acpl5xPairMode {
AspxAcpl1,
AspxAcpl2,
}
pub struct Acpl5xPairFrame<'a> {
pub mode: Acpl5xPairMode,
pub x0: &'a [[(f32, f32); NUM_QMF_SUBBANDS]],
pub x1: &'a [[(f32, f32); NUM_QMF_SUBBANDS]],
pub x2: &'a [[(f32, f32); NUM_QMF_SUBBANDS]],
pub x3: Option<&'a [[(f32, f32); NUM_QMF_SUBBANDS]]>,
pub x4: Option<&'a [[(f32, f32); NUM_QMF_SUBBANDS]]>,
pub alpha_1_dq: &'a [Vec<f32>],
pub beta_1_dq: &'a [Vec<f32>],
pub alpha_2_dq: &'a [Vec<f32>],
pub beta_2_dq: &'a [Vec<f32>],
pub num_param_bands: u32,
pub acpl_qmf_band: u32,
pub steep_1: bool,
pub steep_2: bool,
pub param_timeslots_1: &'a [u8],
pub param_timeslots_2: &'a [u8],
}
pub struct Acpl5xPairOutput {
pub z0: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>, pub z1: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>, pub z2: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>, pub z3: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>, pub z4: Vec<[(f32, f32); NUM_QMF_SUBBANDS]>, }
pub fn run_pseudocode_117_5x(
state: &mut Acpl5xPairState,
frame: Acpl5xPairFrame<'_>,
) -> Acpl5xPairOutput {
let num_ts = frame.x0.len();
debug_assert_eq!(frame.x1.len(), num_ts);
debug_assert_eq!(frame.x2.len(), num_ts);
let pair1_x1 = match frame.mode {
Acpl5xPairMode::AspxAcpl1 => frame.x3,
Acpl5xPairMode::AspxAcpl2 => None,
};
let pair1 = AcplCpeFrame {
x0: frame.x0,
x1: pair1_x1,
alpha_dq: frame.alpha_1_dq,
beta_dq: frame.beta_1_dq,
num_param_bands: frame.num_param_bands,
acpl_qmf_band: frame.acpl_qmf_band,
steep: frame.steep_1,
param_timeslots: frame.param_timeslots_1,
};
let out1 = run_pseudocode_115_pair(&mut state.left_pair, pair1);
let pair2_x1 = match frame.mode {
Acpl5xPairMode::AspxAcpl1 => frame.x4,
Acpl5xPairMode::AspxAcpl2 => None,
};
let pair2 = AcplCpeFrame {
x0: frame.x1,
x1: pair2_x1,
alpha_dq: frame.alpha_2_dq,
beta_dq: frame.beta_2_dq,
num_param_bands: frame.num_param_bands,
acpl_qmf_band: frame.acpl_qmf_band,
steep: frame.steep_2,
param_timeslots: frame.param_timeslots_2,
};
let out2 = run_pseudocode_115_pair(&mut state.right_pair, pair2);
let sq2 = (2.0f32).sqrt();
let scale_inplace = |z: &mut [[(f32, f32); NUM_QMF_SUBBANDS]], s: f32| {
for col in z.iter_mut() {
for sb in 0..NUM_QMF_SUBBANDS {
col[sb].0 *= s;
col[sb].1 *= s;
}
}
};
let mut z0 = out1.z0;
let mut z1 = out1.z1;
let mut z2 = out2.z0;
let mut z3 = out2.z1;
scale_inplace(&mut z1, sq2);
scale_inplace(&mut z3, sq2);
let z4: Vec<[(f32, f32); NUM_QMF_SUBBANDS]> = frame.x2.to_vec();
let _ = (&mut z0, &mut z2);
Acpl5xPairOutput { z0, z1, z2, z3, z4 }
}
use crate::acpl::{AcplConfig1ch, AcplData1ch, AcplInterpolationType};
use crate::qmf::{QmfAnalysisBank, QmfSynthesisBank};
#[derive(Debug, Clone)]
pub struct AcplSubstreamState {
pub alpha_diff: AcplDiffState,
pub beta_diff: AcplDiffState,
pub cpe: AcplCpeState,
}
impl AcplSubstreamState {
pub fn new() -> Self {
Self {
alpha_diff: AcplDiffState::new(),
beta_diff: AcplDiffState::new(),
cpe: AcplCpeState::new(DecorrelatorId::D0),
}
}
}
impl Default for AcplSubstreamState {
fn default() -> Self {
Self::new()
}
}
pub fn run_acpl_1ch_pcm(
pcm_in: &[f32],
cfg: &AcplConfig1ch,
data: &AcplData1ch,
state: &mut AcplSubstreamState,
) -> Option<(Vec<f32>, Vec<f32>)> {
if pcm_in.is_empty() || pcm_in.len() % NUM_QMF_SUBBANDS != 0 {
return None;
}
let n_slots = pcm_in.len() / NUM_QMF_SUBBANDS;
if n_slots == 0 {
return None;
}
let mut ana = QmfAnalysisBank::new();
let x0 = ana.process_block(pcm_in);
let alpha_q = differential_decode(&data.alpha1, cfg.num_param_bands, &mut state.alpha_diff);
let beta_q = differential_decode(&data.beta1, cfg.num_param_bands, &mut state.beta_diff);
let (alpha_dq, beta_dq) = dequantize_alpha_beta(&alpha_q, &beta_q, cfg.quant_mode);
if alpha_dq.is_empty() {
return None;
}
let frame = AcplCpeFrame {
x0: &x0,
x1: None,
alpha_dq: &alpha_dq,
beta_dq: &beta_dq,
num_param_bands: cfg.num_param_bands,
acpl_qmf_band: cfg.qmf_band as u32,
steep: matches!(
data.framing.interpolation_type,
AcplInterpolationType::Steep
),
param_timeslots: &data.framing.param_timeslots,
};
let out = run_pseudocode_115_pair(&mut state.cpe, frame);
let mut syn0 = QmfSynthesisBank::new();
let mut syn1 = QmfSynthesisBank::new();
let mut left = Vec::with_capacity(pcm_in.len());
let mut right = Vec::with_capacity(pcm_in.len());
for ts in 0..n_slots {
left.extend_from_slice(&syn0.process_slot(&out.z0[ts]));
right.extend_from_slice(&syn1.process_slot(&out.z1[ts]));
}
Some((left, right))
}
pub fn run_acpl_1ch_pcm_stereo(
pcm_m: &[f32],
pcm_s: &[f32],
cfg: &AcplConfig1ch,
data: &AcplData1ch,
state: &mut AcplSubstreamState,
) -> Option<(Vec<f32>, Vec<f32>)> {
if pcm_m.is_empty() || pcm_m.len() % NUM_QMF_SUBBANDS != 0 || pcm_m.len() != pcm_s.len() {
return None;
}
let n_slots = pcm_m.len() / NUM_QMF_SUBBANDS;
if n_slots == 0 {
return None;
}
let mut ana_m = QmfAnalysisBank::new();
let mut ana_s = QmfAnalysisBank::new();
let x0 = ana_m.process_block(pcm_m);
let x1 = ana_s.process_block(pcm_s);
let alpha_q = differential_decode(&data.alpha1, cfg.num_param_bands, &mut state.alpha_diff);
let beta_q = differential_decode(&data.beta1, cfg.num_param_bands, &mut state.beta_diff);
let (alpha_dq, beta_dq) = dequantize_alpha_beta(&alpha_q, &beta_q, cfg.quant_mode);
if alpha_dq.is_empty() {
return None;
}
let frame = AcplCpeFrame {
x0: &x0,
x1: Some(&x1),
alpha_dq: &alpha_dq,
beta_dq: &beta_dq,
num_param_bands: cfg.num_param_bands,
acpl_qmf_band: cfg.qmf_band as u32,
steep: matches!(
data.framing.interpolation_type,
AcplInterpolationType::Steep
),
param_timeslots: &data.framing.param_timeslots,
};
let out = run_pseudocode_115_pair(&mut state.cpe, frame);
let mut syn0 = QmfSynthesisBank::new();
let mut syn1 = QmfSynthesisBank::new();
let mut left = Vec::with_capacity(pcm_m.len());
let mut right = Vec::with_capacity(pcm_m.len());
for ts in 0..n_slots {
left.extend_from_slice(&syn0.process_slot(&out.z0[ts]));
right.extend_from_slice(&syn1.process_slot(&out.z1[ts]));
}
Some((left, right))
}
use crate::acpl::{AcplConfig2ch, AcplData1ch as AcplData1chTy, AcplData2ch};
#[derive(Debug, Clone)]
pub struct Acpl5xPcmOutput {
pub left: Vec<f32>,
pub right: Vec<f32>,
pub centre: Vec<f32>,
pub left_surround: Vec<f32>,
pub right_surround: Vec<f32>,
}
pub struct Acpl5xPairPcmState {
pub pair: Acpl5xPairState,
pub ana_l: QmfAnalysisBank,
pub ana_r: QmfAnalysisBank,
pub ana_c: QmfAnalysisBank,
pub ana_ls: QmfAnalysisBank,
pub ana_rs: QmfAnalysisBank,
pub syn_l: QmfSynthesisBank,
pub syn_r: QmfSynthesisBank,
pub syn_c: QmfSynthesisBank,
pub syn_ls: QmfSynthesisBank,
pub syn_rs: QmfSynthesisBank,
}
impl Acpl5xPairPcmState {
pub fn new() -> Self {
Self {
pair: Acpl5xPairState::new(),
ana_l: QmfAnalysisBank::new(),
ana_r: QmfAnalysisBank::new(),
ana_c: QmfAnalysisBank::new(),
ana_ls: QmfAnalysisBank::new(),
ana_rs: QmfAnalysisBank::new(),
syn_l: QmfSynthesisBank::new(),
syn_r: QmfSynthesisBank::new(),
syn_c: QmfSynthesisBank::new(),
syn_ls: QmfSynthesisBank::new(),
syn_rs: QmfSynthesisBank::new(),
}
}
}
impl Default for Acpl5xPairPcmState {
fn default() -> Self {
Self::new()
}
}
#[allow(clippy::too_many_arguments)]
pub fn run_acpl_5x_pair_pcm(
mode: Acpl5xPairMode,
pcm_l: &[f32],
pcm_r: &[f32],
pcm_c: &[f32],
pcm_ls: Option<&[f32]>,
pcm_rs: Option<&[f32]>,
cfg_1: &AcplConfig1ch,
data_1: &AcplData1chTy,
cfg_2: &AcplConfig1ch,
data_2: &AcplData1chTy,
state: &mut Acpl5xPairPcmState,
) -> Option<Acpl5xPcmOutput> {
if pcm_l.is_empty() || pcm_l.len() % NUM_QMF_SUBBANDS != 0 {
return None;
}
if pcm_l.len() != pcm_r.len() || pcm_l.len() != pcm_c.len() {
return None;
}
let n_slots = pcm_l.len() / NUM_QMF_SUBBANDS;
if n_slots == 0 {
return None;
}
match mode {
Acpl5xPairMode::AspxAcpl1 => {
let (ls, rs) = (pcm_ls?, pcm_rs?);
if ls.len() != pcm_l.len() || rs.len() != pcm_l.len() {
return None;
}
}
Acpl5xPairMode::AspxAcpl2 => {
if pcm_ls.is_some() || pcm_rs.is_some() {
return None;
}
}
}
let x0 = state.ana_l.process_block(pcm_l);
let x1 = state.ana_r.process_block(pcm_r);
let x2 = state.ana_c.process_block(pcm_c);
let x3_owned = pcm_ls.map(|p| state.ana_ls.process_block(p));
let x4_owned = pcm_rs.map(|p| state.ana_rs.process_block(p));
let alpha1_q = differential_decode(
&data_1.alpha1,
cfg_1.num_param_bands,
&mut state.pair.alpha1_diff,
);
let beta1_q = differential_decode(
&data_1.beta1,
cfg_1.num_param_bands,
&mut state.pair.beta1_diff,
);
let (alpha1_dq, beta1_dq) = dequantize_alpha_beta(&alpha1_q, &beta1_q, cfg_1.quant_mode);
let alpha2_q = differential_decode(
&data_2.alpha1,
cfg_2.num_param_bands,
&mut state.pair.alpha2_diff,
);
let beta2_q = differential_decode(
&data_2.beta1,
cfg_2.num_param_bands,
&mut state.pair.beta2_diff,
);
let (alpha2_dq, beta2_dq) = dequantize_alpha_beta(&alpha2_q, &beta2_q, cfg_2.quant_mode);
if alpha1_dq.is_empty() || alpha2_dq.is_empty() {
return None;
}
let frame = Acpl5xPairFrame {
mode,
x0: &x0,
x1: &x1,
x2: &x2,
x3: x3_owned.as_deref(),
x4: x4_owned.as_deref(),
alpha_1_dq: &alpha1_dq,
beta_1_dq: &beta1_dq,
alpha_2_dq: &alpha2_dq,
beta_2_dq: &beta2_dq,
num_param_bands: cfg_1.num_param_bands,
acpl_qmf_band: 0,
steep_1: matches!(
data_1.framing.interpolation_type,
AcplInterpolationType::Steep
),
steep_2: matches!(
data_2.framing.interpolation_type,
AcplInterpolationType::Steep
),
param_timeslots_1: &data_1.framing.param_timeslots,
param_timeslots_2: &data_2.framing.param_timeslots,
};
let out = run_pseudocode_117_5x(&mut state.pair, frame);
let mut left = Vec::with_capacity(pcm_l.len());
let mut right = Vec::with_capacity(pcm_l.len());
let mut centre = Vec::with_capacity(pcm_l.len());
let mut left_surround = Vec::with_capacity(pcm_l.len());
let mut right_surround = Vec::with_capacity(pcm_l.len());
for ts in 0..n_slots {
left.extend_from_slice(&state.syn_l.process_slot(&out.z0[ts]));
right.extend_from_slice(&state.syn_r.process_slot(&out.z2[ts]));
centre.extend_from_slice(&state.syn_c.process_slot(&out.z4[ts]));
left_surround.extend_from_slice(&state.syn_ls.process_slot(&out.z1[ts]));
right_surround.extend_from_slice(&state.syn_rs.process_slot(&out.z3[ts]));
}
Some(Acpl5xPcmOutput {
left,
right,
centre,
left_surround,
right_surround,
})
}
pub struct Acpl5xMchPcmState {
pub mch: AcplMchState,
pub ana_l: QmfAnalysisBank,
pub ana_r: QmfAnalysisBank,
pub ana_c: QmfAnalysisBank,
pub syn_l: QmfSynthesisBank,
pub syn_r: QmfSynthesisBank,
pub syn_c: QmfSynthesisBank,
pub syn_ls: QmfSynthesisBank,
pub syn_rs: QmfSynthesisBank,
pub alpha1_diff: AcplDiffState,
pub alpha2_diff: AcplDiffState,
pub beta1_diff: AcplDiffState,
pub beta2_diff: AcplDiffState,
pub beta3_diff: AcplDiffState,
pub gamma_diff: [AcplDiffState; 6],
}
impl Acpl5xMchPcmState {
pub fn new() -> Self {
Self {
mch: AcplMchState::new(),
ana_l: QmfAnalysisBank::new(),
ana_r: QmfAnalysisBank::new(),
ana_c: QmfAnalysisBank::new(),
syn_l: QmfSynthesisBank::new(),
syn_r: QmfSynthesisBank::new(),
syn_c: QmfSynthesisBank::new(),
syn_ls: QmfSynthesisBank::new(),
syn_rs: QmfSynthesisBank::new(),
alpha1_diff: AcplDiffState::new(),
alpha2_diff: AcplDiffState::new(),
beta1_diff: AcplDiffState::new(),
beta2_diff: AcplDiffState::new(),
beta3_diff: AcplDiffState::new(),
gamma_diff: [
AcplDiffState::new(),
AcplDiffState::new(),
AcplDiffState::new(),
AcplDiffState::new(),
AcplDiffState::new(),
AcplDiffState::new(),
],
}
}
}
impl Default for Acpl5xMchPcmState {
fn default() -> Self {
Self::new()
}
}
pub fn run_acpl_5x_mch_pcm(
pcm_l: &[f32],
pcm_r: &[f32],
pcm_c: &[f32],
cfg: &AcplConfig2ch,
data: &AcplData2ch,
state: &mut Acpl5xMchPcmState,
) -> Option<Acpl5xPcmOutput> {
if pcm_l.is_empty() || pcm_l.len() % NUM_QMF_SUBBANDS != 0 {
return None;
}
if pcm_l.len() != pcm_r.len() || pcm_l.len() != pcm_c.len() {
return None;
}
let n_slots = pcm_l.len() / NUM_QMF_SUBBANDS;
if n_slots == 0 {
return None;
}
let x0 = state.ana_l.process_block(pcm_l);
let x1 = state.ana_r.process_block(pcm_r);
let x2 = state.ana_c.process_block(pcm_c);
let nb = cfg.num_param_bands;
let alpha1_q = differential_decode(&data.alpha1, nb, &mut state.alpha1_diff);
let alpha2_q = differential_decode(&data.alpha2, nb, &mut state.alpha2_diff);
let beta1_q = differential_decode(&data.beta1, nb, &mut state.beta1_diff);
let beta2_q = differential_decode(&data.beta2, nb, &mut state.beta2_diff);
let beta3_q = differential_decode(&data.beta3, nb, &mut state.beta3_diff);
let g1q = differential_decode(&data.gamma1, nb, &mut state.gamma_diff[0]);
let g2q = differential_decode(&data.gamma2, nb, &mut state.gamma_diff[1]);
let g3q = differential_decode(&data.gamma3, nb, &mut state.gamma_diff[2]);
let g4q = differential_decode(&data.gamma4, nb, &mut state.gamma_diff[3]);
let g5q = differential_decode(&data.gamma5, nb, &mut state.gamma_diff[4]);
let g6q = differential_decode(&data.gamma6, nb, &mut state.gamma_diff[5]);
let (alpha1_dq, beta1_dq) = dequantize_alpha_beta(&alpha1_q, &beta1_q, cfg.quant_mode_0);
let (alpha2_dq, beta2_dq) = dequantize_alpha_beta(&alpha2_q, &beta2_q, cfg.quant_mode_0);
let beta3_dq = dequantize_beta3(&beta3_q, cfg.quant_mode_0);
let g1_dq = dequantize_gamma(&g1q, cfg.quant_mode_1);
let g2_dq = dequantize_gamma(&g2q, cfg.quant_mode_1);
let g3_dq = dequantize_gamma(&g3q, cfg.quant_mode_1);
let g4_dq = dequantize_gamma(&g4q, cfg.quant_mode_1);
let g5_dq = dequantize_gamma(&g5q, cfg.quant_mode_1);
let g6_dq = dequantize_gamma(&g6q, cfg.quant_mode_1);
if alpha1_dq.is_empty() {
return None;
}
let frame = AcplMchFrame {
x0: &x0,
x1: &x1,
x2: &x2,
alpha_1_dq: &alpha1_dq,
alpha_2_dq: &alpha2_dq,
beta_1_dq: &beta1_dq,
beta_2_dq: &beta2_dq,
beta_3_dq: &beta3_dq,
g1_dq: &g1_dq,
g2_dq: &g2_dq,
g3_dq: &g3_dq,
g4_dq: &g4_dq,
g5_dq: &g5_dq,
g6_dq: &g6_dq,
num_param_bands: nb,
steep: matches!(
data.framing.interpolation_type,
AcplInterpolationType::Steep
),
param_timeslots: &data.framing.param_timeslots,
};
let out = run_pseudocode_118_5x(&mut state.mch, frame);
let mut left = Vec::with_capacity(pcm_l.len());
let mut right = Vec::with_capacity(pcm_l.len());
let mut centre = Vec::with_capacity(pcm_l.len());
let mut left_surround = Vec::with_capacity(pcm_l.len());
let mut right_surround = Vec::with_capacity(pcm_l.len());
for ts in 0..n_slots {
left.extend_from_slice(&state.syn_l.process_slot(&out.z0[ts]));
right.extend_from_slice(&state.syn_r.process_slot(&out.z2[ts]));
centre.extend_from_slice(&state.syn_c.process_slot(&out.z4[ts]));
left_surround.extend_from_slice(&state.syn_ls.process_slot(&out.z1[ts]));
right_surround.extend_from_slice(&state.syn_rs.process_slot(&out.z3[ts]));
}
Some(Acpl5xPcmOutput {
left,
right,
centre,
left_surround,
right_surround,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::acpl::AcplHuffParam;
#[test]
fn alpha_dq_fine_anchors_table_203() {
assert_eq!(ALPHA_DQ_FINE[0], -2.000000);
assert_eq!(ALPHA_DQ_FINE[16], 0.000000);
assert_eq!(ALPHA_DQ_FINE[32], 2.000000);
for k in 1..=16 {
let lo = ALPHA_DQ_FINE[16 - k];
let hi = ALPHA_DQ_FINE[16 + k];
assert!((lo + hi).abs() < 1e-6, "k={k} lo={lo} hi={hi}");
}
}
#[test]
fn alpha_dq_coarse_anchors_table_205() {
assert_eq!(ALPHA_DQ_COARSE[0], -2.000000);
assert_eq!(ALPHA_DQ_COARSE[8], 0.000000);
assert_eq!(ALPHA_DQ_COARSE[16], 2.000000);
for k in 1..=8 {
let lo = ALPHA_DQ_COARSE[8 - k];
let hi = ALPHA_DQ_COARSE[8 + k];
assert!((lo + hi).abs() < 1e-6, "k={k} lo={lo} hi={hi}");
}
}
#[test]
fn ibeta_fine_table_203_column2() {
assert_eq!(IBETA_FINE[0], 0);
assert_eq!(IBETA_FINE[8], 8);
assert_eq!(IBETA_FINE[16], 0);
assert_eq!(IBETA_FINE[24], 8);
assert_eq!(IBETA_FINE[32], 0);
}
#[test]
fn ibeta_coarse_table_205_column2() {
assert_eq!(IBETA_COARSE[0], 0);
assert_eq!(IBETA_COARSE[4], 4);
assert_eq!(IBETA_COARSE[8], 0);
assert_eq!(IBETA_COARSE[12], 4);
assert_eq!(IBETA_COARSE[16], 0);
}
#[test]
fn beta_dq_fine_anchors_table_204() {
for col in 0..9 {
assert_eq!(BETA_DQ_FINE[0][col], 0.0);
}
assert_eq!(BETA_DQ_FINE[8][0], 4.0);
assert_eq!(BETA_DQ_FINE[8][8], 1.0);
for q in 1..=8 {
for c in 1..9 {
assert!(
BETA_DQ_FINE[q][c] < BETA_DQ_FINE[q][c - 1],
"beta_q={q} col={c}"
);
}
}
}
#[test]
fn beta_dq_coarse_anchors_table_206() {
assert_eq!(BETA_DQ_COARSE[0][0], 0.0);
assert_eq!(BETA_DQ_COARSE[4][0], 4.0);
assert_eq!(BETA_DQ_COARSE[4][4], 1.0);
for q in 1..=4 {
for c in 1..5 {
assert!(
BETA_DQ_COARSE[q][c] < BETA_DQ_COARSE[q][c - 1],
"beta_q={q} col={c}"
);
}
}
}
#[test]
fn beta3_and_gamma_deltas_match_tables_207_208() {
assert_eq!(beta3_delta(AcplQuantMode::Fine), 0.125);
assert_eq!(beta3_delta(AcplQuantMode::Coarse), 0.25);
assert!((gamma_delta(AcplQuantMode::Fine) - 1638.0 / 16384.0).abs() < 1e-9);
assert!((gamma_delta(AcplQuantMode::Coarse) - 3276.0 / 16384.0).abs() < 1e-9);
}
#[test]
fn dequantize_alpha_fine_round_trips_through_signed_index() {
let (a, ib) = dequantize_alpha_index(AcplQuantMode::Fine, 0);
assert_eq!(a, 0.0);
assert_eq!(ib, 0);
let (a, ib) = dequantize_alpha_index(AcplQuantMode::Fine, -16);
assert_eq!(a, -2.000000);
assert_eq!(ib, 0);
let (a, ib) = dequantize_alpha_index(AcplQuantMode::Fine, 8);
assert_eq!(a, 1.000000);
assert_eq!(ib, 8);
}
#[test]
fn dequantize_beta_fine_uses_ibeta_column() {
let v = dequantize_beta_index(AcplQuantMode::Fine, 1, 0);
assert!((v - 0.2375).abs() < 1e-6);
let v = dequantize_beta_index(AcplQuantMode::Fine, -1, 0);
assert!((v + 0.2375).abs() < 1e-6);
let v = dequantize_beta_index(AcplQuantMode::Fine, 8, 8);
assert!((v - 1.0).abs() < 1e-6);
}
#[test]
fn differential_decode_freq_accumulates_from_seed() {
let p = AcplHuffParam {
values: vec![5, 1, -2, 3],
direction_time: false,
};
let mut st = AcplDiffState::new();
let out = differential_decode(&[p], 4, &mut st);
assert_eq!(out, vec![vec![5, 6, 4, 7]]);
assert_eq!(st.q_prev, vec![5, 6, 4, 7]);
}
#[test]
fn differential_decode_time_uses_prev_then_carries() {
let p1 = AcplHuffParam {
values: vec![1, 2, 3],
direction_time: false,
};
let p2 = AcplHuffParam {
values: vec![10, -1, 0],
direction_time: true,
};
let mut st = AcplDiffState::new();
let out = differential_decode(&[p1, p2], 3, &mut st);
assert_eq!(out, vec![vec![1, 3, 6], vec![11, 2, 6]]);
assert_eq!(st.q_prev, vec![11, 2, 6]);
}
#[test]
fn differential_decode_carries_across_frames() {
let mut st = AcplDiffState::new();
let p1 = AcplHuffParam {
values: vec![4, 1],
direction_time: false,
};
let _ = differential_decode(&[p1], 2, &mut st);
assert_eq!(st.q_prev, vec![4, 5]);
let p2 = AcplHuffParam {
values: vec![1, -2],
direction_time: true,
};
let out2 = differential_decode(&[p2], 2, &mut st);
assert_eq!(out2, vec![vec![5, 3]]);
assert_eq!(st.q_prev, vec![5, 3]);
}
#[test]
fn dequantize_alpha_beta_returns_two_synced_matrices() {
let alpha_q = vec![vec![0i32, 8]];
let beta_q = vec![vec![1i32, -1]];
let (a, b) = dequantize_alpha_beta(&alpha_q, &beta_q, AcplQuantMode::Fine);
assert_eq!(a.len(), 1);
assert_eq!(a[0].len(), 2);
assert!((a[0][0] - 0.0).abs() < 1e-6);
assert!((a[0][1] - 1.0).abs() < 1e-6);
assert!((b[0][0] - 0.2375).abs() < 1e-6);
assert!((b[0][1] + 0.0593750).abs() < 1e-6);
}
#[test]
fn interpolate_smooth_one_param_set_linear_ramp() {
let p_set = vec![vec![4.0f32; NUM_QMF_SUBBANDS]];
let prev = vec![0.0f32; NUM_QMF_SUBBANDS];
let inp = InterpInputs {
by_pset: &p_set,
prev: &prev,
};
for ts in 0..4u32 {
let v = interpolate(&inp, 1, 0, ts, 4, false, &[]);
let expected = (ts + 1) as f32;
assert!(
(v - expected).abs() < 1e-6,
"ts={ts} v={v} expected={expected}"
);
}
}
#[test]
fn interpolate_steep_falls_to_constants() {
let p_set = vec![vec![5.0f32; NUM_QMF_SUBBANDS]];
let prev = vec![1.0f32; NUM_QMF_SUBBANDS];
let inp = InterpInputs {
by_pset: &p_set,
prev: &prev,
};
assert_eq!(interpolate(&inp, 1, 0, 0, 4, true, &[2]), 1.0);
assert_eq!(interpolate(&inp, 1, 0, 1, 4, true, &[2]), 1.0);
assert_eq!(interpolate(&inp, 1, 0, 2, 4, true, &[2]), 5.0);
assert_eq!(interpolate(&inp, 1, 0, 3, 4, true, &[2]), 5.0);
}
#[test]
fn interpolate_smooth_two_sets_meets_at_midpoint() {
let p_set = vec![
vec![4.0f32; NUM_QMF_SUBBANDS],
vec![8.0f32; NUM_QMF_SUBBANDS],
];
let prev = vec![0.0f32; NUM_QMF_SUBBANDS];
let inp = InterpInputs {
by_pset: &p_set,
prev: &prev,
};
for ts in 0..8u32 {
let v = interpolate(&inp, 2, 0, ts, 8, false, &[]);
let expected = (ts + 1) as f32;
assert!(
(v - expected).abs() < 1e-5,
"ts={ts} v={v} expected={expected}"
);
}
}
#[test]
fn expand_pb_to_sb_preserves_first_pb_for_low_subbands_15() {
let pb = vec![vec![
0.0f32, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0,
]];
let sb = expand_pb_to_sb(&pb, 15);
assert_eq!(sb[0][0], 0.0);
assert_eq!(sb[0][8], 8.0);
assert_eq!(sb[0][9], 9.0);
assert_eq!(sb[0][14], 11.0); assert_eq!(sb[0][63], 14.0);
}
#[test]
fn region_delay_filter_length_match_table_198() {
assert_eq!(region_delay(0), 7);
assert_eq!(region_filter_length(0), 7);
assert_eq!(region_delay(6), 7);
assert_eq!(region_filter_length(6), 7);
assert_eq!(region_delay(7), 10);
assert_eq!(region_filter_length(7), 4);
assert_eq!(region_delay(22), 10);
assert_eq!(region_filter_length(22), 4);
assert_eq!(region_delay(23), 12);
assert_eq!(region_filter_length(23), 2);
assert_eq!(region_delay(63), 12);
assert_eq!(region_filter_length(63), 2);
}
#[test]
fn region_coeffs_match_tables_199_200_201_first_row() {
assert_eq!(region_coeffs(DecorrelatorId::D0, 0)[0], 1.0);
assert!((region_coeffs(DecorrelatorId::D0, 0)[1] - 0.5306).abs() < 1e-12);
assert!((region_coeffs(DecorrelatorId::D1, 7)[1] - 0.0425).abs() < 1e-12);
assert!((region_coeffs(DecorrelatorId::D2, 23)[1] + 0.6057).abs() < 1e-12);
}
#[test]
fn input_signal_modifier_zero_input_zero_output() {
let mut m = InputSignalModifier::new(DecorrelatorId::D0);
for sb in 0..NUM_QMF_SUBBANDS as u32 {
let y = m.process_sample(sb, (0.0, 0.0));
assert_eq!(y, (0.0, 0.0));
}
}
#[test]
fn input_signal_modifier_impulse_eventually_yields_nonzero() {
let mut m = InputSignalModifier::new(DecorrelatorId::D0);
let mut energy = 0.0f64;
for ts in 0..32 {
let x = if ts == 0 { (1.0f32, 0.0) } else { (0.0, 0.0) };
let y = m.process_sample(0, x);
energy += (y.0 as f64).powi(2) + (y.1 as f64).powi(2);
}
assert!(energy > 0.0, "decorrelator should emit non-zero IIR tail");
}
#[test]
fn transient_ducker_passes_silence_unmodified() {
let mut d = TransientDucker::new();
let p_e = [0.0f32; ACPL_MAX_NUM_PARAM_BANDS];
let g = d.update(&p_e);
for v in g.iter() {
assert_eq!(*v, 1.0, "silence should yield gain 1.0 (no transient)");
}
}
#[test]
fn transient_ducker_attenuates_after_peak() {
let mut d = TransientDucker::new();
let mut spike = [0.0f32; ACPL_MAX_NUM_PARAM_BANDS];
spike[0] = 1.0;
let _ = d.update(&spike);
let zeros = [0.0f32; ACPL_MAX_NUM_PARAM_BANDS];
let g = d.update(&zeros);
assert!(g[0] < 1.0, "ducker should attenuate the post-peak slot");
assert!(g[0] > 0.0);
}
#[test]
fn compute_p_energy_aggregates_per_param_band() {
let mut x = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
x[0] = (3.0, 0.0);
x[1] = (0.0, 4.0);
let e = compute_p_energy(&x, 15);
assert_eq!(e[0], 9.0);
assert_eq!(e[1], 16.0);
for pb in 2..ACPL_MAX_NUM_PARAM_BANDS {
assert_eq!(e[pb], 0.0);
}
}
#[test]
fn apply_transient_ducker_scales_per_pb() {
let x = [(2.0f32, 0.0f32); NUM_QMF_SUBBANDS];
let mut g = [1.0f32; ACPL_MAX_NUM_PARAM_BANDS];
g[0] = 0.5;
let out = apply_transient_ducker(&x, &g, 15);
assert_eq!(out[0].0, 1.0);
assert_eq!(out[1].0, 2.0);
}
#[test]
fn acpl_module_mixes_channels_above_qmf_band_aspx_acpl_2() {
let num_ts = 32usize;
let num_pb = 15u32;
let mut x0 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let zero = [(0.0f32, 0.0f32); NUM_QMF_SUBBANDS];
for ts in 0..num_ts {
let phase = (ts as f32) * 0.5;
x0[ts][12] = (phase.cos(), phase.sin());
x0[ts][30] = (0.5 * phase.cos(), 0.5 * phase.sin());
}
let y: Vec<_> = x0.clone();
let alpha_dq = vec![vec![0.5f32; num_pb as usize]];
let beta_dq = vec![vec![0.5f32; num_pb as usize]];
let frame = AcplCpeFrame {
x0: &x0,
x1: None,
alpha_dq: &alpha_dq,
beta_dq: &beta_dq,
num_param_bands: num_pb,
acpl_qmf_band: 8, steep: false,
param_timeslots: &[],
};
let out = acpl_module(&frame, &y);
assert_eq!(out.z0.len(), num_ts);
assert_eq!(out.z1.len(), num_ts);
let early = 0u32;
let interp_early = 0.5_f32 * ((early + 1) as f32) / (num_ts as f32);
let scale_early = 0.5_f32 * (1.0 + 2.0 * interp_early);
let (z0r, z0i) = out.z0[early as usize][12];
let (x0r, x0i) = x0[early as usize][12];
let exp_re = scale_early * x0r;
let exp_im = scale_early * x0i;
assert!(
(z0r - exp_re).abs() < 1e-5,
"z0r {z0r} vs {exp_re} (scale {scale_early})"
);
assert!((z0i - exp_im).abs() < 1e-5, "z0i {z0i} vs {exp_im}");
assert!(
(z0r - x0r).abs() > 1e-4 || (z0i - x0i).abs() > 1e-4,
"z0[early][12] should differ from x0 (mixing scale != 1.0) — got z0=({z0r},{z0i}) x0=({x0r},{x0i}) scale={scale_early}"
);
for sb in 0..8 {
for ts in 0..num_ts {
let (z0r, z0i) = out.z0[ts][sb];
let (z1r, z1i) = out.z1[ts][sb];
assert!((z0r - 0.5 * x0[ts][sb].0).abs() < 1e-6);
assert!((z0i - 0.5 * x0[ts][sb].1).abs() < 1e-6);
assert!((z1r - 0.5 * x0[ts][sb].0).abs() < 1e-6);
assert!((z1i - 0.5 * x0[ts][sb].1).abs() < 1e-6);
}
}
for ts in 0..num_ts {
for sb in 0..NUM_QMF_SUBBANDS {
assert!(out.z0[ts][sb].0.is_finite());
assert!(out.z0[ts][sb].1.is_finite());
assert!(out.z1[ts][sb].0.is_finite());
assert!(out.z1[ts][sb].1.is_finite());
}
}
let _ = zero;
}
#[test]
fn acpl_module_below_qmf_band_is_mid_side_split() {
let num_ts = 8usize;
let mut x0 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut x1 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
for ts in 0..num_ts {
x0[ts][2] = (1.0, 0.0);
x1[ts][2] = (0.0, 1.0);
}
let y = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let alpha_dq = vec![vec![0.0f32; 15]];
let beta_dq = vec![vec![0.0f32; 15]];
let frame = AcplCpeFrame {
x0: &x0,
x1: Some(&x1),
alpha_dq: &alpha_dq,
beta_dq: &beta_dq,
num_param_bands: 15,
acpl_qmf_band: 4,
steep: false,
param_timeslots: &[],
};
let out = acpl_module(&frame, &y);
for ts in 0..num_ts {
let z0 = out.z0[ts][2];
let z1 = out.z1[ts][2];
assert!((z0.0 - 0.5).abs() < 1e-6);
assert!((z0.1 - 0.5).abs() < 1e-6);
assert!((z1.0 - 0.5).abs() < 1e-6);
assert!((z1.1 + 0.5).abs() < 1e-6);
}
}
#[test]
fn run_pseudocode_115_pair_end_to_end() {
let num_ts = 16usize;
let num_pb = 9u32;
let mut x0 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
for ts in 0..num_ts {
let phase = (ts as f32) * 0.3;
for sb in 0..32 {
x0[ts][sb] = (
0.1 * (phase + sb as f32 * 0.1).cos(),
0.1 * (phase + sb as f32 * 0.1).sin(),
);
}
}
let alpha_dq = vec![vec![0.3f32; num_pb as usize]];
let beta_dq = vec![vec![0.3f32; num_pb as usize]];
let mut state = AcplCpeState::new(DecorrelatorId::D0);
let frame = AcplCpeFrame {
x0: &x0,
x1: None,
alpha_dq: &alpha_dq,
beta_dq: &beta_dq,
num_param_bands: num_pb,
acpl_qmf_band: 4,
steep: false,
param_timeslots: &[],
};
let out = run_pseudocode_115_pair(&mut state, frame);
assert_eq!(out.z0.len(), num_ts);
assert_eq!(out.z1.len(), num_ts);
let mut diff_energy = 0.0f64;
for ts in 0..num_ts {
for sb in 0..NUM_QMF_SUBBANDS {
assert!(out.z0[ts][sb].0.is_finite());
assert!(out.z0[ts][sb].1.is_finite());
assert!(out.z1[ts][sb].0.is_finite());
assert!(out.z1[ts][sb].1.is_finite());
let dr = out.z0[ts][sb].0 - x0[ts][sb].0;
let di = out.z0[ts][sb].1 - x0[ts][sb].1;
diff_energy += (dr as f64).powi(2) + (di as f64).powi(2);
}
}
assert!(diff_energy > 0.0, "output should diverge from x0");
assert_eq!(state.alpha_prev_sb.len(), NUM_QMF_SUBBANDS);
assert_eq!(state.beta_prev_sb.len(), NUM_QMF_SUBBANDS);
for sb in 0..NUM_QMF_SUBBANDS {
assert!((state.alpha_prev_sb[sb] - 0.3).abs() < 1e-6);
assert!((state.beta_prev_sb[sb] - 0.3).abs() < 1e-6);
}
}
#[test]
fn acpl_module_zero_params_passthrough_above_qmf_band() {
let num_ts = 4usize;
let mut x0in = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
for ts in 0..num_ts {
x0in[ts][20] = (2.0, 1.0); }
let y = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let alpha_dq = vec![vec![0.0f32; 15]];
let beta_dq = vec![vec![0.0f32; 15]];
let frame = AcplCpeFrame {
x0: &x0in,
x1: None,
alpha_dq: &alpha_dq,
beta_dq: &beta_dq,
num_param_bands: 15,
acpl_qmf_band: 8,
steep: false,
param_timeslots: &[],
};
let out = acpl_module(&frame, &y);
for ts in 0..num_ts {
let z = out.z0[ts][20];
assert!((z.0 - 1.0).abs() < 1e-6);
assert!((z.1 - 0.5).abs() < 1e-6);
let z1 = out.z1[ts][20];
assert!((z1.0 - 1.0).abs() < 1e-6);
assert!((z1.1 - 0.5).abs() < 1e-6);
}
}
#[test]
fn run_acpl_1ch_pcm_emits_two_channel_pcm_with_energy() {
use crate::acpl::{
Acpl1chMode, AcplConfig1ch, AcplData1ch, AcplFramingData, AcplHuffParam,
AcplInterpolationType, AcplQuantMode,
};
let _ = Acpl1chMode::Full; let n_slots = 32usize;
let n = n_slots * NUM_QMF_SUBBANDS;
let mut pcm = vec![0.0f32; n];
let f = 440.0_f32 / 48_000.0_f32;
for (i, s) in pcm.iter_mut().enumerate() {
*s = (2.0 * std::f32::consts::PI * f * i as f32).sin();
}
let cfg = AcplConfig1ch {
num_param_bands_id: 0,
num_param_bands: 15,
quant_mode: AcplQuantMode::Fine,
qmf_band: 0,
};
let alpha = AcplHuffParam {
values: vec![4i32; cfg.num_param_bands as usize],
direction_time: false,
};
let beta = AcplHuffParam {
values: vec![2i32; cfg.num_param_bands as usize],
direction_time: false,
};
let data = AcplData1ch {
framing: AcplFramingData {
interpolation_type: AcplInterpolationType::Smooth,
num_param_sets_cod: 0,
num_param_sets: 1,
param_timeslots: vec![],
},
alpha1: vec![alpha],
beta1: vec![beta],
};
let mut state = AcplSubstreamState::new();
let (left, right) = run_acpl_1ch_pcm(&pcm, &cfg, &data, &mut state).expect("synth runs");
assert_eq!(left.len(), pcm.len());
assert_eq!(right.len(), pcm.len());
let start = 1024usize;
let e_l: f64 = left[start..].iter().map(|&s| (s as f64).powi(2)).sum();
let e_r: f64 = right[start..].iter().map(|&s| (s as f64).powi(2)).sum();
assert!(e_l > 1e-6, "left channel silent (e={e_l})");
assert!(e_r > 1e-6, "right channel silent (e={e_r})");
let mut diffs = 0usize;
for (l, r) in left[start..].iter().zip(right[start..].iter()) {
if (l - r).abs() > 1e-6 {
diffs += 1;
}
}
assert!(
diffs > (left.len() - start) / 4,
"channels too similar (diffs={diffs})"
);
}
#[test]
fn run_acpl_1ch_pcm_stereo_emits_two_channels_distinct_from_l_r_passthrough() {
use crate::acpl::{
AcplConfig1ch, AcplData1ch, AcplFramingData, AcplHuffParam, AcplInterpolationType,
AcplQuantMode,
};
let n_slots = 32usize;
let n = n_slots * NUM_QMF_SUBBANDS;
let mut pcm_m = vec![0.0f32; n];
let mut pcm_s = vec![0.0f32; n];
let f_m = 440.0_f32 / 48_000.0_f32;
let f_s = 220.0_f32 / 48_000.0_f32;
for i in 0..n {
pcm_m[i] = (2.0 * std::f32::consts::PI * f_m * i as f32).sin();
pcm_s[i] = 0.3 * (2.0 * std::f32::consts::PI * f_s * i as f32).sin();
}
let cfg = AcplConfig1ch {
num_param_bands_id: 0,
num_param_bands: 15,
quant_mode: AcplQuantMode::Fine,
qmf_band: 8,
};
let alpha = AcplHuffParam {
values: vec![4i32; cfg.num_param_bands as usize],
direction_time: false,
};
let beta = AcplHuffParam {
values: vec![2i32; cfg.num_param_bands as usize],
direction_time: false,
};
let data = AcplData1ch {
framing: AcplFramingData {
interpolation_type: AcplInterpolationType::Smooth,
num_param_sets_cod: 0,
num_param_sets: 1,
param_timeslots: vec![],
},
alpha1: vec![alpha],
beta1: vec![beta],
};
let mut state = AcplSubstreamState::new();
let (left, right) = run_acpl_1ch_pcm_stereo(&pcm_m, &pcm_s, &cfg, &data, &mut state)
.expect("stereo synth runs");
assert_eq!(left.len(), pcm_m.len());
assert_eq!(right.len(), pcm_m.len());
let start = 1024usize;
let e_l: f64 = left[start..].iter().map(|&s| (s as f64).powi(2)).sum();
let e_r: f64 = right[start..].iter().map(|&s| (s as f64).powi(2)).sum();
assert!(e_l > 1e-6, "left channel silent (e={e_l})");
assert!(e_r > 1e-6, "right channel silent (e={e_r})");
let mut diff_from_input = 0usize;
for i in start..left.len() {
if (left[i] - pcm_m[i]).abs() > 1e-3 {
diff_from_input += 1;
}
}
assert!(
diff_from_input > (left.len() - start) / 4,
"left channel matches input PCM (diff_from_input={diff_from_input})"
);
}
#[test]
fn run_acpl_1ch_pcm_stereo_rejects_mismatched_lengths() {
use crate::acpl::{
AcplConfig1ch, AcplData1ch, AcplFramingData, AcplInterpolationType, AcplQuantMode,
};
let pcm_m = vec![0.0f32; 64];
let pcm_s = vec![0.0f32; 128];
let cfg = AcplConfig1ch {
num_param_bands_id: 0,
num_param_bands: 15,
quant_mode: AcplQuantMode::Fine,
qmf_band: 0,
};
let data = AcplData1ch {
framing: AcplFramingData {
interpolation_type: AcplInterpolationType::Smooth,
num_param_sets_cod: 0,
num_param_sets: 1,
param_timeslots: vec![],
},
alpha1: vec![],
beta1: vec![],
};
let mut state = AcplSubstreamState::new();
assert!(run_acpl_1ch_pcm_stereo(&pcm_m, &pcm_s, &cfg, &data, &mut state).is_none());
}
#[test]
fn run_acpl_1ch_pcm_rejects_misaligned_pcm() {
use crate::acpl::{
AcplConfig1ch, AcplData1ch, AcplFramingData, AcplInterpolationType, AcplQuantMode,
};
let pcm = vec![0.0f32; 65]; let cfg = AcplConfig1ch {
num_param_bands_id: 0,
num_param_bands: 15,
quant_mode: AcplQuantMode::Fine,
qmf_band: 0,
};
let data = AcplData1ch {
framing: AcplFramingData {
interpolation_type: AcplInterpolationType::Smooth,
num_param_sets_cod: 0,
num_param_sets: 1,
param_timeslots: vec![],
},
alpha1: vec![],
beta1: vec![],
};
let mut state = AcplSubstreamState::new();
assert!(run_acpl_1ch_pcm(&pcm, &cfg, &data, &mut state).is_none());
}
#[test]
fn transform_unit_gammas_select_carriers() {
let num_ts = 8usize;
let mut x0 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut x1 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
for ts in 0..num_ts {
x0[ts][10] = (1.0, 0.0);
x1[ts][10] = (0.0, 1.0);
}
let g1 = vec![vec![1.0f32; 15]];
let g2 = vec![vec![0.0f32; 15]];
let prev = vec![1.0f32; NUM_QMF_SUBBANDS];
let prev_zero = vec![0.0f32; NUM_QMF_SUBBANDS];
let v = transform(&x0, &x1, &g1, &g2, 15, &prev, &prev_zero, false, &[]);
for ts in 0..num_ts {
let (vr, vi) = v[ts][10];
assert!((vr - 1.0).abs() < 1e-5, "ts={ts} vr={vr}, expected x0=1.0");
assert!((vi - 0.0).abs() < 1e-5, "ts={ts} vi={vi}");
assert_eq!(v[ts][20], (0.0, 0.0));
}
let g1 = vec![vec![0.0f32; 15]];
let g2 = vec![vec![1.0f32; 15]];
let prev_g2 = vec![1.0f32; NUM_QMF_SUBBANDS];
let v = transform(&x0, &x1, &g1, &g2, 15, &prev_zero, &prev_g2, false, &[]);
for ts in 0..num_ts {
let (vr, vi) = v[ts][10];
assert!((vr - 0.0).abs() < 1e-5);
assert!((vi - 1.0).abs() < 1e-5);
}
}
#[test]
fn transform_mixes_two_carriers_with_gammas() {
let num_ts = 4usize;
let mut x0 = vec![[(2.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut x1 = vec![[(0.0f32, 3.0f32); NUM_QMF_SUBBANDS]; num_ts];
let g1 = vec![vec![0.5f32; 15]];
let g2 = vec![vec![0.25f32; 15]];
let prev_g1 = vec![0.5f32; NUM_QMF_SUBBANDS];
let prev_g2 = vec![0.25f32; NUM_QMF_SUBBANDS];
let v = transform(&x0, &x1, &g1, &g2, 15, &prev_g1, &prev_g2, false, &[]);
for ts in 0..num_ts {
for sb in 0..NUM_QMF_SUBBANDS {
let (vr, vi) = v[ts][sb];
assert!((vr - 1.0).abs() < 1e-5, "ts={ts} sb={sb} vr={vr}");
assert!((vi - 0.75).abs() < 1e-5, "ts={ts} sb={sb} vi={vi}");
}
}
let _ = (&mut x0, &mut x1);
}
#[test]
fn acpl_module2_zero_gammas_is_silent() {
let num_ts = 4usize;
let x0 = vec![[(1.0f32, 0.5f32); NUM_QMF_SUBBANDS]; num_ts];
let x1 = vec![[(0.5f32, 1.0f32); NUM_QMF_SUBBANDS]; num_ts];
let y = vec![[(2.0f32, 2.0f32); NUM_QMF_SUBBANDS]; num_ts];
let zero = vec![vec![0.0f32; 15]];
let (z0, z1) = acpl_module2(
&x0,
&x1,
&y,
&zero,
&zero,
&zero,
&zero,
&zero,
15,
false,
&[],
);
for ts in 0..num_ts {
for sb in 0..NUM_QMF_SUBBANDS {
assert_eq!(z0[ts][sb], (0.0, 0.0));
assert_eq!(z1[ts][sb], (0.0, 0.0));
}
}
}
#[test]
fn acpl_module2_unit_g1_zero_alpha_beta_passes_half_x0_to_both() {
let num_ts = 4usize;
let mut x0 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let x1 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let y = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
for ts in 0..num_ts {
x0[ts][5] = (4.0, 2.0);
}
let g1 = vec![vec![1.0f32; 15]];
let g2 = vec![vec![0.0f32; 15]];
let g1a = vec![vec![0.0f32; 15]]; let g2a = vec![vec![0.0f32; 15]];
let b = vec![vec![0.0f32; 15]];
let (z0, z1) = acpl_module2(&x0, &x1, &y, &g1, &g2, &g1a, &g2a, &b, 15, false, &[]);
let last = num_ts - 1;
let (z0r, z0i) = z0[last][5];
let (z1r, z1i) = z1[last][5];
assert!((z0r - 2.0).abs() < 1e-5, "z0r={z0r} expected 2.0");
assert!((z0i - 1.0).abs() < 1e-5, "z0i={z0i} expected 1.0");
assert!((z1r - 2.0).abs() < 1e-5, "z1r={z1r}");
assert!((z1i - 1.0).abs() < 1e-5, "z1i={z1i}");
}
#[test]
fn acpl_module3_adds_decorrelator_residual() {
let num_ts = 4usize;
let mut z0 = vec![[(1.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut z1 = vec![[(0.0f32, 1.0f32); NUM_QMF_SUBBANDS]; num_ts];
let y2 = vec![[(4.0f32, 4.0f32); NUM_QMF_SUBBANDS]; num_ts];
let b3 = vec![vec![1.0f32; 15]];
let b3a = vec![vec![0.0f32; 15]];
acpl_module3(&mut z0, &mut z1, &y2, &b3, &b3a, 15, false, &[]);
let last = num_ts - 1;
let (z0r, z0i) = z0[last][7];
assert!((z0r - 2.0).abs() < 1e-5, "z0r={z0r}");
assert!((z0i - 1.0).abs() < 1e-5, "z0i={z0i}");
let (z1r, z1i) = z1[last][7];
assert!((z1r - 1.0).abs() < 1e-5, "z1r={z1r}");
assert!((z1i - 2.0).abs() < 1e-5, "z1i={z1i}");
}
#[test]
fn acpl_module3_zero_beta3_is_noop() {
let num_ts = 4usize;
let mut z0 = vec![[(7.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut z1 = vec![[(0.0f32, 7.0f32); NUM_QMF_SUBBANDS]; num_ts];
let y2 = vec![[(99.0f32, -99.0f32); NUM_QMF_SUBBANDS]; num_ts];
let zero = vec![vec![0.0f32; 15]];
acpl_module3(&mut z0, &mut z1, &y2, &zero, &zero, 15, false, &[]);
for ts in 0..num_ts {
for sb in 0..NUM_QMF_SUBBANDS {
assert_eq!(z0[ts][sb], (7.0, 0.0));
assert_eq!(z1[ts][sb], (0.0, 7.0));
}
}
}
#[test]
fn run_pseudocode_118_5x_emits_five_finite_channels() {
let num_ts = 16usize;
let num_pb = 9u32;
let mut x0 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut x1 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut x2 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
for ts in 0..num_ts {
for sb in 0..32 {
let p = (ts as f32) * 0.2 + sb as f32 * 0.05;
x0[ts][sb] = (0.1 * p.cos(), 0.1 * p.sin());
x1[ts][sb] = (0.05 * p.sin(), 0.05 * p.cos());
x2[ts][sb] = (0.03 * (p * 1.3).cos(), 0.0);
}
}
let alpha_1 = vec![vec![0.4f32; num_pb as usize]];
let alpha_2 = vec![vec![-0.2f32; num_pb as usize]];
let beta_1 = vec![vec![0.3f32; num_pb as usize]];
let beta_2 = vec![vec![0.2f32; num_pb as usize]];
let beta_3 = vec![vec![0.15f32; num_pb as usize]];
let g1 = vec![vec![0.6f32; num_pb as usize]];
let g2 = vec![vec![0.3f32; num_pb as usize]];
let g3 = vec![vec![0.2f32; num_pb as usize]];
let g4 = vec![vec![0.5f32; num_pb as usize]];
let g5 = vec![vec![0.1f32; num_pb as usize]];
let g6 = vec![vec![0.1f32; num_pb as usize]];
let mut state = AcplMchState::new();
let frame = AcplMchFrame {
x0: &x0,
x1: &x1,
x2: &x2,
alpha_1_dq: &alpha_1,
alpha_2_dq: &alpha_2,
beta_1_dq: &beta_1,
beta_2_dq: &beta_2,
beta_3_dq: &beta_3,
g1_dq: &g1,
g2_dq: &g2,
g3_dq: &g3,
g4_dq: &g4,
g5_dq: &g5,
g6_dq: &g6,
num_param_bands: num_pb,
steep: false,
param_timeslots: &[],
};
let out = run_pseudocode_118_5x(&mut state, frame);
assert_eq!(out.z0.len(), num_ts);
assert_eq!(out.z1.len(), num_ts);
assert_eq!(out.z2.len(), num_ts);
assert_eq!(out.z3.len(), num_ts);
assert_eq!(out.z4.len(), num_ts);
for z in [&out.z0, &out.z1, &out.z2, &out.z3, &out.z4] {
for col in z.iter() {
for sb in 0..NUM_QMF_SUBBANDS {
assert!(
col[sb].0.is_finite() && col[sb].1.is_finite(),
"non-finite output: {:?}",
col[sb]
);
}
}
}
for (name, z) in [
("z0=L", &out.z0),
("z1=Ls", &out.z1),
("z2=R", &out.z2),
("z3=Rs", &out.z3),
("z4=C", &out.z4),
] {
let e: f64 = z
.iter()
.flat_map(|col| col.iter())
.map(|s| (s.0 as f64).powi(2) + (s.1 as f64).powi(2))
.sum();
assert!(e > 0.0, "channel {name} silent (energy {e})");
}
assert_eq!(state.g1_prev_sb.len(), NUM_QMF_SUBBANDS);
for sb in 0..NUM_QMF_SUBBANDS {
assert!((state.g1_prev_sb[sb] - 0.6).abs() < 1e-6);
}
}
#[test]
fn run_pseudocode_118_5x_zero_alpha_beta_remains_finite() {
let num_ts = 8usize;
let num_pb = 9u32;
let x0 = vec![[(0.5f32, 0.5f32); NUM_QMF_SUBBANDS]; num_ts];
let x1 = vec![[(0.5f32, -0.5f32); NUM_QMF_SUBBANDS]; num_ts];
let x2 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let zero = vec![vec![0.0f32; num_pb as usize]];
let g1 = vec![vec![1.0f32; num_pb as usize]]; let mut state = AcplMchState::new();
let frame = AcplMchFrame {
x0: &x0,
x1: &x1,
x2: &x2,
alpha_1_dq: &zero,
alpha_2_dq: &zero,
beta_1_dq: &zero,
beta_2_dq: &zero,
beta_3_dq: &zero,
g1_dq: &g1,
g2_dq: &zero,
g3_dq: &zero,
g4_dq: &zero,
g5_dq: &zero,
g6_dq: &zero,
num_param_bands: num_pb,
steep: false,
param_timeslots: &[],
};
let out = run_pseudocode_118_5x(&mut state, frame);
for col in out.z0.iter() {
for sb in 0..NUM_QMF_SUBBANDS {
assert!(col[sb].0.is_finite());
assert!(col[sb].1.is_finite());
}
}
}
#[test]
fn pb_matrix_helpers() {
let a = vec![vec![1.0f32, 2.0, 3.0]];
let b = vec![vec![10.0f32, 20.0, 30.0]];
let c = vec![vec![100.0f32, 200.0, 300.0]];
let prod = pb_matrix_mul(&a, &b);
assert_eq!(prod, vec![vec![10.0, 40.0, 90.0]]);
let sum = pb_matrix_sum3(&a, &b, &c);
assert_eq!(sum, vec![vec![111.0, 222.0, 333.0]]);
let scaled = pb_matrix_scale(&a, -2.0);
assert_eq!(scaled, vec![vec![-2.0, -4.0, -6.0]]);
}
#[test]
fn pseudocode_118_scaling_factor() {
let s = 1.0f32 + 2.0 * (0.5f32).sqrt();
let expected = 1.0f32 + (2.0f32).sqrt();
assert!((s - expected).abs() < 1e-6);
}
#[test]
fn acpl_mch_state_zero_init() {
let s = AcplMchState::new();
assert!(s.g1_prev_sb.iter().all(|&v| v == 0.0));
assert!(s.g2_prev_sb.iter().all(|&v| v == 0.0));
assert!(s.g3_prev_sb.iter().all(|&v| v == 0.0));
assert!(s.g4_prev_sb.iter().all(|&v| v == 0.0));
assert_eq!(s.g1_prev_sb.len(), NUM_QMF_SUBBANDS);
}
#[test]
fn acpl_5x_pair_state_initial_decorrelators() {
let s = Acpl5xPairState::new();
assert!(matches!(s.left_pair.decorrelator.which, DecorrelatorId::D0));
assert!(matches!(
s.right_pair.decorrelator.which,
DecorrelatorId::D1
));
assert!(s.left_pair.alpha_prev_sb.iter().all(|&v| v == 0.0));
assert!(s.right_pair.alpha_prev_sb.iter().all(|&v| v == 0.0));
assert!(s.alpha1_diff.q_prev.is_empty());
assert!(s.alpha2_diff.q_prev.is_empty());
}
#[test]
fn run_pseudocode_117_5x_aspx_acpl_2_passthrough_centre_and_finite() {
let num_ts = 16usize;
let num_pb = 9u32;
let mut x0 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut x1 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut x2 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
for ts in 0..num_ts {
x0[ts][8] = (1.0, 0.0);
x1[ts][12] = (0.0, 1.0);
x2[ts][20] = (0.5, -0.5);
}
let alpha1 = vec![vec![0.3f32; num_pb as usize]];
let beta1 = vec![vec![0.2f32; num_pb as usize]];
let alpha2 = vec![vec![-0.4f32; num_pb as usize]];
let beta2 = vec![vec![0.1f32; num_pb as usize]];
let mut state = Acpl5xPairState::new();
let frame = Acpl5xPairFrame {
mode: Acpl5xPairMode::AspxAcpl2,
x0: &x0,
x1: &x1,
x2: &x2,
x3: None,
x4: None,
alpha_1_dq: &alpha1,
beta_1_dq: &beta1,
alpha_2_dq: &alpha2,
beta_2_dq: &beta2,
num_param_bands: num_pb,
acpl_qmf_band: 0,
steep_1: false,
steep_2: false,
param_timeslots_1: &[],
param_timeslots_2: &[],
};
let out = run_pseudocode_117_5x(&mut state, frame);
assert_eq!(out.z0.len(), num_ts);
assert_eq!(out.z1.len(), num_ts);
assert_eq!(out.z2.len(), num_ts);
assert_eq!(out.z3.len(), num_ts);
assert_eq!(out.z4.len(), num_ts);
for ts in 0..num_ts {
for sb in 0..NUM_QMF_SUBBANDS {
assert_eq!(out.z4[ts][sb], x2[ts][sb], "z4 != x2 at ts={ts} sb={sb}");
}
}
for ts in 0..num_ts {
for sb in 0..NUM_QMF_SUBBANDS {
for (re, im) in [
out.z0[ts][sb],
out.z1[ts][sb],
out.z2[ts][sb],
out.z3[ts][sb],
out.z4[ts][sb],
] {
assert!(re.is_finite(), "non-finite re at ts={ts} sb={sb}");
assert!(im.is_finite(), "non-finite im at ts={ts} sb={sb}");
}
}
}
}
#[test]
fn run_pseudocode_117_5x_aspx_acpl_1_low_band_ms_split() {
let num_ts = 4usize;
let num_pb = 9u32;
let mut x0 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut x1 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let x2 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut x3 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut x4 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
for ts in 0..num_ts {
x0[ts][2] = (0.7, 0.0);
x3[ts][2] = (0.3, 0.0);
x1[ts][2] = (0.5, 0.5);
x4[ts][2] = (0.1, -0.1);
}
let alpha1 = vec![vec![0.0f32; num_pb as usize]];
let beta1 = vec![vec![0.0f32; num_pb as usize]];
let alpha2 = vec![vec![0.0f32; num_pb as usize]];
let beta2 = vec![vec![0.0f32; num_pb as usize]];
let mut state = Acpl5xPairState::new();
let frame = Acpl5xPairFrame {
mode: Acpl5xPairMode::AspxAcpl1,
x0: &x0,
x1: &x1,
x2: &x2,
x3: Some(&x3),
x4: Some(&x4),
alpha_1_dq: &alpha1,
beta_1_dq: &beta1,
alpha_2_dq: &alpha2,
beta_2_dq: &beta2,
num_param_bands: num_pb,
acpl_qmf_band: NUM_QMF_SUBBANDS as u32,
steep_1: false,
steep_2: false,
param_timeslots_1: &[],
param_timeslots_2: &[],
};
let out = run_pseudocode_117_5x(&mut state, frame);
let sq2 = (2.0f32).sqrt();
for ts in 0..num_ts {
let (z0r, z0i) = out.z0[ts][2];
let (z1r, z1i) = out.z1[ts][2];
assert!((z0r - 1.0).abs() < 1e-5, "ts={ts} z0r={z0r}");
assert!((z0i - 0.0).abs() < 1e-5, "ts={ts} z0i={z0i}");
assert!(
(z1r - 0.4 * sq2).abs() < 1e-5,
"ts={ts} z1r={z1r} expected={}",
0.4 * sq2
);
assert!((z1i - 0.0).abs() < 1e-5, "ts={ts} z1i={z1i}");
let (z2r, z2i) = out.z2[ts][2];
let (z3r, z3i) = out.z3[ts][2];
assert!((z2r - 0.6).abs() < 1e-5, "ts={ts} z2r={z2r}");
assert!((z2i - 0.4).abs() < 1e-5, "ts={ts} z2i={z2i}");
assert!(
(z3r - 0.4 * sq2).abs() < 1e-5,
"ts={ts} z3r={z3r} expected={}",
0.4 * sq2
);
assert!(
(z3i - 0.6 * sq2).abs() < 1e-5,
"ts={ts} z3i={z3i} expected={}",
0.6 * sq2
);
}
}
#[test]
fn run_pseudocode_117_5x_state_carries_across_frames() {
let num_ts = 8usize;
let num_pb = 9u32;
let x0 = vec![[(1.0f32, 0.5f32); NUM_QMF_SUBBANDS]; num_ts];
let x1 = vec![[(0.5f32, 1.0f32); NUM_QMF_SUBBANDS]; num_ts];
let x2 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let alpha1 = vec![vec![0.42f32; num_pb as usize]];
let beta1 = vec![vec![0.13f32; num_pb as usize]];
let alpha2 = vec![vec![-0.11f32; num_pb as usize]];
let beta2 = vec![vec![0.21f32; num_pb as usize]];
let mut state = Acpl5xPairState::new();
assert!(state.left_pair.alpha_prev_sb.iter().all(|&v| v == 0.0));
assert!(state.right_pair.alpha_prev_sb.iter().all(|&v| v == 0.0));
let frame = Acpl5xPairFrame {
mode: Acpl5xPairMode::AspxAcpl2,
x0: &x0,
x1: &x1,
x2: &x2,
x3: None,
x4: None,
alpha_1_dq: &alpha1,
beta_1_dq: &beta1,
alpha_2_dq: &alpha2,
beta_2_dq: &beta2,
num_param_bands: num_pb,
acpl_qmf_band: 0,
steep_1: false,
steep_2: false,
param_timeslots_1: &[],
param_timeslots_2: &[],
};
let _out1 = run_pseudocode_117_5x(&mut state, frame);
for sb in 0..NUM_QMF_SUBBANDS {
assert!(
(state.left_pair.alpha_prev_sb[sb] - 0.42).abs() < 1e-6,
"left alpha_prev[{sb}] = {}",
state.left_pair.alpha_prev_sb[sb]
);
assert!(
(state.right_pair.alpha_prev_sb[sb] - (-0.11)).abs() < 1e-6,
"right alpha_prev[{sb}] = {}",
state.right_pair.alpha_prev_sb[sb]
);
assert!(
(state.left_pair.beta_prev_sb[sb] - 0.13).abs() < 1e-6,
"left beta_prev[{sb}] = {}",
state.left_pair.beta_prev_sb[sb]
);
assert!(
(state.right_pair.beta_prev_sb[sb] - 0.21).abs() < 1e-6,
"right beta_prev[{sb}] = {}",
state.right_pair.beta_prev_sb[sb]
);
}
}
#[test]
fn run_pseudocode_117_5x_aspx_acpl_2_matches_two_115_passes() {
let num_ts = 12usize;
let num_pb = 9u32;
let mut x0 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let mut x1 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
let x2 = vec![[(0.0f32, 0.0f32); NUM_QMF_SUBBANDS]; num_ts];
for ts in 0..num_ts {
for sb in 0..32 {
let phase = (ts as f32 * 0.2) + (sb as f32 * 0.1);
x0[ts][sb] = (0.1 * phase.cos(), 0.1 * phase.sin());
x1[ts][sb] = (0.05 * phase.sin(), 0.05 * phase.cos());
}
}
let alpha1 = vec![vec![0.3f32; num_pb as usize]];
let beta1 = vec![vec![0.2f32; num_pb as usize]];
let alpha2 = vec![vec![0.1f32; num_pb as usize]];
let beta2 = vec![vec![0.4f32; num_pb as usize]];
let mut ref_state_l = AcplCpeState::new(DecorrelatorId::D0);
let mut ref_state_r = AcplCpeState::new(DecorrelatorId::D1);
let ref_l = run_pseudocode_115_pair(
&mut ref_state_l,
AcplCpeFrame {
x0: &x0,
x1: None,
alpha_dq: &alpha1,
beta_dq: &beta1,
num_param_bands: num_pb,
acpl_qmf_band: 0,
steep: false,
param_timeslots: &[],
},
);
let ref_r = run_pseudocode_115_pair(
&mut ref_state_r,
AcplCpeFrame {
x0: &x1,
x1: None,
alpha_dq: &alpha2,
beta_dq: &beta2,
num_param_bands: num_pb,
acpl_qmf_band: 0,
steep: false,
param_timeslots: &[],
},
);
let mut state = Acpl5xPairState::new();
let frame = Acpl5xPairFrame {
mode: Acpl5xPairMode::AspxAcpl2,
x0: &x0,
x1: &x1,
x2: &x2,
x3: None,
x4: None,
alpha_1_dq: &alpha1,
beta_1_dq: &beta1,
alpha_2_dq: &alpha2,
beta_2_dq: &beta2,
num_param_bands: num_pb,
acpl_qmf_band: 0,
steep_1: false,
steep_2: false,
param_timeslots_1: &[],
param_timeslots_2: &[],
};
let out = run_pseudocode_117_5x(&mut state, frame);
let sq2 = (2.0f32).sqrt();
for ts in 0..num_ts {
for sb in [0usize, 5, 16, 31, 47, 63] {
let (a_r, a_i) = out.z0[ts][sb];
let (e_r, e_i) = ref_l.z0[ts][sb];
assert!(
(a_r - e_r).abs() < 1e-5 && (a_i - e_i).abs() < 1e-5,
"z0 mismatch ts={ts} sb={sb} got ({a_r},{a_i}) want ({e_r},{e_i})"
);
let (a_r, a_i) = out.z2[ts][sb];
let (e_r, e_i) = ref_r.z0[ts][sb];
assert!(
(a_r - e_r).abs() < 1e-5 && (a_i - e_i).abs() < 1e-5,
"z2 mismatch ts={ts} sb={sb} got ({a_r},{a_i}) want ({e_r},{e_i})"
);
let (a_r, a_i) = out.z1[ts][sb];
let (e_r, e_i) = (ref_l.z1[ts][sb].0 * sq2, ref_l.z1[ts][sb].1 * sq2);
assert!(
(a_r - e_r).abs() < 1e-5 && (a_i - e_i).abs() < 1e-5,
"z1 mismatch ts={ts} sb={sb} got ({a_r},{a_i}) want ({e_r},{e_i})"
);
let (a_r, a_i) = out.z3[ts][sb];
let (e_r, e_i) = (ref_r.z1[ts][sb].0 * sq2, ref_r.z1[ts][sb].1 * sq2);
assert!(
(a_r - e_r).abs() < 1e-5 && (a_i - e_i).abs() < 1e-5,
"z3 mismatch ts={ts} sb={sb} got ({a_r},{a_i}) want ({e_r},{e_i})"
);
}
}
}
#[test]
fn run_acpl_5x_pair_pcm_rejects_misaligned_inputs() {
use crate::acpl::{
AcplConfig1ch, AcplData1ch, AcplFramingData, AcplHuffParam, AcplInterpolationType,
AcplQuantMode,
};
let cfg = AcplConfig1ch {
num_param_bands_id: 0,
num_param_bands: 15,
quant_mode: AcplQuantMode::Fine,
qmf_band: 0,
};
let data = AcplData1ch {
framing: AcplFramingData {
interpolation_type: AcplInterpolationType::Smooth,
num_param_sets_cod: 0,
num_param_sets: 1,
param_timeslots: vec![],
},
alpha1: vec![AcplHuffParam {
values: vec![4i32; cfg.num_param_bands as usize],
direction_time: false,
}],
beta1: vec![AcplHuffParam {
values: vec![2i32; cfg.num_param_bands as usize],
direction_time: false,
}],
};
let mut state = Acpl5xPairPcmState::new();
let pcm_short = vec![0.0f32; 65];
assert!(run_acpl_5x_pair_pcm(
Acpl5xPairMode::AspxAcpl2,
&pcm_short,
&pcm_short,
&pcm_short,
None,
None,
&cfg,
&data,
&cfg,
&data,
&mut state,
)
.is_none());
let pcm = vec![0.0f32; 64];
assert!(run_acpl_5x_pair_pcm(
Acpl5xPairMode::AspxAcpl2,
&pcm,
&pcm,
&pcm,
Some(&pcm),
Some(&pcm),
&cfg,
&data,
&cfg,
&data,
&mut state,
)
.is_none());
assert!(run_acpl_5x_pair_pcm(
Acpl5xPairMode::AspxAcpl1,
&pcm,
&pcm,
&pcm,
None,
None,
&cfg,
&data,
&cfg,
&data,
&mut state,
)
.is_none());
}
#[test]
fn run_acpl_5x_pair_pcm_aspx_acpl_2_emits_five_channels() {
use crate::acpl::{
AcplConfig1ch, AcplData1ch, AcplFramingData, AcplHuffParam, AcplInterpolationType,
AcplQuantMode,
};
let n_slots = 64usize;
let n = n_slots * NUM_QMF_SUBBANDS;
let mut pcm_l = vec![0.0f32; n];
let mut pcm_r = vec![0.0f32; n];
let mut pcm_c = vec![0.0f32; n];
let f_l = 440.0f32 / 48_000.0;
let f_r = 220.0f32 / 48_000.0;
let f_c = 880.0f32 / 48_000.0;
for i in 0..n {
pcm_l[i] = (2.0 * std::f32::consts::PI * f_l * i as f32).sin();
pcm_r[i] = 0.5 * (2.0 * std::f32::consts::PI * f_r * i as f32).sin();
pcm_c[i] = 0.3 * (2.0 * std::f32::consts::PI * f_c * i as f32).sin();
}
let cfg = AcplConfig1ch {
num_param_bands_id: 0,
num_param_bands: 15,
quant_mode: AcplQuantMode::Fine,
qmf_band: 0,
};
let data1 = AcplData1ch {
framing: AcplFramingData {
interpolation_type: AcplInterpolationType::Smooth,
num_param_sets_cod: 0,
num_param_sets: 1,
param_timeslots: vec![],
},
alpha1: vec![AcplHuffParam {
values: vec![4i32; cfg.num_param_bands as usize],
direction_time: false,
}],
beta1: vec![AcplHuffParam {
values: vec![2i32; cfg.num_param_bands as usize],
direction_time: false,
}],
};
let data2 = AcplData1ch {
framing: AcplFramingData {
interpolation_type: AcplInterpolationType::Smooth,
num_param_sets_cod: 0,
num_param_sets: 1,
param_timeslots: vec![],
},
alpha1: vec![AcplHuffParam {
values: vec![-3i32; cfg.num_param_bands as usize],
direction_time: false,
}],
beta1: vec![AcplHuffParam {
values: vec![1i32; cfg.num_param_bands as usize],
direction_time: false,
}],
};
let mut state = Acpl5xPairPcmState::new();
let out = run_acpl_5x_pair_pcm(
Acpl5xPairMode::AspxAcpl2,
&pcm_l,
&pcm_r,
&pcm_c,
None,
None,
&cfg,
&data1,
&cfg,
&data2,
&mut state,
)
.expect("synth runs");
assert_eq!(out.left.len(), n);
assert_eq!(out.right.len(), n);
assert_eq!(out.centre.len(), n);
assert_eq!(out.left_surround.len(), n);
assert_eq!(out.right_surround.len(), n);
let start = 2048usize;
let energy =
|b: &[f32]| -> f64 { b[start..].iter().map(|&s| (s as f64).powi(2)).sum::<f64>() };
assert!(energy(&out.left) > 1e-6, "left silent");
assert!(energy(&out.right) > 1e-6, "right silent");
assert!(energy(&out.centre) > 1e-6, "centre silent");
assert!(energy(&out.left_surround) > 1e-6, "Ls silent");
assert!(energy(&out.right_surround) > 1e-6, "Rs silent");
for b in [
&out.left,
&out.right,
&out.centre,
&out.left_surround,
&out.right_surround,
] {
for &s in b.iter() {
assert!(s.is_finite(), "non-finite sample");
}
}
}
#[test]
fn run_acpl_5x_mch_pcm_aspx_acpl_3_emits_five_channels() {
use crate::acpl::{
AcplConfig2ch, AcplData2ch, AcplFramingData, AcplHuffParam, AcplInterpolationType,
AcplQuantMode,
};
let n_slots = 64usize;
let n = n_slots * NUM_QMF_SUBBANDS;
let mut pcm_l = vec![0.0f32; n];
let mut pcm_r = vec![0.0f32; n];
let mut pcm_c = vec![0.0f32; n];
let f_l = 440.0f32 / 48_000.0;
let f_r = 220.0f32 / 48_000.0;
let f_c = 660.0f32 / 48_000.0;
for i in 0..n {
pcm_l[i] = (2.0 * std::f32::consts::PI * f_l * i as f32).sin();
pcm_r[i] = (2.0 * std::f32::consts::PI * f_r * i as f32).cos();
pcm_c[i] = 0.3 * (2.0 * std::f32::consts::PI * f_c * i as f32).sin();
}
let cfg = AcplConfig2ch {
num_param_bands_id: 0,
num_param_bands: 15,
quant_mode_0: AcplQuantMode::Fine,
quant_mode_1: AcplQuantMode::Fine,
};
let mk_huff = |seed: i32| AcplHuffParam {
values: vec![seed; cfg.num_param_bands as usize],
direction_time: false,
};
let data = AcplData2ch {
framing: AcplFramingData {
interpolation_type: AcplInterpolationType::Smooth,
num_param_sets_cod: 0,
num_param_sets: 1,
param_timeslots: vec![],
},
alpha1: vec![mk_huff(4)],
alpha2: vec![mk_huff(-3)],
beta1: vec![mk_huff(2)],
beta2: vec![mk_huff(1)],
beta3: vec![mk_huff(0)],
gamma1: vec![mk_huff(2)],
gamma2: vec![mk_huff(0)],
gamma3: vec![mk_huff(0)],
gamma4: vec![mk_huff(2)],
gamma5: vec![mk_huff(1)],
gamma6: vec![mk_huff(1)],
};
let mut state = Acpl5xMchPcmState::new();
let pcm_bad = vec![0.0f32; 65];
assert!(
run_acpl_5x_mch_pcm(&pcm_bad, &pcm_bad, &pcm_bad, &cfg, &data, &mut state).is_none()
);
let out = run_acpl_5x_mch_pcm(&pcm_l, &pcm_r, &pcm_c, &cfg, &data, &mut state)
.expect("synth runs");
assert_eq!(out.left.len(), n);
assert_eq!(out.right.len(), n);
assert_eq!(out.centre.len(), n);
assert_eq!(out.left_surround.len(), n);
assert_eq!(out.right_surround.len(), n);
let start = 2048usize;
let energy =
|b: &[f32]| -> f64 { b[start..].iter().map(|&s| (s as f64).powi(2)).sum::<f64>() };
for b in [
&out.left,
&out.right,
&out.centre,
&out.left_surround,
&out.right_surround,
] {
for &s in b.iter() {
assert!(s.is_finite(), "non-finite sample");
}
}
assert!(energy(&out.left) > 1e-6, "left silent");
assert!(energy(&out.right) > 1e-6, "right silent");
assert!(energy(&out.centre) > 1e-6, "centre silent");
}
}