summaryrefslogtreecommitdiff
path: root/libs/fluidsynth/src/fluid_rev.c
blob: f3ee0e47f9c673ea2d23ae34625e466bdedd8ab9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
/******************************************************************************
 * FluidSynth - A Software Synthesizer
 *
 * Copyright (C) 2003  Peter Hanappe and others.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA
 *
 *
 *                           FDN REVERB
 *
 * Freeverb used by fluidsynth (v.1.1.10 and previous) is based on
 * Schroeder-Moorer reverberator:
 * https://ccrma.stanford.edu/~jos/pasp/Freeverb.html
 *
 * This FDN reverberation is based on jot FDN reverberator.
 * https://ccrma.stanford.edu/~jos/Reverb/FDN_Late_Reverberation.html
 * Like Freeverb it is a late reverb which is convenient for Fluidsynth.
 *
 *
 *                                        .-------------------.
 *                      .-----------------|                   |
 *                      |              -  |      Feedback     |
 *                      |  .--------------|       Matrix      |
 *                      |  |              |___________________|
 *                      |  |                         /|\   /|\
 *                     \|/ |   .---------. .-------.  |  -  |   .------.
 *                   .->+ ---->| Delay 0 |-|L.P.F 0|--*-------->|      |-> out
 *      .---------.  |     |   |_________| |_______|        |   |      |  left
 *      |Tone     |  |     |       -           -            |   |Stereo|
 * In ->|corrector|--*     |       -           -            |   | unit |
 * mono |_________|  |    \|/  .---------. .-------.        |   |      |-> out
 *                    ---->+ ->| Delay 7 |-|L.P.F 7|--------*-->|      |  right
 *                             |_________| |_______|            |______|
 *                                          /|\ /|\              /|\ /|\
 *                                           |   |                |   |
 *                                roomsize --/   |       width  --/   |
 *                                    damp ------/       level  ------/
 *
 * It takes a monophonic input and produces a stereo output.
 *
 * The parameters are the same than for Freeverb.
 * Also the default response of these parameters are the same than for Freeverb:
 *  - roomsize (0 to 1): control the reverb time from 0.7 to 12.5 s.
 *    This reverberation time is ofen called T60DC.
 *
 *  - damp (0 to 1): controls the reverb time frequency dependency.
 *    This controls the reverb time for the frequency sample rate/2
 *
 *    When 0, the reverb time for high frequencies is the same as
 *    for DC frequency.
 *    When > 0, high frequencies have less reverb time than lower frequencies.
 *
 *  - width (0 to 100): controls the left/right output separation.
 *    When 0, there are no separation and the signal on left and right.
 *    output is the same. This sounds like a monophonic signal.
 *    When 100, the separation between left and right is maximum.
 *
 *  - level (0 to 1), controls the ouput level reverberation.
 *
 * This FDN reverb produces a better quality reverberation tail than Freeverb with
 * far less ringing by using modulated delay lines that help to cancel
 * the building of a lot of resonances in the reverberation tail even when
 * using only 8 delays lines (NBR_DELAYS = 8) (default).
 *
 * The frequency density (often called "modal density" is one property that
 * contributes to sound quality. Although 8 lines give good result, using 12 delays
 * lines brings the overall frequency density quality a bit higher.
 * This quality augmentation is noticeable particularly when using long reverb time
 * (roomsize = 1) on solo instrument with long release time. Of course the cpu load
 * augmentation is +50% relatively to 8 lines.
 *
 * As a general rule the reverberation tail quality is easier to perceive by ear
 * when using:
 * - percussive instruments (i.e piano and others).
 * - long reverb time (roomsize = 1).
 * - no damping (damp = 0).
 * - Using headphone. Avoid using loud speaker, you will be quickly misguided by the
 *   natural reverberation of the room in which you are.
 *
 * The cpu load for 8 lines is a bit lower than for freeverb (- 3%),
 * but higher for 12 lines (+ 41%).
 *
 *
 * The memory consumption is less than for freeverb
 * (see the results table below).
 *
 * Two macros are usable at compiler time:
 * - NBR_DELAYS: number of delay lines. 8 (default) or 12.
 * - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response of
 *   roomsize parameter.
 *   When this macro is not defined (the default), roomsize has the same
 *   response that Freeverb, that is:
 *   - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s).
 *
 *   When this macro is defined, roomsize behaves linearly:
 *   - roomsize (0 to 1) controls reverb time linearly  (0.7 to 12.5 s).
 *   This linear response is convenient when using GUI controls.
 *
 * --------------------------------------------------------------------------
 * Compare table:
 * Note: the cpu load in % are relative each to other. These values are
 * given by the fluidsynth profile commands.
 * --------------------------------------------------------------------------
 * reverb    | NBR_DELAYS     | Performances    | memory size       | quality
 *           |                | (cpu_load: %)   | (bytes)(see note) |
 * ==========================================================================
 * freeverb  | 2 x 8 comb     |  0.670 %        | 204616            | ringing
 *           | 2 x 4 all-pass |                 |                   |
 * ----------|---------------------------------------------------------------
 *    FDN    | 8              |  0.650 %        | 112160            | far less
 * modulated |                |(feeverb - 3%)   | (55% freeverb)    | ringing
 *           |---------------------------------------------------------------
 *           | 12             |  0.942 %        | 168240            | best than
 *           |                |(freeverb + 41%) | (82 %freeverb)    | 8 lines
 *---------------------------------------------------------------------------
 *
 * Note:
 * Values in this column is the memory consumption for sample rate <= 44100Hz.
 * For sample rate > 44100Hz , multiply these values by (sample rate / 44100Hz).
 *
 *
 *----------------------------------------------------------------------------
 * 'Denormalise' method to avoid loss of performance.
 * --------------------------------------------------
 * According to music-dsp thread 'Denormalise', Pentium processors
 * have a hardware 'feature', that is of interest here, related to
 * numeric underflow.  We have a recursive filter. The output decays
 * exponentially, if the input stops.  So the numbers get smaller and
 * smaller... At some point, they reach 'denormal' level.  This will
 * lead to drastic spikes in the CPU load.  The effect was reproduced
 * with the reverb - sometimes the average load over 10 s doubles!!.
 *
 * The 'undenormalise' macro fixes the problem: As soon as the number
 * is close enough to denormal level, the macro forces the number to
 * 0.0f.  The original macro is:
 *
 * #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f
 *
 * This will zero out a number when it reaches the denormal level.
 * Advantage: Maximum dynamic range Disadvantage: We'll have to check
 * every sample, expensive.  The alternative macro comes from a later
 * mail from Jon Watte. It will zap a number before it reaches
 * denormal level. Jon suggests to run it once per block instead of
 * every sample.
 */

