EFX: Align some arrays used in intrinsics (#180)
[openal-soft.git] / Alc / effects / reverb.c
blob3c5e5e96af6b0678d294c2ebef89b338161e1d32
1 /**
2 * Ambisonic reverb engine for the OpenAL cross platform audio library
3 * Copyright (C) 2008-2017 by Chris Robinson and Christopher Fitzgerald.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <math.h>
27 #include "alMain.h"
28 #include "alu.h"
29 #include "alAuxEffectSlot.h"
30 #include "alListener.h"
31 #include "alError.h"
32 #include "filters/defs.h"
34 /* This is a user config option for modifying the overall output of the reverb
35 * effect.
37 ALfloat ReverbBoost = 1.0f;
39 /* This is the maximum number of samples processed for each inner loop
40 * iteration. */
41 #define MAX_UPDATE_SAMPLES 256
43 /* The number of samples used for cross-faded delay lines. This can be used
44 * to balance the compensation for abrupt line changes and attenuation due to
45 * minimally lengthed recursive lines. Try to keep this below the device
46 * update size.
48 #define FADE_SAMPLES 128
50 /* The number of spatialized lines or channels to process. Four channels allows
51 * for a 3D A-Format response. NOTE: This can't be changed without taking care
52 * of the conversion matrices, and a few places where the length arrays are
53 * assumed to have 4 elements.
55 #define NUM_LINES 4
58 /* The B-Format to A-Format conversion matrix. The arrangement of rows is
59 * deliberately chosen to align the resulting lines to their spatial opposites
60 * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
61 * back left). It's not quite opposite, since the A-Format results in a
62 * tetrahedron, but it's close enough. Should the model be extended to 8-lines
63 * in the future, true opposites can be used.
65 static const aluMatrixf B2A = {{
66 { 0.288675134595f, 0.288675134595f, 0.288675134595f, 0.288675134595f },
67 { 0.288675134595f, -0.288675134595f, -0.288675134595f, 0.288675134595f },
68 { 0.288675134595f, 0.288675134595f, -0.288675134595f, -0.288675134595f },
69 { 0.288675134595f, -0.288675134595f, 0.288675134595f, -0.288675134595f }
70 }};
72 /* Converts A-Format to B-Format. */
73 static const aluMatrixf A2B = {{
74 { 0.866025403785f, 0.866025403785f, 0.866025403785f, 0.866025403785f },
75 { 0.866025403785f, -0.866025403785f, 0.866025403785f, -0.866025403785f },
76 { 0.866025403785f, -0.866025403785f, -0.866025403785f, 0.866025403785f },
77 { 0.866025403785f, 0.866025403785f, -0.866025403785f, -0.866025403785f }
78 }};
80 static const ALfloat FadeStep = 1.0f / FADE_SAMPLES;
82 /* The all-pass and delay lines have a variable length dependent on the
83 * effect's density parameter, which helps alter the perceived environment
84 * size. The size-to-density conversion is a cubed scale:
86 * density = min(1.0, pow(size, 3.0) / DENSITY_SCALE);
88 * The line lengths scale linearly with room size, so the inverse density
89 * conversion is needed, taking the cube root of the re-scaled density to
90 * calculate the line length multiplier:
92 * length_mult = max(5.0, cbrtf(density*DENSITY_SCALE));
94 * The density scale below will result in a max line multiplier of 50, for an
95 * effective size range of 5m to 50m.
97 static const ALfloat DENSITY_SCALE = 125000.0f;
99 /* All delay line lengths are specified in seconds.
101 * To approximate early reflections, we break them up into primary (those
102 * arriving from the same direction as the source) and secondary (those
103 * arriving from the opposite direction).
105 * The early taps decorrelate the 4-channel signal to approximate an average
106 * room response for the primary reflections after the initial early delay.
108 * Given an average room dimension (d_a) and the speed of sound (c) we can
109 * calculate the average reflection delay (r_a) regardless of listener and
110 * source positions as:
112 * r_a = d_a / c
113 * c = 343.3
115 * This can extended to finding the average difference (r_d) between the
116 * maximum (r_1) and minimum (r_0) reflection delays:
118 * r_0 = 2 / 3 r_a
119 * = r_a - r_d / 2
120 * = r_d
121 * r_1 = 4 / 3 r_a
122 * = r_a + r_d / 2
123 * = 2 r_d
124 * r_d = 2 / 3 r_a
125 * = r_1 - r_0
127 * As can be determined by integrating the 1D model with a source (s) and
128 * listener (l) positioned across the dimension of length (d_a):
130 * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
132 * The initial taps (T_(i=0)^N) are then specified by taking a power series
133 * that ranges between r_0 and half of r_1 less r_0:
135 * R_i = 2^(i / (2 N - 1)) r_d
136 * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
137 * = r_0 + T_i
138 * T_i = R_i - r_0
139 * = (2^(i / (2 N - 1)) - 1) r_d
141 * Assuming an average of 1m, we get the following taps:
143 static const ALfloat EARLY_TAP_LENGTHS[NUM_LINES] =
145 0.0000000e+0f, 2.0213520e-4f, 4.2531060e-4f, 6.7171600e-4f
148 /* The early all-pass filter lengths are based on the early tap lengths:
150 * A_i = R_i / a
152 * Where a is the approximate maximum all-pass cycle limit (20).
154 static const ALfloat EARLY_ALLPASS_LENGTHS[NUM_LINES] =
156 9.7096800e-5f, 1.0720356e-4f, 1.1836234e-4f, 1.3068260e-4f
159 /* The early delay lines are used to transform the primary reflections into
160 * the secondary reflections. The A-format is arranged in such a way that
161 * the channels/lines are spatially opposite:
163 * C_i is opposite C_(N-i-1)
165 * The delays of the two opposing reflections (R_i and O_i) from a source
166 * anywhere along a particular dimension always sum to twice its full delay:
168 * 2 r_a = R_i + O_i
170 * With that in mind we can determine the delay between the two reflections
171 * and thus specify our early line lengths (L_(i=0)^N) using:
173 * O_i = 2 r_a - R_(N-i-1)
174 * L_i = O_i - R_(N-i-1)
175 * = 2 (r_a - R_(N-i-1))
176 * = 2 (r_a - T_(N-i-1) - r_0)
177 * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
179 * Using an average dimension of 1m, we get:
181 static const ALfloat EARLY_LINE_LENGTHS[NUM_LINES] =
183 5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f
186 /* The late all-pass filter lengths are based on the late line lengths:
188 * A_i = (5 / 3) L_i / r_1
190 static const ALfloat LATE_ALLPASS_LENGTHS[NUM_LINES] =
192 1.6182800e-4f, 2.0389060e-4f, 2.8159360e-4f, 3.2365600e-4f
195 /* The late lines are used to approximate the decaying cycle of recursive
196 * late reflections.
198 * Splitting the lines in half, we start with the shortest reflection paths
199 * (L_(i=0)^(N/2)):
201 * L_i = 2^(i / (N - 1)) r_d
203 * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
205 * L_i = 2 r_a - L_(i-N/2)
206 * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
208 * For our 1m average room, we get:
210 static const ALfloat LATE_LINE_LENGTHS[NUM_LINES] =
212 1.9419362e-3f, 2.4466860e-3f, 3.3791220e-3f, 3.8838720e-3f
216 typedef struct DelayLineI {
217 /* The delay lines use interleaved samples, with the lengths being powers
218 * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
220 ALsizei Mask;
221 ALfloat (*Line)[NUM_LINES];
222 } DelayLineI;
224 typedef struct VecAllpass {
225 DelayLineI Delay;
226 ALsizei Offset[NUM_LINES][2];
227 } VecAllpass;
229 typedef struct T60Filter {
230 /* Two filters are used to adjust the signal. One to control the low
231 * frequencies, and one to control the high frequencies. The HF filter also
232 * adjusts the overall output gain, affecting the remaining mid-band.
234 ALfloat HFCoeffs[3];
235 ALfloat LFCoeffs[3];
237 /* The HF and LF filters each keep a state of the last input and last
238 * output sample.
240 ALfloat HFState[2];
241 ALfloat LFState[2];
242 } T60Filter;
244 typedef struct EarlyReflections {
245 /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
246 * The spread from this filter also helps smooth out the reverb tail.
248 VecAllpass VecAp;
250 /* An echo line is used to complete the second half of the early
251 * reflections.
253 DelayLineI Delay;
254 ALsizei Offset[NUM_LINES][2];
255 ALfloat Coeff[NUM_LINES];
257 /* The gain for each output channel based on 3D panning. */
258 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
259 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
260 } EarlyReflections;
262 typedef struct LateReverb {
263 /* Attenuation to compensate for the modal density and decay rate of the
264 * late lines.
266 ALfloat DensityGain;
268 /* A recursive delay line is used fill in the reverb tail. */
269 DelayLineI Delay;
270 ALsizei Offset[NUM_LINES][2];
272 /* T60 decay filters are used to simulate absorption. */
273 T60Filter T60[NUM_LINES];
275 /* A Gerzon vector all-pass filter is used to simulate diffusion. */
276 VecAllpass VecAp;
278 /* The gain for each output channel based on 3D panning. */
279 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
280 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
281 } LateReverb;
283 typedef struct ALreverbState {
284 DERIVE_FROM_TYPE(ALeffectState);
286 /* All delay lines are allocated as a single buffer to reduce memory
287 * fragmentation and management code.
289 ALfloat *SampleBuffer;
290 ALuint TotalSamples;
292 /* Master effect filters */
293 struct {
294 BiquadState Lp;
295 BiquadState Hp;
296 } Filter[NUM_LINES];
298 /* Core delay line (early reflections and late reverb tap from this). */
299 DelayLineI Delay;
301 /* Tap points for early reflection delay. */
302 ALsizei EarlyDelayTap[NUM_LINES][2];
303 ALfloat EarlyDelayCoeff[NUM_LINES];
305 /* Tap points for late reverb feed and delay. */
306 ALsizei LateFeedTap;
307 ALsizei LateDelayTap[NUM_LINES][2];
309 /* The feed-back and feed-forward all-pass coefficient. */
310 ALfloat ApFeedCoeff;
312 /* Coefficients for the all-pass and line scattering matrices. */
313 ALfloat MixX;
314 ALfloat MixY;
316 EarlyReflections Early;
318 LateReverb Late;
320 /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
321 ALsizei FadeCount;
323 /* The current write offset for all delay lines. */
324 ALsizei Offset;
326 /* Temporary storage used when processing. */
327 alignas(16) ALfloat AFormatSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
328 alignas(16) ALfloat ReverbSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
329 alignas(16) ALfloat EarlySamples[NUM_LINES][MAX_UPDATE_SAMPLES];
330 } ALreverbState;
332 static ALvoid ALreverbState_Destruct(ALreverbState *State);
333 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device);
334 static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
335 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
336 DECLARE_DEFAULT_ALLOCATORS(ALreverbState)
338 DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);
340 static void ALreverbState_Construct(ALreverbState *state)
342 ALsizei i, j;
344 ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
345 SET_VTABLE2(ALreverbState, ALeffectState, state);
347 state->TotalSamples = 0;
348 state->SampleBuffer = NULL;
350 for(i = 0;i < NUM_LINES;i++)
352 BiquadState_clear(&state->Filter[i].Lp);
353 BiquadState_clear(&state->Filter[i].Hp);
356 state->Delay.Mask = 0;
357 state->Delay.Line = NULL;
359 for(i = 0;i < NUM_LINES;i++)
361 state->EarlyDelayTap[i][0] = 0;
362 state->EarlyDelayTap[i][1] = 0;
363 state->EarlyDelayCoeff[i] = 0.0f;
366 state->LateFeedTap = 0;
368 for(i = 0;i < NUM_LINES;i++)
370 state->LateDelayTap[i][0] = 0;
371 state->LateDelayTap[i][1] = 0;
374 state->ApFeedCoeff = 0.0f;
375 state->MixX = 0.0f;
376 state->MixY = 0.0f;
378 state->Early.VecAp.Delay.Mask = 0;
379 state->Early.VecAp.Delay.Line = NULL;
380 state->Early.Delay.Mask = 0;
381 state->Early.Delay.Line = NULL;
382 for(i = 0;i < NUM_LINES;i++)
384 state->Early.VecAp.Offset[i][0] = 0;
385 state->Early.VecAp.Offset[i][1] = 0;
386 state->Early.Offset[i][0] = 0;
387 state->Early.Offset[i][1] = 0;
388 state->Early.Coeff[i] = 0.0f;
391 state->Late.DensityGain = 0.0f;
393 state->Late.Delay.Mask = 0;
394 state->Late.Delay.Line = NULL;
395 state->Late.VecAp.Delay.Mask = 0;
396 state->Late.VecAp.Delay.Line = NULL;
397 for(i = 0;i < NUM_LINES;i++)
399 state->Late.Offset[i][0] = 0;
400 state->Late.Offset[i][1] = 0;
402 state->Late.VecAp.Offset[i][0] = 0;
403 state->Late.VecAp.Offset[i][1] = 0;
405 for(j = 0;j < 3;j++)
407 state->Late.T60[i].HFCoeffs[j] = 0.0f;
408 state->Late.T60[i].LFCoeffs[j] = 0.0f;
410 state->Late.T60[i].HFState[0] = 0.0f;
411 state->Late.T60[i].HFState[1] = 0.0f;
412 state->Late.T60[i].LFState[0] = 0.0f;
413 state->Late.T60[i].LFState[1] = 0.0f;
416 for(i = 0;i < NUM_LINES;i++)
418 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
420 state->Early.CurrentGain[i][j] = 0.0f;
421 state->Early.PanGain[i][j] = 0.0f;
422 state->Late.CurrentGain[i][j] = 0.0f;
423 state->Late.PanGain[i][j] = 0.0f;
427 state->FadeCount = 0;
428 state->Offset = 0;
431 static ALvoid ALreverbState_Destruct(ALreverbState *State)
433 al_free(State->SampleBuffer);
434 State->SampleBuffer = NULL;
436 ALeffectState_Destruct(STATIC_CAST(ALeffectState,State));
439 /**************************************
440 * Device Update *
441 **************************************/
443 static inline ALfloat CalcDelayLengthMult(ALfloat density)
445 return maxf(5.0f, cbrtf(density*DENSITY_SCALE));
448 /* Given the allocated sample buffer, this function updates each delay line
449 * offset.
451 static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay)
453 union {
454 ALfloat *f;
455 ALfloat (*f4)[NUM_LINES];
456 } u;
457 u.f = &sampleBuffer[(ptrdiff_t)Delay->Line * NUM_LINES];
458 Delay->Line = u.f4;
461 /* Calculate the length of a delay line and store its mask and offset. */
462 static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALuint frequency,
463 const ALuint extra, DelayLineI *Delay)
465 ALuint samples;
467 /* All line lengths are powers of 2, calculated from their lengths in
468 * seconds, rounded up.
470 samples = fastf2i(ceilf(length*frequency));
471 samples = NextPowerOf2(samples + extra);
473 /* All lines share a single sample buffer. */
474 Delay->Mask = samples - 1;
475 Delay->Line = (ALfloat(*)[NUM_LINES])offset;
477 /* Return the sample count for accumulation. */
478 return samples;
481 /* Calculates the delay line metrics and allocates the shared sample buffer
482 * for all lines given the sample rate (frequency). If an allocation failure
483 * occurs, it returns AL_FALSE.
485 static ALboolean AllocLines(const ALuint frequency, ALreverbState *State)
487 ALuint totalSamples, i;
488 ALfloat multiplier, length;
490 /* All delay line lengths are calculated to accomodate the full range of
491 * lengths given their respective paramters.
493 totalSamples = 0;
495 /* Multiplier for the maximum density value, i.e. density=1, which is
496 * actually the least density...
498 multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
500 /* The main delay length includes the maximum early reflection delay, the
501 * largest early tap width, the maximum late reverb delay, and the
502 * largest late tap width. Finally, it must also be extended by the
503 * update size (MAX_UPDATE_SAMPLES) for block processing.
505 length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier +
506 AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
507 (LATE_LINE_LENGTHS[NUM_LINES-1] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
508 totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES,
509 &State->Delay);
511 /* The early vector all-pass line. */
512 length = EARLY_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
513 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
514 &State->Early.VecAp.Delay);
516 /* The early reflection line. */
517 length = EARLY_LINE_LENGTHS[NUM_LINES-1] * multiplier;
518 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
519 &State->Early.Delay);
521 /* The late vector all-pass line. */
522 length = LATE_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
523 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
524 &State->Late.VecAp.Delay);
526 /* The late delay lines are calculated from the larger of the maximum
527 * density line length or the maximum echo time.
529 length = maxf(AL_EAXREVERB_MAX_ECHO_TIME, LATE_LINE_LENGTHS[NUM_LINES-1]*multiplier);
530 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
531 &State->Late.Delay);
533 if(totalSamples != State->TotalSamples)
535 ALfloat *newBuffer;
537 TRACE("New reverb buffer length: %ux4 samples\n", totalSamples);
538 newBuffer = al_calloc(16, sizeof(ALfloat[NUM_LINES]) * totalSamples);
539 if(!newBuffer) return AL_FALSE;
541 al_free(State->SampleBuffer);
542 State->SampleBuffer = newBuffer;
543 State->TotalSamples = totalSamples;
546 /* Update all delays to reflect the new sample buffer. */
547 RealizeLineOffset(State->SampleBuffer, &State->Delay);
548 RealizeLineOffset(State->SampleBuffer, &State->Early.VecAp.Delay);
549 RealizeLineOffset(State->SampleBuffer, &State->Early.Delay);
550 RealizeLineOffset(State->SampleBuffer, &State->Late.VecAp.Delay);
551 RealizeLineOffset(State->SampleBuffer, &State->Late.Delay);
553 /* Clear the sample buffer. */
554 for(i = 0;i < State->TotalSamples;i++)
555 State->SampleBuffer[i] = 0.0f;
557 return AL_TRUE;
560 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
562 ALuint frequency = Device->Frequency;
563 ALfloat multiplier;
565 /* Allocate the delay lines. */
566 if(!AllocLines(frequency, State))
567 return AL_FALSE;
569 multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
571 /* The late feed taps are set a fixed position past the latest delay tap. */
572 State->LateFeedTap = fastf2i((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
573 EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier) *
574 frequency);
576 return AL_TRUE;
579 /**************************************
580 * Effect Update *
581 **************************************/
583 /* Calculate a decay coefficient given the length of each cycle and the time
584 * until the decay reaches -60 dB.
586 static inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
588 return powf(REVERB_DECAY_GAIN, length/decayTime);
591 /* Calculate a decay length from a coefficient and the time until the decay
592 * reaches -60 dB.
594 static inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
596 return log10f(coeff) * decayTime / log10f(REVERB_DECAY_GAIN);
599 /* Calculate an attenuation to be applied to the input of any echo models to
600 * compensate for modal density and decay time.
602 static inline ALfloat CalcDensityGain(const ALfloat a)
604 /* The energy of a signal can be obtained by finding the area under the
605 * squared signal. This takes the form of Sum(x_n^2), where x is the
606 * amplitude for the sample n.
608 * Decaying feedback matches exponential decay of the form Sum(a^n),
609 * where a is the attenuation coefficient, and n is the sample. The area
610 * under this decay curve can be calculated as: 1 / (1 - a).
612 * Modifying the above equation to find the area under the squared curve
613 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
614 * calculated by inverting the square root of this approximation,
615 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
617 return sqrtf(1.0f - a*a);
620 /* Calculate the scattering matrix coefficients given a diffusion factor. */
621 static inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
623 ALfloat n, t;
625 /* The matrix is of order 4, so n is sqrt(4 - 1). */
626 n = sqrtf(3.0f);
627 t = diffusion * atanf(n);
629 /* Calculate the first mixing matrix coefficient. */
630 *x = cosf(t);
631 /* Calculate the second mixing matrix coefficient. */
632 *y = sinf(t) / n;
635 /* Calculate the limited HF ratio for use with the late reverb low-pass
636 * filters.
638 static ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
639 const ALfloat decayTime, const ALfloat SpeedOfSound)
641 ALfloat limitRatio;
643 /* Find the attenuation due to air absorption in dB (converting delay
644 * time to meters using the speed of sound). Then reversing the decay
645 * equation, solve for HF ratio. The delay length is cancelled out of
646 * the equation, so it can be calculated once for all lines.
648 limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound);
650 /* Using the limit calculated above, apply the upper bound to the HF ratio.
652 return minf(limitRatio, hfRatio);
655 /* Calculates the first-order high-pass coefficients following the I3DL2
656 * reference model. This is the transfer function:
658 * 1 - z^-1
659 * H(z) = p ------------
660 * 1 - p z^-1
662 * And this is the I3DL2 coefficient calculation given gain (g) and reference
663 * angular frequency (w):
666 * p = ------------------------------------------------------
667 * g cos(w) + sqrt((cos(w) - 1) (g^2 cos(w) + g^2 - 2))
669 * The coefficient is applied to the partial differential filter equation as:
671 * c_0 = p
672 * c_1 = -p
673 * c_2 = p
674 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
677 static inline void CalcHighpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
679 ALfloat g, g2, cw, p;
681 if(gain >= 1.0f)
683 coeffs[0] = 1.0f;
684 coeffs[1] = 0.0f;
685 coeffs[2] = 0.0f;
686 return;
689 g = maxf(0.001f, gain);
690 g2 = g * g;
691 cw = cosf(w);
692 p = g / (g*cw + sqrtf((cw - 1.0f) * (g2*cw + g2 - 2.0f)));
694 coeffs[0] = p;
695 coeffs[1] = -p;
696 coeffs[2] = p;
699 /* Calculates the first-order low-pass coefficients following the I3DL2
700 * reference model. This is the transfer function:
702 * (1 - a) z^0
703 * H(z) = ----------------
704 * 1 z^0 - a z^-1
706 * And this is the I3DL2 coefficient calculation given gain (g) and reference
707 * angular frequency (w):
709 * 1 - g^2 cos(w) - sqrt(2 g^2 (1 - cos(w)) - g^4 (1 - cos(w)^2))
710 * a = ----------------------------------------------------------------
711 * 1 - g^2
713 * The coefficient is applied to the partial differential filter equation as:
715 * c_0 = 1 - a
716 * c_1 = 0
717 * c_2 = a
718 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
721 static inline void CalcLowpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
723 ALfloat g, g2, cw, a;
725 if(gain >= 1.0f)
727 coeffs[0] = 1.0f;
728 coeffs[1] = 0.0f;
729 coeffs[2] = 0.0f;
730 return;
733 /* Be careful with gains < 0.001, as that causes the coefficient
734 * to head towards 1, which will flatten the signal. */
735 g = maxf(0.001f, gain);
736 g2 = g * g;
737 cw = cosf(w);
738 a = (1.0f - g2*cw - sqrtf((2.0f*g2*(1.0f - cw)) - g2*g2*(1.0f - cw*cw))) /
739 (1.0f - g2);
741 coeffs[0] = 1.0f - a;
742 coeffs[1] = 0.0f;
743 coeffs[2] = a;
746 /* Calculates the first-order low-shelf coefficients. The shelf filters are
747 * used in place of low/high-pass filters to preserve the mid-band. This is
748 * the transfer function:
750 * a_0 + a_1 z^-1
751 * H(z) = ----------------
752 * 1 + b_1 z^-1
754 * And these are the coefficient calculations given cut gain (g) and a center
755 * angular frequency (w):
757 * sin(0.5 (pi - w) - 0.25 pi)
758 * p = -----------------------------
759 * sin(0.5 (pi - w) + 0.25 pi)
761 * g + 1 g + 1
762 * a = ------- + sqrt((-------)^2 - 1)
763 * g - 1 g - 1
765 * 1 + g + (1 - g) a
766 * b_0 = -------------------
769 * 1 - g + (1 + g) a
770 * b_1 = -------------------
773 * The coefficients are applied to the partial differential filter equation
774 * as:
776 * b_0 + p b_1
777 * c_0 = -------------
778 * 1 + p a
780 * -(b_1 + p b_0)
781 * c_1 = ----------------
782 * 1 + p a
784 * p + a
785 * c_2 = ---------
786 * 1 + p a
788 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
791 static inline void CalcLowShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
793 ALfloat g, rw, p, n;
794 ALfloat alpha, beta0, beta1;
796 if(gain >= 1.0f)
798 coeffs[0] = 1.0f;
799 coeffs[1] = 0.0f;
800 coeffs[2] = 0.0f;
801 return;
804 g = maxf(0.001f, gain);
805 rw = F_PI - w;
806 p = sinf(0.5f*rw - 0.25f*F_PI) / sinf(0.5f*rw + 0.25f*F_PI);
807 n = (g + 1.0f) / (g - 1.0f);
808 alpha = n + sqrtf(n*n - 1.0f);
809 beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f;
810 beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f;
812 coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha);
813 coeffs[1] = -(beta1 + p*beta0) / (1.0f + p*alpha);
814 coeffs[2] = (p + alpha) / (1.0f + p*alpha);
817 /* Calculates the first-order high-shelf coefficients. The shelf filters are
818 * used in place of low/high-pass filters to preserve the mid-band. This is
819 * the transfer function:
821 * a_0 + a_1 z^-1
822 * H(z) = ----------------
823 * 1 + b_1 z^-1
825 * And these are the coefficient calculations given cut gain (g) and a center
826 * angular frequency (w):
828 * sin(0.5 w - 0.25 pi)
829 * p = ----------------------
830 * sin(0.5 w + 0.25 pi)
832 * g + 1 g + 1
833 * a = ------- + sqrt((-------)^2 - 1)
834 * g - 1 g - 1
836 * 1 + g + (1 - g) a
837 * b_0 = -------------------
840 * 1 - g + (1 + g) a
841 * b_1 = -------------------
844 * The coefficients are applied to the partial differential filter equation
845 * as:
847 * b_0 + p b_1
848 * c_0 = -------------
849 * 1 + p a
851 * b_1 + p b_0
852 * c_1 = -------------
853 * 1 + p a
855 * -(p + a)
856 * c_2 = ----------
857 * 1 + p a
859 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
862 static inline void CalcHighShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
864 ALfloat g, p, n;
865 ALfloat alpha, beta0, beta1;
867 if(gain >= 1.0f)
869 coeffs[0] = 1.0f;
870 coeffs[1] = 0.0f;
871 coeffs[2] = 0.0f;
872 return;
875 g = maxf(0.001f, gain);
876 p = sinf(0.5f*w - 0.25f*F_PI) / sinf(0.5f*w + 0.25f*F_PI);
877 n = (g + 1.0f) / (g - 1.0f);
878 alpha = n + sqrtf(n*n - 1.0f);
879 beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f;
880 beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f;
882 coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha);
883 coeffs[1] = (beta1 + p*beta0) / (1.0f + p*alpha);
884 coeffs[2] = -(p + alpha) / (1.0f + p*alpha);
887 /* Calculates the 3-band T60 damping coefficients for a particular delay line
888 * of specified length using a combination of two low/high-pass/shelf or
889 * pass-through filter sections (producing 3 coefficients each) given decay
890 * times for each band split at two (LF/HF) reference frequencies (w).
892 static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime,
893 const ALfloat mfDecayTime, const ALfloat hfDecayTime,
894 const ALfloat lfW, const ALfloat hfW, ALfloat lfcoeffs[3],
895 ALfloat hfcoeffs[3])
897 ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime);
898 ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime);
899 ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime);
901 if(lfGain <= mfGain)
903 CalcHighpassCoeffs(lfGain / mfGain, lfW, lfcoeffs);
904 if(mfGain >= hfGain)
906 CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs);
907 hfcoeffs[0] *= mfGain; hfcoeffs[1] *= mfGain;
909 else
911 CalcLowShelfCoeffs(mfGain / hfGain, hfW, hfcoeffs);
912 hfcoeffs[0] *= hfGain; hfcoeffs[1] *= hfGain;
915 else
917 CalcHighShelfCoeffs(mfGain / lfGain, lfW, lfcoeffs);
918 if(mfGain >= hfGain)
920 CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs);
921 hfcoeffs[0] *= lfGain; hfcoeffs[1] *= lfGain;
923 else
925 ALfloat hg = mfGain / lfGain;
926 ALfloat lg = mfGain / hfGain;
927 ALfloat mg = maxf(lfGain, hfGain) / maxf(hg, lg);
929 CalcLowShelfCoeffs(lg, hfW, hfcoeffs);
930 hfcoeffs[0] *= mg; hfcoeffs[1] *= mg;
935 /* Update the offsets for the main effect delay line. */
936 static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ALreverbState *State)
938 ALfloat multiplier, length;
939 ALuint i;
941 multiplier = CalcDelayLengthMult(density);
943 /* Early reflection taps are decorrelated by means of an average room
944 * reflection approximation described above the definition of the taps.
945 * This approximation is linear and so the above density multiplier can
946 * be applied to adjust the width of the taps. A single-band decay
947 * coefficient is applied to simulate initial attenuation and absorption.
949 * Late reverb taps are based on the late line lengths to allow a zero-
950 * delay path and offsets that would continue the propagation naturally
951 * into the late lines.
953 for(i = 0;i < NUM_LINES;i++)
955 length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
956 State->EarlyDelayTap[i][1] = fastf2i(length * frequency);
958 length = EARLY_TAP_LENGTHS[i]*multiplier;
959 State->EarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime);
961 length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
962 State->LateDelayTap[i][1] = State->LateFeedTap + fastf2i(length * frequency);
966 /* Update the early reflection line lengths and gain coefficients. */
967 static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat decayTime, const ALuint frequency, EarlyReflections *Early)
969 ALfloat multiplier, length;
970 ALsizei i;
972 multiplier = CalcDelayLengthMult(density);
974 for(i = 0;i < NUM_LINES;i++)
976 /* Calculate the length (in seconds) of each all-pass line. */
977 length = EARLY_ALLPASS_LENGTHS[i] * multiplier;
979 /* Calculate the delay offset for each all-pass line. */
980 Early->VecAp.Offset[i][1] = fastf2i(length * frequency);
982 /* Calculate the length (in seconds) of each delay line. */
983 length = EARLY_LINE_LENGTHS[i] * multiplier;
985 /* Calculate the delay offset for each delay line. */
986 Early->Offset[i][1] = fastf2i(length * frequency);
988 /* Calculate the gain (coefficient) for each line. */
989 Early->Coeff[i] = CalcDecayCoeff(length, decayTime);
993 /* Update the late reverb line lengths and T60 coefficients. */
994 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)
996 ALfloat multiplier, length, bandWeights[3];
997 ALsizei i;
999 /* To compensate for changes in modal density and decay time of the late
1000 * reverb signal, the input is attenuated based on the maximal energy of
1001 * the outgoing signal. This approximation is used to keep the apparent
1002 * energy of the signal equal for all ranges of density and decay time.
1004 * The average length of the delay lines is used to calculate the
1005 * attenuation coefficient.
1007 multiplier = CalcDelayLengthMult(density);
1008 length = (LATE_LINE_LENGTHS[0] + LATE_LINE_LENGTHS[1] +
1009 LATE_LINE_LENGTHS[2] + LATE_LINE_LENGTHS[3]) / 4.0f * multiplier;
1010 /* Include the echo transformation (see below). */
1011 length = lerp(length, echoTime, echoDepth);
1012 length += (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
1013 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f * multiplier;
1014 /* The density gain calculation uses an average decay time weighted by
1015 * approximate bandwidth. This attempts to compensate for losses of
1016 * energy that reduce decay time due to scattering into highly attenuated
1017 * bands.
1019 bandWeights[0] = lfW;
1020 bandWeights[1] = hfW - lfW;
1021 bandWeights[2] = F_TAU - hfW;
1022 Late->DensityGain = CalcDensityGain(
1023 CalcDecayCoeff(length, (bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime +
1024 bandWeights[2]*hfDecayTime) / F_TAU)
1027 for(i = 0;i < NUM_LINES;i++)
1029 /* Calculate the length (in seconds) of each all-pass line. */
1030 length = LATE_ALLPASS_LENGTHS[i] * multiplier;
1032 /* Calculate the delay offset for each all-pass line. */
1033 Late->VecAp.Offset[i][1] = fastf2i(length * frequency);
1035 /* Calculate the length (in seconds) of each delay line. This also
1036 * applies the echo transformation. As the EAX echo depth approaches
1037 * 1, the line lengths approach a length equal to the echoTime. This
1038 * helps to produce distinct echoes along the tail.
1040 length = lerp(LATE_LINE_LENGTHS[i] * multiplier, echoTime, echoDepth);
1042 /* Calculate the delay offset for each delay line. */
1043 Late->Offset[i][1] = fastf2i(length*frequency + 0.5f);
1045 /* Approximate the absorption that the vector all-pass would exhibit
1046 * given the current diffusion so we don't have to process a full T60
1047 * filter for each of its four lines.
1049 length += lerp(LATE_ALLPASS_LENGTHS[i],
1050 (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
1051 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f,
1052 diffusion) * multiplier;
1054 /* Calculate the T60 damping coefficients for each line. */
1055 CalcT60DampingCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime,
1056 lfW, hfW, Late->T60[i].LFCoeffs,
1057 Late->T60[i].HFCoeffs);
1061 /* Creates a transform matrix given a reverb vector. The vector pans the reverb
1062 * reflections toward the given direction, using its magnitude (up to 1) as a
1063 * focal strength. This function results in a B-Format transformation matrix
1064 * that spatially focuses the signal in the desired direction.
1066 static aluMatrixf GetTransformFromVector(const ALfloat *vec)
1068 const ALfloat sqrt_3 = 1.732050808f;
1069 aluMatrixf focus;
1070 ALfloat norm[3];
1071 ALfloat mag;
1073 /* Normalize the panning vector according to the N3D scale, which has an
1074 * extra sqrt(3) term on the directional components. Converting from OpenAL
1075 * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
1076 * that the reverb panning vectors use right-handed coordinates, unlike the
1077 * rest of OpenAL which use left-handed. This is fixed by negating Z, which
1078 * cancels out with the B-Format Z negation.
1080 mag = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
1081 if(mag > 1.0f)
1083 norm[0] = vec[0] / mag * -sqrt_3;
1084 norm[1] = vec[1] / mag * sqrt_3;
1085 norm[2] = vec[2] / mag * sqrt_3;
1086 mag = 1.0f;
1088 else
1090 /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
1091 * term. There's no need to renormalize the magnitude since it would
1092 * just be reapplied in the matrix.
1094 norm[0] = vec[0] * -sqrt_3;
1095 norm[1] = vec[1] * sqrt_3;
1096 norm[2] = vec[2] * sqrt_3;
1099 aluMatrixfSet(&focus,
1100 1.0f, 0.0f, 0.0f, 0.0f,
1101 norm[0], 1.0f-mag, 0.0f, 0.0f,
1102 norm[1], 0.0f, 1.0f-mag, 0.0f,
1103 norm[2], 0.0f, 0.0f, 1.0f-mag
1106 return focus;
1109 /* Update the early and late 3D panning gains. */
1110 static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat gain, const ALfloat earlyGain, const ALfloat lateGain, ALreverbState *State)
1112 aluMatrixf transform, rot;
1113 ALsizei i;
1115 STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
1116 STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;
1118 /* Note: _res is transposed. */
1119 #define MATRIX_MULT(_res, _m1, _m2) do { \
1120 int row, col; \
1121 for(col = 0;col < 4;col++) \
1123 for(row = 0;row < 4;row++) \
1124 _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
1125 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
1127 } while(0)
1128 /* Create a matrix that first converts A-Format to B-Format, then
1129 * transforms the B-Format signal according to the panning vector.
1131 rot = GetTransformFromVector(ReflectionsPan);
1132 MATRIX_MULT(transform, rot, A2B);
1133 memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
1134 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
1135 ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], gain*earlyGain,
1136 State->Early.PanGain[i]);
1138 rot = GetTransformFromVector(LateReverbPan);
1139 MATRIX_MULT(transform, rot, A2B);
1140 memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
1141 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
1142 ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], gain*lateGain,
1143 State->Late.PanGain[i]);
1144 #undef MATRIX_MULT
1147 static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
1149 const ALCdevice *Device = Context->Device;
1150 const ALlistener *Listener = Context->Listener;
1151 ALuint frequency = Device->Frequency;
1152 ALfloat lf0norm, hf0norm, hfRatio;
1153 ALfloat lfDecayTime, hfDecayTime;
1154 ALfloat gain, gainlf, gainhf;
1155 ALsizei i;
1157 /* Calculate the master filters */
1158 hf0norm = props->Reverb.HFReference / frequency;
1159 /* Restrict the filter gains from going below -60dB to keep the filter from
1160 * killing most of the signal.
1162 gainhf = maxf(props->Reverb.GainHF, 0.001f);
1163 BiquadState_setParams(&State->Filter[0].Lp, BiquadType_HighShelf, gainhf, hf0norm,
1164 calc_rcpQ_from_slope(gainhf, 1.0f));
1165 lf0norm = props->Reverb.LFReference / frequency;
1166 gainlf = maxf(props->Reverb.GainLF, 0.001f);
1167 BiquadState_setParams(&State->Filter[0].Hp, BiquadType_LowShelf, gainlf, lf0norm,
1168 calc_rcpQ_from_slope(gainlf, 1.0f));
1169 for(i = 1;i < NUM_LINES;i++)
1171 BiquadState_copyParams(&State->Filter[i].Lp, &State->Filter[0].Lp);
1172 BiquadState_copyParams(&State->Filter[i].Hp, &State->Filter[0].Hp);
1175 /* Update the main effect delay and associated taps. */
1176 UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
1177 props->Reverb.Density, props->Reverb.DecayTime, frequency,
1178 State);
1180 /* Calculate the all-pass feed-back/forward coefficient. */
1181 State->ApFeedCoeff = sqrtf(0.5f) * powf(props->Reverb.Diffusion, 2.0f);
1183 /* Update the early lines. */
1184 UpdateEarlyLines(props->Reverb.Density, props->Reverb.DecayTime,
1185 frequency, &State->Early);
1187 /* Get the mixing matrix coefficients. */
1188 CalcMatrixCoeffs(props->Reverb.Diffusion, &State->MixX, &State->MixY);
1190 /* If the HF limit parameter is flagged, calculate an appropriate limit
1191 * based on the air absorption parameter.
1193 hfRatio = props->Reverb.DecayHFRatio;
1194 if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
1195 hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
1196 props->Reverb.DecayTime, Listener->Params.ReverbSpeedOfSound
1199 /* Calculate the LF/HF decay times. */
1200 lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
1201 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1202 hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
1203 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1205 /* Update the late lines. */
1206 UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
1207 lfDecayTime, props->Reverb.DecayTime, hfDecayTime,
1208 F_TAU * lf0norm, F_TAU * hf0norm,
1209 props->Reverb.EchoTime, props->Reverb.EchoDepth,
1210 frequency, &State->Late);
1212 /* Update early and late 3D panning. */
1213 gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
1214 Update3DPanning(Device, props->Reverb.ReflectionsPan,
1215 props->Reverb.LateReverbPan, gain,
1216 props->Reverb.ReflectionsGain,
1217 props->Reverb.LateReverbGain, State);
1219 /* Determine if delay-line cross-fading is required. */
1220 for(i = 0;i < NUM_LINES;i++)
1222 if(State->EarlyDelayTap[i][1] != State->EarlyDelayTap[i][0] ||
1223 State->Early.VecAp.Offset[i][1] != State->Early.VecAp.Offset[i][0] ||
1224 State->Early.Offset[i][1] != State->Early.Offset[i][0] ||
1225 State->LateDelayTap[i][1] != State->LateDelayTap[i][0] ||
1226 State->Late.VecAp.Offset[i][1] != State->Late.VecAp.Offset[i][0] ||
1227 State->Late.Offset[i][1] != State->Late.Offset[i][0])
1229 State->FadeCount = 0;
1230 break;
1236 /**************************************
1237 * Effect Processing *
1238 **************************************/
1240 /* Basic delay line input/output routines. */
1241 static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c)
1243 return Delay->Line[offset&Delay->Mask][c];
1246 /* Cross-faded delay line output routine. Instead of interpolating the
1247 * offsets, this interpolates (cross-fades) the outputs at each offset.
1249 static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0,
1250 const ALsizei off1, const ALsizei c, const ALfloat mu)
1252 return Delay->Line[off0&Delay->Mask][c]*(1.0f-mu) +
1253 Delay->Line[off1&Delay->Mask][c]*( mu);
1255 #define UnfadedDelayLineOut(d, o0, o1, c, mu) DelayLineOut(d, o0, c)
1257 static inline ALvoid DelayLineIn(DelayLineI *Delay, ALsizei offset, const ALsizei c,
1258 const ALfloat *restrict in, ALsizei count)
1260 ALsizei i;
1261 for(i = 0;i < count;i++)
1262 Delay->Line[(offset++)&Delay->Mask][c] = *(in++);
1265 static inline ALvoid DelayLineIn4(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
1267 ALsizei i;
1268 offset &= Delay->Mask;
1269 for(i = 0;i < NUM_LINES;i++)
1270 Delay->Line[offset][i] = in[i];
1273 static inline ALvoid DelayLineIn4Rev(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
1275 ALsizei i;
1276 offset &= Delay->Mask;
1277 for(i = 0;i < NUM_LINES;i++)
1278 Delay->Line[offset][i] = in[NUM_LINES-1-i];
1281 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1282 * for both the below vector all-pass model and to perform modal feed-back
1283 * delay network (FDN) mixing.
1285 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1286 * matrix with a single unitary rotational parameter:
1288 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1289 * [ -a, d, c, -b ]
1290 * [ -b, -c, d, a ]
1291 * [ -c, b, -a, d ]
1293 * The rotation is constructed from the effect's diffusion parameter,
1294 * yielding:
1296 * 1 = x^2 + 3 y^2
1298 * Where a, b, and c are the coefficient y with differing signs, and d is the
1299 * coefficient x. The final matrix is thus:
1301 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1302 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1303 * [ y, -y, x, y ] x = cos(t)
1304 * [ -y, -y, -y, x ] y = sin(t) / n
1306 * Any square orthogonal matrix with an order that is a power of two will
1307 * work (where ^T is transpose, ^-1 is inverse):
1309 * M^T = M^-1
1311 * Using that knowledge, finding an appropriate matrix can be accomplished
1312 * naively by searching all combinations of:
1314 * M = D + S - S^T
1316 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1317 * whose combination of signs are being iterated.
1319 static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *restrict in,
1320 const ALfloat xCoeff, const ALfloat yCoeff)
1322 out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
1323 out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
1324 out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
1325 out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
1328 /* Same as above, but reverses the input. */
1329 static inline void VectorPartialScatterRev(ALfloat *restrict out, const ALfloat *restrict in,
1330 const ALfloat xCoeff, const ALfloat yCoeff)
1332 out[0] = xCoeff*in[3] + yCoeff*(in[0] + -in[1] + in[2] );
1333 out[1] = xCoeff*in[2] + yCoeff*(in[0] + in[1] + -in[3]);
1334 out[2] = xCoeff*in[1] + yCoeff*(in[0] + -in[2] + in[3]);
1335 out[3] = xCoeff*in[0] + yCoeff*( -in[1] + -in[2] + -in[3]);
1338 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1339 * filter to the 4-line input.
1341 * It works by vectorizing a regular all-pass filter and replacing the delay
1342 * element with a scattering matrix (like the one above) and a diagonal
1343 * matrix of delay elements.
1345 * Two static specializations are used for transitional (cross-faded) delay
1346 * line processing and non-transitional processing.
1348 #define DECL_TEMPLATE(T) \
1349 static void VectorAllpass_##T(ALfloat *restrict out, \
1350 const ALfloat *restrict in, \
1351 const ALsizei offset, const ALfloat feedCoeff, \
1352 const ALfloat xCoeff, const ALfloat yCoeff, \
1353 const ALfloat mu, VecAllpass *Vap) \
1355 ALfloat f[NUM_LINES], fs[NUM_LINES]; \
1356 ALfloat input; \
1357 ALsizei i; \
1359 (void)mu; /* Ignore for Unfaded. */ \
1361 for(i = 0;i < NUM_LINES;i++) \
1363 input = in[i]; \
1364 out[i] = T##DelayLineOut(&Vap->Delay, offset-Vap->Offset[i][0], \
1365 offset-Vap->Offset[i][1], i, mu) - \
1366 feedCoeff*input; \
1367 f[i] = input + feedCoeff*out[i]; \
1369 VectorPartialScatter(fs, f, xCoeff, yCoeff); \
1371 DelayLineIn4(&Vap->Delay, offset, fs); \
1373 DECL_TEMPLATE(Unfaded)
1374 DECL_TEMPLATE(Faded)
1375 #undef DECL_TEMPLATE
1377 /* This generates early reflections.
1379 * This is done by obtaining the primary reflections (those arriving from the
1380 * same direction as the source) from the main delay line. These are
1381 * attenuated and all-pass filtered (based on the diffusion parameter).
1383 * The early lines are then fed in reverse (according to the approximately
1384 * opposite spatial location of the A-Format lines) to create the secondary
1385 * reflections (those arriving from the opposite direction as the source).
1387 * The early response is then completed by combining the primary reflections
1388 * with the delayed and attenuated output from the early lines.
1390 * Finally, the early response is reversed, scattered (based on diffusion),
1391 * and fed into the late reverb section of the main delay line.
1393 * Two static specializations are used for transitional (cross-faded) delay
1394 * line processing and non-transitional processing.
1396 #define DECL_TEMPLATE(T) \
1397 static void EarlyReflection_##T(ALreverbState *State, const ALsizei todo, \
1398 ALfloat fade, \
1399 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) \
1401 ALsizei offset = State->Offset; \
1402 const ALfloat apFeedCoeff = State->ApFeedCoeff; \
1403 const ALfloat mixX = State->MixX; \
1404 const ALfloat mixY = State->MixY; \
1405 ALfloat f[NUM_LINES], fr[NUM_LINES]; \
1406 ALsizei i, j; \
1408 for(i = 0;i < todo;i++) \
1410 for(j = 0;j < NUM_LINES;j++) \
1411 fr[j] = T##DelayLineOut(&State->Delay, \
1412 offset-State->EarlyDelayTap[j][0], \
1413 offset-State->EarlyDelayTap[j][1], j, fade \
1414 ) * State->EarlyDelayCoeff[j]; \
1416 VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \
1417 &State->Early.VecAp); \
1419 DelayLineIn4Rev(&State->Early.Delay, offset, f); \
1421 for(j = 0;j < NUM_LINES;j++) \
1422 f[j] += T##DelayLineOut(&State->Early.Delay, \
1423 offset-State->Early.Offset[j][0], \
1424 offset-State->Early.Offset[j][1], j, fade \
1425 ) * State->Early.Coeff[j]; \
1427 for(j = 0;j < NUM_LINES;j++) \
1428 out[j][i] = f[j]; \
1430 VectorPartialScatterRev(fr, f, mixX, mixY); \
1432 DelayLineIn4(&State->Delay, offset-State->LateFeedTap, fr); \
1434 offset++; \
1435 fade += FadeStep; \
1438 DECL_TEMPLATE(Unfaded)
1439 DECL_TEMPLATE(Faded)
1440 #undef DECL_TEMPLATE
1442 /* Applies a first order filter section. */
1443 static inline ALfloat FirstOrderFilter(const ALfloat in, const ALfloat *restrict coeffs,
1444 ALfloat *restrict state)
1446 ALfloat out = coeffs[0]*in + coeffs[1]*state[0] + coeffs[2]*state[1];
1447 state[0] = in;
1448 state[1] = out;
1449 return out;
1452 /* Applies the two T60 damping filter sections. */
1453 static inline void LateT60Filter(ALfloat *restrict out, const ALfloat *restrict in,
1454 T60Filter *filter)
1456 ALsizei i;
1457 for(i = 0;i < NUM_LINES;i++)
1458 out[i] = FirstOrderFilter(
1459 FirstOrderFilter(in[i], filter[i].HFCoeffs, filter[i].HFState),
1460 filter[i].LFCoeffs, filter[i].LFState
1464 /* This generates the reverb tail using a modified feed-back delay network
1465 * (FDN).
1467 * Results from the early reflections are attenuated by the density gain and
1468 * mixed with the output from the late delay lines.
1470 * The late response is then completed by T60 and all-pass filtering the mix.
1472 * Finally, the lines are reversed (so they feed their opposite directions)
1473 * and scattered with the FDN matrix before re-feeding the delay lines.
1475 * Two variations are made, one for for transitional (cross-faded) delay line
1476 * processing and one for non-transitional processing.
1478 #define DECL_TEMPLATE(T) \
1479 static void LateReverb_##T(ALreverbState *State, const ALsizei todo, \
1480 ALfloat fade, \
1481 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) \
1483 const ALfloat apFeedCoeff = State->ApFeedCoeff; \
1484 const ALfloat mixX = State->MixX; \
1485 const ALfloat mixY = State->MixY; \
1486 ALsizei offset; \
1487 ALsizei i, j; \
1489 offset = State->Offset; \
1490 for(i = 0;i < todo;i++) \
1492 ALfloat f[NUM_LINES], fr[NUM_LINES]; \
1494 for(j = 0;j < NUM_LINES;j++) \
1495 f[j] = T##DelayLineOut(&State->Delay, \
1496 offset - State->LateDelayTap[j][0], \
1497 offset - State->LateDelayTap[j][1], j, fade \
1498 ) * State->Late.DensityGain; \
1500 for(j = 0;j < NUM_LINES;j++) \
1501 f[j] += T##DelayLineOut(&State->Late.Delay, \
1502 offset - State->Late.Offset[j][0], \
1503 offset - State->Late.Offset[j][1], j, fade \
1504 ); \
1506 LateT60Filter(fr, f, State->Late.T60); \
1507 VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \
1508 &State->Late.VecAp); \
1510 for(j = 0;j < NUM_LINES;j++) \
1511 out[j][i] = f[j]; \
1513 VectorPartialScatterRev(fr, f, mixX, mixY); \
1515 DelayLineIn4(&State->Late.Delay, offset, fr); \
1517 offset++; \
1518 fade += FadeStep; \
1521 DECL_TEMPLATE(Unfaded)
1522 DECL_TEMPLATE(Faded)
1523 #undef DECL_TEMPLATE
1525 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
1527 ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->AFormatSamples;
1528 ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples;
1529 ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples;
1530 ALsizei fadeCount = State->FadeCount;
1531 ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES;
1532 ALsizei base, c;
1534 /* Process reverb for these samples. */
1535 for(base = 0;base < SamplesToDo;)
1537 ALsizei todo = mini(SamplesToDo-base, MAX_UPDATE_SAMPLES);
1538 /* If cross-fading, don't do more samples than there are to fade. */
1539 if(FADE_SAMPLES-fadeCount > 0)
1540 todo = mini(todo, FADE_SAMPLES-fadeCount);
1542 /* Convert B-Format to A-Format for processing. */
1543 memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
1544 for(c = 0;c < NUM_LINES;c++)
1545 MixRowSamples(afmt[c], B2A.m[c],
1546 SamplesIn, MAX_EFFECT_CHANNELS, base, todo
1549 /* Process the samples for reverb. */
1550 for(c = 0;c < NUM_LINES;c++)
1552 /* Band-pass the incoming samples. Use the early output lines for
1553 * temp storage.
1555 BiquadState_process(&State->Filter[c].Lp, early[0], afmt[c], todo);
1556 BiquadState_process(&State->Filter[c].Hp, early[1], early[0], todo);
1558 /* Feed the initial delay line. */
1559 DelayLineIn(&State->Delay, State->Offset, c, early[1], todo);
1562 if(UNLIKELY(fadeCount < FADE_SAMPLES))
1564 /* Generate early reflections. */
1565 EarlyReflection_Faded(State, todo, fade, early);
1567 /* Generate late reverb. */
1568 LateReverb_Faded(State, todo, fade, late);
1569 fade = minf(1.0f, fade + todo*FadeStep);
1571 else
1573 /* Generate early reflections. */
1574 EarlyReflection_Unfaded(State, todo, fade, early);
1576 /* Generate late reverb. */
1577 LateReverb_Unfaded(State, todo, fade, late);
1580 /* Step all delays forward. */
1581 State->Offset += todo;
1583 if(UNLIKELY(fadeCount < FADE_SAMPLES) && (fadeCount += todo) >= FADE_SAMPLES)
1585 /* Update the cross-fading delay line taps. */
1586 fadeCount = FADE_SAMPLES;
1587 fade = 1.0f;
1588 for(c = 0;c < NUM_LINES;c++)
1590 State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
1591 State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1];
1592 State->Early.Offset[c][0] = State->Early.Offset[c][1];
1593 State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
1594 State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1];
1595 State->Late.Offset[c][0] = State->Late.Offset[c][1];
1599 /* Mix the A-Format results to output, implicitly converting back to
1600 * B-Format.
1602 for(c = 0;c < NUM_LINES;c++)
1603 MixSamples(early[c], NumChannels, SamplesOut,
1604 State->Early.CurrentGain[c], State->Early.PanGain[c],
1605 SamplesToDo-base, base, todo
1607 for(c = 0;c < NUM_LINES;c++)
1608 MixSamples(late[c], NumChannels, SamplesOut,
1609 State->Late.CurrentGain[c], State->Late.PanGain[c],
1610 SamplesToDo-base, base, todo
1613 base += todo;
1615 State->FadeCount = fadeCount;
1619 typedef struct ReverbStateFactory {
1620 DERIVE_FROM_TYPE(EffectStateFactory);
1621 } ReverbStateFactory;
1623 static ALeffectState *ReverbStateFactory_create(ReverbStateFactory* UNUSED(factory))
1625 ALreverbState *state;
1627 NEW_OBJ0(state, ALreverbState)();
1628 if(!state) return NULL;
1630 return STATIC_CAST(ALeffectState, state);
1633 DEFINE_EFFECTSTATEFACTORY_VTABLE(ReverbStateFactory);
1635 EffectStateFactory *ReverbStateFactory_getFactory(void)
1637 static ReverbStateFactory ReverbFactory = { { GET_VTABLE2(ReverbStateFactory, EffectStateFactory) } };
1639 return STATIC_CAST(EffectStateFactory, &ReverbFactory);
1643 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1645 ALeffectProps *props = &effect->Props;
1646 switch(param)
1648 case AL_EAXREVERB_DECAY_HFLIMIT:
1649 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1650 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
1651 props->Reverb.DecayHFLimit = val;
1652 break;
1654 default:
1655 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1656 param);
1659 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1660 { ALeaxreverb_setParami(effect, context, param, vals[0]); }
1661 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1663 ALeffectProps *props = &effect->Props;
1664 switch(param)
1666 case AL_EAXREVERB_DENSITY:
1667 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1668 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
1669 props->Reverb.Density = val;
1670 break;
1672 case AL_EAXREVERB_DIFFUSION:
1673 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1674 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
1675 props->Reverb.Diffusion = val;
1676 break;
1678 case AL_EAXREVERB_GAIN:
1679 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1680 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
1681 props->Reverb.Gain = val;
1682 break;
1684 case AL_EAXREVERB_GAINHF:
1685 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1686 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
1687 props->Reverb.GainHF = val;
1688 break;
1690 case AL_EAXREVERB_GAINLF:
1691 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1692 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
1693 props->Reverb.GainLF = val;
1694 break;
1696 case AL_EAXREVERB_DECAY_TIME:
1697 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1698 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
1699 props->Reverb.DecayTime = val;
1700 break;
1702 case AL_EAXREVERB_DECAY_HFRATIO:
1703 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1704 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
1705 props->Reverb.DecayHFRatio = val;
1706 break;
1708 case AL_EAXREVERB_DECAY_LFRATIO:
1709 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1710 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
1711 props->Reverb.DecayLFRatio = val;
1712 break;
1714 case AL_EAXREVERB_REFLECTIONS_GAIN:
1715 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1716 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
1717 props->Reverb.ReflectionsGain = val;
1718 break;
1720 case AL_EAXREVERB_REFLECTIONS_DELAY:
1721 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1722 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
1723 props->Reverb.ReflectionsDelay = val;
1724 break;
1726 case AL_EAXREVERB_LATE_REVERB_GAIN:
1727 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1728 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
1729 props->Reverb.LateReverbGain = val;
1730 break;
1732 case AL_EAXREVERB_LATE_REVERB_DELAY:
1733 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1734 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
1735 props->Reverb.LateReverbDelay = val;
1736 break;
1738 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1739 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1740 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
1741 props->Reverb.AirAbsorptionGainHF = val;
1742 break;
1744 case AL_EAXREVERB_ECHO_TIME:
1745 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1746 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
1747 props->Reverb.EchoTime = val;
1748 break;
1750 case AL_EAXREVERB_ECHO_DEPTH:
1751 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1752 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
1753 props->Reverb.EchoDepth = val;
1754 break;
1756 case AL_EAXREVERB_MODULATION_TIME:
1757 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1758 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
1759 props->Reverb.ModulationTime = val;
1760 break;
1762 case AL_EAXREVERB_MODULATION_DEPTH:
1763 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1764 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
1765 props->Reverb.ModulationDepth = val;
1766 break;
1768 case AL_EAXREVERB_HFREFERENCE:
1769 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1770 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
1771 props->Reverb.HFReference = val;
1772 break;
1774 case AL_EAXREVERB_LFREFERENCE:
1775 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1776 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
1777 props->Reverb.LFReference = val;
1778 break;
1780 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1781 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1782 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
1783 props->Reverb.RoomRolloffFactor = val;
1784 break;
1786 default:
1787 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1788 param);
1791 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1793 ALeffectProps *props = &effect->Props;
1794 switch(param)
1796 case AL_EAXREVERB_REFLECTIONS_PAN:
1797 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1798 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
1799 props->Reverb.ReflectionsPan[0] = vals[0];
1800 props->Reverb.ReflectionsPan[1] = vals[1];
1801 props->Reverb.ReflectionsPan[2] = vals[2];
1802 break;
1803 case AL_EAXREVERB_LATE_REVERB_PAN:
1804 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1805 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
1806 props->Reverb.LateReverbPan[0] = vals[0];
1807 props->Reverb.LateReverbPan[1] = vals[1];
1808 props->Reverb.LateReverbPan[2] = vals[2];
1809 break;
1811 default:
1812 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1813 break;
1817 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1819 const ALeffectProps *props = &effect->Props;
1820 switch(param)
1822 case AL_EAXREVERB_DECAY_HFLIMIT:
1823 *val = props->Reverb.DecayHFLimit;
1824 break;
1826 default:
1827 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1828 param);
1831 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1832 { ALeaxreverb_getParami(effect, context, param, vals); }
1833 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1835 const ALeffectProps *props = &effect->Props;
1836 switch(param)
1838 case AL_EAXREVERB_DENSITY:
1839 *val = props->Reverb.Density;
1840 break;
1842 case AL_EAXREVERB_DIFFUSION:
1843 *val = props->Reverb.Diffusion;
1844 break;
1846 case AL_EAXREVERB_GAIN:
1847 *val = props->Reverb.Gain;
1848 break;
1850 case AL_EAXREVERB_GAINHF:
1851 *val = props->Reverb.GainHF;
1852 break;
1854 case AL_EAXREVERB_GAINLF:
1855 *val = props->Reverb.GainLF;
1856 break;
1858 case AL_EAXREVERB_DECAY_TIME:
1859 *val = props->Reverb.DecayTime;
1860 break;
1862 case AL_EAXREVERB_DECAY_HFRATIO:
1863 *val = props->Reverb.DecayHFRatio;
1864 break;
1866 case AL_EAXREVERB_DECAY_LFRATIO:
1867 *val = props->Reverb.DecayLFRatio;
1868 break;
1870 case AL_EAXREVERB_REFLECTIONS_GAIN:
1871 *val = props->Reverb.ReflectionsGain;
1872 break;
1874 case AL_EAXREVERB_REFLECTIONS_DELAY:
1875 *val = props->Reverb.ReflectionsDelay;
1876 break;
1878 case AL_EAXREVERB_LATE_REVERB_GAIN:
1879 *val = props->Reverb.LateReverbGain;
1880 break;
1882 case AL_EAXREVERB_LATE_REVERB_DELAY:
1883 *val = props->Reverb.LateReverbDelay;
1884 break;
1886 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1887 *val = props->Reverb.AirAbsorptionGainHF;
1888 break;
1890 case AL_EAXREVERB_ECHO_TIME:
1891 *val = props->Reverb.EchoTime;
1892 break;
1894 case AL_EAXREVERB_ECHO_DEPTH:
1895 *val = props->Reverb.EchoDepth;
1896 break;
1898 case AL_EAXREVERB_MODULATION_TIME:
1899 *val = props->Reverb.ModulationTime;
1900 break;
1902 case AL_EAXREVERB_MODULATION_DEPTH:
1903 *val = props->Reverb.ModulationDepth;
1904 break;
1906 case AL_EAXREVERB_HFREFERENCE:
1907 *val = props->Reverb.HFReference;
1908 break;
1910 case AL_EAXREVERB_LFREFERENCE:
1911 *val = props->Reverb.LFReference;
1912 break;
1914 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1915 *val = props->Reverb.RoomRolloffFactor;
1916 break;
1918 default:
1919 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1920 param);
1923 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
1925 const ALeffectProps *props = &effect->Props;
1926 switch(param)
1928 case AL_EAXREVERB_REFLECTIONS_PAN:
1929 vals[0] = props->Reverb.ReflectionsPan[0];
1930 vals[1] = props->Reverb.ReflectionsPan[1];
1931 vals[2] = props->Reverb.ReflectionsPan[2];
1932 break;
1933 case AL_EAXREVERB_LATE_REVERB_PAN:
1934 vals[0] = props->Reverb.LateReverbPan[0];
1935 vals[1] = props->Reverb.LateReverbPan[1];
1936 vals[2] = props->Reverb.LateReverbPan[2];
1937 break;
1939 default:
1940 ALeaxreverb_getParamf(effect, context, param, vals);
1941 break;
1945 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
1947 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1949 ALeffectProps *props = &effect->Props;
1950 switch(param)
1952 case AL_REVERB_DECAY_HFLIMIT:
1953 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
1954 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
1955 props->Reverb.DecayHFLimit = val;
1956 break;
1958 default:
1959 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
1962 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1963 { ALreverb_setParami(effect, context, param, vals[0]); }
1964 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1966 ALeffectProps *props = &effect->Props;
1967 switch(param)
1969 case AL_REVERB_DENSITY:
1970 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
1971 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
1972 props->Reverb.Density = val;
1973 break;
1975 case AL_REVERB_DIFFUSION:
1976 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
1977 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
1978 props->Reverb.Diffusion = val;
1979 break;
1981 case AL_REVERB_GAIN:
1982 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
1983 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
1984 props->Reverb.Gain = val;
1985 break;
1987 case AL_REVERB_GAINHF:
1988 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
1989 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
1990 props->Reverb.GainHF = val;
1991 break;
1993 case AL_REVERB_DECAY_TIME:
1994 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
1995 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
1996 props->Reverb.DecayTime = val;
1997 break;
1999 case AL_REVERB_DECAY_HFRATIO:
2000 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
2001 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
2002 props->Reverb.DecayHFRatio = val;
2003 break;
2005 case AL_REVERB_REFLECTIONS_GAIN:
2006 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
2007 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
2008 props->Reverb.ReflectionsGain = val;
2009 break;
2011 case AL_REVERB_REFLECTIONS_DELAY:
2012 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
2013 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
2014 props->Reverb.ReflectionsDelay = val;
2015 break;
2017 case AL_REVERB_LATE_REVERB_GAIN:
2018 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
2019 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
2020 props->Reverb.LateReverbGain = val;
2021 break;
2023 case AL_REVERB_LATE_REVERB_DELAY:
2024 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
2025 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
2026 props->Reverb.LateReverbDelay = val;
2027 break;
2029 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2030 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
2031 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
2032 props->Reverb.AirAbsorptionGainHF = val;
2033 break;
2035 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2036 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
2037 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
2038 props->Reverb.RoomRolloffFactor = val;
2039 break;
2041 default:
2042 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2045 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
2046 { ALreverb_setParamf(effect, context, param, vals[0]); }
2048 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
2050 const ALeffectProps *props = &effect->Props;
2051 switch(param)
2053 case AL_REVERB_DECAY_HFLIMIT:
2054 *val = props->Reverb.DecayHFLimit;
2055 break;
2057 default:
2058 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
2061 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
2062 { ALreverb_getParami(effect, context, param, vals); }
2063 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
2065 const ALeffectProps *props = &effect->Props;
2066 switch(param)
2068 case AL_REVERB_DENSITY:
2069 *val = props->Reverb.Density;
2070 break;
2072 case AL_REVERB_DIFFUSION:
2073 *val = props->Reverb.Diffusion;
2074 break;
2076 case AL_REVERB_GAIN:
2077 *val = props->Reverb.Gain;
2078 break;
2080 case AL_REVERB_GAINHF:
2081 *val = props->Reverb.GainHF;
2082 break;
2084 case AL_REVERB_DECAY_TIME:
2085 *val = props->Reverb.DecayTime;
2086 break;
2088 case AL_REVERB_DECAY_HFRATIO:
2089 *val = props->Reverb.DecayHFRatio;
2090 break;
2092 case AL_REVERB_REFLECTIONS_GAIN:
2093 *val = props->Reverb.ReflectionsGain;
2094 break;
2096 case AL_REVERB_REFLECTIONS_DELAY:
2097 *val = props->Reverb.ReflectionsDelay;
2098 break;
2100 case AL_REVERB_LATE_REVERB_GAIN:
2101 *val = props->Reverb.LateReverbGain;
2102 break;
2104 case AL_REVERB_LATE_REVERB_DELAY:
2105 *val = props->Reverb.LateReverbDelay;
2106 break;
2108 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2109 *val = props->Reverb.AirAbsorptionGainHF;
2110 break;
2112 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2113 *val = props->Reverb.RoomRolloffFactor;
2114 break;
2116 default:
2117 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2120 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2121 { ALreverb_getParamf(effect, context, param, vals); }
2123 DEFINE_ALEFFECT_VTABLE(ALreverb);