use crate::analysis::BINS;
use crate::bands::{BANDS, band_bins};
use crate::fixed::{acc, norm, sat, scale32, shift};
use crate::tables::BAND_INV_WIDTH;
const ENERGY_FLOOR: i64 = (3 << 16) | 8192;
const RAMP: [i16; 2] = [8, 16];
#[derive(Clone)]
pub struct NoiseEstimate {
pub energy: [i64; BANDS],
frames: i16,
weight: [i16; 3],
}
impl NoiseEstimate {
pub fn fast() -> Self {
Self::with_weights([6554, 16384, 29491])
}
pub fn slow() -> Self {
Self::with_weights([3277, 13107, 26214])
}
fn with_weights(weight: [i16; 3]) -> Self {
NoiseEstimate {
energy: [0; BANDS],
frames: -1,
weight,
}
}
fn adaptation_weight(&mut self) -> i16 {
let count = self.frames;
if count <= RAMP[0] {
self.frames = count + 1;
self.weight[0]
} else if count <= RAMP[1] {
self.frames = count + 1;
self.weight[1]
} else {
self.frames = count;
self.weight[2]
}
}
pub fn update(&mut self, spectrum: &[i16; BINS], headroom: i16, active: bool) {
if self.frames < 0 {
self.frames = 0;
self.seed(spectrum, headroom);
}
if active {
return;
}
let old = self.adaptation_weight();
let fresh = 32767 - old;
for (band, (&width, energy)) in BAND_INV_WIDTH
.iter()
.zip(self.energy.iter_mut())
.enumerate()
{
let sum = band_sum(spectrum, band);
let mean = scale32(sum, width);
let update = norm(scale32(mean, fresh), -headroom);
let blended = sat(acc(scale32(*energy, old) + shift(update, 4)));
*energy = blended.max(ENERGY_FLOOR);
}
}
fn seed(&mut self, spectrum: &[i16; BINS], headroom: i16) {
for (band, (&width, energy)) in BAND_INV_WIDTH
.iter()
.zip(self.energy.iter_mut())
.enumerate()
{
let sum = band_sum(spectrum, band);
let mean = scale32(sum, width);
*energy = norm(mean, 4 - headroom);
}
}
}
fn band_sum(spectrum: &[i16; BINS], band: usize) -> i64 {
let mut total = 0i64;
for bin in band_bins(band) {
total = sat(acc(total + ((spectrum[bin] as i64) << 16)));
}
total
}