From 078b50e0ed6a0b89aa2e1f84bbbebbf24b885a4e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 4 Jan 2023 22:02:29 -0800 Subject: Simplify handling effect output for spatial effects Effects are given a 3D ambisonic buffer of the same order as the device, for processing surround sound. Effects that pass input channels to matching output channels as it processes them don't need to mix each input channel to all output channels. At most, an input channel may mix to a different output channel, if the target buffer uses a different channel layout, and need a gain adjustment, if it uses a different scaling. With a 2D output buffer, a number of channels can be skipped altogether. --- alc/effects/equalizer.cpp | 56 ++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 22 deletions(-) (limited to 'alc/effects/equalizer.cpp') diff --git a/alc/effects/equalizer.cpp b/alc/effects/equalizer.cpp index 4bce34a2..de067911 100644 --- a/alc/effects/equalizer.cpp +++ b/alc/effects/equalizer.cpp @@ -87,12 +87,14 @@ namespace { struct EqualizerState final : public EffectState { struct { + uint mTargetChannel{INVALID_CHANNEL_INDEX}; + /* Effect parameters */ - BiquadFilter filter[4]; + BiquadFilter mFilter[4]; /* Effect gains for each channel */ - float CurrentGains[MaxAmbiChannels]{}; - float TargetGains[MaxAmbiChannels]{}; + float mCurrentGain{}; + float mTargetGain{}; } mChans[MaxAmbiChannels]; alignas(16) FloatBufferLine mSampleBuffer{}; @@ -111,8 +113,10 @@ void EqualizerState::deviceUpdate(const DeviceBase*, const Buffer&) { for(auto &e : mChans) { - std::for_each(std::begin(e.filter), std::end(e.filter), std::mem_fn(&BiquadFilter::clear)); - std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f); + e.mTargetChannel = INVALID_CHANNEL_INDEX; + std::for_each(std::begin(e.mFilter), std::end(e.mFilter), + std::mem_fn(&BiquadFilter::clear)); + e.mCurrentGain = 0.0f; } } @@ -131,48 +135,56 @@ void EqualizerState::update(const ContextBase *context, const EffectSlot *slot, */ gain = std::sqrt(props->Equalizer.LowGain); f0norm = props->Equalizer.LowCutoff / frequency; - mChans[0].filter[0].setParamsFromSlope(BiquadType::LowShelf, f0norm, gain, 0.75f); + mChans[0].mFilter[0].setParamsFromSlope(BiquadType::LowShelf, f0norm, gain, 0.75f); gain = std::sqrt(props->Equalizer.Mid1Gain); f0norm = props->Equalizer.Mid1Center / frequency; - mChans[0].filter[1].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain, + mChans[0].mFilter[1].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain, props->Equalizer.Mid1Width); gain = std::sqrt(props->Equalizer.Mid2Gain); f0norm = props->Equalizer.Mid2Center / frequency; - mChans[0].filter[2].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain, + mChans[0].mFilter[2].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain, props->Equalizer.Mid2Width); gain = std::sqrt(props->Equalizer.HighGain); f0norm = props->Equalizer.HighCutoff / frequency; - mChans[0].filter[3].setParamsFromSlope(BiquadType::HighShelf, f0norm, gain, 0.75f); + mChans[0].mFilter[3].setParamsFromSlope(BiquadType::HighShelf, f0norm, gain, 0.75f); /* Copy the filter coefficients for the other input channels. */ for(size_t i{1u};i < slot->Wet.Buffer.size();++i) { - mChans[i].filter[0].copyParamsFrom(mChans[0].filter[0]); - mChans[i].filter[1].copyParamsFrom(mChans[0].filter[1]); - mChans[i].filter[2].copyParamsFrom(mChans[0].filter[2]); - mChans[i].filter[3].copyParamsFrom(mChans[0].filter[3]); + mChans[i].mFilter[0].copyParamsFrom(mChans[0].mFilter[0]); + mChans[i].mFilter[1].copyParamsFrom(mChans[0].mFilter[1]); + mChans[i].mFilter[2].copyParamsFrom(mChans[0].mFilter[2]); + mChans[i].mFilter[3].copyParamsFrom(mChans[0].mFilter[3]); } mOutTarget = target.Main->Buffer; - auto set_gains = [slot,target](auto &chan, al::span coeffs) - { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); }; - SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains); + auto set_channel = [this](size_t idx, uint target, float gain) + { + mChans[idx].mTargetChannel = target; + mChans[idx].mTargetGain = gain; + }; + target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel); } void EqualizerState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { const al::span buffer{mSampleBuffer.data(), samplesToDo}; - auto chan = std::addressof(mChans[0]); + auto chan = std::begin(mChans); for(const auto &input : samplesIn) { - const al::span inbuf{input.data(), samplesToDo}; - DualBiquad{chan->filter[0], chan->filter[1]}.process(inbuf, buffer.begin()); - DualBiquad{chan->filter[2], chan->filter[3]}.process(buffer, buffer.begin()); - - MixSamples(buffer, samplesOut, chan->CurrentGains, chan->TargetGains, samplesToDo, 0u); + const size_t outidx{chan->mTargetChannel}; + if(outidx != INVALID_CHANNEL_INDEX) + { + const al::span inbuf{input.data(), samplesToDo}; + DualBiquad{chan->mFilter[0], chan->mFilter[1]}.process(inbuf, buffer.begin()); + DualBiquad{chan->mFilter[2], chan->mFilter[3]}.process(buffer, buffer.begin()); + + MixSamples(buffer, {&samplesOut[outidx], 1}, &chan->mCurrentGain, &chan->mTargetGain, + samplesToDo, 0u); + } ++chan; } } -- cgit v1.2.3