5 Written by Jezar at Dreampoint, June 2000
6 http://www.dreampoint.co.uk
7 This code is public domain
9 Translated to C by Peter Hanappe, Mai 2001
12 #include "fluid_sys.h"
13 #include "fluid_rev.h"
15 /***************************************************************
22 * We have a recursive filter. The output decays exponentially, if the input
23 * stops. So the numbers get smaller and smaller... At some point, they reach
24 * 'denormal' level. On some platforms this will lead to drastic spikes in the
25 * CPU load. This is especially noticable on some older Pentium (especially
26 * Pentium 3) processors, but even more modern Intel Core processors still show
27 * reduced performance with denormals. While there are compile-time switches to
28 * treat denormals as zero for a lot of processors, those are not available or
29 * effective on all platforms.
31 * The fix used here: Use a small DC-offset in the filter calculations. Now
32 * the signals converge not against 0, but against the offset. The constant
33 * offset is invisible from the outside world (i.e. it does not appear at the
34 * output. There is a very small turn-on transient response, which should not
37 #define DC_OFFSET 1e-8
39 typedef struct _fluid_allpass fluid_allpass;
40 typedef struct _fluid_comb fluid_comb;
44 fluid_real_t feedback;
50 void fluid_allpass_init(fluid_allpass *allpass);
51 void fluid_allpass_setfeedback(fluid_allpass *allpass, fluid_real_t val);
52 fluid_real_t fluid_allpass_getfeedback(fluid_allpass *allpass);
55 fluid_allpass_setbuffer(fluid_allpass *allpass, int size)
58 allpass->buffer = FLUID_ARRAY(fluid_real_t, size);
59 allpass->bufsize = size;
63 fluid_allpass_release(fluid_allpass *allpass)
65 FLUID_FREE(allpass->buffer);
69 fluid_allpass_init(fluid_allpass *allpass)
72 int len = allpass->bufsize;
73 fluid_real_t *buf = allpass->buffer;
75 for(i = 0; i < len; i++)
77 buf[i] = DC_OFFSET; /* this is not 100 % correct. */
82 fluid_allpass_setfeedback(fluid_allpass *allpass, fluid_real_t val)
84 allpass->feedback = val;
88 fluid_allpass_getfeedback(fluid_allpass *allpass)
90 return allpass->feedback;
93 #define fluid_allpass_process(_allpass, _input) \
95 fluid_real_t output; \
96 fluid_real_t bufout; \
97 bufout = _allpass.buffer[_allpass.bufidx]; \
98 output = bufout-_input; \
99 _allpass.buffer[_allpass.bufidx] = _input + (bufout * _allpass.feedback); \
100 if (++_allpass.bufidx >= _allpass.bufsize) { \
101 _allpass.bufidx = 0; \
108 fluid_real_t feedback;
109 fluid_real_t filterstore;
112 fluid_real_t *buffer;
117 void fluid_comb_setbuffer(fluid_comb *comb, int size);
118 void fluid_comb_release(fluid_comb *comb);
119 void fluid_comb_init(fluid_comb *comb);
120 void fluid_comb_setdamp(fluid_comb *comb, fluid_real_t val);
121 fluid_real_t fluid_comb_getdamp(fluid_comb *comb);
122 void fluid_comb_setfeedback(fluid_comb *comb, fluid_real_t val);
123 fluid_real_t fluid_comb_getfeedback(fluid_comb *comb);
126 fluid_comb_setbuffer(fluid_comb *comb, int size)
128 comb->filterstore = 0;
130 comb->buffer = FLUID_ARRAY(fluid_real_t, size);
131 comb->bufsize = size;
135 fluid_comb_release(fluid_comb *comb)
137 FLUID_FREE(comb->buffer);
141 fluid_comb_init(fluid_comb *comb)
144 fluid_real_t *buf = comb->buffer;
145 int len = comb->bufsize;
147 for(i = 0; i < len; i++)
149 buf[i] = DC_OFFSET; /* This is not 100 % correct. */
154 fluid_comb_setdamp(fluid_comb *comb, fluid_real_t val)
157 comb->damp2 = 1 - val;
161 fluid_comb_getdamp(fluid_comb *comb)
167 fluid_comb_setfeedback(fluid_comb *comb, fluid_real_t val)
169 comb->feedback = val;
173 fluid_comb_getfeedback(fluid_comb *comb)
175 return comb->feedback;
178 #define fluid_comb_process(_comb, _input, _output) \
180 fluid_real_t _tmp = _comb.buffer[_comb.bufidx]; \
181 _comb.filterstore = (_tmp * _comb.damp2) + (_comb.filterstore * _comb.damp1); \
182 _comb.buffer[_comb.bufidx] = _input + (_comb.filterstore * _comb.feedback); \
183 if (++_comb.bufidx >= _comb.bufsize) { \
190 #define numallpasses 4
191 #define fixedgain 0.015f
192 /* scale_wet_width is a compensation weight factor to get an output
193 amplitude (wet) rather independent of the width setting.
194 0: the output amplitude is fully dependant on the width setting.
195 >0: the output amplitude is less dependant on the width setting.
196 With a scale_wet_width of 0.2 the output amplitude is rather
197 independent of width setting (see fluid_revmodel_update()).
199 #define scale_wet_width 0.2f
200 #define scalewet 3.0f
201 #define scaledamp 1.0f
202 #define scaleroom 0.28f
203 #define offsetroom 0.7f
204 #define stereospread 23
207 These values assume 44.1KHz sample rate
208 they will probably be OK for 48KHz sample rate
209 but would need scaling for 96KHz (or other) sample rates.
210 The values were obtained by listening tests.
212 #define combtuningL1 1116
213 #define combtuningR1 (1116 + stereospread)
214 #define combtuningL2 1188
215 #define combtuningR2 (1188 + stereospread)
216 #define combtuningL3 1277
217 #define combtuningR3 (1277 + stereospread)
218 #define combtuningL4 1356
219 #define combtuningR4 (1356 + stereospread)
220 #define combtuningL5 1422
221 #define combtuningR5 (1422 + stereospread)
222 #define combtuningL6 1491
223 #define combtuningR6 (1491 + stereospread)
224 #define combtuningL7 1557
225 #define combtuningR7 (1557 + stereospread)
226 #define combtuningL8 1617
227 #define combtuningR8 (1617 + stereospread)
228 #define allpasstuningL1 556
229 #define allpasstuningR1 (556 + stereospread)
230 #define allpasstuningL2 441
231 #define allpasstuningR2 (441 + stereospread)
232 #define allpasstuningL3 341
233 #define allpasstuningR3 (341 + stereospread)
234 #define allpasstuningL4 225
235 #define allpasstuningR4 (225 + stereospread)
237 struct _fluid_revmodel_t
239 fluid_real_t roomsize;
241 fluid_real_t level, wet1, wet2;
245 The following are all declared inline
246 to remove the need for dynamic allocation
247 with its subsequent error-checking messiness
250 fluid_comb combL[numcombs];
251 fluid_comb combR[numcombs];
252 /* Allpass filters */
253 fluid_allpass allpassL[numallpasses];
254 fluid_allpass allpassR[numallpasses];
257 static void fluid_revmodel_update(fluid_revmodel_t *rev);
258 static void fluid_revmodel_init(fluid_revmodel_t *rev);
259 void fluid_set_revmodel_buffers(fluid_revmodel_t *rev, fluid_real_t sample_rate);
262 new_fluid_revmodel(fluid_real_t sample_rate)
264 fluid_revmodel_t *rev;
265 rev = FLUID_NEW(fluid_revmodel_t);
272 fluid_set_revmodel_buffers(rev, sample_rate);
274 /* Set default values */
275 fluid_allpass_setfeedback(&rev->allpassL[0], 0.5f);
276 fluid_allpass_setfeedback(&rev->allpassR[0], 0.5f);
277 fluid_allpass_setfeedback(&rev->allpassL[1], 0.5f);
278 fluid_allpass_setfeedback(&rev->allpassR[1], 0.5f);
279 fluid_allpass_setfeedback(&rev->allpassL[2], 0.5f);
280 fluid_allpass_setfeedback(&rev->allpassR[2], 0.5f);
281 fluid_allpass_setfeedback(&rev->allpassL[3], 0.5f);
282 fluid_allpass_setfeedback(&rev->allpassR[3], 0.5f);
284 rev->gain = fixedgain;
290 delete_fluid_revmodel(fluid_revmodel_t *rev)
293 fluid_return_if_fail(rev != NULL);
295 for(i = 0; i < numcombs; i++)
297 fluid_comb_release(&rev->combL[i]);
298 fluid_comb_release(&rev->combR[i]);
301 for(i = 0; i < numallpasses; i++)
303 fluid_allpass_release(&rev->allpassL[i]);
304 fluid_allpass_release(&rev->allpassR[i]);
311 fluid_set_revmodel_buffers(fluid_revmodel_t *rev, fluid_real_t sample_rate)
314 float srfactor = sample_rate / 44100.0f;
316 fluid_comb_setbuffer(&rev->combL[0], combtuningL1 * srfactor);
317 fluid_comb_setbuffer(&rev->combR[0], combtuningR1 * srfactor);
318 fluid_comb_setbuffer(&rev->combL[1], combtuningL2 * srfactor);
319 fluid_comb_setbuffer(&rev->combR[1], combtuningR2 * srfactor);
320 fluid_comb_setbuffer(&rev->combL[2], combtuningL3 * srfactor);
321 fluid_comb_setbuffer(&rev->combR[2], combtuningR3 * srfactor);
322 fluid_comb_setbuffer(&rev->combL[3], combtuningL4 * srfactor);
323 fluid_comb_setbuffer(&rev->combR[3], combtuningR4 * srfactor);
324 fluid_comb_setbuffer(&rev->combL[4], combtuningL5 * srfactor);
325 fluid_comb_setbuffer(&rev->combR[4], combtuningR5 * srfactor);
326 fluid_comb_setbuffer(&rev->combL[5], combtuningL6 * srfactor);
327 fluid_comb_setbuffer(&rev->combR[5], combtuningR6 * srfactor);
328 fluid_comb_setbuffer(&rev->combL[6], combtuningL7 * srfactor);
329 fluid_comb_setbuffer(&rev->combR[6], combtuningR7 * srfactor);
330 fluid_comb_setbuffer(&rev->combL[7], combtuningL8 * srfactor);
331 fluid_comb_setbuffer(&rev->combR[7], combtuningR8 * srfactor);
332 fluid_allpass_setbuffer(&rev->allpassL[0], allpasstuningL1 * srfactor);
333 fluid_allpass_setbuffer(&rev->allpassR[0], allpasstuningR1 * srfactor);
334 fluid_allpass_setbuffer(&rev->allpassL[1], allpasstuningL2 * srfactor);
335 fluid_allpass_setbuffer(&rev->allpassR[1], allpasstuningR2 * srfactor);
336 fluid_allpass_setbuffer(&rev->allpassL[2], allpasstuningL3 * srfactor);
337 fluid_allpass_setbuffer(&rev->allpassR[2], allpasstuningR3 * srfactor);
338 fluid_allpass_setbuffer(&rev->allpassL[3], allpasstuningL4 * srfactor);
339 fluid_allpass_setbuffer(&rev->allpassR[3], allpasstuningR4 * srfactor);
341 /* Clear all buffers */
342 fluid_revmodel_init(rev);
347 fluid_revmodel_init(fluid_revmodel_t *rev)
351 for(i = 0; i < numcombs; i++)
353 fluid_comb_init(&rev->combL[i]);
354 fluid_comb_init(&rev->combR[i]);
357 for(i = 0; i < numallpasses; i++)
359 fluid_allpass_init(&rev->allpassL[i]);
360 fluid_allpass_init(&rev->allpassR[i]);
365 fluid_revmodel_reset(fluid_revmodel_t *rev)
367 fluid_revmodel_init(rev);
371 fluid_revmodel_processreplace(fluid_revmodel_t *rev, fluid_real_t *in,
372 fluid_real_t *left_out, fluid_real_t *right_out)
375 fluid_real_t outL, outR, input;
377 for(k = 0; k < FLUID_BUFSIZE; k++)
382 /* The original Freeverb code expects a stereo signal and 'input'
383 * is set to the sum of the left and right input sample. Since
384 * this code works on a mono signal, 'input' is set to twice the
386 input = (2.0f * in[k] + DC_OFFSET) * rev->gain;
388 /* Accumulate comb filters in parallel */
389 for(i = 0; i < numcombs; i++)
391 fluid_comb_process(rev->combL[i], input, outL);
392 fluid_comb_process(rev->combR[i], input, outR);
395 /* Feed through allpasses in series */
396 for(i = 0; i < numallpasses; i++)
398 fluid_allpass_process(rev->allpassL[i], outL);
399 fluid_allpass_process(rev->allpassR[i], outR);
402 /* Remove the DC offset */
406 /* Calculate output REPLACING anything already there */
407 left_out[k] = outL * rev->wet1 + outR * rev->wet2;
408 right_out[k] = outR * rev->wet1 + outL * rev->wet2;
413 fluid_revmodel_processmix(fluid_revmodel_t *rev, fluid_real_t *in,
414 fluid_real_t *left_out, fluid_real_t *right_out)
417 fluid_real_t outL, outR, input;
419 for(k = 0; k < FLUID_BUFSIZE; k++)
424 /* The original Freeverb code expects a stereo signal and 'input'
425 * is set to the sum of the left and right input sample. Since
426 * this code works on a mono signal, 'input' is set to twice the
428 input = (2.0f * in[k] + DC_OFFSET) * rev->gain;
430 /* Accumulate comb filters in parallel */
431 for(i = 0; i < numcombs; i++)
433 fluid_comb_process(rev->combL[i], input, outL);
434 fluid_comb_process(rev->combR[i], input, outR);
437 /* Feed through allpasses in series */
438 for(i = 0; i < numallpasses; i++)
440 fluid_allpass_process(rev->allpassL[i], outL);
441 fluid_allpass_process(rev->allpassR[i], outR);
444 /* Remove the DC offset */
448 /* Calculate output MIXING with anything already there */
449 left_out[k] += outL * rev->wet1 + outR * rev->wet2;
450 right_out[k] += outR * rev->wet1 + outL * rev->wet2;
455 fluid_revmodel_update(fluid_revmodel_t *rev)
457 /* Recalculate internal values after parameter change */
460 /* The stereo amplitude equation (wet1 and wet2 below) have a
461 tendency to produce high amplitude with high width values ( 1 < width < 100).
462 This results in an unwanted noisy output clipped by the audio card.
463 To avoid this dependency, we divide by (1 + rev->width * scale_wet_width)
464 Actually, with a scale_wet_width of 0.2, (regardless of level setting),
465 the output amplitude (wet) seems rather independent of width setting */
466 fluid_real_t wet = (rev->level * scalewet) /
467 (1.0f + rev->width * scale_wet_width);
469 /* wet1 and wet2 are used by the stereo effect controled by the width setting
470 for producing a stereo ouptput from a monophonic reverb signal.
471 Please see the note above about a side effect tendency */
472 rev->wet1 = wet * (rev->width / 2.0f + 0.5f);
473 rev->wet2 = wet * ((1.0f - rev->width) / 2.0f);
475 for(i = 0; i < numcombs; i++)
477 fluid_comb_setfeedback(&rev->combL[i], rev->roomsize);
478 fluid_comb_setfeedback(&rev->combR[i], rev->roomsize);
481 for(i = 0; i < numcombs; i++)
483 fluid_comb_setdamp(&rev->combL[i], rev->damp);
484 fluid_comb_setdamp(&rev->combR[i], rev->damp);
489 * Set one or more reverb parameters.
490 * @param rev Reverb instance
491 * @param set One or more flags from #fluid_revmodel_set_t indicating what
492 * parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters)
493 * @param roomsize Reverb room size
494 * @param damping Reverb damping
495 * @param width Reverb width
496 * @param level Reverb level
499 fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize,
500 fluid_real_t damping, fluid_real_t width, fluid_real_t level)
502 if(set & FLUID_REVMODEL_SET_ROOMSIZE)
504 /* With upper limit above 1.07, the output amplitude will grow
505 exponentially. So, keeping this upper limit to 1.0 seems sufficient
506 as it produces yet a long reverb time */
507 fluid_clip(roomsize, 0.0f, 1.0f);
508 rev->roomsize = (roomsize * scaleroom) + offsetroom;
511 if(set & FLUID_REVMODEL_SET_DAMPING)
513 rev->damp = damping * scaledamp;
516 if(set & FLUID_REVMODEL_SET_WIDTH)
521 if(set & FLUID_REVMODEL_SET_LEVEL)
523 fluid_clip(level, 0.0f, 1.0f);
527 fluid_revmodel_update(rev);
531 fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate)
535 for(i = 0; i < numcombs; i++)
537 fluid_comb_release(&rev->combL[i]);
538 fluid_comb_release(&rev->combR[i]);
541 for(i = 0; i < numallpasses; i++)
543 fluid_allpass_release(&rev->allpassL[i]);
544 fluid_allpass_release(&rev->allpassR[i]);
547 fluid_set_revmodel_buffers(rev, sample_rate);