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 */
41 BiquadFilter bandpass
;
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
);
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
);
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
];
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
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
,
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
;
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
));
156 /* Third step, do bandpass filtering of distorted signal. */
157 BiquadFilter_process(&state
->bandpass
, buffer
[1], buffer
[0], todo
);
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
))
169 for(i
= 0;i
< todo
;i
++)
170 SamplesOut
[k
][base
+i
] += gain
* buffer
[1][i
*4];
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
;
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
;
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
;
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
;
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
;
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
;
243 alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion float property 0x%04x",
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
;
259 case AL_DISTORTION_EDGE
:
260 *val
= props
->Distortion
.Edge
;
263 case AL_DISTORTION_GAIN
:
264 *val
= props
->Distortion
.Gain
;
267 case AL_DISTORTION_LOWPASS_CUTOFF
:
268 *val
= props
->Distortion
.LowpassCutoff
;
271 case AL_DISTORTION_EQCENTER
:
272 *val
= props
->Distortion
.EQCenter
;
275 case AL_DISTORTION_EQBANDWIDTH
:
276 *val
= props
->Distortion
.EQBandwidth
;
280 alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion float property 0x%04x",
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
);