aboutsummaryrefslogtreecommitdiffstats
path: root/alc/effects/equalizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'alc/effects/equalizer.cpp')
-rw-r--r--alc/effects/equalizer.cpp294
1 files changed, 80 insertions, 214 deletions
diff --git a/alc/effects/equalizer.cpp b/alc/effects/equalizer.cpp
index 929bff14..50bec4ad 100644
--- a/alc/effects/equalizer.cpp
+++ b/alc/effects/equalizer.cpp
@@ -20,18 +20,25 @@
#include "config.h"
-#include <cmath>
-#include <cstdlib>
-
#include <algorithm>
+#include <array>
+#include <cstdlib>
#include <functional>
-
-#include "al/auxeffectslot.h"
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alu.h"
-#include "filters/biquad.h"
-#include "vecmat.h"
+#include <iterator>
+#include <utility>
+
+#include "alc/effects/base.h"
+#include "almalloc.h"
+#include "alspan.h"
+#include "core/ambidefs.h"
+#include "core/bufferline.h"
+#include "core/context.h"
+#include "core/devformat.h"
+#include "core/device.h"
+#include "core/effectslot.h"
+#include "core/filters/biquad.h"
+#include "core/mixer.h"
+#include "intrusive_ptr.h"
namespace {
@@ -80,255 +87,114 @@ namespace {
struct EqualizerState final : public EffectState {
struct {
+ uint mTargetChannel{InvalidChannelIndex};
+
/* Effect parameters */
- BiquadFilter filter[4];
+ BiquadFilter mFilter[4];
/* Effect gains for each channel */
- ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]{};
- ALfloat TargetGains[MAX_OUTPUT_CHANNELS]{};
- } mChans[MAX_AMBI_CHANNELS];
+ float mCurrentGain{};
+ float mTargetGain{};
+ } mChans[MaxAmbiChannels];
- ALfloat mSampleBuffer[BUFFERSIZE]{};
+ alignas(16) FloatBufferLine mSampleBuffer{};
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override;
+ 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 process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
+ const al::span<FloatBufferLine> samplesOut) override;
DEF_NEWDEL(EqualizerState)
};
-ALboolean EqualizerState::deviceUpdate(const ALCdevice*)
+void EqualizerState::deviceUpdate(const DeviceBase*, const BufferStorage*)
{
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 = InvalidChannelIndex;
+ std::for_each(std::begin(e.mFilter), std::end(e.mFilter),
+ std::mem_fn(&BiquadFilter::clear));
+ e.mCurrentGain = 0.0f;
}
- return AL_TRUE;
}
-void EqualizerState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
+void EqualizerState::update(const ContextBase *context, const EffectSlot *slot,
+ const EffectProps *props, const EffectTarget target)
{
- const ALCdevice *device{context->mDevice.get()};
- auto frequency = static_cast<ALfloat>(device->Frequency);
- ALfloat gain, f0norm;
+ const DeviceBase *device{context->mDevice};
+ auto frequency = static_cast<float>(device->Frequency);
+ float gain, f0norm;
/* Calculate coefficients for the each type of filter. Note that the shelf
* and peaking filters' gain is for the centerpoint of the transition band,
- * meaning its dB needs to be doubled for the shelf or peak to reach the
- * provided gain.
+ * while the effect property gains are for the shelf/peak itself. So the
+ * property gains need their dB halved (sqrt of linear gain) for the
+ * shelf/peak to reach the provided gain.
*/
- gain = maxf(std::sqrt(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
- f0norm = props->Equalizer.LowCutoff/frequency;
- mChans[0].filter[0].setParams(BiquadType::LowShelf, gain, f0norm,
- BiquadFilter::rcpQFromSlope(gain, 0.75f));
+ gain = std::sqrt(props->Equalizer.LowGain);
+ f0norm = props->Equalizer.LowCutoff / frequency;
+ mChans[0].mFilter[0].setParamsFromSlope(BiquadType::LowShelf, f0norm, gain, 0.75f);
- gain = maxf(std::sqrt(props->Equalizer.Mid1Gain), 0.0625f);
- f0norm = props->Equalizer.Mid1Center/frequency;
- mChans[0].filter[1].setParams(BiquadType::Peaking, gain, f0norm,
- BiquadFilter::rcpQFromBandwidth(f0norm, props->Equalizer.Mid1Width));
+ gain = std::sqrt(props->Equalizer.Mid1Gain);
+ f0norm = props->Equalizer.Mid1Center / frequency;
+ mChans[0].mFilter[1].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain,
+ props->Equalizer.Mid1Width);
- gain = maxf(std::sqrt(props->Equalizer.Mid2Gain), 0.0625f);
- f0norm = props->Equalizer.Mid2Center/frequency;
- mChans[0].filter[2].setParams(BiquadType::Peaking, gain, f0norm,
- BiquadFilter::rcpQFromBandwidth(f0norm, props->Equalizer.Mid2Width));
+ gain = std::sqrt(props->Equalizer.Mid2Gain);
+ f0norm = props->Equalizer.Mid2Center / frequency;
+ mChans[0].mFilter[2].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain,
+ props->Equalizer.Mid2Width);
- gain = maxf(std::sqrt(props->Equalizer.HighGain), 0.0625f);
- f0norm = props->Equalizer.HighCutoff/frequency;
- mChans[0].filter[3].setParams(BiquadType::HighShelf, gain, f0norm,
- BiquadFilter::rcpQFromSlope(gain, 0.75f));
+ gain = std::sqrt(props->Equalizer.HighGain);
+ f0norm = props->Equalizer.HighCutoff / frequency;
+ 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;
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
+ auto set_channel = [this](size_t idx, uint outchan, float outgain)
{
- auto coeffs = GetAmbiIdentityRow(i);
- ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mChans[i].TargetGains);
- }
+ mChans[idx].mTargetChannel = outchan;
+ mChans[idx].mTargetGain = outgain;
+ };
+ target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
}
void EqualizerState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
{
- auto chandata = std::addressof(mChans[0]);
+ const al::span<float> buffer{mSampleBuffer.data(), samplesToDo};
+ auto chan = std::begin(mChans);
for(const auto &input : samplesIn)
{
- chandata->filter[0].process(mSampleBuffer, input.data(), samplesToDo);
- chandata->filter[1].process(mSampleBuffer, mSampleBuffer, samplesToDo);
- chandata->filter[2].process(mSampleBuffer, mSampleBuffer, samplesToDo);
- chandata->filter[3].process(mSampleBuffer, mSampleBuffer, samplesToDo);
-
- MixSamples({mSampleBuffer, samplesToDo}, samplesOut, chandata->CurrentGains,
- chandata->TargetGains, samplesToDo, 0);
- ++chandata;
- }
-}
-
-
-void Equalizer_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
-{ context->setError(AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
-void Equalizer_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ context->setError(AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
-void Equalizer_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_EQUALIZER_LOW_GAIN:
- if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band gain out of range");
- props->Equalizer.LowGain = val;
- break;
-
- case AL_EQUALIZER_LOW_CUTOFF:
- if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band cutoff out of range");
- props->Equalizer.LowCutoff = val;
- break;
-
- case AL_EQUALIZER_MID1_GAIN:
- if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band gain out of range");
- props->Equalizer.Mid1Gain = val;
- break;
-
- case AL_EQUALIZER_MID1_CENTER:
- if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band center out of range");
- props->Equalizer.Mid1Center = val;
- break;
-
- case AL_EQUALIZER_MID1_WIDTH:
- if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band width out of range");
- props->Equalizer.Mid1Width = val;
- break;
-
- case AL_EQUALIZER_MID2_GAIN:
- if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band gain out of range");
- props->Equalizer.Mid2Gain = val;
- break;
-
- case AL_EQUALIZER_MID2_CENTER:
- if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band center out of range");
- props->Equalizer.Mid2Center = val;
- break;
-
- case AL_EQUALIZER_MID2_WIDTH:
- if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band width out of range");
- props->Equalizer.Mid2Width = val;
- break;
-
- case AL_EQUALIZER_HIGH_GAIN:
- if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band gain out of range");
- props->Equalizer.HighGain = val;
- break;
-
- case AL_EQUALIZER_HIGH_CUTOFF:
- if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band cutoff out of range");
- props->Equalizer.HighCutoff = val;
- break;
-
- default:
- context->setError(AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
- }
-}
-void Equalizer_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Equalizer_setParamf(props, context, param, vals[0]); }
-
-void Equalizer_getParami(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ context->setError(AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
-void Equalizer_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ context->setError(AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
-void Equalizer_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_EQUALIZER_LOW_GAIN:
- *val = props->Equalizer.LowGain;
- break;
-
- case AL_EQUALIZER_LOW_CUTOFF:
- *val = props->Equalizer.LowCutoff;
- break;
-
- case AL_EQUALIZER_MID1_GAIN:
- *val = props->Equalizer.Mid1Gain;
- break;
-
- case AL_EQUALIZER_MID1_CENTER:
- *val = props->Equalizer.Mid1Center;
- break;
-
- case AL_EQUALIZER_MID1_WIDTH:
- *val = props->Equalizer.Mid1Width;
- break;
-
- case AL_EQUALIZER_MID2_GAIN:
- *val = props->Equalizer.Mid2Gain;
- break;
-
- case AL_EQUALIZER_MID2_CENTER:
- *val = props->Equalizer.Mid2Center;
- break;
-
- case AL_EQUALIZER_MID2_WIDTH:
- *val = props->Equalizer.Mid2Width;
- break;
-
- case AL_EQUALIZER_HIGH_GAIN:
- *val = props->Equalizer.HighGain;
- break;
-
- case AL_EQUALIZER_HIGH_CUTOFF:
- *val = props->Equalizer.HighCutoff;
- break;
-
- default:
- context->setError(AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
+ const size_t outidx{chan->mTargetChannel};
+ if(outidx != InvalidChannelIndex)
+ {
+ const al::span<const float> 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].data(), chan->mCurrentGain, chan->mTargetGain,
+ samplesToDo);
+ }
+ ++chan;
}
}
-void Equalizer_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Equalizer_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Equalizer);
struct EqualizerStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new EqualizerState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Equalizer_vtable; }
+ al::intrusive_ptr<EffectState> create() override
+ { return al::intrusive_ptr<EffectState>{new EqualizerState{}}; }
};
-EffectProps EqualizerStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
- props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
- props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
- props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
- props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
- props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
- props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
- props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
- props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
- props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
- return props;
-}
-
} // namespace
EffectStateFactory *EqualizerStateFactory_getFactory()