Do multiple samples at once in each reverb component
This commit is contained in:
parent
e472cfcc53
commit
8f8bf1f605
@ -32,6 +32,10 @@
|
||||
#include "alError.h"
|
||||
|
||||
|
||||
/* This is the maximum number of samples processed for each inner loop
|
||||
* iteration. */
|
||||
#define MAX_UPDATE_SAMPLES 64
|
||||
|
||||
typedef struct DelayLine
|
||||
{
|
||||
// The delay lines use sample lengths that are powers of 2 to allow the
|
||||
@ -234,10 +238,94 @@ 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)
|
||||
// Given an input sample, this function produces modulation for the late
|
||||
// reverb.
|
||||
static inline ALfloat EAXModulation(ALreverbState *State, ALuint offset, ALfloat in)
|
||||
{
|
||||
return coeff * Delay->Line[offset&Delay->Mask];
|
||||
ALfloat sinus, frac;
|
||||
ALfloat out0, out1;
|
||||
ALuint delay;
|
||||
|
||||
// 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 - cosf(F_TAU * State->Mod.Index / State->Mod.Range);
|
||||
|
||||
// Step the modulation index forward, keeping it bound to its range.
|
||||
State->Mod.Index = (State->Mod.Index + 1) % 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));
|
||||
delay = fastf2u(frac);
|
||||
frac -= delay;
|
||||
|
||||
// Get the two samples crossed by the offset, and feed the delay line
|
||||
// with the next input sample.
|
||||
out0 = DelayLineOut(&State->Mod.Delay, offset - delay);
|
||||
out1 = DelayLineOut(&State->Mod.Delay, offset - delay - 1);
|
||||
DelayLineIn(&State->Mod.Delay, offset, in);
|
||||
|
||||
// The output is obtained by linearly interpolating the two samples that
|
||||
// were acquired above.
|
||||
return lerp(out0, out1, frac);
|
||||
}
|
||||
|
||||
// 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])
|
||||
{
|
||||
ALfloat d[4], v, f[4];
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALuint offset = State->Offset+i;
|
||||
|
||||
// Obtain the decayed results of each early delay line.
|
||||
d[0] = DelayLineOut(&State->Early.Delay[0], offset-State->Early.Offset[0]) * State->Early.Coeff[0];
|
||||
d[1] = DelayLineOut(&State->Early.Delay[1], offset-State->Early.Offset[1]) * State->Early.Coeff[1];
|
||||
d[2] = DelayLineOut(&State->Early.Delay[2], offset-State->Early.Offset[2]) * State->Early.Coeff[2];
|
||||
d[3] = DelayLineOut(&State->Early.Delay[3], offset-State->Early.Offset[3]) * State->Early.Coeff[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 += DelayLineOut(&State->Delay, offset-State->DelayTap[0]);
|
||||
|
||||
// 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], offset, f[0]);
|
||||
DelayLineIn(&State->Early.Delay[1], offset, f[1]);
|
||||
DelayLineIn(&State->Early.Delay[2], offset, f[2]);
|
||||
DelayLineIn(&State->Early.Delay[3], offset, f[3]);
|
||||
|
||||
// Output the results of the junction for all four channels.
|
||||
out[i][0] += State->Early.Gain * f[0];
|
||||
out[i][1] += State->Early.Gain * f[1];
|
||||
out[i][2] += State->Early.Gain * f[2];
|
||||
out[i][3] += State->Early.Gain * f[3];
|
||||
}
|
||||
}
|
||||
|
||||
// Basic attenuated all-pass input/output routine.
|
||||
@ -255,115 +343,15 @@ static inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint in
|
||||
return (coeff * out) - feed;
|
||||
}
|
||||
|
||||
// Given an input sample, this function produces modulation for the late
|
||||
// reverb.
|
||||
static inline ALfloat EAXModulation(ALreverbState *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 - cosf(F_TAU * 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 = fastf2u(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(ALreverbState *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(ALreverbState *State, ALfloat in, ALfloat *restrict 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(ALreverbState *State, ALuint index, ALfloat in)
|
||||
static inline ALfloat LateAllPassInOut(ALreverbState *State, ALuint offset, ALuint index, ALfloat in)
|
||||
{
|
||||
return AllpassInOut(&State->Late.ApDelay[index],
|
||||
State->Offset - State->Late.ApOffset[index],
|
||||
State->Offset, in, State->Late.ApFeedCoeff,
|
||||
offset - State->Late.ApOffset[index],
|
||||
offset, in, State->Late.ApFeedCoeff,
|
||||
State->Late.ApCoeff[index]);
|
||||
}
|
||||
|
||||
// Delay line output routine for late reverb.
|
||||
static inline ALfloat LateDelayLineOut(ALreverbState *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(ALreverbState *State, ALuint index, ALfloat in)
|
||||
{
|
||||
@ -374,186 +362,204 @@ 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, const ALfloat *restrict in, ALfloat *restrict out)
|
||||
static inline ALvoid LateReverb(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[4])
|
||||
{
|
||||
ALfloat d[4], f[4];
|
||||
ALuint i;
|
||||
|
||||
// Obtain the decayed results of the cyclical delay lines, and add the
|
||||
// corresponding input channels. Then pass the results through the
|
||||
// low-pass filters.
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALuint offset = State->Offset+i;
|
||||
|
||||
// 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));
|
||||
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]);
|
||||
|
||||
// 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]);
|
||||
// Obtain the decayed results of the cyclical delay lines, and add the
|
||||
// corresponding input channels. 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];
|
||||
|
||||
/* 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] ));
|
||||
// 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]);
|
||||
|
||||
// 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];
|
||||
// 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]);
|
||||
|
||||
// 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]);
|
||||
/* 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).
|
||||
// Mix early reflections and late reverb.
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
// Given an input sample, this function mixes echo into the four-channel late
|
||||
// reverb.
|
||||
static inline ALvoid EAXEcho(ALreverbState *State, ALfloat in, ALfloat *restrict late)
|
||||
static inline ALvoid EAXEcho(ALreverbState *State, ALuint todo, ALfloat (*restrict late)[4])
|
||||
{
|
||||
ALfloat out, feed;
|
||||
ALuint i;
|
||||
|
||||
// Get the latest attenuated echo sample for output.
|
||||
feed = AttenuatedDelayLineOut(&State->Echo.Delay,
|
||||
State->Offset - State->Echo.Offset,
|
||||
State->Echo.Coeff);
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALuint offset = State->Offset+i;
|
||||
|
||||
// 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;
|
||||
// Get the latest attenuated echo sample for output.
|
||||
feed = DelayLineOut(&State->Echo.Delay, offset-State->Echo.Offset) *
|
||||
State->Echo.Coeff;
|
||||
|
||||
// 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;
|
||||
// Mix the output into the late reverb channels.
|
||||
out = State->Echo.MixCoeff[0] * feed;
|
||||
late[i][0] = (State->Echo.MixCoeff[1] * late[i][0]) + out;
|
||||
late[i][1] = (State->Echo.MixCoeff[1] * late[i][1]) + out;
|
||||
late[i][2] = (State->Echo.MixCoeff[1] * late[i][2]) + out;
|
||||
late[i][3] = (State->Echo.MixCoeff[1] * late[i][3]) + out;
|
||||
|
||||
// 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);
|
||||
// Mix the energy-attenuated input with the output and pass it through
|
||||
// the echo low-pass filter.
|
||||
feed += DelayLineOut(&State->Delay, offset-State->DelayTap[1]) *
|
||||
State->Echo.DensityGain;
|
||||
feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff);
|
||||
State->Echo.LpSample = feed;
|
||||
|
||||
// Feed the delay with the mixed and filtered sample.
|
||||
DelayLineIn(&State->Echo.Delay, State->Offset, feed);
|
||||
// Then the echo all-pass filter.
|
||||
feed = AllpassInOut(&State->Echo.ApDelay, offset-State->Echo.ApOffset,
|
||||
offset, feed, State->Echo.ApFeedCoeff,
|
||||
State->Echo.ApCoeff);
|
||||
|
||||
// Feed the delay with the mixed and filtered sample.
|
||||
DelayLineIn(&State->Echo.Delay, offset, feed);
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the non-EAX reverb pass on a given input sample, resulting in
|
||||
// four-channel output.
|
||||
static inline ALvoid VerbPass(ALreverbState *State, ALfloat in, ALfloat *restrict out)
|
||||
static inline ALvoid VerbPass(ALreverbState *State, ALuint todo, const ALfloat *in, ALfloat (*restrict out)[4])
|
||||
{
|
||||
ALfloat feed, late[4], taps[4];
|
||||
ALuint i;
|
||||
|
||||
// Filter the incoming sample.
|
||||
in = ALfilterState_processSingle(&State->LpFilter, in);
|
||||
|
||||
// Feed the initial delay line.
|
||||
DelayLineIn(&State->Delay, State->Offset, in);
|
||||
// Low-pass filter the incoming samples.
|
||||
for(i = 0;i < todo;i++)
|
||||
DelayLineIn(&State->Delay, State->Offset+i,
|
||||
ALfilterState_processSingle(&State->LpFilter, in[i])
|
||||
);
|
||||
|
||||
// Calculate the early reflection from the first delay tap.
|
||||
in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]);
|
||||
EarlyReflection(State, in, out);
|
||||
EarlyReflection(State, todo, out);
|
||||
|
||||
// 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);
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALuint offset = State->Offset+i;
|
||||
ALfloat sample = DelayLineOut(&State->Delay, offset - State->DelayTap[1]) *
|
||||
State->Late.DensityGain;
|
||||
DelayLineIn(&State->Decorrelator, offset, sample);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// Mix early reflections and late reverb.
|
||||
out[0] += late[0];
|
||||
out[1] += late[1];
|
||||
out[2] += late[2];
|
||||
out[3] += late[3];
|
||||
LateReverb(State, todo, out);
|
||||
|
||||
// Step all delays forward one sample.
|
||||
State->Offset++;
|
||||
State->Offset += todo;
|
||||
}
|
||||
|
||||
// Perform the EAX reverb pass on a given input sample, resulting in four-
|
||||
// channel output.
|
||||
static inline ALvoid EAXVerbPass(ALreverbState *State, ALfloat in, ALfloat *restrict early, ALfloat *restrict late)
|
||||
static inline ALvoid EAXVerbPass(ALreverbState *State, ALuint todo, const ALfloat *input, ALfloat (*restrict early)[4], ALfloat (*restrict late)[4])
|
||||
{
|
||||
ALfloat feed, taps[4];
|
||||
ALuint i;
|
||||
|
||||
// Low-pass filter the incoming sample.
|
||||
in = ALfilterState_processSingle(&State->LpFilter, in);
|
||||
in = ALfilterState_processSingle(&State->HpFilter, in);
|
||||
// Band-pass and modulate the incoming samples.
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat sample = input[i];
|
||||
sample = ALfilterState_processSingle(&State->LpFilter, sample);
|
||||
sample = ALfilterState_processSingle(&State->HpFilter, sample);
|
||||
|
||||
// Perform any modulation on the input.
|
||||
in = EAXModulation(State, in);
|
||||
// Perform any modulation on the input.
|
||||
sample = EAXModulation(State, State->Offset+i, sample);
|
||||
|
||||
// Feed the initial delay line.
|
||||
DelayLineIn(&State->Delay, State->Offset, in);
|
||||
// Feed the initial delay line.
|
||||
DelayLineIn(&State->Delay, State->Offset+i, sample);
|
||||
}
|
||||
|
||||
// Calculate the early reflection from the first delay tap.
|
||||
in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]);
|
||||
EarlyReflection(State, in, early);
|
||||
EarlyReflection(State, todo, 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);
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALuint offset = State->Offset+i;
|
||||
ALfloat sample = DelayLineOut(&State->Delay, offset - State->DelayTap[1]) *
|
||||
State->Late.DensityGain;
|
||||
DelayLineIn(&State->Decorrelator, offset, sample);
|
||||
}
|
||||
|
||||
// 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);
|
||||
LateReverb(State, todo, late);
|
||||
|
||||
// Calculate and mix in any echo.
|
||||
EAXEcho(State, in, late);
|
||||
EAXEcho(State, todo, late);
|
||||
|
||||
// Step all delays forward one sample.
|
||||
State->Offset++;
|
||||
// Step all delays forward.
|
||||
State->Offset += todo;
|
||||
}
|
||||
|
||||
static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
@ -561,9 +567,16 @@ static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint Samples
|
||||
ALfloat (*restrict out)[4] = State->ReverbSamples;
|
||||
ALuint index, c;
|
||||
|
||||
memset(out, 0, SamplesToDo*4*sizeof(ALfloat));
|
||||
|
||||
/* Process reverb for these samples. */
|
||||
for(index = 0;index < SamplesToDo;index++)
|
||||
VerbPass(State, SamplesIn[index], out[index]);
|
||||
for(index = 0;index < SamplesToDo;)
|
||||
{
|
||||
ALfloat todo = minu(SamplesToDo, MAX_UPDATE_SAMPLES);
|
||||
|
||||
VerbPass(State, todo, SamplesIn+index, out+index);
|
||||
index += todo;
|
||||
}
|
||||
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
@ -582,9 +595,17 @@ static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo,
|
||||
ALfloat (*restrict late)[4] = State->ReverbSamples;
|
||||
ALuint index, c;
|
||||
|
||||
memset(early, 0, SamplesToDo*4*sizeof(ALfloat));
|
||||
memset(late, 0, SamplesToDo*4*sizeof(ALfloat));
|
||||
|
||||
/* Process reverb for these samples. */
|
||||
for(index = 0;index < SamplesToDo;index++)
|
||||
EAXVerbPass(State, SamplesIn[index], early[index], late[index]);
|
||||
for(index = 0;index < SamplesToDo;)
|
||||
{
|
||||
ALfloat todo = minu(SamplesToDo, MAX_UPDATE_SAMPLES);
|
||||
|
||||
EAXVerbPass(State, todo, SamplesIn+index, early+index, late+index);
|
||||
index += todo;
|
||||
}
|
||||
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
@ -621,13 +642,13 @@ static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay)
|
||||
}
|
||||
|
||||
// Calculate the length of a delay line and store its mask and offset.
|
||||
static ALuint CalcLineLength(ALfloat length, ptrdiff_t offset, ALuint frequency, DelayLine *Delay)
|
||||
static ALuint CalcLineLength(ALfloat length, ptrdiff_t offset, ALuint frequency, ALuint extra, DelayLine *Delay)
|
||||
{
|
||||
ALuint samples;
|
||||
|
||||
// All line lengths are powers of 2, calculated from their lengths, with
|
||||
// an additional sample in case of rounding errors.
|
||||
samples = NextPowerOf2(fastf2u(length * frequency) + 1);
|
||||
samples = NextPowerOf2(fastf2u(length * frequency)+extra + 1);
|
||||
// All lines share a single sample buffer.
|
||||
Delay->Mask = samples - 1;
|
||||
Delay->Line = (ALfloat*)offset;
|
||||
@ -654,48 +675,49 @@ static ALboolean AllocLines(ALuint frequency, ALreverbState *State)
|
||||
* 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,
|
||||
length = (AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f);
|
||||
totalSamples += CalcLineLength(length, totalSamples, frequency, 1,
|
||||
&State->Mod.Delay);
|
||||
|
||||
// The initial delay is the sum of the reflections and late reverb
|
||||
// delays.
|
||||
// delays. This must include space for storing a loop update to feed the
|
||||
// early reflections, decorrelator, and echo.
|
||||
length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
|
||||
AL_EAXREVERB_MAX_LATE_REVERB_DELAY;
|
||||
totalSamples += CalcLineLength(length, totalSamples, frequency,
|
||||
&State->Delay);
|
||||
MAX_UPDATE_SAMPLES, &State->Delay);
|
||||
|
||||
// The early reflection lines.
|
||||
for(index = 0;index < 4;index++)
|
||||
totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples,
|
||||
frequency, &State->Early.Delay[index]);
|
||||
frequency, 0, &State->Early.Delay[index]);
|
||||
|
||||
// The decorrelator line is calculated from the lowest reverb density (a
|
||||
// parameter value of 1).
|
||||
// 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,
|
||||
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,
|
||||
frequency, &State->Late.ApDelay[index]);
|
||||
frequency, 0, &State->Late.ApDelay[index]);
|
||||
|
||||
// The late delay lines are calculated from the lowest reverb density.
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
length = LATE_LINE_LENGTH[index] * (1.0f + LATE_LINE_MULTIPLIER);
|
||||
totalSamples += CalcLineLength(length, totalSamples, frequency,
|
||||
totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
|
||||
&State->Late.Delay[index]);
|
||||
}
|
||||
|
||||
// The echo all-pass and delay lines.
|
||||
totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples,
|
||||
frequency, &State->Echo.ApDelay);
|
||||
frequency, 0, &State->Echo.ApDelay);
|
||||
totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples,
|
||||
frequency, &State->Echo.Delay);
|
||||
frequency, 0, &State->Echo.Delay);
|
||||
|
||||
if(totalSamples != State->TotalSamples)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user