Rename ALreverbState to ReverbState
[openal-soft.git] / Alc / effects / reverb.c
blob882b364f9fd401526cbcb66b3f196016c897e3f7
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 ReverbState {
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 } ReverbState;
325 static ALvoid ReverbState_Destruct(ReverbState *State);
326 static ALboolean ReverbState_deviceUpdate(ReverbState *State, ALCdevice *Device);
327 static ALvoid ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
328 static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
329 DECLARE_DEFAULT_ALLOCATORS(ReverbState)
331 DEFINE_ALEFFECTSTATE_VTABLE(ReverbState);
333 static void ReverbState_Construct(ReverbState *state)
335 ALsizei i, j;
337 ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
338 SET_VTABLE2(ReverbState, 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 ReverbState_Destruct(ReverbState *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, ReverbState *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 ReverbState_deviceUpdate(ReverbState *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, ReverbState *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 aluMatrixf focus;
806 ALfloat norm[3];
807 ALfloat mag;
809 /* Normalize the panning vector according to the N3D scale, which has an
810 * extra sqrt(3) term on the directional components. Converting from OpenAL
811 * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
812 * that the reverb panning vectors use left-handed coordinates, unlike the
813 * rest of OpenAL which use right-handed. This is fixed by negating Z,
814 * which cancels out with the B-Format Z negation.
816 mag = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
817 if(mag > 1.0f)
819 norm[0] = vec[0] / mag * -SQRTF_3;
820 norm[1] = vec[1] / mag * SQRTF_3;
821 norm[2] = vec[2] / mag * SQRTF_3;
822 mag = 1.0f;
824 else
826 /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
827 * term. There's no need to renormalize the magnitude since it would
828 * just be reapplied in the matrix.
830 norm[0] = vec[0] * -SQRTF_3;
831 norm[1] = vec[1] * SQRTF_3;
832 norm[2] = vec[2] * SQRTF_3;
835 aluMatrixfSet(&focus,
836 1.0f, 0.0f, 0.0f, 0.0f,
837 norm[0], 1.0f-mag, 0.0f, 0.0f,
838 norm[1], 0.0f, 1.0f-mag, 0.0f,
839 norm[2], 0.0f, 0.0f, 1.0f-mag
842 return focus;
845 /* Update the early and late 3D panning gains. */
846 static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat earlyGain, const ALfloat lateGain, ReverbState *State)
848 aluMatrixf transform, rot;
849 ALsizei i;
851 STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
852 STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;
854 /* Note: _res is transposed. */
855 #define MATRIX_MULT(_res, _m1, _m2) do { \
856 int row, col; \
857 for(col = 0;col < 4;col++) \
859 for(row = 0;row < 4;row++) \
860 _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
861 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
863 } while(0)
864 /* Create a matrix that first converts A-Format to B-Format, then
865 * transforms the B-Format signal according to the panning vector.
867 rot = GetTransformFromVector(ReflectionsPan);
868 MATRIX_MULT(transform, rot, A2B);
869 memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
870 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
871 ComputePanGains(&Device->FOAOut, transform.m[i], earlyGain,
872 State->Early.PanGain[i]);
874 rot = GetTransformFromVector(LateReverbPan);
875 MATRIX_MULT(transform, rot, A2B);
876 memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
877 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
878 ComputePanGains(&Device->FOAOut, transform.m[i], lateGain,
879 State->Late.PanGain[i]);
880 #undef MATRIX_MULT
883 static ALvoid ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
885 const ALCdevice *Device = Context->Device;
886 const ALlistener *Listener = Context->Listener;
887 ALuint frequency = Device->Frequency;
888 ALfloat lf0norm, hf0norm, hfRatio;
889 ALfloat lfDecayTime, hfDecayTime;
890 ALfloat gain, gainlf, gainhf;
891 ALsizei i;
893 /* Calculate the master filters */
894 hf0norm = minf(props->Reverb.HFReference / frequency, 0.49f);
895 /* Restrict the filter gains from going below -60dB to keep the filter from
896 * killing most of the signal.
898 gainhf = maxf(props->Reverb.GainHF, 0.001f);
899 BiquadFilter_setParams(&State->Filter[0].Lp, BiquadType_HighShelf, gainhf, hf0norm,
900 calc_rcpQ_from_slope(gainhf, 1.0f));
901 lf0norm = minf(props->Reverb.LFReference / frequency, 0.49f);
902 gainlf = maxf(props->Reverb.GainLF, 0.001f);
903 BiquadFilter_setParams(&State->Filter[0].Hp, BiquadType_LowShelf, gainlf, lf0norm,
904 calc_rcpQ_from_slope(gainlf, 1.0f));
905 for(i = 1;i < NUM_LINES;i++)
907 BiquadFilter_copyParams(&State->Filter[i].Lp, &State->Filter[0].Lp);
908 BiquadFilter_copyParams(&State->Filter[i].Hp, &State->Filter[0].Hp);
911 /* Update the main effect delay and associated taps. */
912 UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
913 props->Reverb.Density, props->Reverb.DecayTime, frequency,
914 State);
916 /* Update the early lines. */
917 UpdateEarlyLines(props->Reverb.Density, props->Reverb.Diffusion,
918 props->Reverb.DecayTime, frequency, &State->Early);
920 /* Get the mixing matrix coefficients. */
921 CalcMatrixCoeffs(props->Reverb.Diffusion, &State->MixX, &State->MixY);
923 /* If the HF limit parameter is flagged, calculate an appropriate limit
924 * based on the air absorption parameter.
926 hfRatio = props->Reverb.DecayHFRatio;
927 if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
928 hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
929 props->Reverb.DecayTime, Listener->Params.ReverbSpeedOfSound
932 /* Calculate the LF/HF decay times. */
933 lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
934 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
935 hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
936 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
938 /* Update the late lines. */
939 UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
940 lfDecayTime, props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm,
941 frequency, &State->Late
944 /* Update early and late 3D panning. */
945 gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
946 Update3DPanning(Device, props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan,
947 props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain,
948 State);
950 /* Calculate the max update size from the smallest relevant delay. */
951 State->MaxUpdate[1] = mini(MAX_UPDATE_SAMPLES,
952 mini(State->Early.Offset[0][1], State->Late.Offset[0][1])
955 /* Determine if delay-line cross-fading is required. TODO: Add some fuzz
956 * for the float comparisons? The math should be stable enough that the
957 * result should be the same if nothing's changed, and changes in the float
958 * values should (though may not always) be matched by changes in delay
959 * offsets.
961 if(State->Late.DensityGain[1] != State->Late.DensityGain[0])
962 State->FadeCount = 0;
963 else for(i = 0;i < NUM_LINES;i++)
965 if(State->EarlyDelayTap[i][1] != State->EarlyDelayTap[i][0] ||
966 State->EarlyDelayCoeff[i][1] != State->EarlyDelayCoeff[i][0] ||
967 State->Early.VecAp.Offset[i][1] != State->Early.VecAp.Offset[i][0] ||
968 State->Early.Offset[i][1] != State->Early.Offset[i][0] ||
969 State->Early.Coeff[i][1] != State->Early.Coeff[i][0] ||
970 State->LateDelayTap[i][1] != State->LateDelayTap[i][0] ||
971 State->Late.VecAp.Offset[i][1] != State->Late.VecAp.Offset[i][0] ||
972 State->Late.Offset[i][1] != State->Late.Offset[i][0] ||
973 State->Late.T60[i].MidGain[1] != State->Late.T60[i].MidGain[0])
975 State->FadeCount = 0;
976 break;
982 /**************************************
983 * Effect Processing *
984 **************************************/
986 /* Basic delay line input/output routines. */
987 static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c)
989 return Delay->Line[offset&Delay->Mask][c];
992 /* Cross-faded delay line output routine. Instead of interpolating the
993 * offsets, this interpolates (cross-fades) the outputs at each offset.
995 static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0,
996 const ALsizei off1, const ALsizei c,
997 const ALfloat sc0, const ALfloat sc1)
999 return Delay->Line[off0&Delay->Mask][c]*sc0 +
1000 Delay->Line[off1&Delay->Mask][c]*sc1;
1004 static inline void DelayLineIn(const DelayLineI *Delay, ALsizei offset, const ALsizei c,
1005 const ALfloat *restrict in, ALsizei count)
1007 ALsizei i;
1008 for(i = 0;i < count;i++)
1009 Delay->Line[(offset++)&Delay->Mask][c] = *(in++);
1012 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1013 * for both the below vector all-pass model and to perform modal feed-back
1014 * delay network (FDN) mixing.
1016 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1017 * matrix with a single unitary rotational parameter:
1019 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1020 * [ -a, d, c, -b ]
1021 * [ -b, -c, d, a ]
1022 * [ -c, b, -a, d ]
1024 * The rotation is constructed from the effect's diffusion parameter,
1025 * yielding:
1027 * 1 = x^2 + 3 y^2
1029 * Where a, b, and c are the coefficient y with differing signs, and d is the
1030 * coefficient x. The final matrix is thus:
1032 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1033 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1034 * [ y, -y, x, y ] x = cos(t)
1035 * [ -y, -y, -y, x ] y = sin(t) / n
1037 * Any square orthogonal matrix with an order that is a power of two will
1038 * work (where ^T is transpose, ^-1 is inverse):
1040 * M^T = M^-1
1042 * Using that knowledge, finding an appropriate matrix can be accomplished
1043 * naively by searching all combinations of:
1045 * M = D + S - S^T
1047 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1048 * whose combination of signs are being iterated.
1050 static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *restrict in,
1051 const ALfloat xCoeff, const ALfloat yCoeff)
1053 out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
1054 out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
1055 out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
1056 out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
1058 #define VectorScatterDelayIn(delay, o, in, xcoeff, ycoeff) \
1059 VectorPartialScatter((delay)->Line[(o)&(delay)->Mask], in, xcoeff, ycoeff)
1061 /* Utilizes the above, but reverses the input channels. */
1062 static inline void VectorScatterRevDelayIn(const DelayLineI *Delay, ALint offset,
1063 const ALfloat xCoeff, const ALfloat yCoeff,
1064 const ALfloat (*restrict in)[MAX_UPDATE_SAMPLES],
1065 const ALsizei count)
1067 const DelayLineI delay = *Delay;
1068 ALsizei i, j;
1070 for(i = 0;i < count;++i)
1072 ALfloat f[NUM_LINES];
1073 for(j = 0;j < NUM_LINES;j++)
1074 f[NUM_LINES-1-j] = in[j][i];
1076 VectorScatterDelayIn(&delay, offset++, f, xCoeff, yCoeff);
1080 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1081 * filter to the 4-line input.
1083 * It works by vectorizing a regular all-pass filter and replacing the delay
1084 * element with a scattering matrix (like the one above) and a diagonal
1085 * matrix of delay elements.
1087 * Two static specializations are used for transitional (cross-faded) delay
1088 * line processing and non-transitional processing.
1090 static void VectorAllpass_Unfaded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
1091 const ALfloat xCoeff, const ALfloat yCoeff, ALsizei todo,
1092 VecAllpass *Vap)
1094 const DelayLineI delay = Vap->Delay;
1095 const ALfloat feedCoeff = Vap->Coeff;
1096 ALsizei vap_offset[NUM_LINES];
1097 ALsizei i, j;
1099 ASSUME(todo > 0);
1101 for(j = 0;j < NUM_LINES;j++)
1102 vap_offset[j] = offset-Vap->Offset[j][0];
1103 for(i = 0;i < todo;i++)
1105 ALfloat f[NUM_LINES];
1107 for(j = 0;j < NUM_LINES;j++)
1109 ALfloat input = samples[j][i];
1110 ALfloat out = DelayLineOut(&delay, vap_offset[j]++, j) - feedCoeff*input;
1111 f[j] = input + feedCoeff*out;
1113 samples[j][i] = out;
1116 VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
1117 ++offset;
1120 static void VectorAllpass_Faded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
1121 const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade,
1122 ALsizei todo, VecAllpass *Vap)
1124 const DelayLineI delay = Vap->Delay;
1125 const ALfloat feedCoeff = Vap->Coeff;
1126 ALsizei vap_offset[NUM_LINES][2];
1127 ALsizei i, j;
1129 ASSUME(todo > 0);
1131 for(j = 0;j < NUM_LINES;j++)
1133 vap_offset[j][0] = offset-Vap->Offset[j][0];
1134 vap_offset[j][1] = offset-Vap->Offset[j][1];
1136 for(i = 0;i < todo;i++)
1138 ALfloat f[NUM_LINES];
1140 for(j = 0;j < NUM_LINES;j++)
1142 ALfloat input = samples[j][i];
1143 ALfloat out =
1144 FadedDelayLineOut(&delay, vap_offset[j][0]++, vap_offset[j][1]++, j,
1145 1.0f-fade, fade
1146 ) - feedCoeff*input;
1147 f[j] = input + feedCoeff*out;
1149 samples[j][i] = out;
1151 fade += FadeStep;
1153 VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
1154 ++offset;
1158 /* This generates early reflections.
1160 * This is done by obtaining the primary reflections (those arriving from the
1161 * same direction as the source) from the main delay line. These are
1162 * attenuated and all-pass filtered (based on the diffusion parameter).
1164 * The early lines are then fed in reverse (according to the approximately
1165 * opposite spatial location of the A-Format lines) to create the secondary
1166 * reflections (those arriving from the opposite direction as the source).
1168 * The early response is then completed by combining the primary reflections
1169 * with the delayed and attenuated output from the early lines.
1171 * Finally, the early response is reversed, scattered (based on diffusion),
1172 * and fed into the late reverb section of the main delay line.
1174 * Two static specializations are used for transitional (cross-faded) delay
1175 * line processing and non-transitional processing.
1177 static void EarlyReflection_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
1178 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1180 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1181 const DelayLineI early_delay = State->Early.Delay;
1182 const DelayLineI main_delay = State->Delay;
1183 const ALfloat mixX = State->MixX;
1184 const ALfloat mixY = State->MixY;
1185 ALsizei late_feed_tap;
1186 ALsizei i, j;
1188 ASSUME(todo > 0);
1190 /* First, load decorrelated samples from the main delay line as the primary
1191 * reflections.
1193 for(j = 0;j < NUM_LINES;j++)
1195 ALsizei early_delay_tap = offset - State->EarlyDelayTap[j][0];
1196 ALfloat coeff = State->EarlyDelayCoeff[j][0];
1197 for(i = 0;i < todo;i++)
1198 temps[j][i] = DelayLineOut(&main_delay, early_delay_tap++, j) * coeff;
1201 /* Apply a vector all-pass, to help color the initial reflections based on
1202 * the diffusion strength.
1204 VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Early.VecAp);
1206 /* Apply a delay and bounce to generate secondary reflections, combine with
1207 * the primary reflections and write out the result for mixing.
1209 for(j = 0;j < NUM_LINES;j++)
1211 ALint early_feedb_tap = offset - State->Early.Offset[j][0];
1212 ALfloat early_feedb_coeff = State->Early.Coeff[j][0];
1214 for(i = 0;i < todo;i++)
1215 out[j][i] = DelayLineOut(&early_delay, early_feedb_tap++, j)*early_feedb_coeff +
1216 temps[j][i];
1218 for(j = 0;j < NUM_LINES;j++)
1219 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(ReverbState *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;
1283 for(j = 0;j < NUM_LINES;j++)
1284 DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
1286 late_feed_tap = offset - State->LateFeedTap;
1287 VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
1290 /* Applies the two T60 damping filter sections. */
1291 static inline void LateT60Filter(ALfloat *restrict samples, const ALsizei todo, T60Filter *filter)
1293 ALfloat temp[MAX_UPDATE_SAMPLES];
1294 BiquadFilter_process(&filter->HFFilter, temp, samples, todo);
1295 BiquadFilter_process(&filter->LFFilter, samples, temp, todo);
1298 /* This generates the reverb tail using a modified feed-back delay network
1299 * (FDN).
1301 * Results from the early reflections are mixed with the output from the late
1302 * delay lines.
1304 * The late response is then completed by T60 and all-pass filtering the mix.
1306 * Finally, the lines are reversed (so they feed their opposite directions)
1307 * and scattered with the FDN matrix before re-feeding the delay lines.
1309 * Two variations are made, one for for transitional (cross-faded) delay line
1310 * processing and one for non-transitional processing.
1312 static void LateReverb_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
1313 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1315 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1316 const DelayLineI late_delay = State->Late.Delay;
1317 const DelayLineI main_delay = State->Delay;
1318 const ALfloat mixX = State->MixX;
1319 const ALfloat mixY = State->MixY;
1320 ALsizei i, j;
1322 ASSUME(todo > 0);
1324 /* First, load decorrelated samples from the main and feedback delay lines.
1325 * Filter the signal to apply its frequency-dependent decay.
1327 for(j = 0;j < NUM_LINES;j++)
1329 ALsizei late_delay_tap = offset - State->LateDelayTap[j][0];
1330 ALsizei late_feedb_tap = offset - State->Late.Offset[j][0];
1331 ALfloat midGain = State->Late.T60[j].MidGain[0];
1332 const ALfloat densityGain = State->Late.DensityGain[0] * midGain;
1333 for(i = 0;i < todo;i++)
1334 temps[j][i] = DelayLineOut(&main_delay, late_delay_tap++, j)*densityGain +
1335 DelayLineOut(&late_delay, late_feedb_tap++, j)*midGain;
1336 LateT60Filter(temps[j], todo, &State->Late.T60[j]);
1339 /* Apply a vector all-pass to improve micro-surface diffusion, and write
1340 * out the results for mixing.
1342 VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Late.VecAp);
1344 for(j = 0;j < NUM_LINES;j++)
1345 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1347 /* Finally, scatter and bounce the results to refeed the feedback buffer. */
1348 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, out, todo);
1350 static void LateReverb_Faded(ReverbState *State, ALsizei offset, const ALsizei todo,
1351 const ALfloat fade, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1353 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1354 const DelayLineI late_delay = State->Late.Delay;
1355 const DelayLineI main_delay = State->Delay;
1356 const ALfloat mixX = State->MixX;
1357 const ALfloat mixY = State->MixY;
1358 ALsizei i, j;
1360 ASSUME(todo > 0);
1362 for(j = 0;j < NUM_LINES;j++)
1364 const ALfloat oldMidGain = State->Late.T60[j].MidGain[0];
1365 const ALfloat midGain = State->Late.T60[j].MidGain[1];
1366 const ALfloat oldMidStep = -oldMidGain / FADE_SAMPLES;
1367 const ALfloat midStep = midGain / FADE_SAMPLES;
1368 const ALfloat oldDensityGain = State->Late.DensityGain[0] * oldMidGain;
1369 const ALfloat densityGain = State->Late.DensityGain[1] * midGain;
1370 const ALfloat oldDensityStep = -oldDensityGain / FADE_SAMPLES;
1371 const ALfloat densityStep = densityGain / FADE_SAMPLES;
1372 ALsizei late_delay_tap0 = offset - State->LateDelayTap[j][0];
1373 ALsizei late_delay_tap1 = offset - State->LateDelayTap[j][1];
1374 ALsizei late_feedb_tap0 = offset - State->Late.Offset[j][0];
1375 ALsizei late_feedb_tap1 = offset - State->Late.Offset[j][1];
1376 ALfloat fadeCount = fade * FADE_SAMPLES;
1377 for(i = 0;i < todo;i++)
1379 const ALfloat fade0 = oldDensityGain + oldDensityStep*fadeCount;
1380 const ALfloat fade1 = densityStep*fadeCount;
1381 const ALfloat gfade0 = oldMidGain + oldMidStep*fadeCount;
1382 const ALfloat gfade1 = midStep*fadeCount;
1383 temps[j][i] =
1384 FadedDelayLineOut(&main_delay, late_delay_tap0++, late_delay_tap1++, j,
1385 fade0, fade1) +
1386 FadedDelayLineOut(&late_delay, late_feedb_tap0++, late_feedb_tap1++, j,
1387 gfade0, gfade1);
1388 fadeCount += 1.0f;
1390 LateT60Filter(temps[j], todo, &State->Late.T60[j]);
1393 VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->Late.VecAp);
1395 for(j = 0;j < NUM_LINES;j++)
1396 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1398 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, temps, todo);
1401 static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
1403 ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1404 ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES] = State->MixSamples;
1405 ALsizei fadeCount = State->FadeCount;
1406 ALsizei offset = State->Offset;
1407 ALsizei base, c;
1409 /* Process reverb for these samples. */
1410 for(base = 0;base < SamplesToDo;)
1412 ALsizei todo = SamplesToDo - base;
1413 /* If cross-fading, don't do more samples than there are to fade. */
1414 if(FADE_SAMPLES-fadeCount > 0)
1416 todo = mini(todo, FADE_SAMPLES-fadeCount);
1417 todo = mini(todo, State->MaxUpdate[0]);
1419 todo = mini(todo, State->MaxUpdate[1]);
1420 /* If this is not the final update, ensure the update size is a
1421 * multiple of 4 for the SIMD mixers.
1423 if(todo < SamplesToDo-base)
1424 todo &= ~3;
1426 /* Convert B-Format to A-Format for processing. */
1427 memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
1428 for(c = 0;c < NUM_LINES;c++)
1429 MixRowSamples(afmt[c], B2A.m[c],
1430 SamplesIn, MAX_EFFECT_CHANNELS, base, todo
1433 /* Process the samples for reverb. */
1434 for(c = 0;c < NUM_LINES;c++)
1436 /* Band-pass the incoming samples. */
1437 BiquadFilter_process(&State->Filter[c].Lp, samples[0], afmt[c], todo);
1438 BiquadFilter_process(&State->Filter[c].Hp, samples[1], samples[0], todo);
1440 /* Feed the initial delay line. */
1441 DelayLineIn(&State->Delay, offset, c, samples[1], todo);
1444 if(UNLIKELY(fadeCount < FADE_SAMPLES))
1446 ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES;
1448 /* Generate early reflections. */
1449 EarlyReflection_Faded(State, offset, todo, fade, samples);
1450 /* Mix the A-Format results to output, implicitly converting back
1451 * to B-Format.
1453 for(c = 0;c < NUM_LINES;c++)
1454 MixSamples(samples[c], NumChannels, SamplesOut,
1455 State->Early.CurrentGain[c], State->Early.PanGain[c],
1456 SamplesToDo-base, base, todo
1459 /* Generate and mix late reverb. */
1460 LateReverb_Faded(State, offset, todo, fade, samples);
1461 for(c = 0;c < NUM_LINES;c++)
1462 MixSamples(samples[c], NumChannels, SamplesOut,
1463 State->Late.CurrentGain[c], State->Late.PanGain[c],
1464 SamplesToDo-base, base, todo
1467 /* Step fading forward. */
1468 fadeCount += todo;
1469 if(LIKELY(fadeCount >= FADE_SAMPLES))
1471 /* Update the cross-fading delay line taps. */
1472 fadeCount = FADE_SAMPLES;
1473 for(c = 0;c < NUM_LINES;c++)
1475 State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
1476 State->EarlyDelayCoeff[c][0] = State->EarlyDelayCoeff[c][1];
1477 State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1];
1478 State->Early.Offset[c][0] = State->Early.Offset[c][1];
1479 State->Early.Coeff[c][0] = State->Early.Coeff[c][1];
1480 State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
1481 State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1];
1482 State->Late.Offset[c][0] = State->Late.Offset[c][1];
1483 State->Late.T60[c].MidGain[0] = State->Late.T60[c].MidGain[1];
1485 State->Late.DensityGain[0] = State->Late.DensityGain[1];
1486 State->MaxUpdate[0] = State->MaxUpdate[1];
1489 else
1491 /* Generate and mix early reflections. */
1492 EarlyReflection_Unfaded(State, offset, todo, samples);
1493 for(c = 0;c < NUM_LINES;c++)
1494 MixSamples(samples[c], NumChannels, SamplesOut,
1495 State->Early.CurrentGain[c], State->Early.PanGain[c],
1496 SamplesToDo-base, base, todo
1499 /* Generate and mix late reverb. */
1500 LateReverb_Unfaded(State, offset, todo, samples);
1501 for(c = 0;c < NUM_LINES;c++)
1502 MixSamples(samples[c], NumChannels, SamplesOut,
1503 State->Late.CurrentGain[c], State->Late.PanGain[c],
1504 SamplesToDo-base, base, todo
1508 /* Step all delays forward. */
1509 offset += todo;
1511 base += todo;
1513 State->Offset = offset;
1514 State->FadeCount = fadeCount;
1518 typedef struct ReverbStateFactory {
1519 DERIVE_FROM_TYPE(EffectStateFactory);
1520 } ReverbStateFactory;
1522 static ALeffectState *ReverbStateFactory_create(ReverbStateFactory* UNUSED(factory))
1524 ReverbState *state;
1526 NEW_OBJ0(state, ReverbState)();
1527 if(!state) return NULL;
1529 return STATIC_CAST(ALeffectState, state);
1532 DEFINE_EFFECTSTATEFACTORY_VTABLE(ReverbStateFactory);
1534 EffectStateFactory *ReverbStateFactory_getFactory(void)
1536 static ReverbStateFactory ReverbFactory = { { GET_VTABLE2(ReverbStateFactory, EffectStateFactory) } };
1538 return STATIC_CAST(EffectStateFactory, &ReverbFactory);
1542 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1544 ALeffectProps *props = &effect->Props;
1545 switch(param)
1547 case AL_EAXREVERB_DECAY_HFLIMIT:
1548 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1549 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
1550 props->Reverb.DecayHFLimit = val;
1551 break;
1553 default:
1554 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1555 param);
1558 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1559 { ALeaxreverb_setParami(effect, context, param, vals[0]); }
1560 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1562 ALeffectProps *props = &effect->Props;
1563 switch(param)
1565 case AL_EAXREVERB_DENSITY:
1566 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1567 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
1568 props->Reverb.Density = val;
1569 break;
1571 case AL_EAXREVERB_DIFFUSION:
1572 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1573 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
1574 props->Reverb.Diffusion = val;
1575 break;
1577 case AL_EAXREVERB_GAIN:
1578 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1579 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
1580 props->Reverb.Gain = val;
1581 break;
1583 case AL_EAXREVERB_GAINHF:
1584 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1585 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
1586 props->Reverb.GainHF = val;
1587 break;
1589 case AL_EAXREVERB_GAINLF:
1590 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1591 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
1592 props->Reverb.GainLF = val;
1593 break;
1595 case AL_EAXREVERB_DECAY_TIME:
1596 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1597 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
1598 props->Reverb.DecayTime = val;
1599 break;
1601 case AL_EAXREVERB_DECAY_HFRATIO:
1602 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1603 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
1604 props->Reverb.DecayHFRatio = val;
1605 break;
1607 case AL_EAXREVERB_DECAY_LFRATIO:
1608 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1609 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
1610 props->Reverb.DecayLFRatio = val;
1611 break;
1613 case AL_EAXREVERB_REFLECTIONS_GAIN:
1614 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1615 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
1616 props->Reverb.ReflectionsGain = val;
1617 break;
1619 case AL_EAXREVERB_REFLECTIONS_DELAY:
1620 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1621 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
1622 props->Reverb.ReflectionsDelay = val;
1623 break;
1625 case AL_EAXREVERB_LATE_REVERB_GAIN:
1626 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1627 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
1628 props->Reverb.LateReverbGain = val;
1629 break;
1631 case AL_EAXREVERB_LATE_REVERB_DELAY:
1632 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1633 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
1634 props->Reverb.LateReverbDelay = val;
1635 break;
1637 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1638 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1639 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
1640 props->Reverb.AirAbsorptionGainHF = val;
1641 break;
1643 case AL_EAXREVERB_ECHO_TIME:
1644 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1645 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
1646 props->Reverb.EchoTime = val;
1647 break;
1649 case AL_EAXREVERB_ECHO_DEPTH:
1650 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1651 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
1652 props->Reverb.EchoDepth = val;
1653 break;
1655 case AL_EAXREVERB_MODULATION_TIME:
1656 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1657 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
1658 props->Reverb.ModulationTime = val;
1659 break;
1661 case AL_EAXREVERB_MODULATION_DEPTH:
1662 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1663 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
1664 props->Reverb.ModulationDepth = val;
1665 break;
1667 case AL_EAXREVERB_HFREFERENCE:
1668 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1669 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
1670 props->Reverb.HFReference = val;
1671 break;
1673 case AL_EAXREVERB_LFREFERENCE:
1674 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1675 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
1676 props->Reverb.LFReference = val;
1677 break;
1679 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1680 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1681 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
1682 props->Reverb.RoomRolloffFactor = val;
1683 break;
1685 default:
1686 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1687 param);
1690 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1692 ALeffectProps *props = &effect->Props;
1693 switch(param)
1695 case AL_EAXREVERB_REFLECTIONS_PAN:
1696 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1697 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
1698 props->Reverb.ReflectionsPan[0] = vals[0];
1699 props->Reverb.ReflectionsPan[1] = vals[1];
1700 props->Reverb.ReflectionsPan[2] = vals[2];
1701 break;
1702 case AL_EAXREVERB_LATE_REVERB_PAN:
1703 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1704 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
1705 props->Reverb.LateReverbPan[0] = vals[0];
1706 props->Reverb.LateReverbPan[1] = vals[1];
1707 props->Reverb.LateReverbPan[2] = vals[2];
1708 break;
1710 default:
1711 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1712 break;
1716 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1718 const ALeffectProps *props = &effect->Props;
1719 switch(param)
1721 case AL_EAXREVERB_DECAY_HFLIMIT:
1722 *val = props->Reverb.DecayHFLimit;
1723 break;
1725 default:
1726 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1727 param);
1730 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1731 { ALeaxreverb_getParami(effect, context, param, vals); }
1732 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1734 const ALeffectProps *props = &effect->Props;
1735 switch(param)
1737 case AL_EAXREVERB_DENSITY:
1738 *val = props->Reverb.Density;
1739 break;
1741 case AL_EAXREVERB_DIFFUSION:
1742 *val = props->Reverb.Diffusion;
1743 break;
1745 case AL_EAXREVERB_GAIN:
1746 *val = props->Reverb.Gain;
1747 break;
1749 case AL_EAXREVERB_GAINHF:
1750 *val = props->Reverb.GainHF;
1751 break;
1753 case AL_EAXREVERB_GAINLF:
1754 *val = props->Reverb.GainLF;
1755 break;
1757 case AL_EAXREVERB_DECAY_TIME:
1758 *val = props->Reverb.DecayTime;
1759 break;
1761 case AL_EAXREVERB_DECAY_HFRATIO:
1762 *val = props->Reverb.DecayHFRatio;
1763 break;
1765 case AL_EAXREVERB_DECAY_LFRATIO:
1766 *val = props->Reverb.DecayLFRatio;
1767 break;
1769 case AL_EAXREVERB_REFLECTIONS_GAIN:
1770 *val = props->Reverb.ReflectionsGain;
1771 break;
1773 case AL_EAXREVERB_REFLECTIONS_DELAY:
1774 *val = props->Reverb.ReflectionsDelay;
1775 break;
1777 case AL_EAXREVERB_LATE_REVERB_GAIN:
1778 *val = props->Reverb.LateReverbGain;
1779 break;
1781 case AL_EAXREVERB_LATE_REVERB_DELAY:
1782 *val = props->Reverb.LateReverbDelay;
1783 break;
1785 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1786 *val = props->Reverb.AirAbsorptionGainHF;
1787 break;
1789 case AL_EAXREVERB_ECHO_TIME:
1790 *val = props->Reverb.EchoTime;
1791 break;
1793 case AL_EAXREVERB_ECHO_DEPTH:
1794 *val = props->Reverb.EchoDepth;
1795 break;
1797 case AL_EAXREVERB_MODULATION_TIME:
1798 *val = props->Reverb.ModulationTime;
1799 break;
1801 case AL_EAXREVERB_MODULATION_DEPTH:
1802 *val = props->Reverb.ModulationDepth;
1803 break;
1805 case AL_EAXREVERB_HFREFERENCE:
1806 *val = props->Reverb.HFReference;
1807 break;
1809 case AL_EAXREVERB_LFREFERENCE:
1810 *val = props->Reverb.LFReference;
1811 break;
1813 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1814 *val = props->Reverb.RoomRolloffFactor;
1815 break;
1817 default:
1818 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1819 param);
1822 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
1824 const ALeffectProps *props = &effect->Props;
1825 switch(param)
1827 case AL_EAXREVERB_REFLECTIONS_PAN:
1828 vals[0] = props->Reverb.ReflectionsPan[0];
1829 vals[1] = props->Reverb.ReflectionsPan[1];
1830 vals[2] = props->Reverb.ReflectionsPan[2];
1831 break;
1832 case AL_EAXREVERB_LATE_REVERB_PAN:
1833 vals[0] = props->Reverb.LateReverbPan[0];
1834 vals[1] = props->Reverb.LateReverbPan[1];
1835 vals[2] = props->Reverb.LateReverbPan[2];
1836 break;
1838 default:
1839 ALeaxreverb_getParamf(effect, context, param, vals);
1840 break;
1844 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
1846 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1848 ALeffectProps *props = &effect->Props;
1849 switch(param)
1851 case AL_REVERB_DECAY_HFLIMIT:
1852 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
1853 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
1854 props->Reverb.DecayHFLimit = val;
1855 break;
1857 default:
1858 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
1861 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1862 { ALreverb_setParami(effect, context, param, vals[0]); }
1863 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1865 ALeffectProps *props = &effect->Props;
1866 switch(param)
1868 case AL_REVERB_DENSITY:
1869 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
1870 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
1871 props->Reverb.Density = val;
1872 break;
1874 case AL_REVERB_DIFFUSION:
1875 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
1876 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
1877 props->Reverb.Diffusion = val;
1878 break;
1880 case AL_REVERB_GAIN:
1881 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
1882 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
1883 props->Reverb.Gain = val;
1884 break;
1886 case AL_REVERB_GAINHF:
1887 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
1888 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
1889 props->Reverb.GainHF = val;
1890 break;
1892 case AL_REVERB_DECAY_TIME:
1893 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
1894 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
1895 props->Reverb.DecayTime = val;
1896 break;
1898 case AL_REVERB_DECAY_HFRATIO:
1899 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
1900 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
1901 props->Reverb.DecayHFRatio = val;
1902 break;
1904 case AL_REVERB_REFLECTIONS_GAIN:
1905 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
1906 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
1907 props->Reverb.ReflectionsGain = val;
1908 break;
1910 case AL_REVERB_REFLECTIONS_DELAY:
1911 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
1912 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
1913 props->Reverb.ReflectionsDelay = val;
1914 break;
1916 case AL_REVERB_LATE_REVERB_GAIN:
1917 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
1918 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
1919 props->Reverb.LateReverbGain = val;
1920 break;
1922 case AL_REVERB_LATE_REVERB_DELAY:
1923 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
1924 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
1925 props->Reverb.LateReverbDelay = val;
1926 break;
1928 case AL_REVERB_AIR_ABSORPTION_GAINHF:
1929 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
1930 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
1931 props->Reverb.AirAbsorptionGainHF = val;
1932 break;
1934 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
1935 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
1936 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
1937 props->Reverb.RoomRolloffFactor = val;
1938 break;
1940 default:
1941 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
1944 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1945 { ALreverb_setParamf(effect, context, param, vals[0]); }
1947 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1949 const ALeffectProps *props = &effect->Props;
1950 switch(param)
1952 case AL_REVERB_DECAY_HFLIMIT:
1953 *val = props->Reverb.DecayHFLimit;
1954 break;
1956 default:
1957 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
1960 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1961 { ALreverb_getParami(effect, context, param, vals); }
1962 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1964 const ALeffectProps *props = &effect->Props;
1965 switch(param)
1967 case AL_REVERB_DENSITY:
1968 *val = props->Reverb.Density;
1969 break;
1971 case AL_REVERB_DIFFUSION:
1972 *val = props->Reverb.Diffusion;
1973 break;
1975 case AL_REVERB_GAIN:
1976 *val = props->Reverb.Gain;
1977 break;
1979 case AL_REVERB_GAINHF:
1980 *val = props->Reverb.GainHF;
1981 break;
1983 case AL_REVERB_DECAY_TIME:
1984 *val = props->Reverb.DecayTime;
1985 break;
1987 case AL_REVERB_DECAY_HFRATIO:
1988 *val = props->Reverb.DecayHFRatio;
1989 break;
1991 case AL_REVERB_REFLECTIONS_GAIN:
1992 *val = props->Reverb.ReflectionsGain;
1993 break;
1995 case AL_REVERB_REFLECTIONS_DELAY:
1996 *val = props->Reverb.ReflectionsDelay;
1997 break;
1999 case AL_REVERB_LATE_REVERB_GAIN:
2000 *val = props->Reverb.LateReverbGain;
2001 break;
2003 case AL_REVERB_LATE_REVERB_DELAY:
2004 *val = props->Reverb.LateReverbDelay;
2005 break;
2007 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2008 *val = props->Reverb.AirAbsorptionGainHF;
2009 break;
2011 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2012 *val = props->Reverb.RoomRolloffFactor;
2013 break;
2015 default:
2016 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2019 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2020 { ALreverb_getParamf(effect, context, param, vals); }
2022 DEFINE_ALEFFECT_VTABLE(ALreverb);