// This is a simplified version of Pop2 by Airwindows converted to rust by Ardura
#[derive(Clone, Copy)]
pub(crate) struct Compressor {
sample_rate: f32,
// 0 to 1 for each like AW
amount: f32,
attack: f32,
release: f32,
drive: f32,
// Data holding variables
speed_l: f32,
speed_r: f32,
coefficient_l: f32,
coefficient_r: f32,
}
impl Compressor {
pub fn new(sample_rate: f32, amount: f32, attack: f32, release: f32, drive: f32) -> Self {
Compressor {
sample_rate: sample_rate,
amount: amount,
attack: attack,
release: release,
drive: drive,
speed_l: 1000.0,
speed_r: 1000.0,
coefficient_l: 1.0,
coefficient_r: 1.0,
}
}
pub fn update(&mut self, sample_rate: f32, amount: f32, attack: f32, release: f32, drive: f32) {
self.sample_rate = sample_rate;
let overallscale = self.sample_rate / 44100.0;
self.amount = amount;
self.attack = (attack.powi(4) * 100000.0 + 10.0) * overallscale;
self.release = (release.powi(5) * 2000000.0 + 20.0) * overallscale;
self.drive = drive;
}
pub fn process(&mut self, input_l: f32, input_r: f32) -> (f32, f32) {
let threshold = 1.0 - ((1.0 - (1.0 - self.amount).powi(2)) * 0.9);
let max_release = self.release * 4.0;
let mu_makeup_gain = (1.0 / threshold).sqrt() * self.drive;
// Start by getting pregain based off threshold
let pre_gain = 1.0 / threshold;
let mut output_l = input_l * pre_gain;
let mut output_r = input_r * pre_gain;
// Adjust coefficients for L
if output_l.abs() > threshold {
let variance = threshold / output_l.abs();
let mu_attack_l = (self.speed_l.abs()).sqrt();
self.coefficient_l = self.coefficient_l * (mu_attack_l - 1.0)
+ if variance < threshold {
threshold
} else {
variance
};
self.coefficient_l = self.coefficient_l / mu_attack_l;
let mu_new_speed_l = self.speed_l * (self.speed_l - 1.0) + self.release;
self.speed_l = mu_new_speed_l / self.speed_l;
self.speed_l = self.speed_l.min(max_release);
} else {
self.coefficient_l = self.coefficient_l * (self.speed_l.powi(2) - 1.0) + 1.0;
self.coefficient_l = self.coefficient_l / (self.speed_l.powi(2));
let mu_new_speed_l = self.speed_l * (self.speed_l - 1.0) + self.attack;
self.speed_l = mu_new_speed_l / self.speed_l;
}
// Adjust coefficients for R
if output_r.abs() > threshold {
let variance = threshold / output_r.abs();
let mu_attack_r = (self.speed_r.abs()).sqrt();
self.coefficient_r = self.coefficient_r * (mu_attack_r - 1.0)
+ if variance < threshold {
threshold
} else {
variance
};
self.coefficient_r = self.coefficient_r / mu_attack_r;
let mu_new_speed_r = self.speed_r * (self.speed_r - 1.0) + self.release;
self.speed_r = mu_new_speed_r / self.speed_r;
self.speed_r = self.speed_r.min(max_release);
} else {
self.coefficient_r = self.coefficient_r * (self.speed_r.powi(2) - 1.0) + 1.0;
self.coefficient_r = self.coefficient_r / (self.speed_r.powi(2));
let mu_new_speed_r = self.speed_r * (self.speed_r - 1.0) + self.attack;
self.speed_r = mu_new_speed_r / self.speed_r;
}
self.coefficient_l = self.coefficient_l.powi(2);
self.coefficient_r = self.coefficient_r.powi(2);
output_l *= mu_makeup_gain;
output_r *= mu_makeup_gain;
(output_l, output_r)
}
}