Clean up the biquad filter a bit
[openal-soft.git] / Alc / effects / reverb.cpp
blob68c763372ec53fa6f07ee8b07d611e4d9d7a301a
1 /**
2 * Ambisonic reverb engine for the OpenAL cross platform audio library
3 * Copyright (C) 2008-2017 by Chris Robinson and Christopher Fitzgerald.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <math.h>
27 #include "alMain.h"
28 #include "alcontext.h"
29 #include "alu.h"
30 #include "alAuxEffectSlot.h"
31 #include "alListener.h"
32 #include "alError.h"
33 #include "filters/defs.h"
34 #include "vecmat.h"
36 /* This is a user config option for modifying the overall output of the reverb
37 * effect.
39 ALfloat ReverbBoost = 1.0f;
41 /* This is the maximum number of samples processed for each inner loop
42 * iteration. */
43 #define MAX_UPDATE_SAMPLES 256
45 /* The number of samples used for cross-faded delay lines. This can be used
46 * to balance the compensation for abrupt line changes and attenuation due to
47 * minimally lengthed recursive lines. Try to keep this below the device
48 * update size.
50 #define FADE_SAMPLES 128
52 /* The number of spatialized lines or channels to process. Four channels allows
53 * for a 3D A-Format response. NOTE: This can't be changed without taking care
54 * of the conversion matrices, and a few places where the length arrays are
55 * assumed to have 4 elements.
57 #define NUM_LINES 4
60 /* The B-Format to A-Format conversion matrix. The arrangement of rows is
61 * deliberately chosen to align the resulting lines to their spatial opposites
62 * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
63 * back left). It's not quite opposite, since the A-Format results in a
64 * tetrahedron, but it's close enough. Should the model be extended to 8-lines
65 * in the future, true opposites can be used.
67 static const aluMatrixf B2A = {{
68 { 0.288675134595f, 0.288675134595f, 0.288675134595f, 0.288675134595f },
69 { 0.288675134595f, -0.288675134595f, -0.288675134595f, 0.288675134595f },
70 { 0.288675134595f, 0.288675134595f, -0.288675134595f, -0.288675134595f },
71 { 0.288675134595f, -0.288675134595f, 0.288675134595f, -0.288675134595f }
72 }};
74 /* Converts A-Format to B-Format. */
75 static const aluMatrixf A2B = {{
76 { 0.866025403785f, 0.866025403785f, 0.866025403785f, 0.866025403785f },
77 { 0.866025403785f, -0.866025403785f, 0.866025403785f, -0.866025403785f },
78 { 0.866025403785f, -0.866025403785f, -0.866025403785f, 0.866025403785f },
79 { 0.866025403785f, 0.866025403785f, -0.866025403785f, -0.866025403785f }
80 }};
82 static const ALfloat FadeStep = 1.0f / FADE_SAMPLES;
84 /* The all-pass and delay lines have a variable length dependent on the
85 * effect's density parameter, which helps alter the perceived environment
86 * size. The size-to-density conversion is a cubed scale:
88 * density = min(1.0, pow(size, 3.0) / DENSITY_SCALE);
90 * The line lengths scale linearly with room size, so the inverse density
91 * conversion is needed, taking the cube root of the re-scaled density to
92 * calculate the line length multiplier:
94 * length_mult = max(5.0, cbrtf(density*DENSITY_SCALE));
96 * The density scale below will result in a max line multiplier of 50, for an
97 * effective size range of 5m to 50m.
99 static const ALfloat DENSITY_SCALE = 125000.0f;
101 /* All delay line lengths are specified in seconds.
103 * To approximate early reflections, we break them up into primary (those
104 * arriving from the same direction as the source) and secondary (those
105 * arriving from the opposite direction).
107 * The early taps decorrelate the 4-channel signal to approximate an average
108 * room response for the primary reflections after the initial early delay.
110 * Given an average room dimension (d_a) and the speed of sound (c) we can
111 * calculate the average reflection delay (r_a) regardless of listener and
112 * source positions as:
114 * r_a = d_a / c
115 * c = 343.3
117 * This can extended to finding the average difference (r_d) between the
118 * maximum (r_1) and minimum (r_0) reflection delays:
120 * r_0 = 2 / 3 r_a
121 * = r_a - r_d / 2
122 * = r_d
123 * r_1 = 4 / 3 r_a
124 * = r_a + r_d / 2
125 * = 2 r_d
126 * r_d = 2 / 3 r_a
127 * = r_1 - r_0
129 * As can be determined by integrating the 1D model with a source (s) and
130 * listener (l) positioned across the dimension of length (d_a):
132 * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
134 * The initial taps (T_(i=0)^N) are then specified by taking a power series
135 * that ranges between r_0 and half of r_1 less r_0:
137 * R_i = 2^(i / (2 N - 1)) r_d
138 * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
139 * = r_0 + T_i
140 * T_i = R_i - r_0
141 * = (2^(i / (2 N - 1)) - 1) r_d
143 * Assuming an average of 1m, we get the following taps:
145 static const ALfloat EARLY_TAP_LENGTHS[NUM_LINES] =
147 0.0000000e+0f, 2.0213520e-4f, 4.2531060e-4f, 6.7171600e-4f
150 /* The early all-pass filter lengths are based on the early tap lengths:
152 * A_i = R_i / a
154 * Where a is the approximate maximum all-pass cycle limit (20).
156 static const ALfloat EARLY_ALLPASS_LENGTHS[NUM_LINES] =
158 9.7096800e-5f, 1.0720356e-4f, 1.1836234e-4f, 1.3068260e-4f
161 /* The early delay lines are used to transform the primary reflections into
162 * the secondary reflections. The A-format is arranged in such a way that
163 * the channels/lines are spatially opposite:
165 * C_i is opposite C_(N-i-1)
167 * The delays of the two opposing reflections (R_i and O_i) from a source
168 * anywhere along a particular dimension always sum to twice its full delay:
170 * 2 r_a = R_i + O_i
172 * With that in mind we can determine the delay between the two reflections
173 * and thus specify our early line lengths (L_(i=0)^N) using:
175 * O_i = 2 r_a - R_(N-i-1)
176 * L_i = O_i - R_(N-i-1)
177 * = 2 (r_a - R_(N-i-1))
178 * = 2 (r_a - T_(N-i-1) - r_0)
179 * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
181 * Using an average dimension of 1m, we get:
183 static const ALfloat EARLY_LINE_LENGTHS[NUM_LINES] =
185 5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f
188 /* The late all-pass filter lengths are based on the late line lengths:
190 * A_i = (5 / 3) L_i / r_1
192 static const ALfloat LATE_ALLPASS_LENGTHS[NUM_LINES] =
194 1.6182800e-4f, 2.0389060e-4f, 2.8159360e-4f, 3.2365600e-4f
197 /* The late lines are used to approximate the decaying cycle of recursive
198 * late reflections.
200 * Splitting the lines in half, we start with the shortest reflection paths
201 * (L_(i=0)^(N/2)):
203 * L_i = 2^(i / (N - 1)) r_d
205 * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
207 * L_i = 2 r_a - L_(i-N/2)
208 * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
210 * For our 1m average room, we get:
212 static const ALfloat LATE_LINE_LENGTHS[NUM_LINES] =
214 1.9419362e-3f, 2.4466860e-3f, 3.3791220e-3f, 3.8838720e-3f
218 typedef struct DelayLineI {
219 /* The delay lines use interleaved samples, with the lengths being powers
220 * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
222 ALsizei Mask;
223 ALfloat (*Line)[NUM_LINES];
224 } DelayLineI;
226 typedef struct VecAllpass {
227 DelayLineI Delay;
228 ALfloat Coeff;
229 ALsizei Offset[NUM_LINES][2];
230 } VecAllpass;
232 typedef struct T60Filter {
233 /* Two filters are used to adjust the signal. One to control the low
234 * frequencies, and one to control the high frequencies.
236 ALfloat MidGain[2];
237 BiquadFilter HFFilter, LFFilter;
238 } T60Filter;
240 typedef struct EarlyReflections {
241 /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
242 * The spread from this filter also helps smooth out the reverb tail.
244 VecAllpass VecAp;
246 /* An echo line is used to complete the second half of the early
247 * reflections.
249 DelayLineI Delay;
250 ALsizei Offset[NUM_LINES][2];
251 ALfloat Coeff[NUM_LINES][2];
253 /* The gain for each output channel based on 3D panning. */
254 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
255 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
256 } EarlyReflections;
258 typedef struct LateReverb {
259 /* A recursive delay line is used fill in the reverb tail. */
260 DelayLineI Delay;
261 ALsizei Offset[NUM_LINES][2];
263 /* Attenuation to compensate for the modal density and decay rate of the
264 * late lines.
266 ALfloat DensityGain[2];
268 /* T60 decay filters are used to simulate absorption. */
269 T60Filter T60[NUM_LINES];
271 /* A Gerzon vector all-pass filter is used to simulate diffusion. */
272 VecAllpass VecAp;
274 /* The gain for each output channel based on 3D panning. */
275 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
276 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
277 } LateReverb;
279 struct ReverbState final : public ALeffectState {
280 /* All delay lines are allocated as a single buffer to reduce memory
281 * fragmentation and management code.
283 ALfloat *SampleBuffer;
284 ALuint TotalSamples;
286 struct {
287 /* Calculated parameters which indicate if cross-fading is needed after
288 * an update.
290 ALfloat Density, Diffusion;
291 ALfloat DecayTime, HFDecayTime, LFDecayTime;
292 ALfloat HFReference, LFReference;
293 } Params;
295 /* Master effect filters */
296 struct {
297 BiquadFilter Lp;
298 BiquadFilter Hp;
299 } Filter[NUM_LINES];
301 /* Core delay line (early reflections and late reverb tap from this). */
302 DelayLineI Delay;
304 /* Tap points for early reflection delay. */
305 ALsizei EarlyDelayTap[NUM_LINES][2];
306 ALfloat EarlyDelayCoeff[NUM_LINES][2];
308 /* Tap points for late reverb feed and delay. */
309 ALsizei LateFeedTap;
310 ALsizei LateDelayTap[NUM_LINES][2];
312 /* Coefficients for the all-pass and line scattering matrices. */
313 ALfloat MixX;
314 ALfloat MixY;
316 EarlyReflections Early;
318 LateReverb Late;
320 /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
321 ALsizei FadeCount;
323 /* Maximum number of samples to process at once. */
324 ALsizei MaxUpdate[2];
326 /* The current write offset for all delay lines. */
327 ALsizei Offset;
329 /* Temporary storage used when processing. */
330 alignas(16) ALfloat TempSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
331 alignas(16) ALfloat MixSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
334 static ALvoid ReverbState_Destruct(ReverbState *State);
335 static ALboolean ReverbState_deviceUpdate(ReverbState *State, ALCdevice *Device);
336 static ALvoid ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
337 static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
338 DECLARE_DEFAULT_ALLOCATORS(ReverbState)
340 DEFINE_ALEFFECTSTATE_VTABLE(ReverbState);
342 static void ReverbState_Construct(ReverbState *state)
344 new (state) ReverbState{};
346 ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
347 SET_VTABLE2(ReverbState, ALeffectState, state);
349 state->TotalSamples = 0;
350 state->SampleBuffer = NULL;
352 state->Params.Density = AL_EAXREVERB_DEFAULT_DENSITY;
353 state->Params.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION;
354 state->Params.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME;
355 state->Params.HFDecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME*AL_EAXREVERB_DEFAULT_DECAY_HFRATIO;
356 state->Params.LFDecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME*AL_EAXREVERB_DEFAULT_DECAY_LFRATIO;
357 state->Params.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE;
358 state->Params.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE;
360 for(ALsizei i{0};i < NUM_LINES;i++)
362 BiquadFilter_clear(&state->Filter[i].Lp);
363 BiquadFilter_clear(&state->Filter[i].Hp);
366 state->Delay.Mask = 0;
367 state->Delay.Line = NULL;
369 for(ALsizei i{0};i < NUM_LINES;i++)
371 state->EarlyDelayTap[i][0] = 0;
372 state->EarlyDelayTap[i][1] = 0;
373 state->EarlyDelayCoeff[i][0] = 0.0f;
374 state->EarlyDelayCoeff[i][1] = 0.0f;
377 state->LateFeedTap = 0;
379 for(ALsizei i{0};i < NUM_LINES;i++)
381 state->LateDelayTap[i][0] = 0;
382 state->LateDelayTap[i][1] = 0;
385 state->MixX = 0.0f;
386 state->MixY = 0.0f;
388 state->Early.VecAp.Delay.Mask = 0;
389 state->Early.VecAp.Delay.Line = NULL;
390 state->Early.VecAp.Coeff = 0.0f;
391 state->Early.Delay.Mask = 0;
392 state->Early.Delay.Line = NULL;
393 for(ALsizei i{0};i < NUM_LINES;i++)
395 state->Early.VecAp.Offset[i][0] = 0;
396 state->Early.VecAp.Offset[i][1] = 0;
397 state->Early.Offset[i][0] = 0;
398 state->Early.Offset[i][1] = 0;
399 state->Early.Coeff[i][0] = 0.0f;
400 state->Early.Coeff[i][1] = 0.0f;
403 state->Late.DensityGain[0] = 0.0f;
404 state->Late.DensityGain[1] = 0.0f;
405 state->Late.Delay.Mask = 0;
406 state->Late.Delay.Line = NULL;
407 state->Late.VecAp.Delay.Mask = 0;
408 state->Late.VecAp.Delay.Line = NULL;
409 state->Late.VecAp.Coeff = 0.0f;
410 for(ALsizei i{0};i < NUM_LINES;i++)
412 state->Late.Offset[i][0] = 0;
413 state->Late.Offset[i][1] = 0;
415 state->Late.VecAp.Offset[i][0] = 0;
416 state->Late.VecAp.Offset[i][1] = 0;
418 state->Late.T60[i].MidGain[0] = 0.0f;
419 state->Late.T60[i].MidGain[1] = 0.0f;
420 BiquadFilter_clear(&state->Late.T60[i].HFFilter);
421 BiquadFilter_clear(&state->Late.T60[i].LFFilter);
424 for(ALsizei i{0};i < NUM_LINES;i++)
426 for(ALsizei j{0};j < MAX_OUTPUT_CHANNELS;j++)
428 state->Early.CurrentGain[i][j] = 0.0f;
429 state->Early.PanGain[i][j] = 0.0f;
430 state->Late.CurrentGain[i][j] = 0.0f;
431 state->Late.PanGain[i][j] = 0.0f;
435 state->FadeCount = 0;
436 state->MaxUpdate[0] = MAX_UPDATE_SAMPLES;
437 state->MaxUpdate[1] = MAX_UPDATE_SAMPLES;
438 state->Offset = 0;
441 static ALvoid ReverbState_Destruct(ReverbState *State)
443 al_free(State->SampleBuffer);
444 State->SampleBuffer = NULL;
446 ALeffectState_Destruct(STATIC_CAST(ALeffectState,State));
447 State->~ReverbState();
450 /**************************************
451 * Device Update *
452 **************************************/
454 static inline ALfloat CalcDelayLengthMult(ALfloat density)
456 return maxf(5.0f, cbrtf(density*DENSITY_SCALE));
459 /* Given the allocated sample buffer, this function updates each delay line
460 * offset.
462 static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay)
464 union {
465 ALfloat *f;
466 ALfloat (*f4)[NUM_LINES];
467 } u;
468 u.f = &sampleBuffer[(ptrdiff_t)Delay->Line * NUM_LINES];
469 Delay->Line = u.f4;
472 /* Calculate the length of a delay line and store its mask and offset. */
473 static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALuint frequency,
474 const ALuint extra, DelayLineI *Delay)
476 ALuint samples;
478 /* All line lengths are powers of 2, calculated from their lengths in
479 * seconds, rounded up.
481 samples = float2int(ceilf(length*frequency));
482 samples = NextPowerOf2(samples + extra);
484 /* All lines share a single sample buffer. */
485 Delay->Mask = samples - 1;
486 Delay->Line = (ALfloat(*)[NUM_LINES])offset;
488 /* Return the sample count for accumulation. */
489 return samples;
492 /* Calculates the delay line metrics and allocates the shared sample buffer
493 * for all lines given the sample rate (frequency). If an allocation failure
494 * occurs, it returns AL_FALSE.
496 static ALboolean AllocLines(const ALuint frequency, ReverbState *State)
498 ALuint totalSamples, i;
499 ALfloat multiplier, length;
501 /* All delay line lengths are calculated to accomodate the full range of
502 * lengths given their respective paramters.
504 totalSamples = 0;
506 /* Multiplier for the maximum density value, i.e. density=1, which is
507 * actually the least density...
509 multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
511 /* The main delay length includes the maximum early reflection delay, the
512 * largest early tap width, the maximum late reverb delay, and the
513 * largest late tap width. Finally, it must also be extended by the
514 * update size (MAX_UPDATE_SAMPLES) for block processing.
516 length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier +
517 AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
518 (LATE_LINE_LENGTHS[NUM_LINES-1] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
519 totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES,
520 &State->Delay);
522 /* The early vector all-pass line. */
523 length = EARLY_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
524 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
525 &State->Early.VecAp.Delay);
527 /* The early reflection line. */
528 length = EARLY_LINE_LENGTHS[NUM_LINES-1] * multiplier;
529 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
530 &State->Early.Delay);
532 /* The late vector all-pass line. */
533 length = LATE_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
534 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
535 &State->Late.VecAp.Delay);
537 /* The late delay lines are calculated from the largest maximum density
538 * line length.
540 length = LATE_LINE_LENGTHS[NUM_LINES-1] * multiplier;
541 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
542 &State->Late.Delay);
544 if(totalSamples != State->TotalSamples)
546 ALfloat *newBuffer;
548 TRACE("New reverb buffer length: %ux4 samples\n", totalSamples);
549 newBuffer = static_cast<ALfloat*>(al_calloc(16,
550 sizeof(ALfloat[NUM_LINES]) * totalSamples));
551 if(!newBuffer) return AL_FALSE;
553 al_free(State->SampleBuffer);
554 State->SampleBuffer = newBuffer;
555 State->TotalSamples = totalSamples;
558 /* Update all delays to reflect the new sample buffer. */
559 RealizeLineOffset(State->SampleBuffer, &State->Delay);
560 RealizeLineOffset(State->SampleBuffer, &State->Early.VecAp.Delay);
561 RealizeLineOffset(State->SampleBuffer, &State->Early.Delay);
562 RealizeLineOffset(State->SampleBuffer, &State->Late.VecAp.Delay);
563 RealizeLineOffset(State->SampleBuffer, &State->Late.Delay);
565 /* Clear the sample buffer. */
566 for(i = 0;i < State->TotalSamples;i++)
567 State->SampleBuffer[i] = 0.0f;
569 return AL_TRUE;
572 static ALboolean ReverbState_deviceUpdate(ReverbState *State, ALCdevice *Device)
574 ALuint frequency = Device->Frequency;
575 ALfloat multiplier;
576 ALsizei i, j;
578 /* Allocate the delay lines. */
579 if(!AllocLines(frequency, State))
580 return AL_FALSE;
582 multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
584 /* The late feed taps are set a fixed position past the latest delay tap. */
585 State->LateFeedTap = float2int((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
586 EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier) *
587 frequency);
589 /* Clear filters and gain coefficients since the delay lines were all just
590 * cleared (if not reallocated).
592 for(i = 0;i < NUM_LINES;i++)
594 BiquadFilter_clear(&State->Filter[i].Lp);
595 BiquadFilter_clear(&State->Filter[i].Hp);
598 for(i = 0;i < NUM_LINES;i++)
600 State->EarlyDelayCoeff[i][0] = 0.0f;
601 State->EarlyDelayCoeff[i][1] = 0.0f;
604 for(i = 0;i < NUM_LINES;i++)
606 State->Early.Coeff[i][0] = 0.0f;
607 State->Early.Coeff[i][1] = 0.0f;
610 State->Late.DensityGain[0] = 0.0f;
611 State->Late.DensityGain[1] = 0.0f;
612 for(i = 0;i < NUM_LINES;i++)
614 State->Late.T60[i].MidGain[0] = 0.0f;
615 State->Late.T60[i].MidGain[1] = 0.0f;
616 BiquadFilter_clear(&State->Late.T60[i].HFFilter);
617 BiquadFilter_clear(&State->Late.T60[i].LFFilter);
620 for(i = 0;i < NUM_LINES;i++)
622 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
624 State->Early.CurrentGain[i][j] = 0.0f;
625 State->Early.PanGain[i][j] = 0.0f;
626 State->Late.CurrentGain[i][j] = 0.0f;
627 State->Late.PanGain[i][j] = 0.0f;
631 /* Reset counters and offset base. */
632 State->FadeCount = 0;
633 State->MaxUpdate[0] = MAX_UPDATE_SAMPLES;
634 State->MaxUpdate[1] = MAX_UPDATE_SAMPLES;
635 State->Offset = 0;
637 return AL_TRUE;
640 /**************************************
641 * Effect Update *
642 **************************************/
644 /* Calculate a decay coefficient given the length of each cycle and the time
645 * until the decay reaches -60 dB.
647 static inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
649 return powf(REVERB_DECAY_GAIN, length/decayTime);
652 /* Calculate a decay length from a coefficient and the time until the decay
653 * reaches -60 dB.
655 static inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
657 return log10f(coeff) * decayTime / log10f(REVERB_DECAY_GAIN);
660 /* Calculate an attenuation to be applied to the input of any echo models to
661 * compensate for modal density and decay time.
663 static inline ALfloat CalcDensityGain(const ALfloat a)
665 /* The energy of a signal can be obtained by finding the area under the
666 * squared signal. This takes the form of Sum(x_n^2), where x is the
667 * amplitude for the sample n.
669 * Decaying feedback matches exponential decay of the form Sum(a^n),
670 * where a is the attenuation coefficient, and n is the sample. The area
671 * under this decay curve can be calculated as: 1 / (1 - a).
673 * Modifying the above equation to find the area under the squared curve
674 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
675 * calculated by inverting the square root of this approximation,
676 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
678 return sqrtf(1.0f - a*a);
681 /* Calculate the scattering matrix coefficients given a diffusion factor. */
682 static inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
684 ALfloat n, t;
686 /* The matrix is of order 4, so n is sqrt(4 - 1). */
687 n = sqrtf(3.0f);
688 t = diffusion * atanf(n);
690 /* Calculate the first mixing matrix coefficient. */
691 *x = cosf(t);
692 /* Calculate the second mixing matrix coefficient. */
693 *y = sinf(t) / n;
696 /* Calculate the limited HF ratio for use with the late reverb low-pass
697 * filters.
699 static ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
700 const ALfloat decayTime, const ALfloat SpeedOfSound)
702 ALfloat limitRatio;
704 /* Find the attenuation due to air absorption in dB (converting delay
705 * time to meters using the speed of sound). Then reversing the decay
706 * equation, solve for HF ratio. The delay length is cancelled out of
707 * the equation, so it can be calculated once for all lines.
709 limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound);
711 /* Using the limit calculated above, apply the upper bound to the HF ratio.
713 return minf(limitRatio, hfRatio);
717 /* Calculates the 3-band T60 damping coefficients for a particular delay line
718 * of specified length, using a combination of two shelf filter sections given
719 * decay times for each band split at two reference frequencies.
721 static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime,
722 const ALfloat mfDecayTime, const ALfloat hfDecayTime,
723 const ALfloat lf0norm, const ALfloat hf0norm,
724 T60Filter *filter)
726 ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime);
727 ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime);
728 ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime);
730 filter->MidGain[1] = mfGain;
731 BiquadFilter_setParams(&filter->LFFilter, BiquadType::LowShelf, lfGain/mfGain, lf0norm,
732 calc_rcpQ_from_slope(lfGain/mfGain, 1.0f));
733 BiquadFilter_setParams(&filter->HFFilter, BiquadType::HighShelf, hfGain/mfGain, hf0norm,
734 calc_rcpQ_from_slope(hfGain/mfGain, 1.0f));
737 /* Update the offsets for the main effect delay line. */
738 static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ReverbState *State)
740 ALfloat multiplier, length;
741 ALuint i;
743 multiplier = CalcDelayLengthMult(density);
745 /* Early reflection taps are decorrelated by means of an average room
746 * reflection approximation described above the definition of the taps.
747 * This approximation is linear and so the above density multiplier can
748 * be applied to adjust the width of the taps. A single-band decay
749 * coefficient is applied to simulate initial attenuation and absorption.
751 * Late reverb taps are based on the late line lengths to allow a zero-
752 * delay path and offsets that would continue the propagation naturally
753 * into the late lines.
755 for(i = 0;i < NUM_LINES;i++)
757 length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
758 State->EarlyDelayTap[i][1] = float2int(length * frequency);
760 length = EARLY_TAP_LENGTHS[i]*multiplier;
761 State->EarlyDelayCoeff[i][1] = CalcDecayCoeff(length, decayTime);
763 length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
764 State->LateDelayTap[i][1] = State->LateFeedTap + float2int(length * frequency);
768 /* Update the early reflection line lengths and gain coefficients. */
769 static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat diffusion, const ALfloat decayTime, const ALuint frequency, EarlyReflections *Early)
771 ALfloat multiplier, length;
772 ALsizei i;
774 multiplier = CalcDelayLengthMult(density);
776 /* Calculate the all-pass feed-back/forward coefficient. */
777 Early->VecAp.Coeff = sqrtf(0.5f) * powf(diffusion, 2.0f);
779 for(i = 0;i < NUM_LINES;i++)
781 /* Calculate the length (in seconds) of each all-pass line. */
782 length = EARLY_ALLPASS_LENGTHS[i] * multiplier;
784 /* Calculate the delay offset for each all-pass line. */
785 Early->VecAp.Offset[i][1] = float2int(length * frequency);
787 /* Calculate the length (in seconds) of each delay line. */
788 length = EARLY_LINE_LENGTHS[i] * multiplier;
790 /* Calculate the delay offset for each delay line. */
791 Early->Offset[i][1] = float2int(length * frequency);
793 /* Calculate the gain (coefficient) for each line. */
794 Early->Coeff[i][1] = CalcDecayCoeff(length, decayTime);
798 /* Update the late reverb line lengths and T60 coefficients. */
799 static ALvoid UpdateLateLines(const ALfloat density, const ALfloat diffusion, const ALfloat lfDecayTime, const ALfloat mfDecayTime, const ALfloat hfDecayTime, const ALfloat lf0norm, const ALfloat hf0norm, const ALuint frequency, LateReverb *Late)
801 /* Scaling factor to convert the normalized reference frequencies from
802 * representing 0...freq to 0...max_reference.
804 const ALfloat norm_weight_factor = (ALfloat)frequency / AL_EAXREVERB_MAX_HFREFERENCE;
805 ALfloat multiplier, length, bandWeights[3];
806 ALsizei i;
808 /* To compensate for changes in modal density and decay time of the late
809 * reverb signal, the input is attenuated based on the maximal energy of
810 * the outgoing signal. This approximation is used to keep the apparent
811 * energy of the signal equal for all ranges of density and decay time.
813 * The average length of the delay lines is used to calculate the
814 * attenuation coefficient.
816 multiplier = CalcDelayLengthMult(density);
817 length = (LATE_LINE_LENGTHS[0] + LATE_LINE_LENGTHS[1] +
818 LATE_LINE_LENGTHS[2] + LATE_LINE_LENGTHS[3]) / 4.0f * multiplier;
819 length += (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
820 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f * multiplier;
821 /* The density gain calculation uses an average decay time weighted by
822 * approximate bandwidth. This attempts to compensate for losses of energy
823 * that reduce decay time due to scattering into highly attenuated bands.
825 bandWeights[0] = lf0norm*norm_weight_factor;
826 bandWeights[1] = hf0norm*norm_weight_factor - lf0norm*norm_weight_factor;
827 bandWeights[2] = 1.0f - hf0norm*norm_weight_factor;
828 Late->DensityGain[1] = CalcDensityGain(
829 CalcDecayCoeff(length,
830 bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime + bandWeights[2]*hfDecayTime
834 /* Calculate the all-pass feed-back/forward coefficient. */
835 Late->VecAp.Coeff = sqrtf(0.5f) * powf(diffusion, 2.0f);
837 for(i = 0;i < NUM_LINES;i++)
839 /* Calculate the length (in seconds) of each all-pass line. */
840 length = LATE_ALLPASS_LENGTHS[i] * multiplier;
842 /* Calculate the delay offset for each all-pass line. */
843 Late->VecAp.Offset[i][1] = float2int(length * frequency);
845 /* Calculate the length (in seconds) of each delay line. */
846 length = LATE_LINE_LENGTHS[i] * multiplier;
848 /* Calculate the delay offset for each delay line. */
849 Late->Offset[i][1] = float2int(length*frequency + 0.5f);
851 /* Approximate the absorption that the vector all-pass would exhibit
852 * given the current diffusion so we don't have to process a full T60
853 * filter for each of its four lines.
855 length += lerp(LATE_ALLPASS_LENGTHS[i],
856 (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
857 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f,
858 diffusion) * multiplier;
860 /* Calculate the T60 damping coefficients for each line. */
861 CalcT60DampingCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime,
862 lf0norm, hf0norm, &Late->T60[i]);
866 /* Creates a transform matrix given a reverb vector. The vector pans the reverb
867 * reflections toward the given direction, using its magnitude (up to 1) as a
868 * focal strength. This function results in a B-Format transformation matrix
869 * that spatially focuses the signal in the desired direction.
871 static aluMatrixf GetTransformFromVector(const ALfloat *vec)
873 aluMatrixf focus;
874 ALfloat norm[3];
875 ALfloat mag;
877 /* Normalize the panning vector according to the N3D scale, which has an
878 * extra sqrt(3) term on the directional components. Converting from OpenAL
879 * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
880 * that the reverb panning vectors use left-handed coordinates, unlike the
881 * rest of OpenAL which use right-handed. This is fixed by negating Z,
882 * which cancels out with the B-Format Z negation.
884 mag = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
885 if(mag > 1.0f)
887 norm[0] = vec[0] / mag * -SQRTF_3;
888 norm[1] = vec[1] / mag * SQRTF_3;
889 norm[2] = vec[2] / mag * SQRTF_3;
890 mag = 1.0f;
892 else
894 /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
895 * term. There's no need to renormalize the magnitude since it would
896 * just be reapplied in the matrix.
898 norm[0] = vec[0] * -SQRTF_3;
899 norm[1] = vec[1] * SQRTF_3;
900 norm[2] = vec[2] * SQRTF_3;
903 aluMatrixfSet(&focus,
904 1.0f, 0.0f, 0.0f, 0.0f,
905 norm[0], 1.0f-mag, 0.0f, 0.0f,
906 norm[1], 0.0f, 1.0f-mag, 0.0f,
907 norm[2], 0.0f, 0.0f, 1.0f-mag
910 return focus;
913 /* Update the early and late 3D panning gains. */
914 static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat earlyGain, const ALfloat lateGain, ReverbState *State)
916 aluMatrixf transform, rot;
917 ALsizei i;
919 STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
920 STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;
922 /* Note: _res is transposed. */
923 #define MATRIX_MULT(_res, _m1, _m2) do { \
924 int row, col; \
925 for(col = 0;col < 4;col++) \
927 for(row = 0;row < 4;row++) \
928 _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
929 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
931 } while(0)
932 /* Create a matrix that first converts A-Format to B-Format, then
933 * transforms the B-Format signal according to the panning vector.
935 rot = GetTransformFromVector(ReflectionsPan);
936 MATRIX_MULT(transform, rot, A2B);
937 memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
938 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
939 ComputePanGains(&Device->FOAOut, transform.m[i], earlyGain,
940 State->Early.PanGain[i]);
942 rot = GetTransformFromVector(LateReverbPan);
943 MATRIX_MULT(transform, rot, A2B);
944 memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
945 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
946 ComputePanGains(&Device->FOAOut, transform.m[i], lateGain,
947 State->Late.PanGain[i]);
948 #undef MATRIX_MULT
951 static void ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
953 const ALCdevice *Device = Context->Device;
954 const ALlistener &Listener = Context->Listener;
955 ALuint frequency = Device->Frequency;
956 ALfloat lf0norm, hf0norm, hfRatio;
957 ALfloat lfDecayTime, hfDecayTime;
958 ALfloat gain, gainlf, gainhf;
959 ALsizei i;
961 /* Calculate the master filters */
962 hf0norm = minf(props->Reverb.HFReference / frequency, 0.49f);
963 /* Restrict the filter gains from going below -60dB to keep the filter from
964 * killing most of the signal.
966 gainhf = maxf(props->Reverb.GainHF, 0.001f);
967 BiquadFilter_setParams(&State->Filter[0].Lp, BiquadType::HighShelf, gainhf, hf0norm,
968 calc_rcpQ_from_slope(gainhf, 1.0f));
969 lf0norm = minf(props->Reverb.LFReference / frequency, 0.49f);
970 gainlf = maxf(props->Reverb.GainLF, 0.001f);
971 BiquadFilter_setParams(&State->Filter[0].Hp, BiquadType::LowShelf, gainlf, lf0norm,
972 calc_rcpQ_from_slope(gainlf, 1.0f));
973 for(i = 1;i < NUM_LINES;i++)
975 BiquadFilter_copyParams(&State->Filter[i].Lp, &State->Filter[0].Lp);
976 BiquadFilter_copyParams(&State->Filter[i].Hp, &State->Filter[0].Hp);
979 /* Update the main effect delay and associated taps. */
980 UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
981 props->Reverb.Density, props->Reverb.DecayTime, frequency,
982 State);
984 /* Update the early lines. */
985 UpdateEarlyLines(props->Reverb.Density, props->Reverb.Diffusion,
986 props->Reverb.DecayTime, frequency, &State->Early);
988 /* Get the mixing matrix coefficients. */
989 CalcMatrixCoeffs(props->Reverb.Diffusion, &State->MixX, &State->MixY);
991 /* If the HF limit parameter is flagged, calculate an appropriate limit
992 * based on the air absorption parameter.
994 hfRatio = props->Reverb.DecayHFRatio;
995 if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
996 hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
997 props->Reverb.DecayTime, Listener.Params.ReverbSpeedOfSound
1000 /* Calculate the LF/HF decay times. */
1001 lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
1002 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1003 hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
1004 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1006 /* Update the late lines. */
1007 UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
1008 lfDecayTime, props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm,
1009 frequency, &State->Late
1012 /* Update early and late 3D panning. */
1013 gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
1014 Update3DPanning(Device, props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan,
1015 props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain,
1016 State);
1018 /* Calculate the max update size from the smallest relevant delay. */
1019 State->MaxUpdate[1] = mini(MAX_UPDATE_SAMPLES,
1020 mini(State->Early.Offset[0][1], State->Late.Offset[0][1])
1023 /* Determine if delay-line cross-fading is required. Density is essentially
1024 * a master control for the feedback delays, so changes the offsets of many
1025 * delay lines.
1027 if(State->Params.Density != props->Reverb.Density ||
1028 /* Diffusion and decay times influences the decay rate (gain) of the
1029 * late reverb T60 filter.
1031 State->Params.Diffusion != props->Reverb.Diffusion ||
1032 State->Params.DecayTime != props->Reverb.DecayTime ||
1033 State->Params.HFDecayTime != hfDecayTime ||
1034 State->Params.LFDecayTime != lfDecayTime ||
1035 /* HF/LF References control the weighting used to calculate the density
1036 * gain.
1038 State->Params.HFReference != props->Reverb.HFReference ||
1039 State->Params.LFReference != props->Reverb.LFReference)
1040 State->FadeCount = 0;
1041 State->Params.Density = props->Reverb.Density;
1042 State->Params.Diffusion = props->Reverb.Diffusion;
1043 State->Params.DecayTime = props->Reverb.DecayTime;
1044 State->Params.HFDecayTime = hfDecayTime;
1045 State->Params.LFDecayTime = lfDecayTime;
1046 State->Params.HFReference = props->Reverb.HFReference;
1047 State->Params.LFReference = props->Reverb.LFReference;
1051 /**************************************
1052 * Effect Processing *
1053 **************************************/
1055 /* Basic delay line input/output routines. */
1056 static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c)
1058 return Delay->Line[offset&Delay->Mask][c];
1061 /* Cross-faded delay line output routine. Instead of interpolating the
1062 * offsets, this interpolates (cross-fades) the outputs at each offset.
1064 static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0,
1065 const ALsizei off1, const ALsizei c,
1066 const ALfloat sc0, const ALfloat sc1)
1068 return Delay->Line[off0&Delay->Mask][c]*sc0 +
1069 Delay->Line[off1&Delay->Mask][c]*sc1;
1073 static inline void DelayLineIn(const DelayLineI *Delay, ALsizei offset, const ALsizei c,
1074 const ALfloat *RESTRICT in, ALsizei count)
1076 ALsizei i;
1077 for(i = 0;i < count;i++)
1078 Delay->Line[(offset++)&Delay->Mask][c] = *(in++);
1081 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1082 * for both the below vector all-pass model and to perform modal feed-back
1083 * delay network (FDN) mixing.
1085 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1086 * matrix with a single unitary rotational parameter:
1088 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1089 * [ -a, d, c, -b ]
1090 * [ -b, -c, d, a ]
1091 * [ -c, b, -a, d ]
1093 * The rotation is constructed from the effect's diffusion parameter,
1094 * yielding:
1096 * 1 = x^2 + 3 y^2
1098 * Where a, b, and c are the coefficient y with differing signs, and d is the
1099 * coefficient x. The final matrix is thus:
1101 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1102 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1103 * [ y, -y, x, y ] x = cos(t)
1104 * [ -y, -y, -y, x ] y = sin(t) / n
1106 * Any square orthogonal matrix with an order that is a power of two will
1107 * work (where ^T is transpose, ^-1 is inverse):
1109 * M^T = M^-1
1111 * Using that knowledge, finding an appropriate matrix can be accomplished
1112 * naively by searching all combinations of:
1114 * M = D + S - S^T
1116 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1117 * whose combination of signs are being iterated.
1119 static inline void VectorPartialScatter(ALfloat *RESTRICT out, const ALfloat *RESTRICT in,
1120 const ALfloat xCoeff, const ALfloat yCoeff)
1122 out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
1123 out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
1124 out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
1125 out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
1127 #define VectorScatterDelayIn(delay, o, in, xcoeff, ycoeff) \
1128 VectorPartialScatter((delay)->Line[(o)&(delay)->Mask], in, xcoeff, ycoeff)
1130 /* Utilizes the above, but reverses the input channels. */
1131 static inline void VectorScatterRevDelayIn(const DelayLineI *Delay, ALint offset,
1132 const ALfloat xCoeff, const ALfloat yCoeff,
1133 const ALfloat (*RESTRICT in)[MAX_UPDATE_SAMPLES],
1134 const ALsizei count)
1136 const DelayLineI delay = *Delay;
1137 ALsizei i, j;
1139 for(i = 0;i < count;++i)
1141 ALfloat f[NUM_LINES];
1142 for(j = 0;j < NUM_LINES;j++)
1143 f[NUM_LINES-1-j] = in[j][i];
1145 VectorScatterDelayIn(&delay, offset++, f, xCoeff, yCoeff);
1149 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1150 * filter to the 4-line input.
1152 * It works by vectorizing a regular all-pass filter and replacing the delay
1153 * element with a scattering matrix (like the one above) and a diagonal
1154 * matrix of delay elements.
1156 * Two static specializations are used for transitional (cross-faded) delay
1157 * line processing and non-transitional processing.
1159 static void VectorAllpass_Unfaded(ALfloat (*RESTRICT samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
1160 const ALfloat xCoeff, const ALfloat yCoeff, ALsizei todo,
1161 VecAllpass *Vap)
1163 const DelayLineI delay = Vap->Delay;
1164 const ALfloat feedCoeff = Vap->Coeff;
1165 ALsizei vap_offset[NUM_LINES];
1166 ALsizei i, j;
1168 ASSUME(todo > 0);
1170 for(j = 0;j < NUM_LINES;j++)
1171 vap_offset[j] = offset-Vap->Offset[j][0];
1172 for(i = 0;i < todo;i++)
1174 ALfloat f[NUM_LINES];
1176 for(j = 0;j < NUM_LINES;j++)
1178 ALfloat input = samples[j][i];
1179 ALfloat out = DelayLineOut(&delay, vap_offset[j]++, j) - feedCoeff*input;
1180 f[j] = input + feedCoeff*out;
1182 samples[j][i] = out;
1185 VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
1186 ++offset;
1189 static void VectorAllpass_Faded(ALfloat (*RESTRICT samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
1190 const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade,
1191 ALsizei todo, VecAllpass *Vap)
1193 const DelayLineI delay = Vap->Delay;
1194 const ALfloat feedCoeff = Vap->Coeff;
1195 ALsizei vap_offset[NUM_LINES][2];
1196 ALsizei i, j;
1198 ASSUME(todo > 0);
1200 fade *= 1.0f/FADE_SAMPLES;
1201 for(j = 0;j < NUM_LINES;j++)
1203 vap_offset[j][0] = offset-Vap->Offset[j][0];
1204 vap_offset[j][1] = offset-Vap->Offset[j][1];
1206 for(i = 0;i < todo;i++)
1208 ALfloat f[NUM_LINES];
1210 for(j = 0;j < NUM_LINES;j++)
1212 ALfloat input = samples[j][i];
1213 ALfloat out =
1214 FadedDelayLineOut(&delay, vap_offset[j][0]++, vap_offset[j][1]++, j,
1215 1.0f-fade, fade
1216 ) - feedCoeff*input;
1217 f[j] = input + feedCoeff*out;
1219 samples[j][i] = out;
1221 fade += FadeStep;
1223 VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
1224 ++offset;
1228 /* This generates early reflections.
1230 * This is done by obtaining the primary reflections (those arriving from the
1231 * same direction as the source) from the main delay line. These are
1232 * attenuated and all-pass filtered (based on the diffusion parameter).
1234 * The early lines are then fed in reverse (according to the approximately
1235 * opposite spatial location of the A-Format lines) to create the secondary
1236 * reflections (those arriving from the opposite direction as the source).
1238 * The early response is then completed by combining the primary reflections
1239 * with the delayed and attenuated output from the early lines.
1241 * Finally, the early response is reversed, scattered (based on diffusion),
1242 * and fed into the late reverb section of the main delay line.
1244 * Two static specializations are used for transitional (cross-faded) delay
1245 * line processing and non-transitional processing.
1247 static void EarlyReflection_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
1248 ALfloat (*RESTRICT out)[MAX_UPDATE_SAMPLES])
1250 ALfloat (*RESTRICT temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1251 const DelayLineI early_delay = State->Early.Delay;
1252 const DelayLineI main_delay = State->Delay;
1253 const ALfloat mixX = State->MixX;
1254 const ALfloat mixY = State->MixY;
1255 ALsizei late_feed_tap;
1256 ALsizei i, j;
1258 ASSUME(todo > 0);
1260 /* First, load decorrelated samples from the main delay line as the primary
1261 * reflections.
1263 for(j = 0;j < NUM_LINES;j++)
1265 ALsizei early_delay_tap = offset - State->EarlyDelayTap[j][0];
1266 ALfloat coeff = State->EarlyDelayCoeff[j][0];
1267 for(i = 0;i < todo;i++)
1268 temps[j][i] = DelayLineOut(&main_delay, early_delay_tap++, j) * coeff;
1271 /* Apply a vector all-pass, to help color the initial reflections based on
1272 * the diffusion strength.
1274 VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Early.VecAp);
1276 /* Apply a delay and bounce to generate secondary reflections, combine with
1277 * the primary reflections and write out the result for mixing.
1279 for(j = 0;j < NUM_LINES;j++)
1281 ALint early_feedb_tap = offset - State->Early.Offset[j][0];
1282 ALfloat early_feedb_coeff = State->Early.Coeff[j][0];
1284 for(i = 0;i < todo;i++)
1285 out[j][i] = DelayLineOut(&early_delay, early_feedb_tap++, j)*early_feedb_coeff +
1286 temps[j][i];
1288 for(j = 0;j < NUM_LINES;j++)
1289 DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
1291 /* Also write the result back to the main delay line for the late reverb
1292 * stage to pick up at the appropriate time, appplying a scatter and
1293 * bounce to improve the initial diffusion in the late reverb.
1295 late_feed_tap = offset - State->LateFeedTap;
1296 VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
1298 static void EarlyReflection_Faded(ReverbState *State, ALsizei offset, const ALsizei todo,
1299 const ALfloat fade, ALfloat (*RESTRICT out)[MAX_UPDATE_SAMPLES])
1301 ALfloat (*RESTRICT temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1302 const DelayLineI early_delay = State->Early.Delay;
1303 const DelayLineI main_delay = State->Delay;
1304 const ALfloat mixX = State->MixX;
1305 const ALfloat mixY = State->MixY;
1306 ALsizei late_feed_tap;
1307 ALsizei i, j;
1309 ASSUME(todo > 0);
1311 for(j = 0;j < NUM_LINES;j++)
1313 ALsizei early_delay_tap0 = offset - State->EarlyDelayTap[j][0];
1314 ALsizei early_delay_tap1 = offset - State->EarlyDelayTap[j][1];
1315 ALfloat oldCoeff = State->EarlyDelayCoeff[j][0];
1316 ALfloat oldCoeffStep = -oldCoeff / FADE_SAMPLES;
1317 ALfloat newCoeffStep = State->EarlyDelayCoeff[j][1] / FADE_SAMPLES;
1318 ALfloat fadeCount = fade;
1320 for(i = 0;i < todo;i++)
1322 const ALfloat fade0 = oldCoeff + oldCoeffStep*fadeCount;
1323 const ALfloat fade1 = newCoeffStep*fadeCount;
1324 temps[j][i] = FadedDelayLineOut(&main_delay,
1325 early_delay_tap0++, early_delay_tap1++, j, fade0, fade1
1327 fadeCount += 1.0f;
1331 VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->Early.VecAp);
1333 for(j = 0;j < NUM_LINES;j++)
1335 ALint feedb_tap0 = offset - State->Early.Offset[j][0];
1336 ALint feedb_tap1 = offset - State->Early.Offset[j][1];
1337 ALfloat feedb_oldCoeff = State->Early.Coeff[j][0];
1338 ALfloat feedb_oldCoeffStep = -feedb_oldCoeff / FADE_SAMPLES;
1339 ALfloat feedb_newCoeffStep = State->Early.Coeff[j][1] / FADE_SAMPLES;
1340 ALfloat fadeCount = fade;
1342 for(i = 0;i < todo;i++)
1344 const ALfloat fade0 = feedb_oldCoeff + feedb_oldCoeffStep*fadeCount;
1345 const ALfloat fade1 = feedb_newCoeffStep*fadeCount;
1346 out[j][i] = FadedDelayLineOut(&early_delay,
1347 feedb_tap0++, feedb_tap1++, j, fade0, fade1
1348 ) + temps[j][i];
1349 fadeCount += 1.0f;
1352 for(j = 0;j < NUM_LINES;j++)
1353 DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
1355 late_feed_tap = offset - State->LateFeedTap;
1356 VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
1359 /* Applies the two T60 damping filter sections. */
1360 static inline void LateT60Filter(ALfloat *RESTRICT samples, const ALsizei todo, T60Filter *filter)
1362 ALfloat temp[MAX_UPDATE_SAMPLES];
1363 BiquadFilter_process(&filter->HFFilter, temp, samples, todo);
1364 BiquadFilter_process(&filter->LFFilter, samples, temp, todo);
1367 /* This generates the reverb tail using a modified feed-back delay network
1368 * (FDN).
1370 * Results from the early reflections are mixed with the output from the late
1371 * delay lines.
1373 * The late response is then completed by T60 and all-pass filtering the mix.
1375 * Finally, the lines are reversed (so they feed their opposite directions)
1376 * and scattered with the FDN matrix before re-feeding the delay lines.
1378 * Two variations are made, one for for transitional (cross-faded) delay line
1379 * processing and one for non-transitional processing.
1381 static void LateReverb_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
1382 ALfloat (*RESTRICT out)[MAX_UPDATE_SAMPLES])
1384 ALfloat (*RESTRICT temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1385 const DelayLineI late_delay = State->Late.Delay;
1386 const DelayLineI main_delay = State->Delay;
1387 const ALfloat mixX = State->MixX;
1388 const ALfloat mixY = State->MixY;
1389 ALsizei i, j;
1391 ASSUME(todo > 0);
1393 /* First, load decorrelated samples from the main and feedback delay lines.
1394 * Filter the signal to apply its frequency-dependent decay.
1396 for(j = 0;j < NUM_LINES;j++)
1398 ALsizei late_delay_tap = offset - State->LateDelayTap[j][0];
1399 ALsizei late_feedb_tap = offset - State->Late.Offset[j][0];
1400 ALfloat midGain = State->Late.T60[j].MidGain[0];
1401 const ALfloat densityGain = State->Late.DensityGain[0] * midGain;
1402 for(i = 0;i < todo;i++)
1403 temps[j][i] = DelayLineOut(&main_delay, late_delay_tap++, j)*densityGain +
1404 DelayLineOut(&late_delay, late_feedb_tap++, j)*midGain;
1405 LateT60Filter(temps[j], todo, &State->Late.T60[j]);
1408 /* Apply a vector all-pass to improve micro-surface diffusion, and write
1409 * out the results for mixing.
1411 VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Late.VecAp);
1413 for(j = 0;j < NUM_LINES;j++)
1414 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1416 /* Finally, scatter and bounce the results to refeed the feedback buffer. */
1417 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, out, todo);
1419 static void LateReverb_Faded(ReverbState *State, ALsizei offset, const ALsizei todo,
1420 const ALfloat fade, ALfloat (*RESTRICT out)[MAX_UPDATE_SAMPLES])
1422 ALfloat (*RESTRICT temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1423 const DelayLineI late_delay = State->Late.Delay;
1424 const DelayLineI main_delay = State->Delay;
1425 const ALfloat mixX = State->MixX;
1426 const ALfloat mixY = State->MixY;
1427 ALsizei i, j;
1429 ASSUME(todo > 0);
1431 for(j = 0;j < NUM_LINES;j++)
1433 const ALfloat oldMidGain = State->Late.T60[j].MidGain[0];
1434 const ALfloat midGain = State->Late.T60[j].MidGain[1];
1435 const ALfloat oldMidStep = -oldMidGain / FADE_SAMPLES;
1436 const ALfloat midStep = midGain / FADE_SAMPLES;
1437 const ALfloat oldDensityGain = State->Late.DensityGain[0] * oldMidGain;
1438 const ALfloat densityGain = State->Late.DensityGain[1] * midGain;
1439 const ALfloat oldDensityStep = -oldDensityGain / FADE_SAMPLES;
1440 const ALfloat densityStep = densityGain / FADE_SAMPLES;
1441 ALsizei late_delay_tap0 = offset - State->LateDelayTap[j][0];
1442 ALsizei late_delay_tap1 = offset - State->LateDelayTap[j][1];
1443 ALsizei late_feedb_tap0 = offset - State->Late.Offset[j][0];
1444 ALsizei late_feedb_tap1 = offset - State->Late.Offset[j][1];
1445 ALfloat fadeCount = fade;
1447 for(i = 0;i < todo;i++)
1449 const ALfloat fade0 = oldDensityGain + oldDensityStep*fadeCount;
1450 const ALfloat fade1 = densityStep*fadeCount;
1451 const ALfloat gfade0 = oldMidGain + oldMidStep*fadeCount;
1452 const ALfloat gfade1 = midStep*fadeCount;
1453 temps[j][i] =
1454 FadedDelayLineOut(&main_delay, late_delay_tap0++, late_delay_tap1++, j,
1455 fade0, fade1) +
1456 FadedDelayLineOut(&late_delay, late_feedb_tap0++, late_feedb_tap1++, j,
1457 gfade0, gfade1);
1458 fadeCount += 1.0f;
1460 LateT60Filter(temps[j], todo, &State->Late.T60[j]);
1463 VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->Late.VecAp);
1465 for(j = 0;j < NUM_LINES;j++)
1466 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1468 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, temps, todo);
1471 static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
1473 ALfloat (*RESTRICT afmt)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1474 ALfloat (*RESTRICT samples)[MAX_UPDATE_SAMPLES] = State->MixSamples;
1475 ALsizei fadeCount = State->FadeCount;
1476 ALsizei offset = State->Offset;
1477 ALsizei base, c;
1479 /* Process reverb for these samples. */
1480 for(base = 0;base < SamplesToDo;)
1482 ALsizei todo = SamplesToDo - base;
1483 /* If cross-fading, don't do more samples than there are to fade. */
1484 if(FADE_SAMPLES-fadeCount > 0)
1486 todo = mini(todo, FADE_SAMPLES-fadeCount);
1487 todo = mini(todo, State->MaxUpdate[0]);
1489 todo = mini(todo, State->MaxUpdate[1]);
1490 /* If this is not the final update, ensure the update size is a
1491 * multiple of 4 for the SIMD mixers.
1493 if(todo < SamplesToDo-base)
1494 todo &= ~3;
1496 /* Convert B-Format to A-Format for processing. */
1497 memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
1498 for(c = 0;c < NUM_LINES;c++)
1499 MixRowSamples(afmt[c], B2A.m[c],
1500 SamplesIn, MAX_EFFECT_CHANNELS, base, todo
1503 /* Process the samples for reverb. */
1504 for(c = 0;c < NUM_LINES;c++)
1506 /* Band-pass the incoming samples. */
1507 BiquadFilter_process(&State->Filter[c].Lp, samples[0], afmt[c], todo);
1508 BiquadFilter_process(&State->Filter[c].Hp, samples[1], samples[0], todo);
1510 /* Feed the initial delay line. */
1511 DelayLineIn(&State->Delay, offset, c, samples[1], todo);
1514 if(UNLIKELY(fadeCount < FADE_SAMPLES))
1516 ALfloat fade = (ALfloat)fadeCount;
1518 /* Generate early reflections. */
1519 EarlyReflection_Faded(State, offset, todo, fade, samples);
1520 /* Mix the A-Format results to output, implicitly converting back
1521 * to B-Format.
1523 for(c = 0;c < NUM_LINES;c++)
1524 MixSamples(samples[c], NumChannels, SamplesOut,
1525 State->Early.CurrentGain[c], State->Early.PanGain[c],
1526 SamplesToDo-base, base, todo
1529 /* Generate and mix late reverb. */
1530 LateReverb_Faded(State, offset, todo, fade, samples);
1531 for(c = 0;c < NUM_LINES;c++)
1532 MixSamples(samples[c], NumChannels, SamplesOut,
1533 State->Late.CurrentGain[c], State->Late.PanGain[c],
1534 SamplesToDo-base, base, todo
1537 /* Step fading forward. */
1538 fadeCount += todo;
1539 if(LIKELY(fadeCount >= FADE_SAMPLES))
1541 /* Update the cross-fading delay line taps. */
1542 fadeCount = FADE_SAMPLES;
1543 for(c = 0;c < NUM_LINES;c++)
1545 State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
1546 State->EarlyDelayCoeff[c][0] = State->EarlyDelayCoeff[c][1];
1547 State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1];
1548 State->Early.Offset[c][0] = State->Early.Offset[c][1];
1549 State->Early.Coeff[c][0] = State->Early.Coeff[c][1];
1550 State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
1551 State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1];
1552 State->Late.Offset[c][0] = State->Late.Offset[c][1];
1553 State->Late.T60[c].MidGain[0] = State->Late.T60[c].MidGain[1];
1555 State->Late.DensityGain[0] = State->Late.DensityGain[1];
1556 State->MaxUpdate[0] = State->MaxUpdate[1];
1559 else
1561 /* Generate and mix early reflections. */
1562 EarlyReflection_Unfaded(State, offset, todo, samples);
1563 for(c = 0;c < NUM_LINES;c++)
1564 MixSamples(samples[c], NumChannels, SamplesOut,
1565 State->Early.CurrentGain[c], State->Early.PanGain[c],
1566 SamplesToDo-base, base, todo
1569 /* Generate and mix late reverb. */
1570 LateReverb_Unfaded(State, offset, todo, samples);
1571 for(c = 0;c < NUM_LINES;c++)
1572 MixSamples(samples[c], NumChannels, SamplesOut,
1573 State->Late.CurrentGain[c], State->Late.PanGain[c],
1574 SamplesToDo-base, base, todo
1578 /* Step all delays forward. */
1579 offset += todo;
1581 base += todo;
1583 State->Offset = offset;
1584 State->FadeCount = fadeCount;
1588 struct ReverbStateFactory final : public EffectStateFactory {
1589 ALeffectState *create() override;
1592 ALeffectState *ReverbStateFactory::create()
1594 ReverbState *state;
1595 NEW_OBJ0(state, ReverbState)();
1596 return state;
1599 EffectStateFactory *ReverbStateFactory_getFactory(void)
1601 static ReverbStateFactory ReverbFactory{};
1602 return &ReverbFactory;
1606 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1608 ALeffectProps *props = &effect->Props;
1609 switch(param)
1611 case AL_EAXREVERB_DECAY_HFLIMIT:
1612 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1613 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
1614 props->Reverb.DecayHFLimit = val;
1615 break;
1617 default:
1618 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1619 param);
1622 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1623 { ALeaxreverb_setParami(effect, context, param, vals[0]); }
1624 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1626 ALeffectProps *props = &effect->Props;
1627 switch(param)
1629 case AL_EAXREVERB_DENSITY:
1630 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1631 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
1632 props->Reverb.Density = val;
1633 break;
1635 case AL_EAXREVERB_DIFFUSION:
1636 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1637 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
1638 props->Reverb.Diffusion = val;
1639 break;
1641 case AL_EAXREVERB_GAIN:
1642 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1643 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
1644 props->Reverb.Gain = val;
1645 break;
1647 case AL_EAXREVERB_GAINHF:
1648 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1649 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
1650 props->Reverb.GainHF = val;
1651 break;
1653 case AL_EAXREVERB_GAINLF:
1654 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1655 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
1656 props->Reverb.GainLF = val;
1657 break;
1659 case AL_EAXREVERB_DECAY_TIME:
1660 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1661 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
1662 props->Reverb.DecayTime = val;
1663 break;
1665 case AL_EAXREVERB_DECAY_HFRATIO:
1666 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1667 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
1668 props->Reverb.DecayHFRatio = val;
1669 break;
1671 case AL_EAXREVERB_DECAY_LFRATIO:
1672 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1673 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
1674 props->Reverb.DecayLFRatio = val;
1675 break;
1677 case AL_EAXREVERB_REFLECTIONS_GAIN:
1678 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1679 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
1680 props->Reverb.ReflectionsGain = val;
1681 break;
1683 case AL_EAXREVERB_REFLECTIONS_DELAY:
1684 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1685 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
1686 props->Reverb.ReflectionsDelay = val;
1687 break;
1689 case AL_EAXREVERB_LATE_REVERB_GAIN:
1690 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1691 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
1692 props->Reverb.LateReverbGain = val;
1693 break;
1695 case AL_EAXREVERB_LATE_REVERB_DELAY:
1696 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1697 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
1698 props->Reverb.LateReverbDelay = val;
1699 break;
1701 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1702 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1703 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
1704 props->Reverb.AirAbsorptionGainHF = val;
1705 break;
1707 case AL_EAXREVERB_ECHO_TIME:
1708 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1709 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
1710 props->Reverb.EchoTime = val;
1711 break;
1713 case AL_EAXREVERB_ECHO_DEPTH:
1714 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1715 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
1716 props->Reverb.EchoDepth = val;
1717 break;
1719 case AL_EAXREVERB_MODULATION_TIME:
1720 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1721 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
1722 props->Reverb.ModulationTime = val;
1723 break;
1725 case AL_EAXREVERB_MODULATION_DEPTH:
1726 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1727 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
1728 props->Reverb.ModulationDepth = val;
1729 break;
1731 case AL_EAXREVERB_HFREFERENCE:
1732 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1733 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
1734 props->Reverb.HFReference = val;
1735 break;
1737 case AL_EAXREVERB_LFREFERENCE:
1738 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1739 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
1740 props->Reverb.LFReference = val;
1741 break;
1743 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1744 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1745 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
1746 props->Reverb.RoomRolloffFactor = val;
1747 break;
1749 default:
1750 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1751 param);
1754 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1756 ALeffectProps *props = &effect->Props;
1757 switch(param)
1759 case AL_EAXREVERB_REFLECTIONS_PAN:
1760 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1761 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
1762 props->Reverb.ReflectionsPan[0] = vals[0];
1763 props->Reverb.ReflectionsPan[1] = vals[1];
1764 props->Reverb.ReflectionsPan[2] = vals[2];
1765 break;
1766 case AL_EAXREVERB_LATE_REVERB_PAN:
1767 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1768 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
1769 props->Reverb.LateReverbPan[0] = vals[0];
1770 props->Reverb.LateReverbPan[1] = vals[1];
1771 props->Reverb.LateReverbPan[2] = vals[2];
1772 break;
1774 default:
1775 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1776 break;
1780 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1782 const ALeffectProps *props = &effect->Props;
1783 switch(param)
1785 case AL_EAXREVERB_DECAY_HFLIMIT:
1786 *val = props->Reverb.DecayHFLimit;
1787 break;
1789 default:
1790 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1791 param);
1794 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1795 { ALeaxreverb_getParami(effect, context, param, vals); }
1796 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1798 const ALeffectProps *props = &effect->Props;
1799 switch(param)
1801 case AL_EAXREVERB_DENSITY:
1802 *val = props->Reverb.Density;
1803 break;
1805 case AL_EAXREVERB_DIFFUSION:
1806 *val = props->Reverb.Diffusion;
1807 break;
1809 case AL_EAXREVERB_GAIN:
1810 *val = props->Reverb.Gain;
1811 break;
1813 case AL_EAXREVERB_GAINHF:
1814 *val = props->Reverb.GainHF;
1815 break;
1817 case AL_EAXREVERB_GAINLF:
1818 *val = props->Reverb.GainLF;
1819 break;
1821 case AL_EAXREVERB_DECAY_TIME:
1822 *val = props->Reverb.DecayTime;
1823 break;
1825 case AL_EAXREVERB_DECAY_HFRATIO:
1826 *val = props->Reverb.DecayHFRatio;
1827 break;
1829 case AL_EAXREVERB_DECAY_LFRATIO:
1830 *val = props->Reverb.DecayLFRatio;
1831 break;
1833 case AL_EAXREVERB_REFLECTIONS_GAIN:
1834 *val = props->Reverb.ReflectionsGain;
1835 break;
1837 case AL_EAXREVERB_REFLECTIONS_DELAY:
1838 *val = props->Reverb.ReflectionsDelay;
1839 break;
1841 case AL_EAXREVERB_LATE_REVERB_GAIN:
1842 *val = props->Reverb.LateReverbGain;
1843 break;
1845 case AL_EAXREVERB_LATE_REVERB_DELAY:
1846 *val = props->Reverb.LateReverbDelay;
1847 break;
1849 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1850 *val = props->Reverb.AirAbsorptionGainHF;
1851 break;
1853 case AL_EAXREVERB_ECHO_TIME:
1854 *val = props->Reverb.EchoTime;
1855 break;
1857 case AL_EAXREVERB_ECHO_DEPTH:
1858 *val = props->Reverb.EchoDepth;
1859 break;
1861 case AL_EAXREVERB_MODULATION_TIME:
1862 *val = props->Reverb.ModulationTime;
1863 break;
1865 case AL_EAXREVERB_MODULATION_DEPTH:
1866 *val = props->Reverb.ModulationDepth;
1867 break;
1869 case AL_EAXREVERB_HFREFERENCE:
1870 *val = props->Reverb.HFReference;
1871 break;
1873 case AL_EAXREVERB_LFREFERENCE:
1874 *val = props->Reverb.LFReference;
1875 break;
1877 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1878 *val = props->Reverb.RoomRolloffFactor;
1879 break;
1881 default:
1882 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1883 param);
1886 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
1888 const ALeffectProps *props = &effect->Props;
1889 switch(param)
1891 case AL_EAXREVERB_REFLECTIONS_PAN:
1892 vals[0] = props->Reverb.ReflectionsPan[0];
1893 vals[1] = props->Reverb.ReflectionsPan[1];
1894 vals[2] = props->Reverb.ReflectionsPan[2];
1895 break;
1896 case AL_EAXREVERB_LATE_REVERB_PAN:
1897 vals[0] = props->Reverb.LateReverbPan[0];
1898 vals[1] = props->Reverb.LateReverbPan[1];
1899 vals[2] = props->Reverb.LateReverbPan[2];
1900 break;
1902 default:
1903 ALeaxreverb_getParamf(effect, context, param, vals);
1904 break;
1908 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
1910 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1912 ALeffectProps *props = &effect->Props;
1913 switch(param)
1915 case AL_REVERB_DECAY_HFLIMIT:
1916 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
1917 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
1918 props->Reverb.DecayHFLimit = val;
1919 break;
1921 default:
1922 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
1925 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1926 { ALreverb_setParami(effect, context, param, vals[0]); }
1927 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1929 ALeffectProps *props = &effect->Props;
1930 switch(param)
1932 case AL_REVERB_DENSITY:
1933 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
1934 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
1935 props->Reverb.Density = val;
1936 break;
1938 case AL_REVERB_DIFFUSION:
1939 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
1940 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
1941 props->Reverb.Diffusion = val;
1942 break;
1944 case AL_REVERB_GAIN:
1945 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
1946 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
1947 props->Reverb.Gain = val;
1948 break;
1950 case AL_REVERB_GAINHF:
1951 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
1952 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
1953 props->Reverb.GainHF = val;
1954 break;
1956 case AL_REVERB_DECAY_TIME:
1957 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
1958 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
1959 props->Reverb.DecayTime = val;
1960 break;
1962 case AL_REVERB_DECAY_HFRATIO:
1963 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
1964 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
1965 props->Reverb.DecayHFRatio = val;
1966 break;
1968 case AL_REVERB_REFLECTIONS_GAIN:
1969 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
1970 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
1971 props->Reverb.ReflectionsGain = val;
1972 break;
1974 case AL_REVERB_REFLECTIONS_DELAY:
1975 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
1976 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
1977 props->Reverb.ReflectionsDelay = val;
1978 break;
1980 case AL_REVERB_LATE_REVERB_GAIN:
1981 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
1982 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
1983 props->Reverb.LateReverbGain = val;
1984 break;
1986 case AL_REVERB_LATE_REVERB_DELAY:
1987 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
1988 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
1989 props->Reverb.LateReverbDelay = val;
1990 break;
1992 case AL_REVERB_AIR_ABSORPTION_GAINHF:
1993 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
1994 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
1995 props->Reverb.AirAbsorptionGainHF = val;
1996 break;
1998 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
1999 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
2000 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
2001 props->Reverb.RoomRolloffFactor = val;
2002 break;
2004 default:
2005 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2008 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
2009 { ALreverb_setParamf(effect, context, param, vals[0]); }
2011 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
2013 const ALeffectProps *props = &effect->Props;
2014 switch(param)
2016 case AL_REVERB_DECAY_HFLIMIT:
2017 *val = props->Reverb.DecayHFLimit;
2018 break;
2020 default:
2021 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
2024 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
2025 { ALreverb_getParami(effect, context, param, vals); }
2026 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
2028 const ALeffectProps *props = &effect->Props;
2029 switch(param)
2031 case AL_REVERB_DENSITY:
2032 *val = props->Reverb.Density;
2033 break;
2035 case AL_REVERB_DIFFUSION:
2036 *val = props->Reverb.Diffusion;
2037 break;
2039 case AL_REVERB_GAIN:
2040 *val = props->Reverb.Gain;
2041 break;
2043 case AL_REVERB_GAINHF:
2044 *val = props->Reverb.GainHF;
2045 break;
2047 case AL_REVERB_DECAY_TIME:
2048 *val = props->Reverb.DecayTime;
2049 break;
2051 case AL_REVERB_DECAY_HFRATIO:
2052 *val = props->Reverb.DecayHFRatio;
2053 break;
2055 case AL_REVERB_REFLECTIONS_GAIN:
2056 *val = props->Reverb.ReflectionsGain;
2057 break;
2059 case AL_REVERB_REFLECTIONS_DELAY:
2060 *val = props->Reverb.ReflectionsDelay;
2061 break;
2063 case AL_REVERB_LATE_REVERB_GAIN:
2064 *val = props->Reverb.LateReverbGain;
2065 break;
2067 case AL_REVERB_LATE_REVERB_DELAY:
2068 *val = props->Reverb.LateReverbDelay;
2069 break;
2071 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2072 *val = props->Reverb.AirAbsorptionGainHF;
2073 break;
2075 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2076 *val = props->Reverb.RoomRolloffFactor;
2077 break;
2079 default:
2080 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2083 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2084 { ALreverb_getParamf(effect, context, param, vals); }
2086 DEFINE_ALEFFECT_VTABLE(ALreverb);