use super::bits::BitWriter;
use super::det_math;
use super::enc_quant::MAX_BANDS;
use super::ics::WindowSequence;
use super::swb::LONG_WINDOW_LEN;
#[path = "enc_tns_lpc.rs"]
mod lpc;
use lpc::{analysis_filter, autocorrelate};
#[path = "enc_tns_short.rs"]
mod short;
pub use short::{EncTnsShort, decide_short};
#[cfg(test)]
#[path = "enc_tns_short_tests.rs"]
mod enc_tns_short_tests;
pub const MAX_ORDER: usize = 12;
const K_MIN: f64 = 0.1;
const PG_MIN: f64 = 1.585;
const TNS_MAX_BANDS_LONG: [u8; 12] = [31, 31, 34, 40, 42, 51, 46, 46, 42, 42, 42, 39];
#[derive(Debug, Clone, Copy)]
pub struct EncTns {
on: bool,
order: u8,
start: u8,
end: u8,
spacer: u8,
length: u8,
coef: [u8; MAX_ORDER],
short: EncTnsShort,
}
impl EncTns {
pub fn off() -> Self {
Self {
on: false,
order: 0,
start: 0,
end: 0,
spacer: 0,
length: 0,
coef: [0; MAX_ORDER],
short: EncTnsShort::off(),
}
}
#[cfg(test)]
pub fn is_on(&self) -> bool {
self.on || self.short.is_on()
}
pub fn band_range(&self) -> Option<(usize, usize)> {
if self.on {
Some((usize::from(self.start), usize::from(self.end)))
} else {
None
}
}
pub fn bits(&self) -> usize {
if self.short.is_on() {
return self.short.bits();
}
if !self.on {
return 1;
}
let spacer = if self.spacer > 0 { 6 + 5 } else { 0 };
1 + 2 + 1 + spacer + 6 + 5 + 2 + 4 * self.order as usize
}
pub fn emit(&self, w: &mut BitWriter) {
if self.short.is_on() {
self.short.emit(w);
return;
}
w.write_bit(self.on);
self.emit_body(w);
}
pub(crate) fn emit_body(&self, w: &mut BitWriter) {
if self.short.is_on() || !self.on {
return;
}
w.write(if self.spacer > 0 { 2 } else { 1 }, 2); w.write_bit(true); if self.spacer > 0 {
w.write(u32::from(self.spacer), 6);
w.write(0, 5); }
w.write(u32::from(self.length), 6);
w.write(u32::from(self.order), 5);
w.write_bit(false); w.write_bit(false); for &c in self.coef.iter().take(self.order as usize) {
w.write(u32::from(c), 4);
}
}
}
fn levinson(r: &[f64; MAX_ORDER + 1]) -> Option<[f64; MAX_ORDER]> {
let mut k = [0.0f64; MAX_ORDER];
let mut a = [0.0f64; MAX_ORDER];
let mut e = r[0];
for m in 1..=MAX_ORDER {
let mut acc = r[m];
for i in 1..m {
acc += a[i - 1] * r[m - i];
}
let km = -acc / e;
if !(-1.0..1.0).contains(&km) {
return None;
}
k[m - 1] = km;
let old = a;
for i in 1..m {
a[i - 1] = old[i - 1] + km * old[m - i - 1];
}
a[m - 1] = km;
e *= 1.0 - km * km;
}
Some(k)
}
fn quantize_coef(k: f64) -> u8 {
let k = (k as f32).clamp(-0.999, 0.999);
let angle = det_math::atan(k / (1.0 - k * k).sqrt()) * std::f32::consts::FRAC_2_PI;
let t = if k >= 0.0 {
(angle * 7.5).round() as i32
} else {
(angle * 8.5).round() as i32
};
(t.clamp(-8, 7) & 0xF) as u8
}
fn dequantize_coef(wire: u8) -> f32 {
let t = (i32::from(wire) << 28) >> 28; let scaled = if t >= 0 {
t as f32 / 7.5
} else {
t as f32 / 8.5
};
let s = det_math::sincos(scaled.abs() * std::f32::consts::FRAC_PI_2).0;
if scaled < 0.0 { -s } else { s }
}
fn step_up(kq: &[f32]) -> [f64; MAX_ORDER] {
let mut a = [0.0f64; MAX_ORDER];
for (m0, &km) in kq.iter().enumerate() {
let m = m0 + 1;
let k = f64::from(km);
let old = a;
for i in 1..m {
a[i - 1] = old[i - 1] + k * old[m - i - 1];
}
a[m - 1] = k;
}
a
}
pub fn decide_long(
spec: &mut [f32; LONG_WINDOW_LEN],
offsets: &[u16],
fs_index: u8,
coded: &[bool],
) -> EncTns {
let n_swb = offsets.len() - 1;
let Some(&max_bands) = TNS_MAX_BANDS_LONG.get(fs_index as usize) else {
return EncTns::off();
};
let max_bands = n_swb.min(usize::from(max_bands));
let coded = &coded[..n_swb.min(coded.len())];
let Some(first) = coded.iter().position(|&c| c) else {
return EncTns::off(); };
let Some(last) = coded.iter().rposition(|&c| c) else {
return EncTns::off();
};
let start = first.min(max_bands);
let end = (last + 1).min(max_bands);
let (lo, hi) = (usize::from(offsets[start]), usize::from(offsets[end]));
if hi - lo <= MAX_ORDER {
return EncTns::off();
}
let r = autocorrelate(&spec[lo..hi]);
if r[0] <= 0.0 {
return EncTns::off();
}
let Some(k) = levinson(&r) else {
return EncTns::off();
};
let Some(order) = (1..=MAX_ORDER).rev().find(|&m| k[m - 1].abs() >= K_MIN) else {
return EncTns::off(); };
let (spacer, length) = if end == max_bands {
(0u8, (n_swb - start) as u8)
} else {
((n_swb - end) as u8, (end - start) as u8)
};
let mut out = EncTns {
on: true,
order: order as u8,
start: start as u8,
end: end as u8,
spacer,
length,
coef: [0; MAX_ORDER],
short: EncTnsShort::off(),
};
let mut kq = [0.0f32; MAX_ORDER];
for i in 0..order {
out.coef[i] = quantize_coef(k[i]);
kq[i] = dequantize_coef(out.coef[i]);
}
let a = step_up(&kq[..order]);
let mut filtered = [0.0f32; LONG_WINDOW_LEN];
filtered[lo..hi].copy_from_slice(&spec[lo..hi]);
analysis_filter(&mut filtered[lo..hi], &spec[lo..hi], &a[..order]);
let (mut e_in, mut e_out) = (0.0f64, 0.0f64);
for i in lo..hi {
e_in += f64::from(spec[i]) * f64::from(spec[i]);
e_out += f64::from(filtered[i]) * f64::from(filtered[i]);
}
if e_in < PG_MIN * e_out {
return EncTns::off();
}
spec[lo..hi].copy_from_slice(&filtered[lo..hi]);
out
}
#[allow(clippy::too_many_arguments)]
pub fn decide_frame(
specs: &mut [[f32; LONG_WINDOW_LEN]; 2],
channels: usize,
seq: WindowSequence,
offsets: &[u16],
fs_index: u8,
coded: &[[bool; MAX_BANDS]; 2],
enabled: bool,
short_tns: bool,
) -> [EncTns; 2] {
if !enabled {
return [EncTns::off(), EncTns::off()];
}
if seq.is_eight_short() {
if !short_tns {
return [EncTns::off(), EncTns::off()];
}
let mut out = [EncTns::off(), EncTns::off()];
for ch in 0..channels {
out[ch].set_short(decide_short(&mut specs[ch], offsets, fs_index));
}
return out;
}
let mut out = [EncTns::off(), EncTns::off()];
for ch in 0..channels {
out[ch] = decide_long(&mut specs[ch], offsets, fs_index, &coded[ch]);
}
out
}
#[cfg(test)]
#[path = "enc_tns_tests.rs"]
mod enc_tns_tests;