/* Denormalising part II:
 *
 * Another method fixes the problem cheaper: Use a small DC-offset in
 * the filter calculations.  Now the signals converge not against 0,
 * but against the offset.  The constant offset is invisible from the
 * outside world (i.e. it does not appear at the output.  There is a
 * very small turn-on transient response, which should not cause
 * problems.
 */
#include "fluid_rev.h"
#include "fluid_sys.h"

/*----------------------------------------------------------------------------
                        Configuration macros at compiler time.

 3 macros are usable at compiler time:
  - NBR_DELAYs: number of delay lines. 8 (default) or 12.
  - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response for
    roomsize parameter.
  - DENORMALISING enable denormalising handling.
-----------------------------------------------------------------------------*/
//#define INFOS_PRINT /* allows message to be printed on the console. */

/* Number of delay lines (must be only 8 or 12)
  8 is the default.
 12 produces a better quality but is +50% cpu expensive
*/
#define NBR_DELAYS        8 /* default*/

/* response curve of parameter roomsize  */
/*
    The default response is the same as Freeverb:
    - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s).

    when ROOMSIZE_RESPONSE_LINEAR is defined, the response is:
    - roomsize (0 to 1) controls reverb time linearly  (0.7 to 12.5 s).
*/
//#define ROOMSIZE_RESPONSE_LINEAR

/* DENORMALISING enable denormalising handling */
#define DENORMALISING

#ifdef DENORMALISING
#define DC_OFFSET 1e-8f
#else
#define DC_OFFSET  0.0f
#endif

/*----------------------------------------------------------------------------
 Initial internal reverb settings (at reverb creation time)
-----------------------------------------------------------------------------*/
/* SCALE_WET_WIDTH is a compensation weight factor to get an output
   amplitude (wet) rather independent of the width setting.
    0: the output amplitude is fully dependant on the width setting.
   >0: the output amplitude is less dependant on the width setting.
   With a SCALE_WET_WIDTH of 0.2 the output amplitude is rather
   independent of width setting (see fluid_revmodel_update()).
 */
#define SCALE_WET_WIDTH 0.2f

/* It is best to inject the input signal less ofen. This contributes to obtain
a flatter response on comb filter. So the input gain is set to 0.1 rather 1.0. */
#define FIXED_GAIN 0.1f /* input gain */

/* SCALE_WET is adjusted to 5.0 to get internal output level equivalent to freeverb */
#define SCALE_WET 5.0f /* scale output gain */

/*----------------------------------------------------------------------------
 Internal FDN late reverb settings
-----------------------------------------------------------------------------*/

/*-- Reverberation time settings ----------------------------------
 MIN_DC_REV_TIME est defined egal to the minimum value of freeverb:
 MAX_DC_REV_TIME est defined egal to the maximum value of freeverb:
 T60DC is computed from gi and the longuest delay line in freeverb: L8 = 1617
 T60 = -3 * Li * T / log10(gi)
 T60 = -3 * Li *  / (log10(gi) * sr)

  - Li: length of comb filter delay line.
  - sr: sample rate.
  - gi: the feedback gain.

 The minimum value for freeverb correspond to gi = 0.7.
 with Mi = 1617, sr at 44100 Hz, and gi = 0.7 => MIN_DC_REV_TIME = 0.7 s

 The maximum value for freeverb correspond to gi = 0.98.
 with Mi = 1617, sr at 44100 Hz, and gi = 0.98 => MAX_DC_REV_TIME = 12.5 s
*/

#define MIN_DC_REV_TIME 0.7f	/* minimum T60DC reverb time: seconds */
#define MAX_DC_REV_TIME 12.5f	/* maximumm T60DC time in seconds */
#define RANGE_REV_TIME (MAX_DC_REV_TIME - MIN_DC_REV_TIME)

/* macro to compute internal reverberation time versus roomsize parameter  */
#define GET_DC_REV_TIME(roomsize) (MIN_DC_REV_TIME + RANGE_REV_TIME * roomsize)

/*-- Modulation related settings ----------------------------------*/
/* For many instruments, the range for MOD_FREQ and MOD_DEPTH should be:

 MOD_DEPTH: [3..6] (in samples).
 MOD_FREQ: [0.5 ..2.0] (in Hz).

 Values below the lower limits are often not sufficient to cancel unwanted
 "ringing"(resonant frequency).
 Values above upper limits augment the unwanted "chorus".

 With NBR_DELAYS to 8:
  MOD_DEPTH must be >= 4 to cancel the unwanted "ringing".[4..6].
 With NBR_DELAYS to 12:
  MOD_DEPTH to 3 is sufficient to cancel the unwanted "ringing".[3..6]
*/
#define MOD_DEPTH 4		/* modulation depth (samples)*/
#define MOD_RATE 50		/* modulation rate  (samples)*/
#define MOD_FREQ 1.0f	/* modulation frequency (Hz) */
/*
 Number of samples to add to the desired length of a delay line. This
 allow to take account of modulation interpolation.
 1 is sufficient with MOD_DEPTH equal to 6.
*/
#define INTERP_SAMPLES_NBR 1

/* phase offset between modulators waveform */
#define MOD_PHASE  (360.0f/(float) NBR_DELAYS)

#if (NBR_DELAYS == 8)
    #define DELAY_L0 601
    #define DELAY_L1 691
    #define DELAY_L2 773
    #define DELAY_L3 839
    #define DELAY_L4 919
    #define DELAY_L5 997
    #define DELAY_L6 1061
    #define DELAY_L7 1129
#elif (NBR_DELAYS == 12)
    #define DELAY_L0 601
    #define DELAY_L1 691
    #define DELAY_L2 773
    #define DELAY_L3 839
    #define DELAY_L4 919
    #define DELAY_L5 997
    #define DELAY_L6 1061
    #define DELAY_L7 1093
    #define DELAY_L8 1129
    #define DELAY_L9 1151
    #define DELAY_L10 1171
    #define DELAY_L11 1187
#endif


/*---------------------------------------------------------------------------*/
/* The FDN late feed back matrix: A
                            T
  A   = P  -  2 / N * u  * u
   N     N             N    N

  N: the matrix dimension (i.e NBR_DELAYS).
  P: permutation matrix.
  u: is a colomn vector of 1.

*/
#define FDN_MATRIX_FACTOR (fluid_real_t)(-2.0 / NBR_DELAYS)

/*----------------------------------------------------------------------------
             Internal FDN late structures and static functions
-----------------------------------------------------------------------------*/


/*-----------------------------------------------------------------------------
 Delay absorbent low pass filter
-----------------------------------------------------------------------------*/
typedef struct
{
    fluid_real_t buffer;
    fluid_real_t b0, a1;         /* filter coefficients */
} fdn_delay_lpf;

