use super::codec::{PITCH_DEN, PITCH_NUM};
use std::sync::OnceLock;
pub const ONE: i64 = 1 << 23;
pub const PHASES: usize = PITCH_DEN as usize;
pub const TAPS: usize = 32;
const FIRST_TAP: i128 = 16;
const A: f64 = 0.827_50;
const Z: f64 = 1.208_02;
const L: f64 = 11.91;
fn sinc(x: f64) -> f64 {
if x == 0.0 {
1.0
} else {
(std::f64::consts::PI * x).sin() / (std::f64::consts::PI * x)
}
}
fn g(delta: f64) -> f64 {
if delta.abs() >= L {
return 0.0;
}
A * sinc(delta / Z) * (0.543 + 0.457 * (std::f64::consts::PI * delta / L).cos())
}
pub fn taps() -> &'static [[i32; TAPS]; PHASES] {
static BANK: OnceLock<Box<[[i32; TAPS]; PHASES]>> = OnceLock::new();
BANK.get_or_init(|| {
let mut bank = Box::new([[0i32; TAPS]; PHASES]);
for (phase, row) in bank.iter_mut().enumerate() {
let fraction = phase as f64 / PHASES as f64;
let real: Vec<f64> = (0..TAPS)
.map(|j| g(j as f64 - FIRST_TAP as f64 + fraction))
.collect();
let gain = ONE as f64 / real.iter().sum::<f64>();
for (slot, &value) in row.iter_mut().zip(&real) {
*slot = (value * gain).round() as i32;
}
let residue = ONE - row.iter().map(|&t| i64::from(t)).sum::<i64>();
let peak = (0..TAPS).max_by_key(|&j| row[j].abs()).unwrap_or(0);
row[peak] += residue as i32;
}
bank
})
}
pub fn lattice(field: usize) -> (i128, usize) {
let t = u128::from(PITCH_NUM) * field as u128;
(
(t / u128::from(PITCH_DEN)) as i128,
(t % u128::from(PITCH_DEN)) as usize,
)
}
pub fn accumulate(source: &[i16], field: usize) -> i64 {
let (base, phase) = lattice(field);
let row = &taps()[phase];
let mut acc = 0i64;
for (j, &tap) in row.iter().enumerate() {
let at = base + FIRST_TAP - j as i128;
if at >= 0 && at < source.len() as i128 {
acc += i64::from(source[at as usize]) * i64::from(tap);
}
}
acc
}
pub fn field(source: &[i16], at: usize) -> i64 {
accumulate(source, at) / ONE
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_phase_has_unity_dc_gain() {
for row in taps().iter() {
assert_eq!(row.iter().map(|&t| i64::from(t)).sum::<i64>(), ONE);
}
}
#[test]
fn a_constant_resamples_to_itself() {
let source = vec![1000i16; 4096];
for f in 20..3000 {
assert_eq!(field(&source, f), 1000, "field {f}");
}
}
#[test]
fn the_kernel_is_mirror_symmetric() {
let bank = taps();
for phase in 1..PHASES {
for j in 0..TAPS {
let a = bank[phase][j];
let b = bank[PHASES - phase][TAPS - 1 - j];
assert!((a - b).abs() <= 8, "phase {phase} tap {j}: {a} vs {b}");
}
}
}
#[test]
fn the_lattice_closes_after_a_superperiod() {
for f in 0..1000 {
let (a, pa) = lattice(f);
let (b, pb) = lattice(f + PITCH_DEN as usize);
assert_eq!(pa, pb);
assert_eq!(b - a, i128::from(PITCH_NUM));
}
}
#[test]
fn one_impulse_lights_only_the_kernels_support() {
let mut source = vec![0i16; 4096];
source[2048] = 30_000;
let lit: Vec<usize> = (0..3000).filter(|&f| field(&source, f) != 0).collect();
let (first, last) = (lit[0], lit[lit.len() - 1]);
let (near, _) = lattice(first);
let (far, _) = lattice(last);
assert!((2048 - near) <= 13 && (far - 2048) <= 13);
}
}