use crate::HALF;
use crate::fixed::{acc, exp, hi, round, sat, shift};
use crate::tables::SINE as SINE_TABLE;
pub const WINDOW: usize = 256;
pub const OVERLAP: usize = WINDOW - HALF;
const TAPER: usize = 10;
const MAX_HEADROOM: i16 = 8;
const PREEMPHASIS: i16 = 26214;
#[derive(Clone)]
pub struct Analysis {
pub history: [i16; OVERLAP],
pub last_windowed: i16,
pub prev_headroom: i16,
}
impl Default for Analysis {
fn default() -> Self {
Analysis {
history: [0; OVERLAP],
last_windowed: 0,
prev_headroom: 0,
}
}
}
fn normalise_window(window: &mut [i16; WINDOW]) -> i16 {
let mut peak = 0i64;
for &sample in window.iter() {
let magnitude = sat(((sample as i64) << 16).abs());
if magnitude > peak {
peak = magnitude;
}
}
let headroom = if peak == 0 {
MAX_HEADROOM
} else {
(exp(peak) as i16).min(MAX_HEADROOM)
};
for sample in window {
*sample = hi(shift((*sample as i64) << 16, headroom as i32));
}
headroom
}
fn taper_window(window: &[i16; WINDOW]) -> [i16; WINDOW] {
let taper = &crate::tables::TAPER[..TAPER];
let mut out = [0i16; WINDOW];
for n in 0..WINDOW {
out[n] = if n < TAPER {
hi(shift(acc((window[n] as i64) * (taper[n] as i64) * 2), -8))
} else if n >= WINDOW - TAPER {
hi(shift(
acc((window[n] as i64) * (taper[WINDOW - 1 - n] as i64) * 2),
-8,
))
} else {
hi(shift((window[n] as i64) << 16, -8))
};
}
out
}
impl Analysis {
pub fn window(&mut self, block: &[i16; HALF]) -> ([i16; WINDOW], i16) {
let mut buf = [0i16; WINDOW];
buf[..OVERLAP].copy_from_slice(&self.history);
buf[OVERLAP..].copy_from_slice(block);
self.history.copy_from_slice(&block[HALF - OVERLAP..]);
let headroom = normalise_window(&mut buf);
(taper_window(&buf), headroom)
}
pub fn preemphasise(&mut self, window: &mut [i16; WINDOW], headroom: i16) {
let realign = -((headroom - self.prev_headroom) as i32).abs();
let mut previous = hi(shift((self.last_windowed as i64) << 16, realign));
self.last_windowed = window[WINDOW - 1];
for sample in window.iter_mut() {
let x = *sample;
let a = acc(((x as i64) << 16) - (previous as i64) * (PREEMPHASIS as i64) * 2);
previous = x;
*sample = hi(a);
}
}
}
pub const FFT_POINTS: usize = 128;
const QUARTER: usize = 32;
pub const BINS: usize = FFT_POINTS + 1;
pub struct Spectrum {
pub re: [i16; BINS],
pub im: [i16; BINS],
}
pub fn deinterleave(block: &[i16; WINDOW]) -> Spectrum {
let mut s = Spectrum {
re: [0; BINS],
im: [0; BINS],
};
for n in 0..FFT_POINTS {
s.re[n] = block[2 * n];
s.im[n] = block[2 * n + 1];
}
s
}
fn fft_butterfly(s: &mut Spectrum, i: usize, j: usize, twiddle: usize) {
let (re_i, re_j) = (s.re[i] as i64, s.re[j] as i64);
let (im_i, im_j) = (s.im[i] as i64, s.im[j] as i64);
let dre = hi((re_i - re_j) << 16) as i64;
let dim = im_i - im_j;
s.re[i] = hi((re_i + re_j) << 16);
s.im[i] = hi((im_i + im_j) << 16);
let sin = crate::tables::SINE[twiddle] as i64;
let cos = crate::tables::SINE[QUARTER + twiddle] as i64;
s.re[j] = hi(round(acc(cos * dre * 2 + sin * dim * 2)));
s.im[j] = hi(round(acc(cos * dim * 2 - sin * dre * 2)));
}
fn fft_stage(s: &mut Spectrum, span: usize, blocks: usize) {
for group in 0..span {
for k in 0..blocks {
let i = group + 2 * span * k;
fft_butterfly(s, i, i + span, group * blocks);
}
}
}
pub fn fft(s: &mut Spectrum) {
let mut span = FFT_POINTS / 2;
let mut blocks = 1usize;
while span >= 1 {
fft_stage(s, span, blocks);
span /= 2;
blocks *= 2;
if blocks > FFT_POINTS {
break;
}
}
bit_reverse(s);
}
fn bit_reverse(s: &mut Spectrum) {
let mut j = 0u16;
for i in 0..FFT_POINTS - 1 {
if (i as u16) < j {
s.re.swap(i, j as usize);
s.im.swap(i, j as usize);
}
j = bitrev16(bitrev16(j).wrapping_add(bitrev16(FFT_POINTS as u16 / 2)));
}
}
fn bitrev16(v: u16) -> u16 {
v.reverse_bits()
}
const ROTATOR_STEP: (i16, i16) = (32758, -804);
const ANCHOR_PERIOD: usize = 32;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Forward,
Inverse,
}
struct RealRotator {
sin: i16,
cos: i16,
anchor: usize,
countdown: usize,
forward: bool,
}
impl RealRotator {
fn new(forward: bool) -> Self {
Self {
sin: 0,
cos: if forward { 32767 } else { -32768 },
anchor: crate::tables::SINE_ANCHOR_STEP,
countdown: ANCHOR_PERIOD - 1,
forward,
}
}
fn step(&mut self) {
if self.countdown == 0 {
self.sin = negate(SINE_TABLE[self.anchor]);
self.cos = SINE_TABLE[self.anchor + QUARTER];
if !self.forward {
self.cos = negate(self.cos);
}
self.anchor += crate::tables::SINE_ANCHOR_STEP;
self.countdown = ANCHOR_PERIOD - 1;
} else {
self.countdown -= 1;
let (sin, cos) = advance(self.sin, self.cos, self.forward);
(self.sin, self.cos) = renormalise(sin, cos);
}
}
}
fn unpack_bin_pair(spectrum: &mut Spectrum, bin: usize, sin: i16, cos: i16) {
let (low, high) = (bin, FFT_POINTS - bin);
let sum_re = (spectrum.re[low] as i64) + (spectrum.re[high] as i64);
let diff_re = hi(((spectrum.re[low] as i64) - (spectrum.re[high] as i64)) << 16) as i64;
let sum_im = hi(((spectrum.im[low] as i64) + (spectrum.im[high] as i64)) << 16);
let diff_im = (spectrum.im[low] as i64) - (spectrum.im[high] as i64);
let p = hi(round(acc(
(sum_im as i64) * (cos as i64) * 2 + diff_re * (sin as i64) * 2
))) as i64;
let q = hi(round(acc(
(sum_im as i64) * (sin as i64) * 2 - diff_re * (cos as i64) * 2
))) as i64;
spectrum.im[high] = hi(shift(acc((q - diff_im) << 16), -1));
spectrum.im[low] = hi(shift(acc((diff_im + q) << 16), -1));
spectrum.re[high] = hi(shift(acc((sum_re - p) << 16), -1));
spectrum.re[low] = hi(shift(acc((sum_re + p) << 16), -1));
}
pub fn unpack_real(s: &mut Spectrum, direction: Direction) {
let forward = direction == Direction::Forward;
let mut rotator = RealRotator::new(forward);
if forward {
s.re[FFT_POINTS] = s.re[0];
s.im[FFT_POINTS] = s.im[0];
}
for bin in 0..=FFT_POINTS / 2 {
unpack_bin_pair(s, bin, rotator.sin, rotator.cos);
rotator.step();
}
}
pub(crate) fn negate(v: i16) -> i16 {
hi(sat(-((v as i64) << 16)))
}
fn advance(sin: i16, cos: i16, forward: bool) -> (i16, i16) {
let (cs, sn) = ROTATOR_STEP;
let turn = if forward { 1i64 } else { -1 };
let s = hi(round(sat(
(cs as i64) * (sin as i64) * 2 + turn * (sn as i64) * (cos as i64) * 2
)));
let c = hi(round(sat(
(cs as i64) * (cos as i64) * 2 - turn * (sn as i64) * (sin as i64) * 2
)));
(s, c)
}
fn renormalise(sin: i16, cos: i16) -> (i16, i16) {
let mut n = sat((cos as i64) * (cos as i64) * 2);
n = sat(acc(n + (sin as i64) * (sin as i64) * 2));
let norm = hi(sat(acc(n + (1 << 15))));
let quotient = crate::analysis::ratio(16384, norm);
let e = exp((norm as i64) << 16);
let scaled = shift((quotient as i64) << 16, e);
let sum = sat(acc((16384i64 << 16) + ((hi(scaled) as i64) << 16)));
let mid = sat(acc(shift(sum, -1) + (1 << 15)));
let apply = |v: i16| {
let prod = acc((v as i64) * (hi(mid) as i64) * 2) & !0xffff;
hi(shift(prod, 1))
};
(apply(sin), apply(cos))
}
pub fn ratio(numerator: i16, denominator: i16) -> i16 {
if numerator < 0 || denominator < 0 || numerator > denominator {
return 0;
}
if numerator == denominator {
return 32767;
}
crate::fixed::low(crate::fixed::restoring_divide(
(numerator as i64) << 16,
denominator,
15,
))
}
const CORR_MIN_LAG: usize = 16;
const CORR_LAGS: usize = 113;
pub fn voicing(window: &[i16; WINDOW]) -> i16 {
let mut energy = 0i64;
for &v in window.iter() {
energy = sat(acc(energy + (v as i64) * (v as i64) * 2));
}
let energy = hi(sat(shift(energy, 8)));
if energy == 0 {
return 0;
}
let mut peak = 0i64;
for j in 0..CORR_LAGS {
let lag = CORR_MIN_LAG + j;
let taps = WINDOW - lag;
let mut c = 0i64;
for i in 0..taps {
c = sat(acc(c + (window[i] as i64) * (window[lag + i] as i64) * 2));
}
let c = sat(shift(c, 8));
if c > peak {
peak = c;
}
}
ratio(hi(peak), energy)
}
pub fn magnitude(s: &Spectrum) -> [i16; BINS] {
let mut mag = [0i16; BINS];
for (m, (&real, &imaginary)) in mag.iter_mut().zip(s.re.iter().zip(s.im.iter())) {
let re = ((real as i64) << 16).abs();
let im = ((imaginary as i64) << 16).abs();
*m = hi(sat(acc(im + ((hi(re) as i64) << 16))));
}
mag
}