use crate::{self as knyst, prelude::GenState};
use knyst_macro::impl_gen;
use crate::Sample;
pub struct RandjaCompressor {
threshold: Sample,
attack: Sample,
release: Sample,
envelope_decay: Sample,
transfer_a: Sample,
transfer_b: Sample,
env: Sample,
gain: Sample,
output: Sample,
}
#[impl_gen]
impl RandjaCompressor {
#[allow(missing_docs)]
pub fn new() -> Self {
Self {
threshold: 1.,
attack: 0.,
release: 0.,
envelope_decay: 0.,
output: 1.,
transfer_a: 0.,
transfer_b: 1.,
env: 0.,
gain: 1.,
}
}
pub fn set_threshold(&mut self, threshold: Sample) -> &mut Self {
self.threshold = threshold;
self.transfer_b = self.output * self.threshold.powf(-self.transfer_a);
self
}
pub fn set_ratio(&mut self, ratio: f32) {
self.transfer_a = ratio - 1.;
self.transfer_b = self.output * self.threshold.powf(-self.transfer_a);
}
pub fn set_attack(&mut self, attack: f32) {
self.attack = (-1. / attack).exp();
}
pub fn set_release(&mut self, release: Sample) {
self.release = (-1. / release).exp();
self.envelope_decay = (-4. / release).exp();
}
pub fn set_output(&mut self, output: Sample) {
self.output = output;
self.transfer_b = self.output * self.threshold.powf(-self.transfer_a);
}
pub fn reset(&mut self) {
self.env = 0.;
self.gain = 1.;
}
#[allow(missing_docs)]
pub fn process(
&mut self,
input_left: &[Sample],
input_right: &[Sample],
output_left: &mut [Sample],
output_right: &mut [Sample],
) -> GenState {
for (((il, ir), ol), or) in input_left
.iter()
.zip(input_right)
.zip(output_left.iter_mut())
.zip(output_right.iter_mut())
{
let det = il.abs().max(ir.abs()) + 10e-30;
self.env = if det >= self.env {
det
} else {
det + self.envelope_decay * (self.env - det)
};
let transfer_gain = if self.env > self.threshold {
self.env.powf(self.transfer_a) * self.transfer_b
} else {
self.output
};
self.gain = if transfer_gain < self.gain {
transfer_gain + self.attack * (self.gain - transfer_gain)
} else {
transfer_gain + self.release * (self.gain - transfer_gain)
};
*ol = il * self.gain;
*or = ir * self.gain;
}
GenState::Continue
}
}