aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2017-05-12 20:53:14 -0700
committerChris Robinson <[email protected]>2017-05-12 20:53:14 -0700
commit87d4710bc4f980663fa8ca67a9ef416aa56b2a93 (patch)
tree85c7117018044ffb3cfbed6168fe373be0498494
parentd456c799fdb844a3ef9e96a95084a7b68928247f (diff)
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.
-rw-r--r--Alc/effects/reverb.c175
1 files changed, 71 insertions, 104 deletions
diff --git a/Alc/effects/reverb.c b/Alc/effects/reverb.c
index 07a1b679..c1b0369b 100644
--- a/Alc/effects/reverb.c
+++ b/Alc/effects/reverb.c
@@ -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;
@@ -143,6 +127,19 @@ typedef struct ALreverbState {
} 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)