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 #define AMP_ENVELOPE_MIN 0.5f
31 #define AMP_ENVELOPE_MAX 2.0f
33 #define ATTACK_TIME 0.1f /* 100ms to rise from min to max */
34 #define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
37 typedef struct ALcompressorState
{
38 DERIVE_FROM_TYPE(ALeffectState
);
40 /* Effect gains for each channel */
41 ALfloat Gain
[MAX_EFFECT_CHANNELS
][MAX_OUTPUT_CHANNELS
];
43 /* Effect parameters */
50 static ALvoid
ALcompressorState_Destruct(ALcompressorState
*state
);
51 static ALboolean
ALcompressorState_deviceUpdate(ALcompressorState
*state
, ALCdevice
*device
);
52 static ALvoid
ALcompressorState_update(ALcompressorState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
);
53 static ALvoid
ALcompressorState_process(ALcompressorState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
);
54 DECLARE_DEFAULT_ALLOCATORS(ALcompressorState
)
56 DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState
);
59 static void ALcompressorState_Construct(ALcompressorState
*state
)
61 ALeffectState_Construct(STATIC_CAST(ALeffectState
, state
));
62 SET_VTABLE2(ALcompressorState
, ALeffectState
, state
);
64 state
->Enabled
= AL_TRUE
;
65 state
->AttackMult
= 1.0f
;
66 state
->ReleaseMult
= 1.0f
;
67 state
->EnvFollower
= 1.0f
;
70 static ALvoid
ALcompressorState_Destruct(ALcompressorState
*state
)
72 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
75 static ALboolean
ALcompressorState_deviceUpdate(ALcompressorState
*state
, ALCdevice
*device
)
77 /* Number of samples to do a full attack and release (non-integer sample
80 const ALfloat attackCount
= (ALfloat
)device
->Frequency
* ATTACK_TIME
;
81 const ALfloat releaseCount
= (ALfloat
)device
->Frequency
* RELEASE_TIME
;
83 /* Calculate per-sample multipliers to attack and release at the desired
86 state
->AttackMult
= powf(AMP_ENVELOPE_MAX
/AMP_ENVELOPE_MIN
, 1.0f
/attackCount
);
87 state
->ReleaseMult
= powf(AMP_ENVELOPE_MIN
/AMP_ENVELOPE_MAX
, 1.0f
/releaseCount
);
92 static ALvoid
ALcompressorState_update(ALcompressorState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
)
94 const ALCdevice
*device
= context
->Device
;
97 state
->Enabled
= props
->Compressor
.OnOff
;
99 STATIC_CAST(ALeffectState
,state
)->OutBuffer
= device
->FOAOut
.Buffer
;
100 STATIC_CAST(ALeffectState
,state
)->OutChannels
= device
->FOAOut
.NumChannels
;
102 ComputeFirstOrderGains(&device
->FOAOut
, IdentityMatrixf
.m
[i
],
103 slot
->Params
.Gain
, state
->Gain
[i
]);
106 static ALvoid
ALcompressorState_process(ALcompressorState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
111 for(base
= 0;base
< SamplesToDo
;)
114 ALsizei td
= mini(256, SamplesToDo
-base
);
115 ALfloat env
= state
->EnvFollower
;
117 /* Generate the per-sample gains from the signal envelope. */
120 for(i
= 0;i
< td
;++i
)
122 /* Clamp the absolute amplitude to the defined envelope limits,
123 * then attack or release the envelope to reach it.
125 ALfloat amplitude
= clampf(fabsf(SamplesIn
[0][base
+i
]),
126 AMP_ENVELOPE_MIN
, AMP_ENVELOPE_MAX
);
128 env
= minf(env
*state
->AttackMult
, amplitude
);
129 else if(amplitude
< env
)
130 env
= maxf(env
*state
->ReleaseMult
, amplitude
);
132 /* Apply the reciprocal of the envelope to normalize the volume
133 * (compress the dynamic range).
135 gains
[i
] = 1.0f
/ env
;
140 /* Same as above, except the amplitude is forced to 1. This helps
141 * ensure smooth gain changes when the compressor is turned on and
144 for(i
= 0;i
< td
;++i
)
146 ALfloat amplitude
= 1.0f
;
148 env
= minf(env
*state
->AttackMult
, amplitude
);
149 else if(amplitude
< env
)
150 env
= maxf(env
*state
->ReleaseMult
, amplitude
);
152 gains
[i
] = 1.0f
/ env
;
155 state
->EnvFollower
= env
;
157 /* Now compress the signal amplitude to output. */
158 for(j
= 0;j
< MAX_EFFECT_CHANNELS
;j
++)
160 for(k
= 0;k
< NumChannels
;k
++)
162 ALfloat gain
= state
->Gain
[j
][k
];
163 if(!(fabsf(gain
) > GAIN_SILENCE_THRESHOLD
))
166 for(i
= 0;i
< td
;i
++)
167 SamplesOut
[k
][base
+i
] += SamplesIn
[j
][base
+i
] * gains
[i
] * gain
;
176 typedef struct CompressorStateFactory
{
177 DERIVE_FROM_TYPE(EffectStateFactory
);
178 } CompressorStateFactory
;
180 static ALeffectState
*CompressorStateFactory_create(CompressorStateFactory
*UNUSED(factory
))
182 ALcompressorState
*state
;
184 NEW_OBJ0(state
, ALcompressorState
)();
185 if(!state
) return NULL
;
187 return STATIC_CAST(ALeffectState
, state
);
190 DEFINE_EFFECTSTATEFACTORY_VTABLE(CompressorStateFactory
);
192 EffectStateFactory
*CompressorStateFactory_getFactory(void)
194 static CompressorStateFactory CompressorFactory
= { { GET_VTABLE2(CompressorStateFactory
, EffectStateFactory
) } };
196 return STATIC_CAST(EffectStateFactory
, &CompressorFactory
);
200 void ALcompressor_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
202 ALeffectProps
*props
= &effect
->Props
;
205 case AL_COMPRESSOR_ONOFF
:
206 if(!(val
>= AL_COMPRESSOR_MIN_ONOFF
&& val
<= AL_COMPRESSOR_MAX_ONOFF
))
207 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Compressor state out of range");
208 props
->Compressor
.OnOff
= val
;
212 alSetError(context
, AL_INVALID_ENUM
, "Invalid compressor integer property 0x%04x",
216 void ALcompressor_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
217 { ALcompressor_setParami(effect
, context
, param
, vals
[0]); }
218 void ALcompressor_setParamf(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALfloat
UNUSED(val
))
219 { alSetError(context
, AL_INVALID_ENUM
, "Invalid compressor float property 0x%04x", param
); }
220 void ALcompressor_setParamfv(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, const ALfloat
*UNUSED(vals
))
221 { alSetError(context
, AL_INVALID_ENUM
, "Invalid compressor float-vector property 0x%04x", param
); }
223 void ALcompressor_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
225 const ALeffectProps
*props
= &effect
->Props
;
228 case AL_COMPRESSOR_ONOFF
:
229 *val
= props
->Compressor
.OnOff
;
233 alSetError(context
, AL_INVALID_ENUM
, "Invalid compressor integer property 0x%04x",
237 void ALcompressor_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
238 { ALcompressor_getParami(effect
, context
, param
, vals
); }
239 void ALcompressor_getParamf(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALfloat
*UNUSED(val
))
240 { alSetError(context
, AL_INVALID_ENUM
, "Invalid compressor float property 0x%04x", param
); }
241 void ALcompressor_getParamfv(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALfloat
*UNUSED(vals
))
242 { alSetError(context
, AL_INVALID_ENUM
, "Invalid compressor float-vector property 0x%04x", param
); }
244 DEFINE_ALEFFECT_VTABLE(ALcompressor
);