127 lines
3.6 KiB
Rust
127 lines
3.6 KiB
Rust
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<f64, 2048>,
|
|
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<T>(&mut self, threshold: T)
|
|
where
|
|
T: Into<Decibel>,
|
|
{
|
|
self.state.threshold = threshold.into();
|
|
}
|
|
pub fn set_attack<T>(&mut self, attack: T)
|
|
where
|
|
T: Into<Duration>,
|
|
{
|
|
self.smoother.set_attack(attack);
|
|
}
|
|
pub fn set_release<T>(&mut self, release: T)
|
|
where
|
|
T: Into<Duration>,
|
|
{
|
|
self.smoother.set_release(release);
|
|
}
|
|
pub fn set_ratio<R>(&mut self, ratio: R)
|
|
where
|
|
R: Into<Ratio>,
|
|
{
|
|
self.state.ratio = ratio.into();
|
|
}
|
|
pub fn set_gain<T>(&mut self, gain: T)
|
|
where
|
|
T: Into<Decibel>,
|
|
{
|
|
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
|
|
}
|
|
} |