use crate::fixedvec::FixedVec;
use crate::mathops::mac;
use crate::pitch::pitch_xcorr;
pub fn lpc(lpc: &mut [f32], ac: &[f32], p: usize) {
let lpc = &mut lpc[..p];
lpc.fill(0.0);
let mut error = ac[0];
#[allow(clippy::neg_cmp_op_on_partial_ord)]
if !(ac[0] > 1e-10) {
return;
}
for i in 0..p {
let mut rr = 0.0f32;
for j in 0..i {
rr = mac(rr, lpc[j], ac[i - j]);
}
rr += ac[i + 1];
let r = -(rr / error);
lpc[i] = r;
for j in 0..(i + 1) >> 1 {
let tmp1 = lpc[j];
let tmp2 = lpc[i - 1 - j];
lpc[j] = mac(tmp1, r, tmp2);
lpc[i - 1 - j] = mac(tmp2, r, tmp1);
}
error = mac(error, -(r * r), error);
if error <= 0.001 * ac[0] {
break;
}
}
}
const AUTOCORR_WINDOWED_MAX: usize = 2880;
pub fn autocorr(
x: &[f32],
ac: &mut [f32],
window: Option<&[f32]>,
overlap: usize,
lag: usize,
n: usize,
) {
match window {
Some(win) => autocorr_windowed(x, ac, win, overlap, lag, n),
None => autocorr_of(&x[..n], ac, lag),
}
}
#[inline(never)]
fn autocorr_windowed(x: &[f32], ac: &mut [f32], win: &[f32], overlap: usize, lag: usize, n: usize) {
if x.len() < n {
return;
}
let mut xx: FixedVec<f32, AUTOCORR_WINDOWED_MAX> = FixedVec::new();
xx.resize(n, 0.0);
xx.copy_from_slice(&x[..n]);
for i in 0..overlap {
xx[i] *= win[i];
xx[n - 1 - i] *= win[i];
}
autocorr_of(&xx, ac, lag);
}
fn autocorr_of(xx: &[f32], ac: &mut [f32], lag: usize) {
let n = xx.len();
let fast_n = n - lag;
pitch_xcorr(xx, xx, ac, fast_n, lag + 1);
for (k, out) in ac[..=lag].iter_mut().enumerate() {
let mut d = 0.0f32;
for (&a, &b) in xx[k + fast_n..].iter().zip(&xx[fast_n..]) {
d = mac(d, a, b);
}
*out += d;
}
}
pub(crate) fn celt_fir(x: &[f32], at: usize, num: &[f32], y: &mut [f32]) {
let ord = num.len();
for (i, out) in y.iter_mut().enumerate() {
let hist = &x[at + i - ord..at + i];
let mut sum = x[at + i];
for j in 0..ord {
sum = mac(sum, num[ord - 1 - j], hist[j]);
}
*out = sum;
}
}
pub(crate) fn celt_iir(buf: &mut [f32], at: usize, n: usize, den: &[f32]) {
let ord = den.len();
let mut i = 0;
while i + 4 <= n {
let p = at + i;
let mut sum = [buf[p], buf[p + 1], buf[p + 2], buf[p + 3]];
for (k, s) in sum.iter_mut().enumerate() {
for j in 0..ord - k {
*s = mac(*s, den[ord - 1 - j], -buf[p - ord + j + k]);
}
}
buf[p] = sum[0];
sum[1] = mac(sum[1], -buf[p], den[0]);
buf[p + 1] = sum[1];
sum[2] = mac(sum[2], -buf[p + 1], den[0]);
sum[2] = mac(sum[2], -buf[p], den[1]);
buf[p + 2] = sum[2];
sum[3] = mac(sum[3], -buf[p + 2], den[0]);
sum[3] = mac(sum[3], -buf[p + 1], den[1]);
sum[3] = mac(sum[3], -buf[p], den[2]);
buf[p + 3] = sum[3];
i += 4;
}
while i < n {
let p = at + i;
let mut sum = buf[p];
for j in 0..ord {
sum = mac(sum, den[ord - 1 - j], -buf[p - ord + j]);
}
buf[p] = sum;
i += 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pitch::tests::{KINDS, Rng, assert_same, samples};
#[cfg(not(feature = "std"))]
use std::vec::Vec;
fn autocorr_plain(
x: &[f32],
ac: &mut [f32],
window: Option<&[f32]>,
overlap: usize,
lag: usize,
n: usize,
) {
let mut xx = x[..n].to_vec();
if let Some(win) = window {
for i in 0..overlap {
xx[i] = x[i] * win[i];
xx[n - i - 1] = x[n - i - 1] * win[i];
}
}
let fast_n = n - lag;
pitch_xcorr(&xx, &xx, ac, fast_n, lag + 1);
for k in 0..=lag {
let mut d = 0.0f32;
for i in (k + fast_n)..n {
d = mac(d, xx[i], xx[i - k]);
}
ac[k] += d;
}
}
#[test]
fn autocorr_matches_the_plain_version() {
let mut rng = Rng::new(28);
let win: Vec<f32> = (0..120).map(|i| ((i as f32 + 0.5) / 120.0).sin()).collect();
for kind in KINDS {
for (n, lag, overlap) in [
(5usize, 4usize, 0usize),
(24, 4, 0),
(25, 24, 0),
(64, 8, 20),
(240, 24, 120),
(988, 4, 0),
(1024, 24, 120),
(2880, 24, 120),
] {
let x = samples(&mut rng, n, kind);
for window in [None, Some(&win[..])] {
if window.is_none() && overlap > 0 {
continue;
}
let mut got = vec![f32::NAN; lag + 1];
autocorr(&x, &mut got, window, overlap, lag, n);
let mut want = vec![f32::NAN; lag + 1];
autocorr_plain(&x, &mut want, window, overlap, lag, n);
assert_same(
&got,
&want,
&format!("{kind:?} n {n} lag {lag} window {}", window.is_some()),
);
}
}
}
}
#[test]
fn lpc_clears_the_orders_it_does_not_reach() {
let ac = [1.0f32, 0.9999, 0.9998, 0.9997, 0.9996];
let mut coef = [7.0f32; 4];
lpc(&mut coef, &ac, 4);
assert!(coef[0] < -0.99);
assert!(coef[1..].iter().all(|&c| c == 0.0), "{coef:?}");
let mut coef = [7.0f32; 4];
lpc(&mut coef, &[0.0; 5], 4);
assert_eq!(coef, [0.0; 4]);
}
}