diff options
author | Chris Robinson <[email protected]> | 2018-07-29 00:18:46 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2018-07-29 00:18:46 -0700 |
commit | 3894c580bf0059e93bb6171b661e2f96dc1ab27f (patch) | |
tree | 6eef28c3b1960bddc499afdde71e2439b0af3187 /Alc/effects | |
parent | f1bf932a84a188b1f79a6affc17265128f738ae6 (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.c | 122 |
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; } } |