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 "alcontext.h"
28 #include "alAuxEffectSlot.h"
31 #include "filters/defs.h"
34 struct ALdistortionState final
: public ALeffectState
{
35 /* Effect gains for each channel */
36 ALfloat Gain
[MAX_OUTPUT_CHANNELS
];
38 /* Effect parameters */
40 BiquadFilter bandpass
;
44 ALfloat Buffer
[2][BUFFERSIZE
];
47 static ALvoid
ALdistortionState_Destruct(ALdistortionState
*state
);
48 static ALboolean
ALdistortionState_deviceUpdate(ALdistortionState
*state
, ALCdevice
*device
);
49 static ALvoid
ALdistortionState_update(ALdistortionState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
);
50 static ALvoid
ALdistortionState_process(ALdistortionState
*state
, ALsizei SamplesToDo
, const ALfloat (*RESTRICT SamplesIn
)[BUFFERSIZE
], ALfloat (*RESTRICT SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
);
51 DECLARE_DEFAULT_ALLOCATORS(ALdistortionState
)
53 DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState
);
56 static void ALdistortionState_Construct(ALdistortionState
*state
)
58 new (state
) ALdistortionState
{};
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
));
66 state
->~ALdistortionState();
69 static ALboolean
ALdistortionState_deviceUpdate(ALdistortionState
*state
, ALCdevice
*UNUSED(device
))
71 BiquadFilter_clear(&state
->lowpass
);
72 BiquadFilter_clear(&state
->bandpass
);
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 BiquadFilter_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 BiquadFilter_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 ComputePanGains(&device
->Dry
, coeffs
, slot
->Params
.Gain
*props
->Distortion
.Gain
, 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
;
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 struct DistortionStateFactory final
: public EffectStateFactory
{
179 ALeffectState
*create() override
;
182 ALeffectState
*DistortionStateFactory::create()
184 ALdistortionState
*state
;
185 NEW_OBJ0(state
, ALdistortionState
)();
189 EffectStateFactory
*DistortionStateFactory_getFactory(void)
191 static DistortionStateFactory DistortionFactory
{};
192 return &DistortionFactory
;
196 void ALdistortion_setParami(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
UNUSED(val
))
197 { alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion integer property 0x%04x", param
); }
198 void ALdistortion_setParamiv(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, const ALint
*UNUSED(vals
))
199 { alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion integer-vector property 0x%04x", param
); }
200 void ALdistortion_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
202 ALeffectProps
*props
= &effect
->Props
;
205 case AL_DISTORTION_EDGE
:
206 if(!(val
>= AL_DISTORTION_MIN_EDGE
&& val
<= AL_DISTORTION_MAX_EDGE
))
207 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Distortion edge out of range");
208 props
->Distortion
.Edge
= val
;
211 case AL_DISTORTION_GAIN
:
212 if(!(val
>= AL_DISTORTION_MIN_GAIN
&& val
<= AL_DISTORTION_MAX_GAIN
))
213 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Distortion gain out of range");
214 props
->Distortion
.Gain
= val
;
217 case AL_DISTORTION_LOWPASS_CUTOFF
:
218 if(!(val
>= AL_DISTORTION_MIN_LOWPASS_CUTOFF
&& val
<= AL_DISTORTION_MAX_LOWPASS_CUTOFF
))
219 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Distortion low-pass cutoff out of range");
220 props
->Distortion
.LowpassCutoff
= val
;
223 case AL_DISTORTION_EQCENTER
:
224 if(!(val
>= AL_DISTORTION_MIN_EQCENTER
&& val
<= AL_DISTORTION_MAX_EQCENTER
))
225 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Distortion EQ center out of range");
226 props
->Distortion
.EQCenter
= val
;
229 case AL_DISTORTION_EQBANDWIDTH
:
230 if(!(val
>= AL_DISTORTION_MIN_EQBANDWIDTH
&& val
<= AL_DISTORTION_MAX_EQBANDWIDTH
))
231 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Distortion EQ bandwidth out of range");
232 props
->Distortion
.EQBandwidth
= val
;
236 alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion float property 0x%04x",
240 void ALdistortion_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
241 { ALdistortion_setParamf(effect
, context
, param
, vals
[0]); }
243 void ALdistortion_getParami(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
*UNUSED(val
))
244 { alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion integer property 0x%04x", param
); }
245 void ALdistortion_getParamiv(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
*UNUSED(vals
))
246 { alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion integer-vector property 0x%04x", param
); }
247 void ALdistortion_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
249 const ALeffectProps
*props
= &effect
->Props
;
252 case AL_DISTORTION_EDGE
:
253 *val
= props
->Distortion
.Edge
;
256 case AL_DISTORTION_GAIN
:
257 *val
= props
->Distortion
.Gain
;
260 case AL_DISTORTION_LOWPASS_CUTOFF
:
261 *val
= props
->Distortion
.LowpassCutoff
;
264 case AL_DISTORTION_EQCENTER
:
265 *val
= props
->Distortion
.EQCenter
;
268 case AL_DISTORTION_EQBANDWIDTH
:
269 *val
= props
->Distortion
.EQBandwidth
;
273 alSetError(context
, AL_INVALID_ENUM
, "Invalid distortion float property 0x%04x",
277 void ALdistortion_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
278 { ALdistortion_getParamf(effect
, context
, param
, vals
); }
280 DEFINE_ALEFFECT_VTABLE(ALdistortion
);