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