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
27 #include "alAuxEffectSlot.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 */
45 ALfloat Buffer
[2][BUFFERSIZE
];
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
);
62 BiquadState_clear(&state
->lowpass
);
63 BiquadState_clear(&state
->bandpass
);
66 static ALvoid
ALdistortionState_Destruct(ALdistortionState
*state
)
68 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
71 static ALboolean
ALdistortionState_deviceUpdate(ALdistortionState
*UNUSED(state
), ALCdevice
*UNUSED(device
))
76 static ALvoid
ALdistortionState_update(ALdistortionState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
)
78 const ALCdevice
*device
= context
->Device
;
79 ALfloat frequency
= (ALfloat
)device
->Frequency
;
80 ALfloat coeffs
[MAX_AMBI_COEFFS
];
85 /* Store waveshaper edge settings. */
86 edge
= sinf(props
->Distortion
.Edge
* (F_PI_2
));
87 edge
= minf(edge
, 0.99f
);
88 state
->edge_coeff
= 2.0f
* edge
/ (1.0f
-edge
);
90 cutoff
= props
->Distortion
.LowpassCutoff
;
91 /* Bandwidth value is constant in octaves. */
92 bandwidth
= (cutoff
/ 2.0f
) / (cutoff
* 0.67f
);
93 /* Multiply sampling frequency by the amount of oversampling done during
96 BiquadState_setParams(&state
->lowpass
, BiquadType_LowPass
, 1.0f
,
97 cutoff
/ (frequency
*4.0f
), calc_rcpQ_from_bandwidth(cutoff
/ (frequency
*4.0f
), bandwidth
)
100 cutoff
= props
->Distortion
.EQCenter
;
101 /* Convert bandwidth in Hz to octaves. */
102 bandwidth
= props
->Distortion
.EQBandwidth
/ (cutoff
* 0.67f
);
103 BiquadState_setParams(&state
->bandpass
, BiquadType_BandPass
, 1.0f
,
104 cutoff
/ (frequency
*4.0f
), calc_rcpQ_from_bandwidth(cutoff
/ (frequency
*4.0f
), bandwidth
)
107 CalcAngleCoeffs(0.0f
, 0.0f
, 0.0f
, coeffs
);
108 ComputeDryPanGains(&device
->Dry
, coeffs
, slot
->Params
.Gain
* props
->Distortion
.Gain
,
112 static ALvoid
ALdistortionState_process(ALdistortionState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
114 ALfloat (*restrict buffer
)[BUFFERSIZE
] = state
->Buffer
;
115 const ALfloat fc
= state
->edge_coeff
;
119 for(base
= 0;base
< SamplesToDo
;)
121 /* Perform 4x oversampling to avoid aliasing. Oversampling greatly
122 * improves distortion quality and allows to implement lowpass and
123 * bandpass filters using high frequencies, at which classic IIR
124 * filters became unstable.
126 ALsizei todo
= mini(BUFFERSIZE
, (SamplesToDo
-base
) * 4);
128 /* Fill oversample buffer using zero stuffing. Multiply the sample by
129 * the amount of oversampling to maintain the signal's power.
131 for(i
= 0;i
< todo
;i
++)
132 buffer
[0][i
] = !(i
&3) ? SamplesIn
[0][(i
>>2)+base
] * 4.0f
: 0.0f
;
134 /* First step, do lowpass filtering of original signal. Additionally
135 * perform buffer interpolation and lowpass cutoff for oversampling
136 * (which is fortunately first step of distortion). So combine three
137 * operations into the one.
139 BiquadState_process(&state
->lowpass
, buffer
[1], buffer
[0], todo
);
141 /* Second step, do distortion using waveshaper function to emulate
142 * signal processing during tube overdriving. Three steps of
143 * waveshaping are intended to modify waveform without boost/clipping/
144 * attenuation process.
146 for(i
= 0;i
< todo
;i
++)
148 ALfloat smp
= buffer
[1][i
];
150 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*fabsf(smp
));
151 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*fabsf(smp
)) * -1.0f
;
152 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*fabsf(smp
));
157 /* Third step, do bandpass filtering of distorted signal. */
158 BiquadState_process(&state
->bandpass
, buffer
[1], buffer
[0], todo
);
161 for(k
= 0;k
< NumChannels
;k
++)
163 /* Fourth step, final, do attenuation and perform decimation,
164 * storing only one sample out of four.
166 ALfloat gain
= state
->Gain
[k
];
167 if(!(fabsf(gain
) > GAIN_SILENCE_THRESHOLD
))
170 for(i
= 0;i
< todo
;i
++)
171 SamplesOut
[k
][base
+i
] += gain
* buffer
[1][i
*4];
179 typedef struct DistortionStateFactory
{
180 DERIVE_FROM_TYPE(EffectStateFactory
);
181 } DistortionStateFactory
;
183 static ALeffectState
*DistortionStateFactory_create(DistortionStateFactory
*UNUSED(factory
))
185 ALdistortionState
*state
;
187 NEW_OBJ0(state
, ALdistortionState
)();
188 if(!state
) return NULL
;
190 return STATIC_CAST(ALeffectState
, state
);
193 DEFINE_EFFECTSTATEFACTORY_VTABLE(DistortionStateFactory
);
196 EffectStateFactory
*DistortionStateFactory_getFactory(void)
198 static DistortionStateFactory DistortionFactory
= { { GET_VTABLE2(DistortionStateFactory
, EffectStateFactory
) } };
200 return STATIC_CAST(EffectStateFactory
, &DistortionFactory
);
204 void ALdistortion_setParami(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
UNUSED(val
))
205 { alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion integer property 0x%04x", param
); }
206 void ALdistortion_setParamiv(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, const ALint
*UNUSED(vals
))
207 { alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion integer-vector property 0x%04x", param
); }
208 void ALdistortion_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
210 ALeffectProps
*props
= &effect
->Props
;
213 case AL_DISTORTION_EDGE
:
214 if(!(val
>= AL_DISTORTION_MIN_EDGE
&& val
<= AL_DISTORTION_MAX_EDGE
))
215 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Distortion edge out of range");
216 props
->Distortion
.Edge
= val
;
219 case AL_DISTORTION_GAIN
:
220 if(!(val
>= AL_DISTORTION_MIN_GAIN
&& val
<= AL_DISTORTION_MAX_GAIN
))
221 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Distortion gain out of range");
222 props
->Distortion
.Gain
= val
;
225 case AL_DISTORTION_LOWPASS_CUTOFF
:
226 if(!(val
>= AL_DISTORTION_MIN_LOWPASS_CUTOFF
&& val
<= AL_DISTORTION_MAX_LOWPASS_CUTOFF
))
227 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Distortion low-pass cutoff out of range");
228 props
->Distortion
.LowpassCutoff
= val
;
231 case AL_DISTORTION_EQCENTER
:
232 if(!(val
>= AL_DISTORTION_MIN_EQCENTER
&& val
<= AL_DISTORTION_MAX_EQCENTER
))
233 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Distortion EQ center out of range");
234 props
->Distortion
.EQCenter
= val
;
237 case AL_DISTORTION_EQBANDWIDTH
:
238 if(!(val
>= AL_DISTORTION_MIN_EQBANDWIDTH
&& val
<= AL_DISTORTION_MAX_EQBANDWIDTH
))
239 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Distortion EQ bandwidth out of range");
240 props
->Distortion
.EQBandwidth
= val
;
244 alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion float property 0x%04x",
248 void ALdistortion_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
249 { ALdistortion_setParamf(effect
, context
, param
, vals
[0]); }
251 void ALdistortion_getParami(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
*UNUSED(val
))
252 { alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion integer property 0x%04x", param
); }
253 void ALdistortion_getParamiv(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
*UNUSED(vals
))
254 { alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion integer-vector property 0x%04x", param
); }
255 void ALdistortion_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
257 const ALeffectProps
*props
= &effect
->Props
;
260 case AL_DISTORTION_EDGE
:
261 *val
= props
->Distortion
.Edge
;
264 case AL_DISTORTION_GAIN
:
265 *val
= props
->Distortion
.Gain
;
268 case AL_DISTORTION_LOWPASS_CUTOFF
:
269 *val
= props
->Distortion
.LowpassCutoff
;
272 case AL_DISTORTION_EQCENTER
:
273 *val
= props
->Distortion
.EQCenter
;
276 case AL_DISTORTION_EQBANDWIDTH
:
277 *val
= props
->Distortion
.EQBandwidth
;
281 alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion float property 0x%04x",
285 void ALdistortion_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
286 { ALdistortion_getParamf(effect
, context
, param
, vals
); }
288 DEFINE_ALEFFECT_VTABLE(ALdistortion
);