/*-----------------------------------------------------------------------------
 Sets coefficients for delay absorbent low pass filter.
 @param lpf pointer on low pass filter structure.
 @param b0,a1 coefficients.
-----------------------------------------------------------------------------*/
static void set_fdn_delay_lpf(fdn_delay_lpf *lpf,
                              fluid_real_t b0, fluid_real_t  a1)
{
    lpf->b0 = b0;
    lpf->a1 = a1;
}

/*-----------------------------------------------------------------------------
 Process delay absorbent low pass filter.
 @param mod_delay modulated delay line.
 @param in, input sample.
 @param out output sample.
-----------------------------------------------------------------------------*/
/* process low pass damping filter (input, output, delay) */
#define process_damping_filter(in,out,mod_delay) \
{\
    out = in * mod_delay->dl.damping.b0 - mod_delay->dl.damping.buffer * \
                                            mod_delay->dl.damping.a1;\
    mod_delay->dl.damping.buffer = out;\
}\


/*-----------------------------------------------------------------------------
 Delay line :
 The delay line is composed of the line plus an absorbent low pass filter
 to get frequency dependant reverb time.
-----------------------------------------------------------------------------*/
typedef struct
{
    fluid_real_t *line; /* buffer line */
    int   size;    /* effective internal size (in samples) */
    /*-------------*/
    int line_in;  /* line in position */
    int line_out; /* line out position */
    /*-------------*/
    fdn_delay_lpf damping; /* damping low pass filter */
} delay_line;


/*-----------------------------------------------------------------------------
 Clears a delay line to DC_OFFSET float value.
 @param dl pointer on delay line structure
-----------------------------------------------------------------------------*/
static void clear_delay_line(delay_line *dl)
{
    int i;

    for(i = 0; i < dl->size; i++)
    {
        dl->line[i] = DC_OFFSET;
    }
}

/*-----------------------------------------------------------------------------
 Push a sample val into the delay line
-----------------------------------------------------------------------------*/
#define push_in_delay_line(dl, val) \
{\
    dl->line[dl->line_in] = val;\
    /* Incrementation and circular motion if necessary */\
    if(++dl->line_in >= dl->size) dl->line_in -= dl->size;\
}\

/*-----------------------------------------------------------------------------
 Modulator for modulated delay line
-----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
 Sinusoidal modulator
-----------------------------------------------------------------------------*/
/* modulator are integrated in modulated delay line */
typedef struct
{
    fluid_real_t   a1;          /* Coefficient: a1 = 2 * cos(w) */
    fluid_real_t   buffer1;     /* buffer1 */
    fluid_real_t   buffer2;     /* buffer2 */
    fluid_real_t   reset_buffer2;/* reset value of buffer2 */
} sinus_modulator;

/*-----------------------------------------------------------------------------
 Sets the frequency of sinus oscillator.

 @param mod pointer on modulator structure.
 @param freq frequency of the oscillator in Hz.
 @param sample_rate sample rate on audio output in Hz.
 @param phase initial phase of the oscillator in degree (0 to 360).
-----------------------------------------------------------------------------*/
static void set_mod_frequency(sinus_modulator *mod,
                              float freq, float sample_rate, float phase)
{
    fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* intial angle */
    fluid_real_t a;

    mod->a1 = 2 * FLUID_COS(w);

    a = (2 * FLUID_M_PI / 360) * phase;

    mod->buffer2 = FLUID_SIN(a - w); /* y(n-1) = sin(-intial angle) */
    mod->buffer1 = FLUID_SIN(a); /* y(n) = sin(initial phase) */
    mod->reset_buffer2 = FLUID_SIN(FLUID_M_PI / 2 - w); /* reset value for PI/2 */
}

/*-----------------------------------------------------------------------------
 Gets current value of sinus modulator:
   y(n) = a1 . y(n-1)  -  y(n-2)
   out = a1 . buffer1  -  buffer2

 @param pointer on modulator structure.
 @return current value of the modulator sine wave.
-----------------------------------------------------------------------------*/
static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod)
{
    fluid_real_t out;
    out = mod->a1 * mod->buffer1 - mod->buffer2;
    mod->buffer2 = mod->buffer1;

    if(out >= 1.0f) /* reset in case of instability near PI/2 */
    {
        out = 1.0f; /* forces output to the right value */
        mod->buffer2 = mod->reset_buffer2;
    }

    if(out <= -1.0f) /* reset in case of instability near -PI/2 */
    {
        out = -1.0f; /* forces output to the right value */
        mod->buffer2 = - mod->reset_buffer2;
    }

    mod->buffer1 = out;
    return  out;
}

/*-----------------------------------------------------------------------------
 Modulated delay line. The line is composed of:
 - the delay line with its damping low pass filter.
 - the sinusoidal modulator.
 - center output position modulated by the modulator.
 - variable rate control of center output position.
 - first order All-Pass interpolator.
-----------------------------------------------------------------------------*/
typedef struct
{
    /* delay line with damping low pass filter member */
    delay_line dl; /* delayed line */
    /*---------------------------*/
    /* Sinusoidal modulator member */
    sinus_modulator mod; /* sinus modulator */
    /*-------------------------*/
    /* center output position members */
    fluid_real_t  center_pos_mod; /* center output position modulated by modulator */
    int          mod_depth;   /* modulation depth (in samples) */
    /*-------------------------*/
    /* variable rate control of center output position */
    int index_rate;  /* index rate to know when to update center_pos_mod */
    int mod_rate;    /* rate at which center_pos_mod is updated */
    /*-------------------------*/
    /* first order All-Pass interpolator members */
    fluid_real_t  frac_pos_mod; /* fractional position part between samples) */
    /* previous value used when interpolating using fractional */
    fluid_real_t  buffer;
} mod_delay_line;


