Mix reverb to output in the inner loop
[openal-soft.git] / Alc / effects / reverb.c
blobfac639b87561989200bc9eee1498b48acdb37fa5
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 /* This is the maximum number of samples processed for each inner loop
36 * iteration. */
37 #define MAX_UPDATE_SAMPLES 256
39 typedef struct DelayLine
41 // The delay lines use sample lengths that are powers of 2 to allow the
42 // use of bit-masking instead of a modulus for wrapping.
43 ALuint Mask;
44 ALfloat *Line;
45 } DelayLine;
47 typedef struct ALreverbState {
48 DERIVE_FROM_TYPE(ALeffectState);
50 ALboolean IsEax;
52 // All delay lines are allocated as a single buffer to reduce memory
53 // fragmentation and management code.
54 ALfloat *SampleBuffer;
55 ALuint TotalSamples;
57 // Master effect filters
58 ALfilterState LpFilter;
59 ALfilterState HpFilter; // EAX only
61 struct {
62 // Modulator delay line.
63 DelayLine Delay;
65 // The vibrato time is tracked with an index over a modulus-wrapped
66 // range (in samples).
67 ALuint Index;
68 ALuint Range;
70 // The depth of frequency change (also in samples) and its filter.
71 ALfloat Depth;
72 ALfloat Coeff;
73 ALfloat Filter;
74 } Mod;
76 // Initial effect delay.
77 DelayLine Delay;
78 // The tap points for the initial delay. First tap goes to early
79 // reflections, the last to late reverb.
80 ALuint DelayTap[2];
82 struct {
83 // Output gain for early reflections.
84 ALfloat Gain;
86 // Early reflections are done with 4 delay lines.
87 ALfloat Coeff[4];
88 DelayLine Delay[4];
89 ALuint Offset[4];
91 // The gain for each output channel based on 3D panning (only for the
92 // EAX path).
93 ALfloat PanGain[MAX_OUTPUT_CHANNELS];
94 } Early;
96 // Decorrelator delay line.
97 DelayLine Decorrelator;
98 // There are actually 4 decorrelator taps, but the first occurs at the
99 // initial sample.
100 ALuint DecoTap[3];
102 struct {
103 // Output gain for late reverb.
104 ALfloat Gain;
106 // Attenuation to compensate for the modal density and decay rate of
107 // the late lines.
108 ALfloat DensityGain;
110 // The feed-back and feed-forward all-pass coefficient.
111 ALfloat ApFeedCoeff;
113 // Mixing matrix coefficient.
114 ALfloat MixCoeff;
116 // Late reverb has 4 parallel all-pass filters.
117 ALfloat ApCoeff[4];
118 DelayLine ApDelay[4];
119 ALuint ApOffset[4];
121 // In addition to 4 cyclical delay lines.
122 ALfloat Coeff[4];
123 DelayLine Delay[4];
124 ALuint Offset[4];
126 // The cyclical delay lines are 1-pole low-pass filtered.
127 ALfloat LpCoeff[4];
128 ALfloat LpSample[4];
130 // The gain for each output channel based on 3D panning (only for the
131 // EAX path).
132 ALfloat PanGain[MAX_OUTPUT_CHANNELS];
133 } Late;
135 struct {
136 // Attenuation to compensate for the modal density and decay rate of
137 // the echo line.
138 ALfloat DensityGain;
140 // Echo delay and all-pass lines.
141 DelayLine Delay;
142 DelayLine ApDelay;
144 ALfloat Coeff;
145 ALfloat ApFeedCoeff;
146 ALfloat ApCoeff;
148 ALuint Offset;
149 ALuint ApOffset;
151 // The echo line is 1-pole low-pass filtered.
152 ALfloat LpCoeff;
153 ALfloat LpSample;
155 // Echo mixing coefficient.
156 ALfloat MixCoeff;
157 } Echo;
159 // The current read offset for all delay lines.
160 ALuint Offset;
162 // The gain for each output channel (non-EAX path only; aliased from
163 // Late.PanGain)
164 ALfloat *Gain;
166 /* Temporary storage used when processing. */
167 ALfloat ReverbSamples[MAX_UPDATE_SAMPLES][4];
168 ALfloat EarlySamples[MAX_UPDATE_SAMPLES][4];
169 } ALreverbState;
171 /* This is a user config option for modifying the overall output of the reverb
172 * effect.
174 ALfloat ReverbBoost = 1.0f;
176 /* Specifies whether to use a standard reverb effect in place of EAX reverb */
177 ALboolean EmulateEAXReverb = AL_FALSE;
179 /* This coefficient is used to define the maximum frequency range controlled
180 * by the modulation depth. The current value of 0.1 will allow it to swing
181 * from 0.9x to 1.1x. This value must be below 1. At 1 it will cause the
182 * sampler to stall on the downswing, and above 1 it will cause it to sample
183 * backwards.
185 static const ALfloat MODULATION_DEPTH_COEFF = 0.1f;
187 /* A filter is used to avoid the terrible distortion caused by changing
188 * modulation time and/or depth. To be consistent across different sample
189 * rates, the coefficient must be raised to a constant divided by the sample
190 * rate: coeff^(constant / rate).
192 static const ALfloat MODULATION_FILTER_COEFF = 0.048f;
193 static const ALfloat MODULATION_FILTER_CONST = 100000.0f;
195 // When diffusion is above 0, an all-pass filter is used to take the edge off
196 // the echo effect. It uses the following line length (in seconds).
197 static const ALfloat ECHO_ALLPASS_LENGTH = 0.0133f;
199 // Input into the late reverb is decorrelated between four channels. Their
200 // timings are dependent on a fraction and multiplier. See the
201 // UpdateDecorrelator() routine for the calculations involved.
202 static const ALfloat DECO_FRACTION = 0.15f;
203 static const ALfloat DECO_MULTIPLIER = 2.0f;
205 // All delay line lengths are specified in seconds.
207 // The lengths of the early delay lines.
208 static const ALfloat EARLY_LINE_LENGTH[4] =
210 0.0015f, 0.0045f, 0.0135f, 0.0405f
213 // The lengths of the late all-pass delay lines.
214 static const ALfloat ALLPASS_LINE_LENGTH[4] =
216 0.0151f, 0.0167f, 0.0183f, 0.0200f,
219 // The lengths of the late cyclical delay lines.
220 static const ALfloat LATE_LINE_LENGTH[4] =
222 0.0211f, 0.0311f, 0.0461f, 0.0680f
225 // The late cyclical delay lines have a variable length dependent on the
226 // effect's density parameter (inverted for some reason) and this multiplier.
227 static const ALfloat LATE_LINE_MULTIPLIER = 4.0f;
230 // Basic delay line input/output routines.
231 static inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset)
233 return Delay->Line[offset&Delay->Mask];
236 static inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in)
238 Delay->Line[offset&Delay->Mask] = in;
241 // Given an input sample, this function produces modulation for the late
242 // reverb.
243 static inline ALfloat EAXModulation(ALreverbState *State, ALuint offset, ALfloat in)
245 ALfloat sinus, frac;
246 ALfloat out0, out1;
247 ALuint delay;
249 // Calculate the sinus rythm (dependent on modulation time and the
250 // sampling rate). The center of the sinus is moved to reduce the delay
251 // of the effect when the time or depth are low.
252 sinus = 1.0f - cosf(F_TAU * State->Mod.Index / State->Mod.Range);
254 // Step the modulation index forward, keeping it bound to its range.
255 State->Mod.Index = (State->Mod.Index + 1) % State->Mod.Range;
257 // The depth determines the range over which to read the input samples
258 // from, so it must be filtered to reduce the distortion caused by even
259 // small parameter changes.
260 State->Mod.Filter = lerp(State->Mod.Filter, State->Mod.Depth,
261 State->Mod.Coeff);
263 // Calculate the read offset and fraction between it and the next sample.
264 frac = (1.0f + (State->Mod.Filter * sinus));
265 delay = fastf2u(frac);
266 frac -= delay;
268 // Get the two samples crossed by the offset, and feed the delay line
269 // with the next input sample.
270 out0 = DelayLineOut(&State->Mod.Delay, offset - delay);
271 out1 = DelayLineOut(&State->Mod.Delay, offset - delay - 1);
272 DelayLineIn(&State->Mod.Delay, offset, in);
274 // The output is obtained by linearly interpolating the two samples that
275 // were acquired above.
276 return lerp(out0, out1, frac);
279 // Given some input sample, this function produces four-channel outputs for the
280 // early reflections.
281 static inline ALvoid EarlyReflection(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[4])
283 ALfloat d[4], v, f[4];
284 ALuint i;
286 for(i = 0;i < todo;i++)
288 ALuint offset = State->Offset+i;
290 // Obtain the decayed results of each early delay line.
291 d[0] = DelayLineOut(&State->Early.Delay[0], offset-State->Early.Offset[0]) * State->Early.Coeff[0];
292 d[1] = DelayLineOut(&State->Early.Delay[1], offset-State->Early.Offset[1]) * State->Early.Coeff[1];
293 d[2] = DelayLineOut(&State->Early.Delay[2], offset-State->Early.Offset[2]) * State->Early.Coeff[2];
294 d[3] = DelayLineOut(&State->Early.Delay[3], offset-State->Early.Offset[3]) * State->Early.Coeff[3];
296 /* The following uses a lossless scattering junction from waveguide
297 * theory. It actually amounts to a householder mixing matrix, which
298 * will produce a maximally diffuse response, and means this can
299 * probably be considered a simple feed-back delay network (FDN).
301 * ---
303 * v = 2/N / d_i
304 * ---
305 * i=1
307 v = (d[0] + d[1] + d[2] + d[3]) * 0.5f;
308 // The junction is loaded with the input here.
309 v += DelayLineOut(&State->Delay, offset-State->DelayTap[0]);
311 // Calculate the feed values for the delay lines.
312 f[0] = v - d[0];
313 f[1] = v - d[1];
314 f[2] = v - d[2];
315 f[3] = v - d[3];
317 // Re-feed the delay lines.
318 DelayLineIn(&State->Early.Delay[0], offset, f[0]);
319 DelayLineIn(&State->Early.Delay[1], offset, f[1]);
320 DelayLineIn(&State->Early.Delay[2], offset, f[2]);
321 DelayLineIn(&State->Early.Delay[3], offset, f[3]);
323 // Output the results of the junction for all four channels.
324 out[i][0] = State->Early.Gain * f[0];
325 out[i][1] = State->Early.Gain * f[1];
326 out[i][2] = State->Early.Gain * f[2];
327 out[i][3] = State->Early.Gain * f[3];
331 // Basic attenuated all-pass input/output routine.
332 static inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff)
334 ALfloat out, feed;
336 out = DelayLineOut(Delay, outOffset);
337 feed = feedCoeff * in;
338 DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in);
340 // The time-based attenuation is only applied to the delay output to
341 // keep it from affecting the feed-back path (which is already controlled
342 // by the all-pass feed coefficient).
343 return (coeff * out) - feed;
346 // All-pass input/output routine for late reverb.
347 static inline ALfloat LateAllPassInOut(ALreverbState *State, ALuint offset, ALuint index, ALfloat in)
349 return AllpassInOut(&State->Late.ApDelay[index],
350 offset - State->Late.ApOffset[index],
351 offset, in, State->Late.ApFeedCoeff,
352 State->Late.ApCoeff[index]);
355 // Low-pass filter input/output routine for late reverb.
356 static inline ALfloat LateLowPassInOut(ALreverbState *State, ALuint index, ALfloat in)
358 in = lerp(in, State->Late.LpSample[index], State->Late.LpCoeff[index]);
359 State->Late.LpSample[index] = in;
360 return in;
363 // Given four decorrelated input samples, this function produces four-channel
364 // output for the late reverb.
365 static inline ALvoid LateReverb(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[4])
367 ALfloat d[4], f[4];
368 ALuint i;
370 for(i = 0;i < todo;i++)
372 ALuint offset = State->Offset+i;
374 f[0] = DelayLineOut(&State->Decorrelator, offset);
375 f[1] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[0]);
376 f[2] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[1]);
377 f[3] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[2]);
379 // Obtain the decayed results of the cyclical delay lines, and add the
380 // corresponding input channels. Then pass the results through the
381 // low-pass filters.
382 f[0] += DelayLineOut(&State->Late.Delay[0], offset-State->Late.Offset[0]) * State->Late.Coeff[0];
383 f[1] += DelayLineOut(&State->Late.Delay[1], offset-State->Late.Offset[1]) * State->Late.Coeff[1];
384 f[2] += DelayLineOut(&State->Late.Delay[2], offset-State->Late.Offset[2]) * State->Late.Coeff[2];
385 f[3] += DelayLineOut(&State->Late.Delay[3], offset-State->Late.Offset[3]) * State->Late.Coeff[3];
387 // This is where the feed-back cycles from line 0 to 1 to 3 to 2 and
388 // back to 0.
389 d[0] = LateLowPassInOut(State, 2, f[2]);
390 d[1] = LateLowPassInOut(State, 0, f[0]);
391 d[2] = LateLowPassInOut(State, 3, f[3]);
392 d[3] = LateLowPassInOut(State, 1, f[1]);
394 // To help increase diffusion, run each line through an all-pass filter.
395 // When there is no diffusion, the shortest all-pass filter will feed
396 // the shortest delay line.
397 d[0] = LateAllPassInOut(State, offset, 0, d[0]);
398 d[1] = LateAllPassInOut(State, offset, 1, d[1]);
399 d[2] = LateAllPassInOut(State, offset, 2, d[2]);
400 d[3] = LateAllPassInOut(State, offset, 3, d[3]);
402 /* Late reverb is done with a modified feed-back delay network (FDN)
403 * topology. Four input lines are each fed through their own all-pass
404 * filter and then into the mixing matrix. The four outputs of the
405 * mixing matrix are then cycled back to the inputs. Each output feeds
406 * a different input to form a circlular feed cycle.
408 * The mixing matrix used is a 4D skew-symmetric rotation matrix
409 * derived using a single unitary rotational parameter:
411 * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
412 * [ -a, d, c, -b ]
413 * [ -b, -c, d, a ]
414 * [ -c, b, -a, d ]
416 * The rotation is constructed from the effect's diffusion parameter,
417 * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y
418 * with differing signs, and d is the coefficient x. The matrix is
419 * thus:
421 * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
422 * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
423 * [ y, -y, x, y ] x = cos(t)
424 * [ -y, -y, -y, x ] y = sin(t) / n
426 * To reduce the number of multiplies, the x coefficient is applied
427 * with the cyclical delay line coefficients. Thus only the y
428 * coefficient is applied when mixing, and is modified to be: y / x.
430 f[0] = d[0] + (State->Late.MixCoeff * ( d[1] + -d[2] + d[3]));
431 f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3]));
432 f[2] = d[2] + (State->Late.MixCoeff * ( d[0] + -d[1] + d[3]));
433 f[3] = d[3] + (State->Late.MixCoeff * (-d[0] + -d[1] + -d[2] ));
435 // Output the results of the matrix for all four channels, attenuated by
436 // the late reverb gain (which is attenuated by the 'x' mix coefficient).
437 // Mix early reflections and late reverb.
438 out[i][0] += State->Late.Gain * f[0];
439 out[i][1] += State->Late.Gain * f[1];
440 out[i][2] += State->Late.Gain * f[2];
441 out[i][3] += State->Late.Gain * f[3];
443 // Re-feed the cyclical delay lines.
444 DelayLineIn(&State->Late.Delay[0], offset, f[0]);
445 DelayLineIn(&State->Late.Delay[1], offset, f[1]);
446 DelayLineIn(&State->Late.Delay[2], offset, f[2]);
447 DelayLineIn(&State->Late.Delay[3], offset, f[3]);
451 // Given an input sample, this function mixes echo into the four-channel late
452 // reverb.
453 static inline ALvoid EAXEcho(ALreverbState *State, ALuint todo, ALfloat (*restrict late)[4])
455 ALfloat out, feed;
456 ALuint i;
458 for(i = 0;i < todo;i++)
460 ALuint offset = State->Offset+i;
462 // Get the latest attenuated echo sample for output.
463 feed = DelayLineOut(&State->Echo.Delay, offset-State->Echo.Offset) *
464 State->Echo.Coeff;
466 // Mix the output into the late reverb channels.
467 out = State->Echo.MixCoeff * feed;
468 late[i][0] += out;
469 late[i][1] += out;
470 late[i][2] += out;
471 late[i][3] += out;
473 // Mix the energy-attenuated input with the output and pass it through
474 // the echo low-pass filter.
475 feed += DelayLineOut(&State->Delay, offset-State->DelayTap[1]) *
476 State->Echo.DensityGain;
477 feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff);
478 State->Echo.LpSample = feed;
480 // Then the echo all-pass filter.
481 feed = AllpassInOut(&State->Echo.ApDelay, offset-State->Echo.ApOffset,
482 offset, feed, State->Echo.ApFeedCoeff,
483 State->Echo.ApCoeff);
485 // Feed the delay with the mixed and filtered sample.
486 DelayLineIn(&State->Echo.Delay, offset, feed);
490 // Perform the non-EAX reverb pass on a given input sample, resulting in
491 // four-channel output.
492 static inline ALvoid VerbPass(ALreverbState *State, ALuint todo, const ALfloat *in, ALfloat (*restrict out)[4])
494 ALuint i;
496 // Low-pass filter the incoming samples.
497 for(i = 0;i < todo;i++)
498 DelayLineIn(&State->Delay, State->Offset+i,
499 ALfilterState_processSingle(&State->LpFilter, in[i])
502 // Calculate the early reflection from the first delay tap.
503 EarlyReflection(State, todo, out);
505 // Feed the decorrelator from the energy-attenuated output of the second
506 // delay tap.
507 for(i = 0;i < todo;i++)
509 ALuint offset = State->Offset+i;
510 ALfloat sample = DelayLineOut(&State->Delay, offset - State->DelayTap[1]) *
511 State->Late.DensityGain;
512 DelayLineIn(&State->Decorrelator, offset, sample);
515 // Calculate the late reverb from the decorrelator taps.
516 LateReverb(State, todo, out);
518 // Step all delays forward one sample.
519 State->Offset += todo;
522 // Perform the EAX reverb pass on a given input sample, resulting in four-
523 // channel output.
524 static inline ALvoid EAXVerbPass(ALreverbState *State, ALuint todo, const ALfloat *input, ALfloat (*restrict early)[4], ALfloat (*restrict late)[4])
526 ALuint i;
528 // Band-pass and modulate the incoming samples.
529 for(i = 0;i < todo;i++)
531 ALfloat sample = input[i];
532 sample = ALfilterState_processSingle(&State->LpFilter, sample);
533 sample = ALfilterState_processSingle(&State->HpFilter, sample);
535 // Perform any modulation on the input.
536 sample = EAXModulation(State, State->Offset+i, sample);
538 // Feed the initial delay line.
539 DelayLineIn(&State->Delay, State->Offset+i, sample);
542 // Calculate the early reflection from the first delay tap.
543 EarlyReflection(State, todo, early);
545 // Feed the decorrelator from the energy-attenuated output of the second
546 // delay tap.
547 for(i = 0;i < todo;i++)
549 ALuint offset = State->Offset+i;
550 ALfloat sample = DelayLineOut(&State->Delay, offset - State->DelayTap[1]) *
551 State->Late.DensityGain;
552 DelayLineIn(&State->Decorrelator, offset, sample);
555 // Calculate the late reverb from the decorrelator taps.
556 memset(late, 0, sizeof(*late)*todo);
557 LateReverb(State, todo, late);
559 // Calculate and mix in any echo.
560 EAXEcho(State, todo, late);
562 // Step all delays forward.
563 State->Offset += todo;
566 static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
568 ALfloat (*restrict out)[4] = State->ReverbSamples;
569 ALuint index, c, i;
571 memset(out, 0, SamplesToDo*4*sizeof(ALfloat));
573 /* Process reverb for these samples. */
574 for(index = 0;index < SamplesToDo;)
576 ALuint todo = minu(SamplesToDo-index, MAX_UPDATE_SAMPLES);
578 VerbPass(State, todo, &SamplesIn[index], out);
580 for(c = 0;c < NumChannels;c++)
582 ALfloat gain = State->Gain[c];
583 if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
584 continue;
585 for(i = 0;i < todo;i++)
586 SamplesOut[c][index+i] += gain*out[i][c&3];
589 index += todo;
593 static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
595 ALfloat (*restrict early)[4] = State->EarlySamples;
596 ALfloat (*restrict late)[4] = State->ReverbSamples;
597 ALuint index, c, i;
598 ALfloat gain;
600 /* Process reverb for these samples. */
601 for(index = 0;index < SamplesToDo;)
603 ALuint todo = minu(SamplesToDo-index, MAX_UPDATE_SAMPLES);
605 EAXVerbPass(State, todo, &SamplesIn[index], early, late);
607 for(c = 0;c < NumChannels;c++)
609 gain = State->Early.PanGain[c];
610 if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
612 for(i = 0;i < todo;i++)
613 SamplesOut[c][index+i] += gain*early[i][c&3];
615 gain = State->Late.PanGain[c];
616 if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
618 for(i = 0;i < todo;i++)
619 SamplesOut[c][index+i] += gain*late[i][c&3];
623 index += todo;
627 static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
629 if(State->IsEax)
630 ALreverbState_processEax(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
631 else
632 ALreverbState_processStandard(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
635 // Given the allocated sample buffer, this function updates each delay line
636 // offset.
637 static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay)
639 Delay->Line = &sampleBuffer[(ptrdiff_t)Delay->Line];
642 // Calculate the length of a delay line and store its mask and offset.
643 static ALuint CalcLineLength(ALfloat length, ptrdiff_t offset, ALuint frequency, ALuint extra, DelayLine *Delay)
645 ALuint samples;
647 // All line lengths are powers of 2, calculated from their lengths, with
648 // an additional sample in case of rounding errors.
649 samples = fastf2u(length*frequency) + extra;
650 samples = NextPowerOf2(samples + 1);
651 // All lines share a single sample buffer.
652 Delay->Mask = samples - 1;
653 Delay->Line = (ALfloat*)offset;
654 // Return the sample count for accumulation.
655 return samples;
658 /* Calculates the delay line metrics and allocates the shared sample buffer
659 * for all lines given the sample rate (frequency). If an allocation failure
660 * occurs, it returns AL_FALSE.
662 static ALboolean AllocLines(ALuint frequency, ALreverbState *State)
664 ALuint totalSamples, index;
665 ALfloat length;
666 ALfloat *newBuffer = NULL;
668 // All delay line lengths are calculated to accomodate the full range of
669 // lengths given their respective paramters.
670 totalSamples = 0;
672 /* The modulator's line length is calculated from the maximum modulation
673 * time and depth coefficient, and halfed for the low-to-high frequency
674 * swing. An additional sample is added to keep it stable when there is no
675 * modulation.
677 length = (AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f);
678 totalSamples += CalcLineLength(length, totalSamples, frequency, 1,
679 &State->Mod.Delay);
681 // The initial delay is the sum of the reflections and late reverb
682 // delays. This must include space for storing a loop update to feed the
683 // early reflections, decorrelator, and echo.
684 length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
685 AL_EAXREVERB_MAX_LATE_REVERB_DELAY;
686 totalSamples += CalcLineLength(length, totalSamples, frequency,
687 MAX_UPDATE_SAMPLES, &State->Delay);
689 // The early reflection lines.
690 for(index = 0;index < 4;index++)
691 totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples,
692 frequency, 0, &State->Early.Delay[index]);
694 // The decorrelator line is calculated from the lowest reverb density (a
695 // parameter value of 1). This must include space for storing a loop update
696 // to feed the late reverb.
697 length = (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) *
698 LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER);
699 totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES,
700 &State->Decorrelator);
702 // The late all-pass lines.
703 for(index = 0;index < 4;index++)
704 totalSamples += CalcLineLength(ALLPASS_LINE_LENGTH[index], totalSamples,
705 frequency, 0, &State->Late.ApDelay[index]);
707 // The late delay lines are calculated from the lowest reverb density.
708 for(index = 0;index < 4;index++)
710 length = LATE_LINE_LENGTH[index] * (1.0f + LATE_LINE_MULTIPLIER);
711 totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
712 &State->Late.Delay[index]);
715 // The echo all-pass and delay lines.
716 totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples,
717 frequency, 0, &State->Echo.ApDelay);
718 totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples,
719 frequency, 0, &State->Echo.Delay);
721 if(totalSamples != State->TotalSamples)
723 TRACE("New reverb buffer length: %u samples (%f sec)\n", totalSamples, totalSamples/(float)frequency);
724 newBuffer = realloc(State->SampleBuffer, sizeof(ALfloat) * totalSamples);
725 if(newBuffer == NULL)
726 return AL_FALSE;
727 State->SampleBuffer = newBuffer;
728 State->TotalSamples = totalSamples;
731 // Update all delays to reflect the new sample buffer.
732 RealizeLineOffset(State->SampleBuffer, &State->Delay);
733 RealizeLineOffset(State->SampleBuffer, &State->Decorrelator);
734 for(index = 0;index < 4;index++)
736 RealizeLineOffset(State->SampleBuffer, &State->Early.Delay[index]);
737 RealizeLineOffset(State->SampleBuffer, &State->Late.ApDelay[index]);
738 RealizeLineOffset(State->SampleBuffer, &State->Late.Delay[index]);
740 RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay);
741 RealizeLineOffset(State->SampleBuffer, &State->Echo.ApDelay);
742 RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay);
744 // Clear the sample buffer.
745 for(index = 0;index < State->TotalSamples;index++)
746 State->SampleBuffer[index] = 0.0f;
748 return AL_TRUE;
751 static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
753 ALuint frequency = Device->Frequency, index;
755 // Allocate the delay lines.
756 if(!AllocLines(frequency, State))
757 return AL_FALSE;
759 // Calculate the modulation filter coefficient. Notice that the exponent
760 // is calculated given the current sample rate. This ensures that the
761 // resulting filter response over time is consistent across all sample
762 // rates.
763 State->Mod.Coeff = powf(MODULATION_FILTER_COEFF,
764 MODULATION_FILTER_CONST / frequency);
766 // The early reflection and late all-pass filter line lengths are static,
767 // so their offsets only need to be calculated once.
768 for(index = 0;index < 4;index++)
770 State->Early.Offset[index] = fastf2u(EARLY_LINE_LENGTH[index] *
771 frequency);
772 State->Late.ApOffset[index] = fastf2u(ALLPASS_LINE_LENGTH[index] *
773 frequency);
776 // The echo all-pass filter line length is static, so its offset only
777 // needs to be calculated once.
778 State->Echo.ApOffset = fastf2u(ECHO_ALLPASS_LENGTH * frequency);
780 return AL_TRUE;
783 // Calculate a decay coefficient given the length of each cycle and the time
784 // until the decay reaches -60 dB.
785 static inline ALfloat CalcDecayCoeff(ALfloat length, ALfloat decayTime)
787 return powf(0.001f/*-60 dB*/, length/decayTime);
790 // Calculate a decay length from a coefficient and the time until the decay
791 // reaches -60 dB.
792 static inline ALfloat CalcDecayLength(ALfloat coeff, ALfloat decayTime)
794 return log10f(coeff) * decayTime / log10f(0.001f)/*-60 dB*/;
797 // Calculate an attenuation to be applied to the input of any echo models to
798 // compensate for modal density and decay time.
799 static inline ALfloat CalcDensityGain(ALfloat a)
801 /* The energy of a signal can be obtained by finding the area under the
802 * squared signal. This takes the form of Sum(x_n^2), where x is the
803 * amplitude for the sample n.
805 * Decaying feedback matches exponential decay of the form Sum(a^n),
806 * where a is the attenuation coefficient, and n is the sample. The area
807 * under this decay curve can be calculated as: 1 / (1 - a).
809 * Modifying the above equation to find the squared area under the curve
810 * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
811 * calculated by inverting the square root of this approximation,
812 * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
814 return sqrtf(1.0f - (a * a));
817 // Calculate the mixing matrix coefficients given a diffusion factor.
818 static inline ALvoid CalcMatrixCoeffs(ALfloat diffusion, ALfloat *x, ALfloat *y)
820 ALfloat n, t;
822 // The matrix is of order 4, so n is sqrt (4 - 1).
823 n = sqrtf(3.0f);
824 t = diffusion * atanf(n);
826 // Calculate the first mixing matrix coefficient.
827 *x = cosf(t);
828 // Calculate the second mixing matrix coefficient.
829 *y = sinf(t) / n;
832 // Calculate the limited HF ratio for use with the late reverb low-pass
833 // filters.
834 static ALfloat CalcLimitedHfRatio(ALfloat hfRatio, ALfloat airAbsorptionGainHF, ALfloat decayTime)
836 ALfloat limitRatio;
838 /* Find the attenuation due to air absorption in dB (converting delay
839 * time to meters using the speed of sound). Then reversing the decay
840 * equation, solve for HF ratio. The delay length is cancelled out of
841 * the equation, so it can be calculated once for all lines.
843 limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) *
844 SPEEDOFSOUNDMETRESPERSEC);
845 /* Using the limit calculated above, apply the upper bound to the HF
846 * ratio. Also need to limit the result to a minimum of 0.1, just like the
847 * HF ratio parameter. */
848 return clampf(limitRatio, 0.1f, hfRatio);
851 // Calculate the coefficient for a HF (and eventually LF) decay damping
852 // filter.
853 static inline ALfloat CalcDampingCoeff(ALfloat hfRatio, ALfloat length, ALfloat decayTime, ALfloat decayCoeff, ALfloat cw)
855 ALfloat coeff, g;
857 // Eventually this should boost the high frequencies when the ratio
858 // exceeds 1.
859 coeff = 0.0f;
860 if (hfRatio < 1.0f)
862 // Calculate the low-pass coefficient by dividing the HF decay
863 // coefficient by the full decay coefficient.
864 g = CalcDecayCoeff(length, decayTime * hfRatio) / decayCoeff;
866 // Damping is done with a 1-pole filter, so g needs to be squared.
867 g *= g;
868 if(g < 0.9999f) /* 1-epsilon */
870 /* Be careful with gains < 0.001, as that causes the coefficient
871 * head towards 1, which will flatten the signal. */
872 g = maxf(g, 0.001f);
873 coeff = (1 - g*cw - sqrtf(2*g*(1-cw) - g*g*(1 - cw*cw))) /
874 (1 - g);
877 // Very low decay times will produce minimal output, so apply an
878 // upper bound to the coefficient.
879 coeff = minf(coeff, 0.98f);
881 return coeff;
884 // Update the EAX modulation index, range, and depth. Keep in mind that this
885 // kind of vibrato is additive and not multiplicative as one may expect. The
886 // downswing will sound stronger than the upswing.
887 static ALvoid UpdateModulator(ALfloat modTime, ALfloat modDepth, ALuint frequency, ALreverbState *State)
889 ALuint range;
891 /* Modulation is calculated in two parts.
893 * The modulation time effects the sinus applied to the change in
894 * frequency. An index out of the current time range (both in samples)
895 * is incremented each sample. The range is bound to a reasonable
896 * minimum (1 sample) and when the timing changes, the index is rescaled
897 * to the new range (to keep the sinus consistent).
899 range = maxu(fastf2u(modTime*frequency), 1);
900 State->Mod.Index = (ALuint)(State->Mod.Index * (ALuint64)range /
901 State->Mod.Range);
902 State->Mod.Range = range;
904 /* The modulation depth effects the amount of frequency change over the
905 * range of the sinus. It needs to be scaled by the modulation time so
906 * that a given depth produces a consistent change in frequency over all
907 * ranges of time. Since the depth is applied to a sinus value, it needs
908 * to be halfed once for the sinus range and again for the sinus swing
909 * in time (half of it is spent decreasing the frequency, half is spent
910 * increasing it).
912 State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f /
913 2.0f * frequency;
916 // Update the offsets for the initial effect delay line.
917 static ALvoid UpdateDelayLine(ALfloat earlyDelay, ALfloat lateDelay, ALuint frequency, ALreverbState *State)
919 // Calculate the initial delay taps.
920 State->DelayTap[0] = fastf2u(earlyDelay * frequency);
921 State->DelayTap[1] = fastf2u((earlyDelay + lateDelay) * frequency);
924 // Update the early reflections gain and line coefficients.
925 static ALvoid UpdateEarlyLines(ALfloat reverbGain, ALfloat earlyGain, ALfloat lateDelay, ALreverbState *State)
927 ALuint index;
929 // Calculate the early reflections gain (from the master effect gain, and
930 // reflections gain parameters) with a constant attenuation of 0.5.
931 State->Early.Gain = 0.5f * reverbGain * earlyGain;
933 // Calculate the gain (coefficient) for each early delay line using the
934 // late delay time. This expands the early reflections to the start of
935 // the late reverb.
936 for(index = 0;index < 4;index++)
937 State->Early.Coeff[index] = CalcDecayCoeff(EARLY_LINE_LENGTH[index],
938 lateDelay);
941 // Update the offsets for the decorrelator line.
942 static ALvoid UpdateDecorrelator(ALfloat density, ALuint frequency, ALreverbState *State)
944 ALuint index;
945 ALfloat length;
947 /* The late reverb inputs are decorrelated to smooth the reverb tail and
948 * reduce harsh echos. The first tap occurs immediately, while the
949 * remaining taps are delayed by multiples of a fraction of the smallest
950 * cyclical delay time.
952 * offset[index] = (FRACTION (MULTIPLIER^index)) smallest_delay
954 for(index = 0;index < 3;index++)
956 length = (DECO_FRACTION * powf(DECO_MULTIPLIER, (ALfloat)index)) *
957 LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER));
958 State->DecoTap[index] = fastf2u(length * frequency);
962 // Update the late reverb gains, line lengths, and line coefficients.
963 static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix, ALfloat density, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State)
965 ALfloat length;
966 ALuint index;
968 /* Calculate the late reverb gain (from the master effect gain, and late
969 * reverb gain parameters). Since the output is tapped prior to the
970 * application of the next delay line coefficients, this gain needs to be
971 * attenuated by the 'x' mixing matrix coefficient as well. Also attenuate
972 * the late reverb when echo depth is high and diffusion is low, so the
973 * echo is slightly stronger than the decorrelated echos in the reverb
974 * tail.
976 State->Late.Gain = reverbGain * lateGain * xMix *
977 (1.0f - (echoDepth*0.5f*(1.0f - diffusion)));
979 /* To compensate for changes in modal density and decay time of the late
980 * reverb signal, the input is attenuated based on the maximal energy of
981 * the outgoing signal. This approximation is used to keep the apparent
982 * energy of the signal equal for all ranges of density and decay time.
984 * The average length of the cyclcical delay lines is used to calculate
985 * the attenuation coefficient.
987 length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] +
988 LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]) / 4.0f;
989 length *= 1.0f + (density * LATE_LINE_MULTIPLIER);
990 State->Late.DensityGain = CalcDensityGain(
991 CalcDecayCoeff(length, decayTime)
994 // Calculate the all-pass feed-back and feed-forward coefficient.
995 State->Late.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f);
997 for(index = 0;index < 4;index++)
999 // Calculate the gain (coefficient) for each all-pass line.
1000 State->Late.ApCoeff[index] = CalcDecayCoeff(
1001 ALLPASS_LINE_LENGTH[index], decayTime
1004 // Calculate the length (in seconds) of each cyclical delay line.
1005 length = LATE_LINE_LENGTH[index] *
1006 (1.0f + (density * LATE_LINE_MULTIPLIER));
1008 // Calculate the delay offset for each cyclical delay line.
1009 State->Late.Offset[index] = fastf2u(length * frequency);
1011 // Calculate the gain (coefficient) for each cyclical line.
1012 State->Late.Coeff[index] = CalcDecayCoeff(length, decayTime);
1014 // Calculate the damping coefficient for each low-pass filter.
1015 State->Late.LpCoeff[index] = CalcDampingCoeff(
1016 hfRatio, length, decayTime, State->Late.Coeff[index], cw
1019 // Attenuate the cyclical line coefficients by the mixing coefficient
1020 // (x).
1021 State->Late.Coeff[index] *= xMix;
1025 // Update the echo gain, line offset, line coefficients, and mixing
1026 // coefficients.
1027 static ALvoid UpdateEchoLine(ALfloat reverbGain, ALfloat lateGain, ALfloat echoTime, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State)
1029 // Update the offset and coefficient for the echo delay line.
1030 State->Echo.Offset = fastf2u(echoTime * frequency);
1032 // Calculate the decay coefficient for the echo line.
1033 State->Echo.Coeff = CalcDecayCoeff(echoTime, decayTime);
1035 // Calculate the energy-based attenuation coefficient for the echo delay
1036 // line.
1037 State->Echo.DensityGain = CalcDensityGain(State->Echo.Coeff);
1039 // Calculate the echo all-pass feed coefficient.
1040 State->Echo.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f);
1042 // Calculate the echo all-pass attenuation coefficient.
1043 State->Echo.ApCoeff = CalcDecayCoeff(ECHO_ALLPASS_LENGTH, decayTime);
1045 // Calculate the damping coefficient for each low-pass filter.
1046 State->Echo.LpCoeff = CalcDampingCoeff(hfRatio, echoTime, decayTime,
1047 State->Echo.Coeff, cw);
1049 /* Calculate the echo mixing coefficients. The first is applied to the
1050 * echo itself. The second is used to attenuate the late reverb when
1051 * echo depth is high and diffusion is low, so the echo is slightly
1052 * stronger than the decorrelated echos in the reverb tail.
1054 State->Echo.MixCoeff = reverbGain * lateGain * echoDepth;
1057 // Update the early and late 3D panning gains.
1058 static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALreverbState *State)
1060 ALfloat earlyPan[3] = { ReflectionsPan[0], ReflectionsPan[1], -ReflectionsPan[2] };
1061 ALfloat latePan[3] = { LateReverbPan[0], LateReverbPan[1], -LateReverbPan[2] };
1062 ALfloat AmbientGains[MAX_OUTPUT_CHANNELS];
1063 ALfloat DirGains[MAX_OUTPUT_CHANNELS];
1064 ALfloat length, invlen;
1065 ALuint i;
1067 // Bost gain by sqrt(2)? Seems to better match other implementations....
1068 Gain *= 1.4142f;
1070 ComputeAmbientGains(Device, Gain, AmbientGains);
1072 length = earlyPan[0]*earlyPan[0] + earlyPan[1]*earlyPan[1] + earlyPan[2]*earlyPan[2];
1073 if(!(length > FLT_EPSILON))
1075 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
1076 State->Early.PanGain[i] = AmbientGains[i];
1078 else
1080 invlen = 1.0f / sqrtf(length);
1081 earlyPan[0] *= invlen;
1082 earlyPan[1] *= invlen;
1083 earlyPan[2] *= invlen;
1085 length = minf(length, 1.0f);
1086 ComputeDirectionalGains(Device, earlyPan, Gain, DirGains);
1087 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
1088 State->Early.PanGain[i] = lerp(AmbientGains[i], DirGains[i], length);
1091 length = latePan[0]*latePan[0] + latePan[1]*latePan[1] + latePan[2]*latePan[2];
1092 if(!(length > FLT_EPSILON))
1094 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
1095 State->Late.PanGain[i] = AmbientGains[i];
1097 else
1099 invlen = 1.0f / sqrtf(length);
1100 latePan[0] *= invlen;
1101 latePan[1] *= invlen;
1102 latePan[2] *= invlen;
1104 length = minf(length, 1.0f);
1105 ComputeDirectionalGains(Device, latePan, Gain, DirGains);
1106 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
1107 State->Late.PanGain[i] = lerp(AmbientGains[i], DirGains[i], length);
1111 static ALvoid ALreverbState_update(ALreverbState *State, ALCdevice *Device, const ALeffectslot *Slot)
1113 const ALeffectProps *props = &Slot->EffectProps;
1114 ALuint frequency = Device->Frequency;
1115 ALfloat lfscale, hfscale, hfRatio;
1116 ALfloat cw, x, y;
1118 if(Slot->EffectType == AL_EFFECT_EAXREVERB && !EmulateEAXReverb)
1119 State->IsEax = AL_TRUE;
1120 else if(Slot->EffectType == AL_EFFECT_REVERB || EmulateEAXReverb)
1121 State->IsEax = AL_FALSE;
1123 // Calculate the master filters
1124 hfscale = props->Reverb.HFReference / frequency;
1125 ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf,
1126 props->Reverb.GainHF, hfscale, 0.0f);
1127 lfscale = props->Reverb.LFReference / frequency;
1128 ALfilterState_setParams(&State->HpFilter, ALfilterType_LowShelf,
1129 props->Reverb.GainLF, lfscale, 0.0f);
1131 // Update the modulator line.
1132 UpdateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth,
1133 frequency, State);
1135 // Update the initial effect delay.
1136 UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
1137 frequency, State);
1139 // Update the early lines.
1140 UpdateEarlyLines(props->Reverb.Gain, props->Reverb.ReflectionsGain,
1141 props->Reverb.LateReverbDelay, State);
1143 // Update the decorrelator.
1144 UpdateDecorrelator(props->Reverb.Density, frequency, State);
1146 // Get the mixing matrix coefficients (x and y).
1147 CalcMatrixCoeffs(props->Reverb.Diffusion, &x, &y);
1148 // Then divide x into y to simplify the matrix calculation.
1149 State->Late.MixCoeff = y / x;
1151 // If the HF limit parameter is flagged, calculate an appropriate limit
1152 // based on the air absorption parameter.
1153 hfRatio = props->Reverb.DecayHFRatio;
1154 if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
1155 hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
1156 props->Reverb.DecayTime);
1158 cw = cosf(F_TAU * hfscale);
1159 // Update the late lines.
1160 UpdateLateLines(props->Reverb.Gain, props->Reverb.LateReverbGain, x,
1161 props->Reverb.Density, props->Reverb.DecayTime,
1162 props->Reverb.Diffusion, props->Reverb.EchoDepth,
1163 hfRatio, cw, frequency, State);
1165 // Update the echo line.
1166 UpdateEchoLine(props->Reverb.Gain, props->Reverb.LateReverbGain,
1167 props->Reverb.EchoTime, props->Reverb.DecayTime,
1168 props->Reverb.Diffusion, props->Reverb.EchoDepth,
1169 hfRatio, cw, frequency, State);
1171 // Update early and late 3D panning.
1172 Update3DPanning(Device, props->Reverb.ReflectionsPan,
1173 props->Reverb.LateReverbPan,
1174 Slot->Gain * ReverbBoost, State);
1178 static ALvoid ALreverbState_Destruct(ALreverbState *State)
1180 free(State->SampleBuffer);
1181 State->SampleBuffer = NULL;
1184 DECLARE_DEFAULT_ALLOCATORS(ALreverbState)
1186 DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);
1189 typedef struct ALreverbStateFactory {
1190 DERIVE_FROM_TYPE(ALeffectStateFactory);
1191 } ALreverbStateFactory;
1193 static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory))
1195 ALreverbState *state;
1196 ALuint index;
1198 state = ALreverbState_New(sizeof(*state));
1199 if(!state) return NULL;
1200 SET_VTABLE2(ALreverbState, ALeffectState, state);
1202 state->TotalSamples = 0;
1203 state->SampleBuffer = NULL;
1205 ALfilterState_clear(&state->LpFilter);
1206 ALfilterState_clear(&state->HpFilter);
1208 state->Mod.Delay.Mask = 0;
1209 state->Mod.Delay.Line = NULL;
1210 state->Mod.Index = 0;
1211 state->Mod.Range = 1;
1212 state->Mod.Depth = 0.0f;
1213 state->Mod.Coeff = 0.0f;
1214 state->Mod.Filter = 0.0f;
1216 state->Delay.Mask = 0;
1217 state->Delay.Line = NULL;
1218 state->DelayTap[0] = 0;
1219 state->DelayTap[1] = 0;
1221 state->Early.Gain = 0.0f;
1222 for(index = 0;index < 4;index++)
1224 state->Early.Coeff[index] = 0.0f;
1225 state->Early.Delay[index].Mask = 0;
1226 state->Early.Delay[index].Line = NULL;
1227 state->Early.Offset[index] = 0;
1230 state->Decorrelator.Mask = 0;
1231 state->Decorrelator.Line = NULL;
1232 state->DecoTap[0] = 0;
1233 state->DecoTap[1] = 0;
1234 state->DecoTap[2] = 0;
1236 state->Late.Gain = 0.0f;
1237 state->Late.DensityGain = 0.0f;
1238 state->Late.ApFeedCoeff = 0.0f;
1239 state->Late.MixCoeff = 0.0f;
1240 for(index = 0;index < 4;index++)
1242 state->Late.ApCoeff[index] = 0.0f;
1243 state->Late.ApDelay[index].Mask = 0;
1244 state->Late.ApDelay[index].Line = NULL;
1245 state->Late.ApOffset[index] = 0;
1247 state->Late.Coeff[index] = 0.0f;
1248 state->Late.Delay[index].Mask = 0;
1249 state->Late.Delay[index].Line = NULL;
1250 state->Late.Offset[index] = 0;
1252 state->Late.LpCoeff[index] = 0.0f;
1253 state->Late.LpSample[index] = 0.0f;
1256 for(index = 0;index < MAX_OUTPUT_CHANNELS;index++)
1258 state->Early.PanGain[index] = 0.0f;
1259 state->Late.PanGain[index] = 0.0f;
1262 state->Echo.DensityGain = 0.0f;
1263 state->Echo.Delay.Mask = 0;
1264 state->Echo.Delay.Line = NULL;
1265 state->Echo.ApDelay.Mask = 0;
1266 state->Echo.ApDelay.Line = NULL;
1267 state->Echo.Coeff = 0.0f;
1268 state->Echo.ApFeedCoeff = 0.0f;
1269 state->Echo.ApCoeff = 0.0f;
1270 state->Echo.Offset = 0;
1271 state->Echo.ApOffset = 0;
1272 state->Echo.LpCoeff = 0.0f;
1273 state->Echo.LpSample = 0.0f;
1274 state->Echo.MixCoeff = 0.0f;
1276 state->Offset = 0;
1278 state->Gain = state->Late.PanGain;
1280 return STATIC_CAST(ALeffectState, state);
1283 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALreverbStateFactory);
1285 ALeffectStateFactory *ALreverbStateFactory_getFactory(void)
1287 static ALreverbStateFactory ReverbFactory = { { GET_VTABLE2(ALreverbStateFactory, ALeffectStateFactory) } };
1289 return STATIC_CAST(ALeffectStateFactory, &ReverbFactory);
1293 void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1295 ALeffectProps *props = &effect->Props;
1296 switch(param)
1298 case AL_EAXREVERB_DECAY_HFLIMIT:
1299 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
1300 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1301 props->Reverb.DecayHFLimit = val;
1302 break;
1304 default:
1305 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1308 void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1310 ALeaxreverb_setParami(effect, context, param, vals[0]);
1312 void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1314 ALeffectProps *props = &effect->Props;
1315 switch(param)
1317 case AL_EAXREVERB_DENSITY:
1318 if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
1319 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1320 props->Reverb.Density = val;
1321 break;
1323 case AL_EAXREVERB_DIFFUSION:
1324 if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
1325 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1326 props->Reverb.Diffusion = val;
1327 break;
1329 case AL_EAXREVERB_GAIN:
1330 if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
1331 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1332 props->Reverb.Gain = val;
1333 break;
1335 case AL_EAXREVERB_GAINHF:
1336 if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
1337 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1338 props->Reverb.GainHF = val;
1339 break;
1341 case AL_EAXREVERB_GAINLF:
1342 if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
1343 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1344 props->Reverb.GainLF = val;
1345 break;
1347 case AL_EAXREVERB_DECAY_TIME:
1348 if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
1349 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1350 props->Reverb.DecayTime = val;
1351 break;
1353 case AL_EAXREVERB_DECAY_HFRATIO:
1354 if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
1355 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1356 props->Reverb.DecayHFRatio = val;
1357 break;
1359 case AL_EAXREVERB_DECAY_LFRATIO:
1360 if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
1361 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1362 props->Reverb.DecayLFRatio = val;
1363 break;
1365 case AL_EAXREVERB_REFLECTIONS_GAIN:
1366 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
1367 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1368 props->Reverb.ReflectionsGain = val;
1369 break;
1371 case AL_EAXREVERB_REFLECTIONS_DELAY:
1372 if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
1373 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1374 props->Reverb.ReflectionsDelay = val;
1375 break;
1377 case AL_EAXREVERB_LATE_REVERB_GAIN:
1378 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
1379 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1380 props->Reverb.LateReverbGain = val;
1381 break;
1383 case AL_EAXREVERB_LATE_REVERB_DELAY:
1384 if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
1385 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1386 props->Reverb.LateReverbDelay = val;
1387 break;
1389 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1390 if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
1391 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1392 props->Reverb.AirAbsorptionGainHF = val;
1393 break;
1395 case AL_EAXREVERB_ECHO_TIME:
1396 if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
1397 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1398 props->Reverb.EchoTime = val;
1399 break;
1401 case AL_EAXREVERB_ECHO_DEPTH:
1402 if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
1403 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1404 props->Reverb.EchoDepth = val;
1405 break;
1407 case AL_EAXREVERB_MODULATION_TIME:
1408 if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
1409 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1410 props->Reverb.ModulationTime = val;
1411 break;
1413 case AL_EAXREVERB_MODULATION_DEPTH:
1414 if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
1415 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1416 props->Reverb.ModulationDepth = val;
1417 break;
1419 case AL_EAXREVERB_HFREFERENCE:
1420 if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
1421 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1422 props->Reverb.HFReference = val;
1423 break;
1425 case AL_EAXREVERB_LFREFERENCE:
1426 if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
1427 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1428 props->Reverb.LFReference = val;
1429 break;
1431 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1432 if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
1433 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1434 props->Reverb.RoomRolloffFactor = val;
1435 break;
1437 default:
1438 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1441 void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1443 ALeffectProps *props = &effect->Props;
1444 switch(param)
1446 case AL_EAXREVERB_REFLECTIONS_PAN:
1447 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1448 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1449 LockContext(context);
1450 props->Reverb.ReflectionsPan[0] = vals[0];
1451 props->Reverb.ReflectionsPan[1] = vals[1];
1452 props->Reverb.ReflectionsPan[2] = vals[2];
1453 UnlockContext(context);
1454 break;
1455 case AL_EAXREVERB_LATE_REVERB_PAN:
1456 if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
1457 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1458 LockContext(context);
1459 props->Reverb.LateReverbPan[0] = vals[0];
1460 props->Reverb.LateReverbPan[1] = vals[1];
1461 props->Reverb.LateReverbPan[2] = vals[2];
1462 UnlockContext(context);
1463 break;
1465 default:
1466 ALeaxreverb_setParamf(effect, context, param, vals[0]);
1467 break;
1471 void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1473 const ALeffectProps *props = &effect->Props;
1474 switch(param)
1476 case AL_EAXREVERB_DECAY_HFLIMIT:
1477 *val = props->Reverb.DecayHFLimit;
1478 break;
1480 default:
1481 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1484 void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1486 ALeaxreverb_getParami(effect, context, param, vals);
1488 void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1490 const ALeffectProps *props = &effect->Props;
1491 switch(param)
1493 case AL_EAXREVERB_DENSITY:
1494 *val = props->Reverb.Density;
1495 break;
1497 case AL_EAXREVERB_DIFFUSION:
1498 *val = props->Reverb.Diffusion;
1499 break;
1501 case AL_EAXREVERB_GAIN:
1502 *val = props->Reverb.Gain;
1503 break;
1505 case AL_EAXREVERB_GAINHF:
1506 *val = props->Reverb.GainHF;
1507 break;
1509 case AL_EAXREVERB_GAINLF:
1510 *val = props->Reverb.GainLF;
1511 break;
1513 case AL_EAXREVERB_DECAY_TIME:
1514 *val = props->Reverb.DecayTime;
1515 break;
1517 case AL_EAXREVERB_DECAY_HFRATIO:
1518 *val = props->Reverb.DecayHFRatio;
1519 break;
1521 case AL_EAXREVERB_DECAY_LFRATIO:
1522 *val = props->Reverb.DecayLFRatio;
1523 break;
1525 case AL_EAXREVERB_REFLECTIONS_GAIN:
1526 *val = props->Reverb.ReflectionsGain;
1527 break;
1529 case AL_EAXREVERB_REFLECTIONS_DELAY:
1530 *val = props->Reverb.ReflectionsDelay;
1531 break;
1533 case AL_EAXREVERB_LATE_REVERB_GAIN:
1534 *val = props->Reverb.LateReverbGain;
1535 break;
1537 case AL_EAXREVERB_LATE_REVERB_DELAY:
1538 *val = props->Reverb.LateReverbDelay;
1539 break;
1541 case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
1542 *val = props->Reverb.AirAbsorptionGainHF;
1543 break;
1545 case AL_EAXREVERB_ECHO_TIME:
1546 *val = props->Reverb.EchoTime;
1547 break;
1549 case AL_EAXREVERB_ECHO_DEPTH:
1550 *val = props->Reverb.EchoDepth;
1551 break;
1553 case AL_EAXREVERB_MODULATION_TIME:
1554 *val = props->Reverb.ModulationTime;
1555 break;
1557 case AL_EAXREVERB_MODULATION_DEPTH:
1558 *val = props->Reverb.ModulationDepth;
1559 break;
1561 case AL_EAXREVERB_HFREFERENCE:
1562 *val = props->Reverb.HFReference;
1563 break;
1565 case AL_EAXREVERB_LFREFERENCE:
1566 *val = props->Reverb.LFReference;
1567 break;
1569 case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
1570 *val = props->Reverb.RoomRolloffFactor;
1571 break;
1573 default:
1574 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1577 void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
1579 const ALeffectProps *props = &effect->Props;
1580 switch(param)
1582 case AL_EAXREVERB_REFLECTIONS_PAN:
1583 LockContext(context);
1584 vals[0] = props->Reverb.ReflectionsPan[0];
1585 vals[1] = props->Reverb.ReflectionsPan[1];
1586 vals[2] = props->Reverb.ReflectionsPan[2];
1587 UnlockContext(context);
1588 break;
1589 case AL_EAXREVERB_LATE_REVERB_PAN:
1590 LockContext(context);
1591 vals[0] = props->Reverb.LateReverbPan[0];
1592 vals[1] = props->Reverb.LateReverbPan[1];
1593 vals[2] = props->Reverb.LateReverbPan[2];
1594 UnlockContext(context);
1595 break;
1597 default:
1598 ALeaxreverb_getParamf(effect, context, param, vals);
1599 break;
1603 DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
1605 void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
1607 ALeffectProps *props = &effect->Props;
1608 switch(param)
1610 case AL_REVERB_DECAY_HFLIMIT:
1611 if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
1612 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1613 props->Reverb.DecayHFLimit = val;
1614 break;
1616 default:
1617 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1620 void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
1622 ALreverb_setParami(effect, context, param, vals[0]);
1624 void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
1626 ALeffectProps *props = &effect->Props;
1627 switch(param)
1629 case AL_REVERB_DENSITY:
1630 if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
1631 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1632 props->Reverb.Density = val;
1633 break;
1635 case AL_REVERB_DIFFUSION:
1636 if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
1637 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1638 props->Reverb.Diffusion = val;
1639 break;
1641 case AL_REVERB_GAIN:
1642 if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
1643 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1644 props->Reverb.Gain = val;
1645 break;
1647 case AL_REVERB_GAINHF:
1648 if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
1649 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1650 props->Reverb.GainHF = val;
1651 break;
1653 case AL_REVERB_DECAY_TIME:
1654 if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
1655 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1656 props->Reverb.DecayTime = val;
1657 break;
1659 case AL_REVERB_DECAY_HFRATIO:
1660 if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
1661 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1662 props->Reverb.DecayHFRatio = val;
1663 break;
1665 case AL_REVERB_REFLECTIONS_GAIN:
1666 if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
1667 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1668 props->Reverb.ReflectionsGain = val;
1669 break;
1671 case AL_REVERB_REFLECTIONS_DELAY:
1672 if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
1673 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1674 props->Reverb.ReflectionsDelay = val;
1675 break;
1677 case AL_REVERB_LATE_REVERB_GAIN:
1678 if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
1679 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1680 props->Reverb.LateReverbGain = val;
1681 break;
1683 case AL_REVERB_LATE_REVERB_DELAY:
1684 if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
1685 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1686 props->Reverb.LateReverbDelay = val;
1687 break;
1689 case AL_REVERB_AIR_ABSORPTION_GAINHF:
1690 if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
1691 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1692 props->Reverb.AirAbsorptionGainHF = val;
1693 break;
1695 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
1696 if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
1697 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
1698 props->Reverb.RoomRolloffFactor = val;
1699 break;
1701 default:
1702 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1705 void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
1707 ALreverb_setParamf(effect, context, param, vals[0]);
1710 void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
1712 const ALeffectProps *props = &effect->Props;
1713 switch(param)
1715 case AL_REVERB_DECAY_HFLIMIT:
1716 *val = props->Reverb.DecayHFLimit;
1717 break;
1719 default:
1720 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1723 void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
1725 ALreverb_getParami(effect, context, param, vals);
1727 void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
1729 const ALeffectProps *props = &effect->Props;
1730 switch(param)
1732 case AL_REVERB_DENSITY:
1733 *val = props->Reverb.Density;
1734 break;
1736 case AL_REVERB_DIFFUSION:
1737 *val = props->Reverb.Diffusion;
1738 break;
1740 case AL_REVERB_GAIN:
1741 *val = props->Reverb.Gain;
1742 break;
1744 case AL_REVERB_GAINHF:
1745 *val = props->Reverb.GainHF;
1746 break;
1748 case AL_REVERB_DECAY_TIME:
1749 *val = props->Reverb.DecayTime;
1750 break;
1752 case AL_REVERB_DECAY_HFRATIO:
1753 *val = props->Reverb.DecayHFRatio;
1754 break;
1756 case AL_REVERB_REFLECTIONS_GAIN:
1757 *val = props->Reverb.ReflectionsGain;
1758 break;
1760 case AL_REVERB_REFLECTIONS_DELAY:
1761 *val = props->Reverb.ReflectionsDelay;
1762 break;
1764 case AL_REVERB_LATE_REVERB_GAIN:
1765 *val = props->Reverb.LateReverbGain;
1766 break;
1768 case AL_REVERB_LATE_REVERB_DELAY:
1769 *val = props->Reverb.LateReverbDelay;
1770 break;
1772 case AL_REVERB_AIR_ABSORPTION_GAINHF:
1773 *val = props->Reverb.AirAbsorptionGainHF;
1774 break;
1776 case AL_REVERB_ROOM_ROLLOFF_FACTOR:
1777 *val = props->Reverb.RoomRolloffFactor;
1778 break;
1780 default:
1781 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
1784 void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
1786 ALreverb_getParamf(effect, context, param, vals);
1789 DEFINE_ALEFFECT_VTABLE(ALreverb);