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