Restructured project for separate plugins
This commit is contained in:
127
dsp/src/comp.rs
Normal file
127
dsp/src/comp.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user