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
, ALsizei offset
, const ALsizei c
,
1305 const ALfloat
*restrict in
, ALsizei count
)
1308 for(i
= 0;i
< count
;i
++)
1309 Delay
->Line
[(offset
++)&Delay
->Mask
][c
] = *(in
++);
1312 static inline ALvoid
DelayLineIn4(DelayLineI
*Delay
, ALsizei offset
, const ALfloat in
[NUM_LINES
])
1315 offset
&= Delay
->Mask
;
1316 for(i
= 0;i
< NUM_LINES
;i
++)
1317 Delay
->Line
[offset
][i
] = in
[i
];
1320 static inline ALvoid
DelayLineIn4Rev(DelayLineI
*Delay
, ALsizei offset
, const ALfloat in
[NUM_LINES
])
1323 offset
&= Delay
->Mask
;
1324 for(i
= 0;i
< NUM_LINES
;i
++)
1325 Delay
->Line
[offset
][i
] = in
[NUM_LINES
-1-i
];
1328 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1329 * for both the below vector all-pass model and to perform modal feed-back
1330 * delay network (FDN) mixing.
1332 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1333 * matrix with a single unitary rotational parameter:
1335 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1340 * The rotation is constructed from the effect's diffusion parameter,
1345 * Where a, b, and c are the coefficient y with differing signs, and d is the
1346 * coefficient x. The final matrix is thus:
1348 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1349 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1350 * [ y, -y, x, y ] x = cos(t)
1351 * [ -y, -y, -y, x ] y = sin(t) / n
1353 * Any square orthogonal matrix with an order that is a power of two will
1354 * work (where ^T is transpose, ^-1 is inverse):
1358 * Using that knowledge, finding an appropriate matrix can be accomplished
1359 * naively by searching all combinations of:
1363 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1364 * whose combination of signs are being iterated.
1366 static inline void VectorPartialScatter(ALfloat
*restrict out
, const ALfloat
*restrict in
,
1367 const ALfloat xCoeff
, const ALfloat yCoeff
)
1369 out
[0] = xCoeff
*in
[0] + yCoeff
*( in
[1] + -in
[2] + in
[3]);
1370 out
[1] = xCoeff
*in
[1] + yCoeff
*(-in
[0] + in
[2] + in
[3]);
1371 out
[2] = xCoeff
*in
[2] + yCoeff
*( in
[0] + -in
[1] + in
[3]);
1372 out
[3] = xCoeff
*in
[3] + yCoeff
*(-in
[0] + -in
[1] + -in
[2] );
1375 /* Same as above, but reverses the input. */
1376 static inline void VectorPartialScatterRev(ALfloat
*restrict out
, const ALfloat
*restrict in
,
1377 const ALfloat xCoeff
, const ALfloat yCoeff
)
1379 out
[0] = xCoeff
*in
[3] + yCoeff
*(in
[0] + -in
[1] + in
[2] );
1380 out
[1] = xCoeff
*in
[2] + yCoeff
*(in
[0] + in
[1] + -in
[3]);
1381 out
[2] = xCoeff
*in
[1] + yCoeff
*(in
[0] + -in
[2] + in
[3]);
1382 out
[3] = xCoeff
*in
[0] + yCoeff
*( + -in
[1] + -in
[2] + -in
[3]);
1385 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1386 * filter to the 4-line input.
1388 * It works by vectorizing a regular all-pass filter and replacing the delay
1389 * element with a scattering matrix (like the one above) and a diagonal
1390 * matrix of delay elements.
1392 * Two static specializations are used for transitional (cross-faded) delay
1393 * line processing and non-transitional processing.
1395 #define DECL_TEMPLATE(T) \
1396 static void VectorAllpass_##T(ALfloat *restrict out, \
1397 const ALfloat *restrict in, \
1398 const ALsizei offset, const ALfloat feedCoeff, \
1399 const ALfloat xCoeff, const ALfloat yCoeff, \
1400 const ALfloat mu, VecAllpass *Vap) \
1402 ALfloat f[NUM_LINES], fs[NUM_LINES]; \
1406 (void)mu; /* Ignore for Unfaded. */ \
1408 for(i = 0;i < NUM_LINES;i++) \
1411 out[i] = T##DelayLineOut(&Vap->Delay, offset-Vap->Offset[i][0], \
1412 offset-Vap->Offset[i][1], i, mu) - \
1414 f[i] = input + feedCoeff*out[i]; \
1416 VectorPartialScatter(fs, f, xCoeff, yCoeff); \
1418 DelayLineIn4(&Vap->Delay, offset, fs); \
1420 DECL_TEMPLATE(Unfaded
)
1421 DECL_TEMPLATE(Faded
)
1422 #undef DECL_TEMPLATE
1424 /* This generates early reflections.
1426 * This is done by obtaining the primary reflections (those arriving from the
1427 * same direction as the source) from the main delay line. These are
1428 * attenuated and all-pass filtered (based on the diffusion parameter).
1430 * The early lines are then fed in reverse (according to the approximately
1431 * opposite spatial location of the A-Format lines) to create the secondary
1432 * reflections (those arriving from the opposite direction as the source).
1434 * The early response is then completed by combining the primary reflections
1435 * with the delayed and attenuated output from the early lines.
1437 * Finally, the early response is reversed, scattered (based on diffusion),
1438 * and fed into the late reverb section of the main delay line.
1440 * Two static specializations are used for transitional (cross-faded) delay
1441 * line processing and non-transitional processing.
1443 #define DECL_TEMPLATE(T) \
1444 static ALvoid EarlyReflection_##T(ALreverbState *State, const ALsizei todo, \
1446 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])\
1448 ALsizei offset = State->Offset; \
1449 const ALfloat apFeedCoeff = State->ApFeedCoeff; \
1450 const ALfloat mixX = State->MixX; \
1451 const ALfloat mixY = State->MixY; \
1452 ALfloat f[NUM_LINES], fr[NUM_LINES]; \
1455 for(i = 0;i < todo;i++) \
1457 for(j = 0;j < NUM_LINES;j++) \
1458 fr[j] = T##DelayLineOut(&State->Delay, \
1459 offset-State->EarlyDelayTap[j][0], \
1460 offset-State->EarlyDelayTap[j][1], j, fade \
1461 ) * State->EarlyDelayCoeff[j]; \
1463 VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \
1464 &State->Early.VecAp); \
1466 DelayLineIn4Rev(&State->Early.Delay, offset, f); \
1468 for(j = 0;j < NUM_LINES;j++) \
1469 f[j] += T##DelayLineOut(&State->Early.Delay, \
1470 offset-State->Early.Offset[j][0], \
1471 offset-State->Early.Offset[j][1], j, fade \
1472 ) * State->Early.Coeff[j]; \
1474 for(j = 0;j < NUM_LINES;j++) \
1477 VectorPartialScatterRev(fr, f, mixX, mixY); \
1479 DelayLineIn4(&State->Delay, offset-State->LateFeedTap, fr); \
1485 DECL_TEMPLATE(Unfaded
)
1486 DECL_TEMPLATE(Faded
)
1487 #undef DECL_TEMPLATE
1489 /* Applies a first order filter section. */
1490 static inline ALfloat
FirstOrderFilter(const ALfloat in
, const ALfloat
*restrict coeffs
,
1491 ALfloat
*restrict state
)
1493 ALfloat out
= coeffs
[0]*in
+ coeffs
[1]*state
[0] + coeffs
[2]*state
[1];
1499 /* Applies the two T60 damping filter sections. */
1500 static inline void LateT60Filter(ALfloat
*restrict out
, const ALfloat
*restrict in
,
1504 for(i
= 0;i
< NUM_LINES
;i
++)
1505 out
[i
] = FirstOrderFilter(
1506 FirstOrderFilter(in
[i
], filter
[i
].HFCoeffs
, filter
[i
].HFState
),
1507 filter
[i
].LFCoeffs
, filter
[i
].LFState
1511 /* This generates the reverb tail using a modified feed-back delay network
1514 * Results from the early reflections are attenuated by the density gain and
1515 * mixed with the output from the late delay lines.
1517 * The late response is then completed by T60 and all-pass filtering the mix.
1519 * Finally, the lines are reversed (so they feed their opposite directions)
1520 * and scattered with the FDN matrix before re-feeding the delay lines.
1522 * Two variations are made, one for for transitional (cross-faded) delay line
1523 * processing and one for non-transitional processing.
1525 static ALvoid
LateReverb_Faded(ALreverbState
*State
, const ALsizei todo
, ALfloat fade
,
1526 ALfloat (*restrict out
)[MAX_UPDATE_SAMPLES
])
1528 const ALfloat apFeedCoeff
= State
->ApFeedCoeff
;
1529 const ALfloat mixX
= State
->MixX
;
1530 const ALfloat mixY
= State
->MixY
;
1534 offset
= State
->Offset
;
1535 for(i
= 0;i
< todo
;i
++)
1537 ALfloat f
[NUM_LINES
], fr
[NUM_LINES
];
1539 for(j
= 0;j
< NUM_LINES
;j
++)
1540 f
[j
] = FadedDelayLineOut(&State
->Delay
,
1541 offset
- State
->LateDelayTap
[j
][0],
1542 offset
- State
->LateDelayTap
[j
][1], j
, fade
1543 ) * State
->Late
.DensityGain
;
1545 for(j
= 0;j
< NUM_LINES
;j
++)
1546 f
[j
] += FadedDelayLineOut(&State
->Late
.Delay
,
1547 offset
- State
->Late
.Offset
[j
][0],
1548 offset
- State
->Late
.Offset
[j
][1], j
, fade
1551 LateT60Filter(fr
, f
, State
->Late
.T60
);
1552 VectorAllpass_Faded(f
, fr
, offset
, apFeedCoeff
, mixX
, mixY
, fade
,
1553 &State
->Late
.VecAp
);
1555 for(j
= 0;j
< NUM_LINES
;j
++)
1558 VectorPartialScatterRev(fr
, f
, mixX
, mixY
);
1560 DelayLineIn4(&State
->Late
.Delay
, offset
, fr
);
1566 static ALvoid
LateReverb_Unfaded(ALreverbState
*State
, const ALsizei todo
, ALfloat fade
,
1567 ALfloat (*restrict out
)[MAX_UPDATE_SAMPLES
])
1569 const ALfloat apFeedCoeff
= State
->ApFeedCoeff
;
1570 const ALfloat mixX
= State
->MixX
;
1571 const ALfloat mixY
= State
->MixY
;
1575 offset
= State
->Offset
;
1576 for(i
= 0;i
< todo
;i
++)
1578 ALfloat f
[NUM_LINES
], fr
[NUM_LINES
];
1580 for(j
= 0;j
< NUM_LINES
;j
++)
1581 f
[j
] = DelayLineOut(&State
->Delay
, offset
-State
->LateDelayTap
[j
][0], j
) *
1582 State
->Late
.DensityGain
;
1584 for(j
= 0;j
< NUM_LINES
;j
++)
1585 f
[j
] += DelayLineOut(&State
->Late
.Delay
, offset
-State
->Late
.Offset
[j
][0], j
);
1587 LateT60Filter(fr
, f
, State
->Late
.T60
);
1588 VectorAllpass_Unfaded(f
, fr
, offset
, apFeedCoeff
, mixX
, mixY
, fade
,
1589 &State
->Late
.VecAp
);
1591 for(j
= 0;j
< NUM_LINES
;j
++)
1594 VectorPartialScatterRev(fr
, f
, mixX
, mixY
);
1596 DelayLineIn4(&State
->Late
.Delay
, offset
, fr
);
1602 static ALvoid
ALreverbState_process(ALreverbState
*State
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
1604 ALfloat (*restrict afmt
)[MAX_UPDATE_SAMPLES
] = State
->AFormatSamples
;
1605 ALfloat (*restrict early
)[MAX_UPDATE_SAMPLES
] = State
->EarlySamples
;
1606 ALfloat (*restrict late
)[MAX_UPDATE_SAMPLES
] = State
->ReverbSamples
;
1607 ALsizei fadeCount
= State
->FadeCount
;
1608 ALfloat fade
= (ALfloat
)fadeCount
/ FADE_SAMPLES
;
1611 /* Process reverb for these samples. */
1612 for(base
= 0;base
< SamplesToDo
;)
1614 ALsizei todo
= mini(SamplesToDo
-base
, MAX_UPDATE_SAMPLES
);
1615 /* If cross-fading, don't do more samples than there are to fade. */
1616 if(FADE_SAMPLES
-fadeCount
> 0)
1617 todo
= mini(todo
, FADE_SAMPLES
-fadeCount
);
1619 /* Convert B-Format to A-Format for processing. */
1620 memset(afmt
, 0, sizeof(*afmt
)*NUM_LINES
);
1621 for(c
= 0;c
< NUM_LINES
;c
++)
1622 MixRowSamples(afmt
[c
], B2A
.m
[c
],
1623 SamplesIn
, MAX_EFFECT_CHANNELS
, base
, todo
1626 /* Process the samples for reverb. */
1627 for(c
= 0;c
< NUM_LINES
;c
++)
1629 /* Band-pass the incoming samples. Use the early output lines for
1632 ALfilterState_process(&State
->Filter
[c
].Lp
, early
[0], afmt
[c
], todo
);
1633 ALfilterState_process(&State
->Filter
[c
].Hp
, early
[1], early
[0], todo
);
1635 /* Feed the initial delay line. */
1636 DelayLineIn(&State
->Delay
, State
->Offset
, c
, early
[1], todo
);
1639 if(UNLIKELY(fadeCount
< FADE_SAMPLES
))
1641 /* Generate early reflections. */
1642 EarlyReflection_Faded(State
, todo
, fade
, early
);
1644 /* Generate late reverb. */
1645 LateReverb_Faded(State
, todo
, fade
, late
);
1646 fade
= minf(1.0f
, fade
+ todo
*FadeStep
);
1650 /* Generate early reflections. */
1651 EarlyReflection_Unfaded(State
, todo
, fade
, early
);
1653 /* Generate late reverb. */
1654 LateReverb_Unfaded(State
, todo
, fade
, late
);
1657 /* Step all delays forward. */
1658 State
->Offset
+= todo
;
1660 if(UNLIKELY(fadeCount
< FADE_SAMPLES
) && (fadeCount
+= todo
) >= FADE_SAMPLES
)
1662 /* Update the cross-fading delay line taps. */
1663 fadeCount
= FADE_SAMPLES
;
1665 for(c
= 0;c
< NUM_LINES
;c
++)
1667 State
->EarlyDelayTap
[c
][0] = State
->EarlyDelayTap
[c
][1];
1668 State
->Early
.VecAp
.Offset
[c
][0] = State
->Early
.VecAp
.Offset
[c
][1];
1669 State
->Early
.Offset
[c
][0] = State
->Early
.Offset
[c
][1];
1670 State
->LateDelayTap
[c
][0] = State
->LateDelayTap
[c
][1];
1671 State
->Late
.VecAp
.Offset
[c
][0] = State
->Late
.VecAp
.Offset
[c
][1];
1672 State
->Late
.Offset
[c
][0] = State
->Late
.Offset
[c
][1];
1676 /* Mix the A-Format results to output, implicitly converting back to
1679 for(c
= 0;c
< NUM_LINES
;c
++)
1680 MixSamples(early
[c
], NumChannels
, SamplesOut
,
1681 State
->Early
.CurrentGain
[c
], State
->Early
.PanGain
[c
],
1682 SamplesToDo
-base
, base
, todo
1684 for(c
= 0;c
< NUM_LINES
;c
++)
1685 MixSamples(late
[c
], NumChannels
, SamplesOut
,
1686 State
->Late
.CurrentGain
[c
], State
->Late
.PanGain
[c
],
1687 SamplesToDo
-base
, base
, todo
1692 State
->FadeCount
= fadeCount
;
1696 typedef struct ReverbStateFactory
{
1697 DERIVE_FROM_TYPE(EffectStateFactory
);
1698 } ReverbStateFactory
;
1700 static ALeffectState
*ReverbStateFactory_create(ReverbStateFactory
* UNUSED(factory
))
1702 ALreverbState
*state
;
1704 NEW_OBJ0(state
, ALreverbState
)();
1705 if(!state
) return NULL
;
1707 return STATIC_CAST(ALeffectState
, state
);
1710 DEFINE_EFFECTSTATEFACTORY_VTABLE(ReverbStateFactory
);
1712 EffectStateFactory
*ReverbStateFactory_getFactory(void)
1714 static ReverbStateFactory ReverbFactory
= { { GET_VTABLE2(ReverbStateFactory
, EffectStateFactory
) } };
1716 return STATIC_CAST(EffectStateFactory
, &ReverbFactory
);
1720 void ALeaxreverb_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
1722 ALeffectProps
*props
= &effect
->Props
;
1725 case AL_EAXREVERB_DECAY_HFLIMIT
:
1726 if(!(val
>= AL_EAXREVERB_MIN_DECAY_HFLIMIT
&& val
<= AL_EAXREVERB_MAX_DECAY_HFLIMIT
))
1727 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay hflimit out of range");
1728 props
->Reverb
.DecayHFLimit
= val
;
1732 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb integer property 0x%04x",
1736 void ALeaxreverb_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
1737 { ALeaxreverb_setParami(effect
, context
, param
, vals
[0]); }
1738 void ALeaxreverb_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
1740 ALeffectProps
*props
= &effect
->Props
;
1743 case AL_EAXREVERB_DENSITY
:
1744 if(!(val
>= AL_EAXREVERB_MIN_DENSITY
&& val
<= AL_EAXREVERB_MAX_DENSITY
))
1745 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb density out of range");
1746 props
->Reverb
.Density
= val
;
1749 case AL_EAXREVERB_DIFFUSION
:
1750 if(!(val
>= AL_EAXREVERB_MIN_DIFFUSION
&& val
<= AL_EAXREVERB_MAX_DIFFUSION
))
1751 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb diffusion out of range");
1752 props
->Reverb
.Diffusion
= val
;
1755 case AL_EAXREVERB_GAIN
:
1756 if(!(val
>= AL_EAXREVERB_MIN_GAIN
&& val
<= AL_EAXREVERB_MAX_GAIN
))
1757 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb gain out of range");
1758 props
->Reverb
.Gain
= val
;
1761 case AL_EAXREVERB_GAINHF
:
1762 if(!(val
>= AL_EAXREVERB_MIN_GAINHF
&& val
<= AL_EAXREVERB_MAX_GAINHF
))
1763 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb gainhf out of range");
1764 props
->Reverb
.GainHF
= val
;
1767 case AL_EAXREVERB_GAINLF
:
1768 if(!(val
>= AL_EAXREVERB_MIN_GAINLF
&& val
<= AL_EAXREVERB_MAX_GAINLF
))
1769 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb gainlf out of range");
1770 props
->Reverb
.GainLF
= val
;
1773 case AL_EAXREVERB_DECAY_TIME
:
1774 if(!(val
>= AL_EAXREVERB_MIN_DECAY_TIME
&& val
<= AL_EAXREVERB_MAX_DECAY_TIME
))
1775 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay time out of range");
1776 props
->Reverb
.DecayTime
= val
;
1779 case AL_EAXREVERB_DECAY_HFRATIO
:
1780 if(!(val
>= AL_EAXREVERB_MIN_DECAY_HFRATIO
&& val
<= AL_EAXREVERB_MAX_DECAY_HFRATIO
))
1781 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay hfratio out of range");
1782 props
->Reverb
.DecayHFRatio
= val
;
1785 case AL_EAXREVERB_DECAY_LFRATIO
:
1786 if(!(val
>= AL_EAXREVERB_MIN_DECAY_LFRATIO
&& val
<= AL_EAXREVERB_MAX_DECAY_LFRATIO
))
1787 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay lfratio out of range");
1788 props
->Reverb
.DecayLFRatio
= val
;
1791 case AL_EAXREVERB_REFLECTIONS_GAIN
:
1792 if(!(val
>= AL_EAXREVERB_MIN_REFLECTIONS_GAIN
&& val
<= AL_EAXREVERB_MAX_REFLECTIONS_GAIN
))
1793 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb reflections gain out of range");
1794 props
->Reverb
.ReflectionsGain
= val
;
1797 case AL_EAXREVERB_REFLECTIONS_DELAY
:
1798 if(!(val
>= AL_EAXREVERB_MIN_REFLECTIONS_DELAY
&& val
<= AL_EAXREVERB_MAX_REFLECTIONS_DELAY
))
1799 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb reflections delay out of range");
1800 props
->Reverb
.ReflectionsDelay
= val
;
1803 case AL_EAXREVERB_LATE_REVERB_GAIN
:
1804 if(!(val
>= AL_EAXREVERB_MIN_LATE_REVERB_GAIN
&& val
<= AL_EAXREVERB_MAX_LATE_REVERB_GAIN
))
1805 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb late reverb gain out of range");
1806 props
->Reverb
.LateReverbGain
= val
;
1809 case AL_EAXREVERB_LATE_REVERB_DELAY
:
1810 if(!(val
>= AL_EAXREVERB_MIN_LATE_REVERB_DELAY
&& val
<= AL_EAXREVERB_MAX_LATE_REVERB_DELAY
))
1811 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb late reverb delay out of range");
1812 props
->Reverb
.LateReverbDelay
= val
;
1815 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF
:
1816 if(!(val
>= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF
&& val
<= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF
))
1817 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb air absorption gainhf out of range");
1818 props
->Reverb
.AirAbsorptionGainHF
= val
;
1821 case AL_EAXREVERB_ECHO_TIME
:
1822 if(!(val
>= AL_EAXREVERB_MIN_ECHO_TIME
&& val
<= AL_EAXREVERB_MAX_ECHO_TIME
))
1823 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb echo time out of range");
1824 props
->Reverb
.EchoTime
= val
;
1827 case AL_EAXREVERB_ECHO_DEPTH
:
1828 if(!(val
>= AL_EAXREVERB_MIN_ECHO_DEPTH
&& val
<= AL_EAXREVERB_MAX_ECHO_DEPTH
))
1829 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb echo depth out of range");
1830 props
->Reverb
.EchoDepth
= val
;
1833 case AL_EAXREVERB_MODULATION_TIME
:
1834 if(!(val
>= AL_EAXREVERB_MIN_MODULATION_TIME
&& val
<= AL_EAXREVERB_MAX_MODULATION_TIME
))
1835 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb modulation time out of range");
1836 props
->Reverb
.ModulationTime
= val
;
1839 case AL_EAXREVERB_MODULATION_DEPTH
:
1840 if(!(val
>= AL_EAXREVERB_MIN_MODULATION_DEPTH
&& val
<= AL_EAXREVERB_MAX_MODULATION_DEPTH
))
1841 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb modulation depth out of range");
1842 props
->Reverb
.ModulationDepth
= val
;
1845 case AL_EAXREVERB_HFREFERENCE
:
1846 if(!(val
>= AL_EAXREVERB_MIN_HFREFERENCE
&& val
<= AL_EAXREVERB_MAX_HFREFERENCE
))
1847 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb hfreference out of range");
1848 props
->Reverb
.HFReference
= val
;
1851 case AL_EAXREVERB_LFREFERENCE
:
1852 if(!(val
>= AL_EAXREVERB_MIN_LFREFERENCE
&& val
<= AL_EAXREVERB_MAX_LFREFERENCE
))
1853 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb lfreference out of range");
1854 props
->Reverb
.LFReference
= val
;
1857 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR
:
1858 if(!(val
>= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR
&& val
<= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR
))
1859 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb room rolloff factor out of range");
1860 props
->Reverb
.RoomRolloffFactor
= val
;
1864 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb float property 0x%04x",
1868 void ALeaxreverb_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
1870 ALeffectProps
*props
= &effect
->Props
;
1873 case AL_EAXREVERB_REFLECTIONS_PAN
:
1874 if(!(isfinite(vals
[0]) && isfinite(vals
[1]) && isfinite(vals
[2])))
1875 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb reflections pan out of range");
1876 props
->Reverb
.ReflectionsPan
[0] = vals
[0];
1877 props
->Reverb
.ReflectionsPan
[1] = vals
[1];
1878 props
->Reverb
.ReflectionsPan
[2] = vals
[2];
1880 case AL_EAXREVERB_LATE_REVERB_PAN
:
1881 if(!(isfinite(vals
[0]) && isfinite(vals
[1]) && isfinite(vals
[2])))
1882 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb late reverb pan out of range");
1883 props
->Reverb
.LateReverbPan
[0] = vals
[0];
1884 props
->Reverb
.LateReverbPan
[1] = vals
[1];
1885 props
->Reverb
.LateReverbPan
[2] = vals
[2];
1889 ALeaxreverb_setParamf(effect
, context
, param
, vals
[0]);
1894 void ALeaxreverb_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
1896 const ALeffectProps
*props
= &effect
->Props
;
1899 case AL_EAXREVERB_DECAY_HFLIMIT
:
1900 *val
= props
->Reverb
.DecayHFLimit
;
1904 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb integer property 0x%04x",
1908 void ALeaxreverb_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
1909 { ALeaxreverb_getParami(effect
, context
, param
, vals
); }
1910 void ALeaxreverb_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
1912 const ALeffectProps
*props
= &effect
->Props
;
1915 case AL_EAXREVERB_DENSITY
:
1916 *val
= props
->Reverb
.Density
;
1919 case AL_EAXREVERB_DIFFUSION
:
1920 *val
= props
->Reverb
.Diffusion
;
1923 case AL_EAXREVERB_GAIN
:
1924 *val
= props
->Reverb
.Gain
;
1927 case AL_EAXREVERB_GAINHF
:
1928 *val
= props
->Reverb
.GainHF
;
1931 case AL_EAXREVERB_GAINLF
:
1932 *val
= props
->Reverb
.GainLF
;
1935 case AL_EAXREVERB_DECAY_TIME
:
1936 *val
= props
->Reverb
.DecayTime
;
1939 case AL_EAXREVERB_DECAY_HFRATIO
:
1940 *val
= props
->Reverb
.DecayHFRatio
;
1943 case AL_EAXREVERB_DECAY_LFRATIO
:
1944 *val
= props
->Reverb
.DecayLFRatio
;
1947 case AL_EAXREVERB_REFLECTIONS_GAIN
:
1948 *val
= props
->Reverb
.ReflectionsGain
;
1951 case AL_EAXREVERB_REFLECTIONS_DELAY
:
1952 *val
= props
->Reverb
.ReflectionsDelay
;
1955 case AL_EAXREVERB_LATE_REVERB_GAIN
:
1956 *val
= props
->Reverb
.LateReverbGain
;
1959 case AL_EAXREVERB_LATE_REVERB_DELAY
:
1960 *val
= props
->Reverb
.LateReverbDelay
;
1963 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF
:
1964 *val
= props
->Reverb
.AirAbsorptionGainHF
;
1967 case AL_EAXREVERB_ECHO_TIME
:
1968 *val
= props
->Reverb
.EchoTime
;
1971 case AL_EAXREVERB_ECHO_DEPTH
:
1972 *val
= props
->Reverb
.EchoDepth
;
1975 case AL_EAXREVERB_MODULATION_TIME
:
1976 *val
= props
->Reverb
.ModulationTime
;
1979 case AL_EAXREVERB_MODULATION_DEPTH
:
1980 *val
= props
->Reverb
.ModulationDepth
;
1983 case AL_EAXREVERB_HFREFERENCE
:
1984 *val
= props
->Reverb
.HFReference
;
1987 case AL_EAXREVERB_LFREFERENCE
:
1988 *val
= props
->Reverb
.LFReference
;
1991 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR
:
1992 *val
= props
->Reverb
.RoomRolloffFactor
;
1996 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb float property 0x%04x",
2000 void ALeaxreverb_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
2002 const ALeffectProps
*props
= &effect
->Props
;
2005 case AL_EAXREVERB_REFLECTIONS_PAN
:
2006 vals
[0] = props
->Reverb
.ReflectionsPan
[0];
2007 vals
[1] = props
->Reverb
.ReflectionsPan
[1];
2008 vals
[2] = props
->Reverb
.ReflectionsPan
[2];
2010 case AL_EAXREVERB_LATE_REVERB_PAN
:
2011 vals
[0] = props
->Reverb
.LateReverbPan
[0];
2012 vals
[1] = props
->Reverb
.LateReverbPan
[1];
2013 vals
[2] = props
->Reverb
.LateReverbPan
[2];
2017 ALeaxreverb_getParamf(effect
, context
, param
, vals
);
2022 DEFINE_ALEFFECT_VTABLE(ALeaxreverb
);
2024 void ALreverb_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
2026 ALeffectProps
*props
= &effect
->Props
;
2029 case AL_REVERB_DECAY_HFLIMIT
:
2030 if(!(val
>= AL_REVERB_MIN_DECAY_HFLIMIT
&& val
<= AL_REVERB_MAX_DECAY_HFLIMIT
))
2031 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb decay hflimit out of range");
2032 props
->Reverb
.DecayHFLimit
= val
;
2036 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb integer property 0x%04x", param
);
2039 void ALreverb_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
2040 { ALreverb_setParami(effect
, context
, param
, vals
[0]); }
2041 void ALreverb_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
2043 ALeffectProps
*props
= &effect
->Props
;
2046 case AL_REVERB_DENSITY
:
2047 if(!(val
>= AL_REVERB_MIN_DENSITY
&& val
<= AL_REVERB_MAX_DENSITY
))
2048 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb density out of range");
2049 props
->Reverb
.Density
= val
;
2052 case AL_REVERB_DIFFUSION
:
2053 if(!(val
>= AL_REVERB_MIN_DIFFUSION
&& val
<= AL_REVERB_MAX_DIFFUSION
))
2054 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb diffusion out of range");
2055 props
->Reverb
.Diffusion
= val
;
2058 case AL_REVERB_GAIN
:
2059 if(!(val
>= AL_REVERB_MIN_GAIN
&& val
<= AL_REVERB_MAX_GAIN
))
2060 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb gain out of range");
2061 props
->Reverb
.Gain
= val
;
2064 case AL_REVERB_GAINHF
:
2065 if(!(val
>= AL_REVERB_MIN_GAINHF
&& val
<= AL_REVERB_MAX_GAINHF
))
2066 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb gainhf out of range");
2067 props
->Reverb
.GainHF
= val
;
2070 case AL_REVERB_DECAY_TIME
:
2071 if(!(val
>= AL_REVERB_MIN_DECAY_TIME
&& val
<= AL_REVERB_MAX_DECAY_TIME
))
2072 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb decay time out of range");
2073 props
->Reverb
.DecayTime
= val
;
2076 case AL_REVERB_DECAY_HFRATIO
:
2077 if(!(val
>= AL_REVERB_MIN_DECAY_HFRATIO
&& val
<= AL_REVERB_MAX_DECAY_HFRATIO
))
2078 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb decay hfratio out of range");
2079 props
->Reverb
.DecayHFRatio
= val
;
2082 case AL_REVERB_REFLECTIONS_GAIN
:
2083 if(!(val
>= AL_REVERB_MIN_REFLECTIONS_GAIN
&& val
<= AL_REVERB_MAX_REFLECTIONS_GAIN
))
2084 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb reflections gain out of range");
2085 props
->Reverb
.ReflectionsGain
= val
;
2088 case AL_REVERB_REFLECTIONS_DELAY
:
2089 if(!(val
>= AL_REVERB_MIN_REFLECTIONS_DELAY
&& val
<= AL_REVERB_MAX_REFLECTIONS_DELAY
))
2090 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb reflections delay out of range");
2091 props
->Reverb
.ReflectionsDelay
= val
;
2094 case AL_REVERB_LATE_REVERB_GAIN
:
2095 if(!(val
>= AL_REVERB_MIN_LATE_REVERB_GAIN
&& val
<= AL_REVERB_MAX_LATE_REVERB_GAIN
))
2096 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb late reverb gain out of range");
2097 props
->Reverb
.LateReverbGain
= val
;
2100 case AL_REVERB_LATE_REVERB_DELAY
:
2101 if(!(val
>= AL_REVERB_MIN_LATE_REVERB_DELAY
&& val
<= AL_REVERB_MAX_LATE_REVERB_DELAY
))
2102 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb late reverb delay out of range");
2103 props
->Reverb
.LateReverbDelay
= val
;
2106 case AL_REVERB_AIR_ABSORPTION_GAINHF
:
2107 if(!(val
>= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF
&& val
<= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF
))
2108 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb air absorption gainhf out of range");
2109 props
->Reverb
.AirAbsorptionGainHF
= val
;
2112 case AL_REVERB_ROOM_ROLLOFF_FACTOR
:
2113 if(!(val
>= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR
&& val
<= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR
))
2114 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb room rolloff factor out of range");
2115 props
->Reverb
.RoomRolloffFactor
= val
;
2119 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb float property 0x%04x", param
);
2122 void ALreverb_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
2123 { ALreverb_setParamf(effect
, context
, param
, vals
[0]); }
2125 void ALreverb_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
2127 const ALeffectProps
*props
= &effect
->Props
;
2130 case AL_REVERB_DECAY_HFLIMIT
:
2131 *val
= props
->Reverb
.DecayHFLimit
;
2135 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb integer property 0x%04x", param
);
2138 void ALreverb_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
2139 { ALreverb_getParami(effect
, context
, param
, vals
); }
2140 void ALreverb_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
2142 const ALeffectProps
*props
= &effect
->Props
;
2145 case AL_REVERB_DENSITY
:
2146 *val
= props
->Reverb
.Density
;
2149 case AL_REVERB_DIFFUSION
:
2150 *val
= props
->Reverb
.Diffusion
;
2153 case AL_REVERB_GAIN
:
2154 *val
= props
->Reverb
.Gain
;
2157 case AL_REVERB_GAINHF
:
2158 *val
= props
->Reverb
.GainHF
;
2161 case AL_REVERB_DECAY_TIME
:
2162 *val
= props
->Reverb
.DecayTime
;
2165 case AL_REVERB_DECAY_HFRATIO
:
2166 *val
= props
->Reverb
.DecayHFRatio
;
2169 case AL_REVERB_REFLECTIONS_GAIN
:
2170 *val
= props
->Reverb
.ReflectionsGain
;
2173 case AL_REVERB_REFLECTIONS_DELAY
:
2174 *val
= props
->Reverb
.ReflectionsDelay
;
2177 case AL_REVERB_LATE_REVERB_GAIN
:
2178 *val
= props
->Reverb
.LateReverbGain
;
2181 case AL_REVERB_LATE_REVERB_DELAY
:
2182 *val
= props
->Reverb
.LateReverbDelay
;
2185 case AL_REVERB_AIR_ABSORPTION_GAINHF
:
2186 *val
= props
->Reverb
.AirAbsorptionGainHF
;
2189 case AL_REVERB_ROOM_ROLLOFF_FACTOR
:
2190 *val
= props
->Reverb
.RoomRolloffFactor
;
2194 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb float property 0x%04x", param
);
2197 void ALreverb_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
2198 { ALreverb_getParamf(effect
, context
, param
, vals
); }
2200 DEFINE_ALEFFECT_VTABLE(ALreverb
);