Skip to main content

boomie/
effects.rs

1use std::collections::VecDeque;
2
3#[derive(Debug, Clone)]
4pub struct ReverbParams {
5    pub room_size: f32,
6    pub damping: f32,
7    pub wet: f32,
8    pub width: f32,
9}
10
11impl Default for ReverbParams {
12    fn default() -> Self {
13        ReverbParams {
14            room_size: 0.5,
15            damping: 0.5,
16            wet: 0.3,
17            width: 1.0,
18        }
19    }
20}
21
22#[derive(Debug, Clone)]
23pub struct DelayParams {
24    pub time: f32,
25    pub feedback: f32,
26    pub wet: f32,
27}
28
29impl Default for DelayParams {
30    fn default() -> Self {
31        DelayParams {
32            time: 0.25,
33            feedback: 0.4,
34            wet: 0.3,
35        }
36    }
37}
38
39#[derive(Debug, Clone)]
40pub struct DistortionParams {
41    pub drive: f32,
42    pub tone: f32,
43    pub wet: f32,
44}
45
46impl Default for DistortionParams {
47    fn default() -> Self {
48        DistortionParams {
49            drive: 2.0,
50            tone: 0.7,
51            wet: 0.5,
52        }
53    }
54}
55
56#[derive(Debug, Clone)]
57pub struct FilterParams {
58    pub cutoff: f32, // Cutoff frequency in Hz
59    pub resonance: f32, // Q factor
60    pub filter_type: FilterType,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq)]
64pub enum FilterType {
65    LowPass, 
66    HighPass,
67    BandPass,
68}
69
70#[derive(Debug, Clone)]
71pub struct EffectsChain {
72    pub reverb: Option<ReverbParams>,
73    pub delay: Option<DelayParams>,
74    pub distortion: Option<DistortionParams>,
75    pub filter: Option<FilterParams>,
76}
77
78impl EffectsChain {
79    pub fn has_any(&self) -> bool {
80        self.reverb.is_some() || self.delay.is_some() || self.distortion.is_some() || self.filter.is_some()
81    }
82}
83
84
85impl Default for EffectsChain {
86    fn default() -> Self {
87        EffectsChain {
88            reverb: None,
89            delay: None,
90            distortion: None,
91            filter: None,
92        }
93    }
94}
95
96pub struct EffectsProcessor {
97    sample_rate: f32,
98    comb_buffers: Vec<VecDeque<f32>>,
99    comb_filter_state: Vec<f32>,
100    allpass_buffers: Vec<VecDeque<f32>>,
101    delay_buffer: VecDeque<f32>,
102    lowpass_state: f32,
103    filter_state: (f32, f32), // Biquad filter state (y[n-1], y[n-2])
104}
105
106impl EffectsProcessor {
107    pub fn new(sample_rate: f32) -> Self {
108        let scale = sample_rate / 44100.0; 
109        let comb_delays = vec![ // Freeverb design, 8 combs
110            (1116.0 * scale) as usize,
111            (1188.0 * scale) as usize,
112            (1277.0 * scale) as usize,
113            (1356.0 * scale) as usize,
114            (1422.0 * scale) as usize,
115            (1491.0 * scale) as usize,
116            (1557.0 * scale) as usize,
117            (1617.0 * scale) as usize,
118        ];
119
120        let allpass_delays = vec![
121            (556.0 * scale) as usize,
122            (441.0 * scale) as usize,
123            (341.0 * scale) as usize,
124            (225.0 * scale) as usize,
125        ];
126
127        EffectsProcessor {
128            sample_rate,
129            comb_buffers: comb_delays.iter()
130                .map(|&size| VecDeque::from(vec![0.0; size]))
131                .collect(),
132            comb_filter_state: vec![0.0; 8],
133            allpass_buffers: allpass_delays.iter()
134                .map(|&size| VecDeque::from(vec![0.0; size]))
135                .collect(),
136            delay_buffer: VecDeque::from(vec![0.0; (sample_rate * 2.0) as usize]),
137            lowpass_state: 0.0,
138            filter_state: (0.0, 0.0),
139        }
140    }
141
142    pub fn process(&mut self, input: f32, effects: &EffectsChain) -> f32 {
143        let mut output = input;
144
145        // Apply filter first in the chain for cleaner frequency shaping
146        if let Some(filter) = &effects.filter {
147            output = self.apply_filter(output, filter);
148        }
149
150        if let Some(dist) = &effects.distortion {
151            output = self.apply_distortion(output, dist);
152        }
153
154        if let Some(delay) = &effects.delay {
155            output = self.apply_delay(output, delay);
156        }
157
158        if let Some(reverb) = &effects.reverb {
159            output = self.apply_reverb(output, reverb);
160        }
161
162        output
163    }
164
165    // Biquad filter implementation for lowpass/highpass/bandpass
166    fn apply_filter(&mut self, input: f32, params: &FilterParams) -> f32 {
167        let omega = std::f32::consts::TAU * params.cutoff / self.sample_rate;
168        let alpha = omega.sin() * params.resonance;
169        
170        // Calculate biquad coefficients based on filter type
171        let (b0, b1, b2, a0, a1, a2) = match params.filter_type {
172            FilterType::LowPass => {
173                let cos_omega = omega.cos();
174                (
175                    (1.0 - cos_omega) / 2.0,
176                    1.0 - cos_omega,
177                    (1.0 - cos_omega) / 2.0,
178                    1.0 + alpha,
179                    -2.0 * cos_omega,
180                    1.0 - alpha,
181                )
182            }
183            FilterType::HighPass => {
184                let cos_omega = omega.cos();
185                (
186                    (1.0 + cos_omega) / 2.0,
187                    -(1.0 + cos_omega),
188                    (1.0 + cos_omega) / 2.0,
189                    1.0 + alpha,
190                    -2.0 * cos_omega,
191                    1.0 - alpha,
192                )
193            }
194            FilterType::BandPass => {
195                let cos_omega = omega.cos();
196                (
197                    alpha,
198                    0.0,
199                    -alpha,
200                    1.0 + alpha,
201                    -2.0 * cos_omega,
202                    1.0 - alpha,
203                )
204            }
205        };
206
207        // y[n] = (b0*x[n] + b1*x[n-1] + b2*x[n-2] - a1*y[n-1] - a2*y[n-2]) / a0
208        let output = (b0 * input + b1 * self.filter_state.0 + b2 * self.filter_state.1
209            - a1 * self.filter_state.0 - a2 * self.filter_state.1) / a0;
210
211        self.filter_state.1 = self.filter_state.0;
212        self.filter_state.0 = output;
213
214        output
215    }
216
217    fn apply_distortion(&mut self, input: f32, params: &DistortionParams) -> f32 {
218        let driven = input * params.drive;
219        let distorted = if driven > 1.0 {
220            2.0 / 3.0
221        } else if driven < -1.0 {
222            -2.0 / 3.0
223        } else {
224            driven - (driven.powi(3) / 3.0)
225        };
226
227        let alpha = 1.0 - params.tone;
228        self.lowpass_state = self.lowpass_state * alpha + distorted * (1.0 - alpha);
229
230        input * (1.0 - params.wet) + self.lowpass_state * params.wet
231    }
232
233    fn apply_delay(&mut self, input: f32, params: &DelayParams) -> f32 {
234        let delay_samples = (params.time * self.sample_rate) as usize;
235        let delay_samples = delay_samples.min(self.delay_buffer.len() - 1);
236
237        let delayed = self.delay_buffer[delay_samples];
238
239        Self::cycle_buffer(&mut self.delay_buffer, input + delayed * params.feedback);
240
241        input * (1.0 - params.wet) + delayed * params.wet
242    }
243
244    fn apply_reverb(&mut self, input: f32, params: &ReverbParams) -> f32 {
245        let mut output = 0.0;
246
247        for i in 0..8 {
248            let delayed = self.comb_buffers[i].back().copied().unwrap_or(0.0);
249            
250            self.comb_filter_state[i] = delayed * (1.0 - params.damping) + 
251                                        self.comb_filter_state[i] * params.damping;
252            
253            let feedback = self.comb_filter_state[i] * params.room_size;
254            
255            Self::cycle_buffer(&mut self.comb_buffers[i], input + feedback);
256            
257            output += delayed;
258        }
259
260        output /= 8.0;
261
262        for buffer in &mut self.allpass_buffers {
263            let delayed = buffer.back().copied().unwrap_or(0.0);
264            let new_val = output + delayed * 0.5;
265            Self::cycle_buffer(buffer, new_val);
266            output = delayed - output * 0.5;
267        }
268
269        input * (1.0 - params.wet) + output * params.wet
270    }
271
272    #[inline]
273    fn cycle_buffer(buffer: &mut VecDeque<f32>, new_value: f32) {
274        buffer.pop_back();
275        buffer.push_front(new_value);
276    }
277}