use super::envelope::{DetectionMode, EnvelopeFollower};
use crate::gain::Gain;
#[derive(Debug, Clone)]
pub struct CompressorConfig {
pub threshold_db: f32, pub ratio: f32, pub knee_db: f32, pub attack_ms: f32, pub release_ms: f32, pub makeup_gain_db: f32, pub mode: DetectionMode, }
impl Default for CompressorConfig {
fn default() -> Self {
Self {
threshold_db: -20.0,
ratio: 4.0,
knee_db: 6.0,
attack_ms: 10.0,
release_ms: 100.0,
makeup_gain_db: 0.0,
mode: DetectionMode::Peak,
}
}
}
#[derive(Debug, Clone)]
pub struct Compressor {
config: CompressorConfig,
envelope: EnvelopeFollower,
}
impl Compressor {
pub fn new(sample_rate: f32, config: CompressorConfig) -> Self {
let envelope = EnvelopeFollower::new(
sample_rate,
config.attack_ms,
config.release_ms,
config.mode,
);
Self { config, envelope }
}
pub fn set_config(&mut self, sample_rate: f32, config: CompressorConfig) {
self.envelope
.set_time_constants(sample_rate, config.attack_ms, config.release_ms);
self.envelope.set_mode(config.mode);
self.config = config;
}
pub fn config(&self) -> &CompressorConfig {
&self.config
}
#[inline(always)]
fn compute_gain_reduction_db(&self, env_db: f32) -> f32 {
let t = self.config.threshold_db;
let r = self.config.ratio.max(1.0);
let w = self.config.knee_db;
if w <= 0.0 {
if env_db <= t {
0.0
} else {
(t - env_db) * (1.0 - 1.0 / r)
}
} else if 2.0 * (env_db - t) < -w {
0.0
} else if 2.0 * (env_db - t).abs() <= w {
let diff = env_db - t + w / 2.0;
-(diff * diff) / (2.0 * w) * (1.0 - 1.0 / r)
} else {
(t - env_db) * (1.0 - 1.0 / r)
}
}
#[inline(always)]
pub fn process_sample(&mut self, input: f32) -> f32 {
let env_linear = self.envelope.process_sample(input);
let env_db = Gain::linear_to_db(env_linear);
let gain_reduction_db = self.compute_gain_reduction_db(env_db);
let total_gain_db = gain_reduction_db + self.config.makeup_gain_db;
let total_gain_linear = Gain::db_to_linear(total_gain_db);
input * total_gain_linear
}
pub fn process_buffer(&mut self, buffer: &mut [f32]) {
for sample in buffer.iter_mut() {
*sample = self.process_sample(*sample);
}
}
pub fn reset(&mut self) {
self.envelope.reset();
}
}