Rename BiquadState to BiquadFilter
[openal-soft.git] / Alc / effects / distortion.c
blobf4e9969c032fb3a539d1244f50d76e483b16b756
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2013 by Mike Gorchak
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <math.h>
24 #include <stdlib.h>
26 #include "alMain.h"
27 #include "alAuxEffectSlot.h"
28 #include "alError.h"
29 #include "alu.h"
30 #include "filters/defs.h"
33 typedef struct ALdistortionState {
34 DERIVE_FROM_TYPE(ALeffectState);
36 /* Effect gains for each channel */
37 ALfloat Gain[MAX_OUTPUT_CHANNELS];
39 /* Effect parameters */
40 BiquadFilter lowpass;
41 BiquadFilter bandpass;
42 ALfloat attenuation;
43 ALfloat edge_coeff;
45 ALfloat Buffer[2][BUFFERSIZE];
46 } ALdistortionState;
48 static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
49 static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
50 static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
51 static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
52 DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
54 DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
57 static void ALdistortionState_Construct(ALdistortionState *state)
59 ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
60 SET_VTABLE2(ALdistortionState, ALeffectState, state);
63 static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
65 ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
68 static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *UNUSED(device))
70 BiquadFilter_clear(&state->lowpass);
71 BiquadFilter_clear(&state->bandpass);
72 return AL_TRUE;
75 static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
77 const ALCdevice *device = context->Device;
78 ALfloat frequency = (ALfloat)device->Frequency;
79 ALfloat coeffs[MAX_AMBI_COEFFS];
80 ALfloat bandwidth;
81 ALfloat cutoff;
82 ALfloat edge;
84 /* Store waveshaper edge settings. */
85 edge = sinf(props->Distortion.Edge * (F_PI_2));
86 edge = minf(edge, 0.99f);
87 state->edge_coeff = 2.0f * edge / (1.0f-edge);
89 cutoff = props->Distortion.LowpassCutoff;
90 /* Bandwidth value is constant in octaves. */
91 bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
92 /* Multiply sampling frequency by the amount of oversampling done during
93 * processing.
95 BiquadFilter_setParams(&state->lowpass, BiquadType_LowPass, 1.0f,
96 cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
99 cutoff = props->Distortion.EQCenter;
100 /* Convert bandwidth in Hz to octaves. */
101 bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
102 BiquadFilter_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
103 cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
106 CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
107 ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain * props->Distortion.Gain,
108 state->Gain);
111 static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
113 ALfloat (*restrict buffer)[BUFFERSIZE] = state->Buffer;
114 const ALfloat fc = state->edge_coeff;
115 ALsizei base;
116 ALsizei i, k;
118 for(base = 0;base < SamplesToDo;)
120 /* Perform 4x oversampling to avoid aliasing. Oversampling greatly
121 * improves distortion quality and allows to implement lowpass and
122 * bandpass filters using high frequencies, at which classic IIR
123 * filters became unstable.
125 ALsizei todo = mini(BUFFERSIZE, (SamplesToDo-base) * 4);
127 /* Fill oversample buffer using zero stuffing. Multiply the sample by
128 * the amount of oversampling to maintain the signal's power.
130 for(i = 0;i < todo;i++)
131 buffer[0][i] = !(i&3) ? SamplesIn[0][(i>>2)+base] * 4.0f : 0.0f;
133 /* First step, do lowpass filtering of original signal. Additionally
134 * perform buffer interpolation and lowpass cutoff for oversampling
135 * (which is fortunately first step of distortion). So combine three
136 * operations into the one.
138 BiquadFilter_process(&state->lowpass, buffer[1], buffer[0], todo);
140 /* Second step, do distortion using waveshaper function to emulate
141 * signal processing during tube overdriving. Three steps of
142 * waveshaping are intended to modify waveform without boost/clipping/
143 * attenuation process.
145 for(i = 0;i < todo;i++)
147 ALfloat smp = buffer[1][i];
149 smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
150 smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
151 smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
153 buffer[0][i] = smp;
156 /* Third step, do bandpass filtering of distorted signal. */
157 BiquadFilter_process(&state->bandpass, buffer[1], buffer[0], todo);
159 todo >>= 2;
160 for(k = 0;k < NumChannels;k++)
162 /* Fourth step, final, do attenuation and perform decimation,
163 * storing only one sample out of four.
165 ALfloat gain = state->Gain[k];
166 if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
167 continue;
169 for(i = 0;i < todo;i++)
170 SamplesOut[k][base+i] += gain * buffer[1][i*4];
173 base += todo;
178 typedef struct DistortionStateFactory {
179 DERIVE_FROM_TYPE(EffectStateFactory);
180 } DistortionStateFactory;
182 static ALeffectState *DistortionStateFactory_create(DistortionStateFactory *UNUSED(factory))
184 ALdistortionState *state;
186 NEW_OBJ0(state, ALdistortionState)();
187 if(!state) return NULL;
189 return STATIC_CAST(ALeffectState, state);
192 DEFINE_EFFECTSTATEFACTORY_VTABLE(DistortionStateFactory);
195 EffectStateFactory *DistortionStateFactory_getFactory(void)
197 static DistortionStateFactory DistortionFactory = { { GET_VTABLE2(DistortionStateFactory, EffectStateFactory) } };
199 return STATIC_CAST(EffectStateFactory, &DistortionFactory);
203 void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
204 { alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
205 void ALdistortion_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
206 { alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
207 void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
209 ALeffectProps *props = &effect->Props;
210 switch(param)
212 case AL_DISTORTION_EDGE:
213 if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
214 SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion edge out of range");
215 props->Distortion.Edge = val;
216 break;
218 case AL_DISTORTION_GAIN:
219 if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
220 SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion gain out of range");
221 props->Distortion.Gain = val;
222 break;
224 case AL_DISTORTION_LOWPASS_CUTOFF:
225 if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
226 SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion low-pass cutoff out of range");
227 props->Distortion.LowpassCutoff = val;
228 break;
230 case AL_DISTORTION_EQCENTER:
231 if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
232 SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ center out of range");
233 props->Distortion.EQCenter = val;
234 break;
236 case AL_DISTORTION_EQBANDWIDTH:
237 if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
238 SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range");
239 props->Distortion.EQBandwidth = val;
240 break;
242 default:
243 alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
244 param);
247 void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
248 { ALdistortion_setParamf(effect, context, param, vals[0]); }
250 void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
251 { alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
252 void ALdistortion_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
253 { alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
254 void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
256 const ALeffectProps *props = &effect->Props;
257 switch(param)
259 case AL_DISTORTION_EDGE:
260 *val = props->Distortion.Edge;
261 break;
263 case AL_DISTORTION_GAIN:
264 *val = props->Distortion.Gain;
265 break;
267 case AL_DISTORTION_LOWPASS_CUTOFF:
268 *val = props->Distortion.LowpassCutoff;
269 break;
271 case AL_DISTORTION_EQCENTER:
272 *val = props->Distortion.EQCenter;
273 break;
275 case AL_DISTORTION_EQBANDWIDTH:
276 *val = props->Distortion.EQBandwidth;
277 break;
279 default:
280 alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
281 param);
284 void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
285 { ALdistortion_getParamf(effect, context, param, vals); }
287 DEFINE_ALEFFECT_VTABLE(ALdistortion);