From 37066353a830f08771919d7dc170e67c02dd2e8c Mon Sep 17 00:00:00 2001 From: "Antti S. Lankila" Date: Tue, 3 Aug 2010 18:16:49 +0300 Subject: Fix some shortcomings of the audio level estimation Use crude approximation of the equal loudness curves (a single 2nd order bandpass at 1 kHz). Because we must be stateless, the filter is always reset first, which somewhat damages the behavior. Limit adjustment range to 30 dB to avoid problems when sound goes very quiet. Make ramp up fast and ramp down slow. This gives reasonable response to transients between periods of silence. --- libs/audioflinger/AudioDSP.cpp | 81 +++++++++++++++++++++++++++------------- libs/audioflinger/AudioDSP.h | 7 +++- libs/audioflinger/AudioMixer.cpp | 15 +++++--- 3 files changed, 71 insertions(+), 32 deletions(-) (limited to 'libs') diff --git a/libs/audioflinger/AudioDSP.cpp b/libs/audioflinger/AudioDSP.cpp index a0ae416..24f99fc 100644 --- a/libs/audioflinger/AudioDSP.cpp +++ b/libs/audioflinger/AudioDSP.cpp @@ -107,6 +107,13 @@ void Biquad::setRC(float center_frequency, float sampling_frequency) setCoefficients(1, a1, 0, b0, 0, 0); } +void Biquad::reset() +{ + mY0 = 0; + state.i32.mX = 0; + state.i32.mY = 0; +} + /* * Peaking equalizer, low shelf and high shelf are taken from * the good old Audio EQ Cookbook by Robert Bristow-Johnson. @@ -159,6 +166,21 @@ void Biquad::setHighShelf(float center_frequency, float sampling_frequency, floa setCoefficients(a0, a1, a2, b0, b1, b2); } +void Biquad::setBandPass(float center_frequency, float sampling_frequency, float resonance) +{ + float w0 = 2 * (float) M_PI * center_frequency / sampling_frequency; + float alpha = sinf(w0) / (2*resonance); + + float b0 = sinf(w0)/2; + float b1 = 0; + float b2 = -sinf(w0)/2; + float a0 = 1 + alpha; + float a1 = -2*cosf(w0); + float a2 = 1 - alpha; + + setCoefficients(a0, a1, a2, b0, b1, b2); +} + /* returns output scaled by fixedPoint factor */ inline int32_t Biquad::process(int16_t x0) { @@ -228,6 +250,7 @@ EffectCompression::~EffectCompression() void EffectCompression::configure(const float samplingFrequency) { Effect::configure(samplingFrequency); + mWeighter.setBandPass(1000, samplingFrequency, sqrtf(2)/2); } void EffectCompression::setRatio(float compressionRatio) @@ -241,34 +264,19 @@ void EffectCompression::process(int32_t *inout, int32_t frames) int32_t EffectCompression::estimateLevel(const int16_t *audioData, int32_t frames, int32_t samplesPerFrame) { - /* FIXME: find a cheap approximation of equal loudness curve and apply - * it here. Something like replaygain's, but not so darn expensive. */ + mWeighter.reset(); uint32_t power = 0; - int32_t samples = frames * samplesPerFrame; - for (int32_t i = 0; i < samples; i ++) { - int16_t tmp = *audioData ++; - power += tmp * tmp >> 16; - } - - /* FIXME: code below should be ported to integer. */ - float signalPower = (65536.0f*power) / samples / 32768.0f / 32768.0f; - /* -50 .. 0 dB. - * We don't go to -100 dB because of the >> 16 losing bits above. */ - float signalPowerDb = logf(signalPower + 1e-5f) / logf(10) * 10; - /* target 83 dB SPL */ - signalPowerDb += 96 - 83; - - /* now we have an estimate of the signal power in range from - * -96 dB to 0 dB. Now we estimate what level we want. */ - float desiredLevelDb = signalPowerDb / mCompressionRatio; + for (int32_t i = 0; i < frames; i ++) { + int16_t tmp = *audioData; + audioData += samplesPerFrame; - /* turn back to multiplier */ - float correctionDb = desiredLevelDb - signalPowerDb; + int32_t out = mWeighter.process(tmp) >> 12; + power += out * out >> 16; + } - return int32_t(65536 * powf(10, correctionDb / 20)); + return power; } - EffectTone::EffectTone() { for (int32_t i = 0; i < 5; i ++) { @@ -480,9 +488,32 @@ int32_t AudioDSP::estimateLevel(const int16_t *input, int32_t frames, int32_t sa { if (! mCompressionEnable) { return 65536; - } else { - return mCompression.estimateLevel(input, frames, samplesPerFrame); } + + /* Analyze both channels separately, pick the maximum power measured. */ + int maximumPower = 0; + for (int channel = 0; channel < samplesPerFrame; channel ++) { + int candidatePower = mCompression.estimateLevel(input + channel, frames, samplesPerFrame); + if (candidatePower > maximumPower) { + maximumPower = candidatePower; + } + } + + /* FIXME: code below should be ported to integer. */ + float signalPower = (65536.0f*maximumPower) / frames / 32768.0f / 32768.0f; + /* -30 .. 0 dB. */ + float signalPowerDb = logf(signalPower + 1e-3f) / logf(10) * 10; + /* target 83 dB SPL, and the weighter function peaks at -3 dB */ + signalPowerDb += 96 - 83 + 3; + + /* now we have an estimate of the signal power in range from + * -96 dB to 0 dB. Now we estimate what level we want. */ + float desiredLevelDb = signalPowerDb / mCompression.mCompressionRatio; + + /* turn back to multiplier */ + float correctionDb = desiredLevelDb - signalPowerDb; + + return int32_t(65536 * powf(10, correctionDb / 20)); } /* input is 28-bit interleaved stereo in integer format */ diff --git a/libs/audioflinger/AudioDSP.h b/libs/audioflinger/AudioDSP.h index e89b0ea..7ac1756 100644 --- a/libs/audioflinger/AudioDSP.h +++ b/libs/audioflinger/AudioDSP.h @@ -53,8 +53,10 @@ class Biquad { Biquad(); void setRC(float cf, float sf); void setPeakingEqualizer(float cf, float sf, float gain, float bw); + void setBandPass(float cf, float sf, float resonance); void setLowShelf(float cf, float sf, float gain, float slope); void setHighShelf(float cf, float sf, float gain, float slope); + void reset(); int32_t process(int16_t x0); }; @@ -70,9 +72,12 @@ class Effect { }; class EffectCompression : public Effect { - float mCompressionRatio; + private: + Biquad mWeighter; public: + float mCompressionRatio; + EffectCompression(); ~EffectCompression(); void configure(const float samplingFrequency); diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp index a25a4c0..d53ffc0 100644 --- a/libs/audioflinger/AudioMixer.cpp +++ b/libs/audioflinger/AudioMixer.cpp @@ -249,18 +249,21 @@ void AudioMixer::track_t::adjustVolumeRamp(AudioDSP& dsp) int32_t d = desiredVolume - prevVolume[i]; /* limit change rate to smooth the compressor. */ - int32_t volChangeLimit = prevVolume[i] >> 12; - if (volChangeLimit == 0) { - volChangeLimit = 1; - } + int32_t volChangeLimit = (prevVolume[i] >> 11); + volChangeLimit += 1; int32_t volInc = d / int32_t(frameCount); if (volInc > volChangeLimit) { volInc = volChangeLimit; } - if (volInc < -volChangeLimit) { - volInc = -volChangeLimit; + + /* Make ramps down slow, but ramps up fast. */ + volChangeLimit >>= 3; + volChangeLimit -= 1; + if (volInc < -(volChangeLimit)) { + volInc = -(volChangeLimit); } + volumeInc[i] = volInc; } } -- cgit v1.1