/*-----------------------------------------------------------------------------
 Modulated delay line initialization.

 Sets the length line ( alloc delay samples).
 Remark: the function sets the internal size accordling to the length delay_length.
 As the delay line is a modulated line, its internal size is augmented by mod_depth.
 The size is also augmented by INTERP_SAMPLES_NBR to take account of interpolation.

 @param mdl, pointer on modulated delay line.
 @param delay_length the length of the delay line in samples.
 @param mod_depth depth of the modulation in samples (amplitude of the sine wave).
 @param mod_rate the rate of the modulation in samples.
 @return FLUID_OK if success , FLUID_FAILED if memory error.

 Return FLUID_OK if success, FLUID_FAILED if memory error.
-----------------------------------------------------------------------------*/
static int set_mod_delay_line(mod_delay_line *mdl,
                              int delay_length,
                              int mod_depth,
                              int mod_rate
                             )
{
    /*-----------------------------------------------------------------------*/
    /* checks parameter */
    if(delay_length < 1)
    {
        return FLUID_FAILED;
    }

    /* limits mod_depth to the requested delay length */
    if(mod_depth >= delay_length)
    {
        FLUID_LOG(FLUID_INFO,
                  "fdn reverb: modulation depth has been limited");
        mod_depth = delay_length - 1;
    }

    mdl->mod_depth = mod_depth;
    /*-----------------------------------------------------------------------
     allocates delay_line and initialize members:
       - line, size, line_in, line_out...
    */
    {
        /* total size of the line:
        size = INTERP_SAMPLES_NBR + mod_depth + delay_length */
        mdl->dl.size = delay_length + mod_depth + INTERP_SAMPLES_NBR;
        mdl->dl.line = FLUID_ARRAY(fluid_real_t, mdl->dl.size);

        if(! mdl->dl.line)
        {
            return FLUID_FAILED;
        }

        clear_delay_line(&mdl->dl); /* clears the buffer */

        /* Initializes line_in to the start of the buffer */
        mdl->dl.line_in = 0;
        /*  Initializes line_out index INTERP_SAMPLES_NBR samples after line_in */
        /*  so that the delay between line_out and line_in is:
            mod_depth + delay_length */
        mdl->dl.line_out = mdl->dl.line_in + INTERP_SAMPLES_NBR;
    }

    /* Damping low pass filter -------------------*/
    mdl->dl.damping.buffer = 0;
    /*------------------------------------------------------------------------
     Initializes modulation members:
     - modulated center position: center_pos_mod
     - index rate to know when to update center_pos_mod:index_rate
     - modulation rate (the speed at which center_pos_mod is modulated: mod_rate
     - interpolator member: buffer, frac_pos_mod
     -------------------------------------------------------------------------*/
    /* Sets the modulation rate. This rate defines how often
     the  center position (center_pos_mod ) is modulated .
     The value is expressed in samples. The default value is 1 that means that
     center_pos_mod is updated at every sample.
     For example with a value of 2, the center position position will be
     updated only one time every 2 samples only.
    */
    mdl->mod_rate = 1; /* default modulation rate: every one sample */

    if(mod_rate > mdl->dl.size)
    {
        FLUID_LOG(FLUID_INFO,
                  "fdn reverb: modulation rate is out of range");
    }
    else
    {
        mdl->mod_rate = mod_rate;
    }

    /* Initializes the modulated center position (center_pos_mod) so that:
        - the delay between line_out and center_pos_mod is mod_depth.
        - the delay between center_pos_mod and line_in is delay_length.
     */
    mdl->center_pos_mod = (fluid_real_t) INTERP_SAMPLES_NBR + mod_depth;

    /* index rate to control when to update center_pos_mod */
    /* Important: must be set to get center_pos_mod immediatly used for the
       reading of first sample (see get_mod_delay()) */
    mdl->index_rate = mdl->mod_rate;

    /* initializes 1st order All-Pass interpolator members */
    mdl->buffer = 0;       /* previous delay sample value */
    mdl->frac_pos_mod = 0; /* fractional position (between consecutives sample) */
    return FLUID_OK;
}

/*-----------------------------------------------------------------------------
 Return norminal delay length

 @param mdl, pointer on modulated delay line.
-----------------------------------------------------------------------------*/
static int get_mod_delay_line_length(mod_delay_line *mdl)
{
    return (mdl->dl.size - mdl->mod_depth - INTERP_SAMPLES_NBR);
}

/*-----------------------------------------------------------------------------
 Reads the sample value out of the modulated delay line.
 @param mdl, pointer on modulated delay line.
 @return the sample value.
-----------------------------------------------------------------------------*/
static FLUID_INLINE fluid_real_t get_mod_delay(mod_delay_line *mdl)
{
    fluid_real_t out_index;  /* new modulated index position */
    int int_out_index; /* integer part of out_index */
    fluid_real_t out; /* value to return */

    /* Checks if the modulator must be updated (every mod_rate samples). */
    /* Important: center_pos_mod must be used immediatly for the
       first sample. So, mdl->index_rate must be initialized
       to mdl->mod_rate (set_mod_delay_line())  */

    if(++mdl->index_rate >= mdl->mod_rate)
    {
        mdl->index_rate = 0;

        /* out_index = center position (center_pos_mod) + sinus waweform */
        out_index = mdl->center_pos_mod +
                    get_mod_sinus(&mdl->mod) * mdl->mod_depth;

        /* extracts integer part in int_out_index */
        if(out_index >= 0.0f)
        {
            int_out_index = (int)out_index; /* current integer part */

            /* forces read index (line_out)  with integer modulation value  */
            /* Boundary check and circular motion as needed */
            if((mdl->dl.line_out = int_out_index) >= mdl->dl.size)
            {
                mdl->dl.line_out -= mdl->dl.size;
            }
        }
        else /* negative */
        {
            int_out_index = (int)(out_index - 1); /* previous integer part */
            /* forces read index (line_out) with integer modulation value  */
            /* circular motion as needed */
            mdl->dl.line_out   = int_out_index + mdl->dl.size;
        }

        /* extracts fractionnal part. (it will be used when interpolating
          between line_out and line_out +1) and memorize it.
          Memorizing is necessary for modulation rate above 1 */
        mdl->frac_pos_mod = out_index - int_out_index;

        /* updates center position (center_pos_mod) to the next position
           specified by modulation rate */
        if((mdl->center_pos_mod += mdl->mod_rate) >= mdl->dl.size)
        {
            mdl->center_pos_mod -= mdl->dl.size;
        }
    }

    /*  First order all-pass interpolation ----------------------------------*/
    /* https://ccrma.stanford.edu/~jos/pasp/First_Order_Allpass_Interpolation.html */
    /*  begins interpolation: read current sample */
    out = mdl->dl.line[mdl->dl.line_out];

    /* updates line_out to the next sample.
       Boundary check and circular motion as needed */
    if(++mdl->dl.line_out >= mdl->dl.size)
    {
        mdl->dl.line_out -= mdl->dl.size;
    }

    /* Fractional interpolation beetween next sample (at next position) and
       previous output added to current sample.
    */
    out += mdl->frac_pos_mod * (mdl->dl.line[mdl->dl.line_out] - mdl->buffer);
    mdl->buffer = out; /* memorizes current output */
    return out;
}

/*-----------------------------------------------------------------------------
 Late structure
-----------------------------------------------------------------------------*/
struct _fluid_late
{
    fluid_real_t samplerate;       /* sample rate */
    /*----- High pass tone corrector -------------------------------------*/
    fluid_real_t tone_buffer;
    fluid_real_t b1, b2;
    /*----- Modulated delay lines lines ----------------------------------*/
    mod_delay_line mod_delay_lines[NBR_DELAYS];
    /*-----------------------------------------------------------------------*/
    /* Output coefficients for separate Left and right stereo outputs */
    fluid_real_t out_left_gain[NBR_DELAYS]; /* Left delay lines' output gains */
    fluid_real_t out_right_gain[NBR_DELAYS];/* Right delay lines' output gains*/
};

