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:
@@ -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);
|
||||
|
||||
@@ -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); \
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user