Revert "Don't fade the all-pass delay changes"
[openal-soft.git] / Alc / effects / reverb.c
blob864163be79e36d2775ffbc5e8fb04d455145a435
1 /**
2 * Ambisonic reverb engine for the OpenAL cross platform audio library
3 * Copyright (C) 2008-2017 by Chris Robinson and Christopher Fitzgerald.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <math.h>
27 #include "alMain.h"
28 #include "alu.h"
29 #include "alAuxEffectSlot.h"
30 #include "alEffect.h"
31 #include "alFilter.h"
32 #include "alListener.h"
33 #include "alError.h"
34 #include "mixer_defs.h"
36 /* This is a user config option for modifying the overall output of the reverb
37 * effect.
39 ALfloat ReverbBoost = 1.0f;
41 /* This is the maximum number of samples processed for each inner loop
42 * iteration. */
43 #define MAX_UPDATE_SAMPLES 256
45 /* The number of samples used for cross-faded delay lines. This can be used
46 * to balance the compensation for abrupt line changes and attenuation due to
47 * minimally lengthed recursive lines. Try to keep this below the device
48 * update size.
50 #define FADE_SAMPLES 128
52 /* The number of spatialized lines or channels to process. Four channels allows
53 * for a 3D A-Format response. NOTE: This can't be changed without taking care
54 * of the conversion matrices, and a few places where the length arrays are
55 * assumed to have 4 elements.
57 #define NUM_LINES 4
60 /* The B-Format to A-Format conversion matrix. The arrangement of rows is
61 * deliberately chosen to align the resulting lines to their spatial opposites
62 * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
63 * back left). It's not quite opposite, since the A-Format results in a
64 * tetrahedron, but it's close enough. Should the model be extended to 8-lines
65 * in the future, true opposites can be used.
67 static const aluMatrixf B2A = {{
68 { 0.288675134595f, 0.288675134595f, 0.288675134595f, 0.288675134595f },
69 { 0.288675134595f, -0.288675134595f, -0.288675134595f, 0.288675134595f },
70 { 0.288675134595f, 0.288675134595f, -0.288675134595f, -0.288675134595f },
71 { 0.288675134595f, -0.288675134595f, 0.288675134595f, -0.288675134595f }
72 }};
74 /* Converts A-Format to B-Format. */
75 static const aluMatrixf A2B = {{
76 { 0.866025403785f, 0.866025403785f, 0.866025403785f, 0.866025403785f },
77 { 0.866025403785f, -0.866025403785f, 0.866025403785f, -0.866025403785f },
78 { 0.866025403785f, -0.866025403785f, -0.866025403785f, 0.866025403785f },
79 { 0.866025403785f, 0.866025403785f, -0.866025403785f, -0.866025403785f }
80 }};
82 static const ALfloat FadeStep = 1.0f / FADE_SAMPLES;
84 /* The all-pass and delay lines have a variable length dependent on the
85 * effect's density parameter. The resulting density multiplier is:
87 * multiplier = 1 + (density * LINE_MULTIPLIER)
89 * Thus the line multiplier below will result in a maximum density multiplier
90 * of 10.
92 static const ALfloat LINE_MULTIPLIER = 9.0f;
94 /* All delay line lengths are specified in seconds.
96 * To approximate early reflections, we break them up into primary (those
97 * arriving from the same direction as the source) and secondary (those
98 * arriving from the opposite direction).
100 * The early taps decorrelate the 4-channel signal to approximate an average
101 * room response for the primary reflections after the initial early delay.
103 * Given an average room dimension (d_a) and the speed of sound (c) we can
104 * calculate the average reflection delay (r_a) regardless of listener and
105 * source positions as:
107 * r_a = d_a / c
108 * c = 343.3
110 * This can extended to finding the average difference (r_d) between the
111 * maximum (r_1) and minimum (r_0) reflection delays:
113 * r_0 = 2 / 3 r_a
114 * = r_a - r_d / 2
115 * = r_d
116 * r_1 = 4 / 3 r_a
117 * = r_a + r_d / 2
118 * = 2 r_d
119 * r_d = 2 / 3 r_a
120 * = r_1 - r_0
122 * As can be determined by integrating the 1D model with a source (s) and
123 * listener (l) positioned across the dimension of length (d_a):
125 * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
127 * The initial taps (T_(i=0)^N) are then specified by taking a power series
128 * that ranges between r_0 and half of r_1 less r_0:
130 * R_i = 2^(i / (2 N - 1)) r_d
131 * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
132 * = r_0 + T_i
133 * T_i = R_i - r_0
134 * = (2^(i / (2 N - 1)) - 1) r_d
136 * Assuming an average of 5m (up to 50m with the density multiplier), we get
137 * the following taps:
139 static const ALfloat EARLY_TAP_LENGTHS[NUM_LINES] =
141 0.000000e+0f, 1.010676e-3f, 2.126553e-3f, 3.358580e-3f
144 /* The early all-pass filter lengths are based on the early tap lengths:
146 * A_i = R_i / a
148 * Where a is the approximate maximum all-pass cycle limit (20).
150 static const ALfloat EARLY_ALLPASS_LENGTHS[NUM_LINES] =
152 4.854840e-4f, 5.360178e-4f, 5.918117e-4f, 6.534130e-4f
155 /* The early delay lines are used to transform the primary reflections into
156 * the secondary reflections. The A-format is arranged in such a way that
157 * the channels/lines are spatially opposite:
159 * C_i is opposite C_(N-i-1)
161 * The delays of the two opposing reflections (R_i and O_i) from a source
162 * anywhere along a particular dimension always sum to twice its full delay:
164 * 2 r_a = R_i + O_i
166 * With that in mind we can determine the delay between the two reflections
167 * and thus specify our early line lengths (L_(i=0)^N) using:
169 * O_i = 2 r_a - R_(N-i-1)
170 * L_i = O_i - R_(N-i-1)
171 * = 2 (r_a - R_(N-i-1))
172 * = 2 (r_a - T_(N-i-1) - r_0)
173 * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
175 * Using an average dimension of 5m, we get:
177 static const ALfloat EARLY_LINE_LENGTHS[NUM_LINES] =
179 2.992520e-3f, 5.456575e-3f, 7.688329e-3f, 9.709681e-3f
182 /* The late all-pass filter lengths are based on the late line lengths:
184 * A_i = (5 / 3) L_i / r_1
186 static const ALfloat LATE_ALLPASS_LENGTHS[NUM_LINES] =
188 8.091400e-4f, 1.019453e-3f, 1.407968e-3f, 1.618280e-3f
191 /* The late lines are used to approximate the decaying cycle of recursive
192 * late reflections.
194 * Splitting the lines in half, we start with the shortest reflection paths
195 * (L_(i=0)^(N/2)):
197 * L_i = 2^(i / (N - 1)) r_d
199 * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
201 * L_i = 2 r_a - L_(i-N/2)
202 * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
204 * For our 5m average room, we get:
206 static const ALfloat LATE_LINE_LENGTHS[NUM_LINES] =
208 9.709681e-3f, 1.223343e-2f, 1.689561e-2f, 1.941936e-2f
211 /* This coefficient is used to define the delay scale from the sinus, according
212 * to the modulation depth property. This value must be below the shortest late
213 * line length (0.0097), otherwise with certain parameters (high mod time, low
214 * density) the downswing can sample before the input.
216 static const ALfloat MODULATION_DEPTH_COEFF = 0.0032f;
219 typedef struct DelayLineI {
220 /* The delay lines use interleaved samples, with the lengths being powers
221 * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
223 ALsizei Mask;
224 ALfloat (*Line)[NUM_LINES];
225 } DelayLineI;
227 typedef struct VecAllpass {
228 DelayLineI Delay;
229 ALsizei Offset[NUM_LINES][2];
230 } VecAllpass;
232 typedef struct T60Filter {
233 /* Two filters are used to adjust the signal. One to control the low
234 * frequencies, and one to control the high frequencies. The HF filter also
235 * adjusts the overall output gain, affecting the remaining mid-band.
237 ALfloat HFCoeffs[3];
238 ALfloat LFCoeffs[3];
240 /* The HF and LF filters each keep a state of the last input and last
241 * output sample.
243 ALfloat HFState[2];
244 ALfloat LFState[2];
245 } T60Filter;
247 typedef struct EarlyReflections {
248 /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
249 * The spread from this filter also helps smooth out the reverb tail.
251 VecAllpass VecAp;
253 /* An echo line is used to complete the second half of the early
254 * reflections.
256 DelayLineI Delay;
257 ALsizei Offset[NUM_LINES][2];
258 ALfloat Coeff[NUM_LINES];
260 /* The gain for each output channel based on 3D panning. */
261 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
262 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
263 } EarlyReflections;
265 typedef struct Modulator {
266 /* The vibrato time is tracked with an index over a modulus-wrapped range
267 * (in samples).
269 ALsizei Index;
270 ALsizei Range;
271 ALfloat IdxScale;
273 /* The LFO delay scale (in samples scaled by FRACTIONONE). */
274 ALfloat Depth[2];
275 } Modulator;
277 typedef struct LateReverb {
278 /* Attenuation to compensate for the modal density and decay rate of the
279 * late lines.
281 ALfloat DensityGain;
283 /* A recursive delay line is used fill in the reverb tail. */
284 DelayLineI Delay;
285 ALsizei Offset[NUM_LINES][2];
287 /* T60 decay filters are used to simulate absorption. */
288 T60Filter T60[NUM_LINES];
290 /* A Gerzon vector all-pass filter is used to simulate diffusion. */
291 VecAllpass VecAp;
293 /* The gain for each output channel based on 3D panning. */
294 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
295 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
296 } LateReverb;
298 typedef struct ALreverbState {
299 DERIVE_FROM_TYPE(ALeffectState);
301 /* All delay lines are allocated as a single buffer to reduce memory
302 * fragmentation and management code.
304 ALfloat *SampleBuffer;
305 ALuint TotalSamples;
307 /* Master effect filters */
308 struct {
309 ALfilterState Lp;
310 ALfilterState Hp;
311 } Filter[NUM_LINES];
313 /* Core delay line (early reflections and late reverb tap from this). */
314 DelayLineI Delay;
316 /* Tap points for early reflection delay. */
317 ALsizei EarlyDelayTap[NUM_LINES][2];
318 ALfloat EarlyDelayCoeff[NUM_LINES];
320 /* Tap points for late reverb feed and delay. */
321 ALsizei LateFeedTap;
322 ALsizei LateDelayTap[NUM_LINES][2];
324 /* The feed-back and feed-forward all-pass coefficient. */
325 ALfloat ApFeedCoeff;
327 /* Coefficients for the all-pass and line scattering matrices. */
328 ALfloat MixX;
329 ALfloat MixY;
331 EarlyReflections Early;
333 Modulator Mod;
335 LateReverb Late;
337 /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
338 ALsizei FadeCount;
340 /* The current write offset for all delay lines. */
341 ALsizei Offset;
343 /* Temporary storage used when processing. */
344 alignas(16) ALsizei ModulationDelays[NUM_LINES][MAX_UPDATE_SAMPLES][2];
345 alignas(16) ALfloat AFormatSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
346 alignas(16) ALfloat ReverbSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
347 alignas(16) ALfloat EarlySamples[NUM_LINES][MAX_UPDATE_SAMPLES];
348 } ALreverbState;
350 static ALvoid ALreverbState_Destruct(ALreverbState *State);
351 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device);
352 static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
353 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
354 DECLARE_DEFAULT_ALLOCATORS(ALreverbState)
356 DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);
358 static void ALreverbState_Construct(ALreverbState *state)
360 ALsizei i, j;
362 ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
363 SET_VTABLE2(ALreverbState, ALeffectState, state);
365 state->TotalSamples = 0;
366 state->SampleBuffer = NULL;
368 for(i = 0;i < NUM_LINES;i++)
370 ALfilterState_clear(&state->Filter[i].Lp);
371 ALfilterState_clear(&state->Filter[i].Hp);
374 state->Delay.Mask = 0;
375 state->Delay.Line = NULL;
377 for(i = 0;i < NUM_LINES;i++)
379 state->EarlyDelayTap[i][0] = 0;
380 state->EarlyDelayTap[i][1] = 0;
381 state->EarlyDelayCoeff[i] = 0.0f;
384 state->LateFeedTap = 0;
386 for(i = 0;i < NUM_LINES;i++)
388 state->LateDelayTap[i][0] = 0;
389 state->LateDelayTap[i][1] = 0;
392 state->ApFeedCoeff = 0.0f;
393 state->MixX = 0.0f;
394 state->MixY = 0.0f;
396 state->Early.VecAp.Delay.Mask = 0;
397 state->Early.VecAp.Delay.Line = NULL;
398 state->Early.Delay.Mask = 0;
399 state->Early.Delay.Line = NULL;
400 for(i = 0;i < NUM_LINES;i++)
402 state->Early.VecAp.Offset[i][0] = 0;
403 state->Early.VecAp.Offset[i][1] = 0;
404 state->Early.Offset[i][0] = 0;
405 state->Early.Offset[i][1] = 0;
406 state->Early.Coeff[i] = 0.0f;
409 state->Mod.Index = 0;
410 state->Mod.Range = 1;
411 state->Mod.IdxScale = 0.0f;
412 state->Mod.Depth[0] = 0.0f;
413 state->Mod.Depth[1] = 0.0f;
415 state->Late.DensityGain = 0.0f;
417 state->Late.Delay.Mask = 0;
418 state->Late.Delay.Line = NULL;
419 state->Late.VecAp.Delay.Mask = 0;
420 state->Late.VecAp.Delay.Line = NULL;
421 for(i = 0;i < NUM_LINES;i++)
423 state->Late.Offset[i][0] = 0;
424 state->Late.Offset[i][1] = 0;
426 state->Late.VecAp.Offset[i][0] = 0;
427 state->Late.VecAp.Offset[i][1] = 0;
429 for(j = 0;j < 3;j++)
431 state->Late.T60[i].HFCoeffs[j] = 0.0f;
432 state->Late.T60[i].LFCoeffs[j] = 0.0f;
434 state->Late.T60[i].HFState[0] = 0.0f;
435 state->Late.T60[i].HFState[1] = 0.0f;
436 state->Late.T60[i].LFState[0] = 0.0f;
437 state->Late.T60[i].LFState[1] = 0.0f;
440 for(i = 0;i < NUM_LINES;i++)
442 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
444 state->Early.CurrentGain[i][j] = 0.0f;
445 state->Early.PanGain[i][j] = 0.0f;
446 state->Late.CurrentGain[i][j] = 0.0f;
447 state->Late.PanGain[i][j] = 0.0f;
451 state->FadeCount = 0;
452 state->Offset = 0;
455 static ALvoid ALreverbState_Destruct(ALreverbState *State)
457 al_free(State->SampleBuffer);
458 State->SampleBuffer = NULL;
460 ALeffectState_Destruct(STATIC_CAST(ALeffectState,State));
463 /**************************************
464 * Device Update *
465 **************************************/
467 /* Given the allocated sample buffer, this function updates each delay line
468 * offset.
470 static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay)
472 union {
473 ALfloat *f;
474 ALfloat (*f4)[NUM_LINES];
475 } u;
476 u.f = &sampleBuffer[(ptrdiff_t)Delay->Line * NUM_LINES];
477 Delay->Line = u.f4;
480 /* Calculate the length of a delay line and store its mask and offset. */
481 static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALuint frequency,
482 const ALuint extra, DelayLineI *Delay)
484 ALuint samples;
486 /* All line lengths are powers of 2, calculated from their lengths in
487 * seconds, rounded up.
489 samples = fastf2i(ceilf(length*frequency));
490 samples = NextPowerOf2(samples + extra);
492 /* All lines share a single sample buffer. */
493 Delay->Mask = samples - 1;
494 Delay->Line = (ALfloat(*)[NUM_LINES])offset;
496 /* Return the sample count for accumulation. */
497 return samples;
500 /* Calculates the delay line metrics and allocates the shared sample buffer
501 * for all lines given the sample rate (frequency). If an allocation failure
502 * occurs, it returns AL_FALSE.
504 static ALboolean AllocLines(const ALuint frequency, ALreverbState *State)
506 ALuint totalSamples, i;
507 ALfloat multiplier, length;
509 /* All delay line lengths are calculated to accomodate the full range of
510 * lengths given their respective paramters.
512 totalSamples = 0;
514 /* Multiplier for the maximum density value, i.e. density=1, which is
515 * actually the least density...
517 multiplier = 1.0f + AL_EAXREVERB_MAX_DENSITY*LINE_MULTIPLIER;
519 /* The main delay length includes the maximum early reflection delay, the
520 * largest early tap width, the maximum late reverb delay, and the
521 * largest late tap width. Finally, it must also be extended by the
522 * update size (MAX_UPDATE_SAMPLES) for block processing.
524 length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier +
525 AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
526 (LATE_LINE_LENGTHS[NUM_LINES-1] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
527 totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES,
528 &State->Delay);
530 /* The early vector all-pass line. */
531 length = EARLY_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
532 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
533 &State->Early.VecAp.Delay);
535 /* The early reflection line. */
536 length = EARLY_LINE_LENGTHS[NUM_LINES-1] * multiplier;
537 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
538 &State->Early.Delay);
540 /* The late vector all-pass line. */
541 length = LATE_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
542 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
543 &State->Late.VecAp.Delay);
545 /* The late delay lines are calculated from the larger of the maximum
546 * density line length or the maximum echo time, and includes the maximum
547 * modulation-related delay. The modulator's delay is calculated from the
548 * depth coefficient.
550 length = maxf(AL_EAXREVERB_MAX_ECHO_TIME, LATE_LINE_LENGTHS[NUM_LINES-1]*multiplier) +
551 MODULATION_DEPTH_COEFF;
552 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
553 &State->Late.Delay);
555 if(totalSamples != State->TotalSamples)
557 ALfloat *newBuffer;
559 TRACE("New reverb buffer length: %ux4 samples\n", totalSamples);
560 newBuffer = al_calloc(16, sizeof(ALfloat[NUM_LINES]) * totalSamples);
561 if(!newBuffer) return AL_FALSE;
563 al_free(State->SampleBuffer);
564 State->SampleBuffer = newBuffer;
565 State->TotalSamples = totalSamples;
568 /* Update all delays to reflect the new sample buffer. */
569 RealizeLineOffset(State->SampleBuffer, &State->Delay);
570 RealizeLineOffset(State->SampleBuffer, &State->Early.VecAp.Delay);
571 RealizeLineOffset(State->SampleBuffer, &State->Early.Delay);
572 RealizeLineOffset(State->SampleBuffer, &State->Late.VecAp.Delay);
573 RealizeLineOffset(State->SampleBuffer, &State->Late.Delay);
575 /* Clear the sample buffer. */
576 for(i = 0;i < State->TotalSamples;i++)
577 State->SampleBuffer[i] = 0.0f;
579 return AL_TRUE;
582 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
584 ALuint frequency = Device->Frequency;
585 ALfloat multiplier;
587 /* Allocate the delay lines. */
588 if(!AllocLines(frequency, State))
589 return AL_FALSE;
591 multiplier = 1.0f + AL_EAXREVERB_MAX_DENSITY*LINE_MULTIPLIER;
593 /* The late feed taps are set a fixed position past the latest delay tap. */
594 State->LateFeedTap = fastf2i((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
595 EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier) *
596 frequency);
598 return AL_TRUE;
601 /**************************************
602 * Effect Update *
603 **************************************/
605 /* Calculate a decay coefficient given the length of each cycle and the time
606 * until the decay reaches -60 dB.
608 static inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
610 return powf(REVERB_DECAY_GAIN, length/decayTime);
613 /* Calculate a decay length from a coefficient and the time until the decay
614 * reaches -60 dB.
616 static inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
618 return log10f(coeff) * decayTime / log10f(REVERB_DECAY_GAIN);
621 /* Calculate an attenuation to be applied to the input of any echo models to
622 * compensate for modal density and decay time.
624 static inline ALfloat CalcDensityGain(const ALfloat a)
626 /* The energy of a signal can be obtained by finding the area under the
627 * squared signal. This takes the form of Sum(x_n^2), where x is the
628 * amplitude for the sample n.
630 * Decaying feedback matches exponential decay of the form Sum(a^n),
631 * where a is the attenuation coefficient, and n is the sample. The area
632 * under this decay curve can be calculated as: 1 / (1 - a).
634 * Modifying the above equation to find the area under the squared curve
635 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
636 * calculated by inverting the square root of this approximation,
637 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
639 return sqrtf(1.0f - a*a);
642 /* Calculate the scattering matrix coefficients given a diffusion factor. */
643 static inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
645 ALfloat n, t;
647 /* The matrix is of order 4, so n is sqrt(4 - 1). */
648 n = sqrtf(3.0f);
649 t = diffusion * atanf(n);
651 /* Calculate the first mixing matrix coefficient. */
652 *x = cosf(t);
653 /* Calculate the second mixing matrix coefficient. */
654 *y = sinf(t) / n;
657 /* Calculate the limited HF ratio for use with the late reverb low-pass
658 * filters.
660 static ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
661 const ALfloat decayTime, const ALfloat SpeedOfSound)
663 ALfloat limitRatio;
665 /* Find the attenuation due to air absorption in dB (converting delay
666 * time to meters using the speed of sound). Then reversing the decay
667 * equation, solve for HF ratio. The delay length is cancelled out of
668 * the equation, so it can be calculated once for all lines.
670 limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound);
672 /* Using the limit calculated above, apply the upper bound to the HF ratio.
674 return minf(limitRatio, hfRatio);
677 /* Calculates the first-order high-pass coefficients following the I3DL2
678 * reference model. This is the transfer function:
680 * 1 - z^-1
681 * H(z) = p ------------
682 * 1 - p z^-1
684 * And this is the I3DL2 coefficient calculation given gain (g) and reference
685 * angular frequency (w):
688 * p = ------------------------------------------------------
689 * g cos(w) + sqrt((cos(w) - 1) (g^2 cos(w) + g^2 - 2))
691 * The coefficient is applied to the partial differential filter equation as:
693 * c_0 = p
694 * c_1 = -p
695 * c_2 = p
696 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
699 static inline void CalcHighpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
701 ALfloat g, g2, cw, p;
703 if(gain >= 1.0f)
705 coeffs[0] = 1.0f;
706 coeffs[1] = 0.0f;
707 coeffs[2] = 0.0f;
708 return;
711 g = maxf(0.001f, gain);
712 g2 = g * g;
713 cw = cosf(w);
714 p = g / (g*cw + sqrtf((cw - 1.0f) * (g2*cw + g2 - 2.0f)));
716 coeffs[0] = p;
717 coeffs[1] = -p;
718 coeffs[2] = p;
721 /* Calculates the first-order low-pass coefficients following the I3DL2
722 * reference model. This is the transfer function:
724 * (1 - a) z^0
725 * H(z) = ----------------
726 * 1 z^0 - a z^-1
728 * And this is the I3DL2 coefficient calculation given gain (g) and reference
729 * angular frequency (w):
731 * 1 - g^2 cos(w) - sqrt(2 g^2 (1 - cos(w)) - g^4 (1 - cos(w)^2))
732 * a = ----------------------------------------------------------------
733 * 1 - g^2
735 * The coefficient is applied to the partial differential filter equation as:
737 * c_0 = 1 - a
738 * c_1 = 0
739 * c_2 = a
740 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
743 static inline void CalcLowpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
745 ALfloat g, g2, cw, a;
747 if(gain >= 1.0f)
749 coeffs[0] = 1.0f;
750 coeffs[1] = 0.0f;
751 coeffs[2] = 0.0f;
752 return;
755 /* Be careful with gains < 0.001, as that causes the coefficient
756 * to head towards 1, which will flatten the signal. */
757 g = maxf(0.001f, gain);
758 g2 = g * g;
759 cw = cosf(w);
760 a = (1.0f - g2*cw - sqrtf((2.0f*g2*(1.0f - cw)) - g2*g2*(1.0f - cw*cw))) /
761 (1.0f - g2);
763 coeffs[0] = 1.0f - a;
764 coeffs[1] = 0.0f;
765 coeffs[2] = a;
768 /* Calculates the first-order low-shelf coefficients. The shelf filters are
769 * used in place of low/high-pass filters to preserve the mid-band. This is
770 * the transfer function:
772 * a_0 + a_1 z^-1
773 * H(z) = ----------------
774 * 1 + b_1 z^-1
776 * And these are the coefficient calculations given cut gain (g) and a center
777 * angular frequency (w):
779 * sin(0.5 (pi - w) - 0.25 pi)
780 * p = -----------------------------
781 * sin(0.5 (pi - w) + 0.25 pi)
783 * g + 1 g + 1
784 * a = ------- + sqrt((-------)^2 - 1)
785 * g - 1 g - 1
787 * 1 + g + (1 - g) a
788 * b_0 = -------------------
791 * 1 - g + (1 + g) a
792 * b_1 = -------------------
795 * The coefficients are applied to the partial differential filter equation
796 * as:
798 * b_0 + p b_1
799 * c_0 = -------------
800 * 1 + p a
802 * -(b_1 + p b_0)
803 * c_1 = ----------------
804 * 1 + p a
806 * p + a
807 * c_2 = ---------
808 * 1 + p a
810 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
813 static inline void CalcLowShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
815 ALfloat g, rw, p, n;
816 ALfloat alpha, beta0, beta1;
818 if(gain >= 1.0f)
820 coeffs[0] = 1.0f;
821 coeffs[1] = 0.0f;
822 coeffs[2] = 0.0f;
823 return;
826 g = maxf(0.001f, gain);
827 rw = F_PI - w;
828 p = sinf(0.5f*rw - 0.25f*F_PI) / sinf(0.5f*rw + 0.25f*F_PI);
829 n = (g + 1.0f) / (g - 1.0f);
830 alpha = n + sqrtf(n*n - 1.0f);
831 beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f;
832 beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f;
834 coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha);
835 coeffs[1] = -(beta1 + p*beta0) / (1.0f + p*alpha);
836 coeffs[2] = (p + alpha) / (1.0f + p*alpha);
839 /* Calculates the first-order high-shelf coefficients. The shelf filters are
840 * used in place of low/high-pass filters to preserve the mid-band. This is
841 * the transfer function:
843 * a_0 + a_1 z^-1
844 * H(z) = ----------------
845 * 1 + b_1 z^-1
847 * And these are the coefficient calculations given cut gain (g) and a center
848 * angular frequency (w):
850 * sin(0.5 w - 0.25 pi)
851 * p = ----------------------
852 * sin(0.5 w + 0.25 pi)
854 * g + 1 g + 1
855 * a = ------- + sqrt((-------)^2 - 1)
856 * g - 1 g - 1
858 * 1 + g + (1 - g) a
859 * b_0 = -------------------
862 * 1 - g + (1 + g) a
863 * b_1 = -------------------
866 * The coefficients are applied to the partial differential filter equation
867 * as:
869 * b_0 + p b_1
870 * c_0 = -------------
871 * 1 + p a
873 * b_1 + p b_0
874 * c_1 = -------------
875 * 1 + p a
877 * -(p + a)
878 * c_2 = ----------
879 * 1 + p a
881 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
884 static inline void CalcHighShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
886 ALfloat g, p, n;
887 ALfloat alpha, beta0, beta1;
889 if(gain >= 1.0f)
891 coeffs[0] = 1.0f;
892 coeffs[1] = 0.0f;
893 coeffs[2] = 0.0f;
894 return;
897 g = maxf(0.001f, gain);
898 p = sinf(0.5f*w - 0.25f*F_PI) / sinf(0.5f*w + 0.25f*F_PI);
899 n = (g + 1.0f) / (g - 1.0f);
900 alpha = n + sqrtf(n*n - 1.0f);
901 beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f;
902 beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f;
904 coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha);
905 coeffs[1] = (beta1 + p*beta0) / (1.0f + p*alpha);
906 coeffs[2] = -(p + alpha) / (1.0f + p*alpha);
909 /* Calculates the 3-band T60 damping coefficients for a particular delay line
910 * of specified length using a combination of two low/high-pass/shelf or
911 * pass-through filter sections (producing 3 coefficients each) given decay
912 * times for each band split at two (LF/HF) reference frequencies (w).
914 static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime,
915 const ALfloat mfDecayTime, const ALfloat hfDecayTime,
916 const ALfloat lfW, const ALfloat hfW, ALfloat lfcoeffs[3],
917 ALfloat hfcoeffs[3])
919 ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime);
920 ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime);
921 ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime);
923 if(lfGain <= mfGain)
925 CalcHighpassCoeffs(lfGain / mfGain, lfW, lfcoeffs);
926 if(mfGain >= hfGain)
928 CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs);
929 hfcoeffs[0] *= mfGain; hfcoeffs[1] *= mfGain;
931 else
933 CalcLowShelfCoeffs(mfGain / hfGain, hfW, hfcoeffs);
934 hfcoeffs[0] *= hfGain; hfcoeffs[1] *= hfGain;
937 else
939 CalcHighShelfCoeffs(mfGain / lfGain, lfW, lfcoeffs);
940 if(mfGain >= hfGain)
942 CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs);
943 hfcoeffs[0] *= lfGain; hfcoeffs[1] *= lfGain;
945 else
947 ALfloat hg = mfGain / lfGain;
948 ALfloat lg = mfGain / hfGain;
949 ALfloat mg = maxf(lfGain, hfGain) / maxf(hg, lg);
951 CalcLowShelfCoeffs(lg, hfW, hfcoeffs);
952 hfcoeffs[0] *= mg; hfcoeffs[1] *= mg;
957 /* Update the EAX modulation index, range, and depth. Keep in mind that this
958 * kind of vibrato is additive and not multiplicative as one may expect. The
959 * downswing will sound stronger than the upswing.
961 static ALvoid UpdateModulator(const ALfloat modTime, const ALfloat modDepth,
962 const ALuint frequency, Modulator *Mod)
964 ALsizei range;
966 /* Modulation is calculated in two parts.
968 * The modulation time effects the speed of the sinus. An index out of the
969 * current range (both in samples) is incremented each sample, so a longer
970 * time implies a larger range. When the timing changes, the index is
971 * rescaled to the new range to keep the sinus consistent.
973 range = fastf2i(modTime*frequency + 0.5f);
974 Mod->Index = (ALsizei)(Mod->Index * (ALint64)range / Mod->Range)%range;
975 Mod->Range = range;
976 Mod->IdxScale = F_TAU / range;
978 /* The modulation depth effects the scale of the sinus, which varies the
979 * delay for the tapped output. This delay changing over time changes the
980 * pitch, creating the modulation effect. The scale needs to be multiplied
981 * by the modulation time (itself scaled by the max modulation time) so
982 * that a given depth produces a consistent shift in frequency over all
983 * ranges of time.
985 Mod->Depth[1] = modDepth * MODULATION_DEPTH_COEFF *
986 (modTime / AL_EAXREVERB_MAX_MODULATION_TIME) *
987 frequency * FRACTIONONE;
990 /* Update the offsets for the main effect delay line. */
991 static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ALreverbState *State)
993 ALfloat multiplier, length;
994 ALuint i;
996 multiplier = 1.0f + density*LINE_MULTIPLIER;
998 /* Early reflection taps are decorrelated by means of an average room
999 * reflection approximation described above the definition of the taps.
1000 * This approximation is linear and so the above density multiplier can
1001 * be applied to adjust the width of the taps. A single-band decay
1002 * coefficient is applied to simulate initial attenuation and absorption.
1004 * Late reverb taps are based on the late line lengths to allow a zero-
1005 * delay path and offsets that would continue the propagation naturally
1006 * into the late lines.
1008 for(i = 0;i < NUM_LINES;i++)
1010 length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
1011 State->EarlyDelayTap[i][1] = fastf2i(length * frequency);
1013 length = EARLY_TAP_LENGTHS[i]*multiplier;
1014 State->EarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime);
1016 length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
1017 State->LateDelayTap[i][1] = State->LateFeedTap + fastf2i(length * frequency);
1021 /* Update the early reflection line lengths and gain coefficients. */
1022 static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat decayTime, const ALuint frequency, EarlyReflections *Early)
1024 ALfloat multiplier, length;
1025 ALsizei i;
1027 multiplier = 1.0f + density*LINE_MULTIPLIER;
1029 for(i = 0;i < NUM_LINES;i++)
1031 /* Calculate the length (in seconds) of each all-pass line. */
1032 length = EARLY_ALLPASS_LENGTHS[i] * multiplier;
1034 /* Calculate the delay offset for each all-pass line. */
1035 Early->VecAp.Offset[i][1] = fastf2i(length * frequency);
1037 /* Calculate the length (in seconds) of each delay line. */
1038 length = EARLY_LINE_LENGTHS[i] * multiplier;
1040 /* Calculate the delay offset for each delay line. */
1041 Early->Offset[i][1] = fastf2i(length * frequency);
1043 /* Calculate the gain (coefficient) for each line. */
1044 Early->Coeff[i] = CalcDecayCoeff(length, decayTime);
1048 /* Update the late reverb line lengths and T60 coefficients. */
1049 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)
1051 ALfloat multiplier, length, bandWeights[3];
1052 ALsizei i;
1054 /* To compensate for changes in modal density and decay time of the late
1055 * reverb signal, the input is attenuated based on the maximal energy of
1056 * the outgoing signal. This approximation is used to keep the apparent
1057 * energy of the signal equal for all ranges of density and decay time.
1059 * The average length of the delay lines is used to calculate the
1060 * attenuation coefficient.
1062 multiplier = 1.0f + density*LINE_MULTIPLIER;
1063 length = (LATE_LINE_LENGTHS[0] + LATE_LINE_LENGTHS[1] +
1064 LATE_LINE_LENGTHS[2] + LATE_LINE_LENGTHS[3]) / 4.0f * multiplier;
1065 /* Include the echo transformation (see below). */
1066 length = lerp(length, echoTime, echoDepth);
1067 length += (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
1068 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f * multiplier;
1069 /* The density gain calculation uses an average decay time weighted by
1070 * approximate bandwidth. This attempts to compensate for losses of
1071 * energy that reduce decay time due to scattering into highly attenuated
1072 * bands.
1074 bandWeights[0] = lfW;
1075 bandWeights[1] = hfW - lfW;
1076 bandWeights[2] = F_TAU - hfW;
1077 Late->DensityGain = CalcDensityGain(
1078 CalcDecayCoeff(length, (bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime +
1079 bandWeights[2]*hfDecayTime) / F_TAU)
1082 for(i = 0;i < NUM_LINES;i++)
1084 /* Calculate the length (in seconds) of each all-pass line. */
1085 length = LATE_ALLPASS_LENGTHS[i] * multiplier;
1087 /* Calculate the delay offset for each all-pass line. */
1088 Late->VecAp.Offset[i][1] = fastf2i(length * frequency);
1090 /* Calculate the length (in seconds) of each delay line. This also
1091 * applies the echo transformation. As the EAX echo depth approaches
1092 * 1, the line lengths approach a length equal to the echoTime. This
1093 * helps to produce distinct echoes along the tail.
1095 length = lerp(LATE_LINE_LENGTHS[i] * multiplier, echoTime, echoDepth);
1097 /* Calculate the delay offset for each delay line. */
1098 Late->Offset[i][1] = fastf2i(length*frequency + 0.5f);
1100 /* Approximate the absorption that the vector all-pass would exhibit
1101 * given the current diffusion so we don't have to process a full T60
1102 * filter for each of its four lines.
1104 length += lerp(LATE_ALLPASS_LENGTHS[i],
1105 (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
1106 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f,
1107 diffusion) * multiplier;
1109 /* Calculate the T60 damping coefficients for each line. */
1110 CalcT60DampingCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime,
1111 lfW, hfW, Late->T60[i].LFCoeffs,
1112 Late->T60[i].HFCoeffs);
1116 /* Creates a transform matrix given a reverb vector. This works by creating a
1117 * Z-focus transform, then a rotate transform around X, then Y, to place the
1118 * focal point in the direction of the vector, using the vector length as a
1119 * focus strength.
1121 * This isn't technically correct since the vector is supposed to define the
1122 * aperture and not rotate the perceived soundfield, but in practice it's
1123 * probably good enough.
1125 static aluMatrixf GetTransformFromVector(const ALfloat *vec)
1127 aluMatrixf zfocus, xrot, yrot;
1128 aluMatrixf tmp1, tmp2;
1129 ALfloat length;
1130 ALfloat sa, a;
1132 length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
1134 /* Define a Z-focus (X in Ambisonics) transform, given the panning vector
1135 * length.
1137 sa = sinf(minf(length, 1.0f) * (F_PI/4.0f));
1138 aluMatrixfSet(&zfocus,
1139 1.0f/(1.0f+sa), 0.0f, 0.0f, (sa/(1.0f+sa))/1.732050808f,
1140 0.0f, sqrtf((1.0f-sa)/(1.0f+sa)), 0.0f, 0.0f,
1141 0.0f, 0.0f, sqrtf((1.0f-sa)/(1.0f+sa)), 0.0f,
1142 (sa/(1.0f+sa))*1.732050808f, 0.0f, 0.0f, 1.0f/(1.0f+sa)
1145 /* Define rotation around X (Y in Ambisonics) */
1146 a = atan2f(vec[1], sqrtf(vec[0]*vec[0] + vec[2]*vec[2]));
1147 aluMatrixfSet(&xrot,
1148 1.0f, 0.0f, 0.0f, 0.0f,
1149 0.0f, 1.0f, 0.0f, 0.0f,
1150 0.0f, 0.0f, cosf(a), sinf(a),
1151 0.0f, 0.0f, -sinf(a), cosf(a)
1154 /* Define rotation around Y (Z in Ambisonics). NOTE: EFX's reverb vectors
1155 * use a right-handled coordinate system, compared to the rest of OpenAL
1156 * which uses left-handed. This is fixed by negating Z, however it would
1157 * need to also be negated to get a proper Ambisonics angle, thus
1158 * cancelling it out.
1160 a = atan2f(-vec[0], vec[2]);
1161 aluMatrixfSet(&yrot,
1162 1.0f, 0.0f, 0.0f, 0.0f,
1163 0.0f, cosf(a), 0.0f, sinf(a),
1164 0.0f, 0.0f, 1.0f, 0.0f,
1165 0.0f, -sinf(a), 0.0f, cosf(a)
1168 #define MATRIX_MULT(_res, _m1, _m2) do { \
1169 int row, col; \
1170 for(col = 0;col < 4;col++) \
1172 for(row = 0;row < 4;row++) \
1173 _res.m[row][col] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
1174 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
1176 } while(0)
1177 /* Define a matrix that first focuses on Z, then rotates around X then Y to
1178 * focus the output in the direction of the vector.
1180 MATRIX_MULT(tmp1, xrot, zfocus);
1181 MATRIX_MULT(tmp2, yrot, tmp1);
1182 #undef MATRIX_MULT
1184 return tmp2;
1187 /* Update the early and late 3D panning gains. */
1188 static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat gain, const ALfloat earlyGain, const ALfloat lateGain, ALreverbState *State)
1190 aluMatrixf transform, rot;
1191 ALsizei i;
1193 STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
1194 STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;
1196 /* Note: _res is transposed. */
1197 #define MATRIX_MULT(_res, _m1, _m2) do { \
1198 int row, col; \
1199 for(col = 0;col < 4;col++) \
1201 for(row = 0;row < 4;row++) \
1202 _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
1203 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
1205 } while(0)
1206 /* Create a matrix that first converts A-Format to B-Format, then rotates
1207 * the B-Format soundfield according to the panning vector.
1209 rot = GetTransformFromVector(ReflectionsPan);
1210 MATRIX_MULT(transform, rot, A2B);
1211 memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
1212 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
1213 ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], gain*earlyGain,
1214 State->Early.PanGain[i]);
1216 rot = GetTransformFromVector(LateReverbPan);
1217 MATRIX_MULT(transform, rot, A2B);
1218 memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
1219 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
1220 ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], gain*lateGain,
1221 State->Late.PanGain[i]);
1222 #undef MATRIX_MULT
1225 static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
1227 const ALCdevice *Device = Context->Device;
1228 const ALlistener *Listener = Context->Listener;
1229 ALuint frequency = Device->Frequency;
1230 ALfloat lf0norm, hf0norm, hfRatio;
1231 ALfloat lfDecayTime, hfDecayTime;
1232 ALfloat gain, gainlf, gainhf;
1233 ALsizei i;
1235 /* Calculate the master filters */
1236 hf0norm = props->Reverb.HFReference / frequency;
1237 /* Restrict the filter gains from going below -60dB to keep the filter from
1238 * killing most of the signal.
1240 gainhf = maxf(props->Reverb.GainHF, 0.001f);
1241 ALfilterState_setParams(&State->Filter[0].Lp, ALfilterType_HighShelf,
1242 gainhf, hf0norm, calc_rcpQ_from_slope(gainhf, 1.0f));
1243 lf0norm = props->Reverb.LFReference / frequency;
1244 gainlf = maxf(props->Reverb.GainLF, 0.001f);
1245 ALfilterState_setParams(&State->Filter[0].Hp, ALfilterType_LowShelf,
1246 gainlf, lf0norm, calc_rcpQ_from_slope(gainlf, 1.0f));
1247 for(i = 1;i < NUM_LINES;i++)
1249 ALfilterState_copyParams(&State->Filter[i].Lp, &State->Filter[0].Lp);
1250 ALfilterState_copyParams(&State->Filter[i].Hp, &State->Filter[0].Hp);
1253 /* Update the main effect delay and associated taps. */
1254 UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
1255 props->Reverb.Density, props->Reverb.DecayTime, frequency,
1256 State);
1258 /* Calculate the all-pass feed-back/forward coefficient. */
1259 State->ApFeedCoeff = sqrtf(0.5f) * powf(props->Reverb.Diffusion, 2.0f);
1261 /* Update the early lines. */
1262 UpdateEarlyLines(props->Reverb.Density, props->Reverb.DecayTime,
1263 frequency, &State->Early);
1265 /* Get the mixing matrix coefficients. */
1266 CalcMatrixCoeffs(props->Reverb.Diffusion, &State->MixX, &State->MixY);
1268 /* If the HF limit parameter is flagged, calculate an appropriate limit
1269 * based on the air absorption parameter.
1271 hfRatio = props->Reverb.DecayHFRatio;
1272 if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
1273 hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
1274 props->Reverb.DecayTime, Listener->Params.ReverbSpeedOfSound
1277 /* Calculate the LF/HF decay times. */
1278 lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
1279 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1280 hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
1281 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1283 /* Update the modulator parameters. */
1284 UpdateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth,
1285 frequency, &State->Mod);
1287 /* Update the late lines. */
1288 UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
1289 lfDecayTime, props->Reverb.DecayTime, hfDecayTime,
1290 F_TAU * lf0norm, F_TAU * hf0norm,
1291 props->Reverb.EchoTime, props->Reverb.EchoDepth,
1292 frequency, &State->Late);
1294 /* Update early and late 3D panning. */
1295 gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
1296 Update3DPanning(Device, props->Reverb.ReflectionsPan,
1297 props->Reverb.LateReverbPan, gain,
1298 props->Reverb.ReflectionsGain,
1299 props->Reverb.LateReverbGain, State);
1301 /* Determine if delay-line cross-fading is required. */
1302 for(i = 0;i < NUM_LINES;i++)
1304 if(State->EarlyDelayTap[i][1] != State->EarlyDelayTap[i][0] ||
1305 State->Early.VecAp.Offset[i][1] != State->Early.VecAp.Offset[i][0] ||
1306 State->Early.Offset[i][1] != State->Early.Offset[i][0] ||
1307 State->LateDelayTap[i][1] != State->LateDelayTap[i][0] ||
1308 State->Late.VecAp.Offset[i][1] != State->Late.VecAp.Offset[i][0] ||
1309 State->Late.Offset[i][1] != State->Late.Offset[i][0])
1311 State->FadeCount = 0;
1312 break;
1315 if(State->Mod.Depth[1] != State->Mod.Depth[0])
1316 State->FadeCount = 0;
1320 /**************************************
1321 * Effect Processing *
1322 **************************************/
1324 /* Basic delay line input/output routines. */
1325 static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c)
1327 return Delay->Line[offset&Delay->Mask][c];
1330 /* Cross-faded delay line output routine. Instead of interpolating the
1331 * offsets, this interpolates (cross-fades) the outputs at each offset.
1333 static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0,
1334 const ALsizei off1, const ALsizei c, const ALfloat mu)
1336 return Delay->Line[off0&Delay->Mask][c]*(1.0f-mu) +
1337 Delay->Line[off1&Delay->Mask][c]*( mu);
1339 #define UnfadedDelayLineOut(d, o0, o1, c, mu) DelayLineOut(d, o0, c)
1341 static inline ALvoid DelayLineIn(DelayLineI *Delay, const ALsizei offset, const ALsizei c, const ALfloat in)
1343 Delay->Line[offset&Delay->Mask][c] = in;
1346 static inline ALvoid DelayLineIn4(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
1348 ALsizei i;
1349 offset &= Delay->Mask;
1350 for(i = 0;i < NUM_LINES;i++)
1351 Delay->Line[offset][i] = in[i];
1354 static inline ALvoid DelayLineIn4Rev(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
1356 ALsizei i;
1357 offset &= Delay->Mask;
1358 for(i = 0;i < NUM_LINES;i++)
1359 Delay->Line[offset][i] = in[NUM_LINES-1-i];
1362 static void CalcModulationDelays(Modulator *Mod, ALsizei (*restrict delays)[MAX_UPDATE_SAMPLES][2],
1363 const ALsizei (*restrict offsets)[2], const ALsizei todo)
1365 const ALsizei phase_offset = Mod->Range >> 2;
1366 ALfloat sinus;
1367 ALsizei c, i;
1369 for(c = 0;c < NUM_LINES;c++)
1371 ALsizei offset0 = offsets[c][0] << FRACTIONBITS;
1372 ALsizei offset1 = offsets[c][1] << FRACTIONBITS;
1373 ALsizei index = Mod->Index + phase_offset*c;
1374 for(i = 0;i < todo;i++)
1376 /* Calculate the sinus rhythm (dependent on modulation time and the
1377 * sampling rate).
1379 sinus = sinf(index * Mod->IdxScale);
1380 index = (index+1) % Mod->Range;
1382 /* Calculate the read offset. */
1383 delays[c][i][0] = fastf2i(sinus*Mod->Depth[0]) + offset0;
1384 delays[c][i][1] = fastf2i(sinus*Mod->Depth[1]) + offset1;
1387 Mod->Index = (Mod->Index+todo) % Mod->Range;
1390 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1391 * for both the below vector all-pass model and to perform modal feed-back
1392 * delay network (FDN) mixing.
1394 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1395 * matrix with a single unitary rotational parameter:
1397 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1398 * [ -a, d, c, -b ]
1399 * [ -b, -c, d, a ]
1400 * [ -c, b, -a, d ]
1402 * The rotation is constructed from the effect's diffusion parameter,
1403 * yielding:
1405 * 1 = x^2 + 3 y^2
1407 * Where a, b, and c are the coefficient y with differing signs, and d is the
1408 * coefficient x. The final matrix is thus:
1410 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1411 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1412 * [ y, -y, x, y ] x = cos(t)
1413 * [ -y, -y, -y, x ] y = sin(t) / n
1415 * Any square orthogonal matrix with an order that is a power of two will
1416 * work (where ^T is transpose, ^-1 is inverse):
1418 * M^T = M^-1
1420 * Using that knowledge, finding an appropriate matrix can be accomplished
1421 * naively by searching all combinations of:
1423 * M = D + S - S^T
1425 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1426 * whose combination of signs are being iterated.
1428 static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *restrict in,
1429 const ALfloat xCoeff, const ALfloat yCoeff)
1431 out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
1432 out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
1433 out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
1434 out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
1437 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1438 * filter to the 4-line input.
1440 * It works by vectorizing a regular all-pass filter and replacing the delay
1441 * element with a scattering matrix (like the one above) and a diagonal
1442 * matrix of delay elements.
1444 * Two static specializations are used for transitional (cross-faded) delay
1445 * line processing and non-transitional processing.
1447 #define DECL_TEMPLATE(T) \
1448 static void VectorAllpass_##T(ALfloat *restrict out, \
1449 const ALfloat *restrict in, \
1450 const ALsizei offset, const ALfloat feedCoeff, \
1451 const ALfloat xCoeff, const ALfloat yCoeff, \
1452 const ALfloat mu, VecAllpass *Vap) \
1454 ALfloat f[NUM_LINES], fs[NUM_LINES]; \
1455 ALfloat input; \
1456 ALsizei i; \
1458 (void)mu; /* Ignore for Unfaded. */ \
1460 for(i = 0;i < NUM_LINES;i++) \
1462 input = in[i]; \
1463 out[i] = T##DelayLineOut(&Vap->Delay, offset-Vap->Offset[i][0], \
1464 offset-Vap->Offset[i][1], i, mu) - \
1465 feedCoeff*input; \
1466 f[i] = input + feedCoeff*out[i]; \
1468 VectorPartialScatter(fs, f, xCoeff, yCoeff); \
1470 DelayLineIn4(&Vap->Delay, offset, fs); \
1472 DECL_TEMPLATE(Unfaded)
1473 DECL_TEMPLATE(Faded)
1474 #undef DECL_TEMPLATE
1476 /* A helper to reverse vector components. */
1477 static inline void VectorReverse(ALfloat *restrict out, const ALfloat *restrict in)
1479 ALsizei i;
1480 for(i = 0;i < NUM_LINES;i++)
1481 out[i] = in[NUM_LINES-1-i];
1484 /* This generates early reflections.
1486 * This is done by obtaining the primary reflections (those arriving from the
1487 * same direction as the source) from the main delay line. These are
1488 * attenuated and all-pass filtered (based on the diffusion parameter).
1490 * The early lines are then fed in reverse (according to the approximately
1491 * opposite spatial location of the A-Format lines) to create the secondary
1492 * reflections (those arriving from the opposite direction as the source).
1494 * The early response is then completed by combining the primary reflections
1495 * with the delayed and attenuated output from the early lines.
1497 * Finally, the early response is reversed, scattered (based on diffusion),
1498 * and fed into the late reverb section of the main delay line.
1500 * Two static specializations are used for transitional (cross-faded) delay
1501 * line processing and non-transitional processing.
1503 #define DECL_TEMPLATE(T) \
1504 static ALvoid EarlyReflection_##T(ALreverbState *State, const ALsizei todo, \
1505 ALfloat fade, \
1506 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])\
1508 ALsizei offset = State->Offset; \
1509 const ALfloat apFeedCoeff = State->ApFeedCoeff; \
1510 const ALfloat mixX = State->MixX; \
1511 const ALfloat mixY = State->MixY; \
1512 ALfloat f[NUM_LINES], fr[NUM_LINES]; \
1513 ALsizei i, j; \
1515 for(i = 0;i < todo;i++) \
1517 for(j = 0;j < NUM_LINES;j++) \
1518 fr[j] = T##DelayLineOut(&State->Delay, \
1519 offset-State->EarlyDelayTap[j][0], \
1520 offset-State->EarlyDelayTap[j][1], j, fade \
1521 ) * State->EarlyDelayCoeff[j]; \
1523 VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \
1524 &State->Early.VecAp); \
1526 DelayLineIn4Rev(&State->Early.Delay, offset, f); \
1528 for(j = 0;j < NUM_LINES;j++) \
1529 f[j] += T##DelayLineOut(&State->Early.Delay, \
1530 offset-State->Early.Offset[j][0], \
1531 offset-State->Early.Offset[j][1], j, fade \
1532 ) * State->Early.Coeff[j]; \
1534 for(j = 0;j < NUM_LINES;j++) \
1535 out[j][i] = f[j]; \
1537 VectorReverse(fr, f); \
1538 VectorPartialScatter(f, fr, mixX, mixY); \
1540 DelayLineIn4(&State->Delay, offset-State->LateFeedTap, f); \
1542 offset++; \
1543 fade += FadeStep; \
1546 DECL_TEMPLATE(Unfaded)
1547 DECL_TEMPLATE(Faded)
1548 #undef DECL_TEMPLATE
1550 /* Applies a first order filter section. */
1551 static inline ALfloat FirstOrderFilter(const ALfloat in, const ALfloat *restrict coeffs,
1552 ALfloat *restrict state)
1554 ALfloat out = coeffs[0]*in + coeffs[1]*state[0] + coeffs[2]*state[1];
1555 state[0] = in;
1556 state[1] = out;
1557 return out;
1560 /* Applies the two T60 damping filter sections. */
1561 static inline void LateT60Filter(ALfloat *restrict out, const ALfloat *restrict in,
1562 T60Filter *filter)
1564 ALsizei i;
1565 for(i = 0;i < NUM_LINES;i++)
1566 out[i] = FirstOrderFilter(
1567 FirstOrderFilter(in[i], filter[i].HFCoeffs, filter[i].HFState),
1568 filter[i].LFCoeffs, filter[i].LFState
1572 /* This generates the reverb tail using a modified feed-back delay network
1573 * (FDN).
1575 * Results from the early reflections are attenuated by the density gain and
1576 * mixed with the output from the late delay lines.
1578 * The late response is then completed by T60 and all-pass filtering the mix.
1580 * Finally, the lines are reversed (so they feed their opposite directions)
1581 * and scattered with the FDN matrix before re-feeding the delay lines.
1583 * Two variations are made, one for for transitional (cross-faded) delay line
1584 * processing and one for non-transitional processing.
1586 static ALvoid LateReverb_Faded(ALreverbState *State, const ALsizei todo, ALfloat fade,
1587 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1589 ALsizei (*restrict moddelay)[MAX_UPDATE_SAMPLES][2] = State->ModulationDelays;
1590 const ALfloat apFeedCoeff = State->ApFeedCoeff;
1591 const ALfloat mixX = State->MixX;
1592 const ALfloat mixY = State->MixY;
1593 ALsizei offset;
1594 ALsizei i, j;
1596 CalcModulationDelays(&State->Mod, moddelay, State->Late.Offset, todo);
1598 offset = State->Offset;
1599 for(i = 0;i < todo;i++)
1601 ALfloat f[NUM_LINES], fr[NUM_LINES];
1603 for(j = 0;j < NUM_LINES;j++)
1604 f[j] = FadedDelayLineOut(&State->Delay,
1605 offset - State->LateDelayTap[j][0],
1606 offset - State->LateDelayTap[j][1], j, fade
1607 ) * State->Late.DensityGain;
1609 for(j = 0;j < NUM_LINES;j++)
1610 f[j] += FadedDelayLineOut(&State->Late.Delay,
1611 offset - State->Late.Offset[j][0],
1612 offset - State->Late.Offset[j][1], j, fade
1615 LateT60Filter(fr, f, State->Late.T60);
1616 VectorAllpass_Faded(f, fr, offset, apFeedCoeff, mixX, mixY, fade,
1617 &State->Late.VecAp);
1619 for(j = 0;j < NUM_LINES;j++)
1620 out[j][i] = f[j];
1622 VectorReverse(fr, f);
1623 VectorPartialScatter(f, fr, mixX, mixY);
1625 DelayLineIn4(&State->Late.Delay, offset, f);
1627 offset++;
1628 fade += FadeStep;
1631 static ALvoid LateReverb_Unfaded(ALreverbState *State, const ALsizei todo, ALfloat fade,
1632 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1634 ALsizei (*restrict moddelay)[MAX_UPDATE_SAMPLES][2] = State->ModulationDelays;
1635 const ALfloat apFeedCoeff = State->ApFeedCoeff;
1636 const ALfloat mixX = State->MixX;
1637 const ALfloat mixY = State->MixY;
1638 ALsizei offset;
1639 ALsizei i, j;
1641 CalcModulationDelays(&State->Mod, moddelay, State->Late.Offset, todo);
1643 offset = State->Offset;
1644 for(i = 0;i < todo;i++)
1646 ALfloat f[NUM_LINES], fr[NUM_LINES];
1648 for(j = 0;j < NUM_LINES;j++)
1649 f[j] = DelayLineOut(&State->Delay, offset-State->LateDelayTap[j][0], j) *
1650 State->Late.DensityGain;
1652 for(j = 0;j < NUM_LINES;j++)
1653 f[j] += DelayLineOut(&State->Late.Delay, offset-State->Late.Offset[j][0], j);
1655 LateT60Filter(fr, f, State->Late.T60);
1656 VectorAllpass_Unfaded(f, fr, offset, apFeedCoeff, mixX, mixY, fade,
1657 &State->Late.VecAp);
1659 for(j = 0;j < NUM_LINES;j++)
1660 out[j][i] = f[j];
1662 VectorReverse(fr, f);
1663 VectorPartialScatter(f, fr, mixX, mixY);
1665 DelayLineIn4(&State->Late.Delay, offset, f);
1667 offset++;
1671 static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
1673 ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->AFormatSamples;
1674 ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples;
1675 ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples;
1676 ALsizei fadeCount = State->FadeCount;
1677 ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES;
1678 ALsizei base, c, i;
1680 /* Process reverb for these samples. */
1681 for(base = 0;base < SamplesToDo;)
1683 ALsizei todo = mini(SamplesToDo-base, MAX_UPDATE_SAMPLES);
1684 /* If cross-fading, don't do more samples than there are to fade. */
1685 if(FADE_SAMPLES-fadeCount > 0)
1686 todo = mini(todo, FADE_SAMPLES-fadeCount);
1688 /* Convert B-Format to A-Format for processing. */
1689 memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
1690 for(c = 0;c < NUM_LINES;c++)
1691 MixRowSamples(afmt[c], B2A.m[c],
1692 SamplesIn, MAX_EFFECT_CHANNELS, base, todo
1695 /* Process the samples for reverb. */
1696 for(c = 0;c < NUM_LINES;c++)
1698 /* Band-pass the incoming samples. Use the early output lines for
1699 * temp storage.
1701 ALfilterState_process(&State->Filter[c].Lp, early[0], afmt[c], todo);
1702 ALfilterState_process(&State->Filter[c].Hp, early[1], early[0], todo);
1704 /* Feed the initial delay line. */
1705 for(i = 0;i < todo;i++)
1706 DelayLineIn(&State->Delay, State->Offset+i, c, early[1][i]);
1709 if(UNLIKELY(fadeCount < FADE_SAMPLES))
1711 /* Generate early reflections. */
1712 EarlyReflection_Faded(State, todo, fade, early);
1714 /* Generate late reverb. */
1715 LateReverb_Faded(State, todo, fade, late);
1716 fade = minf(1.0f, fade + todo*FadeStep);
1718 else
1720 /* Generate early reflections. */
1721 EarlyReflection_Unfaded(State, todo, fade, early);
1723 /* Generate late reverb. */
1724 LateReverb_Unfaded(State, todo, fade, late);
1727 /* Step all delays forward. */
1728 State->Offset += todo;
1730 if(UNLIKELY(fadeCount < FADE_SAMPLES) && (fadeCount += todo) >= FADE_SAMPLES)
1732 /* Update the cross-fading delay line taps. */
1733 fadeCount = FADE_SAMPLES;
1734 fade = 1.0f;
1735 for(c = 0;c < NUM_LINES;c++)
1737 State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
1738 State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1];
1739 State->Early.Offset[c][0] = State->Early.Offset[c][1];
1740 State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
1741 State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1];
1742 State->Late.Offset[c][0] = State->Late.Offset[c][1];
1744 State->Mod.Depth[0] = State->Mod.Depth[1];
1747 /* Mix the A-Format results to output, implicitly converting back to
1748 * B-Format.
1750 for(c = 0;c < NUM_LINES;c++)
1751 MixSamples(early[c], NumChannels, SamplesOut,
1752 State->Early.CurrentGain[c], State->Early.PanGain[c],
1753 SamplesToDo-base, base, todo
1755 for(c = 0;c < NUM_LINES;c++)
1756 MixSamples(late[c], NumChannels, SamplesOut,
1757 State->Late.CurrentGain[c], State->Late.PanGain[c],
1758 SamplesToDo-base, base, todo
1761 base += todo;
1763 State->FadeCount = fadeCount;
1767 typedef struct ALreverbStateFactory {
1768 DERIVE_FROM_TYPE(ALeffectStateFactory);
1769 } ALreverbStateFactory;
1771 static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory))
1773 ALreverbState *state;
1775 NEW_OBJ0(state, ALreverbState)();
1776 if(!state) return NULL;
1778 return STATIC_CAST(ALeffectState, state);
1781 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALreverbStateFactory);
1783 ALeffectStateFactory *ALreverbStateFactory_getFactory(void)
1785 static ALreverbStateFactory ReverbFactory = { { GET_VTABLE2(ALreverbStateFactory, ALeffectStateFactory) } };
1787 return STATIC_CAST(ALeffectStateFactory, &ReverbFactory);
1791 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1793 ALeffectProps *props = &effect->Props;
1794 switch(param)
1796 case AL_EAXREVERB_DECAY_HFLIMIT:
1797 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1798 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
1799 props->Reverb.DecayHFLimit = val;
1800 break;
1802 default:
1803 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1804 param);
1807 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1808 { ALeaxreverb_setParami(effect, context, param, vals[0]); }
1809 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1811 ALeffectProps *props = &effect->Props;
1812 switch(param)
1814 case AL_EAXREVERB_DENSITY:
1815 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1816 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
1817 props->Reverb.Density = val;
1818 break;
1820 case AL_EAXREVERB_DIFFUSION:
1821 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1822 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
1823 props->Reverb.Diffusion = val;
1824 break;
1826 case AL_EAXREVERB_GAIN:
1827 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1828 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
1829 props->Reverb.Gain = val;
1830 break;
1832 case AL_EAXREVERB_GAINHF:
1833 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1834 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
1835 props->Reverb.GainHF = val;
1836 break;
1838 case AL_EAXREVERB_GAINLF:
1839 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1840 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
1841 props->Reverb.GainLF = val;
1842 break;
1844 case AL_EAXREVERB_DECAY_TIME:
1845 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1846 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
1847 props->Reverb.DecayTime = val;
1848 break;
1850 case AL_EAXREVERB_DECAY_HFRATIO:
1851 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1852 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
1853 props->Reverb.DecayHFRatio = val;
1854 break;
1856 case AL_EAXREVERB_DECAY_LFRATIO:
1857 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1858 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
1859 props->Reverb.DecayLFRatio = val;
1860 break;
1862 case AL_EAXREVERB_REFLECTIONS_GAIN:
1863 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1864 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
1865 props->Reverb.ReflectionsGain = val;
1866 break;
1868 case AL_EAXREVERB_REFLECTIONS_DELAY:
1869 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1870 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
1871 props->Reverb.ReflectionsDelay = val;
1872 break;
1874 case AL_EAXREVERB_LATE_REVERB_GAIN:
1875 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1876 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
1877 props->Reverb.LateReverbGain = val;
1878 break;
1880 case AL_EAXREVERB_LATE_REVERB_DELAY:
1881 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1882 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
1883 props->Reverb.LateReverbDelay = val;
1884 break;
1886 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1887 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1888 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
1889 props->Reverb.AirAbsorptionGainHF = val;
1890 break;
1892 case AL_EAXREVERB_ECHO_TIME:
1893 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1894 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
1895 props->Reverb.EchoTime = val;
1896 break;
1898 case AL_EAXREVERB_ECHO_DEPTH:
1899 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1900 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
1901 props->Reverb.EchoDepth = val;
1902 break;
1904 case AL_EAXREVERB_MODULATION_TIME:
1905 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1906 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
1907 props->Reverb.ModulationTime = val;
1908 break;
1910 case AL_EAXREVERB_MODULATION_DEPTH:
1911 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1912 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
1913 props->Reverb.ModulationDepth = val;
1914 break;
1916 case AL_EAXREVERB_HFREFERENCE:
1917 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1918 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
1919 props->Reverb.HFReference = val;
1920 break;
1922 case AL_EAXREVERB_LFREFERENCE:
1923 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1924 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
1925 props->Reverb.LFReference = val;
1926 break;
1928 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1929 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1930 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
1931 props->Reverb.RoomRolloffFactor = val;
1932 break;
1934 default:
1935 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1936 param);
1939 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1941 ALeffectProps *props = &effect->Props;
1942 switch(param)
1944 case AL_EAXREVERB_REFLECTIONS_PAN:
1945 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1946 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
1947 props->Reverb.ReflectionsPan[0] = vals[0];
1948 props->Reverb.ReflectionsPan[1] = vals[1];
1949 props->Reverb.ReflectionsPan[2] = vals[2];
1950 break;
1951 case AL_EAXREVERB_LATE_REVERB_PAN:
1952 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1953 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
1954 props->Reverb.LateReverbPan[0] = vals[0];
1955 props->Reverb.LateReverbPan[1] = vals[1];
1956 props->Reverb.LateReverbPan[2] = vals[2];
1957 break;
1959 default:
1960 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1961 break;
1965 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1967 const ALeffectProps *props = &effect->Props;
1968 switch(param)
1970 case AL_EAXREVERB_DECAY_HFLIMIT:
1971 *val = props->Reverb.DecayHFLimit;
1972 break;
1974 default:
1975 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1976 param);
1979 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1980 { ALeaxreverb_getParami(effect, context, param, vals); }
1981 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1983 const ALeffectProps *props = &effect->Props;
1984 switch(param)
1986 case AL_EAXREVERB_DENSITY:
1987 *val = props->Reverb.Density;
1988 break;
1990 case AL_EAXREVERB_DIFFUSION:
1991 *val = props->Reverb.Diffusion;
1992 break;
1994 case AL_EAXREVERB_GAIN:
1995 *val = props->Reverb.Gain;
1996 break;
1998 case AL_EAXREVERB_GAINHF:
1999 *val = props->Reverb.GainHF;
2000 break;
2002 case AL_EAXREVERB_GAINLF:
2003 *val = props->Reverb.GainLF;
2004 break;
2006 case AL_EAXREVERB_DECAY_TIME:
2007 *val = props->Reverb.DecayTime;
2008 break;
2010 case AL_EAXREVERB_DECAY_HFRATIO:
2011 *val = props->Reverb.DecayHFRatio;
2012 break;
2014 case AL_EAXREVERB_DECAY_LFRATIO:
2015 *val = props->Reverb.DecayLFRatio;
2016 break;
2018 case AL_EAXREVERB_REFLECTIONS_GAIN:
2019 *val = props->Reverb.ReflectionsGain;
2020 break;
2022 case AL_EAXREVERB_REFLECTIONS_DELAY:
2023 *val = props->Reverb.ReflectionsDelay;
2024 break;
2026 case AL_EAXREVERB_LATE_REVERB_GAIN:
2027 *val = props->Reverb.LateReverbGain;
2028 break;
2030 case AL_EAXREVERB_LATE_REVERB_DELAY:
2031 *val = props->Reverb.LateReverbDelay;
2032 break;
2034 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
2035 *val = props->Reverb.AirAbsorptionGainHF;
2036 break;
2038 case AL_EAXREVERB_ECHO_TIME:
2039 *val = props->Reverb.EchoTime;
2040 break;
2042 case AL_EAXREVERB_ECHO_DEPTH:
2043 *val = props->Reverb.EchoDepth;
2044 break;
2046 case AL_EAXREVERB_MODULATION_TIME:
2047 *val = props->Reverb.ModulationTime;
2048 break;
2050 case AL_EAXREVERB_MODULATION_DEPTH:
2051 *val = props->Reverb.ModulationDepth;
2052 break;
2054 case AL_EAXREVERB_HFREFERENCE:
2055 *val = props->Reverb.HFReference;
2056 break;
2058 case AL_EAXREVERB_LFREFERENCE:
2059 *val = props->Reverb.LFReference;
2060 break;
2062 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
2063 *val = props->Reverb.RoomRolloffFactor;
2064 break;
2066 default:
2067 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
2068 param);
2071 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2073 const ALeffectProps *props = &effect->Props;
2074 switch(param)
2076 case AL_EAXREVERB_REFLECTIONS_PAN:
2077 vals[0] = props->Reverb.ReflectionsPan[0];
2078 vals[1] = props->Reverb.ReflectionsPan[1];
2079 vals[2] = props->Reverb.ReflectionsPan[2];
2080 break;
2081 case AL_EAXREVERB_LATE_REVERB_PAN:
2082 vals[0] = props->Reverb.LateReverbPan[0];
2083 vals[1] = props->Reverb.LateReverbPan[1];
2084 vals[2] = props->Reverb.LateReverbPan[2];
2085 break;
2087 default:
2088 ALeaxreverb_getParamf(effect, context, param, vals);
2089 break;
2093 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
2095 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
2097 ALeffectProps *props = &effect->Props;
2098 switch(param)
2100 case AL_REVERB_DECAY_HFLIMIT:
2101 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
2102 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
2103 props->Reverb.DecayHFLimit = val;
2104 break;
2106 default:
2107 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
2110 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
2111 { ALreverb_setParami(effect, context, param, vals[0]); }
2112 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
2114 ALeffectProps *props = &effect->Props;
2115 switch(param)
2117 case AL_REVERB_DENSITY:
2118 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
2119 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
2120 props->Reverb.Density = val;
2121 break;
2123 case AL_REVERB_DIFFUSION:
2124 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
2125 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
2126 props->Reverb.Diffusion = val;
2127 break;
2129 case AL_REVERB_GAIN:
2130 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
2131 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
2132 props->Reverb.Gain = val;
2133 break;
2135 case AL_REVERB_GAINHF:
2136 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
2137 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
2138 props->Reverb.GainHF = val;
2139 break;
2141 case AL_REVERB_DECAY_TIME:
2142 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
2143 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
2144 props->Reverb.DecayTime = val;
2145 break;
2147 case AL_REVERB_DECAY_HFRATIO:
2148 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
2149 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
2150 props->Reverb.DecayHFRatio = val;
2151 break;
2153 case AL_REVERB_REFLECTIONS_GAIN:
2154 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
2155 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
2156 props->Reverb.ReflectionsGain = val;
2157 break;
2159 case AL_REVERB_REFLECTIONS_DELAY:
2160 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
2161 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
2162 props->Reverb.ReflectionsDelay = val;
2163 break;
2165 case AL_REVERB_LATE_REVERB_GAIN:
2166 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
2167 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
2168 props->Reverb.LateReverbGain = val;
2169 break;
2171 case AL_REVERB_LATE_REVERB_DELAY:
2172 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
2173 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
2174 props->Reverb.LateReverbDelay = val;
2175 break;
2177 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2178 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
2179 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
2180 props->Reverb.AirAbsorptionGainHF = val;
2181 break;
2183 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2184 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
2185 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
2186 props->Reverb.RoomRolloffFactor = val;
2187 break;
2189 default:
2190 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2193 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
2194 { ALreverb_setParamf(effect, context, param, vals[0]); }
2196 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
2198 const ALeffectProps *props = &effect->Props;
2199 switch(param)
2201 case AL_REVERB_DECAY_HFLIMIT:
2202 *val = props->Reverb.DecayHFLimit;
2203 break;
2205 default:
2206 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
2209 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
2210 { ALreverb_getParami(effect, context, param, vals); }
2211 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
2213 const ALeffectProps *props = &effect->Props;
2214 switch(param)
2216 case AL_REVERB_DENSITY:
2217 *val = props->Reverb.Density;
2218 break;
2220 case AL_REVERB_DIFFUSION:
2221 *val = props->Reverb.Diffusion;
2222 break;
2224 case AL_REVERB_GAIN:
2225 *val = props->Reverb.Gain;
2226 break;
2228 case AL_REVERB_GAINHF:
2229 *val = props->Reverb.GainHF;
2230 break;
2232 case AL_REVERB_DECAY_TIME:
2233 *val = props->Reverb.DecayTime;
2234 break;
2236 case AL_REVERB_DECAY_HFRATIO:
2237 *val = props->Reverb.DecayHFRatio;
2238 break;
2240 case AL_REVERB_REFLECTIONS_GAIN:
2241 *val = props->Reverb.ReflectionsGain;
2242 break;
2244 case AL_REVERB_REFLECTIONS_DELAY:
2245 *val = props->Reverb.ReflectionsDelay;
2246 break;
2248 case AL_REVERB_LATE_REVERB_GAIN:
2249 *val = props->Reverb.LateReverbGain;
2250 break;
2252 case AL_REVERB_LATE_REVERB_DELAY:
2253 *val = props->Reverb.LateReverbDelay;
2254 break;
2256 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2257 *val = props->Reverb.AirAbsorptionGainHF;
2258 break;
2260 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2261 *val = props->Reverb.RoomRolloffFactor;
2262 break;
2264 default:
2265 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2268 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2269 { ALreverb_getParamf(effect, context, param, vals); }
2271 DEFINE_ALEFFECT_VTABLE(ALreverb);