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

1use crate::engine::audio::effects::processors::super_trait::EffectProcessor;
2use std::fmt::Debug;
3
4/// Drive effect - tube-style saturation
5#[derive(Debug, Clone)]
6pub struct DriveProcessor {
7    amount: f32,
8    tone: f32,
9    mix: f32,
10    color: f32,
11    prev_l: f32,
12    prev_r: f32,
13}
14
15impl DriveProcessor {
16    pub fn new(amount: f32, tone: f32, color: f32, mix: f32) -> Self {
17        Self {
18            amount: amount.clamp(0.0, 1.0),
19            tone: tone.clamp(0.0, 1.0),
20            mix: mix.clamp(0.0, 1.0),
21            color: color.clamp(0.0, 1.0),
22            prev_l: 0.0,
23            prev_r: 0.0,
24        }
25    }
26}
27
28impl Default for DriveProcessor {
29    fn default() -> Self {
30        Self::new(0.5, 0.5, 0.5, 0.7)
31    }
32}
33
34impl EffectProcessor for DriveProcessor {
35    fn process(&mut self, samples: &mut [f32], _sample_rate: u32) {
36        // Map amount (0.0..1.0) to a perceptible drive/gain range (1x .. 20x)
37        let gain = 1.0 + self.amount * 19.0; // 1x to 20x
38
39        // Process interleaved stereo by frames
40        for i in (0..samples.len()).step_by(2) {
41            // Left
42            let input_l = samples[i];
43            let driven_l = input_l * gain;
44
45            // Use tanh waveshaper for more audible saturation behaviour
46            let distorted_l = driven_l.tanh();
47
48            // Tone mixes the distorted vs original to shape brightness
49            let mut toned_l = distorted_l * self.tone + input_l * (1.0 - self.tone);
50            let alpha = 0.05 + self.color * 0.95; // 0.05..1.0 smoothing
51            toned_l = alpha * toned_l + (1.0 - alpha) * self.prev_l;
52            self.prev_l = toned_l;
53            samples[i] = input_l * (1.0 - self.mix) + toned_l * self.mix;
54
55            // Right
56            if i + 1 < samples.len() {
57                let input_r = samples[i + 1];
58                let driven_r = input_r * gain;
59                let distorted_r = driven_r.tanh();
60                let mut toned_r = distorted_r * self.tone + input_r * (1.0 - self.tone);
61                let alpha_r = 0.05 + self.color * 0.95;
62                toned_r = alpha_r * toned_r + (1.0 - alpha_r) * self.prev_r;
63                self.prev_r = toned_r;
64                samples[i + 1] = input_r * (1.0 - self.mix) + toned_r * self.mix;
65            }
66        }
67    }
68
69    fn reset(&mut self) {
70        self.prev_l = 0.0;
71        self.prev_r = 0.0;
72    }
73
74    fn name(&self) -> &str {
75        "Drive"
76    }
77}