#[cfg(not(feature = "std"))]
use crate::compat::Math;
use super::freq::fma;
pub const MAX_FRAME_SIZE: usize = 384;
pub const SILK_MAX_ORDER_LPC: usize = 16;
pub const FIND_LPC_COND_FAC: f32 = 1e-5;
pub fn silk_burg_analysis(
a: &mut [f32],
x: &[f32],
min_inv_gain: f32,
subfr_length: usize,
nb_subfr: usize,
d: usize,
) -> f32 {
assert!(subfr_length * nb_subfr <= MAX_FRAME_SIZE);
assert!(subfr_length >= 1 && d <= SILK_MAX_ORDER_LPC && d <= subfr_length);
let x = &x[..subfr_length * nb_subfr];
let a = &mut a[..d];
let min_inv_gain = min_inv_gain as f64;
let mut c0 = silk_energy_flp(x);
let mut c_first_row = [0.0f64; SILK_MAX_ORDER_LPC];
for x_ptr in x.chunks_exact(subfr_length) {
for n in 1..d + 1 {
c_first_row[n - 1] += silk_inner_product_flp(&x_ptr[..subfr_length - n], &x_ptr[n..]);
}
}
let mut c_last_row = c_first_row;
let start = fma(FIND_LPC_COND_FAC as f64, c0, c0) + 1e-9f32 as f64;
let mut caf = [0.0f64; SILK_MAX_ORDER_LPC + 1];
let mut cab = [0.0f64; SILK_MAX_ORDER_LPC + 1];
caf[0] = start;
cab[0] = start;
let mut inv_gain = 1.0f64;
let mut reached_max_gain = false;
let mut af = [0.0f64; SILK_MAX_ORDER_LPC];
for n in 0..d {
for x_ptr in x.chunks_exact(subfr_length) {
let mut tmp1 = x_ptr[n] as f64;
let mut tmp2 = x_ptr[subfr_length - n - 1] as f64;
for k in 0..n {
c_first_row[k] -= (x_ptr[n] * x_ptr[n - k - 1]) as f64;
c_last_row[k] -=
(x_ptr[subfr_length - n - 1] * x_ptr[subfr_length - n + k]) as f64;
let atmp = af[k];
tmp1 = fma(x_ptr[n - k - 1] as f64, atmp, tmp1);
tmp2 = fma(x_ptr[subfr_length - n + k] as f64, atmp, tmp2);
}
for k in 0..=n {
caf[k] = fma(-tmp1, x_ptr[n - k] as f64, caf[k]);
cab[k] = fma(-tmp2, x_ptr[subfr_length - n + k - 1] as f64, cab[k]);
}
}
let mut tmp1 = c_first_row[n];
let mut tmp2 = c_last_row[n];
for k in 0..n {
let atmp = af[k];
tmp1 = fma(c_last_row[n - k - 1], atmp, tmp1);
tmp2 = fma(c_first_row[n - k - 1], atmp, tmp2);
}
caf[n + 1] = tmp1;
cab[n + 1] = tmp2;
let mut num = cab[n + 1];
let mut nrg_b = cab[0];
let mut nrg_f = caf[0];
for k in 0..n {
let atmp = af[k];
num = fma(cab[n - k], atmp, num);
nrg_b = fma(cab[k + 1], atmp, nrg_b);
nrg_f = fma(caf[k + 1], atmp, nrg_f);
}
debug_assert!(nrg_f > 0.0);
debug_assert!(nrg_b > 0.0);
let mut rc = -2.0 * num / (nrg_f + nrg_b);
debug_assert!(rc > -1.0 && rc < 1.0);
let tmp1 = inv_gain * fma(-rc, rc, 1.0);
if tmp1 <= min_inv_gain {
rc = (1.0 - min_inv_gain / inv_gain).sqrt();
if num > 0.0 {
rc = -rc;
}
inv_gain = min_inv_gain;
reached_max_gain = true;
} else {
inv_gain = tmp1;
}
for k in 0..(n + 1) >> 1 {
let tmp1 = af[k];
let tmp2 = af[n - k - 1];
af[k] = fma(rc, tmp2, tmp1);
af[n - k - 1] = fma(rc, tmp1, tmp2);
}
af[n] = rc;
if reached_max_gain {
af[n + 1..d].fill(0.0);
break;
}
for k in 0..=n + 1 {
let tmp1 = caf[k];
caf[k] = fma(rc, cab[n + 1 - k], caf[k]);
cab[n + 1 - k] = fma(rc, tmp1, cab[n + 1 - k]);
}
}
let nrg_f = if reached_max_gain {
for (ak, &f) in a.iter_mut().zip(af.iter()) {
*ak = (-f) as f32;
}
for x_ptr in x.chunks_exact(subfr_length) {
c0 -= silk_energy_flp(&x_ptr[..d]);
}
c0 * inv_gain
} else {
let mut nrg_f = caf[0];
let mut tmp1 = 1.0f64;
for (k, ak) in a.iter_mut().enumerate() {
let atmp = af[k];
nrg_f = fma(caf[k + 1], atmp, nrg_f);
tmp1 = fma(atmp, atmp, tmp1);
*ak = (-atmp) as f32;
}
fma(-(FIND_LPC_COND_FAC as f64 * c0), tmp1, nrg_f)
};
let nrg = nrg_f as f32;
if 0.0 > nrg { 0.0 } else { nrg }
}
fn silk_energy_flp(data: &[f32]) -> f64 {
let mut result = 0.0f64;
let (blocks, rest) = data.as_chunks::<4>();
for b in blocks {
let (d0, d1, d2, d3) = (b[0] as f64, b[1] as f64, b[2] as f64, b[3] as f64);
result += d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3;
}
for &v in rest {
let v = v as f64;
result += v * v;
}
debug_assert!(result >= 0.0);
result
}
fn silk_inner_product_flp(data1: &[f32], data2: &[f32]) -> f64 {
let data2 = &data2[..data1.len()];
let mut result = 0.0f64;
let (blocks1, rest1) = data1.as_chunks::<4>();
let (blocks2, rest2) = data2.as_chunks::<4>();
for (a, b) in blocks1.iter().zip(blocks2.iter()) {
result += a[0] as f64 * b[0] as f64
+ a[1] as f64 * b[1] as f64
+ a[2] as f64 * b[2] as f64
+ a[3] as f64 * b[3] as f64;
}
for (&a, &b) in rest1.iter().zip(rest2.iter()) {
result += a as f64 * b as f64;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn energy_and_inner_product_follow_the_c_order() {
let x: [f32; 7] = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
assert_eq!(silk_energy_flp(&x), 140.0);
assert_eq!(silk_inner_product_flp(&x[..6], &x[1..]), 1.0 * 2.0 + 6.0 + 12.0 + 20.0 + 30.0 + 42.0);
assert_eq!(silk_energy_flp(&[]), 0.0);
}
#[test]
fn a_first_order_process_gives_its_coefficient() {
let mut x = [0.0f32; 160];
let mut state = 1u32;
let mut prev = 0.0f32;
for v in x.iter_mut() {
state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
let noise = (state >> 8) as f32 / (1u32 << 24) as f32 - 0.5;
prev = 0.9 * prev + noise;
*v = prev;
}
let mut a = [0.0f32; 16];
let nrg = silk_burg_analysis(&mut a, &x, 1e-3, 160, 1, 16);
assert!((a[0] - 0.9).abs() < 0.1, "{a:?}");
assert!(nrg > 0.0);
}
#[test]
fn a_pure_sine_reaches_the_maximum_gain() {
let mut x = [0.0f32; 80];
for (n, v) in x.iter_mut().enumerate() {
*v = (0.3 * n as f64).sin() as f32 * 1000.0;
}
let mut a = [1.0f32; 16];
let nrg = silk_burg_analysis(&mut a, &x, 1e-3, 80, 1, 16);
assert!(nrg >= 0.0 && nrg.is_finite());
assert!(a.iter().all(|v| v.is_finite()), "{a:?}");
assert!(a[8..].iter().all(|&v| v == 0.0), "{a:?}");
}
#[test]
fn silence_gives_zero_coefficients() {
let mut a = [1.0f32; 16];
let nrg = silk_burg_analysis(&mut a, &[0.0; 79], 1e-3, 79, 1, 16);
assert!(a.iter().all(|&v| v == 0.0), "{a:?}");
assert_eq!(nrg, 1e-9f32 as f64 as f32);
}
}