#[allow(unused_imports)]
use crate::math::FloatMath;
use crate::pipeline::DspNode;
#[derive(Debug, Clone, Copy)]
pub struct DynamicsCompressor {
threshold_db: f32,
ratio: f32,
knee_db: f32,
makeup_gain_linear: f32,
attack_coeff: f32,
release_coeff: f32,
envelope_db: f32,
}
impl DynamicsCompressor {
pub fn new(
threshold_db: f32,
ratio: f32,
knee_db: f32,
attack_s: f32,
release_s: f32,
makeup_gain_db: f32,
sample_rate_hz: f32,
) -> Self {
let attack_coeff = (-1.0 / (attack_s.max(1e-5) * sample_rate_hz)).exp();
let release_coeff = (-1.0 / (release_s.max(1e-5) * sample_rate_hz)).exp();
let makeup_gain_linear = (10.0f32).powf(makeup_gain_db / 20.0);
Self {
threshold_db,
ratio: ratio.max(1.0),
knee_db: knee_db.max(0.0),
makeup_gain_linear,
attack_coeff,
release_coeff,
envelope_db: 0.0,
}
}
pub fn process(&mut self, input: f32) -> f32 {
let abs_in = input.abs();
let input_db = if abs_in > 1e-6 {
20.0 * abs_in.log10()
} else {
-120.0
};
let target_gain_db = if self.knee_db > 0.0
&& (2.0 * (input_db - self.threshold_db)).abs() <= self.knee_db
{
let delta = input_db - self.threshold_db + self.knee_db / 2.0;
-(1.0 - 1.0 / self.ratio) * delta * delta / (2.0 * self.knee_db)
} else if input_db > self.threshold_db {
-(input_db - self.threshold_db) * (1.0 - 1.0 / self.ratio)
} else {
0.0
};
if target_gain_db < self.envelope_db {
self.envelope_db = self.attack_coeff * self.envelope_db + (1.0 - self.attack_coeff) * target_gain_db;
} else {
self.envelope_db = self.release_coeff * self.envelope_db + (1.0 - self.release_coeff) * target_gain_db;
}
let gain_linear = (10.0f32).powf(self.envelope_db / 20.0) * self.makeup_gain_linear;
input * gain_linear
}
pub fn reset(&mut self) {
self.envelope_db = 0.0;
}
}
impl DspNode<f32> for DynamicsCompressor {
#[inline(always)]
fn process_sample(&mut self, input: f32) -> f32 {
self.process(input)
}
}
#[derive(Debug, Clone, Copy)]
pub struct NoiseGate {
threshold_db: f32,
reduction_linear: f32,
attack_coeff: f32,
release_coeff: f32,
envelope_linear: f32,
}
impl NoiseGate {
pub fn new(threshold_db: f32, reduction_db: f32, attack_s: f32, release_s: f32, sample_rate_hz: f32) -> Self {
let attack_coeff = (-1.0 / (attack_s.max(1e-5) * sample_rate_hz)).exp();
let release_coeff = (-1.0 / (release_s.max(1e-5) * sample_rate_hz)).exp();
let reduction_linear = (10.0f32).powf(reduction_db / 20.0);
Self {
threshold_db,
reduction_linear,
attack_coeff,
release_coeff,
envelope_linear: 0.0,
}
}
pub fn process(&mut self, input: f32) -> f32 {
let abs_in = input.abs();
let input_db = if abs_in > 1e-6 {
20.0 * abs_in.log10()
} else {
-120.0
};
let target_gain = if input_db >= self.threshold_db {
1.0
} else {
self.reduction_linear
};
if target_gain > self.envelope_linear {
self.envelope_linear = self.attack_coeff * self.envelope_linear + (1.0 - self.attack_coeff) * target_gain;
} else {
self.envelope_linear = self.release_coeff * self.envelope_linear + (1.0 - self.release_coeff) * target_gain;
}
input * self.envelope_linear
}
pub fn reset(&mut self) {
self.envelope_linear = 0.0;
}
}
impl DspNode<f32> for NoiseGate {
#[inline(always)]
fn process_sample(&mut self, input: f32) -> f32 {
self.process(input)
}
}