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, which helps alter the perceived environment
86 * size. The size-to-density conversion is a cubed scale:
88 * density = min(1.0, pow(size, 3.0) / DENSITY_SCALE);
90 * The line lengths scale linearly with room size, so the inverse density
91 * conversion is needed, taking the cube root of the re-scaled density to
92 * calculate the line length multiplier:
94 * length_mult = max(5.0, cbrtf(density*DENSITY_SCALE));
96 * The density scale below will result in a max line multiplier of 50, for an
97 * effective size range of 5m to 50m.
99 static const ALfloat DENSITY_SCALE
= 125000.0f
;
101 /* All delay line lengths are specified in seconds.
103 * To approximate early reflections, we break them up into primary (those
104 * arriving from the same direction as the source) and secondary (those
105 * arriving from the opposite direction).
107 * The early taps decorrelate the 4-channel signal to approximate an average
108 * room response for the primary reflections after the initial early delay.
110 * Given an average room dimension (d_a) and the speed of sound (c) we can
111 * calculate the average reflection delay (r_a) regardless of listener and
112 * source positions as:
117 * This can extended to finding the average difference (r_d) between the
118 * maximum (r_1) and minimum (r_0) reflection delays:
129 * As can be determined by integrating the 1D model with a source (s) and
130 * listener (l) positioned across the dimension of length (d_a):
132 * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
134 * The initial taps (T_(i=0)^N) are then specified by taking a power series
135 * that ranges between r_0 and half of r_1 less r_0:
137 * R_i = 2^(i / (2 N - 1)) r_d
138 * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
141 * = (2^(i / (2 N - 1)) - 1) r_d
143 * Assuming an average of 1m, we get the following taps:
145 static const ALfloat EARLY_TAP_LENGTHS
[NUM_LINES
] =
147 0.0000000e+0f
, 2.0213520e-4f
, 4.2531060e-4f
, 6.7171600e-4f
150 /* The early all-pass filter lengths are based on the early tap lengths:
154 * Where a is the approximate maximum all-pass cycle limit (20).
156 static const ALfloat EARLY_ALLPASS_LENGTHS
[NUM_LINES
] =
158 9.7096800e-5f
, 1.0720356e-4f
, 1.1836234e-4f
, 1.3068260e-4f
161 /* The early delay lines are used to transform the primary reflections into
162 * the secondary reflections. The A-format is arranged in such a way that
163 * the channels/lines are spatially opposite:
165 * C_i is opposite C_(N-i-1)
167 * The delays of the two opposing reflections (R_i and O_i) from a source
168 * anywhere along a particular dimension always sum to twice its full delay:
172 * With that in mind we can determine the delay between the two reflections
173 * and thus specify our early line lengths (L_(i=0)^N) using:
175 * O_i = 2 r_a - R_(N-i-1)
176 * L_i = O_i - R_(N-i-1)
177 * = 2 (r_a - R_(N-i-1))
178 * = 2 (r_a - T_(N-i-1) - r_0)
179 * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
181 * Using an average dimension of 1m, we get:
183 static const ALfloat EARLY_LINE_LENGTHS
[NUM_LINES
] =
185 5.9850400e-4f
, 1.0913150e-3f
, 1.5376658e-3f
, 1.9419362e-3f
188 /* The late all-pass filter lengths are based on the late line lengths:
190 * A_i = (5 / 3) L_i / r_1
192 static const ALfloat LATE_ALLPASS_LENGTHS
[NUM_LINES
] =
194 1.6182800e-4f
, 2.0389060e-4f
, 2.8159360e-4f
, 3.2365600e-4f
197 /* The late lines are used to approximate the decaying cycle of recursive
200 * Splitting the lines in half, we start with the shortest reflection paths
203 * L_i = 2^(i / (N - 1)) r_d
205 * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
207 * L_i = 2 r_a - L_(i-N/2)
208 * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
210 * For our 1m average room, we get:
212 static const ALfloat LATE_LINE_LENGTHS
[NUM_LINES
] =
214 1.9419362e-3f
, 2.4466860e-3f
, 3.3791220e-3f
, 3.8838720e-3f
218 typedef struct DelayLineI
{
219 /* The delay lines use interleaved samples, with the lengths being powers
220 * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
223 ALfloat (*Line
)[NUM_LINES
];
226 typedef struct VecAllpass
{
228 ALsizei Offset
[NUM_LINES
][2];
231 typedef struct T60Filter
{
232 /* Two filters are used to adjust the signal. One to control the low
233 * frequencies, and one to control the high frequencies. The HF filter also
234 * adjusts the overall output gain, affecting the remaining mid-band.
239 /* The HF and LF filters each keep a state of the last input and last
246 typedef struct EarlyReflections
{
247 /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
248 * The spread from this filter also helps smooth out the reverb tail.
252 /* An echo line is used to complete the second half of the early
256 ALsizei Offset
[NUM_LINES
][2];
257 ALfloat Coeff
[NUM_LINES
];
259 /* The gain for each output channel based on 3D panning. */
260 ALfloat CurrentGain
[NUM_LINES
][MAX_OUTPUT_CHANNELS
];
261 ALfloat PanGain
[NUM_LINES
][MAX_OUTPUT_CHANNELS
];
264 typedef struct LateReverb
{
265 /* Attenuation to compensate for the modal density and decay rate of the
270 /* A recursive delay line is used fill in the reverb tail. */
272 ALsizei Offset
[NUM_LINES
][2];
274 /* T60 decay filters are used to simulate absorption. */
275 T60Filter T60
[NUM_LINES
];
277 /* A Gerzon vector all-pass filter is used to simulate diffusion. */
280 /* The gain for each output channel based on 3D panning. */
281 ALfloat CurrentGain
[NUM_LINES
][MAX_OUTPUT_CHANNELS
];
282 ALfloat PanGain
[NUM_LINES
][MAX_OUTPUT_CHANNELS
];
285 typedef struct ALreverbState
{
286 DERIVE_FROM_TYPE(ALeffectState
);
288 /* All delay lines are allocated as a single buffer to reduce memory
289 * fragmentation and management code.
291 ALfloat
*SampleBuffer
;
294 /* Master effect filters */
300 /* Core delay line (early reflections and late reverb tap from this). */
303 /* Tap points for early reflection delay. */
304 ALsizei EarlyDelayTap
[NUM_LINES
][2];
305 ALfloat EarlyDelayCoeff
[NUM_LINES
];
307 /* Tap points for late reverb feed and delay. */
309 ALsizei LateDelayTap
[NUM_LINES
][2];
311 /* The feed-back and feed-forward all-pass coefficient. */
314 /* Coefficients for the all-pass and line scattering matrices. */
318 EarlyReflections Early
;
322 /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
325 /* The current write offset for all delay lines. */
328 /* Temporary storage used when processing. */
329 alignas(16) ALfloat AFormatSamples
[NUM_LINES
][MAX_UPDATE_SAMPLES
];
330 alignas(16) ALfloat ReverbSamples
[NUM_LINES
][MAX_UPDATE_SAMPLES
];
331 alignas(16) ALfloat EarlySamples
[NUM_LINES
][MAX_UPDATE_SAMPLES
];
334 static ALvoid
ALreverbState_Destruct(ALreverbState
*State
);
335 static ALboolean
ALreverbState_deviceUpdate(ALreverbState
*State
, ALCdevice
*Device
);
336 static ALvoid
ALreverbState_update(ALreverbState
*State
, const ALCcontext
*Context
, const ALeffectslot
*Slot
, const ALeffectProps
*props
);
337 static ALvoid
ALreverbState_process(ALreverbState
*State
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
);
338 DECLARE_DEFAULT_ALLOCATORS(ALreverbState
)
340 DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState
);
342 static void ALreverbState_Construct(ALreverbState
*state
)
346 ALeffectState_Construct(STATIC_CAST(ALeffectState
, state
));
347 SET_VTABLE2(ALreverbState
, ALeffectState
, state
);
349 state
->TotalSamples
= 0;
350 state
->SampleBuffer
= NULL
;
352 for(i
= 0;i
< NUM_LINES
;i
++)
354 ALfilterState_clear(&state
->Filter
[i
].Lp
);
355 ALfilterState_clear(&state
->Filter
[i
].Hp
);
358 state
->Delay
.Mask
= 0;
359 state
->Delay
.Line
= NULL
;
361 for(i
= 0;i
< NUM_LINES
;i
++)
363 state
->EarlyDelayTap
[i
][0] = 0;
364 state
->EarlyDelayTap
[i
][1] = 0;
365 state
->EarlyDelayCoeff
[i
] = 0.0f
;
368 state
->LateFeedTap
= 0;
370 for(i
= 0;i
< NUM_LINES
;i
++)
372 state
->LateDelayTap
[i
][0] = 0;
373 state
->LateDelayTap
[i
][1] = 0;
376 state
->ApFeedCoeff
= 0.0f
;
380 state
->Early
.VecAp
.Delay
.Mask
= 0;
381 state
->Early
.VecAp
.Delay
.Line
= NULL
;
382 state
->Early
.Delay
.Mask
= 0;
383 state
->Early
.Delay
.Line
= NULL
;
384 for(i
= 0;i
< NUM_LINES
;i
++)
386 state
->Early
.VecAp
.Offset
[i
][0] = 0;
387 state
->Early
.VecAp
.Offset
[i
][1] = 0;
388 state
->Early
.Offset
[i
][0] = 0;
389 state
->Early
.Offset
[i
][1] = 0;
390 state
->Early
.Coeff
[i
] = 0.0f
;
393 state
->Late
.DensityGain
= 0.0f
;
395 state
->Late
.Delay
.Mask
= 0;
396 state
->Late
.Delay
.Line
= NULL
;
397 state
->Late
.VecAp
.Delay
.Mask
= 0;
398 state
->Late
.VecAp
.Delay
.Line
= NULL
;
399 for(i
= 0;i
< NUM_LINES
;i
++)
401 state
->Late
.Offset
[i
][0] = 0;
402 state
->Late
.Offset
[i
][1] = 0;
404 state
->Late
.VecAp
.Offset
[i
][0] = 0;
405 state
->Late
.VecAp
.Offset
[i
][1] = 0;
409 state
->Late
.T60
[i
].HFCoeffs
[j
] = 0.0f
;
410 state
->Late
.T60
[i
].LFCoeffs
[j
] = 0.0f
;
412 state
->Late
.T60
[i
].HFState
[0] = 0.0f
;
413 state
->Late
.T60
[i
].HFState
[1] = 0.0f
;
414 state
->Late
.T60
[i
].LFState
[0] = 0.0f
;
415 state
->Late
.T60
[i
].LFState
[1] = 0.0f
;
418 for(i
= 0;i
< NUM_LINES
;i
++)
420 for(j
= 0;j
< MAX_OUTPUT_CHANNELS
;j
++)
422 state
->Early
.CurrentGain
[i
][j
] = 0.0f
;
423 state
->Early
.PanGain
[i
][j
] = 0.0f
;
424 state
->Late
.CurrentGain
[i
][j
] = 0.0f
;
425 state
->Late
.PanGain
[i
][j
] = 0.0f
;
429 state
->FadeCount
= 0;
433 static ALvoid
ALreverbState_Destruct(ALreverbState
*State
)
435 al_free(State
->SampleBuffer
);
436 State
->SampleBuffer
= NULL
;
438 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,State
));
441 /**************************************
443 **************************************/
445 static inline ALfloat
CalcDelayLengthMult(ALfloat density
)
447 return maxf(5.0f
, cbrtf(density
*DENSITY_SCALE
));
450 /* Given the allocated sample buffer, this function updates each delay line
453 static inline ALvoid
RealizeLineOffset(ALfloat
*sampleBuffer
, DelayLineI
*Delay
)
457 ALfloat (*f4
)[NUM_LINES
];
459 u
.f
= &sampleBuffer
[(ptrdiff_t)Delay
->Line
* NUM_LINES
];
463 /* Calculate the length of a delay line and store its mask and offset. */
464 static ALuint
CalcLineLength(const ALfloat length
, const ptrdiff_t offset
, const ALuint frequency
,
465 const ALuint extra
, DelayLineI
*Delay
)
469 /* All line lengths are powers of 2, calculated from their lengths in
470 * seconds, rounded up.
472 samples
= fastf2i(ceilf(length
*frequency
));
473 samples
= NextPowerOf2(samples
+ extra
);
475 /* All lines share a single sample buffer. */
476 Delay
->Mask
= samples
- 1;
477 Delay
->Line
= (ALfloat(*)[NUM_LINES
])offset
;
479 /* Return the sample count for accumulation. */
483 /* Calculates the delay line metrics and allocates the shared sample buffer
484 * for all lines given the sample rate (frequency). If an allocation failure
485 * occurs, it returns AL_FALSE.
487 static ALboolean
AllocLines(const ALuint frequency
, ALreverbState
*State
)
489 ALuint totalSamples
, i
;
490 ALfloat multiplier
, length
;
492 /* All delay line lengths are calculated to accomodate the full range of
493 * lengths given their respective paramters.
497 /* Multiplier for the maximum density value, i.e. density=1, which is
498 * actually the least density...
500 multiplier
= CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY
);
502 /* The main delay length includes the maximum early reflection delay, the
503 * largest early tap width, the maximum late reverb delay, and the
504 * largest late tap width. Finally, it must also be extended by the
505 * update size (MAX_UPDATE_SAMPLES) for block processing.
507 length
= AL_EAXREVERB_MAX_REFLECTIONS_DELAY
+ EARLY_TAP_LENGTHS
[NUM_LINES
-1]*multiplier
+
508 AL_EAXREVERB_MAX_LATE_REVERB_DELAY
+
509 (LATE_LINE_LENGTHS
[NUM_LINES
-1] - LATE_LINE_LENGTHS
[0])*0.25f
*multiplier
;
510 totalSamples
+= CalcLineLength(length
, totalSamples
, frequency
, MAX_UPDATE_SAMPLES
,
513 /* The early vector all-pass line. */
514 length
= EARLY_ALLPASS_LENGTHS
[NUM_LINES
-1] * multiplier
;
515 totalSamples
+= CalcLineLength(length
, totalSamples
, frequency
, 0,
516 &State
->Early
.VecAp
.Delay
);
518 /* The early reflection line. */
519 length
= EARLY_LINE_LENGTHS
[NUM_LINES
-1] * multiplier
;
520 totalSamples
+= CalcLineLength(length
, totalSamples
, frequency
, 0,
521 &State
->Early
.Delay
);
523 /* The late vector all-pass line. */
524 length
= LATE_ALLPASS_LENGTHS
[NUM_LINES
-1] * multiplier
;
525 totalSamples
+= CalcLineLength(length
, totalSamples
, frequency
, 0,
526 &State
->Late
.VecAp
.Delay
);
528 /* The late delay lines are calculated from the larger of the maximum
529 * density line length or the maximum echo time.
531 length
= maxf(AL_EAXREVERB_MAX_ECHO_TIME
, LATE_LINE_LENGTHS
[NUM_LINES
-1]*multiplier
);
532 totalSamples
+= CalcLineLength(length
, totalSamples
, frequency
, 0,
535 if(totalSamples
!= State
->TotalSamples
)
539 TRACE("New reverb buffer length: %ux4 samples\n", totalSamples
);
540 newBuffer
= al_calloc(16, sizeof(ALfloat
[NUM_LINES
]) * totalSamples
);
541 if(!newBuffer
) return AL_FALSE
;
543 al_free(State
->SampleBuffer
);
544 State
->SampleBuffer
= newBuffer
;
545 State
->TotalSamples
= totalSamples
;
548 /* Update all delays to reflect the new sample buffer. */
549 RealizeLineOffset(State
->SampleBuffer
, &State
->Delay
);
550 RealizeLineOffset(State
->SampleBuffer
, &State
->Early
.VecAp
.Delay
);
551 RealizeLineOffset(State
->SampleBuffer
, &State
->Early
.Delay
);
552 RealizeLineOffset(State
->SampleBuffer
, &State
->Late
.VecAp
.Delay
);
553 RealizeLineOffset(State
->SampleBuffer
, &State
->Late
.Delay
);
555 /* Clear the sample buffer. */
556 for(i
= 0;i
< State
->TotalSamples
;i
++)
557 State
->SampleBuffer
[i
] = 0.0f
;
562 static ALboolean
ALreverbState_deviceUpdate(ALreverbState
*State
, ALCdevice
*Device
)
564 ALuint frequency
= Device
->Frequency
;
567 /* Allocate the delay lines. */
568 if(!AllocLines(frequency
, State
))
571 multiplier
= CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY
);
573 /* The late feed taps are set a fixed position past the latest delay tap. */
574 State
->LateFeedTap
= fastf2i((AL_EAXREVERB_MAX_REFLECTIONS_DELAY
+
575 EARLY_TAP_LENGTHS
[NUM_LINES
-1]*multiplier
) *
581 /**************************************
583 **************************************/
585 /* Calculate a decay coefficient given the length of each cycle and the time
586 * until the decay reaches -60 dB.
588 static inline ALfloat
CalcDecayCoeff(const ALfloat length
, const ALfloat decayTime
)
590 return powf(REVERB_DECAY_GAIN
, length
/decayTime
);
593 /* Calculate a decay length from a coefficient and the time until the decay
596 static inline ALfloat
CalcDecayLength(const ALfloat coeff
, const ALfloat decayTime
)
598 return log10f(coeff
) * decayTime
/ log10f(REVERB_DECAY_GAIN
);
601 /* Calculate an attenuation to be applied to the input of any echo models to
602 * compensate for modal density and decay time.
604 static inline ALfloat
CalcDensityGain(const ALfloat a
)
606 /* The energy of a signal can be obtained by finding the area under the
607 * squared signal. This takes the form of Sum(x_n^2), where x is the
608 * amplitude for the sample n.
610 * Decaying feedback matches exponential decay of the form Sum(a^n),
611 * where a is the attenuation coefficient, and n is the sample. The area
612 * under this decay curve can be calculated as: 1 / (1 - a).
614 * Modifying the above equation to find the area under the squared curve
615 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
616 * calculated by inverting the square root of this approximation,
617 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
619 return sqrtf(1.0f
- a
*a
);
622 /* Calculate the scattering matrix coefficients given a diffusion factor. */
623 static inline ALvoid
CalcMatrixCoeffs(const ALfloat diffusion
, ALfloat
*x
, ALfloat
*y
)
627 /* The matrix is of order 4, so n is sqrt(4 - 1). */
629 t
= diffusion
* atanf(n
);
631 /* Calculate the first mixing matrix coefficient. */
633 /* Calculate the second mixing matrix coefficient. */
637 /* Calculate the limited HF ratio for use with the late reverb low-pass
640 static ALfloat
CalcLimitedHfRatio(const ALfloat hfRatio
, const ALfloat airAbsorptionGainHF
,
641 const ALfloat decayTime
, const ALfloat SpeedOfSound
)
645 /* Find the attenuation due to air absorption in dB (converting delay
646 * time to meters using the speed of sound). Then reversing the decay
647 * equation, solve for HF ratio. The delay length is cancelled out of
648 * the equation, so it can be calculated once for all lines.
650 limitRatio
= 1.0f
/ (CalcDecayLength(airAbsorptionGainHF
, decayTime
) * SpeedOfSound
);
652 /* Using the limit calculated above, apply the upper bound to the HF ratio.
654 return minf(limitRatio
, hfRatio
);
657 /* Calculates the first-order high-pass coefficients following the I3DL2
658 * reference model. This is the transfer function:
661 * H(z) = p ------------
664 * And this is the I3DL2 coefficient calculation given gain (g) and reference
665 * angular frequency (w):
668 * p = ------------------------------------------------------
669 * g cos(w) + sqrt((cos(w) - 1) (g^2 cos(w) + g^2 - 2))
671 * The coefficient is applied to the partial differential filter equation as:
676 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
679 static inline void CalcHighpassCoeffs(const ALfloat gain
, const ALfloat w
, ALfloat coeffs
[3])
681 ALfloat g
, g2
, cw
, p
;
691 g
= maxf(0.001f
, gain
);
694 p
= g
/ (g
*cw
+ sqrtf((cw
- 1.0f
) * (g2
*cw
+ g2
- 2.0f
)));
701 /* Calculates the first-order low-pass coefficients following the I3DL2
702 * reference model. This is the transfer function:
705 * H(z) = ----------------
708 * And this is the I3DL2 coefficient calculation given gain (g) and reference
709 * angular frequency (w):
711 * 1 - g^2 cos(w) - sqrt(2 g^2 (1 - cos(w)) - g^4 (1 - cos(w)^2))
712 * a = ----------------------------------------------------------------
715 * The coefficient is applied to the partial differential filter equation as:
720 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
723 static inline void CalcLowpassCoeffs(const ALfloat gain
, const ALfloat w
, ALfloat coeffs
[3])
725 ALfloat g
, g2
, cw
, a
;
735 /* Be careful with gains < 0.001, as that causes the coefficient
736 * to head towards 1, which will flatten the signal. */
737 g
= maxf(0.001f
, gain
);
740 a
= (1.0f
- g2
*cw
- sqrtf((2.0f
*g2
*(1.0f
- cw
)) - g2
*g2
*(1.0f
- cw
*cw
))) /
743 coeffs
[0] = 1.0f
- a
;
748 /* Calculates the first-order low-shelf coefficients. The shelf filters are
749 * used in place of low/high-pass filters to preserve the mid-band. This is
750 * the transfer function:
753 * H(z) = ----------------
756 * And these are the coefficient calculations given cut gain (g) and a center
757 * angular frequency (w):
759 * sin(0.5 (pi - w) - 0.25 pi)
760 * p = -----------------------------
761 * sin(0.5 (pi - w) + 0.25 pi)
764 * a = ------- + sqrt((-------)^2 - 1)
768 * b_0 = -------------------
772 * b_1 = -------------------
775 * The coefficients are applied to the partial differential filter equation
779 * c_0 = -------------
783 * c_1 = ----------------
790 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
793 static inline void CalcLowShelfCoeffs(const ALfloat gain
, const ALfloat w
, ALfloat coeffs
[3])
796 ALfloat alpha
, beta0
, beta1
;
806 g
= maxf(0.001f
, gain
);
808 p
= sinf(0.5f
*rw
- 0.25f
*F_PI
) / sinf(0.5f
*rw
+ 0.25f
*F_PI
);
809 n
= (g
+ 1.0f
) / (g
- 1.0f
);
810 alpha
= n
+ sqrtf(n
*n
- 1.0f
);
811 beta0
= (1.0f
+ g
+ (1.0f
- g
)*alpha
) / 2.0f
;
812 beta1
= (1.0f
- g
+ (1.0f
+ g
)*alpha
) / 2.0f
;
814 coeffs
[0] = (beta0
+ p
*beta1
) / (1.0f
+ p
*alpha
);
815 coeffs
[1] = -(beta1
+ p
*beta0
) / (1.0f
+ p
*alpha
);
816 coeffs
[2] = (p
+ alpha
) / (1.0f
+ p
*alpha
);
819 /* Calculates the first-order high-shelf coefficients. The shelf filters are
820 * used in place of low/high-pass filters to preserve the mid-band. This is
821 * the transfer function:
824 * H(z) = ----------------
827 * And these are the coefficient calculations given cut gain (g) and a center
828 * angular frequency (w):
830 * sin(0.5 w - 0.25 pi)
831 * p = ----------------------
832 * sin(0.5 w + 0.25 pi)
835 * a = ------- + sqrt((-------)^2 - 1)
839 * b_0 = -------------------
843 * b_1 = -------------------
846 * The coefficients are applied to the partial differential filter equation
850 * c_0 = -------------
854 * c_1 = -------------
861 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
864 static inline void CalcHighShelfCoeffs(const ALfloat gain
, const ALfloat w
, ALfloat coeffs
[3])
867 ALfloat alpha
, beta0
, beta1
;
877 g
= maxf(0.001f
, gain
);
878 p
= sinf(0.5f
*w
- 0.25f
*F_PI
) / sinf(0.5f
*w
+ 0.25f
*F_PI
);
879 n
= (g
+ 1.0f
) / (g
- 1.0f
);
880 alpha
= n
+ sqrtf(n
*n
- 1.0f
);
881 beta0
= (1.0f
+ g
+ (1.0f
- g
)*alpha
) / 2.0f
;
882 beta1
= (1.0f
- g
+ (1.0f
+ g
)*alpha
) / 2.0f
;
884 coeffs
[0] = (beta0
+ p
*beta1
) / (1.0f
+ p
*alpha
);
885 coeffs
[1] = (beta1
+ p
*beta0
) / (1.0f
+ p
*alpha
);
886 coeffs
[2] = -(p
+ alpha
) / (1.0f
+ p
*alpha
);
889 /* Calculates the 3-band T60 damping coefficients for a particular delay line
890 * of specified length using a combination of two low/high-pass/shelf or
891 * pass-through filter sections (producing 3 coefficients each) given decay
892 * times for each band split at two (LF/HF) reference frequencies (w).
894 static void CalcT60DampingCoeffs(const ALfloat length
, const ALfloat lfDecayTime
,
895 const ALfloat mfDecayTime
, const ALfloat hfDecayTime
,
896 const ALfloat lfW
, const ALfloat hfW
, ALfloat lfcoeffs
[3],
899 ALfloat lfGain
= CalcDecayCoeff(length
, lfDecayTime
);
900 ALfloat mfGain
= CalcDecayCoeff(length
, mfDecayTime
);
901 ALfloat hfGain
= CalcDecayCoeff(length
, hfDecayTime
);
905 CalcHighpassCoeffs(lfGain
/ mfGain
, lfW
, lfcoeffs
);
908 CalcLowpassCoeffs(hfGain
/ mfGain
, hfW
, hfcoeffs
);
909 hfcoeffs
[0] *= mfGain
; hfcoeffs
[1] *= mfGain
;
913 CalcLowShelfCoeffs(mfGain
/ hfGain
, hfW
, hfcoeffs
);
914 hfcoeffs
[0] *= hfGain
; hfcoeffs
[1] *= hfGain
;
919 CalcHighShelfCoeffs(mfGain
/ lfGain
, lfW
, lfcoeffs
);
922 CalcLowpassCoeffs(hfGain
/ mfGain
, hfW
, hfcoeffs
);
923 hfcoeffs
[0] *= lfGain
; hfcoeffs
[1] *= lfGain
;
927 ALfloat hg
= mfGain
/ lfGain
;
928 ALfloat lg
= mfGain
/ hfGain
;
929 ALfloat mg
= maxf(lfGain
, hfGain
) / maxf(hg
, lg
);
931 CalcLowShelfCoeffs(lg
, hfW
, hfcoeffs
);
932 hfcoeffs
[0] *= mg
; hfcoeffs
[1] *= mg
;
937 /* Update the offsets for the main effect delay line. */
938 static ALvoid
UpdateDelayLine(const ALfloat earlyDelay
, const ALfloat lateDelay
, const ALfloat density
, const ALfloat decayTime
, const ALuint frequency
, ALreverbState
*State
)
940 ALfloat multiplier
, length
;
943 multiplier
= CalcDelayLengthMult(density
);
945 /* Early reflection taps are decorrelated by means of an average room
946 * reflection approximation described above the definition of the taps.
947 * This approximation is linear and so the above density multiplier can
948 * be applied to adjust the width of the taps. A single-band decay
949 * coefficient is applied to simulate initial attenuation and absorption.
951 * Late reverb taps are based on the late line lengths to allow a zero-
952 * delay path and offsets that would continue the propagation naturally
953 * into the late lines.
955 for(i
= 0;i
< NUM_LINES
;i
++)
957 length
= earlyDelay
+ EARLY_TAP_LENGTHS
[i
]*multiplier
;
958 State
->EarlyDelayTap
[i
][1] = fastf2i(length
* frequency
);
960 length
= EARLY_TAP_LENGTHS
[i
]*multiplier
;
961 State
->EarlyDelayCoeff
[i
] = CalcDecayCoeff(length
, decayTime
);
963 length
= lateDelay
+ (LATE_LINE_LENGTHS
[i
] - LATE_LINE_LENGTHS
[0])*0.25f
*multiplier
;
964 State
->LateDelayTap
[i
][1] = State
->LateFeedTap
+ fastf2i(length
* frequency
);
968 /* Update the early reflection line lengths and gain coefficients. */
969 static ALvoid
UpdateEarlyLines(const ALfloat density
, const ALfloat decayTime
, const ALuint frequency
, EarlyReflections
*Early
)
971 ALfloat multiplier
, length
;
974 multiplier
= CalcDelayLengthMult(density
);
976 for(i
= 0;i
< NUM_LINES
;i
++)
978 /* Calculate the length (in seconds) of each all-pass line. */
979 length
= EARLY_ALLPASS_LENGTHS
[i
] * multiplier
;
981 /* Calculate the delay offset for each all-pass line. */
982 Early
->VecAp
.Offset
[i
][1] = fastf2i(length
* frequency
);
984 /* Calculate the length (in seconds) of each delay line. */
985 length
= EARLY_LINE_LENGTHS
[i
] * multiplier
;
987 /* Calculate the delay offset for each delay line. */
988 Early
->Offset
[i
][1] = fastf2i(length
* frequency
);
990 /* Calculate the gain (coefficient) for each line. */
991 Early
->Coeff
[i
] = CalcDecayCoeff(length
, decayTime
);
995 /* Update the late reverb line lengths and T60 coefficients. */
996 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
)
998 ALfloat multiplier
, length
, bandWeights
[3];
1001 /* To compensate for changes in modal density and decay time of the late
1002 * reverb signal, the input is attenuated based on the maximal energy of
1003 * the outgoing signal. This approximation is used to keep the apparent
1004 * energy of the signal equal for all ranges of density and decay time.
1006 * The average length of the delay lines is used to calculate the
1007 * attenuation coefficient.
1009 multiplier
= CalcDelayLengthMult(density
);
1010 length
= (LATE_LINE_LENGTHS
[0] + LATE_LINE_LENGTHS
[1] +
1011 LATE_LINE_LENGTHS
[2] + LATE_LINE_LENGTHS
[3]) / 4.0f
* multiplier
;
1012 /* Include the echo transformation (see below). */
1013 length
= lerp(length
, echoTime
, echoDepth
);
1014 length
+= (LATE_ALLPASS_LENGTHS
[0] + LATE_ALLPASS_LENGTHS
[1] +
1015 LATE_ALLPASS_LENGTHS
[2] + LATE_ALLPASS_LENGTHS
[3]) / 4.0f
* multiplier
;
1016 /* The density gain calculation uses an average decay time weighted by
1017 * approximate bandwidth. This attempts to compensate for losses of
1018 * energy that reduce decay time due to scattering into highly attenuated
1021 bandWeights
[0] = lfW
;
1022 bandWeights
[1] = hfW
- lfW
;
1023 bandWeights
[2] = F_TAU
- hfW
;
1024 Late
->DensityGain
= CalcDensityGain(
1025 CalcDecayCoeff(length
, (bandWeights
[0]*lfDecayTime
+ bandWeights
[1]*mfDecayTime
+
1026 bandWeights
[2]*hfDecayTime
) / F_TAU
)
1029 for(i
= 0;i
< NUM_LINES
;i
++)
1031 /* Calculate the length (in seconds) of each all-pass line. */
1032 length
= LATE_ALLPASS_LENGTHS
[i
] * multiplier
;
1034 /* Calculate the delay offset for each all-pass line. */
1035 Late
->VecAp
.Offset
[i
][1] = fastf2i(length
* frequency
);
1037 /* Calculate the length (in seconds) of each delay line. This also
1038 * applies the echo transformation. As the EAX echo depth approaches
1039 * 1, the line lengths approach a length equal to the echoTime. This
1040 * helps to produce distinct echoes along the tail.
1042 length
= lerp(LATE_LINE_LENGTHS
[i
] * multiplier
, echoTime
, echoDepth
);
1044 /* Calculate the delay offset for each delay line. */
1045 Late
->Offset
[i
][1] = fastf2i(length
*frequency
+ 0.5f
);
1047 /* Approximate the absorption that the vector all-pass would exhibit
1048 * given the current diffusion so we don't have to process a full T60
1049 * filter for each of its four lines.
1051 length
+= lerp(LATE_ALLPASS_LENGTHS
[i
],
1052 (LATE_ALLPASS_LENGTHS
[0] + LATE_ALLPASS_LENGTHS
[1] +
1053 LATE_ALLPASS_LENGTHS
[2] + LATE_ALLPASS_LENGTHS
[3]) / 4.0f
,
1054 diffusion
) * multiplier
;
1056 /* Calculate the T60 damping coefficients for each line. */
1057 CalcT60DampingCoeffs(length
, lfDecayTime
, mfDecayTime
, hfDecayTime
,
1058 lfW
, hfW
, Late
->T60
[i
].LFCoeffs
,
1059 Late
->T60
[i
].HFCoeffs
);
1063 /* Creates a transform matrix given a reverb vector. This works by first
1064 * creating an inverse rotation around Y then X, applying a Z-focus transform,
1065 * then non-inverse rotations back around X then Y, to place the focal point in
1066 * the direction of the vector, using the vector length as a focus strength.
1068 * This convoluted construction ultimately results in a B-Format transformation
1069 * matrix that retains its original orientation, but spatially focuses the
1070 * signal in the desired direction. There is probably a more efficient way to
1071 * do this, but let's see how good the optimizer is.
1073 static aluMatrixf
GetTransformFromVector(const ALfloat
*vec
)
1075 const ALfloat sqrt_3
= 1.732050808f
;
1076 aluMatrixf zfocus
, xrot
, yrot
;
1077 aluMatrixf tmp1
, tmp2
;
1081 length
= sqrtf(vec
[0]*vec
[0] + vec
[1]*vec
[1] + vec
[2]*vec
[2]);
1083 /* Define a Z-focus (X in Ambisonics) transform, given the panning vector
1086 sa
= sinf(minf(length
, 1.0f
) * (F_PI
/2.0f
));
1087 aluMatrixfSet(&zfocus
,
1088 1.0f
/(1.0f
+sa
), 0.0f
, 0.0f
, sa
/(1.0f
+sa
)/sqrt_3
,
1089 0.0f
, sqrtf((1.0f
-sa
)/(1.0f
+sa
)), 0.0f
, 0.0f
,
1090 0.0f
, 0.0f
, sqrtf((1.0f
-sa
)/(1.0f
+sa
)), 0.0f
,
1091 sa
/(1.0f
+sa
)*sqrt_3
, 0.0f
, 0.0f
, 1.0f
/(1.0f
+sa
)
1094 /* Define rotation around X (Y in Ambisonics) */
1095 a
= atan2f(vec
[1], sqrtf(vec
[0]*vec
[0] + vec
[2]*vec
[2]));
1096 aluMatrixfSet(&xrot
,
1097 1.0f
, 0.0f
, 0.0f
, 0.0f
,
1098 0.0f
, 1.0f
, 0.0f
, 0.0f
,
1099 0.0f
, 0.0f
, cosf(a
), sinf(a
),
1100 0.0f
, 0.0f
, -sinf(a
), cosf(a
)
1103 /* Define rotation around Y (Z in Ambisonics). NOTE: EFX's reverb vectors
1104 * use a right-handled coordinate system, compared to the rest of OpenAL
1105 * which uses left-handed. This is fixed by negating Z, however it would
1106 * need to also be negated to get a proper Ambisonics angle, thus
1107 * cancelling it out.
1109 a
= atan2f(-vec
[0], vec
[2]);
1110 aluMatrixfSet(&yrot
,
1111 1.0f
, 0.0f
, 0.0f
, 0.0f
,
1112 0.0f
, cosf(a
), 0.0f
, sinf(a
),
1113 0.0f
, 0.0f
, 1.0f
, 0.0f
,
1114 0.0f
, -sinf(a
), 0.0f
, cosf(a
)
1117 /* First, define a matrix that applies the inverse of the Y- then X-
1118 * rotation matrices, so that the desired direction lands on Z.
1120 #define MATRIX_INVMULT(_res, _m1, _m2) do { \
1122 for(col = 0;col < 4;col++) \
1124 for(row = 0;row < 4;row++) \
1125 _res.m[row][col] = _m1.m[0][row]*_m2.m[col][0] + \
1126 _m1.m[1][row]*_m2.m[col][1] + \
1127 _m1.m[2][row]*_m2.m[col][2] + \
1128 _m1.m[3][row]*_m2.m[col][3]; \
1131 MATRIX_INVMULT(tmp1
, xrot
, yrot
);
1132 #undef MATRIX_INVMULT
1134 #define MATRIX_MULT(_res, _m1, _m2) do { \
1136 for(col = 0;col < 4;col++) \
1138 for(row = 0;row < 4;row++) \
1139 _res.m[row][col] = _m1.m[row][0]*_m2.m[0][col] + \
1140 _m1.m[row][1]*_m2.m[1][col] + \
1141 _m1.m[row][2]*_m2.m[2][col] + \
1142 _m1.m[row][3]*_m2.m[3][col]; \
1145 /* Now apply matrices to focus on Z, then rotate back around X then Y, to
1146 * result in a focus in the direction of the vector.
1148 MATRIX_MULT(tmp2
, zfocus
, tmp1
);
1149 MATRIX_MULT(tmp1
, xrot
, tmp2
);
1150 MATRIX_MULT(tmp2
, yrot
, tmp1
);
1156 /* Update the early and late 3D panning gains. */
1157 static ALvoid
Update3DPanning(const ALCdevice
*Device
, const ALfloat
*ReflectionsPan
, const ALfloat
*LateReverbPan
, const ALfloat gain
, const ALfloat earlyGain
, const ALfloat lateGain
, ALreverbState
*State
)
1159 aluMatrixf transform
, rot
;
1162 STATIC_CAST(ALeffectState
,State
)->OutBuffer
= Device
->FOAOut
.Buffer
;
1163 STATIC_CAST(ALeffectState
,State
)->OutChannels
= Device
->FOAOut
.NumChannels
;
1165 /* Note: _res is transposed. */
1166 #define MATRIX_MULT(_res, _m1, _m2) do { \
1168 for(col = 0;col < 4;col++) \
1170 for(row = 0;row < 4;row++) \
1171 _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
1172 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
1175 /* Create a matrix that first converts A-Format to B-Format, then
1176 * transforms the B-Format signal according to the panning vector.
1178 rot
= GetTransformFromVector(ReflectionsPan
);
1179 MATRIX_MULT(transform
, rot
, A2B
);
1180 memset(&State
->Early
.PanGain
, 0, sizeof(State
->Early
.PanGain
));
1181 for(i
= 0;i
< MAX_EFFECT_CHANNELS
;i
++)
1182 ComputeFirstOrderGains(&Device
->FOAOut
, transform
.m
[i
], gain
*earlyGain
,
1183 State
->Early
.PanGain
[i
]);
1185 rot
= GetTransformFromVector(LateReverbPan
);
1186 MATRIX_MULT(transform
, rot
, A2B
);
1187 memset(&State
->Late
.PanGain
, 0, sizeof(State
->Late
.PanGain
));
1188 for(i
= 0;i
< MAX_EFFECT_CHANNELS
;i
++)
1189 ComputeFirstOrderGains(&Device
->FOAOut
, transform
.m
[i
], gain
*lateGain
,
1190 State
->Late
.PanGain
[i
]);
1194 static ALvoid
ALreverbState_update(ALreverbState
*State
, const ALCcontext
*Context
, const ALeffectslot
*Slot
, const ALeffectProps
*props
)
1196 const ALCdevice
*Device
= Context
->Device
;
1197 const ALlistener
*Listener
= Context
->Listener
;
1198 ALuint frequency
= Device
->Frequency
;
1199 ALfloat lf0norm
, hf0norm
, hfRatio
;
1200 ALfloat lfDecayTime
, hfDecayTime
;
1201 ALfloat gain
, gainlf
, gainhf
;
1204 /* Calculate the master filters */
1205 hf0norm
= props
->Reverb
.HFReference
/ frequency
;
1206 /* Restrict the filter gains from going below -60dB to keep the filter from
1207 * killing most of the signal.
1209 gainhf
= maxf(props
->Reverb
.GainHF
, 0.001f
);
1210 ALfilterState_setParams(&State
->Filter
[0].Lp
, ALfilterType_HighShelf
,
1211 gainhf
, hf0norm
, calc_rcpQ_from_slope(gainhf
, 1.0f
));
1212 lf0norm
= props
->Reverb
.LFReference
/ frequency
;
1213 gainlf
= maxf(props
->Reverb
.GainLF
, 0.001f
);
1214 ALfilterState_setParams(&State
->Filter
[0].Hp
, ALfilterType_LowShelf
,
1215 gainlf
, lf0norm
, calc_rcpQ_from_slope(gainlf
, 1.0f
));
1216 for(i
= 1;i
< NUM_LINES
;i
++)
1218 ALfilterState_copyParams(&State
->Filter
[i
].Lp
, &State
->Filter
[0].Lp
);
1219 ALfilterState_copyParams(&State
->Filter
[i
].Hp
, &State
->Filter
[0].Hp
);
1222 /* Update the main effect delay and associated taps. */
1223 UpdateDelayLine(props
->Reverb
.ReflectionsDelay
, props
->Reverb
.LateReverbDelay
,
1224 props
->Reverb
.Density
, props
->Reverb
.DecayTime
, frequency
,
1227 /* Calculate the all-pass feed-back/forward coefficient. */
1228 State
->ApFeedCoeff
= sqrtf(0.5f
) * powf(props
->Reverb
.Diffusion
, 2.0f
);
1230 /* Update the early lines. */
1231 UpdateEarlyLines(props
->Reverb
.Density
, props
->Reverb
.DecayTime
,
1232 frequency
, &State
->Early
);
1234 /* Get the mixing matrix coefficients. */
1235 CalcMatrixCoeffs(props
->Reverb
.Diffusion
, &State
->MixX
, &State
->MixY
);
1237 /* If the HF limit parameter is flagged, calculate an appropriate limit
1238 * based on the air absorption parameter.
1240 hfRatio
= props
->Reverb
.DecayHFRatio
;
1241 if(props
->Reverb
.DecayHFLimit
&& props
->Reverb
.AirAbsorptionGainHF
< 1.0f
)
1242 hfRatio
= CalcLimitedHfRatio(hfRatio
, props
->Reverb
.AirAbsorptionGainHF
,
1243 props
->Reverb
.DecayTime
, Listener
->Params
.ReverbSpeedOfSound
1246 /* Calculate the LF/HF decay times. */
1247 lfDecayTime
= clampf(props
->Reverb
.DecayTime
* props
->Reverb
.DecayLFRatio
,
1248 AL_EAXREVERB_MIN_DECAY_TIME
, AL_EAXREVERB_MAX_DECAY_TIME
);
1249 hfDecayTime
= clampf(props
->Reverb
.DecayTime
* hfRatio
,
1250 AL_EAXREVERB_MIN_DECAY_TIME
, AL_EAXREVERB_MAX_DECAY_TIME
);
1252 /* Update the late lines. */
1253 UpdateLateLines(props
->Reverb
.Density
, props
->Reverb
.Diffusion
,
1254 lfDecayTime
, props
->Reverb
.DecayTime
, hfDecayTime
,
1255 F_TAU
* lf0norm
, F_TAU
* hf0norm
,
1256 props
->Reverb
.EchoTime
, props
->Reverb
.EchoDepth
,
1257 frequency
, &State
->Late
);
1259 /* Update early and late 3D panning. */
1260 gain
= props
->Reverb
.Gain
* Slot
->Params
.Gain
* ReverbBoost
;
1261 Update3DPanning(Device
, props
->Reverb
.ReflectionsPan
,
1262 props
->Reverb
.LateReverbPan
, gain
,
1263 props
->Reverb
.ReflectionsGain
,
1264 props
->Reverb
.LateReverbGain
, State
);
1266 /* Determine if delay-line cross-fading is required. */
1267 for(i
= 0;i
< NUM_LINES
;i
++)
1269 if(State
->EarlyDelayTap
[i
][1] != State
->EarlyDelayTap
[i
][0] ||
1270 State
->Early
.VecAp
.Offset
[i
][1] != State
->Early
.VecAp
.Offset
[i
][0] ||
1271 State
->Early
.Offset
[i
][1] != State
->Early
.Offset
[i
][0] ||
1272 State
->LateDelayTap
[i
][1] != State
->LateDelayTap
[i
][0] ||
1273 State
->Late
.VecAp
.Offset
[i
][1] != State
->Late
.VecAp
.Offset
[i
][0] ||
1274 State
->Late
.Offset
[i
][1] != State
->Late
.Offset
[i
][0])
1276 State
->FadeCount
= 0;
1283 /**************************************
1284 * Effect Processing *
1285 **************************************/
1287 /* Basic delay line input/output routines. */
1288 static inline ALfloat
DelayLineOut(const DelayLineI
*Delay
, const ALsizei offset
, const ALsizei c
)
1290 return Delay
->Line
[offset
&Delay
->Mask
][c
];
1293 /* Cross-faded delay line output routine. Instead of interpolating the
1294 * offsets, this interpolates (cross-fades) the outputs at each offset.
1296 static inline ALfloat
FadedDelayLineOut(const DelayLineI
*Delay
, const ALsizei off0
,
1297 const ALsizei off1
, const ALsizei c
, const ALfloat mu
)
1299 return Delay
->Line
[off0
&Delay
->Mask
][c
]*(1.0f
-mu
) +
1300 Delay
->Line
[off1
&Delay
->Mask
][c
]*( mu
);
1302 #define UnfadedDelayLineOut(d, o0, o1, c, mu) DelayLineOut(d, o0, c)
1304 static inline ALvoid
DelayLineIn(DelayLineI
*Delay
, const ALsizei offset
, const ALsizei c
, const ALfloat in
)
1306 Delay
->Line
[offset
&Delay
->Mask
][c
] = in
;
1309 static inline ALvoid
DelayLineIn4(DelayLineI
*Delay
, ALsizei offset
, const ALfloat in
[NUM_LINES
])
1312 offset
&= Delay
->Mask
;
1313 for(i
= 0;i
< NUM_LINES
;i
++)
1314 Delay
->Line
[offset
][i
] = in
[i
];
1317 static inline ALvoid
DelayLineIn4Rev(DelayLineI
*Delay
, ALsizei offset
, const ALfloat in
[NUM_LINES
])
1320 offset
&= Delay
->Mask
;
1321 for(i
= 0;i
< NUM_LINES
;i
++)
1322 Delay
->Line
[offset
][i
] = in
[NUM_LINES
-1-i
];
1325 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1326 * for both the below vector all-pass model and to perform modal feed-back
1327 * delay network (FDN) mixing.
1329 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1330 * matrix with a single unitary rotational parameter:
1332 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1337 * The rotation is constructed from the effect's diffusion parameter,
1342 * Where a, b, and c are the coefficient y with differing signs, and d is the
1343 * coefficient x. The final matrix is thus:
1345 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1346 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1347 * [ y, -y, x, y ] x = cos(t)
1348 * [ -y, -y, -y, x ] y = sin(t) / n
1350 * Any square orthogonal matrix with an order that is a power of two will
1351 * work (where ^T is transpose, ^-1 is inverse):
1355 * Using that knowledge, finding an appropriate matrix can be accomplished
1356 * naively by searching all combinations of:
1360 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1361 * whose combination of signs are being iterated.
1363 static inline void VectorPartialScatter(ALfloat
*restrict out
, const ALfloat
*restrict in
,
1364 const ALfloat xCoeff
, const ALfloat yCoeff
)
1366 out
[0] = xCoeff
*in
[0] + yCoeff
*( in
[1] + -in
[2] + in
[3]);
1367 out
[1] = xCoeff
*in
[1] + yCoeff
*(-in
[0] + in
[2] + in
[3]);
1368 out
[2] = xCoeff
*in
[2] + yCoeff
*( in
[0] + -in
[1] + in
[3]);
1369 out
[3] = xCoeff
*in
[3] + yCoeff
*(-in
[0] + -in
[1] + -in
[2] );
1372 /* Same as above, but reverses the input. */
1373 static inline void VectorPartialScatterRev(ALfloat
*restrict out
, const ALfloat
*restrict in
,
1374 const ALfloat xCoeff
, const ALfloat yCoeff
)
1376 out
[0] = xCoeff
*in
[3] + yCoeff
*(in
[0] + -in
[1] + in
[2] );
1377 out
[1] = xCoeff
*in
[2] + yCoeff
*(in
[0] + in
[1] + -in
[3]);
1378 out
[2] = xCoeff
*in
[1] + yCoeff
*(in
[0] + -in
[2] + in
[3]);
1379 out
[3] = xCoeff
*in
[0] + yCoeff
*( + -in
[1] + -in
[2] + -in
[3]);
1382 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1383 * filter to the 4-line input.
1385 * It works by vectorizing a regular all-pass filter and replacing the delay
1386 * element with a scattering matrix (like the one above) and a diagonal
1387 * matrix of delay elements.
1389 * Two static specializations are used for transitional (cross-faded) delay
1390 * line processing and non-transitional processing.
1392 #define DECL_TEMPLATE(T) \
1393 static void VectorAllpass_##T(ALfloat *restrict out, \
1394 const ALfloat *restrict in, \
1395 const ALsizei offset, const ALfloat feedCoeff, \
1396 const ALfloat xCoeff, const ALfloat yCoeff, \
1397 const ALfloat mu, VecAllpass *Vap) \
1399 ALfloat f[NUM_LINES], fs[NUM_LINES]; \
1403 (void)mu; /* Ignore for Unfaded. */ \
1405 for(i = 0;i < NUM_LINES;i++) \
1408 out[i] = T##DelayLineOut(&Vap->Delay, offset-Vap->Offset[i][0], \
1409 offset-Vap->Offset[i][1], i, mu) - \
1411 f[i] = input + feedCoeff*out[i]; \
1413 VectorPartialScatter(fs, f, xCoeff, yCoeff); \
1415 DelayLineIn4(&Vap->Delay, offset, fs); \
1417 DECL_TEMPLATE(Unfaded
)
1418 DECL_TEMPLATE(Faded
)
1419 #undef DECL_TEMPLATE
1421 /* This generates early reflections.
1423 * This is done by obtaining the primary reflections (those arriving from the
1424 * same direction as the source) from the main delay line. These are
1425 * attenuated and all-pass filtered (based on the diffusion parameter).
1427 * The early lines are then fed in reverse (according to the approximately
1428 * opposite spatial location of the A-Format lines) to create the secondary
1429 * reflections (those arriving from the opposite direction as the source).
1431 * The early response is then completed by combining the primary reflections
1432 * with the delayed and attenuated output from the early lines.
1434 * Finally, the early response is reversed, scattered (based on diffusion),
1435 * and fed into the late reverb section of the main delay line.
1437 * Two static specializations are used for transitional (cross-faded) delay
1438 * line processing and non-transitional processing.
1440 #define DECL_TEMPLATE(T) \
1441 static ALvoid EarlyReflection_##T(ALreverbState *State, const ALsizei todo, \
1443 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])\
1445 ALsizei offset = State->Offset; \
1446 const ALfloat apFeedCoeff = State->ApFeedCoeff; \
1447 const ALfloat mixX = State->MixX; \
1448 const ALfloat mixY = State->MixY; \
1449 ALfloat f[NUM_LINES], fr[NUM_LINES]; \
1452 for(i = 0;i < todo;i++) \
1454 for(j = 0;j < NUM_LINES;j++) \
1455 fr[j] = T##DelayLineOut(&State->Delay, \
1456 offset-State->EarlyDelayTap[j][0], \
1457 offset-State->EarlyDelayTap[j][1], j, fade \
1458 ) * State->EarlyDelayCoeff[j]; \
1460 VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \
1461 &State->Early.VecAp); \
1463 DelayLineIn4Rev(&State->Early.Delay, offset, f); \
1465 for(j = 0;j < NUM_LINES;j++) \
1466 f[j] += T##DelayLineOut(&State->Early.Delay, \
1467 offset-State->Early.Offset[j][0], \
1468 offset-State->Early.Offset[j][1], j, fade \
1469 ) * State->Early.Coeff[j]; \
1471 for(j = 0;j < NUM_LINES;j++) \
1474 VectorPartialScatterRev(fr, f, mixX, mixY); \
1476 DelayLineIn4(&State->Delay, offset-State->LateFeedTap, fr); \
1482 DECL_TEMPLATE(Unfaded
)
1483 DECL_TEMPLATE(Faded
)
1484 #undef DECL_TEMPLATE
1486 /* Applies a first order filter section. */
1487 static inline ALfloat
FirstOrderFilter(const ALfloat in
, const ALfloat
*restrict coeffs
,
1488 ALfloat
*restrict state
)
1490 ALfloat out
= coeffs
[0]*in
+ coeffs
[1]*state
[0] + coeffs
[2]*state
[1];
1496 /* Applies the two T60 damping filter sections. */
1497 static inline void LateT60Filter(ALfloat
*restrict out
, const ALfloat
*restrict in
,
1501 for(i
= 0;i
< NUM_LINES
;i
++)
1502 out
[i
] = FirstOrderFilter(
1503 FirstOrderFilter(in
[i
], filter
[i
].HFCoeffs
, filter
[i
].HFState
),
1504 filter
[i
].LFCoeffs
, filter
[i
].LFState
1508 /* This generates the reverb tail using a modified feed-back delay network
1511 * Results from the early reflections are attenuated by the density gain and
1512 * mixed with the output from the late delay lines.
1514 * The late response is then completed by T60 and all-pass filtering the mix.
1516 * Finally, the lines are reversed (so they feed their opposite directions)
1517 * and scattered with the FDN matrix before re-feeding the delay lines.
1519 * Two variations are made, one for for transitional (cross-faded) delay line
1520 * processing and one for non-transitional processing.
1522 static ALvoid
LateReverb_Faded(ALreverbState
*State
, const ALsizei todo
, ALfloat fade
,
1523 ALfloat (*restrict out
)[MAX_UPDATE_SAMPLES
])
1525 const ALfloat apFeedCoeff
= State
->ApFeedCoeff
;
1526 const ALfloat mixX
= State
->MixX
;
1527 const ALfloat mixY
= State
->MixY
;
1531 offset
= State
->Offset
;
1532 for(i
= 0;i
< todo
;i
++)
1534 ALfloat f
[NUM_LINES
], fr
[NUM_LINES
];
1536 for(j
= 0;j
< NUM_LINES
;j
++)
1537 f
[j
] = FadedDelayLineOut(&State
->Delay
,
1538 offset
- State
->LateDelayTap
[j
][0],
1539 offset
- State
->LateDelayTap
[j
][1], j
, fade
1540 ) * State
->Late
.DensityGain
;
1542 for(j
= 0;j
< NUM_LINES
;j
++)
1543 f
[j
] += FadedDelayLineOut(&State
->Late
.Delay
,
1544 offset
- State
->Late
.Offset
[j
][0],
1545 offset
- State
->Late
.Offset
[j
][1], j
, fade
1548 LateT60Filter(fr
, f
, State
->Late
.T60
);
1549 VectorAllpass_Faded(f
, fr
, offset
, apFeedCoeff
, mixX
, mixY
, fade
,
1550 &State
->Late
.VecAp
);
1552 for(j
= 0;j
< NUM_LINES
;j
++)
1555 VectorPartialScatterRev(fr
, f
, mixX
, mixY
);
1557 DelayLineIn4(&State
->Late
.Delay
, offset
, fr
);
1563 static ALvoid
LateReverb_Unfaded(ALreverbState
*State
, const ALsizei todo
, ALfloat fade
,
1564 ALfloat (*restrict out
)[MAX_UPDATE_SAMPLES
])
1566 const ALfloat apFeedCoeff
= State
->ApFeedCoeff
;
1567 const ALfloat mixX
= State
->MixX
;
1568 const ALfloat mixY
= State
->MixY
;
1572 offset
= State
->Offset
;
1573 for(i
= 0;i
< todo
;i
++)
1575 ALfloat f
[NUM_LINES
], fr
[NUM_LINES
];
1577 for(j
= 0;j
< NUM_LINES
;j
++)
1578 f
[j
] = DelayLineOut(&State
->Delay
, offset
-State
->LateDelayTap
[j
][0], j
) *
1579 State
->Late
.DensityGain
;
1581 for(j
= 0;j
< NUM_LINES
;j
++)
1582 f
[j
] += DelayLineOut(&State
->Late
.Delay
, offset
-State
->Late
.Offset
[j
][0], j
);
1584 LateT60Filter(fr
, f
, State
->Late
.T60
);
1585 VectorAllpass_Unfaded(f
, fr
, offset
, apFeedCoeff
, mixX
, mixY
, fade
,
1586 &State
->Late
.VecAp
);
1588 for(j
= 0;j
< NUM_LINES
;j
++)
1591 VectorPartialScatterRev(fr
, f
, mixX
, mixY
);
1593 DelayLineIn4(&State
->Late
.Delay
, offset
, fr
);
1599 static ALvoid
ALreverbState_process(ALreverbState
*State
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
1601 ALfloat (*restrict afmt
)[MAX_UPDATE_SAMPLES
] = State
->AFormatSamples
;
1602 ALfloat (*restrict early
)[MAX_UPDATE_SAMPLES
] = State
->EarlySamples
;
1603 ALfloat (*restrict late
)[MAX_UPDATE_SAMPLES
] = State
->ReverbSamples
;
1604 ALsizei fadeCount
= State
->FadeCount
;
1605 ALfloat fade
= (ALfloat
)fadeCount
/ FADE_SAMPLES
;
1608 /* Process reverb for these samples. */
1609 for(base
= 0;base
< SamplesToDo
;)
1611 ALsizei todo
= mini(SamplesToDo
-base
, MAX_UPDATE_SAMPLES
);
1612 /* If cross-fading, don't do more samples than there are to fade. */
1613 if(FADE_SAMPLES
-fadeCount
> 0)
1614 todo
= mini(todo
, FADE_SAMPLES
-fadeCount
);
1616 /* Convert B-Format to A-Format for processing. */
1617 memset(afmt
, 0, sizeof(*afmt
)*NUM_LINES
);
1618 for(c
= 0;c
< NUM_LINES
;c
++)
1619 MixRowSamples(afmt
[c
], B2A
.m
[c
],
1620 SamplesIn
, MAX_EFFECT_CHANNELS
, base
, todo
1623 /* Process the samples for reverb. */
1624 for(c
= 0;c
< NUM_LINES
;c
++)
1626 /* Band-pass the incoming samples. Use the early output lines for
1629 ALfilterState_process(&State
->Filter
[c
].Lp
, early
[0], afmt
[c
], todo
);
1630 ALfilterState_process(&State
->Filter
[c
].Hp
, early
[1], early
[0], todo
);
1632 /* Feed the initial delay line. */
1633 for(i
= 0;i
< todo
;i
++)
1634 DelayLineIn(&State
->Delay
, State
->Offset
+i
, c
, early
[1][i
]);
1637 if(UNLIKELY(fadeCount
< FADE_SAMPLES
))
1639 /* Generate early reflections. */
1640 EarlyReflection_Faded(State
, todo
, fade
, early
);
1642 /* Generate late reverb. */
1643 LateReverb_Faded(State
, todo
, fade
, late
);
1644 fade
= minf(1.0f
, fade
+ todo
*FadeStep
);
1648 /* Generate early reflections. */
1649 EarlyReflection_Unfaded(State
, todo
, fade
, early
);
1651 /* Generate late reverb. */
1652 LateReverb_Unfaded(State
, todo
, fade
, late
);
1655 /* Step all delays forward. */
1656 State
->Offset
+= todo
;
1658 if(UNLIKELY(fadeCount
< FADE_SAMPLES
) && (fadeCount
+= todo
) >= FADE_SAMPLES
)
1660 /* Update the cross-fading delay line taps. */
1661 fadeCount
= FADE_SAMPLES
;
1663 for(c
= 0;c
< NUM_LINES
;c
++)
1665 State
->EarlyDelayTap
[c
][0] = State
->EarlyDelayTap
[c
][1];
1666 State
->Early
.VecAp
.Offset
[c
][0] = State
->Early
.VecAp
.Offset
[c
][1];
1667 State
->Early
.Offset
[c
][0] = State
->Early
.Offset
[c
][1];
1668 State
->LateDelayTap
[c
][0] = State
->LateDelayTap
[c
][1];
1669 State
->Late
.VecAp
.Offset
[c
][0] = State
->Late
.VecAp
.Offset
[c
][1];
1670 State
->Late
.Offset
[c
][0] = State
->Late
.Offset
[c
][1];
1674 /* Mix the A-Format results to output, implicitly converting back to
1677 for(c
= 0;c
< NUM_LINES
;c
++)
1678 MixSamples(early
[c
], NumChannels
, SamplesOut
,
1679 State
->Early
.CurrentGain
[c
], State
->Early
.PanGain
[c
],
1680 SamplesToDo
-base
, base
, todo
1682 for(c
= 0;c
< NUM_LINES
;c
++)
1683 MixSamples(late
[c
], NumChannels
, SamplesOut
,
1684 State
->Late
.CurrentGain
[c
], State
->Late
.PanGain
[c
],
1685 SamplesToDo
-base
, base
, todo
1690 State
->FadeCount
= fadeCount
;
1694 typedef struct ALreverbStateFactory
{
1695 DERIVE_FROM_TYPE(ALeffectStateFactory
);
1696 } ALreverbStateFactory
;
1698 static ALeffectState
*ALreverbStateFactory_create(ALreverbStateFactory
* UNUSED(factory
))
1700 ALreverbState
*state
;
1702 NEW_OBJ0(state
, ALreverbState
)();
1703 if(!state
) return NULL
;
1705 return STATIC_CAST(ALeffectState
, state
);
1708 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALreverbStateFactory
);
1710 ALeffectStateFactory
*ALreverbStateFactory_getFactory(void)
1712 static ALreverbStateFactory ReverbFactory
= { { GET_VTABLE2(ALreverbStateFactory
, ALeffectStateFactory
) } };
1714 return STATIC_CAST(ALeffectStateFactory
, &ReverbFactory
);
1718 void ALeaxreverb_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
1720 ALeffectProps
*props
= &effect
->Props
;
1723 case AL_EAXREVERB_DECAY_HFLIMIT
:
1724 if(!(val
>= AL_EAXREVERB_MIN_DECAY_HFLIMIT
&& val
<= AL_EAXREVERB_MAX_DECAY_HFLIMIT
))
1725 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay hflimit out of range");
1726 props
->Reverb
.DecayHFLimit
= val
;
1730 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb integer property 0x%04x",
1734 void ALeaxreverb_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
1735 { ALeaxreverb_setParami(effect
, context
, param
, vals
[0]); }
1736 void ALeaxreverb_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
1738 ALeffectProps
*props
= &effect
->Props
;
1741 case AL_EAXREVERB_DENSITY
:
1742 if(!(val
>= AL_EAXREVERB_MIN_DENSITY
&& val
<= AL_EAXREVERB_MAX_DENSITY
))
1743 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb density out of range");
1744 props
->Reverb
.Density
= val
;
1747 case AL_EAXREVERB_DIFFUSION
:
1748 if(!(val
>= AL_EAXREVERB_MIN_DIFFUSION
&& val
<= AL_EAXREVERB_MAX_DIFFUSION
))
1749 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb diffusion out of range");
1750 props
->Reverb
.Diffusion
= val
;
1753 case AL_EAXREVERB_GAIN
:
1754 if(!(val
>= AL_EAXREVERB_MIN_GAIN
&& val
<= AL_EAXREVERB_MAX_GAIN
))
1755 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb gain out of range");
1756 props
->Reverb
.Gain
= val
;
1759 case AL_EAXREVERB_GAINHF
:
1760 if(!(val
>= AL_EAXREVERB_MIN_GAINHF
&& val
<= AL_EAXREVERB_MAX_GAINHF
))
1761 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb gainhf out of range");
1762 props
->Reverb
.GainHF
= val
;
1765 case AL_EAXREVERB_GAINLF
:
1766 if(!(val
>= AL_EAXREVERB_MIN_GAINLF
&& val
<= AL_EAXREVERB_MAX_GAINLF
))
1767 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb gainlf out of range");
1768 props
->Reverb
.GainLF
= val
;
1771 case AL_EAXREVERB_DECAY_TIME
:
1772 if(!(val
>= AL_EAXREVERB_MIN_DECAY_TIME
&& val
<= AL_EAXREVERB_MAX_DECAY_TIME
))
1773 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay time out of range");
1774 props
->Reverb
.DecayTime
= val
;
1777 case AL_EAXREVERB_DECAY_HFRATIO
:
1778 if(!(val
>= AL_EAXREVERB_MIN_DECAY_HFRATIO
&& val
<= AL_EAXREVERB_MAX_DECAY_HFRATIO
))
1779 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay hfratio out of range");
1780 props
->Reverb
.DecayHFRatio
= val
;
1783 case AL_EAXREVERB_DECAY_LFRATIO
:
1784 if(!(val
>= AL_EAXREVERB_MIN_DECAY_LFRATIO
&& val
<= AL_EAXREVERB_MAX_DECAY_LFRATIO
))
1785 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay lfratio out of range");
1786 props
->Reverb
.DecayLFRatio
= val
;
1789 case AL_EAXREVERB_REFLECTIONS_GAIN
:
1790 if(!(val
>= AL_EAXREVERB_MIN_REFLECTIONS_GAIN
&& val
<= AL_EAXREVERB_MAX_REFLECTIONS_GAIN
))
1791 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb reflections gain out of range");
1792 props
->Reverb
.ReflectionsGain
= val
;
1795 case AL_EAXREVERB_REFLECTIONS_DELAY
:
1796 if(!(val
>= AL_EAXREVERB_MIN_REFLECTIONS_DELAY
&& val
<= AL_EAXREVERB_MAX_REFLECTIONS_DELAY
))
1797 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb reflections delay out of range");
1798 props
->Reverb
.ReflectionsDelay
= val
;
1801 case AL_EAXREVERB_LATE_REVERB_GAIN
:
1802 if(!(val
>= AL_EAXREVERB_MIN_LATE_REVERB_GAIN
&& val
<= AL_EAXREVERB_MAX_LATE_REVERB_GAIN
))
1803 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb late reverb gain out of range");
1804 props
->Reverb
.LateReverbGain
= val
;
1807 case AL_EAXREVERB_LATE_REVERB_DELAY
:
1808 if(!(val
>= AL_EAXREVERB_MIN_LATE_REVERB_DELAY
&& val
<= AL_EAXREVERB_MAX_LATE_REVERB_DELAY
))
1809 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb late reverb delay out of range");
1810 props
->Reverb
.LateReverbDelay
= val
;
1813 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF
:
1814 if(!(val
>= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF
&& val
<= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF
))
1815 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb air absorption gainhf out of range");
1816 props
->Reverb
.AirAbsorptionGainHF
= val
;
1819 case AL_EAXREVERB_ECHO_TIME
:
1820 if(!(val
>= AL_EAXREVERB_MIN_ECHO_TIME
&& val
<= AL_EAXREVERB_MAX_ECHO_TIME
))
1821 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb echo time out of range");
1822 props
->Reverb
.EchoTime
= val
;
1825 case AL_EAXREVERB_ECHO_DEPTH
:
1826 if(!(val
>= AL_EAXREVERB_MIN_ECHO_DEPTH
&& val
<= AL_EAXREVERB_MAX_ECHO_DEPTH
))
1827 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb echo depth out of range");
1828 props
->Reverb
.EchoDepth
= val
;
1831 case AL_EAXREVERB_MODULATION_TIME
:
1832 if(!(val
>= AL_EAXREVERB_MIN_MODULATION_TIME
&& val
<= AL_EAXREVERB_MAX_MODULATION_TIME
))
1833 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb modulation time out of range");
1834 props
->Reverb
.ModulationTime
= val
;
1837 case AL_EAXREVERB_MODULATION_DEPTH
:
1838 if(!(val
>= AL_EAXREVERB_MIN_MODULATION_DEPTH
&& val
<= AL_EAXREVERB_MAX_MODULATION_DEPTH
))
1839 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb modulation depth out of range");
1840 props
->Reverb
.ModulationDepth
= val
;
1843 case AL_EAXREVERB_HFREFERENCE
:
1844 if(!(val
>= AL_EAXREVERB_MIN_HFREFERENCE
&& val
<= AL_EAXREVERB_MAX_HFREFERENCE
))
1845 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb hfreference out of range");
1846 props
->Reverb
.HFReference
= val
;
1849 case AL_EAXREVERB_LFREFERENCE
:
1850 if(!(val
>= AL_EAXREVERB_MIN_LFREFERENCE
&& val
<= AL_EAXREVERB_MAX_LFREFERENCE
))
1851 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb lfreference out of range");
1852 props
->Reverb
.LFReference
= val
;
1855 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR
:
1856 if(!(val
>= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR
&& val
<= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR
))
1857 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb room rolloff factor out of range");
1858 props
->Reverb
.RoomRolloffFactor
= val
;
1862 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb float property 0x%04x",
1866 void ALeaxreverb_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
1868 ALeffectProps
*props
= &effect
->Props
;
1871 case AL_EAXREVERB_REFLECTIONS_PAN
:
1872 if(!(isfinite(vals
[0]) && isfinite(vals
[1]) && isfinite(vals
[2])))
1873 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb reflections pan out of range");
1874 props
->Reverb
.ReflectionsPan
[0] = vals
[0];
1875 props
->Reverb
.ReflectionsPan
[1] = vals
[1];
1876 props
->Reverb
.ReflectionsPan
[2] = vals
[2];
1878 case AL_EAXREVERB_LATE_REVERB_PAN
:
1879 if(!(isfinite(vals
[0]) && isfinite(vals
[1]) && isfinite(vals
[2])))
1880 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb late reverb pan out of range");
1881 props
->Reverb
.LateReverbPan
[0] = vals
[0];
1882 props
->Reverb
.LateReverbPan
[1] = vals
[1];
1883 props
->Reverb
.LateReverbPan
[2] = vals
[2];
1887 ALeaxreverb_setParamf(effect
, context
, param
, vals
[0]);
1892 void ALeaxreverb_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
1894 const ALeffectProps
*props
= &effect
->Props
;
1897 case AL_EAXREVERB_DECAY_HFLIMIT
:
1898 *val
= props
->Reverb
.DecayHFLimit
;
1902 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb integer property 0x%04x",
1906 void ALeaxreverb_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
1907 { ALeaxreverb_getParami(effect
, context
, param
, vals
); }
1908 void ALeaxreverb_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
1910 const ALeffectProps
*props
= &effect
->Props
;
1913 case AL_EAXREVERB_DENSITY
:
1914 *val
= props
->Reverb
.Density
;
1917 case AL_EAXREVERB_DIFFUSION
:
1918 *val
= props
->Reverb
.Diffusion
;
1921 case AL_EAXREVERB_GAIN
:
1922 *val
= props
->Reverb
.Gain
;
1925 case AL_EAXREVERB_GAINHF
:
1926 *val
= props
->Reverb
.GainHF
;
1929 case AL_EAXREVERB_GAINLF
:
1930 *val
= props
->Reverb
.GainLF
;
1933 case AL_EAXREVERB_DECAY_TIME
:
1934 *val
= props
->Reverb
.DecayTime
;
1937 case AL_EAXREVERB_DECAY_HFRATIO
:
1938 *val
= props
->Reverb
.DecayHFRatio
;
1941 case AL_EAXREVERB_DECAY_LFRATIO
:
1942 *val
= props
->Reverb
.DecayLFRatio
;
1945 case AL_EAXREVERB_REFLECTIONS_GAIN
:
1946 *val
= props
->Reverb
.ReflectionsGain
;
1949 case AL_EAXREVERB_REFLECTIONS_DELAY
:
1950 *val
= props
->Reverb
.ReflectionsDelay
;
1953 case AL_EAXREVERB_LATE_REVERB_GAIN
:
1954 *val
= props
->Reverb
.LateReverbGain
;
1957 case AL_EAXREVERB_LATE_REVERB_DELAY
:
1958 *val
= props
->Reverb
.LateReverbDelay
;
1961 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF
:
1962 *val
= props
->Reverb
.AirAbsorptionGainHF
;
1965 case AL_EAXREVERB_ECHO_TIME
:
1966 *val
= props
->Reverb
.EchoTime
;
1969 case AL_EAXREVERB_ECHO_DEPTH
:
1970 *val
= props
->Reverb
.EchoDepth
;
1973 case AL_EAXREVERB_MODULATION_TIME
:
1974 *val
= props
->Reverb
.ModulationTime
;
1977 case AL_EAXREVERB_MODULATION_DEPTH
:
1978 *val
= props
->Reverb
.ModulationDepth
;
1981 case AL_EAXREVERB_HFREFERENCE
:
1982 *val
= props
->Reverb
.HFReference
;
1985 case AL_EAXREVERB_LFREFERENCE
:
1986 *val
= props
->Reverb
.LFReference
;
1989 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR
:
1990 *val
= props
->Reverb
.RoomRolloffFactor
;
1994 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb float property 0x%04x",
1998 void ALeaxreverb_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
2000 const ALeffectProps
*props
= &effect
->Props
;
2003 case AL_EAXREVERB_REFLECTIONS_PAN
:
2004 vals
[0] = props
->Reverb
.ReflectionsPan
[0];
2005 vals
[1] = props
->Reverb
.ReflectionsPan
[1];
2006 vals
[2] = props
->Reverb
.ReflectionsPan
[2];
2008 case AL_EAXREVERB_LATE_REVERB_PAN
:
2009 vals
[0] = props
->Reverb
.LateReverbPan
[0];
2010 vals
[1] = props
->Reverb
.LateReverbPan
[1];
2011 vals
[2] = props
->Reverb
.LateReverbPan
[2];
2015 ALeaxreverb_getParamf(effect
, context
, param
, vals
);
2020 DEFINE_ALEFFECT_VTABLE(ALeaxreverb
);
2022 void ALreverb_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
2024 ALeffectProps
*props
= &effect
->Props
;
2027 case AL_REVERB_DECAY_HFLIMIT
:
2028 if(!(val
>= AL_REVERB_MIN_DECAY_HFLIMIT
&& val
<= AL_REVERB_MAX_DECAY_HFLIMIT
))
2029 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb decay hflimit out of range");
2030 props
->Reverb
.DecayHFLimit
= val
;
2034 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb integer property 0x%04x", param
);
2037 void ALreverb_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
2038 { ALreverb_setParami(effect
, context
, param
, vals
[0]); }
2039 void ALreverb_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
2041 ALeffectProps
*props
= &effect
->Props
;
2044 case AL_REVERB_DENSITY
:
2045 if(!(val
>= AL_REVERB_MIN_DENSITY
&& val
<= AL_REVERB_MAX_DENSITY
))
2046 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb density out of range");
2047 props
->Reverb
.Density
= val
;
2050 case AL_REVERB_DIFFUSION
:
2051 if(!(val
>= AL_REVERB_MIN_DIFFUSION
&& val
<= AL_REVERB_MAX_DIFFUSION
))
2052 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb diffusion out of range");
2053 props
->Reverb
.Diffusion
= val
;
2056 case AL_REVERB_GAIN
:
2057 if(!(val
>= AL_REVERB_MIN_GAIN
&& val
<= AL_REVERB_MAX_GAIN
))
2058 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb gain out of range");
2059 props
->Reverb
.Gain
= val
;
2062 case AL_REVERB_GAINHF
:
2063 if(!(val
>= AL_REVERB_MIN_GAINHF
&& val
<= AL_REVERB_MAX_GAINHF
))
2064 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb gainhf out of range");
2065 props
->Reverb
.GainHF
= val
;
2068 case AL_REVERB_DECAY_TIME
:
2069 if(!(val
>= AL_REVERB_MIN_DECAY_TIME
&& val
<= AL_REVERB_MAX_DECAY_TIME
))
2070 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb decay time out of range");
2071 props
->Reverb
.DecayTime
= val
;
2074 case AL_REVERB_DECAY_HFRATIO
:
2075 if(!(val
>= AL_REVERB_MIN_DECAY_HFRATIO
&& val
<= AL_REVERB_MAX_DECAY_HFRATIO
))
2076 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb decay hfratio out of range");
2077 props
->Reverb
.DecayHFRatio
= val
;
2080 case AL_REVERB_REFLECTIONS_GAIN
:
2081 if(!(val
>= AL_REVERB_MIN_REFLECTIONS_GAIN
&& val
<= AL_REVERB_MAX_REFLECTIONS_GAIN
))
2082 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb reflections gain out of range");
2083 props
->Reverb
.ReflectionsGain
= val
;
2086 case AL_REVERB_REFLECTIONS_DELAY
:
2087 if(!(val
>= AL_REVERB_MIN_REFLECTIONS_DELAY
&& val
<= AL_REVERB_MAX_REFLECTIONS_DELAY
))
2088 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb reflections delay out of range");
2089 props
->Reverb
.ReflectionsDelay
= val
;
2092 case AL_REVERB_LATE_REVERB_GAIN
:
2093 if(!(val
>= AL_REVERB_MIN_LATE_REVERB_GAIN
&& val
<= AL_REVERB_MAX_LATE_REVERB_GAIN
))
2094 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb late reverb gain out of range");
2095 props
->Reverb
.LateReverbGain
= val
;
2098 case AL_REVERB_LATE_REVERB_DELAY
:
2099 if(!(val
>= AL_REVERB_MIN_LATE_REVERB_DELAY
&& val
<= AL_REVERB_MAX_LATE_REVERB_DELAY
))
2100 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb late reverb delay out of range");
2101 props
->Reverb
.LateReverbDelay
= val
;
2104 case AL_REVERB_AIR_ABSORPTION_GAINHF
:
2105 if(!(val
>= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF
&& val
<= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF
))
2106 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb air absorption gainhf out of range");
2107 props
->Reverb
.AirAbsorptionGainHF
= val
;
2110 case AL_REVERB_ROOM_ROLLOFF_FACTOR
:
2111 if(!(val
>= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR
&& val
<= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR
))
2112 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb room rolloff factor out of range");
2113 props
->Reverb
.RoomRolloffFactor
= val
;
2117 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb float property 0x%04x", param
);
2120 void ALreverb_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
2121 { ALreverb_setParamf(effect
, context
, param
, vals
[0]); }
2123 void ALreverb_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
2125 const ALeffectProps
*props
= &effect
->Props
;
2128 case AL_REVERB_DECAY_HFLIMIT
:
2129 *val
= props
->Reverb
.DecayHFLimit
;
2133 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb integer property 0x%04x", param
);
2136 void ALreverb_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
2137 { ALreverb_getParami(effect
, context
, param
, vals
); }
2138 void ALreverb_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
2140 const ALeffectProps
*props
= &effect
->Props
;
2143 case AL_REVERB_DENSITY
:
2144 *val
= props
->Reverb
.Density
;
2147 case AL_REVERB_DIFFUSION
:
2148 *val
= props
->Reverb
.Diffusion
;
2151 case AL_REVERB_GAIN
:
2152 *val
= props
->Reverb
.Gain
;
2155 case AL_REVERB_GAINHF
:
2156 *val
= props
->Reverb
.GainHF
;
2159 case AL_REVERB_DECAY_TIME
:
2160 *val
= props
->Reverb
.DecayTime
;
2163 case AL_REVERB_DECAY_HFRATIO
:
2164 *val
= props
->Reverb
.DecayHFRatio
;
2167 case AL_REVERB_REFLECTIONS_GAIN
:
2168 *val
= props
->Reverb
.ReflectionsGain
;
2171 case AL_REVERB_REFLECTIONS_DELAY
:
2172 *val
= props
->Reverb
.ReflectionsDelay
;
2175 case AL_REVERB_LATE_REVERB_GAIN
:
2176 *val
= props
->Reverb
.LateReverbGain
;
2179 case AL_REVERB_LATE_REVERB_DELAY
:
2180 *val
= props
->Reverb
.LateReverbDelay
;
2183 case AL_REVERB_AIR_ABSORPTION_GAINHF
:
2184 *val
= props
->Reverb
.AirAbsorptionGainHF
;
2187 case AL_REVERB_ROOM_ROLLOFF_FACTOR
:
2188 *val
= props
->Reverb
.RoomRolloffFactor
;
2192 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb float property 0x%04x", param
);
2195 void ALreverb_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
2196 { ALreverb_getParamf(effect
, context
, param
, vals
); }
2198 DEFINE_ALEFFECT_VTABLE(ALreverb
);