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

1use crate::engine::audio::effects::processors::super_trait::EffectProcessor;
2use std::f32::consts::PI;
3
4#[derive(Debug, Clone)]
5pub struct ChorusProcessor {
6    depth: f32,
7    rate: f32,
8    mix: f32,
9    phase: f32,
10    delay_buffer: Vec<f32>,
11    buffer_pos: usize,
12}
13
14impl ChorusProcessor {
15    pub fn new(depth: f32, rate: f32, mix: f32) -> Self {
16        Self {
17            depth: depth.clamp(0.0, 1.0),
18            rate: rate.clamp(0.1, 10.0),
19            mix: mix.clamp(0.0, 1.0),
20            phase: 0.0,
21            delay_buffer: vec![0.0; 8820], // ~200ms at 44.1kHz
22            buffer_pos: 0,
23        }
24    }
25}
26
27impl Default for ChorusProcessor {
28    fn default() -> Self {
29        Self::new(0.7, 0.5, 0.5)
30    }
31}
32
33impl EffectProcessor for ChorusProcessor {
34    fn process(&mut self, samples: &mut [f32], sample_rate: u32) {
35        let max_delay_samples = (0.020 * sample_rate as f32) as usize; // 20ms max delay
36
37        for i in (0..samples.len()).step_by(2) {
38            // Update LFO phase
39            self.phase += self.rate / sample_rate as f32;
40            if self.phase >= 1.0 {
41                self.phase -= 1.0;
42            }
43
44            // Calculate delay offset using sine LFO
45            let lfo = (2.0 * PI * self.phase).sin();
46            let delay_samples =
47                (self.depth * max_delay_samples as f32 * (lfo + 1.0) / 2.0) as usize;
48            let delay_samples = delay_samples.min(self.delay_buffer.len() - 1);
49
50            // Process left and right channels
51            for ch in 0..2 {
52                if i + ch < samples.len() {
53                    let input = samples[i + ch];
54
55                    // Write to delay buffer
56                    self.delay_buffer[self.buffer_pos] = input;
57
58                    // Read from delayed position
59                    let read_pos = (self.buffer_pos + self.delay_buffer.len() - delay_samples)
60                        % self.delay_buffer.len();
61                    let delayed = self.delay_buffer[read_pos];
62
63                    // Mix wet and dry
64                    samples[i + ch] = input * (1.0 - self.mix) + delayed * self.mix;
65                }
66            }
67
68            self.buffer_pos = (self.buffer_pos + 1) % self.delay_buffer.len();
69        }
70    }
71
72    fn reset(&mut self) {
73        self.phase = 0.0;
74        self.delay_buffer.fill(0.0);
75        self.buffer_pos = 0;
76    }
77
78    fn name(&self) -> &str {
79        "Chorus"
80    }
81}