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.c469
1 files changed, 310 insertions, 159 deletions
diff --git a/Alc/effects/reverb.c b/Alc/effects/reverb.c
index 7ef95cf3..a14b6926 100644
--- a/Alc/effects/reverb.c
+++ b/Alc/effects/reverb.c
@@ -1252,9 +1252,9 @@ static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei o
return Delay->Line[off0&Delay->Mask][c]*(1.0f-mu) +
Delay->Line[off1&Delay->Mask][c]*( mu);
}
-#define UnfadedDelayLineOut(d, o0, o1, c, mu) DelayLineOut(d, o0, c)
-static inline ALvoid DelayLineIn(DelayLineI *Delay, ALsizei offset, const ALsizei c,
+
+static inline ALvoid DelayLineIn(const DelayLineI *Delay, ALsizei offset, const ALsizei c,
const ALfloat *restrict in, ALsizei count)
{
ALsizei i;
@@ -1262,15 +1262,7 @@ static inline ALvoid DelayLineIn(DelayLineI *Delay, ALsizei offset, const ALsize
Delay->Line[(offset++)&Delay->Mask][c] = *(in++);
}
-static inline ALvoid DelayLineIn4(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
-{
- ALsizei i;
- offset &= Delay->Mask;
- for(i = 0;i < NUM_LINES;i++)
- Delay->Line[offset][i] = in[i];
-}
-
-static inline ALvoid DelayLineIn4Rev(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
+static inline ALvoid DelayLineIn4Rev(const DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
{
ALsizei i;
offset &= Delay->Mask;
@@ -1324,6 +1316,8 @@ static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *re
out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
}
+#define VectorScatterDelayIn(delay, o, in, xcoeff, ycoeff) \
+ VectorPartialScatter((delay)->Line[(o)&(delay)->Mask], in, xcoeff, ycoeff)
/* Same as above, but reverses the input. */
static inline void VectorPartialScatterRev(ALfloat *restrict out, const ALfloat *restrict in,
@@ -1334,6 +1328,8 @@ static inline void VectorPartialScatterRev(ALfloat *restrict out, const ALfloat
out[2] = xCoeff*in[1] + yCoeff*(in[0] + -in[2] + in[3]);
out[3] = xCoeff*in[0] + yCoeff*( -in[1] + -in[2] + -in[3]);
}
+#define VectorScatterRevDelayIn(delay, o, in, xcoeff, ycoeff) \
+ VectorPartialScatterRev((delay)->Line[(o)&(delay)->Mask], in, xcoeff, ycoeff)
/* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
* filter to the 4-line input.
@@ -1345,34 +1341,72 @@ static inline void VectorPartialScatterRev(ALfloat *restrict out, const ALfloat
* Two static specializations are used for transitional (cross-faded) delay
* line processing and non-transitional processing.
*/
-#define DECL_TEMPLATE(T) \
-static void VectorAllpass_##T(ALfloat *restrict out, \
- const ALfloat *restrict in, \
- const ALsizei offset, const ALfloat feedCoeff, \
- const ALfloat xCoeff, const ALfloat yCoeff, \
- const ALfloat mu, VecAllpass *Vap) \
-{ \
- ALfloat f[NUM_LINES], fs[NUM_LINES]; \
- ALfloat input; \
- ALsizei i; \
- \
- (void)mu; /* Ignore for Unfaded. */ \
- \
- for(i = 0;i < NUM_LINES;i++) \
- { \
- input = in[i]; \
- out[i] = T##DelayLineOut(&Vap->Delay, offset-Vap->Offset[i][0], \
- offset-Vap->Offset[i][1], i, mu) - \
- feedCoeff*input; \
- f[i] = input + feedCoeff*out[i]; \
- } \
- VectorPartialScatter(fs, f, xCoeff, yCoeff); \
- \
- DelayLineIn4(&Vap->Delay, offset, fs); \
+static void VectorAllpass_Unfaded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES],
+ ALsizei offset, const ALfloat feedCoeff,
+ const ALfloat xCoeff, const ALfloat yCoeff,
+ ALsizei todo, VecAllpass *Vap)
+{
+ const DelayLineI delay = Vap->Delay;
+ ALsizei vap_offset[NUM_LINES];
+ ALsizei i, j;
+
+ ASSUME(todo > 0);
+
+ for(j = 0;j < NUM_LINES;j++)
+ vap_offset[j] = offset-Vap->Offset[j][0];
+ for(i = 0;i < todo;i++)
+ {
+ ALfloat f[NUM_LINES];
+
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ ALfloat input = samples[j][i];
+ ALfloat out = DelayLineOut(&delay, vap_offset[j]++, j) - feedCoeff*input;
+ f[j] = input + feedCoeff*out;
+
+ samples[j][i] = out;
+ }
+
+ VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
+ ++offset;
+ }
+}
+static void VectorAllpass_Faded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES],
+ ALsizei offset, const ALfloat feedCoeff,
+ const ALfloat xCoeff, const ALfloat yCoeff,
+ ALfloat fade, ALsizei todo, VecAllpass *Vap)
+{
+ const DelayLineI delay = Vap->Delay;
+ ALsizei vap_offset[NUM_LINES][2];
+ ALsizei i, j;
+
+ ASSUME(todo > 0);
+
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ vap_offset[j][0] = offset-Vap->Offset[j][0];
+ vap_offset[j][1] = offset-Vap->Offset[j][1];
+ }
+ for(i = 0;i < todo;i++)
+ {
+ ALfloat f[NUM_LINES];
+
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ ALfloat input = samples[j][i];
+ ALfloat out =
+ FadedDelayLineOut(&delay, vap_offset[j][0]++, vap_offset[j][1]++, j, fade) -
+ feedCoeff*input;
+ f[j] = input + feedCoeff*out;
+
+ samples[j][i] = out;
+ }
+ fade += FadeStep;
+
+ VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
+ ++offset;
+ }
}
-DECL_TEMPLATE(Unfaded)
-DECL_TEMPLATE(Faded)
-#undef DECL_TEMPLATE
/* This generates early reflections.
*
@@ -1393,51 +1427,128 @@ DECL_TEMPLATE(Faded)
* Two static specializations are used for transitional (cross-faded) delay
* line processing and non-transitional processing.
*/
-#define DECL_TEMPLATE(T) \
-static void EarlyReflection_##T(ALreverbState *State, const ALsizei todo, \
- ALfloat fade, \
- ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) \
-{ \
- ALsizei offset = State->Offset; \
- const ALfloat apFeedCoeff = State->ApFeedCoeff; \
- const ALfloat mixX = State->MixX; \
- const ALfloat mixY = State->MixY; \
- ALfloat f[NUM_LINES], fr[NUM_LINES]; \
- ALsizei i, j; \
- \
- for(i = 0;i < todo;i++) \
- { \
- for(j = 0;j < NUM_LINES;j++) \
- fr[j] = T##DelayLineOut(&State->Delay, \
- offset-State->EarlyDelayTap[j][0], \
- offset-State->EarlyDelayTap[j][1], j, fade \
- ) * State->EarlyDelayCoeff[j]; \
- \
- VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \
- &State->Early.VecAp); \
- \
- DelayLineIn4Rev(&State->Early.Delay, offset, f); \
- \
- for(j = 0;j < NUM_LINES;j++) \
- f[j] += T##DelayLineOut(&State->Early.Delay, \
- offset-State->Early.Offset[j][0], \
- offset-State->Early.Offset[j][1], j, fade \
- ) * State->Early.Coeff[j]; \
- \
- for(j = 0;j < NUM_LINES;j++) \
- out[j][i] = f[j]; \
- \
- VectorPartialScatterRev(fr, f, mixX, mixY); \
- \
- DelayLineIn4(&State->Delay, offset-State->LateFeedTap, fr); \
- \
- offset++; \
- fade += FadeStep; \
- } \
+static void EarlyReflection_Unfaded(ALreverbState *State, const ALsizei todo,
+ ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
+{
+ ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
+ const DelayLineI early_delay = State->Early.Delay;
+ const DelayLineI main_delay = State->Delay;
+ ALsizei early_feedb_tap[NUM_LINES];
+ ALfloat early_feedb_coeff[NUM_LINES];
+ const ALfloat mixX = State->MixX;
+ const ALfloat mixY = State->MixY;
+ ALsizei offset = State->Offset;
+ ALsizei late_feed_tap;
+ ALsizei i, j;
+
+ ASSUME(todo > 0);
+
+ /* First, load decorrelated samples from the main delay line as the primary
+ * reflections.
+ */
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ ALsizei early_delay_tap = offset - State->EarlyDelayTap[j][0];
+ ALfloat coeff = State->EarlyDelayCoeff[j];
+ for(i = 0;i < todo;i++)
+ temps[j][i] = DelayLineOut(&main_delay, early_delay_tap++, j) * coeff;
+ }
+
+ /* Apply a vector all-pass, to help color the initial reflections based on
+ * the diffusion strength.
+ */
+ VectorAllpass_Unfaded(temps, offset, State->ApFeedCoeff, mixX, mixY, todo,
+ &State->Early.VecAp);
+
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ early_feedb_tap[j] = offset - State->Early.Offset[j][0];
+ early_feedb_coeff[j] = State->Early.Coeff[j];
+ }
+ late_feed_tap = offset - State->LateFeedTap;
+ for(i = 0;i < todo;i++)
+ {
+ ALfloat f[NUM_LINES];
+
+ for(j = 0;j < NUM_LINES;j++)
+ f[j] = temps[j][i];
+
+ /* Apply a delay and bounce to generate secondary reflections, combine
+ * with the primary reflections and write out the result for mixing.
+ */
+ DelayLineIn4Rev(&early_delay, offset, f);
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ f[j] += DelayLineOut(&early_delay, early_feedb_tap[j]++, j) * early_feedb_coeff[j];
+ out[j][i] = f[j];
+ }
+
+ /* Also write the result back to the main delay line for the late
+ * reverb stage to pick up at the appropriate time, appplying a scatter
+ * and bounce to improve the initial diffusion in the late reverb.
+ */
+ VectorScatterRevDelayIn(&main_delay, late_feed_tap++, f, mixX, mixY);
+ offset++;
+ }
+}
+static void EarlyReflection_Faded(ALreverbState *State, const ALsizei todo, ALfloat fade,
+ ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
+{
+ ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
+ const DelayLineI early_delay = State->Early.Delay;
+ const DelayLineI main_delay = State->Delay;
+ ALsizei early_feedb_tap[NUM_LINES][2];
+ ALfloat early_feedb_coeff[NUM_LINES];
+ const ALfloat mixX = State->MixX;
+ const ALfloat mixY = State->MixY;
+ ALsizei offset = State->Offset;
+ ALsizei late_feed_tap;
+ ALsizei i, j;
+
+ ASSUME(todo > 0);
+
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ ALsizei early_delay_tap0 = offset - State->EarlyDelayTap[j][0];
+ ALsizei early_delay_tap1 = offset - State->EarlyDelayTap[j][1];
+ ALfloat coeff = State->EarlyDelayCoeff[j];
+ for(i = 0;i < todo;i++)
+ temps[j][i] = FadedDelayLineOut(&main_delay,
+ early_delay_tap0++, early_delay_tap1++, j, fade
+ ) * coeff;
+ }
+
+ VectorAllpass_Faded(temps, offset, State->ApFeedCoeff, mixX, mixY, fade, todo,
+ &State->Early.VecAp);
+
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ early_feedb_tap[j][0] = offset - State->Early.Offset[j][0];
+ early_feedb_tap[j][1] = offset - State->Early.Offset[j][1];
+ early_feedb_coeff[j] = State->Early.Coeff[j];
+ }
+ late_feed_tap = offset - State->LateFeedTap;
+ for(i = 0;i < todo;i++)
+ {
+ ALfloat f[NUM_LINES];
+
+ for(j = 0;j < NUM_LINES;j++)
+ f[j] = temps[j][i];
+
+ DelayLineIn4Rev(&early_delay, offset, f);
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ f[j] += FadedDelayLineOut(&early_delay,
+ early_feedb_tap[j][0]++, early_feedb_tap[j][1]++, j, fade
+ ) * early_feedb_coeff[j];
+ out[j][i] = f[j];
+ }
+ fade += FadeStep;
+
+ VectorScatterRevDelayIn(&main_delay, late_feed_tap++, f, mixX, mixY);
+ offset++;
+ }
}
-DECL_TEMPLATE(Unfaded)
-DECL_TEMPLATE(Faded)
-#undef DECL_TEMPLATE
/* Applies the two T60 damping filter sections. */
static inline void LateT60Filter(ALfloat *restrict samples, const ALsizei todo, T60Filter *filter)
@@ -1452,6 +1563,8 @@ static inline void LateT60Filter(ALfloat *restrict samples, const ALsizei todo,
ALfloat lfz = filter->LFState;
ALsizei i;
+ ASSUME(todo > 0);
+
for(i = 0;i < todo;i++)
{
ALfloat in = samples[i];
@@ -1471,8 +1584,8 @@ static inline void LateT60Filter(ALfloat *restrict samples, const ALsizei todo,
/* This generates the reverb tail using a modified feed-back delay network
* (FDN).
*
- * Results from the early reflections are attenuated by the density gain and
- * mixed with the output from the late delay lines.
+ * Results from the early reflections are mixed with the output from the late
+ * delay lines.
*
* The late response is then completed by T60 and all-pass filtering the mix.
*
@@ -1482,61 +1595,99 @@ static inline void LateT60Filter(ALfloat *restrict samples, const ALsizei todo,
* Two variations are made, one for for transitional (cross-faded) delay line
* processing and one for non-transitional processing.
*/
-#define DECL_TEMPLATE(T) \
-static void LateReverb_##T(ALreverbState *State, const ALsizei todo, \
- ALfloat fade, \
- ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) \
-{ \
- ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples; \
- const ALfloat apFeedCoeff = State->ApFeedCoeff; \
- const ALfloat mixX = State->MixX; \
- const ALfloat mixY = State->MixY; \
- ALsizei offset; \
- ALsizei i, j; \
- \
- for(j = 0;j < NUM_LINES;j++) \
- { \
- ALfloat fader = fade; \
- offset = State->Offset; \
- for(i = 0;i < todo;i++) \
- { \
- temps[j][i] = T##DelayLineOut(&State->Delay, \
- offset - State->LateDelayTap[j][0], \
- offset - State->LateDelayTap[j][1], j, fader \
- ) + T##DelayLineOut(&State->Late.Delay, \
- offset - State->Late.Offset[j][0], \
- offset - State->Late.Offset[j][1], j, fader \
- ); \
- ++offset; \
- fader += FadeStep; \
- } \
- LateT60Filter(temps[j], todo, &State->Late.T60[j]); \
- } \
- \
- offset = State->Offset; \
- for(i = 0;i < todo;i++) \
- { \
- ALfloat f[NUM_LINES], fr[NUM_LINES]; \
- for(j = 0;j < NUM_LINES;j++) \
- fr[j] = temps[j][i]; \
- \
- VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \
- &State->Late.VecAp); \
- \
- for(j = 0;j < NUM_LINES;j++) \
- out[j][i] = f[j]; \
- \
- VectorPartialScatterRev(fr, f, mixX, mixY); \
- \
- DelayLineIn4(&State->Late.Delay, offset, fr); \
- \
- offset++; \
- fade += FadeStep; \
- } \
+static void LateReverb_Unfaded(ALreverbState *State, const ALsizei todo,
+ ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
+{
+ ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
+ const DelayLineI late_delay = State->Late.Delay;
+ const DelayLineI main_delay = State->Delay;
+ const ALfloat mixX = State->MixX;
+ const ALfloat mixY = State->MixY;
+ ALsizei offset = State->Offset;
+ ALsizei i, j;
+
+ ASSUME(todo > 0);
+
+ /* First, load decorrelated samples from the main and feedback delay lines.
+ * Filter the signal to apply its frequency-dependent decay.
+ */
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ ALsizei late_delay_tap = offset - State->LateDelayTap[j][0];
+ ALsizei late_feedb_tap = offset - State->Late.Offset[j][0];
+ for(i = 0;i < todo;i++)
+ temps[j][i] = DelayLineOut(&main_delay, late_delay_tap++, j) +
+ DelayLineOut(&late_delay, late_feedb_tap++, j);
+ LateT60Filter(temps[j], todo, &State->Late.T60[j]);
+ }
+
+ /* Apply a vector all-pass to improve micro-surface diffusion, and write
+ * out the results for mixing.
+ */
+ VectorAllpass_Unfaded(temps, offset, State->ApFeedCoeff, mixX, mixY, todo, &State->Late.VecAp);
+
+ for(j = 0;j < NUM_LINES;j++)
+ memcpy(out[j], temps[j], todo*sizeof(ALfloat));
+
+ for(i = 0;i < todo;i++)
+ {
+ ALfloat f[NUM_LINES];
+ for(j = 0;j < NUM_LINES;j++)
+ f[j] = temps[j][i];
+
+ /* Finally, scatter and bounce the results to refeed the feedback
+ * buffer.
+ */
+ VectorScatterRevDelayIn(&late_delay, offset, f, mixX, mixY);
+ offset++;
+ }
+}
+static void LateReverb_Faded(ALreverbState *State, const ALsizei todo, ALfloat fade,
+ ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
+{
+ ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
+ const DelayLineI late_delay = State->Late.Delay;
+ const DelayLineI main_delay = State->Delay;
+ const ALfloat mixX = State->MixX;
+ const ALfloat mixY = State->MixY;
+ ALsizei offset = State->Offset;
+ ALsizei i, j;
+
+ ASSUME(todo > 0);
+
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ ALsizei late_delay_tap0 = offset - State->LateDelayTap[j][0];
+ ALsizei late_delay_tap1 = offset - State->LateDelayTap[j][1];
+ ALsizei late_feedb_tap0 = offset - State->Late.Offset[j][0];
+ ALsizei late_feedb_tap1 = offset - State->Late.Offset[j][1];
+ ALfloat fader = fade;
+ for(i = 0;i < todo;i++)
+ {
+ temps[j][i] =
+ FadedDelayLineOut(&main_delay, late_delay_tap0++, late_delay_tap1++, j, fader) +
+ FadedDelayLineOut(&late_delay, late_feedb_tap0++, late_feedb_tap1++, j, fader);
+ fader += FadeStep;
+ }
+ LateT60Filter(temps[j], todo, &State->Late.T60[j]);
+ }
+
+ VectorAllpass_Faded(temps, offset, State->ApFeedCoeff, mixX, mixY, fade, todo,
+ &State->Late.VecAp);
+
+ for(j = 0;j < NUM_LINES;j++)
+ memcpy(out[j], temps[j], todo*sizeof(ALfloat));
+
+ for(i = 0;i < todo;i++)
+ {
+ ALfloat f[NUM_LINES];
+ for(j = 0;j < NUM_LINES;j++)
+ f[j] = temps[j][i];
+
+ VectorScatterRevDelayIn(&late_delay, offset, f, mixX, mixY);
+ offset++;
+ }
}
-DECL_TEMPLATE(Unfaded)
-DECL_TEMPLATE(Faded)
-#undef DECL_TEMPLATE
static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
@@ -1572,10 +1723,10 @@ static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, c
DelayLineIn(&State->Delay, State->Offset, c, samples[1], todo);
}
- if(LIKELY(fadeCount >= FADE_SAMPLES))
+ if(UNLIKELY(fadeCount < FADE_SAMPLES))
{
- /* Generate and mix early reflections. */
- EarlyReflection_Unfaded(State, todo, fade, samples);
+ /* Generate early reflections. */
+ EarlyReflection_Faded(State, todo, fade, samples);
/* Mix the A-Format results to output, implicitly converting back
* to B-Format.
*/
@@ -1586,24 +1737,6 @@ static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, c
);
/* Generate and mix late reverb. */
- LateReverb_Unfaded(State, todo, fade, samples);
- for(c = 0;c < NUM_LINES;c++)
- MixSamples(samples[c], NumChannels, SamplesOut,
- State->Late.CurrentGain[c], State->Late.PanGain[c],
- SamplesToDo-base, base, todo
- );
- }
- else
- {
- /* Generate early reflections. */
- EarlyReflection_Faded(State, todo, fade, samples);
- for(c = 0;c < NUM_LINES;c++)
- MixSamples(samples[c], NumChannels, SamplesOut,
- State->Early.CurrentGain[c], State->Early.PanGain[c],
- SamplesToDo-base, base, todo
- );
-
- /* Generate and mix late reverb. */
LateReverb_Faded(State, todo, fade, samples);
for(c = 0;c < NUM_LINES;c++)
MixSamples(samples[c], NumChannels, SamplesOut,
@@ -1630,6 +1763,24 @@ static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, c
}
}
}
+ else
+ {
+ /* Generate and mix early reflections. */
+ EarlyReflection_Unfaded(State, todo, samples);
+ for(c = 0;c < NUM_LINES;c++)
+ MixSamples(samples[c], NumChannels, SamplesOut,
+ State->Early.CurrentGain[c], State->Early.PanGain[c],
+ SamplesToDo-base, base, todo
+ );
+
+ /* Generate and mix late reverb. */
+ LateReverb_Unfaded(State, todo, samples);
+ for(c = 0;c < NUM_LINES;c++)
+ MixSamples(samples[c], NumChannels, SamplesOut,
+ State->Late.CurrentGain[c], State->Late.PanGain[c],
+ SamplesToDo-base, base, todo
+ );
+ }
/* Step all delays forward. */
State->Offset += todo;