use super::bwexpander_32::bwexpander_32;
use super::lpc_inv_pred_gain::SILK_MAX_ORDER_LPC;
use super::table_lsf_cos::{LSF_COS_TAB_SZ_FIX, SILK_LSF_COS_TAB_FIX_Q12};
const BIN_DIV_STEPS_A2NLSF_FIX: usize = 3;
const MAX_ITERATIONS_A2NLSF_FIX: usize = 16;
#[allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss
)]
pub fn a2nlsf(nlsf_q15: &mut [i16], a_q16: &mut [i32]) {
let d = nlsf_q15.len();
assert_eq!(d, a_q16.len(), "filter order mismatch");
assert!(d.is_multiple_of(2), "SILK requires an even LPC order");
assert!(d <= SILK_MAX_ORDER_LPC, "order exceeds SILK_MAX_ORDER_LPC");
let dd = d / 2;
let mut p = [0i32; SILK_MAX_ORDER_LPC / 2 + 1];
let mut q = [0i32; SILK_MAX_ORDER_LPC / 2 + 1];
a2nlsf_init(&a_q16[..d], &mut p[..=dd], &mut q[..=dd], dd);
let mut xlo = i32::from(SILK_LSF_COS_TAB_FIX_Q12[0]);
let mut ylo = a2nlsf_eval_poly(&p[..=dd], xlo, dd);
let mut root_ix = 0usize;
if ylo < 0 {
nlsf_q15[0] = 0;
root_ix = 1;
ylo = a2nlsf_eval_poly(&q[..=dd], xlo, dd);
}
let mut k = 1usize;
let mut iteration = 0usize;
let mut thr = 0;
while root_ix < d {
if k > LSF_COS_TAB_SZ_FIX {
iteration += 1;
if iteration > MAX_ITERATIONS_A2NLSF_FIX {
let spacing = div32_16(1 << 15, (d + 1) as i32) as i16;
nlsf_q15[0] = spacing;
for idx in 1..d {
nlsf_q15[idx] = nlsf_q15[idx - 1].wrapping_add(spacing);
}
return;
}
let chirp_q16 = (1 << 16) - (1 << iteration);
bwexpander_32(&mut a_q16[..d], chirp_q16);
a2nlsf_init(&a_q16[..d], &mut p[..=dd], &mut q[..=dd], dd);
xlo = i32::from(SILK_LSF_COS_TAB_FIX_Q12[0]);
ylo = a2nlsf_eval_poly(&p[..=dd], xlo, dd);
if ylo < 0 {
nlsf_q15[0] = 0;
root_ix = 1;
ylo = a2nlsf_eval_poly(&q[..=dd], xlo, dd);
} else {
root_ix = 0;
}
k = 1;
thr = 0;
continue;
}
let mut xhi = i32::from(SILK_LSF_COS_TAB_FIX_Q12[k]);
let mut yhi = if (root_ix & 1) == 0 {
a2nlsf_eval_poly(&p[..=dd], xhi, dd)
} else {
a2nlsf_eval_poly(&q[..=dd], xhi, dd)
};
if (ylo <= 0 && yhi >= thr) || (ylo >= 0 && yhi <= -thr) {
thr = if yhi == 0 { 1 } else { 0 };
let mut ffrac = -256;
for m in 0..BIN_DIV_STEPS_A2NLSF_FIX {
let xmid = rshift_round32(xlo.wrapping_add(xhi), 1);
let ymid = if (root_ix & 1) == 0 {
a2nlsf_eval_poly(&p[..=dd], xmid, dd)
} else {
a2nlsf_eval_poly(&q[..=dd], xmid, dd)
};
if (ylo <= 0 && ymid >= 0) || (ylo >= 0 && ymid <= 0) {
xhi = xmid;
yhi = ymid;
} else {
xlo = xmid;
ylo = ymid;
ffrac = add_rshift(ffrac, 128, m);
}
}
if ylo.abs() < 65_536 {
let den = ylo.wrapping_sub(yhi);
if den != 0 {
let nom =
(ylo << (8 - BIN_DIV_STEPS_A2NLSF_FIX)).wrapping_add(rshift32(den, 1));
ffrac = ffrac.wrapping_add(div32(nom, den));
}
} else {
let denom = rshift32(ylo.wrapping_sub(yhi), 8 - BIN_DIV_STEPS_A2NLSF_FIX);
if denom != 0 {
ffrac = ffrac.wrapping_add(div32(ylo, denom));
}
}
let value = ((k as i32) << 8).wrapping_add(ffrac);
let clamped = value.clamp(0, i32::from(i16::MAX));
debug_assert!(clamped >= 0, "NLSF values must be non-negative");
nlsf_q15[root_ix] = clamped as i16;
root_ix += 1;
if root_ix >= d {
break;
}
xlo = i32::from(SILK_LSF_COS_TAB_FIX_Q12[k - 1]);
ylo = (1 - ((root_ix & 2) as i32)) << 12;
} else {
k += 1;
xlo = xhi;
ylo = yhi;
thr = 0;
}
}
}
fn a2nlsf_trans_poly(poly: &mut [i32], dd: usize) {
for k in 2..=dd {
for n in ((k + 1)..=dd).rev() {
let idx = n - 2;
poly[idx] = poly[idx].wrapping_sub(poly[n]);
}
let idx = k - 2;
poly[idx] = poly[idx].wrapping_sub(poly[k] << 1);
}
}
fn a2nlsf_eval_poly(poly: &[i32], x: i32, dd: usize) -> i32 {
let mut y32 = poly[dd];
let x_q16 = x << 4;
if dd == 8 {
y32 = smlaaw(poly[7], y32, x_q16);
y32 = smlaaw(poly[6], y32, x_q16);
y32 = smlaaw(poly[5], y32, x_q16);
y32 = smlaaw(poly[4], y32, x_q16);
y32 = smlaaw(poly[3], y32, x_q16);
y32 = smlaaw(poly[2], y32, x_q16);
y32 = smlaaw(poly[1], y32, x_q16);
smlaaw(poly[0], y32, x_q16)
} else {
for n in (0..dd).rev() {
y32 = smlaaw(poly[n], y32, x_q16);
}
y32
}
}
fn a2nlsf_init(a_q16: &[i32], p: &mut [i32], q: &mut [i32], dd: usize) {
p[dd] = 1 << 16;
q[dd] = 1 << 16;
for k in 0..dd {
let even = a_q16[dd - k - 1];
let odd = a_q16[dd + k];
let sum = even.wrapping_add(odd);
let diff = odd.wrapping_sub(even);
p[k] = sum.wrapping_neg();
q[k] = diff;
}
for k in (1..=dd).rev() {
p[k - 1] = p[k - 1].wrapping_sub(p[k]);
q[k - 1] = q[k - 1].wrapping_add(q[k]);
}
a2nlsf_trans_poly(p, dd);
a2nlsf_trans_poly(q, dd);
}
fn add_rshift(value: i32, addend: i32, shift: usize) -> i32 {
value.wrapping_add(addend >> shift)
}
fn rshift_round32(value: i32, shift: u32) -> i32 {
if shift == 0 {
return value;
}
if shift == 1 {
(value >> 1) + (value & 1)
} else {
((value >> (shift - 1)) + 1) >> 1
}
}
fn rshift32(value: i32, shift: usize) -> i32 {
if shift == 0 { value } else { value >> shift }
}
fn div32(a: i32, b: i32) -> i32 {
a / b
}
fn div32_16(a: i32, b: i32) -> i32 {
a / b
}
fn smlaaw(a: i32, b: i32, c: i32) -> i32 {
a.wrapping_add(((i64::from(b) * i64::from(c)) >> 16) as i32)
}
#[cfg(test)]
mod tests {
use super::SILK_MAX_ORDER_LPC;
use super::a2nlsf;
#[test]
fn matches_reference_output_for_known_lpc() {
let order = 16;
let mut a_q16 = [0i32; SILK_MAX_ORDER_LPC];
a_q16[..order].copy_from_slice(&[
15520, 2208, 4400, 8720, -1360, 13632, 1152, 7184, -1312, -6496, -15904, 3872, 11968,
-10720, 8272, -7616,
]);
let mut output = [0i16; SILK_MAX_ORDER_LPC];
a2nlsf(&mut output[..order], &mut a_q16[..order]);
let expected = [
1496, 2925, 5334, 8052, 9524, 10640, 13688, 15291, 16759, 19462, 21048, 22212, 25217,
26443, 29500, 31037,
];
assert_eq!(&output[..order], &expected);
}
}