aboutsummaryrefslogtreecommitdiffstats
path: root/alc/effects
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2023-02-08 17:22:20 -0800
committerChris Robinson <[email protected]>2023-02-08 17:22:20 -0800
commite181b3b75381b1e81a1b0a4129b3ce72fe87fc0d (patch)
tree6c10b90360358c8bab94597780995231c46718d5 /alc/effects
parent996a742b736f6f0738aaed18ddb3a20b288eaed3 (diff)
Use better panning positions for the chorus effect
Also avoid putting larger buffers on the stack.
Diffstat (limited to 'alc/effects')
-rw-r--r--alc/effects/chorus.cpp117
1 files changed, 61 insertions, 56 deletions
diff --git a/alc/effects/chorus.cpp b/alc/effects/chorus.cpp
index 44d8c32a..4997a448 100644
--- a/alc/effects/chorus.cpp
+++ b/alc/effects/chorus.cpp
@@ -48,10 +48,8 @@ namespace {
using uint = unsigned int;
-#define MAX_UPDATE_SAMPLES 256
-
struct ChorusState final : public EffectState {
- al::vector<float,16> mSampleBuffer;
+ al::vector<float,16> mDelayBuffer;
uint mOffset{0};
uint mLfoOffset{0};
@@ -59,7 +57,13 @@ struct ChorusState final : public EffectState {
float mLfoScale{0.0f};
uint mLfoDisp{0};
- /* Gains for left and right sides */
+ /* Calculated delays to apply to the left and right outputs. */
+ uint mModDelays[2][BufferLineSize];
+
+ /* Temp storage for the modulated left and right outputs. */
+ alignas(16) float mBuffer[2][BufferLineSize];
+
+ /* Gains for left and right outputs. */
struct {
float Current[MaxAmbiChannels]{};
float Target[MaxAmbiChannels]{};
@@ -71,8 +75,8 @@ struct ChorusState final : public EffectState {
float mDepth{0.0f};
float mFeedback{0.0f};
- void getTriangleDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo);
- void getSinusoidDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo);
+ void calcTriangleDelays(const size_t todo);
+ void calcSinusoidDelays(const size_t todo);
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
@@ -89,10 +93,10 @@ void ChorusState::deviceUpdate(const DeviceBase *Device, const Buffer&)
const auto frequency = static_cast<float>(Device->Frequency);
const size_t maxlen{NextPowerOf2(float2uint(max_delay*2.0f*frequency) + 1u)};
- if(maxlen != mSampleBuffer.size())
- al::vector<float,16>(maxlen).swap(mSampleBuffer);
+ if(maxlen != mDelayBuffer.size())
+ decltype(mDelayBuffer)(maxlen).swap(mDelayBuffer);
- std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f);
+ std::fill(mDelayBuffer.begin(), mDelayBuffer.end(), 0.0f);
for(auto &e : mGains)
{
std::fill(std::begin(e.Current), std::end(e.Current), 0.0f);
@@ -120,8 +124,13 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot,
mFeedback = props->Chorus.Feedback;
/* Gains for left and right sides */
- static constexpr auto lcoeffs = CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f});
- static constexpr auto rcoeffs = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f});
+ static constexpr auto inv_sqrt2 = static_cast<float>(1.0 / al::numbers::sqrt2);
+ static constexpr auto lcoeffs_pw = CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f});
+ static constexpr auto rcoeffs_pw = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f});
+ static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs({-inv_sqrt2, 0.0f, inv_sqrt2});
+ static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs({ inv_sqrt2, 0.0f, inv_sqrt2});
+ auto &lcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? lcoeffs_nrml : lcoeffs_pw;
+ auto &rcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? rcoeffs_nrml : rcoeffs_pw;
mOutTarget = target.Main->Buffer;
ComputePanGains(target.Main, lcoeffs.data(), Slot->Gain, mGains[0].Target);
@@ -162,7 +171,7 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot,
}
-void ChorusState::getTriangleDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo)
+void ChorusState::calcTriangleDelays(const size_t todo)
{
const uint lfo_range{mLfoRange};
const float lfo_scale{mLfoScale};
@@ -183,7 +192,7 @@ void ChorusState::getTriangleDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const si
{
size_t rem{minz(todo-i, lfo_range-offset)};
do {
- delays[0][i++] = gen_lfo(offset++);
+ mModDelays[0][i++] = gen_lfo(offset++);
} while(--rem);
if(offset == lfo_range)
offset = 0;
@@ -194,7 +203,7 @@ void ChorusState::getTriangleDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const si
{
size_t rem{minz(todo-i, lfo_range-offset)};
do {
- delays[1][i++] = gen_lfo(offset++);
+ mModDelays[1][i++] = gen_lfo(offset++);
} while(--rem);
if(offset == lfo_range)
offset = 0;
@@ -203,7 +212,7 @@ void ChorusState::getTriangleDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const si
mLfoOffset = static_cast<uint>(mLfoOffset+todo) % lfo_range;
}
-void ChorusState::getSinusoidDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo)
+void ChorusState::calcSinusoidDelays(const size_t todo)
{
const uint lfo_range{mLfoRange};
const float lfo_scale{mLfoScale};
@@ -224,7 +233,7 @@ void ChorusState::getSinusoidDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const si
{
size_t rem{minz(todo-i, lfo_range-offset)};
do {
- delays[0][i++] = gen_lfo(offset++);
+ mModDelays[0][i++] = gen_lfo(offset++);
} while(--rem);
if(offset == lfo_range)
offset = 0;
@@ -235,7 +244,7 @@ void ChorusState::getSinusoidDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const si
{
size_t rem{minz(todo-i, lfo_range-offset)};
do {
- delays[1][i++] = gen_lfo(offset++);
+ mModDelays[1][i++] = gen_lfo(offset++);
} while(--rem);
if(offset == lfo_range)
offset = 0;
@@ -246,52 +255,48 @@ void ChorusState::getSinusoidDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const si
void ChorusState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
{
- const size_t bufmask{mSampleBuffer.size()-1};
+ const size_t bufmask{mDelayBuffer.size()-1};
const float feedback{mFeedback};
const uint avgdelay{(static_cast<uint>(mDelay) + MixerFracHalf) >> MixerFracBits};
- float *RESTRICT delaybuf{mSampleBuffer.data()};
+ float *RESTRICT delaybuf{mDelayBuffer.data()};
uint offset{mOffset};
- for(size_t base{0u};base < samplesToDo;)
- {
- const size_t todo{minz(MAX_UPDATE_SAMPLES, samplesToDo-base)};
-
- uint moddelays[2][MAX_UPDATE_SAMPLES];
- if(mWaveform == ChorusWaveform::Sinusoid)
- getSinusoidDelays(moddelays, todo);
- else /*if(mWaveform == ChorusWaveform::Triangle)*/
- getTriangleDelays(moddelays, todo);
+ if(mWaveform == ChorusWaveform::Sinusoid)
+ calcSinusoidDelays(samplesToDo);
+ else /*if(mWaveform == ChorusWaveform::Triangle)*/
+ calcTriangleDelays(samplesToDo);
- alignas(16) float temps[2][MAX_UPDATE_SAMPLES];
- for(size_t i{0u};i < todo;++i)
- {
- // Feed the buffer's input first (necessary for delays < 1).
- delaybuf[offset&bufmask] = samplesIn[0][base+i];
-
- // Tap for the left output.
- uint delay{offset - (moddelays[0][i]>>MixerFracBits)};
- float mu{static_cast<float>(moddelays[0][i]&MixerFracMask) * (1.0f/MixerFracOne)};
- temps[0][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
- delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu);
-
- // Tap for the right output.
- delay = offset - (moddelays[1][i]>>MixerFracBits);
- mu = static_cast<float>(moddelays[1][i]&MixerFracMask) * (1.0f/MixerFracOne);
- temps[1][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
- delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu);
-
- // Accumulate feedback from the average delay of the taps.
- delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
- ++offset;
- }
-
- for(size_t c{0};c < 2;++c)
- MixSamples({temps[c], todo}, samplesOut, mGains[c].Current, mGains[c].Target,
- samplesToDo-base, base);
-
- base += todo;
+ const uint *RESTRICT ldelays{mModDelays[0]};
+ const uint *RESTRICT rdelays{mModDelays[1]};
+ float *RESTRICT lbuffer{al::assume_aligned<16>(mBuffer[0])};
+ float *RESTRICT rbuffer{al::assume_aligned<16>(mBuffer[1])};
+ for(size_t i{0u};i < samplesToDo;++i)
+ {
+ // Feed the buffer's input first (necessary for delays < 1).
+ delaybuf[offset&bufmask] = samplesIn[0][i];
+
+ // Tap for the left output.
+ uint delay{offset - (ldelays[i]>>MixerFracBits)};
+ float mu{static_cast<float>(ldelays[i]&MixerFracMask) * (1.0f/MixerFracOne)};
+ lbuffer[i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
+ delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu);
+
+ // Tap for the right output.
+ delay = offset - (rdelays[i]>>MixerFracBits);
+ mu = static_cast<float>(rdelays[i]&MixerFracMask) * (1.0f/MixerFracOne);
+ rbuffer[i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
+ delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu);
+
+ // Accumulate feedback from the average delay of the taps.
+ delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
+ ++offset;
}
+ MixSamples({lbuffer, samplesToDo}, samplesOut, mGains[0].Current, mGains[0].Target,
+ samplesToDo, 0);
+ MixSamples({rbuffer, samplesToDo}, samplesOut, mGains[1].Current, mGains[1].Target,
+ samplesToDo, 0);
+
mOffset = offset;
}