aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/effects
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2018-07-29 00:18:46 -0700
committerChris Robinson <[email protected]>2018-07-29 00:18:46 -0700
commit3894c580bf0059e93bb6171b661e2f96dc1ab27f (patch)
tree6eef28c3b1960bddc499afdde71e2439b0af3187 /Alc/effects
parentf1bf932a84a188b1f79a6affc17265128f738ae6 (diff)
Use just the omnidirectional response for the compressor effect
This is not the output compressor/limiter, but the EFX effect. Consequently, it simply compresses the dynamic range around 1.0 (boosting samples below it by up to double, reducing samples above it by as much as half). This is not intended to prevent clipping on the output, but to instead reduce the range between quiet sounds and loud sounds.
Diffstat (limited to 'Alc/effects')
-rw-r--r--Alc/effects/compressor.c122
1 files changed, 58 insertions, 64 deletions
diff --git a/Alc/effects/compressor.c b/Alc/effects/compressor.c
index 4ec28f9a..a5f1953b 100644
--- a/Alc/effects/compressor.c
+++ b/Alc/effects/compressor.c
@@ -27,6 +27,13 @@
#include "alu.h"
+#define AMP_ENVELOPE_MIN 0.5f
+#define AMP_ENVELOPE_MAX 2.0f
+
+#define ATTACK_TIME 0.1f /* 100ms to rise from min to max */
+#define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
+
+
typedef struct ALcompressorState {
DERIVE_FROM_TYPE(ALeffectState);
@@ -35,9 +42,9 @@ typedef struct ALcompressorState {
/* Effect parameters */
ALboolean Enabled;
- ALfloat AttackRate;
- ALfloat ReleaseRate;
- ALfloat GainCtrl;
+ ALfloat AttackMult;
+ ALfloat ReleaseMult;
+ ALfloat EnvFollower;
} ALcompressorState;
static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
@@ -55,9 +62,9 @@ static void ALcompressorState_Construct(ALcompressorState *state)
SET_VTABLE2(ALcompressorState, ALeffectState, state);
state->Enabled = AL_TRUE;
- state->AttackRate = 0.0f;
- state->ReleaseRate = 0.0f;
- state->GainCtrl = 1.0f;
+ state->AttackMult = 1.0f;
+ state->ReleaseMult = 1.0f;
+ state->EnvFollower = 1.0f;
}
static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
@@ -67,11 +74,17 @@ static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
{
- const ALfloat attackTime = device->Frequency * 0.2f; /* 200ms Attack */
- const ALfloat releaseTime = device->Frequency * 0.4f; /* 400ms Release */
-
- state->AttackRate = 1.0f / attackTime;
- state->ReleaseRate = 1.0f / releaseTime;
+ /* Number of samples to do a full attack and release (non-integer sample
+ * counts are okay).
+ */
+ const ALfloat attackCount = (ALfloat)device->Frequency * ATTACK_TIME;
+ const ALfloat releaseCount = (ALfloat)device->Frequency * RELEASE_TIME;
+
+ /* Calculate per-sample multipliers to attack and release at the desired
+ * rates.
+ */
+ state->AttackMult = powf(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount);
+ state->ReleaseMult = powf(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
return AL_TRUE;
}
@@ -97,71 +110,52 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
for(base = 0;base < SamplesToDo;)
{
- ALfloat temps[64][4];
- ALsizei td = mini(64, SamplesToDo-base);
-
- /* Load samples into the temp buffer first. */
- for(j = 0;j < 4;j++)
- {
- for(i = 0;i < td;i++)
- temps[i][j] = SamplesIn[j][i+base];
- }
+ ALfloat gains[256];
+ ALsizei td = mini(256, SamplesToDo-base);
+ ALfloat env = state->EnvFollower;
+ /* Generate the per-sample gains from the signal envelope. */
if(state->Enabled)
{
- ALfloat gain = state->GainCtrl;
- ALfloat output, amplitude;
-
- for(i = 0;i < td;i++)
+ for(i = 0;i < td;++i)
{
- /* Roughly calculate the maximum amplitude from the 4-channel
- * signal, and attack or release the gain control to reach it.
+ /* Clamp the absolute amplitude to the defined envelope limits,
+ * then attack or release the envelope to reach it.
+ */
+ ALfloat amplitude = clampf(fabsf(SamplesIn[0][base+i]),
+ AMP_ENVELOPE_MIN, AMP_ENVELOPE_MAX);
+ if(amplitude > env)
+ env = minf(env*state->AttackMult, amplitude);
+ else if(amplitude < env)
+ env = maxf(env*state->ReleaseMult, amplitude);
+
+ /* Apply the reciprocal of the envelope to normalize the volume
+ * (compress the dynamic range).
*/
- amplitude = fabsf(temps[i][0]);
- amplitude = maxf(amplitude + fabsf(temps[i][1]),
- maxf(amplitude + fabsf(temps[i][2]),
- amplitude + fabsf(temps[i][3])));
- if(amplitude > gain)
- gain = minf(gain+state->AttackRate, amplitude);
- else if(amplitude < gain)
- gain = maxf(gain-state->ReleaseRate, amplitude);
-
- /* Apply the inverse of the gain control to normalize/compress
- * the volume. */
- output = 1.0f / clampf(gain, 0.5f, 2.0f);
- for(j = 0;j < 4;j++)
- temps[i][j] *= output;
+ gains[i] = 1.0f / env;
}
-
- state->GainCtrl = gain;
}
else
{
- ALfloat gain = state->GainCtrl;
- ALfloat output, amplitude;
-
- for(i = 0;i < td;i++)
+ /* Same as above, except the amplitude is forced to 1. This helps
+ * ensure smooth gain changes when the compressor is turned on and
+ * off.
+ */
+ for(i = 0;i < td;++i)
{
- /* Same as above, except the amplitude is forced to 1. This
- * helps ensure smooth gain changes when the compressor is
- * turned on and off.
- */
- amplitude = 1.0f;
- if(amplitude > gain)
- gain = minf(gain+state->AttackRate, amplitude);
- else if(amplitude < gain)
- gain = maxf(gain-state->ReleaseRate, amplitude);
-
- output = 1.0f / clampf(gain, 0.5f, 2.0f);
- for(j = 0;j < 4;j++)
- temps[i][j] *= output;
- }
+ ALfloat amplitude = 1.0f;
+ if(amplitude > env)
+ env = minf(env*state->AttackMult, amplitude);
+ else if(amplitude < env)
+ env = maxf(env*state->ReleaseMult, amplitude);
- state->GainCtrl = gain;
+ gains[i] = 1.0f / env;
+ }
}
+ state->EnvFollower = env;
- /* Now mix to the output. */
- for(j = 0;j < 4;j++)
+ /* Now compress the signal amplitude to output. */
+ for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
{
for(k = 0;k < NumChannels;k++)
{
@@ -170,7 +164,7 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
continue;
for(i = 0;i < td;i++)
- SamplesOut[k][base+i] += gain * temps[i][j];
+ SamplesOut[k][base+i] += SamplesIn[k][base+i] * gains[i] * gain;
}
}