use super::super::bits::BitWriter;
use super::super::swb::{LONG_WINDOW_LEN, SHORT_WINDOW_LEN};
use super::{
EncTns, K_MIN, MAX_ORDER, PG_MIN, analysis_filter, autocorrelate, dequantize_coef, levinson,
quantize_coef, step_up,
};
const TNS_MAX_BANDS_SHORT: [u8; 12] = [9, 9, 10, 14, 14, 14, 14, 14, 14, 14, 14, 14];
const MAX_ORDER_SHORT: usize = 7;
const N_WIN: usize = 8;
#[derive(Debug, Clone, Copy)]
pub struct EncTnsShort {
on: bool,
order: [u8; N_WIN],
length: [u8; N_WIN],
coef: [[u8; MAX_ORDER_SHORT]; N_WIN],
}
impl EncTnsShort {
pub fn off() -> Self {
Self {
on: false,
order: [0; N_WIN],
length: [0; N_WIN],
coef: [[0; MAX_ORDER_SHORT]; N_WIN],
}
}
pub fn is_on(&self) -> bool {
self.on
}
pub fn bits(&self) -> usize {
if !self.on {
return 1;
}
let mut n = 1; for w in 0..N_WIN {
n += 1; if self.order[w] > 0 {
n += 1 + 4 + 3 + 1 + 1 + 4 * self.order[w] as usize;
}
}
n
}
pub fn emit(&self, w: &mut BitWriter) {
w.write_bit(self.on);
if !self.on {
return;
}
for w_i in 0..N_WIN {
if self.order[w_i] == 0 {
w.write(0, 1);
continue;
}
w.write(1, 1); w.write_bit(true); w.write(u32::from(self.length[w_i]), 4);
w.write(u32::from(self.order[w_i]), 3);
w.write_bit(false); w.write_bit(false); for &c in self.coef[w_i].iter().take(self.order[w_i] as usize) {
w.write(u32::from(c), 4);
}
}
}
pub fn window_on(&self, w: usize) -> bool {
w < N_WIN && self.order[w] > 0
}
}
fn decide_window(
spec: &mut [f32],
offsets: &[u16],
fs_index: u8,
n_swb: usize,
) -> (u8, u8, [u8; MAX_ORDER_SHORT]) {
let zero = (0u8, 0u8, [0u8; MAX_ORDER_SHORT]);
let Some(&max_bands) = TNS_MAX_BANDS_SHORT.get(fs_index as usize) else {
return zero;
};
let max_bands = n_swb.min(usize::from(max_bands));
if max_bands == 0 {
return zero;
}
let lo = usize::from(offsets[0]);
let hi = usize::from(offsets[max_bands]).min(spec.len());
if hi.saturating_sub(lo) <= MAX_ORDER_SHORT {
return zero;
}
let r = autocorrelate(&spec[lo..hi]);
if r[0] <= 0.0 {
return zero;
}
let Some(k) = levinson(&r) else {
return zero;
};
let Some(order) = (1..=MAX_ORDER_SHORT)
.rev()
.find(|&m| k[m - 1].abs() >= K_MIN)
else {
return zero;
};
let mut coef = [0u8; MAX_ORDER_SHORT];
let mut kq = [0.0f32; MAX_ORDER];
for i in 0..order {
coef[i] = quantize_coef(k[i]);
kq[i] = dequantize_coef(coef[i]);
}
let a = step_up(&kq[..order]);
let mut filtered = [0.0f32; SHORT_WINDOW_LEN];
let n = hi - lo;
filtered[..n].copy_from_slice(&spec[lo..hi]);
analysis_filter(&mut filtered[..n], &spec[lo..hi], &a[..order]);
let (mut e_in, mut e_out) = (0.0f64, 0.0f64);
for i in 0..n {
e_in += f64::from(spec[lo + i]) * f64::from(spec[lo + i]);
e_out += f64::from(filtered[i]) * f64::from(filtered[i]);
}
if e_in < PG_MIN * e_out {
return zero;
}
spec[lo..hi].copy_from_slice(&filtered[..n]);
(order as u8, n_swb as u8, coef)
}
pub fn decide_short(
spec: &mut [f32; LONG_WINDOW_LEN],
offsets: &[u16],
fs_index: u8,
) -> EncTnsShort {
let n_swb = offsets.len() - 1;
let mut out = EncTnsShort::off();
for w in 0..N_WIN {
let base = w * SHORT_WINDOW_LEN;
let (order, length, coef) = decide_window(
&mut spec[base..base + SHORT_WINDOW_LEN],
offsets,
fs_index,
n_swb,
);
out.order[w] = order;
out.length[w] = length;
out.coef[w] = coef;
if order > 0 {
out.on = true;
}
}
out
}
impl EncTns {
pub(crate) fn set_short(&mut self, s: EncTnsShort) {
self.short = s;
if s.is_on() {
self.on = false; }
}
pub(crate) fn short(&self) -> EncTnsShort {
self.short
}
}