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