diff options
author | Chris Robinson <[email protected]> | 2017-04-26 18:38:09 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2017-04-26 18:38:09 -0700 |
commit | ca5c732261bfacdb4702fcc253b3ef0dad357a35 (patch) | |
tree | 9d9ff19e5bc80a3a5d44ba2c7a464b85097d37f1 /Alc/ALu.c | |
parent | 1754d54c18b58995718d2695151a945ee232b0f1 (diff) |
Implement a limiter on the device output
This reduces the output volume when the mixed samples extend outside of -1,+1,
to prevent excessive clipping. It can reduce the volume by -80dB in 50ms, and
increase it by +80dB in 1s (it will not go below -80dB or above 0dB).
Diffstat (limited to 'Alc/ALu.c')
-rw-r--r-- | Alc/ALu.c | 60 |
1 files changed, 59 insertions, 1 deletions
@@ -1333,6 +1333,49 @@ static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray } +static ALfloat ApplyLimiter(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALsizei NumChans, + const ALfloat AttackRate, const ALfloat ReleaseRate, + const ALfloat InGain, ALfloat (*restrict Gains), + const ALsizei SamplesToDo) +{ + bool do_limit = false; + ALsizei c, i; + + OutBuffer = ASSUME_ALIGNED(OutBuffer, 16); + Gains = ASSUME_ALIGNED(Gains, 16); + + for(i = 0;i < SamplesToDo;i++) + Gains[i] = 1.0f; + + for(c = 0;c < NumChans;c++) + { + ALfloat lastgain = InGain; + for(i = 0;i < SamplesToDo;i++) + { + /* Clamp limiter range to 0dB...-80dB. */ + ALfloat gain = 1.0f / clampf(fabsf(OutBuffer[c][i]), 1.0f, 1000.0f); + if(lastgain >= gain) + lastgain = maxf(lastgain*AttackRate, gain); + else + lastgain = minf(lastgain/ReleaseRate, gain); + do_limit |= (lastgain < 1.0f); + + lastgain = minf(lastgain, Gains[i]); + Gains[i] = lastgain; + } + } + if(do_limit) + { + for(c = 0;c < NumChans;c++) + { + for(i = 0;i < SamplesToDo;i++) + OutBuffer[c][i] *= Gains[i]; + } + } + + return Gains[SamplesToDo-1]; +} + static inline ALfloat aluF2F(ALfloat val) { return val; } @@ -1579,8 +1622,23 @@ void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) { ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer; ALsizei OutChannels = device->RealOut.NumChannels; - DistanceComp *DistComp = device->ChannelDelay; + DistanceComp *DistComp; + + if(device->LimiterGain > 0.0f) + { + /* Limiter attack drops -80dB in 50ms. */ + const ALfloat AttackRate = powf(0.0001f, 1.0f/(device->Frequency*0.05f)); + /* Limiter release raises +80dB in 1s. */ + const ALfloat ReleaseRate = powf(0.0001f, 1.0f/(device->Frequency*1.0f)); + + /* Use NFCtrlData for temp gain storage. */ + device->LimiterGain = ApplyLimiter(OutBuffer, OutChannels, + AttackRate, ReleaseRate, device->LimiterGain, device->NFCtrlData, + SamplesToDo + ); + } + DistComp = device->ChannelDelay; #define WRITE(T, a, b, c, d, e) do { \ Write_##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \ buffer = (T*)buffer + (d)*(e); \ |