aboutsummaryrefslogtreecommitdiffstats
path: root/alc/effects/fshifter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'alc/effects/fshifter.cpp')
-rw-r--r--alc/effects/fshifter.cpp321
1 files changed, 122 insertions, 199 deletions
diff --git a/alc/effects/fshifter.cpp b/alc/effects/fshifter.cpp
index 1b935047..3e6a7385 100644
--- a/alc/effects/fshifter.cpp
+++ b/alc/effects/fshifter.cpp
@@ -20,207 +20,219 @@
#include "config.h"
-#include <cmath>
-#include <cstdlib>
+#include <algorithm>
#include <array>
+#include <cmath>
#include <complex>
-#include <algorithm>
-
-#include "al/auxeffectslot.h"
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alu.h"
+#include <cstdlib>
+#include <iterator>
+#include "alc/effects/base.h"
#include "alcomplex.h"
+#include "almalloc.h"
+#include "alnumbers.h"
+#include "alnumeric.h"
+#include "alspan.h"
+#include "core/bufferline.h"
+#include "core/context.h"
+#include "core/devformat.h"
+#include "core/device.h"
+#include "core/effectslot.h"
+#include "core/mixer.h"
+#include "core/mixer/defs.h"
+#include "intrusive_ptr.h"
+
namespace {
+using uint = unsigned int;
using complex_d = std::complex<double>;
-#define HIL_SIZE 1024
-#define OVERSAMP (1<<2)
+constexpr size_t HilSize{1024};
+constexpr size_t HilHalfSize{HilSize >> 1};
+constexpr size_t OversampleFactor{4};
-#define HIL_STEP (HIL_SIZE / OVERSAMP)
-#define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))
+static_assert(HilSize%OversampleFactor == 0, "Factor must be a clean divisor of the size");
+constexpr size_t HilStep{HilSize / OversampleFactor};
/* Define a Hann window, used to filter the HIL input and output. */
-/* Making this constexpr seems to require C++14. */
-std::array<ALdouble,HIL_SIZE> InitHannWindow()
-{
- std::array<ALdouble,HIL_SIZE> ret;
- /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
- for(size_t i{0};i < HIL_SIZE>>1;i++)
+struct Windower {
+ alignas(16) std::array<double,HilSize> mData;
+
+ Windower()
{
- constexpr double scale{al::MathDefs<double>::Pi() / double{HIL_SIZE-1}};
- const double val{std::sin(static_cast<double>(i) * scale)};
- ret[i] = ret[HIL_SIZE-1-i] = val * val;
+ /* Create lookup table of the Hann window for the desired size. */
+ for(size_t i{0};i < HilHalfSize;i++)
+ {
+ constexpr double scale{al::numbers::pi / double{HilSize}};
+ const double val{std::sin((static_cast<double>(i)+0.5) * scale)};
+ mData[i] = mData[HilSize-1-i] = val * val;
+ }
}
- return ret;
-}
-alignas(16) const std::array<ALdouble,HIL_SIZE> HannWindow = InitHannWindow();
+};
+const Windower gWindow{};
struct FshifterState final : public EffectState {
/* Effect parameters */
- size_t mCount{};
- ALsizei mPhaseStep[2]{};
- ALsizei mPhase[2]{};
- ALdouble mSign[2]{};
-
+ size_t mCount{};
+ size_t mPos{};
+ std::array<uint,2> mPhaseStep{};
+ std::array<uint,2> mPhase{};
+ std::array<double,2> mSign{};
- /*Effects buffers*/
- ALfloat mInFIFO[HIL_SIZE]{};
- complex_d mOutFIFO[HIL_SIZE]{};
- complex_d mOutputAccum[HIL_SIZE]{};
- complex_d mAnalytic[HIL_SIZE]{};
- complex_d mOutdata[BUFFERSIZE]{};
+ /* Effects buffers */
+ std::array<double,HilSize> mInFIFO{};
+ std::array<complex_d,HilStep> mOutFIFO{};
+ std::array<complex_d,HilSize> mOutputAccum{};
+ std::array<complex_d,HilSize> mAnalytic{};
+ std::array<complex_d,BufferLineSize> mOutdata{};
- alignas(16) ALfloat mBufferOut[BUFFERSIZE]{};
+ alignas(16) FloatBufferLine mBufferOut{};
/* Effect gains for each output channel */
struct {
- ALfloat Current[MAX_OUTPUT_CHANNELS]{};
- ALfloat Target[MAX_OUTPUT_CHANNELS]{};
+ float Current[MaxAmbiChannels]{};
+ float Target[MaxAmbiChannels]{};
} mGains[2];
- 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(FshifterState)
};
-ALboolean FshifterState::deviceUpdate(const ALCdevice*)
+void FshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*)
{
/* (Re-)initializing parameters and clear the buffers. */
- mCount = FIFO_LATENCY;
+ mCount = 0;
+ mPos = HilSize - HilStep;
- std::fill(std::begin(mPhaseStep), std::end(mPhaseStep), 0);
- std::fill(std::begin(mPhase), std::end(mPhase), 0);
- std::fill(std::begin(mSign), std::end(mSign), 1.0);
- std::fill(std::begin(mInFIFO), std::end(mInFIFO), 0.0f);
- std::fill(std::begin(mOutFIFO), std::end(mOutFIFO), complex_d{});
- std::fill(std::begin(mOutputAccum), std::end(mOutputAccum), complex_d{});
- std::fill(std::begin(mAnalytic), std::end(mAnalytic), complex_d{});
+ mPhaseStep.fill(0u);
+ mPhase.fill(0u);
+ mSign.fill(1.0);
+ mInFIFO.fill(0.0);
+ mOutFIFO.fill(complex_d{});
+ mOutputAccum.fill(complex_d{});
+ mAnalytic.fill(complex_d{});
for(auto &gain : mGains)
{
std::fill(std::begin(gain.Current), std::end(gain.Current), 0.0f);
std::fill(std::begin(gain.Target), std::end(gain.Target), 0.0f);
}
-
- return AL_TRUE;
}
-void FshifterState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
+void FshifterState::update(const ContextBase *context, const EffectSlot *slot,
+ const EffectProps *props, const EffectTarget target)
{
- const ALCdevice *device{context->mDevice.get()};
+ const DeviceBase *device{context->mDevice};
- ALfloat step{props->Fshifter.Frequency / static_cast<ALfloat>(device->Frequency)};
- mPhaseStep[0] = mPhaseStep[1] = fastf2i(minf(step, 0.5f) * FRACTIONONE);
+ const float step{props->Fshifter.Frequency / static_cast<float>(device->Frequency)};
+ mPhaseStep[0] = mPhaseStep[1] = fastf2u(minf(step, 1.0f) * MixerFracOne);
switch(props->Fshifter.LeftDirection)
{
- case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
+ case FShifterDirection::Down:
mSign[0] = -1.0;
break;
-
- case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
+ case FShifterDirection::Up:
mSign[0] = 1.0;
break;
-
- case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
+ case FShifterDirection::Off:
mPhase[0] = 0;
mPhaseStep[0] = 0;
break;
}
- switch (props->Fshifter.RightDirection)
+ switch(props->Fshifter.RightDirection)
{
- case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
+ case FShifterDirection::Down:
mSign[1] = -1.0;
break;
-
- case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
+ case FShifterDirection::Up:
mSign[1] = 1.0;
break;
-
- case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
+ case FShifterDirection::Off:
mPhase[1] = 0;
mPhaseStep[1] = 0;
break;
}
- ALfloat coeffs[2][MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({-1.0f, 0.0f, -1.0f}, 0.0f, coeffs[0]);
- CalcDirectionCoeffs({ 1.0f, 0.0f, -1.0f}, 0.0f, coeffs[1]);
+ 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, coeffs[0], slot->Params.Gain, mGains[0].Target);
- ComputePanGains(target.Main, coeffs[1], slot->Params.Gain, mGains[1].Target);
+ ComputePanGains(target.Main, lcoeffs.data(), slot->Gain, mGains[0].Target);
+ ComputePanGains(target.Main, rcoeffs.data(), slot->Gain, mGains[1].Target);
}
void FshifterState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
{
- static constexpr complex_d complex_zero{0.0, 0.0};
- ALfloat *RESTRICT BufferOut = mBufferOut;
- size_t j, k;
-
for(size_t base{0u};base < samplesToDo;)
{
- const size_t todo{minz(HIL_SIZE-mCount, samplesToDo-base)};
-
- ASSUME(todo > 0);
+ size_t todo{minz(HilStep-mCount, samplesToDo-base)};
/* Fill FIFO buffer with samples data */
- k = mCount;
- for(j = 0;j < todo;j++,k++)
- {
- mInFIFO[k] = samplesIn[0][base+j];
- mOutdata[base+j] = mOutFIFO[k-FIFO_LATENCY];
- }
- mCount += todo;
- base += todo;
+ const size_t pos{mPos};
+ size_t count{mCount};
+ do {
+ mInFIFO[pos+count] = samplesIn[0][base];
+ mOutdata[base] = mOutFIFO[count];
+ ++base; ++count;
+ } while(--todo);
+ mCount = count;
/* Check whether FIFO buffer is filled */
- if(mCount < HIL_SIZE) continue;
- mCount = FIFO_LATENCY;
+ if(mCount < HilStep) break;
+ mCount = 0;
+ mPos = (mPos+HilStep) & (HilSize-1);
/* Real signal windowing and store in Analytic buffer */
- for(k = 0;k < HIL_SIZE;k++)
- {
- mAnalytic[k].real(mInFIFO[k] * HannWindow[k]);
- mAnalytic[k].imag(0.0);
- }
+ for(size_t src{mPos}, k{0u};src < HilSize;++src,++k)
+ mAnalytic[k] = mInFIFO[src]*gWindow.mData[k];
+ for(size_t src{0u}, k{HilSize-mPos};src < mPos;++src,++k)
+ mAnalytic[k] = mInFIFO[src]*gWindow.mData[k];
/* Processing signal by Discrete Hilbert Transform (analytical signal). */
complex_hilbert(mAnalytic);
/* Windowing and add to output accumulator */
- for(k = 0;k < HIL_SIZE;k++)
- mOutputAccum[k] += 2.0/OVERSAMP*HannWindow[k]*mAnalytic[k];
-
- /* Shift accumulator, input & output FIFO */
- for(k = 0;k < HIL_STEP;k++) mOutFIFO[k] = mOutputAccum[k];
- for(j = 0;k < HIL_SIZE;k++,j++) mOutputAccum[j] = mOutputAccum[k];
- for(;j < HIL_SIZE;j++) mOutputAccum[j] = complex_zero;
- for(k = 0;k < FIFO_LATENCY;k++)
- mInFIFO[k] = mInFIFO[k+HIL_STEP];
+ for(size_t dst{mPos}, k{0u};dst < HilSize;++dst,++k)
+ mOutputAccum[dst] += 2.0/OversampleFactor*gWindow.mData[k]*mAnalytic[k];
+ for(size_t dst{0u}, k{HilSize-mPos};dst < mPos;++dst,++k)
+ mOutputAccum[dst] += 2.0/OversampleFactor*gWindow.mData[k]*mAnalytic[k];
+
+ /* Copy out the accumulated result, then clear for the next iteration. */
+ std::copy_n(mOutputAccum.cbegin() + mPos, HilStep, mOutFIFO.begin());
+ std::fill_n(mOutputAccum.begin() + mPos, HilStep, complex_d{});
}
/* Process frequency shifter using the analytic signal obtained. */
- for(ALsizei c{0};c < 2;++c)
+ float *RESTRICT BufferOut{al::assume_aligned<16>(mBufferOut.data())};
+ for(size_t c{0};c < 2;++c)
{
- for(k = 0;k < samplesToDo;++k)
+ const uint phase_step{mPhaseStep[c]};
+ uint phase_idx{mPhase[c]};
+ for(size_t k{0};k < samplesToDo;++k)
{
- double phase = mPhase[c] * ((1.0 / FRACTIONONE) * al::MathDefs<double>::Tau());
+ const double phase{phase_idx * (al::numbers::pi*2.0 / MixerFracOne)};
BufferOut[k] = static_cast<float>(mOutdata[k].real()*std::cos(phase) +
mOutdata[k].imag()*std::sin(phase)*mSign[c]);
- mPhase[c] += mPhaseStep[c];
- mPhase[c] &= FRACTIONMASK;
+ phase_idx += phase_step;
+ phase_idx &= MixerFracMask;
}
+ mPhase[c] = phase_idx;
/* Now, mix the processed sound data to the output. */
MixSamples({BufferOut, samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target,
@@ -229,100 +241,11 @@ void FshifterState::process(const size_t samplesToDo, const al::span<const Float
}
-void Fshifter_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_FREQUENCY_SHIFTER_FREQUENCY:
- if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter frequency out of range");
- props->Fshifter.Frequency = val;
- break;
-
- default:
- context->setError(AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
- param);
- }
-}
-void Fshifter_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Fshifter_setParamf(props, context, param, vals[0]); }
-
-void Fshifter_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
- if(!(val >= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter left direction out of range");
- props->Fshifter.LeftDirection = val;
- break;
-
- case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
- if(!(val >= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter right direction out of range");
- props->Fshifter.RightDirection = val;
- break;
-
- default:
- context->setError(AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x",
- param);
- }
-}
-void Fshifter_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Fshifter_setParami(props, context, param, vals[0]); }
-
-void Fshifter_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
- *val = props->Fshifter.LeftDirection;
- break;
- case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
- *val = props->Fshifter.RightDirection;
- break;
- default:
- context->setError(AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x",
- param);
- }
-}
-void Fshifter_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Fshifter_getParami(props, context, param, vals); }
-
-void Fshifter_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_FREQUENCY_SHIFTER_FREQUENCY:
- *val = props->Fshifter.Frequency;
- break;
-
- default:
- context->setError(AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
- param);
- }
-}
-void Fshifter_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Fshifter_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Fshifter);
-
-
struct FshifterStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new FshifterState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Fshifter_vtable; }
+ al::intrusive_ptr<EffectState> create() override
+ { return al::intrusive_ptr<EffectState>{new FshifterState{}}; }
};
-EffectProps FshifterStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
- props.Fshifter.LeftDirection = AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION;
- props.Fshifter.RightDirection = AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION;
- return props;
-}
-
} // namespace
EffectStateFactory *FshifterStateFactory_getFactory()