Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/faudio/src/FAudioFX_reverb.c
4389 views
1
/* FAudio - XAudio Reimplementation for FNA
2
*
3
* Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team
4
*
5
* This software is provided 'as-is', without any express or implied warranty.
6
* In no event will the authors be held liable for any damages arising from
7
* the use of this software.
8
*
9
* Permission is granted to anyone to use this software for any purpose,
10
* including commercial applications, and to alter it and redistribute it
11
* freely, subject to the following restrictions:
12
*
13
* 1. The origin of this software must not be misrepresented; you must not
14
* claim that you wrote the original software. If you use this software in a
15
* product, an acknowledgment in the product documentation would be
16
* appreciated but is not required.
17
*
18
* 2. Altered source versions must be plainly marked as such, and must not be
19
* misrepresented as being the original software.
20
*
21
* 3. This notice may not be removed or altered from any source distribution.
22
*
23
* Ethan "flibitijibibo" Lee <[email protected]>
24
*
25
*/
26
27
#include "FAudioFX.h"
28
#include "FAudio_internal.h"
29
30
/* #define DISABLE_SUBNORMALS */
31
#ifdef DISABLE_SUBNORMALS
32
#include <math.h> /* ONLY USE THIS FOR fpclassify/_fpclass! */
33
34
/* VS2010 doesn't define fpclassify (which is C99), so here it is. */
35
#if defined(_MSC_VER) && !defined(fpclassify)
36
#define IS_SUBNORMAL(a) (_fpclass(a) & (_FPCLASS_ND | _FPCLASS_PD))
37
#else
38
#define IS_SUBNORMAL(a) (fpclassify(a) == FP_SUBNORMAL)
39
#endif
40
#endif /* DISABLE_SUBNORMALS */
41
42
/* Utility Functions */
43
44
static inline float DbGainToFactor(float gain)
45
{
46
return (float) FAudio_pow(10, gain / 20.0f);
47
}
48
49
static inline uint32_t MsToSamples(float msec, int32_t sampleRate)
50
{
51
return (uint32_t) ((sampleRate * msec) / 1000.0f);
52
}
53
54
#ifndef DISABLE_SUBNORMALS
55
#define Undenormalize(a) ((a))
56
#else /* DISABLE_SUBNORMALS */
57
static inline float Undenormalize(float sample_in)
58
{
59
if (IS_SUBNORMAL(sample_in))
60
{
61
return 0.0f;
62
}
63
return sample_in;
64
}
65
#endif /* DISABLE_SUBNORMALS */
66
67
/* Component - Delay */
68
69
#define DSP_DELAY_MAX_DELAY_MS 300
70
71
typedef struct DspDelay
72
{
73
int32_t sampleRate;
74
uint32_t capacity; /* In samples */
75
uint32_t delay; /* In samples */
76
uint32_t read_idx;
77
uint32_t write_idx;
78
float *buffer;
79
} DspDelay;
80
81
static inline void DspDelay_Initialize(
82
DspDelay *filter,
83
int32_t sampleRate,
84
float delay_ms,
85
FAudioMallocFunc pMalloc
86
) {
87
FAudio_assert(delay_ms >= 0 && delay_ms <= DSP_DELAY_MAX_DELAY_MS);
88
89
filter->sampleRate = sampleRate;
90
filter->capacity = MsToSamples(DSP_DELAY_MAX_DELAY_MS, sampleRate);
91
filter->delay = MsToSamples(delay_ms, sampleRate);
92
filter->read_idx = 0;
93
filter->write_idx = filter->delay;
94
filter->buffer = (float*) pMalloc(filter->capacity * sizeof(float));
95
FAudio_zero(filter->buffer, filter->capacity * sizeof(float));
96
}
97
98
static inline void DspDelay_Change(DspDelay *filter, float delay_ms)
99
{
100
FAudio_assert(delay_ms >= 0 && delay_ms <= DSP_DELAY_MAX_DELAY_MS);
101
102
/* Length */
103
filter->delay = MsToSamples(delay_ms, filter->sampleRate);
104
filter->read_idx = (filter->write_idx - filter->delay + filter->capacity) % filter->capacity;
105
}
106
107
static inline float DspDelay_Read(DspDelay *filter)
108
{
109
float delay_out;
110
111
FAudio_assert(filter->read_idx < filter->capacity);
112
113
delay_out = filter->buffer[filter->read_idx];
114
filter->read_idx = (filter->read_idx + 1) % filter->capacity;
115
return delay_out;
116
}
117
118
static inline void DspDelay_Write(DspDelay *filter, float sample)
119
{
120
FAudio_assert(filter->write_idx < filter->capacity);
121
122
filter->buffer[filter->write_idx] = sample;
123
filter->write_idx = (filter->write_idx + 1) % filter->capacity;
124
}
125
126
static inline float DspDelay_Process(DspDelay *filter, float sample_in)
127
{
128
float delay_out = DspDelay_Read(filter);
129
DspDelay_Write(filter, sample_in);
130
return delay_out;
131
}
132
133
/* FIXME: This is currently unused! What was it for...? -flibit
134
static inline float DspDelay_Tap(DspDelay *filter, uint32_t delay)
135
{
136
FAudio_assert(delay <= filter->delay);
137
return filter->buffer[(filter->write_idx - delay + filter->capacity) % filter->capacity];
138
}
139
*/
140
141
static inline void DspDelay_Reset(DspDelay *filter)
142
{
143
filter->read_idx = 0;
144
filter->write_idx = filter->delay;
145
FAudio_zero(filter->buffer, filter->capacity * sizeof(float));
146
}
147
148
static inline void DspDelay_Destroy(DspDelay *filter, FAudioFreeFunc pFree)
149
{
150
pFree(filter->buffer);
151
}
152
153
static inline float DspComb_FeedbackFromRT60(DspDelay *delay, float rt60_ms)
154
{
155
float exponent = (
156
(-3.0f * delay->delay * 1000.0f) /
157
(delay->sampleRate * rt60_ms)
158
);
159
return (float) FAudio_pow(10.0f, exponent);
160
}
161
162
/* Component - Bi-Quad Filter */
163
164
typedef enum DspBiQuadType
165
{
166
DSP_BIQUAD_LOWSHELVING,
167
DSP_BIQUAD_HIGHSHELVING
168
} DspBiQuadType;
169
170
typedef struct DspBiQuad
171
{
172
int32_t sampleRate;
173
float a0, a1, a2;
174
float b1, b2;
175
float c0, d0;
176
float delay0, delay1;
177
} DspBiQuad;
178
179
static inline void DspBiQuad_Change(
180
DspBiQuad *filter,
181
DspBiQuadType type,
182
float frequency,
183
float q,
184
float gain
185
) {
186
const float TWOPI = (float)6.283185307179586476925286766559005;
187
float theta_c = (TWOPI * frequency) / (float) filter->sampleRate;
188
float mu = DbGainToFactor(gain);
189
float beta = (type == DSP_BIQUAD_LOWSHELVING) ?
190
4.0f / (1 + mu) :
191
(1 + mu) / 4.0f;
192
float delta = beta * (float) FAudio_tan(theta_c * 0.5f);
193
float gamma = (1 - delta) / (1 + delta);
194
195
if (type == DSP_BIQUAD_LOWSHELVING)
196
{
197
filter->a0 = (1 - gamma) * 0.5f;
198
filter->a1 = filter->a0;
199
}
200
else
201
{
202
filter->a0 = (1 + gamma) * 0.5f;
203
filter->a1 = -filter->a0;
204
}
205
206
filter->a2 = 0.0f;
207
filter->b1 = -gamma;
208
filter->b2 = 0.0f;
209
filter->c0 = mu - 1.0f;
210
filter->d0 = 1.0f;
211
}
212
213
static inline void DspBiQuad_Initialize(
214
DspBiQuad *filter,
215
int32_t sampleRate,
216
DspBiQuadType type,
217
float frequency, /* Corner frequency */
218
float q, /* Only used by low/high-pass filters */
219
float gain /* Only used by low/high-shelving filters */
220
) {
221
filter->sampleRate = sampleRate;
222
filter->delay0 = 0.0f;
223
filter->delay1 = 0.0f;
224
DspBiQuad_Change(filter, type, frequency, q, gain);
225
}
226
227
static inline float DspBiQuad_Process(DspBiQuad *filter, float sample_in)
228
{
229
/* Direct Form II Transposed:
230
* - Less delay registers than Direct Form I
231
* - More numerically stable than Direct Form II
232
*/
233
float result = (filter->a0 * sample_in) + filter->delay0;
234
filter->delay0 = (filter->a1 * sample_in) - (filter->b1 * result) + filter->delay1;
235
filter->delay1 = (filter->a2 * sample_in) - (filter->b2 * result);
236
237
return Undenormalize(
238
(result * filter->c0) +
239
(sample_in * filter->d0)
240
);
241
}
242
243
static inline void DspBiQuad_Reset(DspBiQuad *filter)
244
{
245
filter->delay0 = 0.0f;
246
filter->delay1 = 0.0f;
247
}
248
249
static inline void DspBiQuad_Destroy(DspBiQuad *filter)
250
{
251
}
252
253
/* Component - Comb Filter with Integrated Low/High Shelving Filters */
254
255
typedef struct DspCombShelving
256
{
257
DspDelay comb_delay;
258
float comb_feedback_gain;
259
260
DspBiQuad low_shelving;
261
DspBiQuad high_shelving;
262
} DspCombShelving;
263
264
static inline void DspCombShelving_Initialize(
265
DspCombShelving *filter,
266
int32_t sampleRate,
267
float delay_ms,
268
float rt60_ms,
269
float low_frequency,
270
float low_gain,
271
float high_frequency,
272
float high_gain,
273
FAudioMallocFunc pMalloc
274
) {
275
DspDelay_Initialize(&filter->comb_delay, sampleRate, delay_ms, pMalloc);
276
filter->comb_feedback_gain = DspComb_FeedbackFromRT60(
277
&filter->comb_delay,
278
rt60_ms
279
);
280
281
DspBiQuad_Initialize(
282
&filter->low_shelving,
283
sampleRate,
284
DSP_BIQUAD_LOWSHELVING,
285
low_frequency,
286
0.0f,
287
low_gain
288
);
289
DspBiQuad_Initialize(
290
&filter->high_shelving,
291
sampleRate,
292
DSP_BIQUAD_HIGHSHELVING,
293
high_frequency,
294
0.0f,
295
high_gain
296
);
297
}
298
299
static inline float DspCombShelving_Process(
300
DspCombShelving *filter,
301
float sample_in
302
) {
303
float delay_out, feedback, to_buf;
304
305
delay_out = DspDelay_Read(&filter->comb_delay);
306
307
/* Apply shelving filters */
308
feedback = DspBiQuad_Process(&filter->high_shelving, delay_out);
309
feedback = DspBiQuad_Process(&filter->low_shelving, feedback);
310
311
/* Apply comb filter */
312
to_buf = Undenormalize(sample_in + (filter->comb_feedback_gain * feedback));
313
DspDelay_Write(&filter->comb_delay, to_buf);
314
315
return delay_out;
316
}
317
318
static inline void DspCombShelving_Reset(DspCombShelving *filter)
319
{
320
DspDelay_Reset(&filter->comb_delay);
321
DspBiQuad_Reset(&filter->low_shelving);
322
DspBiQuad_Reset(&filter->high_shelving);
323
}
324
325
static inline void DspCombShelving_Destroy(
326
DspCombShelving *filter,
327
FAudioFreeFunc pFree
328
) {
329
DspDelay_Destroy(&filter->comb_delay, pFree);
330
DspBiQuad_Destroy(&filter->low_shelving);
331
DspBiQuad_Destroy(&filter->high_shelving);
332
}
333
334
/* Component - Delaying All-Pass Filter */
335
336
typedef struct DspAllPass
337
{
338
DspDelay delay;
339
float feedback_gain;
340
} DspAllPass;
341
342
static inline void DspAllPass_Initialize(
343
DspAllPass *filter,
344
int32_t sampleRate,
345
float delay_ms,
346
float gain,
347
FAudioMallocFunc pMalloc
348
) {
349
DspDelay_Initialize(&filter->delay, sampleRate, delay_ms, pMalloc);
350
filter->feedback_gain = gain;
351
}
352
353
static inline void DspAllPass_Change(DspAllPass *filter, float delay_ms, float gain)
354
{
355
DspDelay_Change(&filter->delay, delay_ms);
356
filter->feedback_gain = gain;
357
}
358
359
static inline float DspAllPass_Process(DspAllPass *filter, float sample_in)
360
{
361
float delay_out, to_buf;
362
363
delay_out = DspDelay_Read(&filter->delay);
364
365
to_buf = Undenormalize(sample_in + (filter->feedback_gain * delay_out));
366
DspDelay_Write(&filter->delay, to_buf);
367
368
return Undenormalize(delay_out - (filter->feedback_gain * to_buf));
369
}
370
371
static inline void DspAllPass_Reset(DspAllPass *filter)
372
{
373
DspDelay_Reset(&filter->delay);
374
}
375
376
static inline void DspAllPass_Destroy(DspAllPass *filter, FAudioFreeFunc pFree)
377
{
378
DspDelay_Destroy(&filter->delay, pFree);
379
}
380
381
/*
382
Reverb network - loosely based on the reverberator from
383
"Designing Audio Effect Plug-Ins in C++" by Will Pirkle and
384
the classic classic Schroeder-Moorer reverberator with modifications
385
to fit the XAudio2FX parameters.
386
387
388
In +--------+ +----+ +------------+ +-----+
389
----|--->PreDelay---->APF1---+--->Sub LeftCh |----->| | Left Out
390
| +--------+ +----+ | +------------+ | Wet |-------->
391
| | +------------+ | |
392
| |---|Sub RightCh |----->| Dry |
393
| +------------+ | | Right Out
394
| | Mix |-------->
395
+----------------------------------------------->| |
396
+-----+
397
Sub routine per channel :
398
399
In +-----+ +-----+ * cg
400
---+->|Delay|--+---|Comb1|------+
401
| +-----+ | +-----+ |
402
| | |
403
| | +-----+ * cg |
404
| +--->Comb2|------+
405
| | +-----+ | +-----+
406
| | +---->| SUM |--------+
407
| | +-----+ * cg | +-----+ |
408
| +--->... |------+ |
409
| * g0 | +-----+ | |
410
| | | |
411
| +--->-----+ * cg | |
412
| |Comb8|------+ |
413
| +-----+ |
414
v |
415
+-----+ g1 +----+ +----+ +----+ +----+ |
416
| SUM |<------|APF4|<--|APF3|<--|APF2|<--|APF1|<-----+
417
+-----+ +----+ +----+ +----+ +----+
418
|
419
|
420
| +-------------+ Out
421
+----------->|RoomFilter |------------------------>
422
+-------------+
423
424
425
Parameters:
426
427
float WetDryMix; 0 - 100 (0 = fully dry, 100 = fully wet)
428
uint32_t ReflectionsDelay; 0 - 300 ms
429
uint8_t ReverbDelay; 0 - 85 ms
430
uint8_t RearDelay; 0 - 5 ms
431
uint8_t PositionLeft; 0 - 30
432
uint8_t PositionRight; 0 - 30
433
uint8_t PositionMatrixLeft; 0 - 30
434
uint8_t PositionMatrixRight; 0 - 30
435
uint8_t EarlyDiffusion; 0 - 15
436
uint8_t LateDiffusion; 0 - 15
437
uint8_t LowEQGain; 0 - 12 (formula dB = LowEQGain - 8)
438
uint8_t LowEQCutoff; 0 - 9 (formula Hz = 50 + (LowEQCutoff * 50))
439
uint8_t HighEQGain; 0 - 8 (formula dB = HighEQGain - 8)
440
uint8_t HighEQCutoff; 0 - 14 (formula Hz = 1000 + (HighEqCutoff * 500))
441
float RoomFilterFreq; 20 - 20000Hz
442
float RoomFilterMain; -100 - 0dB
443
float RoomFilterHF; -100 - 0dB
444
float ReflectionsGain; -100 - 20dB
445
float ReverbGain; -100 - 20dB
446
float DecayTime; 0.1 - .... ms
447
float Density; 0 - 100 %
448
float RoomSize; 1 - 100 feet (NOT USED YET)
449
450
*/
451
452
#define REVERB_COUNT_COMB 8
453
#define REVERB_COUNT_APF_IN 1
454
#define REVERB_COUNT_APF_OUT 4
455
456
static float COMB_DELAYS[REVERB_COUNT_COMB] =
457
{
458
25.31f,
459
26.94f,
460
28.96f,
461
30.75f,
462
32.24f,
463
33.80f,
464
35.31f,
465
36.67f
466
};
467
468
static float APF_IN_DELAYS[REVERB_COUNT_APF_IN] =
469
{
470
13.28f,
471
/* 28.13f */
472
};
473
474
static float APF_OUT_DELAYS[REVERB_COUNT_APF_OUT] =
475
{
476
5.10f,
477
12.61f,
478
10.0f,
479
7.73f
480
};
481
482
typedef enum FAudio_ChannelPositionFlags
483
{
484
Position_Left = 0x1,
485
Position_Right = 0x2,
486
Position_Center = 0x4,
487
Position_Rear = 0x8,
488
} FAudio_ChannelPositionFlags;
489
490
static FAudio_ChannelPositionFlags FAudio_GetChannelPositionFlags(int32_t total_channels, int32_t channel)
491
{
492
switch (total_channels)
493
{
494
case 1:
495
return Position_Center;
496
497
case 2:
498
return (channel == 0) ? Position_Left : Position_Right;
499
500
case 4:
501
switch (channel)
502
{
503
case 0:
504
return Position_Left;
505
case 1:
506
return Position_Right;
507
case 2:
508
return Position_Left | Position_Rear;
509
case 3:
510
return Position_Right | Position_Rear;
511
}
512
FAudio_assert(0 && "Unsupported channel count");
513
break;
514
case 5:
515
switch (channel)
516
{
517
case 0:
518
return Position_Left;
519
case 1:
520
return Position_Right;
521
case 2:
522
return Position_Center;
523
case 3:
524
return Position_Left | Position_Rear;
525
case 4:
526
return Position_Right | Position_Rear;
527
}
528
FAudio_assert(0 && "Unsupported channel count");
529
break;
530
default:
531
FAudio_assert(0 && "Unsupported channel count");
532
break;
533
}
534
535
/* shouldn't happen, but default to left speaker */
536
return Position_Left;
537
}
538
539
float FAudio_GetStereoSpreadDelayMS(int32_t total_channels, int32_t channel)
540
{
541
FAudio_ChannelPositionFlags flags = FAudio_GetChannelPositionFlags(total_channels, channel);
542
return (flags & Position_Right) ? 0.5216f : 0.0f;
543
}
544
545
typedef struct DspReverbChannel
546
{
547
DspDelay reverb_delay;
548
DspCombShelving lpf_comb[REVERB_COUNT_COMB];
549
DspAllPass apf_out[REVERB_COUNT_APF_OUT];
550
DspBiQuad room_high_shelf;
551
float early_gain;
552
float gain;
553
} DspReverbChannel;
554
555
typedef struct DspReverb
556
{
557
DspDelay early_delay;
558
DspAllPass apf_in[REVERB_COUNT_APF_IN];
559
560
int32_t in_channels;
561
int32_t out_channels;
562
int32_t reverb_channels;
563
DspReverbChannel channel[5];
564
565
float early_gain;
566
float reverb_gain;
567
float room_gain;
568
float wet_ratio;
569
float dry_ratio;
570
} DspReverb;
571
572
static inline void DspReverb_Create(
573
DspReverb *reverb,
574
int32_t sampleRate,
575
int32_t in_channels,
576
int32_t out_channels,
577
FAudioMallocFunc pMalloc
578
) {
579
int32_t i, c;
580
581
FAudio_assert(in_channels == 1 || in_channels == 2 || in_channels == 6);
582
FAudio_assert(out_channels == 1 || out_channels == 2 || out_channels == 6);
583
584
FAudio_zero(reverb, sizeof(DspReverb));
585
DspDelay_Initialize(&reverb->early_delay, sampleRate, 10, pMalloc);
586
587
for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
588
{
589
DspAllPass_Initialize(
590
&reverb->apf_in[i],
591
sampleRate,
592
APF_IN_DELAYS[i],
593
0.5f,
594
pMalloc
595
);
596
}
597
598
if (out_channels == 6)
599
{
600
reverb->reverb_channels = (in_channels == 6) ? 5 : 4;
601
}
602
else
603
{
604
reverb->reverb_channels = out_channels;
605
}
606
607
for (c = 0; c < reverb->reverb_channels; c += 1)
608
{
609
DspDelay_Initialize(
610
&reverb->channel[c].reverb_delay,
611
sampleRate,
612
10,
613
pMalloc
614
);
615
616
for (i = 0; i < REVERB_COUNT_COMB; i += 1)
617
{
618
DspCombShelving_Initialize(
619
&reverb->channel[c].lpf_comb[i],
620
sampleRate,
621
COMB_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c),
622
500,
623
500,
624
-6,
625
5000,
626
-6,
627
pMalloc
628
);
629
}
630
631
for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
632
{
633
DspAllPass_Initialize(
634
&reverb->channel[c].apf_out[i],
635
sampleRate,
636
APF_OUT_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c),
637
0.5f,
638
pMalloc
639
);
640
}
641
642
DspBiQuad_Initialize(
643
&reverb->channel[c].room_high_shelf,
644
sampleRate,
645
DSP_BIQUAD_HIGHSHELVING,
646
5000,
647
0,
648
-10
649
);
650
reverb->channel[c].gain = 1.0f;
651
}
652
653
reverb->early_gain = 1.0f;
654
reverb->reverb_gain = 1.0f;
655
reverb->dry_ratio = 0.0f;
656
reverb->wet_ratio = 1.0f;
657
reverb->in_channels = in_channels;
658
reverb->out_channels = out_channels;
659
}
660
661
static inline void DspReverb_Destroy(DspReverb *reverb, FAudioFreeFunc pFree)
662
{
663
int32_t i, c;
664
665
DspDelay_Destroy(&reverb->early_delay, pFree);
666
667
for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
668
{
669
DspAllPass_Destroy(&reverb->apf_in[i], pFree);
670
}
671
672
for (c = 0; c < reverb->reverb_channels; c += 1)
673
{
674
DspDelay_Destroy(&reverb->channel[c].reverb_delay, pFree);
675
676
for (i = 0; i < REVERB_COUNT_COMB; i += 1)
677
{
678
DspCombShelving_Destroy(
679
&reverb->channel[c].lpf_comb[i],
680
pFree
681
);
682
}
683
684
DspBiQuad_Destroy(&reverb->channel[c].room_high_shelf);
685
686
for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
687
{
688
DspAllPass_Destroy(
689
&reverb->channel[c].apf_out[i],
690
pFree
691
);
692
}
693
}
694
}
695
696
static inline void DspReverb_SetParameters(
697
DspReverb *reverb,
698
FAudioFXReverbParameters *params
699
) {
700
float early_diffusion, late_diffusion;
701
DspCombShelving *comb;
702
int32_t i, c;
703
704
/* Pre-Delay */
705
DspDelay_Change(&reverb->early_delay, (float) params->ReflectionsDelay);
706
707
/* Early Reflections - Diffusion */
708
early_diffusion = 0.6f - ((params->EarlyDiffusion / 15.0f) * 0.2f);
709
710
for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
711
{
712
DspAllPass_Change(
713
&reverb->apf_in[i],
714
APF_IN_DELAYS[i],
715
early_diffusion
716
);
717
}
718
719
/* Reverberation */
720
for (c = 0; c < reverb->reverb_channels; c += 1)
721
{
722
float channel_delay =
723
(FAudio_GetChannelPositionFlags(reverb->reverb_channels, c) & Position_Rear) ?
724
params->RearDelay :
725
0.0f;
726
727
DspDelay_Change(
728
&reverb->channel[c].reverb_delay,
729
(float) params->ReverbDelay + channel_delay
730
);
731
732
for (i = 0; i < REVERB_COUNT_COMB; i += 1)
733
{
734
comb = &reverb->channel[c].lpf_comb[i];
735
736
/* Set decay time of comb filter */
737
DspDelay_Change(
738
&comb->comb_delay,
739
COMB_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c)
740
);
741
comb->comb_feedback_gain = DspComb_FeedbackFromRT60(
742
&comb->comb_delay,
743
FAudio_max(params->DecayTime, FAUDIOFX_REVERB_MIN_DECAY_TIME) * 1000.0f
744
);
745
746
/* High/Low shelving */
747
DspBiQuad_Change(
748
&comb->low_shelving,
749
DSP_BIQUAD_LOWSHELVING,
750
50.0f + params->LowEQCutoff * 50.0f,
751
0.0f,
752
params->LowEQGain - 8.0f
753
);
754
DspBiQuad_Change(
755
&comb->high_shelving,
756
DSP_BIQUAD_HIGHSHELVING,
757
1000 + params->HighEQCutoff * 500.0f,
758
0.0f,
759
params->HighEQGain - 8.0f
760
);
761
}
762
}
763
764
/* Gain */
765
reverb->early_gain = DbGainToFactor(params->ReflectionsGain);
766
reverb->reverb_gain = DbGainToFactor(params->ReverbGain);
767
reverb->room_gain = DbGainToFactor(params->RoomFilterMain);
768
769
/* Late Diffusion */
770
late_diffusion = 0.6f - ((params->LateDiffusion / 15.0f) * 0.2f);
771
772
for (c = 0; c < reverb->reverb_channels; c += 1)
773
{
774
FAudio_ChannelPositionFlags position = FAudio_GetChannelPositionFlags(reverb->reverb_channels, c);
775
float gain;
776
777
for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
778
{
779
DspAllPass_Change(
780
&reverb->channel[c].apf_out[i],
781
APF_OUT_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c),
782
late_diffusion
783
);
784
}
785
786
DspBiQuad_Change(
787
&reverb->channel[c].room_high_shelf,
788
DSP_BIQUAD_HIGHSHELVING,
789
params->RoomFilterFreq,
790
0.0f,
791
params->RoomFilterMain + params->RoomFilterHF
792
);
793
794
if (position & Position_Left)
795
{
796
gain = params->PositionMatrixLeft;
797
}
798
else if (position & Position_Right)
799
{
800
gain = params->PositionMatrixRight;
801
}
802
else /*if (position & Position_Center) */
803
{
804
gain = (params->PositionMatrixLeft + params->PositionMatrixRight) / 2.0f;
805
}
806
reverb->channel[c].gain = 1.5f - (gain / 27.0f) * 0.5f;
807
808
if (position & Position_Rear)
809
{
810
/* Rear-channel Attenuation */
811
reverb->channel[c].gain *= 0.75f;
812
}
813
814
if (position & Position_Left)
815
{
816
gain = params->PositionLeft;
817
}
818
else if (position & Position_Right)
819
{
820
gain = params->PositionRight;
821
}
822
else /*if (position & Position_Center) */
823
{
824
gain = (params->PositionLeft + params->PositionRight) / 2.0f;
825
}
826
reverb->channel[c].early_gain = 1.2f - (gain / 6.0f) * 0.2f;
827
reverb->channel[c].early_gain = (
828
reverb->channel[c].early_gain *
829
reverb->early_gain
830
);
831
}
832
833
/* Wet/Dry Mix (100 = fully wet, 0 = fully dry) */
834
reverb->wet_ratio = params->WetDryMix / 100.0f;
835
reverb->dry_ratio = 1.0f - reverb->wet_ratio;
836
}
837
838
static inline void DspReverb_SetParameters9(
839
DspReverb *reverb,
840
FAudioFXReverbParameters9 *params
841
) {
842
FAudioFXReverbParameters oldParams;
843
oldParams.WetDryMix = params->WetDryMix;
844
oldParams.ReflectionsDelay = params->ReflectionsDelay;
845
oldParams.ReverbDelay = params->ReverbDelay;
846
oldParams.RearDelay = params->RearDelay;
847
oldParams.PositionLeft = params->PositionLeft;
848
oldParams.PositionRight = params->PositionRight;
849
oldParams.PositionMatrixLeft = params->PositionMatrixLeft;
850
oldParams.PositionMatrixRight = params->PositionMatrixRight;
851
oldParams.EarlyDiffusion = params->EarlyDiffusion;
852
oldParams.LateDiffusion = params->LateDiffusion;
853
oldParams.LowEQGain = params->LowEQGain;
854
oldParams.LowEQCutoff = params->LowEQCutoff;
855
oldParams.HighEQGain = params->HighEQGain;
856
oldParams.HighEQCutoff = params->HighEQCutoff;
857
oldParams.RoomFilterFreq = params->RoomFilterFreq;
858
oldParams.RoomFilterMain = params->RoomFilterMain;
859
oldParams.RoomFilterHF = params->RoomFilterHF;
860
oldParams.ReflectionsGain = params->ReflectionsGain;
861
oldParams.ReverbGain = params->ReverbGain;
862
oldParams.DecayTime = params->DecayTime;
863
oldParams.Density = params->Density;
864
oldParams.RoomSize = params->RoomSize;
865
DspReverb_SetParameters(reverb, &oldParams);
866
}
867
868
static inline float DspReverb_INTERNAL_ProcessEarly(
869
DspReverb *reverb,
870
float sample_in
871
) {
872
float early;
873
int32_t i;
874
875
/* Pre-Delay */
876
early = DspDelay_Process(&reverb->early_delay, sample_in);
877
878
/* Early Reflections */
879
for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
880
{
881
early = DspAllPass_Process(&reverb->apf_in[i], early);
882
}
883
884
return early;
885
}
886
887
static inline float DspReverb_INTERNAL_ProcessChannel(
888
DspReverb *reverb,
889
DspReverbChannel *channel,
890
float sample_in
891
) {
892
float revdelay, early_late, sample_out;
893
int32_t i;
894
895
revdelay = DspDelay_Process(&channel->reverb_delay, sample_in);
896
897
sample_out = 0.0f;
898
for (i = 0; i < REVERB_COUNT_COMB; i += 1)
899
{
900
sample_out += DspCombShelving_Process(
901
&channel->lpf_comb[i],
902
revdelay
903
);
904
}
905
sample_out /= (float) REVERB_COUNT_COMB;
906
907
/* Output Diffusion */
908
for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
909
{
910
sample_out = DspAllPass_Process(
911
&channel->apf_out[i],
912
sample_out
913
);
914
}
915
916
/* Combine early reflections and reverberation */
917
early_late = (
918
(sample_in * channel->early_gain) +
919
(sample_out * reverb->reverb_gain)
920
);
921
922
/* Room filter */
923
sample_out = DspBiQuad_Process(
924
&channel->room_high_shelf,
925
early_late * reverb->room_gain
926
);
927
928
/* PositionMatrixLeft/Right */
929
return sample_out * channel->gain;
930
}
931
932
/* Reverb Process Functions */
933
934
static inline float DspReverb_INTERNAL_Process_1_to_1(
935
DspReverb *reverb,
936
float *restrict samples_in,
937
float *restrict samples_out,
938
size_t sample_count
939
) {
940
const float *in_end = samples_in + sample_count;
941
float in, early, late, out;
942
float squared_sum = 0.0f;
943
944
while (samples_in < in_end)
945
{
946
/* Input */
947
in = *samples_in++;
948
949
/* Early Reflections */
950
early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
951
952
/* Reverberation */
953
late = DspReverb_INTERNAL_ProcessChannel(
954
reverb,
955
&reverb->channel[0],
956
early
957
);
958
959
/* Wet/Dry Mix */
960
out = (late * reverb->wet_ratio) + (in * reverb->dry_ratio);
961
squared_sum += out * out;
962
963
/* Output */
964
*samples_out++ = out;
965
}
966
967
return squared_sum;
968
}
969
970
static inline float DspReverb_INTERNAL_Process_1_to_5p1(
971
DspReverb *reverb,
972
float *restrict samples_in,
973
float *restrict samples_out,
974
size_t sample_count
975
) {
976
const float *in_end = samples_in + sample_count;
977
float in, in_ratio, early, late[4];
978
float squared_sum = 0.0f;
979
int32_t c;
980
981
while (samples_in < in_end)
982
{
983
/* Input */
984
in = *samples_in++;
985
in_ratio = in * reverb->dry_ratio;
986
987
/* Early Reflections */
988
early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
989
990
/* Reverberation with Wet/Dry Mix */
991
for (c = 0; c < 4; c += 1)
992
{
993
late[c] = (DspReverb_INTERNAL_ProcessChannel(
994
reverb,
995
&reverb->channel[c],
996
early
997
) * reverb->wet_ratio) + in_ratio;
998
squared_sum += late[c] * late[c];
999
}
1000
1001
/* Output */
1002
*samples_out++ = late[0]; /* Front Left */
1003
*samples_out++ = late[1]; /* Front Right */
1004
*samples_out++ = 0.0f; /* Center */
1005
*samples_out++ = 0.0f; /* LFE */
1006
*samples_out++ = late[2]; /* Rear Left */
1007
*samples_out++ = late[3]; /* Rear Right */
1008
}
1009
1010
return squared_sum;
1011
}
1012
1013
static inline float DspReverb_INTERNAL_Process_2_to_2(
1014
DspReverb *reverb,
1015
float *restrict samples_in,
1016
float *restrict samples_out,
1017
size_t sample_count
1018
) {
1019
const float *in_end = samples_in + sample_count;
1020
float in, early, late[2];
1021
float squared_sum = 0;
1022
1023
while (samples_in < in_end)
1024
{
1025
/* Input - Combine 2 channels into 1 */
1026
in = (samples_in[0] + samples_in[1]) / 2.0f;
1027
1028
/* Early Reflections */
1029
early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
1030
1031
/* Reverberation with Wet/Dry Mix */
1032
late[0] = (DspReverb_INTERNAL_ProcessChannel(
1033
reverb,
1034
&reverb->channel[0],
1035
early
1036
) * reverb->wet_ratio) + samples_in[0] * reverb->dry_ratio;
1037
late[1] = (DspReverb_INTERNAL_ProcessChannel(
1038
reverb,
1039
&reverb->channel[1],
1040
early
1041
) * reverb->wet_ratio) + samples_in[1] * reverb->dry_ratio;
1042
squared_sum += (late[0] * late[0]) + (late[1] * late[1]);
1043
1044
/* Output */
1045
*samples_out++ = late[0];
1046
*samples_out++ = late[1];
1047
1048
samples_in += 2;
1049
}
1050
1051
return squared_sum;
1052
}
1053
1054
static inline float DspReverb_INTERNAL_Process_2_to_5p1(
1055
DspReverb *reverb,
1056
float *restrict samples_in,
1057
float *restrict samples_out,
1058
size_t sample_count
1059
) {
1060
const float *in_end = samples_in + sample_count;
1061
float in, in_ratio, early, late[4];
1062
float squared_sum = 0;
1063
int32_t c;
1064
1065
while (samples_in < in_end)
1066
{
1067
/* Input - Combine 2 channels into 1 */
1068
in = (samples_in[0] + samples_in[1]) / 2.0f;
1069
in_ratio = in * reverb->dry_ratio;
1070
samples_in += 2;
1071
1072
/* Early Reflections */
1073
early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
1074
1075
/* Reverberation with Wet/Dry Mix */
1076
for (c = 0; c < 4; c += 1)
1077
{
1078
late[c] = (DspReverb_INTERNAL_ProcessChannel(
1079
reverb,
1080
&reverb->channel[c],
1081
early
1082
) * reverb->wet_ratio) + in_ratio;
1083
squared_sum += late[c] * late[c];
1084
}
1085
1086
/* Output */
1087
*samples_out++ = late[0]; /* Front Left */
1088
*samples_out++ = late[1]; /* Front Right */
1089
*samples_out++ = 0.0f; /* Center */
1090
*samples_out++ = 0.0f; /* LFE */
1091
*samples_out++ = late[2]; /* Rear Left */
1092
*samples_out++ = late[3]; /* Rear Right */
1093
}
1094
1095
return squared_sum;
1096
}
1097
1098
static inline float DspReverb_INTERNAL_Process_5p1_to_5p1(
1099
DspReverb *reverb,
1100
float *restrict samples_in,
1101
float *restrict samples_out,
1102
size_t sample_count
1103
) {
1104
const float *in_end = samples_in + sample_count;
1105
float in, in_ratio, early, late[5];
1106
float squared_sum = 0;
1107
int32_t c;
1108
1109
while (samples_in < in_end)
1110
{
1111
/* Input - Combine non-LFE channels into 1 */
1112
in = (samples_in[0] + samples_in[1] + samples_in[2] +
1113
samples_in[4] + samples_in[5]) / 5.0f;
1114
in_ratio = in * reverb->dry_ratio;
1115
1116
/* Early Reflections */
1117
early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
1118
1119
/* Reverberation with Wet/Dry Mix */
1120
for (c = 0; c < 5; c += 1)
1121
{
1122
late[c] = (DspReverb_INTERNAL_ProcessChannel(
1123
reverb,
1124
&reverb->channel[c],
1125
early
1126
) * reverb->wet_ratio) + in_ratio;
1127
squared_sum += late[c] * late[c];
1128
}
1129
1130
/* Output */
1131
*samples_out++ = late[0]; /* Front Left */
1132
*samples_out++ = late[1]; /* Front Right */
1133
*samples_out++ = late[2]; /* Center */
1134
*samples_out++ = samples_in[3]; /* LFE, pass through */
1135
*samples_out++ = late[3]; /* Rear Left */
1136
*samples_out++ = late[4]; /* Rear Right */
1137
1138
samples_in += 6;
1139
}
1140
1141
return squared_sum;
1142
}
1143
1144
#undef OUTPUT_SAMPLE
1145
1146
/* Reverb FAPO Implementation */
1147
1148
const FAudioGUID FAudioFX_CLSID_AudioReverb = /* 2.7 */
1149
{
1150
0x6A93130E,
1151
0xCB4E,
1152
0x4CE1,
1153
{
1154
0xA9,
1155
0xCF,
1156
0xE7,
1157
0x58,
1158
0x80,
1159
0x0B,
1160
0xB1,
1161
0x79
1162
}
1163
};
1164
1165
static FAPORegistrationProperties ReverbProperties =
1166
{
1167
/* .clsid = */ {0},
1168
/*.FriendlyName = */
1169
{
1170
'R', 'e', 'v', 'e', 'r', 'b', '\0'
1171
},
1172
/*.CopyrightInfo = */ {
1173
'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
1174
'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
1175
},
1176
/*.MajorVersion = */ 0,
1177
/*.MinorVersion = */ 0,
1178
/*.Flags = */ (
1179
FAPO_FLAG_FRAMERATE_MUST_MATCH |
1180
FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
1181
FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
1182
FAPO_FLAG_INPLACE_SUPPORTED
1183
),
1184
/*.MinInputBufferCount = */ 1,
1185
/*.MaxInputBufferCount = */ 1,
1186
/*.MinOutputBufferCount = */ 1,
1187
/*.MaxOutputBufferCount = */ 1
1188
};
1189
1190
typedef struct FAudioFXReverb
1191
{
1192
FAPOBase base;
1193
1194
uint16_t inChannels;
1195
uint16_t outChannels;
1196
uint32_t sampleRate;
1197
uint16_t inBlockAlign;
1198
uint16_t outBlockAlign;
1199
1200
uint8_t apiVersion;
1201
DspReverb reverb;
1202
} FAudioFXReverb;
1203
1204
static inline int8_t IsFloatFormat(const FAudioWaveFormatEx *format)
1205
{
1206
if (format->wFormatTag == FAUDIO_FORMAT_IEEE_FLOAT)
1207
{
1208
/* Plain ol' WaveFormatEx */
1209
return 1;
1210
}
1211
1212
if (format->wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
1213
{
1214
/* WaveFormatExtensible, match GUID */
1215
#define MAKE_SUBFORMAT_GUID(guid, fmt) \
1216
static FAudioGUID KSDATAFORMAT_SUBTYPE_##guid = \
1217
{ \
1218
(uint16_t) (fmt), 0x0000, 0x0010, \
1219
{ \
1220
0x80, 0x00, 0x00, 0xaa, \
1221
0x00, 0x38, 0x9b, 0x71 \
1222
} \
1223
}
1224
MAKE_SUBFORMAT_GUID(IEEE_FLOAT, 3);
1225
#undef MAKE_SUBFORMAT_GUID
1226
1227
if (FAudio_memcmp(
1228
&((FAudioWaveFormatExtensible*) format)->SubFormat,
1229
&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
1230
sizeof(FAudioGUID)
1231
) == 0) {
1232
return 1;
1233
}
1234
}
1235
1236
return 0;
1237
}
1238
1239
uint32_t FAudioFXReverb_IsInputFormatSupported(
1240
FAPOBase *fapo,
1241
const FAudioWaveFormatEx *pOutputFormat,
1242
const FAudioWaveFormatEx *pRequestedInputFormat,
1243
FAudioWaveFormatEx **ppSupportedInputFormat
1244
) {
1245
uint32_t result = 0;
1246
1247
#define SET_SUPPORTED_FIELD(field, value) \
1248
result = 1; \
1249
if (ppSupportedInputFormat && *ppSupportedInputFormat) \
1250
{ \
1251
(*ppSupportedInputFormat)->field = (value); \
1252
}
1253
1254
/* Sample Rate */
1255
if (pOutputFormat->nSamplesPerSec != pRequestedInputFormat->nSamplesPerSec)
1256
{
1257
SET_SUPPORTED_FIELD(nSamplesPerSec, pOutputFormat->nSamplesPerSec);
1258
}
1259
1260
/* Data Type */
1261
if (!IsFloatFormat(pRequestedInputFormat))
1262
{
1263
SET_SUPPORTED_FIELD(wFormatTag, FAUDIO_FORMAT_IEEE_FLOAT);
1264
}
1265
1266
/* Input/Output Channel Count */
1267
if (pOutputFormat->nChannels == 1 || pOutputFormat->nChannels == 2)
1268
{
1269
if (pRequestedInputFormat->nChannels != pOutputFormat->nChannels)
1270
{
1271
SET_SUPPORTED_FIELD(nChannels, pOutputFormat->nChannels);
1272
}
1273
}
1274
else if (pOutputFormat->nChannels == 6)
1275
{
1276
if ( pRequestedInputFormat->nChannels != 1 &&
1277
pRequestedInputFormat->nChannels != 2 &&
1278
pRequestedInputFormat->nChannels != 6 )
1279
{
1280
SET_SUPPORTED_FIELD(nChannels, 1);
1281
}
1282
}
1283
else
1284
{
1285
SET_SUPPORTED_FIELD(nChannels, 1);
1286
}
1287
1288
#undef SET_SUPPORTED_FIELD
1289
1290
return result;
1291
}
1292
1293
1294
uint32_t FAudioFXReverb_IsOutputFormatSupported(
1295
FAPOBase *fapo,
1296
const FAudioWaveFormatEx *pInputFormat,
1297
const FAudioWaveFormatEx *pRequestedOutputFormat,
1298
FAudioWaveFormatEx **ppSupportedOutputFormat
1299
) {
1300
uint32_t result = 0;
1301
1302
#define SET_SUPPORTED_FIELD(field, value) \
1303
result = 1; \
1304
if (ppSupportedOutputFormat && *ppSupportedOutputFormat) \
1305
{ \
1306
(*ppSupportedOutputFormat)->field = (value); \
1307
}
1308
1309
/* Sample Rate */
1310
if (pInputFormat->nSamplesPerSec != pRequestedOutputFormat->nSamplesPerSec)
1311
{
1312
SET_SUPPORTED_FIELD(nSamplesPerSec, pInputFormat->nSamplesPerSec);
1313
}
1314
1315
/* Data Type */
1316
if (!IsFloatFormat(pRequestedOutputFormat))
1317
{
1318
SET_SUPPORTED_FIELD(wFormatTag, FAUDIO_FORMAT_IEEE_FLOAT);
1319
}
1320
1321
/* Input/Output Channel Count */
1322
if (pInputFormat->nChannels == 1 || pInputFormat->nChannels == 2)
1323
{
1324
if ( pRequestedOutputFormat->nChannels != pInputFormat->nChannels &&
1325
pRequestedOutputFormat->nChannels != 6)
1326
{
1327
SET_SUPPORTED_FIELD(nChannels, pInputFormat->nChannels);
1328
}
1329
}
1330
else if (pInputFormat->nChannels == 6)
1331
{
1332
if (pRequestedOutputFormat->nChannels != 6)
1333
{
1334
SET_SUPPORTED_FIELD(nChannels, pInputFormat->nChannels);
1335
}
1336
}
1337
else
1338
{
1339
SET_SUPPORTED_FIELD(nChannels, 1);
1340
}
1341
1342
#undef SET_SUPPORTED_FIELD
1343
1344
return result;
1345
}
1346
1347
uint32_t FAudioFXReverb_Initialize(
1348
FAudioFXReverb *fapo,
1349
const void* pData,
1350
uint32_t DataByteSize
1351
) {
1352
#define INITPARAMS(offset) \
1353
FAudio_memcpy( \
1354
fapo->base.m_pParameterBlocks + DataByteSize * offset, \
1355
pData, \
1356
DataByteSize \
1357
);
1358
INITPARAMS(0)
1359
INITPARAMS(1)
1360
INITPARAMS(2)
1361
#undef INITPARAMS
1362
return 0;
1363
}
1364
1365
uint32_t FAudioFXReverb_LockForProcess(
1366
FAudioFXReverb *fapo,
1367
uint32_t InputLockedParameterCount,
1368
const FAPOLockForProcessBufferParameters *pInputLockedParameters,
1369
uint32_t OutputLockedParameterCount,
1370
const FAPOLockForProcessBufferParameters *pOutputLockedParameters
1371
) {
1372
/* Reverb specific validation */
1373
if (!IsFloatFormat(pInputLockedParameters->pFormat))
1374
{
1375
return FAPO_E_FORMAT_UNSUPPORTED;
1376
}
1377
1378
if ( pInputLockedParameters->pFormat->nSamplesPerSec < FAUDIOFX_REVERB_MIN_FRAMERATE ||
1379
pInputLockedParameters->pFormat->nSamplesPerSec > FAUDIOFX_REVERB_MAX_FRAMERATE )
1380
{
1381
return FAPO_E_FORMAT_UNSUPPORTED;
1382
}
1383
1384
if (!( (pInputLockedParameters->pFormat->nChannels == 1 &&
1385
(pOutputLockedParameters->pFormat->nChannels == 1 ||
1386
pOutputLockedParameters->pFormat->nChannels == 6)) ||
1387
(pInputLockedParameters->pFormat->nChannels == 2 &&
1388
(pOutputLockedParameters->pFormat->nChannels == 2 ||
1389
pOutputLockedParameters->pFormat->nChannels == 6)) ||
1390
(pInputLockedParameters->pFormat->nChannels == 6 &&
1391
pOutputLockedParameters->pFormat->nChannels == 6)))
1392
{
1393
return FAPO_E_FORMAT_UNSUPPORTED;
1394
}
1395
1396
/* Save the things we care about */
1397
fapo->inChannels = pInputLockedParameters->pFormat->nChannels;
1398
fapo->outChannels = pOutputLockedParameters->pFormat->nChannels;
1399
fapo->sampleRate = pOutputLockedParameters->pFormat->nSamplesPerSec;
1400
fapo->inBlockAlign = pInputLockedParameters->pFormat->nBlockAlign;
1401
fapo->outBlockAlign = pOutputLockedParameters->pFormat->nBlockAlign;
1402
1403
/* Create the network */
1404
DspReverb_Create(
1405
&fapo->reverb,
1406
fapo->sampleRate,
1407
fapo->inChannels,
1408
fapo->outChannels,
1409
fapo->base.pMalloc
1410
);
1411
1412
/* Initialize the effect to a default setting */
1413
if (fapo->apiVersion == 9)
1414
{
1415
DspReverb_SetParameters9(
1416
&fapo->reverb,
1417
(FAudioFXReverbParameters9*) fapo->base.m_pParameterBlocks
1418
);
1419
}
1420
else
1421
{
1422
DspReverb_SetParameters(
1423
&fapo->reverb,
1424
(FAudioFXReverbParameters*) fapo->base.m_pParameterBlocks
1425
);
1426
}
1427
1428
/* Call parent to do basic validation */
1429
return FAPOBase_LockForProcess(
1430
&fapo->base,
1431
InputLockedParameterCount,
1432
pInputLockedParameters,
1433
OutputLockedParameterCount,
1434
pOutputLockedParameters
1435
);
1436
}
1437
1438
static inline void FAudioFXReverb_CopyBuffer(
1439
FAudioFXReverb *fapo,
1440
float *restrict buffer_in,
1441
float *restrict buffer_out,
1442
size_t frames_in
1443
) {
1444
/* In-place processing? */
1445
if (buffer_in == buffer_out)
1446
{
1447
return;
1448
}
1449
1450
/* equal channel count */
1451
if (fapo->inBlockAlign == fapo->outBlockAlign)
1452
{
1453
FAudio_memcpy(
1454
buffer_out,
1455
buffer_in,
1456
fapo->inBlockAlign * frames_in
1457
);
1458
return;
1459
}
1460
1461
/* 1 -> 5.1 */
1462
if (fapo->inChannels == 1 && fapo->outChannels == 6)
1463
{
1464
const float *in_end = buffer_in + frames_in;
1465
while (buffer_in < in_end)
1466
{
1467
*buffer_out++ = *buffer_in;
1468
*buffer_out++ = *buffer_in++;
1469
*buffer_out++ = 0.0f;
1470
*buffer_out++ = 0.0f;
1471
*buffer_out++ = 0.0f;
1472
*buffer_out++ = 0.0f;
1473
}
1474
return;
1475
}
1476
1477
/* 2 -> 5.1 */
1478
if (fapo->inChannels == 2 && fapo->outChannels == 6)
1479
{
1480
const float *in_end = buffer_in + (frames_in * 2);
1481
while (buffer_in < in_end)
1482
{
1483
*buffer_out++ = *buffer_in++;
1484
*buffer_out++ = *buffer_in++;
1485
*buffer_out++ = 0.0f;
1486
*buffer_out++ = 0.0f;
1487
*buffer_out++ = 0.0f;
1488
*buffer_out++ = 0.0f;
1489
}
1490
return;
1491
}
1492
1493
FAudio_assert(0 && "Unsupported channel combination");
1494
FAudio_zero(buffer_out, fapo->outBlockAlign * frames_in);
1495
}
1496
1497
void FAudioFXReverb_Process(
1498
FAudioFXReverb *fapo,
1499
uint32_t InputProcessParameterCount,
1500
const FAPOProcessBufferParameters* pInputProcessParameters,
1501
uint32_t OutputProcessParameterCount,
1502
FAPOProcessBufferParameters* pOutputProcessParameters,
1503
int32_t IsEnabled
1504
) {
1505
FAudioFXReverbParameters *params;
1506
uint8_t update_params = FAPOBase_ParametersChanged(&fapo->base);
1507
float total;
1508
1509
params = (FAudioFXReverbParameters*) FAPOBase_BeginProcess(&fapo->base);
1510
1511
/* Update parameters before doing anything else */
1512
if (update_params)
1513
{
1514
if (fapo->apiVersion == 9)
1515
{
1516
DspReverb_SetParameters9(
1517
&fapo->reverb,
1518
(FAudioFXReverbParameters9*) params
1519
);
1520
}
1521
else
1522
{
1523
DspReverb_SetParameters(&fapo->reverb, params);
1524
}
1525
}
1526
1527
/* Handle disabled filter */
1528
if (IsEnabled == 0)
1529
{
1530
pOutputProcessParameters->BufferFlags = pInputProcessParameters->BufferFlags;
1531
1532
if (pOutputProcessParameters->BufferFlags != FAPO_BUFFER_SILENT)
1533
{
1534
FAudioFXReverb_CopyBuffer(
1535
fapo,
1536
(float*) pInputProcessParameters->pBuffer,
1537
(float*) pOutputProcessParameters->pBuffer,
1538
pInputProcessParameters->ValidFrameCount
1539
);
1540
}
1541
1542
FAPOBase_EndProcess(&fapo->base);
1543
return;
1544
}
1545
1546
/* XAudio2 passes a 'silent' buffer when no input buffer is available to play the effect tail */
1547
if (pInputProcessParameters->BufferFlags == FAPO_BUFFER_SILENT)
1548
{
1549
/* Make sure input data is usable. FIXME: Is this required? */
1550
FAudio_zero(
1551
pInputProcessParameters->pBuffer,
1552
pInputProcessParameters->ValidFrameCount * fapo->inBlockAlign
1553
);
1554
}
1555
1556
/* Run reverb effect */
1557
#define PROCESS(pin, pout) \
1558
DspReverb_INTERNAL_Process_##pin##_to_##pout( \
1559
&fapo->reverb, \
1560
(float*) pInputProcessParameters->pBuffer, \
1561
(float*) pOutputProcessParameters->pBuffer, \
1562
pInputProcessParameters->ValidFrameCount * fapo->inChannels \
1563
)
1564
switch (fapo->reverb.out_channels)
1565
{
1566
case 1:
1567
total = PROCESS(1, 1);
1568
break;
1569
case 2:
1570
total = PROCESS(2, 2);
1571
break;
1572
default: /* 5.1 */
1573
if (fapo->reverb.in_channels == 1)
1574
{
1575
total = PROCESS(1, 5p1);
1576
}
1577
else if (fapo->reverb.in_channels == 2)
1578
{
1579
total = PROCESS(2, 5p1);
1580
}
1581
else /* 5.1 */
1582
{
1583
total = PROCESS(5p1, 5p1);
1584
}
1585
break;
1586
}
1587
#undef PROCESS
1588
1589
/* Set BufferFlags to silent so PLAY_TAILS knows when to stop */
1590
pOutputProcessParameters->BufferFlags = (total < 0.0000001f) ?
1591
FAPO_BUFFER_SILENT :
1592
FAPO_BUFFER_VALID;
1593
1594
FAPOBase_EndProcess(&fapo->base);
1595
}
1596
1597
void FAudioFXReverb_Reset(FAudioFXReverb *fapo)
1598
{
1599
int32_t i, c;
1600
FAPOBase_Reset(&fapo->base);
1601
1602
/* Reset the cached state of the reverb filter */
1603
DspDelay_Reset(&fapo->reverb.early_delay);
1604
1605
for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
1606
{
1607
DspAllPass_Reset(&fapo->reverb.apf_in[i]);
1608
}
1609
1610
for (c = 0; c < fapo->reverb.reverb_channels; c += 1)
1611
{
1612
DspDelay_Reset(&fapo->reverb.channel[c].reverb_delay);
1613
1614
for (i = 0; i < REVERB_COUNT_COMB; i += 1)
1615
{
1616
DspCombShelving_Reset(&fapo->reverb.channel[c].lpf_comb[i]);
1617
}
1618
1619
DspBiQuad_Reset(&fapo->reverb.channel[c].room_high_shelf);
1620
1621
for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
1622
{
1623
DspAllPass_Reset(&fapo->reverb.channel[c].apf_out[i]);
1624
}
1625
}
1626
}
1627
1628
void FAudioFXReverb_Free(void* fapo)
1629
{
1630
FAudioFXReverb *reverb = (FAudioFXReverb*) fapo;
1631
DspReverb_Destroy(&reverb->reverb, reverb->base.pFree);
1632
reverb->base.pFree(reverb->base.m_pParameterBlocks);
1633
reverb->base.pFree(fapo);
1634
}
1635
1636
/* Public API (Version 7) */
1637
1638
uint32_t FAudioCreateReverb(FAPO** ppApo, uint32_t Flags)
1639
{
1640
return FAudioCreateReverbWithCustomAllocatorEXT(
1641
ppApo,
1642
Flags,
1643
FAudio_malloc,
1644
FAudio_free,
1645
FAudio_realloc
1646
);
1647
}
1648
1649
uint32_t FAudioCreateReverbWithCustomAllocatorEXT(
1650
FAPO** ppApo,
1651
uint32_t Flags,
1652
FAudioMallocFunc customMalloc,
1653
FAudioFreeFunc customFree,
1654
FAudioReallocFunc customRealloc
1655
) {
1656
const FAudioFXReverbParameters fxdefault =
1657
{
1658
FAUDIOFX_REVERB_DEFAULT_WET_DRY_MIX,
1659
FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_DELAY,
1660
FAUDIOFX_REVERB_DEFAULT_REVERB_DELAY,
1661
FAUDIOFX_REVERB_DEFAULT_REAR_DELAY,
1662
FAUDIOFX_REVERB_DEFAULT_POSITION,
1663
FAUDIOFX_REVERB_DEFAULT_POSITION,
1664
FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
1665
FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
1666
FAUDIOFX_REVERB_DEFAULT_EARLY_DIFFUSION,
1667
FAUDIOFX_REVERB_DEFAULT_LATE_DIFFUSION,
1668
FAUDIOFX_REVERB_DEFAULT_LOW_EQ_GAIN,
1669
FAUDIOFX_REVERB_DEFAULT_LOW_EQ_CUTOFF,
1670
FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_GAIN,
1671
FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_CUTOFF,
1672
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_FREQ,
1673
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_MAIN,
1674
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_HF,
1675
FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_GAIN,
1676
FAUDIOFX_REVERB_DEFAULT_REVERB_GAIN,
1677
FAUDIOFX_REVERB_DEFAULT_DECAY_TIME,
1678
FAUDIOFX_REVERB_DEFAULT_DENSITY,
1679
FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE
1680
};
1681
1682
/* Allocate... */
1683
FAudioFXReverb *result = (FAudioFXReverb*) customMalloc(sizeof(FAudioFXReverb));
1684
uint8_t *params = (uint8_t*) customMalloc(
1685
sizeof(FAudioFXReverbParameters) * 3
1686
);
1687
result->apiVersion = 7;
1688
1689
/* Initialize... */
1690
FAudio_memcpy(
1691
&ReverbProperties.clsid,
1692
&FAudioFX_CLSID_AudioReverb,
1693
sizeof(FAudioGUID)
1694
);
1695
CreateFAPOBaseWithCustomAllocatorEXT(
1696
&result->base,
1697
&ReverbProperties,
1698
params,
1699
sizeof(FAudioFXReverbParameters),
1700
0,
1701
customMalloc,
1702
customFree,
1703
customRealloc
1704
);
1705
1706
result->inChannels = 0;
1707
result->outChannels = 0;
1708
result->sampleRate = 0;
1709
FAudio_zero(&result->reverb, sizeof(DspReverb));
1710
1711
/* Function table... */
1712
#define ASSIGN_VT(name) \
1713
result->base.base.name = (name##Func) FAudioFXReverb_##name;
1714
ASSIGN_VT(LockForProcess);
1715
ASSIGN_VT(IsInputFormatSupported);
1716
ASSIGN_VT(IsOutputFormatSupported);
1717
ASSIGN_VT(Initialize);
1718
ASSIGN_VT(Reset);
1719
ASSIGN_VT(Process);
1720
result->base.Destructor = FAudioFXReverb_Free;
1721
#undef ASSIGN_VT
1722
1723
/* Prepare the default parameters */
1724
result->base.base.Initialize(
1725
result,
1726
&fxdefault,
1727
sizeof(FAudioFXReverbParameters)
1728
);
1729
1730
/* Finally. */
1731
*ppApo = &result->base.base;
1732
return 0;
1733
}
1734
1735
void ReverbConvertI3DL2ToNative(
1736
const FAudioFXReverbI3DL2Parameters *pI3DL2,
1737
FAudioFXReverbParameters *pNative
1738
) {
1739
float reflectionsDelay;
1740
float reverbDelay;
1741
1742
pNative->RearDelay = FAUDIOFX_REVERB_DEFAULT_REAR_DELAY;
1743
pNative->PositionLeft = FAUDIOFX_REVERB_DEFAULT_POSITION;
1744
pNative->PositionRight = FAUDIOFX_REVERB_DEFAULT_POSITION;
1745
pNative->PositionMatrixLeft = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
1746
pNative->PositionMatrixRight = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
1747
pNative->RoomSize = FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE;
1748
pNative->LowEQCutoff = 4;
1749
pNative->HighEQCutoff = 6;
1750
1751
pNative->RoomFilterMain = (float) pI3DL2->Room / 100.0f;
1752
pNative->RoomFilterHF = (float) pI3DL2->RoomHF / 100.0f;
1753
1754
if (pI3DL2->DecayHFRatio >= 1.0f)
1755
{
1756
int32_t index = (int32_t) (-4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
1757
if (index < -8)
1758
{
1759
index = -8;
1760
}
1761
pNative->LowEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
1762
pNative->HighEQGain = 8;
1763
pNative->DecayTime = pI3DL2->DecayTime * pI3DL2->DecayHFRatio;
1764
}
1765
else
1766
{
1767
int32_t index = (int32_t) (4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
1768
if (index < -8)
1769
{
1770
index = -8;
1771
}
1772
pNative->LowEQGain = 8;
1773
pNative->HighEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
1774
pNative->DecayTime = pI3DL2->DecayTime;
1775
}
1776
1777
reflectionsDelay = pI3DL2->ReflectionsDelay * 1000.0f;
1778
if (reflectionsDelay >= FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY)
1779
{
1780
reflectionsDelay = (float) (FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY - 1);
1781
}
1782
else if (reflectionsDelay <= 1)
1783
{
1784
reflectionsDelay = 1;
1785
}
1786
pNative->ReflectionsDelay = (uint32_t) reflectionsDelay;
1787
1788
reverbDelay = pI3DL2->ReverbDelay * 1000.0f;
1789
if (reverbDelay >= FAUDIOFX_REVERB_MAX_REVERB_DELAY)
1790
{
1791
reverbDelay = (float) (FAUDIOFX_REVERB_MAX_REVERB_DELAY - 1);
1792
}
1793
pNative->ReverbDelay = (uint8_t) reverbDelay;
1794
1795
pNative->ReflectionsGain = pI3DL2->Reflections / 100.0f;
1796
pNative->ReverbGain = pI3DL2->Reverb / 100.0f;
1797
pNative->EarlyDiffusion = (uint8_t) (15.0f * pI3DL2->Diffusion / 100.0f);
1798
pNative->LateDiffusion = pNative->EarlyDiffusion;
1799
pNative->Density = pI3DL2->Density;
1800
pNative->RoomFilterFreq = pI3DL2->HFReference;
1801
1802
pNative->WetDryMix = pI3DL2->WetDryMix;
1803
}
1804
1805
/* Public API (Version 9) */
1806
1807
uint32_t FAudioCreateReverb9(FAPO** ppApo, uint32_t Flags)
1808
{
1809
return FAudioCreateReverb9WithCustomAllocatorEXT(
1810
ppApo,
1811
Flags,
1812
FAudio_malloc,
1813
FAudio_free,
1814
FAudio_realloc
1815
);
1816
}
1817
1818
uint32_t FAudioCreateReverb9WithCustomAllocatorEXT(
1819
FAPO** ppApo,
1820
uint32_t Flags,
1821
FAudioMallocFunc customMalloc,
1822
FAudioFreeFunc customFree,
1823
FAudioReallocFunc customRealloc
1824
) {
1825
const FAudioFXReverbParameters9 fxdefault =
1826
{
1827
FAUDIOFX_REVERB_DEFAULT_WET_DRY_MIX,
1828
FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_DELAY,
1829
FAUDIOFX_REVERB_DEFAULT_REVERB_DELAY,
1830
FAUDIOFX_REVERB_DEFAULT_REAR_DELAY, /* FIXME: 7POINT1? */
1831
FAUDIOFX_REVERB_DEFAULT_7POINT1_SIDE_DELAY,
1832
FAUDIOFX_REVERB_DEFAULT_POSITION,
1833
FAUDIOFX_REVERB_DEFAULT_POSITION,
1834
FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
1835
FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
1836
FAUDIOFX_REVERB_DEFAULT_EARLY_DIFFUSION,
1837
FAUDIOFX_REVERB_DEFAULT_LATE_DIFFUSION,
1838
FAUDIOFX_REVERB_DEFAULT_LOW_EQ_GAIN,
1839
FAUDIOFX_REVERB_DEFAULT_LOW_EQ_CUTOFF,
1840
FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_GAIN,
1841
FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_CUTOFF,
1842
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_FREQ,
1843
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_MAIN,
1844
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_HF,
1845
FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_GAIN,
1846
FAUDIOFX_REVERB_DEFAULT_REVERB_GAIN,
1847
FAUDIOFX_REVERB_DEFAULT_DECAY_TIME,
1848
FAUDIOFX_REVERB_DEFAULT_DENSITY,
1849
FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE
1850
};
1851
1852
/* Allocate... */
1853
FAudioFXReverb *result = (FAudioFXReverb*) customMalloc(sizeof(FAudioFXReverb));
1854
uint8_t *params = (uint8_t*) customMalloc(
1855
sizeof(FAudioFXReverbParameters9) * 3
1856
);
1857
result->apiVersion = 9;
1858
1859
/* Initialize... */
1860
FAudio_memcpy(
1861
&ReverbProperties.clsid,
1862
&FAudioFX_CLSID_AudioReverb,
1863
sizeof(FAudioGUID)
1864
);
1865
CreateFAPOBaseWithCustomAllocatorEXT(
1866
&result->base,
1867
&ReverbProperties,
1868
params,
1869
sizeof(FAudioFXReverbParameters9),
1870
0,
1871
customMalloc,
1872
customFree,
1873
customRealloc
1874
);
1875
1876
result->inChannels = 0;
1877
result->outChannels = 0;
1878
result->sampleRate = 0;
1879
FAudio_zero(&result->reverb, sizeof(DspReverb));
1880
1881
/* Function table... */
1882
#define ASSIGN_VT(name) \
1883
result->base.base.name = (name##Func) FAudioFXReverb_##name;
1884
ASSIGN_VT(LockForProcess);
1885
ASSIGN_VT(IsInputFormatSupported);
1886
ASSIGN_VT(IsOutputFormatSupported);
1887
ASSIGN_VT(Initialize);
1888
ASSIGN_VT(Reset);
1889
ASSIGN_VT(Process);
1890
result->base.Destructor = FAudioFXReverb_Free;
1891
#undef ASSIGN_VT
1892
1893
/* Prepare the default parameters */
1894
result->base.base.Initialize(
1895
result,
1896
&fxdefault,
1897
sizeof(FAudioFXReverbParameters9)
1898
);
1899
1900
/* Finally. */
1901
*ppApo = &result->base.base;
1902
return 0;
1903
}
1904
1905
void ReverbConvertI3DL2ToNative9(
1906
const FAudioFXReverbI3DL2Parameters *pI3DL2,
1907
FAudioFXReverbParameters9 *pNative,
1908
int32_t sevenDotOneReverb
1909
) {
1910
float reflectionsDelay;
1911
float reverbDelay;
1912
1913
if (sevenDotOneReverb)
1914
{
1915
pNative->RearDelay = FAUDIOFX_REVERB_DEFAULT_7POINT1_REAR_DELAY;
1916
}
1917
else
1918
{
1919
pNative->RearDelay = FAUDIOFX_REVERB_DEFAULT_REAR_DELAY;
1920
}
1921
pNative->SideDelay = FAUDIOFX_REVERB_DEFAULT_7POINT1_SIDE_DELAY;
1922
pNative->PositionLeft = FAUDIOFX_REVERB_DEFAULT_POSITION;
1923
pNative->PositionRight = FAUDIOFX_REVERB_DEFAULT_POSITION;
1924
pNative->PositionMatrixLeft = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
1925
pNative->PositionMatrixRight = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
1926
pNative->RoomSize = FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE;
1927
pNative->LowEQCutoff = 4;
1928
pNative->HighEQCutoff = 6;
1929
1930
pNative->RoomFilterMain = (float) pI3DL2->Room / 100.0f;
1931
pNative->RoomFilterHF = (float) pI3DL2->RoomHF / 100.0f;
1932
1933
if (pI3DL2->DecayHFRatio >= 1.0f)
1934
{
1935
int32_t index = (int32_t) (-4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
1936
if (index < -8)
1937
{
1938
index = -8;
1939
}
1940
pNative->LowEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
1941
pNative->HighEQGain = 8;
1942
pNative->DecayTime = pI3DL2->DecayTime * pI3DL2->DecayHFRatio;
1943
}
1944
else
1945
{
1946
int32_t index = (int32_t) (4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
1947
if (index < -8)
1948
{
1949
index = -8;
1950
}
1951
pNative->LowEQGain = 8;
1952
pNative->HighEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
1953
pNative->DecayTime = pI3DL2->DecayTime;
1954
}
1955
1956
reflectionsDelay = pI3DL2->ReflectionsDelay * 1000.0f;
1957
if (reflectionsDelay >= FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY)
1958
{
1959
reflectionsDelay = (float) (FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY - 1);
1960
}
1961
else if (reflectionsDelay <= 1)
1962
{
1963
reflectionsDelay = 1;
1964
}
1965
pNative->ReflectionsDelay = (uint32_t) reflectionsDelay;
1966
1967
reverbDelay = pI3DL2->ReverbDelay * 1000.0f;
1968
if (reverbDelay >= FAUDIOFX_REVERB_MAX_REVERB_DELAY)
1969
{
1970
reverbDelay = (float) (FAUDIOFX_REVERB_MAX_REVERB_DELAY - 1);
1971
}
1972
pNative->ReverbDelay = (uint8_t) reverbDelay;
1973
1974
pNative->ReflectionsGain = pI3DL2->Reflections / 100.0f;
1975
pNative->ReverbGain = pI3DL2->Reverb / 100.0f;
1976
pNative->EarlyDiffusion = (uint8_t) (15.0f * pI3DL2->Diffusion / 100.0f);
1977
pNative->LateDiffusion = pNative->EarlyDiffusion;
1978
pNative->Density = pI3DL2->Density;
1979
pNative->RoomFilterFreq = pI3DL2->HFReference;
1980
1981
pNative->WetDryMix = pI3DL2->WetDryMix;
1982
}
1983
1984
/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
1985
1986