diff options
author | Antti S. Lankila <alankila@gmail.com> | 2010-09-05 22:41:03 +0300 |
---|---|---|
committer | Antti S. Lankila <alankila@gmail.com> | 2010-09-06 01:08:06 +0300 |
commit | d3fa051bfa81d026f5a0fa7def38f9bf1bb07221 (patch) | |
tree | 9f48dfe54ca917c72dde506562c78d03613a6747 /libs | |
parent | e080c01b997a55e2fe85c01a3374a23165b7adb8 (diff) | |
download | frameworks_base-d3fa051bfa81d026f5a0fa7def38f9bf1bb07221.zip frameworks_base-d3fa051bfa81d026f5a0fa7def38f9bf1bb07221.tar.gz frameworks_base-d3fa051bfa81d026f5a0fa7def38f9bf1bb07221.tar.bz2 |
Add noise-shaped dithering support
Used rectangular probability distribution function before by accident,
depite calculating the high-passed triangular probability density function
I intended to use. This commit rectifies the mistake and also adds
noise-shaped error feedback similar to JACK.
Diffstat (limited to 'libs')
-rw-r--r-- | libs/audioflinger/AudioFlinger.cpp | 4 | ||||
-rw-r--r-- | libs/audioflinger/AudioFlinger.h | 2 | ||||
-rw-r--r-- | libs/audioflinger/AudioMixer.cpp | 65 | ||||
-rw-r--r-- | libs/audioflinger/AudioMixer.h | 15 |
4 files changed, 63 insertions, 23 deletions
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index 890692d..60bd19a 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -3399,7 +3399,7 @@ bool AudioFlinger::RecordThread::threadLoop() // ditherAndClamp() works as long as all buffers returned by mActiveTrack->getNextBuffer() // are 32 bit aligned which should be always true. if (mChannelCount == 2 && mReqChannelCount == 1) { - AudioMixer::ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut); + AudioMixer::ditherAndClamp(&dither, mRsmpOutBuffer, mRsmpOutBuffer, framesOut); // the resampler always outputs stereo samples: do post stereo to mono conversion int16_t *src = (int16_t *)mRsmpOutBuffer; int16_t *dst = buffer.i16; @@ -3408,7 +3408,7 @@ bool AudioFlinger::RecordThread::threadLoop() src += 2; } } else { - AudioMixer::ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut); + AudioMixer::ditherAndClamp(&dither, (int32_t *)buffer.raw, mRsmpOutBuffer, framesOut); } } diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index 05915e5..c39abba 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -43,6 +43,7 @@ #include "AudioBufferProvider.h" #include "AudioDSP.h" +#include "AudioMixer.h" namespace android { @@ -766,6 +767,7 @@ private: int mReqChannelCount; uint32_t mReqSampleRate; ssize_t mBytesRead; + dither_t dither; }; class RecordHandle : public android::BnAudioRecord { diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp index 1add9f4..2a4756e 100644 --- a/libs/audioflinger/AudioMixer.cpp +++ b/libs/audioflinger/AudioMixer.cpp @@ -38,10 +38,10 @@ static inline int16_t clamp16(int32_t sample) return sample; } -inline static int32_t prng() { - static int32_t seed = 1; - seed = (seed * 12345) + 1103515245; - return int32_t(seed & 0xfff); +inline static uint32_t prng() { + static uint32_t seed = 22222; + seed = (seed * 196314165) + 907633515; + return seed >> 20; } // ---------------------------------------------------------------------------- @@ -621,24 +621,51 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, t->in = in; } -void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c) +int32_t AudioMixer::lipshitz(int32_t* state, int32_t input) +{ +#define COEFF(x) int32_t(x * 4096.0f + 0.5f) + int32_t output = + COEFF(-2.033f) * input + + COEFF(+2.165f) * state[0] + + COEFF(-1.959f) * state[1] + + COEFF(+1.590f) * state[2] + + COEFF(-0.6149f) * state[3]; +#undef COEFF + + state[3] = state[2]; + state[2] = state[1]; + state[1] = state[0]; + state[0] = input; + + return output >> 12; +} + + +void AudioMixer::ditherAndClamp(dither_t* state, int32_t* out, int32_t const *sums, size_t c) { - int32_t oldDitherValue = prng(); for (size_t i=0 ; i<c ; i++) { int32_t l = *sums++; int32_t r = *sums++; - /* Apply dither to output. This is the high-passed triangular - * probability density function, discussed in "A Theory of - * Nonsubtractive Dither", by Robert A. Wannamaker et al. */ - int32_t ditherValue = prng(); - int32_t dithering = oldDitherValue - ditherValue; - oldDitherValue = ditherValue; - - int32_t nl = (l + ditherValue) >> 12; - int32_t nr = (r + ditherValue) >> 12; - l = clamp16(nl); - r = clamp16(nr); + /* Noise-shaped dither function. */ + + /* High-passed Triangular PDF according to + * "A Theory of Nonsubtractive Dither" by Robert Wannamaker et al. + * Other software seems to prefer (prng() + prng()) >> 1 as the + * random source, which they highpass, but that distribution is not + * triangular. */ + int32_t newDither = prng(); + int32_t dithering = newDither - state->oldDither; + state->oldDither = newDither; + + l += lipshitz(state->lipshitzL, state->errorL) + dithering; + r += lipshitz(state->lipshitzR, state->errorR) + dithering; + state->errorL = l & 0xfff; + state->errorR = r & 0xfff; + + l = clamp16(l >> 12); + r = clamp16(r >> 12); + *out++ = (r<<16) | (l & 0xFFFF); } } @@ -721,7 +748,7 @@ void AudioMixer::process__genericNoResampling(state_t* state, void* output, Audi } dsp.process(outTemp, BLOCKSIZE); - ditherAndClamp(out, outTemp, BLOCKSIZE); + ditherAndClamp(&state->dither, out, outTemp, BLOCKSIZE); out += BLOCKSIZE; numFrames -= BLOCKSIZE; } while (numFrames); @@ -778,7 +805,7 @@ void AudioMixer::process__genericResampling(state_t* state, void* output, AudioD } dsp.process(outTemp, numFrames); - ditherAndClamp(out, outTemp, numFrames); + ditherAndClamp(&state->dither, out, outTemp, numFrames); } // ---------------------------------------------------------------------------- diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h index 9aa92ed..e0438196 100644 --- a/libs/audioflinger/AudioMixer.h +++ b/libs/audioflinger/AudioMixer.h @@ -32,6 +32,13 @@ namespace android { #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) +struct dither_t { + /* Dithering variables */ + int32_t errorL, errorR; + int32_t lipshitzL[4], lipshitzR[4]; + int32_t oldDither; +}; + // ---------------------------------------------------------------------------- class AudioMixer @@ -86,7 +93,7 @@ public: uint32_t trackNames() const { return mTrackNames; } - static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c); + static void ditherAndClamp(dither_t* dither, int32_t* out, const int32_t* sums, size_t c); private: enum { @@ -162,7 +169,10 @@ private: int32_t *outputTemp; int32_t *resampleTemp; int32_t reserved[2]; - track_t tracks[32]; __attribute__((aligned(32))); + track_t tracks[32]; + + dither_t dither; + __attribute__((aligned(32))); }; int mActiveTrack; @@ -174,6 +184,7 @@ private: void invalidateState(uint32_t mask); + static int32_t lipshitz(int32_t* state, int32_t input); static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, AudioDSP& dsp); static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, AudioDSP& dsp); static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, AudioDSP& dsp); |