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

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