Add a slight bit of attenuation to the reverb output
[openal-soft.git] / Alc / effects / reverb.c
blob820ab70a2d57bd01f8409967bf968c83757a312b
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 "alError.h"
33 #include "mixer_defs.h"
35 /* This is the maximum number of samples processed for each inner loop
36 * iteration. */
37 #define MAX_UPDATE_SAMPLES 256
39 /* The number of samples used for cross-faded delay lines. This can be used
40 * to balance the compensation for abrupt line changes and attenuation due to
41 * minimally lengthed recursive lines. Try to keep this below the device
42 * update size.
44 #define FADE_SAMPLES 128
46 #ifdef __GNUC__
47 #define UNEXPECTED(x) __builtin_expect((bool)(x), 0)
48 #else
49 #define UNEXPECTED(x) (x)
50 #endif
52 static MixerFunc MixSamples = Mix_C;
53 static RowMixerFunc MixRowSamples = MixRow_C;
55 static alonce_flag mixfunc_inited = AL_ONCE_FLAG_INIT;
56 static void init_mixfunc(void)
58 MixSamples = SelectMixer();
59 MixRowSamples = SelectRowMixer();
62 typedef struct DelayLine {
63 /* The delay lines use sample lengths that are powers of 2 to allow the
64 * use of bit-masking instead of a modulus for wrapping.
66 ALsizei Mask;
67 ALfloat *Line;
68 } DelayLine;
70 typedef struct Allpass {
71 DelayLine Delay;
72 ALsizei Offset[2];
73 } Allpass;
75 typedef struct ALreverbState {
76 DERIVE_FROM_TYPE(ALeffectState);
78 ALboolean IsEax;
80 /* All delay lines are allocated as a single buffer to reduce memory
81 * fragmentation and management code.
83 ALfloat *SampleBuffer;
84 ALuint TotalSamples;
86 /* Master effect filters */
87 struct {
88 ALfilterState Lp;
89 ALfilterState Hp; /* EAX only */
90 } Filter[4];
92 struct {
93 /* Modulator delay lines. */
94 DelayLine Delay[4];
96 /* The vibrato time is tracked with an index over a modulus-wrapped
97 * range (in samples).
99 ALuint Index;
100 ALuint Range;
102 /* The depth of frequency change (also in samples) and its filter. */
103 ALfloat Depth;
104 ALfloat Coeff;
105 ALfloat Filter;
106 } Mod; /* EAX only */
108 /* Core delay line (early reflections and late reverb tap from this). */
109 DelayLine Delay;
111 /* Tap points for early reflection delay. */
112 ALsizei EarlyDelayTap[4][2];
113 ALfloat EarlyDelayCoeff[4];
115 /* Tap points for late reverb feed and delay. */
116 ALsizei LateFeedTap;
117 ALsizei LateDelayTap[4][2];
119 /* The feed-back and feed-forward all-pass coefficient. */
120 ALfloat ApFeedCoeff;
122 /* Coefficients for the all-pass and line scattering matrices. */
123 ALfloat MixX;
124 ALfloat MixY;
126 struct {
127 /* A Gerzon vector all-pass filter is used to simulate initial
128 * diffusion. The spread from this filter also helps smooth out the
129 * reverb tail.
131 Allpass Ap[4];
133 /* Echo lines are used to complete the second half of the early
134 * reflections.
136 DelayLine Delay[4];
137 ALsizei Offset[4][2];
138 ALfloat Coeff[4];
140 /* The gain for each output channel based on 3D panning. */
141 ALfloat CurrentGain[4][MAX_OUTPUT_CHANNELS];
142 ALfloat PanGain[4][MAX_OUTPUT_CHANNELS];
143 } Early;
145 struct {
146 /* Attenuation to compensate for the modal density and decay rate of
147 * the late lines.
149 ALfloat DensityGain;
151 /* Recursive delay lines are used fill in the reverb tail. */
152 DelayLine Delay[4];
153 ALsizei Offset[4][2];
155 /* T60 decay filters are used to simulate absorption. */
156 struct {
157 ALfloat LFCoeffs[3];
158 ALfloat HFCoeffs[3];
159 ALfloat MidCoeff;
160 /* The LF and HF filters keep a state of the last input and last
161 * output sample.
163 ALfloat States[2][2];
164 } Filters[4];
166 /* A Gerzon vector all-pass filter is used to simulate diffusion. */
167 Allpass Ap[4];
169 /* The gain for each output channel based on 3D panning. */
170 ALfloat CurrentGain[4][MAX_OUTPUT_CHANNELS];
171 ALfloat PanGain[4][MAX_OUTPUT_CHANNELS];
172 } Late;
174 /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
175 ALsizei FadeCount;
177 /* The current write offset for all delay lines. */
178 ALsizei Offset;
180 /* Temporary storage used when processing. */
181 alignas(16) ALfloat AFormatSamples[4][MAX_UPDATE_SAMPLES];
182 alignas(16) ALfloat ReverbSamples[4][MAX_UPDATE_SAMPLES];
183 alignas(16) ALfloat EarlySamples[4][MAX_UPDATE_SAMPLES];
184 } ALreverbState;
186 static ALvoid ALreverbState_Destruct(ALreverbState *State);
187 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device);
188 static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
189 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
190 DECLARE_DEFAULT_ALLOCATORS(ALreverbState)
192 DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);
194 static void ALreverbState_Construct(ALreverbState *state)
196 ALsizei i, j;
198 ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
199 SET_VTABLE2(ALreverbState, ALeffectState, state);
201 state->IsEax = AL_FALSE;
203 state->TotalSamples = 0;
204 state->SampleBuffer = NULL;
206 for(i = 0;i < 4;i++)
208 ALfilterState_clear(&state->Filter[i].Lp);
209 ALfilterState_clear(&state->Filter[i].Hp);
211 state->Mod.Delay[i].Mask = 0;
212 state->Mod.Delay[i].Line = NULL;
215 state->Mod.Index = 0;
216 state->Mod.Range = 1;
217 state->Mod.Depth = 0.0f;
218 state->Mod.Coeff = 0.0f;
219 state->Mod.Filter = 0.0f;
221 state->Delay.Mask = 0;
222 state->Delay.Line = NULL;
224 for(i = 0;i < 4;i++)
226 state->EarlyDelayTap[i][0] = 0;
227 state->EarlyDelayTap[i][1] = 0;
228 state->EarlyDelayCoeff[i] = 0.0f;
231 state->LateFeedTap = 0;
233 for(i = 0;i < 4;i++)
235 state->LateDelayTap[i][0] = 0;
236 state->LateDelayTap[i][1] = 0;
239 state->ApFeedCoeff = 0.0f;
240 state->MixX = 0.0f;
241 state->MixY = 0.0f;
243 for(i = 0;i < 4;i++)
245 state->Early.Ap[i].Delay.Mask = 0;
246 state->Early.Ap[i].Delay.Line = NULL;
247 state->Early.Ap[i].Offset[0] = 0;
248 state->Early.Ap[i].Offset[1] = 0;
249 state->Early.Delay[i].Mask = 0;
250 state->Early.Delay[i].Line = NULL;
251 state->Early.Offset[i][0] = 0;
252 state->Early.Offset[i][1] = 0;
253 state->Early.Coeff[i] = 0.0f;
256 state->Late.DensityGain = 0.0f;
258 for(i = 0;i < 4;i++)
260 state->Late.Ap[i].Delay.Mask = 0;
261 state->Late.Ap[i].Delay.Line = NULL;
262 state->Late.Ap[i].Offset[0] = 0;
263 state->Late.Ap[i].Offset[1] = 0;
265 state->Late.Delay[i].Mask = 0;
266 state->Late.Delay[i].Line = NULL;
267 state->Late.Offset[i][0] = 0;
268 state->Late.Offset[i][1] = 0;
270 for(j = 0;j < 3;j++)
272 state->Late.Filters[i].LFCoeffs[j] = 0.0f;
273 state->Late.Filters[i].HFCoeffs[j] = 0.0f;
275 state->Late.Filters[i].MidCoeff = 0.0f;
277 state->Late.Filters[i].States[0][0] = 0.0f;
278 state->Late.Filters[i].States[0][1] = 0.0f;
279 state->Late.Filters[i].States[1][0] = 0.0f;
280 state->Late.Filters[i].States[1][1] = 0.0f;
283 for(i = 0;i < 4;i++)
285 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
287 state->Early.CurrentGain[i][j] = 0.0f;
288 state->Early.PanGain[i][j] = 0.0f;
289 state->Late.CurrentGain[i][j] = 0.0f;
290 state->Late.PanGain[i][j] = 0.0f;
294 state->FadeCount = 0;
295 state->Offset = 0;
298 static ALvoid ALreverbState_Destruct(ALreverbState *State)
300 al_free(State->SampleBuffer);
301 State->SampleBuffer = NULL;
303 ALeffectState_Destruct(STATIC_CAST(ALeffectState,State));
306 /* The B-Format to A-Format conversion matrix. The arrangement of rows is
307 * deliberately chosen to align the resulting lines to their spatial opposites
308 * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
309 * back left). It's not quite opposite, since the A-Format results in a
310 * tetrahedron, but it's close enough. Should the model be extended to 8-lines
311 * in the future, true opposites can be used.
313 static const aluMatrixf B2A = {{
314 { 0.288675134595f, 0.288675134595f, 0.288675134595f, 0.288675134595f },
315 { 0.288675134595f, -0.288675134595f, -0.288675134595f, 0.288675134595f },
316 { 0.288675134595f, 0.288675134595f, -0.288675134595f, -0.288675134595f },
317 { 0.288675134595f, -0.288675134595f, 0.288675134595f, -0.288675134595f }
320 /* Converts A-Format to B-Format (transposed). */
321 static const aluMatrixf A2B = {{
322 { 0.8660254038f, 0.8660254038f, 0.8660254038f, 0.8660254038f },
323 { 0.8660254038f, -0.8660254038f, -0.8660254038f, 0.8660254038f },
324 { 0.8660254038f, 0.8660254038f, -0.8660254038f, -0.8660254038f },
325 { 0.8660254038f, -0.8660254038f, 0.8660254038f, -0.8660254038f }
328 static const ALfloat FadeStep = 1.0f / FADE_SAMPLES;
330 /* This is a user config option for modifying the overall output of the reverb
331 * effect.
333 ALfloat ReverbBoost = 1.0f;
335 /* Specifies whether to use a standard reverb effect in place of EAX reverb (no
336 * high-pass, modulation, or echo).
338 ALboolean EmulateEAXReverb = AL_FALSE;
340 /* This coefficient is used to define the maximum frequency range controlled
341 * by the modulation depth. The current value of 0.025 will allow it to
342 * swing from 0.975x to 1.025x. This value must be below 1. At 1 it will
343 * cause the sampler to stall on the downswing, and above 1 it will cause it
344 * to sample backwards.
346 static const ALfloat MODULATION_DEPTH_COEFF = 0.025f;
348 /* A filter is used to avoid the terrible distortion caused by changing
349 * modulation time and/or depth. To be consistent across different sample
350 * rates, the coefficient must be raised to a constant divided by the sample
351 * rate: coeff^(constant / rate).
353 static const ALfloat MODULATION_FILTER_COEFF = 0.048f;
354 static const ALfloat MODULATION_FILTER_CONST = 100000.0f;
356 /* The all-pass and delay lines have a variable length dependent on the
357 * effect's density parameter. The resulting density multiplier is:
359 * multiplier = 1 + (density * LINE_MULTIPLIER)
361 * Thus the line multiplier below will result in a maximum density multiplier
362 * of 10.
364 static const ALfloat LINE_MULTIPLIER = 9.0f;
366 /* All delay line lengths are specified in seconds.
368 * To approximate early reflections, we break them up into primary (those
369 * arriving from the same direction as the source) and secondary (those
370 * arriving from the opposite direction).
372 * The early taps decorrelate the 4-channel signal to approximate an average
373 * room response for the primary reflections after the initial early delay.
375 * Given an average room dimension (d_a) and the speed of sound (c) we can
376 * calculate the average reflection delay (r_a) regardless of listener and
377 * source positions as:
379 * r_a = d_a / c
380 * c = 343.3
382 * This can extended to finding the average difference (r_d) between the
383 * maximum (r_1) and minimum (r_0) reflection delays:
385 * r_0 = 2 / 3 r_a
386 * = r_a - r_d / 2
387 * = r_d
388 * r_1 = 4 / 3 r_a
389 * = r_a + r_d / 2
390 * = 2 r_d
391 * r_d = 2 / 3 r_a
392 * = r_1 - r_0
394 * As can be determined by integrating the 1D model with a source (s) and
395 * listener (l) positioned across the dimension of length (d_a):
397 * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
399 * The initial taps (T_(i=0)^N) are then specified by taking a power series
400 * that ranges between r_0 and half of r_1 less r_0:
402 * R_i = 2^(i / (2 N - 1)) r_d
403 * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
404 * = r_0 + T_i
405 * T_i = R_i - r_0
406 * = (2^(i / (2 N - 1)) - 1) r_d
408 * Assuming an average of 5m (up to 50m with the density multiplier), we get
409 * the following taps:
411 static const ALfloat EARLY_TAP_LENGTHS[4] =
413 0.000000e+0f, 1.010676e-3f, 2.126553e-3f, 3.358580e-3f
416 /* The early all-pass filter lengths are based on the early tap lengths:
418 * A_i = R_i / a
420 * Where a is the approximate maximum all-pass cycle limit (20).
422 static const ALfloat EARLY_ALLPASS_LENGTHS[4] =
424 4.854840e-4f, 5.360178e-4f, 5.918117e-4f, 6.534130e-4f
427 /* The early delay lines are used to transform the primary reflections into
428 * the secondary reflections. The A-format is arranged in such a way that
429 * the channels/lines are spatially opposite:
431 * C_i is opposite C_(N-i-1)
433 * The delays of the two opposing reflections (R_i and O_i) from a source
434 * anywhere along a particular dimension always sum to twice its full delay:
436 * 2 r_a = R_i + O_i
438 * With that in mind we can determine the delay between the two reflections
439 * and thus specify our early line lengths (L_(i=0)^N) using:
441 * O_i = 2 r_a - R_(N-i-1)
442 * L_i = O_i - R_(N-i-1)
443 * = 2 (r_a - R_(N-i-1))
444 * = 2 (r_a - T_(N-i-1) - r_0)
445 * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
447 * Using an average dimension of 5m, we get:
449 static const ALfloat EARLY_LINE_LENGTHS[4] =
451 2.992520e-3f, 5.456575e-3f, 7.688329e-3f, 9.709681e-3f
454 /* The late all-pass filter lengths are based on the late line lengths:
456 * A_i = (5 / 3) L_i / r_1
458 static const ALfloat LATE_ALLPASS_LENGTHS[4] =
460 8.091400e-4f, 1.019453e-3f, 1.407968e-3f, 1.618280e-3f
463 /* The late lines are used to approximate the decaying cycle of recursive
464 * late reflections.
466 * Splitting the lines in half, we start with the shortest reflection paths
467 * (L_(i=0)^(N/2)):
469 * L_i = 2^(i / (N - 1)) r_d
471 * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
473 * L_i = 2 r_a - L_(i-N/2)
474 * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
476 * For our 5m average room, we get:
478 static const ALfloat LATE_LINE_LENGTHS[4] =
480 9.709681e-3f, 1.223343e-2f, 1.689561e-2f, 1.941936e-2f
483 /* HACK: Workaround for a modff bug in 32-bit Windows, which attempts to write
484 * a 64-bit double to the 32-bit float parameter.
486 #if defined(_WIN32) && !defined (_M_X64) && !defined(_M_ARM)
487 static inline float hack_modff(float x, float *y)
489 double di;
490 double df = modf((double)x, &di);
491 *y = (float)di;
492 return (float)df;
494 #define modff hack_modff
495 #endif
497 /**************************************
498 * Device Update *
499 **************************************/
501 /* Given the allocated sample buffer, this function updates each delay line
502 * offset.
504 static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay)
506 Delay->Line = &sampleBuffer[(ptrdiff_t)Delay->Line];
509 /* Calculate the length of a delay line and store its mask and offset. */
510 static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALuint frequency,
511 const ALuint extra, DelayLine *Delay)
513 ALuint samples;
515 /* All line lengths are powers of 2, calculated from their lengths, with
516 * an additional sample in case of rounding errors.
518 samples = fastf2u(length*frequency) + extra;
519 samples = NextPowerOf2(samples + 1);
521 /* All lines share a single sample buffer. */
522 Delay->Mask = samples - 1;
523 Delay->Line = (ALfloat*)offset;
525 /* Return the sample count for accumulation. */
526 return samples;
529 /* Calculates the delay line metrics and allocates the shared sample buffer
530 * for all lines given the sample rate (frequency). If an allocation failure
531 * occurs, it returns AL_FALSE.
533 static ALboolean AllocLines(const ALuint frequency, ALreverbState *State)
535 ALuint totalSamples, i;
536 ALfloat multiplier, length;
538 /* All delay line lengths are calculated to accomodate the full range of
539 * lengths given their respective paramters.
541 totalSamples = 0;
543 /* The modulator's line length is calculated from the maximum modulation
544 * time and depth coefficient, and halfed for the low-to-high frequency
545 * swing. An additional sample is added to keep it stable when there is no
546 * modulation.
548 length = (AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f);
549 for(i = 0;i < 4;i++)
550 totalSamples += CalcLineLength(length, totalSamples, frequency, 1,
551 &State->Mod.Delay[i]);
553 /* The main delay length includes the maximum early reflection delay, the
554 * largest early tap width, the maximum late reverb delay, and the
555 * largest late tap width. Finally, it must also be extended by the
556 * update size (MAX_UPDATE_SAMPLES*4) for block processing.
558 multiplier = 1.0f + LINE_MULTIPLIER;
559 length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
560 EARLY_TAP_LENGTHS[3]*multiplier +
561 AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
562 (LATE_LINE_LENGTHS[3] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
563 /* Multiply length by 4, since we're storing 4 interleaved channels in the
564 * main delay line.
566 totalSamples += CalcLineLength(length*4, totalSamples, frequency,
567 MAX_UPDATE_SAMPLES*4, &State->Delay);
569 /* The early all-pass lines. */
570 for(i = 0;i < 4;i++)
572 length = EARLY_ALLPASS_LENGTHS[i] * multiplier;
573 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
574 &State->Early.Ap[i].Delay);
577 /* The early reflection lines. */
578 for(i = 0;i < 4;i++)
580 length = EARLY_LINE_LENGTHS[i] * multiplier;
581 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
582 &State->Early.Delay[i]);
585 /* The late vector all-pass lines. */
586 for(i = 0;i < 4;i++)
588 length = LATE_ALLPASS_LENGTHS[i] * multiplier;
589 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
590 &State->Late.Ap[i].Delay);
593 /* The late delay lines are calculated from the larger of the maximum
594 * density line length or the maximum echo time.
596 for(i = 0;i < 4;i++)
598 length = maxf(AL_EAXREVERB_MAX_ECHO_TIME, LATE_LINE_LENGTHS[i] * multiplier);
599 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
600 &State->Late.Delay[i]);
603 if(totalSamples != State->TotalSamples)
605 ALfloat *newBuffer;
607 TRACE("New reverb buffer length: %u samples\n", totalSamples);
608 newBuffer = al_calloc(16, sizeof(ALfloat) * totalSamples);
609 if(!newBuffer) return AL_FALSE;
611 al_free(State->SampleBuffer);
612 State->SampleBuffer = newBuffer;
613 State->TotalSamples = totalSamples;
616 /* Update all delays to reflect the new sample buffer. */
617 RealizeLineOffset(State->SampleBuffer, &State->Delay);
618 for(i = 0;i < 4;i++)
620 RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay[i]);
622 RealizeLineOffset(State->SampleBuffer, &State->Early.Ap[i].Delay);
623 RealizeLineOffset(State->SampleBuffer, &State->Early.Delay[i]);
625 RealizeLineOffset(State->SampleBuffer, &State->Late.Ap[i].Delay);
626 RealizeLineOffset(State->SampleBuffer, &State->Late.Delay[i]);
629 /* Clear the sample buffer. */
630 for(i = 0;i < State->TotalSamples;i++)
631 State->SampleBuffer[i] = 0.0f;
633 return AL_TRUE;
636 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
638 ALuint frequency = Device->Frequency, i;
639 ALfloat multiplier;
641 /* Allocate the delay lines. */
642 if(!AllocLines(frequency, State))
643 return AL_FALSE;
645 /* Calculate the modulation filter coefficient. Notice that the exponent
646 * is calculated given the current sample rate. This ensures that the
647 * resulting filter response over time is consistent across all sample
648 * rates.
650 State->Mod.Coeff = powf(MODULATION_FILTER_COEFF,
651 MODULATION_FILTER_CONST / frequency);
653 multiplier = 1.0f + LINE_MULTIPLIER;
655 /* The late feed taps are set a fixed position past the latest delay tap. */
656 for(i = 0;i < 4;i++)
657 State->LateFeedTap = fastf2u((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
658 EARLY_TAP_LENGTHS[3]*multiplier) *
659 frequency);
661 return AL_TRUE;
664 /**************************************
665 * Effect Update *
666 **************************************/
668 /* Calculate a decay coefficient given the length of each cycle and the time
669 * until the decay reaches -60 dB.
671 static inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
673 return powf(0.001f/*-60 dB*/, length/decayTime);
676 /* Calculate a decay length from a coefficient and the time until the decay
677 * reaches -60 dB.
679 static inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
681 return log10f(coeff) * decayTime / log10f(0.001f)/*-60 dB*/;
684 /* Calculate an attenuation to be applied to the input of any echo models to
685 * compensate for modal density and decay time.
687 static inline ALfloat CalcDensityGain(const ALfloat a)
689 /* The energy of a signal can be obtained by finding the area under the
690 * squared signal. This takes the form of Sum(x_n^2), where x is the
691 * amplitude for the sample n.
693 * Decaying feedback matches exponential decay of the form Sum(a^n),
694 * where a is the attenuation coefficient, and n is the sample. The area
695 * under this decay curve can be calculated as: 1 / (1 - a).
697 * Modifying the above equation to find the area under the squared curve
698 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
699 * calculated by inverting the square root of this approximation,
700 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
702 return sqrtf(1.0f - a*a);
705 /* Calculate the scattering matrix coefficients given a diffusion factor. */
706 static inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
708 ALfloat n, t;
710 /* The matrix is of order 4, so n is sqrt(4 - 1). */
711 n = sqrtf(3.0f);
712 t = diffusion * atanf(n);
714 /* Calculate the first mixing matrix coefficient. */
715 *x = cosf(t);
716 /* Calculate the second mixing matrix coefficient. */
717 *y = sinf(t) / n;
720 /* Calculate the limited HF ratio for use with the late reverb low-pass
721 * filters.
723 static ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
724 const ALfloat decayTime)
726 ALfloat limitRatio;
728 /* Find the attenuation due to air absorption in dB (converting delay
729 * time to meters using the speed of sound). Then reversing the decay
730 * equation, solve for HF ratio. The delay length is cancelled out of
731 * the equation, so it can be calculated once for all lines.
733 limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) *
734 SPEEDOFSOUNDMETRESPERSEC);
735 /* Using the limit calculated above, apply the upper bound to the HF
736 * ratio. Also need to limit the result to a minimum of 0.1, just like
737 * the HF ratio parameter.
739 return clampf(limitRatio, 0.1f, hfRatio);
742 /* Calculates the first-order high-pass coefficients following the I3DL2
743 * reference model. This is the transfer function:
745 * 1 - z^-1
746 * H(z) = p ------------
747 * 1 - p z^-1
749 * And this is the I3DL2 coefficient calculation given gain (g) and reference
750 * angular frequency (w):
753 * p = ------------------------------------------------------
754 * g cos(w) + sqrt((cos(w) - 1) (g^2 cos(w) + g^2 - 2))
756 * The coefficient is applied to the partial differential filter equation as:
758 * c_0 = p
759 * c_1 = -p
760 * c_2 = p
761 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
764 static inline void CalcHighpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
766 ALfloat g, g2, cw, p;
768 if(gain >= 1.0f)
770 coeffs[0] = 1.0f;
771 coeffs[1] = 0.0f;
772 coeffs[2] = 0.0f;
774 return;
777 g = maxf(0.001f, gain);
778 g2 = g * g;
779 cw = cosf(w);
780 p = g / (g*cw + sqrt((cw - 1.0f) * (g2*cw + g2 - 2.0f)));
782 coeffs[0] = p;
783 coeffs[1] = -p;
784 coeffs[2] = p;
787 /* Calculates the first-order low-pass coefficients following the I3DL2
788 * reference model. This is the transfer function:
790 * (1 - a) z^0
791 * H(z) = ----------------
792 * 1 z^0 - a z^-1
794 * And this is the I3DL2 coefficient calculation given gain (g) and reference
795 * angular frequency (w):
797 * 1 - g^2 cos(w) - sqrt(2 g^2 (1 - cos(w)) - g^4 (1 - cos(w)^2))
798 * a = ----------------------------------------------------------------
799 * 1 - g^2
801 * The coefficient is applied to the partial differential filter equation as:
803 * c_0 = 1 - a
804 * c_1 = 0
805 * c_2 = a
806 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
809 static inline void CalcLowpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
811 ALfloat g, g2, cw, a;
813 if(gain >= 1.0f)
815 coeffs[0] = 1.0f;
816 coeffs[1] = 0.0f;
817 coeffs[2] = 0.0f;
819 return;
822 /* Be careful with gains < 0.001, as that causes the coefficient
823 * to head towards 1, which will flatten the signal. */
824 g = maxf(0.001f, gain);
825 g2 = g * g;
826 cw = cosf(w);
827 a = (1.0f - g2*cw - sqrtf((2.0f*g2*(1.0f - cw)) - g2*g2*(1.0f - cw*cw))) /
828 (1.0f - g2);
830 coeffs[0] = 1.0f - a;
831 coeffs[1] = 0.0f;
832 coeffs[2] = a;
835 /* Calculates the first-order low-shelf coefficients. The shelf filters are
836 * used in place of low/high-pass filters to preserve the mid-band. This is
837 * the transfer function:
839 * a_0 + a_1 z^-1
840 * H(z) = ----------------
841 * 1 + b_1 z^-1
843 * And these are the coefficient calculations given cut gain (g) and a center
844 * angular frequency (w):
846 * sin(0.5 (pi - w) - 0.25 pi)
847 * p = -----------------------------
848 * sin(0.5 (pi - w) + 0.25 pi)
850 * g + 1 g + 1
851 * a = ------- + sqrt((-------)^2 - 1)
852 * g - 1 g - 1
854 * 1 + g + (1 - g) a
855 * b_0 = -------------------
858 * 1 - g + (1 + g) a
859 * b_1 = -------------------
862 * The coefficients are applied to the partial differential filter equation
863 * as:
865 * b_0 + p b_1
866 * c_0 = -------------
867 * 1 + p a
869 * -(b_1 + p b_0)
870 * c_1 = ----------------
871 * 1 + p a
873 * p + a
874 * c_2 = ---------
875 * 1 + p a
877 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
880 static inline void CalcLowShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
882 ALfloat g, rw, p, n;
883 ALfloat alpha, beta0, beta1;
885 if(gain >= 1.0f)
887 coeffs[0] = 1.0f;
888 coeffs[1] = 0.0f;
889 coeffs[2] = 0.0f;
891 return;
894 g = maxf(0.001f, gain);
895 rw = F_PI - w;
896 p = sinf(0.5f*rw - 0.25f*F_PI) / sinf(0.5f*rw + 0.25f*F_PI);
897 n = (g + 1.0f) / (g - 1.0f);
898 alpha = n + sqrtf(n*n - 1.0f);
899 beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f;
900 beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f;
902 coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha);
903 coeffs[1] = -(beta1 + p*beta0) / (1.0f + p*alpha);
904 coeffs[2] = (p + alpha) / (1.0f + p*alpha);
907 /* Calculates the first-order high-shelf coefficients. The shelf filters are
908 * used in place of low/high-pass filters to preserve the mid-band. This is
909 * the transfer function:
911 * a_0 + a_1 z^-1
912 * H(z) = ----------------
913 * 1 + b_1 z^-1
915 * And these are the coefficient calculations given cut gain (g) and a center
916 * angular frequency (w):
918 * sin(0.5 w - 0.25 pi)
919 * p = ----------------------
920 * sin(0.5 w + 0.25 pi)
922 * g + 1 g + 1
923 * a = ------- + sqrt((-------)^2 - 1)
924 * g - 1 g - 1
926 * 1 + g + (1 - g) a
927 * b_0 = -------------------
930 * 1 - g + (1 + g) a
931 * b_1 = -------------------
934 * The coefficients are applied to the partial differential filter equation
935 * as:
937 * b_0 + p b_1
938 * c_0 = -------------
939 * 1 + p a
941 * b_1 + p b_0
942 * c_1 = -------------
943 * 1 + p a
945 * -(p + a)
946 * c_2 = ----------
947 * 1 + p a
949 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
952 static inline void CalcHighShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
954 ALfloat g, p, n;
955 ALfloat alpha, beta0, beta1;
957 if(gain >= 1.0f)
959 coeffs[0] = 1.0f;
960 coeffs[1] = 0.0f;
961 coeffs[2] = 0.0f;
963 return;
966 g = maxf(0.001f, gain);
967 p = sinf(0.5f*w - 0.25f*F_PI) / sinf(0.5f*w + 0.25f*F_PI);
968 n = (g + 1.0f) / (g - 1.0f);
969 alpha = n + sqrtf(n*n - 1.0f);
970 beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f;
971 beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f;
973 coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha);
974 coeffs[1] = (beta1 + p*beta0) / (1.0f + p*alpha);
975 coeffs[2] = -(p + alpha) / (1.0f + p*alpha);
978 /* Calculates the 3-band T60 damping coefficients for a particular delay line
979 * of specified length using a combination of two low/high-pass/shelf or
980 * pass-through filter sections (producing 3 coefficients each) and a general
981 * gain (7th coefficient) given decay times for each band split at two (LF/
982 * HF) reference frequencies (w).
984 static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime,
985 const ALfloat mfDecayTime, const ALfloat hfDecayTime,
986 const ALfloat lfW, const ALfloat hfW, ALfloat lfcoeffs[3],
987 ALfloat hfcoeffs[3], ALfloat *midcoeff)
989 ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime);
990 ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime);
991 ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime);
993 if(lfGain < mfGain)
995 if(mfGain < hfGain)
997 CalcLowShelfCoeffs(mfGain / hfGain, hfW, lfcoeffs);
998 CalcHighpassCoeffs(lfGain / mfGain, lfW, hfcoeffs);
999 *midcoeff = hfGain;
1001 else if(mfGain > hfGain)
1003 CalcHighpassCoeffs(lfGain / mfGain, lfW, lfcoeffs);
1004 CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs);
1005 *midcoeff = mfGain;
1007 else
1009 lfcoeffs[0] = 1.0f;
1010 lfcoeffs[1] = 0.0f;
1011 lfcoeffs[2] = 0.0f;
1012 CalcHighpassCoeffs(lfGain / mfGain, lfW, hfcoeffs);
1013 *midcoeff = mfGain;
1016 else if(lfGain > mfGain)
1018 if(mfGain < hfGain)
1020 double hg = mfGain / lfGain;
1021 double lg = mfGain / hfGain;
1023 CalcHighShelfCoeffs(hg, lfW, lfcoeffs);
1024 CalcLowShelfCoeffs(lg, hfW, hfcoeffs);
1025 *midcoeff = maxf(lfGain, hfGain) / maxf(hg, lg);
1027 else if(mfGain > hfGain)
1029 CalcHighShelfCoeffs(mfGain / lfGain, lfW, lfcoeffs);
1030 CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs);
1031 *midcoeff = lfGain;
1033 else
1035 lfcoeffs[0] = 1.0f;
1036 lfcoeffs[1] = 0.0f;
1037 lfcoeffs[2] = 0.0f;
1038 CalcHighShelfCoeffs(mfGain / lfGain, lfW, hfcoeffs);
1039 *midcoeff = lfGain;
1042 else
1044 lfcoeffs[0] = 1.0f;
1045 lfcoeffs[1] = 0.0f;
1046 lfcoeffs[2] = 0.0f;
1048 if(mfGain < hfGain)
1050 CalcLowShelfCoeffs(mfGain / hfGain, hfW, hfcoeffs);
1051 *midcoeff = hfGain;
1053 else if(mfGain > hfGain)
1055 CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs);
1056 *midcoeff = mfGain;
1058 else
1060 hfcoeffs[3] = 1.0f;
1061 hfcoeffs[4] = 0.0f;
1062 hfcoeffs[5] = 0.0f;
1063 *midcoeff = mfGain;
1068 /* Update the EAX modulation index, range, and depth. Keep in mind that this
1069 * kind of vibrato is additive and not multiplicative as one may expect. The
1070 * downswing will sound stronger than the upswing.
1072 static ALvoid UpdateModulator(const ALfloat modTime, const ALfloat modDepth, const ALuint frequency, ALreverbState *State)
1074 ALuint range;
1076 /* Modulation is calculated in two parts.
1078 * The modulation time effects the sinus applied to the change in
1079 * frequency. An index out of the current time range (both in samples)
1080 * is incremented each sample. The range is bound to a reasonable
1081 * minimum (1 sample) and when the timing changes, the index is rescaled
1082 * to the new range (to keep the sinus consistent).
1084 range = maxu(fastf2u(modTime*frequency), 1);
1085 State->Mod.Index = (ALuint)(State->Mod.Index * (ALuint64)range /
1086 State->Mod.Range);
1087 State->Mod.Range = range;
1089 /* The modulation depth effects the amount of frequency change over the
1090 * range of the sinus. It needs to be scaled by the modulation time so
1091 * that a given depth produces a consistent change in frequency over all
1092 * ranges of time. Since the depth is applied to a sinus value, it needs
1093 * to be halfed once for the sinus range and again for the sinus swing
1094 * in time (half of it is spent decreasing the frequency, half is spent
1095 * increasing it).
1097 State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f /
1098 2.0f * frequency;
1101 /* Update the offsets for the main effect delay line. */
1102 static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ALreverbState *State)
1104 ALfloat multiplier, length;
1105 ALuint i;
1107 multiplier = 1.0f + density*LINE_MULTIPLIER;
1109 /* Early reflection taps are decorrelated by means of an average room
1110 * reflection approximation described above the definition of the taps.
1111 * This approximation is linear and so the above density multiplier can
1112 * be applied to adjust the width of the taps. A single-band decay
1113 * coefficient is applied to simulate initial attenuation and absorption.
1115 * Late reverb taps are based on the late line lengths to allow a zero-
1116 * delay path and offsets that would continue the propagation naturally
1117 * into the late lines.
1119 for(i = 0;i < 4;i++)
1121 length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
1122 State->EarlyDelayTap[i][1] = fastf2u(length * frequency);
1124 length = EARLY_TAP_LENGTHS[i]*multiplier;
1125 State->EarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime);
1127 length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
1128 State->LateDelayTap[i][1] = State->LateFeedTap + fastf2u(length * frequency);
1132 /* Update the early reflection line lengths and gain coefficients. */
1133 static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat decayTime, const ALuint frequency, ALreverbState *State)
1135 ALfloat multiplier, length;
1136 ALsizei i;
1138 multiplier = 1.0f + density*LINE_MULTIPLIER;
1140 for(i = 0;i < 4;i++)
1142 /* Calculate the length (in seconds) of each all-pass line. */
1143 length = EARLY_ALLPASS_LENGTHS[i] * multiplier;
1145 /* Calculate the delay offset for each all-pass line. */
1146 State->Early.Ap[i].Offset[1] = fastf2u(length * frequency);
1148 /* Calculate the length (in seconds) of each delay line. */
1149 length = EARLY_LINE_LENGTHS[i] * multiplier;
1151 /* Calculate the delay offset for each delay line. */
1152 State->Early.Offset[i][1] = fastf2u(length * frequency);
1154 /* Calculate the gain (coefficient) for each line. */
1155 State->Early.Coeff[i] = CalcDecayCoeff(length, decayTime);
1159 /* Update the late reverb line lengths and T60 coefficients. */
1160 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, ALreverbState *State)
1162 ALfloat multiplier, length, bandWeights[3];
1163 ALsizei i;
1165 /* To compensate for changes in modal density and decay time of the late
1166 * reverb signal, the input is attenuated based on the maximal energy of
1167 * the outgoing signal. This approximation is used to keep the apparent
1168 * energy of the signal equal for all ranges of density and decay time.
1170 * The average length of the delay lines is used to calculate the
1171 * attenuation coefficient.
1173 multiplier = 1.0f + density*LINE_MULTIPLIER;
1174 length = (LATE_LINE_LENGTHS[0] + LATE_LINE_LENGTHS[1] +
1175 LATE_LINE_LENGTHS[2] + LATE_LINE_LENGTHS[3]) / 4.0f * multiplier;
1176 /* Include the echo transformation (see below). */
1177 length = lerp(length, echoTime, echoDepth);
1178 length += (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
1179 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f * multiplier;
1180 /* The density gain calculation uses an average decay time weighted by
1181 * approximate bandwidth. This attempts to compensate for losses of
1182 * energy that reduce decay time due to scattering into highly attenuated
1183 * bands.
1185 bandWeights[0] = lfW;
1186 bandWeights[1] = hfW - lfW;
1187 bandWeights[2] = F_TAU - hfW;
1188 State->Late.DensityGain = CalcDensityGain(
1189 CalcDecayCoeff(length, (bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime +
1190 bandWeights[2]*hfDecayTime) / F_TAU)
1193 for(i = 0;i < 4;i++)
1195 /* Calculate the length (in seconds) of each all-pass line. */
1196 length = LATE_ALLPASS_LENGTHS[i] * multiplier;
1198 /* Calculate the delay offset for each all-pass line. */
1199 State->Late.Ap[i].Offset[1] = fastf2u(length * frequency);
1201 /* Calculate the length (in seconds) of each delay line. This also
1202 * applies the echo transformation. As the EAX echo depth approaches
1203 * 1, the line lengths approach a length equal to the echoTime. This
1204 * helps to produce distinct echoes along the tail.
1206 length = lerp(LATE_LINE_LENGTHS[i] * multiplier, echoTime, echoDepth);
1208 /* Calculate the delay offset for each delay line. */
1209 State->Late.Offset[i][1] = fastf2u(length * frequency);
1211 /* Approximate the absorption that the vector all-pass would exhibit
1212 * given the current diffusion so we don't have to process a full T60
1213 * filter for each of its four lines.
1215 length += lerp(LATE_ALLPASS_LENGTHS[i],
1216 (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
1217 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f,
1218 diffusion) * multiplier;
1220 /* Calculate the T60 damping coefficients for each line. */
1221 CalcT60DampingCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime,
1222 lfW, hfW, State->Late.Filters[i].LFCoeffs,
1223 State->Late.Filters[i].HFCoeffs,
1224 &State->Late.Filters[i].MidCoeff);
1228 /* Creates a transform matrix given a reverb vector. This works by creating a
1229 * Z-focus transform, then a rotate transform around X, then Y, to place the
1230 * focal point in the direction of the vector, using the vector length as a
1231 * focus strength.
1233 * This isn't technically correct since the vector is supposed to define the
1234 * aperture and not rotate the perceived soundfield, but in practice it's
1235 * probably good enough.
1237 static aluMatrixf GetTransformFromVector(const ALfloat *vec)
1239 aluMatrixf zfocus, xrot, yrot;
1240 aluMatrixf tmp1, tmp2;
1241 ALfloat length;
1242 ALfloat sa, a;
1244 length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
1246 /* Define a Z-focus (X in Ambisonics) transform, given the panning vector
1247 * length.
1249 sa = sinf(minf(length, 1.0f) * (F_PI/4.0f));
1250 aluMatrixfSet(&zfocus,
1251 1.0f/(1.0f+sa), 0.0f, 0.0f, (sa/(1.0f+sa))/1.732050808f,
1252 0.0f, sqrtf((1.0f-sa)/(1.0f+sa)), 0.0f, 0.0f,
1253 0.0f, 0.0f, sqrtf((1.0f-sa)/(1.0f+sa)), 0.0f,
1254 (sa/(1.0f+sa))*1.732050808f, 0.0f, 0.0f, 1.0f/(1.0f+sa)
1257 /* Define rotation around X (Y in Ambisonics) */
1258 a = atan2f(vec[1], sqrtf(vec[0]*vec[0] + vec[2]*vec[2]));
1259 aluMatrixfSet(&xrot,
1260 1.0f, 0.0f, 0.0f, 0.0f,
1261 0.0f, 1.0f, 0.0f, 0.0f,
1262 0.0f, 0.0f, cosf(a), sinf(a),
1263 0.0f, 0.0f, -sinf(a), cosf(a)
1266 /* Define rotation around Y (Z in Ambisonics). NOTE: EFX's reverb vectors
1267 * use a right-handled coordinate system, compared to the rest of OpenAL
1268 * which uses left-handed. This is fixed by negating Z, however it would
1269 * need to also be negated to get a proper Ambisonics angle, thus
1270 * cancelling it out.
1272 a = atan2f(-vec[0], vec[2]);
1273 aluMatrixfSet(&yrot,
1274 1.0f, 0.0f, 0.0f, 0.0f,
1275 0.0f, cosf(a), 0.0f, sinf(a),
1276 0.0f, 0.0f, 1.0f, 0.0f,
1277 0.0f, -sinf(a), 0.0f, cosf(a)
1280 #define MATRIX_MULT(_res, _m1, _m2) do { \
1281 int row, col; \
1282 for(col = 0;col < 4;col++) \
1284 for(row = 0;row < 4;row++) \
1285 _res.m[row][col] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
1286 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
1288 } while(0)
1289 /* Define a matrix that first focuses on Z, then rotates around X then Y to
1290 * focus the output in the direction of the vector.
1292 MATRIX_MULT(tmp1, xrot, zfocus);
1293 MATRIX_MULT(tmp2, yrot, tmp1);
1294 #undef MATRIX_MULT
1296 return tmp2;
1299 /* Update the early and late 3D panning gains. */
1300 static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat gain, const ALfloat earlyGain, const ALfloat lateGain, ALreverbState *State)
1302 aluMatrixf transform, rot;
1303 ALsizei i;
1305 STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
1306 STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;
1308 /* Note: Both _m2 and _res are transposed. */
1309 #define MATRIX_MULT(_res, _m1, _m2) do { \
1310 int row, col; \
1311 for(col = 0;col < 4;col++) \
1313 for(row = 0;row < 4;row++) \
1314 _res.m[col][row] = _m1.m[row][0]*_m2.m[col][0] + _m1.m[row][1]*_m2.m[col][1] + \
1315 _m1.m[row][2]*_m2.m[col][2] + _m1.m[row][3]*_m2.m[col][3]; \
1317 } while(0)
1318 /* Create a matrix that first converts A-Format to B-Format, then rotates
1319 * the B-Format soundfield according to the panning vector.
1321 rot = GetTransformFromVector(ReflectionsPan);
1322 MATRIX_MULT(transform, rot, A2B);
1323 memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
1324 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
1325 ComputeFirstOrderGains(Device->FOAOut, transform.m[i], gain*earlyGain, State->Early.PanGain[i]);
1327 rot = GetTransformFromVector(LateReverbPan);
1328 MATRIX_MULT(transform, rot, A2B);
1329 memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
1330 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
1331 ComputeFirstOrderGains(Device->FOAOut, transform.m[i], gain*lateGain, State->Late.PanGain[i]);
1332 #undef MATRIX_MULT
1335 static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
1337 ALuint frequency = Device->Frequency;
1338 ALfloat lfScale, hfScale, hfRatio;
1339 ALfloat lfDecayTime, hfDecayTime;
1340 ALfloat gain, gainlf, gainhf;
1341 ALsizei i;
1343 if(Slot->Params.EffectType == AL_EFFECT_EAXREVERB && !EmulateEAXReverb)
1344 State->IsEax = AL_TRUE;
1345 else if(Slot->Params.EffectType == AL_EFFECT_REVERB || EmulateEAXReverb)
1346 State->IsEax = AL_FALSE;
1348 /* Calculate the master filters */
1349 hfScale = props->Reverb.HFReference / frequency;
1350 /* Restrict the filter gains from going below -40dB to keep the I3DL2
1351 * model from killing most of the signal.
1353 gainhf = maxf(props->Reverb.GainHF, 0.01f);
1354 ALfilterState_setParams(&State->Filter[0].Lp, ALfilterType_HighShelf,
1355 gainhf, hfScale, calc_rcpQ_from_slope(gainhf, 1.0f));
1356 lfScale = props->Reverb.LFReference / frequency;
1357 gainlf = maxf(props->Reverb.GainLF, 0.01f);
1358 ALfilterState_setParams(&State->Filter[0].Hp, ALfilterType_LowShelf,
1359 gainlf, lfScale, calc_rcpQ_from_slope(gainlf, 1.0f));
1360 for(i = 1;i < 4;i++)
1362 State->Filter[i].Lp.b0 = State->Filter[0].Lp.b0;
1363 State->Filter[i].Lp.b1 = State->Filter[0].Lp.b1;
1364 State->Filter[i].Lp.b2 = State->Filter[0].Lp.b2;
1365 State->Filter[i].Lp.a1 = State->Filter[0].Lp.a1;
1366 State->Filter[i].Lp.a2 = State->Filter[0].Lp.a2;
1368 State->Filter[i].Hp.b0 = State->Filter[0].Hp.b0;
1369 State->Filter[i].Hp.b1 = State->Filter[0].Hp.b1;
1370 State->Filter[i].Hp.b2 = State->Filter[0].Hp.b2;
1371 State->Filter[i].Hp.a1 = State->Filter[0].Hp.a1;
1372 State->Filter[i].Hp.a2 = State->Filter[0].Hp.a2;
1375 /* Update the modulator line. */
1376 UpdateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth,
1377 frequency, State);
1379 /* Update the main effect delay and associated taps. */
1380 UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
1381 props->Reverb.Density, props->Reverb.DecayTime, frequency,
1382 State);
1384 /* Calculate the all-pass feed-back/forward coefficient. */
1385 State->ApFeedCoeff = sqrtf(0.5f) * powf(props->Reverb.Diffusion, 2.0f);
1387 /* Update the early lines. */
1388 UpdateEarlyLines(props->Reverb.Density, props->Reverb.DecayTime,
1389 frequency, State);
1391 /* Get the mixing matrix coefficients. */
1392 CalcMatrixCoeffs(props->Reverb.Diffusion, &State->MixX, &State->MixY);
1394 /* If the HF limit parameter is flagged, calculate an appropriate limit
1395 * based on the air absorption parameter.
1397 hfRatio = props->Reverb.DecayHFRatio;
1398 if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
1399 hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
1400 props->Reverb.DecayTime);
1402 /* Calculate the LF/HF decay times. */
1403 lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
1404 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1405 hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
1406 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1408 /* Update the late lines. */
1409 UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
1410 lfDecayTime, props->Reverb.DecayTime, hfDecayTime,
1411 F_TAU * lfScale, F_TAU * hfScale,
1412 props->Reverb.EchoTime, props->Reverb.EchoDepth,
1413 frequency, State);
1415 /* Update early and late 3D panning. Attenuate the early and late stages
1416 * both by 0.7071 (-3dB) to better balance the mixture.
1418 gain = sqrtf(0.5f) * props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
1419 Update3DPanning(Device, props->Reverb.ReflectionsPan,
1420 props->Reverb.LateReverbPan, gain,
1421 props->Reverb.ReflectionsGain,
1422 props->Reverb.LateReverbGain, State);
1424 /* Determine if delay-line cross-fading is required. */
1425 for(i = 0;i < 4;i++)
1427 if((State->EarlyDelayTap[i][1] != State->EarlyDelayTap[i][0]) ||
1428 (State->Early.Ap[i].Offset[1] != State->Early.Ap[i].Offset[0]) ||
1429 (State->Early.Offset[i][1] != State->Early.Offset[i][0]) ||
1430 (State->LateDelayTap[i][1] != State->LateDelayTap[i][0]) ||
1431 (State->Late.Ap[i].Offset[1] != State->Late.Ap[i].Offset[0]) ||
1432 (State->Late.Offset[i][1] != State->Late.Offset[i][0]))
1434 State->FadeCount = 0;
1435 break;
1441 /**************************************
1442 * Effect Processing *
1443 **************************************/
1445 /* Basic delay line input/output routines. */
1446 static inline ALfloat DelayLineOut(DelayLine *Delay, const ALsizei offset)
1448 return Delay->Line[offset&Delay->Mask];
1451 /* Cross-faded delay line output routine. Instead of interpolating the
1452 * offsets, this interpolates (cross-fades) the outputs at each offset.
1454 static inline ALfloat FadedDelayLineOut(DelayLine *Delay, const ALsizei off0, const ALsizei off1, const ALfloat mu)
1456 return lerp(Delay->Line[off0&Delay->Mask], Delay->Line[off1&Delay->Mask], mu);
1458 #define DELAY_OUT_Faded(d, o0, o1, mu) FadedDelayLineOut(d, o0, o1, mu)
1459 #define DELAY_OUT_Unfaded(d, o0, o1, mu) DelayLineOut(d, o0)
1461 static inline ALvoid DelayLineIn(DelayLine *Delay, const ALsizei offset, const ALfloat in)
1463 Delay->Line[offset&Delay->Mask] = in;
1466 static inline ALfloat DelayLineInOut(DelayLine *Delay, const ALsizei offset, const ALsizei outoffset, const ALfloat in)
1468 Delay->Line[offset&Delay->Mask] = in;
1469 return Delay->Line[(offset-outoffset)&Delay->Mask];
1472 static void CalcModulationDelays(ALreverbState *State, ALfloat *restrict delays, const ALsizei todo)
1474 ALfloat sinus, range;
1475 ALsizei index, i;
1477 index = State->Mod.Index;
1478 range = State->Mod.Filter;
1479 for(i = 0;i < todo;i++)
1481 /* Calculate the sinus rhythm (dependent on modulation time and the
1482 * sampling rate). The center of the sinus is moved to reduce the
1483 * delay of the effect when the time or depth are low.
1485 sinus = 1.0f - cosf(F_TAU * index / State->Mod.Range);
1487 /* Step the modulation index forward, keeping it bound to its range. */
1488 index = (index+1) % State->Mod.Range;
1490 /* The depth determines the range over which to read the input samples
1491 * from, so it must be filtered to reduce the distortion caused by even
1492 * small parameter changes.
1494 range = lerp(range, State->Mod.Depth, State->Mod.Coeff);
1496 /* Calculate the read offset with fraction. */
1497 delays[i] = range*sinus;
1499 State->Mod.Index = index;
1500 State->Mod.Filter = range;
1503 /* Given some input samples, this function produces modulation for the late
1504 * reverb.
1506 static void EAXModulation(DelayLine *ModDelay, ALsizei offset, const ALfloat *restrict delays, ALfloat*restrict dst, const ALfloat*restrict src, const ALsizei todo)
1508 ALfloat frac, fdelay;
1509 ALfloat out0, out1;
1510 ALsizei delay, i;
1512 for(i = 0;i < todo;i++)
1514 /* Separate the integer offset and fraction between it and the next
1515 * sample.
1517 frac = modff(delays[i], &fdelay);
1518 delay = fastf2u(fdelay);
1520 /* Add the incoming sample to the delay line, and get the two samples
1521 * crossed by the offset delay.
1523 out0 = DelayLineInOut(ModDelay, offset, delay, src[i]);
1524 out1 = DelayLineOut(ModDelay, offset - delay - 1);
1525 offset++;
1527 /* The output is obtained by linearly interpolating the two samples
1528 * that were acquired above.
1530 dst[i] = lerp(out0, out1, frac);
1534 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1535 * for both the below vector all-pass model and to perform modal feed-back
1536 * delay network (FDN) mixing.
1538 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1539 * matrix with a single unitary rotational parameter:
1541 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1542 * [ -a, d, c, -b ]
1543 * [ -b, -c, d, a ]
1544 * [ -c, b, -a, d ]
1546 * The rotation is constructed from the effect's diffusion parameter,
1547 * yielding:
1549 * 1 = x^2 + 3 y^2
1551 * Where a, b, and c are the coefficient y with differing signs, and d is the
1552 * coefficient x. The final matrix is thus:
1554 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1555 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1556 * [ y, -y, x, y ] x = cos(t)
1557 * [ -y, -y, -y, x ] y = sin(t) / n
1559 * Any square orthogonal matrix with an order that is a power of two will
1560 * work (where ^T is transpose, ^-1 is inverse):
1562 * M^T = M^-1
1564 * Using that knowledge, finding an appropriate matrix can be accomplished
1565 * naively by searching all combinations of:
1567 * M = D + S - S^T
1569 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1570 * whose combination of signs are being iterated.
1572 static inline void VectorPartialScatter(ALfloat *restrict vec, const ALfloat xCoeff, const ALfloat yCoeff)
1574 const ALfloat f[4] = { vec[0], vec[1], vec[2], vec[3] };
1576 vec[0] = xCoeff*f[0] + yCoeff*( f[1] + -f[2] + f[3]);
1577 vec[1] = xCoeff*f[1] + yCoeff*(-f[0] + f[2] + f[3]);
1578 vec[2] = xCoeff*f[2] + yCoeff*( f[0] + -f[1] + f[3]);
1579 vec[3] = xCoeff*f[3] + yCoeff*(-f[0] + -f[1] + -f[2] );
1582 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1583 * filter to the 4-line input.
1585 * It works by vectorizing a regular all-pass filter and replacing the delay
1586 * element with a scattering matrix (like the one above) and a diagonal
1587 * matrix of delay elements.
1589 * Two static specializations are used for transitional (cross-faded) delay
1590 * line processing and non-transitional processing.
1592 #define DECL_TEMPLATE(T) \
1593 static void VectorAllpass_##T(ALfloat *restrict vec, const ALsizei offset, \
1594 const ALfloat feedCoeff, const ALfloat xCoeff, \
1595 const ALfloat yCoeff, const ALfloat mu, \
1596 Allpass Ap[4]) \
1598 ALfloat input; \
1599 ALfloat f[4]; \
1600 ALsizei i; \
1602 (void)mu; /* Ignore for Unfaded. */ \
1604 for(i = 0;i < 4;i++) \
1606 input = vec[i]; \
1607 vec[i] = DELAY_OUT_##T(&Ap[i].Delay, offset-Ap[i].Offset[0], \
1608 offset-Ap[i].Offset[1], mu) - \
1609 feedCoeff*input; \
1610 f[i] = input + feedCoeff*vec[i]; \
1613 VectorPartialScatter(f, xCoeff, yCoeff); \
1615 for(i = 0;i < 4;i++) \
1616 DelayLineIn(&Ap[i].Delay, offset, f[i]); \
1618 DECL_TEMPLATE(Unfaded)
1619 DECL_TEMPLATE(Faded)
1620 #undef DECL_TEMPLATE
1622 /* A helper to reverse vector components. */
1623 static inline void VectorReverse(ALfloat vec[4])
1625 const ALfloat f[4] = { vec[0], vec[1], vec[2], vec[3] };
1627 vec[0] = f[3];
1628 vec[1] = f[2];
1629 vec[2] = f[1];
1630 vec[3] = f[0];
1633 /* This generates early reflections.
1635 * This is done by obtaining the primary reflections (those arriving from the
1636 * same direction as the source) from the main delay line. These are
1637 * attenuated and all-pass filtered (based on the diffusion parameter).
1639 * The early lines are then fed in reverse (according to the approximately
1640 * opposite spatial location of the A-Format lines) to create the secondary
1641 * reflections (those arriving from the opposite direction as the source).
1643 * The early response is then completed by combining the primary reflections
1644 * with the delayed and attenuated output from the early lines.
1646 * Finally, the early response is reversed, scattered (based on diffusion),
1647 * and fed into the late reverb section of the main delay line.
1649 * Two static specializations are used for transitional (cross-faded) delay
1650 * line processing and non-transitional processing.
1652 #define DECL_TEMPLATE(T) \
1653 static ALvoid EarlyReflection_##T(ALreverbState *State, const ALsizei todo, \
1654 ALfloat fade, \
1655 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])\
1657 ALsizei offset = State->Offset; \
1658 const ALfloat apFeedCoeff = State->ApFeedCoeff; \
1659 const ALfloat mixX = State->MixX; \
1660 const ALfloat mixY = State->MixY; \
1661 ALfloat f[4]; \
1662 ALsizei i, j; \
1664 for(i = 0;i < todo;i++) \
1666 for(j = 0;j < 4;j++) \
1667 f[j] = DELAY_OUT_##T(&State->Delay, \
1668 (offset-State->EarlyDelayTap[j][0])*4 + j, \
1669 (offset-State->EarlyDelayTap[j][1])*4 + j, fade \
1670 ) * State->EarlyDelayCoeff[j]; \
1672 VectorAllpass_##T(f, offset, apFeedCoeff, mixX, mixY, fade, \
1673 State->Early.Ap); \
1675 for(j = 0;j < 4;j++) \
1676 DelayLineIn(&State->Early.Delay[j], offset, f[3 - j]); \
1678 for(j = 0;j < 4;j++) \
1679 f[j] += DELAY_OUT_##T(&State->Early.Delay[j], \
1680 offset-State->Early.Offset[j][0], \
1681 offset-State->Early.Offset[j][1], fade) * \
1682 State->Early.Coeff[j]; \
1684 for(j = 0;j < 4;j++) \
1685 out[j][i] = f[j]; \
1687 VectorReverse(f); \
1689 VectorPartialScatter(f, mixX, mixY); \
1691 for(j = 0;j < 4;j++) \
1692 DelayLineIn(&State->Delay, (offset-State->LateFeedTap)*4 + j, \
1693 f[j]); \
1695 offset++; \
1696 fade += FadeStep; \
1699 DECL_TEMPLATE(Unfaded)
1700 DECL_TEMPLATE(Faded)
1701 #undef DECL_TEMPLATE
1703 /* Applies a first order filter section. */
1704 static inline ALfloat FirstOrderFilter(const ALfloat in, const ALfloat coeffs[3], ALfloat state[2])
1706 ALfloat out = coeffs[0]*in + coeffs[1]*state[0] + coeffs[2]*state[1];
1708 state[0] = in;
1709 state[1] = out;
1711 return out;
1714 /* Applies the two T60 damping filter sections. */
1715 static inline ALfloat LateT60Filter(const ALsizei index, const ALfloat in, ALreverbState *State)
1717 ALfloat out = FirstOrderFilter(in, State->Late.Filters[index].LFCoeffs,
1718 State->Late.Filters[index].States[0]);
1720 return State->Late.Filters[index].MidCoeff *
1721 FirstOrderFilter(out, State->Late.Filters[index].HFCoeffs,
1722 State->Late.Filters[index].States[1]);
1725 /* This generates the reverb tail using a modified feed-back delay network
1726 * (FDN).
1728 * Results from the early reflections are attenuated by the density gain and
1729 * mixed with the output from the late delay lines.
1731 * The late response is then completed by T60 and all-pass filtering the mix.
1733 * Finally, the lines are reversed (so they feed their opposite directions)
1734 * and scattered with the FDN matrix before re-feeding the delay lines.
1736 * Two static specializations are used for transitional (cross-faded) delay
1737 * line processing and non-transitional processing.
1739 #define DECL_TEMPLATE(T) \
1740 static ALvoid LateReverb_##T(ALreverbState *State, const ALsizei todo, \
1741 ALfloat fade, \
1742 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) \
1744 const ALfloat apFeedCoeff = State->ApFeedCoeff; \
1745 const ALfloat mixX = State->MixX; \
1746 const ALfloat mixY = State->MixY; \
1747 ALsizei offset; \
1748 ALsizei i, j; \
1749 ALfloat f[4]; \
1751 offset = State->Offset; \
1752 for(i = 0;i < todo;i++) \
1754 for(j = 0;j < 4;j++) \
1755 f[j] = DELAY_OUT_##T(&State->Delay, \
1756 (offset-State->LateDelayTap[j][0])*4 + j, \
1757 (offset-State->LateDelayTap[j][1])*4 + j, fade \
1758 ) * State->Late.DensityGain; \
1760 for(j = 0;j < 4;j++) \
1761 f[j] += DELAY_OUT_##T(&State->Late.Delay[j], \
1762 offset-State->Late.Offset[j][0], \
1763 offset-State->Late.Offset[j][1], fade); \
1765 for(j = 0;j < 4;j++) \
1766 f[j] = LateT60Filter(j, f[j], State); \
1768 VectorAllpass_##T(f, offset, apFeedCoeff, mixX, mixY, fade, \
1769 State->Late.Ap); \
1771 for(j = 0;j < 4;j++) \
1772 out[j][i] = f[j]; \
1774 VectorReverse(f); \
1776 VectorPartialScatter(f, mixX, mixY); \
1778 for(j = 0;j < 4;j++) \
1779 DelayLineIn(&State->Late.Delay[j], offset, f[j]); \
1781 offset++; \
1782 fade += FadeStep; \
1785 DECL_TEMPLATE(Unfaded)
1786 DECL_TEMPLATE(Faded)
1787 #undef DECL_TEMPLATE
1789 typedef ALfloat (*ProcMethodType)(ALreverbState *State, const ALsizei todo, ALfloat fade,
1790 const ALfloat (*restrict input)[MAX_UPDATE_SAMPLES],
1791 ALfloat (*restrict early)[MAX_UPDATE_SAMPLES], ALfloat (*restrict late)[MAX_UPDATE_SAMPLES]);
1793 /* Perform the non-EAX reverb pass on a given input sample, resulting in
1794 * four-channel output.
1796 static ALfloat VerbPass(ALreverbState *State, const ALsizei todo, ALfloat fade,
1797 const ALfloat (*restrict input)[MAX_UPDATE_SAMPLES],
1798 ALfloat (*restrict early)[MAX_UPDATE_SAMPLES],
1799 ALfloat (*restrict late)[MAX_UPDATE_SAMPLES])
1801 ALsizei i, c;
1803 for(c = 0;c < 4;c++)
1805 /* Low-pass filter the incoming samples (use the early buffer as temp
1806 * storage).
1808 ALfilterState_process(&State->Filter[c].Lp, &early[0][0], input[c], todo);
1810 /* Feed the initial delay line. */
1811 for(i = 0;i < todo;i++)
1812 DelayLineIn(&State->Delay, (State->Offset+i)*4 + c, early[0][i]);
1815 if(fade < 1.0f)
1817 /* Generate early reflections. */
1818 EarlyReflection_Faded(State, todo, fade, early);
1820 /* Generate late reverb. */
1821 LateReverb_Faded(State, todo, fade, late);
1822 fade = minf(1.0f, fade + todo*FadeStep);
1824 else
1826 /* Generate early reflections. */
1827 EarlyReflection_Unfaded(State, todo, fade, early);
1829 /* Generate late reverb. */
1830 LateReverb_Unfaded(State, todo, fade, late);
1833 /* Step all delays forward one sample. */
1834 State->Offset += todo;
1836 return fade;
1839 /* Perform the EAX reverb pass on a given input sample, resulting in four-
1840 * channel output.
1842 static ALfloat EAXVerbPass(ALreverbState *State, const ALsizei todo, ALfloat fade,
1843 const ALfloat (*restrict input)[MAX_UPDATE_SAMPLES],
1844 ALfloat (*restrict early)[MAX_UPDATE_SAMPLES],
1845 ALfloat (*restrict late)[MAX_UPDATE_SAMPLES])
1847 ALsizei i, c;
1849 /* Perform any modulation on the input (use the early and late buffers as
1850 * temp storage).
1852 CalcModulationDelays(State, &late[0][0], todo);
1853 for(c = 0;c < 4;c++)
1855 /* Apply modulation. */
1856 EAXModulation(&State->Mod.Delay[c], State->Offset, &late[0][0],
1857 &early[0][0], input[c], todo);
1859 /* Band-pass the incoming samples. */
1860 ALfilterState_process(&State->Filter[c].Lp, &early[1][0], &early[0][0], todo);
1861 ALfilterState_process(&State->Filter[c].Hp, &early[2][0], &early[1][0], todo);
1863 /* Feed the initial delay line. */
1864 for(i = 0;i < todo;i++)
1865 DelayLineIn(&State->Delay, (State->Offset+i)*4 + c, early[2][i]);
1868 if(fade < 1.0f)
1870 /* Generate early reflections. */
1871 EarlyReflection_Faded(State, todo, fade, early);
1873 /* Generate late reverb. */
1874 LateReverb_Faded(State, todo, fade, late);
1875 fade = minf(1.0f, fade + todo*FadeStep);
1877 else
1879 /* Generate early reflections. */
1880 EarlyReflection_Unfaded(State, todo, fade, early);
1882 /* Generate late reverb. */
1883 LateReverb_Unfaded(State, todo, fade, late);
1886 /* Step all delays forward. */
1887 State->Offset += todo;
1889 return fade;
1892 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
1894 ProcMethodType ReverbProc = State->IsEax ? EAXVerbPass : VerbPass;
1895 ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->AFormatSamples;
1896 ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples;
1897 ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples;
1898 ALsizei fadeCount = State->FadeCount;
1899 ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES;
1900 ALsizei base, c;
1902 /* Process reverb for these samples. */
1903 for(base = 0;base < SamplesToDo;)
1905 ALsizei todo = mini(SamplesToDo-base, MAX_UPDATE_SAMPLES);
1906 /* If cross-fading, don't do more samples than there are to fade. */
1907 if(FADE_SAMPLES-fadeCount > 0)
1908 todo = mini(todo, FADE_SAMPLES-fadeCount);
1910 /* Convert B-Format to A-Format for processing. */
1911 memset(afmt, 0, sizeof(*afmt)*4);
1912 for(c = 0;c < 4;c++)
1913 MixRowSamples(afmt[c], B2A.m[c],
1914 SamplesIn, MAX_EFFECT_CHANNELS, base, todo
1917 /* Process the samples for reverb. */
1918 fade = ReverbProc(State, todo, fade, afmt, early, late);
1919 if(UNEXPECTED(fadeCount < FADE_SAMPLES) && (fadeCount += todo) >= FADE_SAMPLES)
1921 /* Update the cross-fading delay line taps. */
1922 fadeCount = FADE_SAMPLES;
1923 fade = 1.0f;
1924 for(c = 0;c < 4;c++)
1926 State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
1927 State->Early.Ap[c].Offset[0] = State->Early.Ap[c].Offset[1];
1928 State->Early.Offset[c][0] = State->Early.Offset[c][1];
1929 State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
1930 State->Late.Ap[c].Offset[0] = State->Late.Ap[c].Offset[1];
1931 State->Late.Offset[c][0] = State->Late.Offset[c][1];
1935 /* Mix the A-Format results to output, implicitly converting back to
1936 * B-Format.
1938 for(c = 0;c < 4;c++)
1939 MixSamples(early[c], NumChannels, SamplesOut,
1940 State->Early.CurrentGain[c], State->Early.PanGain[c],
1941 SamplesToDo-base, base, todo
1943 for(c = 0;c < 4;c++)
1944 MixSamples(late[c], NumChannels, SamplesOut,
1945 State->Late.CurrentGain[c], State->Late.PanGain[c],
1946 SamplesToDo-base, base, todo
1949 base += todo;
1951 State->FadeCount = fadeCount;
1955 typedef struct ALreverbStateFactory {
1956 DERIVE_FROM_TYPE(ALeffectStateFactory);
1957 } ALreverbStateFactory;
1959 static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory))
1961 ALreverbState *state;
1963 alcall_once(&mixfunc_inited, init_mixfunc);
1965 NEW_OBJ0(state, ALreverbState)();
1966 if(!state) return NULL;
1968 return STATIC_CAST(ALeffectState, state);
1971 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALreverbStateFactory);
1973 ALeffectStateFactory *ALreverbStateFactory_getFactory(void)
1975 static ALreverbStateFactory ReverbFactory = { { GET_VTABLE2(ALreverbStateFactory, ALeffectStateFactory) } };
1977 return STATIC_CAST(ALeffectStateFactory, &ReverbFactory);
1981 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1983 ALeffectProps *props = &effect->Props;
1984 switch(param)
1986 case AL_EAXREVERB_DECAY_HFLIMIT:
1987 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1988 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1989 props->Reverb.DecayHFLimit = val;
1990 break;
1992 default:
1993 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1996 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1998 ALeaxreverb_setParami(effect, context, param, vals[0]);
2000 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
2002 ALeffectProps *props = &effect->Props;
2003 switch(param)
2005 case AL_EAXREVERB_DENSITY:
2006 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
2007 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2008 props->Reverb.Density = val;
2009 break;
2011 case AL_EAXREVERB_DIFFUSION:
2012 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
2013 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2014 props->Reverb.Diffusion = val;
2015 break;
2017 case AL_EAXREVERB_GAIN:
2018 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
2019 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2020 props->Reverb.Gain = val;
2021 break;
2023 case AL_EAXREVERB_GAINHF:
2024 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
2025 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2026 props->Reverb.GainHF = val;
2027 break;
2029 case AL_EAXREVERB_GAINLF:
2030 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
2031 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2032 props->Reverb.GainLF = val;
2033 break;
2035 case AL_EAXREVERB_DECAY_TIME:
2036 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
2037 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2038 props->Reverb.DecayTime = val;
2039 break;
2041 case AL_EAXREVERB_DECAY_HFRATIO:
2042 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
2043 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2044 props->Reverb.DecayHFRatio = val;
2045 break;
2047 case AL_EAXREVERB_DECAY_LFRATIO:
2048 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
2049 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2050 props->Reverb.DecayLFRatio = val;
2051 break;
2053 case AL_EAXREVERB_REFLECTIONS_GAIN:
2054 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
2055 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2056 props->Reverb.ReflectionsGain = val;
2057 break;
2059 case AL_EAXREVERB_REFLECTIONS_DELAY:
2060 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
2061 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2062 props->Reverb.ReflectionsDelay = val;
2063 break;
2065 case AL_EAXREVERB_LATE_REVERB_GAIN:
2066 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
2067 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2068 props->Reverb.LateReverbGain = val;
2069 break;
2071 case AL_EAXREVERB_LATE_REVERB_DELAY:
2072 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
2073 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2074 props->Reverb.LateReverbDelay = val;
2075 break;
2077 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
2078 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
2079 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2080 props->Reverb.AirAbsorptionGainHF = val;
2081 break;
2083 case AL_EAXREVERB_ECHO_TIME:
2084 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
2085 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2086 props->Reverb.EchoTime = val;
2087 break;
2089 case AL_EAXREVERB_ECHO_DEPTH:
2090 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
2091 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2092 props->Reverb.EchoDepth = val;
2093 break;
2095 case AL_EAXREVERB_MODULATION_TIME:
2096 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
2097 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2098 props->Reverb.ModulationTime = val;
2099 break;
2101 case AL_EAXREVERB_MODULATION_DEPTH:
2102 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
2103 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2104 props->Reverb.ModulationDepth = val;
2105 break;
2107 case AL_EAXREVERB_HFREFERENCE:
2108 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
2109 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2110 props->Reverb.HFReference = val;
2111 break;
2113 case AL_EAXREVERB_LFREFERENCE:
2114 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
2115 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2116 props->Reverb.LFReference = val;
2117 break;
2119 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
2120 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
2121 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2122 props->Reverb.RoomRolloffFactor = val;
2123 break;
2125 default:
2126 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
2129 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
2131 ALeffectProps *props = &effect->Props;
2132 switch(param)
2134 case AL_EAXREVERB_REFLECTIONS_PAN:
2135 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
2136 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2137 props->Reverb.ReflectionsPan[0] = vals[0];
2138 props->Reverb.ReflectionsPan[1] = vals[1];
2139 props->Reverb.ReflectionsPan[2] = vals[2];
2140 break;
2141 case AL_EAXREVERB_LATE_REVERB_PAN:
2142 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
2143 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2144 props->Reverb.LateReverbPan[0] = vals[0];
2145 props->Reverb.LateReverbPan[1] = vals[1];
2146 props->Reverb.LateReverbPan[2] = vals[2];
2147 break;
2149 default:
2150 ALeaxreverb_setParamf(effect, context, param, vals[0]);
2151 break;
2155 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
2157 const ALeffectProps *props = &effect->Props;
2158 switch(param)
2160 case AL_EAXREVERB_DECAY_HFLIMIT:
2161 *val = props->Reverb.DecayHFLimit;
2162 break;
2164 default:
2165 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
2168 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
2170 ALeaxreverb_getParami(effect, context, param, vals);
2172 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
2174 const ALeffectProps *props = &effect->Props;
2175 switch(param)
2177 case AL_EAXREVERB_DENSITY:
2178 *val = props->Reverb.Density;
2179 break;
2181 case AL_EAXREVERB_DIFFUSION:
2182 *val = props->Reverb.Diffusion;
2183 break;
2185 case AL_EAXREVERB_GAIN:
2186 *val = props->Reverb.Gain;
2187 break;
2189 case AL_EAXREVERB_GAINHF:
2190 *val = props->Reverb.GainHF;
2191 break;
2193 case AL_EAXREVERB_GAINLF:
2194 *val = props->Reverb.GainLF;
2195 break;
2197 case AL_EAXREVERB_DECAY_TIME:
2198 *val = props->Reverb.DecayTime;
2199 break;
2201 case AL_EAXREVERB_DECAY_HFRATIO:
2202 *val = props->Reverb.DecayHFRatio;
2203 break;
2205 case AL_EAXREVERB_DECAY_LFRATIO:
2206 *val = props->Reverb.DecayLFRatio;
2207 break;
2209 case AL_EAXREVERB_REFLECTIONS_GAIN:
2210 *val = props->Reverb.ReflectionsGain;
2211 break;
2213 case AL_EAXREVERB_REFLECTIONS_DELAY:
2214 *val = props->Reverb.ReflectionsDelay;
2215 break;
2217 case AL_EAXREVERB_LATE_REVERB_GAIN:
2218 *val = props->Reverb.LateReverbGain;
2219 break;
2221 case AL_EAXREVERB_LATE_REVERB_DELAY:
2222 *val = props->Reverb.LateReverbDelay;
2223 break;
2225 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
2226 *val = props->Reverb.AirAbsorptionGainHF;
2227 break;
2229 case AL_EAXREVERB_ECHO_TIME:
2230 *val = props->Reverb.EchoTime;
2231 break;
2233 case AL_EAXREVERB_ECHO_DEPTH:
2234 *val = props->Reverb.EchoDepth;
2235 break;
2237 case AL_EAXREVERB_MODULATION_TIME:
2238 *val = props->Reverb.ModulationTime;
2239 break;
2241 case AL_EAXREVERB_MODULATION_DEPTH:
2242 *val = props->Reverb.ModulationDepth;
2243 break;
2245 case AL_EAXREVERB_HFREFERENCE:
2246 *val = props->Reverb.HFReference;
2247 break;
2249 case AL_EAXREVERB_LFREFERENCE:
2250 *val = props->Reverb.LFReference;
2251 break;
2253 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
2254 *val = props->Reverb.RoomRolloffFactor;
2255 break;
2257 default:
2258 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
2261 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2263 const ALeffectProps *props = &effect->Props;
2264 switch(param)
2266 case AL_EAXREVERB_REFLECTIONS_PAN:
2267 vals[0] = props->Reverb.ReflectionsPan[0];
2268 vals[1] = props->Reverb.ReflectionsPan[1];
2269 vals[2] = props->Reverb.ReflectionsPan[2];
2270 break;
2271 case AL_EAXREVERB_LATE_REVERB_PAN:
2272 vals[0] = props->Reverb.LateReverbPan[0];
2273 vals[1] = props->Reverb.LateReverbPan[1];
2274 vals[2] = props->Reverb.LateReverbPan[2];
2275 break;
2277 default:
2278 ALeaxreverb_getParamf(effect, context, param, vals);
2279 break;
2283 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
2285 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
2287 ALeffectProps *props = &effect->Props;
2288 switch(param)
2290 case AL_REVERB_DECAY_HFLIMIT:
2291 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
2292 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2293 props->Reverb.DecayHFLimit = val;
2294 break;
2296 default:
2297 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
2300 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
2302 ALreverb_setParami(effect, context, param, vals[0]);
2304 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
2306 ALeffectProps *props = &effect->Props;
2307 switch(param)
2309 case AL_REVERB_DENSITY:
2310 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
2311 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2312 props->Reverb.Density = val;
2313 break;
2315 case AL_REVERB_DIFFUSION:
2316 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
2317 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2318 props->Reverb.Diffusion = val;
2319 break;
2321 case AL_REVERB_GAIN:
2322 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
2323 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2324 props->Reverb.Gain = val;
2325 break;
2327 case AL_REVERB_GAINHF:
2328 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
2329 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2330 props->Reverb.GainHF = val;
2331 break;
2333 case AL_REVERB_DECAY_TIME:
2334 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
2335 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2336 props->Reverb.DecayTime = val;
2337 break;
2339 case AL_REVERB_DECAY_HFRATIO:
2340 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
2341 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2342 props->Reverb.DecayHFRatio = val;
2343 break;
2345 case AL_REVERB_REFLECTIONS_GAIN:
2346 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
2347 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2348 props->Reverb.ReflectionsGain = val;
2349 break;
2351 case AL_REVERB_REFLECTIONS_DELAY:
2352 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
2353 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2354 props->Reverb.ReflectionsDelay = val;
2355 break;
2357 case AL_REVERB_LATE_REVERB_GAIN:
2358 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
2359 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2360 props->Reverb.LateReverbGain = val;
2361 break;
2363 case AL_REVERB_LATE_REVERB_DELAY:
2364 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
2365 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2366 props->Reverb.LateReverbDelay = val;
2367 break;
2369 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2370 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
2371 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2372 props->Reverb.AirAbsorptionGainHF = val;
2373 break;
2375 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2376 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
2377 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
2378 props->Reverb.RoomRolloffFactor = val;
2379 break;
2381 default:
2382 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
2385 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
2387 ALreverb_setParamf(effect, context, param, vals[0]);
2390 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
2392 const ALeffectProps *props = &effect->Props;
2393 switch(param)
2395 case AL_REVERB_DECAY_HFLIMIT:
2396 *val = props->Reverb.DecayHFLimit;
2397 break;
2399 default:
2400 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
2403 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
2405 ALreverb_getParami(effect, context, param, vals);
2407 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
2409 const ALeffectProps *props = &effect->Props;
2410 switch(param)
2412 case AL_REVERB_DENSITY:
2413 *val = props->Reverb.Density;
2414 break;
2416 case AL_REVERB_DIFFUSION:
2417 *val = props->Reverb.Diffusion;
2418 break;
2420 case AL_REVERB_GAIN:
2421 *val = props->Reverb.Gain;
2422 break;
2424 case AL_REVERB_GAINHF:
2425 *val = props->Reverb.GainHF;
2426 break;
2428 case AL_REVERB_DECAY_TIME:
2429 *val = props->Reverb.DecayTime;
2430 break;
2432 case AL_REVERB_DECAY_HFRATIO:
2433 *val = props->Reverb.DecayHFRatio;
2434 break;
2436 case AL_REVERB_REFLECTIONS_GAIN:
2437 *val = props->Reverb.ReflectionsGain;
2438 break;
2440 case AL_REVERB_REFLECTIONS_DELAY:
2441 *val = props->Reverb.ReflectionsDelay;
2442 break;
2444 case AL_REVERB_LATE_REVERB_GAIN:
2445 *val = props->Reverb.LateReverbGain;
2446 break;
2448 case AL_REVERB_LATE_REVERB_DELAY:
2449 *val = props->Reverb.LateReverbDelay;
2450 break;
2452 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2453 *val = props->Reverb.AirAbsorptionGainHF;
2454 break;
2456 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2457 *val = props->Reverb.RoomRolloffFactor;
2458 break;
2460 default:
2461 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
2464 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2466 ALreverb_getParamf(effect, context, param, vals);
2469 DEFINE_ALEFFECT_VTABLE(ALreverb);