Mercurial > audio-send
diff Alc/alcReverb.c @ 0:f9476ff7637e
initial forking of open-al to create multiple listeners
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 13:02:31 -0700 |
parents | |
children |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/Alc/alcReverb.c Tue Oct 25 13:02:31 2011 -0700 1.3 @@ -0,0 +1,1374 @@ 1.4 +/** 1.5 + * Reverb for the OpenAL cross platform audio library 1.6 + * Copyright (C) 2008-2009 by Christopher Fitzgerald. 1.7 + * This library is free software; you can redistribute it and/or 1.8 + * modify it under the terms of the GNU Library General Public 1.9 + * License as published by the Free Software Foundation; either 1.10 + * version 2 of the License, or (at your option) any later version. 1.11 + * 1.12 + * This library is distributed in the hope that it will be useful, 1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.15 + * Library General Public License for more details. 1.16 + * 1.17 + * You should have received a copy of the GNU Library General Public 1.18 + * License along with this library; if not, write to the 1.19 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 1.20 + * Boston, MA 02111-1307, USA. 1.21 + * Or go to http://www.gnu.org/copyleft/lgpl.html 1.22 + */ 1.23 + 1.24 +#include "config.h" 1.25 + 1.26 +#include <stdio.h> 1.27 +#include <stdlib.h> 1.28 +#include <math.h> 1.29 + 1.30 +#include "AL/al.h" 1.31 +#include "AL/alc.h" 1.32 +#include "alMain.h" 1.33 +#include "alAuxEffectSlot.h" 1.34 +#include "alEffect.h" 1.35 +#include "alError.h" 1.36 +#include "alu.h" 1.37 + 1.38 +typedef struct DelayLine 1.39 +{ 1.40 + // The delay lines use sample lengths that are powers of 2 to allow the 1.41 + // use of bit-masking instead of a modulus for wrapping. 1.42 + ALuint Mask; 1.43 + ALfloat *Line; 1.44 +} DelayLine; 1.45 + 1.46 +typedef struct ALverbState { 1.47 + // Must be first in all effects! 1.48 + ALeffectState state; 1.49 + 1.50 + // All delay lines are allocated as a single buffer to reduce memory 1.51 + // fragmentation and management code. 1.52 + ALfloat *SampleBuffer; 1.53 + ALuint TotalSamples; 1.54 + // Master effect low-pass filter (2 chained 1-pole filters). 1.55 + FILTER LpFilter; 1.56 + ALfloat LpHistory[2]; 1.57 + struct { 1.58 + // Modulator delay line. 1.59 + DelayLine Delay; 1.60 + // The vibrato time is tracked with an index over a modulus-wrapped 1.61 + // range (in samples). 1.62 + ALuint Index; 1.63 + ALuint Range; 1.64 + // The depth of frequency change (also in samples) and its filter. 1.65 + ALfloat Depth; 1.66 + ALfloat Coeff; 1.67 + ALfloat Filter; 1.68 + } Mod; 1.69 + // Initial effect delay. 1.70 + DelayLine Delay; 1.71 + // The tap points for the initial delay. First tap goes to early 1.72 + // reflections, the last to late reverb. 1.73 + ALuint DelayTap[2]; 1.74 + struct { 1.75 + // Output gain for early reflections. 1.76 + ALfloat Gain; 1.77 + // Early reflections are done with 4 delay lines. 1.78 + ALfloat Coeff[4]; 1.79 + DelayLine Delay[4]; 1.80 + ALuint Offset[4]; 1.81 + // The gain for each output channel based on 3D panning (only for the 1.82 + // EAX path). 1.83 + ALfloat PanGain[MAXCHANNELS]; 1.84 + } Early; 1.85 + // Decorrelator delay line. 1.86 + DelayLine Decorrelator; 1.87 + // There are actually 4 decorrelator taps, but the first occurs at the 1.88 + // initial sample. 1.89 + ALuint DecoTap[3]; 1.90 + struct { 1.91 + // Output gain for late reverb. 1.92 + ALfloat Gain; 1.93 + // Attenuation to compensate for the modal density and decay rate of 1.94 + // the late lines. 1.95 + ALfloat DensityGain; 1.96 + // The feed-back and feed-forward all-pass coefficient. 1.97 + ALfloat ApFeedCoeff; 1.98 + // Mixing matrix coefficient. 1.99 + ALfloat MixCoeff; 1.100 + // Late reverb has 4 parallel all-pass filters. 1.101 + ALfloat ApCoeff[4]; 1.102 + DelayLine ApDelay[4]; 1.103 + ALuint ApOffset[4]; 1.104 + // In addition to 4 cyclical delay lines. 1.105 + ALfloat Coeff[4]; 1.106 + DelayLine Delay[4]; 1.107 + ALuint Offset[4]; 1.108 + // The cyclical delay lines are 1-pole low-pass filtered. 1.109 + ALfloat LpCoeff[4]; 1.110 + ALfloat LpSample[4]; 1.111 + // The gain for each output channel based on 3D panning (only for the 1.112 + // EAX path). 1.113 + ALfloat PanGain[MAXCHANNELS]; 1.114 + } Late; 1.115 + struct { 1.116 + // Attenuation to compensate for the modal density and decay rate of 1.117 + // the echo line. 1.118 + ALfloat DensityGain; 1.119 + // Echo delay and all-pass lines. 1.120 + DelayLine Delay; 1.121 + DelayLine ApDelay; 1.122 + ALfloat Coeff; 1.123 + ALfloat ApFeedCoeff; 1.124 + ALfloat ApCoeff; 1.125 + ALuint Offset; 1.126 + ALuint ApOffset; 1.127 + // The echo line is 1-pole low-pass filtered. 1.128 + ALfloat LpCoeff; 1.129 + ALfloat LpSample; 1.130 + // Echo mixing coefficients. 1.131 + ALfloat MixCoeff[2]; 1.132 + } Echo; 1.133 + // The current read offset for all delay lines. 1.134 + ALuint Offset; 1.135 + 1.136 + // The gain for each output channel (non-EAX path only; aliased from 1.137 + // Late.PanGain) 1.138 + ALfloat *Gain; 1.139 +} ALverbState; 1.140 + 1.141 +/* This is a user config option for modifying the overall output of the reverb 1.142 + * effect. 1.143 + */ 1.144 +ALfloat ReverbBoost = 1.0f; 1.145 + 1.146 +/* Specifies whether to use a standard reverb effect in place of EAX reverb */ 1.147 +ALboolean EmulateEAXReverb = AL_FALSE; 1.148 + 1.149 +/* This coefficient is used to define the maximum frequency range controlled 1.150 + * by the modulation depth. The current value of 0.1 will allow it to swing 1.151 + * from 0.9x to 1.1x. This value must be below 1. At 1 it will cause the 1.152 + * sampler to stall on the downswing, and above 1 it will cause it to sample 1.153 + * backwards. 1.154 + */ 1.155 +static const ALfloat MODULATION_DEPTH_COEFF = 0.1f; 1.156 + 1.157 +/* A filter is used to avoid the terrible distortion caused by changing 1.158 + * modulation time and/or depth. To be consistent across different sample 1.159 + * rates, the coefficient must be raised to a constant divided by the sample 1.160 + * rate: coeff^(constant / rate). 1.161 + */ 1.162 +static const ALfloat MODULATION_FILTER_COEFF = 0.048f; 1.163 +static const ALfloat MODULATION_FILTER_CONST = 100000.0f; 1.164 + 1.165 +// When diffusion is above 0, an all-pass filter is used to take the edge off 1.166 +// the echo effect. It uses the following line length (in seconds). 1.167 +static const ALfloat ECHO_ALLPASS_LENGTH = 0.0133f; 1.168 + 1.169 +// Input into the late reverb is decorrelated between four channels. Their 1.170 +// timings are dependent on a fraction and multiplier. See the 1.171 +// UpdateDecorrelator() routine for the calculations involved. 1.172 +static const ALfloat DECO_FRACTION = 0.15f; 1.173 +static const ALfloat DECO_MULTIPLIER = 2.0f; 1.174 + 1.175 +// All delay line lengths are specified in seconds. 1.176 + 1.177 +// The lengths of the early delay lines. 1.178 +static const ALfloat EARLY_LINE_LENGTH[4] = 1.179 +{ 1.180 + 0.0015f, 0.0045f, 0.0135f, 0.0405f 1.181 +}; 1.182 + 1.183 +// The lengths of the late all-pass delay lines. 1.184 +static const ALfloat ALLPASS_LINE_LENGTH[4] = 1.185 +{ 1.186 + 0.0151f, 0.0167f, 0.0183f, 0.0200f, 1.187 +}; 1.188 + 1.189 +// The lengths of the late cyclical delay lines. 1.190 +static const ALfloat LATE_LINE_LENGTH[4] = 1.191 +{ 1.192 + 0.0211f, 0.0311f, 0.0461f, 0.0680f 1.193 +}; 1.194 + 1.195 +// The late cyclical delay lines have a variable length dependent on the 1.196 +// effect's density parameter (inverted for some reason) and this multiplier. 1.197 +static const ALfloat LATE_LINE_MULTIPLIER = 4.0f; 1.198 + 1.199 +// Calculate the length of a delay line and store its mask and offset. 1.200 +static ALuint CalcLineLength(ALfloat length, ALintptrEXT offset, ALuint frequency, DelayLine *Delay) 1.201 +{ 1.202 + ALuint samples; 1.203 + 1.204 + // All line lengths are powers of 2, calculated from their lengths, with 1.205 + // an additional sample in case of rounding errors. 1.206 + samples = NextPowerOf2((ALuint)(length * frequency) + 1); 1.207 + // All lines share a single sample buffer. 1.208 + Delay->Mask = samples - 1; 1.209 + Delay->Line = (ALfloat*)offset; 1.210 + // Return the sample count for accumulation. 1.211 + return samples; 1.212 +} 1.213 + 1.214 +// Given the allocated sample buffer, this function updates each delay line 1.215 +// offset. 1.216 +static __inline ALvoid RealizeLineOffset(ALfloat * sampleBuffer, DelayLine *Delay) 1.217 +{ 1.218 + Delay->Line = &sampleBuffer[(ALintptrEXT)Delay->Line]; 1.219 +} 1.220 + 1.221 +/* Calculates the delay line metrics and allocates the shared sample buffer 1.222 + * for all lines given a flag indicating whether or not to allocate the EAX- 1.223 + * related delays (eaxFlag) and the sample rate (frequency). If an 1.224 + * allocation failure occurs, it returns AL_FALSE. 1.225 + */ 1.226 +static ALboolean AllocLines(ALboolean eaxFlag, ALuint frequency, ALverbState *State) 1.227 +{ 1.228 + ALuint totalSamples, index; 1.229 + ALfloat length; 1.230 + ALfloat *newBuffer = NULL; 1.231 + 1.232 + // All delay line lengths are calculated to accomodate the full range of 1.233 + // lengths given their respective paramters. 1.234 + totalSamples = 0; 1.235 + if(eaxFlag) 1.236 + { 1.237 + /* The modulator's line length is calculated from the maximum 1.238 + * modulation time and depth coefficient, and halfed for the low-to- 1.239 + * high frequency swing. An additional sample is added to keep it 1.240 + * stable when there is no modulation. 1.241 + */ 1.242 + length = (AL_EAXREVERB_MAX_MODULATION_TIME * MODULATION_DEPTH_COEFF / 1.243 + 2.0f) + (1.0f / frequency); 1.244 + totalSamples += CalcLineLength(length, totalSamples, frequency, 1.245 + &State->Mod.Delay); 1.246 + } 1.247 + 1.248 + // The initial delay is the sum of the reflections and late reverb 1.249 + // delays. 1.250 + if(eaxFlag) 1.251 + length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + 1.252 + AL_EAXREVERB_MAX_LATE_REVERB_DELAY; 1.253 + else 1.254 + length = AL_REVERB_MAX_REFLECTIONS_DELAY + 1.255 + AL_REVERB_MAX_LATE_REVERB_DELAY; 1.256 + totalSamples += CalcLineLength(length, totalSamples, frequency, 1.257 + &State->Delay); 1.258 + 1.259 + // The early reflection lines. 1.260 + for(index = 0;index < 4;index++) 1.261 + totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples, 1.262 + frequency, &State->Early.Delay[index]); 1.263 + 1.264 + // The decorrelator line is calculated from the lowest reverb density (a 1.265 + // parameter value of 1). 1.266 + length = (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) * 1.267 + LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER); 1.268 + totalSamples += CalcLineLength(length, totalSamples, frequency, 1.269 + &State->Decorrelator); 1.270 + 1.271 + // The late all-pass lines. 1.272 + for(index = 0;index < 4;index++) 1.273 + totalSamples += CalcLineLength(ALLPASS_LINE_LENGTH[index], totalSamples, 1.274 + frequency, &State->Late.ApDelay[index]); 1.275 + 1.276 + // The late delay lines are calculated from the lowest reverb density. 1.277 + for(index = 0;index < 4;index++) 1.278 + { 1.279 + length = LATE_LINE_LENGTH[index] * (1.0f + LATE_LINE_MULTIPLIER); 1.280 + totalSamples += CalcLineLength(length, totalSamples, frequency, 1.281 + &State->Late.Delay[index]); 1.282 + } 1.283 + 1.284 + if(eaxFlag) 1.285 + { 1.286 + // The echo all-pass and delay lines. 1.287 + totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples, 1.288 + frequency, &State->Echo.ApDelay); 1.289 + totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples, 1.290 + frequency, &State->Echo.Delay); 1.291 + } 1.292 + 1.293 + if(totalSamples != State->TotalSamples) 1.294 + { 1.295 + newBuffer = realloc(State->SampleBuffer, sizeof(ALfloat) * totalSamples); 1.296 + if(newBuffer == NULL) 1.297 + return AL_FALSE; 1.298 + State->SampleBuffer = newBuffer; 1.299 + State->TotalSamples = totalSamples; 1.300 + } 1.301 + 1.302 + // Update all delays to reflect the new sample buffer. 1.303 + RealizeLineOffset(State->SampleBuffer, &State->Delay); 1.304 + RealizeLineOffset(State->SampleBuffer, &State->Decorrelator); 1.305 + for(index = 0;index < 4;index++) 1.306 + { 1.307 + RealizeLineOffset(State->SampleBuffer, &State->Early.Delay[index]); 1.308 + RealizeLineOffset(State->SampleBuffer, &State->Late.ApDelay[index]); 1.309 + RealizeLineOffset(State->SampleBuffer, &State->Late.Delay[index]); 1.310 + } 1.311 + if(eaxFlag) 1.312 + { 1.313 + RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay); 1.314 + RealizeLineOffset(State->SampleBuffer, &State->Echo.ApDelay); 1.315 + RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay); 1.316 + } 1.317 + 1.318 + // Clear the sample buffer. 1.319 + for(index = 0;index < State->TotalSamples;index++) 1.320 + State->SampleBuffer[index] = 0.0f; 1.321 + 1.322 + return AL_TRUE; 1.323 +} 1.324 + 1.325 +// Calculate a decay coefficient given the length of each cycle and the time 1.326 +// until the decay reaches -60 dB. 1.327 +static __inline ALfloat CalcDecayCoeff(ALfloat length, ALfloat decayTime) 1.328 +{ 1.329 + return aluPow(0.001f/*-60 dB*/, length/decayTime); 1.330 +} 1.331 + 1.332 +// Calculate a decay length from a coefficient and the time until the decay 1.333 +// reaches -60 dB. 1.334 +static __inline ALfloat CalcDecayLength(ALfloat coeff, ALfloat decayTime) 1.335 +{ 1.336 + return log10(coeff) * decayTime / -3.0f/*log10(0.001)*/; 1.337 +} 1.338 + 1.339 +// Calculate the high frequency parameter for the I3DL2 coefficient 1.340 +// calculation. 1.341 +static __inline ALfloat CalcI3DL2HFreq(ALfloat hfRef, ALuint frequency) 1.342 +{ 1.343 + return cos(2.0f * M_PI * hfRef / frequency); 1.344 +} 1.345 + 1.346 +// Calculate an attenuation to be applied to the input of any echo models to 1.347 +// compensate for modal density and decay time. 1.348 +static __inline ALfloat CalcDensityGain(ALfloat a) 1.349 +{ 1.350 + /* The energy of a signal can be obtained by finding the area under the 1.351 + * squared signal. This takes the form of Sum(x_n^2), where x is the 1.352 + * amplitude for the sample n. 1.353 + * 1.354 + * Decaying feedback matches exponential decay of the form Sum(a^n), 1.355 + * where a is the attenuation coefficient, and n is the sample. The area 1.356 + * under this decay curve can be calculated as: 1 / (1 - a). 1.357 + * 1.358 + * Modifying the above equation to find the squared area under the curve 1.359 + * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be 1.360 + * calculated by inverting the square root of this approximation, 1.361 + * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2). 1.362 + */ 1.363 + return aluSqrt(1.0f - (a * a)); 1.364 +} 1.365 + 1.366 +// Calculate the mixing matrix coefficients given a diffusion factor. 1.367 +static __inline ALvoid CalcMatrixCoeffs(ALfloat diffusion, ALfloat *x, ALfloat *y) 1.368 +{ 1.369 + ALfloat n, t; 1.370 + 1.371 + // The matrix is of order 4, so n is sqrt (4 - 1). 1.372 + n = aluSqrt(3.0f); 1.373 + t = diffusion * atan(n); 1.374 + 1.375 + // Calculate the first mixing matrix coefficient. 1.376 + *x = cos(t); 1.377 + // Calculate the second mixing matrix coefficient. 1.378 + *y = sin(t) / n; 1.379 +} 1.380 + 1.381 +// Calculate the limited HF ratio for use with the late reverb low-pass 1.382 +// filters. 1.383 +static ALfloat CalcLimitedHfRatio(ALfloat hfRatio, ALfloat airAbsorptionGainHF, ALfloat decayTime) 1.384 +{ 1.385 + ALfloat limitRatio; 1.386 + 1.387 + /* Find the attenuation due to air absorption in dB (converting delay 1.388 + * time to meters using the speed of sound). Then reversing the decay 1.389 + * equation, solve for HF ratio. The delay length is cancelled out of 1.390 + * the equation, so it can be calculated once for all lines. 1.391 + */ 1.392 + limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * 1.393 + SPEEDOFSOUNDMETRESPERSEC); 1.394 + /* Using the limit calculated above, apply the upper bound to the HF 1.395 + * ratio. Also need to limit the result to a minimum of 0.1, just like the 1.396 + * HF ratio parameter. */ 1.397 + return clampf(limitRatio, 0.1f, hfRatio); 1.398 +} 1.399 + 1.400 +// Calculate the coefficient for a HF (and eventually LF) decay damping 1.401 +// filter. 1.402 +static __inline ALfloat CalcDampingCoeff(ALfloat hfRatio, ALfloat length, ALfloat decayTime, ALfloat decayCoeff, ALfloat cw) 1.403 +{ 1.404 + ALfloat coeff, g; 1.405 + 1.406 + // Eventually this should boost the high frequencies when the ratio 1.407 + // exceeds 1. 1.408 + coeff = 0.0f; 1.409 + if (hfRatio < 1.0f) 1.410 + { 1.411 + // Calculate the low-pass coefficient by dividing the HF decay 1.412 + // coefficient by the full decay coefficient. 1.413 + g = CalcDecayCoeff(length, decayTime * hfRatio) / decayCoeff; 1.414 + 1.415 + // Damping is done with a 1-pole filter, so g needs to be squared. 1.416 + g *= g; 1.417 + coeff = lpCoeffCalc(g, cw); 1.418 + 1.419 + // Very low decay times will produce minimal output, so apply an 1.420 + // upper bound to the coefficient. 1.421 + coeff = minf(coeff, 0.98f); 1.422 + } 1.423 + return coeff; 1.424 +} 1.425 + 1.426 +// Update the EAX modulation index, range, and depth. Keep in mind that this 1.427 +// kind of vibrato is additive and not multiplicative as one may expect. The 1.428 +// downswing will sound stronger than the upswing. 1.429 +static ALvoid UpdateModulator(ALfloat modTime, ALfloat modDepth, ALuint frequency, ALverbState *State) 1.430 +{ 1.431 + ALfloat length; 1.432 + 1.433 + /* Modulation is calculated in two parts. 1.434 + * 1.435 + * The modulation time effects the sinus applied to the change in 1.436 + * frequency. An index out of the current time range (both in samples) 1.437 + * is incremented each sample. The range is bound to a reasonable 1.438 + * minimum (1 sample) and when the timing changes, the index is rescaled 1.439 + * to the new range (to keep the sinus consistent). 1.440 + */ 1.441 + length = modTime * frequency; 1.442 + if (length >= 1.0f) { 1.443 + State->Mod.Index = (ALuint)(State->Mod.Index * length / 1.444 + State->Mod.Range); 1.445 + State->Mod.Range = (ALuint)length; 1.446 + } else { 1.447 + State->Mod.Index = 0; 1.448 + State->Mod.Range = 1; 1.449 + } 1.450 + 1.451 + /* The modulation depth effects the amount of frequency change over the 1.452 + * range of the sinus. It needs to be scaled by the modulation time so 1.453 + * that a given depth produces a consistent change in frequency over all 1.454 + * ranges of time. Since the depth is applied to a sinus value, it needs 1.455 + * to be halfed once for the sinus range and again for the sinus swing 1.456 + * in time (half of it is spent decreasing the frequency, half is spent 1.457 + * increasing it). 1.458 + */ 1.459 + State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f / 1.460 + 2.0f * frequency; 1.461 +} 1.462 + 1.463 +// Update the offsets for the initial effect delay line. 1.464 +static ALvoid UpdateDelayLine(ALfloat earlyDelay, ALfloat lateDelay, ALuint frequency, ALverbState *State) 1.465 +{ 1.466 + // Calculate the initial delay taps. 1.467 + State->DelayTap[0] = (ALuint)(earlyDelay * frequency); 1.468 + State->DelayTap[1] = (ALuint)((earlyDelay + lateDelay) * frequency); 1.469 +} 1.470 + 1.471 +// Update the early reflections gain and line coefficients. 1.472 +static ALvoid UpdateEarlyLines(ALfloat reverbGain, ALfloat earlyGain, ALfloat lateDelay, ALverbState *State) 1.473 +{ 1.474 + ALuint index; 1.475 + 1.476 + // Calculate the early reflections gain (from the master effect gain, and 1.477 + // reflections gain parameters) with a constant attenuation of 0.5. 1.478 + State->Early.Gain = 0.5f * reverbGain * earlyGain; 1.479 + 1.480 + // Calculate the gain (coefficient) for each early delay line using the 1.481 + // late delay time. This expands the early reflections to the start of 1.482 + // the late reverb. 1.483 + for(index = 0;index < 4;index++) 1.484 + State->Early.Coeff[index] = CalcDecayCoeff(EARLY_LINE_LENGTH[index], 1.485 + lateDelay); 1.486 +} 1.487 + 1.488 +// Update the offsets for the decorrelator line. 1.489 +static ALvoid UpdateDecorrelator(ALfloat density, ALuint frequency, ALverbState *State) 1.490 +{ 1.491 + ALuint index; 1.492 + ALfloat length; 1.493 + 1.494 + /* The late reverb inputs are decorrelated to smooth the reverb tail and 1.495 + * reduce harsh echos. The first tap occurs immediately, while the 1.496 + * remaining taps are delayed by multiples of a fraction of the smallest 1.497 + * cyclical delay time. 1.498 + * 1.499 + * offset[index] = (FRACTION (MULTIPLIER^index)) smallest_delay 1.500 + */ 1.501 + for(index = 0;index < 3;index++) 1.502 + { 1.503 + length = (DECO_FRACTION * aluPow(DECO_MULTIPLIER, (ALfloat)index)) * 1.504 + LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER)); 1.505 + State->DecoTap[index] = (ALuint)(length * frequency); 1.506 + } 1.507 +} 1.508 + 1.509 +// Update the late reverb gains, line lengths, and line coefficients. 1.510 +static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix, ALfloat density, ALfloat decayTime, ALfloat diffusion, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALverbState *State) 1.511 +{ 1.512 + ALfloat length; 1.513 + ALuint index; 1.514 + 1.515 + /* Calculate the late reverb gain (from the master effect gain, and late 1.516 + * reverb gain parameters). Since the output is tapped prior to the 1.517 + * application of the next delay line coefficients, this gain needs to be 1.518 + * attenuated by the 'x' mixing matrix coefficient as well. 1.519 + */ 1.520 + State->Late.Gain = reverbGain * lateGain * xMix; 1.521 + 1.522 + /* To compensate for changes in modal density and decay time of the late 1.523 + * reverb signal, the input is attenuated based on the maximal energy of 1.524 + * the outgoing signal. This approximation is used to keep the apparent 1.525 + * energy of the signal equal for all ranges of density and decay time. 1.526 + * 1.527 + * The average length of the cyclcical delay lines is used to calculate 1.528 + * the attenuation coefficient. 1.529 + */ 1.530 + length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] + 1.531 + LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]) / 4.0f; 1.532 + length *= 1.0f + (density * LATE_LINE_MULTIPLIER); 1.533 + State->Late.DensityGain = CalcDensityGain(CalcDecayCoeff(length, 1.534 + decayTime)); 1.535 + 1.536 + // Calculate the all-pass feed-back and feed-forward coefficient. 1.537 + State->Late.ApFeedCoeff = 0.5f * aluPow(diffusion, 2.0f); 1.538 + 1.539 + for(index = 0;index < 4;index++) 1.540 + { 1.541 + // Calculate the gain (coefficient) for each all-pass line. 1.542 + State->Late.ApCoeff[index] = CalcDecayCoeff(ALLPASS_LINE_LENGTH[index], 1.543 + decayTime); 1.544 + 1.545 + // Calculate the length (in seconds) of each cyclical delay line. 1.546 + length = LATE_LINE_LENGTH[index] * (1.0f + (density * 1.547 + LATE_LINE_MULTIPLIER)); 1.548 + 1.549 + // Calculate the delay offset for each cyclical delay line. 1.550 + State->Late.Offset[index] = (ALuint)(length * frequency); 1.551 + 1.552 + // Calculate the gain (coefficient) for each cyclical line. 1.553 + State->Late.Coeff[index] = CalcDecayCoeff(length, decayTime); 1.554 + 1.555 + // Calculate the damping coefficient for each low-pass filter. 1.556 + State->Late.LpCoeff[index] = 1.557 + CalcDampingCoeff(hfRatio, length, decayTime, 1.558 + State->Late.Coeff[index], cw); 1.559 + 1.560 + // Attenuate the cyclical line coefficients by the mixing coefficient 1.561 + // (x). 1.562 + State->Late.Coeff[index] *= xMix; 1.563 + } 1.564 +} 1.565 + 1.566 +// Update the echo gain, line offset, line coefficients, and mixing 1.567 +// coefficients. 1.568 +static ALvoid UpdateEchoLine(ALfloat reverbGain, ALfloat lateGain, ALfloat echoTime, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALverbState *State) 1.569 +{ 1.570 + // Update the offset and coefficient for the echo delay line. 1.571 + State->Echo.Offset = (ALuint)(echoTime * frequency); 1.572 + 1.573 + // Calculate the decay coefficient for the echo line. 1.574 + State->Echo.Coeff = CalcDecayCoeff(echoTime, decayTime); 1.575 + 1.576 + // Calculate the energy-based attenuation coefficient for the echo delay 1.577 + // line. 1.578 + State->Echo.DensityGain = CalcDensityGain(State->Echo.Coeff); 1.579 + 1.580 + // Calculate the echo all-pass feed coefficient. 1.581 + State->Echo.ApFeedCoeff = 0.5f * aluPow(diffusion, 2.0f); 1.582 + 1.583 + // Calculate the echo all-pass attenuation coefficient. 1.584 + State->Echo.ApCoeff = CalcDecayCoeff(ECHO_ALLPASS_LENGTH, decayTime); 1.585 + 1.586 + // Calculate the damping coefficient for each low-pass filter. 1.587 + State->Echo.LpCoeff = CalcDampingCoeff(hfRatio, echoTime, decayTime, 1.588 + State->Echo.Coeff, cw); 1.589 + 1.590 + /* Calculate the echo mixing coefficients. The first is applied to the 1.591 + * echo itself. The second is used to attenuate the late reverb when 1.592 + * echo depth is high and diffusion is low, so the echo is slightly 1.593 + * stronger than the decorrelated echos in the reverb tail. 1.594 + */ 1.595 + State->Echo.MixCoeff[0] = reverbGain * lateGain * echoDepth; 1.596 + State->Echo.MixCoeff[1] = 1.0f - (echoDepth * 0.5f * (1.0f - diffusion)); 1.597 +} 1.598 + 1.599 +// Update the early and late 3D panning gains. 1.600 +static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALverbState *State) 1.601 +{ 1.602 + ALfloat earlyPan[3] = { ReflectionsPan[0], ReflectionsPan[1], 1.603 + ReflectionsPan[2] }; 1.604 + ALfloat latePan[3] = { LateReverbPan[0], LateReverbPan[1], 1.605 + LateReverbPan[2] }; 1.606 + const ALfloat *speakerGain; 1.607 + ALfloat ambientGain; 1.608 + ALfloat dirGain; 1.609 + ALfloat length; 1.610 + ALuint index; 1.611 + ALint pos; 1.612 + 1.613 + Gain *= ReverbBoost; 1.614 + 1.615 + // Attenuate non-directional reverb according to the number of channels 1.616 + ambientGain = aluSqrt(2.0f/Device->NumChan); 1.617 + 1.618 + // Calculate the 3D-panning gains for the early reflections and late 1.619 + // reverb. 1.620 + length = earlyPan[0]*earlyPan[0] + earlyPan[1]*earlyPan[1] + earlyPan[2]*earlyPan[2]; 1.621 + if(length > 1.0f) 1.622 + { 1.623 + length = 1.0f / aluSqrt(length); 1.624 + earlyPan[0] *= length; 1.625 + earlyPan[1] *= length; 1.626 + earlyPan[2] *= length; 1.627 + } 1.628 + length = latePan[0]*latePan[0] + latePan[1]*latePan[1] + latePan[2]*latePan[2]; 1.629 + if(length > 1.0f) 1.630 + { 1.631 + length = 1.0f / aluSqrt(length); 1.632 + latePan[0] *= length; 1.633 + latePan[1] *= length; 1.634 + latePan[2] *= length; 1.635 + } 1.636 + 1.637 + /* This code applies directional reverb just like the mixer applies 1.638 + * directional sources. It diffuses the sound toward all speakers as the 1.639 + * magnitude of the panning vector drops, which is only a rough 1.640 + * approximation of the expansion of sound across the speakers from the 1.641 + * panning direction. 1.642 + */ 1.643 + pos = aluCart2LUTpos(earlyPan[2], earlyPan[0]); 1.644 + speakerGain = Device->PanningLUT[pos]; 1.645 + dirGain = aluSqrt((earlyPan[0] * earlyPan[0]) + (earlyPan[2] * earlyPan[2])); 1.646 + 1.647 + for(index = 0;index < MAXCHANNELS;index++) 1.648 + State->Early.PanGain[index] = 0.0f; 1.649 + for(index = 0;index < Device->NumChan;index++) 1.650 + { 1.651 + enum Channel chan = Device->Speaker2Chan[index]; 1.652 + State->Early.PanGain[chan] = lerp(ambientGain, speakerGain[chan], dirGain) * Gain; 1.653 + } 1.654 + 1.655 + 1.656 + pos = aluCart2LUTpos(latePan[2], latePan[0]); 1.657 + speakerGain = Device->PanningLUT[pos]; 1.658 + dirGain = aluSqrt((latePan[0] * latePan[0]) + (latePan[2] * latePan[2])); 1.659 + 1.660 + for(index = 0;index < MAXCHANNELS;index++) 1.661 + State->Late.PanGain[index] = 0.0f; 1.662 + for(index = 0;index < Device->NumChan;index++) 1.663 + { 1.664 + enum Channel chan = Device->Speaker2Chan[index]; 1.665 + State->Late.PanGain[chan] = lerp(ambientGain, speakerGain[chan], dirGain) * Gain; 1.666 + } 1.667 +} 1.668 + 1.669 +// Basic delay line input/output routines. 1.670 +static __inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset) 1.671 +{ 1.672 + return Delay->Line[offset&Delay->Mask]; 1.673 +} 1.674 + 1.675 +static __inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in) 1.676 +{ 1.677 + Delay->Line[offset&Delay->Mask] = in; 1.678 +} 1.679 + 1.680 +// Attenuated delay line output routine. 1.681 +static __inline ALfloat AttenuatedDelayLineOut(DelayLine *Delay, ALuint offset, ALfloat coeff) 1.682 +{ 1.683 + return coeff * Delay->Line[offset&Delay->Mask]; 1.684 +} 1.685 + 1.686 +// Basic attenuated all-pass input/output routine. 1.687 +static __inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff) 1.688 +{ 1.689 + ALfloat out, feed; 1.690 + 1.691 + out = DelayLineOut(Delay, outOffset); 1.692 + feed = feedCoeff * in; 1.693 + DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in); 1.694 + 1.695 + // The time-based attenuation is only applied to the delay output to 1.696 + // keep it from affecting the feed-back path (which is already controlled 1.697 + // by the all-pass feed coefficient). 1.698 + return (coeff * out) - feed; 1.699 +} 1.700 + 1.701 +// Given an input sample, this function produces modulation for the late 1.702 +// reverb. 1.703 +static __inline ALfloat EAXModulation(ALverbState *State, ALfloat in) 1.704 +{ 1.705 + ALfloat sinus, frac; 1.706 + ALuint offset; 1.707 + ALfloat out0, out1; 1.708 + 1.709 + // Calculate the sinus rythm (dependent on modulation time and the 1.710 + // sampling rate). The center of the sinus is moved to reduce the delay 1.711 + // of the effect when the time or depth are low. 1.712 + sinus = 1.0f - cos(2.0f * M_PI * State->Mod.Index / State->Mod.Range); 1.713 + 1.714 + // The depth determines the range over which to read the input samples 1.715 + // from, so it must be filtered to reduce the distortion caused by even 1.716 + // small parameter changes. 1.717 + State->Mod.Filter = lerp(State->Mod.Filter, State->Mod.Depth, 1.718 + State->Mod.Coeff); 1.719 + 1.720 + // Calculate the read offset and fraction between it and the next sample. 1.721 + frac = (1.0f + (State->Mod.Filter * sinus)); 1.722 + offset = (ALuint)frac; 1.723 + frac -= offset; 1.724 + 1.725 + // Get the two samples crossed by the offset, and feed the delay line 1.726 + // with the next input sample. 1.727 + out0 = DelayLineOut(&State->Mod.Delay, State->Offset - offset); 1.728 + out1 = DelayLineOut(&State->Mod.Delay, State->Offset - offset - 1); 1.729 + DelayLineIn(&State->Mod.Delay, State->Offset, in); 1.730 + 1.731 + // Step the modulation index forward, keeping it bound to its range. 1.732 + State->Mod.Index = (State->Mod.Index + 1) % State->Mod.Range; 1.733 + 1.734 + // The output is obtained by linearly interpolating the two samples that 1.735 + // were acquired above. 1.736 + return lerp(out0, out1, frac); 1.737 +} 1.738 + 1.739 +// Delay line output routine for early reflections. 1.740 +static __inline ALfloat EarlyDelayLineOut(ALverbState *State, ALuint index) 1.741 +{ 1.742 + return AttenuatedDelayLineOut(&State->Early.Delay[index], 1.743 + State->Offset - State->Early.Offset[index], 1.744 + State->Early.Coeff[index]); 1.745 +} 1.746 + 1.747 +// Given an input sample, this function produces four-channel output for the 1.748 +// early reflections. 1.749 +static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *out) 1.750 +{ 1.751 + ALfloat d[4], v, f[4]; 1.752 + 1.753 + // Obtain the decayed results of each early delay line. 1.754 + d[0] = EarlyDelayLineOut(State, 0); 1.755 + d[1] = EarlyDelayLineOut(State, 1); 1.756 + d[2] = EarlyDelayLineOut(State, 2); 1.757 + d[3] = EarlyDelayLineOut(State, 3); 1.758 + 1.759 + /* The following uses a lossless scattering junction from waveguide 1.760 + * theory. It actually amounts to a householder mixing matrix, which 1.761 + * will produce a maximally diffuse response, and means this can probably 1.762 + * be considered a simple feed-back delay network (FDN). 1.763 + * N 1.764 + * --- 1.765 + * \ 1.766 + * v = 2/N / d_i 1.767 + * --- 1.768 + * i=1 1.769 + */ 1.770 + v = (d[0] + d[1] + d[2] + d[3]) * 0.5f; 1.771 + // The junction is loaded with the input here. 1.772 + v += in; 1.773 + 1.774 + // Calculate the feed values for the delay lines. 1.775 + f[0] = v - d[0]; 1.776 + f[1] = v - d[1]; 1.777 + f[2] = v - d[2]; 1.778 + f[3] = v - d[3]; 1.779 + 1.780 + // Re-feed the delay lines. 1.781 + DelayLineIn(&State->Early.Delay[0], State->Offset, f[0]); 1.782 + DelayLineIn(&State->Early.Delay[1], State->Offset, f[1]); 1.783 + DelayLineIn(&State->Early.Delay[2], State->Offset, f[2]); 1.784 + DelayLineIn(&State->Early.Delay[3], State->Offset, f[3]); 1.785 + 1.786 + // Output the results of the junction for all four channels. 1.787 + out[0] = State->Early.Gain * f[0]; 1.788 + out[1] = State->Early.Gain * f[1]; 1.789 + out[2] = State->Early.Gain * f[2]; 1.790 + out[3] = State->Early.Gain * f[3]; 1.791 +} 1.792 + 1.793 +// All-pass input/output routine for late reverb. 1.794 +static __inline ALfloat LateAllPassInOut(ALverbState *State, ALuint index, ALfloat in) 1.795 +{ 1.796 + return AllpassInOut(&State->Late.ApDelay[index], 1.797 + State->Offset - State->Late.ApOffset[index], 1.798 + State->Offset, in, State->Late.ApFeedCoeff, 1.799 + State->Late.ApCoeff[index]); 1.800 +} 1.801 + 1.802 +// Delay line output routine for late reverb. 1.803 +static __inline ALfloat LateDelayLineOut(ALverbState *State, ALuint index) 1.804 +{ 1.805 + return AttenuatedDelayLineOut(&State->Late.Delay[index], 1.806 + State->Offset - State->Late.Offset[index], 1.807 + State->Late.Coeff[index]); 1.808 +} 1.809 + 1.810 +// Low-pass filter input/output routine for late reverb. 1.811 +static __inline ALfloat LateLowPassInOut(ALverbState *State, ALuint index, ALfloat in) 1.812 +{ 1.813 + in = lerp(in, State->Late.LpSample[index], State->Late.LpCoeff[index]); 1.814 + State->Late.LpSample[index] = in; 1.815 + return in; 1.816 +} 1.817 + 1.818 +// Given four decorrelated input samples, this function produces four-channel 1.819 +// output for the late reverb. 1.820 +static __inline ALvoid LateReverb(ALverbState *State, ALfloat *in, ALfloat *out) 1.821 +{ 1.822 + ALfloat d[4], f[4]; 1.823 + 1.824 + // Obtain the decayed results of the cyclical delay lines, and add the 1.825 + // corresponding input channels. Then pass the results through the 1.826 + // low-pass filters. 1.827 + 1.828 + // This is where the feed-back cycles from line 0 to 1 to 3 to 2 and back 1.829 + // to 0. 1.830 + d[0] = LateLowPassInOut(State, 2, in[2] + LateDelayLineOut(State, 2)); 1.831 + d[1] = LateLowPassInOut(State, 0, in[0] + LateDelayLineOut(State, 0)); 1.832 + d[2] = LateLowPassInOut(State, 3, in[3] + LateDelayLineOut(State, 3)); 1.833 + d[3] = LateLowPassInOut(State, 1, in[1] + LateDelayLineOut(State, 1)); 1.834 + 1.835 + // To help increase diffusion, run each line through an all-pass filter. 1.836 + // When there is no diffusion, the shortest all-pass filter will feed the 1.837 + // shortest delay line. 1.838 + d[0] = LateAllPassInOut(State, 0, d[0]); 1.839 + d[1] = LateAllPassInOut(State, 1, d[1]); 1.840 + d[2] = LateAllPassInOut(State, 2, d[2]); 1.841 + d[3] = LateAllPassInOut(State, 3, d[3]); 1.842 + 1.843 + /* Late reverb is done with a modified feed-back delay network (FDN) 1.844 + * topology. Four input lines are each fed through their own all-pass 1.845 + * filter and then into the mixing matrix. The four outputs of the 1.846 + * mixing matrix are then cycled back to the inputs. Each output feeds 1.847 + * a different input to form a circlular feed cycle. 1.848 + * 1.849 + * The mixing matrix used is a 4D skew-symmetric rotation matrix derived 1.850 + * using a single unitary rotational parameter: 1.851 + * 1.852 + * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2 1.853 + * [ -a, d, c, -b ] 1.854 + * [ -b, -c, d, a ] 1.855 + * [ -c, b, -a, d ] 1.856 + * 1.857 + * The rotation is constructed from the effect's diffusion parameter, 1.858 + * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y 1.859 + * with differing signs, and d is the coefficient x. The matrix is thus: 1.860 + * 1.861 + * [ x, y, -y, y ] n = sqrt(matrix_order - 1) 1.862 + * [ -y, x, y, y ] t = diffusion_parameter * atan(n) 1.863 + * [ y, -y, x, y ] x = cos(t) 1.864 + * [ -y, -y, -y, x ] y = sin(t) / n 1.865 + * 1.866 + * To reduce the number of multiplies, the x coefficient is applied with 1.867 + * the cyclical delay line coefficients. Thus only the y coefficient is 1.868 + * applied when mixing, and is modified to be: y / x. 1.869 + */ 1.870 + f[0] = d[0] + (State->Late.MixCoeff * ( d[1] + -d[2] + d[3])); 1.871 + f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3])); 1.872 + f[2] = d[2] + (State->Late.MixCoeff * ( d[0] + -d[1] + d[3])); 1.873 + f[3] = d[3] + (State->Late.MixCoeff * (-d[0] + -d[1] + -d[2] )); 1.874 + 1.875 + // Output the results of the matrix for all four channels, attenuated by 1.876 + // the late reverb gain (which is attenuated by the 'x' mix coefficient). 1.877 + out[0] = State->Late.Gain * f[0]; 1.878 + out[1] = State->Late.Gain * f[1]; 1.879 + out[2] = State->Late.Gain * f[2]; 1.880 + out[3] = State->Late.Gain * f[3]; 1.881 + 1.882 + // Re-feed the cyclical delay lines. 1.883 + DelayLineIn(&State->Late.Delay[0], State->Offset, f[0]); 1.884 + DelayLineIn(&State->Late.Delay[1], State->Offset, f[1]); 1.885 + DelayLineIn(&State->Late.Delay[2], State->Offset, f[2]); 1.886 + DelayLineIn(&State->Late.Delay[3], State->Offset, f[3]); 1.887 +} 1.888 + 1.889 +// Given an input sample, this function mixes echo into the four-channel late 1.890 +// reverb. 1.891 +static __inline ALvoid EAXEcho(ALverbState *State, ALfloat in, ALfloat *late) 1.892 +{ 1.893 + ALfloat out, feed; 1.894 + 1.895 + // Get the latest attenuated echo sample for output. 1.896 + feed = AttenuatedDelayLineOut(&State->Echo.Delay, 1.897 + State->Offset - State->Echo.Offset, 1.898 + State->Echo.Coeff); 1.899 + 1.900 + // Mix the output into the late reverb channels. 1.901 + out = State->Echo.MixCoeff[0] * feed; 1.902 + late[0] = (State->Echo.MixCoeff[1] * late[0]) + out; 1.903 + late[1] = (State->Echo.MixCoeff[1] * late[1]) + out; 1.904 + late[2] = (State->Echo.MixCoeff[1] * late[2]) + out; 1.905 + late[3] = (State->Echo.MixCoeff[1] * late[3]) + out; 1.906 + 1.907 + // Mix the energy-attenuated input with the output and pass it through 1.908 + // the echo low-pass filter. 1.909 + feed += State->Echo.DensityGain * in; 1.910 + feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff); 1.911 + State->Echo.LpSample = feed; 1.912 + 1.913 + // Then the echo all-pass filter. 1.914 + feed = AllpassInOut(&State->Echo.ApDelay, 1.915 + State->Offset - State->Echo.ApOffset, 1.916 + State->Offset, feed, State->Echo.ApFeedCoeff, 1.917 + State->Echo.ApCoeff); 1.918 + 1.919 + // Feed the delay with the mixed and filtered sample. 1.920 + DelayLineIn(&State->Echo.Delay, State->Offset, feed); 1.921 +} 1.922 + 1.923 +// Perform the non-EAX reverb pass on a given input sample, resulting in 1.924 +// four-channel output. 1.925 +static __inline ALvoid VerbPass(ALverbState *State, ALfloat in, ALfloat *early, ALfloat *late) 1.926 +{ 1.927 + ALfloat feed, taps[4]; 1.928 + 1.929 + // Low-pass filter the incoming sample. 1.930 + in = lpFilter2P(&State->LpFilter, 0, in); 1.931 + 1.932 + // Feed the initial delay line. 1.933 + DelayLineIn(&State->Delay, State->Offset, in); 1.934 + 1.935 + // Calculate the early reflection from the first delay tap. 1.936 + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]); 1.937 + EarlyReflection(State, in, early); 1.938 + 1.939 + // Feed the decorrelator from the energy-attenuated output of the second 1.940 + // delay tap. 1.941 + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]); 1.942 + feed = in * State->Late.DensityGain; 1.943 + DelayLineIn(&State->Decorrelator, State->Offset, feed); 1.944 + 1.945 + // Calculate the late reverb from the decorrelator taps. 1.946 + taps[0] = feed; 1.947 + taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]); 1.948 + taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]); 1.949 + taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]); 1.950 + LateReverb(State, taps, late); 1.951 + 1.952 + // Step all delays forward one sample. 1.953 + State->Offset++; 1.954 +} 1.955 + 1.956 +// Perform the EAX reverb pass on a given input sample, resulting in four- 1.957 +// channel output. 1.958 +static __inline ALvoid EAXVerbPass(ALverbState *State, ALfloat in, ALfloat *early, ALfloat *late) 1.959 +{ 1.960 + ALfloat feed, taps[4]; 1.961 + 1.962 + // Low-pass filter the incoming sample. 1.963 + in = lpFilter2P(&State->LpFilter, 0, in); 1.964 + 1.965 + // Perform any modulation on the input. 1.966 + in = EAXModulation(State, in); 1.967 + 1.968 + // Feed the initial delay line. 1.969 + DelayLineIn(&State->Delay, State->Offset, in); 1.970 + 1.971 + // Calculate the early reflection from the first delay tap. 1.972 + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]); 1.973 + EarlyReflection(State, in, early); 1.974 + 1.975 + // Feed the decorrelator from the energy-attenuated output of the second 1.976 + // delay tap. 1.977 + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]); 1.978 + feed = in * State->Late.DensityGain; 1.979 + DelayLineIn(&State->Decorrelator, State->Offset, feed); 1.980 + 1.981 + // Calculate the late reverb from the decorrelator taps. 1.982 + taps[0] = feed; 1.983 + taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]); 1.984 + taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]); 1.985 + taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]); 1.986 + LateReverb(State, taps, late); 1.987 + 1.988 + // Calculate and mix in any echo. 1.989 + EAXEcho(State, in, late); 1.990 + 1.991 + // Step all delays forward one sample. 1.992 + State->Offset++; 1.993 +} 1.994 + 1.995 +// This destroys the reverb state. It should be called only when the effect 1.996 +// slot has a different (or no) effect loaded over the reverb effect. 1.997 +static ALvoid VerbDestroy(ALeffectState *effect) 1.998 +{ 1.999 + ALverbState *State = (ALverbState*)effect; 1.1000 + if(State) 1.1001 + { 1.1002 + free(State->SampleBuffer); 1.1003 + State->SampleBuffer = NULL; 1.1004 + free(State); 1.1005 + } 1.1006 +} 1.1007 + 1.1008 +// This updates the device-dependant reverb state. This is called on 1.1009 +// initialization and any time the device parameters (eg. playback frequency, 1.1010 +// or format) have been changed. 1.1011 +static ALboolean VerbDeviceUpdate(ALeffectState *effect, ALCdevice *Device) 1.1012 +{ 1.1013 + ALverbState *State = (ALverbState*)effect; 1.1014 + ALuint frequency = Device->Frequency; 1.1015 + ALuint index; 1.1016 + 1.1017 + // Allocate the delay lines. 1.1018 + if(!AllocLines(AL_FALSE, frequency, State)) 1.1019 + return AL_FALSE; 1.1020 + 1.1021 + // The early reflection and late all-pass filter line lengths are static, 1.1022 + // so their offsets only need to be calculated once. 1.1023 + for(index = 0;index < 4;index++) 1.1024 + { 1.1025 + State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] * 1.1026 + frequency); 1.1027 + State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] * 1.1028 + frequency); 1.1029 + } 1.1030 + 1.1031 + return AL_TRUE; 1.1032 +} 1.1033 + 1.1034 +// This updates the device-dependant EAX reverb state. This is called on 1.1035 +// initialization and any time the device parameters (eg. playback frequency, 1.1036 +// format) have been changed. 1.1037 +static ALboolean EAXVerbDeviceUpdate(ALeffectState *effect, ALCdevice *Device) 1.1038 +{ 1.1039 + ALverbState *State = (ALverbState*)effect; 1.1040 + ALuint frequency = Device->Frequency, index; 1.1041 + 1.1042 + // Allocate the delay lines. 1.1043 + if(!AllocLines(AL_TRUE, frequency, State)) 1.1044 + return AL_FALSE; 1.1045 + 1.1046 + // Calculate the modulation filter coefficient. Notice that the exponent 1.1047 + // is calculated given the current sample rate. This ensures that the 1.1048 + // resulting filter response over time is consistent across all sample 1.1049 + // rates. 1.1050 + State->Mod.Coeff = aluPow(MODULATION_FILTER_COEFF, 1.1051 + MODULATION_FILTER_CONST / frequency); 1.1052 + 1.1053 + // The early reflection and late all-pass filter line lengths are static, 1.1054 + // so their offsets only need to be calculated once. 1.1055 + for(index = 0;index < 4;index++) 1.1056 + { 1.1057 + State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] * 1.1058 + frequency); 1.1059 + State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] * 1.1060 + frequency); 1.1061 + } 1.1062 + 1.1063 + // The echo all-pass filter line length is static, so its offset only 1.1064 + // needs to be calculated once. 1.1065 + State->Echo.ApOffset = (ALuint)(ECHO_ALLPASS_LENGTH * frequency); 1.1066 + 1.1067 + return AL_TRUE; 1.1068 +} 1.1069 + 1.1070 +// This updates the reverb state. This is called any time the reverb effect 1.1071 +// is loaded into a slot. 1.1072 +static ALvoid VerbUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffectslot *Slot) 1.1073 +{ 1.1074 + ALverbState *State = (ALverbState*)effect; 1.1075 + ALCdevice *Device = Context->Device; 1.1076 + ALuint frequency = Device->Frequency; 1.1077 + ALfloat cw, x, y, hfRatio, gain; 1.1078 + ALuint index; 1.1079 + 1.1080 + // Calculate the master low-pass filter (from the master effect HF gain). 1.1081 + cw = CalcI3DL2HFreq(Slot->effect.Params.Reverb.HFReference, frequency); 1.1082 + // This is done with 2 chained 1-pole filters, so no need to square g. 1.1083 + State->LpFilter.coeff = lpCoeffCalc(Slot->effect.Params.Reverb.GainHF, cw); 1.1084 + 1.1085 + // Update the initial effect delay. 1.1086 + UpdateDelayLine(Slot->effect.Params.Reverb.ReflectionsDelay, 1.1087 + Slot->effect.Params.Reverb.LateReverbDelay, 1.1088 + frequency, State); 1.1089 + 1.1090 + // Update the early lines. 1.1091 + UpdateEarlyLines(Slot->effect.Params.Reverb.Gain, 1.1092 + Slot->effect.Params.Reverb.ReflectionsGain, 1.1093 + Slot->effect.Params.Reverb.LateReverbDelay, State); 1.1094 + 1.1095 + // Update the decorrelator. 1.1096 + UpdateDecorrelator(Slot->effect.Params.Reverb.Density, frequency, State); 1.1097 + 1.1098 + // Get the mixing matrix coefficients (x and y). 1.1099 + CalcMatrixCoeffs(Slot->effect.Params.Reverb.Diffusion, &x, &y); 1.1100 + // Then divide x into y to simplify the matrix calculation. 1.1101 + State->Late.MixCoeff = y / x; 1.1102 + 1.1103 + // If the HF limit parameter is flagged, calculate an appropriate limit 1.1104 + // based on the air absorption parameter. 1.1105 + hfRatio = Slot->effect.Params.Reverb.DecayHFRatio; 1.1106 + if(Slot->effect.Params.Reverb.DecayHFLimit && 1.1107 + Slot->effect.Params.Reverb.AirAbsorptionGainHF < 1.0f) 1.1108 + hfRatio = CalcLimitedHfRatio(hfRatio, 1.1109 + Slot->effect.Params.Reverb.AirAbsorptionGainHF, 1.1110 + Slot->effect.Params.Reverb.DecayTime); 1.1111 + 1.1112 + // Update the late lines. 1.1113 + UpdateLateLines(Slot->effect.Params.Reverb.Gain, Slot->effect.Params.Reverb.LateReverbGain, 1.1114 + x, Slot->effect.Params.Reverb.Density, Slot->effect.Params.Reverb.DecayTime, 1.1115 + Slot->effect.Params.Reverb.Diffusion, hfRatio, cw, frequency, State); 1.1116 + 1.1117 + // Update channel gains 1.1118 + gain = Slot->Gain; 1.1119 + gain *= aluSqrt(2.0f/Device->NumChan); 1.1120 + gain *= ReverbBoost; 1.1121 + for(index = 0;index < MAXCHANNELS;index++) 1.1122 + State->Gain[index] = 0.0f; 1.1123 + for(index = 0;index < Device->NumChan;index++) 1.1124 + { 1.1125 + enum Channel chan = Device->Speaker2Chan[index]; 1.1126 + State->Gain[chan] = gain; 1.1127 + } 1.1128 +} 1.1129 + 1.1130 +// This updates the EAX reverb state. This is called any time the EAX reverb 1.1131 +// effect is loaded into a slot. 1.1132 +static ALvoid EAXVerbUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffectslot *Slot) 1.1133 +{ 1.1134 + ALverbState *State = (ALverbState*)effect; 1.1135 + ALuint frequency = Context->Device->Frequency; 1.1136 + ALfloat cw, x, y, hfRatio; 1.1137 + 1.1138 + // Calculate the master low-pass filter (from the master effect HF gain). 1.1139 + cw = CalcI3DL2HFreq(Slot->effect.Params.Reverb.HFReference, frequency); 1.1140 + // This is done with 2 chained 1-pole filters, so no need to square g. 1.1141 + State->LpFilter.coeff = lpCoeffCalc(Slot->effect.Params.Reverb.GainHF, cw); 1.1142 + 1.1143 + // Update the modulator line. 1.1144 + UpdateModulator(Slot->effect.Params.Reverb.ModulationTime, 1.1145 + Slot->effect.Params.Reverb.ModulationDepth, 1.1146 + frequency, State); 1.1147 + 1.1148 + // Update the initial effect delay. 1.1149 + UpdateDelayLine(Slot->effect.Params.Reverb.ReflectionsDelay, 1.1150 + Slot->effect.Params.Reverb.LateReverbDelay, 1.1151 + frequency, State); 1.1152 + 1.1153 + // Update the early lines. 1.1154 + UpdateEarlyLines(Slot->effect.Params.Reverb.Gain, 1.1155 + Slot->effect.Params.Reverb.ReflectionsGain, 1.1156 + Slot->effect.Params.Reverb.LateReverbDelay, State); 1.1157 + 1.1158 + // Update the decorrelator. 1.1159 + UpdateDecorrelator(Slot->effect.Params.Reverb.Density, frequency, State); 1.1160 + 1.1161 + // Get the mixing matrix coefficients (x and y). 1.1162 + CalcMatrixCoeffs(Slot->effect.Params.Reverb.Diffusion, &x, &y); 1.1163 + // Then divide x into y to simplify the matrix calculation. 1.1164 + State->Late.MixCoeff = y / x; 1.1165 + 1.1166 + // If the HF limit parameter is flagged, calculate an appropriate limit 1.1167 + // based on the air absorption parameter. 1.1168 + hfRatio = Slot->effect.Params.Reverb.DecayHFRatio; 1.1169 + if(Slot->effect.Params.Reverb.DecayHFLimit && 1.1170 + Slot->effect.Params.Reverb.AirAbsorptionGainHF < 1.0f) 1.1171 + hfRatio = CalcLimitedHfRatio(hfRatio, 1.1172 + Slot->effect.Params.Reverb.AirAbsorptionGainHF, 1.1173 + Slot->effect.Params.Reverb.DecayTime); 1.1174 + 1.1175 + // Update the late lines. 1.1176 + UpdateLateLines(Slot->effect.Params.Reverb.Gain, Slot->effect.Params.Reverb.LateReverbGain, 1.1177 + x, Slot->effect.Params.Reverb.Density, Slot->effect.Params.Reverb.DecayTime, 1.1178 + Slot->effect.Params.Reverb.Diffusion, hfRatio, cw, frequency, State); 1.1179 + 1.1180 + // Update the echo line. 1.1181 + UpdateEchoLine(Slot->effect.Params.Reverb.Gain, Slot->effect.Params.Reverb.LateReverbGain, 1.1182 + Slot->effect.Params.Reverb.EchoTime, Slot->effect.Params.Reverb.DecayTime, 1.1183 + Slot->effect.Params.Reverb.Diffusion, Slot->effect.Params.Reverb.EchoDepth, 1.1184 + hfRatio, cw, frequency, State); 1.1185 + 1.1186 + // Update early and late 3D panning. 1.1187 + Update3DPanning(Context->Device, Slot->effect.Params.Reverb.ReflectionsPan, 1.1188 + Slot->effect.Params.Reverb.LateReverbPan, Slot->Gain, State); 1.1189 +} 1.1190 + 1.1191 +// This processes the reverb state, given the input samples and an output 1.1192 +// buffer. 1.1193 +static ALvoid VerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) 1.1194 +{ 1.1195 + ALverbState *State = (ALverbState*)effect; 1.1196 + ALuint index; 1.1197 + ALfloat early[4], late[4], out[4]; 1.1198 + const ALfloat *panGain = State->Gain; 1.1199 + (void)Slot; 1.1200 + 1.1201 + for(index = 0;index < SamplesToDo;index++) 1.1202 + { 1.1203 + // Process reverb for this sample. 1.1204 + VerbPass(State, SamplesIn[index], early, late); 1.1205 + 1.1206 + // Mix early reflections and late reverb. 1.1207 + out[0] = (early[0] + late[0]); 1.1208 + out[1] = (early[1] + late[1]); 1.1209 + out[2] = (early[2] + late[2]); 1.1210 + out[3] = (early[3] + late[3]); 1.1211 + 1.1212 + // Output the results. 1.1213 + SamplesOut[index][FRONT_LEFT] += panGain[FRONT_LEFT] * out[0]; 1.1214 + SamplesOut[index][FRONT_RIGHT] += panGain[FRONT_RIGHT] * out[1]; 1.1215 + SamplesOut[index][FRONT_CENTER] += panGain[FRONT_CENTER] * out[3]; 1.1216 + SamplesOut[index][SIDE_LEFT] += panGain[SIDE_LEFT] * out[0]; 1.1217 + SamplesOut[index][SIDE_RIGHT] += panGain[SIDE_RIGHT] * out[1]; 1.1218 + SamplesOut[index][BACK_LEFT] += panGain[BACK_LEFT] * out[0]; 1.1219 + SamplesOut[index][BACK_RIGHT] += panGain[BACK_RIGHT] * out[1]; 1.1220 + SamplesOut[index][BACK_CENTER] += panGain[BACK_CENTER] * out[2]; 1.1221 + } 1.1222 +} 1.1223 + 1.1224 +// This processes the EAX reverb state, given the input samples and an output 1.1225 +// buffer. 1.1226 +static ALvoid EAXVerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) 1.1227 +{ 1.1228 + ALverbState *State = (ALverbState*)effect; 1.1229 + ALuint index; 1.1230 + ALfloat early[4], late[4]; 1.1231 + (void)Slot; 1.1232 + 1.1233 + for(index = 0;index < SamplesToDo;index++) 1.1234 + { 1.1235 + // Process reverb for this sample. 1.1236 + EAXVerbPass(State, SamplesIn[index], early, late); 1.1237 + 1.1238 + // Unfortunately, while the number and configuration of gains for 1.1239 + // panning adjust according to MAXCHANNELS, the output from the 1.1240 + // reverb engine is not so scalable. 1.1241 + SamplesOut[index][FRONT_LEFT] += 1.1242 + (State->Early.PanGain[FRONT_LEFT]*early[0] + 1.1243 + State->Late.PanGain[FRONT_LEFT]*late[0]); 1.1244 + SamplesOut[index][FRONT_RIGHT] += 1.1245 + (State->Early.PanGain[FRONT_RIGHT]*early[1] + 1.1246 + State->Late.PanGain[FRONT_RIGHT]*late[1]); 1.1247 + SamplesOut[index][FRONT_CENTER] += 1.1248 + (State->Early.PanGain[FRONT_CENTER]*early[3] + 1.1249 + State->Late.PanGain[FRONT_CENTER]*late[3]); 1.1250 + SamplesOut[index][SIDE_LEFT] += 1.1251 + (State->Early.PanGain[SIDE_LEFT]*early[0] + 1.1252 + State->Late.PanGain[SIDE_LEFT]*late[0]); 1.1253 + SamplesOut[index][SIDE_RIGHT] += 1.1254 + (State->Early.PanGain[SIDE_RIGHT]*early[1] + 1.1255 + State->Late.PanGain[SIDE_RIGHT]*late[1]); 1.1256 + SamplesOut[index][BACK_LEFT] += 1.1257 + (State->Early.PanGain[BACK_LEFT]*early[0] + 1.1258 + State->Late.PanGain[BACK_LEFT]*late[0]); 1.1259 + SamplesOut[index][BACK_RIGHT] += 1.1260 + (State->Early.PanGain[BACK_RIGHT]*early[1] + 1.1261 + State->Late.PanGain[BACK_RIGHT]*late[1]); 1.1262 + SamplesOut[index][BACK_CENTER] += 1.1263 + (State->Early.PanGain[BACK_CENTER]*early[2] + 1.1264 + State->Late.PanGain[BACK_CENTER]*late[2]); 1.1265 + } 1.1266 +} 1.1267 + 1.1268 +// This creates the reverb state. It should be called only when the reverb 1.1269 +// effect is loaded into a slot that doesn't already have a reverb effect. 1.1270 +ALeffectState *VerbCreate(void) 1.1271 +{ 1.1272 + ALverbState *State = NULL; 1.1273 + ALuint index; 1.1274 + 1.1275 + State = malloc(sizeof(ALverbState)); 1.1276 + if(!State) 1.1277 + return NULL; 1.1278 + 1.1279 + State->state.Destroy = VerbDestroy; 1.1280 + State->state.DeviceUpdate = VerbDeviceUpdate; 1.1281 + State->state.Update = VerbUpdate; 1.1282 + State->state.Process = VerbProcess; 1.1283 + 1.1284 + State->TotalSamples = 0; 1.1285 + State->SampleBuffer = NULL; 1.1286 + 1.1287 + State->LpFilter.coeff = 0.0f; 1.1288 + State->LpFilter.history[0] = 0.0f; 1.1289 + State->LpFilter.history[1] = 0.0f; 1.1290 + 1.1291 + State->Mod.Delay.Mask = 0; 1.1292 + State->Mod.Delay.Line = NULL; 1.1293 + State->Mod.Index = 0; 1.1294 + State->Mod.Range = 1; 1.1295 + State->Mod.Depth = 0.0f; 1.1296 + State->Mod.Coeff = 0.0f; 1.1297 + State->Mod.Filter = 0.0f; 1.1298 + 1.1299 + State->Delay.Mask = 0; 1.1300 + State->Delay.Line = NULL; 1.1301 + State->DelayTap[0] = 0; 1.1302 + State->DelayTap[1] = 0; 1.1303 + 1.1304 + State->Early.Gain = 0.0f; 1.1305 + for(index = 0;index < 4;index++) 1.1306 + { 1.1307 + State->Early.Coeff[index] = 0.0f; 1.1308 + State->Early.Delay[index].Mask = 0; 1.1309 + State->Early.Delay[index].Line = NULL; 1.1310 + State->Early.Offset[index] = 0; 1.1311 + } 1.1312 + 1.1313 + State->Decorrelator.Mask = 0; 1.1314 + State->Decorrelator.Line = NULL; 1.1315 + State->DecoTap[0] = 0; 1.1316 + State->DecoTap[1] = 0; 1.1317 + State->DecoTap[2] = 0; 1.1318 + 1.1319 + State->Late.Gain = 0.0f; 1.1320 + State->Late.DensityGain = 0.0f; 1.1321 + State->Late.ApFeedCoeff = 0.0f; 1.1322 + State->Late.MixCoeff = 0.0f; 1.1323 + for(index = 0;index < 4;index++) 1.1324 + { 1.1325 + State->Late.ApCoeff[index] = 0.0f; 1.1326 + State->Late.ApDelay[index].Mask = 0; 1.1327 + State->Late.ApDelay[index].Line = NULL; 1.1328 + State->Late.ApOffset[index] = 0; 1.1329 + 1.1330 + State->Late.Coeff[index] = 0.0f; 1.1331 + State->Late.Delay[index].Mask = 0; 1.1332 + State->Late.Delay[index].Line = NULL; 1.1333 + State->Late.Offset[index] = 0; 1.1334 + 1.1335 + State->Late.LpCoeff[index] = 0.0f; 1.1336 + State->Late.LpSample[index] = 0.0f; 1.1337 + } 1.1338 + 1.1339 + for(index = 0;index < MAXCHANNELS;index++) 1.1340 + { 1.1341 + State->Early.PanGain[index] = 0.0f; 1.1342 + State->Late.PanGain[index] = 0.0f; 1.1343 + } 1.1344 + 1.1345 + State->Echo.DensityGain = 0.0f; 1.1346 + State->Echo.Delay.Mask = 0; 1.1347 + State->Echo.Delay.Line = NULL; 1.1348 + State->Echo.ApDelay.Mask = 0; 1.1349 + State->Echo.ApDelay.Line = NULL; 1.1350 + State->Echo.Coeff = 0.0f; 1.1351 + State->Echo.ApFeedCoeff = 0.0f; 1.1352 + State->Echo.ApCoeff = 0.0f; 1.1353 + State->Echo.Offset = 0; 1.1354 + State->Echo.ApOffset = 0; 1.1355 + State->Echo.LpCoeff = 0.0f; 1.1356 + State->Echo.LpSample = 0.0f; 1.1357 + State->Echo.MixCoeff[0] = 0.0f; 1.1358 + State->Echo.MixCoeff[1] = 0.0f; 1.1359 + 1.1360 + State->Offset = 0; 1.1361 + 1.1362 + State->Gain = State->Late.PanGain; 1.1363 + 1.1364 + return &State->state; 1.1365 +} 1.1366 + 1.1367 +ALeffectState *EAXVerbCreate(void) 1.1368 +{ 1.1369 + ALeffectState *State = VerbCreate(); 1.1370 + if(State && EmulateEAXReverb == AL_FALSE) 1.1371 + { 1.1372 + State->DeviceUpdate = EAXVerbDeviceUpdate; 1.1373 + State->Update = EAXVerbUpdate; 1.1374 + State->Process = EAXVerbProcess; 1.1375 + } 1.1376 + return State; 1.1377 +}