diff options
Diffstat (limited to 'Alc')
-rw-r--r-- | Alc/ALc.c | 14 | ||||
-rw-r--r-- | Alc/ALu.c | 60 |
2 files changed, 70 insertions, 4 deletions
@@ -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); \ |