use crate::{ Ratio, SampleRate, amplitude::Amplitude, decibel::Decibel, ring_buffer::RingBuffer, rms, smoother::Smoother, traits::Processor }; use std::time::Duration; #[derive(Debug, Default, Clone, Copy)] pub enum CompressorPeakMode { #[default] Sample, RMS, } #[derive(Debug, Default, Clone, Copy)] pub struct CompressorState { pub peak_mode: CompressorPeakMode, pub threshold: Decibel, pub ratio: Ratio, pub gain: Decibel, pub attack: Duration, pub release: Duration, pub last_rms: f64, pub input_db: Decibel, pub output_static_db: Decibel, pub gain_db: Decibel, pub gain_smoothed_db: Decibel, pub final_gain_db: Decibel, } impl CompressorState { pub fn new() -> Self { Self { peak_mode: CompressorPeakMode::Sample, threshold: Decibel::from(-24.0), ratio: Ratio(1.0, 3.0), gain: Decibel::from(0.0), attack: Duration::from_millis(25), release: Duration::from_millis(100), last_rms: 0.0, input_db: Decibel::from(0.0), output_static_db: Decibel::from(0.0), gain_db: Decibel::from(0.0), gain_smoothed_db: Decibel::from(0.0), final_gain_db: Decibel::from(0.0), } } } pub struct Compressor { pub state: CompressorState, sample_buffer: RingBuffer, smoother: Smoother, } impl Compressor { pub fn new(sample_rate: SampleRate) -> Self { Self::from_state(sample_rate, CompressorState::default()) } pub fn from_state(sample_rate: SampleRate, state: CompressorState) -> Self { Self { sample_buffer: RingBuffer::new(0.0), smoother: Smoother::new(sample_rate), state, } } pub fn set_threshold(&mut self, threshold: T) where T: Into, { self.state.threshold = threshold.into(); } pub fn set_attack(&mut self, attack: T) where T: Into, { self.smoother.set_attack(attack); } pub fn set_release(&mut self, release: T) where T: Into, { self.smoother.set_release(release); } pub fn set_ratio(&mut self, ratio: R) where R: Into, { self.state.ratio = ratio.into(); } pub fn set_gain(&mut self, gain: T) where T: Into, { self.state.gain = gain.into(); } } impl Processor for Compressor { fn process(&mut self, sample: f64) -> f64 { self.sample_buffer[0] = sample; self.state.input_db = Decibel::from(Amplitude(match self.state.peak_mode { CompressorPeakMode::Sample => sample.abs() + f64::EPSILON, CompressorPeakMode::RMS => { self.state.last_rms = rms(&self.sample_buffer, self.state.last_rms); self.state.last_rms.abs().sqrt() + f64::EPSILON } })); self.state.output_static_db = if self.state.input_db < self.state.threshold { self.state.input_db } else { self.state.threshold + (self.state.input_db - self.state.threshold) * self.state.ratio.multiplier() }; self.state.gain_db = self.state.output_static_db - self.state.input_db; self.state.gain_smoothed_db = if self.state.gain_db < self.state.gain_smoothed_db { self.smoother.attack(self.state.gain_db) } else { self.smoother.release(self.state.gain_db) }; self.state.final_gain_db = self.state.gain_smoothed_db + self.state.gain; self.sample_buffer.shift(); sample * self.state.final_gain_db } }