diff options
Diffstat (limited to 'services/audioflinger')
-rw-r--r-- | services/audioflinger/AudioMixer.cpp | 8 | ||||
-rw-r--r-- | services/audioflinger/AudioResampler.cpp | 11 | ||||
-rw-r--r-- | services/audioflinger/AudioResampler.h | 2 | ||||
-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/FastMixer.cpp | 1 | ||||
-rw-r--r-- | services/audioflinger/RecordTracks.h | 2 | ||||
-rw-r--r-- | services/audioflinger/Threads.cpp | 12 | ||||
-rw-r--r-- | services/audioflinger/test-resample.cpp | 182 |
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; } |