Avoid a couple line count assumptions
[openal-soft.git] / Alc / effects / reverb.c
blobc033e4f0c248a7273d8e5849854d52abf38f5913
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 "alu.h"
29 #include "alAuxEffectSlot.h"
30 #include "alListener.h"
31 #include "alError.h"
32 #include "filters/defs.h"
34 /* This is a user config option for modifying the overall output of the reverb
35 * effect.
37 ALfloat ReverbBoost = 1.0f;
39 /* This is the maximum number of samples processed for each inner loop
40 * iteration. */
41 #define MAX_UPDATE_SAMPLES 256
43 /* The number of samples used for cross-faded delay lines. This can be used
44 * to balance the compensation for abrupt line changes and attenuation due to
45 * minimally lengthed recursive lines. Try to keep this below the device
46 * update size.
48 #define FADE_SAMPLES 128
50 /* The number of spatialized lines or channels to process. Four channels allows
51 * for a 3D A-Format response. NOTE: This can't be changed without taking care
52 * of the conversion matrices, and a few places where the length arrays are
53 * assumed to have 4 elements.
55 #define NUM_LINES 4
58 /* The B-Format to A-Format conversion matrix. The arrangement of rows is
59 * deliberately chosen to align the resulting lines to their spatial opposites
60 * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
61 * back left). It's not quite opposite, since the A-Format results in a
62 * tetrahedron, but it's close enough. Should the model be extended to 8-lines
63 * in the future, true opposites can be used.
65 static const aluMatrixf B2A = {{
66 { 0.288675134595f, 0.288675134595f, 0.288675134595f, 0.288675134595f },
67 { 0.288675134595f, -0.288675134595f, -0.288675134595f, 0.288675134595f },
68 { 0.288675134595f, 0.288675134595f, -0.288675134595f, -0.288675134595f },
69 { 0.288675134595f, -0.288675134595f, 0.288675134595f, -0.288675134595f }
70 }};
72 /* Converts A-Format to B-Format. */
73 static const aluMatrixf A2B = {{
74 { 0.866025403785f, 0.866025403785f, 0.866025403785f, 0.866025403785f },
75 { 0.866025403785f, -0.866025403785f, 0.866025403785f, -0.866025403785f },
76 { 0.866025403785f, -0.866025403785f, -0.866025403785f, 0.866025403785f },
77 { 0.866025403785f, 0.866025403785f, -0.866025403785f, -0.866025403785f }
78 }};
80 static const ALfloat FadeStep = 1.0f / FADE_SAMPLES;
82 /* The all-pass and delay lines have a variable length dependent on the
83 * effect's density parameter, which helps alter the perceived environment
84 * size. The size-to-density conversion is a cubed scale:
86 * density = min(1.0, pow(size, 3.0) / DENSITY_SCALE);
88 * The line lengths scale linearly with room size, so the inverse density
89 * conversion is needed, taking the cube root of the re-scaled density to
90 * calculate the line length multiplier:
92 * length_mult = max(5.0, cbrtf(density*DENSITY_SCALE));
94 * The density scale below will result in a max line multiplier of 50, for an
95 * effective size range of 5m to 50m.
97 static const ALfloat DENSITY_SCALE = 125000.0f;
99 /* All delay line lengths are specified in seconds.
101 * To approximate early reflections, we break them up into primary (those
102 * arriving from the same direction as the source) and secondary (those
103 * arriving from the opposite direction).
105 * The early taps decorrelate the 4-channel signal to approximate an average
106 * room response for the primary reflections after the initial early delay.
108 * Given an average room dimension (d_a) and the speed of sound (c) we can
109 * calculate the average reflection delay (r_a) regardless of listener and
110 * source positions as:
112 * r_a = d_a / c
113 * c = 343.3
115 * This can extended to finding the average difference (r_d) between the
116 * maximum (r_1) and minimum (r_0) reflection delays:
118 * r_0 = 2 / 3 r_a
119 * = r_a - r_d / 2
120 * = r_d
121 * r_1 = 4 / 3 r_a
122 * = r_a + r_d / 2
123 * = 2 r_d
124 * r_d = 2 / 3 r_a
125 * = r_1 - r_0
127 * As can be determined by integrating the 1D model with a source (s) and
128 * listener (l) positioned across the dimension of length (d_a):
130 * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
132 * The initial taps (T_(i=0)^N) are then specified by taking a power series
133 * that ranges between r_0 and half of r_1 less r_0:
135 * R_i = 2^(i / (2 N - 1)) r_d
136 * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
137 * = r_0 + T_i
138 * T_i = R_i - r_0
139 * = (2^(i / (2 N - 1)) - 1) r_d
141 * Assuming an average of 1m, we get the following taps:
143 static const ALfloat EARLY_TAP_LENGTHS[NUM_LINES] =
145 0.0000000e+0f, 2.0213520e-4f, 4.2531060e-4f, 6.7171600e-4f
148 /* The early all-pass filter lengths are based on the early tap lengths:
150 * A_i = R_i / a
152 * Where a is the approximate maximum all-pass cycle limit (20).
154 static const ALfloat EARLY_ALLPASS_LENGTHS[NUM_LINES] =
156 9.7096800e-5f, 1.0720356e-4f, 1.1836234e-4f, 1.3068260e-4f
159 /* The early delay lines are used to transform the primary reflections into
160 * the secondary reflections. The A-format is arranged in such a way that
161 * the channels/lines are spatially opposite:
163 * C_i is opposite C_(N-i-1)
165 * The delays of the two opposing reflections (R_i and O_i) from a source
166 * anywhere along a particular dimension always sum to twice its full delay:
168 * 2 r_a = R_i + O_i
170 * With that in mind we can determine the delay between the two reflections
171 * and thus specify our early line lengths (L_(i=0)^N) using:
173 * O_i = 2 r_a - R_(N-i-1)
174 * L_i = O_i - R_(N-i-1)
175 * = 2 (r_a - R_(N-i-1))
176 * = 2 (r_a - T_(N-i-1) - r_0)
177 * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
179 * Using an average dimension of 1m, we get:
181 static const ALfloat EARLY_LINE_LENGTHS[NUM_LINES] =
183 5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f
186 /* The late all-pass filter lengths are based on the late line lengths:
188 * A_i = (5 / 3) L_i / r_1
190 static const ALfloat LATE_ALLPASS_LENGTHS[NUM_LINES] =
192 1.6182800e-4f, 2.0389060e-4f, 2.8159360e-4f, 3.2365600e-4f
195 /* The late lines are used to approximate the decaying cycle of recursive
196 * late reflections.
198 * Splitting the lines in half, we start with the shortest reflection paths
199 * (L_(i=0)^(N/2)):
201 * L_i = 2^(i / (N - 1)) r_d
203 * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
205 * L_i = 2 r_a - L_(i-N/2)
206 * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
208 * For our 1m average room, we get:
210 static const ALfloat LATE_LINE_LENGTHS[NUM_LINES] =
212 1.9419362e-3f, 2.4466860e-3f, 3.3791220e-3f, 3.8838720e-3f
216 typedef struct DelayLineI {
217 /* The delay lines use interleaved samples, with the lengths being powers
218 * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
220 ALsizei Mask;
221 ALfloat (*Line)[NUM_LINES];
222 } DelayLineI;
224 typedef struct VecAllpass {
225 DelayLineI Delay;
226 ALfloat Coeff;
227 ALsizei Offset[NUM_LINES][2];
228 } VecAllpass;
230 typedef struct T60Filter {
231 /* Two filters are used to adjust the signal. One to control the low
232 * frequencies, and one to control the high frequencies.
234 ALfloat MidGain[2];
235 BiquadFilter HFFilter, LFFilter;
236 } T60Filter;
238 typedef struct EarlyReflections {
239 /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
240 * The spread from this filter also helps smooth out the reverb tail.
242 VecAllpass VecAp;
244 /* An echo line is used to complete the second half of the early
245 * reflections.
247 DelayLineI Delay;
248 ALsizei Offset[NUM_LINES][2];
249 ALfloat Coeff[NUM_LINES][2];
251 /* The gain for each output channel based on 3D panning. */
252 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
253 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
254 } EarlyReflections;
256 typedef struct LateReverb {
257 /* A recursive delay line is used fill in the reverb tail. */
258 DelayLineI Delay;
259 ALsizei Offset[NUM_LINES][2];
261 /* Attenuation to compensate for the modal density and decay rate of the
262 * late lines.
264 ALfloat DensityGain[2];
266 /* T60 decay filters are used to simulate absorption. */
267 T60Filter T60[NUM_LINES];
269 /* A Gerzon vector all-pass filter is used to simulate diffusion. */
270 VecAllpass VecAp;
272 /* The gain for each output channel based on 3D panning. */
273 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
274 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
275 } LateReverb;
277 typedef struct ALreverbState {
278 DERIVE_FROM_TYPE(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 /* Master effect filters */
287 struct {
288 BiquadFilter Lp;
289 BiquadFilter Hp;
290 } Filter[NUM_LINES];
292 /* Core delay line (early reflections and late reverb tap from this). */
293 DelayLineI Delay;
295 /* Tap points for early reflection delay. */
296 ALsizei EarlyDelayTap[NUM_LINES][2];
297 ALfloat EarlyDelayCoeff[NUM_LINES][2];
299 /* Tap points for late reverb feed and delay. */
300 ALsizei LateFeedTap;
301 ALsizei LateDelayTap[NUM_LINES][2];
303 /* Coefficients for the all-pass and line scattering matrices. */
304 ALfloat MixX;
305 ALfloat MixY;
307 EarlyReflections Early;
309 LateReverb Late;
311 /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
312 ALsizei FadeCount;
314 /* Maximum number of samples to process at once. */
315 ALsizei MaxUpdate[2];
317 /* The current write offset for all delay lines. */
318 ALsizei Offset;
320 /* Temporary storage used when processing. */
321 alignas(16) ALfloat TempSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
322 alignas(16) ALfloat MixSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
323 } ALreverbState;
325 static ALvoid ALreverbState_Destruct(ALreverbState *State);
326 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device);
327 static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
328 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
329 DECLARE_DEFAULT_ALLOCATORS(ALreverbState)
331 DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);
333 static void ALreverbState_Construct(ALreverbState *state)
335 ALsizei i, j;
337 ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
338 SET_VTABLE2(ALreverbState, ALeffectState, state);
340 state->TotalSamples = 0;
341 state->SampleBuffer = NULL;
343 for(i = 0;i < NUM_LINES;i++)
345 BiquadFilter_clear(&state->Filter[i].Lp);
346 BiquadFilter_clear(&state->Filter[i].Hp);
349 state->Delay.Mask = 0;
350 state->Delay.Line = NULL;
352 for(i = 0;i < NUM_LINES;i++)
354 state->EarlyDelayTap[i][0] = 0;
355 state->EarlyDelayTap[i][1] = 0;
356 state->EarlyDelayCoeff[i][0] = 0.0f;
357 state->EarlyDelayCoeff[i][1] = 0.0f;
360 state->LateFeedTap = 0;
362 for(i = 0;i < NUM_LINES;i++)
364 state->LateDelayTap[i][0] = 0;
365 state->LateDelayTap[i][1] = 0;
368 state->MixX = 0.0f;
369 state->MixY = 0.0f;
371 state->Early.VecAp.Delay.Mask = 0;
372 state->Early.VecAp.Delay.Line = NULL;
373 state->Early.VecAp.Coeff = 0.0f;
374 state->Early.Delay.Mask = 0;
375 state->Early.Delay.Line = NULL;
376 for(i = 0;i < NUM_LINES;i++)
378 state->Early.VecAp.Offset[i][0] = 0;
379 state->Early.VecAp.Offset[i][1] = 0;
380 state->Early.Offset[i][0] = 0;
381 state->Early.Offset[i][1] = 0;
382 state->Early.Coeff[i][0] = 0.0f;
383 state->Early.Coeff[i][1] = 0.0f;
386 state->Late.DensityGain[0] = 0.0f;
387 state->Late.DensityGain[1] = 0.0f;
388 state->Late.Delay.Mask = 0;
389 state->Late.Delay.Line = NULL;
390 state->Late.VecAp.Delay.Mask = 0;
391 state->Late.VecAp.Delay.Line = NULL;
392 state->Late.VecAp.Coeff = 0.0f;
393 for(i = 0;i < NUM_LINES;i++)
395 state->Late.Offset[i][0] = 0;
396 state->Late.Offset[i][1] = 0;
398 state->Late.VecAp.Offset[i][0] = 0;
399 state->Late.VecAp.Offset[i][1] = 0;
401 state->Late.T60[i].MidGain[0] = 0.0f;
402 state->Late.T60[i].MidGain[1] = 0.0f;
403 BiquadFilter_clear(&state->Late.T60[i].HFFilter);
404 BiquadFilter_clear(&state->Late.T60[i].LFFilter);
407 for(i = 0;i < NUM_LINES;i++)
409 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
411 state->Early.CurrentGain[i][j] = 0.0f;
412 state->Early.PanGain[i][j] = 0.0f;
413 state->Late.CurrentGain[i][j] = 0.0f;
414 state->Late.PanGain[i][j] = 0.0f;
418 state->FadeCount = 0;
419 state->MaxUpdate[0] = MAX_UPDATE_SAMPLES;
420 state->MaxUpdate[1] = MAX_UPDATE_SAMPLES;
421 state->Offset = 0;
424 static ALvoid ALreverbState_Destruct(ALreverbState *State)
426 al_free(State->SampleBuffer);
427 State->SampleBuffer = NULL;
429 ALeffectState_Destruct(STATIC_CAST(ALeffectState,State));
432 /**************************************
433 * Device Update *
434 **************************************/
436 static inline ALfloat CalcDelayLengthMult(ALfloat density)
438 return maxf(5.0f, cbrtf(density*DENSITY_SCALE));
441 /* Given the allocated sample buffer, this function updates each delay line
442 * offset.
444 static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay)
446 union {
447 ALfloat *f;
448 ALfloat (*f4)[NUM_LINES];
449 } u;
450 u.f = &sampleBuffer[(ptrdiff_t)Delay->Line * NUM_LINES];
451 Delay->Line = u.f4;
454 /* Calculate the length of a delay line and store its mask and offset. */
455 static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALuint frequency,
456 const ALuint extra, DelayLineI *Delay)
458 ALuint samples;
460 /* All line lengths are powers of 2, calculated from their lengths in
461 * seconds, rounded up.
463 samples = float2int(ceilf(length*frequency));
464 samples = NextPowerOf2(samples + extra);
466 /* All lines share a single sample buffer. */
467 Delay->Mask = samples - 1;
468 Delay->Line = (ALfloat(*)[NUM_LINES])offset;
470 /* Return the sample count for accumulation. */
471 return samples;
474 /* Calculates the delay line metrics and allocates the shared sample buffer
475 * for all lines given the sample rate (frequency). If an allocation failure
476 * occurs, it returns AL_FALSE.
478 static ALboolean AllocLines(const ALuint frequency, ALreverbState *State)
480 ALuint totalSamples, i;
481 ALfloat multiplier, length;
483 /* All delay line lengths are calculated to accomodate the full range of
484 * lengths given their respective paramters.
486 totalSamples = 0;
488 /* Multiplier for the maximum density value, i.e. density=1, which is
489 * actually the least density...
491 multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
493 /* The main delay length includes the maximum early reflection delay, the
494 * largest early tap width, the maximum late reverb delay, and the
495 * largest late tap width. Finally, it must also be extended by the
496 * update size (MAX_UPDATE_SAMPLES) for block processing.
498 length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier +
499 AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
500 (LATE_LINE_LENGTHS[NUM_LINES-1] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
501 totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES,
502 &State->Delay);
504 /* The early vector all-pass line. */
505 length = EARLY_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
506 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
507 &State->Early.VecAp.Delay);
509 /* The early reflection line. */
510 length = EARLY_LINE_LENGTHS[NUM_LINES-1] * multiplier;
511 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
512 &State->Early.Delay);
514 /* The late vector all-pass line. */
515 length = LATE_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
516 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
517 &State->Late.VecAp.Delay);
519 /* The late delay lines are calculated from the largest maximum density
520 * line length.
522 length = LATE_LINE_LENGTHS[NUM_LINES-1] * multiplier;
523 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
524 &State->Late.Delay);
526 if(totalSamples != State->TotalSamples)
528 ALfloat *newBuffer;
530 TRACE("New reverb buffer length: %ux4 samples\n", totalSamples);
531 newBuffer = al_calloc(16, sizeof(ALfloat[NUM_LINES]) * totalSamples);
532 if(!newBuffer) return AL_FALSE;
534 al_free(State->SampleBuffer);
535 State->SampleBuffer = newBuffer;
536 State->TotalSamples = totalSamples;
539 /* Update all delays to reflect the new sample buffer. */
540 RealizeLineOffset(State->SampleBuffer, &State->Delay);
541 RealizeLineOffset(State->SampleBuffer, &State->Early.VecAp.Delay);
542 RealizeLineOffset(State->SampleBuffer, &State->Early.Delay);
543 RealizeLineOffset(State->SampleBuffer, &State->Late.VecAp.Delay);
544 RealizeLineOffset(State->SampleBuffer, &State->Late.Delay);
546 /* Clear the sample buffer. */
547 for(i = 0;i < State->TotalSamples;i++)
548 State->SampleBuffer[i] = 0.0f;
550 return AL_TRUE;
553 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
555 ALuint frequency = Device->Frequency;
556 ALfloat multiplier;
558 /* Allocate the delay lines. */
559 if(!AllocLines(frequency, State))
560 return AL_FALSE;
562 multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
564 /* The late feed taps are set a fixed position past the latest delay tap. */
565 State->LateFeedTap = float2int((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
566 EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier) *
567 frequency);
569 return AL_TRUE;
572 /**************************************
573 * Effect Update *
574 **************************************/
576 /* Calculate a decay coefficient given the length of each cycle and the time
577 * until the decay reaches -60 dB.
579 static inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
581 return powf(REVERB_DECAY_GAIN, length/decayTime);
584 /* Calculate a decay length from a coefficient and the time until the decay
585 * reaches -60 dB.
587 static inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
589 return log10f(coeff) * decayTime / log10f(REVERB_DECAY_GAIN);
592 /* Calculate an attenuation to be applied to the input of any echo models to
593 * compensate for modal density and decay time.
595 static inline ALfloat CalcDensityGain(const ALfloat a)
597 /* The energy of a signal can be obtained by finding the area under the
598 * squared signal. This takes the form of Sum(x_n^2), where x is the
599 * amplitude for the sample n.
601 * Decaying feedback matches exponential decay of the form Sum(a^n),
602 * where a is the attenuation coefficient, and n is the sample. The area
603 * under this decay curve can be calculated as: 1 / (1 - a).
605 * Modifying the above equation to find the area under the squared curve
606 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
607 * calculated by inverting the square root of this approximation,
608 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
610 return sqrtf(1.0f - a*a);
613 /* Calculate the scattering matrix coefficients given a diffusion factor. */
614 static inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
616 ALfloat n, t;
618 /* The matrix is of order 4, so n is sqrt(4 - 1). */
619 n = sqrtf(3.0f);
620 t = diffusion * atanf(n);
622 /* Calculate the first mixing matrix coefficient. */
623 *x = cosf(t);
624 /* Calculate the second mixing matrix coefficient. */
625 *y = sinf(t) / n;
628 /* Calculate the limited HF ratio for use with the late reverb low-pass
629 * filters.
631 static ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
632 const ALfloat decayTime, const ALfloat SpeedOfSound)
634 ALfloat limitRatio;
636 /* Find the attenuation due to air absorption in dB (converting delay
637 * time to meters using the speed of sound). Then reversing the decay
638 * equation, solve for HF ratio. The delay length is cancelled out of
639 * the equation, so it can be calculated once for all lines.
641 limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound);
643 /* Using the limit calculated above, apply the upper bound to the HF ratio.
645 return minf(limitRatio, hfRatio);
649 /* Calculates the 3-band T60 damping coefficients for a particular delay line
650 * of specified length, using a combination of two shelf filter sections given
651 * decay times for each band split at two reference frequencies.
653 static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime,
654 const ALfloat mfDecayTime, const ALfloat hfDecayTime,
655 const ALfloat lf0norm, const ALfloat hf0norm,
656 T60Filter *filter)
658 ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime);
659 ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime);
660 ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime);
662 filter->MidGain[1] = mfGain;
663 BiquadFilter_setParams(&filter->LFFilter, BiquadType_LowShelf, lfGain/mfGain, lf0norm,
664 calc_rcpQ_from_slope(lfGain/mfGain, 1.0f));
665 BiquadFilter_setParams(&filter->HFFilter, BiquadType_HighShelf, hfGain/mfGain, hf0norm,
666 calc_rcpQ_from_slope(hfGain/mfGain, 1.0f));
669 /* Update the offsets for the main effect delay line. */
670 static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ALreverbState *State)
672 ALfloat multiplier, length;
673 ALuint i;
675 multiplier = CalcDelayLengthMult(density);
677 /* Early reflection taps are decorrelated by means of an average room
678 * reflection approximation described above the definition of the taps.
679 * This approximation is linear and so the above density multiplier can
680 * be applied to adjust the width of the taps. A single-band decay
681 * coefficient is applied to simulate initial attenuation and absorption.
683 * Late reverb taps are based on the late line lengths to allow a zero-
684 * delay path and offsets that would continue the propagation naturally
685 * into the late lines.
687 for(i = 0;i < NUM_LINES;i++)
689 length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
690 State->EarlyDelayTap[i][1] = float2int(length * frequency);
692 length = EARLY_TAP_LENGTHS[i]*multiplier;
693 State->EarlyDelayCoeff[i][1] = CalcDecayCoeff(length, decayTime);
695 length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
696 State->LateDelayTap[i][1] = State->LateFeedTap + float2int(length * frequency);
700 /* Update the early reflection line lengths and gain coefficients. */
701 static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat diffusion, const ALfloat decayTime, const ALuint frequency, EarlyReflections *Early)
703 ALfloat multiplier, length;
704 ALsizei i;
706 multiplier = CalcDelayLengthMult(density);
708 /* Calculate the all-pass feed-back/forward coefficient. */
709 Early->VecAp.Coeff = sqrtf(0.5f) * powf(diffusion, 2.0f);
711 for(i = 0;i < NUM_LINES;i++)
713 /* Calculate the length (in seconds) of each all-pass line. */
714 length = EARLY_ALLPASS_LENGTHS[i] * multiplier;
716 /* Calculate the delay offset for each all-pass line. */
717 Early->VecAp.Offset[i][1] = float2int(length * frequency);
719 /* Calculate the length (in seconds) of each delay line. */
720 length = EARLY_LINE_LENGTHS[i] * multiplier;
722 /* Calculate the delay offset for each delay line. */
723 Early->Offset[i][1] = float2int(length * frequency);
725 /* Calculate the gain (coefficient) for each line. */
726 Early->Coeff[i][1] = CalcDecayCoeff(length, decayTime);
730 /* Update the late reverb line lengths and T60 coefficients. */
731 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)
733 /* Scaling factor to convert the normalized reference frequencies from
734 * representing 0...freq to 0...max_reference.
736 const ALfloat norm_weight_factor = (ALfloat)frequency / AL_EAXREVERB_MAX_HFREFERENCE;
737 ALfloat multiplier, length, bandWeights[3];
738 ALsizei i;
740 /* To compensate for changes in modal density and decay time of the late
741 * reverb signal, the input is attenuated based on the maximal energy of
742 * the outgoing signal. This approximation is used to keep the apparent
743 * energy of the signal equal for all ranges of density and decay time.
745 * The average length of the delay lines is used to calculate the
746 * attenuation coefficient.
748 multiplier = CalcDelayLengthMult(density);
749 length = (LATE_LINE_LENGTHS[0] + LATE_LINE_LENGTHS[1] +
750 LATE_LINE_LENGTHS[2] + LATE_LINE_LENGTHS[3]) / 4.0f * multiplier;
751 length += (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
752 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f * multiplier;
753 /* The density gain calculation uses an average decay time weighted by
754 * approximate bandwidth. This attempts to compensate for losses of energy
755 * that reduce decay time due to scattering into highly attenuated bands.
757 bandWeights[0] = lf0norm*norm_weight_factor;
758 bandWeights[1] = hf0norm*norm_weight_factor - lf0norm*norm_weight_factor;
759 bandWeights[2] = 1.0f - hf0norm*norm_weight_factor;
760 Late->DensityGain[1] = CalcDensityGain(
761 CalcDecayCoeff(length,
762 bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime + bandWeights[2]*hfDecayTime
766 /* Calculate the all-pass feed-back/forward coefficient. */
767 Late->VecAp.Coeff = sqrtf(0.5f) * powf(diffusion, 2.0f);
769 for(i = 0;i < NUM_LINES;i++)
771 /* Calculate the length (in seconds) of each all-pass line. */
772 length = LATE_ALLPASS_LENGTHS[i] * multiplier;
774 /* Calculate the delay offset for each all-pass line. */
775 Late->VecAp.Offset[i][1] = float2int(length * frequency);
777 /* Calculate the length (in seconds) of each delay line. */
778 length = LATE_LINE_LENGTHS[i] * multiplier;
780 /* Calculate the delay offset for each delay line. */
781 Late->Offset[i][1] = float2int(length*frequency + 0.5f);
783 /* Approximate the absorption that the vector all-pass would exhibit
784 * given the current diffusion so we don't have to process a full T60
785 * filter for each of its four lines.
787 length += lerp(LATE_ALLPASS_LENGTHS[i],
788 (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
789 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f,
790 diffusion) * multiplier;
792 /* Calculate the T60 damping coefficients for each line. */
793 CalcT60DampingCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime,
794 lf0norm, hf0norm, &Late->T60[i]);
798 /* Creates a transform matrix given a reverb vector. The vector pans the reverb
799 * reflections toward the given direction, using its magnitude (up to 1) as a
800 * focal strength. This function results in a B-Format transformation matrix
801 * that spatially focuses the signal in the desired direction.
803 static aluMatrixf GetTransformFromVector(const ALfloat *vec)
805 const ALfloat sqrt_3 = 1.732050808f;
806 aluMatrixf focus;
807 ALfloat norm[3];
808 ALfloat mag;
810 /* Normalize the panning vector according to the N3D scale, which has an
811 * extra sqrt(3) term on the directional components. Converting from OpenAL
812 * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
813 * that the reverb panning vectors use left-handed coordinates, unlike the
814 * rest of OpenAL which use right-handed. This is fixed by negating Z,
815 * which cancels out with the B-Format Z negation.
817 mag = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
818 if(mag > 1.0f)
820 norm[0] = vec[0] / mag * -sqrt_3;
821 norm[1] = vec[1] / mag * sqrt_3;
822 norm[2] = vec[2] / mag * sqrt_3;
823 mag = 1.0f;
825 else
827 /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
828 * term. There's no need to renormalize the magnitude since it would
829 * just be reapplied in the matrix.
831 norm[0] = vec[0] * -sqrt_3;
832 norm[1] = vec[1] * sqrt_3;
833 norm[2] = vec[2] * sqrt_3;
836 aluMatrixfSet(&focus,
837 1.0f, 0.0f, 0.0f, 0.0f,
838 norm[0], 1.0f-mag, 0.0f, 0.0f,
839 norm[1], 0.0f, 1.0f-mag, 0.0f,
840 norm[2], 0.0f, 0.0f, 1.0f-mag
843 return focus;
846 /* Update the early and late 3D panning gains. */
847 static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat earlyGain, const ALfloat lateGain, ALreverbState *State)
849 aluMatrixf transform, rot;
850 ALsizei i;
852 STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
853 STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;
855 /* Note: _res is transposed. */
856 #define MATRIX_MULT(_res, _m1, _m2) do { \
857 int row, col; \
858 for(col = 0;col < 4;col++) \
860 for(row = 0;row < 4;row++) \
861 _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
862 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
864 } while(0)
865 /* Create a matrix that first converts A-Format to B-Format, then
866 * transforms the B-Format signal according to the panning vector.
868 rot = GetTransformFromVector(ReflectionsPan);
869 MATRIX_MULT(transform, rot, A2B);
870 memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
871 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
872 ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], earlyGain,
873 State->Early.PanGain[i]);
875 rot = GetTransformFromVector(LateReverbPan);
876 MATRIX_MULT(transform, rot, A2B);
877 memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
878 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
879 ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], lateGain,
880 State->Late.PanGain[i]);
881 #undef MATRIX_MULT
884 static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
886 const ALCdevice *Device = Context->Device;
887 const ALlistener *Listener = Context->Listener;
888 ALuint frequency = Device->Frequency;
889 ALfloat lf0norm, hf0norm, hfRatio;
890 ALfloat lfDecayTime, hfDecayTime;
891 ALfloat gain, gainlf, gainhf;
892 ALsizei i;
894 /* Calculate the master filters */
895 hf0norm = minf(props->Reverb.HFReference / frequency, 0.49f);
896 /* Restrict the filter gains from going below -60dB to keep the filter from
897 * killing most of the signal.
899 gainhf = maxf(props->Reverb.GainHF, 0.001f);
900 BiquadFilter_setParams(&State->Filter[0].Lp, BiquadType_HighShelf, gainhf, hf0norm,
901 calc_rcpQ_from_slope(gainhf, 1.0f));
902 lf0norm = minf(props->Reverb.LFReference / frequency, 0.49f);
903 gainlf = maxf(props->Reverb.GainLF, 0.001f);
904 BiquadFilter_setParams(&State->Filter[0].Hp, BiquadType_LowShelf, gainlf, lf0norm,
905 calc_rcpQ_from_slope(gainlf, 1.0f));
906 for(i = 1;i < NUM_LINES;i++)
908 BiquadFilter_copyParams(&State->Filter[i].Lp, &State->Filter[0].Lp);
909 BiquadFilter_copyParams(&State->Filter[i].Hp, &State->Filter[0].Hp);
912 /* Update the main effect delay and associated taps. */
913 UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
914 props->Reverb.Density, props->Reverb.DecayTime, frequency,
915 State);
917 /* Update the early lines. */
918 UpdateEarlyLines(props->Reverb.Density, props->Reverb.Diffusion,
919 props->Reverb.DecayTime, frequency, &State->Early);
921 /* Get the mixing matrix coefficients. */
922 CalcMatrixCoeffs(props->Reverb.Diffusion, &State->MixX, &State->MixY);
924 /* If the HF limit parameter is flagged, calculate an appropriate limit
925 * based on the air absorption parameter.
927 hfRatio = props->Reverb.DecayHFRatio;
928 if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
929 hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
930 props->Reverb.DecayTime, Listener->Params.ReverbSpeedOfSound
933 /* Calculate the LF/HF decay times. */
934 lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
935 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
936 hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
937 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
939 /* Update the late lines. */
940 UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
941 lfDecayTime, props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm,
942 frequency, &State->Late
945 /* Update early and late 3D panning. */
946 gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
947 Update3DPanning(Device, props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan,
948 props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain,
949 State);
951 /* Calculate the max update size from the smallest relevant delay. */
952 State->MaxUpdate[1] = mini(MAX_UPDATE_SAMPLES,
953 mini(State->Early.Offset[0][1], State->Late.Offset[0][1])
956 /* Determine if delay-line cross-fading is required. TODO: Add some fuzz
957 * for the float comparisons? The math should be stable enough that the
958 * result should be the same if nothing's changed, and changes in the float
959 * values should (though may not always) be matched by changes in delay
960 * offsets.
962 if(State->Late.DensityGain[1] != State->Late.DensityGain[0])
963 State->FadeCount = 0;
964 else for(i = 0;i < NUM_LINES;i++)
966 if(State->EarlyDelayTap[i][1] != State->EarlyDelayTap[i][0] ||
967 State->EarlyDelayCoeff[i][1] != State->EarlyDelayCoeff[i][0] ||
968 State->Early.VecAp.Offset[i][1] != State->Early.VecAp.Offset[i][0] ||
969 State->Early.Offset[i][1] != State->Early.Offset[i][0] ||
970 State->Early.Coeff[i][1] != State->Early.Coeff[i][0] ||
971 State->LateDelayTap[i][1] != State->LateDelayTap[i][0] ||
972 State->Late.VecAp.Offset[i][1] != State->Late.VecAp.Offset[i][0] ||
973 State->Late.Offset[i][1] != State->Late.Offset[i][0] ||
974 State->Late.T60[i].MidGain[1] != State->Late.T60[i].MidGain[0])
976 State->FadeCount = 0;
977 break;
983 /**************************************
984 * Effect Processing *
985 **************************************/
987 /* Basic delay line input/output routines. */
988 static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c)
990 return Delay->Line[offset&Delay->Mask][c];
993 /* Cross-faded delay line output routine. Instead of interpolating the
994 * offsets, this interpolates (cross-fades) the outputs at each offset.
996 static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0,
997 const ALsizei off1, const ALsizei c,
998 const ALfloat sc0, const ALfloat sc1)
1000 return Delay->Line[off0&Delay->Mask][c]*sc0 +
1001 Delay->Line[off1&Delay->Mask][c]*sc1;
1005 static inline void DelayLineIn(const DelayLineI *Delay, ALsizei offset, const ALsizei c,
1006 const ALfloat *restrict in, ALsizei count)
1008 ALsizei i;
1009 for(i = 0;i < count;i++)
1010 Delay->Line[(offset++)&Delay->Mask][c] = *(in++);
1013 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1014 * for both the below vector all-pass model and to perform modal feed-back
1015 * delay network (FDN) mixing.
1017 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1018 * matrix with a single unitary rotational parameter:
1020 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1021 * [ -a, d, c, -b ]
1022 * [ -b, -c, d, a ]
1023 * [ -c, b, -a, d ]
1025 * The rotation is constructed from the effect's diffusion parameter,
1026 * yielding:
1028 * 1 = x^2 + 3 y^2
1030 * Where a, b, and c are the coefficient y with differing signs, and d is the
1031 * coefficient x. The final matrix is thus:
1033 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1034 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1035 * [ y, -y, x, y ] x = cos(t)
1036 * [ -y, -y, -y, x ] y = sin(t) / n
1038 * Any square orthogonal matrix with an order that is a power of two will
1039 * work (where ^T is transpose, ^-1 is inverse):
1041 * M^T = M^-1
1043 * Using that knowledge, finding an appropriate matrix can be accomplished
1044 * naively by searching all combinations of:
1046 * M = D + S - S^T
1048 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1049 * whose combination of signs are being iterated.
1051 static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *restrict in,
1052 const ALfloat xCoeff, const ALfloat yCoeff)
1054 out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
1055 out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
1056 out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
1057 out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
1059 #define VectorScatterDelayIn(delay, o, in, xcoeff, ycoeff) \
1060 VectorPartialScatter((delay)->Line[(o)&(delay)->Mask], in, xcoeff, ycoeff)
1062 /* Utilizes the above, but reverses the input channels. */
1063 static inline void VectorScatterRevDelayIn(const DelayLineI *Delay, ALint offset,
1064 const ALfloat xCoeff, const ALfloat yCoeff,
1065 const ALfloat (*restrict in)[MAX_UPDATE_SAMPLES],
1066 const ALsizei count)
1068 const DelayLineI delay = *Delay;
1069 ALsizei i, j;
1071 for(i = 0;i < count;++i)
1073 ALfloat f[NUM_LINES];
1074 for(j = 0;j < NUM_LINES;j++)
1075 f[NUM_LINES-1-j] = in[j][i];
1077 VectorScatterDelayIn(&delay, offset++, f, xCoeff, yCoeff);
1081 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1082 * filter to the 4-line input.
1084 * It works by vectorizing a regular all-pass filter and replacing the delay
1085 * element with a scattering matrix (like the one above) and a diagonal
1086 * matrix of delay elements.
1088 * Two static specializations are used for transitional (cross-faded) delay
1089 * line processing and non-transitional processing.
1091 static void VectorAllpass_Unfaded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
1092 const ALfloat xCoeff, const ALfloat yCoeff, ALsizei todo,
1093 VecAllpass *Vap)
1095 const DelayLineI delay = Vap->Delay;
1096 const ALfloat feedCoeff = Vap->Coeff;
1097 ALsizei vap_offset[NUM_LINES];
1098 ALsizei i, j;
1100 ASSUME(todo > 0);
1102 for(j = 0;j < NUM_LINES;j++)
1103 vap_offset[j] = offset-Vap->Offset[j][0];
1104 for(i = 0;i < todo;i++)
1106 ALfloat f[NUM_LINES];
1108 for(j = 0;j < NUM_LINES;j++)
1110 ALfloat input = samples[j][i];
1111 ALfloat out = DelayLineOut(&delay, vap_offset[j]++, j) - feedCoeff*input;
1112 f[j] = input + feedCoeff*out;
1114 samples[j][i] = out;
1117 VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
1118 ++offset;
1121 static void VectorAllpass_Faded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
1122 const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade,
1123 ALsizei todo, VecAllpass *Vap)
1125 const DelayLineI delay = Vap->Delay;
1126 const ALfloat feedCoeff = Vap->Coeff;
1127 ALsizei vap_offset[NUM_LINES][2];
1128 ALsizei i, j;
1130 ASSUME(todo > 0);
1132 for(j = 0;j < NUM_LINES;j++)
1134 vap_offset[j][0] = offset-Vap->Offset[j][0];
1135 vap_offset[j][1] = offset-Vap->Offset[j][1];
1137 for(i = 0;i < todo;i++)
1139 ALfloat f[NUM_LINES];
1141 for(j = 0;j < NUM_LINES;j++)
1143 ALfloat input = samples[j][i];
1144 ALfloat out =
1145 FadedDelayLineOut(&delay, vap_offset[j][0]++, vap_offset[j][1]++, j,
1146 1.0f-fade, fade
1147 ) - feedCoeff*input;
1148 f[j] = input + feedCoeff*out;
1150 samples[j][i] = out;
1152 fade += FadeStep;
1154 VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
1155 ++offset;
1159 /* This generates early reflections.
1161 * This is done by obtaining the primary reflections (those arriving from the
1162 * same direction as the source) from the main delay line. These are
1163 * attenuated and all-pass filtered (based on the diffusion parameter).
1165 * The early lines are then fed in reverse (according to the approximately
1166 * opposite spatial location of the A-Format lines) to create the secondary
1167 * reflections (those arriving from the opposite direction as the source).
1169 * The early response is then completed by combining the primary reflections
1170 * with the delayed and attenuated output from the early lines.
1172 * Finally, the early response is reversed, scattered (based on diffusion),
1173 * and fed into the late reverb section of the main delay line.
1175 * Two static specializations are used for transitional (cross-faded) delay
1176 * line processing and non-transitional processing.
1178 static void EarlyReflection_Unfaded(ALreverbState *State, ALsizei offset, const ALsizei todo,
1179 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1181 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1182 const DelayLineI early_delay = State->Early.Delay;
1183 const DelayLineI main_delay = State->Delay;
1184 const ALfloat mixX = State->MixX;
1185 const ALfloat mixY = State->MixY;
1186 ALsizei late_feed_tap;
1187 ALsizei i, j;
1189 ASSUME(todo > 0);
1191 /* First, load decorrelated samples from the main delay line as the primary
1192 * reflections.
1194 for(j = 0;j < NUM_LINES;j++)
1196 ALsizei early_delay_tap = offset - State->EarlyDelayTap[j][0];
1197 ALfloat coeff = State->EarlyDelayCoeff[j][0];
1198 for(i = 0;i < todo;i++)
1199 temps[j][i] = DelayLineOut(&main_delay, early_delay_tap++, j) * coeff;
1202 /* Apply a vector all-pass, to help color the initial reflections based on
1203 * the diffusion strength.
1205 VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Early.VecAp);
1207 /* Apply a delay and bounce to generate secondary reflections, combine with
1208 * the primary reflections and write out the result for mixing.
1210 for(j = 0;j < NUM_LINES;j++)
1212 ALint early_feedb_tap = offset - State->Early.Offset[j][0];
1213 ALfloat early_feedb_coeff = State->Early.Coeff[j][0];
1215 for(i = 0;i < todo;i++)
1216 out[j][i] = DelayLineOut(&early_delay, early_feedb_tap++, j)*early_feedb_coeff +
1217 temps[j][i];
1218 DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
1221 /* Also write the result back to the main delay line for the late reverb
1222 * stage to pick up at the appropriate time, appplying a scatter and
1223 * bounce to improve the initial diffusion in the late reverb.
1225 late_feed_tap = offset - State->LateFeedTap;
1226 VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
1228 static void EarlyReflection_Faded(ALreverbState *State, ALsizei offset, const ALsizei todo,
1229 const ALfloat fade, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1231 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1232 const DelayLineI early_delay = State->Early.Delay;
1233 const DelayLineI main_delay = State->Delay;
1234 const ALfloat mixX = State->MixX;
1235 const ALfloat mixY = State->MixY;
1236 ALsizei late_feed_tap;
1237 ALfloat fadeCount;
1238 ALsizei i, j;
1240 ASSUME(todo > 0);
1242 for(j = 0;j < NUM_LINES;j++)
1244 ALsizei early_delay_tap0 = offset - State->EarlyDelayTap[j][0];
1245 ALsizei early_delay_tap1 = offset - State->EarlyDelayTap[j][1];
1246 ALfloat oldCoeff = State->EarlyDelayCoeff[j][0];
1247 ALfloat oldCoeffStep = -oldCoeff / FADE_SAMPLES;
1248 ALfloat newCoeffStep = State->EarlyDelayCoeff[j][1] / FADE_SAMPLES;
1250 fadeCount = fade * FADE_SAMPLES;
1251 for(i = 0;i < todo;i++)
1253 const ALfloat fade0 = oldCoeff + oldCoeffStep*fadeCount;
1254 const ALfloat fade1 = newCoeffStep*fadeCount;
1255 temps[j][i] = FadedDelayLineOut(&main_delay,
1256 early_delay_tap0++, early_delay_tap1++, j, fade0, fade1
1258 fadeCount += 1.0f;
1262 VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->Early.VecAp);
1264 for(j = 0;j < NUM_LINES;j++)
1266 ALint feedb_tap0 = offset - State->Early.Offset[j][0];
1267 ALint feedb_tap1 = offset - State->Early.Offset[j][1];
1268 ALfloat feedb_oldCoeff = State->Early.Coeff[j][0];
1269 ALfloat feedb_oldCoeffStep = -feedb_oldCoeff / FADE_SAMPLES;
1270 ALfloat feedb_newCoeffStep = State->Early.Coeff[j][1] / FADE_SAMPLES;
1272 fadeCount = fade * FADE_SAMPLES;
1273 for(i = 0;i < todo;i++)
1275 const ALfloat fade0 = feedb_oldCoeff + feedb_oldCoeffStep*fadeCount;
1276 const ALfloat fade1 = feedb_newCoeffStep*fadeCount;
1277 out[j][i] = FadedDelayLineOut(&early_delay,
1278 feedb_tap0++, feedb_tap1++, j, fade0, fade1
1279 ) + temps[j][i];
1280 fadeCount += 1.0f;
1282 DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
1285 late_feed_tap = offset - State->LateFeedTap;
1286 VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
1289 /* Applies the two T60 damping filter sections. */
1290 static inline void LateT60Filter(ALfloat *restrict samples, const ALsizei todo, T60Filter *filter)
1292 ALfloat temp[MAX_UPDATE_SAMPLES];
1293 BiquadFilter_process(&filter->HFFilter, temp, samples, todo);
1294 BiquadFilter_process(&filter->LFFilter, samples, temp, todo);
1297 /* This generates the reverb tail using a modified feed-back delay network
1298 * (FDN).
1300 * Results from the early reflections are mixed with the output from the late
1301 * delay lines.
1303 * The late response is then completed by T60 and all-pass filtering the mix.
1305 * Finally, the lines are reversed (so they feed their opposite directions)
1306 * and scattered with the FDN matrix before re-feeding the delay lines.
1308 * Two variations are made, one for for transitional (cross-faded) delay line
1309 * processing and one for non-transitional processing.
1311 static void LateReverb_Unfaded(ALreverbState *State, ALsizei offset, const ALsizei todo,
1312 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1314 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1315 const DelayLineI late_delay = State->Late.Delay;
1316 const DelayLineI main_delay = State->Delay;
1317 const ALfloat mixX = State->MixX;
1318 const ALfloat mixY = State->MixY;
1319 ALsizei i, j;
1321 ASSUME(todo > 0);
1323 /* First, load decorrelated samples from the main and feedback delay lines.
1324 * Filter the signal to apply its frequency-dependent decay.
1326 for(j = 0;j < NUM_LINES;j++)
1328 ALsizei late_delay_tap = offset - State->LateDelayTap[j][0];
1329 ALsizei late_feedb_tap = offset - State->Late.Offset[j][0];
1330 ALfloat midGain = State->Late.T60[j].MidGain[0];
1331 const ALfloat densityGain = State->Late.DensityGain[0] * midGain;
1332 for(i = 0;i < todo;i++)
1333 temps[j][i] = DelayLineOut(&main_delay, late_delay_tap++, j)*densityGain +
1334 DelayLineOut(&late_delay, late_feedb_tap++, j)*midGain;
1335 LateT60Filter(temps[j], todo, &State->Late.T60[j]);
1338 /* Apply a vector all-pass to improve micro-surface diffusion, and write
1339 * out the results for mixing.
1341 VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Late.VecAp);
1343 for(j = 0;j < NUM_LINES;j++)
1344 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1346 /* Finally, scatter and bounce the results to refeed the feedback buffer. */
1347 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, out, todo);
1349 static void LateReverb_Faded(ALreverbState *State, ALsizei offset, const ALsizei todo,
1350 const ALfloat fade, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1352 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1353 const DelayLineI late_delay = State->Late.Delay;
1354 const DelayLineI main_delay = State->Delay;
1355 const ALfloat mixX = State->MixX;
1356 const ALfloat mixY = State->MixY;
1357 ALsizei i, j;
1359 ASSUME(todo > 0);
1361 for(j = 0;j < NUM_LINES;j++)
1363 const ALfloat oldMidGain = State->Late.T60[j].MidGain[0];
1364 const ALfloat midGain = State->Late.T60[j].MidGain[1];
1365 const ALfloat oldMidStep = -oldMidGain / FADE_SAMPLES;
1366 const ALfloat midStep = midGain / FADE_SAMPLES;
1367 const ALfloat oldDensityGain = State->Late.DensityGain[0] * oldMidGain;
1368 const ALfloat densityGain = State->Late.DensityGain[1] * midGain;
1369 const ALfloat oldDensityStep = -oldDensityGain / FADE_SAMPLES;
1370 const ALfloat densityStep = densityGain / FADE_SAMPLES;
1371 ALsizei late_delay_tap0 = offset - State->LateDelayTap[j][0];
1372 ALsizei late_delay_tap1 = offset - State->LateDelayTap[j][1];
1373 ALsizei late_feedb_tap0 = offset - State->Late.Offset[j][0];
1374 ALsizei late_feedb_tap1 = offset - State->Late.Offset[j][1];
1375 ALfloat fadeCount = fade * FADE_SAMPLES;
1376 for(i = 0;i < todo;i++)
1378 const ALfloat fade0 = oldDensityGain + oldDensityStep*fadeCount;
1379 const ALfloat fade1 = densityStep*fadeCount;
1380 const ALfloat gfade0 = oldMidGain + oldMidStep*fadeCount;
1381 const ALfloat gfade1 = midStep*fadeCount;
1382 temps[j][i] =
1383 FadedDelayLineOut(&main_delay, late_delay_tap0++, late_delay_tap1++, j,
1384 fade0, fade1) +
1385 FadedDelayLineOut(&late_delay, late_feedb_tap0++, late_feedb_tap1++, j,
1386 gfade0, gfade1);
1387 fadeCount += 1.0f;
1389 LateT60Filter(temps[j], todo, &State->Late.T60[j]);
1392 VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->Late.VecAp);
1394 for(j = 0;j < NUM_LINES;j++)
1395 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1397 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, temps, todo);
1400 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
1402 ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1403 ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES] = State->MixSamples;
1404 ALsizei fadeCount = State->FadeCount;
1405 ALsizei offset = State->Offset;
1406 ALsizei base, c;
1408 /* Process reverb for these samples. */
1409 for(base = 0;base < SamplesToDo;)
1411 ALsizei todo = SamplesToDo - base;
1412 /* If cross-fading, don't do more samples than there are to fade. */
1413 if(FADE_SAMPLES-fadeCount > 0)
1415 todo = mini(todo, FADE_SAMPLES-fadeCount);
1416 todo = mini(todo, State->MaxUpdate[0]);
1418 todo = mini(todo, State->MaxUpdate[1]);
1420 /* Convert B-Format to A-Format for processing. */
1421 memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
1422 for(c = 0;c < NUM_LINES;c++)
1423 MixRowSamples(afmt[c], B2A.m[c],
1424 SamplesIn, MAX_EFFECT_CHANNELS, base, todo
1427 /* Process the samples for reverb. */
1428 for(c = 0;c < NUM_LINES;c++)
1430 /* Band-pass the incoming samples. */
1431 BiquadFilter_process(&State->Filter[c].Lp, samples[0], afmt[c], todo);
1432 BiquadFilter_process(&State->Filter[c].Hp, samples[1], samples[0], todo);
1434 /* Feed the initial delay line. */
1435 DelayLineIn(&State->Delay, offset, c, samples[1], todo);
1438 if(UNLIKELY(fadeCount < FADE_SAMPLES))
1440 ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES;
1442 /* Generate early reflections. */
1443 EarlyReflection_Faded(State, offset, todo, fade, samples);
1444 /* Mix the A-Format results to output, implicitly converting back
1445 * to B-Format.
1447 for(c = 0;c < NUM_LINES;c++)
1448 MixSamples(samples[c], NumChannels, SamplesOut,
1449 State->Early.CurrentGain[c], State->Early.PanGain[c],
1450 SamplesToDo-base, base, todo
1453 /* Generate and mix late reverb. */
1454 LateReverb_Faded(State, offset, todo, fade, samples);
1455 for(c = 0;c < NUM_LINES;c++)
1456 MixSamples(samples[c], NumChannels, SamplesOut,
1457 State->Late.CurrentGain[c], State->Late.PanGain[c],
1458 SamplesToDo-base, base, todo
1461 /* Step fading forward. */
1462 fadeCount += todo;
1463 if(LIKELY(fadeCount >= FADE_SAMPLES))
1465 /* Update the cross-fading delay line taps. */
1466 fadeCount = FADE_SAMPLES;
1467 for(c = 0;c < NUM_LINES;c++)
1469 State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
1470 State->EarlyDelayCoeff[c][0] = State->EarlyDelayCoeff[c][1];
1471 State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1];
1472 State->Early.Offset[c][0] = State->Early.Offset[c][1];
1473 State->Early.Coeff[c][0] = State->Early.Coeff[c][1];
1474 State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
1475 State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1];
1476 State->Late.Offset[c][0] = State->Late.Offset[c][1];
1477 State->Late.T60[c].MidGain[0] = State->Late.T60[c].MidGain[1];
1479 State->Late.DensityGain[0] = State->Late.DensityGain[1];
1480 State->MaxUpdate[0] = State->MaxUpdate[1];
1483 else
1485 /* Generate and mix early reflections. */
1486 EarlyReflection_Unfaded(State, offset, todo, samples);
1487 for(c = 0;c < NUM_LINES;c++)
1488 MixSamples(samples[c], NumChannels, SamplesOut,
1489 State->Early.CurrentGain[c], State->Early.PanGain[c],
1490 SamplesToDo-base, base, todo
1493 /* Generate and mix late reverb. */
1494 LateReverb_Unfaded(State, offset, todo, samples);
1495 for(c = 0;c < NUM_LINES;c++)
1496 MixSamples(samples[c], NumChannels, SamplesOut,
1497 State->Late.CurrentGain[c], State->Late.PanGain[c],
1498 SamplesToDo-base, base, todo
1502 /* Step all delays forward. */
1503 offset += todo;
1505 base += todo;
1507 State->Offset = offset;
1508 State->FadeCount = fadeCount;
1512 typedef struct ReverbStateFactory {
1513 DERIVE_FROM_TYPE(EffectStateFactory);
1514 } ReverbStateFactory;
1516 static ALeffectState *ReverbStateFactory_create(ReverbStateFactory* UNUSED(factory))
1518 ALreverbState *state;
1520 NEW_OBJ0(state, ALreverbState)();
1521 if(!state) return NULL;
1523 return STATIC_CAST(ALeffectState, state);
1526 DEFINE_EFFECTSTATEFACTORY_VTABLE(ReverbStateFactory);
1528 EffectStateFactory *ReverbStateFactory_getFactory(void)
1530 static ReverbStateFactory ReverbFactory = { { GET_VTABLE2(ReverbStateFactory, EffectStateFactory) } };
1532 return STATIC_CAST(EffectStateFactory, &ReverbFactory);
1536 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1538 ALeffectProps *props = &effect->Props;
1539 switch(param)
1541 case AL_EAXREVERB_DECAY_HFLIMIT:
1542 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1543 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
1544 props->Reverb.DecayHFLimit = val;
1545 break;
1547 default:
1548 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1549 param);
1552 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1553 { ALeaxreverb_setParami(effect, context, param, vals[0]); }
1554 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1556 ALeffectProps *props = &effect->Props;
1557 switch(param)
1559 case AL_EAXREVERB_DENSITY:
1560 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1561 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
1562 props->Reverb.Density = val;
1563 break;
1565 case AL_EAXREVERB_DIFFUSION:
1566 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1567 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
1568 props->Reverb.Diffusion = val;
1569 break;
1571 case AL_EAXREVERB_GAIN:
1572 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1573 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
1574 props->Reverb.Gain = val;
1575 break;
1577 case AL_EAXREVERB_GAINHF:
1578 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1579 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
1580 props->Reverb.GainHF = val;
1581 break;
1583 case AL_EAXREVERB_GAINLF:
1584 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1585 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
1586 props->Reverb.GainLF = val;
1587 break;
1589 case AL_EAXREVERB_DECAY_TIME:
1590 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1591 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
1592 props->Reverb.DecayTime = val;
1593 break;
1595 case AL_EAXREVERB_DECAY_HFRATIO:
1596 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1597 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
1598 props->Reverb.DecayHFRatio = val;
1599 break;
1601 case AL_EAXREVERB_DECAY_LFRATIO:
1602 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1603 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
1604 props->Reverb.DecayLFRatio = val;
1605 break;
1607 case AL_EAXREVERB_REFLECTIONS_GAIN:
1608 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1609 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
1610 props->Reverb.ReflectionsGain = val;
1611 break;
1613 case AL_EAXREVERB_REFLECTIONS_DELAY:
1614 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1615 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
1616 props->Reverb.ReflectionsDelay = val;
1617 break;
1619 case AL_EAXREVERB_LATE_REVERB_GAIN:
1620 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1621 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
1622 props->Reverb.LateReverbGain = val;
1623 break;
1625 case AL_EAXREVERB_LATE_REVERB_DELAY:
1626 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1627 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
1628 props->Reverb.LateReverbDelay = val;
1629 break;
1631 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1632 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1633 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
1634 props->Reverb.AirAbsorptionGainHF = val;
1635 break;
1637 case AL_EAXREVERB_ECHO_TIME:
1638 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1639 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
1640 props->Reverb.EchoTime = val;
1641 break;
1643 case AL_EAXREVERB_ECHO_DEPTH:
1644 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1645 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
1646 props->Reverb.EchoDepth = val;
1647 break;
1649 case AL_EAXREVERB_MODULATION_TIME:
1650 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1651 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
1652 props->Reverb.ModulationTime = val;
1653 break;
1655 case AL_EAXREVERB_MODULATION_DEPTH:
1656 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1657 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
1658 props->Reverb.ModulationDepth = val;
1659 break;
1661 case AL_EAXREVERB_HFREFERENCE:
1662 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1663 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
1664 props->Reverb.HFReference = val;
1665 break;
1667 case AL_EAXREVERB_LFREFERENCE:
1668 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1669 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
1670 props->Reverb.LFReference = val;
1671 break;
1673 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1674 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1675 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
1676 props->Reverb.RoomRolloffFactor = val;
1677 break;
1679 default:
1680 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1681 param);
1684 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1686 ALeffectProps *props = &effect->Props;
1687 switch(param)
1689 case AL_EAXREVERB_REFLECTIONS_PAN:
1690 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1691 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
1692 props->Reverb.ReflectionsPan[0] = vals[0];
1693 props->Reverb.ReflectionsPan[1] = vals[1];
1694 props->Reverb.ReflectionsPan[2] = vals[2];
1695 break;
1696 case AL_EAXREVERB_LATE_REVERB_PAN:
1697 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1698 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
1699 props->Reverb.LateReverbPan[0] = vals[0];
1700 props->Reverb.LateReverbPan[1] = vals[1];
1701 props->Reverb.LateReverbPan[2] = vals[2];
1702 break;
1704 default:
1705 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1706 break;
1710 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1712 const ALeffectProps *props = &effect->Props;
1713 switch(param)
1715 case AL_EAXREVERB_DECAY_HFLIMIT:
1716 *val = props->Reverb.DecayHFLimit;
1717 break;
1719 default:
1720 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1721 param);
1724 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1725 { ALeaxreverb_getParami(effect, context, param, vals); }
1726 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1728 const ALeffectProps *props = &effect->Props;
1729 switch(param)
1731 case AL_EAXREVERB_DENSITY:
1732 *val = props->Reverb.Density;
1733 break;
1735 case AL_EAXREVERB_DIFFUSION:
1736 *val = props->Reverb.Diffusion;
1737 break;
1739 case AL_EAXREVERB_GAIN:
1740 *val = props->Reverb.Gain;
1741 break;
1743 case AL_EAXREVERB_GAINHF:
1744 *val = props->Reverb.GainHF;
1745 break;
1747 case AL_EAXREVERB_GAINLF:
1748 *val = props->Reverb.GainLF;
1749 break;
1751 case AL_EAXREVERB_DECAY_TIME:
1752 *val = props->Reverb.DecayTime;
1753 break;
1755 case AL_EAXREVERB_DECAY_HFRATIO:
1756 *val = props->Reverb.DecayHFRatio;
1757 break;
1759 case AL_EAXREVERB_DECAY_LFRATIO:
1760 *val = props->Reverb.DecayLFRatio;
1761 break;
1763 case AL_EAXREVERB_REFLECTIONS_GAIN:
1764 *val = props->Reverb.ReflectionsGain;
1765 break;
1767 case AL_EAXREVERB_REFLECTIONS_DELAY:
1768 *val = props->Reverb.ReflectionsDelay;
1769 break;
1771 case AL_EAXREVERB_LATE_REVERB_GAIN:
1772 *val = props->Reverb.LateReverbGain;
1773 break;
1775 case AL_EAXREVERB_LATE_REVERB_DELAY:
1776 *val = props->Reverb.LateReverbDelay;
1777 break;
1779 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1780 *val = props->Reverb.AirAbsorptionGainHF;
1781 break;
1783 case AL_EAXREVERB_ECHO_TIME:
1784 *val = props->Reverb.EchoTime;
1785 break;
1787 case AL_EAXREVERB_ECHO_DEPTH:
1788 *val = props->Reverb.EchoDepth;
1789 break;
1791 case AL_EAXREVERB_MODULATION_TIME:
1792 *val = props->Reverb.ModulationTime;
1793 break;
1795 case AL_EAXREVERB_MODULATION_DEPTH:
1796 *val = props->Reverb.ModulationDepth;
1797 break;
1799 case AL_EAXREVERB_HFREFERENCE:
1800 *val = props->Reverb.HFReference;
1801 break;
1803 case AL_EAXREVERB_LFREFERENCE:
1804 *val = props->Reverb.LFReference;
1805 break;
1807 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1808 *val = props->Reverb.RoomRolloffFactor;
1809 break;
1811 default:
1812 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1813 param);
1816 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
1818 const ALeffectProps *props = &effect->Props;
1819 switch(param)
1821 case AL_EAXREVERB_REFLECTIONS_PAN:
1822 vals[0] = props->Reverb.ReflectionsPan[0];
1823 vals[1] = props->Reverb.ReflectionsPan[1];
1824 vals[2] = props->Reverb.ReflectionsPan[2];
1825 break;
1826 case AL_EAXREVERB_LATE_REVERB_PAN:
1827 vals[0] = props->Reverb.LateReverbPan[0];
1828 vals[1] = props->Reverb.LateReverbPan[1];
1829 vals[2] = props->Reverb.LateReverbPan[2];
1830 break;
1832 default:
1833 ALeaxreverb_getParamf(effect, context, param, vals);
1834 break;
1838 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
1840 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1842 ALeffectProps *props = &effect->Props;
1843 switch(param)
1845 case AL_REVERB_DECAY_HFLIMIT:
1846 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
1847 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
1848 props->Reverb.DecayHFLimit = val;
1849 break;
1851 default:
1852 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
1855 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1856 { ALreverb_setParami(effect, context, param, vals[0]); }
1857 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1859 ALeffectProps *props = &effect->Props;
1860 switch(param)
1862 case AL_REVERB_DENSITY:
1863 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
1864 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
1865 props->Reverb.Density = val;
1866 break;
1868 case AL_REVERB_DIFFUSION:
1869 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
1870 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
1871 props->Reverb.Diffusion = val;
1872 break;
1874 case AL_REVERB_GAIN:
1875 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
1876 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
1877 props->Reverb.Gain = val;
1878 break;
1880 case AL_REVERB_GAINHF:
1881 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
1882 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
1883 props->Reverb.GainHF = val;
1884 break;
1886 case AL_REVERB_DECAY_TIME:
1887 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
1888 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
1889 props->Reverb.DecayTime = val;
1890 break;
1892 case AL_REVERB_DECAY_HFRATIO:
1893 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
1894 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
1895 props->Reverb.DecayHFRatio = val;
1896 break;
1898 case AL_REVERB_REFLECTIONS_GAIN:
1899 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
1900 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
1901 props->Reverb.ReflectionsGain = val;
1902 break;
1904 case AL_REVERB_REFLECTIONS_DELAY:
1905 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
1906 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
1907 props->Reverb.ReflectionsDelay = val;
1908 break;
1910 case AL_REVERB_LATE_REVERB_GAIN:
1911 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
1912 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
1913 props->Reverb.LateReverbGain = val;
1914 break;
1916 case AL_REVERB_LATE_REVERB_DELAY:
1917 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
1918 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
1919 props->Reverb.LateReverbDelay = val;
1920 break;
1922 case AL_REVERB_AIR_ABSORPTION_GAINHF:
1923 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
1924 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
1925 props->Reverb.AirAbsorptionGainHF = val;
1926 break;
1928 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
1929 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
1930 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
1931 props->Reverb.RoomRolloffFactor = val;
1932 break;
1934 default:
1935 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
1938 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1939 { ALreverb_setParamf(effect, context, param, vals[0]); }
1941 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1943 const ALeffectProps *props = &effect->Props;
1944 switch(param)
1946 case AL_REVERB_DECAY_HFLIMIT:
1947 *val = props->Reverb.DecayHFLimit;
1948 break;
1950 default:
1951 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
1954 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1955 { ALreverb_getParami(effect, context, param, vals); }
1956 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1958 const ALeffectProps *props = &effect->Props;
1959 switch(param)
1961 case AL_REVERB_DENSITY:
1962 *val = props->Reverb.Density;
1963 break;
1965 case AL_REVERB_DIFFUSION:
1966 *val = props->Reverb.Diffusion;
1967 break;
1969 case AL_REVERB_GAIN:
1970 *val = props->Reverb.Gain;
1971 break;
1973 case AL_REVERB_GAINHF:
1974 *val = props->Reverb.GainHF;
1975 break;
1977 case AL_REVERB_DECAY_TIME:
1978 *val = props->Reverb.DecayTime;
1979 break;
1981 case AL_REVERB_DECAY_HFRATIO:
1982 *val = props->Reverb.DecayHFRatio;
1983 break;
1985 case AL_REVERB_REFLECTIONS_GAIN:
1986 *val = props->Reverb.ReflectionsGain;
1987 break;
1989 case AL_REVERB_REFLECTIONS_DELAY:
1990 *val = props->Reverb.ReflectionsDelay;
1991 break;
1993 case AL_REVERB_LATE_REVERB_GAIN:
1994 *val = props->Reverb.LateReverbGain;
1995 break;
1997 case AL_REVERB_LATE_REVERB_DELAY:
1998 *val = props->Reverb.LateReverbDelay;
1999 break;
2001 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2002 *val = props->Reverb.AirAbsorptionGainHF;
2003 break;
2005 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2006 *val = props->Reverb.RoomRolloffFactor;
2007 break;
2009 default:
2010 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2013 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2014 { ALreverb_getParamf(effect, context, param, vals); }
2016 DEFINE_ALEFFECT_VTABLE(ALreverb);