diff options
Diffstat (limited to 'services')
-rw-r--r-- | services/audioflinger/AudioResampler.cpp | 11 | ||||
-rw-r--r-- | services/audioflinger/AudioResamplerDyn.cpp | 251 | ||||
-rw-r--r-- | services/audioflinger/AudioResamplerDyn.h | 82 | ||||
-rw-r--r-- | services/audioflinger/AudioResamplerFirGen.h | 5 | ||||
-rw-r--r-- | services/audioflinger/AudioResamplerFirProcess.h | 247 | ||||
-rw-r--r-- | services/audioflinger/test-resample.cpp | 92 |
6 files changed, 410 insertions, 278 deletions
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/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 fac3001..8833758 100644 --- a/services/audioflinger/AudioResamplerFirGen.h +++ b/services/audioflinger/AudioResamplerFirGen.h @@ -669,11 +669,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/test-resample.cpp b/services/audioflinger/test-resample.cpp index 7b7cdac..d1de95d 100644 --- a/services/audioflinger/test-resample.cpp +++ b/services/audioflinger/test-resample.cpp @@ -24,20 +24,25 @@ #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] [-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>]" " <output-file>\n", name); fprintf(stderr," -p enable profiling\n"); + fprintf(stderr," -f enable filter profiling\n"); + fprintf(stderr," -F enable floating point -q {dlq|dmq|dhq} only"); fprintf(stderr," -h create wav file\n"); fprintf(stderr," -v verbose : log buffer provider calls\n"); fprintf(stderr," -s stereo (ignored if input file is specified)\n"); @@ -102,6 +107,7 @@ int main(int argc, char* argv[]) { 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 +116,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, "pfFhvsq:i:o:O:P:")) != -1) { switch (ch) { case 'p': profileResample = true; @@ -118,6 +124,9 @@ int main(int argc, char* argv[]) { case 'f': profileFilter = true; break; + case 'F': + useFloat = true; + break; case 'h': writeHeader = true; break; @@ -173,6 +182,12 @@ int main(int argc, char* argv[]) { 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; @@ -224,22 +239,37 @@ int main(int argc, char* argv[]) { } } } + size_t frame_size = channels * sizeof(int16_t); + size_t input_frames = input_size / frame_size; + + // For float processing, convert input int16_t to float array + if (useFloat) { + void *new_vaddr; + + frame_size = channels * sizeof(float); + input_size = input_frames * frame_size; + 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,45 +290,46 @@ 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, frame_size, 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); } + + int bit_depth = useFloat ? 32 : 16; size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq; output_size &= ~7; // always stereo, 32-bits @@ -309,7 +340,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,7 +378,7 @@ 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; @@ -386,7 +417,7 @@ 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); @@ -403,14 +434,14 @@ 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", + printf("quality: %d channels: %d msec: %" PRId64 " Mfrms/s: %.2lf\n", quality, channels, time/1000000, out_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", out_frames); } if (Ovalues.isEmpty()) { Ovalues.push(out_frames); @@ -436,6 +467,13 @@ int main(int argc, char* argv[]) { delete resampler; resampler = NULL; + // 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), out_frames * 2); // stereo samples + } + // mono takes left channel only // stereo right channel is half amplitude of stereo left channel (due to input creation) int32_t* out = (int32_t*) output_vaddr; |