summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
authorAntti S. Lankila <alankila@gmail.com>2010-09-05 22:41:03 +0300
committerAntti S. Lankila <alankila@gmail.com>2010-09-06 01:08:06 +0300
commitd3fa051bfa81d026f5a0fa7def38f9bf1bb07221 (patch)
tree9f48dfe54ca917c72dde506562c78d03613a6747 /libs
parente080c01b997a55e2fe85c01a3374a23165b7adb8 (diff)
downloadframeworks_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.cpp4
-rw-r--r--libs/audioflinger/AudioFlinger.h2
-rw-r--r--libs/audioflinger/AudioMixer.cpp65
-rw-r--r--libs/audioflinger/AudioMixer.h15
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);