rescale_core/drc/
compressor.rs1use super::envelope::{DetectionMode, EnvelopeFollower};
4use crate::gain::Gain;
5
6#[derive(Debug, Clone)]
8pub struct CompressorConfig {
9 pub threshold_db: f32, pub ratio: f32, pub knee_db: f32, pub attack_ms: f32, pub release_ms: f32, pub makeup_gain_db: f32, pub mode: DetectionMode, }
17
18impl Default for CompressorConfig {
19 fn default() -> Self {
20 Self {
21 threshold_db: -20.0,
22 ratio: 4.0,
23 knee_db: 6.0,
24 attack_ms: 10.0,
25 release_ms: 100.0,
26 makeup_gain_db: 0.0,
27 mode: DetectionMode::Peak,
28 }
29 }
30}
31
32#[derive(Debug, Clone)]
33pub struct Compressor {
34 config: CompressorConfig,
35 envelope: EnvelopeFollower,
36}
37
38impl Compressor {
39 pub fn new(sample_rate: f32, config: CompressorConfig) -> Self {
40 let envelope = EnvelopeFollower::new(
41 sample_rate,
42 config.attack_ms,
43 config.release_ms,
44 config.mode,
45 );
46 Self { config, envelope }
47 }
48
49 pub fn set_config(&mut self, sample_rate: f32, config: CompressorConfig) {
50 self.envelope
51 .set_time_constants(sample_rate, config.attack_ms, config.release_ms);
52 self.envelope.set_mode(config.mode);
53 self.config = config;
54 }
55
56 pub fn config(&self) -> &CompressorConfig {
57 &self.config
58 }
59
60 #[inline(always)]
62 fn compute_gain_reduction_db(&self, env_db: f32) -> f32 {
63 let t = self.config.threshold_db;
64 let r = self.config.ratio.max(1.0);
65 let w = self.config.knee_db;
66
67 if w <= 0.0 {
68 if env_db <= t {
70 0.0
71 } else {
72 (t - env_db) * (1.0 - 1.0 / r)
73 }
74 } else if 2.0 * (env_db - t) < -w {
75 0.0
77 } else if 2.0 * (env_db - t).abs() <= w {
78 let diff = env_db - t + w / 2.0;
80 -(diff * diff) / (2.0 * w) * (1.0 - 1.0 / r)
81 } else {
82 (t - env_db) * (1.0 - 1.0 / r)
84 }
85 }
86
87 #[inline(always)]
89 pub fn process_sample(&mut self, input: f32) -> f32 {
90 let env_linear = self.envelope.process_sample(input);
91 let env_db = Gain::linear_to_db(env_linear);
92
93 let gain_reduction_db = self.compute_gain_reduction_db(env_db);
94 let total_gain_db = gain_reduction_db + self.config.makeup_gain_db;
95 let total_gain_linear = Gain::db_to_linear(total_gain_db);
96
97 input * total_gain_linear
98 }
99
100 pub fn process_buffer(&mut self, buffer: &mut [f32]) {
102 for sample in buffer.iter_mut() {
103 *sample = self.process_sample(*sample);
104 }
105 }
106
107 pub fn reset(&mut self) {
108 self.envelope.reset();
109 }
110}