1 /*
2
3 Freeverb
4
5 Written by Jezar at Dreampoint, June 2000
6 http://www.dreampoint.co.uk
7 This code is public domain
8
9 Translated to C by Peter Hanappe, Mai 2001
10 */
11
12 #include "fluid_rev.h"
13
14 /***************************************************************
15 *
16 * REVERB
17 */
18
19 /* Denormalising:
20 *
21 * According to music-dsp thread 'Denormalise', Pentium processors
22 * have a hardware 'feature', that is of interest here, related to
23 * numeric underflow. We have a recursive filter. The output decays
24 * exponentially, if the input stops. So the numbers get smaller and
25 * smaller... At some point, they reach 'denormal' level. This will
26 * lead to drastic spikes in the CPU load. The effect was reproduced
27 * with the reverb - sometimes the average load over 10 s doubles!!.
28 *
29 * The 'undenormalise' macro fixes the problem: As soon as the number
30 * is close enough to denormal level, the macro forces the number to
31 * 0.0f. The original macro is:
32 *
33 * #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f
34 *
35 * This will zero out a number when it reaches the denormal level.
36 * Advantage: Maximum dynamic range Disadvantage: We'll have to check
37 * every sample, expensive. The alternative macro comes from a later
38 * mail from Jon Watte. It will zap a number before it reaches
39 * denormal level. Jon suggests to run it once per block instead of
40 * every sample.
41 */
42
43 # if defined(WITH_FLOATX)
44 # define zap_almost_zero(sample) (((*(unsigned int*)&(sample))&0x7f800000) < 0x08000000)?0.0f:(sample)
45 # else
46 /* 1e-20 was chosen as an arbitrary (small) threshold. */
47 #define zap_almost_zero(sample) fabs(sample)<1e-10 ? 0 : sample;
48 #endif
49
50 /* Denormalising part II:
51 *
52 * Another method fixes the problem cheaper: Use a small DC-offset in
53 * the filter calculations. Now the signals converge not against 0,
54 * but against the offset. The constant offset is invisible from the
55 * outside world (i.e. it does not appear at the output. There is a
56 * very small turn-on transient response, which should not cause
57 * problems.
58 */
59
60
61 //#define DC_OFFSET 0
62 #define DC_OFFSET 1e-8
63 //#define DC_OFFSET 0.001f
64 typedef struct _fluid_allpass fluid_allpass;
65 typedef struct _fluid_comb fluid_comb;
66
67 struct _fluid_allpass {
68 fluid_real_t feedback;
69 fluid_real_t *buffer;
70 int bufsize;
71 int bufidx;
72 };
73
74 void fluid_allpass_setbuffer(fluid_allpass* allpass, fluid_real_t *buf, int size);
75 void fluid_allpass_init(fluid_allpass* allpass);
76 void fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val);
77 fluid_real_t fluid_allpass_getfeedback(fluid_allpass* allpass);
78
79 void
fluid_allpass_setbuffer(fluid_allpass * allpass,fluid_real_t * buf,int size)80 fluid_allpass_setbuffer(fluid_allpass* allpass, fluid_real_t *buf, int size)
81 {
82 allpass->bufidx = 0;
83 allpass->buffer = buf;
84 allpass->bufsize = size;
85 }
86
87 void
fluid_allpass_init(fluid_allpass * allpass)88 fluid_allpass_init(fluid_allpass* allpass)
89 {
90 int i;
91 int len = allpass->bufsize;
92 fluid_real_t* buf = allpass->buffer;
93 for (i = 0; i < len; i++) {
94 buf[i] = DC_OFFSET; /* this is not 100 % correct. */
95 }
96 }
97
98 void
fluid_allpass_setfeedback(fluid_allpass * allpass,fluid_real_t val)99 fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val)
100 {
101 allpass->feedback = val;
102 }
103
104 fluid_real_t
fluid_allpass_getfeedback(fluid_allpass * allpass)105 fluid_allpass_getfeedback(fluid_allpass* allpass)
106 {
107 return allpass->feedback;
108 }
109
110 #define fluid_allpass_process(_allpass, _input) \
111 { \
112 fluid_real_t output; \
113 fluid_real_t bufout; \
114 bufout = _allpass.buffer[_allpass.bufidx]; \
115 output = bufout-_input; \
116 _allpass.buffer[_allpass.bufidx] = _input + (bufout * _allpass.feedback); \
117 if (++_allpass.bufidx >= _allpass.bufsize) { \
118 _allpass.bufidx = 0; \
119 } \
120 _input = output; \
121 }
122
123 /* fluid_real_t fluid_allpass_process(fluid_allpass* allpass, fluid_real_t input) */
124 /* { */
125 /* fluid_real_t output; */
126 /* fluid_real_t bufout; */
127 /* bufout = allpass->buffer[allpass->bufidx]; */
128 /* undenormalise(bufout); */
129 /* output = -input + bufout; */
130 /* allpass->buffer[allpass->bufidx] = input + (bufout * allpass->feedback); */
131 /* if (++allpass->bufidx >= allpass->bufsize) { */
132 /* allpass->bufidx = 0; */
133 /* } */
134 /* return output; */
135 /* } */
136
137 struct _fluid_comb {
138 fluid_real_t feedback;
139 fluid_real_t filterstore;
140 fluid_real_t damp1;
141 fluid_real_t damp2;
142 fluid_real_t *buffer;
143 int bufsize;
144 int bufidx;
145 };
146
147 void fluid_comb_setbuffer(fluid_comb* comb, fluid_real_t *buf, int size);
148 void fluid_comb_init(fluid_comb* comb);
149 void fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val);
150 fluid_real_t fluid_comb_getdamp(fluid_comb* comb);
151 void fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val);
152 fluid_real_t fluid_comb_getfeedback(fluid_comb* comb);
153
154 void
fluid_comb_setbuffer(fluid_comb * comb,fluid_real_t * buf,int size)155 fluid_comb_setbuffer(fluid_comb* comb, fluid_real_t *buf, int size)
156 {
157 comb->filterstore = 0;
158 comb->bufidx = 0;
159 comb->buffer = buf;
160 comb->bufsize = size;
161 }
162
163 void
fluid_comb_init(fluid_comb * comb)164 fluid_comb_init(fluid_comb* comb)
165 {
166 int i;
167 fluid_real_t* buf = comb->buffer;
168 int len = comb->bufsize;
169 for (i = 0; i < len; i++) {
170 buf[i] = DC_OFFSET; /* This is not 100 % correct. */
171 }
172 }
173
174 void
fluid_comb_setdamp(fluid_comb * comb,fluid_real_t val)175 fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val)
176 {
177 comb->damp1 = val;
178 comb->damp2 = 1 - val;
179 }
180
181 fluid_real_t
fluid_comb_getdamp(fluid_comb * comb)182 fluid_comb_getdamp(fluid_comb* comb)
183 {
184 return comb->damp1;
185 }
186
187 void
fluid_comb_setfeedback(fluid_comb * comb,fluid_real_t val)188 fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val)
189 {
190 comb->feedback = val;
191 }
192
193 fluid_real_t
fluid_comb_getfeedback(fluid_comb * comb)194 fluid_comb_getfeedback(fluid_comb* comb)
195 {
196 return comb->feedback;
197 }
198
199 #define fluid_comb_process(_comb, _input, _output) \
200 { \
201 fluid_real_t _tmp = _comb.buffer[_comb.bufidx]; \
202 _comb.filterstore = (_tmp * _comb.damp2) + (_comb.filterstore * _comb.damp1); \
203 _comb.buffer[_comb.bufidx] = _input + (_comb.filterstore * _comb.feedback); \
204 if (++_comb.bufidx >= _comb.bufsize) { \
205 _comb.bufidx = 0; \
206 } \
207 _output += _tmp; \
208 }
209
210 /* fluid_real_t fluid_comb_process(fluid_comb* comb, fluid_real_t input) */
211 /* { */
212 /* fluid_real_t output; */
213
214 /* output = comb->buffer[comb->bufidx]; */
215 /* undenormalise(output); */
216 /* comb->filterstore = (output * comb->damp2) + (comb->filterstore * comb->damp1); */
217 /* undenormalise(comb->filterstore); */
218 /* comb->buffer[comb->bufidx] = input + (comb->filterstore * comb->feedback); */
219 /* if (++comb->bufidx >= comb->bufsize) { */
220 /* comb->bufidx = 0; */
221 /* } */
222
223 /* return output; */
224 /* } */
225
226 #define numcombs 8
227 #define numallpasses 4
228 #define fixedgain 0.015f
229 #define scalewet 3.0f
230 #define scaledamp 1.0f
231 #define scaleroom 0.28f
232 #define offsetroom 0.7f
233 #define initialroom 0.5f
234 #define initialdamp 0.2f
235 #define initialwet 1
236 #define initialdry 0
237 #define initialwidth 1
238 #define stereospread 23
239
240 /*
241 These values assume 44.1KHz sample rate
242 they will probably be OK for 48KHz sample rate
243 but would need scaling for 96KHz (or other) sample rates.
244 The values were obtained by listening tests.
245 */
246 #define combtuningL1 1116
247 #define combtuningR1 1116 + stereospread
248 #define combtuningL2 1188
249 #define combtuningR2 1188 + stereospread
250 #define combtuningL3 1277
251 #define combtuningR3 1277 + stereospread
252 #define combtuningL4 1356
253 #define combtuningR4 1356 + stereospread
254 #define combtuningL5 1422
255 #define combtuningR5 1422 + stereospread
256 #define combtuningL6 1491
257 #define combtuningR6 1491 + stereospread
258 #define combtuningL7 1557
259 #define combtuningR7 1557 + stereospread
260 #define combtuningL8 1617
261 #define combtuningR8 1617 + stereospread
262 #define allpasstuningL1 556
263 #define allpasstuningR1 556 + stereospread
264 #define allpasstuningL2 441
265 #define allpasstuningR2 441 + stereospread
266 #define allpasstuningL3 341
267 #define allpasstuningR3 341 + stereospread
268 #define allpasstuningL4 225
269 #define allpasstuningR4 225 + stereospread
270
271 struct _fluid_revmodel_t {
272 fluid_real_t roomsize;
273 fluid_real_t damp;
274 fluid_real_t wet, wet1, wet2;
275 fluid_real_t width;
276 fluid_real_t gain;
277 /*
278 The following are all declared inline
279 to remove the need for dynamic allocation
280 with its subsequent error-checking messiness
281 */
282 /* Comb filters */
283 fluid_comb combL[numcombs];
284 fluid_comb combR[numcombs];
285 /* Allpass filters */
286 fluid_allpass allpassL[numallpasses];
287 fluid_allpass allpassR[numallpasses];
288 /* Buffers for the combs */
289 fluid_real_t bufcombL1[combtuningL1];
290 fluid_real_t bufcombR1[combtuningR1];
291 fluid_real_t bufcombL2[combtuningL2];
292 fluid_real_t bufcombR2[combtuningR2];
293 fluid_real_t bufcombL3[combtuningL3];
294 fluid_real_t bufcombR3[combtuningR3];
295 fluid_real_t bufcombL4[combtuningL4];
296 fluid_real_t bufcombR4[combtuningR4];
297 fluid_real_t bufcombL5[combtuningL5];
298 fluid_real_t bufcombR5[combtuningR5];
299 fluid_real_t bufcombL6[combtuningL6];
300 fluid_real_t bufcombR6[combtuningR6];
301 fluid_real_t bufcombL7[combtuningL7];
302 fluid_real_t bufcombR7[combtuningR7];
303 fluid_real_t bufcombL8[combtuningL8];
304 fluid_real_t bufcombR8[combtuningR8];
305 /* Buffers for the allpasses */
306 fluid_real_t bufallpassL1[allpasstuningL1];
307 fluid_real_t bufallpassR1[allpasstuningR1];
308 fluid_real_t bufallpassL2[allpasstuningL2];
309 fluid_real_t bufallpassR2[allpasstuningR2];
310 fluid_real_t bufallpassL3[allpasstuningL3];
311 fluid_real_t bufallpassR3[allpasstuningR3];
312 fluid_real_t bufallpassL4[allpasstuningL4];
313 fluid_real_t bufallpassR4[allpasstuningR4];
314 };
315
316 void fluid_revmodel_update(fluid_revmodel_t* rev);
317 void fluid_revmodel_init(fluid_revmodel_t* rev);
318
319 fluid_revmodel_t*
new_fluid_revmodel()320 new_fluid_revmodel()
321 {
322 fluid_revmodel_t* rev;
323 rev = FLUID_NEW(fluid_revmodel_t);
324 if (rev == NULL) {
325 return NULL;
326 }
327
328 /* Tie the components to their buffers */
329 fluid_comb_setbuffer(&rev->combL[0], rev->bufcombL1, combtuningL1);
330 fluid_comb_setbuffer(&rev->combR[0], rev->bufcombR1, combtuningR1);
331 fluid_comb_setbuffer(&rev->combL[1], rev->bufcombL2, combtuningL2);
332 fluid_comb_setbuffer(&rev->combR[1], rev->bufcombR2, combtuningR2);
333 fluid_comb_setbuffer(&rev->combL[2], rev->bufcombL3, combtuningL3);
334 fluid_comb_setbuffer(&rev->combR[2], rev->bufcombR3, combtuningR3);
335 fluid_comb_setbuffer(&rev->combL[3], rev->bufcombL4, combtuningL4);
336 fluid_comb_setbuffer(&rev->combR[3], rev->bufcombR4, combtuningR4);
337 fluid_comb_setbuffer(&rev->combL[4], rev->bufcombL5, combtuningL5);
338 fluid_comb_setbuffer(&rev->combR[4], rev->bufcombR5, combtuningR5);
339 fluid_comb_setbuffer(&rev->combL[5], rev->bufcombL6, combtuningL6);
340 fluid_comb_setbuffer(&rev->combR[5], rev->bufcombR6, combtuningR6);
341 fluid_comb_setbuffer(&rev->combL[6], rev->bufcombL7, combtuningL7);
342 fluid_comb_setbuffer(&rev->combR[6], rev->bufcombR7, combtuningR7);
343 fluid_comb_setbuffer(&rev->combL[7], rev->bufcombL8, combtuningL8);
344 fluid_comb_setbuffer(&rev->combR[7], rev->bufcombR8, combtuningR8);
345 fluid_allpass_setbuffer(&rev->allpassL[0], rev->bufallpassL1, allpasstuningL1);
346 fluid_allpass_setbuffer(&rev->allpassR[0], rev->bufallpassR1, allpasstuningR1);
347 fluid_allpass_setbuffer(&rev->allpassL[1], rev->bufallpassL2, allpasstuningL2);
348 fluid_allpass_setbuffer(&rev->allpassR[1], rev->bufallpassR2, allpasstuningR2);
349 fluid_allpass_setbuffer(&rev->allpassL[2], rev->bufallpassL3, allpasstuningL3);
350 fluid_allpass_setbuffer(&rev->allpassR[2], rev->bufallpassR3, allpasstuningR3);
351 fluid_allpass_setbuffer(&rev->allpassL[3], rev->bufallpassL4, allpasstuningL4);
352 fluid_allpass_setbuffer(&rev->allpassR[3], rev->bufallpassR4, allpasstuningR4);
353 /* Set default values */
354 fluid_allpass_setfeedback(&rev->allpassL[0], 0.5f);
355 fluid_allpass_setfeedback(&rev->allpassR[0], 0.5f);
356 fluid_allpass_setfeedback(&rev->allpassL[1], 0.5f);
357 fluid_allpass_setfeedback(&rev->allpassR[1], 0.5f);
358 fluid_allpass_setfeedback(&rev->allpassL[2], 0.5f);
359 fluid_allpass_setfeedback(&rev->allpassR[2], 0.5f);
360 fluid_allpass_setfeedback(&rev->allpassL[3], 0.5f);
361 fluid_allpass_setfeedback(&rev->allpassR[3], 0.5f);
362
363 /* set values manually, since calling set functions causes update
364 and all values should be initialized for an update */
365 rev->roomsize = initialroom * scaleroom + offsetroom;
366 rev->damp = initialdamp * scaledamp;
367 rev->wet = initialwet * scalewet;
368 rev->width = initialwidth;
369 rev->gain = fixedgain;
370
371 /* now its okay to update reverb */
372 fluid_revmodel_update(rev);
373
374 /* Clear all buffers */
375 fluid_revmodel_init(rev);
376 return rev;
377 }
378
379 void
delete_fluid_revmodel(fluid_revmodel_t * rev)380 delete_fluid_revmodel(fluid_revmodel_t* rev)
381 {
382 FLUID_FREE(rev);
383 }
384
385 void
fluid_revmodel_init(fluid_revmodel_t * rev)386 fluid_revmodel_init(fluid_revmodel_t* rev)
387 {
388 int i;
389 for (i = 0; i < numcombs;i++) {
390 fluid_comb_init(&rev->combL[i]);
391 fluid_comb_init(&rev->combR[i]);
392 }
393 for (i = 0; i < numallpasses; i++) {
394 fluid_allpass_init(&rev->allpassL[i]);
395 fluid_allpass_init(&rev->allpassR[i]);
396 }
397 }
398
399 void
fluid_revmodel_reset(fluid_revmodel_t * rev)400 fluid_revmodel_reset(fluid_revmodel_t* rev)
401 {
402 fluid_revmodel_init(rev);
403 }
404
405 void
fluid_revmodel_processreplace(fluid_revmodel_t * rev,fluid_real_t * in,fluid_real_t * left_out,fluid_real_t * right_out)406 fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in,
407 fluid_real_t *left_out, fluid_real_t *right_out)
408 {
409 int i, k = 0;
410 fluid_real_t outL, outR, input;
411
412 for (k = 0; k < FLUID_BUFSIZE; k++) {
413
414 outL = outR = 0;
415
416 /* The original Freeverb code expects a stereo signal and 'input'
417 * is set to the sum of the left and right input sample. Since
418 * this code works on a mono signal, 'input' is set to twice the
419 * input sample. */
420 input = (2 * in[k] + DC_OFFSET) * rev->gain;
421
422 /* Accumulate comb filters in parallel */
423 for (i = 0; i < numcombs; i++) {
424 fluid_comb_process(rev->combL[i], input, outL);
425 fluid_comb_process(rev->combR[i], input, outR);
426 }
427 /* Feed through allpasses in series */
428 for (i = 0; i < numallpasses; i++) {
429 fluid_allpass_process(rev->allpassL[i], outL);
430 fluid_allpass_process(rev->allpassR[i], outR);
431 }
432
433 /* Remove the DC offset */
434 outL -= DC_OFFSET;
435 outR -= DC_OFFSET;
436
437 /* Calculate output REPLACING anything already there */
438 left_out[k] = outL * rev->wet1 + outR * rev->wet2;
439 right_out[k] = outR * rev->wet1 + outL * rev->wet2;
440 }
441 }
442
443 void
fluid_revmodel_processmix(fluid_revmodel_t * rev,fluid_real_t * in,fluid_real_t * left_out,fluid_real_t * right_out)444 fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in,
445 fluid_real_t *left_out, fluid_real_t *right_out)
446 {
447 int i, k = 0;
448 fluid_real_t outL, outR, input;
449
450 for (k = 0; k < FLUID_BUFSIZE; k++) {
451
452 outL = outR = 0;
453
454 /* The original Freeverb code expects a stereo signal and 'input'
455 * is set to the sum of the left and right input sample. Since
456 * this code works on a mono signal, 'input' is set to twice the
457 * input sample. */
458 input = (2 * in[k] + DC_OFFSET) * rev->gain;
459
460 /* Accumulate comb filters in parallel */
461 for (i = 0; i < numcombs; i++) {
462 fluid_comb_process(rev->combL[i], input, outL);
463 fluid_comb_process(rev->combR[i], input, outR);
464 }
465 /* Feed through allpasses in series */
466 for (i = 0; i < numallpasses; i++) {
467 fluid_allpass_process(rev->allpassL[i], outL);
468 fluid_allpass_process(rev->allpassR[i], outR);
469 }
470
471 /* Remove the DC offset */
472 outL -= DC_OFFSET;
473 outR -= DC_OFFSET;
474
475 /* Calculate output MIXING with anything already there */
476 left_out[k] += outL * rev->wet1 + outR * rev->wet2;
477 right_out[k] += outR * rev->wet1 + outL * rev->wet2;
478 }
479 }
480
481 void
fluid_revmodel_update(fluid_revmodel_t * rev)482 fluid_revmodel_update(fluid_revmodel_t* rev)
483 {
484 /* Recalculate internal values after parameter change */
485 int i;
486
487 rev->wet1 = rev->wet * (rev->width / 2 + 0.5f);
488 rev->wet2 = rev->wet * ((1 - rev->width) / 2);
489
490 for (i = 0; i < numcombs; i++) {
491 fluid_comb_setfeedback(&rev->combL[i], rev->roomsize);
492 fluid_comb_setfeedback(&rev->combR[i], rev->roomsize);
493 }
494
495 for (i = 0; i < numcombs; i++) {
496 fluid_comb_setdamp(&rev->combL[i], rev->damp);
497 fluid_comb_setdamp(&rev->combR[i], rev->damp);
498 }
499 }
500
501 /*
502 The following get/set functions are not inlined, because
503 speed is never an issue when calling them, and also
504 because as you develop the reverb model, you may
505 wish to take dynamic action when they are called.
506 */
507 void
fluid_revmodel_setroomsize(fluid_revmodel_t * rev,fluid_real_t value)508 fluid_revmodel_setroomsize(fluid_revmodel_t* rev, fluid_real_t value)
509 {
510 /* fluid_clip(value, 0.0f, 1.0f); */
511 rev->roomsize = (value * scaleroom) + offsetroom;
512 fluid_revmodel_update(rev);
513 }
514
515 fluid_real_t
fluid_revmodel_getroomsize(fluid_revmodel_t * rev)516 fluid_revmodel_getroomsize(fluid_revmodel_t* rev)
517 {
518 return (rev->roomsize - offsetroom) / scaleroom;
519 }
520
521 void
fluid_revmodel_setdamp(fluid_revmodel_t * rev,fluid_real_t value)522 fluid_revmodel_setdamp(fluid_revmodel_t* rev, fluid_real_t value)
523 {
524 /* fluid_clip(value, 0.0f, 1.0f); */
525 rev->damp = value * scaledamp;
526 fluid_revmodel_update(rev);
527 }
528
529 fluid_real_t
fluid_revmodel_getdamp(fluid_revmodel_t * rev)530 fluid_revmodel_getdamp(fluid_revmodel_t* rev)
531 {
532 return rev->damp / scaledamp;
533 }
534
535 void
fluid_revmodel_setlevel(fluid_revmodel_t * rev,fluid_real_t value)536 fluid_revmodel_setlevel(fluid_revmodel_t* rev, fluid_real_t value)
537 {
538 fluid_clip(value, 0.0f, 1.0f);
539 rev->wet = value * scalewet;
540 fluid_revmodel_update(rev);
541 }
542
543 fluid_real_t
fluid_revmodel_getlevel(fluid_revmodel_t * rev)544 fluid_revmodel_getlevel(fluid_revmodel_t* rev)
545 {
546 return rev->wet / scalewet;
547 }
548
549 void
fluid_revmodel_setwidth(fluid_revmodel_t * rev,fluid_real_t value)550 fluid_revmodel_setwidth(fluid_revmodel_t* rev, fluid_real_t value)
551 {
552 /* fluid_clip(value, 0.0f, 1.0f); */
553 rev->width = value;
554 fluid_revmodel_update(rev);
555 }
556
557 fluid_real_t
fluid_revmodel_getwidth(fluid_revmodel_t * rev)558 fluid_revmodel_getwidth(fluid_revmodel_t* rev)
559 {
560 return rev->width;
561 }
562