Store the output limiter values as fixed-point integers

This helps keep the squared sum stable over larger updates, also avoiding the
need to keep recalculating it.
This commit is contained in:
Chris Robinson 2017-05-08 16:23:16 -07:00
parent f880f67049
commit 4a4442ad91
2 changed files with 12 additions and 13 deletions

View File

@ -1455,24 +1455,18 @@ static void ApplyLimiter(struct OutputLimiter *Limiter,
{ {
ALfloat lastgain = Limiter->Gain; ALfloat lastgain = Limiter->Gain;
ALsizei wpos = Limiter->Pos; ALsizei wpos = Limiter->Pos;
ALfloat sum = 0.0f; ALuint sum = Limiter->SquaredSum;
ALfloat gain; ALfloat gain;
/* Unfortunately we can't store the running sum due to fp inaccuracies
* causing it to drift over time. So we need to recalculate it every
* once in a while (i.e. every invocation).
*/
for(i = 0;i < LIMITER_WINDOW_SIZE;i++)
sum += Limiter->Window[i];
for(i = 0;i < SamplesToDo;i++) for(i = 0;i < SamplesToDo;i++)
{ {
sum -= Limiter->Window[wpos]; sum -= Limiter->Window[wpos];
Limiter->Window[wpos] = Values[i]; Limiter->Window[wpos] = fastf2u(minf(Values[i]*65536.0f, LIMITER_VALUE_MAX));
sum += Values[i]; sum += Limiter->Window[wpos];
/* Clamp limiter range to 0dB...-80dB. */ /* Clamp limiter range to 0dB...-80dB. */
gain = 1.0f / clampf(sqrtf(sum / (ALfloat)LIMITER_WINDOW_SIZE), 1.0f, 1000.0f); gain = 1.0f / clampf(sqrtf((ALfloat)sum / ((ALfloat)LIMITER_WINDOW_SIZE*65536.0f)),
1.0f, 1000.0f);
if(lastgain >= gain) if(lastgain >= gain)
lastgain = maxf(lastgain*AttackRate, gain); lastgain = maxf(lastgain*AttackRate, gain);
else else
@ -1485,6 +1479,7 @@ static void ApplyLimiter(struct OutputLimiter *Limiter,
Limiter->Gain = lastgain; Limiter->Gain = lastgain;
Limiter->Pos = wpos; Limiter->Pos = wpos;
Limiter->SquaredSum = sum;
} }
if(do_limit) if(do_limit)
{ {

View File

@ -299,9 +299,13 @@ void DeinitVoice(ALvoice *voice);
#define LIMITER_WINDOW_SIZE (1<<7) /* 128 */ #define LIMITER_WINDOW_SIZE (1<<7) /* 128 */
#define LIMITER_WINDOW_MASK (LIMITER_WINDOW_SIZE-1) #define LIMITER_WINDOW_MASK (LIMITER_WINDOW_SIZE-1)
#define LIMITER_VALUE_MAX (UINT_MAX / LIMITER_WINDOW_SIZE)
struct OutputLimiter { struct OutputLimiter {
/* RMS detection window and the next write pos. */ /* RMS detection window, sum of values in the window, and the next write
alignas(16) ALfloat Window[LIMITER_WINDOW_SIZE]; * pos. Values are 16.16 fixed-point.
*/
ALuint Window[LIMITER_WINDOW_SIZE];
ALuint SquaredSum;
ALsizei Pos; ALsizei Pos;
/* In milliseconds. */ /* In milliseconds. */