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
28 #include "alAuxEffectSlot.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 ALfilterState lowpass
;
41 ALfilterState bandpass
;
46 static ALvoid
ALdistortionState_Destruct(ALdistortionState
*state
)
48 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
51 static ALboolean
ALdistortionState_deviceUpdate(ALdistortionState
*UNUSED(state
), ALCdevice
*UNUSED(device
))
56 static ALvoid
ALdistortionState_update(ALdistortionState
*state
, const ALCdevice
*Device
, const ALeffectslot
*Slot
, const ALeffectProps
*props
)
58 ALfloat frequency
= (ALfloat
)Device
->Frequency
;
63 /* Store distorted signal attenuation settings */
64 state
->attenuation
= props
->Distortion
.Gain
;
66 /* Store waveshaper edge settings */
67 edge
= sinf(props
->Distortion
.Edge
* (F_PI_2
));
68 edge
= minf(edge
, 0.99f
);
69 state
->edge_coeff
= 2.0f
* edge
/ (1.0f
-edge
);
72 cutoff
= props
->Distortion
.LowpassCutoff
;
73 /* Bandwidth value is constant in octaves */
74 bandwidth
= (cutoff
/ 2.0f
) / (cutoff
* 0.67f
);
75 ALfilterState_setParams(&state
->lowpass
, ALfilterType_LowPass
, 1.0f
,
76 cutoff
/ (frequency
*4.0f
), calc_rcpQ_from_bandwidth(cutoff
/ (frequency
*4.0f
), bandwidth
)
80 cutoff
= props
->Distortion
.EQCenter
;
81 /* Convert bandwidth in Hz to octaves */
82 bandwidth
= props
->Distortion
.EQBandwidth
/ (cutoff
* 0.67f
);
83 ALfilterState_setParams(&state
->bandpass
, ALfilterType_BandPass
, 1.0f
,
84 cutoff
/ (frequency
*4.0f
), calc_rcpQ_from_bandwidth(cutoff
/ (frequency
*4.0f
), bandwidth
)
87 ComputeAmbientGains(Device
->Dry
, Slot
->Params
.Gain
, state
->Gain
);
90 static ALvoid
ALdistortionState_process(ALdistortionState
*state
, ALuint SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALuint NumChannels
)
92 const ALfloat fc
= state
->edge_coeff
;
98 for(base
= 0;base
< SamplesToDo
;)
100 float buffer
[2][64 * 4];
101 ALuint td
= minu(64, SamplesToDo
-base
);
103 /* Perform 4x oversampling to avoid aliasing. */
104 /* Oversampling greatly improves distortion */
105 /* quality and allows to implement lowpass and */
106 /* bandpass filters using high frequencies, at */
107 /* which classic IIR filters became unstable. */
109 /* Fill oversample buffer using zero stuffing */
110 for(it
= 0;it
< td
;it
++)
112 buffer
[0][it
*4 + 0] = SamplesIn
[0][it
+base
];
113 buffer
[0][it
*4 + 1] = 0.0f
;
114 buffer
[0][it
*4 + 2] = 0.0f
;
115 buffer
[0][it
*4 + 3] = 0.0f
;
118 /* First step, do lowpass filtering of original signal, */
119 /* additionally perform buffer interpolation and lowpass */
120 /* cutoff for oversampling (which is fortunately first */
121 /* step of distortion). So combine three operations into */
123 ALfilterState_process(&state
->lowpass
, buffer
[1], buffer
[0], td
*4);
125 /* Second step, do distortion using waveshaper function */
126 /* to emulate signal processing during tube overdriving. */
127 /* Three steps of waveshaping are intended to modify */
128 /* waveform without boost/clipping/attenuation process. */
129 for(it
= 0;it
< td
;it
++)
131 for(ot
= 0;ot
< 4;ot
++)
133 /* Restore signal power by multiplying sample by amount of oversampling */
134 ALfloat smp
= buffer
[1][it
*4 + ot
] * 4.0f
;
136 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*fabsf(smp
));
137 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*fabsf(smp
)) * -1.0f
;
138 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*fabsf(smp
));
140 buffer
[1][it
*4 + ot
] = smp
;
144 /* Third step, do bandpass filtering of distorted signal */
145 ALfilterState_process(&state
->bandpass
, buffer
[0], buffer
[1], td
*4);
147 for(kt
= 0;kt
< NumChannels
;kt
++)
149 /* Fourth step, final, do attenuation and perform decimation,
150 * store only one sample out of 4.
152 ALfloat gain
= state
->Gain
[kt
] * state
->attenuation
;
153 if(!(fabsf(gain
) > GAIN_SILENCE_THRESHOLD
))
156 for(it
= 0;it
< td
;it
++)
157 SamplesOut
[kt
][base
+it
] += gain
* buffer
[0][it
*4];
164 DECLARE_DEFAULT_ALLOCATORS(ALdistortionState
)
166 DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState
);
169 typedef struct ALdistortionStateFactory
{
170 DERIVE_FROM_TYPE(ALeffectStateFactory
);
171 } ALdistortionStateFactory
;
173 static ALeffectState
*ALdistortionStateFactory_create(ALdistortionStateFactory
*UNUSED(factory
))
175 ALdistortionState
*state
;
177 state
= ALdistortionState_New(sizeof(*state
));
178 if(!state
) return NULL
;
179 SET_VTABLE2(ALdistortionState
, ALeffectState
, state
);
181 ALfilterState_clear(&state
->lowpass
);
182 ALfilterState_clear(&state
->bandpass
);
184 return STATIC_CAST(ALeffectState
, state
);
187 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory
);
190 ALeffectStateFactory
*ALdistortionStateFactory_getFactory(void)
192 static ALdistortionStateFactory DistortionFactory
= { { GET_VTABLE2(ALdistortionStateFactory
, ALeffectStateFactory
) } };
194 return STATIC_CAST(ALeffectStateFactory
, &DistortionFactory
);
198 void ALdistortion_setParami(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
UNUSED(val
))
199 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
200 void ALdistortion_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
202 ALdistortion_setParami(effect
, context
, param
, vals
[0]);
204 void ALdistortion_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
206 ALeffectProps
*props
= &effect
->Props
;
209 case AL_DISTORTION_EDGE
:
210 if(!(val
>= AL_DISTORTION_MIN_EDGE
&& val
<= AL_DISTORTION_MAX_EDGE
))
211 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
212 props
->Distortion
.Edge
= val
;
215 case AL_DISTORTION_GAIN
:
216 if(!(val
>= AL_DISTORTION_MIN_GAIN
&& val
<= AL_DISTORTION_MAX_GAIN
))
217 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
218 props
->Distortion
.Gain
= val
;
221 case AL_DISTORTION_LOWPASS_CUTOFF
:
222 if(!(val
>= AL_DISTORTION_MIN_LOWPASS_CUTOFF
&& val
<= AL_DISTORTION_MAX_LOWPASS_CUTOFF
))
223 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
224 props
->Distortion
.LowpassCutoff
= val
;
227 case AL_DISTORTION_EQCENTER
:
228 if(!(val
>= AL_DISTORTION_MIN_EQCENTER
&& val
<= AL_DISTORTION_MAX_EQCENTER
))
229 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
230 props
->Distortion
.EQCenter
= val
;
233 case AL_DISTORTION_EQBANDWIDTH
:
234 if(!(val
>= AL_DISTORTION_MIN_EQBANDWIDTH
&& val
<= AL_DISTORTION_MAX_EQBANDWIDTH
))
235 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
236 props
->Distortion
.EQBandwidth
= val
;
240 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
243 void ALdistortion_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
245 ALdistortion_setParamf(effect
, context
, param
, vals
[0]);
248 void ALdistortion_getParami(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
*UNUSED(val
))
249 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
250 void ALdistortion_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
252 ALdistortion_getParami(effect
, context
, param
, vals
);
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 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
283 void ALdistortion_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
285 ALdistortion_getParamf(effect
, context
, param
, vals
);
288 DEFINE_ALEFFECT_VTABLE(ALdistortion
);