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).
This commit is contained in:
Chris Robinson
2017-04-26 18:38:09 -07:00
parent 1754d54c18
commit ca5c732261
3 changed files with 72 additions and 4 deletions
+11 -3
View File
@@ -2192,13 +2192,19 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
device->FOAOut.NumChannels = device->Dry.NumChannels;
}
/* Need to delay returning failure until replacement Send arrays have been
* allocated with the appropriate size.
*/
device->NumAuxSends = new_sends;
TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n",
device->SourcesMax, device->NumMonoSources, device->NumStereoSources,
device->AuxiliaryEffectSlotMax, device->NumAuxSends);
if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), NULL, "output-limiter", 1))
device->LimiterGain = 1.0f;
else
device->LimiterGain = 0.0f;
/* Need to delay returning failure until replacement Send arrays have been
* allocated with the appropriate size.
*/
update_failed = AL_FALSE;
SetMixerFPUMode(&oldMode);
if(device->DefaultSlot)
@@ -3794,6 +3800,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
device->FOAOut.NumChannels = 0;
device->RealOut.Buffer = NULL;
device->RealOut.NumChannels = 0;
device->LimiterGain = 1.0f;
device->AvgSpeakerDist = 0.0f;
ATOMIC_INIT(&device->ContextList, NULL);
@@ -4322,6 +4329,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
device->FOAOut.NumChannels = 0;
device->RealOut.Buffer = NULL;
device->RealOut.NumChannels = 0;
device->LimiterGain = 1.0f;
device->AvgSpeakerDist = 0.0f;
ATOMIC_INIT(&device->ContextList, NULL);
+59 -1
View File
@@ -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); \
+2
View File
@@ -795,6 +795,8 @@ struct ALCdevice_struct
ALsizei NumChannels;
} RealOut;
ALfloat LimiterGain;
/* The average speaker distance as determined by the ambdec configuration
* (or alternatively, by the NFC-HOA reference delay). Only used for NFC.
*/