aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/effects/reverb.c
diff options
context:
space:
mode:
Diffstat (limited to 'Alc/effects/reverb.c')
-rw-r--r--Alc/effects/reverb.c480
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;