diff options
Diffstat (limited to 'Alc/effects/distortion.c')
-rw-r--r-- | Alc/effects/distortion.c | 241 |
1 files changed, 116 insertions, 125 deletions
diff --git a/Alc/effects/distortion.c b/Alc/effects/distortion.c index 221cec39..de8da4fe 100644 --- a/Alc/effects/distortion.c +++ b/Alc/effects/distortion.c @@ -24,10 +24,10 @@ #include <stdlib.h> #include "alMain.h" -#include "alFilter.h" #include "alAuxEffectSlot.h" #include "alError.h" #include "alu.h" +#include "filters/defs.h" typedef struct ALdistortionState { @@ -37,177 +37,172 @@ typedef struct ALdistortionState { ALfloat Gain[MAX_OUTPUT_CHANNELS]; /* Effect parameters */ - ALfilterState lowpass; - ALfilterState bandpass; + BiquadFilter lowpass; + BiquadFilter bandpass; ALfloat attenuation; ALfloat edge_coeff; + + ALfloat Buffer[2][BUFFERSIZE]; } ALdistortionState; -static ALvoid ALdistortionState_Destruct(ALdistortionState *UNUSED(state)) +static ALvoid ALdistortionState_Destruct(ALdistortionState *state); +static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device); +static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props); +static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); +DECLARE_DEFAULT_ALLOCATORS(ALdistortionState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState); + + +static void ALdistortionState_Construct(ALdistortionState *state) +{ + ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); + SET_VTABLE2(ALdistortionState, ALeffectState, state); +} + +static ALvoid ALdistortionState_Destruct(ALdistortionState *state) { + ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } -static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device)) +static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *UNUSED(device)) { + BiquadFilter_clear(&state->lowpass); + BiquadFilter_clear(&state->bandpass); return AL_TRUE; } -static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot) +static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props) { - ALfloat frequency = (ALfloat)Device->Frequency; + const ALCdevice *device = context->Device; + ALfloat frequency = (ALfloat)device->Frequency; + ALfloat coeffs[MAX_AMBI_COEFFS]; ALfloat bandwidth; ALfloat cutoff; ALfloat edge; - /* Store distorted signal attenuation settings */ - state->attenuation = Slot->EffectProps.Distortion.Gain; - - /* Store waveshaper edge settings */ - edge = sinf(Slot->EffectProps.Distortion.Edge * (F_PI_2)); + /* Store waveshaper edge settings. */ + edge = sinf(props->Distortion.Edge * (F_PI_2)); edge = minf(edge, 0.99f); state->edge_coeff = 2.0f * edge / (1.0f-edge); - /* Lowpass filter */ - cutoff = Slot->EffectProps.Distortion.LowpassCutoff; - /* Bandwidth value is constant in octaves */ + cutoff = props->Distortion.LowpassCutoff; + /* Bandwidth value is constant in octaves. */ bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f); - ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f, + /* Multiply sampling frequency by the amount of oversampling done during + * processing. + */ + BiquadFilter_setParams(&state->lowpass, BiquadType_LowPass, 1.0f, cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth) ); - /* Bandpass filter */ - cutoff = Slot->EffectProps.Distortion.EQCenter; - /* Convert bandwidth in Hz to octaves */ - bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f); - ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f, + cutoff = props->Distortion.EQCenter; + /* Convert bandwidth in Hz to octaves. */ + bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f); + BiquadFilter_setParams(&state->bandpass, BiquadType_BandPass, 1.0f, cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth) ); - ComputeAmbientGains(Device, Slot->Gain, state->Gain); + CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs); + ComputePanGains(&device->Dry, coeffs, slot->Params.Gain*props->Distortion.Gain, state->Gain); } -static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) +static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) { + ALfloat (*restrict buffer)[BUFFERSIZE] = state->Buffer; const ALfloat fc = state->edge_coeff; - ALuint base; - ALuint it; - ALuint ot; - ALuint kt; + ALsizei base; + ALsizei i, k; for(base = 0;base < SamplesToDo;) { - float oversample_buffer[64][4]; - ALuint td = minu(64, SamplesToDo-base); - - /* 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. */ - - /* Fill oversample buffer using zero stuffing */ - for(it = 0;it < td;it++) + /* 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(i = 0;i < todo;i++) + buffer[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. + */ + BiquadFilter_process(&state->lowpass, buffer[1], buffer[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(i = 0;i < todo;i++) { - oversample_buffer[it][0] = SamplesIn[it+base]; - oversample_buffer[it][1] = 0.0f; - oversample_buffer[it][2] = 0.0f; - oversample_buffer[it][3] = 0.0f; - } + ALfloat smp = buffer[1][i]; - /* 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. */ - for(it = 0;it < td;it++) - { - for(ot = 0;ot < 4;ot++) - { - ALfloat smp; - smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]); - - /* Restore signal power by multiplying sample by amount of oversampling */ - oversample_buffer[it][ot] = smp * 4.0f; - } - } + 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)); - for(it = 0;it < td;it++) - { - /* 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(ot = 0;ot < 4;ot++) - { - ALfloat smp = oversample_buffer[it][ot]; - - 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)); - - /* Third step, do bandpass filtering of distorted signal */ - smp = ALfilterState_processSingle(&state->bandpass, smp); - oversample_buffer[it][ot] = smp; - } + buffer[0][i] = smp; } - for(kt = 0;kt < NumChannels;kt++) + /* Third step, do bandpass filtering of distorted signal. */ + BiquadFilter_process(&state->bandpass, buffer[1], buffer[0], todo); + + todo >>= 2; + for(k = 0;k < NumChannels;k++) { /* Fourth step, final, do attenuation and perform decimation, - * store only one sample out of 4. + * storing only one sample out of four. */ - ALfloat gain = state->Gain[kt] * state->attenuation; + ALfloat gain = state->Gain[k]; if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) continue; - for(it = 0;it < td;it++) - SamplesOut[kt][base+it] += gain * oversample_buffer[it][0]; + for(i = 0;i < todo;i++) + SamplesOut[k][base+i] += gain * buffer[1][i*4]; } - base += td; + base += todo; } } -DECLARE_DEFAULT_ALLOCATORS(ALdistortionState) - -DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState); - -typedef struct ALdistortionStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALdistortionStateFactory; +typedef struct DistortionStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} DistortionStateFactory; -static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory)) +static ALeffectState *DistortionStateFactory_create(DistortionStateFactory *UNUSED(factory)) { ALdistortionState *state; - state = ALdistortionState_New(sizeof(*state)); + NEW_OBJ0(state, ALdistortionState)(); if(!state) return NULL; - SET_VTABLE2(ALdistortionState, ALeffectState, state); - - ALfilterState_clear(&state->lowpass); - ALfilterState_clear(&state->bandpass); return STATIC_CAST(ALeffectState, state); } -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory); +DEFINE_EFFECTSTATEFACTORY_VTABLE(DistortionStateFactory); -ALeffectStateFactory *ALdistortionStateFactory_getFactory(void) +EffectStateFactory *DistortionStateFactory_getFactory(void) { - static ALdistortionStateFactory DistortionFactory = { { GET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory) } }; + static DistortionStateFactory DistortionFactory = { { GET_VTABLE2(DistortionStateFactory, EffectStateFactory) } }; - return STATIC_CAST(ALeffectStateFactory, &DistortionFactory); + return STATIC_CAST(EffectStateFactory, &DistortionFactory); } -void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALdistortion_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALdistortion_setParami(effect, context, param, vals[0]); -} +void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); } +void ALdistortion_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); } void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) { ALeffectProps *props = &effect->Props; @@ -215,49 +210,46 @@ void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, { case AL_DISTORTION_EDGE: if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range"); props->Distortion.EQBandwidth = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", + param); } } void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - ALdistortion_setParamf(effect, context, param, vals[0]); -} +{ ALdistortion_setParamf(effect, context, param, vals[0]); } -void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALdistortion_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALdistortion_getParami(effect, context, param, vals); -} +void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); } +void ALdistortion_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); } void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) { const ALeffectProps *props = &effect->Props; @@ -284,12 +276,11 @@ void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", + param); } } void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) -{ - ALdistortion_getParamf(effect, context, param, vals); -} +{ ALdistortion_getParamf(effect, context, param, vals); } DEFINE_ALEFFECT_VTABLE(ALdistortion); |