diff options
Diffstat (limited to 'alc/effects/chorus.cpp')
-rw-r--r-- | alc/effects/chorus.cpp | 117 |
1 files changed, 70 insertions, 47 deletions
diff --git a/alc/effects/chorus.cpp b/alc/effects/chorus.cpp index 9cbc922f..bc6ddaf0 100644 --- a/alc/effects/chorus.cpp +++ b/alc/effects/chorus.cpp @@ -48,7 +48,14 @@ namespace { using uint = unsigned int; -struct ChorusState final : public EffectState { +constexpr auto inv_sqrt2 = static_cast<float>(1.0 / al::numbers::sqrt2); +constexpr auto lcoeffs_pw = CalcDirectionCoeffs(std::array{-1.0f, 0.0f, 0.0f}); +constexpr auto rcoeffs_pw = CalcDirectionCoeffs(std::array{ 1.0f, 0.0f, 0.0f}); +constexpr auto lcoeffs_nrml = CalcDirectionCoeffs(std::array{-inv_sqrt2, 0.0f, inv_sqrt2}); +constexpr auto rcoeffs_nrml = CalcDirectionCoeffs(std::array{ inv_sqrt2, 0.0f, inv_sqrt2}); + + +struct ChorusState : public EffectState { std::vector<float> mDelayBuffer; uint mOffset{0}; @@ -58,16 +65,17 @@ struct ChorusState final : public EffectState { uint mLfoDisp{0}; /* Calculated delays to apply to the left and right outputs. */ - uint mModDelays[2][BufferLineSize]; + std::array<std::array<uint,BufferLineSize>,2> mModDelays{}; /* Temp storage for the modulated left and right outputs. */ - alignas(16) float mBuffer[2][BufferLineSize]; + alignas(16) std::array<FloatBufferLine,2> mBuffer{}; /* Gains for left and right outputs. */ - struct { - float Current[MaxAmbiChannels]{}; - float Target[MaxAmbiChannels]{}; - } mGains[2]; + struct OutGains { + std::array<float,MaxAmbiChannels> Current{}; + std::array<float,MaxAmbiChannels> Target{}; + }; + std::array<OutGains,2> mGains; /* effect parameters */ ChorusWaveform mWaveform{}; @@ -78,65 +86,81 @@ struct ChorusState final : public EffectState { void calcTriangleDelays(const size_t todo); void calcSinusoidDelays(const size_t todo); - void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; - void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, - const EffectTarget target) override; + void deviceUpdate(const DeviceBase *device, const float MaxDelay); + void update(const ContextBase *context, const EffectSlot *slot, const ChorusWaveform waveform, + const float delay, const float depth, const float feedback, const float rate, + int phase, const EffectTarget target); + + void deviceUpdate(const DeviceBase *device, const BufferStorage*) override + { deviceUpdate(device, ChorusMaxDelay); } + void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props_, + const EffectTarget target) override + { + auto &props = std::get<ChorusProps>(*props_); + update(context, slot, props.Waveform, props.Delay, props.Depth, props.Feedback, props.Rate, + props.Phase, target); + } void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, - const al::span<FloatBufferLine> samplesOut) override; + const al::span<FloatBufferLine> samplesOut) final; +}; - DEF_NEWDEL(ChorusState) +struct FlangerState final : public ChorusState { + void deviceUpdate(const DeviceBase *device, const BufferStorage*) final + { ChorusState::deviceUpdate(device, FlangerMaxDelay); } + void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props_, + const EffectTarget target) final + { + auto &props = std::get<FlangerProps>(*props_); + ChorusState::update(context, slot, props.Waveform, props.Delay, props.Depth, + props.Feedback, props.Rate, props.Phase, target); + } }; -void ChorusState::deviceUpdate(const DeviceBase *Device, const BufferStorage*) -{ - constexpr float max_delay{maxf(ChorusMaxDelay, FlangerMaxDelay)}; +void ChorusState::deviceUpdate(const DeviceBase *Device, const float MaxDelay) +{ const auto frequency = static_cast<float>(Device->Frequency); - const size_t maxlen{NextPowerOf2(float2uint(max_delay*2.0f*frequency) + 1u)}; + const size_t maxlen{NextPowerOf2(float2uint(MaxDelay*2.0f*frequency) + 1u)}; if(maxlen != mDelayBuffer.size()) decltype(mDelayBuffer)(maxlen).swap(mDelayBuffer); std::fill(mDelayBuffer.begin(), mDelayBuffer.end(), 0.0f); for(auto &e : mGains) { - std::fill(std::begin(e.Current), std::end(e.Current), 0.0f); - std::fill(std::begin(e.Target), std::end(e.Target), 0.0f); + e.Current.fill(0.0f); + e.Target.fill(0.0f); } } -void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, - const EffectProps *props, const EffectTarget target) +void ChorusState::update(const ContextBase *context, const EffectSlot *slot, + const ChorusWaveform waveform, const float delay, const float depth, const float feedback, + const float rate, int phase, const EffectTarget target) { - constexpr int mindelay{(MaxResamplerPadding>>1) << MixerFracBits}; + static constexpr int mindelay{(MaxResamplerPadding>>1) << MixerFracBits}; /* The LFO depth is scaled to be relative to the sample delay. Clamp the * delay and depth to allow enough padding for resampling. */ - const DeviceBase *device{Context->mDevice}; + const DeviceBase *device{context->mDevice}; const auto frequency = static_cast<float>(device->Frequency); - mWaveform = props->Chorus.Waveform; + mWaveform = waveform; - mDelay = maxi(float2int(props->Chorus.Delay*frequency*MixerFracOne + 0.5f), mindelay); - mDepth = minf(props->Chorus.Depth * static_cast<float>(mDelay), + mDelay = maxi(float2int(delay*frequency*MixerFracOne + 0.5f), mindelay); + mDepth = minf(depth * static_cast<float>(mDelay), static_cast<float>(mDelay - mindelay)); - mFeedback = props->Chorus.Feedback; + mFeedback = feedback; /* Gains for left and right sides */ - static constexpr auto inv_sqrt2 = static_cast<float>(1.0 / al::numbers::sqrt2); - static constexpr auto lcoeffs_pw = CalcDirectionCoeffs(std::array{-1.0f, 0.0f, 0.0f}); - static constexpr auto rcoeffs_pw = CalcDirectionCoeffs(std::array{ 1.0f, 0.0f, 0.0f}); - static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs(std::array{-inv_sqrt2, 0.0f, inv_sqrt2}); - static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs(std::array{ 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; + const bool ispairwise{device->mRenderMode == RenderMode::Pairwise}; + const auto lcoeffs = (!ispairwise) ? al::span{lcoeffs_nrml} : al::span{lcoeffs_pw}; + const auto rcoeffs = (!ispairwise) ? al::span{rcoeffs_nrml} : al::span{rcoeffs_pw}; mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, lcoeffs, Slot->Gain, mGains[0].Target); - ComputePanGains(target.Main, rcoeffs, Slot->Gain, mGains[1].Target); + ComputePanGains(target.Main, lcoeffs, slot->Gain, mGains[0].Target); + ComputePanGains(target.Main, rcoeffs, slot->Gain, mGains[1].Target); - float rate{props->Chorus.Rate}; if(!(rate > 0.0f)) { mLfoOffset = 0; @@ -149,7 +173,7 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, /* Calculate LFO coefficient (number of samples per cycle). Limit the * max range to avoid overflow when calculating the displacement. */ - uint lfo_range{float2uint(minf(frequency/rate + 0.5f, float{INT_MAX/360 - 180}))}; + const uint lfo_range{float2uint(minf(frequency/rate + 0.5f, float{INT_MAX/360 - 180}))}; mLfoOffset = mLfoOffset * lfo_range / mLfoRange; mLfoRange = lfo_range; @@ -164,7 +188,6 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, } /* Calculate lfo phase displacement */ - int phase{props->Chorus.Phase}; if(phase < 0) phase = 360 + phase; mLfoDisp = (mLfoRange*static_cast<uint>(phase) + 180) / 360; } @@ -266,10 +289,10 @@ void ChorusState::process(const size_t samplesToDo, const al::span<const FloatBu else /*if(mWaveform == ChorusWaveform::Triangle)*/ calcTriangleDelays(samplesToDo); - 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])}; + const uint *RESTRICT ldelays{mModDelays[0].data()}; + const uint *RESTRICT rdelays{mModDelays[1].data()}; + float *RESTRICT lbuffer{al::assume_aligned<16>(mBuffer[0].data())}; + float *RESTRICT rbuffer{al::assume_aligned<16>(mBuffer[1].data())}; for(size_t i{0u};i < samplesToDo;++i) { // Feed the buffer's input first (necessary for delays < 1). @@ -292,10 +315,10 @@ void ChorusState::process(const size_t samplesToDo, const al::span<const FloatBu ++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); + MixSamples({lbuffer, samplesToDo}, samplesOut, mGains[0].Current.data(), + mGains[0].Target.data(), samplesToDo, 0); + MixSamples({rbuffer, samplesToDo}, samplesOut, mGains[1].Current.data(), + mGains[1].Target.data(), samplesToDo, 0); mOffset = offset; } @@ -312,7 +335,7 @@ struct ChorusStateFactory final : public EffectStateFactory { */ struct FlangerStateFactory final : public EffectStateFactory { al::intrusive_ptr<EffectState> create() override - { return al::intrusive_ptr<EffectState>{new ChorusState{}}; } + { return al::intrusive_ptr<EffectState>{new FlangerState{}}; } }; } // namespace |