Move a loop into a function
[openal-soft.git] / Alc / effects / reverb.c
blob6861e967f22185a36ab1d1d7ce024dbba27125f7
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 "alEffect.h"
31 #include "alFilter.h"
32 #include "alListener.h"
33 #include "alError.h"
34 #include "mixer_defs.h"
36 /* This is a user config option for modifying the overall output of the reverb
37 * effect.
39 ALfloat ReverbBoost = 1.0f;
41 /* This is the maximum number of samples processed for each inner loop
42 * iteration. */
43 #define MAX_UPDATE_SAMPLES 256
45 /* The number of samples used for cross-faded delay lines. This can be used
46 * to balance the compensation for abrupt line changes and attenuation due to
47 * minimally lengthed recursive lines. Try to keep this below the device
48 * update size.
50 #define FADE_SAMPLES 128
52 /* The number of spatialized lines or channels to process. Four channels allows
53 * for a 3D A-Format response. NOTE: This can't be changed without taking care
54 * of the conversion matrices, and a few places where the length arrays are
55 * assumed to have 4 elements.
57 #define NUM_LINES 4
60 /* The B-Format to A-Format conversion matrix. The arrangement of rows is
61 * deliberately chosen to align the resulting lines to their spatial opposites
62 * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
63 * back left). It's not quite opposite, since the A-Format results in a
64 * tetrahedron, but it's close enough. Should the model be extended to 8-lines
65 * in the future, true opposites can be used.
67 static const aluMatrixf B2A = {{
68 { 0.288675134595f, 0.288675134595f, 0.288675134595f, 0.288675134595f },
69 { 0.288675134595f, -0.288675134595f, -0.288675134595f, 0.288675134595f },
70 { 0.288675134595f, 0.288675134595f, -0.288675134595f, -0.288675134595f },
71 { 0.288675134595f, -0.288675134595f, 0.288675134595f, -0.288675134595f }
72 }};
74 /* Converts A-Format to B-Format. */
75 static const aluMatrixf A2B = {{
76 { 0.866025403785f, 0.866025403785f, 0.866025403785f, 0.866025403785f },
77 { 0.866025403785f, -0.866025403785f, 0.866025403785f, -0.866025403785f },
78 { 0.866025403785f, -0.866025403785f, -0.866025403785f, 0.866025403785f },
79 { 0.866025403785f, 0.866025403785f, -0.866025403785f, -0.866025403785f }
80 }};
82 static const ALfloat FadeStep = 1.0f / FADE_SAMPLES;
84 /* The all-pass and delay lines have a variable length dependent on the
85 * effect's density parameter, which helps alter the perceived environment
86 * size. The size-to-density conversion is a cubed scale:
88 * density = min(1.0, pow(size, 3.0) / DENSITY_SCALE);
90 * The line lengths scale linearly with room size, so the inverse density
91 * conversion is needed, taking the cube root of the re-scaled density to
92 * calculate the line length multiplier:
94 * length_mult = max(5.0, cbrtf(density*DENSITY_SCALE));
96 * The density scale below will result in a max line multiplier of 50, for an
97 * effective size range of 5m to 50m.
99 static const ALfloat DENSITY_SCALE = 125000.0f;
101 /* All delay line lengths are specified in seconds.
103 * To approximate early reflections, we break them up into primary (those
104 * arriving from the same direction as the source) and secondary (those
105 * arriving from the opposite direction).
107 * The early taps decorrelate the 4-channel signal to approximate an average
108 * room response for the primary reflections after the initial early delay.
110 * Given an average room dimension (d_a) and the speed of sound (c) we can
111 * calculate the average reflection delay (r_a) regardless of listener and
112 * source positions as:
114 * r_a = d_a / c
115 * c = 343.3
117 * This can extended to finding the average difference (r_d) between the
118 * maximum (r_1) and minimum (r_0) reflection delays:
120 * r_0 = 2 / 3 r_a
121 * = r_a - r_d / 2
122 * = r_d
123 * r_1 = 4 / 3 r_a
124 * = r_a + r_d / 2
125 * = 2 r_d
126 * r_d = 2 / 3 r_a
127 * = r_1 - r_0
129 * As can be determined by integrating the 1D model with a source (s) and
130 * listener (l) positioned across the dimension of length (d_a):
132 * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
134 * The initial taps (T_(i=0)^N) are then specified by taking a power series
135 * that ranges between r_0 and half of r_1 less r_0:
137 * R_i = 2^(i / (2 N - 1)) r_d
138 * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
139 * = r_0 + T_i
140 * T_i = R_i - r_0
141 * = (2^(i / (2 N - 1)) - 1) r_d
143 * Assuming an average of 1m, we get the following taps:
145 static const ALfloat EARLY_TAP_LENGTHS[NUM_LINES] =
147 0.0000000e+0f, 2.0213520e-4f, 4.2531060e-4f, 6.7171600e-4f
150 /* The early all-pass filter lengths are based on the early tap lengths:
152 * A_i = R_i / a
154 * Where a is the approximate maximum all-pass cycle limit (20).
156 static const ALfloat EARLY_ALLPASS_LENGTHS[NUM_LINES] =
158 9.7096800e-5f, 1.0720356e-4f, 1.1836234e-4f, 1.3068260e-4f
161 /* The early delay lines are used to transform the primary reflections into
162 * the secondary reflections. The A-format is arranged in such a way that
163 * the channels/lines are spatially opposite:
165 * C_i is opposite C_(N-i-1)
167 * The delays of the two opposing reflections (R_i and O_i) from a source
168 * anywhere along a particular dimension always sum to twice its full delay:
170 * 2 r_a = R_i + O_i
172 * With that in mind we can determine the delay between the two reflections
173 * and thus specify our early line lengths (L_(i=0)^N) using:
175 * O_i = 2 r_a - R_(N-i-1)
176 * L_i = O_i - R_(N-i-1)
177 * = 2 (r_a - R_(N-i-1))
178 * = 2 (r_a - T_(N-i-1) - r_0)
179 * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
181 * Using an average dimension of 1m, we get:
183 static const ALfloat EARLY_LINE_LENGTHS[NUM_LINES] =
185 5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f
188 /* The late all-pass filter lengths are based on the late line lengths:
190 * A_i = (5 / 3) L_i / r_1
192 static const ALfloat LATE_ALLPASS_LENGTHS[NUM_LINES] =
194 1.6182800e-4f, 2.0389060e-4f, 2.8159360e-4f, 3.2365600e-4f
197 /* The late lines are used to approximate the decaying cycle of recursive
198 * late reflections.
200 * Splitting the lines in half, we start with the shortest reflection paths
201 * (L_(i=0)^(N/2)):
203 * L_i = 2^(i / (N - 1)) r_d
205 * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
207 * L_i = 2 r_a - L_(i-N/2)
208 * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
210 * For our 1m average room, we get:
212 static const ALfloat LATE_LINE_LENGTHS[NUM_LINES] =
214 1.9419362e-3f, 2.4466860e-3f, 3.3791220e-3f, 3.8838720e-3f
218 typedef struct DelayLineI {
219 /* The delay lines use interleaved samples, with the lengths being powers
220 * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
222 ALsizei Mask;
223 ALfloat (*Line)[NUM_LINES];
224 } DelayLineI;
226 typedef struct VecAllpass {
227 DelayLineI Delay;
228 ALsizei Offset[NUM_LINES][2];
229 } VecAllpass;
231 typedef struct T60Filter {
232 /* Two filters are used to adjust the signal. One to control the low
233 * frequencies, and one to control the high frequencies. The HF filter also
234 * adjusts the overall output gain, affecting the remaining mid-band.
236 ALfloat HFCoeffs[3];
237 ALfloat LFCoeffs[3];
239 /* The HF and LF filters each keep a state of the last input and last
240 * output sample.
242 ALfloat HFState[2];
243 ALfloat LFState[2];
244 } T60Filter;
246 typedef struct EarlyReflections {
247 /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
248 * The spread from this filter also helps smooth out the reverb tail.
250 VecAllpass VecAp;
252 /* An echo line is used to complete the second half of the early
253 * reflections.
255 DelayLineI Delay;
256 ALsizei Offset[NUM_LINES][2];
257 ALfloat Coeff[NUM_LINES];
259 /* The gain for each output channel based on 3D panning. */
260 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
261 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
262 } EarlyReflections;
264 typedef struct LateReverb {
265 /* Attenuation to compensate for the modal density and decay rate of the
266 * late lines.
268 ALfloat DensityGain;
270 /* A recursive delay line is used fill in the reverb tail. */
271 DelayLineI Delay;
272 ALsizei Offset[NUM_LINES][2];
274 /* T60 decay filters are used to simulate absorption. */
275 T60Filter T60[NUM_LINES];
277 /* A Gerzon vector all-pass filter is used to simulate diffusion. */
278 VecAllpass VecAp;
280 /* The gain for each output channel based on 3D panning. */
281 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
282 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
283 } LateReverb;
285 typedef struct ALreverbState {
286 DERIVE_FROM_TYPE(ALeffectState);
288 /* All delay lines are allocated as a single buffer to reduce memory
289 * fragmentation and management code.
291 ALfloat *SampleBuffer;
292 ALuint TotalSamples;
294 /* Master effect filters */
295 struct {
296 ALfilterState Lp;
297 ALfilterState Hp;
298 } Filter[NUM_LINES];
300 /* Core delay line (early reflections and late reverb tap from this). */
301 DelayLineI Delay;
303 /* Tap points for early reflection delay. */
304 ALsizei EarlyDelayTap[NUM_LINES][2];
305 ALfloat EarlyDelayCoeff[NUM_LINES];
307 /* Tap points for late reverb feed and delay. */
308 ALsizei LateFeedTap;
309 ALsizei LateDelayTap[NUM_LINES][2];
311 /* The feed-back and feed-forward all-pass coefficient. */
312 ALfloat ApFeedCoeff;
314 /* Coefficients for the all-pass and line scattering matrices. */
315 ALfloat MixX;
316 ALfloat MixY;
318 EarlyReflections Early;
320 LateReverb Late;
322 /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
323 ALsizei FadeCount;
325 /* The current write offset for all delay lines. */
326 ALsizei Offset;
328 /* Temporary storage used when processing. */
329 alignas(16) ALfloat AFormatSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
330 alignas(16) ALfloat ReverbSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
331 alignas(16) ALfloat EarlySamples[NUM_LINES][MAX_UPDATE_SAMPLES];
332 } ALreverbState;
334 static ALvoid ALreverbState_Destruct(ALreverbState *State);
335 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device);
336 static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
337 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
338 DECLARE_DEFAULT_ALLOCATORS(ALreverbState)
340 DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);
342 static void ALreverbState_Construct(ALreverbState *state)
344 ALsizei i, j;
346 ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
347 SET_VTABLE2(ALreverbState, ALeffectState, state);
349 state->TotalSamples = 0;
350 state->SampleBuffer = NULL;
352 for(i = 0;i < NUM_LINES;i++)
354 ALfilterState_clear(&state->Filter[i].Lp);
355 ALfilterState_clear(&state->Filter[i].Hp);
358 state->Delay.Mask = 0;
359 state->Delay.Line = NULL;
361 for(i = 0;i < NUM_LINES;i++)
363 state->EarlyDelayTap[i][0] = 0;
364 state->EarlyDelayTap[i][1] = 0;
365 state->EarlyDelayCoeff[i] = 0.0f;
368 state->LateFeedTap = 0;
370 for(i = 0;i < NUM_LINES;i++)
372 state->LateDelayTap[i][0] = 0;
373 state->LateDelayTap[i][1] = 0;
376 state->ApFeedCoeff = 0.0f;
377 state->MixX = 0.0f;
378 state->MixY = 0.0f;
380 state->Early.VecAp.Delay.Mask = 0;
381 state->Early.VecAp.Delay.Line = NULL;
382 state->Early.Delay.Mask = 0;
383 state->Early.Delay.Line = NULL;
384 for(i = 0;i < NUM_LINES;i++)
386 state->Early.VecAp.Offset[i][0] = 0;
387 state->Early.VecAp.Offset[i][1] = 0;
388 state->Early.Offset[i][0] = 0;
389 state->Early.Offset[i][1] = 0;
390 state->Early.Coeff[i] = 0.0f;
393 state->Late.DensityGain = 0.0f;
395 state->Late.Delay.Mask = 0;
396 state->Late.Delay.Line = NULL;
397 state->Late.VecAp.Delay.Mask = 0;
398 state->Late.VecAp.Delay.Line = NULL;
399 for(i = 0;i < NUM_LINES;i++)
401 state->Late.Offset[i][0] = 0;
402 state->Late.Offset[i][1] = 0;
404 state->Late.VecAp.Offset[i][0] = 0;
405 state->Late.VecAp.Offset[i][1] = 0;
407 for(j = 0;j < 3;j++)
409 state->Late.T60[i].HFCoeffs[j] = 0.0f;
410 state->Late.T60[i].LFCoeffs[j] = 0.0f;
412 state->Late.T60[i].HFState[0] = 0.0f;
413 state->Late.T60[i].HFState[1] = 0.0f;
414 state->Late.T60[i].LFState[0] = 0.0f;
415 state->Late.T60[i].LFState[1] = 0.0f;
418 for(i = 0;i < NUM_LINES;i++)
420 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
422 state->Early.CurrentGain[i][j] = 0.0f;
423 state->Early.PanGain[i][j] = 0.0f;
424 state->Late.CurrentGain[i][j] = 0.0f;
425 state->Late.PanGain[i][j] = 0.0f;
429 state->FadeCount = 0;
430 state->Offset = 0;
433 static ALvoid ALreverbState_Destruct(ALreverbState *State)
435 al_free(State->SampleBuffer);
436 State->SampleBuffer = NULL;
438 ALeffectState_Destruct(STATIC_CAST(ALeffectState,State));
441 /**************************************
442 * Device Update *
443 **************************************/
445 static inline ALfloat CalcDelayLengthMult(ALfloat density)
447 return maxf(5.0f, cbrtf(density*DENSITY_SCALE));
450 /* Given the allocated sample buffer, this function updates each delay line
451 * offset.
453 static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay)
455 union {
456 ALfloat *f;
457 ALfloat (*f4)[NUM_LINES];
458 } u;
459 u.f = &sampleBuffer[(ptrdiff_t)Delay->Line * NUM_LINES];
460 Delay->Line = u.f4;
463 /* Calculate the length of a delay line and store its mask and offset. */
464 static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALuint frequency,
465 const ALuint extra, DelayLineI *Delay)
467 ALuint samples;
469 /* All line lengths are powers of 2, calculated from their lengths in
470 * seconds, rounded up.
472 samples = fastf2i(ceilf(length*frequency));
473 samples = NextPowerOf2(samples + extra);
475 /* All lines share a single sample buffer. */
476 Delay->Mask = samples - 1;
477 Delay->Line = (ALfloat(*)[NUM_LINES])offset;
479 /* Return the sample count for accumulation. */
480 return samples;
483 /* Calculates the delay line metrics and allocates the shared sample buffer
484 * for all lines given the sample rate (frequency). If an allocation failure
485 * occurs, it returns AL_FALSE.
487 static ALboolean AllocLines(const ALuint frequency, ALreverbState *State)
489 ALuint totalSamples, i;
490 ALfloat multiplier, length;
492 /* All delay line lengths are calculated to accomodate the full range of
493 * lengths given their respective paramters.
495 totalSamples = 0;
497 /* Multiplier for the maximum density value, i.e. density=1, which is
498 * actually the least density...
500 multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
502 /* The main delay length includes the maximum early reflection delay, the
503 * largest early tap width, the maximum late reverb delay, and the
504 * largest late tap width. Finally, it must also be extended by the
505 * update size (MAX_UPDATE_SAMPLES) for block processing.
507 length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier +
508 AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
509 (LATE_LINE_LENGTHS[NUM_LINES-1] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
510 totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES,
511 &State->Delay);
513 /* The early vector all-pass line. */
514 length = EARLY_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
515 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
516 &State->Early.VecAp.Delay);
518 /* The early reflection line. */
519 length = EARLY_LINE_LENGTHS[NUM_LINES-1] * multiplier;
520 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
521 &State->Early.Delay);
523 /* The late vector all-pass line. */
524 length = LATE_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
525 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
526 &State->Late.VecAp.Delay);
528 /* The late delay lines are calculated from the larger of the maximum
529 * density line length or the maximum echo time.
531 length = maxf(AL_EAXREVERB_MAX_ECHO_TIME, LATE_LINE_LENGTHS[NUM_LINES-1]*multiplier);
532 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
533 &State->Late.Delay);
535 if(totalSamples != State->TotalSamples)
537 ALfloat *newBuffer;
539 TRACE("New reverb buffer length: %ux4 samples\n", totalSamples);
540 newBuffer = al_calloc(16, sizeof(ALfloat[NUM_LINES]) * totalSamples);
541 if(!newBuffer) return AL_FALSE;
543 al_free(State->SampleBuffer);
544 State->SampleBuffer = newBuffer;
545 State->TotalSamples = totalSamples;
548 /* Update all delays to reflect the new sample buffer. */
549 RealizeLineOffset(State->SampleBuffer, &State->Delay);
550 RealizeLineOffset(State->SampleBuffer, &State->Early.VecAp.Delay);
551 RealizeLineOffset(State->SampleBuffer, &State->Early.Delay);
552 RealizeLineOffset(State->SampleBuffer, &State->Late.VecAp.Delay);
553 RealizeLineOffset(State->SampleBuffer, &State->Late.Delay);
555 /* Clear the sample buffer. */
556 for(i = 0;i < State->TotalSamples;i++)
557 State->SampleBuffer[i] = 0.0f;
559 return AL_TRUE;
562 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
564 ALuint frequency = Device->Frequency;
565 ALfloat multiplier;
567 /* Allocate the delay lines. */
568 if(!AllocLines(frequency, State))
569 return AL_FALSE;
571 multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
573 /* The late feed taps are set a fixed position past the latest delay tap. */
574 State->LateFeedTap = fastf2i((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
575 EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier) *
576 frequency);
578 return AL_TRUE;
581 /**************************************
582 * Effect Update *
583 **************************************/
585 /* Calculate a decay coefficient given the length of each cycle and the time
586 * until the decay reaches -60 dB.
588 static inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
590 return powf(REVERB_DECAY_GAIN, length/decayTime);
593 /* Calculate a decay length from a coefficient and the time until the decay
594 * reaches -60 dB.
596 static inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
598 return log10f(coeff) * decayTime / log10f(REVERB_DECAY_GAIN);
601 /* Calculate an attenuation to be applied to the input of any echo models to
602 * compensate for modal density and decay time.
604 static inline ALfloat CalcDensityGain(const ALfloat a)
606 /* The energy of a signal can be obtained by finding the area under the
607 * squared signal. This takes the form of Sum(x_n^2), where x is the
608 * amplitude for the sample n.
610 * Decaying feedback matches exponential decay of the form Sum(a^n),
611 * where a is the attenuation coefficient, and n is the sample. The area
612 * under this decay curve can be calculated as: 1 / (1 - a).
614 * Modifying the above equation to find the area under the squared curve
615 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
616 * calculated by inverting the square root of this approximation,
617 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
619 return sqrtf(1.0f - a*a);
622 /* Calculate the scattering matrix coefficients given a diffusion factor. */
623 static inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
625 ALfloat n, t;
627 /* The matrix is of order 4, so n is sqrt(4 - 1). */
628 n = sqrtf(3.0f);
629 t = diffusion * atanf(n);
631 /* Calculate the first mixing matrix coefficient. */
632 *x = cosf(t);
633 /* Calculate the second mixing matrix coefficient. */
634 *y = sinf(t) / n;
637 /* Calculate the limited HF ratio for use with the late reverb low-pass
638 * filters.
640 static ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
641 const ALfloat decayTime, const ALfloat SpeedOfSound)
643 ALfloat limitRatio;
645 /* Find the attenuation due to air absorption in dB (converting delay
646 * time to meters using the speed of sound). Then reversing the decay
647 * equation, solve for HF ratio. The delay length is cancelled out of
648 * the equation, so it can be calculated once for all lines.
650 limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound);
652 /* Using the limit calculated above, apply the upper bound to the HF ratio.
654 return minf(limitRatio, hfRatio);
657 /* Calculates the first-order high-pass coefficients following the I3DL2
658 * reference model. This is the transfer function:
660 * 1 - z^-1
661 * H(z) = p ------------
662 * 1 - p z^-1
664 * And this is the I3DL2 coefficient calculation given gain (g) and reference
665 * angular frequency (w):
668 * p = ------------------------------------------------------
669 * g cos(w) + sqrt((cos(w) - 1) (g^2 cos(w) + g^2 - 2))
671 * The coefficient is applied to the partial differential filter equation as:
673 * c_0 = p
674 * c_1 = -p
675 * c_2 = p
676 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
679 static inline void CalcHighpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
681 ALfloat g, g2, cw, p;
683 if(gain >= 1.0f)
685 coeffs[0] = 1.0f;
686 coeffs[1] = 0.0f;
687 coeffs[2] = 0.0f;
688 return;
691 g = maxf(0.001f, gain);
692 g2 = g * g;
693 cw = cosf(w);
694 p = g / (g*cw + sqrtf((cw - 1.0f) * (g2*cw + g2 - 2.0f)));
696 coeffs[0] = p;
697 coeffs[1] = -p;
698 coeffs[2] = p;
701 /* Calculates the first-order low-pass coefficients following the I3DL2
702 * reference model. This is the transfer function:
704 * (1 - a) z^0
705 * H(z) = ----------------
706 * 1 z^0 - a z^-1
708 * And this is the I3DL2 coefficient calculation given gain (g) and reference
709 * angular frequency (w):
711 * 1 - g^2 cos(w) - sqrt(2 g^2 (1 - cos(w)) - g^4 (1 - cos(w)^2))
712 * a = ----------------------------------------------------------------
713 * 1 - g^2
715 * The coefficient is applied to the partial differential filter equation as:
717 * c_0 = 1 - a
718 * c_1 = 0
719 * c_2 = a
720 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
723 static inline void CalcLowpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
725 ALfloat g, g2, cw, a;
727 if(gain >= 1.0f)
729 coeffs[0] = 1.0f;
730 coeffs[1] = 0.0f;
731 coeffs[2] = 0.0f;
732 return;
735 /* Be careful with gains < 0.001, as that causes the coefficient
736 * to head towards 1, which will flatten the signal. */
737 g = maxf(0.001f, gain);
738 g2 = g * g;
739 cw = cosf(w);
740 a = (1.0f - g2*cw - sqrtf((2.0f*g2*(1.0f - cw)) - g2*g2*(1.0f - cw*cw))) /
741 (1.0f - g2);
743 coeffs[0] = 1.0f - a;
744 coeffs[1] = 0.0f;
745 coeffs[2] = a;
748 /* Calculates the first-order low-shelf coefficients. The shelf filters are
749 * used in place of low/high-pass filters to preserve the mid-band. This is
750 * the transfer function:
752 * a_0 + a_1 z^-1
753 * H(z) = ----------------
754 * 1 + b_1 z^-1
756 * And these are the coefficient calculations given cut gain (g) and a center
757 * angular frequency (w):
759 * sin(0.5 (pi - w) - 0.25 pi)
760 * p = -----------------------------
761 * sin(0.5 (pi - w) + 0.25 pi)
763 * g + 1 g + 1
764 * a = ------- + sqrt((-------)^2 - 1)
765 * g - 1 g - 1
767 * 1 + g + (1 - g) a
768 * b_0 = -------------------
771 * 1 - g + (1 + g) a
772 * b_1 = -------------------
775 * The coefficients are applied to the partial differential filter equation
776 * as:
778 * b_0 + p b_1
779 * c_0 = -------------
780 * 1 + p a
782 * -(b_1 + p b_0)
783 * c_1 = ----------------
784 * 1 + p a
786 * p + a
787 * c_2 = ---------
788 * 1 + p a
790 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
793 static inline void CalcLowShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
795 ALfloat g, rw, p, n;
796 ALfloat alpha, beta0, beta1;
798 if(gain >= 1.0f)
800 coeffs[0] = 1.0f;
801 coeffs[1] = 0.0f;
802 coeffs[2] = 0.0f;
803 return;
806 g = maxf(0.001f, gain);
807 rw = F_PI - w;
808 p = sinf(0.5f*rw - 0.25f*F_PI) / sinf(0.5f*rw + 0.25f*F_PI);
809 n = (g + 1.0f) / (g - 1.0f);
810 alpha = n + sqrtf(n*n - 1.0f);
811 beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f;
812 beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f;
814 coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha);
815 coeffs[1] = -(beta1 + p*beta0) / (1.0f + p*alpha);
816 coeffs[2] = (p + alpha) / (1.0f + p*alpha);
819 /* Calculates the first-order high-shelf coefficients. The shelf filters are
820 * used in place of low/high-pass filters to preserve the mid-band. This is
821 * the transfer function:
823 * a_0 + a_1 z^-1
824 * H(z) = ----------------
825 * 1 + b_1 z^-1
827 * And these are the coefficient calculations given cut gain (g) and a center
828 * angular frequency (w):
830 * sin(0.5 w - 0.25 pi)
831 * p = ----------------------
832 * sin(0.5 w + 0.25 pi)
834 * g + 1 g + 1
835 * a = ------- + sqrt((-------)^2 - 1)
836 * g - 1 g - 1
838 * 1 + g + (1 - g) a
839 * b_0 = -------------------
842 * 1 - g + (1 + g) a
843 * b_1 = -------------------
846 * The coefficients are applied to the partial differential filter equation
847 * as:
849 * b_0 + p b_1
850 * c_0 = -------------
851 * 1 + p a
853 * b_1 + p b_0
854 * c_1 = -------------
855 * 1 + p a
857 * -(p + a)
858 * c_2 = ----------
859 * 1 + p a
861 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
864 static inline void CalcHighShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
866 ALfloat g, p, n;
867 ALfloat alpha, beta0, beta1;
869 if(gain >= 1.0f)
871 coeffs[0] = 1.0f;
872 coeffs[1] = 0.0f;
873 coeffs[2] = 0.0f;
874 return;
877 g = maxf(0.001f, gain);
878 p = sinf(0.5f*w - 0.25f*F_PI) / sinf(0.5f*w + 0.25f*F_PI);
879 n = (g + 1.0f) / (g - 1.0f);
880 alpha = n + sqrtf(n*n - 1.0f);
881 beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f;
882 beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f;
884 coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha);
885 coeffs[1] = (beta1 + p*beta0) / (1.0f + p*alpha);
886 coeffs[2] = -(p + alpha) / (1.0f + p*alpha);
889 /* Calculates the 3-band T60 damping coefficients for a particular delay line
890 * of specified length using a combination of two low/high-pass/shelf or
891 * pass-through filter sections (producing 3 coefficients each) given decay
892 * times for each band split at two (LF/HF) reference frequencies (w).
894 static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime,
895 const ALfloat mfDecayTime, const ALfloat hfDecayTime,
896 const ALfloat lfW, const ALfloat hfW, ALfloat lfcoeffs[3],
897 ALfloat hfcoeffs[3])
899 ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime);
900 ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime);
901 ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime);
903 if(lfGain <= mfGain)
905 CalcHighpassCoeffs(lfGain / mfGain, lfW, lfcoeffs);
906 if(mfGain >= hfGain)
908 CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs);
909 hfcoeffs[0] *= mfGain; hfcoeffs[1] *= mfGain;
911 else
913 CalcLowShelfCoeffs(mfGain / hfGain, hfW, hfcoeffs);
914 hfcoeffs[0] *= hfGain; hfcoeffs[1] *= hfGain;
917 else
919 CalcHighShelfCoeffs(mfGain / lfGain, lfW, lfcoeffs);
920 if(mfGain >= hfGain)
922 CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs);
923 hfcoeffs[0] *= lfGain; hfcoeffs[1] *= lfGain;
925 else
927 ALfloat hg = mfGain / lfGain;
928 ALfloat lg = mfGain / hfGain;
929 ALfloat mg = maxf(lfGain, hfGain) / maxf(hg, lg);
931 CalcLowShelfCoeffs(lg, hfW, hfcoeffs);
932 hfcoeffs[0] *= mg; hfcoeffs[1] *= mg;
937 /* Update the offsets for the main effect delay line. */
938 static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ALreverbState *State)
940 ALfloat multiplier, length;
941 ALuint i;
943 multiplier = CalcDelayLengthMult(density);
945 /* Early reflection taps are decorrelated by means of an average room
946 * reflection approximation described above the definition of the taps.
947 * This approximation is linear and so the above density multiplier can
948 * be applied to adjust the width of the taps. A single-band decay
949 * coefficient is applied to simulate initial attenuation and absorption.
951 * Late reverb taps are based on the late line lengths to allow a zero-
952 * delay path and offsets that would continue the propagation naturally
953 * into the late lines.
955 for(i = 0;i < NUM_LINES;i++)
957 length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
958 State->EarlyDelayTap[i][1] = fastf2i(length * frequency);
960 length = EARLY_TAP_LENGTHS[i]*multiplier;
961 State->EarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime);
963 length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
964 State->LateDelayTap[i][1] = State->LateFeedTap + fastf2i(length * frequency);
968 /* Update the early reflection line lengths and gain coefficients. */
969 static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat decayTime, const ALuint frequency, EarlyReflections *Early)
971 ALfloat multiplier, length;
972 ALsizei i;
974 multiplier = CalcDelayLengthMult(density);
976 for(i = 0;i < NUM_LINES;i++)
978 /* Calculate the length (in seconds) of each all-pass line. */
979 length = EARLY_ALLPASS_LENGTHS[i] * multiplier;
981 /* Calculate the delay offset for each all-pass line. */
982 Early->VecAp.Offset[i][1] = fastf2i(length * frequency);
984 /* Calculate the length (in seconds) of each delay line. */
985 length = EARLY_LINE_LENGTHS[i] * multiplier;
987 /* Calculate the delay offset for each delay line. */
988 Early->Offset[i][1] = fastf2i(length * frequency);
990 /* Calculate the gain (coefficient) for each line. */
991 Early->Coeff[i] = CalcDecayCoeff(length, decayTime);
995 /* Update the late reverb line lengths and T60 coefficients. */
996 static ALvoid UpdateLateLines(const ALfloat density, const ALfloat diffusion, const ALfloat lfDecayTime, const ALfloat mfDecayTime, const ALfloat hfDecayTime, const ALfloat lfW, const ALfloat hfW, const ALfloat echoTime, const ALfloat echoDepth, const ALuint frequency, LateReverb *Late)
998 ALfloat multiplier, length, bandWeights[3];
999 ALsizei i;
1001 /* To compensate for changes in modal density and decay time of the late
1002 * reverb signal, the input is attenuated based on the maximal energy of
1003 * the outgoing signal. This approximation is used to keep the apparent
1004 * energy of the signal equal for all ranges of density and decay time.
1006 * The average length of the delay lines is used to calculate the
1007 * attenuation coefficient.
1009 multiplier = CalcDelayLengthMult(density);
1010 length = (LATE_LINE_LENGTHS[0] + LATE_LINE_LENGTHS[1] +
1011 LATE_LINE_LENGTHS[2] + LATE_LINE_LENGTHS[3]) / 4.0f * multiplier;
1012 /* Include the echo transformation (see below). */
1013 length = lerp(length, echoTime, echoDepth);
1014 length += (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
1015 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f * multiplier;
1016 /* The density gain calculation uses an average decay time weighted by
1017 * approximate bandwidth. This attempts to compensate for losses of
1018 * energy that reduce decay time due to scattering into highly attenuated
1019 * bands.
1021 bandWeights[0] = lfW;
1022 bandWeights[1] = hfW - lfW;
1023 bandWeights[2] = F_TAU - hfW;
1024 Late->DensityGain = CalcDensityGain(
1025 CalcDecayCoeff(length, (bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime +
1026 bandWeights[2]*hfDecayTime) / F_TAU)
1029 for(i = 0;i < NUM_LINES;i++)
1031 /* Calculate the length (in seconds) of each all-pass line. */
1032 length = LATE_ALLPASS_LENGTHS[i] * multiplier;
1034 /* Calculate the delay offset for each all-pass line. */
1035 Late->VecAp.Offset[i][1] = fastf2i(length * frequency);
1037 /* Calculate the length (in seconds) of each delay line. This also
1038 * applies the echo transformation. As the EAX echo depth approaches
1039 * 1, the line lengths approach a length equal to the echoTime. This
1040 * helps to produce distinct echoes along the tail.
1042 length = lerp(LATE_LINE_LENGTHS[i] * multiplier, echoTime, echoDepth);
1044 /* Calculate the delay offset for each delay line. */
1045 Late->Offset[i][1] = fastf2i(length*frequency + 0.5f);
1047 /* Approximate the absorption that the vector all-pass would exhibit
1048 * given the current diffusion so we don't have to process a full T60
1049 * filter for each of its four lines.
1051 length += lerp(LATE_ALLPASS_LENGTHS[i],
1052 (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
1053 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f,
1054 diffusion) * multiplier;
1056 /* Calculate the T60 damping coefficients for each line. */
1057 CalcT60DampingCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime,
1058 lfW, hfW, Late->T60[i].LFCoeffs,
1059 Late->T60[i].HFCoeffs);
1063 /* Creates a transform matrix given a reverb vector. This works by first
1064 * creating an inverse rotation around Y then X, applying a Z-focus transform,
1065 * then non-inverse rotations back around X then Y, to place the focal point in
1066 * the direction of the vector, using the vector length as a focus strength.
1068 * This convoluted construction ultimately results in a B-Format transformation
1069 * matrix that retains its original orientation, but spatially focuses the
1070 * signal in the desired direction. There is probably a more efficient way to
1071 * do this, but let's see how good the optimizer is.
1073 static aluMatrixf GetTransformFromVector(const ALfloat *vec)
1075 const ALfloat sqrt_3 = 1.732050808f;
1076 aluMatrixf zfocus, xrot, yrot;
1077 aluMatrixf tmp1, tmp2;
1078 ALfloat length;
1079 ALfloat sa, a;
1081 length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
1083 /* Define a Z-focus (X in Ambisonics) transform, given the panning vector
1084 * length.
1086 sa = sinf(minf(length, 1.0f) * (F_PI/2.0f));
1087 aluMatrixfSet(&zfocus,
1088 1.0f/(1.0f+sa), 0.0f, 0.0f, sa/(1.0f+sa)/sqrt_3,
1089 0.0f, sqrtf((1.0f-sa)/(1.0f+sa)), 0.0f, 0.0f,
1090 0.0f, 0.0f, sqrtf((1.0f-sa)/(1.0f+sa)), 0.0f,
1091 sa/(1.0f+sa)*sqrt_3, 0.0f, 0.0f, 1.0f/(1.0f+sa)
1094 /* Define rotation around X (Y in Ambisonics) */
1095 a = atan2f(vec[1], sqrtf(vec[0]*vec[0] + vec[2]*vec[2]));
1096 aluMatrixfSet(&xrot,
1097 1.0f, 0.0f, 0.0f, 0.0f,
1098 0.0f, 1.0f, 0.0f, 0.0f,
1099 0.0f, 0.0f, cosf(a), sinf(a),
1100 0.0f, 0.0f, -sinf(a), cosf(a)
1103 /* Define rotation around Y (Z in Ambisonics). NOTE: EFX's reverb vectors
1104 * use a right-handled coordinate system, compared to the rest of OpenAL
1105 * which uses left-handed. This is fixed by negating Z, however it would
1106 * need to also be negated to get a proper Ambisonics angle, thus
1107 * cancelling it out.
1109 a = atan2f(-vec[0], vec[2]);
1110 aluMatrixfSet(&yrot,
1111 1.0f, 0.0f, 0.0f, 0.0f,
1112 0.0f, cosf(a), 0.0f, sinf(a),
1113 0.0f, 0.0f, 1.0f, 0.0f,
1114 0.0f, -sinf(a), 0.0f, cosf(a)
1117 /* First, define a matrix that applies the inverse of the Y- then X-
1118 * rotation matrices, so that the desired direction lands on Z.
1120 #define MATRIX_INVMULT(_res, _m1, _m2) do { \
1121 int row, col; \
1122 for(col = 0;col < 4;col++) \
1124 for(row = 0;row < 4;row++) \
1125 _res.m[row][col] = _m1.m[0][row]*_m2.m[col][0] + \
1126 _m1.m[1][row]*_m2.m[col][1] + \
1127 _m1.m[2][row]*_m2.m[col][2] + \
1128 _m1.m[3][row]*_m2.m[col][3]; \
1130 } while(0)
1131 MATRIX_INVMULT(tmp1, xrot, yrot);
1132 #undef MATRIX_INVMULT
1134 #define MATRIX_MULT(_res, _m1, _m2) do { \
1135 int row, col; \
1136 for(col = 0;col < 4;col++) \
1138 for(row = 0;row < 4;row++) \
1139 _res.m[row][col] = _m1.m[row][0]*_m2.m[0][col] + \
1140 _m1.m[row][1]*_m2.m[1][col] + \
1141 _m1.m[row][2]*_m2.m[2][col] + \
1142 _m1.m[row][3]*_m2.m[3][col]; \
1144 } while(0)
1145 /* Now apply matrices to focus on Z, then rotate back around X then Y, to
1146 * result in a focus in the direction of the vector.
1148 MATRIX_MULT(tmp2, zfocus, tmp1);
1149 MATRIX_MULT(tmp1, xrot, tmp2);
1150 MATRIX_MULT(tmp2, yrot, tmp1);
1151 #undef MATRIX_MULT
1153 return tmp2;
1156 /* Update the early and late 3D panning gains. */
1157 static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat gain, const ALfloat earlyGain, const ALfloat lateGain, ALreverbState *State)
1159 aluMatrixf transform, rot;
1160 ALsizei i;
1162 STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
1163 STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;
1165 /* Note: _res is transposed. */
1166 #define MATRIX_MULT(_res, _m1, _m2) do { \
1167 int row, col; \
1168 for(col = 0;col < 4;col++) \
1170 for(row = 0;row < 4;row++) \
1171 _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
1172 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
1174 } while(0)
1175 /* Create a matrix that first converts A-Format to B-Format, then
1176 * transforms the B-Format signal according to the panning vector.
1178 rot = GetTransformFromVector(ReflectionsPan);
1179 MATRIX_MULT(transform, rot, A2B);
1180 memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
1181 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
1182 ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], gain*earlyGain,
1183 State->Early.PanGain[i]);
1185 rot = GetTransformFromVector(LateReverbPan);
1186 MATRIX_MULT(transform, rot, A2B);
1187 memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
1188 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
1189 ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], gain*lateGain,
1190 State->Late.PanGain[i]);
1191 #undef MATRIX_MULT
1194 static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
1196 const ALCdevice *Device = Context->Device;
1197 const ALlistener *Listener = Context->Listener;
1198 ALuint frequency = Device->Frequency;
1199 ALfloat lf0norm, hf0norm, hfRatio;
1200 ALfloat lfDecayTime, hfDecayTime;
1201 ALfloat gain, gainlf, gainhf;
1202 ALsizei i;
1204 /* Calculate the master filters */
1205 hf0norm = props->Reverb.HFReference / frequency;
1206 /* Restrict the filter gains from going below -60dB to keep the filter from
1207 * killing most of the signal.
1209 gainhf = maxf(props->Reverb.GainHF, 0.001f);
1210 ALfilterState_setParams(&State->Filter[0].Lp, ALfilterType_HighShelf,
1211 gainhf, hf0norm, calc_rcpQ_from_slope(gainhf, 1.0f));
1212 lf0norm = props->Reverb.LFReference / frequency;
1213 gainlf = maxf(props->Reverb.GainLF, 0.001f);
1214 ALfilterState_setParams(&State->Filter[0].Hp, ALfilterType_LowShelf,
1215 gainlf, lf0norm, calc_rcpQ_from_slope(gainlf, 1.0f));
1216 for(i = 1;i < NUM_LINES;i++)
1218 ALfilterState_copyParams(&State->Filter[i].Lp, &State->Filter[0].Lp);
1219 ALfilterState_copyParams(&State->Filter[i].Hp, &State->Filter[0].Hp);
1222 /* Update the main effect delay and associated taps. */
1223 UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
1224 props->Reverb.Density, props->Reverb.DecayTime, frequency,
1225 State);
1227 /* Calculate the all-pass feed-back/forward coefficient. */
1228 State->ApFeedCoeff = sqrtf(0.5f) * powf(props->Reverb.Diffusion, 2.0f);
1230 /* Update the early lines. */
1231 UpdateEarlyLines(props->Reverb.Density, props->Reverb.DecayTime,
1232 frequency, &State->Early);
1234 /* Get the mixing matrix coefficients. */
1235 CalcMatrixCoeffs(props->Reverb.Diffusion, &State->MixX, &State->MixY);
1237 /* If the HF limit parameter is flagged, calculate an appropriate limit
1238 * based on the air absorption parameter.
1240 hfRatio = props->Reverb.DecayHFRatio;
1241 if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
1242 hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
1243 props->Reverb.DecayTime, Listener->Params.ReverbSpeedOfSound
1246 /* Calculate the LF/HF decay times. */
1247 lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
1248 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1249 hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
1250 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1252 /* Update the late lines. */
1253 UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
1254 lfDecayTime, props->Reverb.DecayTime, hfDecayTime,
1255 F_TAU * lf0norm, F_TAU * hf0norm,
1256 props->Reverb.EchoTime, props->Reverb.EchoDepth,
1257 frequency, &State->Late);
1259 /* Update early and late 3D panning. */
1260 gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
1261 Update3DPanning(Device, props->Reverb.ReflectionsPan,
1262 props->Reverb.LateReverbPan, gain,
1263 props->Reverb.ReflectionsGain,
1264 props->Reverb.LateReverbGain, State);
1266 /* Determine if delay-line cross-fading is required. */
1267 for(i = 0;i < NUM_LINES;i++)
1269 if(State->EarlyDelayTap[i][1] != State->EarlyDelayTap[i][0] ||
1270 State->Early.VecAp.Offset[i][1] != State->Early.VecAp.Offset[i][0] ||
1271 State->Early.Offset[i][1] != State->Early.Offset[i][0] ||
1272 State->LateDelayTap[i][1] != State->LateDelayTap[i][0] ||
1273 State->Late.VecAp.Offset[i][1] != State->Late.VecAp.Offset[i][0] ||
1274 State->Late.Offset[i][1] != State->Late.Offset[i][0])
1276 State->FadeCount = 0;
1277 break;
1283 /**************************************
1284 * Effect Processing *
1285 **************************************/
1287 /* Basic delay line input/output routines. */
1288 static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c)
1290 return Delay->Line[offset&Delay->Mask][c];
1293 /* Cross-faded delay line output routine. Instead of interpolating the
1294 * offsets, this interpolates (cross-fades) the outputs at each offset.
1296 static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0,
1297 const ALsizei off1, const ALsizei c, const ALfloat mu)
1299 return Delay->Line[off0&Delay->Mask][c]*(1.0f-mu) +
1300 Delay->Line[off1&Delay->Mask][c]*( mu);
1302 #define UnfadedDelayLineOut(d, o0, o1, c, mu) DelayLineOut(d, o0, c)
1304 static inline ALvoid DelayLineIn(DelayLineI *Delay, ALsizei offset, const ALsizei c,
1305 const ALfloat *restrict in, ALsizei count)
1307 ALsizei i;
1308 for(i = 0;i < count;i++)
1309 Delay->Line[(offset++)&Delay->Mask][c] = *(in++);
1312 static inline ALvoid DelayLineIn4(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
1314 ALsizei i;
1315 offset &= Delay->Mask;
1316 for(i = 0;i < NUM_LINES;i++)
1317 Delay->Line[offset][i] = in[i];
1320 static inline ALvoid DelayLineIn4Rev(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
1322 ALsizei i;
1323 offset &= Delay->Mask;
1324 for(i = 0;i < NUM_LINES;i++)
1325 Delay->Line[offset][i] = in[NUM_LINES-1-i];
1328 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1329 * for both the below vector all-pass model and to perform modal feed-back
1330 * delay network (FDN) mixing.
1332 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1333 * matrix with a single unitary rotational parameter:
1335 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1336 * [ -a, d, c, -b ]
1337 * [ -b, -c, d, a ]
1338 * [ -c, b, -a, d ]
1340 * The rotation is constructed from the effect's diffusion parameter,
1341 * yielding:
1343 * 1 = x^2 + 3 y^2
1345 * Where a, b, and c are the coefficient y with differing signs, and d is the
1346 * coefficient x. The final matrix is thus:
1348 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1349 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1350 * [ y, -y, x, y ] x = cos(t)
1351 * [ -y, -y, -y, x ] y = sin(t) / n
1353 * Any square orthogonal matrix with an order that is a power of two will
1354 * work (where ^T is transpose, ^-1 is inverse):
1356 * M^T = M^-1
1358 * Using that knowledge, finding an appropriate matrix can be accomplished
1359 * naively by searching all combinations of:
1361 * M = D + S - S^T
1363 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1364 * whose combination of signs are being iterated.
1366 static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *restrict in,
1367 const ALfloat xCoeff, const ALfloat yCoeff)
1369 out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
1370 out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
1371 out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
1372 out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
1375 /* Same as above, but reverses the input. */
1376 static inline void VectorPartialScatterRev(ALfloat *restrict out, const ALfloat *restrict in,
1377 const ALfloat xCoeff, const ALfloat yCoeff)
1379 out[0] = xCoeff*in[3] + yCoeff*(in[0] + -in[1] + in[2] );
1380 out[1] = xCoeff*in[2] + yCoeff*(in[0] + in[1] + -in[3]);
1381 out[2] = xCoeff*in[1] + yCoeff*(in[0] + -in[2] + in[3]);
1382 out[3] = xCoeff*in[0] + yCoeff*( + -in[1] + -in[2] + -in[3]);
1385 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1386 * filter to the 4-line input.
1388 * It works by vectorizing a regular all-pass filter and replacing the delay
1389 * element with a scattering matrix (like the one above) and a diagonal
1390 * matrix of delay elements.
1392 * Two static specializations are used for transitional (cross-faded) delay
1393 * line processing and non-transitional processing.
1395 #define DECL_TEMPLATE(T) \
1396 static void VectorAllpass_##T(ALfloat *restrict out, \
1397 const ALfloat *restrict in, \
1398 const ALsizei offset, const ALfloat feedCoeff, \
1399 const ALfloat xCoeff, const ALfloat yCoeff, \
1400 const ALfloat mu, VecAllpass *Vap) \
1402 ALfloat f[NUM_LINES], fs[NUM_LINES]; \
1403 ALfloat input; \
1404 ALsizei i; \
1406 (void)mu; /* Ignore for Unfaded. */ \
1408 for(i = 0;i < NUM_LINES;i++) \
1410 input = in[i]; \
1411 out[i] = T##DelayLineOut(&Vap->Delay, offset-Vap->Offset[i][0], \
1412 offset-Vap->Offset[i][1], i, mu) - \
1413 feedCoeff*input; \
1414 f[i] = input + feedCoeff*out[i]; \
1416 VectorPartialScatter(fs, f, xCoeff, yCoeff); \
1418 DelayLineIn4(&Vap->Delay, offset, fs); \
1420 DECL_TEMPLATE(Unfaded)
1421 DECL_TEMPLATE(Faded)
1422 #undef DECL_TEMPLATE
1424 /* This generates early reflections.
1426 * This is done by obtaining the primary reflections (those arriving from the
1427 * same direction as the source) from the main delay line. These are
1428 * attenuated and all-pass filtered (based on the diffusion parameter).
1430 * The early lines are then fed in reverse (according to the approximately
1431 * opposite spatial location of the A-Format lines) to create the secondary
1432 * reflections (those arriving from the opposite direction as the source).
1434 * The early response is then completed by combining the primary reflections
1435 * with the delayed and attenuated output from the early lines.
1437 * Finally, the early response is reversed, scattered (based on diffusion),
1438 * and fed into the late reverb section of the main delay line.
1440 * Two static specializations are used for transitional (cross-faded) delay
1441 * line processing and non-transitional processing.
1443 #define DECL_TEMPLATE(T) \
1444 static ALvoid EarlyReflection_##T(ALreverbState *State, const ALsizei todo, \
1445 ALfloat fade, \
1446 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])\
1448 ALsizei offset = State->Offset; \
1449 const ALfloat apFeedCoeff = State->ApFeedCoeff; \
1450 const ALfloat mixX = State->MixX; \
1451 const ALfloat mixY = State->MixY; \
1452 ALfloat f[NUM_LINES], fr[NUM_LINES]; \
1453 ALsizei i, j; \
1455 for(i = 0;i < todo;i++) \
1457 for(j = 0;j < NUM_LINES;j++) \
1458 fr[j] = T##DelayLineOut(&State->Delay, \
1459 offset-State->EarlyDelayTap[j][0], \
1460 offset-State->EarlyDelayTap[j][1], j, fade \
1461 ) * State->EarlyDelayCoeff[j]; \
1463 VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \
1464 &State->Early.VecAp); \
1466 DelayLineIn4Rev(&State->Early.Delay, offset, f); \
1468 for(j = 0;j < NUM_LINES;j++) \
1469 f[j] += T##DelayLineOut(&State->Early.Delay, \
1470 offset-State->Early.Offset[j][0], \
1471 offset-State->Early.Offset[j][1], j, fade \
1472 ) * State->Early.Coeff[j]; \
1474 for(j = 0;j < NUM_LINES;j++) \
1475 out[j][i] = f[j]; \
1477 VectorPartialScatterRev(fr, f, mixX, mixY); \
1479 DelayLineIn4(&State->Delay, offset-State->LateFeedTap, fr); \
1481 offset++; \
1482 fade += FadeStep; \
1485 DECL_TEMPLATE(Unfaded)
1486 DECL_TEMPLATE(Faded)
1487 #undef DECL_TEMPLATE
1489 /* Applies a first order filter section. */
1490 static inline ALfloat FirstOrderFilter(const ALfloat in, const ALfloat *restrict coeffs,
1491 ALfloat *restrict state)
1493 ALfloat out = coeffs[0]*in + coeffs[1]*state[0] + coeffs[2]*state[1];
1494 state[0] = in;
1495 state[1] = out;
1496 return out;
1499 /* Applies the two T60 damping filter sections. */
1500 static inline void LateT60Filter(ALfloat *restrict out, const ALfloat *restrict in,
1501 T60Filter *filter)
1503 ALsizei i;
1504 for(i = 0;i < NUM_LINES;i++)
1505 out[i] = FirstOrderFilter(
1506 FirstOrderFilter(in[i], filter[i].HFCoeffs, filter[i].HFState),
1507 filter[i].LFCoeffs, filter[i].LFState
1511 /* This generates the reverb tail using a modified feed-back delay network
1512 * (FDN).
1514 * Results from the early reflections are attenuated by the density gain and
1515 * mixed with the output from the late delay lines.
1517 * The late response is then completed by T60 and all-pass filtering the mix.
1519 * Finally, the lines are reversed (so they feed their opposite directions)
1520 * and scattered with the FDN matrix before re-feeding the delay lines.
1522 * Two variations are made, one for for transitional (cross-faded) delay line
1523 * processing and one for non-transitional processing.
1525 static ALvoid LateReverb_Faded(ALreverbState *State, const ALsizei todo, ALfloat fade,
1526 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1528 const ALfloat apFeedCoeff = State->ApFeedCoeff;
1529 const ALfloat mixX = State->MixX;
1530 const ALfloat mixY = State->MixY;
1531 ALsizei offset;
1532 ALsizei i, j;
1534 offset = State->Offset;
1535 for(i = 0;i < todo;i++)
1537 ALfloat f[NUM_LINES], fr[NUM_LINES];
1539 for(j = 0;j < NUM_LINES;j++)
1540 f[j] = FadedDelayLineOut(&State->Delay,
1541 offset - State->LateDelayTap[j][0],
1542 offset - State->LateDelayTap[j][1], j, fade
1543 ) * State->Late.DensityGain;
1545 for(j = 0;j < NUM_LINES;j++)
1546 f[j] += FadedDelayLineOut(&State->Late.Delay,
1547 offset - State->Late.Offset[j][0],
1548 offset - State->Late.Offset[j][1], j, fade
1551 LateT60Filter(fr, f, State->Late.T60);
1552 VectorAllpass_Faded(f, fr, offset, apFeedCoeff, mixX, mixY, fade,
1553 &State->Late.VecAp);
1555 for(j = 0;j < NUM_LINES;j++)
1556 out[j][i] = f[j];
1558 VectorPartialScatterRev(fr, f, mixX, mixY);
1560 DelayLineIn4(&State->Late.Delay, offset, fr);
1562 offset++;
1563 fade += FadeStep;
1566 static ALvoid LateReverb_Unfaded(ALreverbState *State, const ALsizei todo, ALfloat fade,
1567 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1569 const ALfloat apFeedCoeff = State->ApFeedCoeff;
1570 const ALfloat mixX = State->MixX;
1571 const ALfloat mixY = State->MixY;
1572 ALsizei offset;
1573 ALsizei i, j;
1575 offset = State->Offset;
1576 for(i = 0;i < todo;i++)
1578 ALfloat f[NUM_LINES], fr[NUM_LINES];
1580 for(j = 0;j < NUM_LINES;j++)
1581 f[j] = DelayLineOut(&State->Delay, offset-State->LateDelayTap[j][0], j) *
1582 State->Late.DensityGain;
1584 for(j = 0;j < NUM_LINES;j++)
1585 f[j] += DelayLineOut(&State->Late.Delay, offset-State->Late.Offset[j][0], j);
1587 LateT60Filter(fr, f, State->Late.T60);
1588 VectorAllpass_Unfaded(f, fr, offset, apFeedCoeff, mixX, mixY, fade,
1589 &State->Late.VecAp);
1591 for(j = 0;j < NUM_LINES;j++)
1592 out[j][i] = f[j];
1594 VectorPartialScatterRev(fr, f, mixX, mixY);
1596 DelayLineIn4(&State->Late.Delay, offset, fr);
1598 offset++;
1602 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
1604 ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->AFormatSamples;
1605 ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples;
1606 ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples;
1607 ALsizei fadeCount = State->FadeCount;
1608 ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES;
1609 ALsizei base, c;
1611 /* Process reverb for these samples. */
1612 for(base = 0;base < SamplesToDo;)
1614 ALsizei todo = mini(SamplesToDo-base, MAX_UPDATE_SAMPLES);
1615 /* If cross-fading, don't do more samples than there are to fade. */
1616 if(FADE_SAMPLES-fadeCount > 0)
1617 todo = mini(todo, FADE_SAMPLES-fadeCount);
1619 /* Convert B-Format to A-Format for processing. */
1620 memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
1621 for(c = 0;c < NUM_LINES;c++)
1622 MixRowSamples(afmt[c], B2A.m[c],
1623 SamplesIn, MAX_EFFECT_CHANNELS, base, todo
1626 /* Process the samples for reverb. */
1627 for(c = 0;c < NUM_LINES;c++)
1629 /* Band-pass the incoming samples. Use the early output lines for
1630 * temp storage.
1632 ALfilterState_process(&State->Filter[c].Lp, early[0], afmt[c], todo);
1633 ALfilterState_process(&State->Filter[c].Hp, early[1], early[0], todo);
1635 /* Feed the initial delay line. */
1636 DelayLineIn(&State->Delay, State->Offset, c, early[1], todo);
1639 if(UNLIKELY(fadeCount < FADE_SAMPLES))
1641 /* Generate early reflections. */
1642 EarlyReflection_Faded(State, todo, fade, early);
1644 /* Generate late reverb. */
1645 LateReverb_Faded(State, todo, fade, late);
1646 fade = minf(1.0f, fade + todo*FadeStep);
1648 else
1650 /* Generate early reflections. */
1651 EarlyReflection_Unfaded(State, todo, fade, early);
1653 /* Generate late reverb. */
1654 LateReverb_Unfaded(State, todo, fade, late);
1657 /* Step all delays forward. */
1658 State->Offset += todo;
1660 if(UNLIKELY(fadeCount < FADE_SAMPLES) && (fadeCount += todo) >= FADE_SAMPLES)
1662 /* Update the cross-fading delay line taps. */
1663 fadeCount = FADE_SAMPLES;
1664 fade = 1.0f;
1665 for(c = 0;c < NUM_LINES;c++)
1667 State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
1668 State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1];
1669 State->Early.Offset[c][0] = State->Early.Offset[c][1];
1670 State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
1671 State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1];
1672 State->Late.Offset[c][0] = State->Late.Offset[c][1];
1676 /* Mix the A-Format results to output, implicitly converting back to
1677 * B-Format.
1679 for(c = 0;c < NUM_LINES;c++)
1680 MixSamples(early[c], NumChannels, SamplesOut,
1681 State->Early.CurrentGain[c], State->Early.PanGain[c],
1682 SamplesToDo-base, base, todo
1684 for(c = 0;c < NUM_LINES;c++)
1685 MixSamples(late[c], NumChannels, SamplesOut,
1686 State->Late.CurrentGain[c], State->Late.PanGain[c],
1687 SamplesToDo-base, base, todo
1690 base += todo;
1692 State->FadeCount = fadeCount;
1696 typedef struct ReverbStateFactory {
1697 DERIVE_FROM_TYPE(EffectStateFactory);
1698 } ReverbStateFactory;
1700 static ALeffectState *ReverbStateFactory_create(ReverbStateFactory* UNUSED(factory))
1702 ALreverbState *state;
1704 NEW_OBJ0(state, ALreverbState)();
1705 if(!state) return NULL;
1707 return STATIC_CAST(ALeffectState, state);
1710 DEFINE_EFFECTSTATEFACTORY_VTABLE(ReverbStateFactory);
1712 EffectStateFactory *ReverbStateFactory_getFactory(void)
1714 static ReverbStateFactory ReverbFactory = { { GET_VTABLE2(ReverbStateFactory, EffectStateFactory) } };
1716 return STATIC_CAST(EffectStateFactory, &ReverbFactory);
1720 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1722 ALeffectProps *props = &effect->Props;
1723 switch(param)
1725 case AL_EAXREVERB_DECAY_HFLIMIT:
1726 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1727 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
1728 props->Reverb.DecayHFLimit = val;
1729 break;
1731 default:
1732 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1733 param);
1736 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1737 { ALeaxreverb_setParami(effect, context, param, vals[0]); }
1738 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1740 ALeffectProps *props = &effect->Props;
1741 switch(param)
1743 case AL_EAXREVERB_DENSITY:
1744 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1745 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
1746 props->Reverb.Density = val;
1747 break;
1749 case AL_EAXREVERB_DIFFUSION:
1750 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1751 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
1752 props->Reverb.Diffusion = val;
1753 break;
1755 case AL_EAXREVERB_GAIN:
1756 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1757 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
1758 props->Reverb.Gain = val;
1759 break;
1761 case AL_EAXREVERB_GAINHF:
1762 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1763 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
1764 props->Reverb.GainHF = val;
1765 break;
1767 case AL_EAXREVERB_GAINLF:
1768 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1769 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
1770 props->Reverb.GainLF = val;
1771 break;
1773 case AL_EAXREVERB_DECAY_TIME:
1774 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1775 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
1776 props->Reverb.DecayTime = val;
1777 break;
1779 case AL_EAXREVERB_DECAY_HFRATIO:
1780 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1781 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
1782 props->Reverb.DecayHFRatio = val;
1783 break;
1785 case AL_EAXREVERB_DECAY_LFRATIO:
1786 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1787 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
1788 props->Reverb.DecayLFRatio = val;
1789 break;
1791 case AL_EAXREVERB_REFLECTIONS_GAIN:
1792 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1793 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
1794 props->Reverb.ReflectionsGain = val;
1795 break;
1797 case AL_EAXREVERB_REFLECTIONS_DELAY:
1798 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1799 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
1800 props->Reverb.ReflectionsDelay = val;
1801 break;
1803 case AL_EAXREVERB_LATE_REVERB_GAIN:
1804 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1805 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
1806 props->Reverb.LateReverbGain = val;
1807 break;
1809 case AL_EAXREVERB_LATE_REVERB_DELAY:
1810 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1811 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
1812 props->Reverb.LateReverbDelay = val;
1813 break;
1815 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1816 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1817 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
1818 props->Reverb.AirAbsorptionGainHF = val;
1819 break;
1821 case AL_EAXREVERB_ECHO_TIME:
1822 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1823 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
1824 props->Reverb.EchoTime = val;
1825 break;
1827 case AL_EAXREVERB_ECHO_DEPTH:
1828 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1829 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
1830 props->Reverb.EchoDepth = val;
1831 break;
1833 case AL_EAXREVERB_MODULATION_TIME:
1834 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1835 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
1836 props->Reverb.ModulationTime = val;
1837 break;
1839 case AL_EAXREVERB_MODULATION_DEPTH:
1840 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1841 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
1842 props->Reverb.ModulationDepth = val;
1843 break;
1845 case AL_EAXREVERB_HFREFERENCE:
1846 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1847 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
1848 props->Reverb.HFReference = val;
1849 break;
1851 case AL_EAXREVERB_LFREFERENCE:
1852 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1853 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
1854 props->Reverb.LFReference = val;
1855 break;
1857 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1858 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1859 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
1860 props->Reverb.RoomRolloffFactor = val;
1861 break;
1863 default:
1864 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1865 param);
1868 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1870 ALeffectProps *props = &effect->Props;
1871 switch(param)
1873 case AL_EAXREVERB_REFLECTIONS_PAN:
1874 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1875 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
1876 props->Reverb.ReflectionsPan[0] = vals[0];
1877 props->Reverb.ReflectionsPan[1] = vals[1];
1878 props->Reverb.ReflectionsPan[2] = vals[2];
1879 break;
1880 case AL_EAXREVERB_LATE_REVERB_PAN:
1881 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1882 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
1883 props->Reverb.LateReverbPan[0] = vals[0];
1884 props->Reverb.LateReverbPan[1] = vals[1];
1885 props->Reverb.LateReverbPan[2] = vals[2];
1886 break;
1888 default:
1889 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1890 break;
1894 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1896 const ALeffectProps *props = &effect->Props;
1897 switch(param)
1899 case AL_EAXREVERB_DECAY_HFLIMIT:
1900 *val = props->Reverb.DecayHFLimit;
1901 break;
1903 default:
1904 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1905 param);
1908 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1909 { ALeaxreverb_getParami(effect, context, param, vals); }
1910 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1912 const ALeffectProps *props = &effect->Props;
1913 switch(param)
1915 case AL_EAXREVERB_DENSITY:
1916 *val = props->Reverb.Density;
1917 break;
1919 case AL_EAXREVERB_DIFFUSION:
1920 *val = props->Reverb.Diffusion;
1921 break;
1923 case AL_EAXREVERB_GAIN:
1924 *val = props->Reverb.Gain;
1925 break;
1927 case AL_EAXREVERB_GAINHF:
1928 *val = props->Reverb.GainHF;
1929 break;
1931 case AL_EAXREVERB_GAINLF:
1932 *val = props->Reverb.GainLF;
1933 break;
1935 case AL_EAXREVERB_DECAY_TIME:
1936 *val = props->Reverb.DecayTime;
1937 break;
1939 case AL_EAXREVERB_DECAY_HFRATIO:
1940 *val = props->Reverb.DecayHFRatio;
1941 break;
1943 case AL_EAXREVERB_DECAY_LFRATIO:
1944 *val = props->Reverb.DecayLFRatio;
1945 break;
1947 case AL_EAXREVERB_REFLECTIONS_GAIN:
1948 *val = props->Reverb.ReflectionsGain;
1949 break;
1951 case AL_EAXREVERB_REFLECTIONS_DELAY:
1952 *val = props->Reverb.ReflectionsDelay;
1953 break;
1955 case AL_EAXREVERB_LATE_REVERB_GAIN:
1956 *val = props->Reverb.LateReverbGain;
1957 break;
1959 case AL_EAXREVERB_LATE_REVERB_DELAY:
1960 *val = props->Reverb.LateReverbDelay;
1961 break;
1963 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1964 *val = props->Reverb.AirAbsorptionGainHF;
1965 break;
1967 case AL_EAXREVERB_ECHO_TIME:
1968 *val = props->Reverb.EchoTime;
1969 break;
1971 case AL_EAXREVERB_ECHO_DEPTH:
1972 *val = props->Reverb.EchoDepth;
1973 break;
1975 case AL_EAXREVERB_MODULATION_TIME:
1976 *val = props->Reverb.ModulationTime;
1977 break;
1979 case AL_EAXREVERB_MODULATION_DEPTH:
1980 *val = props->Reverb.ModulationDepth;
1981 break;
1983 case AL_EAXREVERB_HFREFERENCE:
1984 *val = props->Reverb.HFReference;
1985 break;
1987 case AL_EAXREVERB_LFREFERENCE:
1988 *val = props->Reverb.LFReference;
1989 break;
1991 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1992 *val = props->Reverb.RoomRolloffFactor;
1993 break;
1995 default:
1996 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1997 param);
2000 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2002 const ALeffectProps *props = &effect->Props;
2003 switch(param)
2005 case AL_EAXREVERB_REFLECTIONS_PAN:
2006 vals[0] = props->Reverb.ReflectionsPan[0];
2007 vals[1] = props->Reverb.ReflectionsPan[1];
2008 vals[2] = props->Reverb.ReflectionsPan[2];
2009 break;
2010 case AL_EAXREVERB_LATE_REVERB_PAN:
2011 vals[0] = props->Reverb.LateReverbPan[0];
2012 vals[1] = props->Reverb.LateReverbPan[1];
2013 vals[2] = props->Reverb.LateReverbPan[2];
2014 break;
2016 default:
2017 ALeaxreverb_getParamf(effect, context, param, vals);
2018 break;
2022 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
2024 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
2026 ALeffectProps *props = &effect->Props;
2027 switch(param)
2029 case AL_REVERB_DECAY_HFLIMIT:
2030 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
2031 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
2032 props->Reverb.DecayHFLimit = val;
2033 break;
2035 default:
2036 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
2039 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
2040 { ALreverb_setParami(effect, context, param, vals[0]); }
2041 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
2043 ALeffectProps *props = &effect->Props;
2044 switch(param)
2046 case AL_REVERB_DENSITY:
2047 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
2048 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
2049 props->Reverb.Density = val;
2050 break;
2052 case AL_REVERB_DIFFUSION:
2053 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
2054 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
2055 props->Reverb.Diffusion = val;
2056 break;
2058 case AL_REVERB_GAIN:
2059 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
2060 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
2061 props->Reverb.Gain = val;
2062 break;
2064 case AL_REVERB_GAINHF:
2065 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
2066 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
2067 props->Reverb.GainHF = val;
2068 break;
2070 case AL_REVERB_DECAY_TIME:
2071 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
2072 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
2073 props->Reverb.DecayTime = val;
2074 break;
2076 case AL_REVERB_DECAY_HFRATIO:
2077 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
2078 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
2079 props->Reverb.DecayHFRatio = val;
2080 break;
2082 case AL_REVERB_REFLECTIONS_GAIN:
2083 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
2084 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
2085 props->Reverb.ReflectionsGain = val;
2086 break;
2088 case AL_REVERB_REFLECTIONS_DELAY:
2089 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
2090 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
2091 props->Reverb.ReflectionsDelay = val;
2092 break;
2094 case AL_REVERB_LATE_REVERB_GAIN:
2095 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
2096 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
2097 props->Reverb.LateReverbGain = val;
2098 break;
2100 case AL_REVERB_LATE_REVERB_DELAY:
2101 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
2102 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
2103 props->Reverb.LateReverbDelay = val;
2104 break;
2106 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2107 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
2108 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
2109 props->Reverb.AirAbsorptionGainHF = val;
2110 break;
2112 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2113 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
2114 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
2115 props->Reverb.RoomRolloffFactor = val;
2116 break;
2118 default:
2119 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2122 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
2123 { ALreverb_setParamf(effect, context, param, vals[0]); }
2125 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
2127 const ALeffectProps *props = &effect->Props;
2128 switch(param)
2130 case AL_REVERB_DECAY_HFLIMIT:
2131 *val = props->Reverb.DecayHFLimit;
2132 break;
2134 default:
2135 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
2138 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
2139 { ALreverb_getParami(effect, context, param, vals); }
2140 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
2142 const ALeffectProps *props = &effect->Props;
2143 switch(param)
2145 case AL_REVERB_DENSITY:
2146 *val = props->Reverb.Density;
2147 break;
2149 case AL_REVERB_DIFFUSION:
2150 *val = props->Reverb.Diffusion;
2151 break;
2153 case AL_REVERB_GAIN:
2154 *val = props->Reverb.Gain;
2155 break;
2157 case AL_REVERB_GAINHF:
2158 *val = props->Reverb.GainHF;
2159 break;
2161 case AL_REVERB_DECAY_TIME:
2162 *val = props->Reverb.DecayTime;
2163 break;
2165 case AL_REVERB_DECAY_HFRATIO:
2166 *val = props->Reverb.DecayHFRatio;
2167 break;
2169 case AL_REVERB_REFLECTIONS_GAIN:
2170 *val = props->Reverb.ReflectionsGain;
2171 break;
2173 case AL_REVERB_REFLECTIONS_DELAY:
2174 *val = props->Reverb.ReflectionsDelay;
2175 break;
2177 case AL_REVERB_LATE_REVERB_GAIN:
2178 *val = props->Reverb.LateReverbGain;
2179 break;
2181 case AL_REVERB_LATE_REVERB_DELAY:
2182 *val = props->Reverb.LateReverbDelay;
2183 break;
2185 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2186 *val = props->Reverb.AirAbsorptionGainHF;
2187 break;
2189 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2190 *val = props->Reverb.RoomRolloffFactor;
2191 break;
2193 default:
2194 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2197 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2198 { ALreverb_getParamf(effect, context, param, vals); }
2200 DEFINE_ALEFFECT_VTABLE(ALreverb);