diff options
Diffstat (limited to 'alc/effects/modulator.cpp')
-rw-r--r-- | alc/effects/modulator.cpp | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/alc/effects/modulator.cpp b/alc/effects/modulator.cpp new file mode 100644 index 00000000..086482d7 --- /dev/null +++ b/alc/effects/modulator.cpp @@ -0,0 +1,279 @@ +/** + * 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; +} |