typedef struct _fluid_late   fluid_late;
/*-----------------------------------------------------------------------------
 fluidsynth reverb structure
-----------------------------------------------------------------------------*/
struct _fluid_revmodel_t
{
    /* reverb parameters */
    fluid_real_t roomsize; /* acting on reverb time */
    fluid_real_t damp; /* acting on frequency dependent reverb time */
    fluid_real_t level, wet1, wet2; /* output level */
    fluid_real_t width; /* width stereo separation */

    /* fdn reverberation structure */
    fluid_late  late;
};

/*-----------------------------------------------------------------------------
 Updates Reverb time and absorbent filters coefficients from parameters:

 @param late pointer on late structure.
 @param roomsize (0 to 1): acting on reverb time.
 @param damping (0 to 1): acting on absorbent damping filter.

 Design formulas:
 https://ccrma.stanford.edu/~jos/Reverb/First_Order_Delay_Filter_Design.html
 https://ccrma.stanford.edu/~jos/Reverb/Tonal_Correction_Filter.html
-----------------------------------------------------------------------------*/
static void update_rev_time_damping(fluid_late *late,
                                    fluid_real_t roomsize, fluid_real_t damp)
{
    int i;
    fluid_real_t sample_period = 1 / late->samplerate; /* Sampling period */
    int delay_length;               /* delay length */
    fluid_real_t dc_rev_time;       /* Reverb time at 0 Hz (in seconds) */

    fluid_real_t alpha, alpha2;

    /*--------------------------------------------
         Computes dc_rev_time and alpha
    ----------------------------------------------*/
    {
        fluid_real_t gi_tmp, ai_tmp;
#ifdef ROOMSIZE_RESPONSE_LINEAR
        /*   roomsize parameter behave linearly:
         *   - roomsize (0 to 1) controls reverb time linearly  (0.7 to 10 s).
         *   This linear response is convenient when using GUI controls.
        */
        /*-----------------------------------------
              Computes dc_rev_time
        ------------------------------------------*/
        dc_rev_time = GET_DC_REV_TIME(roomsize);
        delay_length = get_mod_delay_line_length(&late->mod_delay_lines[NBR_DELAYS - 1]);
        /* computes gi_tmp from dc_rev_time using relation E2 */
        gi_tmp = FLUID_POW(10, -3 * delay_length *
                           sample_period / dc_rev_time); /* E2 */
#else
        /*   roomsize parameters have the same response that Freeverb, that is:
         *   - roomsize (0 to 1) controls concave reverb time (0.7 to 10 s).
        */
        {
            /*-----------------------------------------
             Computes dc_rev_time
            ------------------------------------------*/
            fluid_real_t gi_min, gi_max;

            /* values gi_min et gi_max are computed using E2 for the line with
              maximum delay */
            delay_length = get_mod_delay_line_length(&late->mod_delay_lines[NBR_DELAYS - 1]);
            gi_max = FLUID_POW(10, (-3 * delay_length / MAX_DC_REV_TIME) *
                                    sample_period); /* E2 */
            gi_min = FLUID_POW(10, (-3 * delay_length / MIN_DC_REV_TIME) *
                                    sample_period); /* E2 */
            /* gi = f(roomsize, gi_max, gi_min) */
            gi_tmp = gi_min + roomsize * (gi_max - gi_min);
            /* Computes T60DC from gi using inverse of relation E2.*/
            dc_rev_time = -3 * FLUID_M_LN10 * delay_length * sample_period / FLUID_LOGF(gi_tmp);
        }
#endif /* ROOMSIZE_RESPONSE_LINEAR */
        /*--------------------------------------------
            Computes alpha
        ----------------------------------------------*/
        /* Computes alpha from damp,ai_tmp,gi_tmp using relation R */
        /* - damp (0 to 1) controls concave reverb time for fs/2 frequency (T60DC to 0) */
        ai_tmp = 1.0f * damp;

        /* Preserve the square of R */
        alpha2 = 1.f / (1.f - ai_tmp / ((20.f / 80.f) * FLUID_LOGF(gi_tmp)));

        alpha = FLUID_SQRT(alpha2); /* R */
    }

    /* updates tone corrector coefficients b1,b2 from alpha */
    {
        /*
         Beta = (1 - alpha)  / (1 + alpha)
         b1 = 1/(1-beta)
         b2 = beta * b1
        */
        fluid_real_t beta = (1 - alpha)  / (1 + alpha);
        late->b1 = 1 / (1 - beta);
        late->b2 = beta * late->b1;
        late->tone_buffer = 0.0f;
    }

    /* updates damping  coefficients of all lines (gi , ai) from dc_rev_time, alpha */
    for(i = 0; i < NBR_DELAYS; i++)
    {
        fluid_real_t gi, ai;

        /* delay length */
        delay_length = get_mod_delay_line_length(&late->mod_delay_lines[i]);

        /* iir low pass filter gain */
        gi = FLUID_POW(10, -3 * delay_length * sample_period / dc_rev_time);

        /* iir low pass filter feedback gain */
        ai = (20.f / 80.f) * FLUID_LOGF(gi) * (1.f - 1.f / alpha2);

        /* b0 = gi * (1 - ai),  a1 = - ai */
        set_fdn_delay_lpf(&late->mod_delay_lines[i].dl.damping,
                          gi * (1.f - ai), -ai);
    }
}

/*-----------------------------------------------------------------------------
 Updates stereo coefficients
 @param late pointer on late structure
 @param wet level integrated in stereo coefficients.
-----------------------------------------------------------------------------*/
static void update_stereo_coefficient(fluid_late *late, fluid_real_t wet1)
{
    int i;
    fluid_real_t wet;

    for(i = 0; i < NBR_DELAYS; i++)
    {
        /*  delay lines output gains vectors Left and Right

                           L    R
                       0 | 1    1|
                       1 |-1    1|
                       2 | 1   -1|
                       3 |-1   -1|

                       4 | 1    1|
                       5 |-1    1|
         stereo gain = 6 | 1   -1|
                       7 |-1   -1|

                       8 | 1    1|
                       9 |-1    1|
                       10| 1   -1|
                       11|-1   -1|
        */

        /* for left line: 00,  ,02,  ,04,  ,06,  ,08,  ,10,  ,12,... left_gain = +1 */
        /* for left line:   ,01,  ,03,  ,05,  ,07,  ,09,  ,11,...    left_gain = -1 */
        wet = wet1;
        if(i & 1)
        {
            wet = -wet1;
        }
        late->out_left_gain[i] = wet;

        /* for right line: 00,01,      ,04,05,     ,08,09,     ,12,13  right_gain = +1 */
        /* for right line:      ,02 ,03,     ,06,07,     ,10,11,...    right_gain = -1 */
        wet = wet1;
        if(i & 2)
        {
            wet = -wet1;
        }
        late->out_right_gain[i] = wet;
    }
}

