use crate::fixed::{acc, mul, mul32x16 as mul32, sat, shift, trunc32};
const COVERAGE: i16 = 20972;
const ONSET_RATIO: i16 = 16384;
const HOLD: i16 = 3;
const HEADROOM: i32 = -7;
pub const PERIODIC: i16 = 0;
pub const ONSET: i16 = 1;
pub const GENERIC: i16 = 2;
#[derive(Clone, Copy, Default)]
pub struct Mode {
previous: i64,
hold: i16,
}
impl Mode {
fn predicted_energy(filtered: &[i16], gain: i16) -> i64 {
let predicted = trunc32(shift(energy(filtered), HEADROOM));
let predicted = trunc32(mul32(predicted, gain));
let predicted = mul32(predicted, gain);
shift(sat(shift(predicted, 2)), 2)
}
fn transient_mode(&mut self, current: i64) -> i16 {
let threshold = shift(mul32(self.previous, ONSET_RATIO), 2);
self.previous = current;
if acc(current - threshold) > 0 {
self.hold = HOLD;
ONSET
} else if self.hold > 0 {
self.hold -= 1;
ONSET
} else {
GENERIC
}
}
pub fn choose(&mut self, target: &[i16], filtered: &[i16], gain: i16) -> i16 {
let predicted = Self::predicted_energy(filtered, gain);
let current = trunc32(shift(energy(target), HEADROOM));
let wanted = mul32(current, COVERAGE);
if acc(predicted - wanted) >= 0 {
self.previous = current;
self.decay();
PERIODIC
} else {
self.transient_mode(current)
}
}
fn decay(&mut self) {
if self.hold > 0 {
self.hold -= 1;
}
}
}
fn energy(block: &[i16]) -> i64 {
let mut total = 0i64;
for &v in block.iter() {
total = acc(total + mul(v, v));
}
total
}
impl Mode {
pub fn restore(&mut self, previous: i64, hold: i16) {
self.previous = previous;
self.hold = hold;
}
pub fn energy(&self) -> i64 {
self.previous
}
pub fn hold(&self) -> i16 {
self.hold
}
}