From de8c5b18248ee5d121cbd4b34b9af94f5e16df35 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 9 Jan 2018 23:21:16 -0800 Subject: Combine the chorus and flanger processing functions Given that they're nearly identical, it should be relatively simple to use the same effect state to process either of them, similar to the reverbs. The big differences seem to be the delay range (much shorter with flanger) and the defaults. --- Alc/effects/chorus.c | 180 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 164 insertions(+), 16 deletions(-) (limited to 'Alc/effects/chorus.c') diff --git a/Alc/effects/chorus.c b/Alc/effects/chorus.c index caea6e93..d028dbb0 100644 --- a/Alc/effects/chorus.c +++ b/Alc/effects/chorus.c @@ -30,9 +30,12 @@ #include "alu.h" -enum ChorusWaveForm { - CWF_Triangle = AL_CHORUS_WAVEFORM_TRIANGLE, - CWF_Sinusoid = AL_CHORUS_WAVEFORM_SINUSOID +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 WaveForm { + WF_Sinusoid, + WF_Triangle }; typedef struct ALchorusState { @@ -54,7 +57,7 @@ typedef struct ALchorusState { } Gains[2]; /* effect parameters */ - enum ChorusWaveForm waveform; + enum WaveForm waveform; ALint delay; ALfloat depth; ALfloat feedback; @@ -79,7 +82,7 @@ static void ALchorusState_Construct(ALchorusState *state) state->offset = 0; state->lfo_offset = 0; state->lfo_range = 1; - state->waveform = CWF_Triangle; + state->waveform = WF_Triangle; } static ALvoid ALchorusState_Destruct(ALchorusState *state) @@ -92,9 +95,10 @@ static ALvoid ALchorusState_Destruct(ALchorusState *state) static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device) { + const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY); ALsizei maxlen; - maxlen = fastf2i(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1; + maxlen = fastf2i(max_delay * 2.0f * Device->Frequency) + 1; maxlen = NextPowerOf2(maxlen); if(maxlen != state->BufferLength) @@ -126,10 +130,10 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Conte switch(props->Chorus.Waveform) { case AL_CHORUS_WAVEFORM_TRIANGLE: - state->waveform = CWF_Triangle; + state->waveform = WF_Triangle; break; case AL_CHORUS_WAVEFORM_SINUSOID: - state->waveform = CWF_Sinusoid; + state->waveform = WF_Sinusoid; break; } @@ -169,10 +173,10 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Conte state->lfo_range = lfo_range; switch(state->waveform) { - case CWF_Triangle: + case WF_Triangle: state->lfo_scale = 4.0f / state->lfo_range; break; - case CWF_Sinusoid: + case WF_Sinusoid: state->lfo_scale = F_TAU / state->lfo_range; break; } @@ -224,19 +228,19 @@ static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, c ALint moddelays[2][256]; ALfloat temps[2][256]; - if(state->waveform == CWF_Triangle) + if(state->waveform == WF_Sinusoid) { - GetTriangleDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale, + GetSinusoidDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale, state->depth, state->delay, todo); - GetTriangleDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range, + GetSinusoidDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range, state->lfo_range, state->lfo_scale, state->depth, state->delay, todo); } - else /*if(state->waveform == CWF_Sinusoid)*/ + else /*if(state->waveform == WF_Triangle)*/ { - GetSinusoidDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale, + GetTriangleDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale, state->depth, state->delay, todo); - GetSinusoidDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range, + GetTriangleDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range, state->lfo_range, state->lfo_scale, state->depth, state->delay, todo); } @@ -420,3 +424,147 @@ void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum par } DEFINE_ALEFFECT_VTABLE(ALchorus); + + +/* 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. + */ +typedef struct ALflangerStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALflangerStateFactory; + +ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory)) +{ + ALchorusState *state; + + NEW_OBJ0(state, ALchorusState)(); + if(!state) return NULL; + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory); + +ALeffectStateFactory *ALflangerStateFactory_getFactory(void) +{ + static ALflangerStateFactory FlangerFactory = { { GET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &FlangerFactory); +} + + +void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_FLANGER_WAVEFORM: + if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Waveform = val; + break; + + case AL_FLANGER_PHASE: + if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Phase = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALflanger_setParami(effect, context, param, vals[0]); +} +void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_FLANGER_RATE: + if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Rate = val; + break; + + case AL_FLANGER_DEPTH: + if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Depth = val; + break; + + case AL_FLANGER_FEEDBACK: + if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Feedback = val; + break; + + case AL_FLANGER_DELAY: + if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Delay = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALflanger_setParamf(effect, context, param, vals[0]); +} + +void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_FLANGER_WAVEFORM: + *val = props->Chorus.Waveform; + break; + + case AL_FLANGER_PHASE: + *val = props->Chorus.Phase; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALflanger_getParami(effect, context, param, vals); +} +void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + 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: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + ALflanger_getParamf(effect, context, param, vals); +} + +DEFINE_ALEFFECT_VTABLE(ALflanger); -- cgit v1.2.3