diff options
author | Chris Robinson <[email protected]> | 2013-05-18 01:33:01 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2013-05-18 01:33:01 -0700 |
commit | 78e7c1c27bb0dcc05fc961e53060be17e3df3e02 (patch) | |
tree | d4a1368d6a4039bf11b1a5ae9995089ee705cc71 | |
parent | a7ad6080f0d3fc783fd5e1811b961ab9efe79cde (diff) |
Implement distortion and equalizer effects
Code provided by Mike Gorchak
-rw-r--r-- | Alc/ALc.c | 21 | ||||
-rw-r--r-- | Alc/alcDistortion.c | 356 | ||||
-rw-r--r-- | Alc/alcEqualizer.c | 446 | ||||
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | OpenAL32/Include/alAuxEffectSlot.h | 2 | ||||
-rw-r--r-- | OpenAL32/Include/alEffect.h | 42 | ||||
-rw-r--r-- | OpenAL32/alAuxEffectSlot.c | 4 | ||||
-rw-r--r-- | OpenAL32/alEffect.c | 39 | ||||
-rw-r--r-- | OpenAL32/alExtension.c | 18 | ||||
-rw-r--r-- | alsoftrc.sample | 4 |
10 files changed, 919 insertions, 15 deletions
@@ -516,9 +516,7 @@ static const ALCenums enumeration[] = { DECL(AL_EFFECT_REVERB), DECL(AL_EFFECT_EAXREVERB), DECL(AL_EFFECT_CHORUS), -#if 0 DECL(AL_EFFECT_DISTORTION), -#endif DECL(AL_EFFECT_ECHO), DECL(AL_EFFECT_FLANGER), #if 0 @@ -530,8 +528,8 @@ static const ALCenums enumeration[] = { #if 0 DECL(AL_EFFECT_AUTOWAH), DECL(AL_EFFECT_COMPRESSOR), - DECL(AL_EFFECT_EQUALIZER), #endif + DECL(AL_EFFECT_EQUALIZER), DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT), DECL(AL_EFFECT_DEDICATED_DIALOGUE), @@ -593,6 +591,23 @@ static const ALCenums enumeration[] = { DECL(AL_FLANGER_FEEDBACK), DECL(AL_FLANGER_DELAY), + DECL(AL_EQUALIZER_LOW_GAIN), + DECL(AL_EQUALIZER_LOW_CUTOFF), + DECL(AL_EQUALIZER_MID1_GAIN), + DECL(AL_EQUALIZER_MID1_CENTER), + DECL(AL_EQUALIZER_MID1_WIDTH), + DECL(AL_EQUALIZER_MID2_GAIN), + DECL(AL_EQUALIZER_MID2_CENTER), + DECL(AL_EQUALIZER_MID2_WIDTH), + DECL(AL_EQUALIZER_HIGH_GAIN), + DECL(AL_EQUALIZER_HIGH_CUTOFF), + + DECL(AL_DISTORTION_EDGE), + DECL(AL_DISTORTION_GAIN), + DECL(AL_DISTORTION_LOWPASS_CUTOFF), + DECL(AL_DISTORTION_EQCENTER), + DECL(AL_DISTORTION_EQBANDWIDTH), + DECL(AL_RING_MODULATOR_FREQUENCY), DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF), DECL(AL_RING_MODULATOR_WAVEFORM), diff --git a/Alc/alcDistortion.c b/Alc/alcDistortion.c new file mode 100644 index 00000000..717c7b3a --- /dev/null +++ b/Alc/alcDistortion.c @@ -0,0 +1,356 @@ +/** + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include <math.h> +#include <stdlib.h> + +#include "alMain.h" +#include "alFilter.h" +#include "alAuxEffectSlot.h" +#include "alError.h" +#include "alu.h" + +/* Filters 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 */ + +typedef enum ALEQFilterType { + LOWPASS, + BANDPASS, +} ALEQFilterType; + +typedef struct ALEQFilter { + ALEQFilterType type; + ALfloat x[2]; /* History of two last input samples */ + ALfloat y[2]; /* History of two last output samples */ + ALfloat a[3]; /* Transfer function coefficients "a" */ + ALfloat b[3]; /* Transfer function coefficients "b" */ +} ALEQFilter; + +typedef struct ALdistortionState { + /* Must be first in all effects! */ + ALeffectState state; + + /* Effect gains for each channel */ + ALfloat Gain[MaxChannels]; + + /* Effect parameters */ + ALEQFilter bandpass; + ALEQFilter lowpass; + ALfloat frequency; + ALfloat attenuation; + ALfloat edge; + + /* Oversample data */ + ALfloat oversample_buffer[BUFFERSIZE][4]; +} ALdistortionState; + +static ALvoid DistortionDestroy(ALeffectState *effect) +{ + ALdistortionState *state = (ALdistortionState*)effect; + + free(state); +} + +static ALboolean DistortionDeviceUpdate(ALeffectState *effect, ALCdevice *Device) +{ + ALdistortionState *state = (ALdistortionState*)effect; + + state->frequency = (ALfloat)Device->Frequency; + + return AL_TRUE; +} + +static ALvoid DistortionUpdate(ALeffectState *effect, ALCdevice *Device, const ALeffectslot *Slot) +{ + ALdistortionState *state = (ALdistortionState*)effect; + ALfloat gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain; + ALuint it; + ALfloat w0; + ALfloat alpha; + ALfloat bandwidth; + ALfloat cutoff; + + for(it = 0; it < Device->NumChan; it++) + { + enum Channel chan = Device->Speaker2Chan[it]; + state->Gain[chan] = gain; + } + + /* Store distorted signal attenuation settings */ + state->attenuation = Slot->effect.Distortion.Gain; + + /* Store waveshaper edge settings */ + state->edge = Slot->effect.Distortion.Edge; + + /* Lowpass filter */ + cutoff = Slot->effect.Distortion.LowpassCutoff; + /* Bandwidth value is constant in octaves */ + bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f); + w0 = 2.0f * F_PI * cutoff / (state->frequency * 4.0f); + alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0)); + state->lowpass.b[0] = (1.0f - cosf(w0)) / 2.0f; + state->lowpass.b[1] = 1.0f - cosf(w0); + state->lowpass.b[2] = (1.0f - cosf(w0)) / 2.0f; + state->lowpass.a[0] = 1.0f + alpha; + state->lowpass.a[1] = -2.0f * cosf(w0); + state->lowpass.a[2] = 1.0f - alpha; + + /* Bandpass filter */ + cutoff = Slot->effect.Distortion.EQCenter; + /* Convert bandwidth in Hz to octaves */ + bandwidth = Slot->effect.Distortion.EQBandwidth / (cutoff * 0.67f); + w0 = 2.0f * F_PI * cutoff / (state->frequency * 4.0f); + alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0)); + state->bandpass.b[0] = alpha; + state->bandpass.b[1] = 0; + state->bandpass.b[2] = -alpha; + state->bandpass.a[0] = 1.0f + alpha; + state->bandpass.a[1] = -2.0f * cosf(w0); + state->bandpass.a[2] = 1.0f - alpha; +} + +static ALvoid DistortionProcess(ALeffectState *effect, ALuint SamplesToDo, const ALfloat *RESTRICT SamplesIn, ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE]) +{ + ALdistortionState *state = (ALdistortionState*)effect; + float *RESTRICT oversample_buffer = &state->oversample_buffer[0][0]; + ALfloat tempsmp; + ALuint it; + ALuint kt; + ALuint st; + + /* 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 < SamplesToDo; it++) + { + oversample_buffer[it*4 + 0] = SamplesIn[it]; + oversample_buffer[it*4 + 1] = 0.0f; + oversample_buffer[it*4 + 2] = 0.0f; + oversample_buffer[it*4 + 3] = 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. */ + for(it = 0; it < SamplesToDo * 4; it++) + { + tempsmp = state->lowpass.b[0] / state->lowpass.a[0] * oversample_buffer[it] + + state->lowpass.b[1] / state->lowpass.a[0] * state->lowpass.x[0] + + state->lowpass.b[2] / state->lowpass.a[0] * state->lowpass.x[1] - + state->lowpass.a[1] / state->lowpass.a[0] * state->lowpass.y[0] - + state->lowpass.a[2] / state->lowpass.a[0] * state->lowpass.y[1]; + + state->lowpass.x[1] = state->lowpass.x[0]; + state->lowpass.x[0] = oversample_buffer[it]; + state->lowpass.y[1] = state->lowpass.y[0]; + state->lowpass.y[0] = tempsmp; + /* Restore signal power by multiplying sample by amount of oversampling */ + oversample_buffer[it] = tempsmp * 4.0f; + } + + for(it = 0; it < SamplesToDo * 4; it++) + { + ALfloat smp = oversample_buffer[it]; + ALfloat edge = sinf(state->edge * (F_PI / 2.0f)); + + /* 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(st = 0; st < 3; st++) + { + smp = (1.0f + 2.0f * edge / (1.0f - edge)) * smp / (1.0f + 2.0f * edge / (1.0f - edge) * fabsf(smp)); + if((st & 0x00000001) == 0x00000001) + smp *= -1.0f; + } + + /* Third step, do bandpass filtering of distorted signal */ + tempsmp = state->bandpass.b[0] / state->bandpass.a[0] * smp + + state->bandpass.b[1] / state->bandpass.a[0] * state->bandpass.x[0] + + state->bandpass.b[2] / state->bandpass.a[0] * state->bandpass.x[1] - + state->bandpass.a[1] / state->bandpass.a[0] * state->bandpass.y[0] - + state->bandpass.a[2] / state->bandpass.a[0] * state->bandpass.y[1]; + + state->bandpass.x[1] = state->bandpass.x[0]; + state->bandpass.x[0] = smp; + state->bandpass.y[1] = state->bandpass.y[0]; + state->bandpass.y[0] = tempsmp; + smp = tempsmp; + + /* Fourth step, final, do attenuation and perform decimation, */ + /* store only one sample out of 4. */ + if(!(it & 0x00000003)) + { + smp *= state->attenuation; + for(kt = 0; kt < MaxChannels; kt++) + SamplesOut[kt][it>>2] += state->Gain[kt] * smp; + } + } +} + +ALeffectState *DistortionCreate(void) +{ + ALdistortionState *state; + + state = malloc(sizeof(*state)); + if(!state) + return NULL; + + state->state.Destroy = DistortionDestroy; + state->state.DeviceUpdate = DistortionDeviceUpdate; + state->state.Update = DistortionUpdate; + state->state.Process = DistortionProcess; + + state->bandpass.type = BANDPASS; + state->lowpass.type = LOWPASS; + + /* Initialize sample history only on filter creation to avoid */ + /* sound clicks if filter settings were changed in runtime. */ + state->bandpass.x[0] = 0.0f; + state->bandpass.x[1] = 0.0f; + state->lowpass.y[0] = 0.0f; + state->lowpass.y[1] = 0.0f; + + return &state->state; +} + +void distortion_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) +{ + effect=effect; + val=val; + + switch(param) + { + default: + alSetError(context, AL_INVALID_ENUM); + break; + } +} +void distortion_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + distortion_SetParami(effect, context, param, vals[0]); +} +void distortion_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + switch(param) + { + case AL_DISTORTION_EDGE: + if(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE) + effect->Distortion.Edge = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_DISTORTION_GAIN: + if(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN) + effect->Distortion.Gain = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_DISTORTION_LOWPASS_CUTOFF: + if(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF) + effect->Distortion.LowpassCutoff = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_DISTORTION_EQCENTER: + if(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER) + effect->Distortion.EQCenter = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_DISTORTION_EQBANDWIDTH: + if(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH) + effect->Distortion.EQBandwidth = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + default: + alSetError(context, AL_INVALID_ENUM); + break; + } +} +void distortion_SetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + distortion_SetParamf(effect, context, param, vals[0]); +} + +void distortion_GetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) +{ + effect=effect; + val=val; + + switch(param) + { + default: + alSetError(context, AL_INVALID_ENUM); + break; + } +} +void distortion_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + distortion_GetParami(effect, context, param, vals); +} +void distortion_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + switch(param) + { + case AL_DISTORTION_EDGE: + *val = effect->Distortion.Edge; + break; + + case AL_DISTORTION_GAIN: + *val = effect->Distortion.Gain; + break; + + case AL_DISTORTION_LOWPASS_CUTOFF: + *val = effect->Distortion.LowpassCutoff; + break; + + case AL_DISTORTION_EQCENTER: + *val = effect->Distortion.EQCenter; + break; + + case AL_DISTORTION_EQBANDWIDTH: + *val = effect->Distortion.EQBandwidth; + break; + + default: + alSetError(context, AL_INVALID_ENUM); + break; + } +} +void distortion_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + distortion_GetParamf(effect, context, param, vals); +} diff --git a/Alc/alcEqualizer.c b/Alc/alcEqualizer.c new file mode 100644 index 00000000..2067c319 --- /dev/null +++ b/Alc/alcEqualizer.c @@ -0,0 +1,446 @@ +/** + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include <math.h> +#include <stdlib.h> + +#include "alMain.h" +#include "alFilter.h" +#include "alAuxEffectSlot.h" +#include "alError.h" +#include "alu.h" + +/* 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 */ + +typedef enum ALEQFilterType { + LOW_SHELF, + HIGH_SHELF, + PEAKING +} ALEQFilterType; + +typedef struct ALEQFilter { + ALEQFilterType type; + ALfloat x[2]; /* History of two last input samples */ + ALfloat y[2]; /* History of two last output samples */ + ALfloat a[3]; /* Transfer function coefficients "a" */ + ALfloat b[3]; /* Transfer function coefficients "b" */ +} ALEQFilter; + +typedef struct ALequalizerState { + /* Must be first in all effects! */ + ALeffectState state; + + /* Effect gains for each channel */ + ALfloat Gain[MaxChannels]; + + /* Effect parameters */ + ALEQFilter bandfilter[4]; + ALfloat frequency; +} ALequalizerState; + +static ALvoid EqualizerDestroy(ALeffectState *effect) +{ + ALequalizerState *state = (ALequalizerState*)effect; + + free(state); +} + +static ALboolean EqualizerDeviceUpdate(ALeffectState *effect, ALCdevice *Device) +{ + ALequalizerState *state = (ALequalizerState*)effect; + + state->frequency = (ALfloat)Device->Frequency; + + return AL_TRUE; +} + +static ALvoid EqualizerUpdate(ALeffectState *effect, ALCdevice *Device, const ALeffectslot *Slot) +{ + ALequalizerState *state = (ALequalizerState*)effect; + ALfloat gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain; + ALuint it; + + for(it = 0; it < Device->NumChan; it++) + { + enum Channel chan = Device->Speaker2Chan[it]; + state->Gain[chan] = gain; + } + + /* Calculate coefficients for the each type of filter */ + for(it = 0; it < 4; it++) + { + ALfloat gain; + ALfloat filter_frequency; + ALfloat bandwidth = 0.0f; + ALfloat w0; + ALfloat alpha = 0.0f; + + /* convert linear gains to filter gains */ + switch (it) + { + case 0: /* Low Shelf */ + gain = powf(10.0f, (20.0f * log10f(Slot->effect.Equalizer.LowGain)) / 40.0f); + filter_frequency = Slot->effect.Equalizer.LowCutoff; + break; + case 1: /* Peaking */ + gain = powf(10.0f, (20.0f * log10f(Slot->effect.Equalizer.Mid1Gain)) / 40.0f); + filter_frequency = Slot->effect.Equalizer.Mid1Center; + bandwidth = Slot->effect.Equalizer.Mid1Width; + break; + case 2: /* Peaking */ + gain = powf(10.0f, (20.0f * log10f(Slot->effect.Equalizer.Mid2Gain)) / 40.0f); + filter_frequency = Slot->effect.Equalizer.Mid2Center; + bandwidth = Slot->effect.Equalizer.Mid2Width; + break; + case 3: /* High Shelf */ + gain = powf(10.0f, (20.0f * log10f(Slot->effect.Equalizer.HighGain)) / 40.0f); + filter_frequency = Slot->effect.Equalizer.HighCutoff; + break; + } + + w0 = 2.0f * F_PI * filter_frequency / state->frequency; + + /* Calculate filter coefficients depending on filter type */ + switch(state->bandfilter[it].type) + { + case LOW_SHELF: + alpha = sinf(w0) / 2.0f * + sqrtf((gain + 1.0f / gain) * (1.0f / 0.75f - 1.0f) + 2.0f); + state->bandfilter[it].b[0] = gain * ((gain + 1.0f) - + (gain - 1.0f) * cosf(w0) + 2.0f * sqrtf(gain) * alpha); + state->bandfilter[it].b[1] = 2.0f * gain * ((gain - 1.0f) - + (gain + 1.0f) * cosf(w0)); + state->bandfilter[it].b[2] = gain * ((gain + 1.0f) - + (gain - 1.0f) * cosf(w0) - 2.0f * sqrtf(gain) * alpha); + state->bandfilter[it].a[0] = (gain + 1.0f) + (gain - 1.0f) * + cosf(w0) + 2.0f * sqrtf(gain) * alpha; + state->bandfilter[it].a[1] = -2.0f * ((gain - 1.0f) + + (gain + 1.0f) * cosf(w0)); + state->bandfilter[it].a[2] = (gain + 1.0f) + (gain - 1.0f) * + cosf(w0) - 2.0f * sqrtf(gain) * alpha; + break; + case HIGH_SHELF: + alpha = sinf(w0) / 2.0f * sqrtf((gain + 1.0f / gain) * + (1.0f / 0.75f - 1.0f) + 2.0f); + state->bandfilter[it].b[0] = gain * ((gain + 1.0f) + + (gain - 1.0f) * cosf(w0) + + 2.0f * sqrtf(gain) * alpha); + state->bandfilter[it].b[1] = -2.0f * gain * ((gain - 1.0f) + + (gain + 1.0f) * + cosf(w0)); + state->bandfilter[it].b[2] = gain * ((gain + 1.0f) + + (gain - 1.0f) * cosf(w0) - + 2.0f * sqrtf(gain) * alpha); + state->bandfilter[it].a[0] = (gain + 1.0f) - + (gain - 1.0f) * cosf(w0) + + 2.0f * sqrtf(gain) * alpha; + state->bandfilter[it].a[1] = 2.0f * ((gain - 1.0f) - + (gain + 1.0f) * cosf(w0)); + state->bandfilter[it].a[2] = (gain + 1.0f) - + (gain - 1.0f) * cosf(w0) - + 2.0f * sqrtf(gain) * alpha; + break; + case PEAKING: + alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0)); + state->bandfilter[it].b[0] = 1.0f + alpha * gain; + state->bandfilter[it].b[1] = -2.0f * cosf(w0); + state->bandfilter[it].b[2] = 1.0f - alpha * gain; + state->bandfilter[it].a[0] = 1.0f + alpha / gain; + state->bandfilter[it].a[1] = -2.0f * cosf(w0); + state->bandfilter[it].a[2] = 1.0f - alpha / gain; + break; + } + } +} + +static ALvoid EqualizerProcess(ALeffectState *effect, ALuint SamplesToDo, const ALfloat *RESTRICT SamplesIn, ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE]) +{ + ALequalizerState *state = (ALequalizerState*)effect; + ALuint it; + ALuint kt; + ALuint ft; + + for (it = 0; it < SamplesToDo; it++) + { + ALfloat tempsmp; + ALfloat smp = SamplesIn[it]; + + for(ft = 0;ft < 4;ft++) + { + tempsmp = state->bandfilter[ft].b[0] / state->bandfilter[ft].a[0] * smp + + state->bandfilter[ft].b[1] / state->bandfilter[ft].a[0] * state->bandfilter[ft].x[0] + + state->bandfilter[ft].b[2] / state->bandfilter[ft].a[0] * state->bandfilter[ft].x[1] - + state->bandfilter[ft].a[1] / state->bandfilter[ft].a[0] * state->bandfilter[ft].y[0] - + state->bandfilter[ft].a[2] / state->bandfilter[ft].a[0] * state->bandfilter[ft].y[1]; + + state->bandfilter[ft].x[1] = state->bandfilter[ft].x[0]; + state->bandfilter[ft].x[0] = smp; + state->bandfilter[ft].y[1] = state->bandfilter[ft].y[0]; + state->bandfilter[ft].y[0] = tempsmp; + smp = tempsmp; + } + + for(kt = 0;kt < MaxChannels;kt++) + SamplesOut[kt][it] += state->Gain[kt] * smp; + } +} + +ALeffectState *EqualizerCreate(void) +{ + ALequalizerState *state; + int it; + + state = malloc(sizeof(*state)); + if(!state) + return NULL; + + state->state.Destroy = EqualizerDestroy; + state->state.DeviceUpdate = EqualizerDeviceUpdate; + state->state.Update = EqualizerUpdate; + state->state.Process = EqualizerProcess; + + state->bandfilter[0].type = LOW_SHELF; + state->bandfilter[1].type = PEAKING; + state->bandfilter[2].type = PEAKING; + state->bandfilter[3].type = HIGH_SHELF; + + /* Initialize sample history only on filter creation to avoid */ + /* sound clicks if filter settings were changed in runtime. */ + for(it = 0; it < 4; it++) + { + state->bandfilter[it].x[0] = 0.0f; + state->bandfilter[it].x[1] = 0.0f; + state->bandfilter[it].y[0] = 0.0f; + state->bandfilter[it].y[1] = 0.0f; + } + + return &state->state; +} + +void equalizer_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) +{ + effect=effect; + val=val; + + switch(param) + { + default: + alSetError(context, AL_INVALID_ENUM); + break; + } +} +void equalizer_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + equalizer_SetParami(effect, context, param, vals[0]); +} +void equalizer_SetParamf(ALeffect *effect, 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) + effect->Equalizer.LowGain = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_EQUALIZER_LOW_CUTOFF: + if(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF) + effect->Equalizer.LowCutoff = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_EQUALIZER_MID1_GAIN: + if(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN) + effect->Equalizer.Mid1Gain = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_EQUALIZER_MID1_CENTER: + if(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER) + effect->Equalizer.Mid1Center = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_EQUALIZER_MID1_WIDTH: + if(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH) + effect->Equalizer.Mid1Width = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_EQUALIZER_MID2_GAIN: + if(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN) + effect->Equalizer.Mid2Gain = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_EQUALIZER_MID2_CENTER: + if(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER) + effect->Equalizer.Mid2Center = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_EQUALIZER_MID2_WIDTH: + if(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH) + effect->Equalizer.Mid2Width = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_EQUALIZER_HIGH_GAIN: + if(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN) + effect->Equalizer.HighGain = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + case AL_EQUALIZER_HIGH_CUTOFF: + if(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF) + effect->Equalizer.HighCutoff = val; + else + alSetError(context, AL_INVALID_VALUE); + break; + + default: + alSetError(context, AL_INVALID_ENUM); + break; + } +} +void equalizer_SetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + equalizer_SetParamf(effect, context, param, vals[0]); +} + +void equalizer_GetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) +{ + effect=effect; + val=val; + + switch(param) + { + default: + alSetError(context, AL_INVALID_ENUM); + break; + } +} +void equalizer_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + equalizer_GetParami(effect, context, param, vals); +} +void equalizer_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + switch(param) + { + case AL_EQUALIZER_LOW_GAIN: + *val = effect->Equalizer.LowGain; + break; + + case AL_EQUALIZER_LOW_CUTOFF: + *val = effect->Equalizer.LowCutoff; + break; + + case AL_EQUALIZER_MID1_GAIN: + *val = effect->Equalizer.Mid1Gain; + break; + + case AL_EQUALIZER_MID1_CENTER: + *val = effect->Equalizer.Mid1Center; + break; + + case AL_EQUALIZER_MID1_WIDTH: + *val = effect->Equalizer.Mid1Width; + break; + + case AL_EQUALIZER_MID2_GAIN: + *val = effect->Equalizer.Mid2Gain; + break; + + case AL_EQUALIZER_MID2_CENTER: + *val = effect->Equalizer.Mid2Center; + break; + + case AL_EQUALIZER_MID2_WIDTH: + *val = effect->Equalizer.Mid2Width; + break; + + case AL_EQUALIZER_HIGH_GAIN: + *val = effect->Equalizer.HighGain; + break; + + case AL_EQUALIZER_HIGH_CUTOFF: + *val = effect->Equalizer.HighCutoff; + break; + + default: + alSetError(context, AL_INVALID_ENUM); + break; + } +} +void equalizer_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + equalizer_GetParamf(effect, context, param, vals); +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 81c0980b..4165cbe1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -456,7 +456,9 @@ SET(ALC_OBJS Alc/ALc.c Alc/alcChorus.c Alc/alcConfig.c Alc/alcDedicated.c + Alc/alcDistortion.c Alc/alcEcho.c + Alc/alcEqualizer.c Alc/alcFlanger.c Alc/alcModulator.c Alc/alcReverb.c diff --git a/OpenAL32/Include/alAuxEffectSlot.h b/OpenAL32/Include/alAuxEffectSlot.h index e39a3d91..c19eb7b3 100644 --- a/OpenAL32/Include/alAuxEffectSlot.h +++ b/OpenAL32/Include/alAuxEffectSlot.h @@ -48,6 +48,8 @@ ALeffectState *ModulatorCreate(void); ALeffectState *DedicatedCreate(void); ALeffectState *ChorusCreate(void); ALeffectState *FlangerCreate(void); +ALeffectState *EqualizerCreate(void); +ALeffectState *DistortionCreate(void); #define ALeffectState_Destroy(a) ((a)->Destroy((a))) #define ALeffectState_DeviceUpdate(a,b) ((a)->DeviceUpdate((a),(b))) diff --git a/OpenAL32/Include/alEffect.h b/OpenAL32/Include/alEffect.h index 87dc717d..a3d40b82 100644 --- a/OpenAL32/Include/alEffect.h +++ b/OpenAL32/Include/alEffect.h @@ -15,6 +15,8 @@ enum { DEDICATED, CHORUS, FLANGER, + EQUALIZER, + DISTORTION, MAX_EFFECTS }; @@ -95,6 +97,28 @@ typedef struct ALeffect ALfloat Delay; } Flanger; + struct { + ALfloat Delay; + ALfloat LowCutoff; + ALfloat LowGain; + ALfloat Mid1Center; + ALfloat Mid1Gain; + ALfloat Mid1Width; + ALfloat Mid2Center; + ALfloat Mid2Gain; + ALfloat Mid2Width; + ALfloat HighCutoff; + ALfloat HighGain; + } Equalizer; + + struct { + ALfloat Edge; + ALfloat Gain; + ALfloat LowpassCutoff; + ALfloat EQCenter; + ALfloat EQBandwidth; + } Distortion; + void (*SetParami)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint val); void (*SetParamiv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals); void (*SetParamf)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val); @@ -149,6 +173,15 @@ void chorus_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALin void chorus_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val); void chorus_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals); +void distortion_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val); +void distortion_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals); +void distortion_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val); +void distortion_SetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals); +void distortion_GetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val); +void distortion_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals); +void distortion_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val); +void distortion_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals); + void echo_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val); void echo_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals); void echo_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val); @@ -158,6 +191,15 @@ void echo_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint void echo_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val); void echo_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals); +void equalizer_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val); +void equalizer_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals); +void equalizer_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val); +void equalizer_SetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals); +void equalizer_GetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val); +void equalizer_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals); +void equalizer_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val); +void equalizer_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals); + void flanger_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val); void flanger_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals); void flanger_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val); diff --git a/OpenAL32/alAuxEffectSlot.c b/OpenAL32/alAuxEffectSlot.c index dbaccaa2..7df6bca4 100644 --- a/OpenAL32/alAuxEffectSlot.c +++ b/OpenAL32/alAuxEffectSlot.c @@ -513,8 +513,12 @@ static ALeffectState *CreateStateByType(ALenum type) return ReverbCreate(); case AL_EFFECT_CHORUS: return ChorusCreate(); + case AL_EFFECT_DISTORTION: + return DistortionCreate(); case AL_EFFECT_ECHO: return EchoCreate(); + case AL_EFFECT_EQUALIZER: + return EqualizerCreate(); case AL_EFFECT_FLANGER: return FlangerCreate(); case AL_EFFECT_RING_MODULATOR: diff --git a/OpenAL32/alEffect.c b/OpenAL32/alEffect.c index 26d5398d..23176beb 100644 --- a/OpenAL32/alEffect.c +++ b/OpenAL32/alEffect.c @@ -430,7 +430,7 @@ static void InitEffectParams(ALeffect *effect, ALenum type) case AL_EFFECT_CHORUS: effect->Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM; effect->Chorus.Phase = AL_CHORUS_DEFAULT_PHASE; - effect->Chorus.Rate = AL_CHORUS_MAX_RATE; + effect->Chorus.Rate = AL_CHORUS_DEFAULT_RATE; effect->Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH; effect->Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK; effect->Chorus.Delay = AL_CHORUS_DEFAULT_DELAY; @@ -443,6 +443,21 @@ static void InitEffectParams(ALeffect *effect, ALenum type) effect->GetParamf = chorus_GetParamf; effect->GetParamfv = chorus_GetParamfv; break; + case AL_EFFECT_DISTORTION: + effect->Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE; + effect->Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN; + effect->Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF; + effect->Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER; + effect->Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH; + effect->SetParami = distortion_SetParami; + effect->SetParamiv = distortion_SetParamiv; + effect->SetParamf = distortion_SetParamf; + effect->SetParamfv = distortion_SetParamfv; + effect->GetParami = distortion_GetParami; + effect->GetParamiv = distortion_GetParamiv; + effect->GetParamf = distortion_GetParamf; + effect->GetParamfv = distortion_GetParamfv; + break; case AL_EFFECT_ECHO: effect->Echo.Delay = AL_ECHO_DEFAULT_DELAY; effect->Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY; @@ -458,10 +473,30 @@ static void InitEffectParams(ALeffect *effect, ALenum type) effect->GetParamf = echo_GetParamf; effect->GetParamfv = echo_GetParamfv; break; + case AL_EFFECT_EQUALIZER: + effect->Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF; + effect->Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN; + effect->Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER; + effect->Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN; + effect->Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH; + effect->Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER; + effect->Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN; + effect->Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH; + effect->Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF; + effect->Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN; + effect->SetParami = equalizer_SetParami; + effect->SetParamiv = equalizer_SetParamiv; + effect->SetParamf = equalizer_SetParamf; + effect->SetParamfv = equalizer_SetParamfv; + effect->GetParami = equalizer_GetParami; + effect->GetParamiv = equalizer_GetParamiv; + effect->GetParamf = equalizer_GetParamf; + effect->GetParamfv = equalizer_GetParamfv; + break; case AL_EFFECT_FLANGER: effect->Flanger.Waveform = AL_FLANGER_DEFAULT_WAVEFORM; effect->Flanger.Phase = AL_FLANGER_DEFAULT_PHASE; - effect->Flanger.Rate = AL_FLANGER_MAX_RATE; + effect->Flanger.Rate = AL_FLANGER_DEFAULT_RATE; effect->Flanger.Depth = AL_FLANGER_DEFAULT_DEPTH; effect->Flanger.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; effect->Flanger.Delay = AL_FLANGER_DEFAULT_DELAY; diff --git a/OpenAL32/alExtension.c b/OpenAL32/alExtension.c index f22c89e5..0261ba7a 100644 --- a/OpenAL32/alExtension.c +++ b/OpenAL32/alExtension.c @@ -36,14 +36,16 @@ const struct EffectList EffectList[] = { - { "eaxreverb", EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB }, - { "reverb", REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB }, - { "echo", ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO }, - { "modulator", MODULATOR, "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR }, - { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT }, - { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE }, - { "chorus", CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS }, - { "flanger", FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER }, + { "eaxreverb", EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB }, + { "reverb", REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB }, + { "echo", ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO }, + { "modulator", MODULATOR, "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR }, + { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT }, + { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE }, + { "chorus", CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS }, + { "flanger", FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER }, + { "equalizer", EQUALIZER, "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER }, + { "distortion", DISTORTION, "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION }, { NULL, 0, NULL, (ALenum)0 } }; diff --git a/alsoftrc.sample b/alsoftrc.sample index 4f2df919..c536493a 100644 --- a/alsoftrc.sample +++ b/alsoftrc.sample @@ -126,8 +126,8 @@ ## excludefx: # Sets which effects to exclude, preventing apps from using them. This can # help for apps that try to use effects which are too CPU intensive for the -# system to handle. Available effects are: eaxreverb,reverb,chorus,echo, -# flanger,modulator,dedicated +# system to handle. Available effects are: eaxreverb,reverb,chorus,distortion, +# echo,equalizer,flanger,modulator,dedicated #excludefx = ## slots: |