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

1use crate::engine::audio::effects::processors::super_trait::EffectProcessor;
2use std::f32::consts::PI;
3
4#[derive(Debug, Clone)]
5pub struct PhaserProcessor {
6    stages: usize,
7    rate: f32,
8    depth: f32,
9    feedback: f32,
10    mix: f32,
11    phase: f32,
12    allpass_states: Vec<[f32; 2]>, // [left, right] per stage
13}
14
15impl PhaserProcessor {
16    pub fn new(stages: usize, rate: f32, depth: f32, feedback: f32, mix: f32) -> Self {
17        let stages = stages.clamp(2, 12);
18        Self {
19            stages,
20            rate: rate.clamp(0.1, 10.0),
21            depth: depth.clamp(0.0, 1.0),
22            feedback: feedback.clamp(0.0, 0.95),
23            mix: mix.clamp(0.0, 1.0),
24            phase: 0.0,
25            allpass_states: vec![[0.0, 0.0]; stages],
26        }
27    }
28}
29
30impl Default for PhaserProcessor {
31    fn default() -> Self {
32        Self::new(4, 0.5, 0.7, 0.5, 0.5)
33    }
34}
35
36impl EffectProcessor for PhaserProcessor {
37    fn process(&mut self, samples: &mut [f32], sample_rate: u32) {
38        for i in (0..samples.len()).step_by(2) {
39            // Update LFO phase
40            self.phase += self.rate / sample_rate as f32;
41            if self.phase >= 1.0 {
42                self.phase -= 1.0;
43            }
44
45            // Calculate allpass coefficient using sine LFO
46            let lfo = (2.0 * PI * self.phase).sin();
47            let coeff = self.depth * lfo * 0.95; // Range: -0.95 to 0.95
48
49            // Process left and right channels
50            for ch in 0..2 {
51                if i + ch < samples.len() {
52                    let mut signal = samples[i + ch];
53
54                    // Cascade allpass filters
55                    for stage in 0..self.stages {
56                        let state = self.allpass_states[stage][ch];
57                        let output = -signal + coeff * (signal - state);
58                        self.allpass_states[stage][ch] = signal;
59                        signal = output + state;
60                    }
61
62                    // Apply feedback
63                    signal = signal * self.feedback;
64
65                    // Mix wet and dry
66                    samples[i + ch] = samples[i + ch] * (1.0 - self.mix) + signal * self.mix;
67                }
68            }
69        }
70    }
71
72    fn reset(&mut self) {
73        self.phase = 0.0;
74        for state in &mut self.allpass_states {
75            state[0] = 0.0;
76            state[1] = 0.0;
77        }
78    }
79
80    fn name(&self) -> &str {
81        "Phaser"
82    }
83}