Use the correct input channel for the compressor effect
[openal-soft.git] / Alc / effects / compressor.c
blobaf60777a4fb7bfd99cc0bad8605ee160ef725f02
1 /**
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
21 #include <stdlib.h>
23 #include "config.h"
24 #include "alError.h"
25 #include "alMain.h"
26 #include "alAuxEffectSlot.h"
27 #include "alu.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 */
44 ALboolean Enabled;
45 ALfloat AttackMult;
46 ALfloat ReleaseMult;
47 ALfloat EnvFollower;
48 } ALcompressorState;
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
78 * counts are okay).
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
84 * rates.
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);
89 return AL_TRUE;
92 static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
94 const ALCdevice *device = context->Device;
95 ALuint i;
97 state->Enabled = props->Compressor.OnOff;
99 STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
100 STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
101 for(i = 0;i < 4;i++)
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)
108 ALsizei i, j, k;
109 ALsizei base;
111 for(base = 0;base < SamplesToDo;)
113 ALfloat gains[256];
114 ALsizei td = mini(256, SamplesToDo-base);
115 ALfloat env = state->EnvFollower;
117 /* Generate the per-sample gains from the signal envelope. */
118 if(state->Enabled)
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);
127 if(amplitude > env)
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;
138 else
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
142 * off.
144 for(i = 0;i < td;++i)
146 ALfloat amplitude = 1.0f;
147 if(amplitude > env)
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))
164 continue;
166 for(i = 0;i < td;i++)
167 SamplesOut[k][base+i] += SamplesIn[j][base+i] * gains[i] * gain;
171 base += td;
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;
203 switch(param)
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;
209 break;
211 default:
212 alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
213 param);
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;
226 switch(param)
228 case AL_COMPRESSOR_ONOFF:
229 *val = props->Compressor.OnOff;
230 break;
232 default:
233 alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
234 param);
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);