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
25 #include "al/auxeffectslot.h"
27 #include "alcontext.h"
34 #define AMP_ENVELOPE_MIN 0.5f
35 #define AMP_ENVELOPE_MAX 2.0f
37 #define ATTACK_TIME 0.1f /* 100ms to rise from min to max */
38 #define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
41 struct CompressorState final
: public EffectState
{
42 /* Effect gains for each channel */
43 ALfloat mGain
[MAX_AMBI_CHANNELS
][MAX_OUTPUT_CHANNELS
]{};
45 /* Effect parameters */
46 ALboolean mEnabled
{AL_TRUE
};
47 ALfloat mAttackMult
{1.0f
};
48 ALfloat mReleaseMult
{1.0f
};
49 ALfloat mEnvFollower
{1.0f
};
52 ALboolean
deviceUpdate(const ALCdevice
*device
) override
;
53 void update(const ALCcontext
*context
, const ALeffectslot
*slot
, const EffectProps
*props
, const EffectTarget target
) override
;
54 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
) override
;
56 DEF_NEWDEL(CompressorState
)
59 ALboolean
CompressorState::deviceUpdate(const ALCdevice
*device
)
61 /* Number of samples to do a full attack and release (non-integer sample
64 const ALfloat attackCount
= static_cast<ALfloat
>(device
->Frequency
) * ATTACK_TIME
;
65 const ALfloat releaseCount
= static_cast<ALfloat
>(device
->Frequency
) * RELEASE_TIME
;
67 /* Calculate per-sample multipliers to attack and release at the desired
70 mAttackMult
= std::pow(AMP_ENVELOPE_MAX
/AMP_ENVELOPE_MIN
, 1.0f
/attackCount
);
71 mReleaseMult
= std::pow(AMP_ENVELOPE_MIN
/AMP_ENVELOPE_MAX
, 1.0f
/releaseCount
);
76 void CompressorState::update(const ALCcontext
*, const ALeffectslot
*slot
, const EffectProps
*props
, const EffectTarget target
)
78 mEnabled
= props
->Compressor
.OnOff
;
80 mOutTarget
= target
.Main
->Buffer
;
81 for(size_t i
{0u};i
< slot
->Wet
.Buffer
.size();++i
)
83 auto coeffs
= GetAmbiIdentityRow(i
);
84 ComputePanGains(target
.Main
, coeffs
.data(), slot
->Params
.Gain
, mGain
[i
]);
88 void CompressorState::process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
90 for(size_t base
{0u};base
< samplesToDo
;)
93 const size_t td
{minz(256, samplesToDo
-base
)};
95 /* Generate the per-sample gains from the signal envelope. */
96 ALfloat env
{mEnvFollower
};
99 for(size_t i
{0u};i
< td
;++i
)
101 /* Clamp the absolute amplitude to the defined envelope limits,
102 * then attack or release the envelope to reach it.
104 const ALfloat amplitude
{clampf(std::fabs(samplesIn
[0][base
+i
]), AMP_ENVELOPE_MIN
,
107 env
= minf(env
*mAttackMult
, amplitude
);
108 else if(amplitude
< env
)
109 env
= maxf(env
*mReleaseMult
, amplitude
);
111 /* Apply the reciprocal of the envelope to normalize the volume
112 * (compress the dynamic range).
114 gains
[i
] = 1.0f
/ env
;
119 /* Same as above, except the amplitude is forced to 1. This helps
120 * ensure smooth gain changes when the compressor is turned on and
123 for(size_t i
{0u};i
< td
;++i
)
125 const ALfloat amplitude
{1.0f
};
127 env
= minf(env
*mAttackMult
, amplitude
);
128 else if(amplitude
< env
)
129 env
= maxf(env
*mReleaseMult
, amplitude
);
131 gains
[i
] = 1.0f
/ env
;
136 /* Now compress the signal amplitude to output. */
137 auto changains
= std::addressof(mGain
[0]);
138 for(const auto &input
: samplesIn
)
140 const ALfloat
*outgains
{*(changains
++)};
141 for(FloatBufferLine
&output
: samplesOut
)
143 const ALfloat gain
{*(outgains
++)};
144 if(!(std::fabs(gain
) > GAIN_SILENCE_THRESHOLD
))
147 for(size_t i
{0u};i
< td
;i
++)
148 output
[base
+i
] += input
[base
+i
] * gains
[i
] * gain
;
157 void Compressor_setParami(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint val
)
161 case AL_COMPRESSOR_ONOFF
:
162 if(!(val
>= AL_COMPRESSOR_MIN_ONOFF
&& val
<= AL_COMPRESSOR_MAX_ONOFF
))
163 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Compressor state out of range");
164 props
->Compressor
.OnOff
= val
!= AL_FALSE
;
168 context
->setError(AL_INVALID_ENUM
, "Invalid compressor integer property 0x%04x",
172 void Compressor_setParamiv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
173 { Compressor_setParami(props
, context
, param
, vals
[0]); }
174 void Compressor_setParamf(EffectProps
*, ALCcontext
*context
, ALenum param
, ALfloat
)
175 { context
->setError(AL_INVALID_ENUM
, "Invalid compressor float property 0x%04x", param
); }
176 void Compressor_setParamfv(EffectProps
*, ALCcontext
*context
, ALenum param
, const ALfloat
*)
177 { context
->setError(AL_INVALID_ENUM
, "Invalid compressor float-vector property 0x%04x", param
); }
179 void Compressor_getParami(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*val
)
183 case AL_COMPRESSOR_ONOFF
:
184 *val
= props
->Compressor
.OnOff
;
188 context
->setError(AL_INVALID_ENUM
, "Invalid compressor integer property 0x%04x",
192 void Compressor_getParamiv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
193 { Compressor_getParami(props
, context
, param
, vals
); }
194 void Compressor_getParamf(const EffectProps
*, ALCcontext
*context
, ALenum param
, ALfloat
*)
195 { context
->setError(AL_INVALID_ENUM
, "Invalid compressor float property 0x%04x", param
); }
196 void Compressor_getParamfv(const EffectProps
*, ALCcontext
*context
, ALenum param
, ALfloat
*)
197 { context
->setError(AL_INVALID_ENUM
, "Invalid compressor float-vector property 0x%04x", param
); }
199 DEFINE_ALEFFECT_VTABLE(Compressor
);
202 struct CompressorStateFactory final
: public EffectStateFactory
{
203 EffectState
*create() override
{ return new CompressorState
{}; }
204 EffectProps
getDefaultProps() const noexcept override
;
205 const EffectVtable
*getEffectVtable() const noexcept override
{ return &Compressor_vtable
; }
208 EffectProps
CompressorStateFactory::getDefaultProps() const noexcept
211 props
.Compressor
.OnOff
= AL_COMPRESSOR_DEFAULT_ONOFF
;
217 EffectStateFactory
*CompressorStateFactory_getFactory()
219 static CompressorStateFactory CompressorFactory
{};
220 return &CompressorFactory
;