aboutsummaryrefslogtreecommitdiffstats
path: root/alc/effects/chorus.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'alc/effects/chorus.cpp')
-rw-r--r--alc/effects/chorus.cpp117
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