Apply reverb modulation to the late feedback lines

This seems to be more in-line with the intended behavior, to allow build-up and
overlap within the reverb decay, rather than a pitch-shift on input.
Unfortunately there's no readily available implementation of this reverb model
that includes modulation to compare with, so a low depth coefficient is used to
keep it very subtle.
This commit is contained in:
Chris Robinson
2017-05-12 20:53:14 -07:00
parent d456c799fd
commit 87d4710bc4
+71 -104
View File
@@ -89,22 +89,6 @@ typedef struct ALreverbState {
ALfilterState Hp; /* EAX only */
} Filter[4];
struct {
/* Modulator delay lines. */
DelayLine Delay[4];
/* The vibrato time is tracked with an index over a modulus-wrapped
* range (in samples).
*/
ALuint Index;
ALuint Range;
/* The depth of frequency change (also in samples) and its filter. */
ALfloat Depth;
ALfloat Coeff;
ALfloat Filter;
} Mod; /* EAX only */
/* Core delay line (early reflections and late reverb tap from this). */
DelayLine Delay;
@@ -142,6 +126,19 @@ typedef struct ALreverbState {
ALfloat PanGain[4][MAX_OUTPUT_CHANNELS];
} Early;
struct {
/* The vibrato time is tracked with an index over a modulus-wrapped
* range (in samples).
*/
ALuint Index;
ALuint Range;
/* The depth of frequency change (also in samples) and its filter. */
ALfloat Depth;
ALfloat Coeff;
ALfloat Filter;
} Mod; /* EAX only */
struct {
/* Attenuation to compensate for the modal density and decay rate of
* the late lines.
@@ -207,17 +204,8 @@ static void ALreverbState_Construct(ALreverbState *state)
{
ALfilterState_clear(&state->Filter[i].Lp);
ALfilterState_clear(&state->Filter[i].Hp);
state->Mod.Delay[i].Mask = 0;
state->Mod.Delay[i].Line = NULL;
}
state->Mod.Index = 0;
state->Mod.Range = 1;
state->Mod.Depth = 0.0f;
state->Mod.Coeff = 0.0f;
state->Mod.Filter = 0.0f;
state->Delay.Mask = 0;
state->Delay.Line = NULL;
@@ -253,6 +241,12 @@ static void ALreverbState_Construct(ALreverbState *state)
state->Early.Coeff[i] = 0.0f;
}
state->Mod.Index = 0;
state->Mod.Range = 1;
state->Mod.Depth = 0.0f;
state->Mod.Coeff = 0.0f;
state->Mod.Filter = 0.0f;
state->Late.DensityGain = 0.0f;
for(i = 0;i < 4;i++)
@@ -337,13 +331,12 @@ ALfloat ReverbBoost = 1.0f;
*/
ALboolean EmulateEAXReverb = AL_FALSE;
/* This coefficient is used to define the maximum frequency range controlled
* by the modulation depth. The current value of 0.025 will allow it to
* swing from 0.975x to 1.025x. This value must be below 1. At 1 it will
* cause the sampler to stall on the downswing, and above 1 it will cause it
* to sample backwards.
/* This coefficient is used to define the sinus depth according to the
* modulation depth property. This value must be below 1, which would cause the
* sampler to stall on the downswing, and above 1 it will cause it to sample
* backwards.
*/
static const ALfloat MODULATION_DEPTH_COEFF = 0.025f;
static const ALfloat MODULATION_DEPTH_COEFF = 1.0f / 2048.0f;
/* A filter is used to avoid the terrible distortion caused by changing
* modulation time and/or depth. To be consistent across different sample
@@ -540,16 +533,6 @@ static ALboolean AllocLines(const ALuint frequency, ALreverbState *State)
*/
totalSamples = 0;
/* 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);
for(i = 0;i < 4;i++)
totalSamples += CalcLineLength(length, totalSamples, frequency, 1,
&State->Mod.Delay[i]);
/* The main delay length includes the maximum early reflection delay, the
* largest early tap width, the maximum late reverb delay, and the
* largest late tap width. Finally, it must also be extended by the
@@ -591,11 +574,15 @@ static ALboolean AllocLines(const ALuint frequency, ALreverbState *State)
}
/* The late delay lines are calculated from the larger of the maximum
* density line length or the maximum echo time.
* density line length or the maximum echo time, and includes the maximum
* modulation-related delay. The modulator's delay is calculated from the
* maximum modulation time and depth coefficient, and halved for the low-
* to-high frequency swing.
*/
for(i = 0;i < 4;i++)
{
length = maxf(AL_EAXREVERB_MAX_ECHO_TIME, LATE_LINE_LENGTHS[i] * multiplier);
length = maxf(AL_EAXREVERB_MAX_ECHO_TIME, LATE_LINE_LENGTHS[i]*multiplier) +
AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f;
totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
&State->Late.Delay[i]);
}
@@ -617,8 +604,6 @@ static ALboolean AllocLines(const ALuint frequency, ALreverbState *State)
RealizeLineOffset(State->SampleBuffer, &State->Delay);
for(i = 0;i < 4;i++)
{
RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay[i]);
RealizeLineOffset(State->SampleBuffer, &State->Early.Ap[i].Delay);
RealizeLineOffset(State->SampleBuffer, &State->Early.Delay[i]);
@@ -1069,7 +1054,8 @@ static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime
* kind of vibrato is additive and not multiplicative as one may expect. The
* downswing will sound stronger than the upswing.
*/
static ALvoid UpdateModulator(const ALfloat modTime, const ALfloat modDepth, const ALuint frequency, ALreverbState *State)
static ALvoid UpdateModulator(const ALfloat modTime, const ALfloat modDepth,
const ALuint frequency, ALreverbState *State)
{
ALuint range;
@@ -1372,10 +1358,6 @@ static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device
State->Filter[i].Hp.a2 = State->Filter[0].Hp.a2;
}
/* Update the modulator line. */
UpdateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth,
frequency, State);
/* Update the main effect delay and associated taps. */
UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
props->Reverb.Density, props->Reverb.DecayTime, frequency,
@@ -1405,6 +1387,10 @@ static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device
hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
/* Update the modulator line. */
UpdateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth,
frequency, State);
/* Update the late lines. */
UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
lfDecayTime, props->Reverb.DecayTime, hfDecayTime,
@@ -1441,7 +1427,7 @@ static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device
**************************************/
/* Basic delay line input/output routines. */
static inline ALfloat DelayLineOut(DelayLine *Delay, const ALsizei offset)
static inline ALfloat DelayLineOut(const DelayLine *Delay, const ALsizei offset)
{
return Delay->Line[offset&Delay->Mask];
}
@@ -1449,7 +1435,7 @@ static inline ALfloat DelayLineOut(DelayLine *Delay, const ALsizei offset)
/* Cross-faded delay line output routine. Instead of interpolating the
* offsets, this interpolates (cross-fades) the outputs at each offset.
*/
static inline ALfloat FadedDelayLineOut(DelayLine *Delay, const ALsizei off0, const ALsizei off1, const ALfloat mu)
static inline ALfloat FadedDelayLineOut(const DelayLine *Delay, const ALsizei off0, const ALsizei off1, const ALfloat mu)
{
return lerp(Delay->Line[off0&Delay->Mask], Delay->Line[off1&Delay->Mask], mu);
}
@@ -1461,12 +1447,6 @@ static inline ALvoid DelayLineIn(DelayLine *Delay, const ALsizei offset, const A
Delay->Line[offset&Delay->Mask] = in;
}
static inline ALfloat DelayLineInOut(DelayLine *Delay, const ALsizei offset, const ALsizei outoffset, const ALfloat in)
{
Delay->Line[offset&Delay->Mask] = in;
return Delay->Line[(offset-outoffset)&Delay->Mask];
}
static void CalcModulationDelays(ALreverbState *State, ALfloat *restrict delays, const ALsizei todo)
{
ALfloat sinus, range;
@@ -1498,37 +1478,6 @@ static void CalcModulationDelays(ALreverbState *State, ALfloat *restrict delays,
State->Mod.Filter = range;
}
/* Given some input samples, this function produces modulation for the late
* reverb.
*/
static void EAXModulation(DelayLine *ModDelay, ALsizei offset, const ALfloat *restrict delays, ALfloat*restrict dst, const ALfloat*restrict src, const ALsizei todo)
{
ALfloat frac, fdelay;
ALfloat out0, out1;
ALsizei delay, i;
for(i = 0;i < todo;i++)
{
/* Separate the integer offset and fraction between it and the next
* sample.
*/
frac = modff(delays[i], &fdelay);
delay = fastf2u(fdelay);
/* Add the incoming sample to the delay line, and get the two samples
* crossed by the offset delay.
*/
out0 = DelayLineInOut(ModDelay, offset, delay, src[i]);
out1 = DelayLineOut(ModDelay, offset - delay - 1);
offset++;
/* The output is obtained by linearly interpolating the two samples
* that were acquired above.
*/
dst[i] = lerp(out0, out1, frac);
}
}
/* Applies a scattering matrix to the 4-line (vector) input. This is used
* for both the below vector all-pass model and to perform modal feed-back
* delay network (FDN) mixing.
@@ -1742,10 +1691,15 @@ static ALvoid LateReverb_##T(ALreverbState *State, const ALsizei todo, \
const ALfloat apFeedCoeff = State->ApFeedCoeff; \
const ALfloat mixX = State->MixX; \
const ALfloat mixY = State->MixY; \
ALfloat fdelay, frac; \
ALsizei delay; \
ALsizei offset; \
ALsizei i, j; \
ALfloat f[4]; \
\
/* Calculations modulation delays, uing the output as temp storage. */ \
CalcModulationDelays(State, &out[0][0], todo); \
\
offset = State->Offset; \
for(i = 0;i < todo;i++) \
{ \
@@ -1755,10 +1709,29 @@ static ALvoid LateReverb_##T(ALreverbState *State, const ALsizei todo, \
(offset-State->LateDelayTap[j][1])*4 + j, fade \
) * State->Late.DensityGain; \
\
/* Separate the integer offset and fraction between it and the next \
* sample. \
*/ \
frac = modff(out[0][i], &fdelay); \
delay = offset - fastf2i(fdelay); \
\
for(j = 0;j < 4;j++) \
f[j] += DELAY_OUT_##T(&State->Late.Delay[j], \
offset-State->Late.Offset[j][0], \
offset-State->Late.Offset[j][1], fade); \
{ \
ALfloat out0, out1; \
\
/* Get the two samples crossed by the offset delay. */ \
out0 = DELAY_OUT_##T(&State->Late.Delay[j], \
delay-State->Late.Offset[j][0], \
delay-State->Late.Offset[j][1], fade); \
out1 = DELAY_OUT_##T(&State->Late.Delay[j], \
delay-State->Late.Offset[j][0]-1, \
delay-State->Late.Offset[j][1]-1, fade); \
\
/* The modulated result is obtained by linearly interpolating the \
* two samples that were acquired above. \
*/ \
f[j] += lerp(out0, out1, frac); \
} \
\
for(j = 0;j < 4;j++) \
f[j] = LateT60Filter(j, f[j], State); \
@@ -1844,23 +1817,17 @@ static ALfloat EAXVerbPass(ALreverbState *State, const ALsizei todo, ALfloat fad
{
ALsizei i, c;
/* Perform any modulation on the input (use the early and late buffers as
* temp storage).
*/
CalcModulationDelays(State, &late[0][0], todo);
for(c = 0;c < 4;c++)
{
/* Apply modulation. */
EAXModulation(&State->Mod.Delay[c], State->Offset, &late[0][0],
&early[0][0], input[c], todo);
/* Band-pass the incoming samples. */
ALfilterState_process(&State->Filter[c].Lp, &early[1][0], &early[0][0], todo);
ALfilterState_process(&State->Filter[c].Hp, &early[2][0], &early[1][0], todo);
/* Band-pass the incoming samples. Use the early output lines for temp
* storage.
*/
ALfilterState_process(&State->Filter[c].Lp, early[0], input[c], todo);
ALfilterState_process(&State->Filter[c].Hp, early[1], early[0], todo);
/* Feed the initial delay line. */
for(i = 0;i < todo;i++)
DelayLineIn(&State->Delay, (State->Offset+i)*4 + c, early[2][i]);
DelayLineIn(&State->Delay, (State->Offset+i)*4 + c, early[1][i]);
}
if(fade < 1.0f)