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