use super::det_math;
use super::error::{Error, Result};
use super::sbr_qmf::QMF_WINDOW;
use std::sync::OnceLock;
pub(crate) const BANDS: usize = 64;
const N2: usize = 128;
const HIST: usize = 640;
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct EncSlot {
pub re: [f32; BANDS],
pub im: [f32; BANDS],
}
impl EncSlot {
pub(crate) fn energy(&self, k: usize) -> f32 {
self.re[k] * self.re[k] + self.im[k] * self.im[k]
}
pub(crate) fn band_energy(&self, lo: usize, hi: usize) -> f32 {
(lo..hi.min(BANDS)).map(|k| self.energy(k)).sum()
}
}
const FORMANT_RATIO: f32 = 1.0e-2;
pub(crate) fn core_cutoff_hz(channels: &[&[EncSlot]], k_x: usize, out_rate: u32) -> u32 {
let k_x = k_x.min(BANDS);
let k0_hz = ((u64::from(out_rate) * k_x as u64) / (BANDS as u64 * 2)) as u32;
if k_x == 0 {
return k0_hz;
}
let mut e = [0.0f32; BANDS];
for slots in channels {
for s in *slots {
for (k, acc) in e.iter_mut().enumerate().take(k_x) {
*acc += s.energy(k);
}
}
}
let mut peak = 0.0f32;
for v in e.iter().take(k_x) {
if *v > peak {
peak = *v;
}
}
if peak < 1.0e-18 {
return k0_hz;
}
let thresh = peak * FORMANT_RATIO;
let mut top = 0usize;
for (k, v) in e.iter().enumerate().take(k_x) {
if *v >= thresh {
top = k;
}
}
let hz = ((u64::from(out_rate) * (top as u64 + 1)) / (BANDS as u64 * 2)) as u32;
if hz == 0 { k0_hz } else { hz.min(k0_hz) }
}
struct Plan {
bitrev: [u16; N2],
tw_re: Vec<f32>,
tw_im: Vec<f32>,
pre_c: [f32; N2],
pre_s: [f32; N2],
post_c: [f32; BANDS],
post_s: [f32; BANDS],
}
fn plan() -> &'static Plan {
static P: OnceLock<Plan> = OnceLock::new();
P.get_or_init(|| {
let mut bitrev = [0u16; N2];
let mut j = 0usize;
for slot in &mut bitrev {
*slot = j as u16;
let mut bit = N2 / 2;
while bit != 0 && j >= bit {
j -= bit;
bit >>= 1;
}
j += bit;
}
let (tw_re, tw_im) = det_math::twiddle_table(N2);
let mut pre_c = [0.0f32; N2];
let mut pre_s = [0.0f32; N2];
for n in 0..N2 {
let (s, c) = det_math::sincos(core::f32::consts::PI * n as f32 / N2 as f32);
pre_c[n] = c;
pre_s[n] = s;
}
let mut post_c = [0.0f32; BANDS];
let mut post_s = [0.0f32; BANDS];
for k in 0..BANDS {
let (s, c) =
det_math::sincos(core::f32::consts::PI * (2 * k + 1) as f32 / (8 * BANDS) as f32);
post_c[k] = c;
post_s[k] = -s;
}
Plan {
bitrev,
tw_re,
tw_im,
pre_c,
pre_s,
post_c,
post_s,
}
})
}
fn fft128(re: &mut [f32; N2], im: &mut [f32; N2], p: &Plan) {
for (i, &rev) in p.bitrev.iter().enumerate() {
let j = rev as usize;
if i < j {
re.swap(i, j);
im.swap(i, j);
}
}
let mut len = 2usize;
let mut off = 0usize;
while len <= N2 {
let half = len / 2;
for i in (0..N2).step_by(len) {
for k in 0..half {
let wr = p.tw_re[off + k];
let wi = p.tw_im[off + k];
let j = i + k + half;
let wrj = wr * re[j];
let wij = wi * im[j];
let tr = wrj - wij;
let wir = wi * re[j];
let wjr = wr * im[j];
let ti = wir + wjr;
let ur = re[i + k];
let ui = im[i + k];
re[j] = ur - tr;
im[j] = ui - ti;
re[i + k] = ur + tr;
im[i + k] = ui + ti;
}
}
off += half;
len *= 2;
}
}
fn modulate(u: &[f32; N2]) -> EncSlot {
let p = plan();
let mut re = [0.0f32; N2];
let mut im = [0.0f32; N2];
for n in 0..N2 {
re[n] = u[n] * p.pre_c[n];
im[n] = u[n] * p.pre_s[n];
}
fft128(&mut re, &mut im, p);
let mut slot = EncSlot {
re: [0.0; BANDS],
im: [0.0; BANDS],
};
for k in 0..BANDS {
let a = re[k] * p.post_c[k];
let b = im[k] * p.post_s[k];
slot.re[k] = a - b;
let c = re[k] * p.post_s[k];
let d = im[k] * p.post_c[k];
slot.im[k] = c + d;
}
slot
}
pub(crate) struct EncAnalysisQmf {
x: [f32; HIST],
}
impl EncAnalysisQmf {
pub(crate) fn new() -> Self {
Self { x: [0.0; HIST] }
}
pub(crate) fn reset(&mut self) {
self.x = [0.0; HIST];
}
pub(crate) fn push_slot(&mut self, samples: &[f32]) -> Result<EncSlot> {
if samples.len() != BANDS {
return Err(Error::SbrQmfInvalid);
}
self.x.copy_within(0..HIST - BANDS, BANDS);
for (n, s) in samples.iter().enumerate() {
self.x[BANDS - 1 - n] = *s;
}
let mut u = [0.0f32; N2];
for (n, un) in u.iter_mut().enumerate() {
let mut acc = 0.0f32;
for j in 0..5 {
let idx = n + j * N2;
acc += self.x[idx] * (QMF_WINDOW[idx] as f32);
}
*un = acc;
}
Ok(modulate(&u))
}
}
impl Default for EncAnalysisQmf {
fn default() -> Self {
Self::new()
}
}