Use a separate macro for the max output channel count
[openal-soft.git] / Alc / effects / reverb.c
blobe1b47ec2823fc7576f8e6f223e0f2250f4bd80ad
1 /**
2 * Reverb for the OpenAL cross platform audio library
3 * Copyright (C) 2008-2009 by 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
21 #include "config.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <math.h>
27 #include "alMain.h"
28 #include "alu.h"
29 #include "alAuxEffectSlot.h"
30 #include "alEffect.h"
31 #include "alFilter.h"
32 #include "alError.h"
35 typedef struct DelayLine
37 // The delay lines use sample lengths that are powers of 2 to allow the
38 // use of bit-masking instead of a modulus for wrapping.
39 ALuint Mask;
40 ALfloat *Line;
41 } DelayLine;
43 typedef struct ALreverbState {
44 DERIVE_FROM_TYPE(ALeffectState);
46 ALboolean IsEax;
48 // All delay lines are allocated as a single buffer to reduce memory
49 // fragmentation and management code.
50 ALfloat *SampleBuffer;
51 ALuint TotalSamples;
53 // Master effect filters
54 ALfilterState LpFilter;
55 ALfilterState HpFilter; // EAX only
57 struct {
58 // Modulator delay line.
59 DelayLine Delay;
61 // The vibrato time is tracked with an index over a modulus-wrapped
62 // range (in samples).
63 ALuint Index;
64 ALuint Range;
66 // The depth of frequency change (also in samples) and its filter.
67 ALfloat Depth;
68 ALfloat Coeff;
69 ALfloat Filter;
70 } Mod;
72 // Initial effect delay.
73 DelayLine Delay;
74 // The tap points for the initial delay. First tap goes to early
75 // reflections, the last to late reverb.
76 ALuint DelayTap[2];
78 struct {
79 // Output gain for early reflections.
80 ALfloat Gain;
82 // Early reflections are done with 4 delay lines.
83 ALfloat Coeff[4];
84 DelayLine Delay[4];
85 ALuint Offset[4];
87 // The gain for each output channel based on 3D panning (only for the
88 // EAX path).
89 ALfloat PanGain[MAX_OUTPUT_CHANNELS];
90 } Early;
92 // Decorrelator delay line.
93 DelayLine Decorrelator;
94 // There are actually 4 decorrelator taps, but the first occurs at the
95 // initial sample.
96 ALuint DecoTap[3];
98 struct {
99 // Output gain for late reverb.
100 ALfloat Gain;
102 // Attenuation to compensate for the modal density and decay rate of
103 // the late lines.
104 ALfloat DensityGain;
106 // The feed-back and feed-forward all-pass coefficient.
107 ALfloat ApFeedCoeff;
109 // Mixing matrix coefficient.
110 ALfloat MixCoeff;
112 // Late reverb has 4 parallel all-pass filters.
113 ALfloat ApCoeff[4];
114 DelayLine ApDelay[4];
115 ALuint ApOffset[4];
117 // In addition to 4 cyclical delay lines.
118 ALfloat Coeff[4];
119 DelayLine Delay[4];
120 ALuint Offset[4];
122 // The cyclical delay lines are 1-pole low-pass filtered.
123 ALfloat LpCoeff[4];
124 ALfloat LpSample[4];
126 // The gain for each output channel based on 3D panning (only for the
127 // EAX path).
128 ALfloat PanGain[MAX_OUTPUT_CHANNELS];
129 } Late;
131 struct {
132 // Attenuation to compensate for the modal density and decay rate of
133 // the echo line.
134 ALfloat DensityGain;
136 // Echo delay and all-pass lines.
137 DelayLine Delay;
138 DelayLine ApDelay;
140 ALfloat Coeff;
141 ALfloat ApFeedCoeff;
142 ALfloat ApCoeff;
144 ALuint Offset;
145 ALuint ApOffset;
147 // The echo line is 1-pole low-pass filtered.
148 ALfloat LpCoeff;
149 ALfloat LpSample;
151 // Echo mixing coefficients.
152 ALfloat MixCoeff[2];
153 } Echo;
155 // The current read offset for all delay lines.
156 ALuint Offset;
158 // The gain for each output channel (non-EAX path only; aliased from
159 // Late.PanGain)
160 ALfloat *Gain;
162 /* Temporary storage used when processing, before deinterlacing. */
163 ALfloat ReverbSamples[BUFFERSIZE][4];
164 ALfloat EarlySamples[BUFFERSIZE][4];
165 } ALreverbState;
167 /* This is a user config option for modifying the overall output of the reverb
168 * effect.
170 ALfloat ReverbBoost = 1.0f;
172 /* Specifies whether to use a standard reverb effect in place of EAX reverb */
173 ALboolean EmulateEAXReverb = AL_FALSE;
175 /* This coefficient is used to define the maximum frequency range controlled
176 * by the modulation depth. The current value of 0.1 will allow it to swing
177 * from 0.9x to 1.1x. This value must be below 1. At 1 it will cause the
178 * sampler to stall on the downswing, and above 1 it will cause it to sample
179 * backwards.
181 static const ALfloat MODULATION_DEPTH_COEFF = 0.1f;
183 /* A filter is used to avoid the terrible distortion caused by changing
184 * modulation time and/or depth. To be consistent across different sample
185 * rates, the coefficient must be raised to a constant divided by the sample
186 * rate: coeff^(constant / rate).
188 static const ALfloat MODULATION_FILTER_COEFF = 0.048f;
189 static const ALfloat MODULATION_FILTER_CONST = 100000.0f;
191 // When diffusion is above 0, an all-pass filter is used to take the edge off
192 // the echo effect. It uses the following line length (in seconds).
193 static const ALfloat ECHO_ALLPASS_LENGTH = 0.0133f;
195 // Input into the late reverb is decorrelated between four channels. Their
196 // timings are dependent on a fraction and multiplier. See the
197 // UpdateDecorrelator() routine for the calculations involved.
198 static const ALfloat DECO_FRACTION = 0.15f;
199 static const ALfloat DECO_MULTIPLIER = 2.0f;
201 // All delay line lengths are specified in seconds.
203 // The lengths of the early delay lines.
204 static const ALfloat EARLY_LINE_LENGTH[4] =
206 0.0015f, 0.0045f, 0.0135f, 0.0405f
209 // The lengths of the late all-pass delay lines.
210 static const ALfloat ALLPASS_LINE_LENGTH[4] =
212 0.0151f, 0.0167f, 0.0183f, 0.0200f,
215 // The lengths of the late cyclical delay lines.
216 static const ALfloat LATE_LINE_LENGTH[4] =
218 0.0211f, 0.0311f, 0.0461f, 0.0680f
221 // The late cyclical delay lines have a variable length dependent on the
222 // effect's density parameter (inverted for some reason) and this multiplier.
223 static const ALfloat LATE_LINE_MULTIPLIER = 4.0f;
226 // Basic delay line input/output routines.
227 static inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset)
229 return Delay->Line[offset&Delay->Mask];
232 static inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in)
234 Delay->Line[offset&Delay->Mask] = in;
237 // Attenuated delay line output routine.
238 static inline ALfloat AttenuatedDelayLineOut(DelayLine *Delay, ALuint offset, ALfloat coeff)
240 return coeff * Delay->Line[offset&Delay->Mask];
243 // Basic attenuated all-pass input/output routine.
244 static inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff)
246 ALfloat out, feed;
248 out = DelayLineOut(Delay, outOffset);
249 feed = feedCoeff * in;
250 DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in);
252 // The time-based attenuation is only applied to the delay output to
253 // keep it from affecting the feed-back path (which is already controlled
254 // by the all-pass feed coefficient).
255 return (coeff * out) - feed;
258 // Given an input sample, this function produces modulation for the late
259 // reverb.
260 static inline ALfloat EAXModulation(ALreverbState *State, ALfloat in)
262 ALfloat sinus, frac;
263 ALuint offset;
264 ALfloat out0, out1;
266 // Calculate the sinus rythm (dependent on modulation time and the
267 // sampling rate). The center of the sinus is moved to reduce the delay
268 // of the effect when the time or depth are low.
269 sinus = 1.0f - cosf(F_2PI * State->Mod.Index / State->Mod.Range);
271 // The depth determines the range over which to read the input samples
272 // from, so it must be filtered to reduce the distortion caused by even
273 // small parameter changes.
274 State->Mod.Filter = lerp(State->Mod.Filter, State->Mod.Depth,
275 State->Mod.Coeff);
277 // Calculate the read offset and fraction between it and the next sample.
278 frac = (1.0f + (State->Mod.Filter * sinus));
279 offset = fastf2u(frac);
280 frac -= offset;
282 // Get the two samples crossed by the offset, and feed the delay line
283 // with the next input sample.
284 out0 = DelayLineOut(&State->Mod.Delay, State->Offset - offset);
285 out1 = DelayLineOut(&State->Mod.Delay, State->Offset - offset - 1);
286 DelayLineIn(&State->Mod.Delay, State->Offset, in);
288 // Step the modulation index forward, keeping it bound to its range.
289 State->Mod.Index = (State->Mod.Index + 1) % State->Mod.Range;
291 // The output is obtained by linearly interpolating the two samples that
292 // were acquired above.
293 return lerp(out0, out1, frac);
296 // Delay line output routine for early reflections.
297 static inline ALfloat EarlyDelayLineOut(ALreverbState *State, ALuint index)
299 return AttenuatedDelayLineOut(&State->Early.Delay[index],
300 State->Offset - State->Early.Offset[index],
301 State->Early.Coeff[index]);
304 // Given an input sample, this function produces four-channel output for the
305 // early reflections.
306 static inline ALvoid EarlyReflection(ALreverbState *State, ALfloat in, ALfloat *restrict out)
308 ALfloat d[4], v, f[4];
310 // Obtain the decayed results of each early delay line.
311 d[0] = EarlyDelayLineOut(State, 0);
312 d[1] = EarlyDelayLineOut(State, 1);
313 d[2] = EarlyDelayLineOut(State, 2);
314 d[3] = EarlyDelayLineOut(State, 3);
316 /* The following uses a lossless scattering junction from waveguide
317 * theory. It actually amounts to a householder mixing matrix, which
318 * will produce a maximally diffuse response, and means this can probably
319 * be considered a simple feed-back delay network (FDN).
321 * ---
323 * v = 2/N / d_i
324 * ---
325 * i=1
327 v = (d[0] + d[1] + d[2] + d[3]) * 0.5f;
328 // The junction is loaded with the input here.
329 v += in;
331 // Calculate the feed values for the delay lines.
332 f[0] = v - d[0];
333 f[1] = v - d[1];
334 f[2] = v - d[2];
335 f[3] = v - d[3];
337 // Re-feed the delay lines.
338 DelayLineIn(&State->Early.Delay[0], State->Offset, f[0]);
339 DelayLineIn(&State->Early.Delay[1], State->Offset, f[1]);
340 DelayLineIn(&State->Early.Delay[2], State->Offset, f[2]);
341 DelayLineIn(&State->Early.Delay[3], State->Offset, f[3]);
343 // Output the results of the junction for all four channels.
344 out[0] = State->Early.Gain * f[0];
345 out[1] = State->Early.Gain * f[1];
346 out[2] = State->Early.Gain * f[2];
347 out[3] = State->Early.Gain * f[3];
350 // All-pass input/output routine for late reverb.
351 static inline ALfloat LateAllPassInOut(ALreverbState *State, ALuint index, ALfloat in)
353 return AllpassInOut(&State->Late.ApDelay[index],
354 State->Offset - State->Late.ApOffset[index],
355 State->Offset, in, State->Late.ApFeedCoeff,
356 State->Late.ApCoeff[index]);
359 // Delay line output routine for late reverb.
360 static inline ALfloat LateDelayLineOut(ALreverbState *State, ALuint index)
362 return AttenuatedDelayLineOut(&State->Late.Delay[index],
363 State->Offset - State->Late.Offset[index],
364 State->Late.Coeff[index]);
367 // Low-pass filter input/output routine for late reverb.
368 static inline ALfloat LateLowPassInOut(ALreverbState *State, ALuint index, ALfloat in)
370 in = lerp(in, State->Late.LpSample[index], State->Late.LpCoeff[index]);
371 State->Late.LpSample[index] = in;
372 return in;
375 // Given four decorrelated input samples, this function produces four-channel
376 // output for the late reverb.
377 static inline ALvoid LateReverb(ALreverbState *State, const ALfloat *restrict in, ALfloat *restrict out)
379 ALfloat d[4], f[4];
381 // Obtain the decayed results of the cyclical delay lines, and add the
382 // corresponding input channels. Then pass the results through the
383 // low-pass filters.
385 // This is where the feed-back cycles from line 0 to 1 to 3 to 2 and back
386 // to 0.
387 d[0] = LateLowPassInOut(State, 2, in[2] + LateDelayLineOut(State, 2));
388 d[1] = LateLowPassInOut(State, 0, in[0] + LateDelayLineOut(State, 0));
389 d[2] = LateLowPassInOut(State, 3, in[3] + LateDelayLineOut(State, 3));
390 d[3] = LateLowPassInOut(State, 1, in[1] + LateDelayLineOut(State, 1));
392 // To help increase diffusion, run each line through an all-pass filter.
393 // When there is no diffusion, the shortest all-pass filter will feed the
394 // shortest delay line.
395 d[0] = LateAllPassInOut(State, 0, d[0]);
396 d[1] = LateAllPassInOut(State, 1, d[1]);
397 d[2] = LateAllPassInOut(State, 2, d[2]);
398 d[3] = LateAllPassInOut(State, 3, d[3]);
400 /* Late reverb is done with a modified feed-back delay network (FDN)
401 * topology. Four input lines are each fed through their own all-pass
402 * filter and then into the mixing matrix. The four outputs of the
403 * mixing matrix are then cycled back to the inputs. Each output feeds
404 * a different input to form a circlular feed cycle.
406 * The mixing matrix used is a 4D skew-symmetric rotation matrix derived
407 * using a single unitary rotational parameter:
409 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
410 * [ -a, d, c, -b ]
411 * [ -b, -c, d, a ]
412 * [ -c, b, -a, d ]
414 * The rotation is constructed from the effect's diffusion parameter,
415 * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y
416 * with differing signs, and d is the coefficient x. The matrix is thus:
418 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
419 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
420 * [ y, -y, x, y ] x = cos(t)
421 * [ -y, -y, -y, x ] y = sin(t) / n
423 * To reduce the number of multiplies, the x coefficient is applied with
424 * the cyclical delay line coefficients. Thus only the y coefficient is
425 * applied when mixing, and is modified to be: y / x.
427 f[0] = d[0] + (State->Late.MixCoeff * ( d[1] + -d[2] + d[3]));
428 f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3]));
429 f[2] = d[2] + (State->Late.MixCoeff * ( d[0] + -d[1] + d[3]));
430 f[3] = d[3] + (State->Late.MixCoeff * (-d[0] + -d[1] + -d[2] ));
432 // Output the results of the matrix for all four channels, attenuated by
433 // the late reverb gain (which is attenuated by the 'x' mix coefficient).
434 out[0] = State->Late.Gain * f[0];
435 out[1] = State->Late.Gain * f[1];
436 out[2] = State->Late.Gain * f[2];
437 out[3] = State->Late.Gain * f[3];
439 // Re-feed the cyclical delay lines.
440 DelayLineIn(&State->Late.Delay[0], State->Offset, f[0]);
441 DelayLineIn(&State->Late.Delay[1], State->Offset, f[1]);
442 DelayLineIn(&State->Late.Delay[2], State->Offset, f[2]);
443 DelayLineIn(&State->Late.Delay[3], State->Offset, f[3]);
446 // Given an input sample, this function mixes echo into the four-channel late
447 // reverb.
448 static inline ALvoid EAXEcho(ALreverbState *State, ALfloat in, ALfloat *restrict late)
450 ALfloat out, feed;
452 // Get the latest attenuated echo sample for output.
453 feed = AttenuatedDelayLineOut(&State->Echo.Delay,
454 State->Offset - State->Echo.Offset,
455 State->Echo.Coeff);
457 // Mix the output into the late reverb channels.
458 out = State->Echo.MixCoeff[0] * feed;
459 late[0] = (State->Echo.MixCoeff[1] * late[0]) + out;
460 late[1] = (State->Echo.MixCoeff[1] * late[1]) + out;
461 late[2] = (State->Echo.MixCoeff[1] * late[2]) + out;
462 late[3] = (State->Echo.MixCoeff[1] * late[3]) + out;
464 // Mix the energy-attenuated input with the output and pass it through
465 // the echo low-pass filter.
466 feed += State->Echo.DensityGain * in;
467 feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff);
468 State->Echo.LpSample = feed;
470 // Then the echo all-pass filter.
471 feed = AllpassInOut(&State->Echo.ApDelay,
472 State->Offset - State->Echo.ApOffset,
473 State->Offset, feed, State->Echo.ApFeedCoeff,
474 State->Echo.ApCoeff);
476 // Feed the delay with the mixed and filtered sample.
477 DelayLineIn(&State->Echo.Delay, State->Offset, feed);
480 // Perform the non-EAX reverb pass on a given input sample, resulting in
481 // four-channel output.
482 static inline ALvoid VerbPass(ALreverbState *State, ALfloat in, ALfloat *restrict out)
484 ALfloat feed, late[4], taps[4];
486 // Filter the incoming sample.
487 in = ALfilterState_processSingle(&State->LpFilter, in);
489 // Feed the initial delay line.
490 DelayLineIn(&State->Delay, State->Offset, in);
492 // Calculate the early reflection from the first delay tap.
493 in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]);
494 EarlyReflection(State, in, out);
496 // Feed the decorrelator from the energy-attenuated output of the second
497 // delay tap.
498 in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]);
499 feed = in * State->Late.DensityGain;
500 DelayLineIn(&State->Decorrelator, State->Offset, feed);
502 // Calculate the late reverb from the decorrelator taps.
503 taps[0] = feed;
504 taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]);
505 taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]);
506 taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]);
507 LateReverb(State, taps, late);
509 // Mix early reflections and late reverb.
510 out[0] += late[0];
511 out[1] += late[1];
512 out[2] += late[2];
513 out[3] += late[3];
515 // Step all delays forward one sample.
516 State->Offset++;
519 // Perform the EAX reverb pass on a given input sample, resulting in four-
520 // channel output.
521 static inline ALvoid EAXVerbPass(ALreverbState *State, ALfloat in, ALfloat *restrict early, ALfloat *restrict late)
523 ALfloat feed, taps[4];
525 // Low-pass filter the incoming sample.
526 in = ALfilterState_processSingle(&State->LpFilter, in);
527 in = ALfilterState_processSingle(&State->HpFilter, in);
529 // Perform any modulation on the input.
530 in = EAXModulation(State, in);
532 // Feed the initial delay line.
533 DelayLineIn(&State->Delay, State->Offset, in);
535 // Calculate the early reflection from the first delay tap.
536 in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]);
537 EarlyReflection(State, in, early);
539 // Feed the decorrelator from the energy-attenuated output of the second
540 // delay tap.
541 in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]);
542 feed = in * State->Late.DensityGain;
543 DelayLineIn(&State->Decorrelator, State->Offset, feed);
545 // Calculate the late reverb from the decorrelator taps.
546 taps[0] = feed;
547 taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]);
548 taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]);
549 taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]);
550 LateReverb(State, taps, late);
552 // Calculate and mix in any echo.
553 EAXEcho(State, in, late);
555 // Step all delays forward one sample.
556 State->Offset++;
559 static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
561 ALfloat (*restrict out)[4] = State->ReverbSamples;
562 ALuint index, c;
564 /* Process reverb for these samples. */
565 for(index = 0;index < SamplesToDo;index++)
566 VerbPass(State, SamplesIn[index], out[index]);
568 for(c = 0;c < MAX_OUTPUT_CHANNELS;c++)
570 ALfloat gain = State->Gain[c];
571 if(!(gain > GAIN_SILENCE_THRESHOLD))
572 continue;
574 for(index = 0;index < SamplesToDo;index++)
575 SamplesOut[c][index] += gain * out[index][c&3];
579 static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
581 ALfloat (*restrict early)[4] = State->EarlySamples;
582 ALfloat (*restrict late)[4] = State->ReverbSamples;
583 ALuint index, c;
585 /* Process reverb for these samples. */
586 for(index = 0;index < SamplesToDo;index++)
587 EAXVerbPass(State, SamplesIn[index], early[index], late[index]);
589 for(c = 0;c < MAX_OUTPUT_CHANNELS;c++)
591 ALfloat earlyGain, lateGain;
593 earlyGain = State->Early.PanGain[c];
594 if(earlyGain > GAIN_SILENCE_THRESHOLD)
596 for(index = 0;index < SamplesToDo;index++)
597 SamplesOut[c][index] += earlyGain*early[index][c&3];
599 lateGain = State->Late.PanGain[c];
600 if(lateGain > GAIN_SILENCE_THRESHOLD)
602 for(index = 0;index < SamplesToDo;index++)
603 SamplesOut[c][index] += lateGain*late[index][c&3];
608 static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
610 if(State->IsEax)
611 ALreverbState_processEax(State, SamplesToDo, SamplesIn, SamplesOut);
612 else
613 ALreverbState_processStandard(State, SamplesToDo, SamplesIn, SamplesOut);
616 // Given the allocated sample buffer, this function updates each delay line
617 // offset.
618 static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay)
620 Delay->Line = &sampleBuffer[(ptrdiff_t)Delay->Line];
623 // Calculate the length of a delay line and store its mask and offset.
624 static ALuint CalcLineLength(ALfloat length, ptrdiff_t offset, ALuint frequency, DelayLine *Delay)
626 ALuint samples;
628 // All line lengths are powers of 2, calculated from their lengths, with
629 // an additional sample in case of rounding errors.
630 samples = NextPowerOf2(fastf2u(length * frequency) + 1);
631 // All lines share a single sample buffer.
632 Delay->Mask = samples - 1;
633 Delay->Line = (ALfloat*)offset;
634 // Return the sample count for accumulation.
635 return samples;
638 /* Calculates the delay line metrics and allocates the shared sample buffer
639 * for all lines given the sample rate (frequency). If an allocation failure
640 * occurs, it returns AL_FALSE.
642 static ALboolean AllocLines(ALuint frequency, ALreverbState *State)
644 ALuint totalSamples, index;
645 ALfloat length;
646 ALfloat *newBuffer = NULL;
648 // All delay line lengths are calculated to accomodate the full range of
649 // lengths given their respective paramters.
650 totalSamples = 0;
652 /* The modulator's line length is calculated from the maximum modulation
653 * time and depth coefficient, and halfed for the low-to-high frequency
654 * swing. An additional sample is added to keep it stable when there is no
655 * modulation.
657 length = (AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f) +
658 (1.0f / frequency);
659 totalSamples += CalcLineLength(length, totalSamples, frequency,
660 &State->Mod.Delay);
662 // The initial delay is the sum of the reflections and late reverb
663 // delays.
664 length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
665 AL_EAXREVERB_MAX_LATE_REVERB_DELAY;
666 totalSamples += CalcLineLength(length, totalSamples, frequency,
667 &State->Delay);
669 // The early reflection lines.
670 for(index = 0;index < 4;index++)
671 totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples,
672 frequency, &State->Early.Delay[index]);
674 // The decorrelator line is calculated from the lowest reverb density (a
675 // parameter value of 1).
676 length = (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) *
677 LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER);
678 totalSamples += CalcLineLength(length, totalSamples, frequency,
679 &State->Decorrelator);
681 // The late all-pass lines.
682 for(index = 0;index < 4;index++)
683 totalSamples += CalcLineLength(ALLPASS_LINE_LENGTH[index], totalSamples,
684 frequency, &State->Late.ApDelay[index]);
686 // The late delay lines are calculated from the lowest reverb density.
687 for(index = 0;index < 4;index++)
689 length = LATE_LINE_LENGTH[index] * (1.0f + LATE_LINE_MULTIPLIER);
690 totalSamples += CalcLineLength(length, totalSamples, frequency,
691 &State->Late.Delay[index]);
694 // The echo all-pass and delay lines.
695 totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples,
696 frequency, &State->Echo.ApDelay);
697 totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples,
698 frequency, &State->Echo.Delay);
700 if(totalSamples != State->TotalSamples)
702 TRACE("New reverb buffer length: %u samples (%f sec)\n", totalSamples, totalSamples/(float)frequency);
703 newBuffer = realloc(State->SampleBuffer, sizeof(ALfloat) * totalSamples);
704 if(newBuffer == NULL)
705 return AL_FALSE;
706 State->SampleBuffer = newBuffer;
707 State->TotalSamples = totalSamples;
710 // Update all delays to reflect the new sample buffer.
711 RealizeLineOffset(State->SampleBuffer, &State->Delay);
712 RealizeLineOffset(State->SampleBuffer, &State->Decorrelator);
713 for(index = 0;index < 4;index++)
715 RealizeLineOffset(State->SampleBuffer, &State->Early.Delay[index]);
716 RealizeLineOffset(State->SampleBuffer, &State->Late.ApDelay[index]);
717 RealizeLineOffset(State->SampleBuffer, &State->Late.Delay[index]);
719 RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay);
720 RealizeLineOffset(State->SampleBuffer, &State->Echo.ApDelay);
721 RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay);
723 // Clear the sample buffer.
724 for(index = 0;index < State->TotalSamples;index++)
725 State->SampleBuffer[index] = 0.0f;
727 return AL_TRUE;
730 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
732 ALuint frequency = Device->Frequency, index;
734 // Allocate the delay lines.
735 if(!AllocLines(frequency, State))
736 return AL_FALSE;
738 // Calculate the modulation filter coefficient. Notice that the exponent
739 // is calculated given the current sample rate. This ensures that the
740 // resulting filter response over time is consistent across all sample
741 // rates.
742 State->Mod.Coeff = powf(MODULATION_FILTER_COEFF,
743 MODULATION_FILTER_CONST / frequency);
745 // The early reflection and late all-pass filter line lengths are static,
746 // so their offsets only need to be calculated once.
747 for(index = 0;index < 4;index++)
749 State->Early.Offset[index] = fastf2u(EARLY_LINE_LENGTH[index] *
750 frequency);
751 State->Late.ApOffset[index] = fastf2u(ALLPASS_LINE_LENGTH[index] *
752 frequency);
755 // The echo all-pass filter line length is static, so its offset only
756 // needs to be calculated once.
757 State->Echo.ApOffset = fastf2u(ECHO_ALLPASS_LENGTH * frequency);
759 return AL_TRUE;
762 // Calculate a decay coefficient given the length of each cycle and the time
763 // until the decay reaches -60 dB.
764 static inline ALfloat CalcDecayCoeff(ALfloat length, ALfloat decayTime)
766 return powf(0.001f/*-60 dB*/, length/decayTime);
769 // Calculate a decay length from a coefficient and the time until the decay
770 // reaches -60 dB.
771 static inline ALfloat CalcDecayLength(ALfloat coeff, ALfloat decayTime)
773 return log10f(coeff) * decayTime / log10f(0.001f)/*-60 dB*/;
776 // Calculate an attenuation to be applied to the input of any echo models to
777 // compensate for modal density and decay time.
778 static inline ALfloat CalcDensityGain(ALfloat a)
780 /* The energy of a signal can be obtained by finding the area under the
781 * squared signal. This takes the form of Sum(x_n^2), where x is the
782 * amplitude for the sample n.
784 * Decaying feedback matches exponential decay of the form Sum(a^n),
785 * where a is the attenuation coefficient, and n is the sample. The area
786 * under this decay curve can be calculated as: 1 / (1 - a).
788 * Modifying the above equation to find the squared area under the curve
789 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
790 * calculated by inverting the square root of this approximation,
791 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
793 return sqrtf(1.0f - (a * a));
796 // Calculate the mixing matrix coefficients given a diffusion factor.
797 static inline ALvoid CalcMatrixCoeffs(ALfloat diffusion, ALfloat *x, ALfloat *y)
799 ALfloat n, t;
801 // The matrix is of order 4, so n is sqrt (4 - 1).
802 n = sqrtf(3.0f);
803 t = diffusion * atanf(n);
805 // Calculate the first mixing matrix coefficient.
806 *x = cosf(t);
807 // Calculate the second mixing matrix coefficient.
808 *y = sinf(t) / n;
811 // Calculate the limited HF ratio for use with the late reverb low-pass
812 // filters.
813 static ALfloat CalcLimitedHfRatio(ALfloat hfRatio, ALfloat airAbsorptionGainHF, ALfloat decayTime)
815 ALfloat limitRatio;
817 /* Find the attenuation due to air absorption in dB (converting delay
818 * time to meters using the speed of sound). Then reversing the decay
819 * equation, solve for HF ratio. The delay length is cancelled out of
820 * the equation, so it can be calculated once for all lines.
822 limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) *
823 SPEEDOFSOUNDMETRESPERSEC);
824 /* Using the limit calculated above, apply the upper bound to the HF
825 * ratio. Also need to limit the result to a minimum of 0.1, just like the
826 * HF ratio parameter. */
827 return clampf(limitRatio, 0.1f, hfRatio);
830 // Calculate the coefficient for a HF (and eventually LF) decay damping
831 // filter.
832 static inline ALfloat CalcDampingCoeff(ALfloat hfRatio, ALfloat length, ALfloat decayTime, ALfloat decayCoeff, ALfloat cw)
834 ALfloat coeff, g;
836 // Eventually this should boost the high frequencies when the ratio
837 // exceeds 1.
838 coeff = 0.0f;
839 if (hfRatio < 1.0f)
841 // Calculate the low-pass coefficient by dividing the HF decay
842 // coefficient by the full decay coefficient.
843 g = CalcDecayCoeff(length, decayTime * hfRatio) / decayCoeff;
845 // Damping is done with a 1-pole filter, so g needs to be squared.
846 g *= g;
847 if(g < 0.9999f) /* 1-epsilon */
849 /* Be careful with gains < 0.001, as that causes the coefficient
850 * head towards 1, which will flatten the signal. */
851 g = maxf(g, 0.001f);
852 coeff = (1 - g*cw - sqrtf(2*g*(1-cw) - g*g*(1 - cw*cw))) /
853 (1 - g);
856 // Very low decay times will produce minimal output, so apply an
857 // upper bound to the coefficient.
858 coeff = minf(coeff, 0.98f);
860 return coeff;
863 // Update the EAX modulation index, range, and depth. Keep in mind that this
864 // kind of vibrato is additive and not multiplicative as one may expect. The
865 // downswing will sound stronger than the upswing.
866 static ALvoid UpdateModulator(ALfloat modTime, ALfloat modDepth, ALuint frequency, ALreverbState *State)
868 ALuint range;
870 /* Modulation is calculated in two parts.
872 * The modulation time effects the sinus applied to the change in
873 * frequency. An index out of the current time range (both in samples)
874 * is incremented each sample. The range is bound to a reasonable
875 * minimum (1 sample) and when the timing changes, the index is rescaled
876 * to the new range (to keep the sinus consistent).
878 range = maxu(fastf2u(modTime*frequency), 1);
879 State->Mod.Index = (ALuint)(State->Mod.Index * (ALuint64)range /
880 State->Mod.Range);
881 State->Mod.Range = range;
883 /* The modulation depth effects the amount of frequency change over the
884 * range of the sinus. It needs to be scaled by the modulation time so
885 * that a given depth produces a consistent change in frequency over all
886 * ranges of time. Since the depth is applied to a sinus value, it needs
887 * to be halfed once for the sinus range and again for the sinus swing
888 * in time (half of it is spent decreasing the frequency, half is spent
889 * increasing it).
891 State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f /
892 2.0f * frequency;
895 // Update the offsets for the initial effect delay line.
896 static ALvoid UpdateDelayLine(ALfloat earlyDelay, ALfloat lateDelay, ALuint frequency, ALreverbState *State)
898 // Calculate the initial delay taps.
899 State->DelayTap[0] = fastf2u(earlyDelay * frequency);
900 State->DelayTap[1] = fastf2u((earlyDelay + lateDelay) * frequency);
903 // Update the early reflections gain and line coefficients.
904 static ALvoid UpdateEarlyLines(ALfloat reverbGain, ALfloat earlyGain, ALfloat lateDelay, ALreverbState *State)
906 ALuint index;
908 // Calculate the early reflections gain (from the master effect gain, and
909 // reflections gain parameters) with a constant attenuation of 0.5.
910 State->Early.Gain = 0.5f * reverbGain * earlyGain;
912 // Calculate the gain (coefficient) for each early delay line using the
913 // late delay time. This expands the early reflections to the start of
914 // the late reverb.
915 for(index = 0;index < 4;index++)
916 State->Early.Coeff[index] = CalcDecayCoeff(EARLY_LINE_LENGTH[index],
917 lateDelay);
920 // Update the offsets for the decorrelator line.
921 static ALvoid UpdateDecorrelator(ALfloat density, ALuint frequency, ALreverbState *State)
923 ALuint index;
924 ALfloat length;
926 /* The late reverb inputs are decorrelated to smooth the reverb tail and
927 * reduce harsh echos. The first tap occurs immediately, while the
928 * remaining taps are delayed by multiples of a fraction of the smallest
929 * cyclical delay time.
931 * offset[index] = (FRACTION (MULTIPLIER^index)) smallest_delay
933 for(index = 0;index < 3;index++)
935 length = (DECO_FRACTION * powf(DECO_MULTIPLIER, (ALfloat)index)) *
936 LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER));
937 State->DecoTap[index] = fastf2u(length * frequency);
941 // Update the late reverb gains, line lengths, and line coefficients.
942 static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix, ALfloat density, ALfloat decayTime, ALfloat diffusion, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State)
944 ALfloat length;
945 ALuint index;
947 /* Calculate the late reverb gain (from the master effect gain, and late
948 * reverb gain parameters). Since the output is tapped prior to the
949 * application of the next delay line coefficients, this gain needs to be
950 * attenuated by the 'x' mixing matrix coefficient as well.
952 State->Late.Gain = reverbGain * lateGain * xMix;
954 /* To compensate for changes in modal density and decay time of the late
955 * reverb signal, the input is attenuated based on the maximal energy of
956 * the outgoing signal. This approximation is used to keep the apparent
957 * energy of the signal equal for all ranges of density and decay time.
959 * The average length of the cyclcical delay lines is used to calculate
960 * the attenuation coefficient.
962 length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] +
963 LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]) / 4.0f;
964 length *= 1.0f + (density * LATE_LINE_MULTIPLIER);
965 State->Late.DensityGain = CalcDensityGain(CalcDecayCoeff(length,
966 decayTime));
968 // Calculate the all-pass feed-back and feed-forward coefficient.
969 State->Late.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f);
971 for(index = 0;index < 4;index++)
973 // Calculate the gain (coefficient) for each all-pass line.
974 State->Late.ApCoeff[index] = CalcDecayCoeff(ALLPASS_LINE_LENGTH[index],
975 decayTime);
977 // Calculate the length (in seconds) of each cyclical delay line.
978 length = LATE_LINE_LENGTH[index] * (1.0f + (density *
979 LATE_LINE_MULTIPLIER));
981 // Calculate the delay offset for each cyclical delay line.
982 State->Late.Offset[index] = fastf2u(length * frequency);
984 // Calculate the gain (coefficient) for each cyclical line.
985 State->Late.Coeff[index] = CalcDecayCoeff(length, decayTime);
987 // Calculate the damping coefficient for each low-pass filter.
988 State->Late.LpCoeff[index] =
989 CalcDampingCoeff(hfRatio, length, decayTime,
990 State->Late.Coeff[index], cw);
992 // Attenuate the cyclical line coefficients by the mixing coefficient
993 // (x).
994 State->Late.Coeff[index] *= xMix;
998 // Update the echo gain, line offset, line coefficients, and mixing
999 // coefficients.
1000 static ALvoid UpdateEchoLine(ALfloat reverbGain, ALfloat lateGain, ALfloat echoTime, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State)
1002 // Update the offset and coefficient for the echo delay line.
1003 State->Echo.Offset = fastf2u(echoTime * frequency);
1005 // Calculate the decay coefficient for the echo line.
1006 State->Echo.Coeff = CalcDecayCoeff(echoTime, decayTime);
1008 // Calculate the energy-based attenuation coefficient for the echo delay
1009 // line.
1010 State->Echo.DensityGain = CalcDensityGain(State->Echo.Coeff);
1012 // Calculate the echo all-pass feed coefficient.
1013 State->Echo.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f);
1015 // Calculate the echo all-pass attenuation coefficient.
1016 State->Echo.ApCoeff = CalcDecayCoeff(ECHO_ALLPASS_LENGTH, decayTime);
1018 // Calculate the damping coefficient for each low-pass filter.
1019 State->Echo.LpCoeff = CalcDampingCoeff(hfRatio, echoTime, decayTime,
1020 State->Echo.Coeff, cw);
1022 /* Calculate the echo mixing coefficients. The first is applied to the
1023 * echo itself. The second is used to attenuate the late reverb when
1024 * echo depth is high and diffusion is low, so the echo is slightly
1025 * stronger than the decorrelated echos in the reverb tail.
1027 State->Echo.MixCoeff[0] = reverbGain * lateGain * echoDepth;
1028 State->Echo.MixCoeff[1] = 1.0f - (echoDepth * 0.5f * (1.0f - diffusion));
1031 // Update the early and late 3D panning gains.
1032 static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALreverbState *State)
1034 ALfloat earlyPan[3] = { ReflectionsPan[0], ReflectionsPan[1], ReflectionsPan[2] };
1035 ALfloat latePan[3] = { LateReverbPan[0], LateReverbPan[1], LateReverbPan[2] };
1036 ALfloat length, invlen;
1038 length = earlyPan[0]*earlyPan[0] + earlyPan[1]*earlyPan[1] + earlyPan[2]*earlyPan[2];
1039 if(!(length > FLT_EPSILON))
1040 earlyPan[0] = earlyPan[1] = earlyPan[2] = 0.0f;
1041 else
1043 invlen = 1.0f / sqrtf(length);
1044 earlyPan[0] *= invlen;
1045 earlyPan[1] *= invlen;
1046 earlyPan[2] *= invlen;
1048 ComputeDirectionalGains(Device, earlyPan, Gain, State->Early.PanGain);
1050 length = latePan[0]*latePan[0] + latePan[1]*latePan[1] + latePan[2]*latePan[2];
1051 if(!(length > FLT_EPSILON))
1052 latePan[0] = latePan[1] = latePan[2] = 0.0f;
1053 else
1055 invlen = 1.0f / sqrtf(length);
1056 latePan[0] *= invlen;
1057 latePan[1] *= invlen;
1058 latePan[2] *= invlen;
1060 ComputeDirectionalGains(Device, latePan, Gain, State->Late.PanGain);
1063 static ALvoid ALreverbState_update(ALreverbState *State, ALCdevice *Device, const ALeffectslot *Slot)
1065 ALuint frequency = Device->Frequency;
1066 ALfloat lfscale, hfscale, hfRatio;
1067 ALfloat cw, x, y;
1069 if(Slot->EffectType == AL_EFFECT_EAXREVERB && !EmulateEAXReverb)
1070 State->IsEax = AL_TRUE;
1071 else if(Slot->EffectType == AL_EFFECT_REVERB || EmulateEAXReverb)
1072 State->IsEax = AL_FALSE;
1074 // Calculate the master low-pass filter (from the master effect HF gain).
1075 if(State->IsEax)
1077 hfscale = Slot->EffectProps.Reverb.HFReference / frequency;
1078 ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf,
1079 Slot->EffectProps.Reverb.GainHF,
1080 hfscale, 0.0f);
1081 lfscale = Slot->EffectProps.Reverb.LFReference / frequency;
1082 ALfilterState_setParams(&State->HpFilter, ALfilterType_LowShelf,
1083 Slot->EffectProps.Reverb.GainLF,
1084 lfscale, 0.0f);
1086 else
1088 hfscale = LOWPASSFREQREF / frequency;
1089 ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf,
1090 Slot->EffectProps.Reverb.GainHF,
1091 hfscale, 0.0f);
1094 if(State->IsEax)
1096 // Update the modulator line.
1097 UpdateModulator(Slot->EffectProps.Reverb.ModulationTime,
1098 Slot->EffectProps.Reverb.ModulationDepth,
1099 frequency, State);
1102 // Update the initial effect delay.
1103 UpdateDelayLine(Slot->EffectProps.Reverb.ReflectionsDelay,
1104 Slot->EffectProps.Reverb.LateReverbDelay,
1105 frequency, State);
1107 // Update the early lines.
1108 UpdateEarlyLines(Slot->EffectProps.Reverb.Gain,
1109 Slot->EffectProps.Reverb.ReflectionsGain,
1110 Slot->EffectProps.Reverb.LateReverbDelay, State);
1112 // Update the decorrelator.
1113 UpdateDecorrelator(Slot->EffectProps.Reverb.Density, frequency, State);
1115 // Get the mixing matrix coefficients (x and y).
1116 CalcMatrixCoeffs(Slot->EffectProps.Reverb.Diffusion, &x, &y);
1117 // Then divide x into y to simplify the matrix calculation.
1118 State->Late.MixCoeff = y / x;
1120 // If the HF limit parameter is flagged, calculate an appropriate limit
1121 // based on the air absorption parameter.
1122 hfRatio = Slot->EffectProps.Reverb.DecayHFRatio;
1123 if(Slot->EffectProps.Reverb.DecayHFLimit &&
1124 Slot->EffectProps.Reverb.AirAbsorptionGainHF < 1.0f)
1125 hfRatio = CalcLimitedHfRatio(hfRatio,
1126 Slot->EffectProps.Reverb.AirAbsorptionGainHF,
1127 Slot->EffectProps.Reverb.DecayTime);
1129 cw = cosf(F_2PI * hfscale);
1130 // Update the late lines.
1131 UpdateLateLines(Slot->EffectProps.Reverb.Gain, Slot->EffectProps.Reverb.LateReverbGain,
1132 x, Slot->EffectProps.Reverb.Density, Slot->EffectProps.Reverb.DecayTime,
1133 Slot->EffectProps.Reverb.Diffusion, hfRatio, cw, frequency, State);
1135 if(State->IsEax)
1137 // Update the echo line.
1138 UpdateEchoLine(Slot->EffectProps.Reverb.Gain, Slot->EffectProps.Reverb.LateReverbGain,
1139 Slot->EffectProps.Reverb.EchoTime, Slot->EffectProps.Reverb.DecayTime,
1140 Slot->EffectProps.Reverb.Diffusion, Slot->EffectProps.Reverb.EchoDepth,
1141 hfRatio, cw, frequency, State);
1143 // Update early and late 3D panning.
1144 Update3DPanning(Device, Slot->EffectProps.Reverb.ReflectionsPan,
1145 Slot->EffectProps.Reverb.LateReverbPan,
1146 Slot->Gain * ReverbBoost, State);
1148 else
1150 /* Update channel gains */
1151 ComputeAmbientGains(Device, Slot->Gain*2.0f, State->Gain);
1156 static ALvoid ALreverbState_Destruct(ALreverbState *State)
1158 free(State->SampleBuffer);
1159 State->SampleBuffer = NULL;
1162 DECLARE_DEFAULT_ALLOCATORS(ALreverbState)
1164 DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);
1167 typedef struct ALreverbStateFactory {
1168 DERIVE_FROM_TYPE(ALeffectStateFactory);
1169 } ALreverbStateFactory;
1171 static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory))
1173 ALreverbState *state;
1174 ALuint index;
1176 state = ALreverbState_New(sizeof(*state));
1177 if(!state) return NULL;
1178 SET_VTABLE2(ALreverbState, ALeffectState, state);
1180 state->TotalSamples = 0;
1181 state->SampleBuffer = NULL;
1183 ALfilterState_clear(&state->LpFilter);
1184 ALfilterState_clear(&state->HpFilter);
1186 state->Mod.Delay.Mask = 0;
1187 state->Mod.Delay.Line = NULL;
1188 state->Mod.Index = 0;
1189 state->Mod.Range = 1;
1190 state->Mod.Depth = 0.0f;
1191 state->Mod.Coeff = 0.0f;
1192 state->Mod.Filter = 0.0f;
1194 state->Delay.Mask = 0;
1195 state->Delay.Line = NULL;
1196 state->DelayTap[0] = 0;
1197 state->DelayTap[1] = 0;
1199 state->Early.Gain = 0.0f;
1200 for(index = 0;index < 4;index++)
1202 state->Early.Coeff[index] = 0.0f;
1203 state->Early.Delay[index].Mask = 0;
1204 state->Early.Delay[index].Line = NULL;
1205 state->Early.Offset[index] = 0;
1208 state->Decorrelator.Mask = 0;
1209 state->Decorrelator.Line = NULL;
1210 state->DecoTap[0] = 0;
1211 state->DecoTap[1] = 0;
1212 state->DecoTap[2] = 0;
1214 state->Late.Gain = 0.0f;
1215 state->Late.DensityGain = 0.0f;
1216 state->Late.ApFeedCoeff = 0.0f;
1217 state->Late.MixCoeff = 0.0f;
1218 for(index = 0;index < 4;index++)
1220 state->Late.ApCoeff[index] = 0.0f;
1221 state->Late.ApDelay[index].Mask = 0;
1222 state->Late.ApDelay[index].Line = NULL;
1223 state->Late.ApOffset[index] = 0;
1225 state->Late.Coeff[index] = 0.0f;
1226 state->Late.Delay[index].Mask = 0;
1227 state->Late.Delay[index].Line = NULL;
1228 state->Late.Offset[index] = 0;
1230 state->Late.LpCoeff[index] = 0.0f;
1231 state->Late.LpSample[index] = 0.0f;
1234 for(index = 0;index < MAX_OUTPUT_CHANNELS;index++)
1236 state->Early.PanGain[index] = 0.0f;
1237 state->Late.PanGain[index] = 0.0f;
1240 state->Echo.DensityGain = 0.0f;
1241 state->Echo.Delay.Mask = 0;
1242 state->Echo.Delay.Line = NULL;
1243 state->Echo.ApDelay.Mask = 0;
1244 state->Echo.ApDelay.Line = NULL;
1245 state->Echo.Coeff = 0.0f;
1246 state->Echo.ApFeedCoeff = 0.0f;
1247 state->Echo.ApCoeff = 0.0f;
1248 state->Echo.Offset = 0;
1249 state->Echo.ApOffset = 0;
1250 state->Echo.LpCoeff = 0.0f;
1251 state->Echo.LpSample = 0.0f;
1252 state->Echo.MixCoeff[0] = 0.0f;
1253 state->Echo.MixCoeff[1] = 0.0f;
1255 state->Offset = 0;
1257 state->Gain = state->Late.PanGain;
1259 return STATIC_CAST(ALeffectState, state);
1262 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALreverbStateFactory);
1264 ALeffectStateFactory *ALreverbStateFactory_getFactory(void)
1266 static ALreverbStateFactory ReverbFactory = { { GET_VTABLE2(ALreverbStateFactory, ALeffectStateFactory) } };
1268 return STATIC_CAST(ALeffectStateFactory, &ReverbFactory);
1272 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1274 ALeffectProps *props = &effect->Props;
1275 switch(param)
1277 case AL_EAXREVERB_DECAY_HFLIMIT:
1278 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1279 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1280 props->Reverb.DecayHFLimit = val;
1281 break;
1283 default:
1284 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1287 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1289 ALeaxreverb_setParami(effect, context, param, vals[0]);
1291 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1293 ALeffectProps *props = &effect->Props;
1294 switch(param)
1296 case AL_EAXREVERB_DENSITY:
1297 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1298 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1299 props->Reverb.Density = val;
1300 break;
1302 case AL_EAXREVERB_DIFFUSION:
1303 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1304 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1305 props->Reverb.Diffusion = val;
1306 break;
1308 case AL_EAXREVERB_GAIN:
1309 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1310 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1311 props->Reverb.Gain = val;
1312 break;
1314 case AL_EAXREVERB_GAINHF:
1315 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1316 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1317 props->Reverb.GainHF = val;
1318 break;
1320 case AL_EAXREVERB_GAINLF:
1321 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1322 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1323 props->Reverb.GainLF = val;
1324 break;
1326 case AL_EAXREVERB_DECAY_TIME:
1327 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1328 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1329 props->Reverb.DecayTime = val;
1330 break;
1332 case AL_EAXREVERB_DECAY_HFRATIO:
1333 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1334 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1335 props->Reverb.DecayHFRatio = val;
1336 break;
1338 case AL_EAXREVERB_DECAY_LFRATIO:
1339 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1340 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1341 props->Reverb.DecayLFRatio = val;
1342 break;
1344 case AL_EAXREVERB_REFLECTIONS_GAIN:
1345 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1346 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1347 props->Reverb.ReflectionsGain = val;
1348 break;
1350 case AL_EAXREVERB_REFLECTIONS_DELAY:
1351 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1352 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1353 props->Reverb.ReflectionsDelay = val;
1354 break;
1356 case AL_EAXREVERB_LATE_REVERB_GAIN:
1357 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1358 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1359 props->Reverb.LateReverbGain = val;
1360 break;
1362 case AL_EAXREVERB_LATE_REVERB_DELAY:
1363 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1364 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1365 props->Reverb.LateReverbDelay = val;
1366 break;
1368 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1369 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1370 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1371 props->Reverb.AirAbsorptionGainHF = val;
1372 break;
1374 case AL_EAXREVERB_ECHO_TIME:
1375 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1376 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1377 props->Reverb.EchoTime = val;
1378 break;
1380 case AL_EAXREVERB_ECHO_DEPTH:
1381 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1382 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1383 props->Reverb.EchoDepth = val;
1384 break;
1386 case AL_EAXREVERB_MODULATION_TIME:
1387 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1388 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1389 props->Reverb.ModulationTime = val;
1390 break;
1392 case AL_EAXREVERB_MODULATION_DEPTH:
1393 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1394 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1395 props->Reverb.ModulationDepth = val;
1396 break;
1398 case AL_EAXREVERB_HFREFERENCE:
1399 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1400 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1401 props->Reverb.HFReference = val;
1402 break;
1404 case AL_EAXREVERB_LFREFERENCE:
1405 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1406 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1407 props->Reverb.LFReference = val;
1408 break;
1410 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1411 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1412 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1413 props->Reverb.RoomRolloffFactor = val;
1414 break;
1416 default:
1417 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1420 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1422 ALeffectProps *props = &effect->Props;
1423 switch(param)
1425 case AL_EAXREVERB_REFLECTIONS_PAN:
1426 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1427 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1428 LockContext(context);
1429 props->Reverb.ReflectionsPan[0] = vals[0];
1430 props->Reverb.ReflectionsPan[1] = vals[1];
1431 props->Reverb.ReflectionsPan[2] = vals[2];
1432 UnlockContext(context);
1433 break;
1434 case AL_EAXREVERB_LATE_REVERB_PAN:
1435 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1436 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1437 LockContext(context);
1438 props->Reverb.LateReverbPan[0] = vals[0];
1439 props->Reverb.LateReverbPan[1] = vals[1];
1440 props->Reverb.LateReverbPan[2] = vals[2];
1441 UnlockContext(context);
1442 break;
1444 default:
1445 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1446 break;
1450 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1452 const ALeffectProps *props = &effect->Props;
1453 switch(param)
1455 case AL_EAXREVERB_DECAY_HFLIMIT:
1456 *val = props->Reverb.DecayHFLimit;
1457 break;
1459 default:
1460 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1463 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1465 ALeaxreverb_getParami(effect, context, param, vals);
1467 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1469 const ALeffectProps *props = &effect->Props;
1470 switch(param)
1472 case AL_EAXREVERB_DENSITY:
1473 *val = props->Reverb.Density;
1474 break;
1476 case AL_EAXREVERB_DIFFUSION:
1477 *val = props->Reverb.Diffusion;
1478 break;
1480 case AL_EAXREVERB_GAIN:
1481 *val = props->Reverb.Gain;
1482 break;
1484 case AL_EAXREVERB_GAINHF:
1485 *val = props->Reverb.GainHF;
1486 break;
1488 case AL_EAXREVERB_GAINLF:
1489 *val = props->Reverb.GainLF;
1490 break;
1492 case AL_EAXREVERB_DECAY_TIME:
1493 *val = props->Reverb.DecayTime;
1494 break;
1496 case AL_EAXREVERB_DECAY_HFRATIO:
1497 *val = props->Reverb.DecayHFRatio;
1498 break;
1500 case AL_EAXREVERB_DECAY_LFRATIO:
1501 *val = props->Reverb.DecayLFRatio;
1502 break;
1504 case AL_EAXREVERB_REFLECTIONS_GAIN:
1505 *val = props->Reverb.ReflectionsGain;
1506 break;
1508 case AL_EAXREVERB_REFLECTIONS_DELAY:
1509 *val = props->Reverb.ReflectionsDelay;
1510 break;
1512 case AL_EAXREVERB_LATE_REVERB_GAIN:
1513 *val = props->Reverb.LateReverbGain;
1514 break;
1516 case AL_EAXREVERB_LATE_REVERB_DELAY:
1517 *val = props->Reverb.LateReverbDelay;
1518 break;
1520 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1521 *val = props->Reverb.AirAbsorptionGainHF;
1522 break;
1524 case AL_EAXREVERB_ECHO_TIME:
1525 *val = props->Reverb.EchoTime;
1526 break;
1528 case AL_EAXREVERB_ECHO_DEPTH:
1529 *val = props->Reverb.EchoDepth;
1530 break;
1532 case AL_EAXREVERB_MODULATION_TIME:
1533 *val = props->Reverb.ModulationTime;
1534 break;
1536 case AL_EAXREVERB_MODULATION_DEPTH:
1537 *val = props->Reverb.ModulationDepth;
1538 break;
1540 case AL_EAXREVERB_HFREFERENCE:
1541 *val = props->Reverb.HFReference;
1542 break;
1544 case AL_EAXREVERB_LFREFERENCE:
1545 *val = props->Reverb.LFReference;
1546 break;
1548 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1549 *val = props->Reverb.RoomRolloffFactor;
1550 break;
1552 default:
1553 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1556 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
1558 const ALeffectProps *props = &effect->Props;
1559 switch(param)
1561 case AL_EAXREVERB_REFLECTIONS_PAN:
1562 LockContext(context);
1563 vals[0] = props->Reverb.ReflectionsPan[0];
1564 vals[1] = props->Reverb.ReflectionsPan[1];
1565 vals[2] = props->Reverb.ReflectionsPan[2];
1566 UnlockContext(context);
1567 break;
1568 case AL_EAXREVERB_LATE_REVERB_PAN:
1569 LockContext(context);
1570 vals[0] = props->Reverb.LateReverbPan[0];
1571 vals[1] = props->Reverb.LateReverbPan[1];
1572 vals[2] = props->Reverb.LateReverbPan[2];
1573 UnlockContext(context);
1574 break;
1576 default:
1577 ALeaxreverb_getParamf(effect, context, param, vals);
1578 break;
1582 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
1584 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1586 ALeffectProps *props = &effect->Props;
1587 switch(param)
1589 case AL_REVERB_DECAY_HFLIMIT:
1590 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
1591 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1592 props->Reverb.DecayHFLimit = val;
1593 break;
1595 default:
1596 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1599 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1601 ALreverb_setParami(effect, context, param, vals[0]);
1603 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1605 ALeffectProps *props = &effect->Props;
1606 switch(param)
1608 case AL_REVERB_DENSITY:
1609 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
1610 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1611 props->Reverb.Density = val;
1612 break;
1614 case AL_REVERB_DIFFUSION:
1615 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
1616 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1617 props->Reverb.Diffusion = val;
1618 break;
1620 case AL_REVERB_GAIN:
1621 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
1622 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1623 props->Reverb.Gain = val;
1624 break;
1626 case AL_REVERB_GAINHF:
1627 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
1628 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1629 props->Reverb.GainHF = val;
1630 break;
1632 case AL_REVERB_DECAY_TIME:
1633 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
1634 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1635 props->Reverb.DecayTime = val;
1636 break;
1638 case AL_REVERB_DECAY_HFRATIO:
1639 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
1640 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1641 props->Reverb.DecayHFRatio = val;
1642 break;
1644 case AL_REVERB_REFLECTIONS_GAIN:
1645 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
1646 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1647 props->Reverb.ReflectionsGain = val;
1648 break;
1650 case AL_REVERB_REFLECTIONS_DELAY:
1651 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
1652 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1653 props->Reverb.ReflectionsDelay = val;
1654 break;
1656 case AL_REVERB_LATE_REVERB_GAIN:
1657 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
1658 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1659 props->Reverb.LateReverbGain = val;
1660 break;
1662 case AL_REVERB_LATE_REVERB_DELAY:
1663 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
1664 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1665 props->Reverb.LateReverbDelay = val;
1666 break;
1668 case AL_REVERB_AIR_ABSORPTION_GAINHF:
1669 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
1670 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1671 props->Reverb.AirAbsorptionGainHF = val;
1672 break;
1674 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
1675 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
1676 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1677 props->Reverb.RoomRolloffFactor = val;
1678 break;
1680 default:
1681 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1684 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1686 ALreverb_setParamf(effect, context, param, vals[0]);
1689 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1691 const ALeffectProps *props = &effect->Props;
1692 switch(param)
1694 case AL_REVERB_DECAY_HFLIMIT:
1695 *val = props->Reverb.DecayHFLimit;
1696 break;
1698 default:
1699 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1702 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1704 ALreverb_getParami(effect, context, param, vals);
1706 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1708 const ALeffectProps *props = &effect->Props;
1709 switch(param)
1711 case AL_REVERB_DENSITY:
1712 *val = props->Reverb.Density;
1713 break;
1715 case AL_REVERB_DIFFUSION:
1716 *val = props->Reverb.Diffusion;
1717 break;
1719 case AL_REVERB_GAIN:
1720 *val = props->Reverb.Gain;
1721 break;
1723 case AL_REVERB_GAINHF:
1724 *val = props->Reverb.GainHF;
1725 break;
1727 case AL_REVERB_DECAY_TIME:
1728 *val = props->Reverb.DecayTime;
1729 break;
1731 case AL_REVERB_DECAY_HFRATIO:
1732 *val = props->Reverb.DecayHFRatio;
1733 break;
1735 case AL_REVERB_REFLECTIONS_GAIN:
1736 *val = props->Reverb.ReflectionsGain;
1737 break;
1739 case AL_REVERB_REFLECTIONS_DELAY:
1740 *val = props->Reverb.ReflectionsDelay;
1741 break;
1743 case AL_REVERB_LATE_REVERB_GAIN:
1744 *val = props->Reverb.LateReverbGain;
1745 break;
1747 case AL_REVERB_LATE_REVERB_DELAY:
1748 *val = props->Reverb.LateReverbDelay;
1749 break;
1751 case AL_REVERB_AIR_ABSORPTION_GAINHF:
1752 *val = props->Reverb.AirAbsorptionGainHF;
1753 break;
1755 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
1756 *val = props->Reverb.RoomRolloffFactor;
1757 break;
1759 default:
1760 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1763 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
1765 ALreverb_getParamf(effect, context, param, vals);
1768 DEFINE_ALEFFECT_VTABLE(ALreverb);