Allow building alffplay without experimental extensions
[openal-soft.git] / Alc / effects / reverb.c
blob8ebc089eaaeb34cde5c241b369b97bc639db9cf9
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 ALfloat Coeff;
227 ALsizei Offset[NUM_LINES][2];
228 } VecAllpass;
230 typedef struct T60Filter {
231 /* Two filters are used to adjust the signal. One to control the low
232 * frequencies, and one to control the high frequencies.
234 ALfloat MidGain[2];
235 BiquadFilter HFFilter, LFFilter;
236 } T60Filter;
238 typedef struct EarlyReflections {
239 /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
240 * The spread from this filter also helps smooth out the reverb tail.
242 VecAllpass VecAp;
244 /* An echo line is used to complete the second half of the early
245 * reflections.
247 DelayLineI Delay;
248 ALsizei Offset[NUM_LINES][2];
249 ALfloat Coeff[NUM_LINES][2];
251 /* The gain for each output channel based on 3D panning. */
252 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
253 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
254 } EarlyReflections;
256 typedef struct LateReverb {
257 /* A recursive delay line is used fill in the reverb tail. */
258 DelayLineI Delay;
259 ALsizei Offset[NUM_LINES][2];
261 /* Attenuation to compensate for the modal density and decay rate of the
262 * late lines.
264 ALfloat DensityGain[2];
266 /* T60 decay filters are used to simulate absorption. */
267 T60Filter T60[NUM_LINES];
269 /* A Gerzon vector all-pass filter is used to simulate diffusion. */
270 VecAllpass VecAp;
272 /* The gain for each output channel based on 3D panning. */
273 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
274 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
275 } LateReverb;
277 typedef struct ReverbState {
278 DERIVE_FROM_TYPE(ALeffectState);
280 /* All delay lines are allocated as a single buffer to reduce memory
281 * fragmentation and management code.
283 ALfloat *SampleBuffer;
284 ALuint TotalSamples;
286 struct {
287 /* Calculated parameters which indicate if cross-fading is needed after
288 * an update.
290 ALfloat Density, Diffusion;
291 ALfloat DecayTime, HFDecayTime, LFDecayTime;
292 ALfloat HFReference, LFReference;
293 } Params;
295 /* Master effect filters */
296 struct {
297 BiquadFilter Lp;
298 BiquadFilter Hp;
299 } Filter[NUM_LINES];
301 /* Core delay line (early reflections and late reverb tap from this). */
302 DelayLineI Delay;
304 /* Tap points for early reflection delay. */
305 ALsizei EarlyDelayTap[NUM_LINES][2];
306 ALfloat EarlyDelayCoeff[NUM_LINES][2];
308 /* Tap points for late reverb feed and delay. */
309 ALsizei LateFeedTap;
310 ALsizei LateDelayTap[NUM_LINES][2];
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 /* Maximum number of samples to process at once. */
324 ALsizei MaxUpdate[2];
326 /* The current write offset for all delay lines. */
327 ALsizei Offset;
329 /* Temporary storage used when processing. */
330 alignas(16) ALfloat TempSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
331 alignas(16) ALfloat MixSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
332 } ReverbState;
334 static ALvoid ReverbState_Destruct(ReverbState *State);
335 static ALboolean ReverbState_deviceUpdate(ReverbState *State, ALCdevice *Device);
336 static ALvoid ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
337 static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
338 DECLARE_DEFAULT_ALLOCATORS(ReverbState)
340 DEFINE_ALEFFECTSTATE_VTABLE(ReverbState);
342 static void ReverbState_Construct(ReverbState *state)
344 ALsizei i, j;
346 ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
347 SET_VTABLE2(ReverbState, ALeffectState, state);
349 state->TotalSamples = 0;
350 state->SampleBuffer = NULL;
352 state->Params.Density = AL_EAXREVERB_DEFAULT_DENSITY;
353 state->Params.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION;
354 state->Params.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME;
355 state->Params.HFDecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME*AL_EAXREVERB_DEFAULT_DECAY_HFRATIO;
356 state->Params.LFDecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME*AL_EAXREVERB_DEFAULT_DECAY_LFRATIO;
357 state->Params.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE;
358 state->Params.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE;
360 for(i = 0;i < NUM_LINES;i++)
362 BiquadFilter_clear(&state->Filter[i].Lp);
363 BiquadFilter_clear(&state->Filter[i].Hp);
366 state->Delay.Mask = 0;
367 state->Delay.Line = NULL;
369 for(i = 0;i < NUM_LINES;i++)
371 state->EarlyDelayTap[i][0] = 0;
372 state->EarlyDelayTap[i][1] = 0;
373 state->EarlyDelayCoeff[i][0] = 0.0f;
374 state->EarlyDelayCoeff[i][1] = 0.0f;
377 state->LateFeedTap = 0;
379 for(i = 0;i < NUM_LINES;i++)
381 state->LateDelayTap[i][0] = 0;
382 state->LateDelayTap[i][1] = 0;
385 state->MixX = 0.0f;
386 state->MixY = 0.0f;
388 state->Early.VecAp.Delay.Mask = 0;
389 state->Early.VecAp.Delay.Line = NULL;
390 state->Early.VecAp.Coeff = 0.0f;
391 state->Early.Delay.Mask = 0;
392 state->Early.Delay.Line = NULL;
393 for(i = 0;i < NUM_LINES;i++)
395 state->Early.VecAp.Offset[i][0] = 0;
396 state->Early.VecAp.Offset[i][1] = 0;
397 state->Early.Offset[i][0] = 0;
398 state->Early.Offset[i][1] = 0;
399 state->Early.Coeff[i][0] = 0.0f;
400 state->Early.Coeff[i][1] = 0.0f;
403 state->Late.DensityGain[0] = 0.0f;
404 state->Late.DensityGain[1] = 0.0f;
405 state->Late.Delay.Mask = 0;
406 state->Late.Delay.Line = NULL;
407 state->Late.VecAp.Delay.Mask = 0;
408 state->Late.VecAp.Delay.Line = NULL;
409 state->Late.VecAp.Coeff = 0.0f;
410 for(i = 0;i < NUM_LINES;i++)
412 state->Late.Offset[i][0] = 0;
413 state->Late.Offset[i][1] = 0;
415 state->Late.VecAp.Offset[i][0] = 0;
416 state->Late.VecAp.Offset[i][1] = 0;
418 state->Late.T60[i].MidGain[0] = 0.0f;
419 state->Late.T60[i].MidGain[1] = 0.0f;
420 BiquadFilter_clear(&state->Late.T60[i].HFFilter);
421 BiquadFilter_clear(&state->Late.T60[i].LFFilter);
424 for(i = 0;i < NUM_LINES;i++)
426 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
428 state->Early.CurrentGain[i][j] = 0.0f;
429 state->Early.PanGain[i][j] = 0.0f;
430 state->Late.CurrentGain[i][j] = 0.0f;
431 state->Late.PanGain[i][j] = 0.0f;
435 state->FadeCount = 0;
436 state->MaxUpdate[0] = MAX_UPDATE_SAMPLES;
437 state->MaxUpdate[1] = MAX_UPDATE_SAMPLES;
438 state->Offset = 0;
441 static ALvoid ReverbState_Destruct(ReverbState *State)
443 al_free(State->SampleBuffer);
444 State->SampleBuffer = NULL;
446 ALeffectState_Destruct(STATIC_CAST(ALeffectState,State));
449 /**************************************
450 * Device Update *
451 **************************************/
453 static inline ALfloat CalcDelayLengthMult(ALfloat density)
455 return maxf(5.0f, cbrtf(density*DENSITY_SCALE));
458 /* Given the allocated sample buffer, this function updates each delay line
459 * offset.
461 static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay)
463 union {
464 ALfloat *f;
465 ALfloat (*f4)[NUM_LINES];
466 } u;
467 u.f = &sampleBuffer[(ptrdiff_t)Delay->Line * NUM_LINES];
468 Delay->Line = u.f4;
471 /* Calculate the length of a delay line and store its mask and offset. */
472 static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALuint frequency,
473 const ALuint extra, DelayLineI *Delay)
475 ALuint samples;
477 /* All line lengths are powers of 2, calculated from their lengths in
478 * seconds, rounded up.
480 samples = float2int(ceilf(length*frequency));
481 samples = NextPowerOf2(samples + extra);
483 /* All lines share a single sample buffer. */
484 Delay->Mask = samples - 1;
485 Delay->Line = (ALfloat(*)[NUM_LINES])offset;
487 /* Return the sample count for accumulation. */
488 return samples;
491 /* Calculates the delay line metrics and allocates the shared sample buffer
492 * for all lines given the sample rate (frequency). If an allocation failure
493 * occurs, it returns AL_FALSE.
495 static ALboolean AllocLines(const ALuint frequency, ReverbState *State)
497 ALuint totalSamples, i;
498 ALfloat multiplier, length;
500 /* All delay line lengths are calculated to accomodate the full range of
501 * lengths given their respective paramters.
503 totalSamples = 0;
505 /* Multiplier for the maximum density value, i.e. density=1, which is
506 * actually the least density...
508 multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
510 /* The main delay length includes the maximum early reflection delay, the
511 * largest early tap width, the maximum late reverb delay, and the
512 * largest late tap width. Finally, it must also be extended by the
513 * update size (MAX_UPDATE_SAMPLES) for block processing.
515 length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier +
516 AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
517 (LATE_LINE_LENGTHS[NUM_LINES-1] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
518 totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES,
519 &State->Delay);
521 /* The early vector all-pass line. */
522 length = EARLY_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
523 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
524 &State->Early.VecAp.Delay);
526 /* The early reflection line. */
527 length = EARLY_LINE_LENGTHS[NUM_LINES-1] * multiplier;
528 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
529 &State->Early.Delay);
531 /* The late vector all-pass line. */
532 length = LATE_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
533 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
534 &State->Late.VecAp.Delay);
536 /* The late delay lines are calculated from the largest maximum density
537 * line length.
539 length = LATE_LINE_LENGTHS[NUM_LINES-1] * multiplier;
540 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
541 &State->Late.Delay);
543 if(totalSamples != State->TotalSamples)
545 ALfloat *newBuffer;
547 TRACE("New reverb buffer length: %ux4 samples\n", totalSamples);
548 newBuffer = al_calloc(16, sizeof(ALfloat[NUM_LINES]) * totalSamples);
549 if(!newBuffer) return AL_FALSE;
551 al_free(State->SampleBuffer);
552 State->SampleBuffer = newBuffer;
553 State->TotalSamples = totalSamples;
556 /* Update all delays to reflect the new sample buffer. */
557 RealizeLineOffset(State->SampleBuffer, &State->Delay);
558 RealizeLineOffset(State->SampleBuffer, &State->Early.VecAp.Delay);
559 RealizeLineOffset(State->SampleBuffer, &State->Early.Delay);
560 RealizeLineOffset(State->SampleBuffer, &State->Late.VecAp.Delay);
561 RealizeLineOffset(State->SampleBuffer, &State->Late.Delay);
563 /* Clear the sample buffer. */
564 for(i = 0;i < State->TotalSamples;i++)
565 State->SampleBuffer[i] = 0.0f;
567 return AL_TRUE;
570 static ALboolean ReverbState_deviceUpdate(ReverbState *State, ALCdevice *Device)
572 ALuint frequency = Device->Frequency;
573 ALfloat multiplier;
574 ALsizei i, j;
576 /* Allocate the delay lines. */
577 if(!AllocLines(frequency, State))
578 return AL_FALSE;
580 multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
582 /* The late feed taps are set a fixed position past the latest delay tap. */
583 State->LateFeedTap = float2int((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
584 EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier) *
585 frequency);
587 /* Clear filters and gain coefficients since the delay lines were all just
588 * cleared (if not reallocated).
590 for(i = 0;i < NUM_LINES;i++)
592 BiquadFilter_clear(&State->Filter[i].Lp);
593 BiquadFilter_clear(&State->Filter[i].Hp);
596 for(i = 0;i < NUM_LINES;i++)
598 State->EarlyDelayCoeff[i][0] = 0.0f;
599 State->EarlyDelayCoeff[i][1] = 0.0f;
602 for(i = 0;i < NUM_LINES;i++)
604 State->Early.Coeff[i][0] = 0.0f;
605 State->Early.Coeff[i][1] = 0.0f;
608 State->Late.DensityGain[0] = 0.0f;
609 State->Late.DensityGain[1] = 0.0f;
610 for(i = 0;i < NUM_LINES;i++)
612 State->Late.T60[i].MidGain[0] = 0.0f;
613 State->Late.T60[i].MidGain[1] = 0.0f;
614 BiquadFilter_clear(&State->Late.T60[i].HFFilter);
615 BiquadFilter_clear(&State->Late.T60[i].LFFilter);
618 for(i = 0;i < NUM_LINES;i++)
620 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
622 State->Early.CurrentGain[i][j] = 0.0f;
623 State->Early.PanGain[i][j] = 0.0f;
624 State->Late.CurrentGain[i][j] = 0.0f;
625 State->Late.PanGain[i][j] = 0.0f;
629 /* Reset counters and offset base. */
630 State->FadeCount = 0;
631 State->MaxUpdate[0] = MAX_UPDATE_SAMPLES;
632 State->MaxUpdate[1] = MAX_UPDATE_SAMPLES;
633 State->Offset = 0;
635 return AL_TRUE;
638 /**************************************
639 * Effect Update *
640 **************************************/
642 /* Calculate a decay coefficient given the length of each cycle and the time
643 * until the decay reaches -60 dB.
645 static inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
647 return powf(REVERB_DECAY_GAIN, length/decayTime);
650 /* Calculate a decay length from a coefficient and the time until the decay
651 * reaches -60 dB.
653 static inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
655 return log10f(coeff) * decayTime / log10f(REVERB_DECAY_GAIN);
658 /* Calculate an attenuation to be applied to the input of any echo models to
659 * compensate for modal density and decay time.
661 static inline ALfloat CalcDensityGain(const ALfloat a)
663 /* The energy of a signal can be obtained by finding the area under the
664 * squared signal. This takes the form of Sum(x_n^2), where x is the
665 * amplitude for the sample n.
667 * Decaying feedback matches exponential decay of the form Sum(a^n),
668 * where a is the attenuation coefficient, and n is the sample. The area
669 * under this decay curve can be calculated as: 1 / (1 - a).
671 * Modifying the above equation to find the area under the squared curve
672 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
673 * calculated by inverting the square root of this approximation,
674 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
676 return sqrtf(1.0f - a*a);
679 /* Calculate the scattering matrix coefficients given a diffusion factor. */
680 static inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
682 ALfloat n, t;
684 /* The matrix is of order 4, so n is sqrt(4 - 1). */
685 n = sqrtf(3.0f);
686 t = diffusion * atanf(n);
688 /* Calculate the first mixing matrix coefficient. */
689 *x = cosf(t);
690 /* Calculate the second mixing matrix coefficient. */
691 *y = sinf(t) / n;
694 /* Calculate the limited HF ratio for use with the late reverb low-pass
695 * filters.
697 static ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
698 const ALfloat decayTime, const ALfloat SpeedOfSound)
700 ALfloat limitRatio;
702 /* Find the attenuation due to air absorption in dB (converting delay
703 * time to meters using the speed of sound). Then reversing the decay
704 * equation, solve for HF ratio. The delay length is cancelled out of
705 * the equation, so it can be calculated once for all lines.
707 limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound);
709 /* Using the limit calculated above, apply the upper bound to the HF ratio.
711 return minf(limitRatio, hfRatio);
715 /* Calculates the 3-band T60 damping coefficients for a particular delay line
716 * of specified length, using a combination of two shelf filter sections given
717 * decay times for each band split at two reference frequencies.
719 static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime,
720 const ALfloat mfDecayTime, const ALfloat hfDecayTime,
721 const ALfloat lf0norm, const ALfloat hf0norm,
722 T60Filter *filter)
724 ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime);
725 ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime);
726 ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime);
728 filter->MidGain[1] = mfGain;
729 BiquadFilter_setParams(&filter->LFFilter, BiquadType_LowShelf, lfGain/mfGain, lf0norm,
730 calc_rcpQ_from_slope(lfGain/mfGain, 1.0f));
731 BiquadFilter_setParams(&filter->HFFilter, BiquadType_HighShelf, hfGain/mfGain, hf0norm,
732 calc_rcpQ_from_slope(hfGain/mfGain, 1.0f));
735 /* Update the offsets for the main effect delay line. */
736 static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ReverbState *State)
738 ALfloat multiplier, length;
739 ALuint i;
741 multiplier = CalcDelayLengthMult(density);
743 /* Early reflection taps are decorrelated by means of an average room
744 * reflection approximation described above the definition of the taps.
745 * This approximation is linear and so the above density multiplier can
746 * be applied to adjust the width of the taps. A single-band decay
747 * coefficient is applied to simulate initial attenuation and absorption.
749 * Late reverb taps are based on the late line lengths to allow a zero-
750 * delay path and offsets that would continue the propagation naturally
751 * into the late lines.
753 for(i = 0;i < NUM_LINES;i++)
755 length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
756 State->EarlyDelayTap[i][1] = float2int(length * frequency);
758 length = EARLY_TAP_LENGTHS[i]*multiplier;
759 State->EarlyDelayCoeff[i][1] = CalcDecayCoeff(length, decayTime);
761 length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
762 State->LateDelayTap[i][1] = State->LateFeedTap + float2int(length * frequency);
766 /* Update the early reflection line lengths and gain coefficients. */
767 static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat diffusion, const ALfloat decayTime, const ALuint frequency, EarlyReflections *Early)
769 ALfloat multiplier, length;
770 ALsizei i;
772 multiplier = CalcDelayLengthMult(density);
774 /* Calculate the all-pass feed-back/forward coefficient. */
775 Early->VecAp.Coeff = sqrtf(0.5f) * powf(diffusion, 2.0f);
777 for(i = 0;i < NUM_LINES;i++)
779 /* Calculate the length (in seconds) of each all-pass line. */
780 length = EARLY_ALLPASS_LENGTHS[i] * multiplier;
782 /* Calculate the delay offset for each all-pass line. */
783 Early->VecAp.Offset[i][1] = float2int(length * frequency);
785 /* Calculate the length (in seconds) of each delay line. */
786 length = EARLY_LINE_LENGTHS[i] * multiplier;
788 /* Calculate the delay offset for each delay line. */
789 Early->Offset[i][1] = float2int(length * frequency);
791 /* Calculate the gain (coefficient) for each line. */
792 Early->Coeff[i][1] = CalcDecayCoeff(length, decayTime);
796 /* Update the late reverb line lengths and T60 coefficients. */
797 static ALvoid UpdateLateLines(const ALfloat density, const ALfloat diffusion, const ALfloat lfDecayTime, const ALfloat mfDecayTime, const ALfloat hfDecayTime, const ALfloat lf0norm, const ALfloat hf0norm, const ALuint frequency, LateReverb *Late)
799 /* Scaling factor to convert the normalized reference frequencies from
800 * representing 0...freq to 0...max_reference.
802 const ALfloat norm_weight_factor = (ALfloat)frequency / AL_EAXREVERB_MAX_HFREFERENCE;
803 ALfloat multiplier, length, bandWeights[3];
804 ALsizei i;
806 /* To compensate for changes in modal density and decay time of the late
807 * reverb signal, the input is attenuated based on the maximal energy of
808 * the outgoing signal. This approximation is used to keep the apparent
809 * energy of the signal equal for all ranges of density and decay time.
811 * The average length of the delay lines is used to calculate the
812 * attenuation coefficient.
814 multiplier = CalcDelayLengthMult(density);
815 length = (LATE_LINE_LENGTHS[0] + LATE_LINE_LENGTHS[1] +
816 LATE_LINE_LENGTHS[2] + LATE_LINE_LENGTHS[3]) / 4.0f * multiplier;
817 length += (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
818 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f * multiplier;
819 /* The density gain calculation uses an average decay time weighted by
820 * approximate bandwidth. This attempts to compensate for losses of energy
821 * that reduce decay time due to scattering into highly attenuated bands.
823 bandWeights[0] = lf0norm*norm_weight_factor;
824 bandWeights[1] = hf0norm*norm_weight_factor - lf0norm*norm_weight_factor;
825 bandWeights[2] = 1.0f - hf0norm*norm_weight_factor;
826 Late->DensityGain[1] = CalcDensityGain(
827 CalcDecayCoeff(length,
828 bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime + bandWeights[2]*hfDecayTime
832 /* Calculate the all-pass feed-back/forward coefficient. */
833 Late->VecAp.Coeff = sqrtf(0.5f) * powf(diffusion, 2.0f);
835 for(i = 0;i < NUM_LINES;i++)
837 /* Calculate the length (in seconds) of each all-pass line. */
838 length = LATE_ALLPASS_LENGTHS[i] * multiplier;
840 /* Calculate the delay offset for each all-pass line. */
841 Late->VecAp.Offset[i][1] = float2int(length * frequency);
843 /* Calculate the length (in seconds) of each delay line. */
844 length = LATE_LINE_LENGTHS[i] * multiplier;
846 /* Calculate the delay offset for each delay line. */
847 Late->Offset[i][1] = float2int(length*frequency + 0.5f);
849 /* Approximate the absorption that the vector all-pass would exhibit
850 * given the current diffusion so we don't have to process a full T60
851 * filter for each of its four lines.
853 length += lerp(LATE_ALLPASS_LENGTHS[i],
854 (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
855 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f,
856 diffusion) * multiplier;
858 /* Calculate the T60 damping coefficients for each line. */
859 CalcT60DampingCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime,
860 lf0norm, hf0norm, &Late->T60[i]);
864 /* Creates a transform matrix given a reverb vector. The vector pans the reverb
865 * reflections toward the given direction, using its magnitude (up to 1) as a
866 * focal strength. This function results in a B-Format transformation matrix
867 * that spatially focuses the signal in the desired direction.
869 static aluMatrixf GetTransformFromVector(const ALfloat *vec)
871 aluMatrixf focus;
872 ALfloat norm[3];
873 ALfloat mag;
875 /* Normalize the panning vector according to the N3D scale, which has an
876 * extra sqrt(3) term on the directional components. Converting from OpenAL
877 * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
878 * that the reverb panning vectors use left-handed coordinates, unlike the
879 * rest of OpenAL which use right-handed. This is fixed by negating Z,
880 * which cancels out with the B-Format Z negation.
882 mag = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
883 if(mag > 1.0f)
885 norm[0] = vec[0] / mag * -SQRTF_3;
886 norm[1] = vec[1] / mag * SQRTF_3;
887 norm[2] = vec[2] / mag * SQRTF_3;
888 mag = 1.0f;
890 else
892 /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
893 * term. There's no need to renormalize the magnitude since it would
894 * just be reapplied in the matrix.
896 norm[0] = vec[0] * -SQRTF_3;
897 norm[1] = vec[1] * SQRTF_3;
898 norm[2] = vec[2] * SQRTF_3;
901 aluMatrixfSet(&focus,
902 1.0f, 0.0f, 0.0f, 0.0f,
903 norm[0], 1.0f-mag, 0.0f, 0.0f,
904 norm[1], 0.0f, 1.0f-mag, 0.0f,
905 norm[2], 0.0f, 0.0f, 1.0f-mag
908 return focus;
911 /* Update the early and late 3D panning gains. */
912 static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat earlyGain, const ALfloat lateGain, ReverbState *State)
914 aluMatrixf transform, rot;
915 ALsizei i;
917 STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
918 STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;
920 /* Note: _res is transposed. */
921 #define MATRIX_MULT(_res, _m1, _m2) do { \
922 int row, col; \
923 for(col = 0;col < 4;col++) \
925 for(row = 0;row < 4;row++) \
926 _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
927 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
929 } while(0)
930 /* Create a matrix that first converts A-Format to B-Format, then
931 * transforms the B-Format signal according to the panning vector.
933 rot = GetTransformFromVector(ReflectionsPan);
934 MATRIX_MULT(transform, rot, A2B);
935 memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
936 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
937 ComputePanGains(&Device->FOAOut, transform.m[i], earlyGain,
938 State->Early.PanGain[i]);
940 rot = GetTransformFromVector(LateReverbPan);
941 MATRIX_MULT(transform, rot, A2B);
942 memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
943 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
944 ComputePanGains(&Device->FOAOut, transform.m[i], lateGain,
945 State->Late.PanGain[i]);
946 #undef MATRIX_MULT
949 static void ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
951 const ALCdevice *Device = Context->Device;
952 const ALlistener *Listener = Context->Listener;
953 ALuint frequency = Device->Frequency;
954 ALfloat lf0norm, hf0norm, hfRatio;
955 ALfloat lfDecayTime, hfDecayTime;
956 ALfloat gain, gainlf, gainhf;
957 ALsizei i;
959 /* Calculate the master filters */
960 hf0norm = minf(props->Reverb.HFReference / frequency, 0.49f);
961 /* Restrict the filter gains from going below -60dB to keep the filter from
962 * killing most of the signal.
964 gainhf = maxf(props->Reverb.GainHF, 0.001f);
965 BiquadFilter_setParams(&State->Filter[0].Lp, BiquadType_HighShelf, gainhf, hf0norm,
966 calc_rcpQ_from_slope(gainhf, 1.0f));
967 lf0norm = minf(props->Reverb.LFReference / frequency, 0.49f);
968 gainlf = maxf(props->Reverb.GainLF, 0.001f);
969 BiquadFilter_setParams(&State->Filter[0].Hp, BiquadType_LowShelf, gainlf, lf0norm,
970 calc_rcpQ_from_slope(gainlf, 1.0f));
971 for(i = 1;i < NUM_LINES;i++)
973 BiquadFilter_copyParams(&State->Filter[i].Lp, &State->Filter[0].Lp);
974 BiquadFilter_copyParams(&State->Filter[i].Hp, &State->Filter[0].Hp);
977 /* Update the main effect delay and associated taps. */
978 UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
979 props->Reverb.Density, props->Reverb.DecayTime, frequency,
980 State);
982 /* Update the early lines. */
983 UpdateEarlyLines(props->Reverb.Density, props->Reverb.Diffusion,
984 props->Reverb.DecayTime, frequency, &State->Early);
986 /* Get the mixing matrix coefficients. */
987 CalcMatrixCoeffs(props->Reverb.Diffusion, &State->MixX, &State->MixY);
989 /* If the HF limit parameter is flagged, calculate an appropriate limit
990 * based on the air absorption parameter.
992 hfRatio = props->Reverb.DecayHFRatio;
993 if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
994 hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
995 props->Reverb.DecayTime, Listener->Params.ReverbSpeedOfSound
998 /* Calculate the LF/HF decay times. */
999 lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
1000 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1001 hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
1002 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
1004 /* Update the late lines. */
1005 UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
1006 lfDecayTime, props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm,
1007 frequency, &State->Late
1010 /* Update early and late 3D panning. */
1011 gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
1012 Update3DPanning(Device, props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan,
1013 props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain,
1014 State);
1016 /* Calculate the max update size from the smallest relevant delay. */
1017 State->MaxUpdate[1] = mini(MAX_UPDATE_SAMPLES,
1018 mini(State->Early.Offset[0][1], State->Late.Offset[0][1])
1021 /* Determine if delay-line cross-fading is required. Density is essentially
1022 * a master control for the feedback delays, so changes the offsets of many
1023 * delay lines.
1025 if(State->Params.Density != props->Reverb.Density ||
1026 /* Diffusion and decay times influences the decay rate (gain) of the
1027 * late reverb T60 filter.
1029 State->Params.Diffusion != props->Reverb.Diffusion ||
1030 State->Params.DecayTime != props->Reverb.DecayTime ||
1031 State->Params.HFDecayTime != hfDecayTime ||
1032 State->Params.LFDecayTime != lfDecayTime ||
1033 /* HF/LF References control the weighting used to calculate the density
1034 * gain.
1036 State->Params.HFReference != props->Reverb.HFReference ||
1037 State->Params.LFReference != props->Reverb.LFReference)
1038 State->FadeCount = 0;
1039 State->Params.Density = props->Reverb.Density;
1040 State->Params.Diffusion = props->Reverb.Diffusion;
1041 State->Params.DecayTime = props->Reverb.DecayTime;
1042 State->Params.HFDecayTime = hfDecayTime;
1043 State->Params.LFDecayTime = lfDecayTime;
1044 State->Params.HFReference = props->Reverb.HFReference;
1045 State->Params.LFReference = props->Reverb.LFReference;
1049 /**************************************
1050 * Effect Processing *
1051 **************************************/
1053 /* Basic delay line input/output routines. */
1054 static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c)
1056 return Delay->Line[offset&Delay->Mask][c];
1059 /* Cross-faded delay line output routine. Instead of interpolating the
1060 * offsets, this interpolates (cross-fades) the outputs at each offset.
1062 static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0,
1063 const ALsizei off1, const ALsizei c,
1064 const ALfloat sc0, const ALfloat sc1)
1066 return Delay->Line[off0&Delay->Mask][c]*sc0 +
1067 Delay->Line[off1&Delay->Mask][c]*sc1;
1071 static inline void DelayLineIn(const DelayLineI *Delay, ALsizei offset, const ALsizei c,
1072 const ALfloat *restrict in, ALsizei count)
1074 ALsizei i;
1075 for(i = 0;i < count;i++)
1076 Delay->Line[(offset++)&Delay->Mask][c] = *(in++);
1079 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1080 * for both the below vector all-pass model and to perform modal feed-back
1081 * delay network (FDN) mixing.
1083 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1084 * matrix with a single unitary rotational parameter:
1086 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1087 * [ -a, d, c, -b ]
1088 * [ -b, -c, d, a ]
1089 * [ -c, b, -a, d ]
1091 * The rotation is constructed from the effect's diffusion parameter,
1092 * yielding:
1094 * 1 = x^2 + 3 y^2
1096 * Where a, b, and c are the coefficient y with differing signs, and d is the
1097 * coefficient x. The final matrix is thus:
1099 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1100 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1101 * [ y, -y, x, y ] x = cos(t)
1102 * [ -y, -y, -y, x ] y = sin(t) / n
1104 * Any square orthogonal matrix with an order that is a power of two will
1105 * work (where ^T is transpose, ^-1 is inverse):
1107 * M^T = M^-1
1109 * Using that knowledge, finding an appropriate matrix can be accomplished
1110 * naively by searching all combinations of:
1112 * M = D + S - S^T
1114 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1115 * whose combination of signs are being iterated.
1117 static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *restrict in,
1118 const ALfloat xCoeff, const ALfloat yCoeff)
1120 out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
1121 out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
1122 out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
1123 out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
1125 #define VectorScatterDelayIn(delay, o, in, xcoeff, ycoeff) \
1126 VectorPartialScatter((delay)->Line[(o)&(delay)->Mask], in, xcoeff, ycoeff)
1128 /* Utilizes the above, but reverses the input channels. */
1129 static inline void VectorScatterRevDelayIn(const DelayLineI *Delay, ALint offset,
1130 const ALfloat xCoeff, const ALfloat yCoeff,
1131 const ALfloat (*restrict in)[MAX_UPDATE_SAMPLES],
1132 const ALsizei count)
1134 const DelayLineI delay = *Delay;
1135 ALsizei i, j;
1137 for(i = 0;i < count;++i)
1139 ALfloat f[NUM_LINES];
1140 for(j = 0;j < NUM_LINES;j++)
1141 f[NUM_LINES-1-j] = in[j][i];
1143 VectorScatterDelayIn(&delay, offset++, f, xCoeff, yCoeff);
1147 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1148 * filter to the 4-line input.
1150 * It works by vectorizing a regular all-pass filter and replacing the delay
1151 * element with a scattering matrix (like the one above) and a diagonal
1152 * matrix of delay elements.
1154 * Two static specializations are used for transitional (cross-faded) delay
1155 * line processing and non-transitional processing.
1157 static void VectorAllpass_Unfaded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
1158 const ALfloat xCoeff, const ALfloat yCoeff, ALsizei todo,
1159 VecAllpass *Vap)
1161 const DelayLineI delay = Vap->Delay;
1162 const ALfloat feedCoeff = Vap->Coeff;
1163 ALsizei vap_offset[NUM_LINES];
1164 ALsizei i, j;
1166 ASSUME(todo > 0);
1168 for(j = 0;j < NUM_LINES;j++)
1169 vap_offset[j] = offset-Vap->Offset[j][0];
1170 for(i = 0;i < todo;i++)
1172 ALfloat f[NUM_LINES];
1174 for(j = 0;j < NUM_LINES;j++)
1176 ALfloat input = samples[j][i];
1177 ALfloat out = DelayLineOut(&delay, vap_offset[j]++, j) - feedCoeff*input;
1178 f[j] = input + feedCoeff*out;
1180 samples[j][i] = out;
1183 VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
1184 ++offset;
1187 static void VectorAllpass_Faded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
1188 const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade,
1189 ALsizei todo, VecAllpass *Vap)
1191 const DelayLineI delay = Vap->Delay;
1192 const ALfloat feedCoeff = Vap->Coeff;
1193 ALsizei vap_offset[NUM_LINES][2];
1194 ALsizei i, j;
1196 ASSUME(todo > 0);
1198 fade *= 1.0f/FADE_SAMPLES;
1199 for(j = 0;j < NUM_LINES;j++)
1201 vap_offset[j][0] = offset-Vap->Offset[j][0];
1202 vap_offset[j][1] = offset-Vap->Offset[j][1];
1204 for(i = 0;i < todo;i++)
1206 ALfloat f[NUM_LINES];
1208 for(j = 0;j < NUM_LINES;j++)
1210 ALfloat input = samples[j][i];
1211 ALfloat out =
1212 FadedDelayLineOut(&delay, vap_offset[j][0]++, vap_offset[j][1]++, j,
1213 1.0f-fade, fade
1214 ) - feedCoeff*input;
1215 f[j] = input + feedCoeff*out;
1217 samples[j][i] = out;
1219 fade += FadeStep;
1221 VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
1222 ++offset;
1226 /* This generates early reflections.
1228 * This is done by obtaining the primary reflections (those arriving from the
1229 * same direction as the source) from the main delay line. These are
1230 * attenuated and all-pass filtered (based on the diffusion parameter).
1232 * The early lines are then fed in reverse (according to the approximately
1233 * opposite spatial location of the A-Format lines) to create the secondary
1234 * reflections (those arriving from the opposite direction as the source).
1236 * The early response is then completed by combining the primary reflections
1237 * with the delayed and attenuated output from the early lines.
1239 * Finally, the early response is reversed, scattered (based on diffusion),
1240 * and fed into the late reverb section of the main delay line.
1242 * Two static specializations are used for transitional (cross-faded) delay
1243 * line processing and non-transitional processing.
1245 static void EarlyReflection_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
1246 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1248 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1249 const DelayLineI early_delay = State->Early.Delay;
1250 const DelayLineI main_delay = State->Delay;
1251 const ALfloat mixX = State->MixX;
1252 const ALfloat mixY = State->MixY;
1253 ALsizei late_feed_tap;
1254 ALsizei i, j;
1256 ASSUME(todo > 0);
1258 /* First, load decorrelated samples from the main delay line as the primary
1259 * reflections.
1261 for(j = 0;j < NUM_LINES;j++)
1263 ALsizei early_delay_tap = offset - State->EarlyDelayTap[j][0];
1264 ALfloat coeff = State->EarlyDelayCoeff[j][0];
1265 for(i = 0;i < todo;i++)
1266 temps[j][i] = DelayLineOut(&main_delay, early_delay_tap++, j) * coeff;
1269 /* Apply a vector all-pass, to help color the initial reflections based on
1270 * the diffusion strength.
1272 VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Early.VecAp);
1274 /* Apply a delay and bounce to generate secondary reflections, combine with
1275 * the primary reflections and write out the result for mixing.
1277 for(j = 0;j < NUM_LINES;j++)
1279 ALint early_feedb_tap = offset - State->Early.Offset[j][0];
1280 ALfloat early_feedb_coeff = State->Early.Coeff[j][0];
1282 for(i = 0;i < todo;i++)
1283 out[j][i] = DelayLineOut(&early_delay, early_feedb_tap++, j)*early_feedb_coeff +
1284 temps[j][i];
1286 for(j = 0;j < NUM_LINES;j++)
1287 DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
1289 /* Also write the result back to the main delay line for the late reverb
1290 * stage to pick up at the appropriate time, appplying a scatter and
1291 * bounce to improve the initial diffusion in the late reverb.
1293 late_feed_tap = offset - State->LateFeedTap;
1294 VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
1296 static void EarlyReflection_Faded(ReverbState *State, ALsizei offset, const ALsizei todo,
1297 const ALfloat fade, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1299 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1300 const DelayLineI early_delay = State->Early.Delay;
1301 const DelayLineI main_delay = State->Delay;
1302 const ALfloat mixX = State->MixX;
1303 const ALfloat mixY = State->MixY;
1304 ALsizei late_feed_tap;
1305 ALsizei i, j;
1307 ASSUME(todo > 0);
1309 for(j = 0;j < NUM_LINES;j++)
1311 ALsizei early_delay_tap0 = offset - State->EarlyDelayTap[j][0];
1312 ALsizei early_delay_tap1 = offset - State->EarlyDelayTap[j][1];
1313 ALfloat oldCoeff = State->EarlyDelayCoeff[j][0];
1314 ALfloat oldCoeffStep = -oldCoeff / FADE_SAMPLES;
1315 ALfloat newCoeffStep = State->EarlyDelayCoeff[j][1] / FADE_SAMPLES;
1316 ALfloat fadeCount = fade;
1318 for(i = 0;i < todo;i++)
1320 const ALfloat fade0 = oldCoeff + oldCoeffStep*fadeCount;
1321 const ALfloat fade1 = newCoeffStep*fadeCount;
1322 temps[j][i] = FadedDelayLineOut(&main_delay,
1323 early_delay_tap0++, early_delay_tap1++, j, fade0, fade1
1325 fadeCount += 1.0f;
1329 VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->Early.VecAp);
1331 for(j = 0;j < NUM_LINES;j++)
1333 ALint feedb_tap0 = offset - State->Early.Offset[j][0];
1334 ALint feedb_tap1 = offset - State->Early.Offset[j][1];
1335 ALfloat feedb_oldCoeff = State->Early.Coeff[j][0];
1336 ALfloat feedb_oldCoeffStep = -feedb_oldCoeff / FADE_SAMPLES;
1337 ALfloat feedb_newCoeffStep = State->Early.Coeff[j][1] / FADE_SAMPLES;
1338 ALfloat fadeCount = fade;
1340 for(i = 0;i < todo;i++)
1342 const ALfloat fade0 = feedb_oldCoeff + feedb_oldCoeffStep*fadeCount;
1343 const ALfloat fade1 = feedb_newCoeffStep*fadeCount;
1344 out[j][i] = FadedDelayLineOut(&early_delay,
1345 feedb_tap0++, feedb_tap1++, j, fade0, fade1
1346 ) + temps[j][i];
1347 fadeCount += 1.0f;
1350 for(j = 0;j < NUM_LINES;j++)
1351 DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
1353 late_feed_tap = offset - State->LateFeedTap;
1354 VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
1357 /* Applies the two T60 damping filter sections. */
1358 static inline void LateT60Filter(ALfloat *restrict samples, const ALsizei todo, T60Filter *filter)
1360 ALfloat temp[MAX_UPDATE_SAMPLES];
1361 BiquadFilter_process(&filter->HFFilter, temp, samples, todo);
1362 BiquadFilter_process(&filter->LFFilter, samples, temp, todo);
1365 /* This generates the reverb tail using a modified feed-back delay network
1366 * (FDN).
1368 * Results from the early reflections are mixed with the output from the late
1369 * delay lines.
1371 * The late response is then completed by T60 and all-pass filtering the mix.
1373 * Finally, the lines are reversed (so they feed their opposite directions)
1374 * and scattered with the FDN matrix before re-feeding the delay lines.
1376 * Two variations are made, one for for transitional (cross-faded) delay line
1377 * processing and one for non-transitional processing.
1379 static void LateReverb_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
1380 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1382 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1383 const DelayLineI late_delay = State->Late.Delay;
1384 const DelayLineI main_delay = State->Delay;
1385 const ALfloat mixX = State->MixX;
1386 const ALfloat mixY = State->MixY;
1387 ALsizei i, j;
1389 ASSUME(todo > 0);
1391 /* First, load decorrelated samples from the main and feedback delay lines.
1392 * Filter the signal to apply its frequency-dependent decay.
1394 for(j = 0;j < NUM_LINES;j++)
1396 ALsizei late_delay_tap = offset - State->LateDelayTap[j][0];
1397 ALsizei late_feedb_tap = offset - State->Late.Offset[j][0];
1398 ALfloat midGain = State->Late.T60[j].MidGain[0];
1399 const ALfloat densityGain = State->Late.DensityGain[0] * midGain;
1400 for(i = 0;i < todo;i++)
1401 temps[j][i] = DelayLineOut(&main_delay, late_delay_tap++, j)*densityGain +
1402 DelayLineOut(&late_delay, late_feedb_tap++, j)*midGain;
1403 LateT60Filter(temps[j], todo, &State->Late.T60[j]);
1406 /* Apply a vector all-pass to improve micro-surface diffusion, and write
1407 * out the results for mixing.
1409 VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Late.VecAp);
1411 for(j = 0;j < NUM_LINES;j++)
1412 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1414 /* Finally, scatter and bounce the results to refeed the feedback buffer. */
1415 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, out, todo);
1417 static void LateReverb_Faded(ReverbState *State, ALsizei offset, const ALsizei todo,
1418 const ALfloat fade, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
1420 ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1421 const DelayLineI late_delay = State->Late.Delay;
1422 const DelayLineI main_delay = State->Delay;
1423 const ALfloat mixX = State->MixX;
1424 const ALfloat mixY = State->MixY;
1425 ALsizei i, j;
1427 ASSUME(todo > 0);
1429 for(j = 0;j < NUM_LINES;j++)
1431 const ALfloat oldMidGain = State->Late.T60[j].MidGain[0];
1432 const ALfloat midGain = State->Late.T60[j].MidGain[1];
1433 const ALfloat oldMidStep = -oldMidGain / FADE_SAMPLES;
1434 const ALfloat midStep = midGain / FADE_SAMPLES;
1435 const ALfloat oldDensityGain = State->Late.DensityGain[0] * oldMidGain;
1436 const ALfloat densityGain = State->Late.DensityGain[1] * midGain;
1437 const ALfloat oldDensityStep = -oldDensityGain / FADE_SAMPLES;
1438 const ALfloat densityStep = densityGain / FADE_SAMPLES;
1439 ALsizei late_delay_tap0 = offset - State->LateDelayTap[j][0];
1440 ALsizei late_delay_tap1 = offset - State->LateDelayTap[j][1];
1441 ALsizei late_feedb_tap0 = offset - State->Late.Offset[j][0];
1442 ALsizei late_feedb_tap1 = offset - State->Late.Offset[j][1];
1443 ALfloat fadeCount = fade;
1445 for(i = 0;i < todo;i++)
1447 const ALfloat fade0 = oldDensityGain + oldDensityStep*fadeCount;
1448 const ALfloat fade1 = densityStep*fadeCount;
1449 const ALfloat gfade0 = oldMidGain + oldMidStep*fadeCount;
1450 const ALfloat gfade1 = midStep*fadeCount;
1451 temps[j][i] =
1452 FadedDelayLineOut(&main_delay, late_delay_tap0++, late_delay_tap1++, j,
1453 fade0, fade1) +
1454 FadedDelayLineOut(&late_delay, late_feedb_tap0++, late_feedb_tap1++, j,
1455 gfade0, gfade1);
1456 fadeCount += 1.0f;
1458 LateT60Filter(temps[j], todo, &State->Late.T60[j]);
1461 VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->Late.VecAp);
1463 for(j = 0;j < NUM_LINES;j++)
1464 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1466 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, temps, todo);
1469 static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
1471 ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->TempSamples;
1472 ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES] = State->MixSamples;
1473 ALsizei fadeCount = State->FadeCount;
1474 ALsizei offset = State->Offset;
1475 ALsizei base, c;
1477 /* Process reverb for these samples. */
1478 for(base = 0;base < SamplesToDo;)
1480 ALsizei todo = SamplesToDo - base;
1481 /* If cross-fading, don't do more samples than there are to fade. */
1482 if(FADE_SAMPLES-fadeCount > 0)
1484 todo = mini(todo, FADE_SAMPLES-fadeCount);
1485 todo = mini(todo, State->MaxUpdate[0]);
1487 todo = mini(todo, State->MaxUpdate[1]);
1488 /* If this is not the final update, ensure the update size is a
1489 * multiple of 4 for the SIMD mixers.
1491 if(todo < SamplesToDo-base)
1492 todo &= ~3;
1494 /* Convert B-Format to A-Format for processing. */
1495 memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
1496 for(c = 0;c < NUM_LINES;c++)
1497 MixRowSamples(afmt[c], B2A.m[c],
1498 SamplesIn, MAX_EFFECT_CHANNELS, base, todo
1501 /* Process the samples for reverb. */
1502 for(c = 0;c < NUM_LINES;c++)
1504 /* Band-pass the incoming samples. */
1505 BiquadFilter_process(&State->Filter[c].Lp, samples[0], afmt[c], todo);
1506 BiquadFilter_process(&State->Filter[c].Hp, samples[1], samples[0], todo);
1508 /* Feed the initial delay line. */
1509 DelayLineIn(&State->Delay, offset, c, samples[1], todo);
1512 if(UNLIKELY(fadeCount < FADE_SAMPLES))
1514 ALfloat fade = (ALfloat)fadeCount;
1516 /* Generate early reflections. */
1517 EarlyReflection_Faded(State, offset, todo, fade, samples);
1518 /* Mix the A-Format results to output, implicitly converting back
1519 * to B-Format.
1521 for(c = 0;c < NUM_LINES;c++)
1522 MixSamples(samples[c], NumChannels, SamplesOut,
1523 State->Early.CurrentGain[c], State->Early.PanGain[c],
1524 SamplesToDo-base, base, todo
1527 /* Generate and mix late reverb. */
1528 LateReverb_Faded(State, offset, todo, fade, samples);
1529 for(c = 0;c < NUM_LINES;c++)
1530 MixSamples(samples[c], NumChannels, SamplesOut,
1531 State->Late.CurrentGain[c], State->Late.PanGain[c],
1532 SamplesToDo-base, base, todo
1535 /* Step fading forward. */
1536 fadeCount += todo;
1537 if(LIKELY(fadeCount >= FADE_SAMPLES))
1539 /* Update the cross-fading delay line taps. */
1540 fadeCount = FADE_SAMPLES;
1541 for(c = 0;c < NUM_LINES;c++)
1543 State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
1544 State->EarlyDelayCoeff[c][0] = State->EarlyDelayCoeff[c][1];
1545 State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1];
1546 State->Early.Offset[c][0] = State->Early.Offset[c][1];
1547 State->Early.Coeff[c][0] = State->Early.Coeff[c][1];
1548 State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
1549 State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1];
1550 State->Late.Offset[c][0] = State->Late.Offset[c][1];
1551 State->Late.T60[c].MidGain[0] = State->Late.T60[c].MidGain[1];
1553 State->Late.DensityGain[0] = State->Late.DensityGain[1];
1554 State->MaxUpdate[0] = State->MaxUpdate[1];
1557 else
1559 /* Generate and mix early reflections. */
1560 EarlyReflection_Unfaded(State, offset, todo, samples);
1561 for(c = 0;c < NUM_LINES;c++)
1562 MixSamples(samples[c], NumChannels, SamplesOut,
1563 State->Early.CurrentGain[c], State->Early.PanGain[c],
1564 SamplesToDo-base, base, todo
1567 /* Generate and mix late reverb. */
1568 LateReverb_Unfaded(State, offset, todo, samples);
1569 for(c = 0;c < NUM_LINES;c++)
1570 MixSamples(samples[c], NumChannels, SamplesOut,
1571 State->Late.CurrentGain[c], State->Late.PanGain[c],
1572 SamplesToDo-base, base, todo
1576 /* Step all delays forward. */
1577 offset += todo;
1579 base += todo;
1581 State->Offset = offset;
1582 State->FadeCount = fadeCount;
1586 typedef struct ReverbStateFactory {
1587 DERIVE_FROM_TYPE(EffectStateFactory);
1588 } ReverbStateFactory;
1590 static ALeffectState *ReverbStateFactory_create(ReverbStateFactory* UNUSED(factory))
1592 ReverbState *state;
1594 NEW_OBJ0(state, ReverbState)();
1595 if(!state) return NULL;
1597 return STATIC_CAST(ALeffectState, state);
1600 DEFINE_EFFECTSTATEFACTORY_VTABLE(ReverbStateFactory);
1602 EffectStateFactory *ReverbStateFactory_getFactory(void)
1604 static ReverbStateFactory ReverbFactory = { { GET_VTABLE2(ReverbStateFactory, EffectStateFactory) } };
1606 return STATIC_CAST(EffectStateFactory, &ReverbFactory);
1610 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1612 ALeffectProps *props = &effect->Props;
1613 switch(param)
1615 case AL_EAXREVERB_DECAY_HFLIMIT:
1616 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1617 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
1618 props->Reverb.DecayHFLimit = val;
1619 break;
1621 default:
1622 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1623 param);
1626 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1627 { ALeaxreverb_setParami(effect, context, param, vals[0]); }
1628 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1630 ALeffectProps *props = &effect->Props;
1631 switch(param)
1633 case AL_EAXREVERB_DENSITY:
1634 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1635 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
1636 props->Reverb.Density = val;
1637 break;
1639 case AL_EAXREVERB_DIFFUSION:
1640 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1641 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
1642 props->Reverb.Diffusion = val;
1643 break;
1645 case AL_EAXREVERB_GAIN:
1646 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1647 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
1648 props->Reverb.Gain = val;
1649 break;
1651 case AL_EAXREVERB_GAINHF:
1652 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1653 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
1654 props->Reverb.GainHF = val;
1655 break;
1657 case AL_EAXREVERB_GAINLF:
1658 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1659 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
1660 props->Reverb.GainLF = val;
1661 break;
1663 case AL_EAXREVERB_DECAY_TIME:
1664 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1665 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
1666 props->Reverb.DecayTime = val;
1667 break;
1669 case AL_EAXREVERB_DECAY_HFRATIO:
1670 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1671 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
1672 props->Reverb.DecayHFRatio = val;
1673 break;
1675 case AL_EAXREVERB_DECAY_LFRATIO:
1676 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1677 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
1678 props->Reverb.DecayLFRatio = val;
1679 break;
1681 case AL_EAXREVERB_REFLECTIONS_GAIN:
1682 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1683 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
1684 props->Reverb.ReflectionsGain = val;
1685 break;
1687 case AL_EAXREVERB_REFLECTIONS_DELAY:
1688 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1689 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
1690 props->Reverb.ReflectionsDelay = val;
1691 break;
1693 case AL_EAXREVERB_LATE_REVERB_GAIN:
1694 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1695 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
1696 props->Reverb.LateReverbGain = val;
1697 break;
1699 case AL_EAXREVERB_LATE_REVERB_DELAY:
1700 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1701 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
1702 props->Reverb.LateReverbDelay = val;
1703 break;
1705 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1706 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1707 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
1708 props->Reverb.AirAbsorptionGainHF = val;
1709 break;
1711 case AL_EAXREVERB_ECHO_TIME:
1712 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1713 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
1714 props->Reverb.EchoTime = val;
1715 break;
1717 case AL_EAXREVERB_ECHO_DEPTH:
1718 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1719 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
1720 props->Reverb.EchoDepth = val;
1721 break;
1723 case AL_EAXREVERB_MODULATION_TIME:
1724 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1725 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
1726 props->Reverb.ModulationTime = val;
1727 break;
1729 case AL_EAXREVERB_MODULATION_DEPTH:
1730 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1731 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
1732 props->Reverb.ModulationDepth = val;
1733 break;
1735 case AL_EAXREVERB_HFREFERENCE:
1736 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1737 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
1738 props->Reverb.HFReference = val;
1739 break;
1741 case AL_EAXREVERB_LFREFERENCE:
1742 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1743 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
1744 props->Reverb.LFReference = val;
1745 break;
1747 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1748 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1749 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
1750 props->Reverb.RoomRolloffFactor = val;
1751 break;
1753 default:
1754 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1755 param);
1758 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1760 ALeffectProps *props = &effect->Props;
1761 switch(param)
1763 case AL_EAXREVERB_REFLECTIONS_PAN:
1764 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1765 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
1766 props->Reverb.ReflectionsPan[0] = vals[0];
1767 props->Reverb.ReflectionsPan[1] = vals[1];
1768 props->Reverb.ReflectionsPan[2] = vals[2];
1769 break;
1770 case AL_EAXREVERB_LATE_REVERB_PAN:
1771 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1772 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
1773 props->Reverb.LateReverbPan[0] = vals[0];
1774 props->Reverb.LateReverbPan[1] = vals[1];
1775 props->Reverb.LateReverbPan[2] = vals[2];
1776 break;
1778 default:
1779 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1780 break;
1784 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1786 const ALeffectProps *props = &effect->Props;
1787 switch(param)
1789 case AL_EAXREVERB_DECAY_HFLIMIT:
1790 *val = props->Reverb.DecayHFLimit;
1791 break;
1793 default:
1794 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1795 param);
1798 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1799 { ALeaxreverb_getParami(effect, context, param, vals); }
1800 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1802 const ALeffectProps *props = &effect->Props;
1803 switch(param)
1805 case AL_EAXREVERB_DENSITY:
1806 *val = props->Reverb.Density;
1807 break;
1809 case AL_EAXREVERB_DIFFUSION:
1810 *val = props->Reverb.Diffusion;
1811 break;
1813 case AL_EAXREVERB_GAIN:
1814 *val = props->Reverb.Gain;
1815 break;
1817 case AL_EAXREVERB_GAINHF:
1818 *val = props->Reverb.GainHF;
1819 break;
1821 case AL_EAXREVERB_GAINLF:
1822 *val = props->Reverb.GainLF;
1823 break;
1825 case AL_EAXREVERB_DECAY_TIME:
1826 *val = props->Reverb.DecayTime;
1827 break;
1829 case AL_EAXREVERB_DECAY_HFRATIO:
1830 *val = props->Reverb.DecayHFRatio;
1831 break;
1833 case AL_EAXREVERB_DECAY_LFRATIO:
1834 *val = props->Reverb.DecayLFRatio;
1835 break;
1837 case AL_EAXREVERB_REFLECTIONS_GAIN:
1838 *val = props->Reverb.ReflectionsGain;
1839 break;
1841 case AL_EAXREVERB_REFLECTIONS_DELAY:
1842 *val = props->Reverb.ReflectionsDelay;
1843 break;
1845 case AL_EAXREVERB_LATE_REVERB_GAIN:
1846 *val = props->Reverb.LateReverbGain;
1847 break;
1849 case AL_EAXREVERB_LATE_REVERB_DELAY:
1850 *val = props->Reverb.LateReverbDelay;
1851 break;
1853 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1854 *val = props->Reverb.AirAbsorptionGainHF;
1855 break;
1857 case AL_EAXREVERB_ECHO_TIME:
1858 *val = props->Reverb.EchoTime;
1859 break;
1861 case AL_EAXREVERB_ECHO_DEPTH:
1862 *val = props->Reverb.EchoDepth;
1863 break;
1865 case AL_EAXREVERB_MODULATION_TIME:
1866 *val = props->Reverb.ModulationTime;
1867 break;
1869 case AL_EAXREVERB_MODULATION_DEPTH:
1870 *val = props->Reverb.ModulationDepth;
1871 break;
1873 case AL_EAXREVERB_HFREFERENCE:
1874 *val = props->Reverb.HFReference;
1875 break;
1877 case AL_EAXREVERB_LFREFERENCE:
1878 *val = props->Reverb.LFReference;
1879 break;
1881 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1882 *val = props->Reverb.RoomRolloffFactor;
1883 break;
1885 default:
1886 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1887 param);
1890 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
1892 const ALeffectProps *props = &effect->Props;
1893 switch(param)
1895 case AL_EAXREVERB_REFLECTIONS_PAN:
1896 vals[0] = props->Reverb.ReflectionsPan[0];
1897 vals[1] = props->Reverb.ReflectionsPan[1];
1898 vals[2] = props->Reverb.ReflectionsPan[2];
1899 break;
1900 case AL_EAXREVERB_LATE_REVERB_PAN:
1901 vals[0] = props->Reverb.LateReverbPan[0];
1902 vals[1] = props->Reverb.LateReverbPan[1];
1903 vals[2] = props->Reverb.LateReverbPan[2];
1904 break;
1906 default:
1907 ALeaxreverb_getParamf(effect, context, param, vals);
1908 break;
1912 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
1914 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1916 ALeffectProps *props = &effect->Props;
1917 switch(param)
1919 case AL_REVERB_DECAY_HFLIMIT:
1920 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
1921 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
1922 props->Reverb.DecayHFLimit = val;
1923 break;
1925 default:
1926 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
1929 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1930 { ALreverb_setParami(effect, context, param, vals[0]); }
1931 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1933 ALeffectProps *props = &effect->Props;
1934 switch(param)
1936 case AL_REVERB_DENSITY:
1937 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
1938 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
1939 props->Reverb.Density = val;
1940 break;
1942 case AL_REVERB_DIFFUSION:
1943 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
1944 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
1945 props->Reverb.Diffusion = val;
1946 break;
1948 case AL_REVERB_GAIN:
1949 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
1950 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
1951 props->Reverb.Gain = val;
1952 break;
1954 case AL_REVERB_GAINHF:
1955 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
1956 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
1957 props->Reverb.GainHF = val;
1958 break;
1960 case AL_REVERB_DECAY_TIME:
1961 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
1962 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
1963 props->Reverb.DecayTime = val;
1964 break;
1966 case AL_REVERB_DECAY_HFRATIO:
1967 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
1968 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
1969 props->Reverb.DecayHFRatio = val;
1970 break;
1972 case AL_REVERB_REFLECTIONS_GAIN:
1973 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
1974 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
1975 props->Reverb.ReflectionsGain = val;
1976 break;
1978 case AL_REVERB_REFLECTIONS_DELAY:
1979 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
1980 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
1981 props->Reverb.ReflectionsDelay = val;
1982 break;
1984 case AL_REVERB_LATE_REVERB_GAIN:
1985 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
1986 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
1987 props->Reverb.LateReverbGain = val;
1988 break;
1990 case AL_REVERB_LATE_REVERB_DELAY:
1991 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
1992 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
1993 props->Reverb.LateReverbDelay = val;
1994 break;
1996 case AL_REVERB_AIR_ABSORPTION_GAINHF:
1997 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
1998 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
1999 props->Reverb.AirAbsorptionGainHF = val;
2000 break;
2002 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2003 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
2004 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
2005 props->Reverb.RoomRolloffFactor = val;
2006 break;
2008 default:
2009 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2012 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
2013 { ALreverb_setParamf(effect, context, param, vals[0]); }
2015 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
2017 const ALeffectProps *props = &effect->Props;
2018 switch(param)
2020 case AL_REVERB_DECAY_HFLIMIT:
2021 *val = props->Reverb.DecayHFLimit;
2022 break;
2024 default:
2025 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
2028 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
2029 { ALreverb_getParami(effect, context, param, vals); }
2030 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
2032 const ALeffectProps *props = &effect->Props;
2033 switch(param)
2035 case AL_REVERB_DENSITY:
2036 *val = props->Reverb.Density;
2037 break;
2039 case AL_REVERB_DIFFUSION:
2040 *val = props->Reverb.Diffusion;
2041 break;
2043 case AL_REVERB_GAIN:
2044 *val = props->Reverb.Gain;
2045 break;
2047 case AL_REVERB_GAINHF:
2048 *val = props->Reverb.GainHF;
2049 break;
2051 case AL_REVERB_DECAY_TIME:
2052 *val = props->Reverb.DecayTime;
2053 break;
2055 case AL_REVERB_DECAY_HFRATIO:
2056 *val = props->Reverb.DecayHFRatio;
2057 break;
2059 case AL_REVERB_REFLECTIONS_GAIN:
2060 *val = props->Reverb.ReflectionsGain;
2061 break;
2063 case AL_REVERB_REFLECTIONS_DELAY:
2064 *val = props->Reverb.ReflectionsDelay;
2065 break;
2067 case AL_REVERB_LATE_REVERB_GAIN:
2068 *val = props->Reverb.LateReverbGain;
2069 break;
2071 case AL_REVERB_LATE_REVERB_DELAY:
2072 *val = props->Reverb.LateReverbDelay;
2073 break;
2075 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2076 *val = props->Reverb.AirAbsorptionGainHF;
2077 break;
2079 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2080 *val = props->Reverb.RoomRolloffFactor;
2081 break;
2083 default:
2084 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2087 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2088 { ALreverb_getParamf(effect, context, param, vals); }
2090 DEFINE_ALEFFECT_VTABLE(ALreverb);