Update Fluidsynth to 2.0.1
[ardour.git] / libs / fluidsynth / src / fluid_rev.c
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_sys.h"
13 #include "fluid_rev.h"
14
15 /***************************************************************
16  *
17  *                           REVERB
18  */
19
20 /* Denormalising:
21  *
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.
30  *
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
35  * cause problems.
36  */
37 #define DC_OFFSET 1e-8
38
39 typedef struct _fluid_allpass fluid_allpass;
40 typedef struct _fluid_comb fluid_comb;
41
42 struct _fluid_allpass
43 {
44     fluid_real_t feedback;
45     fluid_real_t *buffer;
46     int bufsize;
47     int bufidx;
48 };
49
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);
53
54 static void
55 fluid_allpass_setbuffer(fluid_allpass *allpass, int size)
56 {
57     allpass->bufidx = 0;
58     allpass->buffer = FLUID_ARRAY(fluid_real_t, size);
59     allpass->bufsize = size;
60 }
61
62 static void
63 fluid_allpass_release(fluid_allpass *allpass)
64 {
65     FLUID_FREE(allpass->buffer);
66 }
67
68 void
69 fluid_allpass_init(fluid_allpass *allpass)
70 {
71     int i;
72     int len = allpass->bufsize;
73     fluid_real_t *buf = allpass->buffer;
74
75     for(i = 0; i < len; i++)
76     {
77         buf[i] = DC_OFFSET; /* this is not 100 % correct. */
78     }
79 }
80
81 void
82 fluid_allpass_setfeedback(fluid_allpass *allpass, fluid_real_t val)
83 {
84     allpass->feedback = val;
85 }
86
87 fluid_real_t
88 fluid_allpass_getfeedback(fluid_allpass *allpass)
89 {
90     return allpass->feedback;
91 }
92
93 #define fluid_allpass_process(_allpass, _input) \
94 { \
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; \
102   } \
103   _input = output; \
104 }
105
106 struct _fluid_comb
107 {
108     fluid_real_t feedback;
109     fluid_real_t filterstore;
110     fluid_real_t damp1;
111     fluid_real_t damp2;
112     fluid_real_t *buffer;
113     int bufsize;
114     int bufidx;
115 };
116
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);
124
125 void
126 fluid_comb_setbuffer(fluid_comb *comb, int size)
127 {
128     comb->filterstore = 0;
129     comb->bufidx = 0;
130     comb->buffer = FLUID_ARRAY(fluid_real_t, size);
131     comb->bufsize = size;
132 }
133
134 void
135 fluid_comb_release(fluid_comb *comb)
136 {
137     FLUID_FREE(comb->buffer);
138 }
139
140 void
141 fluid_comb_init(fluid_comb *comb)
142 {
143     int i;
144     fluid_real_t *buf = comb->buffer;
145     int len = comb->bufsize;
146
147     for(i = 0; i < len; i++)
148     {
149         buf[i] = DC_OFFSET; /* This is not 100 % correct. */
150     }
151 }
152
153 void
154 fluid_comb_setdamp(fluid_comb *comb, fluid_real_t val)
155 {
156     comb->damp1 = val;
157     comb->damp2 = 1 - val;
158 }
159
160 fluid_real_t
161 fluid_comb_getdamp(fluid_comb *comb)
162 {
163     return comb->damp1;
164 }
165
166 void
167 fluid_comb_setfeedback(fluid_comb *comb, fluid_real_t val)
168 {
169     comb->feedback = val;
170 }
171
172 fluid_real_t
173 fluid_comb_getfeedback(fluid_comb *comb)
174 {
175     return comb->feedback;
176 }
177
178 #define fluid_comb_process(_comb, _input, _output) \
179 { \
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) { \
184     _comb.bufidx = 0; \
185   } \
186   _output += _tmp; \
187 }
188
189 #define numcombs 8
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()).
198  */
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
205
206 /*
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.
211 */
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)
236
237 struct _fluid_revmodel_t
238 {
239     fluid_real_t roomsize;
240     fluid_real_t damp;
241     fluid_real_t level, wet1, wet2;
242     fluid_real_t width;
243     fluid_real_t gain;
244     /*
245      The following are all declared inline
246      to remove the need for dynamic allocation
247      with its subsequent error-checking messiness
248     */
249     /* Comb filters */
250     fluid_comb combL[numcombs];
251     fluid_comb combR[numcombs];
252     /* Allpass filters */
253     fluid_allpass allpassL[numallpasses];
254     fluid_allpass allpassR[numallpasses];
255 };
256
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);
260
261 fluid_revmodel_t *
262 new_fluid_revmodel(fluid_real_t sample_rate)
263 {
264     fluid_revmodel_t *rev;
265     rev = FLUID_NEW(fluid_revmodel_t);
266
267     if(rev == NULL)
268     {
269         return NULL;
270     }
271
272     fluid_set_revmodel_buffers(rev, sample_rate);
273
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);
283
284     rev->gain = fixedgain;
285
286     return rev;
287 }
288
289 void
290 delete_fluid_revmodel(fluid_revmodel_t *rev)
291 {
292     int i;
293     fluid_return_if_fail(rev != NULL);
294
295     for(i = 0; i < numcombs; i++)
296     {
297         fluid_comb_release(&rev->combL[i]);
298         fluid_comb_release(&rev->combR[i]);
299     }
300
301     for(i = 0; i < numallpasses; i++)
302     {
303         fluid_allpass_release(&rev->allpassL[i]);
304         fluid_allpass_release(&rev->allpassR[i]);
305     }
306
307     FLUID_FREE(rev);
308 }
309
310 void
311 fluid_set_revmodel_buffers(fluid_revmodel_t *rev, fluid_real_t sample_rate)
312 {
313
314     float srfactor = sample_rate / 44100.0f;
315
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);
340
341     /* Clear all buffers */
342     fluid_revmodel_init(rev);
343 }
344
345
346 static void
347 fluid_revmodel_init(fluid_revmodel_t *rev)
348 {
349     int i;
350
351     for(i = 0; i < numcombs; i++)
352     {
353         fluid_comb_init(&rev->combL[i]);
354         fluid_comb_init(&rev->combR[i]);
355     }
356
357     for(i = 0; i < numallpasses; i++)
358     {
359         fluid_allpass_init(&rev->allpassL[i]);
360         fluid_allpass_init(&rev->allpassR[i]);
361     }
362 }
363
364 void
365 fluid_revmodel_reset(fluid_revmodel_t *rev)
366 {
367     fluid_revmodel_init(rev);
368 }
369
370 void
371 fluid_revmodel_processreplace(fluid_revmodel_t *rev, fluid_real_t *in,
372                               fluid_real_t *left_out, fluid_real_t *right_out)
373 {
374     int i, k = 0;
375     fluid_real_t outL, outR, input;
376
377     for(k = 0; k < FLUID_BUFSIZE; k++)
378     {
379
380         outL = outR = 0;
381
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
385          * input sample. */
386         input = (2.0f * in[k] + DC_OFFSET) * rev->gain;
387
388         /* Accumulate comb filters in parallel */
389         for(i = 0; i < numcombs; i++)
390         {
391             fluid_comb_process(rev->combL[i], input, outL);
392             fluid_comb_process(rev->combR[i], input, outR);
393         }
394
395         /* Feed through allpasses in series */
396         for(i = 0; i < numallpasses; i++)
397         {
398             fluid_allpass_process(rev->allpassL[i], outL);
399             fluid_allpass_process(rev->allpassR[i], outR);
400         }
401
402         /* Remove the DC offset */
403         outL -= DC_OFFSET;
404         outR -= DC_OFFSET;
405
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;
409     }
410 }
411
412 void
413 fluid_revmodel_processmix(fluid_revmodel_t *rev, fluid_real_t *in,
414                           fluid_real_t *left_out, fluid_real_t *right_out)
415 {
416     int i, k = 0;
417     fluid_real_t outL, outR, input;
418
419     for(k = 0; k < FLUID_BUFSIZE; k++)
420     {
421
422         outL = outR = 0;
423
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
427          * input sample. */
428         input = (2.0f * in[k] + DC_OFFSET) * rev->gain;
429
430         /* Accumulate comb filters in parallel */
431         for(i = 0; i < numcombs; i++)
432         {
433             fluid_comb_process(rev->combL[i], input, outL);
434             fluid_comb_process(rev->combR[i], input, outR);
435         }
436
437         /* Feed through allpasses in series */
438         for(i = 0; i < numallpasses; i++)
439         {
440             fluid_allpass_process(rev->allpassL[i], outL);
441             fluid_allpass_process(rev->allpassR[i], outR);
442         }
443
444         /* Remove the DC offset */
445         outL -= DC_OFFSET;
446         outR -= DC_OFFSET;
447
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;
451     }
452 }
453
454 static void
455 fluid_revmodel_update(fluid_revmodel_t *rev)
456 {
457     /* Recalculate internal values after parameter change */
458     int i;
459
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);
468
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);
474
475     for(i = 0; i < numcombs; i++)
476     {
477         fluid_comb_setfeedback(&rev->combL[i], rev->roomsize);
478         fluid_comb_setfeedback(&rev->combR[i], rev->roomsize);
479     }
480
481     for(i = 0; i < numcombs; i++)
482     {
483         fluid_comb_setdamp(&rev->combL[i], rev->damp);
484         fluid_comb_setdamp(&rev->combR[i], rev->damp);
485     }
486 }
487
488 /**
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
497  */
498 void
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)
501 {
502     if(set & FLUID_REVMODEL_SET_ROOMSIZE)
503     {
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;
509     }
510
511     if(set & FLUID_REVMODEL_SET_DAMPING)
512     {
513         rev->damp = damping * scaledamp;
514     }
515
516     if(set & FLUID_REVMODEL_SET_WIDTH)
517     {
518         rev->width = width;
519     }
520
521     if(set & FLUID_REVMODEL_SET_LEVEL)
522     {
523         fluid_clip(level, 0.0f, 1.0f);
524         rev->level = level;
525     }
526
527     fluid_revmodel_update(rev);
528 }
529
530 void
531 fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate)
532 {
533     int i;
534
535     for(i = 0; i < numcombs; i++)
536     {
537         fluid_comb_release(&rev->combL[i]);
538         fluid_comb_release(&rev->combR[i]);
539     }
540
541     for(i = 0; i < numallpasses; i++)
542     {
543         fluid_allpass_release(&rev->allpassL[i]);
544         fluid_allpass_release(&rev->allpassR[i]);
545     }
546
547     fluid_set_revmodel_buffers(rev, sample_rate);
548 }