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