aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/effects
diff options
context:
space:
mode:
Diffstat (limited to 'Alc/effects')
-rw-r--r--Alc/effects/autowah.cpp298
-rw-r--r--Alc/effects/base.h196
-rw-r--r--Alc/effects/chorus.cpp538
-rw-r--r--Alc/effects/compressor.cpp222
-rw-r--r--Alc/effects/dedicated.cpp159
-rw-r--r--Alc/effects/distortion.cpp269
-rw-r--r--Alc/effects/echo.cpp271
-rw-r--r--Alc/effects/equalizer.cpp337
-rw-r--r--Alc/effects/fshifter.cpp301
-rw-r--r--Alc/effects/modulator.cpp279
-rw-r--r--Alc/effects/null.cpp164
-rw-r--r--Alc/effects/pshifter.cpp405
-rw-r--r--Alc/effects/reverb.cpp2102
-rw-r--r--Alc/effects/vmorpher.cpp430
14 files changed, 0 insertions, 5971 deletions
diff --git a/Alc/effects/autowah.cpp b/Alc/effects/autowah.cpp
deleted file mode 100644
index 96292636..00000000
--- a/Alc/effects/autowah.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2018 by Raul Herraiz.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/biquad.h"
-#include "vecmat.h"
-
-namespace {
-
-#define MIN_FREQ 20.0f
-#define MAX_FREQ 2500.0f
-#define Q_FACTOR 5.0f
-
-struct ALautowahState final : public EffectState {
- /* Effect parameters */
- ALfloat mAttackRate;
- ALfloat mReleaseRate;
- ALfloat mResonanceGain;
- ALfloat mPeakGain;
- ALfloat mFreqMinNorm;
- ALfloat mBandwidthNorm;
- ALfloat mEnvDelay;
-
- /* Filter components derived from the envelope. */
- struct {
- ALfloat cos_w0;
- ALfloat alpha;
- } mEnv[BUFFERSIZE];
-
- struct {
- /* Effect filters' history. */
- struct {
- ALfloat z1, z2;
- } Filter;
-
- /* Effect gains for each output channel */
- ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
- ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
- } mChans[MAX_AMBI_CHANNELS];
-
- /* Effects buffers */
- alignas(16) ALfloat mBufferOut[BUFFERSIZE];
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(ALautowahState)
-};
-
-ALboolean ALautowahState::deviceUpdate(const ALCdevice*)
-{
- /* (Re-)initializing parameters and clear the buffers. */
-
- mAttackRate = 1.0f;
- mReleaseRate = 1.0f;
- mResonanceGain = 10.0f;
- mPeakGain = 4.5f;
- mFreqMinNorm = 4.5e-4f;
- mBandwidthNorm = 0.05f;
- mEnvDelay = 0.0f;
-
- for(auto &e : mEnv)
- {
- e.cos_w0 = 0.0f;
- e.alpha = 0.0f;
- }
-
- for(auto &chan : mChans)
- {
- std::fill(std::begin(chan.CurrentGains), std::end(chan.CurrentGains), 0.0f);
- chan.Filter.z1 = 0.0f;
- chan.Filter.z2 = 0.0f;
- }
-
- return AL_TRUE;
-}
-
-void ALautowahState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device{context->Device};
-
- const ALfloat ReleaseTime{clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f)};
-
- mAttackRate = expf(-1.0f / (props->Autowah.AttackTime*device->Frequency));
- mReleaseRate = expf(-1.0f / (ReleaseTime*device->Frequency));
- /* 0-20dB Resonance Peak gain */
- mResonanceGain = std::sqrt(std::log10(props->Autowah.Resonance)*10.0f / 3.0f);
- mPeakGain = 1.0f - std::log10(props->Autowah.PeakGain/AL_AUTOWAH_MAX_PEAK_GAIN);
- mFreqMinNorm = MIN_FREQ / device->Frequency;
- mBandwidthNorm = (MAX_FREQ-MIN_FREQ) / device->Frequency;
-
- mOutTarget = target.Main->Buffer;
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- auto coeffs = GetAmbiIdentityRow(i);
- ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mChans[i].TargetGains);
- }
-}
-
-void ALautowahState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- const ALfloat attack_rate = mAttackRate;
- const ALfloat release_rate = mReleaseRate;
- const ALfloat res_gain = mResonanceGain;
- const ALfloat peak_gain = mPeakGain;
- const ALfloat freq_min = mFreqMinNorm;
- const ALfloat bandwidth = mBandwidthNorm;
-
- ALfloat env_delay{mEnvDelay};
- for(ALsizei i{0};i < samplesToDo;i++)
- {
- ALfloat w0, sample, a;
-
- /* Envelope follower described on the book: Audio Effects, Theory,
- * Implementation and Application.
- */
- sample = peak_gain * std::fabs(samplesIn[0][i]);
- a = (sample > env_delay) ? attack_rate : release_rate;
- env_delay = lerp(sample, env_delay, a);
-
- /* Calculate the cos and alpha components for this sample's filter. */
- w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * al::MathDefs<float>::Tau();
- mEnv[i].cos_w0 = cosf(w0);
- mEnv[i].alpha = sinf(w0)/(2.0f * Q_FACTOR);
- }
- mEnvDelay = env_delay;
-
- ASSUME(numInput > 0);
- for(ALsizei c{0};c < numInput;++c)
- {
- /* This effectively inlines BiquadFilter_setParams for a peaking
- * filter and BiquadFilter_processC. The alpha and cosine components
- * for the filter coefficients were previously calculated with the
- * envelope. Because the filter changes for each sample, the
- * coefficients are transient and don't need to be held.
- */
- ALfloat z1{mChans[c].Filter.z1};
- ALfloat z2{mChans[c].Filter.z2};
-
- for(ALsizei i{0};i < samplesToDo;i++)
- {
- const ALfloat alpha = mEnv[i].alpha;
- const ALfloat cos_w0 = mEnv[i].cos_w0;
- ALfloat input, output;
- ALfloat a[3], b[3];
-
- b[0] = 1.0f + alpha*res_gain;
- b[1] = -2.0f * cos_w0;
- b[2] = 1.0f - alpha*res_gain;
- a[0] = 1.0f + alpha/res_gain;
- a[1] = -2.0f * cos_w0;
- a[2] = 1.0f - alpha/res_gain;
-
- input = samplesIn[c][i];
- output = input*(b[0]/a[0]) + z1;
- z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
- z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
- mBufferOut[i] = output;
- }
- mChans[c].Filter.z1 = z1;
- mChans[c].Filter.z2 = z2;
-
- /* Now, mix the processed sound data to the output. */
- MixSamples(mBufferOut, samplesOut, mChans[c].CurrentGains, mChans[c].TargetGains,
- samplesToDo, 0, samplesToDo);
- }
-}
-
-
-void ALautowah_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_AUTOWAH_ATTACK_TIME:
- if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah attack time out of range");
- props->Autowah.AttackTime = val;
- break;
-
- case AL_AUTOWAH_RELEASE_TIME:
- if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah release time out of range");
- props->Autowah.ReleaseTime = val;
- break;
-
- case AL_AUTOWAH_RESONANCE:
- if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah resonance out of range");
- props->Autowah.Resonance = val;
- break;
-
- case AL_AUTOWAH_PEAK_GAIN:
- if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah peak gain out of range");
- props->Autowah.PeakGain = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
- }
-}
-void ALautowah_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ ALautowah_setParamf(props, context, param, vals[0]); }
-
-void ALautowah_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param); }
-void ALautowah_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param); }
-
-void ALautowah_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_AUTOWAH_ATTACK_TIME:
- *val = props->Autowah.AttackTime;
- break;
-
- case AL_AUTOWAH_RELEASE_TIME:
- *val = props->Autowah.ReleaseTime;
- break;
-
- case AL_AUTOWAH_RESONANCE:
- *val = props->Autowah.Resonance;
- break;
-
- case AL_AUTOWAH_PEAK_GAIN:
- *val = props->Autowah.PeakGain;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
- }
-
-}
-void ALautowah_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ ALautowah_getParamf(props, context, param, vals); }
-
-void ALautowah_getParami(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param); }
-void ALautowah_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param); }
-
-DEFINE_ALEFFECT_VTABLE(ALautowah);
-
-
-struct AutowahStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ALautowahState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &ALautowah_vtable; }
-};
-
-EffectProps AutowahStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
- props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
- props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
- props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *AutowahStateFactory_getFactory()
-{
- static AutowahStateFactory AutowahFactory{};
- return &AutowahFactory;
-}
diff --git a/Alc/effects/base.h b/Alc/effects/base.h
deleted file mode 100644
index 4f48de22..00000000
--- a/Alc/effects/base.h
+++ /dev/null
@@ -1,196 +0,0 @@
-#ifndef EFFECTS_BASE_H
-#define EFFECTS_BASE_H
-
-#include "alcmain.h"
-#include "almalloc.h"
-#include "alspan.h"
-#include "atomic.h"
-
-
-struct ALeffectslot;
-
-
-union EffectProps {
- struct {
- // Shared Reverb Properties
- ALfloat Density;
- ALfloat Diffusion;
- ALfloat Gain;
- ALfloat GainHF;
- ALfloat DecayTime;
- ALfloat DecayHFRatio;
- ALfloat ReflectionsGain;
- ALfloat ReflectionsDelay;
- ALfloat LateReverbGain;
- ALfloat LateReverbDelay;
- ALfloat AirAbsorptionGainHF;
- ALfloat RoomRolloffFactor;
- ALboolean DecayHFLimit;
-
- // Additional EAX Reverb Properties
- ALfloat GainLF;
- ALfloat DecayLFRatio;
- ALfloat ReflectionsPan[3];
- ALfloat LateReverbPan[3];
- ALfloat EchoTime;
- ALfloat EchoDepth;
- ALfloat ModulationTime;
- ALfloat ModulationDepth;
- ALfloat HFReference;
- ALfloat LFReference;
- } Reverb;
-
- struct {
- ALfloat AttackTime;
- ALfloat ReleaseTime;
- ALfloat Resonance;
- ALfloat PeakGain;
- } Autowah;
-
- struct {
- ALint Waveform;
- ALint Phase;
- ALfloat Rate;
- ALfloat Depth;
- ALfloat Feedback;
- ALfloat Delay;
- } Chorus; /* Also Flanger */
-
- struct {
- ALboolean OnOff;
- } Compressor;
-
- struct {
- ALfloat Edge;
- ALfloat Gain;
- ALfloat LowpassCutoff;
- ALfloat EQCenter;
- ALfloat EQBandwidth;
- } Distortion;
-
- struct {
- ALfloat Delay;
- ALfloat LRDelay;
-
- ALfloat Damping;
- ALfloat Feedback;
-
- ALfloat Spread;
- } Echo;
-
- struct {
- ALfloat LowCutoff;
- ALfloat LowGain;
- ALfloat Mid1Center;
- ALfloat Mid1Gain;
- ALfloat Mid1Width;
- ALfloat Mid2Center;
- ALfloat Mid2Gain;
- ALfloat Mid2Width;
- ALfloat HighCutoff;
- ALfloat HighGain;
- } Equalizer;
-
- struct {
- ALfloat Frequency;
- ALint LeftDirection;
- ALint RightDirection;
- } Fshifter;
-
- struct {
- ALfloat Frequency;
- ALfloat HighPassCutoff;
- ALint Waveform;
- } Modulator;
-
- struct {
- ALint CoarseTune;
- ALint FineTune;
- } Pshifter;
-
- struct {
- ALfloat Rate;
- ALint PhonemeA;
- ALint PhonemeB;
- ALint PhonemeACoarseTuning;
- ALint PhonemeBCoarseTuning;
- ALint Waveform;
- } Vmorpher;
-
- struct {
- ALfloat Gain;
- } Dedicated;
-};
-
-
-struct EffectVtable {
- void (*const setParami)(EffectProps *props, ALCcontext *context, ALenum param, ALint val);
- void (*const setParamiv)(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals);
- void (*const setParamf)(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val);
- void (*const setParamfv)(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals);
-
- void (*const getParami)(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val);
- void (*const getParamiv)(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals);
- void (*const getParamf)(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val);
- void (*const getParamfv)(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals);
-};
-
-#define DEFINE_ALEFFECT_VTABLE(T) \
-const EffectVtable T##_vtable = { \
- T##_setParami, T##_setParamiv, \
- T##_setParamf, T##_setParamfv, \
- T##_getParami, T##_getParamiv, \
- T##_getParamf, T##_getParamfv, \
-}
-
-
-struct EffectTarget {
- MixParams *Main;
- RealMixParams *RealOut;
-};
-
-struct EffectState {
- RefCount mRef{1u};
-
- al::span<FloatBufferLine> mOutTarget;
-
-
- virtual ~EffectState() = default;
-
- virtual ALboolean deviceUpdate(const ALCdevice *device) = 0;
- virtual void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) = 0;
- virtual void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) = 0;
-
- void IncRef() noexcept;
- void DecRef() noexcept;
-};
-
-
-struct EffectStateFactory {
- virtual ~EffectStateFactory() { }
-
- virtual EffectState *create() = 0;
- virtual EffectProps getDefaultProps() const noexcept = 0;
- virtual const EffectVtable *getEffectVtable() const noexcept = 0;
-};
-
-
-EffectStateFactory *NullStateFactory_getFactory(void);
-EffectStateFactory *ReverbStateFactory_getFactory(void);
-EffectStateFactory *StdReverbStateFactory_getFactory(void);
-EffectStateFactory *AutowahStateFactory_getFactory(void);
-EffectStateFactory *ChorusStateFactory_getFactory(void);
-EffectStateFactory *CompressorStateFactory_getFactory(void);
-EffectStateFactory *DistortionStateFactory_getFactory(void);
-EffectStateFactory *EchoStateFactory_getFactory(void);
-EffectStateFactory *EqualizerStateFactory_getFactory(void);
-EffectStateFactory *FlangerStateFactory_getFactory(void);
-EffectStateFactory *FshifterStateFactory_getFactory(void);
-EffectStateFactory *ModulatorStateFactory_getFactory(void);
-EffectStateFactory *PshifterStateFactory_getFactory(void);
-EffectStateFactory* VmorpherStateFactory_getFactory(void);
-
-EffectStateFactory *DedicatedStateFactory_getFactory(void);
-
-
-#endif /* EFFECTS_BASE_H */
diff --git a/Alc/effects/chorus.cpp b/Alc/effects/chorus.cpp
deleted file mode 100644
index d475b57a..00000000
--- a/Alc/effects/chorus.cpp
+++ /dev/null
@@ -1,538 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Mike Gorchak
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <algorithm>
-#include <climits>
-#include <cmath>
-#include <cstdlib>
-#include <iterator>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "AL/efx.h"
-
-#include "alAuxEffectSlot.h"
-#include "alcmain.h"
-#include "alError.h"
-#include "alcontext.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "alspan.h"
-#include "alu.h"
-#include "ambidefs.h"
-#include "effects/base.h"
-#include "math_defs.h"
-#include "opthelpers.h"
-#include "vector.h"
-
-
-namespace {
-
-static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
-static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
-
-enum class WaveForm {
- Sinusoid,
- Triangle
-};
-
-void GetTriangleDelays(ALint *delays, const ALsizei start_offset, const ALsizei lfo_range,
- const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay, const ALsizei todo)
-{
- ASSUME(start_offset >= 0);
- ASSUME(lfo_range > 0);
- ASSUME(todo > 0);
-
- ALsizei offset{start_offset};
- auto gen_lfo = [&offset,lfo_range,lfo_scale,depth,delay]() -> ALint
- {
- offset = (offset+1)%lfo_range;
- return fastf2i((1.0f - std::abs(2.0f - lfo_scale*offset)) * depth) + delay;
- };
- std::generate_n(delays, todo, gen_lfo);
-}
-
-void GetSinusoidDelays(ALint *delays, const ALsizei start_offset, const ALsizei lfo_range,
- const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay, const ALsizei todo)
-{
- ASSUME(start_offset >= 0);
- ASSUME(lfo_range > 0);
- ASSUME(todo > 0);
-
- ALsizei offset{start_offset};
- auto gen_lfo = [&offset,lfo_range,lfo_scale,depth,delay]() -> ALint
- {
- ASSUME(delay >= 0);
- offset = (offset+1)%lfo_range;
- return fastf2i(std::sin(lfo_scale*offset) * depth) + delay;
- };
- std::generate_n(delays, todo, gen_lfo);
-}
-
-struct ChorusState final : public EffectState {
- al::vector<ALfloat,16> mSampleBuffer;
- ALsizei mOffset{0};
-
- ALsizei mLfoOffset{0};
- ALsizei mLfoRange{1};
- ALfloat mLfoScale{0.0f};
- ALint mLfoDisp{0};
-
- /* Gains for left and right sides */
- struct {
- ALfloat Current[MAX_OUTPUT_CHANNELS]{};
- ALfloat Target[MAX_OUTPUT_CHANNELS]{};
- } mGains[2];
-
- /* effect parameters */
- WaveForm mWaveform{};
- ALint mDelay{0};
- ALfloat mDepth{0.0f};
- ALfloat mFeedback{0.0f};
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(ChorusState)
-};
-
-ALboolean ChorusState::deviceUpdate(const ALCdevice *Device)
-{
- const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY);
- size_t maxlen;
-
- maxlen = NextPowerOf2(float2int(max_delay*2.0f*Device->Frequency) + 1u);
- if(maxlen <= 0) return AL_FALSE;
-
- if(maxlen != mSampleBuffer.size())
- {
- mSampleBuffer.resize(maxlen);
- mSampleBuffer.shrink_to_fit();
- }
-
- std::fill(mSampleBuffer.begin(), mSampleBuffer.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);
- }
-
- return AL_TRUE;
-}
-
-void ChorusState::update(const ALCcontext *Context, const ALeffectslot *Slot, const EffectProps *props, const EffectTarget target)
-{
- static constexpr ALsizei mindelay = MAX_RESAMPLE_PADDING << FRACTIONBITS;
-
- switch(props->Chorus.Waveform)
- {
- case AL_CHORUS_WAVEFORM_TRIANGLE:
- mWaveform = WaveForm::Triangle;
- break;
- case AL_CHORUS_WAVEFORM_SINUSOID:
- mWaveform = WaveForm::Sinusoid;
- break;
- }
-
- /* The LFO depth is scaled to be relative to the sample delay. Clamp the
- * delay and depth to allow enough padding for resampling.
- */
- const ALCdevice *device{Context->Device};
- const auto frequency = static_cast<ALfloat>(device->Frequency);
- mDelay = maxi(float2int(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f), mindelay);
- mDepth = minf(props->Chorus.Depth * mDelay, static_cast<ALfloat>(mDelay - mindelay));
-
- mFeedback = props->Chorus.Feedback;
-
- /* Gains for left and right sides */
- ALfloat coeffs[2][MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f}, 0.0f, coeffs[0]);
- CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f}, 0.0f, coeffs[1]);
-
- 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);
-
- ALfloat rate{props->Chorus.Rate};
- if(!(rate > 0.0f))
- {
- mLfoOffset = 0;
- mLfoRange = 1;
- mLfoScale = 0.0f;
- mLfoDisp = 0;
- }
- else
- {
- /* Calculate LFO coefficient (number of samples per cycle). Limit the
- * max range to avoid overflow when calculating the displacement.
- */
- ALsizei lfo_range = float2int(minf(frequency/rate + 0.5f, static_cast<ALfloat>(INT_MAX/360 - 180)));
-
- mLfoOffset = float2int(static_cast<ALfloat>(mLfoOffset)/mLfoRange*lfo_range + 0.5f) % lfo_range;
- mLfoRange = lfo_range;
- switch(mWaveform)
- {
- case WaveForm::Triangle:
- mLfoScale = 4.0f / mLfoRange;
- break;
- case WaveForm::Sinusoid:
- mLfoScale = al::MathDefs<float>::Tau() / mLfoRange;
- break;
- }
-
- /* Calculate lfo phase displacement */
- ALint phase{props->Chorus.Phase};
- if(phase < 0) phase = 360 + phase;
- mLfoDisp = (mLfoRange*phase + 180) / 360;
- }
-}
-
-void ChorusState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- const auto bufmask = static_cast<ALsizei>(mSampleBuffer.size()-1);
- const ALfloat feedback{mFeedback};
- const ALsizei avgdelay{(mDelay + (FRACTIONONE>>1)) >> FRACTIONBITS};
- ALfloat *RESTRICT delaybuf{mSampleBuffer.data()};
- ALsizei offset{mOffset};
-
- for(ALsizei base{0};base < samplesToDo;)
- {
- const ALsizei todo = mini(256, samplesToDo-base);
- ALint moddelays[2][256];
- alignas(16) ALfloat temps[2][256];
-
- if(mWaveform == WaveForm::Sinusoid)
- {
- GetSinusoidDelays(moddelays[0], mLfoOffset, mLfoRange, mLfoScale, mDepth, mDelay,
- todo);
- GetSinusoidDelays(moddelays[1], (mLfoOffset+mLfoDisp)%mLfoRange, mLfoRange, mLfoScale,
- mDepth, mDelay, todo);
- }
- else /*if(mWaveform == WaveForm::Triangle)*/
- {
- GetTriangleDelays(moddelays[0], mLfoOffset, mLfoRange, mLfoScale, mDepth, mDelay,
- todo);
- GetTriangleDelays(moddelays[1], (mLfoOffset+mLfoDisp)%mLfoRange, mLfoRange, mLfoScale,
- mDepth, mDelay, todo);
- }
- mLfoOffset = (mLfoOffset+todo) % mLfoRange;
-
- for(ALsizei i{0};i < todo;i++)
- {
- // Feed the buffer's input first (necessary for delays < 1).
- delaybuf[offset&bufmask] = samplesIn[0][base+i];
-
- // Tap for the left output.
- ALint delay{offset - (moddelays[0][i]>>FRACTIONBITS)};
- ALfloat mu{(moddelays[0][i]&FRACTIONMASK) * (1.0f/FRACTIONONE)};
- temps[0][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
- delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
- mu);
-
- // Tap for the right output.
- delay = offset - (moddelays[1][i]>>FRACTIONBITS);
- mu = (moddelays[1][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
- temps[1][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
- delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
- mu);
-
- // Accumulate feedback from the average delay of the taps.
- delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
- offset++;
- }
-
- for(ALsizei c{0};c < 2;c++)
- MixSamples(temps[c], samplesOut, mGains[c].Current, mGains[c].Target, samplesToDo-base,
- base, todo);
-
- base += todo;
- }
-
- mOffset = offset;
-}
-
-
-void Chorus_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_CHORUS_WAVEFORM:
- if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid chorus waveform");
- props->Chorus.Waveform = val;
- break;
-
- case AL_CHORUS_PHASE:
- if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus phase out of range");
- props->Chorus.Phase = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
- }
-}
-void Chorus_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Chorus_setParami(props, context, param, vals[0]); }
-void Chorus_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_CHORUS_RATE:
- if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus rate out of range");
- props->Chorus.Rate = val;
- break;
-
- case AL_CHORUS_DEPTH:
- if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus depth out of range");
- props->Chorus.Depth = val;
- break;
-
- case AL_CHORUS_FEEDBACK:
- if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus feedback out of range");
- props->Chorus.Feedback = val;
- break;
-
- case AL_CHORUS_DELAY:
- if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus delay out of range");
- props->Chorus.Delay = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
- }
-}
-void Chorus_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Chorus_setParamf(props, context, param, vals[0]); }
-
-void Chorus_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_CHORUS_WAVEFORM:
- *val = props->Chorus.Waveform;
- break;
-
- case AL_CHORUS_PHASE:
- *val = props->Chorus.Phase;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
- }
-}
-void Chorus_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Chorus_getParami(props, context, param, vals); }
-void Chorus_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_CHORUS_RATE:
- *val = props->Chorus.Rate;
- break;
-
- case AL_CHORUS_DEPTH:
- *val = props->Chorus.Depth;
- break;
-
- case AL_CHORUS_FEEDBACK:
- *val = props->Chorus.Feedback;
- break;
-
- case AL_CHORUS_DELAY:
- *val = props->Chorus.Delay;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
- }
-}
-void Chorus_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Chorus_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Chorus);
-
-
-struct ChorusStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ChorusState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Chorus_vtable; }
-};
-
-EffectProps ChorusStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM;
- props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
- props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE;
- props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
- props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
- props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
- return props;
-}
-
-
-void Flanger_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_FLANGER_WAVEFORM:
- if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid flanger waveform");
- props->Chorus.Waveform = val;
- break;
-
- case AL_FLANGER_PHASE:
- if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger phase out of range");
- props->Chorus.Phase = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
- }
-}
-void Flanger_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Flanger_setParami(props, context, param, vals[0]); }
-void Flanger_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_FLANGER_RATE:
- if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger rate out of range");
- props->Chorus.Rate = val;
- break;
-
- case AL_FLANGER_DEPTH:
- if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger depth out of range");
- props->Chorus.Depth = val;
- break;
-
- case AL_FLANGER_FEEDBACK:
- if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger feedback out of range");
- props->Chorus.Feedback = val;
- break;
-
- case AL_FLANGER_DELAY:
- if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger delay out of range");
- props->Chorus.Delay = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
- }
-}
-void Flanger_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Flanger_setParamf(props, context, param, vals[0]); }
-
-void Flanger_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_FLANGER_WAVEFORM:
- *val = props->Chorus.Waveform;
- break;
-
- case AL_FLANGER_PHASE:
- *val = props->Chorus.Phase;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
- }
-}
-void Flanger_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Flanger_getParami(props, context, param, vals); }
-void Flanger_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_FLANGER_RATE:
- *val = props->Chorus.Rate;
- break;
-
- case AL_FLANGER_DEPTH:
- *val = props->Chorus.Depth;
- break;
-
- case AL_FLANGER_FEEDBACK:
- *val = props->Chorus.Feedback;
- break;
-
- case AL_FLANGER_DELAY:
- *val = props->Chorus.Delay;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
- }
-}
-void Flanger_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Flanger_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Flanger);
-
-
-/* Flanger is basically a chorus with a really short delay. They can both use
- * the same processing functions, so piggyback flanger on the chorus functions.
- */
-struct FlangerStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ChorusState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Flanger_vtable; }
-};
-
-EffectProps FlangerStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Chorus.Waveform = AL_FLANGER_DEFAULT_WAVEFORM;
- props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE;
- props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE;
- props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH;
- props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
- props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *ChorusStateFactory_getFactory()
-{
- static ChorusStateFactory ChorusFactory{};
- return &ChorusFactory;
-}
-
-EffectStateFactory *FlangerStateFactory_getFactory()
-{
- static FlangerStateFactory FlangerFactory{};
- return &FlangerFactory;
-}
diff --git a/Alc/effects/compressor.cpp b/Alc/effects/compressor.cpp
deleted file mode 100644
index 4a487097..00000000
--- a/Alc/effects/compressor.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Anis A. Hireche
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cstdlib>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alu.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "vecmat.h"
-
-
-namespace {
-
-#define AMP_ENVELOPE_MIN 0.5f
-#define AMP_ENVELOPE_MAX 2.0f
-
-#define ATTACK_TIME 0.1f /* 100ms to rise from min to max */
-#define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
-
-
-struct CompressorState final : public EffectState {
- /* Effect gains for each channel */
- ALfloat mGain[MAX_AMBI_CHANNELS][MAX_OUTPUT_CHANNELS]{};
-
- /* Effect parameters */
- ALboolean mEnabled{AL_TRUE};
- ALfloat mAttackMult{1.0f};
- ALfloat mReleaseMult{1.0f};
- ALfloat mEnvFollower{1.0f};
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(CompressorState)
-};
-
-ALboolean CompressorState::deviceUpdate(const ALCdevice *device)
-{
- /* Number of samples to do a full attack and release (non-integer sample
- * counts are okay).
- */
- const ALfloat attackCount = static_cast<ALfloat>(device->Frequency) * ATTACK_TIME;
- const ALfloat releaseCount = static_cast<ALfloat>(device->Frequency) * RELEASE_TIME;
-
- /* Calculate per-sample multipliers to attack and release at the desired
- * rates.
- */
- mAttackMult = std::pow(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount);
- mReleaseMult = std::pow(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
-
- return AL_TRUE;
-}
-
-void CompressorState::update(const ALCcontext*, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- mEnabled = props->Compressor.OnOff;
-
- mOutTarget = target.Main->Buffer;
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- auto coeffs = GetAmbiIdentityRow(i);
- ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mGain[i]);
- }
-}
-
-void CompressorState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- for(ALsizei base{0};base < samplesToDo;)
- {
- ALfloat gains[256];
- const ALsizei td{mini(256, samplesToDo-base)};
-
- /* Generate the per-sample gains from the signal envelope. */
- ALfloat env{mEnvFollower};
- if(mEnabled)
- {
- for(ALsizei i{0};i < td;++i)
- {
- /* Clamp the absolute amplitude to the defined envelope limits,
- * then attack or release the envelope to reach it.
- */
- const ALfloat amplitude{clampf(std::fabs(samplesIn[0][base+i]), AMP_ENVELOPE_MIN,
- AMP_ENVELOPE_MAX)};
- if(amplitude > env)
- env = minf(env*mAttackMult, amplitude);
- else if(amplitude < env)
- env = maxf(env*mReleaseMult, amplitude);
-
- /* Apply the reciprocal of the envelope to normalize the volume
- * (compress the dynamic range).
- */
- gains[i] = 1.0f / env;
- }
- }
- else
- {
- /* Same as above, except the amplitude is forced to 1. This helps
- * ensure smooth gain changes when the compressor is turned on and
- * off.
- */
- for(ALsizei i{0};i < td;++i)
- {
- const ALfloat amplitude{1.0f};
- if(amplitude > env)
- env = minf(env*mAttackMult, amplitude);
- else if(amplitude < env)
- env = maxf(env*mReleaseMult, amplitude);
-
- gains[i] = 1.0f / env;
- }
- }
- mEnvFollower = env;
-
- /* Now compress the signal amplitude to output. */
- ASSUME(numInput > 0);
- for(ALsizei j{0};j < numInput;j++)
- {
- const ALfloat *outgains{mGain[j]};
- for(FloatBufferLine &output : samplesOut)
- {
- const ALfloat gain{*(outgains++)};
- if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
-
- for(ALsizei i{0};i < td;i++)
- output[base+i] += samplesIn[j][base+i] * gains[i] * gain;
- }
- }
-
- base += td;
- }
-}
-
-
-void Compressor_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_COMPRESSOR_ONOFF:
- if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Compressor state out of range");
- props->Compressor.OnOff = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
- param);
- }
-}
-void Compressor_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Compressor_setParami(props, context, param, vals[0]); }
-void Compressor_setParamf(EffectProps*, ALCcontext *context, ALenum param, ALfloat)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
-void Compressor_setParamfv(EffectProps*, ALCcontext *context, ALenum param, const ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
-
-void Compressor_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_COMPRESSOR_ONOFF:
- *val = props->Compressor.OnOff;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
- param);
- }
-}
-void Compressor_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Compressor_getParami(props, context, param, vals); }
-void Compressor_getParamf(const EffectProps*, ALCcontext *context, ALenum param, ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
-void Compressor_getParamfv(const EffectProps*, ALCcontext *context, ALenum param, ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
-
-DEFINE_ALEFFECT_VTABLE(Compressor);
-
-
-struct CompressorStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new CompressorState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Compressor_vtable; }
-};
-
-EffectProps CompressorStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *CompressorStateFactory_getFactory()
-{
- static CompressorStateFactory CompressorFactory{};
- return &CompressorFactory;
-}
diff --git a/Alc/effects/dedicated.cpp b/Alc/effects/dedicated.cpp
deleted file mode 100644
index b31b3750..00000000
--- a/Alc/effects/dedicated.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011 by Chris Robinson.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cstdlib>
-#include <cmath>
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-
-namespace {
-
-struct DedicatedState final : public EffectState {
- ALfloat mCurrentGains[MAX_OUTPUT_CHANNELS];
- ALfloat mTargetGains[MAX_OUTPUT_CHANNELS];
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(DedicatedState)
-};
-
-ALboolean DedicatedState::deviceUpdate(const ALCdevice*)
-{
- std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f);
- return AL_TRUE;
-}
-
-void DedicatedState::update(const ALCcontext*, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
-
- const ALfloat Gain{slot->Params.Gain * props->Dedicated.Gain};
-
- if(slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
- {
- const int idx{!target.RealOut ? -1 : GetChannelIdxByName(*target.RealOut, LFE)};
- if(idx != -1)
- {
- mOutTarget = target.RealOut->Buffer;
- mTargetGains[idx] = Gain;
- }
- }
- else if(slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
- {
- /* Dialog goes to the front-center speaker if it exists, otherwise it
- * plays from the front-center location. */
- const int idx{!target.RealOut ? -1 : GetChannelIdxByName(*target.RealOut, FrontCenter)};
- if(idx != -1)
- {
- mOutTarget = target.RealOut->Buffer;
- mTargetGains[idx] = Gain;
- }
- else
- {
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f, coeffs);
-
- mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs, Gain, mTargetGains);
- }
- }
-}
-
-void DedicatedState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- MixSamples(samplesIn[0].data(), samplesOut, mCurrentGains, mTargetGains, samplesToDo, 0,
- samplesToDo);
-}
-
-
-void Dedicated_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
-void Dedicated_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
-void Dedicated_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_DEDICATED_GAIN:
- if(!(val >= 0.0f && std::isfinite(val)))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Dedicated gain out of range");
- props->Dedicated.Gain = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
- }
-}
-void Dedicated_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Dedicated_setParamf(props, context, param, vals[0]); }
-
-void Dedicated_getParami(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
-void Dedicated_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
-void Dedicated_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_DEDICATED_GAIN:
- *val = props->Dedicated.Gain;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
- }
-}
-void Dedicated_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Dedicated_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Dedicated);
-
-
-struct DedicatedStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new DedicatedState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Dedicated_vtable; }
-};
-
-EffectProps DedicatedStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Dedicated.Gain = 1.0f;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *DedicatedStateFactory_getFactory()
-{
- static DedicatedStateFactory DedicatedFactory{};
- return &DedicatedFactory;
-}
diff --git a/Alc/effects/distortion.cpp b/Alc/effects/distortion.cpp
deleted file mode 100644
index 59557395..00000000
--- a/Alc/effects/distortion.cpp
+++ /dev/null
@@ -1,269 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Mike Gorchak
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-
-#include <cmath>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/biquad.h"
-
-
-namespace {
-
-struct DistortionState final : public EffectState {
- /* Effect gains for each channel */
- ALfloat mGain[MAX_OUTPUT_CHANNELS]{};
-
- /* Effect parameters */
- BiquadFilter mLowpass;
- BiquadFilter mBandpass;
- ALfloat mAttenuation{};
- ALfloat mEdgeCoeff{};
-
- ALfloat mBuffer[2][BUFFERSIZE]{};
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(DistortionState)
-};
-
-ALboolean DistortionState::deviceUpdate(const ALCdevice*)
-{
- mLowpass.clear();
- mBandpass.clear();
- return AL_TRUE;
-}
-
-void DistortionState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device{context->Device};
-
- /* Store waveshaper edge settings. */
- const ALfloat edge{
- minf(std::sin(al::MathDefs<float>::Pi()*0.5f * props->Distortion.Edge), 0.99f)};
- mEdgeCoeff = 2.0f * edge / (1.0f-edge);
-
- ALfloat cutoff{props->Distortion.LowpassCutoff};
- /* Bandwidth value is constant in octaves. */
- ALfloat bandwidth{(cutoff / 2.0f) / (cutoff * 0.67f)};
- /* Multiply sampling frequency by the amount of oversampling done during
- * processing.
- */
- auto frequency = static_cast<ALfloat>(device->Frequency);
- mLowpass.setParams(BiquadType::LowPass, 1.0f, cutoff / (frequency*4.0f),
- mLowpass.rcpQFromBandwidth(cutoff / (frequency*4.0f), bandwidth));
-
- cutoff = props->Distortion.EQCenter;
- /* Convert bandwidth in Hz to octaves. */
- bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
- mBandpass.setParams(BiquadType::BandPass, 1.0f, cutoff / (frequency*4.0f),
- mBandpass.rcpQFromBandwidth(cutoff / (frequency*4.0f), bandwidth));
-
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f, coeffs);
-
- mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs, slot->Params.Gain*props->Distortion.Gain, mGain);
-}
-
-void DistortionState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- const ALfloat fc{mEdgeCoeff};
- for(ALsizei base{0};base < samplesToDo;)
- {
- /* Perform 4x oversampling to avoid aliasing. Oversampling greatly
- * improves distortion quality and allows to implement lowpass and
- * bandpass filters using high frequencies, at which classic IIR
- * filters became unstable.
- */
- ALsizei todo{mini(BUFFERSIZE, (samplesToDo-base) * 4)};
-
- /* Fill oversample buffer using zero stuffing. Multiply the sample by
- * the amount of oversampling to maintain the signal's power.
- */
- for(ALsizei i{0};i < todo;i++)
- mBuffer[0][i] = !(i&3) ? samplesIn[0][(i>>2)+base] * 4.0f : 0.0f;
-
- /* First step, do lowpass filtering of original signal. Additionally
- * perform buffer interpolation and lowpass cutoff for oversampling
- * (which is fortunately first step of distortion). So combine three
- * operations into the one.
- */
- mLowpass.process(mBuffer[1], mBuffer[0], todo);
-
- /* Second step, do distortion using waveshaper function to emulate
- * signal processing during tube overdriving. Three steps of
- * waveshaping are intended to modify waveform without boost/clipping/
- * attenuation process.
- */
- for(ALsizei i{0};i < todo;i++)
- {
- ALfloat smp{mBuffer[1][i]};
-
- smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
- smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
- smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
-
- mBuffer[0][i] = smp;
- }
-
- /* Third step, do bandpass filtering of distorted signal. */
- mBandpass.process(mBuffer[1], mBuffer[0], todo);
-
- todo >>= 2;
- const ALfloat *outgains{mGain};
- for(FloatBufferLine &output : samplesOut)
- {
- /* Fourth step, final, do attenuation and perform decimation,
- * storing only one sample out of four.
- */
- const ALfloat gain{*(outgains++)};
- if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
-
- for(ALsizei i{0};i < todo;i++)
- output[base+i] += gain * mBuffer[1][i*4];
- }
-
- base += todo;
- }
-}
-
-
-void Distortion_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
-void Distortion_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
-void Distortion_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_DISTORTION_EDGE:
- if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion edge out of range");
- props->Distortion.Edge = val;
- break;
-
- case AL_DISTORTION_GAIN:
- if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion gain out of range");
- props->Distortion.Gain = val;
- break;
-
- case AL_DISTORTION_LOWPASS_CUTOFF:
- if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion low-pass cutoff out of range");
- props->Distortion.LowpassCutoff = val;
- break;
-
- case AL_DISTORTION_EQCENTER:
- if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ center out of range");
- props->Distortion.EQCenter = val;
- break;
-
- case AL_DISTORTION_EQBANDWIDTH:
- if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range");
- props->Distortion.EQBandwidth = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
- param);
- }
-}
-void Distortion_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Distortion_setParamf(props, context, param, vals[0]); }
-
-void Distortion_getParami(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
-void Distortion_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
-void Distortion_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_DISTORTION_EDGE:
- *val = props->Distortion.Edge;
- break;
-
- case AL_DISTORTION_GAIN:
- *val = props->Distortion.Gain;
- break;
-
- case AL_DISTORTION_LOWPASS_CUTOFF:
- *val = props->Distortion.LowpassCutoff;
- break;
-
- case AL_DISTORTION_EQCENTER:
- *val = props->Distortion.EQCenter;
- break;
-
- case AL_DISTORTION_EQBANDWIDTH:
- *val = props->Distortion.EQBandwidth;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
- param);
- }
-}
-void Distortion_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Distortion_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Distortion);
-
-
-struct DistortionStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new DistortionState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Distortion_vtable; }
-};
-
-EffectProps DistortionStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
- props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN;
- props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
- props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
- props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *DistortionStateFactory_getFactory()
-{
- static DistortionStateFactory DistortionFactory{};
- return &DistortionFactory;
-}
diff --git a/Alc/effects/echo.cpp b/Alc/effects/echo.cpp
deleted file mode 100644
index c10f2eb2..00000000
--- a/Alc/effects/echo.cpp
+++ /dev/null
@@ -1,271 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2009 by Chris Robinson.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alFilter.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/biquad.h"
-#include "vector.h"
-
-
-namespace {
-
-struct EchoState final : public EffectState {
- al::vector<ALfloat,16> mSampleBuffer;
-
- // The echo is two tap. The delay is the number of samples from before the
- // current offset
- struct {
- ALsizei delay{0};
- } mTap[2];
- ALsizei mOffset{0};
-
- /* The panning gains for the two taps */
- struct {
- ALfloat Current[MAX_OUTPUT_CHANNELS]{};
- ALfloat Target[MAX_OUTPUT_CHANNELS]{};
- } mGains[2];
-
- BiquadFilter mFilter;
- ALfloat mFeedGain{0.0f};
-
- alignas(16) ALfloat mTempBuffer[2][BUFFERSIZE];
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(EchoState)
-};
-
-ALboolean EchoState::deviceUpdate(const ALCdevice *Device)
-{
- ALuint maxlen;
-
- // Use the next power of 2 for the buffer length, so the tap offsets can be
- // wrapped using a mask instead of a modulo
- maxlen = float2int(AL_ECHO_MAX_DELAY*Device->Frequency + 0.5f) +
- float2int(AL_ECHO_MAX_LRDELAY*Device->Frequency + 0.5f);
- maxlen = NextPowerOf2(maxlen);
- if(maxlen <= 0) return AL_FALSE;
-
- if(maxlen != mSampleBuffer.size())
- {
- mSampleBuffer.resize(maxlen);
- mSampleBuffer.shrink_to_fit();
- }
-
- std::fill(mSampleBuffer.begin(), mSampleBuffer.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);
- }
-
- return AL_TRUE;
-}
-
-void EchoState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device = context->Device;
- const auto frequency = static_cast<ALfloat>(device->Frequency);
-
- mTap[0].delay = maxi(float2int(props->Echo.Delay*frequency + 0.5f), 1);
- mTap[1].delay = float2int(props->Echo.LRDelay*frequency + 0.5f) + mTap[0].delay;
-
- const ALfloat gainhf{maxf(1.0f - props->Echo.Damping, 0.0625f)}; /* Limit -24dB */
- mFilter.setParams(BiquadType::HighShelf, gainhf, LOWPASSFREQREF/frequency,
- mFilter.rcpQFromSlope(gainhf, 1.0f));
-
- mFeedGain = props->Echo.Feedback;
-
- /* Convert echo spread (where 0 = center, +/-1 = sides) to angle. */
- const ALfloat angle{std::asin(props->Echo.Spread)};
-
- ALfloat coeffs[2][MAX_AMBI_CHANNELS];
- CalcAngleCoeffs(-angle, 0.0f, 0.0f, coeffs[0]);
- CalcAngleCoeffs( angle, 0.0f, 0.0f, coeffs[1]);
-
- 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);
-}
-
-void EchoState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- const auto mask = static_cast<ALsizei>(mSampleBuffer.size()-1);
- ALfloat *RESTRICT delaybuf{mSampleBuffer.data()};
- ALsizei offset{mOffset};
- ALsizei tap1{offset - mTap[0].delay};
- ALsizei tap2{offset - mTap[1].delay};
- ALfloat z1, z2;
-
- ASSUME(samplesToDo > 0);
- ASSUME(mask > 0);
-
- std::tie(z1, z2) = mFilter.getComponents();
- for(ALsizei i{0};i < samplesToDo;)
- {
- offset &= mask;
- tap1 &= mask;
- tap2 &= mask;
-
- ALsizei td{mini(mask+1 - maxi(offset, maxi(tap1, tap2)), samplesToDo-i)};
- do {
- /* Feed the delay buffer's input first. */
- delaybuf[offset] = samplesIn[0][i];
-
- /* Get delayed output from the first and second taps. Use the
- * second tap for feedback.
- */
- mTempBuffer[0][i] = delaybuf[tap1++];
- mTempBuffer[1][i] = delaybuf[tap2++];
- const float feedb{mTempBuffer[1][i++]};
-
- /* Add feedback to the delay buffer with damping and attenuation. */
- delaybuf[offset++] += mFilter.processOne(feedb, z1, z2) * mFeedGain;
- } while(--td);
- }
- mFilter.setComponents(z1, z2);
- mOffset = offset;
-
- for(ALsizei c{0};c < 2;c++)
- MixSamples(mTempBuffer[c], samplesOut, mGains[c].Current, mGains[c].Target, samplesToDo, 0,
- samplesToDo);
-}
-
-
-void Echo_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
-void Echo_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
-void Echo_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_ECHO_DELAY:
- if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo delay out of range");
- props->Echo.Delay = val;
- break;
-
- case AL_ECHO_LRDELAY:
- if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo LR delay out of range");
- props->Echo.LRDelay = val;
- break;
-
- case AL_ECHO_DAMPING:
- if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo damping out of range");
- props->Echo.Damping = val;
- break;
-
- case AL_ECHO_FEEDBACK:
- if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo feedback out of range");
- props->Echo.Feedback = val;
- break;
-
- case AL_ECHO_SPREAD:
- if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo spread out of range");
- props->Echo.Spread = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
- }
-}
-void Echo_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Echo_setParamf(props, context, param, vals[0]); }
-
-void Echo_getParami(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
-void Echo_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
-void Echo_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_ECHO_DELAY:
- *val = props->Echo.Delay;
- break;
-
- case AL_ECHO_LRDELAY:
- *val = props->Echo.LRDelay;
- break;
-
- case AL_ECHO_DAMPING:
- *val = props->Echo.Damping;
- break;
-
- case AL_ECHO_FEEDBACK:
- *val = props->Echo.Feedback;
- break;
-
- case AL_ECHO_SPREAD:
- *val = props->Echo.Spread;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
- }
-}
-void Echo_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Echo_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Echo);
-
-
-struct EchoStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new EchoState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Echo_vtable; }
-};
-
-EffectProps EchoStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Echo.Delay = AL_ECHO_DEFAULT_DELAY;
- props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
- props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING;
- props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
- props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *EchoStateFactory_getFactory()
-{
- static EchoStateFactory EchoFactory{};
- return &EchoFactory;
-}
diff --git a/Alc/effects/equalizer.cpp b/Alc/effects/equalizer.cpp
deleted file mode 100644
index 69ab5021..00000000
--- a/Alc/effects/equalizer.cpp
+++ /dev/null
@@ -1,337 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Mike Gorchak
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-
-#include <algorithm>
-#include <functional>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/biquad.h"
-#include "vecmat.h"
-
-
-namespace {
-
-/* The document "Effects Extension Guide.pdf" says that low and high *
- * frequencies are cutoff frequencies. This is not fully correct, they *
- * are corner frequencies for low and high shelf filters. If they were *
- * just cutoff frequencies, there would be no need in cutoff frequency *
- * gains, which are present. Documentation for "Creative Proteus X2" *
- * software describes 4-band equalizer functionality in a much better *
- * way. This equalizer seems to be a predecessor of OpenAL 4-band *
- * equalizer. With low and high shelf filters we are able to cutoff *
- * frequencies below and/or above corner frequencies using attenuation *
- * gains (below 1.0) and amplify all low and/or high frequencies using *
- * gains above 1.0. *
- * *
- * Low-shelf Low Mid Band High Mid Band High-shelf *
- * corner center center corner *
- * frequency frequency frequency frequency *
- * 50Hz..800Hz 200Hz..3000Hz 1000Hz..8000Hz 4000Hz..16000Hz *
- * *
- * | | | | *
- * | | | | *
- * B -----+ /--+--\ /--+--\ +----- *
- * O |\ | | | | | | /| *
- * O | \ - | - - | - / | *
- * S + | \ | | | | | | / | *
- * T | | | | | | | | | | *
- * ---------+---------------+------------------+---------------+-------- *
- * C | | | | | | | | | | *
- * U - | / | | | | | | \ | *
- * T | / - | - - | - \ | *
- * O |/ | | | | | | \| *
- * F -----+ \--+--/ \--+--/ +----- *
- * F | | | | *
- * | | | | *
- * *
- * Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
- * up to +18dB amplification. Band width varies from 0.01 up to 1.0 in *
- * octaves for two mid bands. *
- * *
- * Implementation is based on the "Cookbook formulae for audio EQ biquad *
- * filter coefficients" by Robert Bristow-Johnson *
- * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
-
-
-struct EqualizerState final : public EffectState {
- struct {
- /* Effect parameters */
- BiquadFilter filter[4];
-
- /* Effect gains for each channel */
- ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]{};
- ALfloat TargetGains[MAX_OUTPUT_CHANNELS]{};
- } mChans[MAX_AMBI_CHANNELS];
-
- ALfloat mSampleBuffer[BUFFERSIZE]{};
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(EqualizerState)
-};
-
-ALboolean EqualizerState::deviceUpdate(const ALCdevice*)
-{
- 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);
- }
- return AL_TRUE;
-}
-
-void EqualizerState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device = context->Device;
- auto frequency = static_cast<ALfloat>(device->Frequency);
- ALfloat gain, f0norm;
-
- /* Calculate coefficients for the each type of filter. Note that the shelf
- * filters' gain is for the reference frequency, which is the centerpoint
- * of the transition band.
- */
- gain = maxf(sqrtf(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 = maxf(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 = maxf(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 = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
- f0norm = props->Equalizer.HighCutoff/frequency;
- mChans[0].filter[3].setParams(BiquadType::HighShelf, gain, f0norm,
- BiquadFilter::rcpQFromSlope(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]);
- }
-
- mOutTarget = target.Main->Buffer;
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- auto coeffs = GetAmbiIdentityRow(i);
- ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mChans[i].TargetGains);
- }
-}
-
-void EqualizerState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- ASSUME(numInput > 0);
- for(ALsizei c{0};c < numInput;c++)
- {
- mChans[c].filter[0].process(mSampleBuffer, samplesIn[c].data(), samplesToDo);
- mChans[c].filter[1].process(mSampleBuffer, mSampleBuffer, samplesToDo);
- mChans[c].filter[2].process(mSampleBuffer, mSampleBuffer, samplesToDo);
- mChans[c].filter[3].process(mSampleBuffer, mSampleBuffer, samplesToDo);
-
- MixSamples(mSampleBuffer, samplesOut, mChans[c].CurrentGains, mChans[c].TargetGains,
- samplesToDo, 0, samplesToDo);
- }
-}
-
-
-void Equalizer_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
-void Equalizer_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, 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:
- alSetError(context, 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*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
-void Equalizer_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, 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:
- alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
- }
-}
-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; }
-};
-
-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()
-{
- static EqualizerStateFactory EqualizerFactory{};
- return &EqualizerFactory;
-}
diff --git a/Alc/effects/fshifter.cpp b/Alc/effects/fshifter.cpp
deleted file mode 100644
index b47aa00e..00000000
--- a/Alc/effects/fshifter.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2018 by Raul Herraiz.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-#include <array>
-#include <complex>
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-#include "alcomplex.h"
-
-namespace {
-
-using complex_d = std::complex<double>;
-
-#define HIL_SIZE 1024
-#define OVERSAMP (1<<2)
-
-#define HIL_STEP (HIL_SIZE / OVERSAMP)
-#define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))
-
-/* 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(ALsizei i{0};i < HIL_SIZE>>1;i++)
- {
- ALdouble val = std::sin(al::MathDefs<double>::Pi() * i / ALdouble{HIL_SIZE-1});
- ret[i] = ret[HIL_SIZE-1-i] = val * val;
- }
- return ret;
-}
-alignas(16) const std::array<ALdouble,HIL_SIZE> HannWindow = InitHannWindow();
-
-
-struct FshifterState final : public EffectState {
- /* Effect parameters */
- ALsizei mCount{};
- ALsizei mPhaseStep{};
- ALsizei mPhase{};
- ALdouble mLdSign{};
-
- /*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]{};
-
- alignas(16) ALfloat mBufferOut[BUFFERSIZE]{};
-
- /* Effect gains for each output channel */
- ALfloat mCurrentGains[MAX_OUTPUT_CHANNELS]{};
- ALfloat mTargetGains[MAX_OUTPUT_CHANNELS]{};
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(FshifterState)
-};
-
-ALboolean FshifterState::deviceUpdate(const ALCdevice*)
-{
- /* (Re-)initializing parameters and clear the buffers. */
- mCount = FIFO_LATENCY;
- mPhaseStep = 0;
- mPhase = 0;
- mLdSign = 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{});
-
- std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f);
- std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
-
- return AL_TRUE;
-}
-
-void FshifterState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device{context->Device};
-
- ALfloat step{props->Fshifter.Frequency / static_cast<ALfloat>(device->Frequency)};
- mPhaseStep = fastf2i(minf(step, 0.5f) * FRACTIONONE);
-
- switch(props->Fshifter.LeftDirection)
- {
- case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
- mLdSign = -1.0;
- break;
-
- case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
- mLdSign = 1.0;
- break;
-
- case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
- mPhase = 0;
- mPhaseStep = 0;
- break;
- }
-
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f, coeffs);
-
- mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs, slot->Params.Gain, mTargetGains);
-}
-
-void FshifterState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- static constexpr complex_d complex_zero{0.0, 0.0};
- ALfloat *RESTRICT BufferOut = mBufferOut;
- ALsizei j, k, base;
-
- for(base = 0;base < samplesToDo;)
- {
- const ALsizei todo{mini(HIL_SIZE-mCount, samplesToDo-base)};
-
- ASSUME(todo > 0);
-
- /* 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;
-
- /* Check whether FIFO buffer is filled */
- if(mCount < HIL_SIZE) continue;
- mCount = FIFO_LATENCY;
-
- /* 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);
- }
-
- /* 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];
- }
-
- /* Process frequency shifter using the analytic signal obtained. */
- for(k = 0;k < samplesToDo;k++)
- {
- double phase = mPhase * ((1.0/FRACTIONONE) * al::MathDefs<double>::Tau());
- BufferOut[k] = static_cast<float>(mOutdata[k].real()*std::cos(phase) +
- mOutdata[k].imag()*std::sin(phase)*mLdSign);
-
- mPhase += mPhaseStep;
- mPhase &= FRACTIONMASK;
- }
-
- /* Now, mix the processed sound data to the output. */
- MixSamples(BufferOut, samplesOut, mCurrentGains, mTargetGains, maxi(samplesToDo, 512), 0,
- samplesToDo);
-}
-
-
-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:
- alSetError(context, 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:
- alSetError(context, 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:
- alSetError(context, 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:
- alSetError(context, 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; }
-};
-
-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()
-{
- static FshifterStateFactory FshifterFactory{};
- return &FshifterFactory;
-}
diff --git a/Alc/effects/modulator.cpp b/Alc/effects/modulator.cpp
deleted file mode 100644
index 086482d7..00000000
--- a/Alc/effects/modulator.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2009 by Chris Robinson.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-
-#include <cmath>
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/biquad.h"
-#include "vecmat.h"
-
-
-namespace {
-
-#define MAX_UPDATE_SAMPLES 128
-
-#define WAVEFORM_FRACBITS 24
-#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
-#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
-
-inline ALfloat Sin(ALsizei index)
-{
- return std::sin(static_cast<ALfloat>(index) *
- (al::MathDefs<float>::Tau() / ALfloat{WAVEFORM_FRACONE}));
-}
-
-inline ALfloat Saw(ALsizei index)
-{
- return static_cast<ALfloat>(index)*(2.0f/WAVEFORM_FRACONE) - 1.0f;
-}
-
-inline ALfloat Square(ALsizei index)
-{
- return static_cast<ALfloat>(((index>>(WAVEFORM_FRACBITS-2))&2) - 1);
-}
-
-inline ALfloat One(ALsizei)
-{
- return 1.0f;
-}
-
-template<ALfloat func(ALsizei)>
-void Modulate(ALfloat *RESTRICT dst, ALsizei index, const ALsizei step, ALsizei todo)
-{
- ALsizei i;
- for(i = 0;i < todo;i++)
- {
- index += step;
- index &= WAVEFORM_FRACMASK;
- dst[i] = func(index);
- }
-}
-
-
-struct ModulatorState final : public EffectState {
- void (*mGetSamples)(ALfloat*RESTRICT, ALsizei, const ALsizei, ALsizei){};
-
- ALsizei mIndex{0};
- ALsizei mStep{1};
-
- struct {
- BiquadFilter Filter;
-
- ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]{};
- ALfloat TargetGains[MAX_OUTPUT_CHANNELS]{};
- } mChans[MAX_AMBI_CHANNELS];
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(ModulatorState)
-};
-
-ALboolean ModulatorState::deviceUpdate(const ALCdevice*)
-{
- for(auto &e : mChans)
- {
- e.Filter.clear();
- std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
- }
- return AL_TRUE;
-}
-
-void ModulatorState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device{context->Device};
-
- const float step{props->Modulator.Frequency / static_cast<ALfloat>(device->Frequency)};
- mStep = fastf2i(clampf(step*WAVEFORM_FRACONE, 0.0f, ALfloat{WAVEFORM_FRACONE-1}));
-
- if(mStep == 0)
- mGetSamples = Modulate<One>;
- else if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
- mGetSamples = Modulate<Sin>;
- else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
- mGetSamples = Modulate<Saw>;
- else /*if(props->Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
- mGetSamples = Modulate<Square>;
-
- ALfloat f0norm{props->Modulator.HighPassCutoff / static_cast<ALfloat>(device->Frequency)};
- f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f);
- /* Bandwidth value is constant in octaves. */
- mChans[0].Filter.setParams(BiquadType::HighPass, 1.0f, f0norm,
- BiquadFilter::rcpQFromBandwidth(f0norm, 0.75f));
- for(size_t i{1u};i < slot->Wet.Buffer.size();++i)
- mChans[i].Filter.copyParamsFrom(mChans[0].Filter);
-
- mOutTarget = target.Main->Buffer;
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- auto coeffs = GetAmbiIdentityRow(i);
- ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mChans[i].TargetGains);
- }
-}
-
-void ModulatorState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- for(ALsizei base{0};base < samplesToDo;)
- {
- alignas(16) ALfloat modsamples[MAX_UPDATE_SAMPLES];
- ALsizei td = mini(MAX_UPDATE_SAMPLES, samplesToDo-base);
- ALsizei c, i;
-
- mGetSamples(modsamples, mIndex, mStep, td);
- mIndex += (mStep*td) & WAVEFORM_FRACMASK;
- mIndex &= WAVEFORM_FRACMASK;
-
- ASSUME(numInput > 0);
- for(c = 0;c < numInput;c++)
- {
- alignas(16) ALfloat temps[MAX_UPDATE_SAMPLES];
-
- mChans[c].Filter.process(temps, &samplesIn[c][base], td);
- for(i = 0;i < td;i++)
- temps[i] *= modsamples[i];
-
- MixSamples(temps, samplesOut, mChans[c].CurrentGains, mChans[c].TargetGains,
- samplesToDo-base, base, td);
- }
-
- base += td;
- }
-}
-
-
-void Modulator_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_RING_MODULATOR_FREQUENCY:
- if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator frequency out of range");
- props->Modulator.Frequency = val;
- break;
-
- case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
- if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator high-pass cutoff out of range");
- props->Modulator.HighPassCutoff = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
- }
-}
-void Modulator_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Modulator_setParamf(props, context, param, vals[0]); }
-void Modulator_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_RING_MODULATOR_FREQUENCY:
- case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
- Modulator_setParamf(props, context, param, static_cast<ALfloat>(val));
- break;
-
- case AL_RING_MODULATOR_WAVEFORM:
- if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid modulator waveform");
- props->Modulator.Waveform = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
- }
-}
-void Modulator_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Modulator_setParami(props, context, param, vals[0]); }
-
-void Modulator_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_RING_MODULATOR_FREQUENCY:
- *val = static_cast<ALint>(props->Modulator.Frequency);
- break;
- case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
- *val = static_cast<ALint>(props->Modulator.HighPassCutoff);
- break;
- case AL_RING_MODULATOR_WAVEFORM:
- *val = props->Modulator.Waveform;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
- }
-}
-void Modulator_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Modulator_getParami(props, context, param, vals); }
-void Modulator_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_RING_MODULATOR_FREQUENCY:
- *val = props->Modulator.Frequency;
- break;
- case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
- *val = props->Modulator.HighPassCutoff;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
- }
-}
-void Modulator_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Modulator_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Modulator);
-
-
-struct ModulatorStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ModulatorState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Modulator_vtable; }
-};
-
-EffectProps ModulatorStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
- props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
- props.Modulator.Waveform = AL_RING_MODULATOR_DEFAULT_WAVEFORM;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *ModulatorStateFactory_getFactory()
-{
- static ModulatorStateFactory ModulatorFactory{};
- return &ModulatorFactory;
-}
diff --git a/Alc/effects/null.cpp b/Alc/effects/null.cpp
deleted file mode 100644
index e55c8699..00000000
--- a/Alc/effects/null.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-#include "config.h"
-
-#include <cstdlib>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-
-
-namespace {
-
-struct NullState final : public EffectState {
- NullState();
- ~NullState() override;
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(NullState)
-};
-
-/* This constructs the effect state. It's called when the object is first
- * created.
- */
-NullState::NullState() = default;
-
-/* This destructs the effect state. It's called only when the effect instance
- * is no longer used.
- */
-NullState::~NullState() = default;
-
-/* This updates the device-dependant effect state. This is called on state
- * initialization and any time the device parameters (e.g. playback frequency,
- * format) have been changed. Will always be followed by a call to the update
- * method, if successful.
- */
-ALboolean NullState::deviceUpdate(const ALCdevice* /*device*/)
-{
- return AL_TRUE;
-}
-
-/* This updates the effect state with new properties. This is called any time
- * the effect is (re)loaded into a slot.
- */
-void NullState::update(const ALCcontext* /*context*/, const ALeffectslot* /*slot*/,
- const EffectProps* /*props*/, const EffectTarget /*target*/)
-{
-}
-
-/* This processes the effect state, for the given number of samples from the
- * input to the output buffer. The result should be added to the output buffer,
- * not replace it.
- */
-void NullState::process(const ALsizei /*samplesToDo*/,
- const FloatBufferLine *RESTRICT /*samplesIn*/, const ALsizei /*numInput*/,
- const al::span<FloatBufferLine> /*samplesOut*/)
-{
-}
-
-
-void NullEffect_setParami(EffectProps* /*props*/, ALCcontext *context, ALenum param, ALint /*val*/)
-{
- switch(param)
- {
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
- }
-}
-void NullEffect_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{
- switch(param)
- {
- default:
- NullEffect_setParami(props, context, param, vals[0]);
- }
-}
-void NullEffect_setParamf(EffectProps* /*props*/, ALCcontext *context, ALenum param, ALfloat /*val*/)
-{
- switch(param)
- {
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
- }
-}
-void NullEffect_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- switch(param)
- {
- default:
- NullEffect_setParamf(props, context, param, vals[0]);
- }
-}
-
-void NullEffect_getParami(const EffectProps* /*props*/, ALCcontext *context, ALenum param, ALint* /*val*/)
-{
- switch(param)
- {
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
- }
-}
-void NullEffect_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{
- switch(param)
- {
- default:
- NullEffect_getParami(props, context, param, vals);
- }
-}
-void NullEffect_getParamf(const EffectProps* /*props*/, ALCcontext *context, ALenum param, ALfloat* /*val*/)
-{
- switch(param)
- {
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
- }
-}
-void NullEffect_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- switch(param)
- {
- default:
- NullEffect_getParamf(props, context, param, vals);
- }
-}
-
-DEFINE_ALEFFECT_VTABLE(NullEffect);
-
-
-struct NullStateFactory final : public EffectStateFactory {
- EffectState *create() override;
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override;
-};
-
-/* Creates EffectState objects of the appropriate type. */
-EffectState *NullStateFactory::create()
-{ return new NullState{}; }
-
-/* Returns an ALeffectProps initialized with this effect type's default
- * property values.
- */
-EffectProps NullStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- return props;
-}
-
-/* Returns a pointer to this effect type's global set/get vtable. */
-const EffectVtable *NullStateFactory::getEffectVtable() const noexcept
-{ return &NullEffect_vtable; }
-
-} // namespace
-
-EffectStateFactory *NullStateFactory_getFactory()
-{
- static NullStateFactory NullFactory{};
- return &NullFactory;
-}
diff --git a/Alc/effects/pshifter.cpp b/Alc/effects/pshifter.cpp
deleted file mode 100644
index 39d3cf1a..00000000
--- a/Alc/effects/pshifter.cpp
+++ /dev/null
@@ -1,405 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2018 by Raul Herraiz.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#ifdef HAVE_SSE_INTRINSICS
-#include <emmintrin.h>
-#endif
-
-#include <cmath>
-#include <cstdlib>
-#include <array>
-#include <complex>
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-#include "alcomplex.h"
-
-
-namespace {
-
-using complex_d = std::complex<double>;
-
-#define STFT_SIZE 1024
-#define STFT_HALF_SIZE (STFT_SIZE>>1)
-#define OVERSAMP (1<<2)
-
-#define STFT_STEP (STFT_SIZE / OVERSAMP)
-#define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
-
-inline int double2int(double d)
-{
-#if defined(HAVE_SSE_INTRINSICS)
- return _mm_cvttsd_si32(_mm_set_sd(d));
-
-#elif ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
- !defined(__SSE2_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2)
-
- int sign, shift;
- int64_t mant;
- union {
- double d;
- int64_t i64;
- } conv;
-
- conv.d = d;
- sign = (conv.i64>>63) | 1;
- shift = ((conv.i64>>52)&0x7ff) - (1023+52);
-
- /* Over/underflow */
- if(UNLIKELY(shift >= 63 || shift < -52))
- return 0;
-
- mant = (conv.i64&0xfffffffffffff_i64) | 0x10000000000000_i64;
- if(LIKELY(shift < 0))
- return (int)(mant >> -shift) * sign;
- return (int)(mant << shift) * sign;
-
-#else
-
- return static_cast<int>(d);
-#endif
-}
-
-/* Define a Hann window, used to filter the STFT input and output. */
-/* Making this constexpr seems to require C++14. */
-std::array<ALdouble,STFT_SIZE> InitHannWindow()
-{
- std::array<ALdouble,STFT_SIZE> ret;
- /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
- for(ALsizei i{0};i < STFT_SIZE>>1;i++)
- {
- ALdouble val = std::sin(al::MathDefs<double>::Pi() * i / ALdouble{STFT_SIZE-1});
- ret[i] = ret[STFT_SIZE-1-i] = val * val;
- }
- return ret;
-}
-alignas(16) const std::array<ALdouble,STFT_SIZE> HannWindow = InitHannWindow();
-
-
-struct ALphasor {
- ALdouble Amplitude;
- ALdouble Phase;
-};
-
-struct ALfrequencyDomain {
- ALdouble Amplitude;
- ALdouble Frequency;
-};
-
-
-/* Converts complex to ALphasor */
-inline ALphasor rect2polar(const complex_d &number)
-{
- ALphasor polar;
- polar.Amplitude = std::abs(number);
- polar.Phase = std::arg(number);
- return polar;
-}
-
-/* Converts ALphasor to complex */
-inline complex_d polar2rect(const ALphasor &number)
-{ return std::polar<double>(number.Amplitude, number.Phase); }
-
-
-struct PshifterState final : public EffectState {
- /* Effect parameters */
- ALsizei mCount;
- ALsizei mPitchShiftI;
- ALfloat mPitchShift;
- ALfloat mFreqPerBin;
-
- /* Effects buffers */
- ALfloat mInFIFO[STFT_SIZE];
- ALfloat mOutFIFO[STFT_STEP];
- ALdouble mLastPhase[STFT_HALF_SIZE+1];
- ALdouble mSumPhase[STFT_HALF_SIZE+1];
- ALdouble mOutputAccum[STFT_SIZE];
-
- complex_d mFFTbuffer[STFT_SIZE];
-
- ALfrequencyDomain mAnalysis_buffer[STFT_HALF_SIZE+1];
- ALfrequencyDomain mSyntesis_buffer[STFT_HALF_SIZE+1];
-
- alignas(16) ALfloat mBufferOut[BUFFERSIZE];
-
- /* Effect gains for each output channel */
- ALfloat mCurrentGains[MAX_OUTPUT_CHANNELS];
- ALfloat mTargetGains[MAX_OUTPUT_CHANNELS];
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(PshifterState)
-};
-
-ALboolean PshifterState::deviceUpdate(const ALCdevice *device)
-{
- /* (Re-)initializing parameters and clear the buffers. */
- mCount = FIFO_LATENCY;
- mPitchShiftI = FRACTIONONE;
- mPitchShift = 1.0f;
- mFreqPerBin = device->Frequency / static_cast<ALfloat>(STFT_SIZE);
-
- std::fill(std::begin(mInFIFO), std::end(mInFIFO), 0.0f);
- std::fill(std::begin(mOutFIFO), std::end(mOutFIFO), 0.0f);
- std::fill(std::begin(mLastPhase), std::end(mLastPhase), 0.0);
- std::fill(std::begin(mSumPhase), std::end(mSumPhase), 0.0);
- std::fill(std::begin(mOutputAccum), std::end(mOutputAccum), 0.0);
- std::fill(std::begin(mFFTbuffer), std::end(mFFTbuffer), complex_d{});
- std::fill(std::begin(mAnalysis_buffer), std::end(mAnalysis_buffer), ALfrequencyDomain{});
- std::fill(std::begin(mSyntesis_buffer), std::end(mSyntesis_buffer), ALfrequencyDomain{});
-
- std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f);
- std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
-
- return AL_TRUE;
-}
-
-void PshifterState::update(const ALCcontext*, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const float pitch{std::pow(2.0f,
- static_cast<ALfloat>(props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune) / 1200.0f
- )};
- mPitchShiftI = fastf2i(pitch*FRACTIONONE);
- mPitchShift = mPitchShiftI * (1.0f/FRACTIONONE);
-
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f, coeffs);
-
- mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs, slot->Params.Gain, mTargetGains);
-}
-
-void PshifterState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- /* Pitch shifter engine based on the work of Stephan Bernsee.
- * http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
- */
-
- static constexpr ALdouble expected{al::MathDefs<double>::Tau() / OVERSAMP};
- const ALdouble freq_per_bin{mFreqPerBin};
- ALfloat *RESTRICT bufferOut{mBufferOut};
- ALsizei count{mCount};
-
- for(ALsizei i{0};i < samplesToDo;)
- {
- do {
- /* Fill FIFO buffer with samples data */
- mInFIFO[count] = samplesIn[0][i];
- bufferOut[i] = mOutFIFO[count - FIFO_LATENCY];
-
- count++;
- } while(++i < samplesToDo && count < STFT_SIZE);
-
- /* Check whether FIFO buffer is filled */
- if(count < STFT_SIZE) break;
- count = FIFO_LATENCY;
-
- /* Real signal windowing and store in FFTbuffer */
- for(ALsizei k{0};k < STFT_SIZE;k++)
- {
- mFFTbuffer[k].real(mInFIFO[k] * HannWindow[k]);
- mFFTbuffer[k].imag(0.0);
- }
-
- /* ANALYSIS */
- /* Apply FFT to FFTbuffer data */
- complex_fft(mFFTbuffer, -1.0);
-
- /* Analyze the obtained data. Since the real FFT is symmetric, only
- * STFT_HALF_SIZE+1 samples are needed.
- */
- for(ALsizei k{0};k < STFT_HALF_SIZE+1;k++)
- {
- /* Compute amplitude and phase */
- ALphasor component{rect2polar(mFFTbuffer[k])};
-
- /* Compute phase difference and subtract expected phase difference */
- double tmp{(component.Phase - mLastPhase[k]) - k*expected};
-
- /* Map delta phase into +/- Pi interval */
- int qpd{double2int(tmp / al::MathDefs<double>::Pi())};
- tmp -= al::MathDefs<double>::Pi() * (qpd + (qpd%2));
-
- /* Get deviation from bin frequency from the +/- Pi interval */
- tmp /= expected;
-
- /* Compute the k-th partials' true frequency, twice the amplitude
- * for maintain the gain (because half of bins are used) and store
- * amplitude and true frequency in analysis buffer.
- */
- mAnalysis_buffer[k].Amplitude = 2.0 * component.Amplitude;
- mAnalysis_buffer[k].Frequency = (k + tmp) * freq_per_bin;
-
- /* Store actual phase[k] for the calculations in the next frame*/
- mLastPhase[k] = component.Phase;
- }
-
- /* PROCESSING */
- /* pitch shifting */
- for(ALsizei k{0};k < STFT_HALF_SIZE+1;k++)
- {
- mSyntesis_buffer[k].Amplitude = 0.0;
- mSyntesis_buffer[k].Frequency = 0.0;
- }
-
- for(ALsizei k{0};k < STFT_HALF_SIZE+1;k++)
- {
- ALsizei j{(k*mPitchShiftI) >> FRACTIONBITS};
- if(j >= STFT_HALF_SIZE+1) break;
-
- mSyntesis_buffer[j].Amplitude += mAnalysis_buffer[k].Amplitude;
- mSyntesis_buffer[j].Frequency = mAnalysis_buffer[k].Frequency * mPitchShift;
- }
-
- /* SYNTHESIS */
- /* Synthesis the processing data */
- for(ALsizei k{0};k < STFT_HALF_SIZE+1;k++)
- {
- ALphasor component;
- ALdouble tmp;
-
- /* Compute bin deviation from scaled freq */
- tmp = mSyntesis_buffer[k].Frequency/freq_per_bin - k;
-
- /* Calculate actual delta phase and accumulate it to get bin phase */
- mSumPhase[k] += (k + tmp) * expected;
-
- component.Amplitude = mSyntesis_buffer[k].Amplitude;
- component.Phase = mSumPhase[k];
-
- /* Compute phasor component to cartesian complex number and storage it into FFTbuffer*/
- mFFTbuffer[k] = polar2rect(component);
- }
- /* zero negative frequencies for recontruct a real signal */
- for(ALsizei k{STFT_HALF_SIZE+1};k < STFT_SIZE;k++)
- mFFTbuffer[k] = complex_d{};
-
- /* Apply iFFT to buffer data */
- complex_fft(mFFTbuffer, 1.0);
-
- /* Windowing and add to output */
- for(ALsizei k{0};k < STFT_SIZE;k++)
- mOutputAccum[k] += HannWindow[k] * mFFTbuffer[k].real() /
- (0.5 * STFT_HALF_SIZE * OVERSAMP);
-
- /* Shift accumulator, input & output FIFO */
- ALsizei j, k;
- for(k = 0;k < STFT_STEP;k++) mOutFIFO[k] = static_cast<ALfloat>(mOutputAccum[k]);
- for(j = 0;k < STFT_SIZE;k++,j++) mOutputAccum[j] = mOutputAccum[k];
- for(;j < STFT_SIZE;j++) mOutputAccum[j] = 0.0;
- for(k = 0;k < FIFO_LATENCY;k++)
- mInFIFO[k] = mInFIFO[k+STFT_STEP];
- }
- mCount = count;
-
- /* Now, mix the processed sound data to the output. */
- MixSamples(bufferOut, samplesOut, mCurrentGains, mTargetGains, maxi(samplesToDo, 512), 0,
- samplesToDo);
-}
-
-
-void Pshifter_setParamf(EffectProps*, ALCcontext *context, ALenum param, ALfloat)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param); }
-void Pshifter_setParamfv(EffectProps*, ALCcontext *context, ALenum param, const ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", param); }
-
-void Pshifter_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_PITCH_SHIFTER_COARSE_TUNE:
- if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter coarse tune out of range");
- props->Pshifter.CoarseTune = val;
- break;
-
- case AL_PITCH_SHIFTER_FINE_TUNE:
- if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter fine tune out of range");
- props->Pshifter.FineTune = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
- }
-}
-void Pshifter_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Pshifter_setParami(props, context, param, vals[0]); }
-
-void Pshifter_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_PITCH_SHIFTER_COARSE_TUNE:
- *val = props->Pshifter.CoarseTune;
- break;
- case AL_PITCH_SHIFTER_FINE_TUNE:
- *val = props->Pshifter.FineTune;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
- }
-}
-void Pshifter_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Pshifter_getParami(props, context, param, vals); }
-
-void Pshifter_getParamf(const EffectProps*, ALCcontext *context, ALenum param, ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param); }
-void Pshifter_getParamfv(const EffectProps*, ALCcontext *context, ALenum param, ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", param); }
-
-DEFINE_ALEFFECT_VTABLE(Pshifter);
-
-
-struct PshifterStateFactory final : public EffectStateFactory {
- EffectState *create() override;
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Pshifter_vtable; }
-};
-
-EffectState *PshifterStateFactory::create()
-{ return new PshifterState{}; }
-
-EffectProps PshifterStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
- props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *PshifterStateFactory_getFactory()
-{
- static PshifterStateFactory PshifterFactory{};
- return &PshifterFactory;
-}
diff --git a/Alc/effects/reverb.cpp b/Alc/effects/reverb.cpp
deleted file mode 100644
index ac996b3f..00000000
--- a/Alc/effects/reverb.cpp
+++ /dev/null
@@ -1,2102 +0,0 @@
-/**
- * Ambisonic reverb engine for the OpenAL cross platform audio library
- * Copyright (C) 2008-2017 by Chris Robinson and Christopher Fitzgerald.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cstdio>
-#include <cstdlib>
-#include <cmath>
-
-#include <array>
-#include <numeric>
-#include <algorithm>
-#include <functional>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alu.h"
-#include "alAuxEffectSlot.h"
-#include "alListener.h"
-#include "alError.h"
-#include "bformatdec.h"
-#include "filters/biquad.h"
-#include "vector.h"
-#include "vecmat.h"
-
-/* This is a user config option for modifying the overall output of the reverb
- * effect.
- */
-ALfloat ReverbBoost = 1.0f;
-
-namespace {
-
-using namespace std::placeholders;
-
-/* The number of samples used for cross-faded delay lines. This can be used
- * to balance the compensation for abrupt line changes and attenuation due to
- * minimally lengthed recursive lines. Try to keep this below the device
- * update size.
- */
-constexpr int FADE_SAMPLES{128};
-
-/* The number of spatialized lines or channels to process. Four channels allows
- * for a 3D A-Format response. NOTE: This can't be changed without taking care
- * of the conversion matrices, and a few places where the length arrays are
- * assumed to have 4 elements.
- */
-constexpr int NUM_LINES{4};
-
-
-/* The B-Format to A-Format conversion matrix. The arrangement of rows is
- * deliberately chosen to align the resulting lines to their spatial opposites
- * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
- * back left). It's not quite opposite, since the A-Format results in a
- * tetrahedron, but it's close enough. Should the model be extended to 8-lines
- * in the future, true opposites can be used.
- */
-alignas(16) constexpr ALfloat B2A[NUM_LINES][MAX_AMBI_CHANNELS]{
- { 0.288675134595f, 0.288675134595f, 0.288675134595f, 0.288675134595f },
- { 0.288675134595f, -0.288675134595f, -0.288675134595f, 0.288675134595f },
- { 0.288675134595f, 0.288675134595f, -0.288675134595f, -0.288675134595f },
- { 0.288675134595f, -0.288675134595f, 0.288675134595f, -0.288675134595f }
-};
-
-/* Converts A-Format to B-Format. */
-alignas(16) constexpr ALfloat A2B[NUM_LINES][NUM_LINES]{
- { 0.866025403785f, 0.866025403785f, 0.866025403785f, 0.866025403785f },
- { 0.866025403785f, -0.866025403785f, 0.866025403785f, -0.866025403785f },
- { 0.866025403785f, -0.866025403785f, -0.866025403785f, 0.866025403785f },
- { 0.866025403785f, 0.866025403785f, -0.866025403785f, -0.866025403785f }
-};
-
-
-constexpr ALfloat FadeStep{1.0f / FADE_SAMPLES};
-
-/* The all-pass and delay lines have a variable length dependent on the
- * effect's density parameter, which helps alter the perceived environment
- * size. The size-to-density conversion is a cubed scale:
- *
- * density = min(1.0, pow(size, 3.0) / DENSITY_SCALE);
- *
- * The line lengths scale linearly with room size, so the inverse density
- * conversion is needed, taking the cube root of the re-scaled density to
- * calculate the line length multiplier:
- *
- * length_mult = max(5.0, cbrt(density*DENSITY_SCALE));
- *
- * The density scale below will result in a max line multiplier of 50, for an
- * effective size range of 5m to 50m.
- */
-constexpr ALfloat DENSITY_SCALE{125000.0f};
-
-/* All delay line lengths are specified in seconds.
- *
- * To approximate early reflections, we break them up into primary (those
- * arriving from the same direction as the source) and secondary (those
- * arriving from the opposite direction).
- *
- * The early taps decorrelate the 4-channel signal to approximate an average
- * room response for the primary reflections after the initial early delay.
- *
- * Given an average room dimension (d_a) and the speed of sound (c) we can
- * calculate the average reflection delay (r_a) regardless of listener and
- * source positions as:
- *
- * r_a = d_a / c
- * c = 343.3
- *
- * This can extended to finding the average difference (r_d) between the
- * maximum (r_1) and minimum (r_0) reflection delays:
- *
- * r_0 = 2 / 3 r_a
- * = r_a - r_d / 2
- * = r_d
- * r_1 = 4 / 3 r_a
- * = r_a + r_d / 2
- * = 2 r_d
- * r_d = 2 / 3 r_a
- * = r_1 - r_0
- *
- * As can be determined by integrating the 1D model with a source (s) and
- * listener (l) positioned across the dimension of length (d_a):
- *
- * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
- *
- * The initial taps (T_(i=0)^N) are then specified by taking a power series
- * that ranges between r_0 and half of r_1 less r_0:
- *
- * R_i = 2^(i / (2 N - 1)) r_d
- * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
- * = r_0 + T_i
- * T_i = R_i - r_0
- * = (2^(i / (2 N - 1)) - 1) r_d
- *
- * Assuming an average of 1m, we get the following taps:
- */
-constexpr std::array<ALfloat,NUM_LINES> EARLY_TAP_LENGTHS{{
- 0.0000000e+0f, 2.0213520e-4f, 4.2531060e-4f, 6.7171600e-4f
-}};
-
-/* The early all-pass filter lengths are based on the early tap lengths:
- *
- * A_i = R_i / a
- *
- * Where a is the approximate maximum all-pass cycle limit (20).
- */
-constexpr std::array<ALfloat,NUM_LINES> EARLY_ALLPASS_LENGTHS{{
- 9.7096800e-5f, 1.0720356e-4f, 1.1836234e-4f, 1.3068260e-4f
-}};
-
-/* The early delay lines are used to transform the primary reflections into
- * the secondary reflections. The A-format is arranged in such a way that
- * the channels/lines are spatially opposite:
- *
- * C_i is opposite C_(N-i-1)
- *
- * The delays of the two opposing reflections (R_i and O_i) from a source
- * anywhere along a particular dimension always sum to twice its full delay:
- *
- * 2 r_a = R_i + O_i
- *
- * With that in mind we can determine the delay between the two reflections
- * and thus specify our early line lengths (L_(i=0)^N) using:
- *
- * O_i = 2 r_a - R_(N-i-1)
- * L_i = O_i - R_(N-i-1)
- * = 2 (r_a - R_(N-i-1))
- * = 2 (r_a - T_(N-i-1) - r_0)
- * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
- *
- * Using an average dimension of 1m, we get:
- */
-constexpr std::array<ALfloat,NUM_LINES> EARLY_LINE_LENGTHS{{
- 5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f
-}};
-
-/* The late all-pass filter lengths are based on the late line lengths:
- *
- * A_i = (5 / 3) L_i / r_1
- */
-constexpr std::array<ALfloat,NUM_LINES> LATE_ALLPASS_LENGTHS{{
- 1.6182800e-4f, 2.0389060e-4f, 2.8159360e-4f, 3.2365600e-4f
-}};
-constexpr auto LATE_ALLPASS_LENGTHS_size = LATE_ALLPASS_LENGTHS.size();
-
-/* The late lines are used to approximate the decaying cycle of recursive
- * late reflections.
- *
- * Splitting the lines in half, we start with the shortest reflection paths
- * (L_(i=0)^(N/2)):
- *
- * L_i = 2^(i / (N - 1)) r_d
- *
- * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
- *
- * L_i = 2 r_a - L_(i-N/2)
- * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
- *
- * For our 1m average room, we get:
- */
-constexpr std::array<ALfloat,NUM_LINES> LATE_LINE_LENGTHS{{
- 1.9419362e-3f, 2.4466860e-3f, 3.3791220e-3f, 3.8838720e-3f
-}};
-constexpr auto LATE_LINE_LENGTHS_size = LATE_LINE_LENGTHS.size();
-
-
-struct DelayLineI {
- /* The delay lines use interleaved samples, with the lengths being powers
- * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
- */
- ALsizei Mask{0};
- ALfloat (*Line)[NUM_LINES]{nullptr};
-
-
- void write(ALsizei offset, const ALsizei c, const ALfloat *RESTRICT in, const ALsizei count) const noexcept
- {
- ASSUME(count > 0);
- for(ALsizei i{0};i < count;)
- {
- offset &= Mask;
- ALsizei td{mini(Mask+1 - offset, count - i)};
- do {
- Line[offset++][c] = in[i++];
- } while(--td);
- }
- }
-};
-
-struct VecAllpass {
- DelayLineI Delay;
- ALfloat Coeff{0.0f};
- ALsizei Offset[NUM_LINES][2]{};
-
- void processFaded(const al::span<FloatBufferLine,NUM_LINES> samples, ALsizei offset,
- const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade, const ALsizei todo);
- void processUnfaded(const al::span<FloatBufferLine,NUM_LINES> samples, ALsizei offset,
- const ALfloat xCoeff, const ALfloat yCoeff, const ALsizei todo);
-};
-
-struct T60Filter {
- /* Two filters are used to adjust the signal. One to control the low
- * frequencies, and one to control the high frequencies.
- */
- ALfloat MidGain[2]{0.0f, 0.0f};
- BiquadFilter HFFilter, LFFilter;
-
- void calcCoeffs(const ALfloat length, const ALfloat lfDecayTime, const ALfloat mfDecayTime,
- const ALfloat hfDecayTime, const ALfloat lf0norm, const ALfloat hf0norm);
-
- /* Applies the two T60 damping filter sections. */
- void process(ALfloat *samples, const ALsizei todo)
- {
- HFFilter.process(samples, samples, todo);
- LFFilter.process(samples, samples, todo);
- }
-};
-
-struct EarlyReflections {
- /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
- * The spread from this filter also helps smooth out the reverb tail.
- */
- VecAllpass VecAp;
-
- /* An echo line is used to complete the second half of the early
- * reflections.
- */
- DelayLineI Delay;
- ALsizei Offset[NUM_LINES][2]{};
- ALfloat Coeff[NUM_LINES][2]{};
-
- /* The gain for each output channel based on 3D panning. */
- ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS]{};
- ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS]{};
-
- void updateLines(const ALfloat density, const ALfloat diffusion, const ALfloat decayTime,
- const ALfloat frequency);
-};
-
-struct LateReverb {
- /* A recursive delay line is used fill in the reverb tail. */
- DelayLineI Delay;
- ALsizei Offset[NUM_LINES][2]{};
-
- /* Attenuation to compensate for the modal density and decay rate of the
- * late lines.
- */
- ALfloat DensityGain[2]{0.0f, 0.0f};
-
- /* T60 decay filters are used to simulate absorption. */
- T60Filter T60[NUM_LINES];
-
- /* A Gerzon vector all-pass filter is used to simulate diffusion. */
- VecAllpass VecAp;
-
- /* The gain for each output channel based on 3D panning. */
- ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS]{};
- ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS]{};
-
- void updateLines(const ALfloat density, const ALfloat diffusion, const ALfloat lfDecayTime,
- const ALfloat mfDecayTime, const ALfloat hfDecayTime, const ALfloat lf0norm,
- const ALfloat hf0norm, const ALfloat frequency);
-};
-
-struct ReverbState final : public EffectState {
- /* All delay lines are allocated as a single buffer to reduce memory
- * fragmentation and management code.
- */
- al::vector<ALfloat,16> mSampleBuffer;
-
- struct {
- /* Calculated parameters which indicate if cross-fading is needed after
- * an update.
- */
- ALfloat Density{AL_EAXREVERB_DEFAULT_DENSITY};
- ALfloat Diffusion{AL_EAXREVERB_DEFAULT_DIFFUSION};
- ALfloat DecayTime{AL_EAXREVERB_DEFAULT_DECAY_TIME};
- ALfloat HFDecayTime{AL_EAXREVERB_DEFAULT_DECAY_HFRATIO * AL_EAXREVERB_DEFAULT_DECAY_TIME};
- ALfloat LFDecayTime{AL_EAXREVERB_DEFAULT_DECAY_LFRATIO * AL_EAXREVERB_DEFAULT_DECAY_TIME};
- ALfloat HFReference{AL_EAXREVERB_DEFAULT_HFREFERENCE};
- ALfloat LFReference{AL_EAXREVERB_DEFAULT_LFREFERENCE};
- } mParams;
-
- /* Master effect filters */
- struct {
- BiquadFilter Lp;
- BiquadFilter Hp;
- } mFilter[NUM_LINES];
-
- /* Core delay line (early reflections and late reverb tap from this). */
- DelayLineI mDelay;
-
- /* Tap points for early reflection delay. */
- ALsizei mEarlyDelayTap[NUM_LINES][2]{};
- ALfloat mEarlyDelayCoeff[NUM_LINES][2]{};
-
- /* Tap points for late reverb feed and delay. */
- ALsizei mLateFeedTap{};
- ALsizei mLateDelayTap[NUM_LINES][2]{};
-
- /* Coefficients for the all-pass and line scattering matrices. */
- ALfloat mMixX{0.0f};
- ALfloat mMixY{0.0f};
-
- EarlyReflections mEarly;
-
- LateReverb mLate;
-
- /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
- ALsizei mFadeCount{0};
-
- /* Maximum number of samples to process at once. */
- ALsizei mMaxUpdate[2]{BUFFERSIZE, BUFFERSIZE};
-
- /* The current write offset for all delay lines. */
- ALsizei mOffset{0};
-
- /* Temporary storage used when processing. */
- alignas(16) std::array<FloatBufferLine,NUM_LINES> mTempSamples{};
- alignas(16) std::array<FloatBufferLine,NUM_LINES> mEarlyBuffer{};
- alignas(16) std::array<FloatBufferLine,NUM_LINES> mLateBuffer{};
-
- using MixOutT = void (ReverbState::*)(const al::span<FloatBufferLine> samplesOut,
- const ALsizei todo);
-
- MixOutT mMixOut{&ReverbState::MixOutPlain};
- std::array<ALfloat,MAX_AMBI_ORDER+1> mOrderScales{};
- std::array<std::array<BandSplitter,NUM_LINES>,2> mAmbiSplitter;
-
-
- void MixOutPlain(const al::span<FloatBufferLine> samplesOut, const ALsizei todo)
- {
- ASSUME(todo > 0);
-
- /* Convert back to B-Format, and mix the results to output. */
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- std::fill_n(mTempSamples[0].begin(), todo, 0.0f);
- MixRowSamples(mTempSamples[0], A2B[c], mEarlyBuffer, 0, todo);
- MixSamples(mTempSamples[0].data(), samplesOut, mEarly.CurrentGain[c],
- mEarly.PanGain[c], todo, 0, todo);
- }
-
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- std::fill_n(mTempSamples[0].begin(), todo, 0.0f);
- MixRowSamples(mTempSamples[0], A2B[c], mLateBuffer, 0, todo);
- MixSamples(mTempSamples[0].data(), samplesOut, mLate.CurrentGain[c], mLate.PanGain[c],
- todo, 0, todo);
- }
- }
-
- void MixOutAmbiUp(const al::span<FloatBufferLine> samplesOut, const ALsizei todo)
- {
- ASSUME(todo > 0);
-
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- std::fill_n(mTempSamples[0].begin(), todo, 0.0f);
- MixRowSamples(mTempSamples[0], A2B[c], mEarlyBuffer, 0, todo);
-
- /* Apply scaling to the B-Format's HF response to "upsample" it to
- * higher-order output.
- */
- const ALfloat hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]};
- mAmbiSplitter[0][c].applyHfScale(mTempSamples[0].data(), hfscale, todo);
-
- MixSamples(mTempSamples[0].data(), samplesOut, mEarly.CurrentGain[c],
- mEarly.PanGain[c], todo, 0, todo);
- }
-
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- std::fill_n(mTempSamples[0].begin(), todo, 0.0f);
- MixRowSamples(mTempSamples[0], A2B[c], mLateBuffer, 0, todo);
-
- const ALfloat hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]};
- mAmbiSplitter[1][c].applyHfScale(mTempSamples[0].data(), hfscale, todo);
-
- MixSamples(mTempSamples[0].data(), samplesOut, mLate.CurrentGain[c], mLate.PanGain[c],
- todo, 0, todo);
- }
- }
-
- bool allocLines(const ALfloat frequency);
-
- void updateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density,
- const ALfloat decayTime, const ALfloat frequency);
- void update3DPanning(const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan,
- const ALfloat earlyGain, const ALfloat lateGain, const EffectTarget &target);
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(ReverbState)
-};
-
-/**************************************
- * Device Update *
- **************************************/
-
-inline ALfloat CalcDelayLengthMult(ALfloat density)
-{ return maxf(5.0f, std::cbrt(density*DENSITY_SCALE)); }
-
-/* Given the allocated sample buffer, this function updates each delay line
- * offset.
- */
-inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay)
-{
- union {
- ALfloat *f;
- ALfloat (*f4)[NUM_LINES];
- } u;
- u.f = &sampleBuffer[reinterpret_cast<ptrdiff_t>(Delay->Line) * NUM_LINES];
- Delay->Line = u.f4;
-}
-
-/* Calculate the length of a delay line and store its mask and offset. */
-ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALfloat frequency,
- const ALuint extra, DelayLineI *Delay)
-{
- /* All line lengths are powers of 2, calculated from their lengths in
- * seconds, rounded up.
- */
- auto samples = static_cast<ALuint>(float2int(std::ceil(length*frequency)));
- samples = NextPowerOf2(samples + extra);
-
- /* All lines share a single sample buffer. */
- Delay->Mask = samples - 1;
- Delay->Line = reinterpret_cast<ALfloat(*)[NUM_LINES]>(offset);
-
- /* Return the sample count for accumulation. */
- return samples;
-}
-
-/* Calculates the delay line metrics and allocates the shared sample buffer
- * for all lines given the sample rate (frequency). If an allocation failure
- * occurs, it returns AL_FALSE.
- */
-bool ReverbState::allocLines(const ALfloat frequency)
-{
- /* All delay line lengths are calculated to accomodate the full range of
- * lengths given their respective paramters.
- */
- ALuint totalSamples{0u};
-
- /* Multiplier for the maximum density value, i.e. density=1, which is
- * actually the least density...
- */
- ALfloat multiplier{CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY)};
-
- /* The main delay length includes the maximum early reflection delay, the
- * largest early tap width, the maximum late reverb delay, and the
- * largest late tap width. Finally, it must also be extended by the
- * update size (BUFFERSIZE) for block processing.
- */
- ALfloat length{AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS.back()*multiplier +
- AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
- (LATE_LINE_LENGTHS.back() - LATE_LINE_LENGTHS.front())/float{LATE_LINE_LENGTHS_size}*multiplier};
- totalSamples += CalcLineLength(length, totalSamples, frequency, BUFFERSIZE, &mDelay);
-
- /* The early vector all-pass line. */
- length = EARLY_ALLPASS_LENGTHS.back() * multiplier;
- totalSamples += CalcLineLength(length, totalSamples, frequency, 0, &mEarly.VecAp.Delay);
-
- /* The early reflection line. */
- length = EARLY_LINE_LENGTHS.back() * multiplier;
- totalSamples += CalcLineLength(length, totalSamples, frequency, 0, &mEarly.Delay);
-
- /* The late vector all-pass line. */
- length = LATE_ALLPASS_LENGTHS.back() * multiplier;
- totalSamples += CalcLineLength(length, totalSamples, frequency, 0, &mLate.VecAp.Delay);
-
- /* The late delay lines are calculated from the largest maximum density
- * line length.
- */
- length = LATE_LINE_LENGTHS.back() * multiplier;
- totalSamples += CalcLineLength(length, totalSamples, frequency, 0, &mLate.Delay);
-
- totalSamples *= NUM_LINES;
- if(totalSamples != mSampleBuffer.size())
- {
- mSampleBuffer.resize(totalSamples);
- mSampleBuffer.shrink_to_fit();
- }
-
- /* Clear the sample buffer. */
- std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f);
-
- /* Update all delays to reflect the new sample buffer. */
- RealizeLineOffset(mSampleBuffer.data(), &mDelay);
- RealizeLineOffset(mSampleBuffer.data(), &mEarly.VecAp.Delay);
- RealizeLineOffset(mSampleBuffer.data(), &mEarly.Delay);
- RealizeLineOffset(mSampleBuffer.data(), &mLate.VecAp.Delay);
- RealizeLineOffset(mSampleBuffer.data(), &mLate.Delay);
-
- return true;
-}
-
-ALboolean ReverbState::deviceUpdate(const ALCdevice *device)
-{
- const auto frequency = static_cast<ALfloat>(device->Frequency);
-
- /* Allocate the delay lines. */
- if(!allocLines(frequency))
- return AL_FALSE;
-
- const ALfloat multiplier{CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY)};
-
- /* The late feed taps are set a fixed position past the latest delay tap. */
- mLateFeedTap = float2int(
- (AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS.back()*multiplier) * frequency);
-
- /* Clear filters and gain coefficients since the delay lines were all just
- * cleared (if not reallocated).
- */
- for(auto &filter : mFilter)
- {
- filter.Lp.clear();
- filter.Hp.clear();
- }
-
- for(auto &coeff : mEarlyDelayCoeff)
- std::fill(std::begin(coeff), std::end(coeff), 0.0f);
- for(auto &coeff : mEarly.Coeff)
- std::fill(std::begin(coeff), std::end(coeff), 0.0f);
-
- mLate.DensityGain[0] = 0.0f;
- mLate.DensityGain[1] = 0.0f;
- for(auto &t60 : mLate.T60)
- {
- t60.MidGain[0] = 0.0f;
- t60.MidGain[1] = 0.0f;
- t60.HFFilter.clear();
- t60.LFFilter.clear();
- }
-
- for(auto &gains : mEarly.CurrentGain)
- std::fill(std::begin(gains), std::end(gains), 0.0f);
- for(auto &gains : mEarly.PanGain)
- std::fill(std::begin(gains), std::end(gains), 0.0f);
- for(auto &gains : mLate.CurrentGain)
- std::fill(std::begin(gains), std::end(gains), 0.0f);
- for(auto &gains : mLate.PanGain)
- std::fill(std::begin(gains), std::end(gains), 0.0f);
-
- /* Reset counters and offset base. */
- mFadeCount = 0;
- std::fill(std::begin(mMaxUpdate), std::end(mMaxUpdate), BUFFERSIZE);
- mOffset = 0;
-
- if(device->mAmbiOrder > 1)
- {
- mMixOut = &ReverbState::MixOutAmbiUp;
- mOrderScales = BFormatDec::GetHFOrderScales(1, device->mAmbiOrder);
- }
- else
- {
- mMixOut = &ReverbState::MixOutPlain;
- mOrderScales.fill(1.0f);
- }
- mAmbiSplitter[0][0].init(400.0f / frequency);
- std::fill(mAmbiSplitter[0].begin()+1, mAmbiSplitter[0].end(), mAmbiSplitter[0][0]);
- std::fill(mAmbiSplitter[1].begin(), mAmbiSplitter[1].end(), mAmbiSplitter[0][0]);
-
- return AL_TRUE;
-}
-
-/**************************************
- * Effect Update *
- **************************************/
-
-/* Calculate a decay coefficient given the length of each cycle and the time
- * until the decay reaches -60 dB.
- */
-inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
-{ return std::pow(REVERB_DECAY_GAIN, length/decayTime); }
-
-/* Calculate a decay length from a coefficient and the time until the decay
- * reaches -60 dB.
- */
-inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
-{ return std::log10(coeff) * decayTime / std::log10(REVERB_DECAY_GAIN); }
-
-/* Calculate an attenuation to be applied to the input of any echo models to
- * compensate for modal density and decay time.
- */
-inline ALfloat CalcDensityGain(const ALfloat a)
-{
- /* The energy of a signal can be obtained by finding the area under the
- * squared signal. This takes the form of Sum(x_n^2), where x is the
- * amplitude for the sample n.
- *
- * Decaying feedback matches exponential decay of the form Sum(a^n),
- * where a is the attenuation coefficient, and n is the sample. The area
- * under this decay curve can be calculated as: 1 / (1 - a).
- *
- * Modifying the above equation to find the area under the squared curve
- * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
- * calculated by inverting the square root of this approximation,
- * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
- */
- return std::sqrt(1.0f - a*a);
-}
-
-/* Calculate the scattering matrix coefficients given a diffusion factor. */
-inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
-{
- /* The matrix is of order 4, so n is sqrt(4 - 1). */
- ALfloat n{std::sqrt(3.0f)};
- ALfloat t{diffusion * std::atan(n)};
-
- /* Calculate the first mixing matrix coefficient. */
- *x = std::cos(t);
- /* Calculate the second mixing matrix coefficient. */
- *y = std::sin(t) / n;
-}
-
-/* Calculate the limited HF ratio for use with the late reverb low-pass
- * filters.
- */
-ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
- const ALfloat decayTime, const ALfloat SpeedOfSound)
-{
- /* Find the attenuation due to air absorption in dB (converting delay
- * time to meters using the speed of sound). Then reversing the decay
- * equation, solve for HF ratio. The delay length is cancelled out of
- * the equation, so it can be calculated once for all lines.
- */
- ALfloat limitRatio{1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound)};
-
- /* Using the limit calculated above, apply the upper bound to the HF ratio.
- */
- return minf(limitRatio, hfRatio);
-}
-
-
-/* Calculates the 3-band T60 damping coefficients for a particular delay line
- * of specified length, using a combination of two shelf filter sections given
- * decay times for each band split at two reference frequencies.
- */
-void T60Filter::calcCoeffs(const ALfloat length, const ALfloat lfDecayTime,
- const ALfloat mfDecayTime, const ALfloat hfDecayTime, const ALfloat lf0norm,
- const ALfloat hf0norm)
-{
- const ALfloat mfGain{CalcDecayCoeff(length, mfDecayTime)};
- const ALfloat lfGain{maxf(CalcDecayCoeff(length, lfDecayTime)/mfGain, 0.001f)};
- const ALfloat hfGain{maxf(CalcDecayCoeff(length, hfDecayTime)/mfGain, 0.001f)};
-
- MidGain[1] = mfGain;
- LFFilter.setParams(BiquadType::LowShelf, lfGain, lf0norm,
- LFFilter.rcpQFromSlope(lfGain, 1.0f));
- HFFilter.setParams(BiquadType::HighShelf, hfGain, hf0norm,
- HFFilter.rcpQFromSlope(hfGain, 1.0f));
-}
-
-/* Update the early reflection line lengths and gain coefficients. */
-void EarlyReflections::updateLines(const ALfloat density, const ALfloat diffusion,
- const ALfloat decayTime, const ALfloat frequency)
-{
- const ALfloat multiplier{CalcDelayLengthMult(density)};
-
- /* Calculate the all-pass feed-back/forward coefficient. */
- VecAp.Coeff = std::sqrt(0.5f) * std::pow(diffusion, 2.0f);
-
- for(ALsizei i{0};i < NUM_LINES;i++)
- {
- /* Calculate the length (in seconds) of each all-pass line. */
- ALfloat length{EARLY_ALLPASS_LENGTHS[i] * multiplier};
-
- /* Calculate the delay offset for each all-pass line. */
- VecAp.Offset[i][1] = float2int(length * frequency);
-
- /* Calculate the length (in seconds) of each delay line. */
- length = EARLY_LINE_LENGTHS[i] * multiplier;
-
- /* Calculate the delay offset for each delay line. */
- Offset[i][1] = float2int(length * frequency);
-
- /* Calculate the gain (coefficient) for each line. */
- Coeff[i][1] = CalcDecayCoeff(length, decayTime);
- }
-}
-
-/* Update the late reverb line lengths and T60 coefficients. */
-void LateReverb::updateLines(const ALfloat density, const ALfloat diffusion,
- const ALfloat lfDecayTime, const ALfloat mfDecayTime, const ALfloat hfDecayTime,
- const ALfloat lf0norm, const ALfloat hf0norm, const ALfloat frequency)
-{
- /* Scaling factor to convert the normalized reference frequencies from
- * representing 0...freq to 0...max_reference.
- */
- const ALfloat norm_weight_factor{frequency / AL_EAXREVERB_MAX_HFREFERENCE};
-
- const ALfloat late_allpass_avg{
- std::accumulate(LATE_ALLPASS_LENGTHS.begin(), LATE_ALLPASS_LENGTHS.end(), 0.0f) /
- float{LATE_ALLPASS_LENGTHS_size}};
-
- /* To compensate for changes in modal density and decay time of the late
- * reverb signal, the input is attenuated based on the maximal energy of
- * the outgoing signal. This approximation is used to keep the apparent
- * energy of the signal equal for all ranges of density and decay time.
- *
- * The average length of the delay lines is used to calculate the
- * attenuation coefficient.
- */
- const ALfloat multiplier{CalcDelayLengthMult(density)};
- ALfloat length{std::accumulate(LATE_LINE_LENGTHS.begin(), LATE_LINE_LENGTHS.end(), 0.0f) /
- float{LATE_LINE_LENGTHS_size} * multiplier};
- length += late_allpass_avg * multiplier;
- /* The density gain calculation uses an average decay time weighted by
- * approximate bandwidth. This attempts to compensate for losses of energy
- * that reduce decay time due to scattering into highly attenuated bands.
- */
- const ALfloat bandWeights[3]{
- lf0norm*norm_weight_factor,
- hf0norm*norm_weight_factor - lf0norm*norm_weight_factor,
- 1.0f - hf0norm*norm_weight_factor};
- DensityGain[1] = CalcDensityGain(
- CalcDecayCoeff(length,
- bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime + bandWeights[2]*hfDecayTime
- )
- );
-
- /* Calculate the all-pass feed-back/forward coefficient. */
- VecAp.Coeff = std::sqrt(0.5f) * std::pow(diffusion, 2.0f);
-
- for(ALsizei i{0};i < NUM_LINES;i++)
- {
- /* Calculate the length (in seconds) of each all-pass line. */
- length = LATE_ALLPASS_LENGTHS[i] * multiplier;
-
- /* Calculate the delay offset for each all-pass line. */
- VecAp.Offset[i][1] = float2int(length * frequency);
-
- /* Calculate the length (in seconds) of each delay line. */
- length = LATE_LINE_LENGTHS[i] * multiplier;
-
- /* Calculate the delay offset for each delay line. */
- Offset[i][1] = float2int(length*frequency + 0.5f);
-
- /* Approximate the absorption that the vector all-pass would exhibit
- * given the current diffusion so we don't have to process a full T60
- * filter for each of its four lines.
- */
- length += lerp(LATE_ALLPASS_LENGTHS[i], late_allpass_avg, diffusion) * multiplier;
-
- /* Calculate the T60 damping coefficients for each line. */
- T60[i].calcCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime, lf0norm, hf0norm);
- }
-}
-
-
-/* Update the offsets for the main effect delay line. */
-void ReverbState::updateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay,
- const ALfloat density, const ALfloat decayTime, const ALfloat frequency)
-{
- const ALfloat multiplier{CalcDelayLengthMult(density)};
-
- /* Early reflection taps are decorrelated by means of an average room
- * reflection approximation described above the definition of the taps.
- * This approximation is linear and so the above density multiplier can
- * be applied to adjust the width of the taps. A single-band decay
- * coefficient is applied to simulate initial attenuation and absorption.
- *
- * Late reverb taps are based on the late line lengths to allow a zero-
- * delay path and offsets that would continue the propagation naturally
- * into the late lines.
- */
- for(ALsizei i{0};i < NUM_LINES;i++)
- {
- ALfloat length{earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier};
- mEarlyDelayTap[i][1] = float2int(length * frequency);
-
- length = EARLY_TAP_LENGTHS[i]*multiplier;
- mEarlyDelayCoeff[i][1] = CalcDecayCoeff(length, decayTime);
-
- length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS.front()) /
- float{LATE_LINE_LENGTHS_size} * multiplier;
- mLateDelayTap[i][1] = mLateFeedTap + float2int(length * frequency);
- }
-}
-
-/* Creates a transform matrix given a reverb vector. The vector pans the reverb
- * reflections toward the given direction, using its magnitude (up to 1) as a
- * focal strength. This function results in a B-Format transformation matrix
- * that spatially focuses the signal in the desired direction.
- */
-alu::Matrix GetTransformFromVector(const ALfloat *vec)
-{
- /* Normalize the panning vector according to the N3D scale, which has an
- * extra sqrt(3) term on the directional components. Converting from OpenAL
- * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
- * that the reverb panning vectors use left-handed coordinates, unlike the
- * rest of OpenAL which use right-handed. This is fixed by negating Z,
- * which cancels out with the B-Format Z negation.
- */
- ALfloat norm[3];
- ALfloat mag{std::sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2])};
- if(mag > 1.0f)
- {
- norm[0] = vec[0] / mag * -al::MathDefs<float>::Sqrt3();
- norm[1] = vec[1] / mag * al::MathDefs<float>::Sqrt3();
- norm[2] = vec[2] / mag * al::MathDefs<float>::Sqrt3();
- mag = 1.0f;
- }
- else
- {
- /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
- * term. There's no need to renormalize the magnitude since it would
- * just be reapplied in the matrix.
- */
- norm[0] = vec[0] * -al::MathDefs<float>::Sqrt3();
- norm[1] = vec[1] * al::MathDefs<float>::Sqrt3();
- norm[2] = vec[2] * al::MathDefs<float>::Sqrt3();
- }
-
- return alu::Matrix{
- 1.0f, 0.0f, 0.0f, 0.0f,
- norm[0], 1.0f-mag, 0.0f, 0.0f,
- norm[1], 0.0f, 1.0f-mag, 0.0f,
- norm[2], 0.0f, 0.0f, 1.0f-mag
- };
-}
-
-/* Update the early and late 3D panning gains. */
-void ReverbState::update3DPanning(const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan,
- const ALfloat earlyGain, const ALfloat lateGain, const EffectTarget &target)
-{
- /* Create matrices that transform a B-Format signal according to the
- * panning vectors.
- */
- const alu::Matrix earlymat{GetTransformFromVector(ReflectionsPan)};
- const alu::Matrix latemat{GetTransformFromVector(LateReverbPan)};
-
- mOutTarget = target.Main->Buffer;
- for(ALsizei i{0};i < NUM_LINES;i++)
- {
- const ALfloat coeffs[MAX_AMBI_CHANNELS]{earlymat[0][i], earlymat[1][i], earlymat[2][i],
- earlymat[3][i]};
- ComputePanGains(target.Main, coeffs, earlyGain, mEarly.PanGain[i]);
- }
- for(ALsizei i{0};i < NUM_LINES;i++)
- {
- const ALfloat coeffs[MAX_AMBI_CHANNELS]{latemat[0][i], latemat[1][i], latemat[2][i],
- latemat[3][i]};
- ComputePanGains(target.Main, coeffs, lateGain, mLate.PanGain[i]);
- }
-}
-
-void ReverbState::update(const ALCcontext *Context, const ALeffectslot *Slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *Device{Context->Device};
- const ALlistener &Listener = Context->Listener;
- const auto frequency = static_cast<ALfloat>(Device->Frequency);
-
- /* Calculate the master filters */
- ALfloat hf0norm{minf(props->Reverb.HFReference / frequency, 0.49f)};
- /* Restrict the filter gains from going below -60dB to keep the filter from
- * killing most of the signal.
- */
- ALfloat gainhf{maxf(props->Reverb.GainHF, 0.001f)};
- mFilter[0].Lp.setParams(BiquadType::HighShelf, gainhf, hf0norm,
- mFilter[0].Lp.rcpQFromSlope(gainhf, 1.0f));
- ALfloat lf0norm{minf(props->Reverb.LFReference / frequency, 0.49f)};
- ALfloat gainlf{maxf(props->Reverb.GainLF, 0.001f)};
- mFilter[0].Hp.setParams(BiquadType::LowShelf, gainlf, lf0norm,
- mFilter[0].Hp.rcpQFromSlope(gainlf, 1.0f));
- for(ALsizei i{1};i < NUM_LINES;i++)
- {
- mFilter[i].Lp.copyParamsFrom(mFilter[0].Lp);
- mFilter[i].Hp.copyParamsFrom(mFilter[0].Hp);
- }
-
- /* Update the main effect delay and associated taps. */
- updateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
- props->Reverb.Density, props->Reverb.DecayTime, frequency);
-
- /* Update the early lines. */
- mEarly.updateLines(props->Reverb.Density, props->Reverb.Diffusion, props->Reverb.DecayTime,
- frequency);
-
- /* Get the mixing matrix coefficients. */
- CalcMatrixCoeffs(props->Reverb.Diffusion, &mMixX, &mMixY);
-
- /* If the HF limit parameter is flagged, calculate an appropriate limit
- * based on the air absorption parameter.
- */
- ALfloat hfRatio{props->Reverb.DecayHFRatio};
- if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
- hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
- props->Reverb.DecayTime, Listener.Params.ReverbSpeedOfSound
- );
-
- /* Calculate the LF/HF decay times. */
- const ALfloat lfDecayTime{clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
- AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME)};
- const ALfloat hfDecayTime{clampf(props->Reverb.DecayTime * hfRatio,
- AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME)};
-
- /* Update the late lines. */
- mLate.updateLines(props->Reverb.Density, props->Reverb.Diffusion, lfDecayTime,
- props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm, frequency);
-
- /* Update early and late 3D panning. */
- const ALfloat gain{props->Reverb.Gain * Slot->Params.Gain * ReverbBoost};
- update3DPanning(props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan,
- props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain, target);
-
- /* Calculate the max update size from the smallest relevant delay. */
- mMaxUpdate[1] = mini(BUFFERSIZE, mini(mEarly.Offset[0][1], mLate.Offset[0][1]));
-
- /* Determine if delay-line cross-fading is required. Density is essentially
- * a master control for the feedback delays, so changes the offsets of many
- * delay lines.
- */
- if(mParams.Density != props->Reverb.Density ||
- /* Diffusion and decay times influences the decay rate (gain) of the
- * late reverb T60 filter.
- */
- mParams.Diffusion != props->Reverb.Diffusion ||
- mParams.DecayTime != props->Reverb.DecayTime ||
- mParams.HFDecayTime != hfDecayTime ||
- mParams.LFDecayTime != lfDecayTime ||
- /* HF/LF References control the weighting used to calculate the density
- * gain.
- */
- mParams.HFReference != props->Reverb.HFReference ||
- mParams.LFReference != props->Reverb.LFReference)
- mFadeCount = 0;
- mParams.Density = props->Reverb.Density;
- mParams.Diffusion = props->Reverb.Diffusion;
- mParams.DecayTime = props->Reverb.DecayTime;
- mParams.HFDecayTime = hfDecayTime;
- mParams.LFDecayTime = lfDecayTime;
- mParams.HFReference = props->Reverb.HFReference;
- mParams.LFReference = props->Reverb.LFReference;
-}
-
-
-/**************************************
- * Effect Processing *
- **************************************/
-
-/* Applies a scattering matrix to the 4-line (vector) input. This is used
- * for both the below vector all-pass model and to perform modal feed-back
- * delay network (FDN) mixing.
- *
- * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
- * matrix with a single unitary rotational parameter:
- *
- * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
- * [ -a, d, c, -b ]
- * [ -b, -c, d, a ]
- * [ -c, b, -a, d ]
- *
- * The rotation is constructed from the effect's diffusion parameter,
- * yielding:
- *
- * 1 = x^2 + 3 y^2
- *
- * Where a, b, and c are the coefficient y with differing signs, and d is the
- * coefficient x. The final matrix is thus:
- *
- * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
- * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
- * [ y, -y, x, y ] x = cos(t)
- * [ -y, -y, -y, x ] y = sin(t) / n
- *
- * Any square orthogonal matrix with an order that is a power of two will
- * work (where ^T is transpose, ^-1 is inverse):
- *
- * M^T = M^-1
- *
- * Using that knowledge, finding an appropriate matrix can be accomplished
- * naively by searching all combinations of:
- *
- * M = D + S - S^T
- *
- * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
- * whose combination of signs are being iterated.
- */
-inline void VectorPartialScatter(ALfloat *RESTRICT out, const ALfloat *RESTRICT in,
- const ALfloat xCoeff, const ALfloat yCoeff)
-{
- out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
- out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
- out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
- out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
-}
-
-/* Utilizes the above, but reverses the input channels. */
-void VectorScatterRevDelayIn(const DelayLineI delay, ALint offset, const ALfloat xCoeff,
- const ALfloat yCoeff, const ALsizei base, const al::span<const FloatBufferLine,NUM_LINES> in,
- const ALsizei count)
-{
- ASSUME(base >= 0);
- ASSUME(count > 0);
-
- for(ALsizei i{0};i < count;)
- {
- offset &= delay.Mask;
- ALsizei td{mini(delay.Mask+1 - offset, count-i)};
- do {
- ALfloat f[NUM_LINES];
- for(ALsizei j{0};j < NUM_LINES;j++)
- f[NUM_LINES-1-j] = in[j][base+i];
- ++i;
-
- VectorPartialScatter(delay.Line[offset++], f, xCoeff, yCoeff);
- } while(--td);
- }
-}
-
-/* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
- * filter to the 4-line input.
- *
- * It works by vectorizing a regular all-pass filter and replacing the delay
- * element with a scattering matrix (like the one above) and a diagonal
- * matrix of delay elements.
- *
- * Two static specializations are used for transitional (cross-faded) delay
- * line processing and non-transitional processing.
- */
-void VecAllpass::processUnfaded(const al::span<FloatBufferLine,NUM_LINES> samples, ALsizei offset,
- const ALfloat xCoeff, const ALfloat yCoeff, const ALsizei todo)
-{
- const DelayLineI delay{Delay};
- const ALfloat feedCoeff{Coeff};
-
- ASSUME(todo > 0);
-
- ALsizei vap_offset[NUM_LINES];
- for(ALsizei j{0};j < NUM_LINES;j++)
- vap_offset[j] = offset - Offset[j][0];
- for(ALsizei i{0};i < todo;)
- {
- for(ALsizei j{0};j < NUM_LINES;j++)
- vap_offset[j] &= delay.Mask;
- offset &= delay.Mask;
-
- ALsizei maxoff{offset};
- for(ALsizei j{0};j < NUM_LINES;j++)
- maxoff = maxi(maxoff, vap_offset[j]);
- ALsizei td{mini(delay.Mask+1 - maxoff, todo - i)};
-
- do {
- ALfloat f[NUM_LINES];
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- const ALfloat input{samples[j][i]};
- const ALfloat out{delay.Line[vap_offset[j]++][j] - feedCoeff*input};
- f[j] = input + feedCoeff*out;
-
- samples[j][i] = out;
- }
- ++i;
-
- VectorPartialScatter(delay.Line[offset++], f, xCoeff, yCoeff);
- } while(--td);
- }
-}
-void VecAllpass::processFaded(const al::span<FloatBufferLine,NUM_LINES> samples, ALsizei offset,
- const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade, const ALsizei todo)
-{
- const DelayLineI delay{Delay};
- const ALfloat feedCoeff{Coeff};
-
- ASSUME(todo > 0);
-
- fade *= 1.0f/FADE_SAMPLES;
- ALsizei vap_offset[NUM_LINES][2];
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- vap_offset[j][0] = offset - Offset[j][0];
- vap_offset[j][1] = offset - Offset[j][1];
- }
- for(ALsizei i{0};i < todo;)
- {
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- vap_offset[j][0] &= delay.Mask;
- vap_offset[j][1] &= delay.Mask;
- }
- offset &= delay.Mask;
-
- ALsizei maxoff{offset};
- for(ALsizei j{0};j < NUM_LINES;j++)
- maxoff = maxi(maxoff, maxi(vap_offset[j][0], vap_offset[j][1]));
- ALsizei td{mini(delay.Mask+1 - maxoff, todo - i)};
-
- do {
- fade += FadeStep;
- ALfloat f[NUM_LINES];
- for(ALsizei j{0};j < NUM_LINES;j++)
- f[j] = delay.Line[vap_offset[j][0]++][j]*(1.0f-fade) +
- delay.Line[vap_offset[j][1]++][j]*fade;
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- const ALfloat input{samples[j][i]};
- const ALfloat out{f[j] - feedCoeff*input};
- f[j] = input + feedCoeff*out;
-
- samples[j][i] = out;
- }
- ++i;
-
- VectorPartialScatter(delay.Line[offset++], f, xCoeff, yCoeff);
- } while(--td);
- }
-}
-
-/* This generates early reflections.
- *
- * This is done by obtaining the primary reflections (those arriving from the
- * same direction as the source) from the main delay line. These are
- * attenuated and all-pass filtered (based on the diffusion parameter).
- *
- * The early lines are then fed in reverse (according to the approximately
- * opposite spatial location of the A-Format lines) to create the secondary
- * reflections (those arriving from the opposite direction as the source).
- *
- * The early response is then completed by combining the primary reflections
- * with the delayed and attenuated output from the early lines.
- *
- * Finally, the early response is reversed, scattered (based on diffusion),
- * and fed into the late reverb section of the main delay line.
- *
- * Two static specializations are used for transitional (cross-faded) delay
- * line processing and non-transitional processing.
- */
-void EarlyReflection_Unfaded(ReverbState *State, const ALsizei offset, const ALsizei todo,
- const ALsizei base, const al::span<FloatBufferLine,NUM_LINES> out)
-{
- const al::span<FloatBufferLine,NUM_LINES> temps{State->mTempSamples};
- const DelayLineI early_delay{State->mEarly.Delay};
- const DelayLineI main_delay{State->mDelay};
- const ALfloat mixX{State->mMixX};
- const ALfloat mixY{State->mMixY};
-
- ASSUME(todo > 0);
-
- /* First, load decorrelated samples from the main delay line as the primary
- * reflections.
- */
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- ALsizei early_delay_tap{offset - State->mEarlyDelayTap[j][0]};
- const ALfloat coeff{State->mEarlyDelayCoeff[j][0]};
- for(ALsizei i{0};i < todo;)
- {
- early_delay_tap &= main_delay.Mask;
- ALsizei td{mini(main_delay.Mask+1 - early_delay_tap, todo - i)};
- do {
- temps[j][i++] = main_delay.Line[early_delay_tap++][j] * coeff;
- } while(--td);
- }
- }
-
- /* Apply a vector all-pass, to help color the initial reflections based on
- * the diffusion strength.
- */
- State->mEarly.VecAp.processUnfaded(temps, offset, mixX, mixY, todo);
-
- /* Apply a delay and bounce to generate secondary reflections, combine with
- * the primary reflections and write out the result for mixing.
- */
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- ALint feedb_tap{offset - State->mEarly.Offset[j][0]};
- const ALfloat feedb_coeff{State->mEarly.Coeff[j][0]};
-
- ASSUME(base >= 0);
- for(ALsizei i{0};i < todo;)
- {
- feedb_tap &= early_delay.Mask;
- ALsizei td{mini(early_delay.Mask+1 - feedb_tap, todo - i)};
- do {
- out[j][base+i] = temps[j][i] + early_delay.Line[feedb_tap++][j]*feedb_coeff;
- ++i;
- } while(--td);
- }
- }
- for(ALsizei j{0};j < NUM_LINES;j++)
- early_delay.write(offset, NUM_LINES-1-j, temps[j].data(), todo);
-
- /* Also write the result back to the main delay line for the late reverb
- * stage to pick up at the appropriate time, appplying a scatter and
- * bounce to improve the initial diffusion in the late reverb.
- */
- const ALsizei late_feed_tap{offset - State->mLateFeedTap};
- VectorScatterRevDelayIn(main_delay, late_feed_tap, mixX, mixY, base,
- {out.cbegin(), out.cend()}, todo);
-}
-void EarlyReflection_Faded(ReverbState *State, const ALsizei offset, const ALsizei todo,
- const ALfloat fade, const ALsizei base, const al::span<FloatBufferLine,NUM_LINES> out)
-{
- const al::span<FloatBufferLine,NUM_LINES> temps{State->mTempSamples};
- const DelayLineI early_delay{State->mEarly.Delay};
- const DelayLineI main_delay{State->mDelay};
- const ALfloat mixX{State->mMixX};
- const ALfloat mixY{State->mMixY};
-
- ASSUME(todo > 0);
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- ALsizei early_delay_tap0{offset - State->mEarlyDelayTap[j][0]};
- ALsizei early_delay_tap1{offset - State->mEarlyDelayTap[j][1]};
- const ALfloat oldCoeff{State->mEarlyDelayCoeff[j][0]};
- const ALfloat oldCoeffStep{-oldCoeff / FADE_SAMPLES};
- const ALfloat newCoeffStep{State->mEarlyDelayCoeff[j][1] / FADE_SAMPLES};
- ALfloat fadeCount{fade};
-
- for(ALsizei i{0};i < todo;)
- {
- early_delay_tap0 &= main_delay.Mask;
- early_delay_tap1 &= main_delay.Mask;
- ALsizei td{mini(main_delay.Mask+1 - maxi(early_delay_tap0, early_delay_tap1), todo-i)};
- do {
- fadeCount += 1.0f;
- const ALfloat fade0{oldCoeff + oldCoeffStep*fadeCount};
- const ALfloat fade1{newCoeffStep*fadeCount};
- temps[j][i++] =
- main_delay.Line[early_delay_tap0++][j]*fade0 +
- main_delay.Line[early_delay_tap1++][j]*fade1;
- } while(--td);
- }
- }
-
- State->mEarly.VecAp.processFaded(temps, offset, mixX, mixY, fade, todo);
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- ALint feedb_tap0{offset - State->mEarly.Offset[j][0]};
- ALint feedb_tap1{offset - State->mEarly.Offset[j][1]};
- const ALfloat feedb_oldCoeff{State->mEarly.Coeff[j][0]};
- const ALfloat feedb_oldCoeffStep{-feedb_oldCoeff / FADE_SAMPLES};
- const ALfloat feedb_newCoeffStep{State->mEarly.Coeff[j][1] / FADE_SAMPLES};
- ALfloat fadeCount{fade};
-
- ASSUME(base >= 0);
- for(ALsizei i{0};i < todo;)
- {
- feedb_tap0 &= early_delay.Mask;
- feedb_tap1 &= early_delay.Mask;
- ALsizei td{mini(early_delay.Mask+1 - maxi(feedb_tap0, feedb_tap1), todo - i)};
-
- do {
- fadeCount += 1.0f;
- const ALfloat fade0{feedb_oldCoeff + feedb_oldCoeffStep*fadeCount};
- const ALfloat fade1{feedb_newCoeffStep*fadeCount};
- out[j][base+i] = temps[j][i] +
- early_delay.Line[feedb_tap0++][j]*fade0 +
- early_delay.Line[feedb_tap1++][j]*fade1;
- ++i;
- } while(--td);
- }
- }
- for(ALsizei j{0};j < NUM_LINES;j++)
- early_delay.write(offset, NUM_LINES-1-j, temps[j].data(), todo);
-
- const ALsizei late_feed_tap{offset - State->mLateFeedTap};
- VectorScatterRevDelayIn(main_delay, late_feed_tap, mixX, mixY, base,
- {out.cbegin(), out.cend()}, todo);
-}
-
-/* This generates the reverb tail using a modified feed-back delay network
- * (FDN).
- *
- * Results from the early reflections are mixed with the output from the late
- * delay lines.
- *
- * The late response is then completed by T60 and all-pass filtering the mix.
- *
- * Finally, the lines are reversed (so they feed their opposite directions)
- * and scattered with the FDN matrix before re-feeding the delay lines.
- *
- * Two variations are made, one for for transitional (cross-faded) delay line
- * processing and one for non-transitional processing.
- */
-void LateReverb_Unfaded(ReverbState *State, const ALsizei offset, const ALsizei todo,
- const ALsizei base, const al::span<FloatBufferLine,NUM_LINES> out)
-{
- const al::span<FloatBufferLine,NUM_LINES> temps{State->mTempSamples};
- const DelayLineI late_delay{State->mLate.Delay};
- const DelayLineI main_delay{State->mDelay};
- const ALfloat mixX{State->mMixX};
- const ALfloat mixY{State->mMixY};
-
- ASSUME(todo > 0);
-
- /* First, load decorrelated samples from the main and feedback delay lines.
- * Filter the signal to apply its frequency-dependent decay.
- */
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- ALsizei late_delay_tap{offset - State->mLateDelayTap[j][0]};
- ALsizei late_feedb_tap{offset - State->mLate.Offset[j][0]};
- const ALfloat midGain{State->mLate.T60[j].MidGain[0]};
- const ALfloat densityGain{State->mLate.DensityGain[0] * midGain};
- for(ALsizei i{0};i < todo;)
- {
- late_delay_tap &= main_delay.Mask;
- late_feedb_tap &= late_delay.Mask;
- ALsizei td{mini(
- mini(main_delay.Mask+1 - late_delay_tap, late_delay.Mask+1 - late_feedb_tap),
- todo - i)};
- do {
- temps[j][i++] =
- main_delay.Line[late_delay_tap++][j]*densityGain +
- late_delay.Line[late_feedb_tap++][j]*midGain;
- } while(--td);
- }
- State->mLate.T60[j].process(temps[j].data(), todo);
- }
-
- /* Apply a vector all-pass to improve micro-surface diffusion, and write
- * out the results for mixing.
- */
- State->mLate.VecAp.processUnfaded(temps, offset, mixX, mixY, todo);
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- std::copy_n(temps[j].begin(), todo, out[j].begin()+base);
-
- /* Finally, scatter and bounce the results to refeed the feedback buffer. */
- VectorScatterRevDelayIn(late_delay, offset, mixX, mixY, base,
- {out.cbegin(), out.cend()}, todo);
-}
-void LateReverb_Faded(ReverbState *State, const ALsizei offset, const ALsizei todo,
- const ALfloat fade, const ALsizei base, const al::span<FloatBufferLine,NUM_LINES> out)
-{
- const al::span<FloatBufferLine,NUM_LINES> temps{State->mTempSamples};
- const DelayLineI late_delay{State->mLate.Delay};
- const DelayLineI main_delay{State->mDelay};
- const ALfloat mixX{State->mMixX};
- const ALfloat mixY{State->mMixY};
-
- ASSUME(todo > 0);
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- const ALfloat oldMidGain{State->mLate.T60[j].MidGain[0]};
- const ALfloat midGain{State->mLate.T60[j].MidGain[1]};
- const ALfloat oldMidStep{-oldMidGain / FADE_SAMPLES};
- const ALfloat midStep{midGain / FADE_SAMPLES};
- const ALfloat oldDensityGain{State->mLate.DensityGain[0] * oldMidGain};
- const ALfloat densityGain{State->mLate.DensityGain[1] * midGain};
- const ALfloat oldDensityStep{-oldDensityGain / FADE_SAMPLES};
- const ALfloat densityStep{densityGain / FADE_SAMPLES};
- ALsizei late_delay_tap0{offset - State->mLateDelayTap[j][0]};
- ALsizei late_delay_tap1{offset - State->mLateDelayTap[j][1]};
- ALsizei late_feedb_tap0{offset - State->mLate.Offset[j][0]};
- ALsizei late_feedb_tap1{offset - State->mLate.Offset[j][1]};
- ALfloat fadeCount{fade};
-
- for(ALsizei i{0};i < todo;)
- {
- late_delay_tap0 &= main_delay.Mask;
- late_delay_tap1 &= main_delay.Mask;
- late_feedb_tap0 &= late_delay.Mask;
- late_feedb_tap1 &= late_delay.Mask;
- ALsizei td{mini(
- mini(main_delay.Mask+1 - maxi(late_delay_tap0, late_delay_tap1),
- late_delay.Mask+1 - maxi(late_feedb_tap0, late_feedb_tap1)),
- todo - i)};
- do {
- fadeCount += 1.0f;
- const ALfloat fade0{oldDensityGain + oldDensityStep*fadeCount};
- const ALfloat fade1{densityStep*fadeCount};
- const ALfloat gfade0{oldMidGain + oldMidStep*fadeCount};
- const ALfloat gfade1{midStep*fadeCount};
- temps[j][i++] =
- main_delay.Line[late_delay_tap0++][j]*fade0 +
- main_delay.Line[late_delay_tap1++][j]*fade1 +
- late_delay.Line[late_feedb_tap0++][j]*gfade0 +
- late_delay.Line[late_feedb_tap1++][j]*gfade1;
- } while(--td);
- }
- State->mLate.T60[j].process(temps[j].data(), todo);
- }
-
- State->mLate.VecAp.processFaded(temps, offset, mixX, mixY, fade, todo);
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- std::copy_n(temps[j].begin(), todo, out[j].begin()+base);
-
- VectorScatterRevDelayIn(late_delay, offset, mixX, mixY, base,
- {out.cbegin(), out.cend()}, todo);
-}
-
-void ReverbState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- ALsizei fadeCount{mFadeCount};
-
- ASSUME(samplesToDo > 0);
-
- /* Convert B-Format to A-Format for processing. */
- const al::span<FloatBufferLine,NUM_LINES> afmt{mTempSamples};
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- std::fill_n(afmt[c].begin(), samplesToDo, 0.0f);
- MixRowSamples(afmt[c], B2A[c], {samplesIn, samplesIn+numInput}, 0, samplesToDo);
-
- /* Band-pass the incoming samples. */
- mFilter[c].Lp.process(afmt[c].data(), afmt[c].data(), samplesToDo);
- mFilter[c].Hp.process(afmt[c].data(), afmt[c].data(), samplesToDo);
- }
-
- /* Process reverb for these samples. */
- for(ALsizei base{0};base < samplesToDo;)
- {
- ALsizei todo{samplesToDo - base};
- /* If cross-fading, don't do more samples than there are to fade. */
- if(FADE_SAMPLES-fadeCount > 0)
- {
- todo = mini(todo, FADE_SAMPLES-fadeCount);
- todo = mini(todo, mMaxUpdate[0]);
- }
- todo = mini(todo, mMaxUpdate[1]);
- ASSUME(todo > 0 && todo <= BUFFERSIZE);
-
- const ALsizei offset{mOffset + base};
- ASSUME(offset >= 0);
-
- /* Feed the initial delay line. */
- for(ALsizei c{0};c < NUM_LINES;c++)
- mDelay.write(offset, c, afmt[c].data()+base, todo);
-
- /* Process the samples for reverb. */
- if(UNLIKELY(fadeCount < FADE_SAMPLES))
- {
- auto fade = static_cast<ALfloat>(fadeCount);
-
- /* Generate early reflections and late reverb. */
- EarlyReflection_Faded(this, offset, todo, fade, base, mEarlyBuffer);
-
- LateReverb_Faded(this, offset, todo, fade, base, mLateBuffer);
-
- /* Step fading forward. */
- fadeCount += todo;
- if(fadeCount >= FADE_SAMPLES)
- {
- /* Update the cross-fading delay line taps. */
- fadeCount = FADE_SAMPLES;
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- mEarlyDelayTap[c][0] = mEarlyDelayTap[c][1];
- mEarlyDelayCoeff[c][0] = mEarlyDelayCoeff[c][1];
- mEarly.VecAp.Offset[c][0] = mEarly.VecAp.Offset[c][1];
- mEarly.Offset[c][0] = mEarly.Offset[c][1];
- mEarly.Coeff[c][0] = mEarly.Coeff[c][1];
- mLateDelayTap[c][0] = mLateDelayTap[c][1];
- mLate.VecAp.Offset[c][0] = mLate.VecAp.Offset[c][1];
- mLate.Offset[c][0] = mLate.Offset[c][1];
- mLate.T60[c].MidGain[0] = mLate.T60[c].MidGain[1];
- }
- mLate.DensityGain[0] = mLate.DensityGain[1];
- mMaxUpdate[0] = mMaxUpdate[1];
- }
- }
- else
- {
- /* Generate early reflections and late reverb. */
- EarlyReflection_Unfaded(this, offset, todo, base, mEarlyBuffer);
-
- LateReverb_Unfaded(this, offset, todo, base, mLateBuffer);
- }
-
- base += todo;
- }
- mOffset = (mOffset+samplesToDo) & 0x3fffffff;
- mFadeCount = fadeCount;
-
- /* Finally, mix early reflections and late reverb. */
- (this->*mMixOut)(samplesOut, samplesToDo);
-}
-
-
-void EAXReverb_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_EAXREVERB_DECAY_HFLIMIT:
- if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
- props->Reverb.DecayHFLimit = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
- param);
- }
-}
-void EAXReverb_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ EAXReverb_setParami(props, context, param, vals[0]); }
-void EAXReverb_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_EAXREVERB_DENSITY:
- if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
- props->Reverb.Density = val;
- break;
-
- case AL_EAXREVERB_DIFFUSION:
- if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
- props->Reverb.Diffusion = val;
- break;
-
- case AL_EAXREVERB_GAIN:
- if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
- props->Reverb.Gain = val;
- break;
-
- case AL_EAXREVERB_GAINHF:
- if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
- props->Reverb.GainHF = val;
- break;
-
- case AL_EAXREVERB_GAINLF:
- if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
- props->Reverb.GainLF = val;
- break;
-
- case AL_EAXREVERB_DECAY_TIME:
- if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
- props->Reverb.DecayTime = val;
- break;
-
- case AL_EAXREVERB_DECAY_HFRATIO:
- if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
- props->Reverb.DecayHFRatio = val;
- break;
-
- case AL_EAXREVERB_DECAY_LFRATIO:
- if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
- props->Reverb.DecayLFRatio = val;
- break;
-
- case AL_EAXREVERB_REFLECTIONS_GAIN:
- if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
- props->Reverb.ReflectionsGain = val;
- break;
-
- case AL_EAXREVERB_REFLECTIONS_DELAY:
- if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
- props->Reverb.ReflectionsDelay = val;
- break;
-
- case AL_EAXREVERB_LATE_REVERB_GAIN:
- if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
- props->Reverb.LateReverbGain = val;
- break;
-
- case AL_EAXREVERB_LATE_REVERB_DELAY:
- if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
- props->Reverb.LateReverbDelay = val;
- break;
-
- case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
- if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
- props->Reverb.AirAbsorptionGainHF = val;
- break;
-
- case AL_EAXREVERB_ECHO_TIME:
- if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
- props->Reverb.EchoTime = val;
- break;
-
- case AL_EAXREVERB_ECHO_DEPTH:
- if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
- props->Reverb.EchoDepth = val;
- break;
-
- case AL_EAXREVERB_MODULATION_TIME:
- if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
- props->Reverb.ModulationTime = val;
- break;
-
- case AL_EAXREVERB_MODULATION_DEPTH:
- if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
- props->Reverb.ModulationDepth = val;
- break;
-
- case AL_EAXREVERB_HFREFERENCE:
- if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
- props->Reverb.HFReference = val;
- break;
-
- case AL_EAXREVERB_LFREFERENCE:
- if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
- props->Reverb.LFReference = val;
- break;
-
- case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
- if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
- props->Reverb.RoomRolloffFactor = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
- param);
- }
-}
-void EAXReverb_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- switch(param)
- {
- case AL_EAXREVERB_REFLECTIONS_PAN:
- if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2])))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
- props->Reverb.ReflectionsPan[0] = vals[0];
- props->Reverb.ReflectionsPan[1] = vals[1];
- props->Reverb.ReflectionsPan[2] = vals[2];
- break;
- case AL_EAXREVERB_LATE_REVERB_PAN:
- if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2])))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
- props->Reverb.LateReverbPan[0] = vals[0];
- props->Reverb.LateReverbPan[1] = vals[1];
- props->Reverb.LateReverbPan[2] = vals[2];
- break;
-
- default:
- EAXReverb_setParamf(props, context, param, vals[0]);
- break;
- }
-}
-
-void EAXReverb_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_EAXREVERB_DECAY_HFLIMIT:
- *val = props->Reverb.DecayHFLimit;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
- param);
- }
-}
-void EAXReverb_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ EAXReverb_getParami(props, context, param, vals); }
-void EAXReverb_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_EAXREVERB_DENSITY:
- *val = props->Reverb.Density;
- break;
-
- case AL_EAXREVERB_DIFFUSION:
- *val = props->Reverb.Diffusion;
- break;
-
- case AL_EAXREVERB_GAIN:
- *val = props->Reverb.Gain;
- break;
-
- case AL_EAXREVERB_GAINHF:
- *val = props->Reverb.GainHF;
- break;
-
- case AL_EAXREVERB_GAINLF:
- *val = props->Reverb.GainLF;
- break;
-
- case AL_EAXREVERB_DECAY_TIME:
- *val = props->Reverb.DecayTime;
- break;
-
- case AL_EAXREVERB_DECAY_HFRATIO:
- *val = props->Reverb.DecayHFRatio;
- break;
-
- case AL_EAXREVERB_DECAY_LFRATIO:
- *val = props->Reverb.DecayLFRatio;
- break;
-
- case AL_EAXREVERB_REFLECTIONS_GAIN:
- *val = props->Reverb.ReflectionsGain;
- break;
-
- case AL_EAXREVERB_REFLECTIONS_DELAY:
- *val = props->Reverb.ReflectionsDelay;
- break;
-
- case AL_EAXREVERB_LATE_REVERB_GAIN:
- *val = props->Reverb.LateReverbGain;
- break;
-
- case AL_EAXREVERB_LATE_REVERB_DELAY:
- *val = props->Reverb.LateReverbDelay;
- break;
-
- case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
- *val = props->Reverb.AirAbsorptionGainHF;
- break;
-
- case AL_EAXREVERB_ECHO_TIME:
- *val = props->Reverb.EchoTime;
- break;
-
- case AL_EAXREVERB_ECHO_DEPTH:
- *val = props->Reverb.EchoDepth;
- break;
-
- case AL_EAXREVERB_MODULATION_TIME:
- *val = props->Reverb.ModulationTime;
- break;
-
- case AL_EAXREVERB_MODULATION_DEPTH:
- *val = props->Reverb.ModulationDepth;
- break;
-
- case AL_EAXREVERB_HFREFERENCE:
- *val = props->Reverb.HFReference;
- break;
-
- case AL_EAXREVERB_LFREFERENCE:
- *val = props->Reverb.LFReference;
- break;
-
- case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
- *val = props->Reverb.RoomRolloffFactor;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
- param);
- }
-}
-void EAXReverb_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- switch(param)
- {
- case AL_EAXREVERB_REFLECTIONS_PAN:
- vals[0] = props->Reverb.ReflectionsPan[0];
- vals[1] = props->Reverb.ReflectionsPan[1];
- vals[2] = props->Reverb.ReflectionsPan[2];
- break;
- case AL_EAXREVERB_LATE_REVERB_PAN:
- vals[0] = props->Reverb.LateReverbPan[0];
- vals[1] = props->Reverb.LateReverbPan[1];
- vals[2] = props->Reverb.LateReverbPan[2];
- break;
-
- default:
- EAXReverb_getParamf(props, context, param, vals);
- break;
- }
-}
-
-DEFINE_ALEFFECT_VTABLE(EAXReverb);
-
-
-struct ReverbStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ReverbState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &EAXReverb_vtable; }
-};
-
-EffectProps ReverbStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Reverb.Density = AL_EAXREVERB_DEFAULT_DENSITY;
- props.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION;
- props.Reverb.Gain = AL_EAXREVERB_DEFAULT_GAIN;
- props.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF;
- props.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF;
- props.Reverb.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME;
- props.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO;
- props.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO;
- props.Reverb.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN;
- props.Reverb.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY;
- props.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
- props.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
- props.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
- props.Reverb.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN;
- props.Reverb.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY;
- props.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
- props.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
- props.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
- props.Reverb.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME;
- props.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH;
- props.Reverb.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME;
- props.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH;
- props.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
- props.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE;
- props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE;
- props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
- props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT;
- return props;
-}
-
-
-void StdReverb_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_REVERB_DECAY_HFLIMIT:
- if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
- props->Reverb.DecayHFLimit = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
- }
-}
-void StdReverb_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ StdReverb_setParami(props, context, param, vals[0]); }
-void StdReverb_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_REVERB_DENSITY:
- if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
- props->Reverb.Density = val;
- break;
-
- case AL_REVERB_DIFFUSION:
- if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
- props->Reverb.Diffusion = val;
- break;
-
- case AL_REVERB_GAIN:
- if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
- props->Reverb.Gain = val;
- break;
-
- case AL_REVERB_GAINHF:
- if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
- props->Reverb.GainHF = val;
- break;
-
- case AL_REVERB_DECAY_TIME:
- if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
- props->Reverb.DecayTime = val;
- break;
-
- case AL_REVERB_DECAY_HFRATIO:
- if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
- props->Reverb.DecayHFRatio = val;
- break;
-
- case AL_REVERB_REFLECTIONS_GAIN:
- if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
- props->Reverb.ReflectionsGain = val;
- break;
-
- case AL_REVERB_REFLECTIONS_DELAY:
- if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
- props->Reverb.ReflectionsDelay = val;
- break;
-
- case AL_REVERB_LATE_REVERB_GAIN:
- if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
- props->Reverb.LateReverbGain = val;
- break;
-
- case AL_REVERB_LATE_REVERB_DELAY:
- if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
- props->Reverb.LateReverbDelay = val;
- break;
-
- case AL_REVERB_AIR_ABSORPTION_GAINHF:
- if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
- props->Reverb.AirAbsorptionGainHF = val;
- break;
-
- case AL_REVERB_ROOM_ROLLOFF_FACTOR:
- if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
- props->Reverb.RoomRolloffFactor = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
- }
-}
-void StdReverb_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ StdReverb_setParamf(props, context, param, vals[0]); }
-
-void StdReverb_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_REVERB_DECAY_HFLIMIT:
- *val = props->Reverb.DecayHFLimit;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
- }
-}
-void StdReverb_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ StdReverb_getParami(props, context, param, vals); }
-void StdReverb_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_REVERB_DENSITY:
- *val = props->Reverb.Density;
- break;
-
- case AL_REVERB_DIFFUSION:
- *val = props->Reverb.Diffusion;
- break;
-
- case AL_REVERB_GAIN:
- *val = props->Reverb.Gain;
- break;
-
- case AL_REVERB_GAINHF:
- *val = props->Reverb.GainHF;
- break;
-
- case AL_REVERB_DECAY_TIME:
- *val = props->Reverb.DecayTime;
- break;
-
- case AL_REVERB_DECAY_HFRATIO:
- *val = props->Reverb.DecayHFRatio;
- break;
-
- case AL_REVERB_REFLECTIONS_GAIN:
- *val = props->Reverb.ReflectionsGain;
- break;
-
- case AL_REVERB_REFLECTIONS_DELAY:
- *val = props->Reverb.ReflectionsDelay;
- break;
-
- case AL_REVERB_LATE_REVERB_GAIN:
- *val = props->Reverb.LateReverbGain;
- break;
-
- case AL_REVERB_LATE_REVERB_DELAY:
- *val = props->Reverb.LateReverbDelay;
- break;
-
- case AL_REVERB_AIR_ABSORPTION_GAINHF:
- *val = props->Reverb.AirAbsorptionGainHF;
- break;
-
- case AL_REVERB_ROOM_ROLLOFF_FACTOR:
- *val = props->Reverb.RoomRolloffFactor;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
- }
-}
-void StdReverb_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ StdReverb_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(StdReverb);
-
-
-struct StdReverbStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ReverbState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &StdReverb_vtable; }
-};
-
-EffectProps StdReverbStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY;
- props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION;
- props.Reverb.Gain = AL_REVERB_DEFAULT_GAIN;
- props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF;
- props.Reverb.GainLF = 1.0f;
- props.Reverb.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME;
- props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO;
- props.Reverb.DecayLFRatio = 1.0f;
- props.Reverb.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN;
- props.Reverb.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY;
- props.Reverb.ReflectionsPan[0] = 0.0f;
- props.Reverb.ReflectionsPan[1] = 0.0f;
- props.Reverb.ReflectionsPan[2] = 0.0f;
- props.Reverb.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN;
- props.Reverb.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY;
- props.Reverb.LateReverbPan[0] = 0.0f;
- props.Reverb.LateReverbPan[1] = 0.0f;
- props.Reverb.LateReverbPan[2] = 0.0f;
- props.Reverb.EchoTime = 0.25f;
- props.Reverb.EchoDepth = 0.0f;
- props.Reverb.ModulationTime = 0.25f;
- props.Reverb.ModulationDepth = 0.0f;
- props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
- props.Reverb.HFReference = 5000.0f;
- props.Reverb.LFReference = 250.0f;
- props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
- props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *ReverbStateFactory_getFactory()
-{
- static ReverbStateFactory ReverbFactory{};
- return &ReverbFactory;
-}
-
-EffectStateFactory *StdReverbStateFactory_getFactory()
-{
- static StdReverbStateFactory ReverbFactory{};
- return &ReverbFactory;
-}
diff --git a/Alc/effects/vmorpher.cpp b/Alc/effects/vmorpher.cpp
deleted file mode 100644
index eebba3f1..00000000
--- a/Alc/effects/vmorpher.cpp
+++ /dev/null
@@ -1,430 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2019 by Anis A. Hireche
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-#include <algorithm>
-#include <functional>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-namespace {
-
-#define MAX_UPDATE_SAMPLES 128
-#define NUM_FORMANTS 4
-#define NUM_FILTERS 2
-#define Q_FACTOR 5.0f
-
-#define VOWEL_A_INDEX 0
-#define VOWEL_B_INDEX 1
-
-#define WAVEFORM_FRACBITS 24
-#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
-#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
-
-inline ALfloat Sin(ALsizei index)
-{
- constexpr ALfloat scale{al::MathDefs<float>::Tau() / ALfloat{WAVEFORM_FRACONE}};
- return std::sin(static_cast<ALfloat>(index) * scale)*0.5f + 0.5f;
-}
-
-inline ALfloat Saw(ALsizei index)
-{
- return static_cast<ALfloat>(index) / ALfloat{WAVEFORM_FRACONE};
-}
-
-inline ALfloat Triangle(ALsizei index)
-{
- return std::fabs(static_cast<ALfloat>(index)*(2.0f/WAVEFORM_FRACONE) - 1.0f);
-}
-
-inline ALfloat Half(ALsizei)
-{
- return 0.5f;
-}
-
-template<ALfloat func(ALsizei)>
-void Oscillate(ALfloat *RESTRICT dst, ALsizei index, const ALsizei step, ALsizei todo)
-{
- for(ALsizei i{0};i < todo;i++)
- {
- index += step;
- index &= WAVEFORM_FRACMASK;
- dst[i] = func(index);
- }
-}
-
-struct FormantFilter
-{
- ALfloat f0norm{0.0f};
- ALfloat fGain{1.0f};
- ALfloat s1{0.0f};
- ALfloat s2{0.0f};
-
- FormantFilter() = default;
- FormantFilter(ALfloat f0norm_, ALfloat gain) : f0norm{f0norm_}, fGain{gain} { }
-
- inline void process(const ALfloat* samplesIn, ALfloat* samplesOut, const ALsizei numInput)
- {
- /* A state variable filter from a topology-preserving transform.
- * Based on a talk given by Ivan Cohen: https://www.youtube.com/watch?v=esjHXGPyrhg
- */
- const ALfloat g = std::tan(al::MathDefs<float>::Pi() * f0norm);
- const ALfloat h = 1.0f / (1 + (g / Q_FACTOR) + (g * g));
-
- for (ALsizei i{0};i < numInput;i++)
- {
- const ALfloat H = h * (samplesIn[i] - (1.0f / Q_FACTOR + g) * s1 - s2);
- const ALfloat B = g * H + s1;
- const ALfloat L = g * B + s2;
-
- s1 = g * H + B;
- s2 = g * B + L;
-
- // Apply peak and accumulate samples.
- samplesOut[i] += B * fGain;
- }
- }
-
- inline void clear()
- {
- s1 = 0.0f;
- s2 = 0.0f;
- }
-};
-
-
-struct VmorpherState final : public EffectState {
- struct {
- /* Effect parameters */
- FormantFilter Formants[NUM_FILTERS][NUM_FORMANTS];
-
- /* Effect gains for each channel */
- ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]{};
- ALfloat TargetGains[MAX_OUTPUT_CHANNELS]{};
- } mChans[MAX_AMBI_CHANNELS];
-
- void (*mGetSamples)(ALfloat* RESTRICT, ALsizei, const ALsizei, ALsizei) {};
-
- ALsizei mIndex{0};
- ALsizei mStep{1};
-
- /* Effects buffers */
- ALfloat mSampleBufferA[MAX_UPDATE_SAMPLES]{};
- ALfloat mSampleBufferB[MAX_UPDATE_SAMPLES]{};
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- static std::array<FormantFilter,4> getFiltersByPhoneme(ALenum phoneme, ALfloat frequency, ALfloat pitch);
-
- DEF_NEWDEL(VmorpherState)
-};
-
-std::array<FormantFilter,4> VmorpherState::getFiltersByPhoneme(ALenum phoneme, ALfloat frequency, ALfloat pitch)
-{
- /* Using soprano formant set of values to
- * better match mid-range frequency space.
- *
- * See: https://www.classes.cs.uchicago.edu/archive/1999/spring/CS295/Computing_Resources/Csound/CsManual3.48b1.HTML/Appendices/table3.html
- */
- switch(phoneme)
- {
- case AL_VOCAL_MORPHER_PHONEME_A:
- return {{
- {( 800 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
- {(1150 * pitch) / frequency, 0.501187f}, /* std::pow(10.0f, -6 / 20.0f); */
- {(2900 * pitch) / frequency, 0.025118f}, /* std::pow(10.0f, -32 / 20.0f); */
- {(3900 * pitch) / frequency, 0.100000f} /* std::pow(10.0f, -20 / 20.0f); */
- }};
- case AL_VOCAL_MORPHER_PHONEME_E:
- return {{
- {( 350 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
- {(2000 * pitch) / frequency, 0.100000f}, /* std::pow(10.0f, -20 / 20.0f); */
- {(2800 * pitch) / frequency, 0.177827f}, /* std::pow(10.0f, -15 / 20.0f); */
- {(3600 * pitch) / frequency, 0.009999f} /* std::pow(10.0f, -40 / 20.0f); */
- }};
- case AL_VOCAL_MORPHER_PHONEME_I:
- return {{
- {( 270 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
- {(2140 * pitch) / frequency, 0.251188f}, /* std::pow(10.0f, -12 / 20.0f); */
- {(2950 * pitch) / frequency, 0.050118f}, /* std::pow(10.0f, -26 / 20.0f); */
- {(3900 * pitch) / frequency, 0.050118f} /* std::pow(10.0f, -26 / 20.0f); */
- }};
- case AL_VOCAL_MORPHER_PHONEME_O:
- return {{
- {( 450 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
- {( 800 * pitch) / frequency, 0.281838f}, /* std::pow(10.0f, -11 / 20.0f); */
- {(2830 * pitch) / frequency, 0.079432f}, /* std::pow(10.0f, -22 / 20.0f); */
- {(3800 * pitch) / frequency, 0.079432f} /* std::pow(10.0f, -22 / 20.0f); */
- }};
- case AL_VOCAL_MORPHER_PHONEME_U:
- return {{
- {( 325 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
- {( 700 * pitch) / frequency, 0.158489f}, /* std::pow(10.0f, -16 / 20.0f); */
- {(2700 * pitch) / frequency, 0.017782f}, /* std::pow(10.0f, -35 / 20.0f); */
- {(3800 * pitch) / frequency, 0.009999f} /* std::pow(10.0f, -40 / 20.0f); */
- }};
- }
- return {};
-}
-
-
-ALboolean VmorpherState::deviceUpdate(const ALCdevice* /*device*/)
-{
- for(auto &e : mChans)
- {
- std::for_each(std::begin(e.Formants[VOWEL_A_INDEX]), std::end(e.Formants[VOWEL_A_INDEX]),
- std::mem_fn(&FormantFilter::clear));
- std::for_each(std::begin(e.Formants[VOWEL_B_INDEX]), std::end(e.Formants[VOWEL_B_INDEX]),
- std::mem_fn(&FormantFilter::clear));
- std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
- }
-
- return AL_TRUE;
-}
-
-void VmorpherState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device{context->Device};
- const ALfloat frequency{static_cast<ALfloat>(device->Frequency)};
- const ALfloat step{props->Vmorpher.Rate / static_cast<ALfloat>(device->Frequency)};
- mStep = fastf2i(clampf(step*WAVEFORM_FRACONE, 0.0f, ALfloat{WAVEFORM_FRACONE-1}));
-
- if(mStep == 0)
- mGetSamples = Oscillate<Half>;
- else if(props->Vmorpher.Waveform == AL_VOCAL_MORPHER_WAVEFORM_SINUSOID)
- mGetSamples = Oscillate<Sin>;
- else if(props->Vmorpher.Waveform == AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH)
- mGetSamples = Oscillate<Saw>;
- else /*if(props->Vmorpher.Waveform == AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE)*/
- mGetSamples = Oscillate<Triangle>;
-
- const ALfloat pitchA{fastf2i(std::pow(2.0f, props->Vmorpher.PhonemeACoarseTuning*100.0f / 2400.0f)*FRACTIONONE) * (1.0f/FRACTIONONE)};
- const ALfloat pitchB{fastf2i(std::pow(2.0f, props->Vmorpher.PhonemeBCoarseTuning*100.0f / 2400.0f)*FRACTIONONE) * (1.0f/FRACTIONONE)};
-
- auto vowelA = getFiltersByPhoneme(props->Vmorpher.PhonemeA, frequency, pitchA);
- auto vowelB = getFiltersByPhoneme(props->Vmorpher.PhonemeB, frequency, pitchB);
-
- /* Copy the filter coefficients to the input channels. */
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- std::copy(vowelA.begin(), vowelA.end(), std::begin(mChans[i].Formants[VOWEL_A_INDEX]));
- std::copy(vowelB.begin(), vowelB.end(), std::begin(mChans[i].Formants[VOWEL_B_INDEX]));
- }
-
- mOutTarget = target.Main->Buffer;
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- auto coeffs = GetAmbiIdentityRow(i);
- ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mChans[i].TargetGains);
- }
-}
-
-void VmorpherState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- /* Following the EFX specification for a conformant implementation which describes
- * the effect as a pair of 4-band formant filters blended together using an LFO.
- */
- for(ALsizei base{0};base < samplesToDo;)
- {
- alignas(16) ALfloat lfo[MAX_UPDATE_SAMPLES];
- const ALsizei td = mini(MAX_UPDATE_SAMPLES, samplesToDo-base);
-
- mGetSamples(lfo, mIndex, mStep, td);
- mIndex += (mStep * td) & WAVEFORM_FRACMASK;
- mIndex &= WAVEFORM_FRACMASK;
-
- ASSUME(numInput > 0);
- for(ALsizei c{0};c < numInput;c++)
- {
- for (ALsizei i{0};i < td;i++)
- {
- mSampleBufferA[i] = 0.0f;
- mSampleBufferB[i] = 0.0f;
- }
-
- auto& vowelA = mChans[c].Formants[VOWEL_A_INDEX];
- auto& vowelB = mChans[c].Formants[VOWEL_B_INDEX];
-
- /* Process first vowel. */
- vowelA[0].process(&samplesIn[c][base], mSampleBufferA, td);
- vowelA[1].process(&samplesIn[c][base], mSampleBufferA, td);
- vowelA[2].process(&samplesIn[c][base], mSampleBufferA, td);
- vowelA[3].process(&samplesIn[c][base], mSampleBufferA, td);
-
- /* Process second vowel. */
- vowelB[0].process(&samplesIn[c][base], mSampleBufferB, td);
- vowelB[1].process(&samplesIn[c][base], mSampleBufferB, td);
- vowelB[2].process(&samplesIn[c][base], mSampleBufferB, td);
- vowelB[3].process(&samplesIn[c][base], mSampleBufferB, td);
-
- alignas(16) ALfloat samplesBlended[MAX_UPDATE_SAMPLES];
-
- for (ALsizei i{0};i < td;i++)
- samplesBlended[i] = lerp(mSampleBufferA[i], mSampleBufferB[i], lfo[i]);
-
- /* Now, mix the processed sound data to the output. */
- MixSamples(samplesBlended, samplesOut, mChans[c].CurrentGains, mChans[c].TargetGains,
- samplesToDo-base, base, td);
- }
-
- base += td;
- }
-}
-
-
-void Vmorpher_setParami(EffectProps* props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_VOCAL_MORPHER_WAVEFORM:
- if(!(val >= AL_VOCAL_MORPHER_MIN_WAVEFORM && val <= AL_VOCAL_MORPHER_MAX_WAVEFORM))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher waveform out of range");
- props->Vmorpher.Waveform = val;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEA:
- if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher phoneme-a out of range");
- props->Vmorpher.PhonemeA = val;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEB:
- if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher phoneme-b out of range");
- props->Vmorpher.PhonemeB = val;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
- if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher phoneme-a coarse tuning out of range");
- props->Vmorpher.PhonemeACoarseTuning = val;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
- if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher phoneme-b coarse tuning out of range");
- props->Vmorpher.PhonemeBCoarseTuning = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", param);
- }
-}
-void Vmorpher_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", param); }
-void Vmorpher_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_VOCAL_MORPHER_RATE:
- if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher rate out of range");
- props->Vmorpher.Rate = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x", param);
- }
-}
-void Vmorpher_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Vmorpher_setParamf(props, context, param, vals[0]); }
-
-void Vmorpher_getParami(const EffectProps* props, ALCcontext *context, ALenum param, ALint* val)
-{
- switch(param)
- {
- case AL_VOCAL_MORPHER_PHONEMEA:
- *val = props->Vmorpher.PhonemeA;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEB:
- *val = props->Vmorpher.PhonemeB;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
- *val = props->Vmorpher.PhonemeACoarseTuning;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
- *val = props->Vmorpher.PhonemeBCoarseTuning;
- break;
-
- case AL_VOCAL_MORPHER_WAVEFORM:
- *val = props->Vmorpher.Waveform;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", param);
- }
-}
-void Vmorpher_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", param); }
-void Vmorpher_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_VOCAL_MORPHER_RATE:
- *val = props->Vmorpher.Rate;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x", param);
- }
-}
-void Vmorpher_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Vmorpher_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Vmorpher);
-
-
-struct VmorpherStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new VmorpherState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Vmorpher_vtable; }
-};
-
-EffectProps VmorpherStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
- props.Vmorpher.PhonemeA = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA;
- props.Vmorpher.PhonemeB = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB;
- props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
- props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
- props.Vmorpher.Waveform = AL_VOCAL_MORPHER_DEFAULT_WAVEFORM;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *VmorpherStateFactory_getFactory()
-{
- static VmorpherStateFactory VmorpherFactory{};
- return &VmorpherFactory;
-}