diff options
Diffstat (limited to 'Alc/effects/reverb.c')
-rw-r--r-- | Alc/effects/reverb.c | 480 |
1 files changed, 249 insertions, 231 deletions
diff --git a/Alc/effects/reverb.c b/Alc/effects/reverb.c index c10cd8f0..c9397b67 100644 --- a/Alc/effects/reverb.c +++ b/Alc/effects/reverb.c @@ -30,12 +30,23 @@ #include "alEffect.h" #include "alFilter.h" #include "alError.h" +#include "mixer_defs.h" /* This is the maximum number of samples processed for each inner loop * iteration. */ #define MAX_UPDATE_SAMPLES 256 + +static MixerFunc MixSamples = Mix_C; + +static alonce_flag mixfunc_inited = AL_ONCE_FLAG_INIT; +static void init_mixfunc(void) +{ + MixSamples = SelectMixer(); +} + + typedef struct DelayLine { // The delay lines use sample lengths that are powers of 2 to allow the @@ -77,11 +88,16 @@ typedef struct ALreverbState { ALfloat Filter; } Mod; // EAX only - // Initial effect delay. + /* Core delay line (early reflections and late reverb tap from this). */ DelayLine Delay; - // The tap points for the initial delay. First tap goes to early - // reflections, the last to late reverb. + /* The tap points for the initial delay. First tap goes to early + * reflections, second to late reverb. + */ ALuint DelayTap[2]; + /* There are actually 4 decorrelator taps, but the first occurs at the late + * reverb tap. + */ + ALuint DecoTap[3]; struct { // Early reflections are done with 4 delay lines. @@ -90,19 +106,10 @@ typedef struct ALreverbState { ALuint Offset[4]; // The gain for each output channel based on 3D panning. - // NOTE: With certain output modes, we may be rendering to the dry - // buffer and the "real" buffer. The two combined may be using more - // than the max output channels, so we need some extra for the real - // output too. - ALfloat PanGain[4][MAX_OUTPUT_CHANNELS*2]; + ALfloat CurrentGain[4][MAX_OUTPUT_CHANNELS+2]; + ALfloat PanGain[4][MAX_OUTPUT_CHANNELS+2]; } Early; - // Decorrelator delay line. - DelayLine Decorrelator; - // There are actually 4 decorrelator taps, but the first occurs at the - // initial sample. - ALuint DecoTap[3]; - struct { // Output gain for late reverb. ALfloat Gain; @@ -132,8 +139,8 @@ typedef struct ALreverbState { ALfloat LpSample[4]; // The gain for each output channel based on 3D panning. - // NOTE: Add some extra in case (see note about early pan). - ALfloat PanGain[4][MAX_OUTPUT_CHANNELS*2]; + ALfloat CurrentGain[4][MAX_OUTPUT_CHANNELS+2]; + ALfloat PanGain[4][MAX_OUTPUT_CHANNELS+2]; } Late; struct { @@ -164,8 +171,8 @@ typedef struct ALreverbState { ALuint Offset; /* Temporary storage used when processing. */ - ALfloat ReverbSamples[MAX_UPDATE_SAMPLES][4]; - ALfloat EarlySamples[MAX_UPDATE_SAMPLES][4]; + alignas(16) ALfloat ReverbSamples[4][MAX_UPDATE_SAMPLES]; + alignas(16) ALfloat EarlySamples[4][MAX_UPDATE_SAMPLES]; } ALreverbState; static ALvoid ALreverbState_Destruct(ALreverbState *State); @@ -205,6 +212,9 @@ static void ALreverbState_Construct(ALreverbState *state) state->Delay.Line = NULL; state->DelayTap[0] = 0; state->DelayTap[1] = 0; + state->DecoTap[0] = 0; + state->DecoTap[1] = 0; + state->DecoTap[2] = 0; for(index = 0;index < 4;index++) { @@ -214,12 +224,6 @@ static void ALreverbState_Construct(ALreverbState *state) state->Early.Offset[index] = 0; } - state->Decorrelator.Mask = 0; - state->Decorrelator.Line = NULL; - state->DecoTap[0] = 0; - state->DecoTap[1] = 0; - state->DecoTap[2] = 0; - state->Late.Gain = 0.0f; state->Late.DensityGain = 0.0f; state->Late.ApFeedCoeff = 0.0f; @@ -399,11 +403,15 @@ static ALboolean AllocLines(ALuint frequency, ALreverbState *State) totalSamples += CalcLineLength(length, totalSamples, frequency, 1, &State->Mod.Delay); - // The initial delay is the sum of the reflections and late reverb - // delays. This must include space for storing a loop update to feed the - // early reflections, decorrelator, and echo. + /* The initial delay is the sum of the reflections and late reverb delays. + * The decorrelator length is calculated from the lowest reverb density (a + * parameter value of 1). This must include space for storing a loop + * update. + */ length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + AL_EAXREVERB_MAX_LATE_REVERB_DELAY; + length += (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) * + LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER); totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES, &State->Delay); @@ -412,14 +420,6 @@ static ALboolean AllocLines(ALuint frequency, ALreverbState *State) totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples, frequency, 0, &State->Early.Delay[index]); - // The decorrelator line is calculated from the lowest reverb density (a - // parameter value of 1). This must include space for storing a loop update - // to feed the late reverb. - length = (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) * - LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER); - totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES, - &State->Decorrelator); - // The late all-pass lines. for(index = 0;index < 4;index++) totalSamples += CalcLineLength(ALLPASS_LINE_LENGTH[index], totalSamples, @@ -454,7 +454,6 @@ static ALboolean AllocLines(ALuint frequency, ALreverbState *State) // Update all delays to reflect the new sample buffer. RealizeLineOffset(State->SampleBuffer, &State->Delay); - RealizeLineOffset(State->SampleBuffer, &State->Decorrelator); for(index = 0;index < 4;index++) { RealizeLineOffset(State->SampleBuffer, &State->Early.Delay[index]); @@ -689,7 +688,7 @@ static ALvoid UpdateDecorrelator(ALfloat density, ALuint frequency, ALreverbStat { length = (DECO_FRACTION * powf(DECO_MULTIPLIER, (ALfloat)index)) * LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER)); - State->DecoTap[index] = fastf2u(length * frequency); + State->DecoTap[index] = fastf2u(length * frequency) + State->DelayTap[1]; } } @@ -1015,12 +1014,12 @@ static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay, frequency, State); - // Update the early lines. - UpdateEarlyLines(props->Reverb.LateReverbDelay, State); - // Update the decorrelator. UpdateDecorrelator(props->Reverb.Density, frequency, State); + // Update the early lines. + UpdateEarlyLines(props->Reverb.LateReverbDelay, State); + // Get the mixing matrix coefficients (x and y). CalcMatrixCoeffs(props->Reverb.Diffusion, &x, &y); // Then divide x into y to simplify the matrix calculation. @@ -1130,7 +1129,7 @@ static void EAXModulation(ALreverbState *State, ALuint offset, ALfloat*restrict // Given some input sample, this function produces four-channel outputs for the // early reflections. -static inline ALvoid EarlyReflection(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[4]) +static inline ALvoid EarlyReflection(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) { ALfloat d[4], v, f[4]; ALuint i; @@ -1175,10 +1174,10 @@ static inline ALvoid EarlyReflection(ALreverbState *State, ALuint todo, ALfloat /* Output the results of the junction for all four channels with a * constant attenuation of 0.5. */ - out[i][0] = f[0] * 0.5f; - out[i][1] = f[1] * 0.5f; - out[i][2] = f[2] * 0.5f; - out[i][3] = f[3] * 0.5f; + out[0][i] = f[0] * 0.5f; + out[1][i] = f[1] * 0.5f; + out[2][i] = f[2] * 0.5f; + out[3][i] = f[3] * 0.5f; } } @@ -1216,123 +1215,129 @@ static inline ALfloat LateLowPassInOut(ALreverbState *State, ALuint index, ALflo // Given four decorrelated input samples, this function produces four-channel // output for the late reverb. -static inline ALvoid LateReverb(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[4]) +static inline ALvoid LateReverb(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) { ALfloat d[4], f[4]; - ALuint i; + ALuint offset; + ALuint base, i; - // Feed the decorrelator from the energy-attenuated output of the second - // delay tap. - for(i = 0;i < todo;i++) + offset = State->Offset; + for(base = 0;base < todo;) { - ALuint offset = State->Offset+i; - ALfloat sample = DelayLineOut(&State->Delay, offset - State->DelayTap[1]) * - State->Late.DensityGain; - DelayLineIn(&State->Decorrelator, offset, sample); - } + ALfloat tmp[MAX_UPDATE_SAMPLES/4][4]; + ALuint tmp_todo = minu(todo, MAX_UPDATE_SAMPLES/4); - for(i = 0;i < todo;i++) - { - ALuint offset = State->Offset+i; + for(i = 0;i < tmp_todo;i++) + { + /* Obtain four decorrelated input samples. */ + f[0] = DelayLineOut(&State->Delay, offset-State->DelayTap[1]) * State->Late.DensityGain; + f[1] = DelayLineOut(&State->Delay, offset-State->DecoTap[0]) * State->Late.DensityGain; + f[2] = DelayLineOut(&State->Delay, offset-State->DecoTap[1]) * State->Late.DensityGain; + f[3] = DelayLineOut(&State->Delay, offset-State->DecoTap[2]) * State->Late.DensityGain; + + /* Add the decayed results of the cyclical delay lines, then pass + * the results through the low-pass filters. + */ + f[0] += DelayLineOut(&State->Late.Delay[0], offset-State->Late.Offset[0]) * State->Late.Coeff[0]; + f[1] += DelayLineOut(&State->Late.Delay[1], offset-State->Late.Offset[1]) * State->Late.Coeff[1]; + f[2] += DelayLineOut(&State->Late.Delay[2], offset-State->Late.Offset[2]) * State->Late.Coeff[2]; + f[3] += DelayLineOut(&State->Late.Delay[3], offset-State->Late.Offset[3]) * State->Late.Coeff[3]; + + /* 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, f[2]); + d[1] = LateLowPassInOut(State, 0, f[0]); + d[2] = LateLowPassInOut(State, 3, f[3]); + d[3] = LateLowPassInOut(State, 1, f[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, offset, 0, d[0]); + d[1] = LateAllPassInOut(State, offset, 1, d[1]); + d[2] = LateAllPassInOut(State, offset, 2, d[2]); + d[3] = LateAllPassInOut(State, offset, 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] )); + + /* Re-feed the cyclical delay lines. */ + DelayLineIn(&State->Late.Delay[0], offset, f[0]); + DelayLineIn(&State->Late.Delay[1], offset, f[1]); + DelayLineIn(&State->Late.Delay[2], offset, f[2]); + DelayLineIn(&State->Late.Delay[3], offset, f[3]); + offset++; + + /* Output the results of the matrix for all four channels, + * attenuated by the late reverb gain (which is attenuated by the + * 'x' mix coefficient). + */ + tmp[i][0] = State->Late.Gain * f[0]; + tmp[i][1] = State->Late.Gain * f[1]; + tmp[i][2] = State->Late.Gain * f[2]; + tmp[i][3] = State->Late.Gain * f[3]; + } - /* Obtain four decorrelated input samples. */ - f[0] = DelayLineOut(&State->Decorrelator, offset); - f[1] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[0]); - f[2] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[1]); - f[3] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[2]); + /* Deinterlace to output */ + for(i = 0;i < tmp_todo;i++) out[0][base+i] = tmp[i][0]; + for(i = 0;i < tmp_todo;i++) out[1][base+i] = tmp[i][1]; + for(i = 0;i < tmp_todo;i++) out[2][base+i] = tmp[i][2]; + for(i = 0;i < tmp_todo;i++) out[3][base+i] = tmp[i][3]; - /* Add the decayed results of the cyclical delay lines, then pass the - * results through the low-pass filters. - */ - f[0] += DelayLineOut(&State->Late.Delay[0], offset-State->Late.Offset[0]) * State->Late.Coeff[0]; - f[1] += DelayLineOut(&State->Late.Delay[1], offset-State->Late.Offset[1]) * State->Late.Coeff[1]; - f[2] += DelayLineOut(&State->Late.Delay[2], offset-State->Late.Offset[2]) * State->Late.Coeff[2]; - f[3] += DelayLineOut(&State->Late.Delay[3], offset-State->Late.Offset[3]) * State->Late.Coeff[3]; - - // 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, f[2]); - d[1] = LateLowPassInOut(State, 0, f[0]); - d[2] = LateLowPassInOut(State, 3, f[3]); - d[3] = LateLowPassInOut(State, 1, f[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, offset, 0, d[0]); - d[1] = LateAllPassInOut(State, offset, 1, d[1]); - d[2] = LateAllPassInOut(State, offset, 2, d[2]); - d[3] = LateAllPassInOut(State, offset, 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[i][0] = State->Late.Gain * f[0]; - out[i][1] = State->Late.Gain * f[1]; - out[i][2] = State->Late.Gain * f[2]; - out[i][3] = State->Late.Gain * f[3]; - - // Re-feed the cyclical delay lines. - DelayLineIn(&State->Late.Delay[0], offset, f[0]); - DelayLineIn(&State->Late.Delay[1], offset, f[1]); - DelayLineIn(&State->Late.Delay[2], offset, f[2]); - DelayLineIn(&State->Late.Delay[3], offset, f[3]); + base += tmp_todo; } } // Given an input sample, this function mixes echo into the four-channel late // reverb. -static inline ALvoid EAXEcho(ALreverbState *State, ALuint todo, ALfloat (*restrict late)[4]) +static inline ALvoid EAXEcho(ALreverbState *State, ALuint todo, ALfloat (*restrict late)[MAX_UPDATE_SAMPLES]) { - ALfloat out, feed; + ALfloat out[MAX_UPDATE_SAMPLES]; + ALfloat feed; + ALuint offset; ALuint i; + offset = State->Offset; for(i = 0;i < todo;i++) { - ALuint offset = State->Offset+i; - // Get the latest attenuated echo sample for output. feed = DelayLineOut(&State->Echo.Delay, offset-State->Echo.Offset) * State->Echo.Coeff; - // Mix the output into the late reverb channels. - out = State->Echo.MixCoeff * feed; - late[i][0] += out; - late[i][1] += out; - late[i][2] += out; - late[i][3] += out; + // Write the output. + out[i] = State->Echo.MixCoeff * feed; // Mix the energy-attenuated input with the output and pass it through // the echo low-pass filter. @@ -1348,19 +1353,26 @@ static inline ALvoid EAXEcho(ALreverbState *State, ALuint todo, ALfloat (*restri // Feed the delay with the mixed and filtered sample. DelayLineIn(&State->Echo.Delay, offset, feed); + offset++; } + + // Mix the output into the late reverb channels. + for(i = 0;i < todo;i++) late[0][i] += out[i]; + for(i = 0;i < todo;i++) late[1][i] += out[i]; + for(i = 0;i < todo;i++) late[2][i] += out[i]; + for(i = 0;i < todo;i++) late[3][i] += out[i]; } // Perform the non-EAX reverb pass on a given input sample, resulting in // four-channel output. -static inline ALvoid VerbPass(ALreverbState *State, ALuint todo, const ALfloat *input, ALfloat (*restrict early)[4], ALfloat (*restrict late)[4]) +static inline ALvoid VerbPass(ALreverbState *State, ALuint todo, const ALfloat *input, ALfloat (*restrict early)[MAX_UPDATE_SAMPLES], ALfloat (*restrict late)[MAX_UPDATE_SAMPLES]) { ALuint i; // Low-pass filter the incoming samples (use the early buffer as temp storage). ALfilterState_process(&State->LpFilter, &early[0][0], input, todo); for(i = 0;i < todo;i++) - DelayLineIn(&State->Delay, State->Offset+i, early[i>>2][i&3]); + DelayLineIn(&State->Delay, State->Offset+i, early[0][i]); // Calculate the early reflection from the first delay tap. EarlyReflection(State, todo, early); @@ -1374,25 +1386,19 @@ static inline ALvoid VerbPass(ALreverbState *State, ALuint todo, const ALfloat * // Perform the EAX reverb pass on a given input sample, resulting in four- // channel output. -static inline ALvoid EAXVerbPass(ALreverbState *State, ALuint todo, const ALfloat *input, ALfloat (*restrict early)[4], ALfloat (*restrict late)[4]) +static inline ALvoid EAXVerbPass(ALreverbState *State, ALuint todo, const ALfloat *input, ALfloat (*restrict early)[MAX_UPDATE_SAMPLES], ALfloat (*restrict late)[MAX_UPDATE_SAMPLES]) { ALuint i; /* Perform any modulation on the input (use the early buffer as temp storage). */ EAXModulation(State, State->Offset, &early[0][0], input, todo); /* Band-pass the incoming samples */ - ALfilterState_process(&State->LpFilter, - &early[MAX_UPDATE_SAMPLES/4][0], &early[0][0], todo - ); - ALfilterState_process(&State->HpFilter, - &early[MAX_UPDATE_SAMPLES*2/4][0], &early[MAX_UPDATE_SAMPLES/4][0], todo - ); + ALfilterState_process(&State->LpFilter, &early[1][0], &early[0][0], todo); + ALfilterState_process(&State->HpFilter, &early[2][0], &early[1][0], todo); // Feed the initial delay line. for(i = 0;i < todo;i++) - DelayLineIn(&State->Delay, State->Offset+i, - early[(MAX_UPDATE_SAMPLES*2/4)+(i>>2)][i&3] - ); + DelayLineIn(&State->Delay, State->Offset+i, early[2][i]); // Calculate the early reflection from the first delay tap. EarlyReflection(State, todo, early); @@ -1407,107 +1413,117 @@ static inline ALvoid EAXVerbPass(ALreverbState *State, ALuint todo, const ALfloa State->Offset += todo; } +static void DoMix(const ALfloat *restrict src, ALfloat (*dst)[BUFFERSIZE], ALuint num_chans, + const ALfloat *restrict target_gains, ALfloat *restrict current_gains, + ALfloat delta, ALuint offset, ALuint total_rem, ALuint todo) +{ + MixGains gains[MAX_OUTPUT_CHANNELS]; + ALuint c; + + for(c = 0;c < num_chans;c++) + { + ALfloat diff; + gains[c].Target = target_gains[c]; + gains[c].Current = current_gains[c]; + diff = gains[c].Target - gains[c].Current; + if(fabsf(diff) >= GAIN_SILENCE_THRESHOLD) + gains[c].Step = diff * delta; + else + { + gains[c].Current = gains[c].Target; + gains[c].Step = 0.0f; + } + } + + MixSamples(src, num_chans, dst, gains, total_rem, offset, todo); + + for(c = 0;c < num_chans;c++) + current_gains[c] = gains[c].Current; +} + static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { - ALfloat (*restrict early)[4] = State->EarlySamples; - ALfloat (*restrict late)[4] = State->ReverbSamples; - ALuint index, c, i, l; - ALfloat gain; + ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples; + ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples; + ALuint base, c; /* Process reverb for these samples. */ - for(index = 0;index < SamplesToDo;) + for(base = 0;base < SamplesToDo;) { - ALuint todo = minu(SamplesToDo-index, MAX_UPDATE_SAMPLES); + const ALfloat delta = 1.0f / (ALfloat)(SamplesToDo-base); + ALuint todo = minu(SamplesToDo-base, MAX_UPDATE_SAMPLES); - VerbPass(State, todo, &SamplesIn[index], early, late); + VerbPass(State, todo, &SamplesIn[base], early, late); - for(l = 0;l < 4;l++) + for(c = 0;c < 4;c++) + { + DoMix(early[c], SamplesOut, NumChannels, State->Early.PanGain[c], + State->Early.CurrentGain[c], delta, base, SamplesToDo-base, todo + ); + if(State->ExtraChannels > 0) + DoMix(early[c], State->ExtraOut, State->ExtraChannels, + State->Early.PanGain[c]+NumChannels, + State->Early.CurrentGain[c]+NumChannels, delta, base, + SamplesToDo-base, todo + ); + } + for(c = 0;c < 4;c++) { - for(c = 0;c < NumChannels;c++) - { - gain = State->Early.PanGain[l][c]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < todo;i++) - SamplesOut[c][index+i] += gain*early[i][l]; - } - gain = State->Late.PanGain[l][c]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < todo;i++) - SamplesOut[c][index+i] += gain*late[i][l]; - } - } - for(c = 0;c < State->ExtraChannels;c++) - { - gain = State->Early.PanGain[l][NumChannels+c]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < todo;i++) - State->ExtraOut[c][index+i] += gain*early[i][l]; - } - gain = State->Late.PanGain[l][NumChannels+c]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < todo;i++) - State->ExtraOut[c][index+i] += gain*late[i][l]; - } - } + DoMix(late[c], SamplesOut, NumChannels, State->Late.PanGain[c], + State->Late.CurrentGain[c], delta, base, SamplesToDo, todo + ); + if(State->ExtraChannels > 0) + DoMix(late[c], State->ExtraOut, State->ExtraChannels, + State->Late.PanGain[c]+NumChannels, + State->Late.CurrentGain[c]+NumChannels, delta, base, + SamplesToDo-base, todo + ); } - index += todo; + base += todo; } } static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { - ALfloat (*restrict early)[4] = State->EarlySamples; - ALfloat (*restrict late)[4] = State->ReverbSamples; - ALuint index, c, i, l; - ALfloat gain; + ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples; + ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples; + ALuint base, c; /* Process reverb for these samples. */ - for(index = 0;index < SamplesToDo;) + for(base = 0;base < SamplesToDo;) { - ALuint todo = minu(SamplesToDo-index, MAX_UPDATE_SAMPLES); + const ALfloat delta = 1.0f / (ALfloat)(SamplesToDo-base); + ALuint todo = minu(SamplesToDo-base, MAX_UPDATE_SAMPLES); - EAXVerbPass(State, todo, &SamplesIn[index], early, late); + EAXVerbPass(State, todo, &SamplesIn[base], early, late); - for(l = 0;l < 4;l++) + for(c = 0;c < 4;c++) { - for(c = 0;c < NumChannels;c++) - { - gain = State->Early.PanGain[l][c]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < todo;i++) - SamplesOut[c][index+i] += gain*early[i][l]; - } - gain = State->Late.PanGain[l][c]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < todo;i++) - SamplesOut[c][index+i] += gain*late[i][l]; - } - } - for(c = 0;c < State->ExtraChannels;c++) - { - gain = State->Early.PanGain[l][NumChannels+c]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < todo;i++) - State->ExtraOut[c][index+i] += gain*early[i][l]; - } - gain = State->Late.PanGain[l][NumChannels+c]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < todo;i++) - State->ExtraOut[c][index+i] += gain*late[i][l]; - } - } + DoMix(early[c], SamplesOut, NumChannels, State->Early.PanGain[c], + State->Early.CurrentGain[c], delta, base, SamplesToDo-base, todo + ); + if(State->ExtraChannels > 0) + DoMix(early[c], State->ExtraOut, State->ExtraChannels, + State->Early.PanGain[c]+NumChannels, + State->Early.CurrentGain[c]+NumChannels, delta, base, + SamplesToDo-base, todo + ); + } + for(c = 0;c < 4;c++) + { + DoMix(late[c], SamplesOut, NumChannels, State->Late.PanGain[c], + State->Late.CurrentGain[c], delta, base, SamplesToDo, todo + ); + if(State->ExtraChannels > 0) + DoMix(late[c], State->ExtraOut, State->ExtraChannels, + State->Late.PanGain[c]+NumChannels, + State->Late.CurrentGain[c]+NumChannels, delta, base, + SamplesToDo-base, todo + ); } - index += todo; + base += todo; } } @@ -1528,6 +1544,8 @@ static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(f { ALreverbState *state; + alcall_once(&mixfunc_inited, init_mixfunc); + NEW_OBJ0(state, ALreverbState)(); if(!state) return NULL; |