/*-----------------------------------------------------------------------------
 fluid_late destructor.
 @param late pointer on late structure.
-----------------------------------------------------------------------------*/
static void delete_fluid_rev_late(fluid_late *late)
{
    int i;
    fluid_return_if_fail(late != NULL);

    /* free the delay lines */
    for(i = 0; i < NBR_DELAYS; i++)
    {
        FLUID_FREE(late->mod_delay_lines[i].dl.line);
    }
}

/*-----------------------------------------------------------------------------
 Creates all modulated lines.
 @param late, pointer on the fnd late reverb to initialize.
 @param sample_rate, the audio sample rate.
 @return FLUID_OK if success, FLUID_FAILED otherwise.
-----------------------------------------------------------------------------*/
static int create_mod_delay_lines(fluid_late *late, fluid_real_t sample_rate)
{
    /* Delay lines length table (in samples) */
    static const int delay_length[NBR_DELAYS] =
    {
        DELAY_L0, DELAY_L1, DELAY_L2, DELAY_L3,
        DELAY_L4, DELAY_L5, DELAY_L6, DELAY_L7,
    #if (NBR_DELAYS == 12)
        DELAY_L8, DELAY_L9, DELAY_L10, DELAY_L11
    #endif
    };

    int result; /* return value */
    int i;

    /*
      1)"modal density" is one property that contributes to the quality of the reverb tail.
        The more is the modal density, the less are unwanted resonant frequencies
        build during the decay time: modal density = total delay / sample rate.

        Delay line's length given by static table delay_length[] is nominal
        to get minimum modal density of 0.15 at sample rate 44100Hz.
        Here we set length_factor to 2 to mutiply this nominal modal
        density by 2. This leads to a default modal density of 0.15 * 2 = 0.3 for
        sample rate <= 44100.

        For sample rate > 44100, length_factor is multiplied by
        sample_rate / 44100. This ensures that the default modal density keeps inchanged.
        (Without this compensation, the default modal density would be diminished for
        new sample rate change above 44100Hz).

      2)Modulated delay line contributes to diminish resonnant frequencies (often called "ringing").
        Modulation depth (mod_depth) is set to nominal value of MOD_DEPTH at sample rate 44100Hz.
        For sample rate > 44100, mod_depth is multiplied by sample_rate / 44100. This ensures
        that the effect of modulated delay line keeps inchanged.
    */
    fluid_real_t length_factor = 2.0f;
    fluid_real_t mod_depth = MOD_DEPTH;
    if(sample_rate > 44100.0f)
    {
        fluid_real_t sample_rate_factor = sample_rate/44100.0f;
        length_factor *= sample_rate_factor;
        mod_depth *= sample_rate_factor;
    }
#ifdef INFOS_PRINT // allows message to be printed on the console.
    printf("length_factor:%f, mod_depth:%f\n", length_factor, mod_depth);
    /* Print: modal density and total memory bytes */
    {
        int i;
        int total_delay; /* total delay in samples */
        for (i = 0, total_delay = 0; i < NBR_DELAYS; i++)
        {
            total_delay += length_factor * delay_length[i];
        }

        /* modal density and total memory bytes */
        printf("modal density:%f, total memory:%d bytes\n",
                total_delay / sample_rate , total_delay * sizeof(fluid_real_t));
    }
#endif

    for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */
    {
        /* allocate delay line and set local delay lines's parameters */
        result = set_mod_delay_line(&late->mod_delay_lines[i],
                                    delay_length[i] * length_factor,
                                    mod_depth, MOD_RATE);

        if(result == FLUID_FAILED)
        {
            return FLUID_FAILED;
        }

        /* Sets local Modulators parameters: frequency and phase
         Each modulateur are shifted of MOD_PHASE degree
        */
        set_mod_frequency(&late->mod_delay_lines[i].mod,
                          MOD_FREQ * MOD_RATE,
                          late->samplerate,
                          (float)(MOD_PHASE * i));
    }
    return FLUID_OK;
}

/*-----------------------------------------------------------------------------
 Creates the fdn reverb.
 @param late, pointer on the fnd late reverb to initialize.
 @param sample_rate the sample rate.
 @return FLUID_OK if success, FLUID_FAILED otherwise.
-----------------------------------------------------------------------------*/
static int create_fluid_rev_late(fluid_late *late, fluid_real_t sample_rate)
{
    FLUID_MEMSET(late, 0,  sizeof(fluid_late));

    late->samplerate = sample_rate;

    /*--------------------------------------------------------------------------
      First initialize the modulated delay lines
    */

    if(create_mod_delay_lines(late, sample_rate) == FLUID_FAILED)
    {
        return FLUID_FAILED;
    }

    return FLUID_OK;
}

/*
 Clears the delay lines.

 @param rev pointer on the reverb.
*/
static void
fluid_revmodel_init(fluid_revmodel_t *rev)
{
    int i;

    /* clears all the delay lines */
    for(i = 0; i < NBR_DELAYS; i ++)
    {
        clear_delay_line(&rev->late.mod_delay_lines[i].dl);
    }
}


/*
 updates internal parameters.

 @param rev pointer on the reverb.
*/
static void
fluid_revmodel_update(fluid_revmodel_t *rev)
{
    /* Recalculate internal values after parameters change */

    /* The stereo amplitude equation (wet1 and wet2 below) have a
    tendency to produce high amplitude with high width values ( 1 < width < 100).
    This results in an unwanted noisy output clipped by the audio card.
    To avoid this dependency, we divide by (1 + rev->width * SCALE_WET_WIDTH)
    Actually, with a SCALE_WET_WIDTH of 0.2, (regardless of level setting),
    the output amplitude (wet) seems rather independent of width setting */
    fluid_real_t wet = (rev->level * SCALE_WET) /
                       (1.0f + rev->width * SCALE_WET_WIDTH);

    /* wet1 and wet2 are used by the stereo effect controled by the width setting
    for producing a stereo ouptput from a monophonic reverb signal.
    Please see the note above about a side effect tendency */

    rev->wet1 = wet * (rev->width / 2.0f + 0.5f);
    rev->wet2 = wet * ((1.0f - rev->width) / 2.0f);

    /* integrates wet1 in stereo coefficient (this will save one multiply) */
    update_stereo_coefficient(&rev->late, rev->wet1);

    if(rev->wet1 > 0.0f)
    {
        rev->wet2 /= rev->wet1;
    }

    /* Reverberation time and damping */
    update_rev_time_damping(&rev->late, rev->roomsize, rev->damp);
}

