Use std::isfinite instead of isfinite
[openal-soft.git] / Alc / effects / reverb.cpp
blob8b63e473644742f9f54c4909bf9533bd0f9193b1
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 <cmath>
28 #include <algorithm>
30 #include "alMain.h"
31 #include "alcontext.h"
32 #include "alu.h"
33 #include "alAuxEffectSlot.h"
34 #include "alListener.h"
35 #include "alError.h"
36 #include "filters/defs.h"
37 #include "vector.h"
38 #include "vecmat.h"
40 /* This is a user config option for modifying the overall output of the reverb
41 * effect.
43 ALfloat ReverbBoost = 1.0f;
45 /* This is the maximum number of samples processed for each inner loop
46 * iteration. */
47 #define MAX_UPDATE_SAMPLES 256
49 /* The number of samples used for cross-faded delay lines. This can be used
50 * to balance the compensation for abrupt line changes and attenuation due to
51 * minimally lengthed recursive lines. Try to keep this below the device
52 * update size.
54 #define FADE_SAMPLES 128
56 /* The number of spatialized lines or channels to process. Four channels allows
57 * for a 3D A-Format response. NOTE: This can't be changed without taking care
58 * of the conversion matrices, and a few places where the length arrays are
59 * assumed to have 4 elements.
61 #define NUM_LINES 4
64 /* The B-Format to A-Format conversion matrix. The arrangement of rows is
65 * deliberately chosen to align the resulting lines to their spatial opposites
66 * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
67 * back left). It's not quite opposite, since the A-Format results in a
68 * tetrahedron, but it's close enough. Should the model be extended to 8-lines
69 * in the future, true opposites can be used.
71 static const aluMatrixf B2A = {{
72 { 0.288675134595f, 0.288675134595f, 0.288675134595f, 0.288675134595f },
73 { 0.288675134595f, -0.288675134595f, -0.288675134595f, 0.288675134595f },
74 { 0.288675134595f, 0.288675134595f, -0.288675134595f, -0.288675134595f },
75 { 0.288675134595f, -0.288675134595f, 0.288675134595f, -0.288675134595f }
76 }};
78 /* Converts A-Format to B-Format. */
79 static const aluMatrixf A2B = {{
80 { 0.866025403785f, 0.866025403785f, 0.866025403785f, 0.866025403785f },
81 { 0.866025403785f, -0.866025403785f, 0.866025403785f, -0.866025403785f },
82 { 0.866025403785f, -0.866025403785f, -0.866025403785f, 0.866025403785f },
83 { 0.866025403785f, 0.866025403785f, -0.866025403785f, -0.866025403785f }
84 }};
86 static const ALfloat FadeStep = 1.0f / FADE_SAMPLES;
88 /* The all-pass and delay lines have a variable length dependent on the
89 * effect's density parameter, which helps alter the perceived environment
90 * size. The size-to-density conversion is a cubed scale:
92 * density = min(1.0, pow(size, 3.0) / DENSITY_SCALE);
94 * The line lengths scale linearly with room size, so the inverse density
95 * conversion is needed, taking the cube root of the re-scaled density to
96 * calculate the line length multiplier:
98 * length_mult = max(5.0, cbrtf(density*DENSITY_SCALE));
100 * The density scale below will result in a max line multiplier of 50, for an
101 * effective size range of 5m to 50m.
103 static const ALfloat DENSITY_SCALE = 125000.0f;
105 /* All delay line lengths are specified in seconds.
107 * To approximate early reflections, we break them up into primary (those
108 * arriving from the same direction as the source) and secondary (those
109 * arriving from the opposite direction).
111 * The early taps decorrelate the 4-channel signal to approximate an average
112 * room response for the primary reflections after the initial early delay.
114 * Given an average room dimension (d_a) and the speed of sound (c) we can
115 * calculate the average reflection delay (r_a) regardless of listener and
116 * source positions as:
118 * r_a = d_a / c
119 * c = 343.3
121 * This can extended to finding the average difference (r_d) between the
122 * maximum (r_1) and minimum (r_0) reflection delays:
124 * r_0 = 2 / 3 r_a
125 * = r_a - r_d / 2
126 * = r_d
127 * r_1 = 4 / 3 r_a
128 * = r_a + r_d / 2
129 * = 2 r_d
130 * r_d = 2 / 3 r_a
131 * = r_1 - r_0
133 * As can be determined by integrating the 1D model with a source (s) and
134 * listener (l) positioned across the dimension of length (d_a):
136 * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
138 * The initial taps (T_(i=0)^N) are then specified by taking a power series
139 * that ranges between r_0 and half of r_1 less r_0:
141 * R_i = 2^(i / (2 N - 1)) r_d
142 * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
143 * = r_0 + T_i
144 * T_i = R_i - r_0
145 * = (2^(i / (2 N - 1)) - 1) r_d
147 * Assuming an average of 1m, we get the following taps:
149 static const ALfloat EARLY_TAP_LENGTHS[NUM_LINES] =
151 0.0000000e+0f, 2.0213520e-4f, 4.2531060e-4f, 6.7171600e-4f
154 /* The early all-pass filter lengths are based on the early tap lengths:
156 * A_i = R_i / a
158 * Where a is the approximate maximum all-pass cycle limit (20).
160 static const ALfloat EARLY_ALLPASS_LENGTHS[NUM_LINES] =
162 9.7096800e-5f, 1.0720356e-4f, 1.1836234e-4f, 1.3068260e-4f
165 /* The early delay lines are used to transform the primary reflections into
166 * the secondary reflections. The A-format is arranged in such a way that
167 * the channels/lines are spatially opposite:
169 * C_i is opposite C_(N-i-1)
171 * The delays of the two opposing reflections (R_i and O_i) from a source
172 * anywhere along a particular dimension always sum to twice its full delay:
174 * 2 r_a = R_i + O_i
176 * With that in mind we can determine the delay between the two reflections
177 * and thus specify our early line lengths (L_(i=0)^N) using:
179 * O_i = 2 r_a - R_(N-i-1)
180 * L_i = O_i - R_(N-i-1)
181 * = 2 (r_a - R_(N-i-1))
182 * = 2 (r_a - T_(N-i-1) - r_0)
183 * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
185 * Using an average dimension of 1m, we get:
187 static const ALfloat EARLY_LINE_LENGTHS[NUM_LINES] =
189 5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f
192 /* The late all-pass filter lengths are based on the late line lengths:
194 * A_i = (5 / 3) L_i / r_1
196 static const ALfloat LATE_ALLPASS_LENGTHS[NUM_LINES] =
198 1.6182800e-4f, 2.0389060e-4f, 2.8159360e-4f, 3.2365600e-4f
201 /* The late lines are used to approximate the decaying cycle of recursive
202 * late reflections.
204 * Splitting the lines in half, we start with the shortest reflection paths
205 * (L_(i=0)^(N/2)):
207 * L_i = 2^(i / (N - 1)) r_d
209 * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
211 * L_i = 2 r_a - L_(i-N/2)
212 * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
214 * For our 1m average room, we get:
216 static const ALfloat LATE_LINE_LENGTHS[NUM_LINES] =
218 1.9419362e-3f, 2.4466860e-3f, 3.3791220e-3f, 3.8838720e-3f
222 typedef struct DelayLineI {
223 /* The delay lines use interleaved samples, with the lengths being powers
224 * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
226 ALsizei Mask;
227 ALfloat (*Line)[NUM_LINES];
228 } DelayLineI;
230 typedef struct VecAllpass {
231 DelayLineI Delay;
232 ALfloat Coeff;
233 ALsizei Offset[NUM_LINES][2];
234 } VecAllpass;
236 typedef struct T60Filter {
237 /* Two filters are used to adjust the signal. One to control the low
238 * frequencies, and one to control the high frequencies.
240 ALfloat MidGain[2];
241 BiquadFilter HFFilter, LFFilter;
242 } T60Filter;
244 typedef struct EarlyReflections {
245 /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
246 * The spread from this filter also helps smooth out the reverb tail.
248 VecAllpass VecAp;
250 /* An echo line is used to complete the second half of the early
251 * reflections.
253 DelayLineI Delay;
254 ALsizei Offset[NUM_LINES][2];
255 ALfloat Coeff[NUM_LINES][2];
257 /* The gain for each output channel based on 3D panning. */
258 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
259 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
260 } EarlyReflections;
262 typedef struct LateReverb {
263 /* A recursive delay line is used fill in the reverb tail. */
264 DelayLineI Delay;
265 ALsizei Offset[NUM_LINES][2];
267 /* Attenuation to compensate for the modal density and decay rate of the
268 * late lines.
270 ALfloat DensityGain[2];
272 /* T60 decay filters are used to simulate absorption. */
273 T60Filter T60[NUM_LINES];
275 /* A Gerzon vector all-pass filter is used to simulate diffusion. */
276 VecAllpass VecAp;
278 /* The gain for each output channel based on 3D panning. */
279 ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
280 ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
281 } LateReverb;
283 struct ReverbState final : public ALeffectState {
284 /* All delay lines are allocated as a single buffer to reduce memory
285 * fragmentation and management code.
287 al::vector<ALfloat,16> mSampleBuffer;
289 struct {
290 /* Calculated parameters which indicate if cross-fading is needed after
291 * an update.
293 ALfloat Density, Diffusion;
294 ALfloat DecayTime, HFDecayTime, LFDecayTime;
295 ALfloat HFReference, LFReference;
296 } mParams;
298 /* Master effect filters */
299 struct {
300 BiquadFilter Lp;
301 BiquadFilter Hp;
302 } mFilter[NUM_LINES];
304 /* Core delay line (early reflections and late reverb tap from this). */
305 DelayLineI mDelay;
307 /* Tap points for early reflection delay. */
308 ALsizei mEarlyDelayTap[NUM_LINES][2];
309 ALfloat mEarlyDelayCoeff[NUM_LINES][2];
311 /* Tap points for late reverb feed and delay. */
312 ALsizei mLateFeedTap;
313 ALsizei mLateDelayTap[NUM_LINES][2];
315 /* Coefficients for the all-pass and line scattering matrices. */
316 ALfloat mMixX;
317 ALfloat mMixY;
319 EarlyReflections mEarly;
321 LateReverb mLate;
323 /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
324 ALsizei mFadeCount;
326 /* Maximum number of samples to process at once. */
327 ALsizei mMaxUpdate[2];
329 /* The current write offset for all delay lines. */
330 ALsizei mOffset;
332 /* Temporary storage used when processing. */
333 alignas(16) ALfloat mTempSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
334 alignas(16) ALfloat mMixBuffer[NUM_LINES][MAX_UPDATE_SAMPLES];
337 static ALvoid ReverbState_Destruct(ReverbState *State);
338 static ALboolean ReverbState_deviceUpdate(ReverbState *State, ALCdevice *Device);
339 static ALvoid ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
340 static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
341 DECLARE_DEFAULT_ALLOCATORS(ReverbState)
343 DEFINE_ALEFFECTSTATE_VTABLE(ReverbState);
345 static void ReverbState_Construct(ReverbState *state)
347 new (state) ReverbState{};
349 ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
350 SET_VTABLE2(ReverbState, ALeffectState, state);
352 state->mParams.Density = AL_EAXREVERB_DEFAULT_DENSITY;
353 state->mParams.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION;
354 state->mParams.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME;
355 state->mParams.HFDecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME*AL_EAXREVERB_DEFAULT_DECAY_HFRATIO;
356 state->mParams.LFDecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME*AL_EAXREVERB_DEFAULT_DECAY_LFRATIO;
357 state->mParams.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE;
358 state->mParams.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE;
360 for(ALsizei i{0};i < NUM_LINES;i++)
362 BiquadFilter_clear(&state->mFilter[i].Lp);
363 BiquadFilter_clear(&state->mFilter[i].Hp);
366 state->mDelay.Mask = 0;
367 state->mDelay.Line = NULL;
369 for(ALsizei i{0};i < NUM_LINES;i++)
371 state->mEarlyDelayTap[i][0] = 0;
372 state->mEarlyDelayTap[i][1] = 0;
373 state->mEarlyDelayCoeff[i][0] = 0.0f;
374 state->mEarlyDelayCoeff[i][1] = 0.0f;
377 state->mLateFeedTap = 0;
379 for(ALsizei i{0};i < NUM_LINES;i++)
381 state->mLateDelayTap[i][0] = 0;
382 state->mLateDelayTap[i][1] = 0;
385 state->mMixX = 0.0f;
386 state->mMixY = 0.0f;
388 state->mEarly.VecAp.Delay.Mask = 0;
389 state->mEarly.VecAp.Delay.Line = NULL;
390 state->mEarly.VecAp.Coeff = 0.0f;
391 state->mEarly.Delay.Mask = 0;
392 state->mEarly.Delay.Line = NULL;
393 for(ALsizei i{0};i < NUM_LINES;i++)
395 state->mEarly.VecAp.Offset[i][0] = 0;
396 state->mEarly.VecAp.Offset[i][1] = 0;
397 state->mEarly.Offset[i][0] = 0;
398 state->mEarly.Offset[i][1] = 0;
399 state->mEarly.Coeff[i][0] = 0.0f;
400 state->mEarly.Coeff[i][1] = 0.0f;
403 state->mLate.DensityGain[0] = 0.0f;
404 state->mLate.DensityGain[1] = 0.0f;
405 state->mLate.Delay.Mask = 0;
406 state->mLate.Delay.Line = NULL;
407 state->mLate.VecAp.Delay.Mask = 0;
408 state->mLate.VecAp.Delay.Line = NULL;
409 state->mLate.VecAp.Coeff = 0.0f;
410 for(ALsizei i{0};i < NUM_LINES;i++)
412 state->mLate.Offset[i][0] = 0;
413 state->mLate.Offset[i][1] = 0;
415 state->mLate.VecAp.Offset[i][0] = 0;
416 state->mLate.VecAp.Offset[i][1] = 0;
418 state->mLate.T60[i].MidGain[0] = 0.0f;
419 state->mLate.T60[i].MidGain[1] = 0.0f;
420 BiquadFilter_clear(&state->mLate.T60[i].HFFilter);
421 BiquadFilter_clear(&state->mLate.T60[i].LFFilter);
424 for(ALsizei i{0};i < NUM_LINES;i++)
426 for(ALsizei j{0};j < MAX_OUTPUT_CHANNELS;j++)
428 state->mEarly.CurrentGain[i][j] = 0.0f;
429 state->mEarly.PanGain[i][j] = 0.0f;
430 state->mLate.CurrentGain[i][j] = 0.0f;
431 state->mLate.PanGain[i][j] = 0.0f;
435 state->mFadeCount = 0;
436 state->mMaxUpdate[0] = MAX_UPDATE_SAMPLES;
437 state->mMaxUpdate[1] = MAX_UPDATE_SAMPLES;
438 state->mOffset = 0;
441 static ALvoid ReverbState_Destruct(ReverbState *State)
443 ALeffectState_Destruct(STATIC_CAST(ALeffectState,State));
444 State->~ReverbState();
447 /**************************************
448 * Device Update *
449 **************************************/
451 static inline ALfloat CalcDelayLengthMult(ALfloat density)
453 return maxf(5.0f, cbrtf(density*DENSITY_SCALE));
456 /* Given the allocated sample buffer, this function updates each delay line
457 * offset.
459 static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay)
461 union {
462 ALfloat *f;
463 ALfloat (*f4)[NUM_LINES];
464 } u;
465 u.f = &sampleBuffer[(ptrdiff_t)Delay->Line * NUM_LINES];
466 Delay->Line = u.f4;
469 /* Calculate the length of a delay line and store its mask and offset. */
470 static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALuint frequency,
471 const ALuint extra, DelayLineI *Delay)
473 ALuint samples;
475 /* All line lengths are powers of 2, calculated from their lengths in
476 * seconds, rounded up.
478 samples = float2int(ceilf(length*frequency));
479 samples = NextPowerOf2(samples + extra);
481 /* All lines share a single sample buffer. */
482 Delay->Mask = samples - 1;
483 Delay->Line = (ALfloat(*)[NUM_LINES])offset;
485 /* Return the sample count for accumulation. */
486 return samples;
489 /* Calculates the delay line metrics and allocates the shared sample buffer
490 * for all lines given the sample rate (frequency). If an allocation failure
491 * occurs, it returns AL_FALSE.
493 static ALboolean AllocLines(const ALuint frequency, ReverbState *State)
495 /* All delay line lengths are calculated to accomodate the full range of
496 * lengths given their respective paramters.
498 ALuint totalSamples{0u};
500 /* Multiplier for the maximum density value, i.e. density=1, which is
501 * actually the least density...
503 ALfloat multiplier{CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY)};
505 /* The main delay length includes the maximum early reflection delay, the
506 * largest early tap width, the maximum late reverb delay, and the
507 * largest late tap width. Finally, it must also be extended by the
508 * update size (MAX_UPDATE_SAMPLES) for block processing.
510 ALfloat length{AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier +
511 AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
512 (LATE_LINE_LENGTHS[NUM_LINES-1] - LATE_LINE_LENGTHS[0])*0.25f*multiplier};
513 totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES,
514 &State->mDelay);
516 /* The early vector all-pass line. */
517 length = EARLY_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
518 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
519 &State->mEarly.VecAp.Delay);
521 /* The early reflection line. */
522 length = EARLY_LINE_LENGTHS[NUM_LINES-1] * multiplier;
523 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
524 &State->mEarly.Delay);
526 /* The late vector all-pass line. */
527 length = LATE_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
528 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
529 &State->mLate.VecAp.Delay);
531 /* The late delay lines are calculated from the largest maximum density
532 * line length.
534 length = LATE_LINE_LENGTHS[NUM_LINES-1] * multiplier;
535 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
536 &State->mLate.Delay);
538 if(totalSamples != State->mSampleBuffer.size())
540 State->mSampleBuffer.resize(sizeof(ALfloat[NUM_LINES]) * totalSamples);
541 State->mSampleBuffer.shrink_to_fit();
544 /* Clear the sample buffer. */
545 std::fill(State->mSampleBuffer.begin(), State->mSampleBuffer.end(), 0.0f);
547 /* Update all delays to reflect the new sample buffer. */
548 RealizeLineOffset(State->mSampleBuffer.data(), &State->mDelay);
549 RealizeLineOffset(State->mSampleBuffer.data(), &State->mEarly.VecAp.Delay);
550 RealizeLineOffset(State->mSampleBuffer.data(), &State->mEarly.Delay);
551 RealizeLineOffset(State->mSampleBuffer.data(), &State->mLate.VecAp.Delay);
552 RealizeLineOffset(State->mSampleBuffer.data(), &State->mLate.Delay);
554 return AL_TRUE;
557 static ALboolean ReverbState_deviceUpdate(ReverbState *State, ALCdevice *Device)
559 ALuint frequency = Device->Frequency;
560 ALfloat multiplier;
561 ALsizei i, j;
563 /* Allocate the delay lines. */
564 if(!AllocLines(frequency, State))
565 return AL_FALSE;
567 multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
569 /* The late feed taps are set a fixed position past the latest delay tap. */
570 State->mLateFeedTap = float2int((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
571 EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier) *
572 frequency);
574 /* Clear filters and gain coefficients since the delay lines were all just
575 * cleared (if not reallocated).
577 for(i = 0;i < NUM_LINES;i++)
579 BiquadFilter_clear(&State->mFilter[i].Lp);
580 BiquadFilter_clear(&State->mFilter[i].Hp);
583 for(i = 0;i < NUM_LINES;i++)
585 State->mEarlyDelayCoeff[i][0] = 0.0f;
586 State->mEarlyDelayCoeff[i][1] = 0.0f;
589 for(i = 0;i < NUM_LINES;i++)
591 State->mEarly.Coeff[i][0] = 0.0f;
592 State->mEarly.Coeff[i][1] = 0.0f;
595 State->mLate.DensityGain[0] = 0.0f;
596 State->mLate.DensityGain[1] = 0.0f;
597 for(i = 0;i < NUM_LINES;i++)
599 State->mLate.T60[i].MidGain[0] = 0.0f;
600 State->mLate.T60[i].MidGain[1] = 0.0f;
601 BiquadFilter_clear(&State->mLate.T60[i].HFFilter);
602 BiquadFilter_clear(&State->mLate.T60[i].LFFilter);
605 for(i = 0;i < NUM_LINES;i++)
607 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
609 State->mEarly.CurrentGain[i][j] = 0.0f;
610 State->mEarly.PanGain[i][j] = 0.0f;
611 State->mLate.CurrentGain[i][j] = 0.0f;
612 State->mLate.PanGain[i][j] = 0.0f;
616 /* Reset counters and offset base. */
617 State->mFadeCount = 0;
618 State->mMaxUpdate[0] = MAX_UPDATE_SAMPLES;
619 State->mMaxUpdate[1] = MAX_UPDATE_SAMPLES;
620 State->mOffset = 0;
622 return AL_TRUE;
625 /**************************************
626 * Effect Update *
627 **************************************/
629 /* Calculate a decay coefficient given the length of each cycle and the time
630 * until the decay reaches -60 dB.
632 static inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
634 return powf(REVERB_DECAY_GAIN, length/decayTime);
637 /* Calculate a decay length from a coefficient and the time until the decay
638 * reaches -60 dB.
640 static inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
642 return log10f(coeff) * decayTime / log10f(REVERB_DECAY_GAIN);
645 /* Calculate an attenuation to be applied to the input of any echo models to
646 * compensate for modal density and decay time.
648 static inline ALfloat CalcDensityGain(const ALfloat a)
650 /* The energy of a signal can be obtained by finding the area under the
651 * squared signal. This takes the form of Sum(x_n^2), where x is the
652 * amplitude for the sample n.
654 * Decaying feedback matches exponential decay of the form Sum(a^n),
655 * where a is the attenuation coefficient, and n is the sample. The area
656 * under this decay curve can be calculated as: 1 / (1 - a).
658 * Modifying the above equation to find the area under the squared curve
659 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
660 * calculated by inverting the square root of this approximation,
661 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
663 return sqrtf(1.0f - a*a);
666 /* Calculate the scattering matrix coefficients given a diffusion factor. */
667 static inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
669 ALfloat n, t;
671 /* The matrix is of order 4, so n is sqrt(4 - 1). */
672 n = sqrtf(3.0f);
673 t = diffusion * atanf(n);
675 /* Calculate the first mixing matrix coefficient. */
676 *x = cosf(t);
677 /* Calculate the second mixing matrix coefficient. */
678 *y = sinf(t) / n;
681 /* Calculate the limited HF ratio for use with the late reverb low-pass
682 * filters.
684 static ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
685 const ALfloat decayTime, const ALfloat SpeedOfSound)
687 ALfloat limitRatio;
689 /* Find the attenuation due to air absorption in dB (converting delay
690 * time to meters using the speed of sound). Then reversing the decay
691 * equation, solve for HF ratio. The delay length is cancelled out of
692 * the equation, so it can be calculated once for all lines.
694 limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound);
696 /* Using the limit calculated above, apply the upper bound to the HF ratio.
698 return minf(limitRatio, hfRatio);
702 /* Calculates the 3-band T60 damping coefficients for a particular delay line
703 * of specified length, using a combination of two shelf filter sections given
704 * decay times for each band split at two reference frequencies.
706 static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime,
707 const ALfloat mfDecayTime, const ALfloat hfDecayTime,
708 const ALfloat lf0norm, const ALfloat hf0norm,
709 T60Filter *filter)
711 ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime);
712 ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime);
713 ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime);
715 filter->MidGain[1] = mfGain;
716 BiquadFilter_setParams(&filter->LFFilter, BiquadType::LowShelf, lfGain/mfGain, lf0norm,
717 calc_rcpQ_from_slope(lfGain/mfGain, 1.0f));
718 BiquadFilter_setParams(&filter->HFFilter, BiquadType::HighShelf, hfGain/mfGain, hf0norm,
719 calc_rcpQ_from_slope(hfGain/mfGain, 1.0f));
722 /* Update the offsets for the main effect delay line. */
723 static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ReverbState *State)
725 ALfloat multiplier, length;
726 ALuint i;
728 multiplier = CalcDelayLengthMult(density);
730 /* Early reflection taps are decorrelated by means of an average room
731 * reflection approximation described above the definition of the taps.
732 * This approximation is linear and so the above density multiplier can
733 * be applied to adjust the width of the taps. A single-band decay
734 * coefficient is applied to simulate initial attenuation and absorption.
736 * Late reverb taps are based on the late line lengths to allow a zero-
737 * delay path and offsets that would continue the propagation naturally
738 * into the late lines.
740 for(i = 0;i < NUM_LINES;i++)
742 length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
743 State->mEarlyDelayTap[i][1] = float2int(length * frequency);
745 length = EARLY_TAP_LENGTHS[i]*multiplier;
746 State->mEarlyDelayCoeff[i][1] = CalcDecayCoeff(length, decayTime);
748 length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
749 State->mLateDelayTap[i][1] = State->mLateFeedTap + float2int(length * frequency);
753 /* Update the early reflection line lengths and gain coefficients. */
754 static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat diffusion, const ALfloat decayTime, const ALuint frequency, EarlyReflections *Early)
756 ALfloat multiplier, length;
757 ALsizei i;
759 multiplier = CalcDelayLengthMult(density);
761 /* Calculate the all-pass feed-back/forward coefficient. */
762 Early->VecAp.Coeff = sqrtf(0.5f) * powf(diffusion, 2.0f);
764 for(i = 0;i < NUM_LINES;i++)
766 /* Calculate the length (in seconds) of each all-pass line. */
767 length = EARLY_ALLPASS_LENGTHS[i] * multiplier;
769 /* Calculate the delay offset for each all-pass line. */
770 Early->VecAp.Offset[i][1] = float2int(length * frequency);
772 /* Calculate the length (in seconds) of each delay line. */
773 length = EARLY_LINE_LENGTHS[i] * multiplier;
775 /* Calculate the delay offset for each delay line. */
776 Early->Offset[i][1] = float2int(length * frequency);
778 /* Calculate the gain (coefficient) for each line. */
779 Early->Coeff[i][1] = CalcDecayCoeff(length, decayTime);
783 /* Update the late reverb line lengths and T60 coefficients. */
784 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)
786 /* Scaling factor to convert the normalized reference frequencies from
787 * representing 0...freq to 0...max_reference.
789 const ALfloat norm_weight_factor = (ALfloat)frequency / AL_EAXREVERB_MAX_HFREFERENCE;
790 ALfloat multiplier, length, bandWeights[3];
791 ALsizei i;
793 /* To compensate for changes in modal density and decay time of the late
794 * reverb signal, the input is attenuated based on the maximal energy of
795 * the outgoing signal. This approximation is used to keep the apparent
796 * energy of the signal equal for all ranges of density and decay time.
798 * The average length of the delay lines is used to calculate the
799 * attenuation coefficient.
801 multiplier = CalcDelayLengthMult(density);
802 length = (LATE_LINE_LENGTHS[0] + LATE_LINE_LENGTHS[1] +
803 LATE_LINE_LENGTHS[2] + LATE_LINE_LENGTHS[3]) / 4.0f * multiplier;
804 length += (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
805 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f * multiplier;
806 /* The density gain calculation uses an average decay time weighted by
807 * approximate bandwidth. This attempts to compensate for losses of energy
808 * that reduce decay time due to scattering into highly attenuated bands.
810 bandWeights[0] = lf0norm*norm_weight_factor;
811 bandWeights[1] = hf0norm*norm_weight_factor - lf0norm*norm_weight_factor;
812 bandWeights[2] = 1.0f - hf0norm*norm_weight_factor;
813 Late->DensityGain[1] = CalcDensityGain(
814 CalcDecayCoeff(length,
815 bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime + bandWeights[2]*hfDecayTime
819 /* Calculate the all-pass feed-back/forward coefficient. */
820 Late->VecAp.Coeff = sqrtf(0.5f) * powf(diffusion, 2.0f);
822 for(i = 0;i < NUM_LINES;i++)
824 /* Calculate the length (in seconds) of each all-pass line. */
825 length = LATE_ALLPASS_LENGTHS[i] * multiplier;
827 /* Calculate the delay offset for each all-pass line. */
828 Late->VecAp.Offset[i][1] = float2int(length * frequency);
830 /* Calculate the length (in seconds) of each delay line. */
831 length = LATE_LINE_LENGTHS[i] * multiplier;
833 /* Calculate the delay offset for each delay line. */
834 Late->Offset[i][1] = float2int(length*frequency + 0.5f);
836 /* Approximate the absorption that the vector all-pass would exhibit
837 * given the current diffusion so we don't have to process a full T60
838 * filter for each of its four lines.
840 length += lerp(LATE_ALLPASS_LENGTHS[i],
841 (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
842 LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f,
843 diffusion) * multiplier;
845 /* Calculate the T60 damping coefficients for each line. */
846 CalcT60DampingCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime,
847 lf0norm, hf0norm, &Late->T60[i]);
851 /* Creates a transform matrix given a reverb vector. The vector pans the reverb
852 * reflections toward the given direction, using its magnitude (up to 1) as a
853 * focal strength. This function results in a B-Format transformation matrix
854 * that spatially focuses the signal in the desired direction.
856 static aluMatrixf GetTransformFromVector(const ALfloat *vec)
858 aluMatrixf focus;
859 ALfloat norm[3];
860 ALfloat mag;
862 /* Normalize the panning vector according to the N3D scale, which has an
863 * extra sqrt(3) term on the directional components. Converting from OpenAL
864 * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
865 * that the reverb panning vectors use left-handed coordinates, unlike the
866 * rest of OpenAL which use right-handed. This is fixed by negating Z,
867 * which cancels out with the B-Format Z negation.
869 mag = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
870 if(mag > 1.0f)
872 norm[0] = vec[0] / mag * -SQRTF_3;
873 norm[1] = vec[1] / mag * SQRTF_3;
874 norm[2] = vec[2] / mag * SQRTF_3;
875 mag = 1.0f;
877 else
879 /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
880 * term. There's no need to renormalize the magnitude since it would
881 * just be reapplied in the matrix.
883 norm[0] = vec[0] * -SQRTF_3;
884 norm[1] = vec[1] * SQRTF_3;
885 norm[2] = vec[2] * SQRTF_3;
888 aluMatrixfSet(&focus,
889 1.0f, 0.0f, 0.0f, 0.0f,
890 norm[0], 1.0f-mag, 0.0f, 0.0f,
891 norm[1], 0.0f, 1.0f-mag, 0.0f,
892 norm[2], 0.0f, 0.0f, 1.0f-mag
895 return focus;
898 /* Update the early and late 3D panning gains. */
899 static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat earlyGain, const ALfloat lateGain, ReverbState *State)
901 aluMatrixf transform, rot;
902 ALsizei i;
904 STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
905 STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;
907 /* Note: _res is transposed. */
908 #define MATRIX_MULT(_res, _m1, _m2) do { \
909 int row, col; \
910 for(col = 0;col < 4;col++) \
912 for(row = 0;row < 4;row++) \
913 _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
914 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
916 } while(0)
917 /* Create a matrix that first converts A-Format to B-Format, then
918 * transforms the B-Format signal according to the panning vector.
920 rot = GetTransformFromVector(ReflectionsPan);
921 MATRIX_MULT(transform, rot, A2B);
922 memset(&State->mEarly.PanGain, 0, sizeof(State->mEarly.PanGain));
923 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
924 ComputePanGains(&Device->FOAOut, transform.m[i], earlyGain,
925 State->mEarly.PanGain[i]);
927 rot = GetTransformFromVector(LateReverbPan);
928 MATRIX_MULT(transform, rot, A2B);
929 memset(&State->mLate.PanGain, 0, sizeof(State->mLate.PanGain));
930 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
931 ComputePanGains(&Device->FOAOut, transform.m[i], lateGain,
932 State->mLate.PanGain[i]);
933 #undef MATRIX_MULT
936 static void ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
938 const ALCdevice *Device = Context->Device;
939 const ALlistener &Listener = Context->Listener;
940 ALuint frequency = Device->Frequency;
941 ALfloat lf0norm, hf0norm, hfRatio;
942 ALfloat lfDecayTime, hfDecayTime;
943 ALfloat gain, gainlf, gainhf;
944 ALsizei i;
946 /* Calculate the master filters */
947 hf0norm = minf(props->Reverb.HFReference / frequency, 0.49f);
948 /* Restrict the filter gains from going below -60dB to keep the filter from
949 * killing most of the signal.
951 gainhf = maxf(props->Reverb.GainHF, 0.001f);
952 BiquadFilter_setParams(&State->mFilter[0].Lp, BiquadType::HighShelf, gainhf, hf0norm,
953 calc_rcpQ_from_slope(gainhf, 1.0f));
954 lf0norm = minf(props->Reverb.LFReference / frequency, 0.49f);
955 gainlf = maxf(props->Reverb.GainLF, 0.001f);
956 BiquadFilter_setParams(&State->mFilter[0].Hp, BiquadType::LowShelf, gainlf, lf0norm,
957 calc_rcpQ_from_slope(gainlf, 1.0f));
958 for(i = 1;i < NUM_LINES;i++)
960 BiquadFilter_copyParams(&State->mFilter[i].Lp, &State->mFilter[0].Lp);
961 BiquadFilter_copyParams(&State->mFilter[i].Hp, &State->mFilter[0].Hp);
964 /* Update the main effect delay and associated taps. */
965 UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
966 props->Reverb.Density, props->Reverb.DecayTime, frequency,
967 State);
969 /* Update the early lines. */
970 UpdateEarlyLines(props->Reverb.Density, props->Reverb.Diffusion,
971 props->Reverb.DecayTime, frequency, &State->mEarly);
973 /* Get the mixing matrix coefficients. */
974 CalcMatrixCoeffs(props->Reverb.Diffusion, &State->mMixX, &State->mMixY);
976 /* If the HF limit parameter is flagged, calculate an appropriate limit
977 * based on the air absorption parameter.
979 hfRatio = props->Reverb.DecayHFRatio;
980 if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
981 hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
982 props->Reverb.DecayTime, Listener.Params.ReverbSpeedOfSound
985 /* Calculate the LF/HF decay times. */
986 lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
987 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
988 hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
989 AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
991 /* Update the late lines. */
992 UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
993 lfDecayTime, props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm,
994 frequency, &State->mLate
997 /* Update early and late 3D panning. */
998 gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
999 Update3DPanning(Device, props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan,
1000 props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain,
1001 State);
1003 /* Calculate the max update size from the smallest relevant delay. */
1004 State->mMaxUpdate[1] = mini(MAX_UPDATE_SAMPLES,
1005 mini(State->mEarly.Offset[0][1], State->mLate.Offset[0][1])
1008 /* Determine if delay-line cross-fading is required. Density is essentially
1009 * a master control for the feedback delays, so changes the offsets of many
1010 * delay lines.
1012 if(State->mParams.Density != props->Reverb.Density ||
1013 /* Diffusion and decay times influences the decay rate (gain) of the
1014 * late reverb T60 filter.
1016 State->mParams.Diffusion != props->Reverb.Diffusion ||
1017 State->mParams.DecayTime != props->Reverb.DecayTime ||
1018 State->mParams.HFDecayTime != hfDecayTime ||
1019 State->mParams.LFDecayTime != lfDecayTime ||
1020 /* HF/LF References control the weighting used to calculate the density
1021 * gain.
1023 State->mParams.HFReference != props->Reverb.HFReference ||
1024 State->mParams.LFReference != props->Reverb.LFReference)
1025 State->mFadeCount = 0;
1026 State->mParams.Density = props->Reverb.Density;
1027 State->mParams.Diffusion = props->Reverb.Diffusion;
1028 State->mParams.DecayTime = props->Reverb.DecayTime;
1029 State->mParams.HFDecayTime = hfDecayTime;
1030 State->mParams.LFDecayTime = lfDecayTime;
1031 State->mParams.HFReference = props->Reverb.HFReference;
1032 State->mParams.LFReference = props->Reverb.LFReference;
1036 /**************************************
1037 * Effect Processing *
1038 **************************************/
1040 /* Basic delay line input/output routines. */
1041 static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c)
1043 return Delay->Line[offset&Delay->Mask][c];
1046 /* Cross-faded delay line output routine. Instead of interpolating the
1047 * offsets, this interpolates (cross-fades) the outputs at each offset.
1049 static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0,
1050 const ALsizei off1, const ALsizei c,
1051 const ALfloat sc0, const ALfloat sc1)
1053 return Delay->Line[off0&Delay->Mask][c]*sc0 +
1054 Delay->Line[off1&Delay->Mask][c]*sc1;
1058 static inline void DelayLineIn(const DelayLineI *Delay, ALsizei offset, const ALsizei c,
1059 const ALfloat *RESTRICT in, ALsizei count)
1061 ALsizei i;
1062 for(i = 0;i < count;i++)
1063 Delay->Line[(offset++)&Delay->Mask][c] = *(in++);
1066 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1067 * for both the below vector all-pass model and to perform modal feed-back
1068 * delay network (FDN) mixing.
1070 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1071 * matrix with a single unitary rotational parameter:
1073 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1074 * [ -a, d, c, -b ]
1075 * [ -b, -c, d, a ]
1076 * [ -c, b, -a, d ]
1078 * The rotation is constructed from the effect's diffusion parameter,
1079 * yielding:
1081 * 1 = x^2 + 3 y^2
1083 * Where a, b, and c are the coefficient y with differing signs, and d is the
1084 * coefficient x. The final matrix is thus:
1086 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1087 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1088 * [ y, -y, x, y ] x = cos(t)
1089 * [ -y, -y, -y, x ] y = sin(t) / n
1091 * Any square orthogonal matrix with an order that is a power of two will
1092 * work (where ^T is transpose, ^-1 is inverse):
1094 * M^T = M^-1
1096 * Using that knowledge, finding an appropriate matrix can be accomplished
1097 * naively by searching all combinations of:
1099 * M = D + S - S^T
1101 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1102 * whose combination of signs are being iterated.
1104 static inline void VectorPartialScatter(ALfloat *RESTRICT out, const ALfloat *RESTRICT in,
1105 const ALfloat xCoeff, const ALfloat yCoeff)
1107 out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
1108 out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
1109 out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
1110 out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
1112 #define VectorScatterDelayIn(delay, o, in, xcoeff, ycoeff) \
1113 VectorPartialScatter((delay)->Line[(o)&(delay)->Mask], in, xcoeff, ycoeff)
1115 /* Utilizes the above, but reverses the input channels. */
1116 static inline void VectorScatterRevDelayIn(const DelayLineI *Delay, ALint offset,
1117 const ALfloat xCoeff, const ALfloat yCoeff,
1118 const ALfloat (*RESTRICT in)[MAX_UPDATE_SAMPLES],
1119 const ALsizei count)
1121 const DelayLineI delay = *Delay;
1122 ALsizei i, j;
1124 for(i = 0;i < count;++i)
1126 ALfloat f[NUM_LINES];
1127 for(j = 0;j < NUM_LINES;j++)
1128 f[NUM_LINES-1-j] = in[j][i];
1130 VectorScatterDelayIn(&delay, offset++, f, xCoeff, yCoeff);
1134 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1135 * filter to the 4-line input.
1137 * It works by vectorizing a regular all-pass filter and replacing the delay
1138 * element with a scattering matrix (like the one above) and a diagonal
1139 * matrix of delay elements.
1141 * Two static specializations are used for transitional (cross-faded) delay
1142 * line processing and non-transitional processing.
1144 static void VectorAllpass_Unfaded(ALfloat (*RESTRICT samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
1145 const ALfloat xCoeff, const ALfloat yCoeff, ALsizei todo,
1146 VecAllpass *Vap)
1148 const DelayLineI delay = Vap->Delay;
1149 const ALfloat feedCoeff = Vap->Coeff;
1150 ALsizei vap_offset[NUM_LINES];
1151 ALsizei i, j;
1153 ASSUME(todo > 0);
1155 for(j = 0;j < NUM_LINES;j++)
1156 vap_offset[j] = offset-Vap->Offset[j][0];
1157 for(i = 0;i < todo;i++)
1159 ALfloat f[NUM_LINES];
1161 for(j = 0;j < NUM_LINES;j++)
1163 ALfloat input = samples[j][i];
1164 ALfloat out = DelayLineOut(&delay, vap_offset[j]++, j) - feedCoeff*input;
1165 f[j] = input + feedCoeff*out;
1167 samples[j][i] = out;
1170 VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
1171 ++offset;
1174 static void VectorAllpass_Faded(ALfloat (*RESTRICT samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
1175 const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade,
1176 ALsizei todo, VecAllpass *Vap)
1178 const DelayLineI delay = Vap->Delay;
1179 const ALfloat feedCoeff = Vap->Coeff;
1180 ALsizei vap_offset[NUM_LINES][2];
1181 ALsizei i, j;
1183 ASSUME(todo > 0);
1185 fade *= 1.0f/FADE_SAMPLES;
1186 for(j = 0;j < NUM_LINES;j++)
1188 vap_offset[j][0] = offset-Vap->Offset[j][0];
1189 vap_offset[j][1] = offset-Vap->Offset[j][1];
1191 for(i = 0;i < todo;i++)
1193 ALfloat f[NUM_LINES];
1195 for(j = 0;j < NUM_LINES;j++)
1197 ALfloat input = samples[j][i];
1198 ALfloat out =
1199 FadedDelayLineOut(&delay, vap_offset[j][0]++, vap_offset[j][1]++, j,
1200 1.0f-fade, fade
1201 ) - feedCoeff*input;
1202 f[j] = input + feedCoeff*out;
1204 samples[j][i] = out;
1206 fade += FadeStep;
1208 VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
1209 ++offset;
1213 /* This generates early reflections.
1215 * This is done by obtaining the primary reflections (those arriving from the
1216 * same direction as the source) from the main delay line. These are
1217 * attenuated and all-pass filtered (based on the diffusion parameter).
1219 * The early lines are then fed in reverse (according to the approximately
1220 * opposite spatial location of the A-Format lines) to create the secondary
1221 * reflections (those arriving from the opposite direction as the source).
1223 * The early response is then completed by combining the primary reflections
1224 * with the delayed and attenuated output from the early lines.
1226 * Finally, the early response is reversed, scattered (based on diffusion),
1227 * and fed into the late reverb section of the main delay line.
1229 * Two static specializations are used for transitional (cross-faded) delay
1230 * line processing and non-transitional processing.
1232 static void EarlyReflection_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
1233 ALfloat (*RESTRICT out)[MAX_UPDATE_SAMPLES])
1235 ALfloat (*RESTRICT temps)[MAX_UPDATE_SAMPLES] = State->mTempSamples;
1236 const DelayLineI early_delay = State->mEarly.Delay;
1237 const DelayLineI main_delay = State->mDelay;
1238 const ALfloat mixX = State->mMixX;
1239 const ALfloat mixY = State->mMixY;
1240 ALsizei late_feed_tap;
1241 ALsizei i, j;
1243 ASSUME(todo > 0);
1245 /* First, load decorrelated samples from the main delay line as the primary
1246 * reflections.
1248 for(j = 0;j < NUM_LINES;j++)
1250 ALsizei early_delay_tap = offset - State->mEarlyDelayTap[j][0];
1251 ALfloat coeff = State->mEarlyDelayCoeff[j][0];
1252 for(i = 0;i < todo;i++)
1253 temps[j][i] = DelayLineOut(&main_delay, early_delay_tap++, j) * coeff;
1256 /* Apply a vector all-pass, to help color the initial reflections based on
1257 * the diffusion strength.
1259 VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->mEarly.VecAp);
1261 /* Apply a delay and bounce to generate secondary reflections, combine with
1262 * the primary reflections and write out the result for mixing.
1264 for(j = 0;j < NUM_LINES;j++)
1266 ALint early_feedb_tap = offset - State->mEarly.Offset[j][0];
1267 ALfloat early_feedb_coeff = State->mEarly.Coeff[j][0];
1269 for(i = 0;i < todo;i++)
1270 out[j][i] = DelayLineOut(&early_delay, early_feedb_tap++, j)*early_feedb_coeff +
1271 temps[j][i];
1273 for(j = 0;j < NUM_LINES;j++)
1274 DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
1276 /* Also write the result back to the main delay line for the late reverb
1277 * stage to pick up at the appropriate time, appplying a scatter and
1278 * bounce to improve the initial diffusion in the late reverb.
1280 late_feed_tap = offset - State->mLateFeedTap;
1281 VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
1283 static void EarlyReflection_Faded(ReverbState *State, ALsizei offset, const ALsizei todo,
1284 const ALfloat fade, ALfloat (*RESTRICT out)[MAX_UPDATE_SAMPLES])
1286 ALfloat (*RESTRICT temps)[MAX_UPDATE_SAMPLES] = State->mTempSamples;
1287 const DelayLineI early_delay = State->mEarly.Delay;
1288 const DelayLineI main_delay = State->mDelay;
1289 const ALfloat mixX = State->mMixX;
1290 const ALfloat mixY = State->mMixY;
1291 ALsizei late_feed_tap;
1292 ALsizei i, j;
1294 ASSUME(todo > 0);
1296 for(j = 0;j < NUM_LINES;j++)
1298 ALsizei early_delay_tap0 = offset - State->mEarlyDelayTap[j][0];
1299 ALsizei early_delay_tap1 = offset - State->mEarlyDelayTap[j][1];
1300 ALfloat oldCoeff = State->mEarlyDelayCoeff[j][0];
1301 ALfloat oldCoeffStep = -oldCoeff / FADE_SAMPLES;
1302 ALfloat newCoeffStep = State->mEarlyDelayCoeff[j][1] / FADE_SAMPLES;
1303 ALfloat fadeCount = fade;
1305 for(i = 0;i < todo;i++)
1307 const ALfloat fade0 = oldCoeff + oldCoeffStep*fadeCount;
1308 const ALfloat fade1 = newCoeffStep*fadeCount;
1309 temps[j][i] = FadedDelayLineOut(&main_delay,
1310 early_delay_tap0++, early_delay_tap1++, j, fade0, fade1
1312 fadeCount += 1.0f;
1316 VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->mEarly.VecAp);
1318 for(j = 0;j < NUM_LINES;j++)
1320 ALint feedb_tap0 = offset - State->mEarly.Offset[j][0];
1321 ALint feedb_tap1 = offset - State->mEarly.Offset[j][1];
1322 ALfloat feedb_oldCoeff = State->mEarly.Coeff[j][0];
1323 ALfloat feedb_oldCoeffStep = -feedb_oldCoeff / FADE_SAMPLES;
1324 ALfloat feedb_newCoeffStep = State->mEarly.Coeff[j][1] / FADE_SAMPLES;
1325 ALfloat fadeCount = fade;
1327 for(i = 0;i < todo;i++)
1329 const ALfloat fade0 = feedb_oldCoeff + feedb_oldCoeffStep*fadeCount;
1330 const ALfloat fade1 = feedb_newCoeffStep*fadeCount;
1331 out[j][i] = FadedDelayLineOut(&early_delay,
1332 feedb_tap0++, feedb_tap1++, j, fade0, fade1
1333 ) + temps[j][i];
1334 fadeCount += 1.0f;
1337 for(j = 0;j < NUM_LINES;j++)
1338 DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
1340 late_feed_tap = offset - State->mLateFeedTap;
1341 VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
1344 /* Applies the two T60 damping filter sections. */
1345 static inline void LateT60Filter(ALfloat *RESTRICT samples, const ALsizei todo, T60Filter *filter)
1347 ALfloat temp[MAX_UPDATE_SAMPLES];
1348 BiquadFilter_process(&filter->HFFilter, temp, samples, todo);
1349 BiquadFilter_process(&filter->LFFilter, samples, temp, todo);
1352 /* This generates the reverb tail using a modified feed-back delay network
1353 * (FDN).
1355 * Results from the early reflections are mixed with the output from the late
1356 * delay lines.
1358 * The late response is then completed by T60 and all-pass filtering the mix.
1360 * Finally, the lines are reversed (so they feed their opposite directions)
1361 * and scattered with the FDN matrix before re-feeding the delay lines.
1363 * Two variations are made, one for for transitional (cross-faded) delay line
1364 * processing and one for non-transitional processing.
1366 static void LateReverb_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
1367 ALfloat (*RESTRICT out)[MAX_UPDATE_SAMPLES])
1369 ALfloat (*RESTRICT temps)[MAX_UPDATE_SAMPLES] = State->mTempSamples;
1370 const DelayLineI late_delay = State->mLate.Delay;
1371 const DelayLineI main_delay = State->mDelay;
1372 const ALfloat mixX = State->mMixX;
1373 const ALfloat mixY = State->mMixY;
1374 ALsizei i, j;
1376 ASSUME(todo > 0);
1378 /* First, load decorrelated samples from the main and feedback delay lines.
1379 * Filter the signal to apply its frequency-dependent decay.
1381 for(j = 0;j < NUM_LINES;j++)
1383 ALsizei late_delay_tap = offset - State->mLateDelayTap[j][0];
1384 ALsizei late_feedb_tap = offset - State->mLate.Offset[j][0];
1385 ALfloat midGain = State->mLate.T60[j].MidGain[0];
1386 const ALfloat densityGain = State->mLate.DensityGain[0] * midGain;
1387 for(i = 0;i < todo;i++)
1388 temps[j][i] = DelayLineOut(&main_delay, late_delay_tap++, j)*densityGain +
1389 DelayLineOut(&late_delay, late_feedb_tap++, j)*midGain;
1390 LateT60Filter(temps[j], todo, &State->mLate.T60[j]);
1393 /* Apply a vector all-pass to improve micro-surface diffusion, and write
1394 * out the results for mixing.
1396 VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->mLate.VecAp);
1398 for(j = 0;j < NUM_LINES;j++)
1399 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1401 /* Finally, scatter and bounce the results to refeed the feedback buffer. */
1402 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, out, todo);
1404 static void LateReverb_Faded(ReverbState *State, ALsizei offset, const ALsizei todo,
1405 const ALfloat fade, ALfloat (*RESTRICT out)[MAX_UPDATE_SAMPLES])
1407 ALfloat (*RESTRICT temps)[MAX_UPDATE_SAMPLES] = State->mTempSamples;
1408 const DelayLineI late_delay = State->mLate.Delay;
1409 const DelayLineI main_delay = State->mDelay;
1410 const ALfloat mixX = State->mMixX;
1411 const ALfloat mixY = State->mMixY;
1412 ALsizei i, j;
1414 ASSUME(todo > 0);
1416 for(j = 0;j < NUM_LINES;j++)
1418 const ALfloat oldMidGain = State->mLate.T60[j].MidGain[0];
1419 const ALfloat midGain = State->mLate.T60[j].MidGain[1];
1420 const ALfloat oldMidStep = -oldMidGain / FADE_SAMPLES;
1421 const ALfloat midStep = midGain / FADE_SAMPLES;
1422 const ALfloat oldDensityGain = State->mLate.DensityGain[0] * oldMidGain;
1423 const ALfloat densityGain = State->mLate.DensityGain[1] * midGain;
1424 const ALfloat oldDensityStep = -oldDensityGain / FADE_SAMPLES;
1425 const ALfloat densityStep = densityGain / FADE_SAMPLES;
1426 ALsizei late_delay_tap0 = offset - State->mLateDelayTap[j][0];
1427 ALsizei late_delay_tap1 = offset - State->mLateDelayTap[j][1];
1428 ALsizei late_feedb_tap0 = offset - State->mLate.Offset[j][0];
1429 ALsizei late_feedb_tap1 = offset - State->mLate.Offset[j][1];
1430 ALfloat fadeCount = fade;
1432 for(i = 0;i < todo;i++)
1434 const ALfloat fade0 = oldDensityGain + oldDensityStep*fadeCount;
1435 const ALfloat fade1 = densityStep*fadeCount;
1436 const ALfloat gfade0 = oldMidGain + oldMidStep*fadeCount;
1437 const ALfloat gfade1 = midStep*fadeCount;
1438 temps[j][i] =
1439 FadedDelayLineOut(&main_delay, late_delay_tap0++, late_delay_tap1++, j,
1440 fade0, fade1) +
1441 FadedDelayLineOut(&late_delay, late_feedb_tap0++, late_feedb_tap1++, j,
1442 gfade0, gfade1);
1443 fadeCount += 1.0f;
1445 LateT60Filter(temps[j], todo, &State->mLate.T60[j]);
1448 VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->mLate.VecAp);
1450 for(j = 0;j < NUM_LINES;j++)
1451 memcpy(out[j], temps[j], todo*sizeof(ALfloat));
1453 VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, temps, todo);
1456 static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
1458 ALfloat (*RESTRICT afmt)[MAX_UPDATE_SAMPLES] = State->mTempSamples;
1459 ALfloat (*RESTRICT samples)[MAX_UPDATE_SAMPLES] = State->mMixBuffer;
1460 ALsizei fadeCount = State->mFadeCount;
1461 ALsizei offset = State->mOffset;
1462 ALsizei base, c;
1464 /* Process reverb for these samples. */
1465 for(base = 0;base < SamplesToDo;)
1467 ALsizei todo = SamplesToDo - base;
1468 /* If cross-fading, don't do more samples than there are to fade. */
1469 if(FADE_SAMPLES-fadeCount > 0)
1471 todo = mini(todo, FADE_SAMPLES-fadeCount);
1472 todo = mini(todo, State->mMaxUpdate[0]);
1474 todo = mini(todo, State->mMaxUpdate[1]);
1475 /* If this is not the final update, ensure the update size is a
1476 * multiple of 4 for the SIMD mixers.
1478 if(todo < SamplesToDo-base)
1479 todo &= ~3;
1481 /* Convert B-Format to A-Format for processing. */
1482 memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
1483 for(c = 0;c < NUM_LINES;c++)
1484 MixRowSamples(afmt[c], B2A.m[c],
1485 SamplesIn, MAX_EFFECT_CHANNELS, base, todo
1488 /* Process the samples for reverb. */
1489 for(c = 0;c < NUM_LINES;c++)
1491 /* Band-pass the incoming samples. */
1492 BiquadFilter_process(&State->mFilter[c].Lp, samples[0], afmt[c], todo);
1493 BiquadFilter_process(&State->mFilter[c].Hp, samples[1], samples[0], todo);
1495 /* Feed the initial delay line. */
1496 DelayLineIn(&State->mDelay, offset, c, samples[1], todo);
1499 if(UNLIKELY(fadeCount < FADE_SAMPLES))
1501 ALfloat fade = (ALfloat)fadeCount;
1503 /* Generate early reflections. */
1504 EarlyReflection_Faded(State, offset, todo, fade, samples);
1505 /* Mix the A-Format results to output, implicitly converting back
1506 * to B-Format.
1508 for(c = 0;c < NUM_LINES;c++)
1509 MixSamples(samples[c], NumChannels, SamplesOut,
1510 State->mEarly.CurrentGain[c], State->mEarly.PanGain[c],
1511 SamplesToDo-base, base, todo
1514 /* Generate and mix late reverb. */
1515 LateReverb_Faded(State, offset, todo, fade, samples);
1516 for(c = 0;c < NUM_LINES;c++)
1517 MixSamples(samples[c], NumChannels, SamplesOut,
1518 State->mLate.CurrentGain[c], State->mLate.PanGain[c],
1519 SamplesToDo-base, base, todo
1522 /* Step fading forward. */
1523 fadeCount += todo;
1524 if(LIKELY(fadeCount >= FADE_SAMPLES))
1526 /* Update the cross-fading delay line taps. */
1527 fadeCount = FADE_SAMPLES;
1528 for(c = 0;c < NUM_LINES;c++)
1530 State->mEarlyDelayTap[c][0] = State->mEarlyDelayTap[c][1];
1531 State->mEarlyDelayCoeff[c][0] = State->mEarlyDelayCoeff[c][1];
1532 State->mEarly.VecAp.Offset[c][0] = State->mEarly.VecAp.Offset[c][1];
1533 State->mEarly.Offset[c][0] = State->mEarly.Offset[c][1];
1534 State->mEarly.Coeff[c][0] = State->mEarly.Coeff[c][1];
1535 State->mLateDelayTap[c][0] = State->mLateDelayTap[c][1];
1536 State->mLate.VecAp.Offset[c][0] = State->mLate.VecAp.Offset[c][1];
1537 State->mLate.Offset[c][0] = State->mLate.Offset[c][1];
1538 State->mLate.T60[c].MidGain[0] = State->mLate.T60[c].MidGain[1];
1540 State->mLate.DensityGain[0] = State->mLate.DensityGain[1];
1541 State->mMaxUpdate[0] = State->mMaxUpdate[1];
1544 else
1546 /* Generate and mix early reflections. */
1547 EarlyReflection_Unfaded(State, offset, todo, samples);
1548 for(c = 0;c < NUM_LINES;c++)
1549 MixSamples(samples[c], NumChannels, SamplesOut,
1550 State->mEarly.CurrentGain[c], State->mEarly.PanGain[c],
1551 SamplesToDo-base, base, todo
1554 /* Generate and mix late reverb. */
1555 LateReverb_Unfaded(State, offset, todo, samples);
1556 for(c = 0;c < NUM_LINES;c++)
1557 MixSamples(samples[c], NumChannels, SamplesOut,
1558 State->mLate.CurrentGain[c], State->mLate.PanGain[c],
1559 SamplesToDo-base, base, todo
1563 /* Step all delays forward. */
1564 offset += todo;
1566 base += todo;
1568 State->mOffset = offset;
1569 State->mFadeCount = fadeCount;
1573 struct ReverbStateFactory final : public EffectStateFactory {
1574 ALeffectState *create() override;
1577 ALeffectState *ReverbStateFactory::create()
1579 ReverbState *state;
1580 NEW_OBJ0(state, ReverbState)();
1581 return state;
1584 EffectStateFactory *ReverbStateFactory_getFactory(void)
1586 static ReverbStateFactory ReverbFactory{};
1587 return &ReverbFactory;
1591 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1593 ALeffectProps *props = &effect->Props;
1594 switch(param)
1596 case AL_EAXREVERB_DECAY_HFLIMIT:
1597 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1598 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
1599 props->Reverb.DecayHFLimit = val;
1600 break;
1602 default:
1603 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1604 param);
1607 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1608 { ALeaxreverb_setParami(effect, context, param, vals[0]); }
1609 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1611 ALeffectProps *props = &effect->Props;
1612 switch(param)
1614 case AL_EAXREVERB_DENSITY:
1615 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1616 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
1617 props->Reverb.Density = val;
1618 break;
1620 case AL_EAXREVERB_DIFFUSION:
1621 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1622 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
1623 props->Reverb.Diffusion = val;
1624 break;
1626 case AL_EAXREVERB_GAIN:
1627 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1628 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
1629 props->Reverb.Gain = val;
1630 break;
1632 case AL_EAXREVERB_GAINHF:
1633 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1634 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
1635 props->Reverb.GainHF = val;
1636 break;
1638 case AL_EAXREVERB_GAINLF:
1639 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1640 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
1641 props->Reverb.GainLF = val;
1642 break;
1644 case AL_EAXREVERB_DECAY_TIME:
1645 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1646 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
1647 props->Reverb.DecayTime = val;
1648 break;
1650 case AL_EAXREVERB_DECAY_HFRATIO:
1651 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1652 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
1653 props->Reverb.DecayHFRatio = val;
1654 break;
1656 case AL_EAXREVERB_DECAY_LFRATIO:
1657 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1658 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
1659 props->Reverb.DecayLFRatio = val;
1660 break;
1662 case AL_EAXREVERB_REFLECTIONS_GAIN:
1663 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1664 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
1665 props->Reverb.ReflectionsGain = val;
1666 break;
1668 case AL_EAXREVERB_REFLECTIONS_DELAY:
1669 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1670 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
1671 props->Reverb.ReflectionsDelay = val;
1672 break;
1674 case AL_EAXREVERB_LATE_REVERB_GAIN:
1675 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1676 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
1677 props->Reverb.LateReverbGain = val;
1678 break;
1680 case AL_EAXREVERB_LATE_REVERB_DELAY:
1681 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1682 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
1683 props->Reverb.LateReverbDelay = val;
1684 break;
1686 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1687 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1688 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
1689 props->Reverb.AirAbsorptionGainHF = val;
1690 break;
1692 case AL_EAXREVERB_ECHO_TIME:
1693 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1694 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
1695 props->Reverb.EchoTime = val;
1696 break;
1698 case AL_EAXREVERB_ECHO_DEPTH:
1699 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1700 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
1701 props->Reverb.EchoDepth = val;
1702 break;
1704 case AL_EAXREVERB_MODULATION_TIME:
1705 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1706 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
1707 props->Reverb.ModulationTime = val;
1708 break;
1710 case AL_EAXREVERB_MODULATION_DEPTH:
1711 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1712 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
1713 props->Reverb.ModulationDepth = val;
1714 break;
1716 case AL_EAXREVERB_HFREFERENCE:
1717 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1718 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
1719 props->Reverb.HFReference = val;
1720 break;
1722 case AL_EAXREVERB_LFREFERENCE:
1723 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1724 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
1725 props->Reverb.LFReference = val;
1726 break;
1728 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1729 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1730 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
1731 props->Reverb.RoomRolloffFactor = val;
1732 break;
1734 default:
1735 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1736 param);
1739 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1741 ALeffectProps *props = &effect->Props;
1742 switch(param)
1744 case AL_EAXREVERB_REFLECTIONS_PAN:
1745 if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2])))
1746 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
1747 props->Reverb.ReflectionsPan[0] = vals[0];
1748 props->Reverb.ReflectionsPan[1] = vals[1];
1749 props->Reverb.ReflectionsPan[2] = vals[2];
1750 break;
1751 case AL_EAXREVERB_LATE_REVERB_PAN:
1752 if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2])))
1753 SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
1754 props->Reverb.LateReverbPan[0] = vals[0];
1755 props->Reverb.LateReverbPan[1] = vals[1];
1756 props->Reverb.LateReverbPan[2] = vals[2];
1757 break;
1759 default:
1760 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1761 break;
1765 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1767 const ALeffectProps *props = &effect->Props;
1768 switch(param)
1770 case AL_EAXREVERB_DECAY_HFLIMIT:
1771 *val = props->Reverb.DecayHFLimit;
1772 break;
1774 default:
1775 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
1776 param);
1779 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1780 { ALeaxreverb_getParami(effect, context, param, vals); }
1781 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1783 const ALeffectProps *props = &effect->Props;
1784 switch(param)
1786 case AL_EAXREVERB_DENSITY:
1787 *val = props->Reverb.Density;
1788 break;
1790 case AL_EAXREVERB_DIFFUSION:
1791 *val = props->Reverb.Diffusion;
1792 break;
1794 case AL_EAXREVERB_GAIN:
1795 *val = props->Reverb.Gain;
1796 break;
1798 case AL_EAXREVERB_GAINHF:
1799 *val = props->Reverb.GainHF;
1800 break;
1802 case AL_EAXREVERB_GAINLF:
1803 *val = props->Reverb.GainLF;
1804 break;
1806 case AL_EAXREVERB_DECAY_TIME:
1807 *val = props->Reverb.DecayTime;
1808 break;
1810 case AL_EAXREVERB_DECAY_HFRATIO:
1811 *val = props->Reverb.DecayHFRatio;
1812 break;
1814 case AL_EAXREVERB_DECAY_LFRATIO:
1815 *val = props->Reverb.DecayLFRatio;
1816 break;
1818 case AL_EAXREVERB_REFLECTIONS_GAIN:
1819 *val = props->Reverb.ReflectionsGain;
1820 break;
1822 case AL_EAXREVERB_REFLECTIONS_DELAY:
1823 *val = props->Reverb.ReflectionsDelay;
1824 break;
1826 case AL_EAXREVERB_LATE_REVERB_GAIN:
1827 *val = props->Reverb.LateReverbGain;
1828 break;
1830 case AL_EAXREVERB_LATE_REVERB_DELAY:
1831 *val = props->Reverb.LateReverbDelay;
1832 break;
1834 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1835 *val = props->Reverb.AirAbsorptionGainHF;
1836 break;
1838 case AL_EAXREVERB_ECHO_TIME:
1839 *val = props->Reverb.EchoTime;
1840 break;
1842 case AL_EAXREVERB_ECHO_DEPTH:
1843 *val = props->Reverb.EchoDepth;
1844 break;
1846 case AL_EAXREVERB_MODULATION_TIME:
1847 *val = props->Reverb.ModulationTime;
1848 break;
1850 case AL_EAXREVERB_MODULATION_DEPTH:
1851 *val = props->Reverb.ModulationDepth;
1852 break;
1854 case AL_EAXREVERB_HFREFERENCE:
1855 *val = props->Reverb.HFReference;
1856 break;
1858 case AL_EAXREVERB_LFREFERENCE:
1859 *val = props->Reverb.LFReference;
1860 break;
1862 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1863 *val = props->Reverb.RoomRolloffFactor;
1864 break;
1866 default:
1867 alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
1868 param);
1871 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
1873 const ALeffectProps *props = &effect->Props;
1874 switch(param)
1876 case AL_EAXREVERB_REFLECTIONS_PAN:
1877 vals[0] = props->Reverb.ReflectionsPan[0];
1878 vals[1] = props->Reverb.ReflectionsPan[1];
1879 vals[2] = props->Reverb.ReflectionsPan[2];
1880 break;
1881 case AL_EAXREVERB_LATE_REVERB_PAN:
1882 vals[0] = props->Reverb.LateReverbPan[0];
1883 vals[1] = props->Reverb.LateReverbPan[1];
1884 vals[2] = props->Reverb.LateReverbPan[2];
1885 break;
1887 default:
1888 ALeaxreverb_getParamf(effect, context, param, vals);
1889 break;
1893 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
1895 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1897 ALeffectProps *props = &effect->Props;
1898 switch(param)
1900 case AL_REVERB_DECAY_HFLIMIT:
1901 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
1902 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
1903 props->Reverb.DecayHFLimit = val;
1904 break;
1906 default:
1907 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
1910 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1911 { ALreverb_setParami(effect, context, param, vals[0]); }
1912 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1914 ALeffectProps *props = &effect->Props;
1915 switch(param)
1917 case AL_REVERB_DENSITY:
1918 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
1919 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
1920 props->Reverb.Density = val;
1921 break;
1923 case AL_REVERB_DIFFUSION:
1924 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
1925 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
1926 props->Reverb.Diffusion = val;
1927 break;
1929 case AL_REVERB_GAIN:
1930 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
1931 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
1932 props->Reverb.Gain = val;
1933 break;
1935 case AL_REVERB_GAINHF:
1936 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
1937 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
1938 props->Reverb.GainHF = val;
1939 break;
1941 case AL_REVERB_DECAY_TIME:
1942 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
1943 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
1944 props->Reverb.DecayTime = val;
1945 break;
1947 case AL_REVERB_DECAY_HFRATIO:
1948 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
1949 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
1950 props->Reverb.DecayHFRatio = val;
1951 break;
1953 case AL_REVERB_REFLECTIONS_GAIN:
1954 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
1955 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
1956 props->Reverb.ReflectionsGain = val;
1957 break;
1959 case AL_REVERB_REFLECTIONS_DELAY:
1960 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
1961 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
1962 props->Reverb.ReflectionsDelay = val;
1963 break;
1965 case AL_REVERB_LATE_REVERB_GAIN:
1966 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
1967 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
1968 props->Reverb.LateReverbGain = val;
1969 break;
1971 case AL_REVERB_LATE_REVERB_DELAY:
1972 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
1973 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
1974 props->Reverb.LateReverbDelay = val;
1975 break;
1977 case AL_REVERB_AIR_ABSORPTION_GAINHF:
1978 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
1979 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
1980 props->Reverb.AirAbsorptionGainHF = val;
1981 break;
1983 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
1984 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
1985 SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
1986 props->Reverb.RoomRolloffFactor = val;
1987 break;
1989 default:
1990 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
1993 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1994 { ALreverb_setParamf(effect, context, param, vals[0]); }
1996 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1998 const ALeffectProps *props = &effect->Props;
1999 switch(param)
2001 case AL_REVERB_DECAY_HFLIMIT:
2002 *val = props->Reverb.DecayHFLimit;
2003 break;
2005 default:
2006 alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
2009 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
2010 { ALreverb_getParami(effect, context, param, vals); }
2011 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
2013 const ALeffectProps *props = &effect->Props;
2014 switch(param)
2016 case AL_REVERB_DENSITY:
2017 *val = props->Reverb.Density;
2018 break;
2020 case AL_REVERB_DIFFUSION:
2021 *val = props->Reverb.Diffusion;
2022 break;
2024 case AL_REVERB_GAIN:
2025 *val = props->Reverb.Gain;
2026 break;
2028 case AL_REVERB_GAINHF:
2029 *val = props->Reverb.GainHF;
2030 break;
2032 case AL_REVERB_DECAY_TIME:
2033 *val = props->Reverb.DecayTime;
2034 break;
2036 case AL_REVERB_DECAY_HFRATIO:
2037 *val = props->Reverb.DecayHFRatio;
2038 break;
2040 case AL_REVERB_REFLECTIONS_GAIN:
2041 *val = props->Reverb.ReflectionsGain;
2042 break;
2044 case AL_REVERB_REFLECTIONS_DELAY:
2045 *val = props->Reverb.ReflectionsDelay;
2046 break;
2048 case AL_REVERB_LATE_REVERB_GAIN:
2049 *val = props->Reverb.LateReverbGain;
2050 break;
2052 case AL_REVERB_LATE_REVERB_DELAY:
2053 *val = props->Reverb.LateReverbDelay;
2054 break;
2056 case AL_REVERB_AIR_ABSORPTION_GAINHF:
2057 *val = props->Reverb.AirAbsorptionGainHF;
2058 break;
2060 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
2061 *val = props->Reverb.RoomRolloffFactor;
2062 break;
2064 default:
2065 alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
2068 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
2069 { ALreverb_getParamf(effect, context, param, vals); }
2071 DEFINE_ALEFFECT_VTABLE(ALreverb);