aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
Diffstat (limited to 'Alc')
-rw-r--r--Alc/ALc.c14
-rw-r--r--Alc/ALu.c60
2 files changed, 70 insertions, 4 deletions
diff --git a/Alc/ALc.c b/Alc/ALc.c
index 808580f3..537513f3 100644
--- a/Alc/ALc.c
+++ b/Alc/ALc.c
@@ -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);
diff --git a/Alc/ALu.c b/Alc/ALu.c
index 587952b2..01e6bbfa 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -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); \