audio_processor_time/reverb/
free_verb.rs

1// Augmented Audio: Audio libraries and applications
2// Copyright (c) 2022 Pedro Tacla Yamada
3//
4// The MIT License (MIT)
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22// THE SOFTWARE.
23use audio_garbage_collector::{make_shared, Shared};
24use audio_processor_traits::parameters::{
25    make_handle_ref, AudioProcessorHandle, AudioProcessorHandleProvider, AudioProcessorHandleRef,
26    FloatType, ParameterSpec, ParameterType, ParameterValue,
27};
28use audio_processor_traits::{AtomicF32, AudioBuffer, AudioContext, AudioProcessor};
29
30use crate::reverb::all_pass::AllPass;
31use crate::reverb::lowpass_feedback_comb_filter::LowpassFeedbackCombFilter;
32use crate::reverb::tuning::*;
33
34struct FreeverbProcessorHandle {
35    width: AtomicF32,
36    gain: AtomicF32,
37    dry: AtomicF32,
38    wet: AtomicF32,
39    damp: AtomicF32,
40    room_size: AtomicF32,
41}
42
43impl FreeverbProcessorHandle {
44    pub fn set_dry(&self, value: f32) {
45        self.dry.set(value * SCALE_DRY);
46    }
47
48    pub fn set_room_size(&self, value: f32) {
49        self.room_size.set(value * SCALE_ROOM + OFFSET_ROOM);
50    }
51
52    pub fn set_damp(&self, value: f32) {
53        self.damp.set(value * SCALE_DAMP);
54    }
55
56    pub fn set_wet(&self, value: f32) {
57        self.wet.set(value * SCALE_WET);
58    }
59}
60
61struct GenericHandle(Shared<FreeverbProcessorHandle>);
62
63impl AudioProcessorHandle for GenericHandle {
64    fn name(&self) -> String {
65        "Reverb".to_string()
66    }
67
68    fn parameter_count(&self) -> usize {
69        4
70    }
71
72    fn get_parameter_spec(&self, index: usize) -> ParameterSpec {
73        let specs: [ParameterSpec; 4] = [
74            ParameterSpec::new(
75                "Dry".into(),
76                ParameterType::Float(FloatType {
77                    range: (0.0, 1.0),
78                    step: None,
79                }),
80            ),
81            ParameterSpec::new(
82                "Room size".into(),
83                ParameterType::Float(FloatType {
84                    range: (0.0, 1.0),
85                    step: None,
86                }),
87            ),
88            ParameterSpec::new(
89                "Damp".into(),
90                ParameterType::Float(FloatType {
91                    range: (0.0, 1.0),
92                    step: None,
93                }),
94            ),
95            ParameterSpec::new(
96                "Wet".into(),
97                ParameterType::Float(FloatType {
98                    range: (0.0, 1.0),
99                    step: None,
100                }),
101            ),
102        ];
103        specs[index].clone()
104    }
105
106    fn get_parameter(&self, index: usize) -> Option<ParameterValue> {
107        match index {
108            0 => Some(self.0.dry.get().into()),
109            1 => Some(self.0.room_size.get().into()),
110            2 => Some(self.0.damp.get().into()),
111            3 => Some(self.0.wet.get().into()),
112            _ => None,
113        }
114    }
115
116    fn set_parameter(&self, index: usize, request: ParameterValue) {
117        if let Ok(value) = request.try_into() {
118            match index {
119                0 => self.0.set_dry(value),
120                1 => self.0.set_room_size(value),
121                2 => self.0.set_damp(value),
122                3 => self.0.set_wet(value),
123                _ => {}
124            }
125        }
126    }
127}
128
129pub struct MonoFreeverbProcessor {
130    comb_filters: [LowpassFeedbackCombFilter; 8],
131    all_pass: [AllPass; 4],
132}
133
134impl MonoFreeverbProcessor {
135    fn new(comb_filters: [LowpassFeedbackCombFilter; 8], all_pass: [AllPass; 4]) -> Self {
136        Self {
137            comb_filters,
138            all_pass,
139        }
140    }
141
142    fn prepare(&mut self) {
143        for all_pass in &mut self.all_pass {
144            all_pass.set_feedback(0.5)
145        }
146    }
147
148    fn update(&mut self, room_size: f32, damp: f32) {
149        for comb in self.comb_filters.iter_mut() {
150            comb.set_feedback(room_size);
151            comb.set_damp(damp);
152        }
153    }
154
155    fn process(&mut self, channel: &mut [f32], wet: f32, gain: f32) {
156        for input in channel {
157            let mut output = 0.0;
158            for comb in self.comb_filters.iter_mut() {
159                output += comb.process(*input * gain);
160            }
161
162            for allpass in self.all_pass.iter_mut() {
163                output = allpass.process(output);
164            }
165
166            *input = output * wet;
167        }
168    }
169}
170
171pub struct FreeverbProcessor {
172    processors: [MonoFreeverbProcessor; 2],
173    handle: Shared<FreeverbProcessorHandle>,
174
175    wet1: f32,
176    wet2: f32,
177    damp1: f32,
178    roomsize1: f32,
179}
180
181impl AudioProcessorHandleProvider for FreeverbProcessor {
182    fn generic_handle(&self) -> AudioProcessorHandleRef {
183        make_handle_ref(GenericHandle(self.handle.clone()))
184    }
185}
186
187impl Default for FreeverbProcessor {
188    fn default() -> Self {
189        Self {
190            processors: [
191                MonoFreeverbProcessor::new(
192                    [
193                        LowpassFeedbackCombFilter::new(COMBTUNING_L1),
194                        LowpassFeedbackCombFilter::new(COMBTUNING_L2),
195                        LowpassFeedbackCombFilter::new(COMBTUNING_L3),
196                        LowpassFeedbackCombFilter::new(COMBTUNING_L4),
197                        LowpassFeedbackCombFilter::new(COMBTUNING_L5),
198                        LowpassFeedbackCombFilter::new(COMBTUNING_L6),
199                        LowpassFeedbackCombFilter::new(COMBTUNING_L7),
200                        LowpassFeedbackCombFilter::new(COMBTUNING_L8),
201                    ],
202                    [
203                        AllPass::new(ALL_PASS_TUNING),
204                        AllPass::new(ALLPASSTUNING_L2),
205                        AllPass::new(ALLPASSTUNING_L3),
206                        AllPass::new(ALLPASSTUNING_L4),
207                    ],
208                ),
209                MonoFreeverbProcessor::new(
210                    [
211                        LowpassFeedbackCombFilter::new(COMBTUNING_R1),
212                        LowpassFeedbackCombFilter::new(COMBTUNING_R2),
213                        LowpassFeedbackCombFilter::new(COMBTUNING_R3),
214                        LowpassFeedbackCombFilter::new(COMBTUNING_R4),
215                        LowpassFeedbackCombFilter::new(COMBTUNING_R5),
216                        LowpassFeedbackCombFilter::new(COMBTUNING_R6),
217                        LowpassFeedbackCombFilter::new(COMBTUNING_R7),
218                        LowpassFeedbackCombFilter::new(COMBTUNING_R8),
219                    ],
220                    [
221                        AllPass::new(ALLPASSTUNING_R1),
222                        AllPass::new(ALLPASSTUNING_R2),
223                        AllPass::new(ALLPASSTUNING_R3),
224                        AllPass::new(ALLPASSTUNING_R4),
225                    ],
226                ),
227            ],
228            handle: make_shared(FreeverbProcessorHandle {
229                width: INITIAL_WIDTH.into(),
230                gain: FIXED_GAIN.into(),
231                dry: INITIAL_DRY.into(),
232                wet: INITIAL_WET.into(),
233                damp: INITIAL_DAMP.into(),
234                room_size: 0.0.into(),
235            }),
236
237            wet1: 0.0,
238            wet2: 0.0,
239            damp1: 0.0,
240            roomsize1: 0.0,
241        }
242    }
243}
244
245impl FreeverbProcessor {
246    fn update(&mut self) {
247        self.wet1 = self.handle.wet.get() * (self.handle.width.get() / 2.0 + 0.5);
248        self.wet2 = self.handle.wet.get() * ((1.0 - self.handle.width.get()) / 2.0);
249
250        self.roomsize1 = self.handle.room_size.get();
251        self.damp1 = self.handle.damp.get();
252
253        for processor in self.processors.iter_mut() {
254            processor.update(self.roomsize1, self.damp1);
255        }
256    }
257}
258
259impl AudioProcessor for FreeverbProcessor {
260    type SampleType = f32;
261
262    fn prepare(&mut self, _context: &mut AudioContext) {
263        for processor in self.processors.iter_mut() {
264            processor.prepare();
265        }
266
267        self.handle.set_wet(INITIAL_WET);
268        self.handle.set_damp(INITIAL_DAMP);
269        self.handle.set_room_size(INITIAL_ROOM);
270    }
271
272    // TODO: I broke dry signal and so on
273    fn process(&mut self, _context: &mut AudioContext, buffer: &mut AudioBuffer<Self::SampleType>) {
274        // TODO - no need to update on every frame
275        self.update();
276
277        for (channel_num, (channel, processor)) in buffer
278            .channels_mut()
279            .iter_mut()
280            .zip(&mut self.processors)
281            .enumerate()
282        {
283            let wet = if channel_num == 1 {
284                self.wet1
285            } else {
286                self.wet2
287            };
288            processor.process(channel, wet, self.handle.gain.get());
289        }
290
291        // let input_left = frame[0];
292        // let input_right = frame[1];
293        //
294        // let input = (input_left + input_right) * self.handle.gain.get();
295        //
296        // let mut output_left = 0.0;
297        // let mut output_right = 0.0;
298        // for (comb_left, comb_right) in self
299        //     .comb_filters_left
300        //     .iter_mut()
301        //     .zip(self.comb_filters_right.iter_mut())
302        // {
303        //     output_left += comb_left.process(input);
304        //     output_right += comb_right.process(input);
305        // }
306        //
307        // for (allpass_left, allpass_right) in self
308        //     .all_pass_left
309        //     .iter_mut()
310        //     .zip(self.all_pass_right.iter_mut())
311        // {
312        //     output_left = allpass_left.process(output_left);
313        //     output_right = allpass_right.process(output_right);
314        // }
315    }
316}