use core::cmp::Ordering;
use super::bwexpander_32::bwexpander_32;
use super::lpc_fit::lpc_fit;
use super::lpc_inv_pred_gain::{SILK_MAX_ORDER_LPC, lpc_inverse_pred_gain};
use super::table_lsf_cos::{LSF_COS_TAB_SZ_FIX, SILK_LSF_COS_TAB_FIX_Q12};
const QA: i32 = 16;
const MAX_LPC_STABILIZE_ITERATIONS: usize = 16;
const SHIFT_QA1_TO_Q12: i32 = QA + 1 - 12;
const ORDERING16: [usize; 16] = [0, 15, 8, 7, 4, 11, 12, 3, 2, 13, 10, 5, 6, 9, 14, 1];
const ORDERING10: [usize; 10] = [0, 9, 6, 3, 4, 5, 8, 1, 2, 7];
#[allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss
)]
pub fn nlsf2a(a_q12: &mut [i16], nlsf_q15: &[i16], _arch: i32) {
assert_eq!(a_q12.len(), nlsf_q15.len(), "order mismatch");
let order = a_q12.len();
assert!(
order == 10 || order == 16,
"SILK only supports 10 or 16 taps"
);
assert!(order <= SILK_MAX_ORDER_LPC, "order exceeds MAX_ORDER_LPC");
let ordering = if order == 16 {
&ORDERING16[..]
} else {
&ORDERING10[..]
};
let mut cos_lsf_qa = [0i32; SILK_MAX_ORDER_LPC];
for &index in ordering.iter().take(order) {
let nlsf = i32::from(nlsf_q15[index]);
assert!(nlsf >= 0, "NLSF values must be non-negative");
let f_int = (nlsf >> (15 - 7)) as usize;
let f_frac = nlsf - ((f_int as i32) << (15 - 7));
assert!(
f_int < LSF_COS_TAB_SZ_FIX,
"index out of cosine table range"
);
let cos_val = i32::from(SILK_LSF_COS_TAB_FIX_Q12[f_int]);
let delta =
i32::from(SILK_LSF_COS_TAB_FIX_Q12[f_int + 1] - SILK_LSF_COS_TAB_FIX_Q12[f_int]);
let interpolated = rshift_round64(
(i64::from(cos_val) << 8) + i64::from(delta) * i64::from(f_frac),
20 - QA,
);
cos_lsf_qa[index] = interpolated;
}
let dd = order / 2;
let mut p = [0i32; SILK_MAX_ORDER_LPC / 2 + 1];
let mut q = [0i32; SILK_MAX_ORDER_LPC / 2 + 1];
nlsf2a_find_poly(&mut p[..=dd], &cos_lsf_qa[..order], dd);
nlsf2a_find_poly(&mut q[..=dd], &cos_lsf_qa[1..order], dd);
let mut a32_qa1 = [0i32; SILK_MAX_ORDER_LPC];
for k in 0..dd {
let ptmp = p[k + 1].wrapping_add(p[k]);
let qtmp = q[k + 1].wrapping_sub(q[k]);
a32_qa1[k] = qtmp.wrapping_neg().wrapping_sub(ptmp);
a32_qa1[order - k - 1] = qtmp.wrapping_sub(ptmp);
}
lpc_fit(a_q12, &mut a32_qa1[..order], 12, QA + 1);
let mut iteration = 0;
while iteration < MAX_LPC_STABILIZE_ITERATIONS {
if lpc_inverse_pred_gain(a_q12) != 0 {
return;
}
let chirp_q16 = (1 << 16) - (2 << iteration);
bwexpander_32(&mut a32_qa1[..order], chirp_q16);
for (dst, &value) in a_q12.iter_mut().zip(a32_qa1[..order].iter()) {
*dst = rshift_round(value, SHIFT_QA1_TO_Q12) as i16;
}
iteration += 1;
}
}
fn nlsf2a_find_poly(out: &mut [i32], clsf: &[i32], dd: usize) {
assert!(dd > 0, "polynomial order must be positive");
assert!(out.len() > dd);
let required = if dd <= 1 { 1 } else { 2 * dd - 1 };
assert!(clsf.len() >= required);
out[0] = 1 << QA;
out[1] = -clsf[0];
for k in 1..dd {
let ftmp = clsf[2 * k];
out[k + 1] =
(out[k - 1] << 1).wrapping_sub(rshift_round64(i64::from(ftmp) * i64::from(out[k]), QA));
for n in (2..=k).rev() {
let product = rshift_round64(i64::from(ftmp) * i64::from(out[n - 1]), QA);
out[n] = out[n].wrapping_add(out[n - 2]).wrapping_sub(product);
}
out[1] = out[1].wrapping_sub(ftmp);
}
}
fn rshift_round(value: i32, shift: i32) -> i32 {
match shift.cmp(&0) {
Ordering::Equal => value,
Ordering::Greater => {
if shift == 1 {
(value >> 1) + (value & 1)
} else {
((value >> (shift - 1)) + 1) >> 1
}
}
Ordering::Less => value.wrapping_shl((-shift) as u32),
}
}
fn rshift_round64(value: i64, shift: i32) -> i32 {
if shift <= 0 {
(value << (-shift)) as i32
} else if shift == 1 {
((value >> 1) + (value & 1)) as i32
} else {
(((value >> (shift - 1)) + 1) >> 1) as i32
}
}
#[cfg(test)]
mod tests {
use super::SILK_MAX_ORDER_LPC;
use super::nlsf2a;
use crate::silk::nlsf_decode::nlsf_decode;
use crate::silk::tables_nlsf_cb_wb::SILK_NLSF_CB_WB;
#[test]
fn produces_stable_lpc_for_wideband_vector() {
let order = usize::try_from(SILK_NLSF_CB_WB.order).unwrap();
let mut nlsf_q15 = [0i16; SILK_MAX_ORDER_LPC];
let mut indices = [0i8; SILK_MAX_ORDER_LPC + 1];
indices[0] = 5; for value in indices.iter_mut().take(order + 1).skip(1) {
*value = 1;
}
nlsf_decode(
&mut nlsf_q15[..order],
&indices[..order + 1],
&SILK_NLSF_CB_WB,
);
let mut a_q12 = [0i16; SILK_MAX_ORDER_LPC];
nlsf2a(&mut a_q12[..order], &nlsf_q15[..order], 0);
let gain = super::lpc_inverse_pred_gain(&a_q12[..order]);
assert!(gain > 0, "LPC coefficients should be stable");
}
}