aboutsummaryrefslogtreecommitdiffstats
path: root/alc/effects
diff options
context:
space:
mode:
Diffstat (limited to 'alc/effects')
-rw-r--r--alc/effects/reverb.cpp210
1 files changed, 104 insertions, 106 deletions
diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp
index ce81dfbd..5d9dbf81 100644
--- a/alc/effects/reverb.cpp
+++ b/alc/effects/reverb.cpp
@@ -53,13 +53,6 @@ using namespace std::placeholders;
*/
constexpr size_t MAX_UPDATE_SAMPLES{256};
-/* The number of samples used for cross-faded delay lines. This can be used
- * to balance the compensation for abrupt line changes and attenuation due to
- * minimally lengthed recursive lines. Try to keep this below the device
- * update size.
- */
-constexpr size_t FADE_SAMPLES{128};
-
/* The number of spatialized lines or channels to process. Four channels allows
* for a 3D A-Format response. NOTE: This can't be changed without taking care
* of the conversion matrices, and a few places where the length arrays are
@@ -91,8 +84,6 @@ alignas(16) constexpr ALfloat A2B[NUM_LINES][NUM_LINES]{
};
-constexpr ALfloat FadeStep{1.0f / FADE_SAMPLES};
-
/* The all-pass and delay lines have a variable length dependent on the
* effect's density parameter, which helps alter the perceived environment
* size. The size-to-density conversion is a cubed scale:
@@ -277,7 +268,8 @@ struct VecAllpass {
size_t Offset[NUM_LINES][2]{};
void processFaded(const al::span<ReverbUpdateLine,NUM_LINES> samples, size_t offset,
- const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade, const size_t todo);
+ const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fadeCount, const ALfloat fadeStep,
+ const size_t todo);
void processUnfaded(const al::span<ReverbUpdateLine,NUM_LINES> samples, size_t offset,
const ALfloat xCoeff, const ALfloat yCoeff, const size_t todo);
};
@@ -390,8 +382,7 @@ struct ReverbState final : public EffectState {
LateReverb mLate;
- /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
- size_t mFadeCount{0};
+ bool mDoFading{};
/* Maximum number of samples to process at once. */
size_t mMaxUpdate[2]{MAX_UPDATE_SAMPLES, MAX_UPDATE_SAMPLES};
@@ -482,11 +473,13 @@ struct ReverbState final : public EffectState {
void update3DPanning(const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan,
const ALfloat earlyGain, const ALfloat lateGain, const EffectTarget &target);
- void earlyUnfaded(const size_t offset, const size_t todo, const size_t base);
- void earlyFaded(const size_t offset, const size_t todo, const ALfloat fade);
+ void earlyUnfaded(const size_t offset, const size_t todo);
+ void earlyFaded(const size_t offset, const size_t todo, const ALfloat fade,
+ const ALfloat fadeStep);
- void lateUnfaded(const size_t offset, const size_t todo, const size_t base);
- void lateFaded(const size_t offset, const size_t todo, const ALfloat fade);
+ void lateUnfaded(const size_t offset, const size_t todo);
+ void lateFaded(const size_t offset, const size_t todo, const ALfloat fade,
+ const ALfloat fadeStep);
ALboolean deviceUpdate(const ALCdevice *device) override;
void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
@@ -612,8 +605,8 @@ ALboolean ReverbState::deviceUpdate(const ALCdevice *device)
for(auto &gains : mLate.PanGain)
std::fill(std::begin(gains), std::end(gains), 0.0f);
- /* Reset counters and offset base. */
- mFadeCount = 0;
+ /* Reset fading and offset base. */
+ mDoFading = true;
std::fill(std::begin(mMaxUpdate), std::end(mMaxUpdate), MAX_UPDATE_SAMPLES);
mOffset = 0;
@@ -977,7 +970,7 @@ void ReverbState::update(const ALCcontext *Context, const ALeffectslot *Slot, co
* a master control for the feedback delays, so changes the offsets of many
* delay lines.
*/
- if(mParams.Density != props->Reverb.Density ||
+ mDoFading |= (mParams.Density != props->Reverb.Density ||
/* Diffusion and decay times influences the decay rate (gain) of the
* late reverb T60 filter.
*/
@@ -989,15 +982,17 @@ void ReverbState::update(const ALCcontext *Context, const ALeffectslot *Slot, co
* gain.
*/
mParams.HFReference != props->Reverb.HFReference ||
- mParams.LFReference != props->Reverb.LFReference)
- mFadeCount = 0;
- mParams.Density = props->Reverb.Density;
- mParams.Diffusion = props->Reverb.Diffusion;
- mParams.DecayTime = props->Reverb.DecayTime;
- mParams.HFDecayTime = hfDecayTime;
- mParams.LFDecayTime = lfDecayTime;
- mParams.HFReference = props->Reverb.HFReference;
- mParams.LFReference = props->Reverb.LFReference;
+ mParams.LFReference != props->Reverb.LFReference);
+ if(mDoFading)
+ {
+ mParams.Density = props->Reverb.Density;
+ mParams.Diffusion = props->Reverb.Diffusion;
+ mParams.DecayTime = props->Reverb.DecayTime;
+ mParams.HFDecayTime = hfDecayTime;
+ mParams.LFDecayTime = lfDecayTime;
+ mParams.HFReference = props->Reverb.HFReference;
+ mParams.LFReference = props->Reverb.LFReference;
+ }
}
@@ -1056,8 +1051,7 @@ inline auto VectorPartialScatter(const std::array<float,NUM_LINES> &RESTRICT in,
/* Utilizes the above, but reverses the input channels. */
void VectorScatterRevDelayIn(const DelayLineI delay, size_t offset, const ALfloat xCoeff,
- const ALfloat yCoeff, const size_t base, const al::span<const ReverbUpdateLine,NUM_LINES> in,
- const size_t count)
+ const ALfloat yCoeff, const al::span<const ReverbUpdateLine,NUM_LINES> in, const size_t count)
{
ASSUME(count > 0);
@@ -1068,7 +1062,7 @@ void VectorScatterRevDelayIn(const DelayLineI delay, size_t offset, const ALfloa
do {
std::array<float,NUM_LINES> f;
for(size_t j{0u};j < NUM_LINES;j++)
- f[NUM_LINES-1-j] = in[j][base+i];
+ f[NUM_LINES-1-j] = in[j][i];
++i;
delay.Line[offset++] = VectorPartialScatter(f, xCoeff, yCoeff);
@@ -1125,14 +1119,14 @@ void VecAllpass::processUnfaded(const al::span<ReverbUpdateLine,NUM_LINES> sampl
}
}
void VecAllpass::processFaded(const al::span<ReverbUpdateLine,NUM_LINES> samples, size_t offset,
- const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade, const size_t todo)
+ const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fadeCount, const ALfloat fadeStep,
+ const size_t todo)
{
const DelayLineI delay{Delay};
const ALfloat feedCoeff{Coeff};
ASSUME(todo > 0);
- fade *= 1.0f/FADE_SAMPLES;
size_t vap_offset[NUM_LINES][2];
for(size_t j{0u};j < NUM_LINES;j++)
{
@@ -1154,7 +1148,9 @@ void VecAllpass::processFaded(const al::span<ReverbUpdateLine,NUM_LINES> samples
size_t td{minz(delay.Mask+1 - maxoff, todo - i)};
do {
- fade += FadeStep;
+ fadeCount += 1.0f;
+ const float fade{fadeCount * fadeStep};
+
std::array<float,NUM_LINES> f;
for(size_t j{0u};j < NUM_LINES;j++)
f[j] = delay.Line[vap_offset[j][0]++][j]*(1.0f-fade) +
@@ -1194,7 +1190,7 @@ void VecAllpass::processFaded(const al::span<ReverbUpdateLine,NUM_LINES> samples
* Two static specializations are used for transitional (cross-faded) delay
* line processing and non-transitional processing.
*/
-void ReverbState::earlyUnfaded(const size_t offset, const size_t todo, const size_t base)
+void ReverbState::earlyUnfaded(const size_t offset, const size_t todo)
{
const al::span<ReverbUpdateLine,NUM_LINES> temps{mTempSamples};
const DelayLineI early_delay{mEarly.Delay};
@@ -1233,7 +1229,7 @@ void ReverbState::earlyUnfaded(const size_t offset, const size_t todo, const siz
{
size_t feedb_tap{offset - mEarly.Offset[j][0]};
const ALfloat feedb_coeff{mEarly.Coeff[j][0]};
- float *out = mEarlySamples[j].data() + base;
+ float *out = mEarlySamples[j].data();
for(size_t i{0u};i < todo;)
{
@@ -1253,9 +1249,10 @@ void ReverbState::earlyUnfaded(const size_t offset, const size_t todo, const siz
* bounce to improve the initial diffusion in the late reverb.
*/
const size_t late_feed_tap{offset - mLateFeedTap};
- VectorScatterRevDelayIn(main_delay, late_feed_tap, mixX, mixY, base, mEarlySamples, todo);
+ VectorScatterRevDelayIn(main_delay, late_feed_tap, mixX, mixY, mEarlySamples, todo);
}
-void ReverbState::earlyFaded(const size_t offset, const size_t todo, const ALfloat fade)
+void ReverbState::earlyFaded(const size_t offset, const size_t todo, const ALfloat fade,
+ const ALfloat fadeStep)
{
const al::span<ReverbUpdateLine,NUM_LINES> temps{mTempSamples};
const DelayLineI early_delay{mEarly.Delay};
@@ -1270,8 +1267,8 @@ void ReverbState::earlyFaded(const size_t offset, const size_t todo, const ALflo
size_t early_delay_tap0{offset - mEarlyDelayTap[j][0]};
size_t early_delay_tap1{offset - mEarlyDelayTap[j][1]};
const ALfloat oldCoeff{mEarlyDelayCoeff[j][0]};
- const ALfloat oldCoeffStep{-oldCoeff / FADE_SAMPLES};
- const ALfloat newCoeffStep{mEarlyDelayCoeff[j][1] / FADE_SAMPLES};
+ const ALfloat oldCoeffStep{-oldCoeff * fadeStep};
+ const ALfloat newCoeffStep{mEarlyDelayCoeff[j][1] * fadeStep};
ALfloat fadeCount{fade};
for(size_t i{0u};i < todo;)
@@ -1290,15 +1287,15 @@ void ReverbState::earlyFaded(const size_t offset, const size_t todo, const ALflo
}
}
- mEarly.VecAp.processFaded(temps, offset, mixX, mixY, fade, todo);
+ mEarly.VecAp.processFaded(temps, offset, mixX, mixY, fade, fadeStep, todo);
for(size_t j{0u};j < NUM_LINES;j++)
{
size_t feedb_tap0{offset - mEarly.Offset[j][0]};
size_t feedb_tap1{offset - mEarly.Offset[j][1]};
const ALfloat feedb_oldCoeff{mEarly.Coeff[j][0]};
- const ALfloat feedb_oldCoeffStep{-feedb_oldCoeff / FADE_SAMPLES};
- const ALfloat feedb_newCoeffStep{mEarly.Coeff[j][1] / FADE_SAMPLES};
+ const ALfloat feedb_oldCoeffStep{-feedb_oldCoeff * fadeStep};
+ const ALfloat feedb_newCoeffStep{mEarly.Coeff[j][1] * fadeStep};
float *out = mEarlySamples[j].data();
ALfloat fadeCount{fade};
@@ -1323,7 +1320,7 @@ void ReverbState::earlyFaded(const size_t offset, const size_t todo, const ALflo
early_delay.write(offset, NUM_LINES-1-j, temps[j].data(), todo);
const size_t late_feed_tap{offset - mLateFeedTap};
- VectorScatterRevDelayIn(main_delay, late_feed_tap, mixX, mixY, 0, mEarlySamples, todo);
+ VectorScatterRevDelayIn(main_delay, late_feed_tap, mixX, mixY, mEarlySamples, todo);
}
/* This generates the reverb tail using a modified feed-back delay network
@@ -1340,7 +1337,7 @@ void ReverbState::earlyFaded(const size_t offset, const size_t todo, const ALflo
* Two variations are made, one for for transitional (cross-faded) delay line
* processing and one for non-transitional processing.
*/
-void ReverbState::lateUnfaded(const size_t offset, const size_t todo, const size_t base)
+void ReverbState::lateUnfaded(const size_t offset, const size_t todo)
{
const al::span<ReverbUpdateLine,NUM_LINES> temps{mTempSamples};
const DelayLineI late_delay{mLate.Delay};
@@ -1379,12 +1376,13 @@ void ReverbState::lateUnfaded(const size_t offset, const size_t todo, const size
*/
mLate.VecAp.processUnfaded(temps, offset, mixX, mixY, todo);
for(size_t j{0u};j < NUM_LINES;j++)
- std::copy_n(temps[j].begin(), todo, mLateSamples[j].begin() + base);
+ std::copy_n(temps[j].begin(), todo, mLateSamples[j].begin());
/* Finally, scatter and bounce the results to refeed the feedback buffer. */
- VectorScatterRevDelayIn(late_delay, offset, mixX, mixY, 0, temps, todo);
+ VectorScatterRevDelayIn(late_delay, offset, mixX, mixY, temps, todo);
}
-void ReverbState::lateFaded(const size_t offset, const size_t todo, const ALfloat fade)
+void ReverbState::lateFaded(const size_t offset, const size_t todo, const ALfloat fade,
+ const ALfloat fadeStep)
{
const al::span<ReverbUpdateLine,NUM_LINES> temps{mTempSamples};
const DelayLineI late_delay{mLate.Delay};
@@ -1398,12 +1396,12 @@ void ReverbState::lateFaded(const size_t offset, const size_t todo, const ALfloa
{
const ALfloat oldMidGain{mLate.T60[j].MidGain[0]};
const ALfloat midGain{mLate.T60[j].MidGain[1]};
- const ALfloat oldMidStep{-oldMidGain / FADE_SAMPLES};
- const ALfloat midStep{midGain / FADE_SAMPLES};
+ const ALfloat oldMidStep{-oldMidGain * fadeStep};
+ const ALfloat midStep{midGain * fadeStep};
const ALfloat oldDensityGain{mLate.DensityGain[0] * oldMidGain};
const ALfloat densityGain{mLate.DensityGain[1] * midGain};
- const ALfloat oldDensityStep{-oldDensityGain / FADE_SAMPLES};
- const ALfloat densityStep{densityGain / FADE_SAMPLES};
+ const ALfloat oldDensityStep{-oldDensityGain * fadeStep};
+ const ALfloat densityStep{densityGain * fadeStep};
size_t late_delay_tap0{offset - mLateDelayTap[j][0]};
size_t late_delay_tap1{offset - mLateDelayTap[j][1]};
size_t late_feedb_tap0{offset - mLate.Offset[j][0]};
@@ -1435,17 +1433,16 @@ void ReverbState::lateFaded(const size_t offset, const size_t todo, const ALfloa
mLate.T60[j].process(temps[j].data(), todo);
}
- mLate.VecAp.processFaded(temps, offset, mixX, mixY, fade, todo);
+ mLate.VecAp.processFaded(temps, offset, mixX, mixY, fade, fadeStep, todo);
for(size_t j{0u};j < NUM_LINES;j++)
std::copy_n(temps[j].begin(), todo, mLateSamples[j].begin());
- VectorScatterRevDelayIn(late_delay, offset, mixX, mixY, 0, temps, todo);
+ VectorScatterRevDelayIn(late_delay, offset, mixX, mixY, temps, todo);
}
void ReverbState::process(const size_t samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
{
size_t offset{mOffset};
- size_t fadeCount{mFadeCount};
ASSUME(samplesToDo > 0);
@@ -1463,67 +1460,68 @@ void ReverbState::process(const size_t samplesToDo, const FloatBufferLine *RESTR
}
/* Process reverb for these samples. */
- for(size_t base{0};base < samplesToDo;)
+ if LIKELY(!mDoFading)
{
- /* Calculate the number of samples we can do this iteration. */
- size_t todo{minz(samplesToDo - base, minz(mMaxUpdate[0], mMaxUpdate[1]))};
- /* Some mixers require maintaining a 4-sample alignment, so ensure that
- * if it's not the last iteration.
- */
- if(base+todo < samplesToDo) todo &= ~3;
- ASSUME(todo > 0);
+ for(size_t base{0};base < samplesToDo;)
+ {
+ /* Calculate the number of samples we can do this iteration. */
+ size_t todo{minz(samplesToDo - base, mMaxUpdate[0])};
+ /* Some mixers require maintaining a 4-sample alignment, so ensure
+ * that if it's not the last iteration.
+ */
+ if(base+todo < samplesToDo) todo &= ~3;
+ ASSUME(todo > 0);
+
+ /* Generate non-faded early reflections and late reverb. */
+ earlyUnfaded(offset, todo);
+ lateUnfaded(offset, todo);
- /* Process the samples for reverb. */
- size_t samples_done{0u};
- if UNLIKELY(fadeCount < FADE_SAMPLES)
+ /* Finally, mix early reflections and late reverb. */
+ (this->*mMixOut)(samplesOut, samplesToDo-base, base, todo);
+
+ offset += todo;
+ base += todo;
+ }
+ }
+ else
+ {
+ const float fadeStep{1.0f / static_cast<float>(samplesToDo)};
+ for(size_t base{0};base < samplesToDo;)
{
- /* If cross-fading, don't do more samples than there are to fade. */
- const size_t tofade{minz(todo, FADE_SAMPLES-fadeCount)};
- auto fade = static_cast<ALfloat>(fadeCount);
+ size_t todo{minz(samplesToDo - base, minz(mMaxUpdate[0], mMaxUpdate[1]))};
+ if(base+todo < samplesToDo) todo &= ~3;
+ ASSUME(todo > 0);
/* Generate cross-faded early reflections and late reverb. */
- earlyFaded(offset, tofade, fade);
- lateFaded(offset, tofade, fade);
-
- /* Step forward by amount faded. */
- samples_done += tofade;
- offset += tofade;
- fadeCount += tofade;
- if(fadeCount == FADE_SAMPLES)
- {
- /* Update the cross-fading delay line taps. */
- for(size_t c{0u};c < NUM_LINES;c++)
- {
- mEarlyDelayTap[c][0] = mEarlyDelayTap[c][1];
- mEarlyDelayCoeff[c][0] = mEarlyDelayCoeff[c][1];
- mEarly.VecAp.Offset[c][0] = mEarly.VecAp.Offset[c][1];
- mEarly.Offset[c][0] = mEarly.Offset[c][1];
- mEarly.Coeff[c][0] = mEarly.Coeff[c][1];
- mLateDelayTap[c][0] = mLateDelayTap[c][1];
- mLate.VecAp.Offset[c][0] = mLate.VecAp.Offset[c][1];
- mLate.Offset[c][0] = mLate.Offset[c][1];
- mLate.T60[c].MidGain[0] = mLate.T60[c].MidGain[1];
- }
- mLate.DensityGain[0] = mLate.DensityGain[1];
- mMaxUpdate[0] = mMaxUpdate[1];
- }
+ auto fadeCount = static_cast<ALfloat>(base);
+ earlyFaded(offset, todo, fadeCount, fadeStep);
+ lateFaded(offset, todo, fadeCount, fadeStep);
+
+ /* Finally, mix early reflections and late reverb. */
+ (this->*mMixOut)(samplesOut, samplesToDo-base, base, todo);
+
+ offset += todo;
+ base += todo;
}
- if LIKELY(samples_done < todo)
+
+ /* Update the cross-fading delay line taps. */
+ for(size_t c{0u};c < NUM_LINES;c++)
{
- /* Generate non-faded early reflections and late reverb. */
- const size_t remaining{todo - samples_done};
- earlyUnfaded(offset, remaining, samples_done);
- lateUnfaded(offset, remaining, samples_done);
- offset += remaining;
+ mEarlyDelayTap[c][0] = mEarlyDelayTap[c][1];
+ mEarlyDelayCoeff[c][0] = mEarlyDelayCoeff[c][1];
+ mEarly.VecAp.Offset[c][0] = mEarly.VecAp.Offset[c][1];
+ mEarly.Offset[c][0] = mEarly.Offset[c][1];
+ mEarly.Coeff[c][0] = mEarly.Coeff[c][1];
+ mLateDelayTap[c][0] = mLateDelayTap[c][1];
+ mLate.VecAp.Offset[c][0] = mLate.VecAp.Offset[c][1];
+ mLate.Offset[c][0] = mLate.Offset[c][1];
+ mLate.T60[c].MidGain[0] = mLate.T60[c].MidGain[1];
}
-
- /* Finally, mix early reflections and late reverb. */
- (this->*mMixOut)(samplesOut, samplesToDo-base, base, todo);
-
- base += todo;
+ mLate.DensityGain[0] = mLate.DensityGain[1];
+ mMaxUpdate[0] = mMaxUpdate[1];
+ mDoFading = false;
}
mOffset = offset;
- mFadeCount = fadeCount;
}