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"
30 #include "alListener.h"
32 #include "filters/defs.h"
34 /* This is a user config option for modifying the overall output of the reverb
37 ALfloat ReverbBoost
= 1.0f
;
39 /* This is the maximum number of samples processed for each inner loop
41 #define MAX_UPDATE_SAMPLES 256
43 /* The number of samples used for cross-faded delay lines. This can be used
44 * to balance the compensation for abrupt line changes and attenuation due to
45 * minimally lengthed recursive lines. Try to keep this below the device
48 #define FADE_SAMPLES 128
50 /* The number of spatialized lines or channels to process. Four channels allows
51 * for a 3D A-Format response. NOTE: This can't be changed without taking care
52 * of the conversion matrices, and a few places where the length arrays are
53 * assumed to have 4 elements.
58 /* The B-Format to A-Format conversion matrix. The arrangement of rows is
59 * deliberately chosen to align the resulting lines to their spatial opposites
60 * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
61 * back left). It's not quite opposite, since the A-Format results in a
62 * tetrahedron, but it's close enough. Should the model be extended to 8-lines
63 * in the future, true opposites can be used.
65 static const aluMatrixf B2A
= {{
66 { 0.288675134595f
, 0.288675134595f
, 0.288675134595f
, 0.288675134595f
},
67 { 0.288675134595f
, -0.288675134595f
, -0.288675134595f
, 0.288675134595f
},
68 { 0.288675134595f
, 0.288675134595f
, -0.288675134595f
, -0.288675134595f
},
69 { 0.288675134595f
, -0.288675134595f
, 0.288675134595f
, -0.288675134595f
}
72 /* Converts A-Format to B-Format. */
73 static const aluMatrixf A2B
= {{
74 { 0.866025403785f
, 0.866025403785f
, 0.866025403785f
, 0.866025403785f
},
75 { 0.866025403785f
, -0.866025403785f
, 0.866025403785f
, -0.866025403785f
},
76 { 0.866025403785f
, -0.866025403785f
, -0.866025403785f
, 0.866025403785f
},
77 { 0.866025403785f
, 0.866025403785f
, -0.866025403785f
, -0.866025403785f
}
80 static const ALfloat FadeStep
= 1.0f
/ FADE_SAMPLES
;
82 /* The all-pass and delay lines have a variable length dependent on the
83 * effect's density parameter, which helps alter the perceived environment
84 * size. The size-to-density conversion is a cubed scale:
86 * density = min(1.0, pow(size, 3.0) / DENSITY_SCALE);
88 * The line lengths scale linearly with room size, so the inverse density
89 * conversion is needed, taking the cube root of the re-scaled density to
90 * calculate the line length multiplier:
92 * length_mult = max(5.0, cbrtf(density*DENSITY_SCALE));
94 * The density scale below will result in a max line multiplier of 50, for an
95 * effective size range of 5m to 50m.
97 static const ALfloat DENSITY_SCALE
= 125000.0f
;
99 /* All delay line lengths are specified in seconds.
101 * To approximate early reflections, we break them up into primary (those
102 * arriving from the same direction as the source) and secondary (those
103 * arriving from the opposite direction).
105 * The early taps decorrelate the 4-channel signal to approximate an average
106 * room response for the primary reflections after the initial early delay.
108 * Given an average room dimension (d_a) and the speed of sound (c) we can
109 * calculate the average reflection delay (r_a) regardless of listener and
110 * source positions as:
115 * This can extended to finding the average difference (r_d) between the
116 * maximum (r_1) and minimum (r_0) reflection delays:
127 * As can be determined by integrating the 1D model with a source (s) and
128 * listener (l) positioned across the dimension of length (d_a):
130 * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
132 * The initial taps (T_(i=0)^N) are then specified by taking a power series
133 * that ranges between r_0 and half of r_1 less r_0:
135 * R_i = 2^(i / (2 N - 1)) r_d
136 * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
139 * = (2^(i / (2 N - 1)) - 1) r_d
141 * Assuming an average of 1m, we get the following taps:
143 static const ALfloat EARLY_TAP_LENGTHS
[NUM_LINES
] =
145 0.0000000e+0f
, 2.0213520e-4f
, 4.2531060e-4f
, 6.7171600e-4f
148 /* The early all-pass filter lengths are based on the early tap lengths:
152 * Where a is the approximate maximum all-pass cycle limit (20).
154 static const ALfloat EARLY_ALLPASS_LENGTHS
[NUM_LINES
] =
156 9.7096800e-5f
, 1.0720356e-4f
, 1.1836234e-4f
, 1.3068260e-4f
159 /* The early delay lines are used to transform the primary reflections into
160 * the secondary reflections. The A-format is arranged in such a way that
161 * the channels/lines are spatially opposite:
163 * C_i is opposite C_(N-i-1)
165 * The delays of the two opposing reflections (R_i and O_i) from a source
166 * anywhere along a particular dimension always sum to twice its full delay:
170 * With that in mind we can determine the delay between the two reflections
171 * and thus specify our early line lengths (L_(i=0)^N) using:
173 * O_i = 2 r_a - R_(N-i-1)
174 * L_i = O_i - R_(N-i-1)
175 * = 2 (r_a - R_(N-i-1))
176 * = 2 (r_a - T_(N-i-1) - r_0)
177 * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
179 * Using an average dimension of 1m, we get:
181 static const ALfloat EARLY_LINE_LENGTHS
[NUM_LINES
] =
183 5.9850400e-4f
, 1.0913150e-3f
, 1.5376658e-3f
, 1.9419362e-3f
186 /* The late all-pass filter lengths are based on the late line lengths:
188 * A_i = (5 / 3) L_i / r_1
190 static const ALfloat LATE_ALLPASS_LENGTHS
[NUM_LINES
] =
192 1.6182800e-4f
, 2.0389060e-4f
, 2.8159360e-4f
, 3.2365600e-4f
195 /* The late lines are used to approximate the decaying cycle of recursive
198 * Splitting the lines in half, we start with the shortest reflection paths
201 * L_i = 2^(i / (N - 1)) r_d
203 * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
205 * L_i = 2 r_a - L_(i-N/2)
206 * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
208 * For our 1m average room, we get:
210 static const ALfloat LATE_LINE_LENGTHS
[NUM_LINES
] =
212 1.9419362e-3f
, 2.4466860e-3f
, 3.3791220e-3f
, 3.8838720e-3f
216 typedef struct DelayLineI
{
217 /* The delay lines use interleaved samples, with the lengths being powers
218 * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
221 ALfloat (*Line
)[NUM_LINES
];
224 typedef struct VecAllpass
{
226 ALsizei Offset
[NUM_LINES
][2];
229 typedef struct T60Filter
{
230 /* Two filters are used to adjust the signal. One to control the low
231 * frequencies, and one to control the high frequencies. The HF filter also
232 * adjusts the overall output gain, affecting the remaining mid-band.
237 /* The HF and LF filters each keep a state of the last input and last
244 typedef struct EarlyReflections
{
245 /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
246 * The spread from this filter also helps smooth out the reverb tail.
250 /* An echo line is used to complete the second half of the early
254 ALsizei Offset
[NUM_LINES
][2];
255 ALfloat Coeff
[NUM_LINES
];
257 /* The gain for each output channel based on 3D panning. */
258 ALfloat CurrentGain
[NUM_LINES
][MAX_OUTPUT_CHANNELS
];
259 ALfloat PanGain
[NUM_LINES
][MAX_OUTPUT_CHANNELS
];
262 typedef struct LateReverb
{
263 /* Attenuation to compensate for the modal density and decay rate of the
268 /* A recursive delay line is used fill in the reverb tail. */
270 ALsizei Offset
[NUM_LINES
][2];
272 /* T60 decay filters are used to simulate absorption. */
273 T60Filter T60
[NUM_LINES
];
275 /* A Gerzon vector all-pass filter is used to simulate diffusion. */
278 /* The gain for each output channel based on 3D panning. */
279 ALfloat CurrentGain
[NUM_LINES
][MAX_OUTPUT_CHANNELS
];
280 ALfloat PanGain
[NUM_LINES
][MAX_OUTPUT_CHANNELS
];
283 typedef struct ALreverbState
{
284 DERIVE_FROM_TYPE(ALeffectState
);
286 /* All delay lines are allocated as a single buffer to reduce memory
287 * fragmentation and management code.
289 ALfloat
*SampleBuffer
;
292 /* Master effect filters */
298 /* Core delay line (early reflections and late reverb tap from this). */
301 /* Tap points for early reflection delay. */
302 ALsizei EarlyDelayTap
[NUM_LINES
][2];
303 ALfloat EarlyDelayCoeff
[NUM_LINES
];
305 /* Tap points for late reverb feed and delay. */
307 ALsizei LateDelayTap
[NUM_LINES
][2];
309 /* The feed-back and feed-forward all-pass coefficient. */
312 /* Coefficients for the all-pass and line scattering matrices. */
316 EarlyReflections Early
;
320 /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
323 /* The current write offset for all delay lines. */
326 /* Temporary storage used when processing. */
327 alignas(16) ALfloat AFormatSamples
[NUM_LINES
][MAX_UPDATE_SAMPLES
];
328 alignas(16) ALfloat ReverbSamples
[NUM_LINES
][MAX_UPDATE_SAMPLES
];
329 alignas(16) ALfloat EarlySamples
[NUM_LINES
][MAX_UPDATE_SAMPLES
];
332 static ALvoid
ALreverbState_Destruct(ALreverbState
*State
);
333 static ALboolean
ALreverbState_deviceUpdate(ALreverbState
*State
, ALCdevice
*Device
);
334 static ALvoid
ALreverbState_update(ALreverbState
*State
, const ALCcontext
*Context
, const ALeffectslot
*Slot
, const ALeffectProps
*props
);
335 static ALvoid
ALreverbState_process(ALreverbState
*State
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
);
336 DECLARE_DEFAULT_ALLOCATORS(ALreverbState
)
338 DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState
);
340 static void ALreverbState_Construct(ALreverbState
*state
)
344 ALeffectState_Construct(STATIC_CAST(ALeffectState
, state
));
345 SET_VTABLE2(ALreverbState
, ALeffectState
, state
);
347 state
->TotalSamples
= 0;
348 state
->SampleBuffer
= NULL
;
350 for(i
= 0;i
< NUM_LINES
;i
++)
352 BiquadState_clear(&state
->Filter
[i
].Lp
);
353 BiquadState_clear(&state
->Filter
[i
].Hp
);
356 state
->Delay
.Mask
= 0;
357 state
->Delay
.Line
= NULL
;
359 for(i
= 0;i
< NUM_LINES
;i
++)
361 state
->EarlyDelayTap
[i
][0] = 0;
362 state
->EarlyDelayTap
[i
][1] = 0;
363 state
->EarlyDelayCoeff
[i
] = 0.0f
;
366 state
->LateFeedTap
= 0;
368 for(i
= 0;i
< NUM_LINES
;i
++)
370 state
->LateDelayTap
[i
][0] = 0;
371 state
->LateDelayTap
[i
][1] = 0;
374 state
->ApFeedCoeff
= 0.0f
;
378 state
->Early
.VecAp
.Delay
.Mask
= 0;
379 state
->Early
.VecAp
.Delay
.Line
= NULL
;
380 state
->Early
.Delay
.Mask
= 0;
381 state
->Early
.Delay
.Line
= NULL
;
382 for(i
= 0;i
< NUM_LINES
;i
++)
384 state
->Early
.VecAp
.Offset
[i
][0] = 0;
385 state
->Early
.VecAp
.Offset
[i
][1] = 0;
386 state
->Early
.Offset
[i
][0] = 0;
387 state
->Early
.Offset
[i
][1] = 0;
388 state
->Early
.Coeff
[i
] = 0.0f
;
391 state
->Late
.DensityGain
= 0.0f
;
393 state
->Late
.Delay
.Mask
= 0;
394 state
->Late
.Delay
.Line
= NULL
;
395 state
->Late
.VecAp
.Delay
.Mask
= 0;
396 state
->Late
.VecAp
.Delay
.Line
= NULL
;
397 for(i
= 0;i
< NUM_LINES
;i
++)
399 state
->Late
.Offset
[i
][0] = 0;
400 state
->Late
.Offset
[i
][1] = 0;
402 state
->Late
.VecAp
.Offset
[i
][0] = 0;
403 state
->Late
.VecAp
.Offset
[i
][1] = 0;
407 state
->Late
.T60
[i
].HFCoeffs
[j
] = 0.0f
;
408 state
->Late
.T60
[i
].LFCoeffs
[j
] = 0.0f
;
410 state
->Late
.T60
[i
].HFState
[0] = 0.0f
;
411 state
->Late
.T60
[i
].HFState
[1] = 0.0f
;
412 state
->Late
.T60
[i
].LFState
[0] = 0.0f
;
413 state
->Late
.T60
[i
].LFState
[1] = 0.0f
;
416 for(i
= 0;i
< NUM_LINES
;i
++)
418 for(j
= 0;j
< MAX_OUTPUT_CHANNELS
;j
++)
420 state
->Early
.CurrentGain
[i
][j
] = 0.0f
;
421 state
->Early
.PanGain
[i
][j
] = 0.0f
;
422 state
->Late
.CurrentGain
[i
][j
] = 0.0f
;
423 state
->Late
.PanGain
[i
][j
] = 0.0f
;
427 state
->FadeCount
= 0;
431 static ALvoid
ALreverbState_Destruct(ALreverbState
*State
)
433 al_free(State
->SampleBuffer
);
434 State
->SampleBuffer
= NULL
;
436 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,State
));
439 /**************************************
441 **************************************/
443 static inline ALfloat
CalcDelayLengthMult(ALfloat density
)
445 return maxf(5.0f
, cbrtf(density
*DENSITY_SCALE
));
448 /* Given the allocated sample buffer, this function updates each delay line
451 static inline ALvoid
RealizeLineOffset(ALfloat
*sampleBuffer
, DelayLineI
*Delay
)
455 ALfloat (*f4
)[NUM_LINES
];
457 u
.f
= &sampleBuffer
[(ptrdiff_t)Delay
->Line
* NUM_LINES
];
461 /* Calculate the length of a delay line and store its mask and offset. */
462 static ALuint
CalcLineLength(const ALfloat length
, const ptrdiff_t offset
, const ALuint frequency
,
463 const ALuint extra
, DelayLineI
*Delay
)
467 /* All line lengths are powers of 2, calculated from their lengths in
468 * seconds, rounded up.
470 samples
= fastf2i(ceilf(length
*frequency
));
471 samples
= NextPowerOf2(samples
+ extra
);
473 /* All lines share a single sample buffer. */
474 Delay
->Mask
= samples
- 1;
475 Delay
->Line
= (ALfloat(*)[NUM_LINES
])offset
;
477 /* Return the sample count for accumulation. */
481 /* Calculates the delay line metrics and allocates the shared sample buffer
482 * for all lines given the sample rate (frequency). If an allocation failure
483 * occurs, it returns AL_FALSE.
485 static ALboolean
AllocLines(const ALuint frequency
, ALreverbState
*State
)
487 ALuint totalSamples
, i
;
488 ALfloat multiplier
, length
;
490 /* All delay line lengths are calculated to accomodate the full range of
491 * lengths given their respective paramters.
495 /* Multiplier for the maximum density value, i.e. density=1, which is
496 * actually the least density...
498 multiplier
= CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY
);
500 /* The main delay length includes the maximum early reflection delay, the
501 * largest early tap width, the maximum late reverb delay, and the
502 * largest late tap width. Finally, it must also be extended by the
503 * update size (MAX_UPDATE_SAMPLES) for block processing.
505 length
= AL_EAXREVERB_MAX_REFLECTIONS_DELAY
+ EARLY_TAP_LENGTHS
[NUM_LINES
-1]*multiplier
+
506 AL_EAXREVERB_MAX_LATE_REVERB_DELAY
+
507 (LATE_LINE_LENGTHS
[NUM_LINES
-1] - LATE_LINE_LENGTHS
[0])*0.25f
*multiplier
;
508 totalSamples
+= CalcLineLength(length
, totalSamples
, frequency
, MAX_UPDATE_SAMPLES
,
511 /* The early vector all-pass line. */
512 length
= EARLY_ALLPASS_LENGTHS
[NUM_LINES
-1] * multiplier
;
513 totalSamples
+= CalcLineLength(length
, totalSamples
, frequency
, 0,
514 &State
->Early
.VecAp
.Delay
);
516 /* The early reflection line. */
517 length
= EARLY_LINE_LENGTHS
[NUM_LINES
-1] * multiplier
;
518 totalSamples
+= CalcLineLength(length
, totalSamples
, frequency
, 0,
519 &State
->Early
.Delay
);
521 /* The late vector all-pass line. */
522 length
= LATE_ALLPASS_LENGTHS
[NUM_LINES
-1] * multiplier
;
523 totalSamples
+= CalcLineLength(length
, totalSamples
, frequency
, 0,
524 &State
->Late
.VecAp
.Delay
);
526 /* The late delay lines are calculated from the larger of the maximum
527 * density line length or the maximum echo time.
529 length
= maxf(AL_EAXREVERB_MAX_ECHO_TIME
, LATE_LINE_LENGTHS
[NUM_LINES
-1]*multiplier
);
530 totalSamples
+= CalcLineLength(length
, totalSamples
, frequency
, 0,
533 if(totalSamples
!= State
->TotalSamples
)
537 TRACE("New reverb buffer length: %ux4 samples\n", totalSamples
);
538 newBuffer
= al_calloc(16, sizeof(ALfloat
[NUM_LINES
]) * totalSamples
);
539 if(!newBuffer
) return AL_FALSE
;
541 al_free(State
->SampleBuffer
);
542 State
->SampleBuffer
= newBuffer
;
543 State
->TotalSamples
= totalSamples
;
546 /* Update all delays to reflect the new sample buffer. */
547 RealizeLineOffset(State
->SampleBuffer
, &State
->Delay
);
548 RealizeLineOffset(State
->SampleBuffer
, &State
->Early
.VecAp
.Delay
);
549 RealizeLineOffset(State
->SampleBuffer
, &State
->Early
.Delay
);
550 RealizeLineOffset(State
->SampleBuffer
, &State
->Late
.VecAp
.Delay
);
551 RealizeLineOffset(State
->SampleBuffer
, &State
->Late
.Delay
);
553 /* Clear the sample buffer. */
554 for(i
= 0;i
< State
->TotalSamples
;i
++)
555 State
->SampleBuffer
[i
] = 0.0f
;
560 static ALboolean
ALreverbState_deviceUpdate(ALreverbState
*State
, ALCdevice
*Device
)
562 ALuint frequency
= Device
->Frequency
;
565 /* Allocate the delay lines. */
566 if(!AllocLines(frequency
, State
))
569 multiplier
= CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY
);
571 /* The late feed taps are set a fixed position past the latest delay tap. */
572 State
->LateFeedTap
= fastf2i((AL_EAXREVERB_MAX_REFLECTIONS_DELAY
+
573 EARLY_TAP_LENGTHS
[NUM_LINES
-1]*multiplier
) *
579 /**************************************
581 **************************************/
583 /* Calculate a decay coefficient given the length of each cycle and the time
584 * until the decay reaches -60 dB.
586 static inline ALfloat
CalcDecayCoeff(const ALfloat length
, const ALfloat decayTime
)
588 return powf(REVERB_DECAY_GAIN
, length
/decayTime
);
591 /* Calculate a decay length from a coefficient and the time until the decay
594 static inline ALfloat
CalcDecayLength(const ALfloat coeff
, const ALfloat decayTime
)
596 return log10f(coeff
) * decayTime
/ log10f(REVERB_DECAY_GAIN
);
599 /* Calculate an attenuation to be applied to the input of any echo models to
600 * compensate for modal density and decay time.
602 static inline ALfloat
CalcDensityGain(const ALfloat a
)
604 /* The energy of a signal can be obtained by finding the area under the
605 * squared signal. This takes the form of Sum(x_n^2), where x is the
606 * amplitude for the sample n.
608 * Decaying feedback matches exponential decay of the form Sum(a^n),
609 * where a is the attenuation coefficient, and n is the sample. The area
610 * under this decay curve can be calculated as: 1 / (1 - a).
612 * Modifying the above equation to find the area under the squared curve
613 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
614 * calculated by inverting the square root of this approximation,
615 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
617 return sqrtf(1.0f
- a
*a
);
620 /* Calculate the scattering matrix coefficients given a diffusion factor. */
621 static inline ALvoid
CalcMatrixCoeffs(const ALfloat diffusion
, ALfloat
*x
, ALfloat
*y
)
625 /* The matrix is of order 4, so n is sqrt(4 - 1). */
627 t
= diffusion
* atanf(n
);
629 /* Calculate the first mixing matrix coefficient. */
631 /* Calculate the second mixing matrix coefficient. */
635 /* Calculate the limited HF ratio for use with the late reverb low-pass
638 static ALfloat
CalcLimitedHfRatio(const ALfloat hfRatio
, const ALfloat airAbsorptionGainHF
,
639 const ALfloat decayTime
, const ALfloat SpeedOfSound
)
643 /* Find the attenuation due to air absorption in dB (converting delay
644 * time to meters using the speed of sound). Then reversing the decay
645 * equation, solve for HF ratio. The delay length is cancelled out of
646 * the equation, so it can be calculated once for all lines.
648 limitRatio
= 1.0f
/ (CalcDecayLength(airAbsorptionGainHF
, decayTime
) * SpeedOfSound
);
650 /* Using the limit calculated above, apply the upper bound to the HF ratio.
652 return minf(limitRatio
, hfRatio
);
655 /* Calculates the first-order high-pass coefficients following the I3DL2
656 * reference model. This is the transfer function:
659 * H(z) = p ------------
662 * And this is the I3DL2 coefficient calculation given gain (g) and reference
663 * angular frequency (w):
666 * p = ------------------------------------------------------
667 * g cos(w) + sqrt((cos(w) - 1) (g^2 cos(w) + g^2 - 2))
669 * The coefficient is applied to the partial differential filter equation as:
674 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
677 static inline void CalcHighpassCoeffs(const ALfloat gain
, const ALfloat w
, ALfloat coeffs
[3])
679 ALfloat g
, g2
, cw
, p
;
689 g
= maxf(0.001f
, gain
);
692 p
= g
/ (g
*cw
+ sqrtf((cw
- 1.0f
) * (g2
*cw
+ g2
- 2.0f
)));
699 /* Calculates the first-order low-pass coefficients following the I3DL2
700 * reference model. This is the transfer function:
703 * H(z) = ----------------
706 * And this is the I3DL2 coefficient calculation given gain (g) and reference
707 * angular frequency (w):
709 * 1 - g^2 cos(w) - sqrt(2 g^2 (1 - cos(w)) - g^4 (1 - cos(w)^2))
710 * a = ----------------------------------------------------------------
713 * The coefficient is applied to the partial differential filter equation as:
718 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
721 static inline void CalcLowpassCoeffs(const ALfloat gain
, const ALfloat w
, ALfloat coeffs
[3])
723 ALfloat g
, g2
, cw
, a
;
733 /* Be careful with gains < 0.001, as that causes the coefficient
734 * to head towards 1, which will flatten the signal. */
735 g
= maxf(0.001f
, gain
);
738 a
= (1.0f
- g2
*cw
- sqrtf((2.0f
*g2
*(1.0f
- cw
)) - g2
*g2
*(1.0f
- cw
*cw
))) /
741 coeffs
[0] = 1.0f
- a
;
746 /* Calculates the first-order low-shelf coefficients. The shelf filters are
747 * used in place of low/high-pass filters to preserve the mid-band. This is
748 * the transfer function:
751 * H(z) = ----------------
754 * And these are the coefficient calculations given cut gain (g) and a center
755 * angular frequency (w):
757 * sin(0.5 (pi - w) - 0.25 pi)
758 * p = -----------------------------
759 * sin(0.5 (pi - w) + 0.25 pi)
762 * a = ------- + sqrt((-------)^2 - 1)
766 * b_0 = -------------------
770 * b_1 = -------------------
773 * The coefficients are applied to the partial differential filter equation
777 * c_0 = -------------
781 * c_1 = ----------------
788 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
791 static inline void CalcLowShelfCoeffs(const ALfloat gain
, const ALfloat w
, ALfloat coeffs
[3])
794 ALfloat alpha
, beta0
, beta1
;
804 g
= maxf(0.001f
, gain
);
806 p
= sinf(0.5f
*rw
- 0.25f
*F_PI
) / sinf(0.5f
*rw
+ 0.25f
*F_PI
);
807 n
= (g
+ 1.0f
) / (g
- 1.0f
);
808 alpha
= n
+ sqrtf(n
*n
- 1.0f
);
809 beta0
= (1.0f
+ g
+ (1.0f
- g
)*alpha
) / 2.0f
;
810 beta1
= (1.0f
- g
+ (1.0f
+ g
)*alpha
) / 2.0f
;
812 coeffs
[0] = (beta0
+ p
*beta1
) / (1.0f
+ p
*alpha
);
813 coeffs
[1] = -(beta1
+ p
*beta0
) / (1.0f
+ p
*alpha
);
814 coeffs
[2] = (p
+ alpha
) / (1.0f
+ p
*alpha
);
817 /* Calculates the first-order high-shelf coefficients. The shelf filters are
818 * used in place of low/high-pass filters to preserve the mid-band. This is
819 * the transfer function:
822 * H(z) = ----------------
825 * And these are the coefficient calculations given cut gain (g) and a center
826 * angular frequency (w):
828 * sin(0.5 w - 0.25 pi)
829 * p = ----------------------
830 * sin(0.5 w + 0.25 pi)
833 * a = ------- + sqrt((-------)^2 - 1)
837 * b_0 = -------------------
841 * b_1 = -------------------
844 * The coefficients are applied to the partial differential filter equation
848 * c_0 = -------------
852 * c_1 = -------------
859 * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
862 static inline void CalcHighShelfCoeffs(const ALfloat gain
, const ALfloat w
, ALfloat coeffs
[3])
865 ALfloat alpha
, beta0
, beta1
;
875 g
= maxf(0.001f
, gain
);
876 p
= sinf(0.5f
*w
- 0.25f
*F_PI
) / sinf(0.5f
*w
+ 0.25f
*F_PI
);
877 n
= (g
+ 1.0f
) / (g
- 1.0f
);
878 alpha
= n
+ sqrtf(n
*n
- 1.0f
);
879 beta0
= (1.0f
+ g
+ (1.0f
- g
)*alpha
) / 2.0f
;
880 beta1
= (1.0f
- g
+ (1.0f
+ g
)*alpha
) / 2.0f
;
882 coeffs
[0] = (beta0
+ p
*beta1
) / (1.0f
+ p
*alpha
);
883 coeffs
[1] = (beta1
+ p
*beta0
) / (1.0f
+ p
*alpha
);
884 coeffs
[2] = -(p
+ alpha
) / (1.0f
+ p
*alpha
);
887 /* Calculates the 3-band T60 damping coefficients for a particular delay line
888 * of specified length using a combination of two low/high-pass/shelf or
889 * pass-through filter sections (producing 3 coefficients each) given decay
890 * times for each band split at two (LF/HF) reference frequencies (w).
892 static void CalcT60DampingCoeffs(const ALfloat length
, const ALfloat lfDecayTime
,
893 const ALfloat mfDecayTime
, const ALfloat hfDecayTime
,
894 const ALfloat lfW
, const ALfloat hfW
, ALfloat lfcoeffs
[3],
897 ALfloat lfGain
= CalcDecayCoeff(length
, lfDecayTime
);
898 ALfloat mfGain
= CalcDecayCoeff(length
, mfDecayTime
);
899 ALfloat hfGain
= CalcDecayCoeff(length
, hfDecayTime
);
903 CalcHighpassCoeffs(lfGain
/ mfGain
, lfW
, lfcoeffs
);
906 CalcLowpassCoeffs(hfGain
/ mfGain
, hfW
, hfcoeffs
);
907 hfcoeffs
[0] *= mfGain
; hfcoeffs
[1] *= mfGain
;
911 CalcLowShelfCoeffs(mfGain
/ hfGain
, hfW
, hfcoeffs
);
912 hfcoeffs
[0] *= hfGain
; hfcoeffs
[1] *= hfGain
;
917 CalcHighShelfCoeffs(mfGain
/ lfGain
, lfW
, lfcoeffs
);
920 CalcLowpassCoeffs(hfGain
/ mfGain
, hfW
, hfcoeffs
);
921 hfcoeffs
[0] *= lfGain
; hfcoeffs
[1] *= lfGain
;
925 ALfloat hg
= mfGain
/ lfGain
;
926 ALfloat lg
= mfGain
/ hfGain
;
927 ALfloat mg
= maxf(lfGain
, hfGain
) / maxf(hg
, lg
);
929 CalcLowShelfCoeffs(lg
, hfW
, hfcoeffs
);
930 hfcoeffs
[0] *= mg
; hfcoeffs
[1] *= mg
;
935 /* Update the offsets for the main effect delay line. */
936 static ALvoid
UpdateDelayLine(const ALfloat earlyDelay
, const ALfloat lateDelay
, const ALfloat density
, const ALfloat decayTime
, const ALuint frequency
, ALreverbState
*State
)
938 ALfloat multiplier
, length
;
941 multiplier
= CalcDelayLengthMult(density
);
943 /* Early reflection taps are decorrelated by means of an average room
944 * reflection approximation described above the definition of the taps.
945 * This approximation is linear and so the above density multiplier can
946 * be applied to adjust the width of the taps. A single-band decay
947 * coefficient is applied to simulate initial attenuation and absorption.
949 * Late reverb taps are based on the late line lengths to allow a zero-
950 * delay path and offsets that would continue the propagation naturally
951 * into the late lines.
953 for(i
= 0;i
< NUM_LINES
;i
++)
955 length
= earlyDelay
+ EARLY_TAP_LENGTHS
[i
]*multiplier
;
956 State
->EarlyDelayTap
[i
][1] = fastf2i(length
* frequency
);
958 length
= EARLY_TAP_LENGTHS
[i
]*multiplier
;
959 State
->EarlyDelayCoeff
[i
] = CalcDecayCoeff(length
, decayTime
);
961 length
= lateDelay
+ (LATE_LINE_LENGTHS
[i
] - LATE_LINE_LENGTHS
[0])*0.25f
*multiplier
;
962 State
->LateDelayTap
[i
][1] = State
->LateFeedTap
+ fastf2i(length
* frequency
);
966 /* Update the early reflection line lengths and gain coefficients. */
967 static ALvoid
UpdateEarlyLines(const ALfloat density
, const ALfloat decayTime
, const ALuint frequency
, EarlyReflections
*Early
)
969 ALfloat multiplier
, length
;
972 multiplier
= CalcDelayLengthMult(density
);
974 for(i
= 0;i
< NUM_LINES
;i
++)
976 /* Calculate the length (in seconds) of each all-pass line. */
977 length
= EARLY_ALLPASS_LENGTHS
[i
] * multiplier
;
979 /* Calculate the delay offset for each all-pass line. */
980 Early
->VecAp
.Offset
[i
][1] = fastf2i(length
* frequency
);
982 /* Calculate the length (in seconds) of each delay line. */
983 length
= EARLY_LINE_LENGTHS
[i
] * multiplier
;
985 /* Calculate the delay offset for each delay line. */
986 Early
->Offset
[i
][1] = fastf2i(length
* frequency
);
988 /* Calculate the gain (coefficient) for each line. */
989 Early
->Coeff
[i
] = CalcDecayCoeff(length
, decayTime
);
993 /* Update the late reverb line lengths and T60 coefficients. */
994 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
)
996 ALfloat multiplier
, length
, bandWeights
[3];
999 /* To compensate for changes in modal density and decay time of the late
1000 * reverb signal, the input is attenuated based on the maximal energy of
1001 * the outgoing signal. This approximation is used to keep the apparent
1002 * energy of the signal equal for all ranges of density and decay time.
1004 * The average length of the delay lines is used to calculate the
1005 * attenuation coefficient.
1007 multiplier
= CalcDelayLengthMult(density
);
1008 length
= (LATE_LINE_LENGTHS
[0] + LATE_LINE_LENGTHS
[1] +
1009 LATE_LINE_LENGTHS
[2] + LATE_LINE_LENGTHS
[3]) / 4.0f
* multiplier
;
1010 /* Include the echo transformation (see below). */
1011 length
= lerp(length
, echoTime
, echoDepth
);
1012 length
+= (LATE_ALLPASS_LENGTHS
[0] + LATE_ALLPASS_LENGTHS
[1] +
1013 LATE_ALLPASS_LENGTHS
[2] + LATE_ALLPASS_LENGTHS
[3]) / 4.0f
* multiplier
;
1014 /* The density gain calculation uses an average decay time weighted by
1015 * approximate bandwidth. This attempts to compensate for losses of
1016 * energy that reduce decay time due to scattering into highly attenuated
1019 bandWeights
[0] = lfW
;
1020 bandWeights
[1] = hfW
- lfW
;
1021 bandWeights
[2] = F_TAU
- hfW
;
1022 Late
->DensityGain
= CalcDensityGain(
1023 CalcDecayCoeff(length
, (bandWeights
[0]*lfDecayTime
+ bandWeights
[1]*mfDecayTime
+
1024 bandWeights
[2]*hfDecayTime
) / F_TAU
)
1027 for(i
= 0;i
< NUM_LINES
;i
++)
1029 /* Calculate the length (in seconds) of each all-pass line. */
1030 length
= LATE_ALLPASS_LENGTHS
[i
] * multiplier
;
1032 /* Calculate the delay offset for each all-pass line. */
1033 Late
->VecAp
.Offset
[i
][1] = fastf2i(length
* frequency
);
1035 /* Calculate the length (in seconds) of each delay line. This also
1036 * applies the echo transformation. As the EAX echo depth approaches
1037 * 1, the line lengths approach a length equal to the echoTime. This
1038 * helps to produce distinct echoes along the tail.
1040 length
= lerp(LATE_LINE_LENGTHS
[i
] * multiplier
, echoTime
, echoDepth
);
1042 /* Calculate the delay offset for each delay line. */
1043 Late
->Offset
[i
][1] = fastf2i(length
*frequency
+ 0.5f
);
1045 /* Approximate the absorption that the vector all-pass would exhibit
1046 * given the current diffusion so we don't have to process a full T60
1047 * filter for each of its four lines.
1049 length
+= lerp(LATE_ALLPASS_LENGTHS
[i
],
1050 (LATE_ALLPASS_LENGTHS
[0] + LATE_ALLPASS_LENGTHS
[1] +
1051 LATE_ALLPASS_LENGTHS
[2] + LATE_ALLPASS_LENGTHS
[3]) / 4.0f
,
1052 diffusion
) * multiplier
;
1054 /* Calculate the T60 damping coefficients for each line. */
1055 CalcT60DampingCoeffs(length
, lfDecayTime
, mfDecayTime
, hfDecayTime
,
1056 lfW
, hfW
, Late
->T60
[i
].LFCoeffs
,
1057 Late
->T60
[i
].HFCoeffs
);
1061 /* Creates a transform matrix given a reverb vector. The vector pans the reverb
1062 * reflections toward the given direction, using its magnitude (up to 1) as a
1063 * focal strength. This function results in a B-Format transformation matrix
1064 * that spatially focuses the signal in the desired direction.
1066 static aluMatrixf
GetTransformFromVector(const ALfloat
*vec
)
1068 const ALfloat sqrt_3
= 1.732050808f
;
1073 /* Normalize the panning vector according to the N3D scale, which has an
1074 * extra sqrt(3) term on the directional components. Converting from OpenAL
1075 * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
1076 * that the reverb panning vectors use right-handed coordinates, unlike the
1077 * rest of OpenAL which use left-handed. This is fixed by negating Z, which
1078 * cancels out with the B-Format Z negation.
1080 mag
= sqrtf(vec
[0]*vec
[0] + vec
[1]*vec
[1] + vec
[2]*vec
[2]);
1083 norm
[0] = vec
[0] / mag
* -sqrt_3
;
1084 norm
[1] = vec
[1] / mag
* sqrt_3
;
1085 norm
[2] = vec
[2] / mag
* sqrt_3
;
1090 /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
1091 * term. There's no need to renormalize the magnitude since it would
1092 * just be reapplied in the matrix.
1094 norm
[0] = vec
[0] * -sqrt_3
;
1095 norm
[1] = vec
[1] * sqrt_3
;
1096 norm
[2] = vec
[2] * sqrt_3
;
1099 aluMatrixfSet(&focus
,
1100 1.0f
, 0.0f
, 0.0f
, 0.0f
,
1101 norm
[0], 1.0f
-mag
, 0.0f
, 0.0f
,
1102 norm
[1], 0.0f
, 1.0f
-mag
, 0.0f
,
1103 norm
[2], 0.0f
, 0.0f
, 1.0f
-mag
1109 /* Update the early and late 3D panning gains. */
1110 static ALvoid
Update3DPanning(const ALCdevice
*Device
, const ALfloat
*ReflectionsPan
, const ALfloat
*LateReverbPan
, const ALfloat gain
, const ALfloat earlyGain
, const ALfloat lateGain
, ALreverbState
*State
)
1112 aluMatrixf transform
, rot
;
1115 STATIC_CAST(ALeffectState
,State
)->OutBuffer
= Device
->FOAOut
.Buffer
;
1116 STATIC_CAST(ALeffectState
,State
)->OutChannels
= Device
->FOAOut
.NumChannels
;
1118 /* Note: _res is transposed. */
1119 #define MATRIX_MULT(_res, _m1, _m2) do { \
1121 for(col = 0;col < 4;col++) \
1123 for(row = 0;row < 4;row++) \
1124 _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
1125 _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
1128 /* Create a matrix that first converts A-Format to B-Format, then
1129 * transforms the B-Format signal according to the panning vector.
1131 rot
= GetTransformFromVector(ReflectionsPan
);
1132 MATRIX_MULT(transform
, rot
, A2B
);
1133 memset(&State
->Early
.PanGain
, 0, sizeof(State
->Early
.PanGain
));
1134 for(i
= 0;i
< MAX_EFFECT_CHANNELS
;i
++)
1135 ComputeFirstOrderGains(&Device
->FOAOut
, transform
.m
[i
], gain
*earlyGain
,
1136 State
->Early
.PanGain
[i
]);
1138 rot
= GetTransformFromVector(LateReverbPan
);
1139 MATRIX_MULT(transform
, rot
, A2B
);
1140 memset(&State
->Late
.PanGain
, 0, sizeof(State
->Late
.PanGain
));
1141 for(i
= 0;i
< MAX_EFFECT_CHANNELS
;i
++)
1142 ComputeFirstOrderGains(&Device
->FOAOut
, transform
.m
[i
], gain
*lateGain
,
1143 State
->Late
.PanGain
[i
]);
1147 static ALvoid
ALreverbState_update(ALreverbState
*State
, const ALCcontext
*Context
, const ALeffectslot
*Slot
, const ALeffectProps
*props
)
1149 const ALCdevice
*Device
= Context
->Device
;
1150 const ALlistener
*Listener
= Context
->Listener
;
1151 ALuint frequency
= Device
->Frequency
;
1152 ALfloat lf0norm
, hf0norm
, hfRatio
;
1153 ALfloat lfDecayTime
, hfDecayTime
;
1154 ALfloat gain
, gainlf
, gainhf
;
1157 /* Calculate the master filters */
1158 hf0norm
= props
->Reverb
.HFReference
/ frequency
;
1159 /* Restrict the filter gains from going below -60dB to keep the filter from
1160 * killing most of the signal.
1162 gainhf
= maxf(props
->Reverb
.GainHF
, 0.001f
);
1163 BiquadState_setParams(&State
->Filter
[0].Lp
, BiquadType_HighShelf
, gainhf
, hf0norm
,
1164 calc_rcpQ_from_slope(gainhf
, 1.0f
));
1165 lf0norm
= props
->Reverb
.LFReference
/ frequency
;
1166 gainlf
= maxf(props
->Reverb
.GainLF
, 0.001f
);
1167 BiquadState_setParams(&State
->Filter
[0].Hp
, BiquadType_LowShelf
, gainlf
, lf0norm
,
1168 calc_rcpQ_from_slope(gainlf
, 1.0f
));
1169 for(i
= 1;i
< NUM_LINES
;i
++)
1171 BiquadState_copyParams(&State
->Filter
[i
].Lp
, &State
->Filter
[0].Lp
);
1172 BiquadState_copyParams(&State
->Filter
[i
].Hp
, &State
->Filter
[0].Hp
);
1175 /* Update the main effect delay and associated taps. */
1176 UpdateDelayLine(props
->Reverb
.ReflectionsDelay
, props
->Reverb
.LateReverbDelay
,
1177 props
->Reverb
.Density
, props
->Reverb
.DecayTime
, frequency
,
1180 /* Calculate the all-pass feed-back/forward coefficient. */
1181 State
->ApFeedCoeff
= sqrtf(0.5f
) * powf(props
->Reverb
.Diffusion
, 2.0f
);
1183 /* Update the early lines. */
1184 UpdateEarlyLines(props
->Reverb
.Density
, props
->Reverb
.DecayTime
,
1185 frequency
, &State
->Early
);
1187 /* Get the mixing matrix coefficients. */
1188 CalcMatrixCoeffs(props
->Reverb
.Diffusion
, &State
->MixX
, &State
->MixY
);
1190 /* If the HF limit parameter is flagged, calculate an appropriate limit
1191 * based on the air absorption parameter.
1193 hfRatio
= props
->Reverb
.DecayHFRatio
;
1194 if(props
->Reverb
.DecayHFLimit
&& props
->Reverb
.AirAbsorptionGainHF
< 1.0f
)
1195 hfRatio
= CalcLimitedHfRatio(hfRatio
, props
->Reverb
.AirAbsorptionGainHF
,
1196 props
->Reverb
.DecayTime
, Listener
->Params
.ReverbSpeedOfSound
1199 /* Calculate the LF/HF decay times. */
1200 lfDecayTime
= clampf(props
->Reverb
.DecayTime
* props
->Reverb
.DecayLFRatio
,
1201 AL_EAXREVERB_MIN_DECAY_TIME
, AL_EAXREVERB_MAX_DECAY_TIME
);
1202 hfDecayTime
= clampf(props
->Reverb
.DecayTime
* hfRatio
,
1203 AL_EAXREVERB_MIN_DECAY_TIME
, AL_EAXREVERB_MAX_DECAY_TIME
);
1205 /* Update the late lines. */
1206 UpdateLateLines(props
->Reverb
.Density
, props
->Reverb
.Diffusion
,
1207 lfDecayTime
, props
->Reverb
.DecayTime
, hfDecayTime
,
1208 F_TAU
* lf0norm
, F_TAU
* hf0norm
,
1209 props
->Reverb
.EchoTime
, props
->Reverb
.EchoDepth
,
1210 frequency
, &State
->Late
);
1212 /* Update early and late 3D panning. */
1213 gain
= props
->Reverb
.Gain
* Slot
->Params
.Gain
* ReverbBoost
;
1214 Update3DPanning(Device
, props
->Reverb
.ReflectionsPan
,
1215 props
->Reverb
.LateReverbPan
, gain
,
1216 props
->Reverb
.ReflectionsGain
,
1217 props
->Reverb
.LateReverbGain
, State
);
1219 /* Determine if delay-line cross-fading is required. */
1220 for(i
= 0;i
< NUM_LINES
;i
++)
1222 if(State
->EarlyDelayTap
[i
][1] != State
->EarlyDelayTap
[i
][0] ||
1223 State
->Early
.VecAp
.Offset
[i
][1] != State
->Early
.VecAp
.Offset
[i
][0] ||
1224 State
->Early
.Offset
[i
][1] != State
->Early
.Offset
[i
][0] ||
1225 State
->LateDelayTap
[i
][1] != State
->LateDelayTap
[i
][0] ||
1226 State
->Late
.VecAp
.Offset
[i
][1] != State
->Late
.VecAp
.Offset
[i
][0] ||
1227 State
->Late
.Offset
[i
][1] != State
->Late
.Offset
[i
][0])
1229 State
->FadeCount
= 0;
1236 /**************************************
1237 * Effect Processing *
1238 **************************************/
1240 /* Basic delay line input/output routines. */
1241 static inline ALfloat
DelayLineOut(const DelayLineI
*Delay
, const ALsizei offset
, const ALsizei c
)
1243 return Delay
->Line
[offset
&Delay
->Mask
][c
];
1246 /* Cross-faded delay line output routine. Instead of interpolating the
1247 * offsets, this interpolates (cross-fades) the outputs at each offset.
1249 static inline ALfloat
FadedDelayLineOut(const DelayLineI
*Delay
, const ALsizei off0
,
1250 const ALsizei off1
, const ALsizei c
, const ALfloat mu
)
1252 return Delay
->Line
[off0
&Delay
->Mask
][c
]*(1.0f
-mu
) +
1253 Delay
->Line
[off1
&Delay
->Mask
][c
]*( mu
);
1255 #define UnfadedDelayLineOut(d, o0, o1, c, mu) DelayLineOut(d, o0, c)
1257 static inline ALvoid
DelayLineIn(DelayLineI
*Delay
, ALsizei offset
, const ALsizei c
,
1258 const ALfloat
*restrict in
, ALsizei count
)
1261 for(i
= 0;i
< count
;i
++)
1262 Delay
->Line
[(offset
++)&Delay
->Mask
][c
] = *(in
++);
1265 static inline ALvoid
DelayLineIn4(DelayLineI
*Delay
, ALsizei offset
, const ALfloat in
[NUM_LINES
])
1268 offset
&= Delay
->Mask
;
1269 for(i
= 0;i
< NUM_LINES
;i
++)
1270 Delay
->Line
[offset
][i
] = in
[i
];
1273 static inline ALvoid
DelayLineIn4Rev(DelayLineI
*Delay
, ALsizei offset
, const ALfloat in
[NUM_LINES
])
1276 offset
&= Delay
->Mask
;
1277 for(i
= 0;i
< NUM_LINES
;i
++)
1278 Delay
->Line
[offset
][i
] = in
[NUM_LINES
-1-i
];
1281 /* Applies a scattering matrix to the 4-line (vector) input. This is used
1282 * for both the below vector all-pass model and to perform modal feed-back
1283 * delay network (FDN) mixing.
1285 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
1286 * matrix with a single unitary rotational parameter:
1288 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
1293 * The rotation is constructed from the effect's diffusion parameter,
1298 * Where a, b, and c are the coefficient y with differing signs, and d is the
1299 * coefficient x. The final matrix is thus:
1301 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
1302 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
1303 * [ y, -y, x, y ] x = cos(t)
1304 * [ -y, -y, -y, x ] y = sin(t) / n
1306 * Any square orthogonal matrix with an order that is a power of two will
1307 * work (where ^T is transpose, ^-1 is inverse):
1311 * Using that knowledge, finding an appropriate matrix can be accomplished
1312 * naively by searching all combinations of:
1316 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
1317 * whose combination of signs are being iterated.
1319 static inline void VectorPartialScatter(ALfloat
*restrict out
, const ALfloat
*restrict in
,
1320 const ALfloat xCoeff
, const ALfloat yCoeff
)
1322 out
[0] = xCoeff
*in
[0] + yCoeff
*( in
[1] + -in
[2] + in
[3]);
1323 out
[1] = xCoeff
*in
[1] + yCoeff
*(-in
[0] + in
[2] + in
[3]);
1324 out
[2] = xCoeff
*in
[2] + yCoeff
*( in
[0] + -in
[1] + in
[3]);
1325 out
[3] = xCoeff
*in
[3] + yCoeff
*(-in
[0] + -in
[1] + -in
[2] );
1328 /* Same as above, but reverses the input. */
1329 static inline void VectorPartialScatterRev(ALfloat
*restrict out
, const ALfloat
*restrict in
,
1330 const ALfloat xCoeff
, const ALfloat yCoeff
)
1332 out
[0] = xCoeff
*in
[3] + yCoeff
*(in
[0] + -in
[1] + in
[2] );
1333 out
[1] = xCoeff
*in
[2] + yCoeff
*(in
[0] + in
[1] + -in
[3]);
1334 out
[2] = xCoeff
*in
[1] + yCoeff
*(in
[0] + -in
[2] + in
[3]);
1335 out
[3] = xCoeff
*in
[0] + yCoeff
*( -in
[1] + -in
[2] + -in
[3]);
1338 /* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
1339 * filter to the 4-line input.
1341 * It works by vectorizing a regular all-pass filter and replacing the delay
1342 * element with a scattering matrix (like the one above) and a diagonal
1343 * matrix of delay elements.
1345 * Two static specializations are used for transitional (cross-faded) delay
1346 * line processing and non-transitional processing.
1348 #define DECL_TEMPLATE(T) \
1349 static void VectorAllpass_##T(ALfloat *restrict out, \
1350 const ALfloat *restrict in, \
1351 const ALsizei offset, const ALfloat feedCoeff, \
1352 const ALfloat xCoeff, const ALfloat yCoeff, \
1353 const ALfloat mu, VecAllpass *Vap) \
1355 ALfloat f[NUM_LINES], fs[NUM_LINES]; \
1359 (void)mu; /* Ignore for Unfaded. */ \
1361 for(i = 0;i < NUM_LINES;i++) \
1364 out[i] = T##DelayLineOut(&Vap->Delay, offset-Vap->Offset[i][0], \
1365 offset-Vap->Offset[i][1], i, mu) - \
1367 f[i] = input + feedCoeff*out[i]; \
1369 VectorPartialScatter(fs, f, xCoeff, yCoeff); \
1371 DelayLineIn4(&Vap->Delay, offset, fs); \
1373 DECL_TEMPLATE(Unfaded
)
1374 DECL_TEMPLATE(Faded
)
1375 #undef DECL_TEMPLATE
1377 /* This generates early reflections.
1379 * This is done by obtaining the primary reflections (those arriving from the
1380 * same direction as the source) from the main delay line. These are
1381 * attenuated and all-pass filtered (based on the diffusion parameter).
1383 * The early lines are then fed in reverse (according to the approximately
1384 * opposite spatial location of the A-Format lines) to create the secondary
1385 * reflections (those arriving from the opposite direction as the source).
1387 * The early response is then completed by combining the primary reflections
1388 * with the delayed and attenuated output from the early lines.
1390 * Finally, the early response is reversed, scattered (based on diffusion),
1391 * and fed into the late reverb section of the main delay line.
1393 * Two static specializations are used for transitional (cross-faded) delay
1394 * line processing and non-transitional processing.
1396 #define DECL_TEMPLATE(T) \
1397 static void EarlyReflection_##T(ALreverbState *State, const ALsizei todo, \
1399 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) \
1401 ALsizei offset = State->Offset; \
1402 const ALfloat apFeedCoeff = State->ApFeedCoeff; \
1403 const ALfloat mixX = State->MixX; \
1404 const ALfloat mixY = State->MixY; \
1405 ALfloat f[NUM_LINES], fr[NUM_LINES]; \
1408 for(i = 0;i < todo;i++) \
1410 for(j = 0;j < NUM_LINES;j++) \
1411 fr[j] = T##DelayLineOut(&State->Delay, \
1412 offset-State->EarlyDelayTap[j][0], \
1413 offset-State->EarlyDelayTap[j][1], j, fade \
1414 ) * State->EarlyDelayCoeff[j]; \
1416 VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \
1417 &State->Early.VecAp); \
1419 DelayLineIn4Rev(&State->Early.Delay, offset, f); \
1421 for(j = 0;j < NUM_LINES;j++) \
1422 f[j] += T##DelayLineOut(&State->Early.Delay, \
1423 offset-State->Early.Offset[j][0], \
1424 offset-State->Early.Offset[j][1], j, fade \
1425 ) * State->Early.Coeff[j]; \
1427 for(j = 0;j < NUM_LINES;j++) \
1430 VectorPartialScatterRev(fr, f, mixX, mixY); \
1432 DelayLineIn4(&State->Delay, offset-State->LateFeedTap, fr); \
1438 DECL_TEMPLATE(Unfaded
)
1439 DECL_TEMPLATE(Faded
)
1440 #undef DECL_TEMPLATE
1442 /* Applies a first order filter section. */
1443 static inline ALfloat
FirstOrderFilter(const ALfloat in
, const ALfloat
*restrict coeffs
,
1444 ALfloat
*restrict state
)
1446 ALfloat out
= coeffs
[0]*in
+ coeffs
[1]*state
[0] + coeffs
[2]*state
[1];
1452 /* Applies the two T60 damping filter sections. */
1453 static inline void LateT60Filter(ALfloat
*restrict out
, const ALfloat
*restrict in
,
1457 for(i
= 0;i
< NUM_LINES
;i
++)
1458 out
[i
] = FirstOrderFilter(
1459 FirstOrderFilter(in
[i
], filter
[i
].HFCoeffs
, filter
[i
].HFState
),
1460 filter
[i
].LFCoeffs
, filter
[i
].LFState
1464 /* This generates the reverb tail using a modified feed-back delay network
1467 * Results from the early reflections are attenuated by the density gain and
1468 * mixed with the output from the late delay lines.
1470 * The late response is then completed by T60 and all-pass filtering the mix.
1472 * Finally, the lines are reversed (so they feed their opposite directions)
1473 * and scattered with the FDN matrix before re-feeding the delay lines.
1475 * Two variations are made, one for for transitional (cross-faded) delay line
1476 * processing and one for non-transitional processing.
1478 #define DECL_TEMPLATE(T) \
1479 static void LateReverb_##T(ALreverbState *State, const ALsizei todo, \
1481 ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) \
1483 const ALfloat apFeedCoeff = State->ApFeedCoeff; \
1484 const ALfloat mixX = State->MixX; \
1485 const ALfloat mixY = State->MixY; \
1489 offset = State->Offset; \
1490 for(i = 0;i < todo;i++) \
1492 ALfloat f[NUM_LINES], fr[NUM_LINES]; \
1494 for(j = 0;j < NUM_LINES;j++) \
1495 f[j] = T##DelayLineOut(&State->Delay, \
1496 offset - State->LateDelayTap[j][0], \
1497 offset - State->LateDelayTap[j][1], j, fade \
1498 ) * State->Late.DensityGain; \
1500 for(j = 0;j < NUM_LINES;j++) \
1501 f[j] += T##DelayLineOut(&State->Late.Delay, \
1502 offset - State->Late.Offset[j][0], \
1503 offset - State->Late.Offset[j][1], j, fade \
1506 LateT60Filter(fr, f, State->Late.T60); \
1507 VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \
1508 &State->Late.VecAp); \
1510 for(j = 0;j < NUM_LINES;j++) \
1513 VectorPartialScatterRev(fr, f, mixX, mixY); \
1515 DelayLineIn4(&State->Late.Delay, offset, fr); \
1521 DECL_TEMPLATE(Unfaded
)
1522 DECL_TEMPLATE(Faded
)
1523 #undef DECL_TEMPLATE
1525 static ALvoid
ALreverbState_process(ALreverbState
*State
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
1527 ALfloat (*restrict afmt
)[MAX_UPDATE_SAMPLES
] = State
->AFormatSamples
;
1528 ALfloat (*restrict early
)[MAX_UPDATE_SAMPLES
] = State
->EarlySamples
;
1529 ALfloat (*restrict late
)[MAX_UPDATE_SAMPLES
] = State
->ReverbSamples
;
1530 ALsizei fadeCount
= State
->FadeCount
;
1531 ALfloat fade
= (ALfloat
)fadeCount
/ FADE_SAMPLES
;
1534 /* Process reverb for these samples. */
1535 for(base
= 0;base
< SamplesToDo
;)
1537 ALsizei todo
= mini(SamplesToDo
-base
, MAX_UPDATE_SAMPLES
);
1538 /* If cross-fading, don't do more samples than there are to fade. */
1539 if(FADE_SAMPLES
-fadeCount
> 0)
1540 todo
= mini(todo
, FADE_SAMPLES
-fadeCount
);
1542 /* Convert B-Format to A-Format for processing. */
1543 memset(afmt
, 0, sizeof(*afmt
)*NUM_LINES
);
1544 for(c
= 0;c
< NUM_LINES
;c
++)
1545 MixRowSamples(afmt
[c
], B2A
.m
[c
],
1546 SamplesIn
, MAX_EFFECT_CHANNELS
, base
, todo
1549 /* Process the samples for reverb. */
1550 for(c
= 0;c
< NUM_LINES
;c
++)
1552 /* Band-pass the incoming samples. Use the early output lines for
1555 BiquadState_process(&State
->Filter
[c
].Lp
, early
[0], afmt
[c
], todo
);
1556 BiquadState_process(&State
->Filter
[c
].Hp
, early
[1], early
[0], todo
);
1558 /* Feed the initial delay line. */
1559 DelayLineIn(&State
->Delay
, State
->Offset
, c
, early
[1], todo
);
1562 if(UNLIKELY(fadeCount
< FADE_SAMPLES
))
1564 /* Generate early reflections. */
1565 EarlyReflection_Faded(State
, todo
, fade
, early
);
1567 /* Generate late reverb. */
1568 LateReverb_Faded(State
, todo
, fade
, late
);
1569 fade
= minf(1.0f
, fade
+ todo
*FadeStep
);
1573 /* Generate early reflections. */
1574 EarlyReflection_Unfaded(State
, todo
, fade
, early
);
1576 /* Generate late reverb. */
1577 LateReverb_Unfaded(State
, todo
, fade
, late
);
1580 /* Step all delays forward. */
1581 State
->Offset
+= todo
;
1583 if(UNLIKELY(fadeCount
< FADE_SAMPLES
) && (fadeCount
+= todo
) >= FADE_SAMPLES
)
1585 /* Update the cross-fading delay line taps. */
1586 fadeCount
= FADE_SAMPLES
;
1588 for(c
= 0;c
< NUM_LINES
;c
++)
1590 State
->EarlyDelayTap
[c
][0] = State
->EarlyDelayTap
[c
][1];
1591 State
->Early
.VecAp
.Offset
[c
][0] = State
->Early
.VecAp
.Offset
[c
][1];
1592 State
->Early
.Offset
[c
][0] = State
->Early
.Offset
[c
][1];
1593 State
->LateDelayTap
[c
][0] = State
->LateDelayTap
[c
][1];
1594 State
->Late
.VecAp
.Offset
[c
][0] = State
->Late
.VecAp
.Offset
[c
][1];
1595 State
->Late
.Offset
[c
][0] = State
->Late
.Offset
[c
][1];
1599 /* Mix the A-Format results to output, implicitly converting back to
1602 for(c
= 0;c
< NUM_LINES
;c
++)
1603 MixSamples(early
[c
], NumChannels
, SamplesOut
,
1604 State
->Early
.CurrentGain
[c
], State
->Early
.PanGain
[c
],
1605 SamplesToDo
-base
, base
, todo
1607 for(c
= 0;c
< NUM_LINES
;c
++)
1608 MixSamples(late
[c
], NumChannels
, SamplesOut
,
1609 State
->Late
.CurrentGain
[c
], State
->Late
.PanGain
[c
],
1610 SamplesToDo
-base
, base
, todo
1615 State
->FadeCount
= fadeCount
;
1619 typedef struct ReverbStateFactory
{
1620 DERIVE_FROM_TYPE(EffectStateFactory
);
1621 } ReverbStateFactory
;
1623 static ALeffectState
*ReverbStateFactory_create(ReverbStateFactory
* UNUSED(factory
))
1625 ALreverbState
*state
;
1627 NEW_OBJ0(state
, ALreverbState
)();
1628 if(!state
) return NULL
;
1630 return STATIC_CAST(ALeffectState
, state
);
1633 DEFINE_EFFECTSTATEFACTORY_VTABLE(ReverbStateFactory
);
1635 EffectStateFactory
*ReverbStateFactory_getFactory(void)
1637 static ReverbStateFactory ReverbFactory
= { { GET_VTABLE2(ReverbStateFactory
, EffectStateFactory
) } };
1639 return STATIC_CAST(EffectStateFactory
, &ReverbFactory
);
1643 void ALeaxreverb_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
1645 ALeffectProps
*props
= &effect
->Props
;
1648 case AL_EAXREVERB_DECAY_HFLIMIT
:
1649 if(!(val
>= AL_EAXREVERB_MIN_DECAY_HFLIMIT
&& val
<= AL_EAXREVERB_MAX_DECAY_HFLIMIT
))
1650 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay hflimit out of range");
1651 props
->Reverb
.DecayHFLimit
= val
;
1655 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb integer property 0x%04x",
1659 void ALeaxreverb_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
1660 { ALeaxreverb_setParami(effect
, context
, param
, vals
[0]); }
1661 void ALeaxreverb_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
1663 ALeffectProps
*props
= &effect
->Props
;
1666 case AL_EAXREVERB_DENSITY
:
1667 if(!(val
>= AL_EAXREVERB_MIN_DENSITY
&& val
<= AL_EAXREVERB_MAX_DENSITY
))
1668 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb density out of range");
1669 props
->Reverb
.Density
= val
;
1672 case AL_EAXREVERB_DIFFUSION
:
1673 if(!(val
>= AL_EAXREVERB_MIN_DIFFUSION
&& val
<= AL_EAXREVERB_MAX_DIFFUSION
))
1674 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb diffusion out of range");
1675 props
->Reverb
.Diffusion
= val
;
1678 case AL_EAXREVERB_GAIN
:
1679 if(!(val
>= AL_EAXREVERB_MIN_GAIN
&& val
<= AL_EAXREVERB_MAX_GAIN
))
1680 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb gain out of range");
1681 props
->Reverb
.Gain
= val
;
1684 case AL_EAXREVERB_GAINHF
:
1685 if(!(val
>= AL_EAXREVERB_MIN_GAINHF
&& val
<= AL_EAXREVERB_MAX_GAINHF
))
1686 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb gainhf out of range");
1687 props
->Reverb
.GainHF
= val
;
1690 case AL_EAXREVERB_GAINLF
:
1691 if(!(val
>= AL_EAXREVERB_MIN_GAINLF
&& val
<= AL_EAXREVERB_MAX_GAINLF
))
1692 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb gainlf out of range");
1693 props
->Reverb
.GainLF
= val
;
1696 case AL_EAXREVERB_DECAY_TIME
:
1697 if(!(val
>= AL_EAXREVERB_MIN_DECAY_TIME
&& val
<= AL_EAXREVERB_MAX_DECAY_TIME
))
1698 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay time out of range");
1699 props
->Reverb
.DecayTime
= val
;
1702 case AL_EAXREVERB_DECAY_HFRATIO
:
1703 if(!(val
>= AL_EAXREVERB_MIN_DECAY_HFRATIO
&& val
<= AL_EAXREVERB_MAX_DECAY_HFRATIO
))
1704 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay hfratio out of range");
1705 props
->Reverb
.DecayHFRatio
= val
;
1708 case AL_EAXREVERB_DECAY_LFRATIO
:
1709 if(!(val
>= AL_EAXREVERB_MIN_DECAY_LFRATIO
&& val
<= AL_EAXREVERB_MAX_DECAY_LFRATIO
))
1710 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb decay lfratio out of range");
1711 props
->Reverb
.DecayLFRatio
= val
;
1714 case AL_EAXREVERB_REFLECTIONS_GAIN
:
1715 if(!(val
>= AL_EAXREVERB_MIN_REFLECTIONS_GAIN
&& val
<= AL_EAXREVERB_MAX_REFLECTIONS_GAIN
))
1716 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb reflections gain out of range");
1717 props
->Reverb
.ReflectionsGain
= val
;
1720 case AL_EAXREVERB_REFLECTIONS_DELAY
:
1721 if(!(val
>= AL_EAXREVERB_MIN_REFLECTIONS_DELAY
&& val
<= AL_EAXREVERB_MAX_REFLECTIONS_DELAY
))
1722 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb reflections delay out of range");
1723 props
->Reverb
.ReflectionsDelay
= val
;
1726 case AL_EAXREVERB_LATE_REVERB_GAIN
:
1727 if(!(val
>= AL_EAXREVERB_MIN_LATE_REVERB_GAIN
&& val
<= AL_EAXREVERB_MAX_LATE_REVERB_GAIN
))
1728 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb late reverb gain out of range");
1729 props
->Reverb
.LateReverbGain
= val
;
1732 case AL_EAXREVERB_LATE_REVERB_DELAY
:
1733 if(!(val
>= AL_EAXREVERB_MIN_LATE_REVERB_DELAY
&& val
<= AL_EAXREVERB_MAX_LATE_REVERB_DELAY
))
1734 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb late reverb delay out of range");
1735 props
->Reverb
.LateReverbDelay
= val
;
1738 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF
:
1739 if(!(val
>= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF
&& val
<= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF
))
1740 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb air absorption gainhf out of range");
1741 props
->Reverb
.AirAbsorptionGainHF
= val
;
1744 case AL_EAXREVERB_ECHO_TIME
:
1745 if(!(val
>= AL_EAXREVERB_MIN_ECHO_TIME
&& val
<= AL_EAXREVERB_MAX_ECHO_TIME
))
1746 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb echo time out of range");
1747 props
->Reverb
.EchoTime
= val
;
1750 case AL_EAXREVERB_ECHO_DEPTH
:
1751 if(!(val
>= AL_EAXREVERB_MIN_ECHO_DEPTH
&& val
<= AL_EAXREVERB_MAX_ECHO_DEPTH
))
1752 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb echo depth out of range");
1753 props
->Reverb
.EchoDepth
= val
;
1756 case AL_EAXREVERB_MODULATION_TIME
:
1757 if(!(val
>= AL_EAXREVERB_MIN_MODULATION_TIME
&& val
<= AL_EAXREVERB_MAX_MODULATION_TIME
))
1758 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb modulation time out of range");
1759 props
->Reverb
.ModulationTime
= val
;
1762 case AL_EAXREVERB_MODULATION_DEPTH
:
1763 if(!(val
>= AL_EAXREVERB_MIN_MODULATION_DEPTH
&& val
<= AL_EAXREVERB_MAX_MODULATION_DEPTH
))
1764 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb modulation depth out of range");
1765 props
->Reverb
.ModulationDepth
= val
;
1768 case AL_EAXREVERB_HFREFERENCE
:
1769 if(!(val
>= AL_EAXREVERB_MIN_HFREFERENCE
&& val
<= AL_EAXREVERB_MAX_HFREFERENCE
))
1770 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb hfreference out of range");
1771 props
->Reverb
.HFReference
= val
;
1774 case AL_EAXREVERB_LFREFERENCE
:
1775 if(!(val
>= AL_EAXREVERB_MIN_LFREFERENCE
&& val
<= AL_EAXREVERB_MAX_LFREFERENCE
))
1776 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb lfreference out of range");
1777 props
->Reverb
.LFReference
= val
;
1780 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR
:
1781 if(!(val
>= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR
&& val
<= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR
))
1782 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb room rolloff factor out of range");
1783 props
->Reverb
.RoomRolloffFactor
= val
;
1787 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb float property 0x%04x",
1791 void ALeaxreverb_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
1793 ALeffectProps
*props
= &effect
->Props
;
1796 case AL_EAXREVERB_REFLECTIONS_PAN
:
1797 if(!(isfinite(vals
[0]) && isfinite(vals
[1]) && isfinite(vals
[2])))
1798 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb reflections pan out of range");
1799 props
->Reverb
.ReflectionsPan
[0] = vals
[0];
1800 props
->Reverb
.ReflectionsPan
[1] = vals
[1];
1801 props
->Reverb
.ReflectionsPan
[2] = vals
[2];
1803 case AL_EAXREVERB_LATE_REVERB_PAN
:
1804 if(!(isfinite(vals
[0]) && isfinite(vals
[1]) && isfinite(vals
[2])))
1805 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "EAX Reverb late reverb pan out of range");
1806 props
->Reverb
.LateReverbPan
[0] = vals
[0];
1807 props
->Reverb
.LateReverbPan
[1] = vals
[1];
1808 props
->Reverb
.LateReverbPan
[2] = vals
[2];
1812 ALeaxreverb_setParamf(effect
, context
, param
, vals
[0]);
1817 void ALeaxreverb_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
1819 const ALeffectProps
*props
= &effect
->Props
;
1822 case AL_EAXREVERB_DECAY_HFLIMIT
:
1823 *val
= props
->Reverb
.DecayHFLimit
;
1827 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb integer property 0x%04x",
1831 void ALeaxreverb_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
1832 { ALeaxreverb_getParami(effect
, context
, param
, vals
); }
1833 void ALeaxreverb_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
1835 const ALeffectProps
*props
= &effect
->Props
;
1838 case AL_EAXREVERB_DENSITY
:
1839 *val
= props
->Reverb
.Density
;
1842 case AL_EAXREVERB_DIFFUSION
:
1843 *val
= props
->Reverb
.Diffusion
;
1846 case AL_EAXREVERB_GAIN
:
1847 *val
= props
->Reverb
.Gain
;
1850 case AL_EAXREVERB_GAINHF
:
1851 *val
= props
->Reverb
.GainHF
;
1854 case AL_EAXREVERB_GAINLF
:
1855 *val
= props
->Reverb
.GainLF
;
1858 case AL_EAXREVERB_DECAY_TIME
:
1859 *val
= props
->Reverb
.DecayTime
;
1862 case AL_EAXREVERB_DECAY_HFRATIO
:
1863 *val
= props
->Reverb
.DecayHFRatio
;
1866 case AL_EAXREVERB_DECAY_LFRATIO
:
1867 *val
= props
->Reverb
.DecayLFRatio
;
1870 case AL_EAXREVERB_REFLECTIONS_GAIN
:
1871 *val
= props
->Reverb
.ReflectionsGain
;
1874 case AL_EAXREVERB_REFLECTIONS_DELAY
:
1875 *val
= props
->Reverb
.ReflectionsDelay
;
1878 case AL_EAXREVERB_LATE_REVERB_GAIN
:
1879 *val
= props
->Reverb
.LateReverbGain
;
1882 case AL_EAXREVERB_LATE_REVERB_DELAY
:
1883 *val
= props
->Reverb
.LateReverbDelay
;
1886 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF
:
1887 *val
= props
->Reverb
.AirAbsorptionGainHF
;
1890 case AL_EAXREVERB_ECHO_TIME
:
1891 *val
= props
->Reverb
.EchoTime
;
1894 case AL_EAXREVERB_ECHO_DEPTH
:
1895 *val
= props
->Reverb
.EchoDepth
;
1898 case AL_EAXREVERB_MODULATION_TIME
:
1899 *val
= props
->Reverb
.ModulationTime
;
1902 case AL_EAXREVERB_MODULATION_DEPTH
:
1903 *val
= props
->Reverb
.ModulationDepth
;
1906 case AL_EAXREVERB_HFREFERENCE
:
1907 *val
= props
->Reverb
.HFReference
;
1910 case AL_EAXREVERB_LFREFERENCE
:
1911 *val
= props
->Reverb
.LFReference
;
1914 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR
:
1915 *val
= props
->Reverb
.RoomRolloffFactor
;
1919 alSetError(context
, AL_INVALID_ENUM
, "Invalid EAX reverb float property 0x%04x",
1923 void ALeaxreverb_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
1925 const ALeffectProps
*props
= &effect
->Props
;
1928 case AL_EAXREVERB_REFLECTIONS_PAN
:
1929 vals
[0] = props
->Reverb
.ReflectionsPan
[0];
1930 vals
[1] = props
->Reverb
.ReflectionsPan
[1];
1931 vals
[2] = props
->Reverb
.ReflectionsPan
[2];
1933 case AL_EAXREVERB_LATE_REVERB_PAN
:
1934 vals
[0] = props
->Reverb
.LateReverbPan
[0];
1935 vals
[1] = props
->Reverb
.LateReverbPan
[1];
1936 vals
[2] = props
->Reverb
.LateReverbPan
[2];
1940 ALeaxreverb_getParamf(effect
, context
, param
, vals
);
1945 DEFINE_ALEFFECT_VTABLE(ALeaxreverb
);
1947 void ALreverb_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
1949 ALeffectProps
*props
= &effect
->Props
;
1952 case AL_REVERB_DECAY_HFLIMIT
:
1953 if(!(val
>= AL_REVERB_MIN_DECAY_HFLIMIT
&& val
<= AL_REVERB_MAX_DECAY_HFLIMIT
))
1954 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb decay hflimit out of range");
1955 props
->Reverb
.DecayHFLimit
= val
;
1959 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb integer property 0x%04x", param
);
1962 void ALreverb_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
1963 { ALreverb_setParami(effect
, context
, param
, vals
[0]); }
1964 void ALreverb_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
1966 ALeffectProps
*props
= &effect
->Props
;
1969 case AL_REVERB_DENSITY
:
1970 if(!(val
>= AL_REVERB_MIN_DENSITY
&& val
<= AL_REVERB_MAX_DENSITY
))
1971 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb density out of range");
1972 props
->Reverb
.Density
= val
;
1975 case AL_REVERB_DIFFUSION
:
1976 if(!(val
>= AL_REVERB_MIN_DIFFUSION
&& val
<= AL_REVERB_MAX_DIFFUSION
))
1977 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb diffusion out of range");
1978 props
->Reverb
.Diffusion
= val
;
1981 case AL_REVERB_GAIN
:
1982 if(!(val
>= AL_REVERB_MIN_GAIN
&& val
<= AL_REVERB_MAX_GAIN
))
1983 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb gain out of range");
1984 props
->Reverb
.Gain
= val
;
1987 case AL_REVERB_GAINHF
:
1988 if(!(val
>= AL_REVERB_MIN_GAINHF
&& val
<= AL_REVERB_MAX_GAINHF
))
1989 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb gainhf out of range");
1990 props
->Reverb
.GainHF
= val
;
1993 case AL_REVERB_DECAY_TIME
:
1994 if(!(val
>= AL_REVERB_MIN_DECAY_TIME
&& val
<= AL_REVERB_MAX_DECAY_TIME
))
1995 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb decay time out of range");
1996 props
->Reverb
.DecayTime
= val
;
1999 case AL_REVERB_DECAY_HFRATIO
:
2000 if(!(val
>= AL_REVERB_MIN_DECAY_HFRATIO
&& val
<= AL_REVERB_MAX_DECAY_HFRATIO
))
2001 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb decay hfratio out of range");
2002 props
->Reverb
.DecayHFRatio
= val
;
2005 case AL_REVERB_REFLECTIONS_GAIN
:
2006 if(!(val
>= AL_REVERB_MIN_REFLECTIONS_GAIN
&& val
<= AL_REVERB_MAX_REFLECTIONS_GAIN
))
2007 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb reflections gain out of range");
2008 props
->Reverb
.ReflectionsGain
= val
;
2011 case AL_REVERB_REFLECTIONS_DELAY
:
2012 if(!(val
>= AL_REVERB_MIN_REFLECTIONS_DELAY
&& val
<= AL_REVERB_MAX_REFLECTIONS_DELAY
))
2013 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb reflections delay out of range");
2014 props
->Reverb
.ReflectionsDelay
= val
;
2017 case AL_REVERB_LATE_REVERB_GAIN
:
2018 if(!(val
>= AL_REVERB_MIN_LATE_REVERB_GAIN
&& val
<= AL_REVERB_MAX_LATE_REVERB_GAIN
))
2019 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb late reverb gain out of range");
2020 props
->Reverb
.LateReverbGain
= val
;
2023 case AL_REVERB_LATE_REVERB_DELAY
:
2024 if(!(val
>= AL_REVERB_MIN_LATE_REVERB_DELAY
&& val
<= AL_REVERB_MAX_LATE_REVERB_DELAY
))
2025 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb late reverb delay out of range");
2026 props
->Reverb
.LateReverbDelay
= val
;
2029 case AL_REVERB_AIR_ABSORPTION_GAINHF
:
2030 if(!(val
>= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF
&& val
<= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF
))
2031 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb air absorption gainhf out of range");
2032 props
->Reverb
.AirAbsorptionGainHF
= val
;
2035 case AL_REVERB_ROOM_ROLLOFF_FACTOR
:
2036 if(!(val
>= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR
&& val
<= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR
))
2037 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Reverb room rolloff factor out of range");
2038 props
->Reverb
.RoomRolloffFactor
= val
;
2042 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb float property 0x%04x", param
);
2045 void ALreverb_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
2046 { ALreverb_setParamf(effect
, context
, param
, vals
[0]); }
2048 void ALreverb_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
2050 const ALeffectProps
*props
= &effect
->Props
;
2053 case AL_REVERB_DECAY_HFLIMIT
:
2054 *val
= props
->Reverb
.DecayHFLimit
;
2058 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb integer property 0x%04x", param
);
2061 void ALreverb_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
2062 { ALreverb_getParami(effect
, context
, param
, vals
); }
2063 void ALreverb_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
2065 const ALeffectProps
*props
= &effect
->Props
;
2068 case AL_REVERB_DENSITY
:
2069 *val
= props
->Reverb
.Density
;
2072 case AL_REVERB_DIFFUSION
:
2073 *val
= props
->Reverb
.Diffusion
;
2076 case AL_REVERB_GAIN
:
2077 *val
= props
->Reverb
.Gain
;
2080 case AL_REVERB_GAINHF
:
2081 *val
= props
->Reverb
.GainHF
;
2084 case AL_REVERB_DECAY_TIME
:
2085 *val
= props
->Reverb
.DecayTime
;
2088 case AL_REVERB_DECAY_HFRATIO
:
2089 *val
= props
->Reverb
.DecayHFRatio
;
2092 case AL_REVERB_REFLECTIONS_GAIN
:
2093 *val
= props
->Reverb
.ReflectionsGain
;
2096 case AL_REVERB_REFLECTIONS_DELAY
:
2097 *val
= props
->Reverb
.ReflectionsDelay
;
2100 case AL_REVERB_LATE_REVERB_GAIN
:
2101 *val
= props
->Reverb
.LateReverbGain
;
2104 case AL_REVERB_LATE_REVERB_DELAY
:
2105 *val
= props
->Reverb
.LateReverbDelay
;
2108 case AL_REVERB_AIR_ABSORPTION_GAINHF
:
2109 *val
= props
->Reverb
.AirAbsorptionGainHF
;
2112 case AL_REVERB_ROOM_ROLLOFF_FACTOR
:
2113 *val
= props
->Reverb
.RoomRolloffFactor
;
2117 alSetError(context
, AL_INVALID_ENUM
, "Invalid reverb float property 0x%04x", param
);
2120 void ALreverb_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
2121 { ALreverb_getParamf(effect
, context
, param
, vals
); }
2123 DEFINE_ALEFFECT_VTABLE(ALreverb
);