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,
is_20ms: bool,
) -> Result<(Vec<i16>, u8)> {
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 i1 = rc.decode_icdf(stage1_icdf, 8);
let mut i2 = vec![0i32; order];
for k in 0..order {
let cb_letter = if is_wb {
tables::NLSF_WB_STAGE2_SELECT[i1][k] as usize
} else {
tables::NLSF_NBMB_STAGE2_SELECT[i1][k] as usize
};
let icdf: &[u8] = if is_wb {
&tables::NLSF_WB_STAGE2_ICDF[cb_letter]
} else {
&tables::NLSF_NBMB_STAGE2_ICDF[cb_letter]
};
let sym = rc.decode_icdf(icdf, 8) as i32 - 4;
let mut idx = sym;
if idx == -4 || idx == 4 {
let ext = rc.decode_icdf(&tables::NLSF_STAGE2_EXTENSION_ICDF, 8) as i32;
if idx > 0 {
idx += ext;
} else {
idx -= ext;
}
}
i2[k] = idx;
}
let interp_coef: u8 = if is_20ms {
rc.decode_icdf(&tables::NLSF_INTERP_ICDF, 8) as u8
} else {
4
};
let qstep: i32 = if is_wb { 9830 } else { 11796 };
let mut res_q10 = vec![0i32; order];
for k in (0..order).rev() {
let prev_term = if k + 1 < order {
let pred = pred_weight_q8(i1, k, is_wb) as i32;
(res_q10[k + 1] * pred) >> 8
} else {
0
};
let i2_k = i2[k];
let sign_i2 = i2_k.signum();
let raw = (i2_k << 10) - sign_i2 * 102;
let dequant = (raw * qstep) >> 16;
res_q10[k] = prev_term + dequant;
}
let cb1_q8: Vec<i32> = if is_wb {
tables::NLSF_WB_CB1_Q8[i1]
.iter()
.map(|&v| v as i32)
.collect()
} else {
tables::NLSF_NBMB_CB1_Q8[i1]
.iter()
.map(|&v| v as i32)
.collect()
};
let w_q9 = compute_ihmw_weights(&cb1_q8);
let mut nlsf_q15 = vec![0i16; order];
for k in 0..order {
let cb_term = cb1_q8[k] << 7;
let weighted = (res_q10[k] << 14) / w_q9[k] as i32;
let v = (cb_term + weighted).clamp(0, 32767);
nlsf_q15[k] = v as i16;
}
Ok((stabilize(&nlsf_q15, is_wb), interp_coef))
}
fn pred_weight_q8(i1: usize, k: usize, is_wb: bool) -> u8 {
if is_wb {
let sel = tables::NLSF_WB_PRED_SELECT[i1][k] as usize;
let list = 2 + sel;
tables::NLSF_PRED_WEIGHTS[list][k]
} else {
let sel = tables::NLSF_NBMB_PRED_SELECT[i1][k] as usize;
let list = sel; tables::NLSF_PRED_WEIGHTS[list][k]
}
}
fn compute_ihmw_weights(cb1_q8: &[i32]) -> Vec<u16> {
let order = cb1_q8.len();
let mut w = vec![0u16; order];
for k in 0..order {
let prev = if k == 0 { 0 } else { cb1_q8[k - 1] };
let next = if k + 1 == order { 256 } else { cb1_q8[k + 1] };
let lo_diff = (cb1_q8[k] - prev).max(1);
let hi_diff = (next - cb1_q8[k]).max(1);
let w2_q18: i32 = (1024 / lo_diff + 1024 / hi_diff) << 16;
w[k] = isqrt_q9_approx(w2_q18 as u32);
}
w
}
fn isqrt_q9_approx(w2_q18: u32) -> u16 {
if w2_q18 == 0 {
return 1;
}
let i = 32 - w2_q18.leading_zeros() as i32; let shift = (i - 8).max(0);
let f = ((w2_q18 >> shift) & 127) as i32;
let base: i32 = if i & 1 == 1 { 32768 } else { 46214 };
let shr = ((32 - i) >> 1).max(0);
let y = base >> shr;
let w_q9 = y + ((213 * f * y) >> 16);
w_q9.clamp(1, u16::MAX as i32) as u16
}
pub fn stabilize(nlsf_in: &[i16], is_wb: bool) -> Vec<i16> {
let order = nlsf_in.len();
let ndelta_min: &[i16] = if is_wb {
&tables::NLSF_WB_MIN_DELTA_Q15
} else {
&tables::NLSF_NBMB_MIN_DELTA_Q15
};
let mut nlsf: Vec<i32> = nlsf_in.iter().map(|&v| v as i32).collect();
for _round in 0..20 {
let mut min_diff = i32::MAX;
let mut min_i: usize = 0;
for i in 0..=order {
let lhs = if i == 0 { 0 } else { nlsf[i - 1] };
let rhs = if i == order { 32768 } else { nlsf[i] };
let diff = (rhs - lhs) - ndelta_min[i] as i32;
if diff < min_diff {
min_diff = diff;
min_i = i;
}
}
if min_diff >= 0 {
break;
}
if min_i == 0 {
nlsf[0] = ndelta_min[0] as i32;
} else if min_i == order {
nlsf[order - 1] = 32768 - ndelta_min[order] as i32;
} else {
let mut min_center = (ndelta_min[min_i] as i32) >> 1;
for k in 0..min_i {
min_center += ndelta_min[k] as i32;
}
let mut max_center = 32768 - ((ndelta_min[min_i] as i32) >> 1);
for k in (min_i + 1)..=order {
max_center -= ndelta_min[k] as i32;
}
let avg = (nlsf[min_i - 1] + nlsf[min_i] + 1) >> 1;
let center = avg.clamp(min_center, max_center);
nlsf[min_i - 1] = center - ((ndelta_min[min_i] as i32) >> 1);
nlsf[min_i] = nlsf[min_i - 1] + ndelta_min[min_i] as i32;
}
}
nlsf.sort();
let mut prev: i32 = 0;
for k in 0..order {
let lower = prev + ndelta_min[k] as i32;
if nlsf[k] < lower {
nlsf[k] = lower;
}
prev = nlsf[k];
}
let mut next: i32 = 32768;
for k in (0..order).rev() {
let upper = next - ndelta_min[k + 1] as i32;
if nlsf[k] > upper {
nlsf[k] = upper;
}
next = nlsf[k];
}
nlsf.iter().map(|&v| v.clamp(1, 32767) as i16).collect()
}
pub fn nlsf_to_lpc(nlsf_q15: &[i16], _bw: OpusBandwidth) -> Vec<f32> {
let order = nlsf_q15.len();
let is_wb = order == 16;
let ordering: &[usize] = if is_wb {
&tables::NLSF_ORDERING_WB
} else {
&tables::NLSF_ORDERING_NB
};
let mut c_q17 = vec![0i32; order];
for k in 0..order {
let n = nlsf_q15[k] as i32;
let i = (n >> 8) as usize;
let f = n & 255;
let i = i.min(127);
let cos_i = tables::COSINE_Q12[i] as i32;
let cos_i1 = tables::COSINE_Q12[i + 1] as i32;
let v = (cos_i * 256 + (cos_i1 - cos_i) * f + 4) >> 3;
c_q17[ordering[k]] = v;
}
let d2 = order / 2;
let mut p_prev = vec![0i64; d2 + 2];
let mut q_prev = vec![0i64; d2 + 2];
p_prev[0] = 1 << 16;
p_prev[1] = -(c_q17[0] as i64);
q_prev[0] = 1 << 16;
q_prev[1] = -(c_q17[1] as i64);
p_prev[2] = p_prev[0];
q_prev[2] = q_prev[0];
for k in 1..d2 {
let mut p_cur = vec![0i64; d2 + 2];
let mut q_cur = vec![0i64; d2 + 2];
let cp = c_q17[2 * k] as i64;
let cq = c_q17[2 * k + 1] as i64;
for j in 0..=k + 1 {
let p_jm2 = if j >= 2 { p_prev[j - 2] } else { 0 };
let q_jm2 = if j >= 2 { q_prev[j - 2] } else { 0 };
let p_jm1 = if j >= 1 { p_prev[j - 1] } else { 0 };
let q_jm1 = if j >= 1 { q_prev[j - 1] } else { 0 };
let p_j = p_prev[j];
let q_j = q_prev[j];
p_cur[j] = p_j + p_jm2 - ((cp * p_jm1 + 32768) >> 16);
q_cur[j] = q_j + q_jm2 - ((cq * q_jm1 + 32768) >> 16);
}
if k + 2 < p_cur.len() {
p_cur[k + 2] = p_cur[k];
q_cur[k + 2] = q_cur[k];
}
p_prev = p_cur;
q_prev = q_cur;
}
let mut a32_q17 = vec![0i64; order];
for k in 0..d2 {
let q_diff = q_prev[k + 1] - q_prev[k];
let p_sum = p_prev[k + 1] + p_prev[k];
a32_q17[k] = -q_diff - p_sum;
a32_q17[order - k - 1] = q_diff - p_sum;
}
let mut lpc = vec![0f32; order];
for k in 0..order {
lpc[k] = (a32_q17[k] as f32) / (1 << 17) as f32;
}
for _round in 0..32 {
let dc: f32 = lpc.iter().sum();
if dc.abs() < 0.02 {
break;
}
let mut g = 1.0f32;
for v in lpc.iter_mut() {
g *= 0.85;
*v *= g;
}
}
let mut g = 1.0f32;
for v in lpc.iter_mut() {
g *= 0.98;
*v *= g;
}
lpc
}
fn bandwidth_expand_q17(a32_q17: &mut [i32]) {
let mut saturated = false;
for _round in 0..10 {
let mut max_abs: i32 = 0;
let mut max_k: usize = 0;
for (k, &v) in a32_q17.iter().enumerate() {
let a = v.unsigned_abs() as i32;
if a > max_abs {
max_abs = a;
max_k = k;
}
}
let maxabs_q12 = ((max_abs + 16) >> 5).min(163838);
if maxabs_q12 <= 32767 {
saturated = true;
break;
}
let denom = (maxabs_q12 * (max_k as i32 + 1)) >> 2;
if denom == 0 {
break;
}
let sc_q16_0 = 65470 - (((maxabs_q12 - 32767) << 14) / denom);
bwexpander_32(a32_q17, sc_q16_0);
}
if !saturated {
for v in a32_q17.iter_mut() {
let q12 = (*v + 16) >> 5;
let clamped = q12.clamp(-32768, 32767);
*v = clamped << 5;
}
}
for round in 0..16 {
if lpc_inverse_pred_gain_is_stable(a32_q17) {
for v in a32_q17.iter_mut() {
let q12 = (*v + 16) >> 5;
*v = q12 << 5;
}
return;
}
if round == 15 {
for v in a32_q17.iter_mut() {
*v = 0;
}
return;
}
let sc_q16_0 = 65536 - (2 << round);
bwexpander_32(a32_q17, sc_q16_0);
for v in a32_q17.iter_mut() {
let q12 = (*v + 16) >> 5;
let clamped = q12.clamp(-32768, 32767);
*v = clamped << 5;
}
}
for v in a32_q17.iter_mut() {
*v = 0;
}
}
fn bwexpander_32(a32_q17: &mut [i32], sc_q16_0: i32) {
let mut sc_q16: i64 = sc_q16_0 as i64;
for v in a32_q17.iter_mut() {
let prod = (*v as i64) * sc_q16;
*v = (prod >> 16) as i32;
sc_q16 = ((sc_q16_0 as i64) * sc_q16 + 32768) >> 16;
}
}
fn lpc_inverse_pred_gain_is_stable(a32_q17: &[i32]) -> bool {
let d_lpc = a32_q17.len();
let a32_q12: Vec<i32> = a32_q17.iter().map(|&v| (v + 16) >> 5).collect();
let dc_resp: i64 = a32_q12.iter().map(|&v| v as i64).sum();
if dc_resp > 4096 {
return false;
}
let mut a_q24: Vec<i64> = a32_q12.iter().map(|&v| (v as i64) << 12).collect();
let mut inv_gain_q30: i64 = 1_i64 << 30;
let mut k = d_lpc as i32 - 1;
while k >= 0 {
let kk = k as usize;
let a_kk = a_q24[kk];
if a_kk.unsigned_abs() > 16_773_022 {
return false;
}
let rc_q31: i64 = -(a_kk) << 7;
let rc_sq = (rc_q31.wrapping_mul(rc_q31)) >> 32;
let div_q30: i64 = (1_i64 << 30) - rc_sq;
inv_gain_q30 = (inv_gain_q30.wrapping_mul(div_q30) >> 32) << 2;
if inv_gain_q30 < 107_374 {
return false;
}
if k == 0 {
break;
}
let b1 = ilog_u32(div_q30 as u64);
let b2 = b1 - 16;
if !(0..=30).contains(&b2) {
return false;
}
let div_shifted = (div_q30 >> (b2 + 1)).max(1);
let inv_qb2 = ((1_i64 << 29) - 1) / div_shifted;
let shamt = 15 - b2;
if !(0..=31).contains(&shamt) {
return false;
}
let err_q29 = (1_i64 << 29) - (((div_q30 << shamt).wrapping_mul(inv_qb2)) >> 16);
let gain_qb1 = (inv_qb2 << 16) + ((err_q29.wrapping_mul(inv_qb2)) >> 13);
let mut new_row = vec![0_i64; kk];
for n in 0..kk {
let num = a_q24[n] - (((a_q24[kk - n - 1].wrapping_mul(rc_q31)) + (1_i64 << 30)) >> 31);
let v = (num.wrapping_mul(gain_qb1) + (1_i64 << (b1 - 1))) >> b1;
new_row[n] = v;
}
for (n, &v) in new_row.iter().enumerate() {
a_q24[n] = v;
}
k -= 1;
}
true
}
fn ilog_u32(x: u64) -> i32 {
if x == 0 {
0
} else {
64 - x.leading_zeros() as i32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ihmw_weights_in_range_nbmb_i1_0() {
let cb1: Vec<i32> = tables::NLSF_NBMB_CB1_Q8[0]
.iter()
.map(|&v| v as i32)
.collect();
let w = compute_ihmw_weights(&cb1);
for (i, &wi) in w.iter().enumerate() {
assert!(
(1819..=5227).contains(&wi),
"NB/MB I1=0 IHMW w[{i}] = {wi} outside spec range [1819,5227]"
);
}
}
#[test]
fn ihmw_weights_in_range_wb_i1_0() {
let cb1: Vec<i32> = tables::NLSF_WB_CB1_Q8[0]
.iter()
.map(|&v| v as i32)
.collect();
let w = compute_ihmw_weights(&cb1);
for (i, &wi) in w.iter().enumerate() {
assert!(
(1819..=5227).contains(&wi),
"WB I1=0 IHMW w[{i}] = {wi} outside spec range [1819,5227]"
);
}
}
#[test]
fn stabilize_monotone_and_min_spacing_nbmb() {
let broken = vec![0i16; 10];
let fixed = stabilize(&broken, false);
assert_eq!(fixed.len(), 10);
let mut prev: i32 = 0;
for (k, &v) in fixed.iter().enumerate() {
assert!(v >= 1, "k={k} v={v}");
assert!(
(v as i32) - prev >= tables::NLSF_NBMB_MIN_DELTA_Q15[k] as i32,
"k={k} v={v} prev={prev}"
);
prev = v as i32;
}
assert!(32768 - prev >= tables::NLSF_NBMB_MIN_DELTA_Q15[10] as i32);
}
#[test]
fn stabilize_monotone_and_min_spacing_wb() {
let broken = vec![0i16; 16];
let fixed = stabilize(&broken, true);
assert_eq!(fixed.len(), 16);
let mut prev: i32 = 0;
for (k, &v) in fixed.iter().enumerate() {
assert!(v >= 1, "k={k} v={v}");
assert!(
(v as i32) - prev >= tables::NLSF_WB_MIN_DELTA_Q15[k] as i32,
"k={k} v={v} prev={prev}"
);
prev = v as i32;
}
assert!(32768 - prev >= tables::NLSF_WB_MIN_DELTA_Q15[16] as i32);
}
#[test]
fn cosine_lut_monotone() {
for w in tables::COSINE_Q12.windows(2) {
assert!(w[0] >= w[1], "{} should be >= {}", w[0], w[1]);
}
assert_eq!(tables::COSINE_Q12[0], 4096);
assert_eq!(tables::COSINE_Q12[64], 0);
assert_eq!(tables::COSINE_Q12[128], -4096);
}
#[test]
fn lpc_dc_response_is_safely_bounded() {
for i1 in 0..32usize {
let nlsf: Vec<i16> = tables::NLSF_NBMB_CB1_Q8[i1]
.iter()
.map(|&v| ((v as i32) << 7) as i16)
.collect();
let lpc = nlsf_to_lpc(&nlsf, OpusBandwidth::Narrowband);
let dc: f32 = lpc.iter().sum();
assert!(dc.abs() < 0.5, "NB I1={i1} LPC DC sum {dc} too large");
}
for i1 in 0..32usize {
let nlsf: Vec<i16> = tables::NLSF_WB_CB1_Q8[i1]
.iter()
.map(|&v| ((v as i32) << 7) as i16)
.collect();
let lpc = nlsf_to_lpc(&nlsf, OpusBandwidth::Wideband);
let dc: f32 = lpc.iter().sum();
assert!(dc.abs() < 0.5, "WB I1={i1} LPC DC sum {dc} too large");
}
}
#[test]
fn nlsf_to_lpc_lengths() {
let nb = vec![
3000i16, 6000, 9000, 12000, 15000, 18000, 21000, 24000, 27000, 30000,
];
let lpc = nlsf_to_lpc(&nb, OpusBandwidth::Narrowband);
assert_eq!(lpc.len(), 10);
let wb: Vec<i16> = (1..=16).map(|k| (k * 2000) as i16).collect();
let lpc = nlsf_to_lpc(&wb, OpusBandwidth::Wideband);
assert_eq!(lpc.len(), 16);
}
}