use crate::{CodecError, CodecResult};
use super::silk_decoder::{nlsf_to_lpc, stabilise_nlsf, SilkBandwidth};
use super::silk_tables as t;
pub(super) struct NlsfDecision {
pub i1: usize,
pub res_idx: Vec<i32>,
pub nlsf_q15: Vec<i16>,
}
pub(super) const NLSF_RES_MAX: i32 = 10;
pub(super) fn autocorrelation(x: &[f32], max_lag: usize) -> Vec<f64> {
let n = x.len();
let mut windowed = Vec::with_capacity(n);
if n > 1 {
let scale = std::f64::consts::PI / (n as f64);
for (i, &s) in x.iter().enumerate() {
let w = 0.5 - 0.5 * (2.0 * scale * (i as f64 + 0.5)).cos();
windowed.push(f64::from(s) * w);
}
} else {
windowed.extend(x.iter().map(|&s| f64::from(s)));
}
let mut r = vec![0.0f64; max_lag + 1];
for lag in 0..=max_lag {
let mut s = 0.0f64;
for i in lag..n {
s += windowed[i] * windowed[i - lag];
}
r[lag] = s;
}
let lag_step = 1.0e-4f64;
for (k, slot) in r.iter_mut().enumerate() {
let arg = 2.0 * std::f64::consts::PI * lag_step * (k as f64);
*slot *= (-0.5 * arg * arg).exp();
}
if r[0] > 0.0 {
r[0] *= 1.000_1;
} else {
r[0] = 1e-12;
}
r
}
pub(super) fn levinson_durbin(r: &[f64], p: usize) -> Vec<f64> {
if p == 0 {
return Vec::new();
}
let mut a = vec![0.0f64; p];
let mut prev = vec![0.0f64; p];
let mut e = if r[0] > 0.0 { r[0] } else { 1e-30 };
for i in 0..p {
let mut k = r[i + 1];
for j in 0..i {
k -= a[j] * r[i - j];
}
let k = if e.abs() > 1e-30 { k / e } else { 0.0 };
let k = k.clamp(-0.999, 0.999);
prev[..i].copy_from_slice(&a[..i]);
for j in 0..i {
a[j] = prev[j] - k * prev[i - 1 - j];
}
a[i] = k;
e *= 1.0 - k * k;
if e <= 0.0 {
e = 1e-30;
}
}
let chirp = 0.99f64;
let mut factor = chirp;
for ak in a.iter_mut() {
*ak *= factor;
factor *= chirp;
}
a
}
fn quantise_nlsf_stage1(samples: &[f32], bw: SilkBandwidth) -> CodecResult<usize> {
let order = bw.lpc_order();
let r = autocorrelation(samples, order);
let a = levinson_durbin(&r, order);
let nlsf_target = lpc_to_nlsf(&a, order);
let mut best_idx = 0usize;
let mut best_dist = f64::INFINITY;
let num_entries = if bw.is_wideband() {
t::NLSF_CB1_WB_Q8.len()
} else {
t::NLSF_CB1_NB_MB_Q8.len()
};
for i in 0..num_entries {
let mut dist = 0.0f64;
for k in 0..order {
let (cb_q8, wght_q9) = if bw.is_wideband() {
(
i32::from(t::NLSF_CB1_WB_Q8[i][k]),
i32::from(t::NLSF_CB1_WGHT_WB_Q9[i][k]),
)
} else {
(
i32::from(t::NLSF_CB1_NB_MB_Q8[i][k]),
i32::from(t::NLSF_CB1_WGHT_NB_MB_Q9[i][k]),
)
};
let cb_nlsf = f64::from(cb_q8) / 256.0;
let diff = nlsf_target[k] - cb_nlsf;
dist += diff * diff * f64::from(wght_q9);
}
if dist < best_dist {
best_dist = dist;
best_idx = i;
}
}
Ok(best_idx)
}
pub(super) fn quantise_nlsf_full(samples: &[f32], bw: SilkBandwidth) -> CodecResult<NlsfDecision> {
let order = bw.lpc_order();
let i1 = quantise_nlsf_stage1(samples, bw)?;
let r = autocorrelation(samples, order);
let a = levinson_durbin(&r, order);
let mut nlsf_target_q15 = vec![0i32; order];
{
let nlsf_target = lpc_to_nlsf(&a, order);
for (k, &v) in nlsf_target.iter().enumerate().take(order) {
nlsf_target_q15[k] = (v * 32768.0).round().clamp(0.0, 32767.0) as i32;
}
}
let (cb_row, wght_row, select_row, pred_row0, pred_row1, _cb2_icdf_set) = if bw.is_wideband() {
(
&t::NLSF_CB1_WB_Q8[i1][..],
&t::NLSF_CB1_WGHT_WB_Q9[i1][..],
&t::NLSF_CB2_SELECT_WB[i1 * (order / 2)..i1 * (order / 2) + order / 2],
&t::NLSF_PRED_WB_Q8[0][..],
&t::NLSF_PRED_WB_Q8[1][..],
&t::NLSF_CB2_ICDF_WB[..],
)
} else {
(
&t::NLSF_CB1_NB_MB_Q8[i1][..],
&t::NLSF_CB1_WGHT_NB_MB_Q9[i1][..],
&t::NLSF_CB2_SELECT_NB_MB[i1 * (order / 2)..i1 * (order / 2) + order / 2],
&t::NLSF_PRED_NB_MB_Q8[0][..],
&t::NLSF_PRED_NB_MB_Q8[1][..],
&t::NLSF_CB2_ICDF_NB_MB[..],
)
};
let mut pred_q8 = vec![0i32; order];
for pair in 0..order / 2 {
let entry = select_row[pair];
let even = 2 * pair;
let odd = even + 1;
if even < order - 1 {
pred_q8[even] = if entry & 0x01 != 0 {
i32::from(pred_row1[even])
} else {
i32::from(pred_row0[even])
};
}
if odd < order - 1 {
pred_q8[odd] = if (entry >> 4) & 0x01 != 0 {
i32::from(pred_row1[odd])
} else {
i32::from(pred_row0[odd])
};
}
}
let qstep = if bw.is_wideband() {
t::NLSF_QSTEP_WB
} else {
t::NLSF_QSTEP_NB_MB
};
let mut target_q10 = vec![0i32; order];
for k in 0..order {
let cb_q8 = i32::from(cb_row[k]);
let wght_q9 = i32::from(wght_row[k]);
let diff_q15 = nlsf_target_q15[k] - (cb_q8 << 7);
let v_q24 = i64::from(diff_q15) * i64::from(wght_q9);
target_q10[k] = (v_q24 >> 14) as i32;
}
let mut res_idx = vec![0i32; order];
let mut out_q10 = 0i32;
let mut residual_q10 = vec![0i32; order];
for coeff in (0..order).rev() {
let pred_q10 = (out_q10 * pred_q8[coeff]) >> 8;
let target_step = target_q10[coeff] - pred_q10;
let r_est = if target_step > 0 {
((i64::from(target_step) * 65536 / i64::from(qstep)) + 102 + 512) >> 10
} else if target_step < 0 {
((i64::from(target_step) * 65536 / i64::from(qstep)) - 102 - 512) >> 10
} else {
0
};
let mut best_r = 0i32;
let mut best_err = i64::MAX;
let mut best_out = pred_q10;
for cand_off in -2..=2 {
let cand_r = ((r_est as i32) + cand_off).clamp(-NLSF_RES_MAX, NLSF_RES_MAX);
let raw = match cand_r.cmp(&0) {
std::cmp::Ordering::Greater => (cand_r << 10) - 102,
std::cmp::Ordering::Less => (cand_r << 10) + 102,
std::cmp::Ordering::Equal => 0,
};
let step = ((i64::from(raw) * i64::from(qstep)) >> 16) as i32;
let cand_out = pred_q10 + step;
let err = (i64::from(target_q10[coeff]) - i64::from(cand_out)).abs();
if err < best_err {
best_err = err;
best_r = cand_r;
best_out = cand_out;
}
}
res_idx[coeff] = best_r;
out_q10 = best_out;
residual_q10[coeff] = best_out;
}
let mut nlsf_q15 = vec![0i32; order];
for coeff in 0..order {
let cb_q8 = i32::from(cb_row[coeff]);
let wght_q9 = i32::from(wght_row[coeff]);
let add = if wght_q9 != 0 {
(residual_q10[coeff] << 14) / wght_q9
} else {
0
};
nlsf_q15[coeff] = (add + (cb_q8 << 7)).clamp(0, 32767);
}
let min_spacing: &[i16] = if bw.is_wideband() {
&t::NLSF_DELTA_MIN_WB_Q15
} else {
&t::NLSF_DELTA_MIN_NB_MB_Q15
};
stabilise_nlsf(&mut nlsf_q15, min_spacing, order);
let nlsf_q15_out: Vec<i16> = nlsf_q15.iter().map(|&v| v as i16).collect();
Ok(NlsfDecision {
i1,
res_idx,
nlsf_q15: nlsf_q15_out,
})
}
pub(super) fn lpc_to_nlsf(a: &[f64], order: usize) -> Vec<f64> {
if order == 0 {
return Vec::new();
}
let mut a_full = vec![0.0f64; order + 1];
a_full[0] = 1.0;
for k in 0..order {
a_full[k + 1] = -a[k];
}
let polylen = order + 2;
let mut p = vec![0.0f64; polylen];
let mut q = vec![0.0f64; polylen];
p[0] = a_full[0];
p[order + 1] = a_full[0];
q[0] = a_full[0];
q[order + 1] = -a_full[0];
for k in 1..=order {
p[k] = a_full[k] + a_full[order + 1 - k];
q[k] = a_full[k] - a_full[order + 1 - k];
}
let eval_mag_sq = |poly: &[f64], theta: f64| -> f64 {
let mut re = 0.0f64;
let mut im = 0.0f64;
for (k, &c) in poly.iter().enumerate() {
re += c * (k as f64 * theta).cos();
im -= c * (k as f64 * theta).sin();
}
re * re + im * im
};
let pi = std::f64::consts::PI;
let steps = 4096;
let mut p_vals = Vec::with_capacity(steps);
let mut q_vals = Vec::with_capacity(steps);
for i in 0..steps {
let theta = pi * (i as f64) / ((steps - 1) as f64);
p_vals.push((theta, eval_mag_sq(&p, theta)));
q_vals.push((theta, eval_mag_sq(&q, theta)));
}
let mut roots: Vec<f64> = Vec::with_capacity(order + 4);
let p_peak = p_vals.iter().fold(0.0f64, |a, &(_, v)| a.max(v));
let q_peak = q_vals.iter().fold(0.0f64, |a, &(_, v)| a.max(v));
let p_thresh = (p_peak * 1e-2).max(1e-12);
let q_thresh = (q_peak * 1e-2).max(1e-12);
for i in 1..(steps - 1) {
let (theta, v) = p_vals[i];
if theta <= 1e-9 || theta >= pi - 1e-9 {
continue;
}
if v < p_vals[i - 1].1 && v < p_vals[i + 1].1 && v < p_thresh {
let v0 = p_vals[i - 1].1;
let v1 = v;
let v2 = p_vals[i + 1].1;
let denom = v0 - 2.0 * v1 + v2;
let frac = if denom.abs() > 1e-30 {
0.5 * (v0 - v2) / denom
} else {
0.0
};
let step = pi / ((steps - 1) as f64);
roots.push(theta + frac * step);
}
}
for i in 1..(steps - 1) {
let (theta, v) = q_vals[i];
if theta <= 1e-9 || theta >= pi - 1e-9 {
continue;
}
if v < q_vals[i - 1].1 && v < q_vals[i + 1].1 && v < q_thresh {
let v0 = q_vals[i - 1].1;
let v1 = v;
let v2 = q_vals[i + 1].1;
let denom = v0 - 2.0 * v1 + v2;
let frac = if denom.abs() > 1e-30 {
0.5 * (v0 - v2) / denom
} else {
0.0
};
let step = pi / ((steps - 1) as f64);
roots.push(theta + frac * step);
}
}
roots.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
while roots.len() < order {
let i = roots.len();
roots.push(pi * ((i + 1) as f64) / (order as f64 + 1.0));
}
roots.truncate(order);
roots.iter().map(|&r| (r / pi).clamp(0.0, 1.0)).collect()
}