1use crate::{
49 dsp::filters::{AllPass, LpfComb},
50 effects::EffectRenderTrait,
51};
52use fyrox_core::{reflect::prelude::*, visitor::prelude::*};
53
54#[derive(Default, Debug, Clone, PartialEq, Visit)]
55struct ChannelReverb {
56 fc: f32,
57 sample_rate: u32,
58 stereo_spread: u32,
59 lp_fb_comb_filters: Vec<LpfComb>,
60 all_pass_filters: Vec<AllPass>,
61}
62
63const DB60: f32 = 0.001;
65
66const DESIGN_SAMPLE_RATE: u32 = 44100;
68
69fn calculate_decay(len: usize, sample_rate: u32, decay_time: f32) -> f32 {
70 let time_len = len as f32 / sample_rate as f32;
71 DB60.powf(time_len / decay_time)
73}
74
75impl ChannelReverb {
76 const TOTAL_FILTERS_COUNT: f32 = 4.0 + 8.0;
78
79 const GAIN: f32 = 1.0 / (2.0 * Self::TOTAL_FILTERS_COUNT);
85
86 const COMB_LENGTHS: [usize; 8] = [1557, 1617, 1491, 1422, 1277, 1356, 1188, 1116];
88 const ALLPASS_LENGTHS: [usize; 4] = [225, 556, 441, 341];
89
90 fn new(stereo_spread: u32, fc: f32, feedback: f32, decay_time: f32) -> Self {
91 let mut reverb = Self {
92 fc,
93 stereo_spread,
94 sample_rate: DESIGN_SAMPLE_RATE,
95 lp_fb_comb_filters: Self::COMB_LENGTHS
96 .iter()
97 .map(|len| LpfComb::new(*len + stereo_spread as usize, fc, feedback))
98 .collect(),
99 all_pass_filters: Self::ALLPASS_LENGTHS
100 .iter()
101 .map(|len| AllPass::new(*len + stereo_spread as usize, 0.5))
102 .collect(),
103 };
104
105 reverb.set_decay_time(decay_time);
106
107 reverb
108 }
109
110 fn set_sample_rate(&mut self, sample_rate: usize) {
111 let scale = sample_rate as f32 / DESIGN_SAMPLE_RATE as f32;
112
113 let feedback = self.lp_fb_comb_filters[0].feedback();
114 self.lp_fb_comb_filters = Self::COMB_LENGTHS
118 .iter()
119 .map(|len| {
120 LpfComb::new(
121 (scale * (*len) as f32) as usize + self.stereo_spread as usize,
122 self.fc,
123 feedback,
124 )
125 })
126 .collect();
127 self.all_pass_filters = Self::ALLPASS_LENGTHS
128 .iter()
129 .map(|len| {
130 AllPass::new(
131 (scale * (*len) as f32) as usize + self.stereo_spread as usize,
132 0.5,
133 )
134 })
135 .collect();
136 }
137
138 fn set_decay_time(&mut self, decay_time: f32) {
139 for comb in self.lp_fb_comb_filters.iter_mut() {
140 comb.set_feedback(calculate_decay(comb.len(), self.sample_rate, decay_time));
141 }
142 }
143
144 fn set_fc(&mut self, fc: f32) {
145 self.fc = fc;
146 for comb in self.lp_fb_comb_filters.iter_mut() {
147 comb.set_fc(fc)
148 }
149 }
150
151 fn feed(&mut self, sample: f32) -> f32 {
152 let input = (sample * Self::GAIN).min(1.0);
153
154 let mut result = 0.0;
155 for comb in self.lp_fb_comb_filters.iter_mut() {
156 result += comb.feed(input);
157 }
158 for allpass in self.all_pass_filters.iter_mut() {
159 result = allpass.feed(result);
160 }
161 result
162 }
163}
164
165#[derive(Debug, Clone, Reflect, PartialEq)]
167pub struct Reverb {
168 dry: f32,
169 wet: f32,
170 #[reflect(setter = "set_decay_time", min_value = 0.0)]
171 decay_time: f32,
172 #[reflect(setter = "set_fc", min_value = 0.0, max_value = 1.0)]
173 fc: f32,
174 #[reflect(hidden)]
175 left: ChannelReverb,
176 #[reflect(hidden)]
177 right: ChannelReverb,
178}
179
180impl Visit for Reverb {
181 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
182 let mut region = visitor.enter_region(name)?;
183
184 self.dry.visit("Dry", &mut region)?;
185 self.wet.visit("Wet", &mut region)?;
186 self.decay_time.visit("DecayTime", &mut region)?;
187 self.fc.visit("Fc", &mut region)?;
188
189 if region.is_reading() {
190 self.left = ChannelReverb::new(0, self.fc, Reverb::FEEDBACK, self.decay_time);
191 self.right = ChannelReverb::new(23, self.fc, Reverb::FEEDBACK, self.decay_time);
192 }
193
194 Ok(())
195 }
196}
197
198impl Default for Reverb {
199 fn default() -> Self {
200 Self::new()
201 }
202}
203
204impl Reverb {
205 const FEEDBACK: f32 = 0.84;
206
207 pub fn new() -> Self {
210 let fc = 0.25615; let decay_time = 2.0;
213
214 Self {
215 dry: 1.0,
216 wet: 1.0,
217 decay_time: 2.0,
218 fc,
219 left: ChannelReverb::new(0, fc, Reverb::FEEDBACK, decay_time),
220 right: ChannelReverb::new(23, fc, Reverb::FEEDBACK, decay_time),
221 }
222 }
223
224 pub fn set_dry(&mut self, dry: f32) {
227 self.dry = dry.clamp(0.0, 1.0);
228 }
229
230 pub fn get_dry(&self) -> f32 {
232 self.dry
233 }
234
235 pub fn set_wet(&mut self, wet: f32) {
241 self.wet = wet.clamp(0.0, 1.0);
242 }
243
244 pub fn get_wet(&self) -> f32 {
246 self.wet
247 }
248
249 pub fn set_sample_rate(&mut self, sample_rate: usize) {
252 self.left.set_sample_rate(sample_rate);
253 self.right.set_sample_rate(sample_rate);
254 }
255
256 pub fn set_decay_time(&mut self, decay_time: f32) {
259 self.decay_time = decay_time;
260 self.left.set_decay_time(decay_time);
261 self.right.set_decay_time(decay_time)
262 }
263
264 pub fn decay_time(&self) -> f32 {
266 self.decay_time
267 }
268
269 pub fn set_fc(&mut self, fc: f32) {
281 self.fc = fc;
282 self.left.set_fc(fc);
283 self.right.set_fc(fc);
284 }
285
286 pub fn fc(&self) -> f32 {
288 self.fc
289 }
290}
291
292impl EffectRenderTrait for Reverb {
293 fn render(&mut self, input: &[(f32, f32)], mix_buf: &mut [(f32, f32)]) {
294 let wet = self.wet;
295 let dry = 1.0 - self.wet;
296
297 for ((out_left, out_right), &(left, right)) in mix_buf.iter_mut().zip(input.iter()) {
298 let mid = (left + right) * 0.5;
299
300 let processed_left = self.left.feed(mid);
301 let processed_right = self.right.feed(mid);
302
303 *out_left = processed_left * wet + processed_right * dry + self.dry * left;
304 *out_right = processed_right * wet + processed_left * dry + self.dry * right;
305 }
306 }
307}
308
309#[cfg(test)]
310mod test {
311 use crate::effects::reverb::{ChannelReverb, Reverb};
312
313 #[test]
315 fn test_reverb_convergence() {
316 let mut reverb = ChannelReverb::new(0, 0.25615, Reverb::FEEDBACK, 5.0);
317 let impulse_amplitude = 2.0;
318 reverb.feed(impulse_amplitude);
319 let mut counter = 0;
320 while counter < 6 * 44100 {
321 let result = reverb.feed(0.0);
322 if result > 1.0 {
323 panic!(
324 "Reverb does not converge with initial impulse of \
325 {impulse_amplitude} amplitude. The output sample was {result} \
326 at {counter} iteration. Energy conservation law was violated."
327 )
328 }
329 counter += 1;
330 }
331 }
332}