use crate::sbr_freq_bands::HiLoTables;
use crate::sbr_qmf::Complex;
use crate::{Error, Result};
pub const T_HF_ADJ: usize = 2;
pub const T_HF_GEN: usize = 8;
pub const EPS_INV: f64 = 1e-6;
pub const MAX_PATCHES: usize = 5;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Patches {
pub start: Vec<usize>,
pub num: Vec<usize>,
}
impl Patches {
#[inline]
#[must_use]
pub fn num_patches(&self) -> usize {
self.num.len()
}
#[must_use]
pub fn borders(&self, k_x: i32) -> Vec<i32> {
let mut b = Vec::with_capacity(self.num.len() + 1);
b.push(k_x);
for &n in &self.num {
b.push(b[b.len() - 1] + n as i32);
}
b
}
}
pub fn build_patches(f_master: &[i32], k0: i32, k_x: i32, m: i32, fs_sbr: u32) -> Result<Patches> {
if f_master.len() < 2 || fs_sbr == 0 {
return Err(Error::SbrFreqBandInvalid);
}
let n_master = f_master.len() - 1;
let mut msb = k0;
let mut usb = k_x;
let mut start = Vec::new();
let mut num = Vec::new();
let goal_sb = ((2.0 * 2.048e6 / f64::from(fs_sbr) + 1.0) / 2.0).floor() as i32;
let mut k = if goal_sb < k_x + m {
let mut kk = 0usize;
for (i, &f) in f_master.iter().enumerate() {
if f < goal_sb {
kk = i + 1;
} else {
break;
}
}
kk
} else {
n_master
};
let mut sb;
let mut guard = 0usize;
loop {
guard += 1;
if guard > 64 {
return Err(Error::SbrFreqBandInvalid);
}
let mut j = k;
let odd = loop {
if j >= f_master.len() {
return Err(Error::SbrFreqBandInvalid);
}
sb = f_master[j];
let odd = (sb - 2 + k0).rem_euclid(2);
if sb <= k0 - 1 + msb - odd {
break odd;
}
if j == 0 {
return Err(Error::SbrFreqBandInvalid);
}
j -= 1;
};
let n = (sb - usb).max(0);
let s = k0 - odd - n;
if n > 0 {
if s < 0 || start.len() >= MAX_PATCHES {
return Err(Error::SbrFreqBandInvalid);
}
start.push(s as usize);
num.push(n as usize);
usb = sb;
msb = sb;
} else {
msb = k_x;
}
if f_master[k] - sb < 3 {
k = n_master;
}
if sb == k_x + m {
break;
}
}
if num.len() > 1 && *num.last().unwrap() < 3 {
num.pop();
start.pop();
}
Ok(Patches { start, num })
}
#[must_use]
pub fn new_bw(prev_mode: u8, cur_mode: u8) -> f64 {
const TABLE: [[f64; 4]; 4] = [
[0.0, 0.6, 0.9, 0.98],
[0.6, 0.75, 0.9, 0.98],
[0.0, 0.75, 0.9, 0.98],
[0.0, 0.75, 0.9, 0.98],
];
TABLE[usize::from(prev_mode.min(3))][usize::from(cur_mode.min(3))]
}
#[must_use]
pub fn chirp_factors(cur_invf: &[u8], prev_invf: &[u8], prev_bw: &[f64]) -> Vec<f64> {
cur_invf
.iter()
.enumerate()
.map(|(i, &cur)| {
let prev_mode = prev_invf.get(i).copied().unwrap_or(0);
let bw_prev = prev_bw.get(i).copied().unwrap_or(0.0);
let nb = new_bw(prev_mode, cur);
let temp = if nb < bw_prev {
0.75 * nb + 0.25 * bw_prev
} else {
0.90625 * nb + 0.09375 * bw_prev
};
if temp < 0.015625 {
0.0
} else {
temp
}
})
.collect()
}
pub fn prediction_coefficients(
x_low: &[[Complex; 32]],
k: usize,
n_slots_frame: usize,
) -> Result<(Complex, Complex)> {
if k >= 32 || x_low.len() < n_slots_frame + 6 + T_HF_ADJ {
return Err(Error::SbrFreqBandInvalid);
}
let phi = |i: usize, j: usize| -> Complex {
let mut acc = Complex::default();
for n in 0..(n_slots_frame + 6) {
let a = x_low[n + T_HF_ADJ - i][k];
let b = x_low[n + T_HF_ADJ - j][k];
acc += a * b.conj();
}
acc
};
let phi01 = phi(0, 1);
let phi02 = phi(0, 2);
let phi11 = phi(1, 1);
let phi12 = phi(1, 2);
let phi22 = phi(2, 2);
let d = phi22.re * phi11.re - phi12.norm_sqr() / (1.0 + EPS_INV);
let alpha1 = if d != 0.0 {
let numer = phi01 * phi12 - phi02 * phi11.re;
Complex::new(numer.re / d, numer.im / d)
} else {
Complex::default()
};
let alpha0 = if phi11.re != 0.0 {
let numer = phi01 + alpha1 * phi12.conj();
Complex::new(-numer.re / phi11.re, -numer.im / phi11.re)
} else {
Complex::default()
};
if alpha0.norm_sqr() >= 16.0 || alpha1.norm_sqr() >= 16.0 {
return Ok((Complex::default(), Complex::default()));
}
Ok((alpha0, alpha1))
}
pub fn reflection_coefficient(
x_low: &[[Complex; 32]],
k: usize,
n_slots_frame: usize,
) -> Result<f64> {
if k >= 32 || x_low.len() < n_slots_frame + 6 + T_HF_ADJ {
return Err(Error::SbrFreqBandInvalid);
}
let mut phi01 = 0.0f64;
let mut phi11 = 0.0f64;
for n in 0..(n_slots_frame + 6) {
let a = x_low[n + T_HF_ADJ][k].re;
let b = x_low[n + T_HF_ADJ - 1][k].re;
phi01 += a * b;
phi11 += b * b;
}
Ok(if phi11 != 0.0 {
(-phi01 / phi11).clamp(-1.0, 1.0)
} else {
0.0
})
}
pub fn generate_hf(
x_low: &[[Complex; 32]],
patches: &Patches,
bw_array: &[f64],
bands: &HiLoTables,
l_range: core::ops::Range<i32>,
n_slots_frame: usize,
) -> Result<Vec<[Complex; 64]>> {
let k_x = bands.k_x;
let mut x_high = vec![[Complex::default(); 64]; x_low.len()];
let mut alphas: [Option<(Complex, Complex)>; 32] = [None; 32];
let g_of = |k: i32| -> Result<usize> {
let nb = &bands.f_table_noise;
for i in 0..nb.len() - 1 {
if nb[i] <= k && k < nb[i + 1] {
return Ok(i);
}
}
Err(Error::SbrFreqBandInvalid)
};
let mut k_off = 0usize;
for (i, (&p_start, &p_num)) in patches.start.iter().zip(patches.num.iter()).enumerate() {
let _ = i;
for x in 0..p_num {
let k = k_x as usize + x + k_off;
let p = p_start + x;
if k >= 64 || p >= 32 {
return Err(Error::SbrFreqBandInvalid);
}
let (a0, a1) = match alphas[p] {
Some(a) => a,
None => {
let a = prediction_coefficients(x_low, p, n_slots_frame)?;
alphas[p] = Some(a);
a
}
};
let bw = *bw_array
.get(g_of(k as i32)?)
.ok_or(Error::SbrFreqBandInvalid)?;
let bw2 = bw * bw;
for l in l_range.clone() {
let c = usize::try_from(l).map_err(|_| Error::SbrFreqBandInvalid)? + T_HF_ADJ;
if c >= x_low.len() || c < 2 {
return Err(Error::SbrFreqBandInvalid);
}
x_high[c][k] =
x_low[c][p] + (a0 * bw) * x_low[c - 1][p] + (a1 * bw2) * x_low[c - 2][p];
}
}
k_off += p_num;
}
Ok(x_high)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_bw_table() {
assert_eq!(new_bw(0, 0), 0.0);
assert_eq!(new_bw(0, 1), 0.6);
assert_eq!(new_bw(1, 0), 0.6);
assert_eq!(new_bw(1, 1), 0.75);
assert_eq!(new_bw(2, 0), 0.0);
assert_eq!(new_bw(2, 1), 0.75);
assert_eq!(new_bw(3, 3), 0.98);
assert_eq!(new_bw(0, 2), 0.9);
}
#[test]
fn chirp_blend_and_flush() {
let bw = chirp_factors(&[3], &[0], &[0.0]);
assert!((bw[0] - 0.888125).abs() < 1e-12);
let bw2 = chirp_factors(&[0], &[3], &bw);
assert!((bw2[0] - 0.22203125).abs() < 1e-12);
let mut cur = bw2;
for _ in 0..4 {
cur = chirp_factors(&[0], &[0], &cur);
}
assert_eq!(cur[0], 0.0);
}
#[test]
fn reflection_coefficient_orientations() {
let n = 32usize;
let cols = n + 6 + T_HF_ADJ;
let mut x = vec![[Complex::default(); 32]; cols];
for (c, col) in x.iter_mut().enumerate() {
col[3] = Complex::new(1.0, 0.0); col[4] = Complex::new(if c % 2 == 0 { 1.0 } else { -1.0 }, 0.0); }
assert_eq!(reflection_coefficient(&x, 3, n).unwrap(), -1.0);
assert_eq!(reflection_coefficient(&x, 4, n).unwrap(), 1.0);
assert_eq!(reflection_coefficient(&x, 5, n).unwrap(), 0.0);
assert!(reflection_coefficient(&x, 32, n).is_err());
}
#[test]
fn patch_construction_hand_walked() {
let f_master: Vec<i32> = (0..=8).map(|i| 8 + 2 * i).collect();
let p = build_patches(&f_master, 8, 8, 16, 85_000).unwrap();
assert_eq!(p.start, vec![2, 2, 4]);
assert_eq!(p.num, vec![6, 6, 4]);
assert_eq!(p.borders(8), vec![8, 14, 20, 24]);
}
#[test]
fn patch_trim_drops_small_tail() {
let f_master = vec![8, 10, 12, 14, 16, 20, 22];
let p = build_patches(&f_master, 8, 8, 14, 85_000).unwrap();
assert_eq!(p.num, vec![6, 6]);
assert_eq!(p.start, vec![2, 2]);
}
#[test]
fn patch_invariants_on_derived_master() {
let fs_sbr = 44_100;
let k0 = crate::sbr_freq_bands::k0(fs_sbr, 5).unwrap();
let k2 = crate::sbr_freq_bands::k2(fs_sbr, 5, k0).unwrap();
let fm = crate::sbr_freq_bands::master_table(k0, k2, 2, true).unwrap();
let bands = HiLoTables::derive(&fm, 0, 2).unwrap();
let p = build_patches(&fm, k0, bands.k_x, bands.m, fs_sbr).unwrap();
assert!(p.num_patches() >= 1 && p.num_patches() <= MAX_PATCHES);
for (&s, &n) in p.start.iter().zip(p.num.iter()) {
assert!(n > 0);
assert!((s + n) as i32 <= k0);
}
let borders = p.borders(bands.k_x);
assert_eq!(borders[0], bands.k_x);
assert!(*borders.last().unwrap() <= bands.k_x + bands.m);
}
fn ar2_xlow(k: usize, a1: Complex, a2: Complex, cols: usize) -> Vec<[Complex; 32]> {
let mut x = vec![[Complex::default(); 32]; cols];
x[0][k] = Complex::new(1.0, 0.3);
x[1][k] = Complex::new(0.2, -0.5);
for n in 2..cols {
let v = a1 * x[n - 1][k] + a2 * x[n - 2][k];
x[n][k] = v;
}
x
}
#[test]
fn prediction_recovers_ar2() {
let a1 = Complex::new(0.9, 0.1);
let a2 = Complex::new(-0.5, 0.05);
let x = ar2_xlow(3, a1, a2, 40);
let (al0, al1) = prediction_coefficients(&x, 3, 32).unwrap();
assert!((al0 + a1).norm_sqr() < 1e-10, "{al0:?}");
assert!((al1 + a2).norm_sqr() < 1e-10, "{al1:?}");
}
#[test]
fn prediction_resets_large_coefficients() {
let a1 = Complex::new(4.5, 0.0);
let a2 = Complex::new(0.0, 0.0);
let mut x = vec![[Complex::default(); 32]; 40];
x[0][0] = Complex::new(1e-6, 0.0);
for n in 1..40 {
let v = a1 * x[n - 1][0];
x[n][0] = v;
}
let _ = a2;
let (al0, al1) = prediction_coefficients(&x, 0, 32).unwrap();
assert_eq!(al0, Complex::default());
assert_eq!(al1, Complex::default());
}
fn tiny_bands() -> HiLoTables {
HiLoTables {
f_table_high: vec![8, 12, 16],
f_table_low: vec![8, 16],
f_table_noise: vec![8, 16],
m: 8,
k_x: 8,
}
}
#[test]
fn generate_copies_and_whitens() {
let a1 = Complex::new(0.8, 0.2);
let a2 = Complex::new(-0.4, 0.0);
let x = ar2_xlow(2, a1, a2, 40);
let patches = Patches {
start: vec![2],
num: vec![8],
};
let bands = tiny_bands();
let hi = generate_hf(&x, &patches, &[0.0], &bands, 0..32, 32).unwrap();
for l in 0..32usize {
let c = l + T_HF_ADJ;
assert_eq!(hi[c][8], x[c][2]);
}
let hi = generate_hf(&x, &patches, &[1.0], &bands, 0..32, 32).unwrap();
let sig: f64 = (0..32).map(|l| x[l + T_HF_ADJ][2].norm_sqr()).sum();
let res: f64 = (0..32).map(|l| hi[l + T_HF_ADJ][8].norm_sqr()).sum();
assert!(res < 1e-10 * sig, "residual {res} vs signal {sig}");
for col in &hi {
assert_eq!(col[20], Complex::default());
}
}
}