summaryrefslogtreecommitdiffstats
path: root/services/audioflinger
diff options
context:
space:
mode:
Diffstat (limited to 'services/audioflinger')
-rw-r--r--services/audioflinger/AudioMixer.cpp8
-rw-r--r--services/audioflinger/AudioResampler.cpp11
-rw-r--r--services/audioflinger/AudioResampler.h2
-rw-r--r--services/audioflinger/AudioResamplerDyn.cpp251
-rw-r--r--services/audioflinger/AudioResamplerDyn.h82
-rw-r--r--services/audioflinger/AudioResamplerFirGen.h5
-rw-r--r--services/audioflinger/AudioResamplerFirProcess.h247
-rw-r--r--services/audioflinger/FastMixer.cpp1
-rw-r--r--services/audioflinger/RecordTracks.h2
-rw-r--r--services/audioflinger/Threads.cpp12
-rw-r--r--services/audioflinger/test-resample.cpp182
11 files changed, 469 insertions, 334 deletions
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index a1783fe..2d67efb 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -1181,7 +1181,7 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
}
switch (t1.mMixerFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
- memcpy_to_float_from_q19_12(reinterpret_cast<float *>(out), outTemp, BLOCKSIZE * 2);
+ memcpy_to_float_from_q4_27(reinterpret_cast<float *>(out), outTemp, BLOCKSIZE * 2);
out += BLOCKSIZE * 2; // output is 2 floats/frame.
break;
case AUDIO_FORMAT_PCM_16_BIT:
@@ -1274,7 +1274,7 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts)
}
switch (t1.mMixerFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
- memcpy_to_float_from_q19_12(reinterpret_cast<float*>(out), outTemp, numFrames*2);
+ memcpy_to_float_from_q4_27(reinterpret_cast<float*>(out), outTemp, numFrames*2);
break;
case AUDIO_FORMAT_PCM_16_BIT:
ditherAndClamp(out, outTemp, numFrames);
@@ -1330,8 +1330,8 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state,
in += 2;
int32_t l = mulRL(1, rl, vrl);
int32_t r = mulRL(0, rl, vrl);
- *fout++ = float_from_q19_12(l);
- *fout++ = float_from_q19_12(r);
+ *fout++ = float_from_q4_27(l);
+ *fout++ = float_from_q4_27(r);
// Note: In case of later int16_t sink output,
// conversion and clamping is done by memcpy_to_i16_from_float().
} while (--outFrames);
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index ca98f16..562c4ea 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -234,7 +234,16 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
case DYN_MED_QUALITY:
case DYN_HIGH_QUALITY:
ALOGV("Create dynamic Resampler = %d", quality);
- resampler = new AudioResamplerDyn(bitDepth, inChannelCount, sampleRate, quality);
+ if (bitDepth == 32) { /* bitDepth == 32 signals float precision */
+ resampler = new AudioResamplerDyn<float, float, float>(bitDepth, inChannelCount,
+ sampleRate, quality);
+ } else if (quality == DYN_HIGH_QUALITY) {
+ resampler = new AudioResamplerDyn<int32_t, int16_t, int32_t>(bitDepth, inChannelCount,
+ sampleRate, quality);
+ } else {
+ resampler = new AudioResamplerDyn<int16_t, int16_t, int32_t>(bitDepth, inChannelCount,
+ sampleRate, quality);
+ }
break;
}
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index 0592855..b84567e 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -63,7 +63,7 @@ public:
// A mono provider delivers a sequence of samples.
// A stereo provider delivers a sequence of interleaved pairs of samples.
// Multi-channel providers are not supported.
- // In either case, 'out' holds interleaved pairs of fixed-point signed Q19.12.
+ // In either case, 'out' holds interleaved pairs of fixed-point Q4.27.
// That is, for a mono provider, there is an implicit up-channeling.
// Since this method accumulates, the caller is responsible for clearing 'out' initially.
// FIXME assumes provider is always successful; it should return the actual frame count.
diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp
index 7e4ca0c..3abe8fd 100644
--- a/services/audioflinger/AudioResamplerDyn.cpp
+++ b/services/audioflinger/AudioResamplerDyn.cpp
@@ -25,6 +25,7 @@
#include <cutils/compiler.h>
#include <cutils/properties.h>
+#include <utils/Debug.h>
#include <utils/Log.h>
#include "AudioResamplerFirOps.h" // USE_NEON and USE_INLINE_ASSEMBLY defined here
@@ -38,9 +39,9 @@
namespace android {
// generate a unique resample type compile-time constant (constexpr)
-#define RESAMPLETYPE(CHANNELS, LOCKED, STRIDE, COEFTYPE) \
- ((((CHANNELS)-1)&1) | !!(LOCKED)<<1 | (COEFTYPE)<<2 \
- | ((STRIDE)==8 ? 1 : (STRIDE)==16 ? 2 : 0)<<3)
+#define RESAMPLETYPE(CHANNELS, LOCKED, STRIDE) \
+ ((((CHANNELS)-1)&1) | !!(LOCKED)<<1 \
+ | ((STRIDE)==8 ? 1 : (STRIDE)==16 ? 2 : 0)<<2)
/*
* InBuffer is a type agnostic input buffer.
@@ -58,42 +59,46 @@ namespace android {
* r = extra space for implementing the ring buffer
*/
-template<typename TI>
-AudioResamplerDyn::InBuffer<TI>::InBuffer()
- : mState(NULL), mImpulse(NULL), mRingFull(NULL), mStateSize(0) {
+template<typename TC, typename TI, typename TO>
+AudioResamplerDyn<TC, TI, TO>::InBuffer::InBuffer()
+ : mState(NULL), mImpulse(NULL), mRingFull(NULL), mStateCount(0)
+{
}
-template<typename TI>
-AudioResamplerDyn::InBuffer<TI>::~InBuffer() {
+template<typename TC, typename TI, typename TO>
+AudioResamplerDyn<TC, TI, TO>::InBuffer::~InBuffer()
+{
init();
}
-template<typename TI>
-void AudioResamplerDyn::InBuffer<TI>::init() {
+template<typename TC, typename TI, typename TO>
+void AudioResamplerDyn<TC, TI, TO>::InBuffer::init()
+{
free(mState);
mState = NULL;
mImpulse = NULL;
mRingFull = NULL;
- mStateSize = 0;
+ mStateCount = 0;
}
// resizes the state buffer to accommodate the appropriate filter length
-template<typename TI>
-void AudioResamplerDyn::InBuffer<TI>::resize(int CHANNELS, int halfNumCoefs) {
+template<typename TC, typename TI, typename TO>
+void AudioResamplerDyn<TC, TI, TO>::InBuffer::resize(int CHANNELS, int halfNumCoefs)
+{
// calculate desired state size
- int stateSize = halfNumCoefs * CHANNELS * 2
- * kStateSizeMultipleOfFilterLength;
+ int stateCount = halfNumCoefs * CHANNELS * 2 * kStateSizeMultipleOfFilterLength;
// check if buffer needs resizing
if (mState
- && stateSize == mStateSize
- && mRingFull-mState == mStateSize-halfNumCoefs*CHANNELS) {
+ && stateCount == mStateCount
+ && mRingFull-mState == mStateCount-halfNumCoefs*CHANNELS) {
return;
}
// create new buffer
- TI* state = (int16_t*)memalign(32, stateSize*sizeof(*state));
- memset(state, 0, stateSize*sizeof(*state));
+ TI* state;
+ (void)posix_memalign(reinterpret_cast<void**>(&state), 32, stateCount*sizeof(*state));
+ memset(state, 0, stateCount*sizeof(*state));
// attempt to preserve state
if (mState) {
@@ -105,8 +110,8 @@ void AudioResamplerDyn::InBuffer<TI>::resize(int CHANNELS, int halfNumCoefs) {
dst += mState-srcLo;
srcLo = mState;
}
- if (srcHi > mState + mStateSize) {
- srcHi = mState + mStateSize;
+ if (srcHi > mState + mStateCount) {
+ srcHi = mState + mStateCount;
}
memcpy(dst, srcLo, (srcHi - srcLo) * sizeof(*srcLo));
free(mState);
@@ -114,27 +119,29 @@ void AudioResamplerDyn::InBuffer<TI>::resize(int CHANNELS, int halfNumCoefs) {
// set class member vars
mState = state;
- mStateSize = stateSize;
- mImpulse = mState + halfNumCoefs*CHANNELS; // actually one sample greater than needed
- mRingFull = mState + mStateSize - halfNumCoefs*CHANNELS;
+ mStateCount = stateCount;
+ mImpulse = state + halfNumCoefs*CHANNELS; // actually one sample greater than needed
+ mRingFull = state + mStateCount - halfNumCoefs*CHANNELS;
}
// copy in the input data into the head (impulse+halfNumCoefs) of the buffer.
-template<typename TI>
+template<typename TC, typename TI, typename TO>
template<int CHANNELS>
-void AudioResamplerDyn::InBuffer<TI>::readAgain(TI*& impulse, const int halfNumCoefs,
- const TI* const in, const size_t inputIndex) {
- int16_t* head = impulse + halfNumCoefs*CHANNELS;
+void AudioResamplerDyn<TC, TI, TO>::InBuffer::readAgain(TI*& impulse, const int halfNumCoefs,
+ const TI* const in, const size_t inputIndex)
+{
+ TI* head = impulse + halfNumCoefs*CHANNELS;
for (size_t i=0 ; i<CHANNELS ; i++) {
head[i] = in[inputIndex*CHANNELS + i];
}
}
// advance the impulse pointer, and load in data into the head (impulse+halfNumCoefs)
-template<typename TI>
+template<typename TC, typename TI, typename TO>
template<int CHANNELS>
-void AudioResamplerDyn::InBuffer<TI>::readAdvance(TI*& impulse, const int halfNumCoefs,
- const TI* const in, const size_t inputIndex) {
+void AudioResamplerDyn<TC, TI, TO>::InBuffer::readAdvance(TI*& impulse, const int halfNumCoefs,
+ const TI* const in, const size_t inputIndex)
+{
impulse += CHANNELS;
if (CC_UNLIKELY(impulse >= mRingFull)) {
@@ -145,7 +152,8 @@ void AudioResamplerDyn::InBuffer<TI>::readAdvance(TI*& impulse, const int halfNu
readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex);
}
-void AudioResamplerDyn::Constants::set(
+template<typename TC, typename TI, typename TO>
+void AudioResamplerDyn<TC, TI, TO>::Constants::set(
int L, int halfNumCoefs, int inSampleRate, int outSampleRate)
{
int bits = 0;
@@ -158,10 +166,11 @@ void AudioResamplerDyn::Constants::set(
mHalfNumCoefs = halfNumCoefs;
}
-AudioResamplerDyn::AudioResamplerDyn(int bitDepth,
+template<typename TC, typename TI, typename TO>
+AudioResamplerDyn<TC, TI, TO>::AudioResamplerDyn(int bitDepth,
int inChannelCount, int32_t sampleRate, src_quality quality)
: AudioResampler(bitDepth, inChannelCount, sampleRate, quality),
- mResampleType(0), mFilterSampleRate(0), mFilterQuality(DEFAULT_QUALITY),
+ mResampleFunc(0), mFilterSampleRate(0), mFilterQuality(DEFAULT_QUALITY),
mCoefBuffer(NULL)
{
mVolumeSimd[0] = mVolumeSimd[1] = 0;
@@ -172,33 +181,48 @@ AudioResamplerDyn::AudioResamplerDyn(int bitDepth,
mConstants.set(128, 8, mSampleRate, mSampleRate); // TODO: set better
}
-AudioResamplerDyn::~AudioResamplerDyn() {
+template<typename TC, typename TI, typename TO>
+AudioResamplerDyn<TC, TI, TO>::~AudioResamplerDyn()
+{
free(mCoefBuffer);
}
-void AudioResamplerDyn::init() {
+template<typename TC, typename TI, typename TO>
+void AudioResamplerDyn<TC, TI, TO>::init()
+{
mFilterSampleRate = 0; // always trigger new filter generation
mInBuffer.init();
}
-void AudioResamplerDyn::setVolume(int16_t left, int16_t right) {
+template<typename TC, typename TI, typename TO>
+void AudioResamplerDyn<TC, TI, TO>::setVolume(int16_t left, int16_t right)
+{
AudioResampler::setVolume(left, right);
- mVolumeSimd[0] = static_cast<int32_t>(left)<<16;
- mVolumeSimd[1] = static_cast<int32_t>(right)<<16;
+ // volume is applied on the output type.
+ if (is_same<TO, float>::value || is_same<TO, double>::value) {
+ const TO scale = 1. / (1UL << 12);
+ mVolumeSimd[0] = static_cast<TO>(left) * scale;
+ mVolumeSimd[1] = static_cast<TO>(right) * scale;
+ } else {
+ mVolumeSimd[0] = static_cast<int32_t>(left) << 16;
+ mVolumeSimd[1] = static_cast<int32_t>(right) << 16;
+ }
}
-template <typename T> T max(T a, T b) {return a > b ? a : b;}
+template<typename T> T max(T a, T b) {return a > b ? a : b;}
-template <typename T> T absdiff(T a, T b) {return a > b ? a - b : b - a;}
+template<typename T> T absdiff(T a, T b) {return a > b ? a - b : b - a;}
-template<typename T>
-void AudioResamplerDyn::createKaiserFir(Constants &c, double stopBandAtten,
- int inSampleRate, int outSampleRate, double tbwCheat) {
- T* buf = reinterpret_cast<T*>(memalign(32, (c.mL+1)*c.mHalfNumCoefs*sizeof(T)));
+template<typename TC, typename TI, typename TO>
+void AudioResamplerDyn<TC, TI, TO>::createKaiserFir(Constants &c,
+ double stopBandAtten, int inSampleRate, int outSampleRate, double tbwCheat)
+{
+ TC* buf;
static const double atten = 0.9998; // to avoid ripple overflow
double fcr;
double tbw = firKaiserTbw(c.mHalfNumCoefs, stopBandAtten);
+ (void)posix_memalign(reinterpret_cast<void**>(&buf), 32, (c.mL+1)*c.mHalfNumCoefs*sizeof(TC));
if (inSampleRate < outSampleRate) { // upsample
fcr = max(0.5*tbwCheat - tbw/2, tbw/2);
} else { // downsample
@@ -206,7 +230,7 @@ void AudioResamplerDyn::createKaiserFir(Constants &c, double stopBandAtten,
}
// create and set filter
firKaiserGen(buf, c.mL, c.mHalfNumCoefs, stopBandAtten, fcr, atten);
- c.setBuf(buf);
+ c.mFirCoefs = buf;
if (mCoefBuffer) {
free(mCoefBuffer);
}
@@ -228,7 +252,8 @@ void AudioResamplerDyn::createKaiserFir(Constants &c, double stopBandAtten,
}
// recursive gcd. Using objdump, it appears the tail recursion is converted to a while loop.
-static int gcd(int n, int m) {
+static int gcd(int n, int m)
+{
if (m == 0) {
return n;
}
@@ -236,7 +261,8 @@ static int gcd(int n, int m) {
}
static bool isClose(int32_t newSampleRate, int32_t prevSampleRate,
- int32_t filterSampleRate, int32_t outSampleRate) {
+ int32_t filterSampleRate, int32_t outSampleRate)
+{
// different upsampling ratios do not need a filter change.
if (filterSampleRate != 0
@@ -253,7 +279,9 @@ static bool isClose(int32_t newSampleRate, int32_t prevSampleRate,
return pdiff < prevSampleRate>>4 && adiff < filterSampleRate>>3;
}
-void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) {
+template<typename TC, typename TI, typename TO>
+void AudioResamplerDyn<TC, TI, TO>::setSampleRate(int32_t inSampleRate)
+{
if (mInSampleRate == inSampleRate) {
return;
}
@@ -357,13 +385,8 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) {
// create the filter
mConstants.set(phases, halfLength, inSampleRate, mSampleRate);
- if (useS32) {
- createKaiserFir<int32_t>(mConstants, stopBandAtten,
- inSampleRate, mSampleRate, tbwCheat);
- } else {
- createKaiserFir<int16_t>(mConstants, stopBandAtten,
- inSampleRate, mSampleRate, tbwCheat);
- }
+ createKaiserFir(mConstants, stopBandAtten,
+ inSampleRate, mSampleRate, tbwCheat);
} // End Kaiser filter
// update phase and state based on the new filter.
@@ -385,7 +408,7 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) {
mPhaseFraction = mPhaseFraction >> c.mShift << c.mShift; // remove fractional phase
}
- mResampleType = RESAMPLETYPE(mChannelCount, locked, stride, !!useS32);
+ setResampler(RESAMPLETYPE(mChannelCount, locked, stride));
#ifdef DEBUG_RESAMPLER
printf("channels:%d %s stride:%d %s coef:%d shift:%d\n",
mChannelCount, locked ? "locked" : "interpolated",
@@ -393,78 +416,45 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) {
#endif
}
-void AudioResamplerDyn::resample(int32_t* out, size_t outFrameCount,
+template<typename TC, typename TI, typename TO>
+void AudioResamplerDyn<TC, TI, TO>::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
- // TODO:
- // 24 cases - this perhaps can be reduced later, as testing might take too long
- switch (mResampleType) {
+ (this->*mResampleFunc)(reinterpret_cast<TO*>(out), outFrameCount, provider);
+}
+template<typename TC, typename TI, typename TO>
+void AudioResamplerDyn<TC, TI, TO>::setResampler(unsigned resampleType)
+{
// stride 16 (falls back to stride 2 for machines that do not support NEON)
- case RESAMPLETYPE(1, true, 16, 0):
- return resample<1, true, 16>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(2, true, 16, 0):
- return resample<2, true, 16>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(1, false, 16, 0):
- return resample<1, false, 16>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(2, false, 16, 0):
- return resample<2, false, 16>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(1, true, 16, 1):
- return resample<1, true, 16>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
- case RESAMPLETYPE(2, true, 16, 1):
- return resample<2, true, 16>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
- case RESAMPLETYPE(1, false, 16, 1):
- return resample<1, false, 16>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
- case RESAMPLETYPE(2, false, 16, 1):
- return resample<2, false, 16>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
-#if 0
- // TODO: Remove these?
- // stride 8
- case RESAMPLETYPE(1, true, 8, 0):
- return resample<1, true, 8>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(2, true, 8, 0):
- return resample<2, true, 8>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(1, false, 8, 0):
- return resample<1, false, 8>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(2, false, 8, 0):
- return resample<2, false, 8>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(1, true, 8, 1):
- return resample<1, true, 8>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
- case RESAMPLETYPE(2, true, 8, 1):
- return resample<2, true, 8>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
- case RESAMPLETYPE(1, false, 8, 1):
- return resample<1, false, 8>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
- case RESAMPLETYPE(2, false, 8, 1):
- return resample<2, false, 8>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
- // stride 2 (can handle any filter length)
- case RESAMPLETYPE(1, true, 2, 0):
- return resample<1, true, 2>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(2, true, 2, 0):
- return resample<2, true, 2>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(1, false, 2, 0):
- return resample<1, false, 2>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(2, false, 2, 0):
- return resample<2, false, 2>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
- case RESAMPLETYPE(1, true, 2, 1):
- return resample<1, true, 2>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
- case RESAMPLETYPE(2, true, 2, 1):
- return resample<2, true, 2>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
- case RESAMPLETYPE(1, false, 2, 1):
- return resample<1, false, 2>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
- case RESAMPLETYPE(2, false, 2, 1):
- return resample<2, false, 2>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
-#endif
+ switch (resampleType) {
+ case RESAMPLETYPE(1, true, 16):
+ mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, true, 16>;
+ return;
+ case RESAMPLETYPE(2, true, 16):
+ mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, true, 16>;
+ return;
+ case RESAMPLETYPE(1, false, 16):
+ mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, false, 16>;
+ return;
+ case RESAMPLETYPE(2, false, 16):
+ mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, false, 16>;
+ return;
default:
- ; // error
+ LOG_ALWAYS_FATAL("Invalid resampler type: %u", resampleType);
+ mResampleFunc = NULL;
+ return;
}
}
-template<int CHANNELS, bool LOCKED, int STRIDE, typename TC>
-void AudioResamplerDyn::resample(int32_t* out, size_t outFrameCount,
- const TC* const coefs, AudioBufferProvider* provider)
+template<typename TC, typename TI, typename TO>
+template<int CHANNELS, bool LOCKED, int STRIDE>
+void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount,
+ AudioBufferProvider* provider)
{
const Constants& c(mConstants);
- int16_t* impulse = mInBuffer.getImpulse();
+ const TC* const coefs = mConstants.mFirCoefs;
+ TI* impulse = mInBuffer.getImpulse();
size_t inputIndex = mInputIndex;
uint32_t phaseFraction = mPhaseFraction;
const uint32_t phaseIncrement = mPhaseIncrement;
@@ -490,8 +480,9 @@ void AudioResamplerDyn::resample(int32_t* out, size_t outFrameCount,
goto resample_exit;
}
if (phaseFraction >= phaseWrapLimit) { // read in data
- mInBuffer.readAdvance<CHANNELS>(
- impulse, c.mHalfNumCoefs, mBuffer.i16, inputIndex);
+ mInBuffer.template readAdvance<CHANNELS>(
+ impulse, c.mHalfNumCoefs,
+ reinterpret_cast<TI*>(mBuffer.raw), inputIndex);
phaseFraction -= phaseWrapLimit;
while (phaseFraction >= phaseWrapLimit) {
inputIndex++;
@@ -500,20 +491,21 @@ void AudioResamplerDyn::resample(int32_t* out, size_t outFrameCount,
provider->releaseBuffer(&mBuffer);
break;
}
- mInBuffer.readAdvance<CHANNELS>(
- impulse, c.mHalfNumCoefs, mBuffer.i16, inputIndex);
+ mInBuffer.template readAdvance<CHANNELS>(
+ impulse, c.mHalfNumCoefs,
+ reinterpret_cast<TI*>(mBuffer.raw), inputIndex);
phaseFraction -= phaseWrapLimit;
}
}
}
- const int16_t* const in = mBuffer.i16;
+ const TI* const in = reinterpret_cast<const TI*>(mBuffer.raw);
const size_t frameCount = mBuffer.frameCount;
const int coefShift = c.mShift;
const int halfNumCoefs = c.mHalfNumCoefs;
- const int32_t* const volumeSimd = mVolumeSimd;
+ const TO* const volumeSimd = mVolumeSimd;
// reread the last input in.
- mInBuffer.readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex);
+ mInBuffer.template readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex);
// main processing loop
while (CC_LIKELY(outputIndex < outputSampleCount)) {
@@ -536,7 +528,7 @@ void AudioResamplerDyn::resample(int32_t* out, size_t outFrameCount,
if (inputIndex >= frameCount) {
goto done; // need a new buffer
}
- mInBuffer.readAdvance<CHANNELS>(impulse, halfNumCoefs, in, inputIndex);
+ mInBuffer.template readAdvance<CHANNELS>(impulse, halfNumCoefs, in, inputIndex);
phaseFraction -= phaseWrapLimit;
}
}
@@ -555,5 +547,10 @@ resample_exit:
mPhaseFraction = phaseFraction;
}
+/* instantiate templates used by AudioResampler::create */
+template class AudioResamplerDyn<float, float, float>;
+template class AudioResamplerDyn<int16_t, int16_t, int32_t>;
+template class AudioResamplerDyn<int32_t, int16_t, int32_t>;
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/services/audioflinger/AudioResamplerDyn.h b/services/audioflinger/AudioResamplerDyn.h
index df1fdbe..8c56319 100644
--- a/services/audioflinger/AudioResamplerDyn.h
+++ b/services/audioflinger/AudioResamplerDyn.h
@@ -25,10 +25,24 @@
namespace android {
+/* AudioResamplerDyn
+ *
+ * This class template is used for floating point and integer resamplers.
+ *
+ * Type variables:
+ * TC = filter coefficient type (one of int16_t, int32_t, or float)
+ * TI = input data type (one of int16_t or float)
+ * TO = output data type (one of int32_t or float)
+ *
+ * For integer input data types TI, the coefficient type TC is either int16_t or int32_t.
+ * For float input data types TI, the coefficient type TC is float.
+ */
+
+template<typename TC, typename TI, typename TO>
class AudioResamplerDyn: public AudioResampler {
public:
- AudioResamplerDyn(int bitDepth, int inChannelCount, int32_t sampleRate,
- src_quality quality);
+ AudioResamplerDyn(int bitDepth, int inChannelCount,
+ int32_t sampleRate, src_quality quality);
virtual ~AudioResamplerDyn();
@@ -46,46 +60,38 @@ private:
class Constants { // stores the filter constants.
public:
Constants() :
- mL(0), mShift(0), mHalfNumCoefs(0), mFirCoefsS16(NULL)
+ mL(0), mShift(0), mHalfNumCoefs(0), mFirCoefs(NULL)
{}
void set(int L, int halfNumCoefs,
int inSampleRate, int outSampleRate);
- inline void setBuf(int16_t* buf) {
- mFirCoefsS16 = buf;
- }
- inline void setBuf(int32_t* buf) {
- mFirCoefsS32 = buf;
- }
- int mL; // interpolation phases in the filter.
- int mShift; // right shift to get polyphase index
+ int mL; // interpolation phases in the filter.
+ int mShift; // right shift to get polyphase index
unsigned int mHalfNumCoefs; // filter half #coefs
- union { // polyphase filter bank
- const int16_t* mFirCoefsS16;
- const int32_t* mFirCoefsS32;
- };
+ const TC* mFirCoefs; // polyphase filter bank
};
- // Input buffer management for a given input type TI, now (int16_t)
- // Is agnostic of the actual type, can work with int32_t and float.
- template<typename TI>
- class InBuffer {
+ class InBuffer { // buffer management for input type TI
public:
InBuffer();
~InBuffer();
void init();
+
void resize(int CHANNELS, int halfNumCoefs);
// used for direct management of the mImpulse pointer
inline TI* getImpulse() {
return mImpulse;
}
+
inline void setImpulse(TI *impulse) {
mImpulse = impulse;
}
+
template<int CHANNELS>
inline void readAgain(TI*& impulse, const int halfNumCoefs,
const TI* const in, const size_t inputIndex);
+
template<int CHANNELS>
inline void readAdvance(TI*& impulse, const int halfNumCoefs,
const TI* const in, const size_t inputIndex);
@@ -94,31 +100,35 @@ private:
// tuning parameter guidelines: 2 <= multiple <= 8
static const int kStateSizeMultipleOfFilterLength = 4;
- TI* mState; // base pointer for the input buffer storage
- TI* mImpulse; // current location of the impulse response (centered)
- TI* mRingFull; // mState <= mImpulse < mRingFull
// in general, mRingFull = mState + mStateSize - halfNumCoefs*CHANNELS.
- size_t mStateSize; // in units of TI.
+ TI* mState; // base pointer for the input buffer storage
+ TI* mImpulse; // current location of the impulse response (centered)
+ TI* mRingFull; // mState <= mImpulse < mRingFull
+ size_t mStateCount; // size of state in units of TI.
};
- template<int CHANNELS, bool LOCKED, int STRIDE, typename TC>
- void resample(int32_t* out, size_t outFrameCount,
- const TC* const coefs, AudioBufferProvider* provider);
-
- template<typename T>
void createKaiserFir(Constants &c, double stopBandAtten,
int inSampleRate, int outSampleRate, double tbwCheat);
- InBuffer<int16_t> mInBuffer;
- Constants mConstants; // current set of coefficient parameters
- int32_t __attribute__ ((aligned (8))) mVolumeSimd[2];
- int32_t mResampleType; // contains the resample type.
- int32_t mFilterSampleRate; // designed filter sample rate.
- src_quality mFilterQuality; // designed filter quality.
- void* mCoefBuffer; // if a filter is created, this is not null
+ void setResampler(unsigned resampleType);
+
+ template<int CHANNELS, bool LOCKED, int STRIDE>
+ void resample(TO* out, size_t outFrameCount, AudioBufferProvider* provider);
+
+ // declare a pointer to member function for resample
+ typedef void (AudioResamplerDyn<TC, TI, TO>::*resample_ABP_t)(TO* out,
+ size_t outFrameCount, AudioBufferProvider* provider);
+
+ // data - the contiguous storage and layout of these is important.
+ InBuffer mInBuffer;
+ Constants mConstants; // current set of coefficient parameters
+ TO __attribute__ ((aligned (8))) mVolumeSimd[2]; // must be aligned or NEON may crash
+ resample_ABP_t mResampleFunc; // called function for resampling
+ int32_t mFilterSampleRate; // designed filter sample rate.
+ src_quality mFilterQuality; // designed filter quality.
+ void* mCoefBuffer; // if a filter is created, this is not null
};
-// ----------------------------------------------------------------------------
}; // namespace android
#endif /*ANDROID_AUDIO_RESAMPLER_DYN_H*/
diff --git a/services/audioflinger/AudioResamplerFirGen.h b/services/audioflinger/AudioResamplerFirGen.h
index 1f21c60..d024b2f 100644
--- a/services/audioflinger/AudioResamplerFirGen.h
+++ b/services/audioflinger/AudioResamplerFirGen.h
@@ -693,11 +693,12 @@ static inline void firKaiserGen(T* coef, int L, int halfNumCoef,
sg.advance();
}
- // (caution!) float version does not need rounding
if (is_same<T, int16_t>::value) { // int16_t needs noise shaping
*coef++ = static_cast<T>(toint(y, 1ULL<<(sizeof(T)*8-1), err));
- } else {
+ } else if (is_same<T, int32_t>::value) {
*coef++ = static_cast<T>(toint(y, 1ULL<<(sizeof(T)*8-1)));
+ } else { // assumed float or double
+ *coef++ = static_cast<T>(y);
}
}
}
diff --git a/services/audioflinger/AudioResamplerFirProcess.h b/services/audioflinger/AudioResamplerFirProcess.h
index 38e387c..76d2d66 100644
--- a/services/audioflinger/AudioResamplerFirProcess.h
+++ b/services/audioflinger/AudioResamplerFirProcess.h
@@ -21,47 +21,55 @@ namespace android {
// depends on AudioResamplerFirOps.h
-template<int CHANNELS, typename TC>
+/* variant for input type TI = int16_t input samples */
+template<typename TC>
static inline
-void mac(
- int32_t& l, int32_t& r,
- const TC coef,
- const int16_t* samples)
+void mac(int32_t& l, int32_t& r, TC coef, const int16_t* samples)
{
- if (CHANNELS == 2) {
- uint32_t rl = *reinterpret_cast<const uint32_t*>(samples);
- l = mulAddRL(1, rl, coef, l);
- r = mulAddRL(0, rl, coef, r);
- } else {
- r = l = mulAdd(samples[0], coef, l);
- }
+ uint32_t rl = *reinterpret_cast<const uint32_t*>(samples);
+ l = mulAddRL(1, rl, coef, l);
+ r = mulAddRL(0, rl, coef, r);
}
-template<int CHANNELS, typename TC>
+template<typename TC>
static inline
-void interpolate(
- int32_t& l, int32_t& r,
- const TC coef_0, const TC coef_1,
- const int16_t lerp, const int16_t* samples)
+void mac(int32_t& l, TC coef, const int16_t* samples)
{
- TC sinc;
+ l = mulAdd(samples[0], coef, l);
+}
- if (is_same<TC, int16_t>::value) {
- sinc = (lerp * ((coef_1-coef_0)<<1)>>16) + coef_0;
- } else {
- sinc = mulAdd(lerp, (coef_1-coef_0)<<1, coef_0);
- }
- if (CHANNELS == 2) {
- uint32_t rl = *reinterpret_cast<const uint32_t*>(samples);
- l = mulAddRL(1, rl, sinc, l);
- r = mulAddRL(0, rl, sinc, r);
- } else {
- r = l = mulAdd(samples[0], sinc, l);
- }
+/* variant for input type TI = float input samples */
+template<typename TC>
+static inline
+void mac(float& l, float& r, TC coef, const float* samples)
+{
+ l += *samples++ * coef;
+ r += *samples++ * coef;
+}
+
+template<typename TC>
+static inline
+void mac(float& l, TC coef, const float* samples)
+{
+ l += *samples++ * coef;
+}
+
+/* variant for output type TO = int32_t output samples */
+static inline
+int32_t volumeAdjust(int32_t value, int32_t volume)
+{
+ return 2 * mulRL(0, value, volume); // Note: only use top 16b
+}
+
+/* variant for output type TO = float output samples */
+static inline
+float volumeAdjust(float value, float volume)
+{
+ return value * volume;
}
/*
- * Calculates a single output sample (two stereo frames).
+ * Calculates a single output frame (two samples).
*
* This function computes both the positive half FIR dot product and
* the negative half FIR dot product, accumulates, and then applies the volume.
@@ -72,30 +80,43 @@ void interpolate(
* filter bank.
*/
-template <int CHANNELS, int STRIDE, typename TC>
+template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO>
static inline
-void ProcessL(int32_t* const out,
+void ProcessL(TO* const out,
int count,
const TC* coefsP,
const TC* coefsN,
- const int16_t* sP,
- const int16_t* sN,
- const int32_t* const volumeLR)
+ const TI* sP,
+ const TI* sN,
+ const TO* const volumeLR)
{
- int32_t l = 0;
- int32_t r = 0;
- do {
- mac<CHANNELS>(l, r, *coefsP++, sP);
- sP -= CHANNELS;
- mac<CHANNELS>(l, r, *coefsN++, sN);
- sN += CHANNELS;
- } while (--count > 0);
- out[0] += 2 * mulRL(0, l, volumeLR[0]); // Note: only use top 16b
- out[1] += 2 * mulRL(0, r, volumeLR[1]); // Note: only use top 16b
+ COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS >= 1 && CHANNELS <= 2)
+ if (CHANNELS == 2) {
+ TO l = 0;
+ TO r = 0;
+ do {
+ mac(l, r, *coefsP++, sP);
+ sP -= CHANNELS;
+ mac(l, r, *coefsN++, sN);
+ sN += CHANNELS;
+ } while (--count > 0);
+ out[0] += volumeAdjust(l, volumeLR[0]);
+ out[1] += volumeAdjust(r, volumeLR[1]);
+ } else { /* CHANNELS == 1 */
+ TO l = 0;
+ do {
+ mac(l, *coefsP++, sP);
+ sP -= CHANNELS;
+ mac(l, *coefsN++, sN);
+ sN += CHANNELS;
+ } while (--count > 0);
+ out[0] += volumeAdjust(l, volumeLR[0]);
+ out[1] += volumeAdjust(l, volumeLR[1]);
+ }
}
/*
- * Calculates a single output sample (two stereo frames) interpolating phase.
+ * Calculates a single output frame (two samples) interpolating phase.
*
* This function computes both the positive half FIR dot product and
* the negative half FIR dot product, accumulates, and then applies the volume.
@@ -106,47 +127,91 @@ void ProcessL(int32_t* const out,
* filter bank.
*/
-template <int CHANNELS, int STRIDE, typename TC>
+template<typename TC, typename T>
+void adjustLerp(T& lerpP __unused)
+{
+}
+
+template<int32_t, typename T>
+void adjustLerp(T& lerpP)
+{
+ lerpP >>= 16; // lerpP is 32bit for NEON int32_t, but always 16 bit for non-NEON path
+}
+
+template<typename TC, typename TINTERP>
+static inline
+TC interpolate(TC coef_0, TC coef_1, TINTERP lerp)
+{
+ return lerp * (coef_1 - coef_0) + coef_0;
+}
+
+template<int16_t, uint32_t>
+static inline
+int16_t interpolate(int16_t coef_0, int16_t coef_1, uint32_t lerp)
+{
+ return (static_cast<int16_t>(lerp) * ((coef_1-coef_0)<<1)>>16) + coef_0;
+}
+
+template<int32_t, uint32_t>
+static inline
+int32_t interpolate(int32_t coef_0, int32_t coef_1, uint32_t lerp)
+{
+ return mulAdd(static_cast<int16_t>(lerp), (coef_1-coef_0)<<1, coef_0);
+}
+
+template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO, typename TINTERP>
static inline
-void Process(int32_t* const out,
+void Process(TO* const out,
int count,
const TC* coefsP,
const TC* coefsN,
- const TC* coefsP1,
- const TC* coefsN1,
- const int16_t* sP,
- const int16_t* sN,
- uint32_t lerpP,
- const int32_t* const volumeLR)
+ const TC* coefsP1 __unused,
+ const TC* coefsN1 __unused,
+ const TI* sP,
+ const TI* sN,
+ TINTERP lerpP,
+ const TO* const volumeLR)
{
- (void) coefsP1; // suppress unused parameter warning
- (void) coefsN1;
- if (sizeof(*coefsP)==4) {
- lerpP >>= 16; // ensure lerpP is 16b
- }
- int32_t l = 0;
- int32_t r = 0;
- for (size_t i = 0; i < count; ++i) {
- interpolate<CHANNELS>(l, r, coefsP[0], coefsP[count], lerpP, sP);
- coefsP++;
- sP -= CHANNELS;
- interpolate<CHANNELS>(l, r, coefsN[count], coefsN[0], lerpP, sN);
- coefsN++;
- sN += CHANNELS;
+ COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS >= 1 && CHANNELS <= 2)
+ adjustLerp<TC, TINTERP>(lerpP); // coefficient type adjustment for interpolation
+
+ if (CHANNELS == 2) {
+ TO l = 0;
+ TO r = 0;
+ for (size_t i = 0; i < count; ++i) {
+ mac(l, r, interpolate(coefsP[0], coefsP[count], lerpP), sP);
+ coefsP++;
+ sP -= CHANNELS;
+ mac(l, r, interpolate(coefsN[count], coefsN[0], lerpP), sN);
+ coefsN++;
+ sN += CHANNELS;
+ }
+ out[0] += volumeAdjust(l, volumeLR[0]);
+ out[1] += volumeAdjust(r, volumeLR[1]);
+ } else { /* CHANNELS == 1 */
+ TO l = 0;
+ for (size_t i = 0; i < count; ++i) {
+ mac(l, interpolate(coefsP[0], coefsP[count], lerpP), sP);
+ coefsP++;
+ sP -= CHANNELS;
+ mac(l, interpolate(coefsN[count], coefsN[0], lerpP), sN);
+ coefsN++;
+ sN += CHANNELS;
+ }
+ out[0] += volumeAdjust(l, volumeLR[0]);
+ out[1] += volumeAdjust(l, volumeLR[1]);
}
- out[0] += 2 * mulRL(0, l, volumeLR[0]); // Note: only use top 16b
- out[1] += 2 * mulRL(0, r, volumeLR[1]); // Note: only use top 16b
}
/*
- * Calculates a single output sample (two stereo frames) from input sample pointer.
+ * Calculates a single output frame (two samples) from input sample pointer.
*
* This sets up the params for the accelerated Process() and ProcessL()
* functions to do the appropriate dot products.
*
- * @param out should point to the output buffer with at least enough space for 2 output frames.
+ * @param out should point to the output buffer with space for at least one output frame.
*
- * @param phase is the fractional distance between input samples for interpolation:
+ * @param phase is the fractional distance between input frames for interpolation:
* phase >= 0 && phase < phaseWrapLimit. It can be thought of as a rational fraction
* of phase/phaseWrapLimit.
*
@@ -195,14 +260,17 @@ void Process(int32_t* const out,
* lerpP = phase << sizeof(phase)*8 - coefShift
* >> (sizeof(phase)-sizeof(*coefs))*8 + 1;
*
+ * For floating point, lerpP is the fractional phase scaled to [0.0, 1.0):
+ *
+ * lerpP = (phase << 32 - coefShift) / (1 << 32); // floating point equivalent
*/
-template<int CHANNELS, bool LOCKED, int STRIDE, typename TC>
+template<int CHANNELS, bool LOCKED, int STRIDE, typename TC, typename TI, typename TO>
static inline
-void fir(int32_t* const out,
+void fir(TO* const out,
const uint32_t phase, const uint32_t phaseWrapLimit,
const int coefShift, const int halfNumCoefs, const TC* const coefs,
- const int16_t* const samples, const int32_t* const volumeLR)
+ const TI* const samples, const TO* const volumeLR)
{
// NOTE: be very careful when modifying the code here. register
// pressure is very high and a small change might cause the compiler
@@ -216,8 +284,8 @@ void fir(int32_t* const out,
uint32_t indexN = (phaseWrapLimit - phase) >> coefShift;
const TC* coefsP = coefs + indexP*halfNumCoefs;
const TC* coefsN = coefs + indexN*halfNumCoefs;
- const int16_t* sP = samples;
- const int16_t* sN = samples + CHANNELS;
+ const TI* sP = samples;
+ const TI* sN = samples + CHANNELS;
// dot product filter.
ProcessL<CHANNELS, STRIDE>(out,
@@ -231,8 +299,8 @@ void fir(int32_t* const out,
const TC* coefsN = coefs + indexN*halfNumCoefs;
const TC* coefsP1 = coefsP + halfNumCoefs;
const TC* coefsN1 = coefsN + halfNumCoefs;
- const int16_t* sP = samples;
- const int16_t* sN = samples + CHANNELS;
+ const TI* sP = samples;
+ const TI* sN = samples + CHANNELS;
// Interpolation fraction lerpP derived by shifting all the way up and down
// to clear the appropriate bits and align to the appropriate level
@@ -242,12 +310,21 @@ void fir(int32_t* const out,
//
// interpolated[P] = index[P]*lerpP + index[P+1]*(1-lerpP)
// interpolated[N] = index[N+1]*lerpP + index[N]*(1-lerpP)
- uint32_t lerpP = phase << (sizeof(phase)*8 - coefShift)
- >> ((sizeof(phase)-sizeof(*coefs))*8 + 1);
// on-the-fly interpolated dot product filter
- Process<CHANNELS, STRIDE>(out,
- halfNumCoefs, coefsP, coefsN, coefsP1, coefsN1, sP, sN, lerpP, volumeLR);
+ if (is_same<TC, float>::value || is_same<TC, double>::value) {
+ static const TC scale = 1. / (65536. * 65536.); // scale phase bits to [0.0, 1.0)
+ TC lerpP = TC(phase << (sizeof(phase)*8 - coefShift)) * scale;
+
+ Process<CHANNELS, STRIDE>(out,
+ halfNumCoefs, coefsP, coefsN, coefsP1, coefsN1, sP, sN, lerpP, volumeLR);
+ } else {
+ uint32_t lerpP = phase << (sizeof(phase)*8 - coefShift)
+ >> ((sizeof(phase)-sizeof(*coefs))*8 + 1);
+
+ Process<CHANNELS, STRIDE>(out,
+ halfNumCoefs, coefsP, coefsN, coefsP1, coefsN1, sP, sN, lerpP, volumeLR);
+ }
}
}
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index adb4aca..ca0d65e 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -236,6 +236,7 @@ bool FastMixer::threadLoop()
sampleRate = Format_sampleRate(format);
ALOG_ASSERT(Format_channelCount(format) == FCC_2);
}
+ dumpState->mSampleRate = sampleRate;
}
if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) {
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 3ec9889..6fc06d8 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -66,7 +66,7 @@ private:
// updated by RecordThread::readInputParameters_l()
AudioResampler *mResampler;
- // interleaved stereo pairs of fixed-point signed Q19.12
+ // interleaved stereo pairs of fixed-point Q4.27
int32_t *mRsmpOutBuffer;
// current allocated frame count for the above, which may be larger than needed
size_t mRsmpOutFrameCount;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 65e9eec..ae3dd8b 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3951,6 +3951,16 @@ bool AudioFlinger::DirectOutputThread::checkForNewParameters_l()
AudioParameter param = AudioParameter(keyValuePair);
int value;
+ if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
+ // forward device change to effects that have requested to be
+ // aware of attached audio device.
+ if (value != AUDIO_DEVICE_NONE) {
+ mOutDevice = value;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice_l(mOutDevice);
+ }
+ }
+ }
if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
// do not accept frame count changes if tracks are open as the track buffer
// size depends on frame count and correct behavior would not be garantied
@@ -4954,7 +4964,7 @@ reacquire_wakelock:
// ditherAndClamp() works as long as all buffers returned by
// activeTrack->getNextBuffer() are 32 bit aligned which should be always true.
if (activeTrack->mChannelCount == 1) {
- // temporarily type pun mRsmpOutBuffer from Q19.12 to int16_t
+ // temporarily type pun mRsmpOutBuffer from Q4.27 to int16_t
ditherAndClamp(activeTrack->mRsmpOutBuffer, activeTrack->mRsmpOutBuffer,
framesOut);
// the resampler always outputs stereo samples:
diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp
index 3ab3ba9..e14b4ae 100644
--- a/services/audioflinger/test-resample.cpp
+++ b/services/audioflinger/test-resample.cpp
@@ -24,23 +24,28 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
+#include <inttypes.h>
#include <time.h>
#include <math.h>
+#include <audio_utils/primitives.h>
#include <audio_utils/sndfile.h>
#include <utils/Vector.h>
using namespace android;
-bool gVerbose = false;
+static bool gVerbose = false;
static int usage(const char* name) {
- fprintf(stderr,"Usage: %s [-p] [-h] [-v] [-s] [-q {dq|lq|mq|hq|vhq|dlq|dmq|dhq}]"
- " [-i input-sample-rate] [-o output-sample-rate] [-O csv] [-P csv] [<input-file>]"
+ fprintf(stderr,"Usage: %s [-p] [-f] [-F] [-v] [-c channels]"
+ " [-q {dq|lq|mq|hq|vhq|dlq|dmq|dhq}]"
+ " [-i input-sample-rate] [-o output-sample-rate]"
+ " [-O csv] [-P csv] [<input-file>]"
" <output-file>\n", name);
fprintf(stderr," -p enable profiling\n");
- fprintf(stderr," -h create wav file\n");
+ fprintf(stderr," -f enable filter profiling\n");
+ fprintf(stderr," -F enable floating point -q {dlq|dmq|dhq} only");
fprintf(stderr," -v verbose : log buffer provider calls\n");
- fprintf(stderr," -s stereo (ignored if input file is specified)\n");
+ fprintf(stderr," -c # channels (1-2 for lq|mq|hq; 1-8 for dlq|dmq|dhq)\n");
fprintf(stderr," -q resampler quality\n");
fprintf(stderr," dq : default quality\n");
fprintf(stderr," lq : low quality\n");
@@ -97,11 +102,10 @@ int parseCSV(const char *string, Vector<int>& values)
}
int main(int argc, char* argv[]) {
-
const char* const progname = argv[0];
bool profileResample = false;
bool profileFilter = false;
- bool writeHeader = false;
+ bool useFloat = false;
int channels = 1;
int input_freq = 0;
int output_freq = 0;
@@ -110,7 +114,7 @@ int main(int argc, char* argv[]) {
Vector<int> Pvalues;
int ch;
- while ((ch = getopt(argc, argv, "pfhvsq:i:o:O:P:")) != -1) {
+ while ((ch = getopt(argc, argv, "pfFvc:q:i:o:O:P:")) != -1) {
switch (ch) {
case 'p':
profileResample = true;
@@ -118,14 +122,14 @@ int main(int argc, char* argv[]) {
case 'f':
profileFilter = true;
break;
- case 'h':
- writeHeader = true;
+ case 'F':
+ useFloat = true;
break;
case 'v':
gVerbose = true;
break;
- case 's':
- channels = 2;
+ case 'c':
+ channels = atoi(optarg);
break;
case 'q':
if (!strcmp(optarg, "dq"))
@@ -173,6 +177,17 @@ int main(int argc, char* argv[]) {
return -1;
}
}
+
+ if (channels < 1
+ || channels > (quality < AudioResampler::DYN_LOW_QUALITY ? 2 : 8)) {
+ fprintf(stderr, "invalid number of audio channels %d\n", channels);
+ return -1;
+ }
+ if (useFloat && quality < AudioResampler::DYN_LOW_QUALITY) {
+ fprintf(stderr, "float processing is only possible for dynamic resamplers\n");
+ return -1;
+ }
+
argc -= optind;
argv += optind;
@@ -219,27 +234,42 @@ int main(int argc, char* argv[]) {
double t = double(i) / input_freq;
double y = sin(M_PI * k * t * t);
int16_t yi = floor(y * 32767.0 + 0.5);
- for (size_t j=0 ; j<(size_t)channels ; j++) {
- in[i*channels + j] = yi / (1+j); // right ch. 1/2 left ch.
+ for (int j = 0; j < channels; j++) {
+ in[i*channels + j] = yi / (1 + j);
}
}
}
+ size_t input_framesize = channels * sizeof(int16_t);
+ size_t input_frames = input_size / input_framesize;
+
+ // For float processing, convert input int16_t to float array
+ if (useFloat) {
+ void *new_vaddr;
+
+ input_framesize = channels * sizeof(float);
+ input_size = input_frames * input_framesize;
+ new_vaddr = malloc(input_size);
+ memcpy_to_float_from_i16(reinterpret_cast<float*>(new_vaddr),
+ reinterpret_cast<int16_t*>(input_vaddr), input_frames * channels);
+ free(input_vaddr);
+ input_vaddr = new_vaddr;
+ }
// ----------------------------------------------------------
class Provider: public AudioBufferProvider {
- int16_t* const mAddr; // base address
+ const void* mAddr; // base address
const size_t mNumFrames; // total frames
- const int mChannels;
+ const size_t mFrameSize; // size of each frame in bytes
size_t mNextFrame; // index of next frame to provide
size_t mUnrel; // number of frames not yet released
const Vector<int> mPvalues; // number of frames provided per call
size_t mNextPidx; // index of next entry in mPvalues to use
public:
- Provider(const void* addr, size_t size, int channels, const Vector<int>& Pvalues)
- : mAddr((int16_t*) addr),
- mNumFrames(size / (channels*sizeof(int16_t))),
- mChannels(channels),
+ Provider(const void* addr, size_t frames, size_t frameSize, const Vector<int>& Pvalues)
+ : mAddr(addr),
+ mNumFrames(frames),
+ mFrameSize(frameSize),
mNextFrame(0), mUnrel(0), mPvalues(Pvalues), mNextPidx(0) {
}
virtual status_t getNextBuffer(Buffer* buffer,
@@ -251,7 +281,7 @@ int main(int argc, char* argv[]) {
}
if (!mPvalues.isEmpty()) {
size_t provided = mPvalues[mNextPidx++];
- printf("mPvalue[%d]=%u not %u\n", mNextPidx-1, provided, buffer->frameCount);
+ printf("mPvalue[%zu]=%zu not %zu\n", mNextPidx-1, provided, buffer->frameCount);
if (provided < buffer->frameCount) {
buffer->frameCount = provided;
}
@@ -260,47 +290,50 @@ int main(int argc, char* argv[]) {
}
}
if (gVerbose) {
- printf("getNextBuffer() requested %u frames out of %u frames available,"
- " and returned %u frames\n",
- requestedFrames, mNumFrames - mNextFrame, buffer->frameCount);
+ printf("getNextBuffer() requested %zu frames out of %zu frames available,"
+ " and returned %zu frames\n",
+ requestedFrames, (size_t) (mNumFrames - mNextFrame), buffer->frameCount);
}
mUnrel = buffer->frameCount;
if (buffer->frameCount > 0) {
- buffer->i16 = &mAddr[mChannels * mNextFrame];
+ buffer->raw = (char *)mAddr + mFrameSize * mNextFrame;
return NO_ERROR;
} else {
- buffer->i16 = NULL;
+ buffer->raw = NULL;
return NOT_ENOUGH_DATA;
}
}
virtual void releaseBuffer(Buffer* buffer) {
if (buffer->frameCount > mUnrel) {
- fprintf(stderr, "ERROR releaseBuffer() released %u frames but only %u available "
+ fprintf(stderr, "ERROR releaseBuffer() released %zu frames but only %zu available "
"to release\n", buffer->frameCount, mUnrel);
mNextFrame += mUnrel;
mUnrel = 0;
} else {
if (gVerbose) {
- printf("releaseBuffer() released %u frames out of %u frames available "
+ printf("releaseBuffer() released %zu frames out of %zu frames available "
"to release\n", buffer->frameCount, mUnrel);
}
mNextFrame += buffer->frameCount;
mUnrel -= buffer->frameCount;
}
buffer->frameCount = 0;
- buffer->i16 = NULL;
+ buffer->raw = NULL;
}
void reset() {
mNextFrame = 0;
}
- } provider(input_vaddr, input_size, channels, Pvalues);
+ } provider(input_vaddr, input_frames, input_framesize, Pvalues);
- size_t input_frames = input_size / (channels * sizeof(int16_t));
if (gVerbose) {
- printf("%u input frames\n", input_frames);
+ printf("%zu input frames\n", input_frames);
}
- size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq;
- output_size &= ~7; // always stereo, 32-bits
+
+ int bit_depth = useFloat ? 32 : 16;
+ int output_channels = channels > 2 ? channels : 2; // output is at least stereo samples
+ size_t output_framesize = output_channels * (useFloat ? sizeof(float) : sizeof(int32_t));
+ size_t output_frames = ((int64_t) input_frames * output_freq) / input_freq;
+ size_t output_size = output_frames * output_framesize;
if (profileFilter) {
// Check how fast sample rate changes are that require filter changes.
@@ -309,7 +342,7 @@ int main(int argc, char* argv[]) {
//
// On fast devices, filters should be generated between 0.1ms - 1ms.
// (single threaded).
- AudioResampler* resampler = AudioResampler::create(16, channels,
+ AudioResampler* resampler = AudioResampler::create(bit_depth, channels,
8000, quality);
int looplimit = 100;
timespec start, end;
@@ -347,13 +380,14 @@ int main(int argc, char* argv[]) {
}
void* output_vaddr = malloc(output_size);
- AudioResampler* resampler = AudioResampler::create(16, channels,
+ AudioResampler* resampler = AudioResampler::create(bit_depth, channels,
output_freq, quality);
- size_t out_frames = output_size/8;
+
/* set volume precision to 12 bits, so the volume scale is 1<<12.
- * This means the "integer" part fits in the Q19.12 precision
- * representation of output int32_t.
+ * The output int32_t is represented as Q4.27, with 4 bits of guard
+ * followed by the int16_t Q.15 portion, and then 12 trailing bits of
+ * additional precision.
*
* Generally 0 < volumePrecision <= 14 (due to the limits of
* int16_t values for Volume). volumePrecision cannot be 0 due
@@ -385,12 +419,12 @@ int main(int argc, char* argv[]) {
const int trials = 4;
const int looplimit = 4;
timespec start, end;
- int64_t time;
+ int64_t time = 0;
for (int n = 0; n < trials; ++n) {
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < looplimit; ++i) {
- resampler->resample((int*) output_vaddr, out_frames, &provider);
+ resampler->resample((int*) output_vaddr, output_frames, &provider);
provider.reset(); // during benchmarking reset only the provider
}
clock_gettime(CLOCK_MONOTONIC, &end);
@@ -402,27 +436,27 @@ int main(int argc, char* argv[]) {
}
}
// Mfrms/s is "Millions of output frames per second".
- printf("quality: %d channels: %d msec: %lld Mfrms/s: %.2lf\n",
- quality, channels, time/1000000, out_frames * looplimit / (time / 1e9) / 1e6);
+ printf("quality: %d channels: %d msec: %" PRId64 " Mfrms/s: %.2lf\n",
+ quality, channels, time/1000000, output_frames * looplimit / (time / 1e9) / 1e6);
resampler->reset();
}
memset(output_vaddr, 0, output_size);
if (gVerbose) {
- printf("resample() %u output frames\n", out_frames);
+ printf("resample() %zu output frames\n", output_frames);
}
if (Ovalues.isEmpty()) {
- Ovalues.push(out_frames);
+ Ovalues.push(output_frames);
}
- for (size_t i = 0, j = 0; i < out_frames; ) {
+ for (size_t i = 0, j = 0; i < output_frames; ) {
size_t thisFrames = Ovalues[j++];
if (j >= Ovalues.size()) {
j = 0;
}
- if (thisFrames == 0 || thisFrames > out_frames - i) {
- thisFrames = out_frames - i;
+ if (thisFrames == 0 || thisFrames > output_frames - i) {
+ thisFrames = output_frames - i;
}
- resampler->resample((int*) output_vaddr + 2*i, thisFrames, &provider);
+ resampler->resample((int*) output_vaddr + output_channels*i, thisFrames, &provider);
i += thisFrames;
}
if (gVerbose) {
@@ -435,17 +469,24 @@ int main(int argc, char* argv[]) {
delete resampler;
resampler = NULL;
- // mono takes left channel only
- // stereo right channel is half amplitude of stereo left channel (due to input creation)
+ // For float processing, convert output format from float to Q4.27,
+ // which is then converted to int16_t for final storage.
+ if (useFloat) {
+ memcpy_to_q4_27_from_float(reinterpret_cast<int32_t*>(output_vaddr),
+ reinterpret_cast<float*>(output_vaddr), output_frames * output_channels);
+ }
+
+ // mono takes left channel only (out of stereo output pair)
+ // stereo and multichannel preserve all channels.
int32_t* out = (int32_t*) output_vaddr;
- int16_t* convert = (int16_t*) malloc(out_frames * channels * sizeof(int16_t));
+ int16_t* convert = (int16_t*) malloc(output_frames * channels * sizeof(int16_t));
// round to half towards zero and saturate at int16 (non-dithered)
const int roundVal = (1<<(volumePrecision-1)) - 1; // volumePrecision > 0
- for (size_t i = 0; i < out_frames; i++) {
+ for (size_t i = 0; i < output_frames; i++) {
for (int j = 0; j < channels; j++) {
- int32_t s = out[i * 2 + j] + roundVal; // add offset here
+ int32_t s = out[i * output_channels + j] + roundVal; // add offset here
if (s < 0) {
s = (s + 1) >> volumePrecision; // round to 0
if (s < -32768) {
@@ -462,29 +503,18 @@ int main(int argc, char* argv[]) {
}
// write output to disk
- if (writeHeader) {
- SF_INFO info;
- info.frames = 0;
- info.samplerate = output_freq;
- info.channels = channels;
- info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
- SNDFILE *sf = sf_open(file_out, SFM_WRITE, &info);
- if (sf == NULL) {
- perror(file_out);
- return EXIT_FAILURE;
- }
- (void) sf_writef_short(sf, convert, out_frames);
- sf_close(sf);
- } else {
- int output_fd = open(file_out, O_WRONLY | O_CREAT | O_TRUNC,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (output_fd < 0) {
- perror(file_out);
- return EXIT_FAILURE;
- }
- write(output_fd, convert, out_frames * channels * sizeof(int16_t));
- close(output_fd);
+ SF_INFO info;
+ info.frames = 0;
+ info.samplerate = output_freq;
+ info.channels = channels;
+ info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+ SNDFILE *sf = sf_open(file_out, SFM_WRITE, &info);
+ if (sf == NULL) {
+ perror(file_out);
+ return EXIT_FAILURE;
}
+ (void) sf_writef_short(sf, convert, output_frames);
+ sf_close(sf);
return EXIT_SUCCESS;
}