1const MIN_SPEED_HZ: f32 = 0.29;
2
3const MAX_SAMPLES_LN2: usize = 12;
10const MAX_SAMPLES: usize = 1 << (MAX_SAMPLES_LN2 - 1);
11const MAX_SAMPLES_ANDMASK: usize = MAX_SAMPLES - 1;
12
13const INTERPOLATION_SUBSAMPLES_LN2: usize = 8;
14const INTERPOLATION_SUBSAMPLES: usize = 1 << (INTERPOLATION_SUBSAMPLES_LN2 - 1);
15const INTERPOLATION_SUBSAMPLES_ANDMASK: usize = INTERPOLATION_SUBSAMPLES - 1;
16
17const INTERPOLATION_SAMPLES: usize = 5;
18
19#[derive(Clone)]
20pub struct Chorus {
21 type_0: ChorusMode,
22 new_type: ChorusMode,
23 depth_ms: f32,
24 new_depth_ms: f32,
25 level: f32,
26 new_level: f32,
27 speed_hz: f32,
28 new_speed_hz: f32,
29 number_blocks: u32,
30 new_number_blocks: u32,
31 chorusbuf: [f32; MAX_SAMPLES],
32 counter: i32,
33 phase: [isize; 99],
34 modulation_period_samples: isize,
35 lookup_tab: Vec<i32>,
36 sample_rate: f32,
37 sinc_table: [[f32; 128]; 5],
38}
39
40impl Chorus {
41 pub fn new(sample_rate: f32) -> Self {
42 let mut chorus = Self {
43 type_0: ChorusMode::Sine,
44 new_type: ChorusMode::Sine,
45 depth_ms: 0f32,
46 new_depth_ms: 0f32,
47 level: 0f32,
48 new_level: 0f32,
49 speed_hz: 0f32,
50 new_speed_hz: 0f32,
51 number_blocks: 0,
52 new_number_blocks: 0,
53 chorusbuf: [0.0; MAX_SAMPLES],
54 counter: 0,
55 phase: [0; 99],
56 modulation_period_samples: 0,
57 lookup_tab: vec![0; (sample_rate / MIN_SPEED_HZ) as usize],
58 sample_rate,
59 sinc_table: [[0f32; 128]; 5],
60 };
61 for i in 0..INTERPOLATION_SAMPLES {
62 for ii in 0..INTERPOLATION_SUBSAMPLES {
63 let i_shifted = i as f32 - INTERPOLATION_SAMPLES as f32 / 2.0
65 + ii as f32 / INTERPOLATION_SUBSAMPLES as f32;
66
67 if i_shifted.abs() < 0.000001 {
68 chorus.sinc_table[i][ii] = 1.0;
70 } else {
71 chorus.sinc_table[i][ii] = (i_shifted * std::f32::consts::PI).sin()
72 / (std::f32::consts::PI * i_shifted);
73 chorus.sinc_table[i][ii] *=
75 0.5 * (1.0 + (2.0 * std::f32::consts::PI * i_shifted / 5.0).cos());
76 }
77 }
78 }
79 chorus.init();
80
81 chorus
82 }
83
84 fn init(&mut self) {
85 self.chorusbuf.fill(0.0);
86 self.set_params(&Default::default());
87 self.update();
88 }
89
90 fn update(&mut self) {
91 if self.new_number_blocks > 99 {
92 log::warn!(
93 "chorus: number blocks larger than max. allowed! Setting value to {}.",
94 99
95 );
96 self.new_number_blocks = 99;
97 }
98 if self.new_speed_hz < 0.29 {
99 log::warn!(
100 "chorus: speed is too low (min {})! Setting value to min.",
101 0.29
102 );
103 self.new_speed_hz = 0.29;
104 } else if self.new_speed_hz > 5.0 {
105 log::warn!(
106 "chorus: speed must be below {} Hz! Setting value to max.",
107 5
108 );
109 self.new_speed_hz = 5.0;
110 }
111 if self.new_depth_ms < 0.0 {
112 log::warn!("chorus: depth must be positive! Setting value to 0.",);
113 self.new_depth_ms = 0.0;
114 }
115 if self.new_level < 0.0 {
116 log::warn!("chorus: level must be positive! Setting value to 0.",);
117 self.new_level = 0.0;
118 } else if self.new_level > 10.0 {
119 log::warn!(
120 "chorus: level must be < 10. A reasonable level is << 1! Setting it to 0.1.",
121 );
122 self.new_level = 0.1;
123 }
124 self.modulation_period_samples = (self.sample_rate / self.new_speed_hz) as isize;
125
126 let mut modulation_depth_samples = (self.new_depth_ms / 1000.0 * self.sample_rate) as i32;
127 if modulation_depth_samples > MAX_SAMPLES as i32 {
128 log::warn!(
129 "chorus: Too high depth. Setting it to max ({}).",
130 MAX_SAMPLES,
131 );
132 modulation_depth_samples = MAX_SAMPLES as i32;
133 }
134 if self.type_0 == ChorusMode::Sine {
135 modulate_sine(
136 &mut self.lookup_tab,
137 self.modulation_period_samples as usize,
138 modulation_depth_samples,
139 );
140 } else if self.type_0 == ChorusMode::Triangle {
141 modulate_triangle(
142 &mut self.lookup_tab,
143 self.modulation_period_samples as i32,
144 modulation_depth_samples,
145 );
146 } else {
147 log::warn!("chorus: Unknown modulation type. Using sinewave.",);
148 self.type_0 = ChorusMode::Sine;
149 modulate_sine(
150 &mut self.lookup_tab,
151 self.modulation_period_samples as usize,
152 modulation_depth_samples,
153 );
154 }
155 for i in 0..(self.number_blocks as usize) {
156 self.phase[i] = (self.modulation_period_samples as f64 * i as f64
157 / self.number_blocks as f64) as isize;
158 }
159 self.counter = 0;
160 self.type_0 = self.new_type;
161 self.depth_ms = self.new_depth_ms;
162 self.level = self.new_level;
163 self.speed_hz = self.new_speed_hz;
164 self.number_blocks = self.new_number_blocks;
165 }
166
167 pub fn process_mix(
168 &mut self,
169 in_0: &mut [f32; 64],
170 left_out: &mut [f32; 64],
171 right_out: &mut [f32; 64],
172 ) {
173 for sample_index in 0..64 {
174 let d_in = in_0[sample_index];
175 let mut d_out = 0.0;
176 self.chorusbuf[self.counter as usize] = d_in;
177
178 for i in 0..(self.number_blocks as usize) {
179 let mut pos_subsamples = INTERPOLATION_SUBSAMPLES as i32 * self.counter
186 - self.lookup_tab[self.phase[i] as usize];
187
188 let mut pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES as i32;
189 pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK as i32;
190
191 for ii in 0..5 {
192 d_out += self.chorusbuf[(pos_samples & MAX_SAMPLES_ANDMASK as i32) as usize]
193 * self.sinc_table[ii as usize][pos_subsamples as usize];
194 pos_samples -= 1;
195 }
196
197 self.phase[i] += 1;
198 self.phase[i] %= self.modulation_period_samples;
199 }
200
201 d_out *= self.level;
202
203 left_out[sample_index] += d_out;
204 right_out[sample_index] += d_out;
205
206 self.counter += 1;
207 self.counter %= MAX_SAMPLES as i32;
208 }
209 }
210
211 pub fn process_replace(&mut self, left_out: &mut [f32; 64], right_out: &mut [f32; 64]) {
212 for sample_index in 0..64 {
213 let d_in = left_out[sample_index];
215 let mut d_out = 0.0;
216
217 self.chorusbuf[self.counter as usize] = d_in;
218
219 for i in 0..(self.number_blocks as usize) {
220 let mut pos_subsamples = INTERPOLATION_SUBSAMPLES as i32 * self.counter
221 - self.lookup_tab[self.phase[i] as usize];
222
223 let mut pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES as i32;
224 pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK as i32;
225
226 for ii in 0..INTERPOLATION_SAMPLES {
227 d_out += self.chorusbuf[(pos_samples & MAX_SAMPLES_ANDMASK as i32) as usize]
228 * self.sinc_table[ii][pos_subsamples as usize];
229 pos_samples -= 1;
230 }
231
232 self.phase[i] += 1;
233 self.phase[i] %= self.modulation_period_samples;
234 }
235 d_out *= self.level;
236
237 left_out[sample_index] = d_out;
238 right_out[sample_index] = d_out;
239
240 self.counter += 1;
241 self.counter %= MAX_SAMPLES as i32;
242 }
243 }
244
245 pub fn reset(&mut self) {
246 self.init();
247 }
248}
249
250#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
252#[repr(u32)]
253pub enum ChorusMode {
254 #[default]
255 Sine = 0,
256 Triangle = 1,
257}
258
259#[derive(Debug, Clone, Copy, PartialEq)]
260pub struct ChorusParams {
261 pub nr: u32,
263 pub level: f32,
265 pub speed: f32,
267 pub depth: f32,
269 pub mode: ChorusMode,
271}
272
273impl Default for ChorusParams {
274 fn default() -> Self {
275 Self {
276 nr: 3,
277 level: 2.0,
278 speed: 0.3,
279 depth: 8.0,
280 mode: ChorusMode::default(),
281 }
282 }
283}
284
285impl Chorus {
286 fn set_nr(&mut self, nr: u32) {
288 self.new_number_blocks = nr;
289 }
290
291 fn nr(&self) -> u32 {
293 self.number_blocks
294 }
295
296 fn set_level(&mut self, level: f32) {
298 self.new_level = level;
299 }
300
301 fn level(&self) -> f32 {
303 self.level
304 }
305
306 fn set_speed_hz(&mut self, speed_hz: f32) {
308 self.new_speed_hz = speed_hz;
309 }
310
311 fn speed_hz(&self) -> f32 {
313 self.speed_hz
314 }
315
316 fn set_depth_ms(&mut self, depth_ms: f32) {
318 self.new_depth_ms = depth_ms;
319 }
320
321 fn depth_ms(&self) -> f32 {
323 self.depth_ms
324 }
325
326 fn set_mode(&mut self, mode: ChorusMode) {
328 self.new_type = mode;
329 }
330
331 fn mode(&self) -> ChorusMode {
333 self.type_0
334 }
335}
336
337impl Chorus {
338 pub fn set_params(&mut self, params: &ChorusParams) {
342 self.set_nr(params.nr);
343 self.set_level(params.level);
344 self.set_speed_hz(params.speed);
345 self.set_depth_ms(params.depth);
346 self.set_mode(params.mode);
347 self.update();
348 }
349
350 pub fn params(&self) -> ChorusParams {
352 ChorusParams {
353 nr: self.nr(),
354 level: self.level(),
355 speed: self.speed_hz(),
356 depth: self.depth_ms(),
357 mode: self.mode(),
358 }
359 }
360}
361
362fn modulate_sine(buf: &mut [i32], len: usize, depth: i32) {
363 for (i, out) in buf.iter_mut().take(len).enumerate() {
364 let val = f64::sin(i as f64 / len as f64 * 2.0 * std::f64::consts::PI);
365 *out = ((1.0 + val) * depth as f64 / 2.0 * INTERPOLATION_SUBSAMPLES as f64) as i32;
366 *out -= 3 * MAX_SAMPLES as i32 * INTERPOLATION_SUBSAMPLES as i32;
367 }
368}
369
370fn modulate_triangle(buf: &mut [i32], len: i32, depth: i32) {
371 let mut i = 0;
372 let mut ii = len - 1;
373 while i <= ii {
374 let val = i as f64 * 2.0 / len as f64 * depth as f64 * INTERPOLATION_SUBSAMPLES as f64;
375 let val2 = (val + 0.5) as i32 - 3 * MAX_SAMPLES as i32 * INTERPOLATION_SUBSAMPLES as i32;
376 let fresh2 = i;
377 i += 1;
378 buf[fresh2 as usize] = val2;
379 let fresh3 = ii;
380 ii -= 1;
381 buf[fresh3 as usize] = val2;
382 }
383}