diff options
-rw-r--r-- | Alc/alcReverb.c | 1125 | ||||
-rw-r--r-- | OpenAL32/Include/alAuxEffectSlot.h | 3 | ||||
-rw-r--r-- | OpenAL32/alAuxEffectSlot.c | 24 |
3 files changed, 538 insertions, 614 deletions
diff --git a/Alc/alcReverb.c b/Alc/alcReverb.c index d4e5d318..d8c06dc4 100644 --- a/Alc/alcReverb.c +++ b/Alc/alcReverb.c @@ -193,6 +193,418 @@ static const ALfloat LATE_LINE_LENGTH[4] = // effect's density parameter (inverted for some reason) and this multiplier. static const ALfloat LATE_LINE_MULTIPLIER = 4.0f; + +// Basic delay line input/output routines. +static __inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset) +{ + return Delay->Line[offset&Delay->Mask]; +} + +static __inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in) +{ + Delay->Line[offset&Delay->Mask] = in; +} + +// Attenuated delay line output routine. +static __inline ALfloat AttenuatedDelayLineOut(DelayLine *Delay, ALuint offset, ALfloat coeff) +{ + return coeff * Delay->Line[offset&Delay->Mask]; +} + +// Basic attenuated all-pass input/output routine. +static __inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff) +{ + ALfloat out, feed; + + out = DelayLineOut(Delay, outOffset); + feed = feedCoeff * in; + DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in); + + // The time-based attenuation is only applied to the delay output to + // keep it from affecting the feed-back path (which is already controlled + // by the all-pass feed coefficient). + return (coeff * out) - feed; +} + +// Given an input sample, this function produces modulation for the late +// reverb. +static __inline ALfloat EAXModulation(ALverbState *State, ALfloat in) +{ + ALfloat sinus, frac; + ALuint offset; + ALfloat out0, out1; + + // Calculate the sinus rythm (dependent on modulation time and the + // sampling rate). The center of the sinus is moved to reduce the delay + // of the effect when the time or depth are low. + sinus = 1.0f - cos(2.0f * M_PI * State->Mod.Index / State->Mod.Range); + + // The depth determines the range over which to read the input samples + // from, so it must be filtered to reduce the distortion caused by even + // small parameter changes. + State->Mod.Filter = lerp(State->Mod.Filter, State->Mod.Depth, + State->Mod.Coeff); + + // Calculate the read offset and fraction between it and the next sample. + frac = (1.0f + (State->Mod.Filter * sinus)); + offset = (ALuint)frac; + frac -= offset; + + // Get the two samples crossed by the offset, and feed the delay line + // with the next input sample. + out0 = DelayLineOut(&State->Mod.Delay, State->Offset - offset); + out1 = DelayLineOut(&State->Mod.Delay, State->Offset - offset - 1); + DelayLineIn(&State->Mod.Delay, State->Offset, in); + + // Step the modulation index forward, keeping it bound to its range. + State->Mod.Index = (State->Mod.Index + 1) % State->Mod.Range; + + // The output is obtained by linearly interpolating the two samples that + // were acquired above. + return lerp(out0, out1, frac); +} + +// Delay line output routine for early reflections. +static __inline ALfloat EarlyDelayLineOut(ALverbState *State, ALuint index) +{ + return AttenuatedDelayLineOut(&State->Early.Delay[index], + State->Offset - State->Early.Offset[index], + State->Early.Coeff[index]); +} + +// Given an input sample, this function produces four-channel output for the +// early reflections. +static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *out) +{ + ALfloat d[4], v, f[4]; + + // Obtain the decayed results of each early delay line. + d[0] = EarlyDelayLineOut(State, 0); + d[1] = EarlyDelayLineOut(State, 1); + d[2] = EarlyDelayLineOut(State, 2); + d[3] = EarlyDelayLineOut(State, 3); + + /* The following uses a lossless scattering junction from waveguide + * theory. It actually amounts to a householder mixing matrix, which + * will produce a maximally diffuse response, and means this can probably + * be considered a simple feed-back delay network (FDN). + * N + * --- + * \ + * v = 2/N / d_i + * --- + * i=1 + */ + v = (d[0] + d[1] + d[2] + d[3]) * 0.5f; + // The junction is loaded with the input here. + v += in; + + // Calculate the feed values for the delay lines. + f[0] = v - d[0]; + f[1] = v - d[1]; + f[2] = v - d[2]; + f[3] = v - d[3]; + + // Re-feed the delay lines. + DelayLineIn(&State->Early.Delay[0], State->Offset, f[0]); + DelayLineIn(&State->Early.Delay[1], State->Offset, f[1]); + DelayLineIn(&State->Early.Delay[2], State->Offset, f[2]); + DelayLineIn(&State->Early.Delay[3], State->Offset, f[3]); + + // Output the results of the junction for all four channels. + out[0] = State->Early.Gain * f[0]; + out[1] = State->Early.Gain * f[1]; + out[2] = State->Early.Gain * f[2]; + out[3] = State->Early.Gain * f[3]; +} + +// All-pass input/output routine for late reverb. +static __inline ALfloat LateAllPassInOut(ALverbState *State, ALuint index, ALfloat in) +{ + return AllpassInOut(&State->Late.ApDelay[index], + State->Offset - State->Late.ApOffset[index], + State->Offset, in, State->Late.ApFeedCoeff, + State->Late.ApCoeff[index]); +} + +// Delay line output routine for late reverb. +static __inline ALfloat LateDelayLineOut(ALverbState *State, ALuint index) +{ + return AttenuatedDelayLineOut(&State->Late.Delay[index], + State->Offset - State->Late.Offset[index], + State->Late.Coeff[index]); +} + +// Low-pass filter input/output routine for late reverb. +static __inline ALfloat LateLowPassInOut(ALverbState *State, ALuint index, ALfloat in) +{ + in = lerp(in, State->Late.LpSample[index], State->Late.LpCoeff[index]); + State->Late.LpSample[index] = in; + return in; +} + +// Given four decorrelated input samples, this function produces four-channel +// output for the late reverb. +static __inline ALvoid LateReverb(ALverbState *State, ALfloat *in, ALfloat *out) +{ + ALfloat d[4], f[4]; + + // Obtain the decayed results of the cyclical delay lines, and add the + // corresponding input channels. Then pass the results through the + // low-pass filters. + + // This is where the feed-back cycles from line 0 to 1 to 3 to 2 and back + // to 0. + d[0] = LateLowPassInOut(State, 2, in[2] + LateDelayLineOut(State, 2)); + d[1] = LateLowPassInOut(State, 0, in[0] + LateDelayLineOut(State, 0)); + d[2] = LateLowPassInOut(State, 3, in[3] + LateDelayLineOut(State, 3)); + d[3] = LateLowPassInOut(State, 1, in[1] + LateDelayLineOut(State, 1)); + + // To help increase diffusion, run each line through an all-pass filter. + // When there is no diffusion, the shortest all-pass filter will feed the + // shortest delay line. + d[0] = LateAllPassInOut(State, 0, d[0]); + d[1] = LateAllPassInOut(State, 1, d[1]); + d[2] = LateAllPassInOut(State, 2, d[2]); + d[3] = LateAllPassInOut(State, 3, d[3]); + + /* Late reverb is done with a modified feed-back delay network (FDN) + * topology. Four input lines are each fed through their own all-pass + * filter and then into the mixing matrix. The four outputs of the + * mixing matrix are then cycled back to the inputs. Each output feeds + * a different input to form a circlular feed cycle. + * + * The mixing matrix used is a 4D skew-symmetric rotation matrix derived + * using a single unitary rotational parameter: + * + * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2 + * [ -a, d, c, -b ] + * [ -b, -c, d, a ] + * [ -c, b, -a, d ] + * + * The rotation is constructed from the effect's diffusion parameter, + * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y + * with differing signs, and d is the coefficient x. The matrix is thus: + * + * [ x, y, -y, y ] n = sqrt(matrix_order - 1) + * [ -y, x, y, y ] t = diffusion_parameter * atan(n) + * [ y, -y, x, y ] x = cos(t) + * [ -y, -y, -y, x ] y = sin(t) / n + * + * To reduce the number of multiplies, the x coefficient is applied with + * the cyclical delay line coefficients. Thus only the y coefficient is + * applied when mixing, and is modified to be: y / x. + */ + f[0] = d[0] + (State->Late.MixCoeff * ( d[1] + -d[2] + d[3])); + f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3])); + f[2] = d[2] + (State->Late.MixCoeff * ( d[0] + -d[1] + d[3])); + f[3] = d[3] + (State->Late.MixCoeff * (-d[0] + -d[1] + -d[2] )); + + // Output the results of the matrix for all four channels, attenuated by + // the late reverb gain (which is attenuated by the 'x' mix coefficient). + out[0] = State->Late.Gain * f[0]; + out[1] = State->Late.Gain * f[1]; + out[2] = State->Late.Gain * f[2]; + out[3] = State->Late.Gain * f[3]; + + // Re-feed the cyclical delay lines. + DelayLineIn(&State->Late.Delay[0], State->Offset, f[0]); + DelayLineIn(&State->Late.Delay[1], State->Offset, f[1]); + DelayLineIn(&State->Late.Delay[2], State->Offset, f[2]); + DelayLineIn(&State->Late.Delay[3], State->Offset, f[3]); +} + +// Given an input sample, this function mixes echo into the four-channel late +// reverb. +static __inline ALvoid EAXEcho(ALverbState *State, ALfloat in, ALfloat *late) +{ + ALfloat out, feed; + + // Get the latest attenuated echo sample for output. + feed = AttenuatedDelayLineOut(&State->Echo.Delay, + State->Offset - State->Echo.Offset, + State->Echo.Coeff); + + // Mix the output into the late reverb channels. + out = State->Echo.MixCoeff[0] * feed; + late[0] = (State->Echo.MixCoeff[1] * late[0]) + out; + late[1] = (State->Echo.MixCoeff[1] * late[1]) + out; + late[2] = (State->Echo.MixCoeff[1] * late[2]) + out; + late[3] = (State->Echo.MixCoeff[1] * late[3]) + out; + + // Mix the energy-attenuated input with the output and pass it through + // the echo low-pass filter. + feed += State->Echo.DensityGain * in; + feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff); + State->Echo.LpSample = feed; + + // Then the echo all-pass filter. + feed = AllpassInOut(&State->Echo.ApDelay, + State->Offset - State->Echo.ApOffset, + State->Offset, feed, State->Echo.ApFeedCoeff, + State->Echo.ApCoeff); + + // Feed the delay with the mixed and filtered sample. + DelayLineIn(&State->Echo.Delay, State->Offset, feed); +} + +// Perform the non-EAX reverb pass on a given input sample, resulting in +// four-channel output. +static __inline ALvoid VerbPass(ALverbState *State, ALfloat in, ALfloat *early, ALfloat *late) +{ + ALfloat feed, taps[4]; + + // Low-pass filter the incoming sample. + in = lpFilter2P(&State->LpFilter, 0, in); + + // Feed the initial delay line. + DelayLineIn(&State->Delay, State->Offset, in); + + // Calculate the early reflection from the first delay tap. + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]); + EarlyReflection(State, in, early); + + // Feed the decorrelator from the energy-attenuated output of the second + // delay tap. + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]); + feed = in * State->Late.DensityGain; + DelayLineIn(&State->Decorrelator, State->Offset, feed); + + // Calculate the late reverb from the decorrelator taps. + taps[0] = feed; + taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]); + taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]); + taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]); + LateReverb(State, taps, late); + + // Step all delays forward one sample. + State->Offset++; +} + +// Perform the EAX reverb pass on a given input sample, resulting in four- +// channel output. +static __inline ALvoid EAXVerbPass(ALverbState *State, ALfloat in, ALfloat *early, ALfloat *late) +{ + ALfloat feed, taps[4]; + + // Low-pass filter the incoming sample. + in = lpFilter2P(&State->LpFilter, 0, in); + + // Perform any modulation on the input. + in = EAXModulation(State, in); + + // Feed the initial delay line. + DelayLineIn(&State->Delay, State->Offset, in); + + // Calculate the early reflection from the first delay tap. + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]); + EarlyReflection(State, in, early); + + // Feed the decorrelator from the energy-attenuated output of the second + // delay tap. + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]); + feed = in * State->Late.DensityGain; + DelayLineIn(&State->Decorrelator, State->Offset, feed); + + // Calculate the late reverb from the decorrelator taps. + taps[0] = feed; + taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]); + taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]); + taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]); + LateReverb(State, taps, late); + + // Calculate and mix in any echo. + EAXEcho(State, in, late); + + // Step all delays forward one sample. + State->Offset++; +} + +// This processes the reverb state, given the input samples and an output +// buffer. +static ALvoid VerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) +{ + ALverbState *State = (ALverbState*)effect; + ALuint index; + ALfloat early[4], late[4], out[4]; + const ALfloat *panGain = State->Gain; + (void)Slot; + + for(index = 0;index < SamplesToDo;index++) + { + // Process reverb for this sample. + VerbPass(State, SamplesIn[index], early, late); + + // Mix early reflections and late reverb. + out[0] = (early[0] + late[0]); + out[1] = (early[1] + late[1]); + out[2] = (early[2] + late[2]); + out[3] = (early[3] + late[3]); + + // Output the results. + SamplesOut[index][FRONT_LEFT] += panGain[FRONT_LEFT] * out[0]; + SamplesOut[index][FRONT_RIGHT] += panGain[FRONT_RIGHT] * out[1]; + SamplesOut[index][FRONT_CENTER] += panGain[FRONT_CENTER] * out[3]; + SamplesOut[index][SIDE_LEFT] += panGain[SIDE_LEFT] * out[0]; + SamplesOut[index][SIDE_RIGHT] += panGain[SIDE_RIGHT] * out[1]; + SamplesOut[index][BACK_LEFT] += panGain[BACK_LEFT] * out[0]; + SamplesOut[index][BACK_RIGHT] += panGain[BACK_RIGHT] * out[1]; + SamplesOut[index][BACK_CENTER] += panGain[BACK_CENTER] * out[2]; + } +} + +// This processes the EAX reverb state, given the input samples and an output +// buffer. +static ALvoid EAXVerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) +{ + ALverbState *State = (ALverbState*)effect; + ALuint index; + ALfloat early[4], late[4]; + (void)Slot; + + for(index = 0;index < SamplesToDo;index++) + { + // Process reverb for this sample. + EAXVerbPass(State, SamplesIn[index], early, late); + + // Unfortunately, while the number and configuration of gains for + // panning adjust according to MAXCHANNELS, the output from the + // reverb engine is not so scalable. + SamplesOut[index][FRONT_LEFT] += + (State->Early.PanGain[FRONT_LEFT]*early[0] + + State->Late.PanGain[FRONT_LEFT]*late[0]); + SamplesOut[index][FRONT_RIGHT] += + (State->Early.PanGain[FRONT_RIGHT]*early[1] + + State->Late.PanGain[FRONT_RIGHT]*late[1]); + SamplesOut[index][FRONT_CENTER] += + (State->Early.PanGain[FRONT_CENTER]*early[3] + + State->Late.PanGain[FRONT_CENTER]*late[3]); + SamplesOut[index][SIDE_LEFT] += + (State->Early.PanGain[SIDE_LEFT]*early[0] + + State->Late.PanGain[SIDE_LEFT]*late[0]); + SamplesOut[index][SIDE_RIGHT] += + (State->Early.PanGain[SIDE_RIGHT]*early[1] + + State->Late.PanGain[SIDE_RIGHT]*late[1]); + SamplesOut[index][BACK_LEFT] += + (State->Early.PanGain[BACK_LEFT]*early[0] + + State->Late.PanGain[BACK_LEFT]*late[0]); + SamplesOut[index][BACK_RIGHT] += + (State->Early.PanGain[BACK_RIGHT]*early[1] + + State->Late.PanGain[BACK_RIGHT]*late[1]); + SamplesOut[index][BACK_CENTER] += + (State->Early.PanGain[BACK_CENTER]*early[2] + + State->Late.PanGain[BACK_CENTER]*late[2]); + } +} + + +// Given the allocated sample buffer, this function updates each delay line +// offset. +static __inline ALvoid RealizeLineOffset(ALfloat * sampleBuffer, DelayLine *Delay) +{ + Delay->Line = &sampleBuffer[(ALintptrEXT)Delay->Line]; +} + // Calculate the length of a delay line and store its mask and offset. static ALuint CalcLineLength(ALfloat length, ALintptrEXT offset, ALuint frequency, DelayLine *Delay) { @@ -208,19 +620,11 @@ static ALuint CalcLineLength(ALfloat length, ALintptrEXT offset, ALuint frequenc return samples; } -// Given the allocated sample buffer, this function updates each delay line -// offset. -static __inline ALvoid RealizeLineOffset(ALfloat * sampleBuffer, DelayLine *Delay) -{ - Delay->Line = &sampleBuffer[(ALintptrEXT)Delay->Line]; -} - /* Calculates the delay line metrics and allocates the shared sample buffer - * for all lines given a flag indicating whether or not to allocate the EAX- - * related delays (eaxFlag) and the sample rate (frequency). If an - * allocation failure occurs, it returns AL_FALSE. + * for all lines given the sample rate (frequency). If an allocation failure + * occurs, it returns AL_FALSE. */ -static ALboolean AllocLines(ALboolean eaxFlag, ALuint frequency, ALverbState *State) +static ALboolean AllocLines(ALuint frequency, ALverbState *State) { ALuint totalSamples, index; ALfloat length; @@ -229,27 +633,21 @@ static ALboolean AllocLines(ALboolean eaxFlag, ALuint frequency, ALverbState *St // All delay line lengths are calculated to accomodate the full range of // lengths given their respective paramters. totalSamples = 0; - if(eaxFlag) - { - /* The modulator's line length is calculated from the maximum - * modulation time and depth coefficient, and halfed for the low-to- - * high frequency swing. An additional sample is added to keep it - * stable when there is no modulation. - */ - length = (AL_EAXREVERB_MAX_MODULATION_TIME * MODULATION_DEPTH_COEFF / - 2.0f) + (1.0f / frequency); - totalSamples += CalcLineLength(length, totalSamples, frequency, - &State->Mod.Delay); - } + + /* The modulator's line length is calculated from the maximum modulation + * time and depth coefficient, and halfed for the low-to-high frequency + * swing. An additional sample is added to keep it stable when there is no + * modulation. + */ + length = (AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f) + + (1.0f / frequency); + totalSamples += CalcLineLength(length, totalSamples, frequency, + &State->Mod.Delay); // The initial delay is the sum of the reflections and late reverb // delays. - if(eaxFlag) - length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + - AL_EAXREVERB_MAX_LATE_REVERB_DELAY; - else - length = AL_REVERB_MAX_REFLECTIONS_DELAY + - AL_REVERB_MAX_LATE_REVERB_DELAY; + length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + + AL_EAXREVERB_MAX_LATE_REVERB_DELAY; totalSamples += CalcLineLength(length, totalSamples, frequency, &State->Delay); @@ -278,14 +676,11 @@ static ALboolean AllocLines(ALboolean eaxFlag, ALuint frequency, ALverbState *St &State->Late.Delay[index]); } - if(eaxFlag) - { - // The echo all-pass and delay lines. - totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples, - frequency, &State->Echo.ApDelay); - totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples, - frequency, &State->Echo.Delay); - } + // The echo all-pass and delay lines. + totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples, + frequency, &State->Echo.ApDelay); + totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples, + frequency, &State->Echo.Delay); if(totalSamples != State->TotalSamples) { @@ -305,12 +700,9 @@ static ALboolean AllocLines(ALboolean eaxFlag, ALuint frequency, ALverbState *St RealizeLineOffset(State->SampleBuffer, &State->Late.ApDelay[index]); RealizeLineOffset(State->SampleBuffer, &State->Late.Delay[index]); } - if(eaxFlag) - { - RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay); - RealizeLineOffset(State->SampleBuffer, &State->Echo.ApDelay); - RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay); - } + RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay); + RealizeLineOffset(State->SampleBuffer, &State->Echo.ApDelay); + RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay); // Clear the sample buffer. for(index = 0;index < State->TotalSamples;index++) @@ -319,6 +711,42 @@ static ALboolean AllocLines(ALboolean eaxFlag, ALuint frequency, ALverbState *St return AL_TRUE; } +// This updates the device-dependant EAX reverb state. This is called on +// initialization and any time the device parameters (eg. playback frequency, +// format) have been changed. +static ALboolean ReverbDeviceUpdate(ALeffectState *effect, ALCdevice *Device) +{ + ALverbState *State = (ALverbState*)effect; + ALuint frequency = Device->Frequency, index; + + // Allocate the delay lines. + if(!AllocLines(frequency, State)) + return AL_FALSE; + + // Calculate the modulation filter coefficient. Notice that the exponent + // is calculated given the current sample rate. This ensures that the + // resulting filter response over time is consistent across all sample + // rates. + State->Mod.Coeff = aluPow(MODULATION_FILTER_COEFF, + MODULATION_FILTER_CONST / frequency); + + // The early reflection and late all-pass filter line lengths are static, + // so their offsets only need to be calculated once. + for(index = 0;index < 4;index++) + { + State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] * + frequency); + State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] * + frequency); + } + + // The echo all-pass filter line length is static, so its offset only + // needs to be calculated once. + State->Echo.ApOffset = (ALuint)(ECHO_ALLPASS_LENGTH * frequency); + + return AL_TRUE; +} + // Calculate a decay coefficient given the length of each cycle and the time // until the decay reaches -60 dB. static __inline ALfloat CalcDecayCoeff(ALfloat length, ALfloat decayTime) @@ -663,484 +1091,38 @@ static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *Reflection } } -// Basic delay line input/output routines. -static __inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset) -{ - return Delay->Line[offset&Delay->Mask]; -} - -static __inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in) -{ - Delay->Line[offset&Delay->Mask] = in; -} - -// Attenuated delay line output routine. -static __inline ALfloat AttenuatedDelayLineOut(DelayLine *Delay, ALuint offset, ALfloat coeff) -{ - return coeff * Delay->Line[offset&Delay->Mask]; -} - -// Basic attenuated all-pass input/output routine. -static __inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff) -{ - ALfloat out, feed; - - out = DelayLineOut(Delay, outOffset); - feed = feedCoeff * in; - DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in); - - // The time-based attenuation is only applied to the delay output to - // keep it from affecting the feed-back path (which is already controlled - // by the all-pass feed coefficient). - return (coeff * out) - feed; -} - -// Given an input sample, this function produces modulation for the late -// reverb. -static __inline ALfloat EAXModulation(ALverbState *State, ALfloat in) -{ - ALfloat sinus, frac; - ALuint offset; - ALfloat out0, out1; - - // Calculate the sinus rythm (dependent on modulation time and the - // sampling rate). The center of the sinus is moved to reduce the delay - // of the effect when the time or depth are low. - sinus = 1.0f - cos(2.0f * M_PI * State->Mod.Index / State->Mod.Range); - - // The depth determines the range over which to read the input samples - // from, so it must be filtered to reduce the distortion caused by even - // small parameter changes. - State->Mod.Filter = lerp(State->Mod.Filter, State->Mod.Depth, - State->Mod.Coeff); - - // Calculate the read offset and fraction between it and the next sample. - frac = (1.0f + (State->Mod.Filter * sinus)); - offset = (ALuint)frac; - frac -= offset; - - // Get the two samples crossed by the offset, and feed the delay line - // with the next input sample. - out0 = DelayLineOut(&State->Mod.Delay, State->Offset - offset); - out1 = DelayLineOut(&State->Mod.Delay, State->Offset - offset - 1); - DelayLineIn(&State->Mod.Delay, State->Offset, in); - - // Step the modulation index forward, keeping it bound to its range. - State->Mod.Index = (State->Mod.Index + 1) % State->Mod.Range; - - // The output is obtained by linearly interpolating the two samples that - // were acquired above. - return lerp(out0, out1, frac); -} - -// Delay line output routine for early reflections. -static __inline ALfloat EarlyDelayLineOut(ALverbState *State, ALuint index) -{ - return AttenuatedDelayLineOut(&State->Early.Delay[index], - State->Offset - State->Early.Offset[index], - State->Early.Coeff[index]); -} - -// Given an input sample, this function produces four-channel output for the -// early reflections. -static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *out) -{ - ALfloat d[4], v, f[4]; - - // Obtain the decayed results of each early delay line. - d[0] = EarlyDelayLineOut(State, 0); - d[1] = EarlyDelayLineOut(State, 1); - d[2] = EarlyDelayLineOut(State, 2); - d[3] = EarlyDelayLineOut(State, 3); - - /* The following uses a lossless scattering junction from waveguide - * theory. It actually amounts to a householder mixing matrix, which - * will produce a maximally diffuse response, and means this can probably - * be considered a simple feed-back delay network (FDN). - * N - * --- - * \ - * v = 2/N / d_i - * --- - * i=1 - */ - v = (d[0] + d[1] + d[2] + d[3]) * 0.5f; - // The junction is loaded with the input here. - v += in; - - // Calculate the feed values for the delay lines. - f[0] = v - d[0]; - f[1] = v - d[1]; - f[2] = v - d[2]; - f[3] = v - d[3]; - - // Re-feed the delay lines. - DelayLineIn(&State->Early.Delay[0], State->Offset, f[0]); - DelayLineIn(&State->Early.Delay[1], State->Offset, f[1]); - DelayLineIn(&State->Early.Delay[2], State->Offset, f[2]); - DelayLineIn(&State->Early.Delay[3], State->Offset, f[3]); - - // Output the results of the junction for all four channels. - out[0] = State->Early.Gain * f[0]; - out[1] = State->Early.Gain * f[1]; - out[2] = State->Early.Gain * f[2]; - out[3] = State->Early.Gain * f[3]; -} - -// All-pass input/output routine for late reverb. -static __inline ALfloat LateAllPassInOut(ALverbState *State, ALuint index, ALfloat in) -{ - return AllpassInOut(&State->Late.ApDelay[index], - State->Offset - State->Late.ApOffset[index], - State->Offset, in, State->Late.ApFeedCoeff, - State->Late.ApCoeff[index]); -} - -// Delay line output routine for late reverb. -static __inline ALfloat LateDelayLineOut(ALverbState *State, ALuint index) -{ - return AttenuatedDelayLineOut(&State->Late.Delay[index], - State->Offset - State->Late.Offset[index], - State->Late.Coeff[index]); -} - -// Low-pass filter input/output routine for late reverb. -static __inline ALfloat LateLowPassInOut(ALverbState *State, ALuint index, ALfloat in) -{ - in = lerp(in, State->Late.LpSample[index], State->Late.LpCoeff[index]); - State->Late.LpSample[index] = in; - return in; -} - -// Given four decorrelated input samples, this function produces four-channel -// output for the late reverb. -static __inline ALvoid LateReverb(ALverbState *State, ALfloat *in, ALfloat *out) -{ - ALfloat d[4], f[4]; - - // Obtain the decayed results of the cyclical delay lines, and add the - // corresponding input channels. Then pass the results through the - // low-pass filters. - - // This is where the feed-back cycles from line 0 to 1 to 3 to 2 and back - // to 0. - d[0] = LateLowPassInOut(State, 2, in[2] + LateDelayLineOut(State, 2)); - d[1] = LateLowPassInOut(State, 0, in[0] + LateDelayLineOut(State, 0)); - d[2] = LateLowPassInOut(State, 3, in[3] + LateDelayLineOut(State, 3)); - d[3] = LateLowPassInOut(State, 1, in[1] + LateDelayLineOut(State, 1)); - - // To help increase diffusion, run each line through an all-pass filter. - // When there is no diffusion, the shortest all-pass filter will feed the - // shortest delay line. - d[0] = LateAllPassInOut(State, 0, d[0]); - d[1] = LateAllPassInOut(State, 1, d[1]); - d[2] = LateAllPassInOut(State, 2, d[2]); - d[3] = LateAllPassInOut(State, 3, d[3]); - - /* Late reverb is done with a modified feed-back delay network (FDN) - * topology. Four input lines are each fed through their own all-pass - * filter and then into the mixing matrix. The four outputs of the - * mixing matrix are then cycled back to the inputs. Each output feeds - * a different input to form a circlular feed cycle. - * - * The mixing matrix used is a 4D skew-symmetric rotation matrix derived - * using a single unitary rotational parameter: - * - * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2 - * [ -a, d, c, -b ] - * [ -b, -c, d, a ] - * [ -c, b, -a, d ] - * - * The rotation is constructed from the effect's diffusion parameter, - * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y - * with differing signs, and d is the coefficient x. The matrix is thus: - * - * [ x, y, -y, y ] n = sqrt(matrix_order - 1) - * [ -y, x, y, y ] t = diffusion_parameter * atan(n) - * [ y, -y, x, y ] x = cos(t) - * [ -y, -y, -y, x ] y = sin(t) / n - * - * To reduce the number of multiplies, the x coefficient is applied with - * the cyclical delay line coefficients. Thus only the y coefficient is - * applied when mixing, and is modified to be: y / x. - */ - f[0] = d[0] + (State->Late.MixCoeff * ( d[1] + -d[2] + d[3])); - f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3])); - f[2] = d[2] + (State->Late.MixCoeff * ( d[0] + -d[1] + d[3])); - f[3] = d[3] + (State->Late.MixCoeff * (-d[0] + -d[1] + -d[2] )); - - // Output the results of the matrix for all four channels, attenuated by - // the late reverb gain (which is attenuated by the 'x' mix coefficient). - out[0] = State->Late.Gain * f[0]; - out[1] = State->Late.Gain * f[1]; - out[2] = State->Late.Gain * f[2]; - out[3] = State->Late.Gain * f[3]; - - // Re-feed the cyclical delay lines. - DelayLineIn(&State->Late.Delay[0], State->Offset, f[0]); - DelayLineIn(&State->Late.Delay[1], State->Offset, f[1]); - DelayLineIn(&State->Late.Delay[2], State->Offset, f[2]); - DelayLineIn(&State->Late.Delay[3], State->Offset, f[3]); -} - -// Given an input sample, this function mixes echo into the four-channel late -// reverb. -static __inline ALvoid EAXEcho(ALverbState *State, ALfloat in, ALfloat *late) -{ - ALfloat out, feed; - - // Get the latest attenuated echo sample for output. - feed = AttenuatedDelayLineOut(&State->Echo.Delay, - State->Offset - State->Echo.Offset, - State->Echo.Coeff); - - // Mix the output into the late reverb channels. - out = State->Echo.MixCoeff[0] * feed; - late[0] = (State->Echo.MixCoeff[1] * late[0]) + out; - late[1] = (State->Echo.MixCoeff[1] * late[1]) + out; - late[2] = (State->Echo.MixCoeff[1] * late[2]) + out; - late[3] = (State->Echo.MixCoeff[1] * late[3]) + out; - - // Mix the energy-attenuated input with the output and pass it through - // the echo low-pass filter. - feed += State->Echo.DensityGain * in; - feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff); - State->Echo.LpSample = feed; - - // Then the echo all-pass filter. - feed = AllpassInOut(&State->Echo.ApDelay, - State->Offset - State->Echo.ApOffset, - State->Offset, feed, State->Echo.ApFeedCoeff, - State->Echo.ApCoeff); - - // Feed the delay with the mixed and filtered sample. - DelayLineIn(&State->Echo.Delay, State->Offset, feed); -} - -// Perform the non-EAX reverb pass on a given input sample, resulting in -// four-channel output. -static __inline ALvoid VerbPass(ALverbState *State, ALfloat in, ALfloat *early, ALfloat *late) -{ - ALfloat feed, taps[4]; - - // Low-pass filter the incoming sample. - in = lpFilter2P(&State->LpFilter, 0, in); - - // Feed the initial delay line. - DelayLineIn(&State->Delay, State->Offset, in); - - // Calculate the early reflection from the first delay tap. - in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]); - EarlyReflection(State, in, early); - - // Feed the decorrelator from the energy-attenuated output of the second - // delay tap. - in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]); - feed = in * State->Late.DensityGain; - DelayLineIn(&State->Decorrelator, State->Offset, feed); - - // Calculate the late reverb from the decorrelator taps. - taps[0] = feed; - taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]); - taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]); - taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]); - LateReverb(State, taps, late); - - // Step all delays forward one sample. - State->Offset++; -} - -// Perform the EAX reverb pass on a given input sample, resulting in four- -// channel output. -static __inline ALvoid EAXVerbPass(ALverbState *State, ALfloat in, ALfloat *early, ALfloat *late) -{ - ALfloat feed, taps[4]; - - // Low-pass filter the incoming sample. - in = lpFilter2P(&State->LpFilter, 0, in); - - // Perform any modulation on the input. - in = EAXModulation(State, in); - - // Feed the initial delay line. - DelayLineIn(&State->Delay, State->Offset, in); - - // Calculate the early reflection from the first delay tap. - in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]); - EarlyReflection(State, in, early); - - // Feed the decorrelator from the energy-attenuated output of the second - // delay tap. - in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]); - feed = in * State->Late.DensityGain; - DelayLineIn(&State->Decorrelator, State->Offset, feed); - - // Calculate the late reverb from the decorrelator taps. - taps[0] = feed; - taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]); - taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]); - taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]); - LateReverb(State, taps, late); - - // Calculate and mix in any echo. - EAXEcho(State, in, late); - - // Step all delays forward one sample. - State->Offset++; -} - -// This destroys the reverb state. It should be called only when the effect -// slot has a different (or no) effect loaded over the reverb effect. -static ALvoid VerbDestroy(ALeffectState *effect) -{ - ALverbState *State = (ALverbState*)effect; - if(State) - { - free(State->SampleBuffer); - State->SampleBuffer = NULL; - free(State); - } -} - -// This updates the device-dependant reverb state. This is called on -// initialization and any time the device parameters (eg. playback frequency, -// or format) have been changed. -static ALboolean VerbDeviceUpdate(ALeffectState *effect, ALCdevice *Device) +// This updates the EAX reverb state. This is called any time the EAX reverb +// effect is loaded into a slot. +static ALvoid ReverbUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffectslot *Slot) { ALverbState *State = (ALverbState*)effect; - ALuint frequency = Device->Frequency; - ALuint index; - - // Allocate the delay lines. - if(!AllocLines(AL_FALSE, frequency, State)) - return AL_FALSE; + ALuint frequency = Context->Device->Frequency; + ALboolean isEAX = AL_FALSE; + ALfloat cw, x, y, hfRatio; - // The early reflection and late all-pass filter line lengths are static, - // so their offsets only need to be calculated once. - for(index = 0;index < 4;index++) + if(Slot->effect.type == AL_EFFECT_EAXREVERB && !EmulateEAXReverb) { - State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] * - frequency); - State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] * - frequency); + State->state.Process = EAXVerbProcess; + isEAX = AL_TRUE; } - - return AL_TRUE; -} - -// This updates the device-dependant EAX reverb state. This is called on -// initialization and any time the device parameters (eg. playback frequency, -// format) have been changed. -static ALboolean EAXVerbDeviceUpdate(ALeffectState *effect, ALCdevice *Device) -{ - ALverbState *State = (ALverbState*)effect; - ALuint frequency = Device->Frequency, index; - - // Allocate the delay lines. - if(!AllocLines(AL_TRUE, frequency, State)) - return AL_FALSE; - - // Calculate the modulation filter coefficient. Notice that the exponent - // is calculated given the current sample rate. This ensures that the - // resulting filter response over time is consistent across all sample - // rates. - State->Mod.Coeff = aluPow(MODULATION_FILTER_COEFF, - MODULATION_FILTER_CONST / frequency); - - // The early reflection and late all-pass filter line lengths are static, - // so their offsets only need to be calculated once. - for(index = 0;index < 4;index++) + else if(Slot->effect.type == AL_EFFECT_REVERB || EmulateEAXReverb) { - State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] * - frequency); - State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] * - frequency); + State->state.Process = VerbProcess; + isEAX = AL_FALSE; } - // The echo all-pass filter line length is static, so its offset only - // needs to be calculated once. - State->Echo.ApOffset = (ALuint)(ECHO_ALLPASS_LENGTH * frequency); - - return AL_TRUE; -} - -// This updates the reverb state. This is called any time the reverb effect -// is loaded into a slot. -static ALvoid VerbUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffectslot *Slot) -{ - ALverbState *State = (ALverbState*)effect; - ALCdevice *Device = Context->Device; - ALuint frequency = Device->Frequency; - ALfloat cw, x, y, hfRatio, gain; - ALuint index; - // Calculate the master low-pass filter (from the master effect HF gain). cw = CalcI3DL2HFreq(Slot->effect.Params.Reverb.HFReference, frequency); // This is done with 2 chained 1-pole filters, so no need to square g. State->LpFilter.coeff = lpCoeffCalc(Slot->effect.Params.Reverb.GainHF, cw); - // Update the initial effect delay. - UpdateDelayLine(Slot->effect.Params.Reverb.ReflectionsDelay, - Slot->effect.Params.Reverb.LateReverbDelay, - frequency, State); - - // Update the early lines. - UpdateEarlyLines(Slot->effect.Params.Reverb.Gain, - Slot->effect.Params.Reverb.ReflectionsGain, - Slot->effect.Params.Reverb.LateReverbDelay, State); - - // Update the decorrelator. - UpdateDecorrelator(Slot->effect.Params.Reverb.Density, frequency, State); - - // Get the mixing matrix coefficients (x and y). - CalcMatrixCoeffs(Slot->effect.Params.Reverb.Diffusion, &x, &y); - // Then divide x into y to simplify the matrix calculation. - State->Late.MixCoeff = y / x; - - // If the HF limit parameter is flagged, calculate an appropriate limit - // based on the air absorption parameter. - hfRatio = Slot->effect.Params.Reverb.DecayHFRatio; - if(Slot->effect.Params.Reverb.DecayHFLimit && - Slot->effect.Params.Reverb.AirAbsorptionGainHF < 1.0f) - hfRatio = CalcLimitedHfRatio(hfRatio, - Slot->effect.Params.Reverb.AirAbsorptionGainHF, - Slot->effect.Params.Reverb.DecayTime); - - // Update the late lines. - UpdateLateLines(Slot->effect.Params.Reverb.Gain, Slot->effect.Params.Reverb.LateReverbGain, - x, Slot->effect.Params.Reverb.Density, Slot->effect.Params.Reverb.DecayTime, - Slot->effect.Params.Reverb.Diffusion, hfRatio, cw, frequency, State); - - // Update channel gains - gain = Slot->Gain; - gain *= aluSqrt(2.0f/Device->NumChan); - gain *= ReverbBoost; - for(index = 0;index < MAXCHANNELS;index++) - State->Gain[index] = 0.0f; - for(index = 0;index < Device->NumChan;index++) + if(isEAX) { - enum Channel chan = Device->Speaker2Chan[index]; - State->Gain[chan] = gain; + // Update the modulator line. + UpdateModulator(Slot->effect.Params.Reverb.ModulationTime, + Slot->effect.Params.Reverb.ModulationDepth, + frequency, State); } -} - -// This updates the EAX reverb state. This is called any time the EAX reverb -// effect is loaded into a slot. -static ALvoid EAXVerbUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffectslot *Slot) -{ - ALverbState *State = (ALverbState*)effect; - ALuint frequency = Context->Device->Frequency; - ALfloat cw, x, y, hfRatio; - - // Calculate the master low-pass filter (from the master effect HF gain). - cw = CalcI3DL2HFreq(Slot->effect.Params.Reverb.HFReference, frequency); - // This is done with 2 chained 1-pole filters, so no need to square g. - State->LpFilter.coeff = lpCoeffCalc(Slot->effect.Params.Reverb.GainHF, cw); - - // Update the modulator line. - UpdateModulator(Slot->effect.Params.Reverb.ModulationTime, - Slot->effect.Params.Reverb.ModulationDepth, - frequency, State); // Update the initial effect delay. UpdateDelayLine(Slot->effect.Params.Reverb.ReflectionsDelay, @@ -1174,97 +1156,52 @@ static ALvoid EAXVerbUpdate(ALeffectState *effect, ALCcontext *Context, const AL x, Slot->effect.Params.Reverb.Density, Slot->effect.Params.Reverb.DecayTime, Slot->effect.Params.Reverb.Diffusion, hfRatio, cw, frequency, State); - // Update the echo line. - UpdateEchoLine(Slot->effect.Params.Reverb.Gain, Slot->effect.Params.Reverb.LateReverbGain, - Slot->effect.Params.Reverb.EchoTime, Slot->effect.Params.Reverb.DecayTime, - Slot->effect.Params.Reverb.Diffusion, Slot->effect.Params.Reverb.EchoDepth, - hfRatio, cw, frequency, State); - - // Update early and late 3D panning. - Update3DPanning(Context->Device, Slot->effect.Params.Reverb.ReflectionsPan, - Slot->effect.Params.Reverb.LateReverbPan, Slot->Gain, State); -} - -// This processes the reverb state, given the input samples and an output -// buffer. -static ALvoid VerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) -{ - ALverbState *State = (ALverbState*)effect; - ALuint index; - ALfloat early[4], late[4], out[4]; - const ALfloat *panGain = State->Gain; - (void)Slot; - - for(index = 0;index < SamplesToDo;index++) + if(isEAX) { - // Process reverb for this sample. - VerbPass(State, SamplesIn[index], early, late); - - // Mix early reflections and late reverb. - out[0] = (early[0] + late[0]); - out[1] = (early[1] + late[1]); - out[2] = (early[2] + late[2]); - out[3] = (early[3] + late[3]); - - // Output the results. - SamplesOut[index][FRONT_LEFT] += panGain[FRONT_LEFT] * out[0]; - SamplesOut[index][FRONT_RIGHT] += panGain[FRONT_RIGHT] * out[1]; - SamplesOut[index][FRONT_CENTER] += panGain[FRONT_CENTER] * out[3]; - SamplesOut[index][SIDE_LEFT] += panGain[SIDE_LEFT] * out[0]; - SamplesOut[index][SIDE_RIGHT] += panGain[SIDE_RIGHT] * out[1]; - SamplesOut[index][BACK_LEFT] += panGain[BACK_LEFT] * out[0]; - SamplesOut[index][BACK_RIGHT] += panGain[BACK_RIGHT] * out[1]; - SamplesOut[index][BACK_CENTER] += panGain[BACK_CENTER] * out[2]; + // Update the echo line. + UpdateEchoLine(Slot->effect.Params.Reverb.Gain, Slot->effect.Params.Reverb.LateReverbGain, + Slot->effect.Params.Reverb.EchoTime, Slot->effect.Params.Reverb.DecayTime, + Slot->effect.Params.Reverb.Diffusion, Slot->effect.Params.Reverb.EchoDepth, + hfRatio, cw, frequency, State); + + // Update early and late 3D panning. + Update3DPanning(Context->Device, Slot->effect.Params.Reverb.ReflectionsPan, + Slot->effect.Params.Reverb.LateReverbPan, Slot->Gain, State); + } + else + { + ALCdevice *Device = Context->Device; + ALfloat gain = Slot->Gain; + ALuint index; + + /* Update channel gains */ + gain *= aluSqrt(2.0f/Device->NumChan) * ReverbBoost; + for(index = 0;index < MAXCHANNELS;index++) + State->Gain[index] = 0.0f; + for(index = 0;index < Device->NumChan;index++) + { + enum Channel chan = Device->Speaker2Chan[index]; + State->Gain[chan] = gain; + } } } -// This processes the EAX reverb state, given the input samples and an output -// buffer. -static ALvoid EAXVerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) +// This destroys the reverb state. It should be called only when the effect +// slot has a different (or no) effect loaded over the reverb effect. +static ALvoid ReverbDestroy(ALeffectState *effect) { ALverbState *State = (ALverbState*)effect; - ALuint index; - ALfloat early[4], late[4]; - (void)Slot; - - for(index = 0;index < SamplesToDo;index++) + if(State) { - // Process reverb for this sample. - EAXVerbPass(State, SamplesIn[index], early, late); - - // Unfortunately, while the number and configuration of gains for - // panning adjust according to MAXCHANNELS, the output from the - // reverb engine is not so scalable. - SamplesOut[index][FRONT_LEFT] += - (State->Early.PanGain[FRONT_LEFT]*early[0] + - State->Late.PanGain[FRONT_LEFT]*late[0]); - SamplesOut[index][FRONT_RIGHT] += - (State->Early.PanGain[FRONT_RIGHT]*early[1] + - State->Late.PanGain[FRONT_RIGHT]*late[1]); - SamplesOut[index][FRONT_CENTER] += - (State->Early.PanGain[FRONT_CENTER]*early[3] + - State->Late.PanGain[FRONT_CENTER]*late[3]); - SamplesOut[index][SIDE_LEFT] += - (State->Early.PanGain[SIDE_LEFT]*early[0] + - State->Late.PanGain[SIDE_LEFT]*late[0]); - SamplesOut[index][SIDE_RIGHT] += - (State->Early.PanGain[SIDE_RIGHT]*early[1] + - State->Late.PanGain[SIDE_RIGHT]*late[1]); - SamplesOut[index][BACK_LEFT] += - (State->Early.PanGain[BACK_LEFT]*early[0] + - State->Late.PanGain[BACK_LEFT]*late[0]); - SamplesOut[index][BACK_RIGHT] += - (State->Early.PanGain[BACK_RIGHT]*early[1] + - State->Late.PanGain[BACK_RIGHT]*late[1]); - SamplesOut[index][BACK_CENTER] += - (State->Early.PanGain[BACK_CENTER]*early[2] + - State->Late.PanGain[BACK_CENTER]*late[2]); + free(State->SampleBuffer); + State->SampleBuffer = NULL; + free(State); } } // This creates the reverb state. It should be called only when the reverb // effect is loaded into a slot that doesn't already have a reverb effect. -ALeffectState *VerbCreate(void) +ALeffectState *ReverbCreate(void) { ALverbState *State = NULL; ALuint index; @@ -1273,9 +1210,9 @@ ALeffectState *VerbCreate(void) if(!State) return NULL; - State->state.Destroy = VerbDestroy; - State->state.DeviceUpdate = VerbDeviceUpdate; - State->state.Update = VerbUpdate; + State->state.Destroy = ReverbDestroy; + State->state.DeviceUpdate = ReverbDeviceUpdate; + State->state.Update = ReverbUpdate; State->state.Process = VerbProcess; State->TotalSamples = 0; @@ -1360,15 +1297,3 @@ ALeffectState *VerbCreate(void) return &State->state; } - -ALeffectState *EAXVerbCreate(void) -{ - ALeffectState *State = VerbCreate(); - if(State && EmulateEAXReverb == AL_FALSE) - { - State->DeviceUpdate = EAXVerbDeviceUpdate; - State->Update = EAXVerbUpdate; - State->Process = EAXVerbProcess; - } - return State; -} diff --git a/OpenAL32/Include/alAuxEffectSlot.h b/OpenAL32/Include/alAuxEffectSlot.h index 28d890cc..b7fe3b4c 100644 --- a/OpenAL32/Include/alAuxEffectSlot.h +++ b/OpenAL32/Include/alAuxEffectSlot.h @@ -46,8 +46,7 @@ struct ALeffectState { }; ALeffectState *NoneCreate(void); -ALeffectState *EAXVerbCreate(void); -ALeffectState *VerbCreate(void); +ALeffectState *ReverbCreate(void); ALeffectState *EchoCreate(void); ALeffectState *ModulatorCreate(void); ALeffectState *DedicatedCreate(void); diff --git a/OpenAL32/alAuxEffectSlot.c b/OpenAL32/alAuxEffectSlot.c index 8d51569b..5cca2a51 100644 --- a/OpenAL32/alAuxEffectSlot.c +++ b/OpenAL32/alAuxEffectSlot.c @@ -530,15 +530,13 @@ static ALvoid InitializeEffect(ALCcontext *Context, ALeffectslot *EffectSlot, AL NewState = NoneCreate(); if(!NewState) err = AL_OUT_OF_MEMORY; } - else if(newtype == AL_EFFECT_EAXREVERB && EffectSlot->effect.type != AL_EFFECT_EAXREVERB) + else if(newtype == AL_EFFECT_EAXREVERB || newtype == AL_EFFECT_REVERB) { - NewState = EAXVerbCreate(); - if(!NewState) err = AL_OUT_OF_MEMORY; - } - else if(newtype == AL_EFFECT_REVERB && EffectSlot->effect.type != AL_EFFECT_REVERB) - { - NewState = VerbCreate(); - if(!NewState) err = AL_OUT_OF_MEMORY; + if(EffectSlot->effect.type != AL_EFFECT_EAXREVERB && EffectSlot->effect.type != AL_EFFECT_REVERB) + { + NewState = ReverbCreate(); + if(!NewState) err = AL_OUT_OF_MEMORY; + } } else if(newtype == AL_EFFECT_ECHO && EffectSlot->effect.type != AL_EFFECT_ECHO) { @@ -550,11 +548,13 @@ static ALvoid InitializeEffect(ALCcontext *Context, ALeffectslot *EffectSlot, AL NewState = ModulatorCreate(); if(!NewState) err = AL_OUT_OF_MEMORY; } - else if((newtype == AL_EFFECT_DEDICATED_DIALOGUE || newtype == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) && - EffectSlot->effect.type != AL_EFFECT_DEDICATED_DIALOGUE && EffectSlot->effect.type != AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) + else if(newtype == AL_EFFECT_DEDICATED_DIALOGUE || newtype == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) { - NewState = DedicatedCreate(); - if(!NewState) err = AL_OUT_OF_MEMORY; + if(EffectSlot->effect.type != AL_EFFECT_DEDICATED_DIALOGUE && EffectSlot->effect.type != AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) + { + NewState = DedicatedCreate(); + if(!NewState) err = AL_OUT_OF_MEMORY; + } } if(err != AL_NO_ERROR) |