Separate the delay line feeding from reading
[openal-soft.git] / Alc / effects / reverb.c
blob2a6aea35ef16a5d8b88f29304aac64fee9f42561
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];
1219 for(j = 0;j < NUM_LINES;j++)
1220 DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
1222 /* Also write the result back to the main delay line for the late reverb
1223 * stage to pick up at the appropriate time, appplying a scatter and
1224 * bounce to improve the initial diffusion in the late reverb.
1226 late_feed_tap = offset - State->LateFeedTap;
1227 VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
1229 static void EarlyReflection_Faded(ALreverbState *State, ALsizei offset, const ALsizei todo,
1230 const ALfloat fade, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1232 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1233 const DelayLineI early_delay = State->Early.Delay;
1234 const DelayLineI main_delay = State->Delay;
1235 const ALfloat mixX = State->MixX;
1236 const ALfloat mixY = State->MixY;
1237 ALsizei late_feed_tap;
1238 ALfloat fadeCount;
1239 ALsizei i, j;
1241 ASSUME(todo > 0);
1243 for(j = 0;j < NUM_LINES;j++)
1245 ALsizei early_delay_tap0 = offset - State->EarlyDelayTap[j][0];
1246 ALsizei early_delay_tap1 = offset - State->EarlyDelayTap[j][1];
1247 ALfloat oldCoeff = State->EarlyDelayCoeff[j][0];
1248 ALfloat oldCoeffStep = -oldCoeff / FADE_SAMPLES;
1249 ALfloat newCoeffStep = State->EarlyDelayCoeff[j][1] / FADE_SAMPLES;
1251 fadeCount = fade * FADE_SAMPLES;
1252 for(i = 0;i < todo;i++)
1254 const ALfloat fade0 = oldCoeff + oldCoeffStep*fadeCount;
1255 const ALfloat fade1 = newCoeffStep*fadeCount;
1256 temps[j][i] = FadedDelayLineOut(&main_delay,
1257 early_delay_tap0++, early_delay_tap1++, j, fade0, fade1
1259 fadeCount += 1.0f;
1263 VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->Early.VecAp);
1265 for(j = 0;j < NUM_LINES;j++)
1267 ALint feedb_tap0 = offset - State->Early.Offset[j][0];
1268 ALint feedb_tap1 = offset - State->Early.Offset[j][1];
1269 ALfloat feedb_oldCoeff = State->Early.Coeff[j][0];
1270 ALfloat feedb_oldCoeffStep = -feedb_oldCoeff / FADE_SAMPLES;
1271 ALfloat feedb_newCoeffStep = State->Early.Coeff[j][1] / FADE_SAMPLES;
1273 fadeCount = fade * FADE_SAMPLES;
1274 for(i = 0;i < todo;i++)
1276 const ALfloat fade0 = feedb_oldCoeff + feedb_oldCoeffStep*fadeCount;
1277 const ALfloat fade1 = feedb_newCoeffStep*fadeCount;
1278 out[j][i] = FadedDelayLineOut(&early_delay,
1279 feedb_tap0++, feedb_tap1++, j, fade0, fade1
1280 ) + temps[j][i];
1281 fadeCount += 1.0f;
1284 for(j = 0;j < NUM_LINES;j++)
1285 DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
1287 late_feed_tap = offset - State->LateFeedTap;
1288 VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
1291 /* Applies the two T60 damping filter sections. */
1292 static inline void LateT60Filter(ALfloat *restrict samples, const ALsizei todo, T60Filter *filter)
1294 ALfloat temp[MAX_UPDATE_SAMPLES];
1295 BiquadFilter_process(&filter->HFFilter, temp, samples, todo);
1296 BiquadFilter_process(&filter->LFFilter, samples, temp, todo);
1299 /* This generates the reverb tail using a modified feed-back delay network
1300 * (FDN).
1302 * Results from the early reflections are mixed with the output from the late
1303 * delay lines.
1305 * The late response is then completed by T60 and all-pass filtering the mix.
1307 * Finally, the lines are reversed (so they feed their opposite directions)
1308 * and scattered with the FDN matrix before re-feeding the delay lines.
1310 * Two variations are made, one for for transitional (cross-faded) delay line
1311 * processing and one for non-transitional processing.
1313 static void LateReverb_Unfaded(ALreverbState *State, ALsizei offset, const ALsizei todo,
1314 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1316 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1317 const DelayLineI late_delay = State->Late.Delay;
1318 const DelayLineI main_delay = State->Delay;
1319 const ALfloat mixX = State->MixX;
1320 const ALfloat mixY = State->MixY;
1321 ALsizei i, j;
1323 ASSUME(todo > 0);
1325 /* First, load decorrelated samples from the main and feedback delay lines.
1326 * Filter the signal to apply its frequency-dependent decay.
1328 for(j = 0;j < NUM_LINES;j++)
1330 ALsizei late_delay_tap = offset - State->LateDelayTap[j][0];
1331 ALsizei late_feedb_tap = offset - State->Late.Offset[j][0];
1332 ALfloat midGain = State->Late.T60[j].MidGain[0];
1333 const ALfloat densityGain = State->Late.DensityGain[0] * midGain;
1334 for(i = 0;i < todo;i++)
1335 temps[j][i] = DelayLineOut(&main_delay, late_delay_tap++, j)*densityGain +
1336 DelayLineOut(&late_delay, late_feedb_tap++, j)*midGain;
1337 LateT60Filter(temps[j], todo, &State->Late.T60[j]);
1340 /* Apply a vector all-pass to improve micro-surface diffusion, and write
1341 * out the results for mixing.
1343 VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Late.VecAp);
1345 for(j = 0;j < NUM_LINES;j++)
1346 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1348 /* Finally, scatter and bounce the results to refeed the feedback buffer. */
1349 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, out, todo);
1351 static void LateReverb_Faded(ALreverbState *State, ALsizei offset, const ALsizei todo,
1352 const ALfloat fade, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1354 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1355 const DelayLineI late_delay = State->Late.Delay;
1356 const DelayLineI main_delay = State->Delay;
1357 const ALfloat mixX = State->MixX;
1358 const ALfloat mixY = State->MixY;
1359 ALsizei i, j;
1361 ASSUME(todo > 0);
1363 for(j = 0;j < NUM_LINES;j++)
1365 const ALfloat oldMidGain = State->Late.T60[j].MidGain[0];
1366 const ALfloat midGain = State->Late.T60[j].MidGain[1];
1367 const ALfloat oldMidStep = -oldMidGain / FADE_SAMPLES;
1368 const ALfloat midStep = midGain / FADE_SAMPLES;
1369 const ALfloat oldDensityGain = State->Late.DensityGain[0] * oldMidGain;
1370 const ALfloat densityGain = State->Late.DensityGain[1] * midGain;
1371 const ALfloat oldDensityStep = -oldDensityGain / FADE_SAMPLES;
1372 const ALfloat densityStep = densityGain / FADE_SAMPLES;
1373 ALsizei late_delay_tap0 = offset - State->LateDelayTap[j][0];
1374 ALsizei late_delay_tap1 = offset - State->LateDelayTap[j][1];
1375 ALsizei late_feedb_tap0 = offset - State->Late.Offset[j][0];
1376 ALsizei late_feedb_tap1 = offset - State->Late.Offset[j][1];
1377 ALfloat fadeCount = fade * FADE_SAMPLES;
1378 for(i = 0;i < todo;i++)
1380 const ALfloat fade0 = oldDensityGain + oldDensityStep*fadeCount;
1381 const ALfloat fade1 = densityStep*fadeCount;
1382 const ALfloat gfade0 = oldMidGain + oldMidStep*fadeCount;
1383 const ALfloat gfade1 = midStep*fadeCount;
1384 temps[j][i] =
1385 FadedDelayLineOut(&main_delay, late_delay_tap0++, late_delay_tap1++, j,
1386 fade0, fade1) +
1387 FadedDelayLineOut(&late_delay, late_feedb_tap0++, late_feedb_tap1++, j,
1388 gfade0, gfade1);
1389 fadeCount += 1.0f;
1391 LateT60Filter(temps[j], todo, &State->Late.T60[j]);
1394 VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->Late.VecAp);
1396 for(j = 0;j < NUM_LINES;j++)
1397 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1399 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, temps, todo);
1402 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
1404 ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1405 ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES] = State->MixSamples;
1406 ALsizei fadeCount = State->FadeCount;
1407 ALsizei offset = State->Offset;
1408 ALsizei base, c;
1410 /* Process reverb for these samples. */
1411 for(base = 0;base < SamplesToDo;)
1413 ALsizei todo = SamplesToDo - base;
1414 /* If cross-fading, don't do more samples than there are to fade. */
1415 if(FADE_SAMPLES-fadeCount > 0)
1417 todo = mini(todo, FADE_SAMPLES-fadeCount);
1418 todo = mini(todo, State->MaxUpdate[0]);
1420 todo = mini(todo, State->MaxUpdate[1]);
1422 /* Convert B-Format to A-Format for processing. */
1423 memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
1424 for(c = 0;c < NUM_LINES;c++)
1425 MixRowSamples(afmt[c], B2A.m[c],
1426 SamplesIn, MAX_EFFECT_CHANNELS, base, todo
1429 /* Process the samples for reverb. */
1430 for(c = 0;c < NUM_LINES;c++)
1432 /* Band-pass the incoming samples. */
1433 BiquadFilter_process(&State->Filter[c].Lp, samples[0], afmt[c], todo);
1434 BiquadFilter_process(&State->Filter[c].Hp, samples[1], samples[0], todo);
1436 /* Feed the initial delay line. */
1437 DelayLineIn(&State->Delay, offset, c, samples[1], todo);
1440 if(UNLIKELY(fadeCount < FADE_SAMPLES))
1442 ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES;
1444 /* Generate early reflections. */
1445 EarlyReflection_Faded(State, offset, todo, fade, samples);
1446 /* Mix the A-Format results to output, implicitly converting back
1447 * to B-Format.
1449 for(c = 0;c < NUM_LINES;c++)
1450 MixSamples(samples[c], NumChannels, SamplesOut,
1451 State->Early.CurrentGain[c], State->Early.PanGain[c],
1452 SamplesToDo-base, base, todo
1455 /* Generate and mix late reverb. */
1456 LateReverb_Faded(State, offset, todo, fade, samples);
1457 for(c = 0;c < NUM_LINES;c++)
1458 MixSamples(samples[c], NumChannels, SamplesOut,
1459 State->Late.CurrentGain[c], State->Late.PanGain[c],
1460 SamplesToDo-base, base, todo
1463 /* Step fading forward. */
1464 fadeCount += todo;
1465 if(LIKELY(fadeCount >= FADE_SAMPLES))
1467 /* Update the cross-fading delay line taps. */
1468 fadeCount = FADE_SAMPLES;
1469 for(c = 0;c < NUM_LINES;c++)
1471 State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
1472 State->EarlyDelayCoeff[c][0] = State->EarlyDelayCoeff[c][1];
1473 State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1];
1474 State->Early.Offset[c][0] = State->Early.Offset[c][1];
1475 State->Early.Coeff[c][0] = State->Early.Coeff[c][1];
1476 State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
1477 State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1];
1478 State->Late.Offset[c][0] = State->Late.Offset[c][1];
1479 State->Late.T60[c].MidGain[0] = State->Late.T60[c].MidGain[1];
1481 State->Late.DensityGain[0] = State->Late.DensityGain[1];
1482 State->MaxUpdate[0] = State->MaxUpdate[1];
1485 else
1487 /* Generate and mix early reflections. */
1488 EarlyReflection_Unfaded(State, offset, todo, samples);
1489 for(c = 0;c < NUM_LINES;c++)
1490 MixSamples(samples[c], NumChannels, SamplesOut,
1491 State->Early.CurrentGain[c], State->Early.PanGain[c],
1492 SamplesToDo-base, base, todo
1495 /* Generate and mix late reverb. */
1496 LateReverb_Unfaded(State, offset, todo, samples);
1497 for(c = 0;c < NUM_LINES;c++)
1498 MixSamples(samples[c], NumChannels, SamplesOut,
1499 State->Late.CurrentGain[c], State->Late.PanGain[c],
1500 SamplesToDo-base, base, todo
1504 /* Step all delays forward. */
1505 offset += todo;
1507 base += todo;
1509 State->Offset = offset;
1510 State->FadeCount = fadeCount;
1514 typedef struct ReverbStateFactory {
1515 DERIVE_FROM_TYPE(EffectStateFactory);
1516 } ReverbStateFactory;
1518 static ALeffectState *ReverbStateFactory_create(ReverbStateFactory* UNUSED(factory))
1520 ALreverbState *state;
1522 NEW_OBJ0(state, ALreverbState)();
1523 if(!state) return NULL;
1525 return STATIC_CAST(ALeffectState, state);
1528 DEFINE_EFFECTSTATEFACTORY_VTABLE(ReverbStateFactory);
1530 EffectStateFactory *ReverbStateFactory_getFactory(void)
1532 static ReverbStateFactory ReverbFactory = { { GET_VTABLE2(ReverbStateFactory, EffectStateFactory) } };
1534 return STATIC_CAST(EffectStateFactory, &ReverbFactory);
1538 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1540 ALeffectProps *props = &effect->Props;
1541 switch(param)
1543 case AL_EAXREVERB_DECAY_HFLIMIT:
1544 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1545 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
1546 props->Reverb.DecayHFLimit = val;
1547 break;
1549 default:
1550 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1551 param);
1554 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1555 { ALeaxreverb_setParami(effect, context, param, vals[0]); }
1556 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1558 ALeffectProps *props = &effect->Props;
1559 switch(param)
1561 case AL_EAXREVERB_DENSITY:
1562 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1563 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
1564 props->Reverb.Density = val;
1565 break;
1567 case AL_EAXREVERB_DIFFUSION:
1568 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1569 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
1570 props->Reverb.Diffusion = val;
1571 break;
1573 case AL_EAXREVERB_GAIN:
1574 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1575 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
1576 props->Reverb.Gain = val;
1577 break;
1579 case AL_EAXREVERB_GAINHF:
1580 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1581 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
1582 props->Reverb.GainHF = val;
1583 break;
1585 case AL_EAXREVERB_GAINLF:
1586 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1587 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
1588 props->Reverb.GainLF = val;
1589 break;
1591 case AL_EAXREVERB_DECAY_TIME:
1592 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1593 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
1594 props->Reverb.DecayTime = val;
1595 break;
1597 case AL_EAXREVERB_DECAY_HFRATIO:
1598 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1599 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
1600 props->Reverb.DecayHFRatio = val;
1601 break;
1603 case AL_EAXREVERB_DECAY_LFRATIO:
1604 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1605 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
1606 props->Reverb.DecayLFRatio = val;
1607 break;
1609 case AL_EAXREVERB_REFLECTIONS_GAIN:
1610 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1611 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
1612 props->Reverb.ReflectionsGain = val;
1613 break;
1615 case AL_EAXREVERB_REFLECTIONS_DELAY:
1616 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1617 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
1618 props->Reverb.ReflectionsDelay = val;
1619 break;
1621 case AL_EAXREVERB_LATE_REVERB_GAIN:
1622 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1623 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
1624 props->Reverb.LateReverbGain = val;
1625 break;
1627 case AL_EAXREVERB_LATE_REVERB_DELAY:
1628 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1629 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
1630 props->Reverb.LateReverbDelay = val;
1631 break;
1633 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1634 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1635 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
1636 props->Reverb.AirAbsorptionGainHF = val;
1637 break;
1639 case AL_EAXREVERB_ECHO_TIME:
1640 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1641 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
1642 props->Reverb.EchoTime = val;
1643 break;
1645 case AL_EAXREVERB_ECHO_DEPTH:
1646 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1647 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
1648 props->Reverb.EchoDepth = val;
1649 break;
1651 case AL_EAXREVERB_MODULATION_TIME:
1652 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1653 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
1654 props->Reverb.ModulationTime = val;
1655 break;
1657 case AL_EAXREVERB_MODULATION_DEPTH:
1658 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1659 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
1660 props->Reverb.ModulationDepth = val;
1661 break;
1663 case AL_EAXREVERB_HFREFERENCE:
1664 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1665 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
1666 props->Reverb.HFReference = val;
1667 break;
1669 case AL_EAXREVERB_LFREFERENCE:
1670 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1671 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
1672 props->Reverb.LFReference = val;
1673 break;
1675 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1676 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1677 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
1678 props->Reverb.RoomRolloffFactor = val;
1679 break;
1681 default:
1682 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1683 param);
1686 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1688 ALeffectProps *props = &effect->Props;
1689 switch(param)
1691 case AL_EAXREVERB_REFLECTIONS_PAN:
1692 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1693 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
1694 props->Reverb.ReflectionsPan[0] = vals[0];
1695 props->Reverb.ReflectionsPan[1] = vals[1];
1696 props->Reverb.ReflectionsPan[2] = vals[2];
1697 break;
1698 case AL_EAXREVERB_LATE_REVERB_PAN:
1699 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1700 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
1701 props->Reverb.LateReverbPan[0] = vals[0];
1702 props->Reverb.LateReverbPan[1] = vals[1];
1703 props->Reverb.LateReverbPan[2] = vals[2];
1704 break;
1706 default:
1707 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1708 break;
1712 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1714 const ALeffectProps *props = &effect->Props;
1715 switch(param)
1717 case AL_EAXREVERB_DECAY_HFLIMIT:
1718 *val = props->Reverb.DecayHFLimit;
1719 break;
1721 default:
1722 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1723 param);
1726 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1727 { ALeaxreverb_getParami(effect, context, param, vals); }
1728 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1730 const ALeffectProps *props = &effect->Props;
1731 switch(param)
1733 case AL_EAXREVERB_DENSITY:
1734 *val = props->Reverb.Density;
1735 break;
1737 case AL_EAXREVERB_DIFFUSION:
1738 *val = props->Reverb.Diffusion;
1739 break;
1741 case AL_EAXREVERB_GAIN:
1742 *val = props->Reverb.Gain;
1743 break;
1745 case AL_EAXREVERB_GAINHF:
1746 *val = props->Reverb.GainHF;
1747 break;
1749 case AL_EAXREVERB_GAINLF:
1750 *val = props->Reverb.GainLF;
1751 break;
1753 case AL_EAXREVERB_DECAY_TIME:
1754 *val = props->Reverb.DecayTime;
1755 break;
1757 case AL_EAXREVERB_DECAY_HFRATIO:
1758 *val = props->Reverb.DecayHFRatio;
1759 break;
1761 case AL_EAXREVERB_DECAY_LFRATIO:
1762 *val = props->Reverb.DecayLFRatio;
1763 break;
1765 case AL_EAXREVERB_REFLECTIONS_GAIN:
1766 *val = props->Reverb.ReflectionsGain;
1767 break;
1769 case AL_EAXREVERB_REFLECTIONS_DELAY:
1770 *val = props->Reverb.ReflectionsDelay;
1771 break;
1773 case AL_EAXREVERB_LATE_REVERB_GAIN:
1774 *val = props->Reverb.LateReverbGain;
1775 break;
1777 case AL_EAXREVERB_LATE_REVERB_DELAY:
1778 *val = props->Reverb.LateReverbDelay;
1779 break;
1781 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1782 *val = props->Reverb.AirAbsorptionGainHF;
1783 break;
1785 case AL_EAXREVERB_ECHO_TIME:
1786 *val = props->Reverb.EchoTime;
1787 break;
1789 case AL_EAXREVERB_ECHO_DEPTH:
1790 *val = props->Reverb.EchoDepth;
1791 break;
1793 case AL_EAXREVERB_MODULATION_TIME:
1794 *val = props->Reverb.ModulationTime;
1795 break;
1797 case AL_EAXREVERB_MODULATION_DEPTH:
1798 *val = props->Reverb.ModulationDepth;
1799 break;
1801 case AL_EAXREVERB_HFREFERENCE:
1802 *val = props->Reverb.HFReference;
1803 break;
1805 case AL_EAXREVERB_LFREFERENCE:
1806 *val = props->Reverb.LFReference;
1807 break;
1809 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1810 *val = props->Reverb.RoomRolloffFactor;
1811 break;
1813 default:
1814 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1815 param);
1818 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
1820 const ALeffectProps *props = &effect->Props;
1821 switch(param)
1823 case AL_EAXREVERB_REFLECTIONS_PAN:
1824 vals[0] = props->Reverb.ReflectionsPan[0];
1825 vals[1] = props->Reverb.ReflectionsPan[1];
1826 vals[2] = props->Reverb.ReflectionsPan[2];
1827 break;
1828 case AL_EAXREVERB_LATE_REVERB_PAN:
1829 vals[0] = props->Reverb.LateReverbPan[0];
1830 vals[1] = props->Reverb.LateReverbPan[1];
1831 vals[2] = props->Reverb.LateReverbPan[2];
1832 break;
1834 default:
1835 ALeaxreverb_getParamf(effect, context, param, vals);
1836 break;
1840 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
1842 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1844 ALeffectProps *props = &effect->Props;
1845 switch(param)
1847 case AL_REVERB_DECAY_HFLIMIT:
1848 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
1849 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
1850 props->Reverb.DecayHFLimit = val;
1851 break;
1853 default:
1854 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
1857 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1858 { ALreverb_setParami(effect, context, param, vals[0]); }
1859 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1861 ALeffectProps *props = &effect->Props;
1862 switch(param)
1864 case AL_REVERB_DENSITY:
1865 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
1866 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
1867 props->Reverb.Density = val;
1868 break;
1870 case AL_REVERB_DIFFUSION:
1871 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
1872 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
1873 props->Reverb.Diffusion = val;
1874 break;
1876 case AL_REVERB_GAIN:
1877 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
1878 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
1879 props->Reverb.Gain = val;
1880 break;
1882 case AL_REVERB_GAINHF:
1883 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
1884 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
1885 props->Reverb.GainHF = val;
1886 break;
1888 case AL_REVERB_DECAY_TIME:
1889 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
1890 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
1891 props->Reverb.DecayTime = val;
1892 break;
1894 case AL_REVERB_DECAY_HFRATIO:
1895 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
1896 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
1897 props->Reverb.DecayHFRatio = val;
1898 break;
1900 case AL_REVERB_REFLECTIONS_GAIN:
1901 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
1902 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
1903 props->Reverb.ReflectionsGain = val;
1904 break;
1906 case AL_REVERB_REFLECTIONS_DELAY:
1907 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
1908 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
1909 props->Reverb.ReflectionsDelay = val;
1910 break;
1912 case AL_REVERB_LATE_REVERB_GAIN:
1913 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
1914 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
1915 props->Reverb.LateReverbGain = val;
1916 break;
1918 case AL_REVERB_LATE_REVERB_DELAY:
1919 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
1920 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
1921 props->Reverb.LateReverbDelay = val;
1922 break;
1924 case AL_REVERB_AIR_ABSORPTION_GAINHF:
1925 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
1926 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
1927 props->Reverb.AirAbsorptionGainHF = val;
1928 break;
1930 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
1931 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
1932 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
1933 props->Reverb.RoomRolloffFactor = val;
1934 break;
1936 default:
1937 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
1940 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1941 { ALreverb_setParamf(effect, context, param, vals[0]); }
1943 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1945 const ALeffectProps *props = &effect->Props;
1946 switch(param)
1948 case AL_REVERB_DECAY_HFLIMIT:
1949 *val = props->Reverb.DecayHFLimit;
1950 break;
1952 default:
1953 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
1956 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1957 { ALreverb_getParami(effect, context, param, vals); }
1958 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1960 const ALeffectProps *props = &effect->Props;
1961 switch(param)
1963 case AL_REVERB_DENSITY:
1964 *val = props->Reverb.Density;
1965 break;
1967 case AL_REVERB_DIFFUSION:
1968 *val = props->Reverb.Diffusion;
1969 break;
1971 case AL_REVERB_GAIN:
1972 *val = props->Reverb.Gain;
1973 break;
1975 case AL_REVERB_GAINHF:
1976 *val = props->Reverb.GainHF;
1977 break;
1979 case AL_REVERB_DECAY_TIME:
1980 *val = props->Reverb.DecayTime;
1981 break;
1983 case AL_REVERB_DECAY_HFRATIO:
1984 *val = props->Reverb.DecayHFRatio;
1985 break;
1987 case AL_REVERB_REFLECTIONS_GAIN:
1988 *val = props->Reverb.ReflectionsGain;
1989 break;
1991 case AL_REVERB_REFLECTIONS_DELAY:
1992 *val = props->Reverb.ReflectionsDelay;
1993 break;
1995 case AL_REVERB_LATE_REVERB_GAIN:
1996 *val = props->Reverb.LateReverbGain;
1997 break;
1999 case AL_REVERB_LATE_REVERB_DELAY:
2000 *val = props->Reverb.LateReverbDelay;
2001 break;
2003 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2004 *val = props->Reverb.AirAbsorptionGainHF;
2005 break;
2007 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2008 *val = props->Reverb.RoomRolloffFactor;
2009 break;
2011 default:
2012 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2015 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2016 { ALreverb_getParamf(effect, context, param, vals); }
2018 DEFINE_ALEFFECT_VTABLE(ALreverb);