2 * OpenAL cross platform audio library
3 * Copyright (C) 2013 by Anis A. Hireche
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
26 #include "alAuxEffectSlot.h"
30 typedef struct ALcompressorState
{
31 DERIVE_FROM_TYPE(ALeffectState
);
33 /* Effect gains for each channel */
34 ALfloat Gain
[MAX_EFFECT_CHANNELS
][MAX_OUTPUT_CHANNELS
];
36 /* Effect parameters */
43 static ALvoid
ALcompressorState_Destruct(ALcompressorState
*state
);
44 static ALboolean
ALcompressorState_deviceUpdate(ALcompressorState
*state
, ALCdevice
*device
);
45 static ALvoid
ALcompressorState_update(ALcompressorState
*state
, const ALCdevice
*device
, const ALeffectslot
*slot
, const ALeffectProps
*props
);
46 static ALvoid
ALcompressorState_process(ALcompressorState
*state
, ALuint SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALuint NumChannels
);
47 DECLARE_DEFAULT_ALLOCATORS(ALcompressorState
)
49 DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState
);
52 static void ALcompressorState_Construct(ALcompressorState
*state
)
54 ALeffectState_Construct(STATIC_CAST(ALeffectState
, state
));
55 SET_VTABLE2(ALcompressorState
, ALeffectState
, state
);
57 state
->Enabled
= AL_TRUE
;
58 state
->AttackRate
= 0.0f
;
59 state
->ReleaseRate
= 0.0f
;
60 state
->GainCtrl
= 1.0f
;
63 static ALvoid
ALcompressorState_Destruct(ALcompressorState
*state
)
65 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
68 static ALboolean
ALcompressorState_deviceUpdate(ALcompressorState
*state
, ALCdevice
*device
)
70 const ALfloat attackTime
= device
->Frequency
* 0.2f
; /* 200ms Attack */
71 const ALfloat releaseTime
= device
->Frequency
* 0.4f
; /* 400ms Release */
73 state
->AttackRate
= 1.0f
/ attackTime
;
74 state
->ReleaseRate
= 1.0f
/ releaseTime
;
79 static ALvoid
ALcompressorState_update(ALcompressorState
*state
, const ALCdevice
*device
, const ALeffectslot
*slot
, const ALeffectProps
*props
)
84 state
->Enabled
= props
->Compressor
.OnOff
;
86 aluMatrixfSet(&matrix
,
87 1.0f
, 0.0f
, 0.0f
, 0.0f
,
88 0.0f
, 1.0f
, 0.0f
, 0.0f
,
89 0.0f
, 0.0f
, 1.0f
, 0.0f
,
90 0.0f
, 0.0f
, 0.0f
, 1.0f
93 STATIC_CAST(ALeffectState
,state
)->OutBuffer
= device
->FOAOut
.Buffer
;
94 STATIC_CAST(ALeffectState
,state
)->OutChannels
= device
->FOAOut
.NumChannels
;
96 ComputeFirstOrderGains(device
->FOAOut
, matrix
.m
[i
], slot
->Params
.Gain
,
100 static ALvoid
ALcompressorState_process(ALcompressorState
*state
, ALuint SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALuint NumChannels
)
105 for(base
= 0;base
< SamplesToDo
;)
107 ALfloat temps
[64][4];
108 ALuint td
= minu(64, SamplesToDo
-base
);
110 /* Load samples into the temp buffer first. */
113 for(i
= 0;i
< td
;i
++)
114 temps
[i
][j
] = SamplesIn
[j
][i
+base
];
119 ALfloat gain
= state
->GainCtrl
;
120 ALfloat output
, amplitude
;
122 for(i
= 0;i
< td
;i
++)
124 /* Roughly calculate the maximum amplitude from the 4-channel
125 * signal, and attack or release the gain control to reach it.
127 amplitude
= fabsf(temps
[i
][0]);
128 amplitude
= maxf(amplitude
+ fabsf(temps
[i
][1]),
129 maxf(amplitude
+ fabsf(temps
[i
][2]),
130 amplitude
+ fabsf(temps
[i
][3])));
132 gain
= minf(gain
+state
->AttackRate
, amplitude
);
133 else if(amplitude
< gain
)
134 gain
= maxf(gain
-state
->ReleaseRate
, amplitude
);
136 /* Apply the inverse of the gain control to normalize/compress
138 output
= 1.0f
/ clampf(gain
, 0.5f
, 2.0f
);
140 temps
[i
][j
] *= output
;
143 state
->GainCtrl
= gain
;
147 ALfloat gain
= state
->GainCtrl
;
148 ALfloat output
, amplitude
;
150 for(i
= 0;i
< td
;i
++)
152 /* Same as above, except the amplitude is forced to 1. This
153 * helps ensure smooth gain changes when the compressor is
158 gain
= minf(gain
+state
->AttackRate
, amplitude
);
159 else if(amplitude
< gain
)
160 gain
= maxf(gain
-state
->ReleaseRate
, amplitude
);
162 output
= 1.0f
/ clampf(gain
, 0.5f
, 2.0f
);
164 temps
[i
][j
] *= output
;
167 state
->GainCtrl
= gain
;
170 /* Now mix to the output. */
173 for(k
= 0;k
< NumChannels
;k
++)
175 ALfloat gain
= state
->Gain
[j
][k
];
176 if(!(fabsf(gain
) > GAIN_SILENCE_THRESHOLD
))
179 for(i
= 0;i
< td
;i
++)
180 SamplesOut
[k
][base
+i
] += gain
* temps
[i
][j
];
189 typedef struct ALcompressorStateFactory
{
190 DERIVE_FROM_TYPE(ALeffectStateFactory
);
191 } ALcompressorStateFactory
;
193 static ALeffectState
*ALcompressorStateFactory_create(ALcompressorStateFactory
*UNUSED(factory
))
195 ALcompressorState
*state
;
197 NEW_OBJ0(state
, ALcompressorState
)();
198 if(!state
) return NULL
;
200 return STATIC_CAST(ALeffectState
, state
);
203 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALcompressorStateFactory
);
205 ALeffectStateFactory
*ALcompressorStateFactory_getFactory(void)
207 static ALcompressorStateFactory CompressorFactory
= { { GET_VTABLE2(ALcompressorStateFactory
, ALeffectStateFactory
) } };
209 return STATIC_CAST(ALeffectStateFactory
, &CompressorFactory
);
213 void ALcompressor_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
215 ALeffectProps
*props
= &effect
->Props
;
218 case AL_COMPRESSOR_ONOFF
:
219 if(!(val
>= AL_COMPRESSOR_MIN_ONOFF
&& val
<= AL_COMPRESSOR_MAX_ONOFF
))
220 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
221 props
->Compressor
.OnOff
= val
;
225 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
228 void ALcompressor_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
230 ALcompressor_setParami(effect
, context
, param
, vals
[0]);
232 void ALcompressor_setParamf(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALfloat
UNUSED(val
))
233 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
234 void ALcompressor_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
236 ALcompressor_setParamf(effect
, context
, param
, vals
[0]);
239 void ALcompressor_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
241 const ALeffectProps
*props
= &effect
->Props
;
244 case AL_COMPRESSOR_ONOFF
:
245 *val
= props
->Compressor
.OnOff
;
248 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
251 void ALcompressor_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
253 ALcompressor_getParami(effect
, context
, param
, vals
);
255 void ALcompressor_getParamf(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALfloat
*UNUSED(val
))
256 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
257 void ALcompressor_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
259 ALcompressor_getParamf(effect
, context
, param
, vals
);
262 DEFINE_ALEFFECT_VTABLE(ALcompressor
);