devalang_wasm/engine/audio/effects/processors/
compressor.rs

1use crate::engine::audio::effects::processors::super_trait::EffectProcessor;
2
3#[derive(Debug, Clone)]
4pub struct CompressorProcessor {
5    threshold: f32,
6    ratio: f32,
7    attack: f32,
8    release: f32,
9    envelope: f32,
10}
11
12impl CompressorProcessor {
13    pub fn new(threshold: f32, ratio: f32, attack: f32, release: f32) -> Self {
14        Self {
15            threshold,
16            ratio: ratio.max(1.0),
17            attack: attack.max(0.001),
18            release: release.max(0.001),
19            envelope: 0.0,
20        }
21    }
22}
23
24impl Default for CompressorProcessor {
25    fn default() -> Self {
26        Self::new(-20.0, 4.0, 0.005, 0.1)
27    }
28}
29
30impl EffectProcessor for CompressorProcessor {
31    fn process(&mut self, samples: &mut [f32], sample_rate: u32) {
32        let attack_coeff = (-1.0 / (self.attack * sample_rate as f32)).exp();
33        let release_coeff = (-1.0 / (self.release * sample_rate as f32)).exp();
34
35        for i in (0..samples.len()).step_by(2) {
36            // Get stereo sample RMS
37            let left = samples[i];
38            let right = if i + 1 < samples.len() {
39                samples[i + 1]
40            } else {
41                left
42            };
43            let rms = ((left * left + right * right) / 2.0).sqrt();
44
45            // Convert to dB
46            let db = if rms > 0.0001 {
47                20.0 * rms.log10()
48            } else {
49                -100.0
50            };
51
52            // Update envelope
53            let target = if db > self.threshold {
54                self.threshold + (db - self.threshold) / self.ratio
55            } else {
56                db
57            };
58
59            let coeff = if target > self.envelope {
60                attack_coeff
61            } else {
62                release_coeff
63            };
64
65            self.envelope = target + coeff * (self.envelope - target);
66
67            // Calculate gain reduction
68            let gain_db = self.envelope - db;
69            let gain = 10.0_f32.powf(gain_db / 20.0);
70
71            // Apply gain
72            samples[i] *= gain;
73            if i + 1 < samples.len() {
74                samples[i + 1] *= gain;
75            }
76        }
77    }
78
79    fn reset(&mut self) {
80        self.envelope = 0.0;
81    }
82
83    fn name(&self) -> &str {
84        "Compressor"
85    }
86}