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
*UNUSED(state
))
50 static ALboolean
ALdistortionState_deviceUpdate(ALdistortionState
*UNUSED(state
), ALCdevice
*UNUSED(device
))
55 static ALvoid
ALdistortionState_update(ALdistortionState
*state
, ALCdevice
*Device
, const ALeffectslot
*Slot
)
57 ALfloat frequency
= (ALfloat
)Device
->Frequency
;
62 /* Store distorted signal attenuation settings */
63 state
->attenuation
= Slot
->EffectProps
.Distortion
.Gain
;
65 /* Store waveshaper edge settings */
66 edge
= sinf(Slot
->EffectProps
.Distortion
.Edge
* (F_PI_2
));
67 edge
= minf(edge
, 0.99f
);
68 state
->edge_coeff
= 2.0f
* edge
/ (1.0f
-edge
);
71 cutoff
= Slot
->EffectProps
.Distortion
.LowpassCutoff
;
72 /* Bandwidth value is constant in octaves */
73 bandwidth
= (cutoff
/ 2.0f
) / (cutoff
* 0.67f
);
74 ALfilterState_setParams(&state
->lowpass
, ALfilterType_LowPass
, 1.0f
,
75 cutoff
/ (frequency
*4.0f
), bandwidth
);
78 cutoff
= Slot
->EffectProps
.Distortion
.EQCenter
;
79 /* Convert bandwidth in Hz to octaves */
80 bandwidth
= Slot
->EffectProps
.Distortion
.EQBandwidth
/ (cutoff
* 0.67f
);
81 ALfilterState_setParams(&state
->bandpass
, ALfilterType_BandPass
, 1.0f
,
82 cutoff
/ (frequency
*4.0f
), bandwidth
);
84 ComputeAmbientGains(Device
, Slot
->Gain
, state
->Gain
);
87 static ALvoid
ALdistortionState_process(ALdistortionState
*state
, ALuint SamplesToDo
, const ALfloat
*restrict SamplesIn
, ALfloat (*restrict SamplesOut
)[BUFFERSIZE
])
89 const ALfloat fc
= state
->edge_coeff
;
90 float oversample_buffer
[64][4];
96 for(base
= 0;base
< SamplesToDo
;)
99 ALuint td
= minu(SamplesToDo
-base
, 64);
101 /* Perform 4x oversampling to avoid aliasing. */
102 /* Oversampling greatly improves distortion */
103 /* quality and allows to implement lowpass and */
104 /* bandpass filters using high frequencies, at */
105 /* which classic IIR filters became unstable. */
107 /* Fill oversample buffer using zero stuffing */
108 for(it
= 0;it
< td
;it
++)
110 oversample_buffer
[it
][0] = SamplesIn
[it
+base
];
111 oversample_buffer
[it
][1] = 0.0f
;
112 oversample_buffer
[it
][2] = 0.0f
;
113 oversample_buffer
[it
][3] = 0.0f
;
116 /* First step, do lowpass filtering of original signal, */
117 /* additionally perform buffer interpolation and lowpass */
118 /* cutoff for oversampling (which is fortunately first */
119 /* step of distortion). So combine three operations into */
121 for(it
= 0;it
< td
;it
++)
123 for(ot
= 0;ot
< 4;ot
++)
126 smp
= ALfilterState_processSingle(&state
->lowpass
, oversample_buffer
[it
][ot
]);
128 /* Restore signal power by multiplying sample by amount of oversampling */
129 oversample_buffer
[it
][ot
] = smp
* 4.0f
;
133 for(it
= 0;it
< td
;it
++)
135 /* Second step, do distortion using waveshaper function */
136 /* to emulate signal processing during tube overdriving. */
137 /* Three steps of waveshaping are intended to modify */
138 /* waveform without boost/clipping/attenuation process. */
139 for(ot
= 0;ot
< 4;ot
++)
141 ALfloat smp
= oversample_buffer
[it
][ot
];
143 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*fabsf(smp
));
144 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*fabsf(smp
)) * -1.0f
;
145 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*fabsf(smp
));
147 /* Third step, do bandpass filtering of distorted signal */
148 smp
= ALfilterState_processSingle(&state
->bandpass
, smp
);
149 oversample_buffer
[it
][ot
] = smp
;
152 /* Fourth step, final, do attenuation and perform decimation, */
153 /* store only one sample out of 4. */
154 temps
[it
] = oversample_buffer
[it
][0] * state
->attenuation
;
157 for(kt
= 0;kt
< MAX_OUTPUT_CHANNELS
;kt
++)
159 ALfloat gain
= state
->Gain
[kt
];
160 if(!(gain
> GAIN_SILENCE_THRESHOLD
))
163 for(it
= 0;it
< td
;it
++)
164 SamplesOut
[kt
][base
+it
] += gain
* temps
[it
];
171 DECLARE_DEFAULT_ALLOCATORS(ALdistortionState
)
173 DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState
);
176 typedef struct ALdistortionStateFactory
{
177 DERIVE_FROM_TYPE(ALeffectStateFactory
);
178 } ALdistortionStateFactory
;
180 static ALeffectState
*ALdistortionStateFactory_create(ALdistortionStateFactory
*UNUSED(factory
))
182 ALdistortionState
*state
;
184 state
= ALdistortionState_New(sizeof(*state
));
185 if(!state
) return NULL
;
186 SET_VTABLE2(ALdistortionState
, ALeffectState
, state
);
188 ALfilterState_clear(&state
->lowpass
);
189 ALfilterState_clear(&state
->bandpass
);
191 return STATIC_CAST(ALeffectState
, state
);
194 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory
);
197 ALeffectStateFactory
*ALdistortionStateFactory_getFactory(void)
199 static ALdistortionStateFactory DistortionFactory
= { { GET_VTABLE2(ALdistortionStateFactory
, ALeffectStateFactory
) } };
201 return STATIC_CAST(ALeffectStateFactory
, &DistortionFactory
);
205 void ALdistortion_setParami(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
UNUSED(val
))
206 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
207 void ALdistortion_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
209 ALdistortion_setParami(effect
, context
, param
, vals
[0]);
211 void ALdistortion_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
213 ALeffectProps
*props
= &effect
->Props
;
216 case AL_DISTORTION_EDGE
:
217 if(!(val
>= AL_DISTORTION_MIN_EDGE
&& val
<= AL_DISTORTION_MAX_EDGE
))
218 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
219 props
->Distortion
.Edge
= val
;
222 case AL_DISTORTION_GAIN
:
223 if(!(val
>= AL_DISTORTION_MIN_GAIN
&& val
<= AL_DISTORTION_MAX_GAIN
))
224 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
225 props
->Distortion
.Gain
= val
;
228 case AL_DISTORTION_LOWPASS_CUTOFF
:
229 if(!(val
>= AL_DISTORTION_MIN_LOWPASS_CUTOFF
&& val
<= AL_DISTORTION_MAX_LOWPASS_CUTOFF
))
230 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
231 props
->Distortion
.LowpassCutoff
= val
;
234 case AL_DISTORTION_EQCENTER
:
235 if(!(val
>= AL_DISTORTION_MIN_EQCENTER
&& val
<= AL_DISTORTION_MAX_EQCENTER
))
236 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
237 props
->Distortion
.EQCenter
= val
;
240 case AL_DISTORTION_EQBANDWIDTH
:
241 if(!(val
>= AL_DISTORTION_MIN_EQBANDWIDTH
&& val
<= AL_DISTORTION_MAX_EQBANDWIDTH
))
242 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
243 props
->Distortion
.EQBandwidth
= val
;
247 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
250 void ALdistortion_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
252 ALdistortion_setParamf(effect
, context
, param
, vals
[0]);
255 void ALdistortion_getParami(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
*UNUSED(val
))
256 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
257 void ALdistortion_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
259 ALdistortion_getParami(effect
, context
, param
, vals
);
261 void ALdistortion_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
263 const ALeffectProps
*props
= &effect
->Props
;
266 case AL_DISTORTION_EDGE
:
267 *val
= props
->Distortion
.Edge
;
270 case AL_DISTORTION_GAIN
:
271 *val
= props
->Distortion
.Gain
;
274 case AL_DISTORTION_LOWPASS_CUTOFF
:
275 *val
= props
->Distortion
.LowpassCutoff
;
278 case AL_DISTORTION_EQCENTER
:
279 *val
= props
->Distortion
.EQCenter
;
282 case AL_DISTORTION_EQBANDWIDTH
:
283 *val
= props
->Distortion
.EQBandwidth
;
287 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
290 void ALdistortion_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
292 ALdistortion_getParamf(effect
, context
, param
, vals
);
295 DEFINE_ALEFFECT_VTABLE(ALdistortion
);