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