1 /*
2 Reverb.cc
3
4 Copyright 2002-7 Tim Goetze <tim@quitte.de>
5
6 http://quitte.de/dsp/
7
8 three reverb units: JVRev, Plate and Plate2x2.
9
10 the former is a rewrite of STK's JVRev, a traditional design.
11
12 original comment:
13
14 This is based on some of the famous
15 Stanford CCRMA reverbs (NRev, KipRev)
16 all based on the Chowning/Moorer/
17 Schroeder reverberators, which use
18 networks of simple allpass and comb
19 delay filters.
20
21 the algorithm is mostly unchanged in this implementation; the delay
22 line lengths have been fiddled with to make the stereo field more
23 evenly weighted, and denormal protection has been added.
24
25 the latter two are based on the circuit discussed in Jon Dattorro's
26 september 1997 JAES paper on effect design (part 1: reverb & filters).
27 */
28 /*
29 This program is free software; you can redistribute it and/or
30 modify it under the terms of the GNU General Public License
31 as published by the Free Software Foundation; either version 2
32 of the License, or (at your option) any later version.
33
34 This program is distributed in the hope that it will be useful,
35 but WITHOUT ANY WARRANTY; without even the implied warranty of
36 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 GNU General Public License for more details.
38
39 You should have received a copy of the GNU General Public License
40 along with this program; if not, write to the Free Software
41 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
42 02111-1307, USA or point your web browser to http://www.gnu.org.
43 */
44
45 #include "basics.h"
46
47 #include "Reverb.h"
48 #include "Descriptor.h"
49
50 int
51 JVRev::default_length[9] = {
52 #if 1 /* slightly modified, tg */
53 1777, 1847, 1993, 2137, 389, 127, 43, 211, 209
54 #else
55 4799, 4999, 5399, 5801, 1051, 337, 113, 573, 487
56 #endif
57 };
58
59 void
init()60 JVRev::init()
61 {
62 memcpy (length, default_length, sizeof (length));
63
64 if (fs != 44100)
65 {
66 double s = fs / 44100.;
67
68 for (int i = 0; i < 9; ++i)
69 {
70 int v = (int) (s * length[i]);
71 v |= 1;
72 while (!DSP::isprime (v))
73 v += 2;
74 length[i] = v;
75 }
76 }
77
78 for (int i = 0; i < 4; ++i)
79 comb[i].init (length[i]);
80
81 for (int i = 0; i < 3; ++i)
82 allpass[i].init (length[i+4]);
83
84 left.init (length[7]);
85 right.init (length[8]);
86
87 /* such a simple number, but i couldn't find a better one. */
88 apc = .7;
89 }
90
91 void
set_t60(sample_t t)92 JVRev::set_t60 (sample_t t)
93 {
94 t60 = t;
95
96 t = max (.00001, t);
97
98 for (int i = 0; i < 4; ++i)
99 comb[i].c = pow (10, (-3 * length[i] / (t * fs)));
100 }
101
102 void
activate()103 JVRev::activate()
104 {
105 for (int i = 0; i < 3; ++i)
106 allpass[i].reset();
107
108 for (int i = 0; i < 4; ++i)
109 comb[i].reset();
110
111 left.reset();
112 right.reset();
113
114 set_t60 (getport(1));
115 }
116
117 template <sample_func_t F>
118 void
one_cycle(int frames)119 JVRev::one_cycle (int frames)
120 {
121 sample_t * s = ports[0];
122
123 if (t60 != *ports[1])
124 set_t60 (getport(1));
125
126 double wet = getport(2), dry = 1 - wet;
127
128 sample_t * dl = ports[3];
129 sample_t * dr = ports[4];
130
131 for (int i = 0; i < frames; ++i)
132 {
133 sample_t x = s[i], a = x + normal;
134
135 x *= dry;
136
137 /* diffusors */
138 a = allpass[0].process (a, -apc);
139 a = allpass[1].process (a, -apc);
140 a = allpass[2].process (a, -apc);
141
142 /* tank */
143 sample_t t = 0;
144 a -= normal;
145
146 for (int j = 0; j < 4; ++j)
147 t += comb[j].process (a);
148
149 F (dl, i, x + wet * left.putget (t), adding_gain);
150 F (dr, i, x + wet * right.putget (t), adding_gain);
151 }
152 }
153
154 /* //////////////////////////////////////////////////////////////////////// */
155
156 PortInfo
157 JVRev::port_info [] =
158 {
159 {
160 "in",
161 INPUT | AUDIO,
162 {BOUNDED, -1, 1}
163 }, {
164 "t60 (s)",
165 INPUT | CONTROL,
166 {BOUNDED | DEFAULT_MID, 0, 4.6}
167 }, {
168 "blend",
169 INPUT | CONTROL,
170 {BOUNDED | DEFAULT_LOW, 0, .28}
171 }, {
172 "out:l",
173 OUTPUT | AUDIO,
174 {0}
175 }, {
176 "out:r",
177 OUTPUT | AUDIO,
178 {0}
179 }
180 };
181
182 template <> void
setup()183 Descriptor<JVRev>::setup()
184 {
185 UniqueID = 1778;
186 Label = "JVRev";
187 Properties = HARD_RT;
188
189 Name = CAPS "JVRev - Stanford-style reverb from STK";
190 Maker = "Tim Goetze <tim@quitte.de>";
191 Copyright = "GPL, 2004-7";
192
193 /* fill port info and vtable */
194 autogen();
195 }
196
197 /* //////////////////////////////////////////////////////////////////////// */
198
199 void
init()200 PlateStub::init()
201 {
202 f_lfo = -1;
203
204 # define L(i) ((int) (l[i] * fs))
205 static float l[] = {
206 0.004771345048889486, 0.0035953092974026408,
207 0.01273478713752898, 0.0093074829474816042,
208 0.022579886428547427, 0.030509727495715868,
209 0.14962534861059779, 0.060481838647894894, 0.12499579987231611,
210 0.14169550754342933, 0.089244313027116023, 0.10628003091293972
211 };
212
213 /* lh */
214 input.lattice[0].init (L(0));
215 input.lattice[1].init (L(1));
216
217 /* rh */
218 input.lattice[2].init (L(2));
219 input.lattice[3].init (L(3));
220
221 /* modulated, width about 12 samples @ 44.1 */
222 tank.mlattice[0].init (L(4), (int) (0.00040322707570310132 * fs));
223 tank.mlattice[1].init (L(5), (int) (0.00040322707570310132 * fs));
224
225 /* lh */
226 tank.delay[0].init (L(6));
227 tank.lattice[0].init (L(7));
228 tank.delay[1].init (L(8));
229
230 /* rh */
231 tank.delay[2].init (L(9));
232 tank.lattice[1].init (L(10));
233 tank.delay[3].init (L(11));
234 # undef L
235
236 # define T(i) ((int) (t[i] * fs))
237 static float t[] = {
238 0.0089378717113000241, 0.099929437854910791, 0.064278754074123853,
239 0.067067638856221232, 0.066866032727394914, 0.006283391015086859,
240 0.01186116057928161, 0.12187090487550822, 0.041262054366452743,
241 0.089815530392123921, 0.070931756325392295, 0.011256342192802662
242 };
243
244 for (int i = 0; i < 12; ++i)
245 tank.taps[i] = T(i);
246 # undef T
247
248 /* tuned for soft attack, ambience */
249 indiff1 = .742;
250 indiff2 = .712;
251
252 dediff1 = .723;
253 dediff2 = .729;
254 }
255
256 inline void
process(sample_t x,sample_t decay,sample_t * _xl,sample_t * _xr)257 PlateStub::process (sample_t x, sample_t decay, sample_t * _xl, sample_t * _xr)
258 {
259 x = input.bandwidth.process (x);
260
261 /* lh */
262 x = input.lattice[0].process (x, indiff1);
263 x = input.lattice[1].process (x, indiff1);
264
265 /* rh */
266 x = input.lattice[2].process (x, indiff2);
267 x = input.lattice[3].process (x, indiff2);
268
269 /* summation point */
270 register sample_t xl = x + decay * tank.delay[3].get();
271 register sample_t xr = x + decay * tank.delay[1].get();
272
273 /* lh */
274 xl = tank.mlattice[0].process (xl, dediff1);
275 xl = tank.delay[0].putget (xl);
276 xl = tank.damping[0].process (xl);
277 xl *= decay;
278 xl = tank.lattice[0].process (xl, dediff2);
279 tank.delay[1].put (xl);
280
281 /* rh */
282 xr = tank.mlattice[1].process (xr, dediff1);
283 xr = tank.delay[2].putget (xr);
284 xr = tank.damping[1].process (xr);
285 xr *= decay;
286 xr = tank.lattice[1].process (xr, dediff2);
287 tank.delay[3].put (xr);
288
289 /* gather output */
290 xl = .6 * tank.delay[2] [tank.taps[0]];
291 xl += .6 * tank.delay[2] [tank.taps[1]];
292 xl -= .6 * tank.lattice[1] [tank.taps[2]];
293 xl += .6 * tank.delay[3] [tank.taps[3]];
294 xl -= .6 * tank.delay[0] [tank.taps[4]];
295 xl += .6 * tank.lattice[0] [tank.taps[5]];
296
297 xr = .6 * tank.delay[0] [tank.taps[6]];
298 xr += .6 * tank.delay[0] [tank.taps[7]];
299 xr -= .6 * tank.lattice[0] [tank.taps[8]];
300 xr += .6 * tank.delay[1] [tank.taps[9]];
301 xr -= .6 * tank.delay[2] [tank.taps[10]];
302 xr += .6 * tank.lattice[1] [tank.taps[11]];
303
304 *_xl = xl;
305 *_xr = xr;
306 }
307
308 /* //////////////////////////////////////////////////////////////////////// */
309
310 template <sample_func_t F>
311 void
one_cycle(int frames)312 Plate::one_cycle (int frames)
313 {
314 sample_t * s = ports[0];
315
316 input.bandwidth.set (exp (-M_PI * (1. - getport(1))));
317
318 sample_t decay = getport(2);
319
320 double damp = exp (-M_PI * getport(3));
321 tank.damping[0].set (damp);
322 tank.damping[1].set (damp);
323
324 sample_t blend = getport(4), dry = 1 - blend;
325
326 sample_t * dl = ports[5];
327 sample_t * dr = ports[6];
328
329 /* the modulated lattices interpolate, which needs truncated float */
330 DSP::FPTruncateMode _truncate;
331
332 for (int i = 0; i < frames; ++i)
333 {
334 normal = -normal;
335 sample_t x = s[i] + normal;
336
337 sample_t xl, xr;
338
339 PlateStub::process (x, decay, &xl, &xr);
340
341 x = dry * s[i];
342
343 F (dl, i, x + blend * xl, adding_gain);
344 F (dr, i, x + blend * xr, adding_gain);
345 }
346 }
347
348 /* //////////////////////////////////////////////////////////////////////// */
349
350 PortInfo
351 Plate::port_info [] =
352 {
353 {
354 "in",
355 INPUT | AUDIO,
356 {BOUNDED, -1, 1}
357 }, {
358 "bandwidth",
359 INPUT | CONTROL,
360 {BOUNDED | DEFAULT_MID, 0.005, .999} /* .9995 */
361 }, {
362 "tail",
363 INPUT | CONTROL,
364 {BOUNDED | DEFAULT_MID, 0, .749} /* .5 */
365 }, {
366 "damping",
367 INPUT | CONTROL,
368 {BOUNDED | DEFAULT_LOW, .0005, 1} /* .0005 */
369 }, {
370 "blend",
371 INPUT | CONTROL,
372 {BOUNDED | DEFAULT_LOW, 0, 1}
373 }, {
374 "out:l",
375 OUTPUT | AUDIO,
376 {0}
377 }, {
378 "out:r",
379 OUTPUT | AUDIO,
380 {0}
381 }
382 };
383
384 template <> void
setup()385 Descriptor<Plate>::setup()
386 {
387 UniqueID = 1779;
388 Label = "Plate";
389 Properties = HARD_RT;
390
391 Name = CAPS "Plate - Versatile plate reverb";
392 Maker = "Tim Goetze <tim@quitte.de>";
393 Copyright = "GPL, 2004-7";
394
395 /* fill port info and vtable */
396 autogen();
397 }
398
399 /* //////////////////////////////////////////////////////////////////////// */
400
401 template <sample_func_t F>
402 void
one_cycle(int frames)403 Plate2x2::one_cycle (int frames)
404 {
405 sample_t * sl = ports[0];
406 sample_t * sr = ports[1];
407
408 input.bandwidth.set (exp (-M_PI * (1. - getport(2))));
409
410 sample_t decay = getport(3);
411
412 double damp = exp (-M_PI * getport(4));
413 tank.damping[0].set (damp);
414 tank.damping[1].set (damp);
415
416 sample_t blend = getport(5), dry = 1 - blend;
417
418 sample_t * dl = ports[6];
419 sample_t * dr = ports[7];
420
421 /* the modulated lattices interpolate, which needs truncated float */
422 DSP::FPTruncateMode _truncate;
423
424 for (int i = 0; i < frames; ++i)
425 {
426 normal = -normal;
427 sample_t x = (sl[i] + sr[i] + normal) * .5;
428
429 sample_t xl, xr;
430 PlateStub::process (x, decay, &xl, &xr);
431
432 xl = blend * xl + dry * sl[i];
433 xr = blend * xr + dry * sr[i];
434
435 F (dl, i, xl, adding_gain);
436 F (dr, i, xr, adding_gain);
437 }
438 }
439
440 /* //////////////////////////////////////////////////////////////////////// */
441
442 PortInfo
443 Plate2x2::port_info [] =
444 {
445 {
446 "in:l",
447 INPUT | AUDIO,
448 {BOUNDED, -1, 1}
449 }, {
450 "in:r",
451 INPUT | AUDIO,
452 {BOUNDED, -1, 1}
453 }, {
454 "bandwidth",
455 INPUT | CONTROL,
456 {BOUNDED | DEFAULT_MID, 0.005, .999} /* .9995 */
457 }, {
458 "tail",
459 INPUT | CONTROL,
460 {BOUNDED | DEFAULT_MID, 0, .749} /* .5 */
461 }, {
462 "damping",
463 INPUT | CONTROL,
464 {BOUNDED | DEFAULT_LOW, .0005, 1} /* .0005 */
465 }, {
466 "blend",
467 INPUT | CONTROL,
468 {BOUNDED | DEFAULT_LOW, 0, 1}
469 }, {
470 "out:l",
471 OUTPUT | AUDIO,
472 {0}
473 }, {
474 "out:r",
475 OUTPUT | AUDIO,
476 {0}
477 }
478 };
479
480 template <> void
setup()481 Descriptor<Plate2x2>::setup()
482 {
483 UniqueID = 1795;
484 Label = "Plate2x2";
485 Properties = HARD_RT;
486
487 Name = CAPS "Plate2x2 - Versatile plate reverb, stereo inputs";
488 Maker = "Tim Goetze <tim@quitte.de>";
489 Copyright = "GPL, 2004-7";
490
491 /* fill port info and vtable */
492 autogen();
493 }
494
495
496