use oxideav_celt::range_decoder::RangeDecoder;
use oxideav_core::Result;
use crate::silk::tables;
use crate::toc::OpusBandwidth;
pub fn decode_nlsf(
rc: &mut RangeDecoder<'_>,
bw: OpusBandwidth,
signal_type: u8,
) -> Result<Vec<i16>> {
let voiced = signal_type == 2;
let is_wb = matches!(bw, OpusBandwidth::Wideband);
let order = if is_wb { 16 } else { 10 };
let stage1_icdf: &[u8] = match (is_wb, voiced) {
(false, false) => &tables::NLSF_NB_STAGE1_UNVOICED_ICDF,
(false, true) => &tables::NLSF_NB_STAGE1_VOICED_ICDF,
(true, false) => &tables::NLSF_WB_STAGE1_UNVOICED_ICDF,
(true, true) => &tables::NLSF_WB_STAGE1_VOICED_ICDF,
};
let stage1 = rc.decode_icdf(stage1_icdf, 8);
let uniform_11 = &tables::NLSF_RESIDUAL_UNIFORM_11_ICDF;
let mut residuals = vec![0i32; order];
for k in 0..order {
let mag = rc.decode_icdf(uniform_11, 8) as i32 - 4;
let sign = if mag != 0 {
if rc.decode_bit_logp(1) {
-1
} else {
1
}
} else {
1
};
residuals[k] = mag * sign;
}
let _interp_coef = rc.decode_icdf(&[192, 128, 64, 0], 8);
let nlsf_q15 = synthesize_nlsf(stage1, voiced, order, &residuals);
Ok(stabilize(&nlsf_q15, order))
}
fn synthesize_nlsf(stage1: usize, voiced: bool, order: usize, residuals: &[i32]) -> Vec<i16> {
let tilt = (stage1 as f32 / 32.0) * 0.25 + if voiced { 0.0 } else { 0.15 };
let mut nlsf = vec![0i16; order];
for k in 0..order {
let base = (k as f32 + 1.0) / (order as f32 + 1.0);
let tilted = base.powf(1.0 + tilt);
let mut q15 = (tilted * 32768.0) as i32;
q15 += residuals[k].clamp(-7, 7) * 128;
nlsf[k] = q15.clamp(1, 32767) as i16;
}
nlsf
}
const MIN_NLSF_SPACING_Q15: i16 = 250;
pub fn stabilize(nlsf_in: &[i16], order: usize) -> Vec<i16> {
let mut nlsf = nlsf_in.to_vec();
if nlsf.len() < order {
nlsf.resize(order, 0);
}
let min_spacing = MIN_NLSF_SPACING_Q15 as i32;
let mut prev = 0i32;
for k in 0..order {
let cur = nlsf[k] as i32;
let floor = prev + min_spacing;
let v = cur.max(floor);
nlsf[k] = v.min(32767) as i16;
prev = nlsf[k] as i32;
}
let mut next = 32768i32;
for k in (0..order).rev() {
let cap = next - min_spacing;
let cur = nlsf[k] as i32;
nlsf[k] = cur.min(cap).max(1) as i16;
next = nlsf[k] as i32;
}
nlsf
}
pub fn nlsf_to_lpc(nlsf_q15: &[i16], _bw: OpusBandwidth) -> Vec<f32> {
let order = nlsf_q15.len();
let cos_lsf: Vec<f32> = nlsf_q15
.iter()
.map(|&q| (core::f32::consts::PI * (q as f32 / 32768.0)).cos())
.collect();
let half = order / 2;
let mut p = vec![0f32; half + 1];
let mut q = vec![0f32; half + 1];
p[0] = 1.0;
q[0] = 1.0;
for i in 0..half {
let cp = cos_lsf[2 * i];
let cq = cos_lsf[2 * i + 1];
let mut new_p = vec![0f32; p.len() + 2];
let mut new_q = vec![0f32; q.len() + 2];
for j in 0..p.len() {
new_p[j] += p[j];
new_p[j + 1] += -2.0 * cp * p[j];
new_p[j + 2] += p[j];
new_q[j] += q[j];
new_q[j + 1] += -2.0 * cq * q[j];
new_q[j + 2] += q[j];
}
p = new_p;
q = new_q;
}
let mut p_full = vec![0f32; p.len() + 1];
let mut q_full = vec![0f32; q.len() + 1];
for j in 0..p.len() {
p_full[j] += p[j];
p_full[j + 1] += p[j];
q_full[j] += q[j];
q_full[j + 1] -= q[j];
}
let mut a = vec![0f32; order + 1];
for i in 0..=order {
a[i] = 0.5 * (p_full[i] + q_full[i]);
}
let mut lpc = vec![0f32; order];
for k in 0..order {
lpc[k] = -a[k + 1];
}
let mut g = 1.0f32;
for k in 0..order {
g *= 0.98;
lpc[k] *= g;
}
lpc
}