/*----------------------------------------------------------------------------
                            Reverb API
-----------------------------------------------------------------------------*/

/*
* Creates a reverb. One created the reverb have no parameters set, so
* fluid_revmodel_set() must be called at least one time after calling
* new_fluid_revmodel().
*
* @param sample_rate sample rate in Hz.
* @return pointer on the new reverb or NULL if memory error.
* Reverb API.
*/
fluid_revmodel_t *
new_fluid_revmodel(fluid_real_t sample_rate)
{
    fluid_revmodel_t *rev;
    rev = FLUID_NEW(fluid_revmodel_t);

    if(rev == NULL)
    {
        return NULL;
    }

    /* create fdn reverb */
    if(create_fluid_rev_late(&rev->late, sample_rate) != FLUID_OK)
    {
        delete_fluid_revmodel(rev);
        return NULL;
    }

    return rev;
}

/*
* free the reverb.
* Note that while the reverb is used by calling any fluid_revmodel_processXXX()
* function, calling delete_fluid_revmodel() isn't multi task safe because
* delay line are freed. To deal properly with this issue follow the steps:
*
* 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX().
*    reverb functions.
* 2) Delete the reverb by calling delete_fluid_revmodel().
*
* @param rev pointer on reverb to free.
* Reverb API.
*/
void
delete_fluid_revmodel(fluid_revmodel_t *rev)
{
    fluid_return_if_fail(rev != NULL);
    delete_fluid_rev_late(&rev->late);
    FLUID_FREE(rev);
}

/*
* Sets one or more reverb parameters. Note this must be called at least one
* time after calling new_fluid_revmodel().
*
* Note that while the reverb is used by calling any fluid_revmodel_processXXX()
* function, calling fluid_revmodel_set() could produce audible clics.
* If this is a problem, optionnaly call fluid_revmodel_reset() before calling
* fluid_revmodel_set().
*
* @param rev Reverb instance.
* @param set One or more flags from #fluid_revmodel_set_t indicating what
*   parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters).
* @param roomsize Reverb room size.
* @param damping Reverb damping.
* @param width Reverb width.
* @param level Reverb level.
*
* Reverb API.
*/
void
fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize,
                   fluid_real_t damping, fluid_real_t width, fluid_real_t level)
{
    /*-----------------------------------*/
    if(set & FLUID_REVMODEL_SET_ROOMSIZE)
    {
        fluid_clip(roomsize, 0.0f, 1.0f);
        rev->roomsize = roomsize;
    }

    /*-----------------------------------*/
    if(set & FLUID_REVMODEL_SET_DAMPING)
    {
        fluid_clip(damping, 0.0f, 1.0f);
        rev->damp = damping;
    }

    /*-----------------------------------*/
    if(set & FLUID_REVMODEL_SET_WIDTH)
    {
        rev->width = width;
    }

    /*-----------------------------------*/
    if(set & FLUID_REVMODEL_SET_LEVEL)
    {
        fluid_clip(level, 0.0f, 1.0f);
        rev->level = level;
    }

    /* updates internal parameters */
    fluid_revmodel_update(rev);
}

/*
* Applies a sample rate change on the reverb.
* Note that while the reverb is used by calling any fluid_revmodel_processXXX()
* function, calling fluid_revmodel_samplerate_change() isn't multi task safe because
* delay line are memory reallocated. To deal properly with this issue follow
* the steps:
* 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX().
*    reverb functions.
* 2) Change sample rate by calling fluid_revmodel_samplerate_change().
* 3) Restart reverb processing (i.e enabling calling of any fluid_revmodel_processXXX()
*    reverb functions.
*
* Another solution is to substitute step (2):
* 2.1) delete the reverb by calling delete_fluid_revmodel().
* 2.2) create the reverb by calling new_fluid_revmodel().
*
* @param rev the reverb.
* @param sample_rate new sample rate value.
* @return FLUID_OK if success, FLUID_FAILED otherwise (memory error).
* Reverb API.
*/
int
fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate)
{
    rev->late.samplerate = sample_rate; /* new sample rate value */

    /* free all delay lines */
    delete_fluid_rev_late(&rev->late);

    /* create all delay lines */
    if(create_mod_delay_lines(&rev->late, sample_rate) == FLUID_FAILED)
    {
        return FLUID_FAILED; /* memory error */
    }

    /* updates damping filter coefficients according to sample rate change */
    update_rev_time_damping(&rev->late, rev->roomsize, rev->damp);

    return FLUID_OK;
}

/*
* Damps the reverb by clearing the delay lines.
* @param rev the reverb.
*
* Reverb API.
*/
void
fluid_revmodel_reset(fluid_revmodel_t *rev)
{
    fluid_revmodel_init(rev);
}

