use super::peak_hold::PeakHold;
use crate::{buffer::WDelay, filter::FirFilter};
pub struct Limiter {
limit: f32,
delay: WDelay<f32>,
peak_hold: PeakHold,
release_scale: f32,
last_release: f32,
filter: FirFilter<f32, f32>,
}
impl Limiter {
pub fn new(limit: f32, attack: usize, hold: usize, release: usize) -> Self {
let mut filter = FirFilter::new_rect(attack).unwrap();
filter.set_scale(1.0 / attack as f32);
for _ in 0..attack {
filter.push(1.0);
}
Self {
limit,
delay: WDelay::create(attack).unwrap(),
peak_hold: PeakHold::new(attack + hold),
release_scale: 1.0 - (-1.0 / release as f32).exp(),
last_release: 1.0,
filter,
}
}
pub fn reset(&mut self) {
self.peak_hold.reset();
self.filter.reset();
self.delay.reset();
}
pub fn execute(&mut self, x: f32) -> f32 {
self.delay.push(x);
let magnitude = x.abs();
let gain = if x <= self.limit { 1.0 } else { self.limit / magnitude };
let peak_min_gain = 1.0 / self.peak_hold.execute(1.0 / gain);
let decay = (peak_min_gain - self.last_release) * self.release_scale;
self.last_release += decay;
self.last_release = self.last_release.min(peak_min_gain);
self.filter.push(self.last_release);
let filtered_gain = self.filter.execute();
let delayed_x = self.delay.read();
delayed_x * filtered_gain
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_no_limit() {
let limit = 1.0;
let attack = 3;
let hold = 2;
let release = 4;
let x = [0.0, 0.1, 0.2, 0.3, 0.4, 0.3, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0];
let expected = [0.0, 0.0, 0.0, 0.0, 0.1, 0.2, 0.3, 0.4, 0.3, 0.2, 0.1, 0.0];
let mut limiter = Limiter::new(limit, attack, hold, release);
for (&x, &exp_y) in x.iter().zip(expected.iter()) {
assert_eq!(limiter.execute(x), exp_y);
}
}
}