Combine the vector reverse and partial scatter where they're together
[openal-soft.git] / Alc / effects / reverb.c
blobf6499bb7b0596ee11942c7ed838b7691180d08d4
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, const ALsizei offset, const ALsizei c, const ALfloat in)
1306 Delay->Line[offset&Delay->Mask][c] = in;
1309 static inline ALvoid DelayLineIn4(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
1311 ALsizei i;
1312 offset &= Delay->Mask;
1313 for(i = 0;i < NUM_LINES;i++)
1314 Delay->Line[offset][i] = in[i];
1317 static inline ALvoid DelayLineIn4Rev(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
1319 ALsizei i;
1320 offset &= Delay->Mask;
1321 for(i = 0;i < NUM_LINES;i++)
1322 Delay->Line[offset][i] = in[NUM_LINES-1-i];
1325 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1326 * for both the below vector all-pass model and to perform modal feed-back
1327 * delay network (FDN) mixing.
1329 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1330 * matrix with a single unitary rotational parameter:
1332 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1333 * [ -a, d, c, -b ]
1334 * [ -b, -c, d, a ]
1335 * [ -c, b, -a, d ]
1337 * The rotation is constructed from the effect's diffusion parameter,
1338 * yielding:
1340 * 1 = x^2 + 3 y^2
1342 * Where a, b, and c are the coefficient y with differing signs, and d is the
1343 * coefficient x. The final matrix is thus:
1345 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1346 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1347 * [ y, -y, x, y ] x = cos(t)
1348 * [ -y, -y, -y, x ] y = sin(t) / n
1350 * Any square orthogonal matrix with an order that is a power of two will
1351 * work (where ^T is transpose, ^-1 is inverse):
1353 * M^T = M^-1
1355 * Using that knowledge, finding an appropriate matrix can be accomplished
1356 * naively by searching all combinations of:
1358 * M = D + S - S^T
1360 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1361 * whose combination of signs are being iterated.
1363 static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *restrict in,
1364 const ALfloat xCoeff, const ALfloat yCoeff)
1366 out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
1367 out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
1368 out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
1369 out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
1372 /* Same as above, but reverses the input. */
1373 static inline void VectorPartialScatterRev(ALfloat *restrict out, const ALfloat *restrict in,
1374 const ALfloat xCoeff, const ALfloat yCoeff)
1376 out[0] = xCoeff*in[3] + yCoeff*(in[0] + -in[1] + in[2] );
1377 out[1] = xCoeff*in[2] + yCoeff*(in[0] + in[1] + -in[3]);
1378 out[2] = xCoeff*in[1] + yCoeff*(in[0] + -in[2] + in[3]);
1379 out[3] = xCoeff*in[0] + yCoeff*( + -in[1] + -in[2] + -in[3]);
1382 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1383 * filter to the 4-line input.
1385 * It works by vectorizing a regular all-pass filter and replacing the delay
1386 * element with a scattering matrix (like the one above) and a diagonal
1387 * matrix of delay elements.
1389 * Two static specializations are used for transitional (cross-faded) delay
1390 * line processing and non-transitional processing.
1392 #define DECL_TEMPLATE(T) \
1393 static void VectorAllpass_##T(ALfloat *restrict out, \
1394 const ALfloat *restrict in, \
1395 const ALsizei offset, const ALfloat feedCoeff, \
1396 const ALfloat xCoeff, const ALfloat yCoeff, \
1397 const ALfloat mu, VecAllpass *Vap) \
1399 ALfloat f[NUM_LINES], fs[NUM_LINES]; \
1400 ALfloat input; \
1401 ALsizei i; \
1403 (void)mu; /* Ignore for Unfaded. */ \
1405 for(i = 0;i < NUM_LINES;i++) \
1407 input = in[i]; \
1408 out[i] = T##DelayLineOut(&Vap->Delay, offset-Vap->Offset[i][0], \
1409 offset-Vap->Offset[i][1], i, mu) - \
1410 feedCoeff*input; \
1411 f[i] = input + feedCoeff*out[i]; \
1413 VectorPartialScatter(fs, f, xCoeff, yCoeff); \
1415 DelayLineIn4(&Vap->Delay, offset, fs); \
1417 DECL_TEMPLATE(Unfaded)
1418 DECL_TEMPLATE(Faded)
1419 #undef DECL_TEMPLATE
1421 /* This generates early reflections.
1423 * This is done by obtaining the primary reflections (those arriving from the
1424 * same direction as the source) from the main delay line. These are
1425 * attenuated and all-pass filtered (based on the diffusion parameter).
1427 * The early lines are then fed in reverse (according to the approximately
1428 * opposite spatial location of the A-Format lines) to create the secondary
1429 * reflections (those arriving from the opposite direction as the source).
1431 * The early response is then completed by combining the primary reflections
1432 * with the delayed and attenuated output from the early lines.
1434 * Finally, the early response is reversed, scattered (based on diffusion),
1435 * and fed into the late reverb section of the main delay line.
1437 * Two static specializations are used for transitional (cross-faded) delay
1438 * line processing and non-transitional processing.
1440 #define DECL_TEMPLATE(T) \
1441 static ALvoid EarlyReflection_##T(ALreverbState *State, const ALsizei todo, \
1442 ALfloat fade, \
1443 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])\
1445 ALsizei offset = State->Offset; \
1446 const ALfloat apFeedCoeff = State->ApFeedCoeff; \
1447 const ALfloat mixX = State->MixX; \
1448 const ALfloat mixY = State->MixY; \
1449 ALfloat f[NUM_LINES], fr[NUM_LINES]; \
1450 ALsizei i, j; \
1452 for(i = 0;i < todo;i++) \
1454 for(j = 0;j < NUM_LINES;j++) \
1455 fr[j] = T##DelayLineOut(&State->Delay, \
1456 offset-State->EarlyDelayTap[j][0], \
1457 offset-State->EarlyDelayTap[j][1], j, fade \
1458 ) * State->EarlyDelayCoeff[j]; \
1460 VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \
1461 &State->Early.VecAp); \
1463 DelayLineIn4Rev(&State->Early.Delay, offset, f); \
1465 for(j = 0;j < NUM_LINES;j++) \
1466 f[j] += T##DelayLineOut(&State->Early.Delay, \
1467 offset-State->Early.Offset[j][0], \
1468 offset-State->Early.Offset[j][1], j, fade \
1469 ) * State->Early.Coeff[j]; \
1471 for(j = 0;j < NUM_LINES;j++) \
1472 out[j][i] = f[j]; \
1474 VectorPartialScatterRev(fr, f, mixX, mixY); \
1476 DelayLineIn4(&State->Delay, offset-State->LateFeedTap, fr); \
1478 offset++; \
1479 fade += FadeStep; \
1482 DECL_TEMPLATE(Unfaded)
1483 DECL_TEMPLATE(Faded)
1484 #undef DECL_TEMPLATE
1486 /* Applies a first order filter section. */
1487 static inline ALfloat FirstOrderFilter(const ALfloat in, const ALfloat *restrict coeffs,
1488 ALfloat *restrict state)
1490 ALfloat out = coeffs[0]*in + coeffs[1]*state[0] + coeffs[2]*state[1];
1491 state[0] = in;
1492 state[1] = out;
1493 return out;
1496 /* Applies the two T60 damping filter sections. */
1497 static inline void LateT60Filter(ALfloat *restrict out, const ALfloat *restrict in,
1498 T60Filter *filter)
1500 ALsizei i;
1501 for(i = 0;i < NUM_LINES;i++)
1502 out[i] = FirstOrderFilter(
1503 FirstOrderFilter(in[i], filter[i].HFCoeffs, filter[i].HFState),
1504 filter[i].LFCoeffs, filter[i].LFState
1508 /* This generates the reverb tail using a modified feed-back delay network
1509 * (FDN).
1511 * Results from the early reflections are attenuated by the density gain and
1512 * mixed with the output from the late delay lines.
1514 * The late response is then completed by T60 and all-pass filtering the mix.
1516 * Finally, the lines are reversed (so they feed their opposite directions)
1517 * and scattered with the FDN matrix before re-feeding the delay lines.
1519 * Two variations are made, one for for transitional (cross-faded) delay line
1520 * processing and one for non-transitional processing.
1522 static ALvoid LateReverb_Faded(ALreverbState *State, const ALsizei todo, ALfloat fade,
1523 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1525 const ALfloat apFeedCoeff = State->ApFeedCoeff;
1526 const ALfloat mixX = State->MixX;
1527 const ALfloat mixY = State->MixY;
1528 ALsizei offset;
1529 ALsizei i, j;
1531 offset = State->Offset;
1532 for(i = 0;i < todo;i++)
1534 ALfloat f[NUM_LINES], fr[NUM_LINES];
1536 for(j = 0;j < NUM_LINES;j++)
1537 f[j] = FadedDelayLineOut(&State->Delay,
1538 offset - State->LateDelayTap[j][0],
1539 offset - State->LateDelayTap[j][1], j, fade
1540 ) * State->Late.DensityGain;
1542 for(j = 0;j < NUM_LINES;j++)
1543 f[j] += FadedDelayLineOut(&State->Late.Delay,
1544 offset - State->Late.Offset[j][0],
1545 offset - State->Late.Offset[j][1], j, fade
1548 LateT60Filter(fr, f, State->Late.T60);
1549 VectorAllpass_Faded(f, fr, offset, apFeedCoeff, mixX, mixY, fade,
1550 &State->Late.VecAp);
1552 for(j = 0;j < NUM_LINES;j++)
1553 out[j][i] = f[j];
1555 VectorPartialScatterRev(fr, f, mixX, mixY);
1557 DelayLineIn4(&State->Late.Delay, offset, fr);
1559 offset++;
1560 fade += FadeStep;
1563 static ALvoid LateReverb_Unfaded(ALreverbState *State, const ALsizei todo, ALfloat fade,
1564 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1566 const ALfloat apFeedCoeff = State->ApFeedCoeff;
1567 const ALfloat mixX = State->MixX;
1568 const ALfloat mixY = State->MixY;
1569 ALsizei offset;
1570 ALsizei i, j;
1572 offset = State->Offset;
1573 for(i = 0;i < todo;i++)
1575 ALfloat f[NUM_LINES], fr[NUM_LINES];
1577 for(j = 0;j < NUM_LINES;j++)
1578 f[j] = DelayLineOut(&State->Delay, offset-State->LateDelayTap[j][0], j) *
1579 State->Late.DensityGain;
1581 for(j = 0;j < NUM_LINES;j++)
1582 f[j] += DelayLineOut(&State->Late.Delay, offset-State->Late.Offset[j][0], j);
1584 LateT60Filter(fr, f, State->Late.T60);
1585 VectorAllpass_Unfaded(f, fr, offset, apFeedCoeff, mixX, mixY, fade,
1586 &State->Late.VecAp);
1588 for(j = 0;j < NUM_LINES;j++)
1589 out[j][i] = f[j];
1591 VectorPartialScatterRev(fr, f, mixX, mixY);
1593 DelayLineIn4(&State->Late.Delay, offset, fr);
1595 offset++;
1599 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
1601 ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->AFormatSamples;
1602 ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples;
1603 ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples;
1604 ALsizei fadeCount = State->FadeCount;
1605 ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES;
1606 ALsizei base, c, i;
1608 /* Process reverb for these samples. */
1609 for(base = 0;base < SamplesToDo;)
1611 ALsizei todo = mini(SamplesToDo-base, MAX_UPDATE_SAMPLES);
1612 /* If cross-fading, don't do more samples than there are to fade. */
1613 if(FADE_SAMPLES-fadeCount > 0)
1614 todo = mini(todo, FADE_SAMPLES-fadeCount);
1616 /* Convert B-Format to A-Format for processing. */
1617 memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
1618 for(c = 0;c < NUM_LINES;c++)
1619 MixRowSamples(afmt[c], B2A.m[c],
1620 SamplesIn, MAX_EFFECT_CHANNELS, base, todo
1623 /* Process the samples for reverb. */
1624 for(c = 0;c < NUM_LINES;c++)
1626 /* Band-pass the incoming samples. Use the early output lines for
1627 * temp storage.
1629 ALfilterState_process(&State->Filter[c].Lp, early[0], afmt[c], todo);
1630 ALfilterState_process(&State->Filter[c].Hp, early[1], early[0], todo);
1632 /* Feed the initial delay line. */
1633 for(i = 0;i < todo;i++)
1634 DelayLineIn(&State->Delay, State->Offset+i, c, early[1][i]);
1637 if(UNLIKELY(fadeCount < FADE_SAMPLES))
1639 /* Generate early reflections. */
1640 EarlyReflection_Faded(State, todo, fade, early);
1642 /* Generate late reverb. */
1643 LateReverb_Faded(State, todo, fade, late);
1644 fade = minf(1.0f, fade + todo*FadeStep);
1646 else
1648 /* Generate early reflections. */
1649 EarlyReflection_Unfaded(State, todo, fade, early);
1651 /* Generate late reverb. */
1652 LateReverb_Unfaded(State, todo, fade, late);
1655 /* Step all delays forward. */
1656 State->Offset += todo;
1658 if(UNLIKELY(fadeCount < FADE_SAMPLES) && (fadeCount += todo) >= FADE_SAMPLES)
1660 /* Update the cross-fading delay line taps. */
1661 fadeCount = FADE_SAMPLES;
1662 fade = 1.0f;
1663 for(c = 0;c < NUM_LINES;c++)
1665 State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
1666 State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1];
1667 State->Early.Offset[c][0] = State->Early.Offset[c][1];
1668 State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
1669 State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1];
1670 State->Late.Offset[c][0] = State->Late.Offset[c][1];
1674 /* Mix the A-Format results to output, implicitly converting back to
1675 * B-Format.
1677 for(c = 0;c < NUM_LINES;c++)
1678 MixSamples(early[c], NumChannels, SamplesOut,
1679 State->Early.CurrentGain[c], State->Early.PanGain[c],
1680 SamplesToDo-base, base, todo
1682 for(c = 0;c < NUM_LINES;c++)
1683 MixSamples(late[c], NumChannels, SamplesOut,
1684 State->Late.CurrentGain[c], State->Late.PanGain[c],
1685 SamplesToDo-base, base, todo
1688 base += todo;
1690 State->FadeCount = fadeCount;
1694 typedef struct ALreverbStateFactory {
1695 DERIVE_FROM_TYPE(ALeffectStateFactory);
1696 } ALreverbStateFactory;
1698 static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory))
1700 ALreverbState *state;
1702 NEW_OBJ0(state, ALreverbState)();
1703 if(!state) return NULL;
1705 return STATIC_CAST(ALeffectState, state);
1708 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALreverbStateFactory);
1710 ALeffectStateFactory *ALreverbStateFactory_getFactory(void)
1712 static ALreverbStateFactory ReverbFactory = { { GET_VTABLE2(ALreverbStateFactory, ALeffectStateFactory) } };
1714 return STATIC_CAST(ALeffectStateFactory, &ReverbFactory);
1718 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1720 ALeffectProps *props = &effect->Props;
1721 switch(param)
1723 case AL_EAXREVERB_DECAY_HFLIMIT:
1724 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1725 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
1726 props->Reverb.DecayHFLimit = val;
1727 break;
1729 default:
1730 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1731 param);
1734 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1735 { ALeaxreverb_setParami(effect, context, param, vals[0]); }
1736 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1738 ALeffectProps *props = &effect->Props;
1739 switch(param)
1741 case AL_EAXREVERB_DENSITY:
1742 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1743 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
1744 props->Reverb.Density = val;
1745 break;
1747 case AL_EAXREVERB_DIFFUSION:
1748 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1749 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
1750 props->Reverb.Diffusion = val;
1751 break;
1753 case AL_EAXREVERB_GAIN:
1754 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1755 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
1756 props->Reverb.Gain = val;
1757 break;
1759 case AL_EAXREVERB_GAINHF:
1760 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1761 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
1762 props->Reverb.GainHF = val;
1763 break;
1765 case AL_EAXREVERB_GAINLF:
1766 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1767 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
1768 props->Reverb.GainLF = val;
1769 break;
1771 case AL_EAXREVERB_DECAY_TIME:
1772 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1773 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
1774 props->Reverb.DecayTime = val;
1775 break;
1777 case AL_EAXREVERB_DECAY_HFRATIO:
1778 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1779 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
1780 props->Reverb.DecayHFRatio = val;
1781 break;
1783 case AL_EAXREVERB_DECAY_LFRATIO:
1784 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1785 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
1786 props->Reverb.DecayLFRatio = val;
1787 break;
1789 case AL_EAXREVERB_REFLECTIONS_GAIN:
1790 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1791 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
1792 props->Reverb.ReflectionsGain = val;
1793 break;
1795 case AL_EAXREVERB_REFLECTIONS_DELAY:
1796 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1797 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
1798 props->Reverb.ReflectionsDelay = val;
1799 break;
1801 case AL_EAXREVERB_LATE_REVERB_GAIN:
1802 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1803 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
1804 props->Reverb.LateReverbGain = val;
1805 break;
1807 case AL_EAXREVERB_LATE_REVERB_DELAY:
1808 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1809 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
1810 props->Reverb.LateReverbDelay = val;
1811 break;
1813 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1814 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1815 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
1816 props->Reverb.AirAbsorptionGainHF = val;
1817 break;
1819 case AL_EAXREVERB_ECHO_TIME:
1820 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1821 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
1822 props->Reverb.EchoTime = val;
1823 break;
1825 case AL_EAXREVERB_ECHO_DEPTH:
1826 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1827 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
1828 props->Reverb.EchoDepth = val;
1829 break;
1831 case AL_EAXREVERB_MODULATION_TIME:
1832 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1833 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
1834 props->Reverb.ModulationTime = val;
1835 break;
1837 case AL_EAXREVERB_MODULATION_DEPTH:
1838 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1839 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
1840 props->Reverb.ModulationDepth = val;
1841 break;
1843 case AL_EAXREVERB_HFREFERENCE:
1844 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1845 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
1846 props->Reverb.HFReference = val;
1847 break;
1849 case AL_EAXREVERB_LFREFERENCE:
1850 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1851 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
1852 props->Reverb.LFReference = val;
1853 break;
1855 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1856 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1857 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
1858 props->Reverb.RoomRolloffFactor = val;
1859 break;
1861 default:
1862 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1863 param);
1866 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1868 ALeffectProps *props = &effect->Props;
1869 switch(param)
1871 case AL_EAXREVERB_REFLECTIONS_PAN:
1872 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1873 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
1874 props->Reverb.ReflectionsPan[0] = vals[0];
1875 props->Reverb.ReflectionsPan[1] = vals[1];
1876 props->Reverb.ReflectionsPan[2] = vals[2];
1877 break;
1878 case AL_EAXREVERB_LATE_REVERB_PAN:
1879 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1880 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
1881 props->Reverb.LateReverbPan[0] = vals[0];
1882 props->Reverb.LateReverbPan[1] = vals[1];
1883 props->Reverb.LateReverbPan[2] = vals[2];
1884 break;
1886 default:
1887 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1888 break;
1892 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1894 const ALeffectProps *props = &effect->Props;
1895 switch(param)
1897 case AL_EAXREVERB_DECAY_HFLIMIT:
1898 *val = props->Reverb.DecayHFLimit;
1899 break;
1901 default:
1902 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1903 param);
1906 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1907 { ALeaxreverb_getParami(effect, context, param, vals); }
1908 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1910 const ALeffectProps *props = &effect->Props;
1911 switch(param)
1913 case AL_EAXREVERB_DENSITY:
1914 *val = props->Reverb.Density;
1915 break;
1917 case AL_EAXREVERB_DIFFUSION:
1918 *val = props->Reverb.Diffusion;
1919 break;
1921 case AL_EAXREVERB_GAIN:
1922 *val = props->Reverb.Gain;
1923 break;
1925 case AL_EAXREVERB_GAINHF:
1926 *val = props->Reverb.GainHF;
1927 break;
1929 case AL_EAXREVERB_GAINLF:
1930 *val = props->Reverb.GainLF;
1931 break;
1933 case AL_EAXREVERB_DECAY_TIME:
1934 *val = props->Reverb.DecayTime;
1935 break;
1937 case AL_EAXREVERB_DECAY_HFRATIO:
1938 *val = props->Reverb.DecayHFRatio;
1939 break;
1941 case AL_EAXREVERB_DECAY_LFRATIO:
1942 *val = props->Reverb.DecayLFRatio;
1943 break;
1945 case AL_EAXREVERB_REFLECTIONS_GAIN:
1946 *val = props->Reverb.ReflectionsGain;
1947 break;
1949 case AL_EAXREVERB_REFLECTIONS_DELAY:
1950 *val = props->Reverb.ReflectionsDelay;
1951 break;
1953 case AL_EAXREVERB_LATE_REVERB_GAIN:
1954 *val = props->Reverb.LateReverbGain;
1955 break;
1957 case AL_EAXREVERB_LATE_REVERB_DELAY:
1958 *val = props->Reverb.LateReverbDelay;
1959 break;
1961 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1962 *val = props->Reverb.AirAbsorptionGainHF;
1963 break;
1965 case AL_EAXREVERB_ECHO_TIME:
1966 *val = props->Reverb.EchoTime;
1967 break;
1969 case AL_EAXREVERB_ECHO_DEPTH:
1970 *val = props->Reverb.EchoDepth;
1971 break;
1973 case AL_EAXREVERB_MODULATION_TIME:
1974 *val = props->Reverb.ModulationTime;
1975 break;
1977 case AL_EAXREVERB_MODULATION_DEPTH:
1978 *val = props->Reverb.ModulationDepth;
1979 break;
1981 case AL_EAXREVERB_HFREFERENCE:
1982 *val = props->Reverb.HFReference;
1983 break;
1985 case AL_EAXREVERB_LFREFERENCE:
1986 *val = props->Reverb.LFReference;
1987 break;
1989 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1990 *val = props->Reverb.RoomRolloffFactor;
1991 break;
1993 default:
1994 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1995 param);
1998 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2000 const ALeffectProps *props = &effect->Props;
2001 switch(param)
2003 case AL_EAXREVERB_REFLECTIONS_PAN:
2004 vals[0] = props->Reverb.ReflectionsPan[0];
2005 vals[1] = props->Reverb.ReflectionsPan[1];
2006 vals[2] = props->Reverb.ReflectionsPan[2];
2007 break;
2008 case AL_EAXREVERB_LATE_REVERB_PAN:
2009 vals[0] = props->Reverb.LateReverbPan[0];
2010 vals[1] = props->Reverb.LateReverbPan[1];
2011 vals[2] = props->Reverb.LateReverbPan[2];
2012 break;
2014 default:
2015 ALeaxreverb_getParamf(effect, context, param, vals);
2016 break;
2020 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
2022 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
2024 ALeffectProps *props = &effect->Props;
2025 switch(param)
2027 case AL_REVERB_DECAY_HFLIMIT:
2028 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
2029 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
2030 props->Reverb.DecayHFLimit = val;
2031 break;
2033 default:
2034 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
2037 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
2038 { ALreverb_setParami(effect, context, param, vals[0]); }
2039 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
2041 ALeffectProps *props = &effect->Props;
2042 switch(param)
2044 case AL_REVERB_DENSITY:
2045 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
2046 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
2047 props->Reverb.Density = val;
2048 break;
2050 case AL_REVERB_DIFFUSION:
2051 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
2052 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
2053 props->Reverb.Diffusion = val;
2054 break;
2056 case AL_REVERB_GAIN:
2057 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
2058 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
2059 props->Reverb.Gain = val;
2060 break;
2062 case AL_REVERB_GAINHF:
2063 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
2064 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
2065 props->Reverb.GainHF = val;
2066 break;
2068 case AL_REVERB_DECAY_TIME:
2069 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
2070 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
2071 props->Reverb.DecayTime = val;
2072 break;
2074 case AL_REVERB_DECAY_HFRATIO:
2075 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
2076 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
2077 props->Reverb.DecayHFRatio = val;
2078 break;
2080 case AL_REVERB_REFLECTIONS_GAIN:
2081 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
2082 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
2083 props->Reverb.ReflectionsGain = val;
2084 break;
2086 case AL_REVERB_REFLECTIONS_DELAY:
2087 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
2088 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
2089 props->Reverb.ReflectionsDelay = val;
2090 break;
2092 case AL_REVERB_LATE_REVERB_GAIN:
2093 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
2094 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
2095 props->Reverb.LateReverbGain = val;
2096 break;
2098 case AL_REVERB_LATE_REVERB_DELAY:
2099 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
2100 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
2101 props->Reverb.LateReverbDelay = val;
2102 break;
2104 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2105 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
2106 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
2107 props->Reverb.AirAbsorptionGainHF = val;
2108 break;
2110 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2111 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
2112 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
2113 props->Reverb.RoomRolloffFactor = val;
2114 break;
2116 default:
2117 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2120 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
2121 { ALreverb_setParamf(effect, context, param, vals[0]); }
2123 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
2125 const ALeffectProps *props = &effect->Props;
2126 switch(param)
2128 case AL_REVERB_DECAY_HFLIMIT:
2129 *val = props->Reverb.DecayHFLimit;
2130 break;
2132 default:
2133 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
2136 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
2137 { ALreverb_getParami(effect, context, param, vals); }
2138 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
2140 const ALeffectProps *props = &effect->Props;
2141 switch(param)
2143 case AL_REVERB_DENSITY:
2144 *val = props->Reverb.Density;
2145 break;
2147 case AL_REVERB_DIFFUSION:
2148 *val = props->Reverb.Diffusion;
2149 break;
2151 case AL_REVERB_GAIN:
2152 *val = props->Reverb.Gain;
2153 break;
2155 case AL_REVERB_GAINHF:
2156 *val = props->Reverb.GainHF;
2157 break;
2159 case AL_REVERB_DECAY_TIME:
2160 *val = props->Reverb.DecayTime;
2161 break;
2163 case AL_REVERB_DECAY_HFRATIO:
2164 *val = props->Reverb.DecayHFRatio;
2165 break;
2167 case AL_REVERB_REFLECTIONS_GAIN:
2168 *val = props->Reverb.ReflectionsGain;
2169 break;
2171 case AL_REVERB_REFLECTIONS_DELAY:
2172 *val = props->Reverb.ReflectionsDelay;
2173 break;
2175 case AL_REVERB_LATE_REVERB_GAIN:
2176 *val = props->Reverb.LateReverbGain;
2177 break;
2179 case AL_REVERB_LATE_REVERB_DELAY:
2180 *val = props->Reverb.LateReverbDelay;
2181 break;
2183 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2184 *val = props->Reverb.AirAbsorptionGainHF;
2185 break;
2187 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2188 *val = props->Reverb.RoomRolloffFactor;
2189 break;
2191 default:
2192 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2195 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2196 { ALreverb_getParamf(effect, context, param, vals); }
2198 DEFINE_ALEFFECT_VTABLE(ALreverb);