186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung/* 286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * Copyright (C) 2013 The Android Open Source Project 386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * 486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * Licensed under the Apache License, Version 2.0 (the "License"); 586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * you may not use this file except in compliance with the License. 686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * You may obtain a copy of the License at 786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * 886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * http://www.apache.org/licenses/LICENSE-2.0 986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * 1086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * Unless required by applicable law or agreed to in writing, software 1186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * distributed under the License is distributed on an "AS IS" BASIS, 1286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * See the License for the specific language governing permissions and 1486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * limitations under the License. 1586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung */ 1686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 1786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#define LOG_TAG "AudioResamplerDyn" 1886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung//#define LOG_NDEBUG 0 1986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 2086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include <malloc.h> 2186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include <string.h> 2286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include <stdlib.h> 2386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include <dlfcn.h> 2486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include <math.h> 2586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 2686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include <cutils/compiler.h> 2786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include <cutils/properties.h> 28d549139155b20d7cbf6a4326133e06def465ef54Andy Hung#include <utils/Debug.h> 2986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include <utils/Log.h> 305e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020Andy Hung#include <audio_utils/primitives.h> 3186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 3286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include "AudioResamplerFirOps.h" // USE_NEON and USE_INLINE_ASSEMBLY defined here 3386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include "AudioResamplerFirProcess.h" 3486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include "AudioResamplerFirProcessNeon.h" 3586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include "AudioResamplerFirGen.h" // requires math.h 3686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#include "AudioResamplerDyn.h" 3786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 3886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung//#define DEBUG_RESAMPLER 3986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 4086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hungnamespace android { 4186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 4286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung/* 4386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * InBuffer is a type agnostic input buffer. 4486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * 4586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * Layout of the state buffer for halfNumCoefs=8. 4686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * 4786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * [rrrrrrppppppppnnnnnnnnrrrrrrrrrrrrrrrrrrr.... rrrrrrr] 4886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * S I R 4986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * 5086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * S = mState 5186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * I = mImpulse 5286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * R = mRingFull 5386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * p = past samples, convoluted with the (p)ositive side of sinc() 5486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * n = future samples, convoluted with the (n)egative side of sinc() 5586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * r = extra space for implementing the ring buffer 5686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung */ 5786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 58771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 59771386e6e6e79697e2d839ef0f25a242946ba1e5Andy HungAudioResamplerDyn<TC, TI, TO>::InBuffer::InBuffer() 60771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung : mState(NULL), mImpulse(NULL), mRingFull(NULL), mStateCount(0) 61771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 6286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 6386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 64771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 65771386e6e6e79697e2d839ef0f25a242946ba1e5Andy HungAudioResamplerDyn<TC, TI, TO>::InBuffer::~InBuffer() 66771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 6786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung init(); 6886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 6986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 70771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 71771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungvoid AudioResamplerDyn<TC, TI, TO>::InBuffer::init() 72771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 7386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung free(mState); 7486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mState = NULL; 7586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mImpulse = NULL; 7686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mRingFull = NULL; 77771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung mStateCount = 0; 7886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 7986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 8086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung// resizes the state buffer to accommodate the appropriate filter length 81771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 82771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungvoid AudioResamplerDyn<TC, TI, TO>::InBuffer::resize(int CHANNELS, int halfNumCoefs) 83771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 8486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // calculate desired state size 85a4daf0b4f934b800a49f199fb8c09409391c8fc0Glenn Kasten size_t stateCount = halfNumCoefs * CHANNELS * 2 * kStateSizeMultipleOfFilterLength; 8686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 8786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // check if buffer needs resizing 8886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (mState 89771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung && stateCount == mStateCount 90a4daf0b4f934b800a49f199fb8c09409391c8fc0Glenn Kasten && mRingFull-mState == (ssize_t) (mStateCount-halfNumCoefs*CHANNELS)) { 9186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung return; 9286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 9386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 9486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // create new buffer 95a4daf0b4f934b800a49f199fb8c09409391c8fc0Glenn Kasten TI* state = NULL; 96771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung (void)posix_memalign(reinterpret_cast<void**>(&state), 32, stateCount*sizeof(*state)); 97771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung memset(state, 0, stateCount*sizeof(*state)); 9886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 9986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // attempt to preserve state 10086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (mState) { 10186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung TI* srcLo = mImpulse - halfNumCoefs*CHANNELS; 10286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung TI* srcHi = mImpulse + halfNumCoefs*CHANNELS; 10386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung TI* dst = state; 10486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 10586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (srcLo < mState) { 10686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung dst += mState-srcLo; 10786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung srcLo = mState; 10886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 109771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung if (srcHi > mState + mStateCount) { 110771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung srcHi = mState + mStateCount; 11186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 11286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung memcpy(dst, srcLo, (srcHi - srcLo) * sizeof(*srcLo)); 11386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung free(mState); 11486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 11586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 11686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // set class member vars 11786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mState = state; 118771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung mStateCount = stateCount; 119771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung mImpulse = state + halfNumCoefs*CHANNELS; // actually one sample greater than needed 120771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung mRingFull = state + mStateCount - halfNumCoefs*CHANNELS; 12186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 12286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 12386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung// copy in the input data into the head (impulse+halfNumCoefs) of the buffer. 124771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 12586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hungtemplate<int CHANNELS> 126771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungvoid AudioResamplerDyn<TC, TI, TO>::InBuffer::readAgain(TI*& impulse, const int halfNumCoefs, 127771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung const TI* const in, const size_t inputIndex) 128771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 129771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung TI* head = impulse + halfNumCoefs*CHANNELS; 13086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung for (size_t i=0 ; i<CHANNELS ; i++) { 13186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung head[i] = in[inputIndex*CHANNELS + i]; 13286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 13386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 13486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 13586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung// advance the impulse pointer, and load in data into the head (impulse+halfNumCoefs) 136771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 13786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hungtemplate<int CHANNELS> 138771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungvoid AudioResamplerDyn<TC, TI, TO>::InBuffer::readAdvance(TI*& impulse, const int halfNumCoefs, 139771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung const TI* const in, const size_t inputIndex) 140771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 14186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung impulse += CHANNELS; 14286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 14386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (CC_UNLIKELY(impulse >= mRingFull)) { 14486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung const size_t shiftDown = mRingFull - mState - halfNumCoefs*CHANNELS; 14586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung memcpy(mState, mState+shiftDown, halfNumCoefs*CHANNELS*2*sizeof(TI)); 14686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung impulse -= shiftDown; 14786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 14886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); 14986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 15086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 151771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 152771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungvoid AudioResamplerDyn<TC, TI, TO>::Constants::set( 15386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung int L, int halfNumCoefs, int inSampleRate, int outSampleRate) 15486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung{ 15586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung int bits = 0; 15686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung int lscale = inSampleRate/outSampleRate < 2 ? L - 1 : 15786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung static_cast<int>(static_cast<uint64_t>(L)*inSampleRate/outSampleRate); 15886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung for (int i=lscale; i; ++bits, i>>=1) 15986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung ; 16086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mL = L; 16186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mShift = kNumPhaseBits - bits; 16286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mHalfNumCoefs = halfNumCoefs; 16386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 16486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 165771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 1663348e36c51e91e78020bcc6578eda83d97c31becAndy HungAudioResamplerDyn<TC, TI, TO>::AudioResamplerDyn( 16786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung int inChannelCount, int32_t sampleRate, src_quality quality) 1683348e36c51e91e78020bcc6578eda83d97c31becAndy Hung : AudioResampler(inChannelCount, sampleRate, quality), 169771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung mResampleFunc(0), mFilterSampleRate(0), mFilterQuality(DEFAULT_QUALITY), 1706582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung mCoefBuffer(NULL) 17186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung{ 17286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mVolumeSimd[0] = mVolumeSimd[1] = 0; 1731af34085e18c4d5ab297232f167a71e89ff7f65dAndy Hung // The AudioResampler base class assumes we are always ready for 1:1 resampling. 1741af34085e18c4d5ab297232f167a71e89ff7f65dAndy Hung // We reset mInSampleRate to 0, so setSampleRate() will calculate filters for 1751af34085e18c4d5ab297232f167a71e89ff7f65dAndy Hung // setSampleRate() for 1:1. (May be removed if precalculated filters are used.) 1761af34085e18c4d5ab297232f167a71e89ff7f65dAndy Hung mInSampleRate = 0; 17786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mConstants.set(128, 8, mSampleRate, mSampleRate); // TODO: set better 17886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 17986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 180771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 181771386e6e6e79697e2d839ef0f25a242946ba1e5Andy HungAudioResamplerDyn<TC, TI, TO>::~AudioResamplerDyn() 182771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 18386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung free(mCoefBuffer); 18486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 18586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 186771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 187771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungvoid AudioResamplerDyn<TC, TI, TO>::init() 188771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 18986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mFilterSampleRate = 0; // always trigger new filter generation 19086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mInBuffer.init(); 19186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 19286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 193771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 1945e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020Andy Hungvoid AudioResamplerDyn<TC, TI, TO>::setVolume(float left, float right) 195771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 19686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung AudioResampler::setVolume(left, right); 197771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung if (is_same<TO, float>::value || is_same<TO, double>::value) { 1985e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020Andy Hung mVolumeSimd[0] = static_cast<TO>(left); 1995e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020Andy Hung mVolumeSimd[1] = static_cast<TO>(right); 2005e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020Andy Hung } else { // integer requires scaling to U4_28 (rounding down) 2015e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020Andy Hung // integer volumes are clamped to 0 to UNITY_GAIN so there 2025e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020Andy Hung // are no issues with signed overflow. 2035e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020Andy Hung mVolumeSimd[0] = u4_28_from_float(clampFloatVol(left)); 2045e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020Andy Hung mVolumeSimd[1] = u4_28_from_float(clampFloatVol(right)); 205771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung } 20686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 20786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 208771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename T> T max(T a, T b) {return a > b ? a : b;} 20986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 210771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename T> T absdiff(T a, T b) {return a > b ? a - b : b - a;} 21186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 212771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 213771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungvoid AudioResamplerDyn<TC, TI, TO>::createKaiserFir(Constants &c, 214771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung double stopBandAtten, int inSampleRate, int outSampleRate, double tbwCheat) 215771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 216a4daf0b4f934b800a49f199fb8c09409391c8fc0Glenn Kasten TC* buf = NULL; 21786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung static const double atten = 0.9998; // to avoid ripple overflow 21886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung double fcr; 21986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung double tbw = firKaiserTbw(c.mHalfNumCoefs, stopBandAtten); 22086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 221771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung (void)posix_memalign(reinterpret_cast<void**>(&buf), 32, (c.mL+1)*c.mHalfNumCoefs*sizeof(TC)); 22286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (inSampleRate < outSampleRate) { // upsample 22386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung fcr = max(0.5*tbwCheat - tbw/2, tbw/2); 22486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } else { // downsample 22586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung fcr = max(0.5*tbwCheat*outSampleRate/inSampleRate - tbw/2, tbw/2); 22686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 22786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // create and set filter 22886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung firKaiserGen(buf, c.mL, c.mHalfNumCoefs, stopBandAtten, fcr, atten); 229771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung c.mFirCoefs = buf; 23086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (mCoefBuffer) { 23186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung free(mCoefBuffer); 23286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 23386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mCoefBuffer = buf; 23486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#ifdef DEBUG_RESAMPLER 23586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // print basic filter stats 23686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung printf("L:%d hnc:%d stopBandAtten:%lf fcr:%lf atten:%lf tbw:%lf\n", 23786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung c.mL, c.mHalfNumCoefs, stopBandAtten, fcr, atten, tbw); 23886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // test the filter and report results 23986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung double fp = (fcr - tbw/2)/c.mL; 24086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung double fs = (fcr + tbw/2)/c.mL; 2416582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung double passMin, passMax, passRipple; 2426582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung double stopMax, stopRipple; 2436582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung testFir(buf, c.mL, c.mHalfNumCoefs, fp, fs, /*passSteps*/ 1000, /*stopSteps*/ 100000, 2446582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung passMin, passMax, passRipple, stopMax, stopRipple); 2456582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung printf("passband(%lf, %lf): %.8lf %.8lf %.8lf\n", 0., fp, passMin, passMax, passRipple); 2466582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung printf("stopband(%lf, %lf): %.8lf %.3lf\n", fs, 0.5, stopMax, stopRipple); 24786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#endif 24886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 24986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 2506582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung// recursive gcd. Using objdump, it appears the tail recursion is converted to a while loop. 251771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungstatic int gcd(int n, int m) 252771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 25386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (m == 0) { 25486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung return n; 25586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 25686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung return gcd(m, n % m); 25786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 25886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 2596582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hungstatic bool isClose(int32_t newSampleRate, int32_t prevSampleRate, 260771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung int32_t filterSampleRate, int32_t outSampleRate) 261771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 2626582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung 2636582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // different upsampling ratios do not need a filter change. 2646582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung if (filterSampleRate != 0 2656582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung && filterSampleRate < outSampleRate 2666582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung && newSampleRate < outSampleRate) 2676582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung return true; 2686582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung 2696582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // check design criteria again if downsampling is detected. 27086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung int pdiff = absdiff(newSampleRate, prevSampleRate); 27186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung int adiff = absdiff(newSampleRate, filterSampleRate); 27286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 27386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // allow up to 6% relative change increments. 27486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // allow up to 12% absolute change increments (from filter design) 27586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung return pdiff < prevSampleRate>>4 && adiff < filterSampleRate>>3; 27686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 27786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 278771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 279771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungvoid AudioResamplerDyn<TC, TI, TO>::setSampleRate(int32_t inSampleRate) 280771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung{ 28186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (mInSampleRate == inSampleRate) { 28286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung return; 28386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 28486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung int32_t oldSampleRate = mInSampleRate; 28586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung uint32_t oldPhaseWrapLimit = mConstants.mL << mConstants.mShift; 28686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung bool useS32 = false; 28786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 28886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mInSampleRate = inSampleRate; 28986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 29086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // TODO: Add precalculated Equiripple filters 29186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 2926582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung if (mFilterQuality != getQuality() || 2936582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung !isClose(inSampleRate, oldSampleRate, mFilterSampleRate, mSampleRate)) { 29486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mFilterSampleRate = inSampleRate; 2956582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung mFilterQuality = getQuality(); 29686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 29786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // Begin Kaiser Filter computation 29886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // 29986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // The quantization floor for S16 is about 96db - 10*log_10(#length) + 3dB. 30086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // Keep the stop band attenuation no greater than 84-85dB for 32 length S16 filters 30186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // 30286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // For s32 we keep the stop band attenuation at the same as 16b resolution, about 30386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // 96-98dB 30486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // 30586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 30686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung double stopBandAtten; 30786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung double tbwCheat = 1.; // how much we "cheat" into aliasing 30886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung int halfLength; 3096582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung if (mFilterQuality == DYN_HIGH_QUALITY) { 31086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // 32b coefficients, 64 length 31186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung useS32 = true; 31286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung stopBandAtten = 98.; 313a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung if (inSampleRate >= mSampleRate * 4) { 314a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung halfLength = 48; 315a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung } else if (inSampleRate >= mSampleRate * 2) { 316a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung halfLength = 40; 317a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung } else { 318a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung halfLength = 32; 319a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung } 3206582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung } else if (mFilterQuality == DYN_LOW_QUALITY) { 32186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // 16b coefficients, 16-32 length 32286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung useS32 = false; 32386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung stopBandAtten = 80.; 324a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung if (inSampleRate >= mSampleRate * 4) { 325a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung halfLength = 24; 326a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung } else if (inSampleRate >= mSampleRate * 2) { 32786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung halfLength = 16; 32886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } else { 32986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung halfLength = 8; 33086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 331a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung if (inSampleRate <= mSampleRate) { 33286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung tbwCheat = 1.05; 33386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } else { 33486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung tbwCheat = 1.03; 33586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 3366582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung } else { // DYN_MED_QUALITY 33786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // 16b coefficients, 32-64 length 3386582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // note: > 64 length filters with 16b coefs can have quantization noise problems 33986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung useS32 = false; 34086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung stopBandAtten = 84.; 341a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung if (inSampleRate >= mSampleRate * 4) { 34286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung halfLength = 32; 343a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung } else if (inSampleRate >= mSampleRate * 2) { 34486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung halfLength = 24; 34586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } else { 34686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung halfLength = 16; 34786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 348a3bb9a3a25b58669d75da4f73764ac4c2bf34158Andy Hung if (inSampleRate <= mSampleRate) { 34986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung tbwCheat = 1.03; 35086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } else { 35186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung tbwCheat = 1.01; 35286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 35386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 35486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 35586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // determine the number of polyphases in the filterbank. 35686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // for 16b, it is desirable to have 2^(16/2) = 256 phases. 35786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // https://ccrma.stanford.edu/~jos/resample/Relation_Interpolation_Error_Quantization.html 35886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // 35986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // We are a bit more lax on this. 36086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 36186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung int phases = mSampleRate / gcd(mSampleRate, inSampleRate); 36286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 3636582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // TODO: Once dynamic sample rate change is an option, the code below 3646582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // should be modified to execute only when dynamic sample rate change is enabled. 3656582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // 3666582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // as above, #phases less than 63 is too few phases for accurate linear interpolation. 3676582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // we increase the phases to compensate, but more phases means more memory per 3686582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // filter and more time to compute the filter. 3696582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // 3706582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // if we know that the filter will be used for dynamic sample rate changes, 3716582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // that would allow us skip this part for fixed sample rate resamplers. 3726582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung // 3736582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung while (phases<63) { 37486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung phases *= 2; // this code only needed to support dynamic rate changes 37586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 3766582f2b14a21e630654c5522ef9ad64e80d5058dAndy Hung 37786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (phases>=256) { // too many phases, always interpolate 37886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung phases = 127; 37986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 38086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 38186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // create the filter 38286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mConstants.set(phases, halfLength, inSampleRate, mSampleRate); 383771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung createKaiserFir(mConstants, stopBandAtten, 384771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung inSampleRate, mSampleRate, tbwCheat); 38586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } // End Kaiser filter 38686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 38786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // update phase and state based on the new filter. 38886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung const Constants& c(mConstants); 38986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mInBuffer.resize(mChannelCount, c.mHalfNumCoefs); 39086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung const uint32_t phaseWrapLimit = c.mL << c.mShift; 39186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // try to preserve as much of the phase fraction as possible for on-the-fly changes 39286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mPhaseFraction = static_cast<unsigned long long>(mPhaseFraction) 39386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * phaseWrapLimit / oldPhaseWrapLimit; 39486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mPhaseFraction %= phaseWrapLimit; // should not do anything, but just in case. 395cd04484f4837b8ca0041d118286ab6a98e84fc75Andy Hung mPhaseIncrement = static_cast<uint32_t>(static_cast<uint64_t>(phaseWrapLimit) 39686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung * inSampleRate / mSampleRate); 39786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 39886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // determine which resampler to use 39986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // check if locked phase (works only if mPhaseIncrement has no "fractional phase bits") 40086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung int locked = (mPhaseIncrement << (sizeof(mPhaseIncrement)*8 - c.mShift)) == 0; 40186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (locked) { 40286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mPhaseFraction = mPhaseFraction >> c.mShift << c.mShift; // remove fractional phase 40386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 40483be2560d9396b3bd32919123bd67a783e6aaf7cAndy Hung 405075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung // stride is the minimum number of filter coefficients processed per loop iteration. 406075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung // We currently only allow a stride of 16 to match with SIMD processing. 407075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung // This means that the filter length must be a multiple of 16, 408075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung // or half the filter length (mHalfNumCoefs) must be a multiple of 8. 409075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung // 410075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung // Note: A stride of 2 is achieved with non-SIMD processing. 411075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung int stride = ((c.mHalfNumCoefs & 7) == 0) ? 16 : 2; 412075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung LOG_ALWAYS_FATAL_IF(stride < 16, "Resampler stride must be 16 or more"); 4135e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020Andy Hung LOG_ALWAYS_FATAL_IF(mChannelCount < 1 || mChannelCount > 8, 414075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung "Resampler channels(%d) must be between 1 to 8", mChannelCount); 415075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung // stride 16 (falls back to stride 2 for machines that do not support NEON) 416075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung if (locked) { 417075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung switch (mChannelCount) { 418075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 1: 419075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, true, 16>; 420075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 421075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 2: 422075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, true, 16>; 423075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 424075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 3: 425075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<3, true, 16>; 426075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 427075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 4: 428075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<4, true, 16>; 429075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 430075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 5: 431075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<5, true, 16>; 432075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 433075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 6: 434075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<6, true, 16>; 435075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 436075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 7: 437075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<7, true, 16>; 438075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 439075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 8: 440075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<8, true, 16>; 441075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 442075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung } 443075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung } else { 444075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung switch (mChannelCount) { 445075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 1: 446075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, false, 16>; 447075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 448075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 2: 449075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, false, 16>; 450075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 451075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 3: 452075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<3, false, 16>; 453075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 454075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 4: 455075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<4, false, 16>; 456075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 457075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 5: 458075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<5, false, 16>; 459075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 460075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 6: 461075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<6, false, 16>; 462075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 463075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 7: 464075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<7, false, 16>; 465075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 466075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung case 8: 467075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<8, false, 16>; 468075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung break; 469075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung } 470075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung } 47186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#ifdef DEBUG_RESAMPLER 47286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung printf("channels:%d %s stride:%d %s coef:%d shift:%d\n", 47386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mChannelCount, locked ? "locked" : "interpolated", 47486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung stride, useS32 ? "S32" : "S16", 2*c.mHalfNumCoefs, c.mShift); 47586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung#endif 47686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 47786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 478771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 4796b3b7e304e0f8f167241b2c75f1eb04a9ef192ecAndy Hungsize_t AudioResamplerDyn<TC, TI, TO>::resample(int32_t* out, size_t outFrameCount, 48086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung AudioBufferProvider* provider) 48186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung{ 4826b3b7e304e0f8f167241b2c75f1eb04a9ef192ecAndy Hung return (this->*mResampleFunc)(reinterpret_cast<TO*>(out), outFrameCount, provider); 483771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung} 48486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 485771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<typename TC, typename TI, typename TO> 486771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate<int CHANNELS, bool LOCKED, int STRIDE> 4876b3b7e304e0f8f167241b2c75f1eb04a9ef192ecAndy Hungsize_t AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, 488771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung AudioBufferProvider* provider) 48986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung{ 490075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung // TODO Mono -> Mono is not supported. OUTPUT_CHANNELS reflects minimum of stereo out. 491075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung const int OUTPUT_CHANNELS = (CHANNELS < 2) ? 2 : CHANNELS; 49286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung const Constants& c(mConstants); 493771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung const TC* const coefs = mConstants.mFirCoefs; 494771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung TI* impulse = mInBuffer.getImpulse(); 495411cb8e45442d16d983a38722c7c7bce487bde6bAndy Hung size_t inputIndex = 0; 49686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung uint32_t phaseFraction = mPhaseFraction; 49786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung const uint32_t phaseIncrement = mPhaseIncrement; 49886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung size_t outputIndex = 0; 499075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung size_t outputSampleCount = outFrameCount * OUTPUT_CHANNELS; 50086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung const uint32_t phaseWrapLimit = c.mL << c.mShift; 501717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung size_t inFrameCount = (phaseIncrement * (uint64_t)outFrameCount + phaseFraction) 502717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung / phaseWrapLimit; 503717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // sanity check that inFrameCount is in signed 32 bit integer range. 504717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung ALOG_ASSERT(0 <= inFrameCount && inFrameCount < (1U << 31)); 505717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung 506717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung //ALOGV("inFrameCount:%d outFrameCount:%d" 507717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // " phaseIncrement:%u phaseFraction:%u phaseWrapLimit:%u", 508717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // inFrameCount, outFrameCount, phaseIncrement, phaseFraction, phaseWrapLimit); 50986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 51086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // NOTE: be very careful when modifying the code here. register 51186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // pressure is very high and a small change might cause the compiler 51286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // to generate far less efficient code. 51386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // Always sanity check the result with objdump or test-resample. 51486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 51586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // the following logic is a bit convoluted to keep the main processing loop 51686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // as tight as possible with register allocation. 51786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung while (outputIndex < outputSampleCount) { 518717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung //ALOGV("LOOP: inFrameCount:%d outputIndex:%d outFrameCount:%d" 519717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // " phaseFraction:%u phaseWrapLimit:%u", 520717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); 521717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung 522717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // check inputIndex overflow 523717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung ALOG_ASSERT(inputIndex <= mBuffer.frameCount, "inputIndex%d > frameCount%d", 524717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung inputIndex, mBuffer.frameCount); 525717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // Buffer is empty, fetch a new one if necessary (inFrameCount > 0). 526717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // We may not fetch a new buffer if the existing data is sufficient. 527717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung while (mBuffer.frameCount == 0 && inFrameCount > 0) { 52886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mBuffer.frameCount = inFrameCount; 529d79072e9dff59f767cce2cda1caab80ce5a0815bGlenn Kasten provider->getNextBuffer(&mBuffer); 53086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (mBuffer.raw == NULL) { 53186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung goto resample_exit; 53286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 533411cb8e45442d16d983a38722c7c7bce487bde6bAndy Hung inFrameCount -= mBuffer.frameCount; 53486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (phaseFraction >= phaseWrapLimit) { // read in data 535771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung mInBuffer.template readAdvance<CHANNELS>( 536771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung impulse, c.mHalfNumCoefs, 537771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung reinterpret_cast<TI*>(mBuffer.raw), inputIndex); 538717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung inputIndex++; 53986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung phaseFraction -= phaseWrapLimit; 54086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung while (phaseFraction >= phaseWrapLimit) { 54186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (inputIndex >= mBuffer.frameCount) { 542411cb8e45442d16d983a38722c7c7bce487bde6bAndy Hung inputIndex = 0; 54386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung provider->releaseBuffer(&mBuffer); 54486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung break; 54586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 546771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung mInBuffer.template readAdvance<CHANNELS>( 547771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung impulse, c.mHalfNumCoefs, 548771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung reinterpret_cast<TI*>(mBuffer.raw), inputIndex); 549717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung inputIndex++; 55086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung phaseFraction -= phaseWrapLimit; 55186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 55286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 55386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 554771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung const TI* const in = reinterpret_cast<const TI*>(mBuffer.raw); 55586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung const size_t frameCount = mBuffer.frameCount; 55686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung const int coefShift = c.mShift; 55786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung const int halfNumCoefs = c.mHalfNumCoefs; 558771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung const TO* const volumeSimd = mVolumeSimd; 55986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 56086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // main processing loop 56186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung while (CC_LIKELY(outputIndex < outputSampleCount)) { 56286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // caution: fir() is inlined and may be large. 56386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // output will be loaded with the appropriate values 56486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // 56586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // from the input samples in impulse[-halfNumCoefs+1]... impulse[halfNumCoefs] 56686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // from the polyphase filter of (phaseFraction / phaseWrapLimit) in coefs. 56786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung // 568717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung //ALOGV("LOOP2: inFrameCount:%d outputIndex:%d outFrameCount:%d" 569717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // " phaseFraction:%u phaseWrapLimit:%u", 570717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); 571717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung ALOG_ASSERT(phaseFraction < phaseWrapLimit); 57286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung fir<CHANNELS, LOCKED, STRIDE>( 57386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung &out[outputIndex], 57486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung phaseFraction, phaseWrapLimit, 57586eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung coefShift, halfNumCoefs, coefs, 57686eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung impulse, volumeSimd); 577075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung 578075abae2a954bf3edf18ad1705c2c0f188454ae0Andy Hung outputIndex += OUTPUT_CHANNELS; 57986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 58086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung phaseFraction += phaseIncrement; 58186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung while (phaseFraction >= phaseWrapLimit) { 58286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung if (inputIndex >= frameCount) { 58386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung goto done; // need a new buffer 58486eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 585771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung mInBuffer.template readAdvance<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); 586717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung inputIndex++; 58786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung phaseFraction -= phaseWrapLimit; 58886eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 58986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 59086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hungdone: 591717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // We arrive here when we're finished or when the input buffer runs out. 592717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // Regardless we need to release the input buffer if we've acquired it. 593717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung if (inputIndex > 0) { // we've acquired a buffer (alternatively could check frameCount) 594717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung ALOG_ASSERT(inputIndex == frameCount, "inputIndex(%d) != frameCount(%d)", 595717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung inputIndex, frameCount); // must have been fully read. 596411cb8e45442d16d983a38722c7c7bce487bde6bAndy Hung inputIndex = 0; 59786eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung provider->releaseBuffer(&mBuffer); 598411cb8e45442d16d983a38722c7c7bce487bde6bAndy Hung ALOG_ASSERT(mBuffer.frameCount == 0); 59986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 60086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung } 60186eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 60286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hungresample_exit: 603717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // inputIndex must be zero in all three cases: 604717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // (1) the buffer never was been acquired; (2) the buffer was 605717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung // released at "done:"; or (3) getNextBuffer() failed. 606717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung ALOG_ASSERT(inputIndex == 0, "Releasing: inputindex:%d frameCount:%d phaseFraction:%u", 607717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung inputIndex, mBuffer.frameCount, phaseFraction); 608717007429a50c02d2acc704a8c1ebbe6760a2c22Andy Hung ALOG_ASSERT(mBuffer.frameCount == 0); // there must be no frames in the buffer 60986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mInBuffer.setImpulse(impulse); 61086eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung mPhaseFraction = phaseFraction; 6116b3b7e304e0f8f167241b2c75f1eb04a9ef192ecAndy Hung return outputIndex / OUTPUT_CHANNELS; 61286eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung} 61386eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung 614771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung/* instantiate templates used by AudioResampler::create */ 615771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate class AudioResamplerDyn<float, float, float>; 616771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate class AudioResamplerDyn<int16_t, int16_t, int32_t>; 617771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hungtemplate class AudioResamplerDyn<int32_t, int16_t, int32_t>; 618771386e6e6e79697e2d839ef0f25a242946ba1e5Andy Hung 61986eae0e5931103e040ac2cdd023ef5db252e09f6Andy Hung// ---------------------------------------------------------------------------- 62063238efb0d674758902918e3cdaac322126484b7Glenn Kasten} // namespace android 621