use crate::fixed::{acc, exp, hi, norm_shift, sat, shift};
use crate::tables::LAG_WINDOW;
pub const ANALYSIS: usize = 400;
pub use crate::LPC_ORDER as ORDER;
pub fn autocorrelate(signal: &[i16; ANALYSIS]) -> [i64; ORDER + 1] {
let w = &crate::tables::ANALYSIS_WINDOW[..ANALYSIS];
let mut windowed = [0i16; ANALYSIS];
for (o, (&x, &c)) in windowed.iter_mut().zip(signal.iter().zip(w.iter())) {
*o = hi(acc((x as i64) * (c as i64) * 2));
}
let mut energy = 1i64;
for &v in windowed.iter() {
energy = acc(energy + (v as i64) * (v as i64) * 2);
}
let shift_out = exp(energy);
let mut out = [0i64; ORDER + 1];
out[0] = shift(energy, shift_out);
for lag in 1..=ORDER {
let mut sum = 0i64;
for i in 0..ANALYSIS - lag {
sum = acc(sum + (windowed[i] as i64) * (windowed[i + lag] as i64) * 2);
}
out[lag] = shift(sum, shift_out);
}
out
}
pub fn lag_window(correlation: &mut [i64; ORDER + 1]) {
for (lag, c) in correlation.iter_mut().enumerate().skip(1) {
*c = mul32(*c, pair(2 * (lag - 1)));
}
}
fn pair(at: usize) -> i64 {
(((LAG_WINDOW[at] as i64) << 16) | (LAG_WINDOW[at + 1] as u16 as i64)) as i32 as i64
}
#[inline]
fn mul32(x: i64, y: i64) -> i64 {
sat(crate::fixed::mul32(x, y))
}
fn reciprocal(value: i64) -> (i64, i16) {
let e = exp(value);
let normalised = shift(value, e);
let negative = normalised < 0;
let magnitude = sat(if normalised < 0 {
-normalised
} else {
normalised
});
let mut quotient = 0i64;
let mut remainder = 0x3fffffffi64;
for _ in 0..31 {
remainder = shift(remainder, 1);
quotient = shift(quotient, 1);
remainder = acc(remainder - magnitude);
if remainder >= 0 {
quotient |= 1;
} else {
remainder = acc(remainder + magnitude);
}
}
(if negative { -quotient } else { quotient }, (e + 1) as i16)
}
pub fn divide(numerator: i64, denominator: i64) -> i64 {
let sign = if denominator >= 0 { 1 } else { -1 };
let magnitude = if denominator < 0 {
-denominator
} else {
denominator
};
let e = exp(magnitude);
let (recip, adjust) = reciprocal(shift(magnitude, e));
let product = mul32(numerator, recip);
let signed = if sign < 0 { acc(-product) } else { product };
shift(signed, (e as i16 + adjust) as i32)
}
const UNITY: i64 = 16384 << 14;
struct LevinsonState {
coefficients: [i64; ORDER + 1],
reflection: [i16; ORDER],
energy: i64,
exponent: i32,
}
impl LevinsonState {
fn new(r: &[i64; ORDER + 1]) -> Self {
let mut coefficients = [0i64; ORDER + 1];
let mut reflection = [0i16; ORDER];
let negated = acc(-divide(r[1], r[0]));
reflection[0] = hi(negated);
coefficients[0] = UNITY;
coefficients[1] = shift(negated, -3);
let mut energy = acc(r[0] + mul32(r[1], negated));
let exponent = exp(energy);
energy = shift(energy, exponent);
Self {
coefficients,
reflection,
energy,
exponent,
}
}
fn prediction_correlation(&self, r: &[i64; ORDER + 1], order: usize) -> i64 {
let mut correlation = 0i64;
for j in 0..order {
correlation = acc(correlation + mul32(self.coefficients[j], r[order - j]));
}
correlation
}
fn negated_reflection(&self, correlation: i64) -> i64 {
let k = shift(
acc(-divide(correlation, self.energy)),
norm_shift(self.exponent),
);
shift(k, 3)
}
fn update_coefficients(&mut self, negated: i64, order: usize) {
let previous = self.coefficients;
for j in 1..order {
self.coefficients[j] = acc(previous[j] + mul32(negated, previous[order - j]));
}
self.coefficients[order] = shift(negated, -3);
}
fn update_energy(&mut self, correlation: i64, negated: i64) {
self.energy =
acc(self.energy + shift(mul32(correlation, negated), norm_shift(self.exponent) + 3));
let increment = exp(self.energy);
self.energy = shift(self.energy, increment);
self.exponent += increment;
}
fn advance(&mut self, r: &[i64; ORDER + 1], order: usize) {
let correlation = self.prediction_correlation(r, order);
let negated = self.negated_reflection(correlation);
self.reflection[order - 1] = hi(negated);
self.update_coefficients(negated, order);
self.update_energy(correlation, negated);
}
}
fn round_coefficients(coefficients: &[i64; ORDER + 1]) -> [i16; ORDER + 1] {
let mut out = [0i16; ORDER + 1];
for (rounded, &coefficient) in out.iter_mut().zip(coefficients) {
*rounded = hi(acc(coefficient + (1 << 15)));
}
out
}
pub fn levinson(r: &[i64; ORDER + 1]) -> ([i16; ORDER + 1], [i16; ORDER]) {
let mut state = LevinsonState::new(r);
for order in 2..=ORDER {
state.advance(r, order);
}
(round_coefficients(&state.coefficients), state.reflection)
}
const HALF_ORDER: usize = ORDER / 2;
pub fn split_polynomials(a: &[i16; ORDER + 1]) -> ([i16; HALF_ORDER + 1], [i16; HALF_ORDER + 1]) {
let mut sum = [0i16; HALF_ORDER + 1];
let mut difference = [0i16; HALF_ORDER + 1];
sum[0] = hi(16384i64 << 13);
difference[0] = sum[0];
for k in 0..HALF_ORDER {
let (low, high) = (a[1 + k] as i64, a[ORDER - k] as i64);
let s = sat(shift(
acc(((low + high) << 16) - ((sum[k] as i64) << 17)),
-1,
));
sum[k + 1] = hi(s);
let d = sat(shift(
acc(((low - high) << 16) + ((difference[k] as i64) << 17)),
-1,
));
difference[k + 1] = hi(d);
}
(sum, difference)
}
const CHEB_TERMS: usize = 5;
pub fn chebyshev(poly: &[i16; CHEB_TERMS], x: i16) -> i64 {
let mut back = 16384i64 << 10;
let mut term = acc(((poly[0] as i64) << 16) + ((x as i64) << 13));
for &c in poly[1..CHEB_TERMS - 1].iter() {
let state = shift(term, -3);
term = acc(((c as i64) << 16) - shift(back, 3));
term = acc(term + shift(mul32(state, (x as i64) << 16), 4));
back = state;
}
let state = shift(term, -3);
let mut b = shift(mul32(state, (x as i64) << 16), 3);
b = acc(b + ((poly[CHEB_TERMS - 1] as i64) << 15));
b = acc(b - shift(back, 3));
sat(shift(b, 3))
}
const GRID_POINTS: usize = 60;
const BISECTIONS: usize = 4;
fn chebyshev_terms(poly: &[i16; HALF_ORDER + 1]) -> [i16; CHEB_TERMS] {
let mut terms = [0i16; CHEB_TERMS];
terms.copy_from_slice(&poly[1..]);
terms
}
struct RootBracket {
low: i16,
high: i16,
low_value: i16,
high_value: i16,
}
impl RootBracket {
fn bisect(&mut self, poly: &[i16; CHEB_TERMS]) {
for _ in 0..BISECTIONS {
let mid = hi(shift(
acc(((self.low as i64) << 16) + ((self.high as i64) << 16)),
-1,
));
let value = hi(chebyshev(poly, mid));
if acc((self.low_value as i64) * (value as i64) * 2) > 0 {
self.low = mid;
self.low_value = value;
} else {
self.high = mid;
self.high_value = value;
}
}
}
fn interpolated_root(&self) -> i16 {
let span = hi(acc(((self.high as i64) << 16) - ((self.low as i64) << 16)));
let drop = acc(((self.high_value as i64) << 16) - ((self.low_value as i64) << 16));
let slope = shift(crate::fixed::divide(drop, span), -5);
hi(acc(-acc(shift(
acc((self.low_value as i64) * (hi(slope) as i64) * 2),
5,
) - ((self.low as i64) << 16))))
}
fn refine(mut self, poly: &[i16; CHEB_TERMS]) -> i16 {
self.bisect(poly);
self.interpolated_root()
}
}
struct RootCursor {
index: usize,
x: i16,
value: i16,
}
impl RootCursor {
fn new(poly: &[i16; CHEB_TERMS]) -> Self {
let x = crate::tables::ROOT_GRID[0];
Self {
index: 1,
x,
value: hi(chebyshev(poly, x)),
}
}
fn next_bracket(&mut self, poly: &[i16; CHEB_TERMS]) -> Option<RootBracket> {
let previous = self.x;
let before = self.value;
self.x = crate::tables::ROOT_GRID[self.index];
self.index += 1;
self.value = hi(chebyshev(poly, self.x));
if acc((before as i64) * (self.value as i64) * 2) > 0 {
return None;
}
self.index -= 1;
Some(RootBracket {
low: self.x,
high: previous,
low_value: self.value,
high_value: before,
})
}
fn restart(&mut self, root: i16, poly: &[i16; CHEB_TERMS]) {
self.x = root;
self.value = hi(chebyshev(poly, root));
}
}
fn alternating_roots(first: &[i16; CHEB_TERMS], second: &[i16; CHEB_TERMS]) -> [i16; ORDER] {
let mut out = [0i16; ORDER];
let mut found = 0usize;
let mut poly = first;
let mut cursor = RootCursor::new(poly);
let mut remaining = GRID_POINTS;
while remaining > 0 {
let Some(bracket) = cursor.next_bracket(poly) else {
remaining -= 1;
continue;
};
let root = bracket.refine(poly);
out[found] = root;
found += 1;
if found == ORDER {
break;
}
poly = if std::ptr::eq(poly, first) {
second
} else {
first
};
cursor.restart(root, poly);
}
out
}
pub fn line_spectrum(
sum: &[i16; HALF_ORDER + 1],
difference: &[i16; HALF_ORDER + 1],
) -> [i16; ORDER] {
let first = chebyshev_terms(sum);
let second = chebyshev_terms(difference);
alternating_roots(&first, &second)
}