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<const ORD: usize>(buf: &mut [f32], at: usize, n: usize, den: &[f32; ORD]) {
const { assert!(ORD >= 4) };
let buf = &mut buf[at - ORD..at + n];
let neg = [-den[0], -den[1], -den[2]];
let mut last = [buf[ORD - 4], buf[ORD - 3], buf[ORD - 2], buf[ORD - 1]];
let mut i = 0;
while i + 4 <= n {
let mut sum: [f32; 4] = buf[i + ORD..i + ORD + 4].try_into().unwrap();
let mut h: [f32; ORD] = buf[i..i + ORD].try_into().unwrap();
h[ORD - 4..].copy_from_slice(&last);
for j in 0..ORD - 3 {
let d = den[ORD - 1 - j];
sum[0] = mac(sum[0], d, -h[j]);
sum[1] = mac(sum[1], d, -h[j + 1]);
sum[2] = mac(sum[2], d, -h[j + 2]);
sum[3] = mac(sum[3], d, -h[j + 3]);
}
sum[0] = mac(sum[0], den[2], -h[ORD - 3]);
sum[1] = mac(sum[1], den[2], -h[ORD - 2]);
sum[2] = mac(sum[2], den[2], -h[ORD - 1]);
sum[0] = mac(sum[0], den[1], -h[ORD - 2]);
sum[1] = mac(sum[1], den[1], -h[ORD - 1]);
sum[0] = mac(sum[0], den[0], -h[ORD - 1]);
let y0 = sum[0];
let y1 = mac(sum[1], y0, neg[0]);
let y2 = mac(mac(sum[2], y1, neg[0]), y0, neg[1]);
let y3 = mac(mac(mac(sum[3], y2, neg[0]), y1, neg[1]), y0, neg[2]);
last = [y0, y1, y2, y3];
buf[i + ORD..i + ORD + 4].copy_from_slice(&last);
i += 4;
}
while i < n {
let p = ORD + 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, Kind, 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()),
);
}
}
}
}
fn celt_iir_plain(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;
}
}
fn predictor<const ORD: usize>(rng: &mut Rng) -> [f32; ORD] {
let x: Vec<f32> = (0..1024)
.map(|i| {
let t = i as f32;
8000.0 * (0.05 * t).sin() + 3000.0 * (0.31 * t).sin() + 300.0 * rng.unit()
})
.collect();
let mut ac = [0.0f32; 25];
autocorr(&x, &mut ac[..=ORD], None, 0, ORD, x.len());
let mut den = [0.0f32; ORD];
lpc(&mut den, &ac[..=ORD], ORD);
den
}
fn check_iir<const ORD: usize>(rng: &mut Rng) {
for kind in KINDS {
for (k, n) in [0usize, 1, 3, 4, 5, 7, 8, 120, 601, 1080].into_iter().enumerate() {
let den = predictor::<ORD>(rng);
let at = ORD + 5 * (k % 2);
let one_special = match kind {
Kind::Huge => Some([f32::MAX, -f32::MAX][k % 2]),
Kind::NonFinite => Some([f32::INFINITY, f32::NEG_INFINITY, f32::NAN][k % 3]),
_ => None,
};
let mut start = samples(rng, at + n + 3, if one_special.is_some() { Kind::Audio } else { kind });
let special = one_special.filter(|_| n > 0).map(|v| (at + 3 * n / 4, v));
if let Some((p, v)) = special {
start[p] = v;
}
let mut got = start.clone();
celt_iir(&mut got, at, n, &den);
let mut want = start.clone();
celt_iir_plain(&mut want, at, n, &den);
assert_same(&got, &want, &format!("order {ORD}, {kind:?}, n {n}, at {at}"));
let finite = special.map_or(got.len(), |(p, _)| p);
assert!(got[..finite].iter().all(|v| v.is_finite()), "order {ORD}, {kind:?}, n {n}: not finite");
}
}
}
#[test]
fn iir_matches_the_plain_version() {
let mut rng = Rng::new(30);
check_iir::<24>(&mut rng);
check_iir::<4>(&mut rng);
check_iir::<8>(&mut rng);
}
#[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]);
}
}