oxisynth_reverb/
lib.rs

1const DC_OFFSET: f32 = 1e-8;
2const STEREO_SPREAD: usize = 23;
3
4const COMBTUNING_L1: usize = 1116;
5const COMBTUNING_R1: usize = 1116 + STEREO_SPREAD;
6const COMBTUNING_L2: usize = 1188;
7const COMBTUNING_R2: usize = 1188 + STEREO_SPREAD;
8const COMBTUNING_L3: usize = 1277;
9const COMBTUNING_R3: usize = 1277 + STEREO_SPREAD;
10const COMBTUNING_L4: usize = 1356;
11const COMBTUNING_R4: usize = 1356 + STEREO_SPREAD;
12const COMBTUNING_L5: usize = 1422;
13const COMBTUNING_R5: usize = 1422 + STEREO_SPREAD;
14const COMBTUNING_L6: usize = 1491;
15const COMBTUNING_R6: usize = 1491 + STEREO_SPREAD;
16const COMBTUNING_L7: usize = 1557;
17const COMBTUNING_R7: usize = 1557 + STEREO_SPREAD;
18const COMBTUNING_L8: usize = 1617;
19const COMBTUNING_R8: usize = 1617 + STEREO_SPREAD;
20const ALLPASSTUNING_L1: usize = 556;
21const ALLPASSTUNING_R1: usize = 556 + STEREO_SPREAD;
22const ALLPASSTUNING_L2: usize = 441;
23const ALLPASSTUNING_R2: usize = 441 + STEREO_SPREAD;
24const ALLPASSTUNING_L3: usize = 341;
25const ALLPASSTUNING_R3: usize = 341 + STEREO_SPREAD;
26const ALLPASSTUNING_L4: usize = 225;
27const ALLPASSTUNING_R4: usize = 225 + STEREO_SPREAD;
28
29#[derive(Clone)]
30struct Comb {
31    feedback: f32,
32    filterstore: f32,
33    damp1: f32,
34    damp2: f32,
35    buffer: Vec<f32>,
36    bufidx: usize,
37}
38
39impl Comb {
40    fn new(size: usize) -> Self {
41        Self {
42            feedback: 0f32,
43            filterstore: 0f32,
44            damp1: 0f32,
45            damp2: 0f32,
46            buffer: vec![DC_OFFSET; size],
47            bufidx: 0,
48        }
49    }
50
51    fn set_damp(&mut self, val: f32) {
52        self.damp1 = val;
53        self.damp2 = 1f32 - val;
54    }
55
56    fn set_feedback(&mut self, val: f32) {
57        self.feedback = val;
58    }
59
60    fn process(&mut self, input: f32) -> f32 {
61        let mut _tmp = self.buffer[self.bufidx];
62        self.filterstore = _tmp * self.damp2 + self.filterstore * self.damp1;
63        self.buffer[self.bufidx] = input + self.filterstore * self.feedback;
64        self.bufidx += 1;
65        if self.bufidx >= self.buffer.len() {
66            self.bufidx = 0
67        }
68        _tmp
69    }
70}
71
72#[derive(Clone)]
73struct AllPass {
74    feedback: f32,
75    buffer: Vec<f32>,
76    bufidx: usize,
77}
78
79impl AllPass {
80    fn new(size: usize, feedback: f32) -> Self {
81        Self {
82            feedback,
83            buffer: vec![DC_OFFSET; size],
84            bufidx: 0,
85        }
86    }
87
88    fn process(&mut self, input: f32) -> f32 {
89        let bufout: f32 = self.buffer[self.bufidx];
90        let output: f32 = bufout - input;
91        self.buffer[self.bufidx] = input + bufout * self.feedback;
92        self.bufidx += 1;
93        if self.bufidx >= self.buffer.len() {
94            self.bufidx = 0
95        }
96        output
97    }
98}
99
100#[derive(Clone)]
101struct LRPair<T> {
102    l: T,
103    r: T,
104}
105
106#[derive(Clone)]
107pub struct Reverb {
108    roomsize: f32,
109    damp: f32,
110    wet: f32,
111    wet1: f32,
112    wet2: f32,
113    width: f32,
114    gain: f32,
115    comb: [LRPair<Comb>; 8],
116    allpass: [LRPair<AllPass>; 4],
117}
118
119impl Default for Reverb {
120    fn default() -> Self {
121        Self::new()
122    }
123}
124
125impl Reverb {
126    pub fn new() -> Self {
127        let mut rev = Self {
128            roomsize: 0.5 * 0.28 + 0.7,
129            damp: 0.2 * 1.0,
130            wet: 1.0 * 3.0,
131            wet1: 0.0,
132            wet2: 0.0,
133            width: 1.0,
134            gain: 0.015,
135            comb: [
136                LRPair {
137                    l: Comb::new(COMBTUNING_L1),
138                    r: Comb::new(COMBTUNING_R1),
139                },
140                LRPair {
141                    l: Comb::new(COMBTUNING_L2),
142                    r: Comb::new(COMBTUNING_R2),
143                },
144                LRPair {
145                    l: Comb::new(COMBTUNING_L3),
146                    r: Comb::new(COMBTUNING_R3),
147                },
148                LRPair {
149                    l: Comb::new(COMBTUNING_L4),
150                    r: Comb::new(COMBTUNING_R4),
151                },
152                LRPair {
153                    l: Comb::new(COMBTUNING_L5),
154                    r: Comb::new(COMBTUNING_R5),
155                },
156                LRPair {
157                    l: Comb::new(COMBTUNING_L6),
158                    r: Comb::new(COMBTUNING_R6),
159                },
160                LRPair {
161                    l: Comb::new(COMBTUNING_L7),
162                    r: Comb::new(COMBTUNING_R7),
163                },
164                LRPair {
165                    l: Comb::new(COMBTUNING_L8),
166                    r: Comb::new(COMBTUNING_R8),
167                },
168            ],
169            allpass: [
170                LRPair {
171                    l: AllPass::new(ALLPASSTUNING_L1, 0.5f32),
172                    r: AllPass::new(ALLPASSTUNING_R1, 0.5f32),
173                },
174                LRPair {
175                    l: AllPass::new(ALLPASSTUNING_L2, 0.5f32),
176                    r: AllPass::new(ALLPASSTUNING_R2, 0.5f32),
177                },
178                LRPair {
179                    l: AllPass::new(ALLPASSTUNING_L3, 0.5f32),
180                    r: AllPass::new(ALLPASSTUNING_R3, 0.5f32),
181                },
182                LRPair {
183                    l: AllPass::new(ALLPASSTUNING_L4, 0.5f32),
184                    r: AllPass::new(ALLPASSTUNING_R4, 0.5f32),
185                },
186            ],
187        };
188        rev.set_params(&Default::default());
189        rev
190    }
191
192    pub fn reset(&mut self) {
193        self.comb = [
194            LRPair {
195                l: Comb::new(COMBTUNING_L1),
196                r: Comb::new(COMBTUNING_R1),
197            },
198            LRPair {
199                l: Comb::new(COMBTUNING_L2),
200                r: Comb::new(COMBTUNING_R2),
201            },
202            LRPair {
203                l: Comb::new(COMBTUNING_L3),
204                r: Comb::new(COMBTUNING_R3),
205            },
206            LRPair {
207                l: Comb::new(COMBTUNING_L4),
208                r: Comb::new(COMBTUNING_R4),
209            },
210            LRPair {
211                l: Comb::new(COMBTUNING_L5),
212                r: Comb::new(COMBTUNING_R5),
213            },
214            LRPair {
215                l: Comb::new(COMBTUNING_L6),
216                r: Comb::new(COMBTUNING_R6),
217            },
218            LRPair {
219                l: Comb::new(COMBTUNING_L7),
220                r: Comb::new(COMBTUNING_R7),
221            },
222            LRPair {
223                l: Comb::new(COMBTUNING_L8),
224                r: Comb::new(COMBTUNING_R8),
225            },
226        ];
227        self.allpass = [
228            LRPair {
229                l: AllPass::new(ALLPASSTUNING_L1, 0.5f32),
230                r: AllPass::new(ALLPASSTUNING_R1, 0.5f32),
231            },
232            LRPair {
233                l: AllPass::new(ALLPASSTUNING_L2, 0.5f32),
234                r: AllPass::new(ALLPASSTUNING_R2, 0.5f32),
235            },
236            LRPair {
237                l: AllPass::new(ALLPASSTUNING_L3, 0.5f32),
238                r: AllPass::new(ALLPASSTUNING_R3, 0.5f32),
239            },
240            LRPair {
241                l: AllPass::new(ALLPASSTUNING_L4, 0.5f32),
242                r: AllPass::new(ALLPASSTUNING_R4, 0.5f32),
243            },
244        ];
245    }
246
247    pub fn process_replace(&mut self, left_out: &mut [f32; 64], right_out: &mut [f32; 64]) {
248        for k in 0..64 {
249            let mut out_r = 0f32;
250            let mut out_l = 0f32;
251
252            // Don't ask me why only left buf is considered an input...
253            let input = (2.0 * left_out[k] + DC_OFFSET) * self.gain;
254
255            for comb in self.comb.iter_mut() {
256                out_l += comb.l.process(input);
257                out_r += comb.r.process(input);
258            }
259
260            for allpass in self.allpass.iter_mut() {
261                out_l = allpass.l.process(out_l);
262                out_r = allpass.r.process(out_r);
263            }
264
265            out_l -= DC_OFFSET;
266            out_r -= DC_OFFSET;
267
268            left_out[k] = out_l * self.wet1 + out_r * self.wet2;
269            right_out[k] = out_r * self.wet1 + out_l * self.wet2;
270        }
271    }
272
273    pub fn process_mix(
274        &mut self,
275        in_0: &mut [f32; 64],
276        left_out: &mut [f32; 64],
277        right_out: &mut [f32; 64],
278    ) {
279        for k in 0..64 {
280            let mut out_r = 0f32;
281            let mut out_l = out_r;
282            let input = (2.0 * in_0[k] + DC_OFFSET) * self.gain;
283
284            for comb in self.comb.iter_mut() {
285                out_l += comb.l.process(input);
286                out_r += comb.r.process(input);
287            }
288
289            for allpass in self.allpass.iter_mut() {
290                out_l = allpass.l.process(out_l);
291                out_r = allpass.r.process(out_r);
292            }
293
294            out_l -= DC_OFFSET;
295            out_r -= DC_OFFSET;
296
297            left_out[k] += out_l * self.wet1 + out_r * self.wet2;
298            right_out[k] += out_r * self.wet1 + out_l * self.wet2;
299        }
300    }
301
302    fn update(&mut self) {
303        self.wet1 = self.wet * (self.width / 2f32 + 0.5f32);
304        self.wet2 = self.wet * ((1f32 - self.width) / 2f32);
305        for comb in self.comb.iter_mut() {
306            comb.l.set_feedback(self.roomsize);
307            comb.r.set_feedback(self.roomsize);
308            comb.l.set_damp(self.damp);
309            comb.r.set_damp(self.damp);
310        }
311    }
312}
313
314#[derive(Debug, Clone, Copy, PartialEq)]
315pub struct ReverbParams {
316    /// Reverb room size
317    pub roomsize: f32,
318    /// Reverb dumping
319    pub damp: f32,
320    /// Reverb width
321    pub width: f32,
322    /// Reverb level
323    pub level: f32,
324}
325
326impl Default for ReverbParams {
327    fn default() -> Self {
328        Self {
329            roomsize: 0.2,
330            damp: 0.0,
331            width: 0.5,
332            level: 0.9,
333        }
334    }
335}
336
337impl Reverb {
338    /// Set the current reverb room size
339    fn set_room_size(&mut self, value: f32) {
340        self.roomsize = value * 0.28 + 0.7;
341    }
342
343    /// Query the current reverb room size
344    fn room_size(&self) -> f32 {
345        (self.roomsize - 0.7) / 0.28
346    }
347
348    /// Set the current reverb dumping
349    fn set_damp(&mut self, value: f32) {
350        self.damp = value * 1.0;
351    }
352
353    /// Query the current reverb dumping
354    fn damp(&self) -> f32 {
355        self.damp / 1.0
356    }
357
358    /// Set the current reverb level
359    fn set_level(&mut self, value: f32) {
360        let value = value.clamp(0.0, 1.0);
361        self.wet = value * 3.0;
362    }
363
364    /// Query the current reverb level
365    fn level(&self) -> f32 {
366        self.wet / 3.0
367    }
368
369    /// Set the current reverb width
370    fn set_width(&mut self, value: f32) {
371        self.width = value;
372    }
373
374    /// Query the current reverb width
375    fn width(&self) -> f32 {
376        self.width
377    }
378}
379
380impl Reverb {
381    /// Set the parameters for the built-in reverb unit
382    pub fn set_params(&mut self, params: &ReverbParams) {
383        self.set_room_size(params.roomsize);
384        self.set_damp(params.damp);
385        self.set_width(params.width);
386        self.set_level(params.level);
387        self.update();
388    }
389
390    /// Query the current reverb params
391    pub fn params(&self) -> ReverbParams {
392        ReverbParams {
393            roomsize: self.room_size(),
394            damp: self.damp(),
395            level: self.level(),
396            width: self.width(),
397        }
398    }
399}