/*-----------------------------------------------------------------------------
* fdn reverb process replace.
* @param rev pointer on reverb.
* @param in monophonic buffer input (FLUID_BUFSIZE sample).
* @param left_out stereo left processed output (FLUID_BUFSIZE sample).
* @param right_out stereo right processed output (FLUID_BUFSIZE sample).
*
* The processed reverb is replacing anything there in out.
* Reverb API.
-----------------------------------------------------------------------------*/
void
fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in,
                              fluid_real_t *left_out, fluid_real_t *right_out)
{
    int i, k;

    fluid_real_t xn;                   /* mono input x(n) */
    fluid_real_t out_tone_filter;      /* tone corrector output */
    fluid_real_t out_left, out_right;  /* output stereo Left  and Right  */
    fluid_real_t matrix_factor;        /* partial matrix computation */
    fluid_real_t delay_out_s;          /* sample */
    fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */

    for(k = 0; k < FLUID_BUFSIZE; k++)
    {
        /* stereo output */
        out_left = out_right = 0;

#ifdef DENORMALISING
        /* Input is adjusted by DC_OFFSET. */
        xn = (in[k]) * FIXED_GAIN + DC_OFFSET;
#else
        xn = (in[k]) * FIXED_GAIN;
#endif

        /*--------------------------------------------------------------------
         tone correction.
        */
        out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer;
        rev->late.tone_buffer = xn;
        xn = out_tone_filter;
        /*--------------------------------------------------------------------
         process  feedback delayed network:
          - xn is the input signal.
          - before inserting in the line input we first we get the delay lines
            output, filter them and compute output in delay_out[].
          - also matrix_factor is computed (to simplify further matrix product)
        ---------------------------------------------------------------------*/
        /* We begin with the modulated output delay line + damping filter */
        matrix_factor = 0;

        for(i = 0; i < NBR_DELAYS; i++)
        {
            mod_delay_line *mdl = &rev->late.mod_delay_lines[i];
            /* get current modulated output */
            delay_out_s = get_mod_delay(mdl);

            /* process low pass damping filter
              (input:delay_out_s, output:delay_out_s) */
            process_damping_filter(delay_out_s, delay_out_s, mdl);

            /* Result in delay_out[], and matrix_factor.
               These wil be use later during input line process */
            delay_out[i] = delay_out_s;   /* result in delay_out[] */
            matrix_factor += delay_out_s; /* result in matrix_factor */

            /* Process stereo output */
            /* stereo left = left + out_left_gain * delay_out */
            out_left += rev->late.out_left_gain[i] * delay_out_s;
            /* stereo right= right+ out_right_gain * delay_out */
            out_right += rev->late.out_right_gain[i] * delay_out_s;
        }

        /* now we process the input delay line.Each input is a combination of
           - xn: input signal
           - delay_out[] the output of a delay line given by a permutation matrix P
           - and matrix_factor.
          This computes: in_delay_line = xn + (delay_out[] * matrix A) with
          an algorithm equivalent but faster than using a product with matrix A.
        */
        /* matrix_factor = output sum * (-2.0)/N  */
        matrix_factor *= FDN_MATRIX_FACTOR;
        matrix_factor += xn; /* adds reverb input signal */

        for(i = 1; i < NBR_DELAYS; i++)
        {
            /* delay_in[i-1] = delay_out[i] + matrix_factor */
            delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl;
            push_in_delay_line(dl, delay_out[i] + matrix_factor);
        }

        /* last line input (NB_DELAY-1) */
        /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */
        {
            delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl;
            push_in_delay_line(dl, delay_out[0] + matrix_factor);
        }

        /*-------------------------------------------------------------------*/
#ifdef DENORMALISING
        /* Removes the DC offset */
        out_left -= DC_OFFSET;
        out_right -= DC_OFFSET;
#endif

        /* Calculates stereo output REPLACING anything already there: */
        /*
            left_out[k]  = out_left * rev->wet1 + out_right * rev->wet2;
            right_out[k] = out_right * rev->wet1 + out_left * rev->wet2;

            As wet1 is integrated in stereo coefficient wet 1 is now
            integrated in out_left and out_right we simplify previous
            relation by suppression of one multiply as this:

            left_out[k]  = out_left  + out_right * rev->wet2;
            right_out[k] = out_right + out_left * rev->wet2;
        */
        left_out[k]  = out_left  + out_right * rev->wet2;
        right_out[k] = out_right + out_left * rev->wet2;
    }
}


/*-----------------------------------------------------------------------------
* fdn reverb process mix.
* @param rev pointer on reverb.
* @param in monophonic buffer input (FLUID_BUFSIZE samples).
* @param left_out stereo left processed output (FLUID_BUFSIZE samples).
* @param right_out stereo right processed output (FLUID_BUFSIZE samples).
*
* The processed reverb is mixed in out with samples already there in out.
* Reverb API.
-----------------------------------------------------------------------------*/
void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in,
                               fluid_real_t *left_out, fluid_real_t *right_out)
{
    int i, k;

    fluid_real_t xn;                   /* mono input x(n) */
    fluid_real_t out_tone_filter;      /* tone corrector output */
    fluid_real_t out_left, out_right;  /* output stereo Left  and Right  */
    fluid_real_t matrix_factor;        /* partial matrix term */
    fluid_real_t delay_out_s;          /* sample */
    fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */

    for(k = 0; k < FLUID_BUFSIZE; k++)
    {
        /* stereo output */
        out_left = out_right = 0;
#ifdef DENORMALISING
        /* Input is adjusted by DC_OFFSET. */
        xn = (in[k]) * FIXED_GAIN + DC_OFFSET;
#else
        xn = (in[k]) * FIXED_GAIN;
#endif

        /*--------------------------------------------------------------------
         tone correction
        */
        out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer;
        rev->late.tone_buffer = xn;
        xn = out_tone_filter;
        /*--------------------------------------------------------------------
         process feedback delayed network:
          - xn is the input signal.
          - before inserting in the line input we first we get the delay lines
            output, filter them and compute output in local delay_out[].
          - also matrix_factor is computed (to simplify further matrix product).
        ---------------------------------------------------------------------*/
        /* We begin with the modulated output delay line + damping filter */
        matrix_factor = 0;

        for(i = 0; i < NBR_DELAYS; i++)
        {
            mod_delay_line *mdl = &rev->late.mod_delay_lines[i];
            /* get current modulated output */
            delay_out_s = get_mod_delay(mdl);

            /* process low pass damping filter
              (input:delay_out_s, output:delay_out_s) */
            process_damping_filter(delay_out_s, delay_out_s, mdl);

            /* Result in delay_out[], and matrix_factor.
               These wil be use later during input line process */
            delay_out[i] = delay_out_s;   /* result in delay_out[] */
            matrix_factor += delay_out_s; /* result in matrix_factor */

            /* Process stereo output */
            /* stereo left = left + out_left_gain * delay_out */
            out_left += rev->late.out_left_gain[i] * delay_out_s;
            /* stereo right= right+ out_right_gain * delay_out */
            out_right += rev->late.out_right_gain[i] * delay_out_s;
        }

        /* now we process the input delay line. Each input is a combination of:
           - xn: input signal
           - delay_out[] the output of a delay line given by a permutation matrix P
           - and matrix_factor.
          This computes: in_delay_line = xn + (delay_out[] * matrix A) with
          an algorithm equivalent but faster than using a product with matrix A.
        */
        /* matrix_factor = output sum * (-2.0)/N  */
        matrix_factor *= FDN_MATRIX_FACTOR;
        matrix_factor += xn; /* adds reverb input signal */

        for(i = 1; i < NBR_DELAYS; i++)
        {
            /* delay_in[i-1] = delay_out[i] + matrix_factor */
            delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl;
            push_in_delay_line(dl, delay_out[i] + matrix_factor);
        }

        /* last line input (NB_DELAY-1) */
        /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */
        {
            delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl;
            push_in_delay_line(dl, delay_out[0] + matrix_factor);
        }

        /*-------------------------------------------------------------------*/
#ifdef DENORMALISING
        /* Removes the DC offset */
        out_left -= DC_OFFSET;
        out_right -= DC_OFFSET;
#endif
        /* Calculates stereo output MIXING anything already there: */
        /*
            left_out[k]  += out_left * rev->wet1 + out_right * rev->wet2;
            right_out[k] += out_right * rev->wet1 + out_left * rev->wet2;

            As wet1 is integrated in stereo coefficient wet 1 is now
            integrated in out_left and out_right we simplify previous
            relation by suppression of one multiply as this:

            left_out[k]  += out_left  + out_right * rev->wet2;
            right_out[k] += out_right + out_left * rev->wet2;
        */
        left_out[k]  += out_left  + out_right * rev->wet2;
        right_out[k] += out_right + out_left * rev->wet2;
    }
}