xref: /aosp_15_r20/frameworks/av/media/libaudioprocessing/AudioResamplerSinc.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker  * Copyright (C) 2007 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker  *
4*ec779b8eSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker  *
8*ec779b8eSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker  *
10*ec779b8eSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker  * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker  */
16*ec779b8eSAndroid Build Coastguard Worker 
17*ec779b8eSAndroid Build Coastguard Worker #define LOG_TAG "AudioResamplerSinc"
18*ec779b8eSAndroid Build Coastguard Worker //#define LOG_NDEBUG 0
19*ec779b8eSAndroid Build Coastguard Worker 
20*ec779b8eSAndroid Build Coastguard Worker #include <malloc.h>
21*ec779b8eSAndroid Build Coastguard Worker #include <pthread.h>
22*ec779b8eSAndroid Build Coastguard Worker #include <string.h>
23*ec779b8eSAndroid Build Coastguard Worker #include <stdlib.h>
24*ec779b8eSAndroid Build Coastguard Worker #include <dlfcn.h>
25*ec779b8eSAndroid Build Coastguard Worker 
26*ec779b8eSAndroid Build Coastguard Worker #include <cutils/compiler.h>
27*ec779b8eSAndroid Build Coastguard Worker #include <cutils/properties.h>
28*ec779b8eSAndroid Build Coastguard Worker 
29*ec779b8eSAndroid Build Coastguard Worker #include <utils/Log.h>
30*ec779b8eSAndroid Build Coastguard Worker #include <audio_utils/primitives.h>
31*ec779b8eSAndroid Build Coastguard Worker 
32*ec779b8eSAndroid Build Coastguard Worker #include "AudioResamplerSinc.h"
33*ec779b8eSAndroid Build Coastguard Worker 
34*ec779b8eSAndroid Build Coastguard Worker #if defined(__clang__) && !__has_builtin(__builtin_assume_aligned)
35*ec779b8eSAndroid Build Coastguard Worker #define __builtin_assume_aligned(p, a) \
36*ec779b8eSAndroid Build Coastguard Worker 	(((uintptr_t(p) % (a)) == 0) ? (p) : (__builtin_unreachable(), (p)))
37*ec779b8eSAndroid Build Coastguard Worker #endif
38*ec779b8eSAndroid Build Coastguard Worker 
39*ec779b8eSAndroid Build Coastguard Worker #if defined(__arm__) && !defined(__thumb__)
40*ec779b8eSAndroid Build Coastguard Worker #define USE_INLINE_ASSEMBLY (true)
41*ec779b8eSAndroid Build Coastguard Worker #else
42*ec779b8eSAndroid Build Coastguard Worker #define USE_INLINE_ASSEMBLY (false)
43*ec779b8eSAndroid Build Coastguard Worker #endif
44*ec779b8eSAndroid Build Coastguard Worker 
45*ec779b8eSAndroid Build Coastguard Worker #if defined(__aarch64__) || defined(__ARM_NEON__)
46*ec779b8eSAndroid Build Coastguard Worker #ifndef USE_NEON
47*ec779b8eSAndroid Build Coastguard Worker #define USE_NEON (true)
48*ec779b8eSAndroid Build Coastguard Worker #endif
49*ec779b8eSAndroid Build Coastguard Worker #else
50*ec779b8eSAndroid Build Coastguard Worker #define USE_NEON (false)
51*ec779b8eSAndroid Build Coastguard Worker #endif
52*ec779b8eSAndroid Build Coastguard Worker #if USE_NEON
53*ec779b8eSAndroid Build Coastguard Worker #include <arm_neon.h>
54*ec779b8eSAndroid Build Coastguard Worker #endif
55*ec779b8eSAndroid Build Coastguard Worker 
56*ec779b8eSAndroid Build Coastguard Worker #define UNUSED(x) ((void)(x))
57*ec779b8eSAndroid Build Coastguard Worker 
58*ec779b8eSAndroid Build Coastguard Worker namespace android {
59*ec779b8eSAndroid Build Coastguard Worker // ----------------------------------------------------------------------------
60*ec779b8eSAndroid Build Coastguard Worker 
61*ec779b8eSAndroid Build Coastguard Worker 
62*ec779b8eSAndroid Build Coastguard Worker /*
63*ec779b8eSAndroid Build Coastguard Worker  * These coeficients are computed with the "fir" utility found in
64*ec779b8eSAndroid Build Coastguard Worker  * tools/resampler_tools
65*ec779b8eSAndroid Build Coastguard Worker  * cmd-line: fir -l 7 -s 48000 -c 20478
66*ec779b8eSAndroid Build Coastguard Worker  */
67*ec779b8eSAndroid Build Coastguard Worker const uint32_t AudioResamplerSinc::mFirCoefsUp[] __attribute__ ((aligned (32))) = {
68*ec779b8eSAndroid Build Coastguard Worker #include "AudioResamplerSincUp.h"
69*ec779b8eSAndroid Build Coastguard Worker };
70*ec779b8eSAndroid Build Coastguard Worker 
71*ec779b8eSAndroid Build Coastguard Worker /*
72*ec779b8eSAndroid Build Coastguard Worker  * These coefficients are optimized for 48KHz -> 44.1KHz
73*ec779b8eSAndroid Build Coastguard Worker  * cmd-line: fir -l 7 -s 48000 -c 17189
74*ec779b8eSAndroid Build Coastguard Worker  */
75*ec779b8eSAndroid Build Coastguard Worker const uint32_t AudioResamplerSinc::mFirCoefsDown[] __attribute__ ((aligned (32))) = {
76*ec779b8eSAndroid Build Coastguard Worker #include "AudioResamplerSincDown.h"
77*ec779b8eSAndroid Build Coastguard Worker };
78*ec779b8eSAndroid Build Coastguard Worker 
79*ec779b8eSAndroid Build Coastguard Worker // we use 15 bits to interpolate between these samples
80*ec779b8eSAndroid Build Coastguard Worker // this cannot change because the mul below rely on it.
81*ec779b8eSAndroid Build Coastguard Worker static const int pLerpBits = 15;
82*ec779b8eSAndroid Build Coastguard Worker 
83*ec779b8eSAndroid Build Coastguard Worker static pthread_once_t once_control = PTHREAD_ONCE_INIT;
84*ec779b8eSAndroid Build Coastguard Worker static readCoefficientsFn readResampleCoefficients = NULL;
85*ec779b8eSAndroid Build Coastguard Worker 
86*ec779b8eSAndroid Build Coastguard Worker /*static*/ AudioResamplerSinc::Constants AudioResamplerSinc::highQualityConstants;
87*ec779b8eSAndroid Build Coastguard Worker /*static*/ AudioResamplerSinc::Constants AudioResamplerSinc::veryHighQualityConstants;
88*ec779b8eSAndroid Build Coastguard Worker 
init_routine()89*ec779b8eSAndroid Build Coastguard Worker void AudioResamplerSinc::init_routine()
90*ec779b8eSAndroid Build Coastguard Worker {
91*ec779b8eSAndroid Build Coastguard Worker     // for high quality resampler, the parameters for coefficients are compile-time constants
92*ec779b8eSAndroid Build Coastguard Worker     Constants *c = &highQualityConstants;
93*ec779b8eSAndroid Build Coastguard Worker     c->coefsBits = RESAMPLE_FIR_LERP_INT_BITS;
94*ec779b8eSAndroid Build Coastguard Worker     c->cShift = kNumPhaseBits - c->coefsBits;
95*ec779b8eSAndroid Build Coastguard Worker     c->cMask = ((1<< c->coefsBits)-1) << c->cShift;
96*ec779b8eSAndroid Build Coastguard Worker     c->pShift = kNumPhaseBits - c->coefsBits - pLerpBits;
97*ec779b8eSAndroid Build Coastguard Worker     c->pMask = ((1<< pLerpBits)-1) << c->pShift;
98*ec779b8eSAndroid Build Coastguard Worker     c->halfNumCoefs = RESAMPLE_FIR_NUM_COEF;
99*ec779b8eSAndroid Build Coastguard Worker 
100*ec779b8eSAndroid Build Coastguard Worker     // for very high quality resampler, the parameters are load-time constants
101*ec779b8eSAndroid Build Coastguard Worker     veryHighQualityConstants = highQualityConstants;
102*ec779b8eSAndroid Build Coastguard Worker 
103*ec779b8eSAndroid Build Coastguard Worker     // Open the dll to get the coefficients for VERY_HIGH_QUALITY
104*ec779b8eSAndroid Build Coastguard Worker     void *resampleCoeffLib = dlopen("libaudio-resampler.so", RTLD_NOW);
105*ec779b8eSAndroid Build Coastguard Worker     ALOGV("Open libaudio-resampler library = %p", resampleCoeffLib);
106*ec779b8eSAndroid Build Coastguard Worker     if (resampleCoeffLib == NULL) {
107*ec779b8eSAndroid Build Coastguard Worker         ALOGE("Could not open audio-resampler library: %s", dlerror());
108*ec779b8eSAndroid Build Coastguard Worker         return;
109*ec779b8eSAndroid Build Coastguard Worker     }
110*ec779b8eSAndroid Build Coastguard Worker 
111*ec779b8eSAndroid Build Coastguard Worker     readResampleFirNumCoeffFn readResampleFirNumCoeff;
112*ec779b8eSAndroid Build Coastguard Worker     readResampleFirLerpIntBitsFn readResampleFirLerpIntBits;
113*ec779b8eSAndroid Build Coastguard Worker 
114*ec779b8eSAndroid Build Coastguard Worker     readResampleCoefficients = (readCoefficientsFn)
115*ec779b8eSAndroid Build Coastguard Worker             dlsym(resampleCoeffLib, "readResamplerCoefficients");
116*ec779b8eSAndroid Build Coastguard Worker     readResampleFirNumCoeff = (readResampleFirNumCoeffFn)
117*ec779b8eSAndroid Build Coastguard Worker             dlsym(resampleCoeffLib, "readResampleFirNumCoeff");
118*ec779b8eSAndroid Build Coastguard Worker     readResampleFirLerpIntBits = (readResampleFirLerpIntBitsFn)
119*ec779b8eSAndroid Build Coastguard Worker             dlsym(resampleCoeffLib, "readResampleFirLerpIntBits");
120*ec779b8eSAndroid Build Coastguard Worker 
121*ec779b8eSAndroid Build Coastguard Worker     if (!readResampleCoefficients || !readResampleFirNumCoeff || !readResampleFirLerpIntBits) {
122*ec779b8eSAndroid Build Coastguard Worker         readResampleCoefficients = NULL;
123*ec779b8eSAndroid Build Coastguard Worker         dlclose(resampleCoeffLib);
124*ec779b8eSAndroid Build Coastguard Worker         resampleCoeffLib = NULL;
125*ec779b8eSAndroid Build Coastguard Worker         ALOGE("Could not find symbol: %s", dlerror());
126*ec779b8eSAndroid Build Coastguard Worker         return;
127*ec779b8eSAndroid Build Coastguard Worker     }
128*ec779b8eSAndroid Build Coastguard Worker 
129*ec779b8eSAndroid Build Coastguard Worker     c = &veryHighQualityConstants;
130*ec779b8eSAndroid Build Coastguard Worker     c->coefsBits = readResampleFirLerpIntBits();
131*ec779b8eSAndroid Build Coastguard Worker     c->cShift = kNumPhaseBits - c->coefsBits;
132*ec779b8eSAndroid Build Coastguard Worker     c->cMask = ((1<<c->coefsBits)-1) << c->cShift;
133*ec779b8eSAndroid Build Coastguard Worker     c->pShift = kNumPhaseBits - c->coefsBits - pLerpBits;
134*ec779b8eSAndroid Build Coastguard Worker     c->pMask = ((1<<pLerpBits)-1) << c->pShift;
135*ec779b8eSAndroid Build Coastguard Worker     // number of zero-crossing on each side
136*ec779b8eSAndroid Build Coastguard Worker     c->halfNumCoefs = readResampleFirNumCoeff();
137*ec779b8eSAndroid Build Coastguard Worker     ALOGV("coefsBits = %d", c->coefsBits);
138*ec779b8eSAndroid Build Coastguard Worker     ALOGV("halfNumCoefs = %d", c->halfNumCoefs);
139*ec779b8eSAndroid Build Coastguard Worker     // note that we "leak" resampleCoeffLib until the process exits
140*ec779b8eSAndroid Build Coastguard Worker }
141*ec779b8eSAndroid Build Coastguard Worker 
142*ec779b8eSAndroid Build Coastguard Worker // ----------------------------------------------------------------------------
143*ec779b8eSAndroid Build Coastguard Worker 
144*ec779b8eSAndroid Build Coastguard Worker #if !USE_NEON
145*ec779b8eSAndroid Build Coastguard Worker 
146*ec779b8eSAndroid Build Coastguard Worker static inline
mulRL(int left,int32_t in,uint32_t vRL)147*ec779b8eSAndroid Build Coastguard Worker int32_t mulRL(int left, int32_t in, uint32_t vRL)
148*ec779b8eSAndroid Build Coastguard Worker {
149*ec779b8eSAndroid Build Coastguard Worker #if USE_INLINE_ASSEMBLY
150*ec779b8eSAndroid Build Coastguard Worker     int32_t out;
151*ec779b8eSAndroid Build Coastguard Worker     if (left) {
152*ec779b8eSAndroid Build Coastguard Worker         asm( "smultb %[out], %[in], %[vRL] \n"
153*ec779b8eSAndroid Build Coastguard Worker              : [out]"=r"(out)
154*ec779b8eSAndroid Build Coastguard Worker              : [in]"%r"(in), [vRL]"r"(vRL)
155*ec779b8eSAndroid Build Coastguard Worker              : );
156*ec779b8eSAndroid Build Coastguard Worker     } else {
157*ec779b8eSAndroid Build Coastguard Worker         asm( "smultt %[out], %[in], %[vRL] \n"
158*ec779b8eSAndroid Build Coastguard Worker              : [out]"=r"(out)
159*ec779b8eSAndroid Build Coastguard Worker              : [in]"%r"(in), [vRL]"r"(vRL)
160*ec779b8eSAndroid Build Coastguard Worker              : );
161*ec779b8eSAndroid Build Coastguard Worker     }
162*ec779b8eSAndroid Build Coastguard Worker     return out;
163*ec779b8eSAndroid Build Coastguard Worker #else
164*ec779b8eSAndroid Build Coastguard Worker     int16_t v = left ? int16_t(vRL) : int16_t(vRL>>16);
165*ec779b8eSAndroid Build Coastguard Worker     return int32_t((int64_t(in) * v) >> 16);
166*ec779b8eSAndroid Build Coastguard Worker #endif
167*ec779b8eSAndroid Build Coastguard Worker }
168*ec779b8eSAndroid Build Coastguard Worker 
169*ec779b8eSAndroid Build Coastguard Worker static inline
mulAdd(int16_t in,int32_t v,int32_t a)170*ec779b8eSAndroid Build Coastguard Worker int32_t mulAdd(int16_t in, int32_t v, int32_t a)
171*ec779b8eSAndroid Build Coastguard Worker {
172*ec779b8eSAndroid Build Coastguard Worker #if USE_INLINE_ASSEMBLY
173*ec779b8eSAndroid Build Coastguard Worker     int32_t out;
174*ec779b8eSAndroid Build Coastguard Worker     asm( "smlawb %[out], %[v], %[in], %[a] \n"
175*ec779b8eSAndroid Build Coastguard Worker          : [out]"=r"(out)
176*ec779b8eSAndroid Build Coastguard Worker          : [in]"%r"(in), [v]"r"(v), [a]"r"(a)
177*ec779b8eSAndroid Build Coastguard Worker          : );
178*ec779b8eSAndroid Build Coastguard Worker     return out;
179*ec779b8eSAndroid Build Coastguard Worker #else
180*ec779b8eSAndroid Build Coastguard Worker     return a + int32_t((int64_t(v) * in) >> 16);
181*ec779b8eSAndroid Build Coastguard Worker #endif
182*ec779b8eSAndroid Build Coastguard Worker }
183*ec779b8eSAndroid Build Coastguard Worker 
184*ec779b8eSAndroid Build Coastguard Worker static inline
mulAddRL(int left,uint32_t inRL,int32_t v,int32_t a)185*ec779b8eSAndroid Build Coastguard Worker int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
186*ec779b8eSAndroid Build Coastguard Worker {
187*ec779b8eSAndroid Build Coastguard Worker #if USE_INLINE_ASSEMBLY
188*ec779b8eSAndroid Build Coastguard Worker     int32_t out;
189*ec779b8eSAndroid Build Coastguard Worker     if (left) {
190*ec779b8eSAndroid Build Coastguard Worker         asm( "smlawb %[out], %[v], %[inRL], %[a] \n"
191*ec779b8eSAndroid Build Coastguard Worker              : [out]"=r"(out)
192*ec779b8eSAndroid Build Coastguard Worker              : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
193*ec779b8eSAndroid Build Coastguard Worker              : );
194*ec779b8eSAndroid Build Coastguard Worker     } else {
195*ec779b8eSAndroid Build Coastguard Worker         asm( "smlawt %[out], %[v], %[inRL], %[a] \n"
196*ec779b8eSAndroid Build Coastguard Worker              : [out]"=r"(out)
197*ec779b8eSAndroid Build Coastguard Worker              : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
198*ec779b8eSAndroid Build Coastguard Worker              : );
199*ec779b8eSAndroid Build Coastguard Worker     }
200*ec779b8eSAndroid Build Coastguard Worker     return out;
201*ec779b8eSAndroid Build Coastguard Worker #else
202*ec779b8eSAndroid Build Coastguard Worker     int16_t s = left ? int16_t(inRL) : int16_t(inRL>>16);
203*ec779b8eSAndroid Build Coastguard Worker     return a + int32_t((int64_t(v) * s) >> 16);
204*ec779b8eSAndroid Build Coastguard Worker #endif
205*ec779b8eSAndroid Build Coastguard Worker }
206*ec779b8eSAndroid Build Coastguard Worker 
207*ec779b8eSAndroid Build Coastguard Worker #endif // !USE_NEON
208*ec779b8eSAndroid Build Coastguard Worker 
209*ec779b8eSAndroid Build Coastguard Worker // ----------------------------------------------------------------------------
210*ec779b8eSAndroid Build Coastguard Worker 
AudioResamplerSinc(int inChannelCount,int32_t sampleRate,src_quality quality)211*ec779b8eSAndroid Build Coastguard Worker AudioResamplerSinc::AudioResamplerSinc(
212*ec779b8eSAndroid Build Coastguard Worker         int inChannelCount, int32_t sampleRate, src_quality quality)
213*ec779b8eSAndroid Build Coastguard Worker     : AudioResampler(inChannelCount, sampleRate, quality),
214*ec779b8eSAndroid Build Coastguard Worker     mState(0), mImpulse(0), mRingFull(0), mFirCoefs(0)
215*ec779b8eSAndroid Build Coastguard Worker {
216*ec779b8eSAndroid Build Coastguard Worker     /*
217*ec779b8eSAndroid Build Coastguard Worker      * Layout of the state buffer for 32 tap:
218*ec779b8eSAndroid Build Coastguard Worker      *
219*ec779b8eSAndroid Build Coastguard Worker      * "present" sample            beginning of 2nd buffer
220*ec779b8eSAndroid Build Coastguard Worker      *                 v                v
221*ec779b8eSAndroid Build Coastguard Worker      *  0              01               2              23              3
222*ec779b8eSAndroid Build Coastguard Worker      *  0              F0               0              F0              F
223*ec779b8eSAndroid Build Coastguard Worker      * [pppppppppppppppInnnnnnnnnnnnnnnnpppppppppppppppInnnnnnnnnnnnnnnn]
224*ec779b8eSAndroid Build Coastguard Worker      *                 ^               ^ head
225*ec779b8eSAndroid Build Coastguard Worker      *
226*ec779b8eSAndroid Build Coastguard Worker      * p = past samples, convoluted with the (p)ositive side of sinc()
227*ec779b8eSAndroid Build Coastguard Worker      * n = future samples, convoluted with the (n)egative side of sinc()
228*ec779b8eSAndroid Build Coastguard Worker      * r = extra space for implementing the ring buffer
229*ec779b8eSAndroid Build Coastguard Worker      *
230*ec779b8eSAndroid Build Coastguard Worker      */
231*ec779b8eSAndroid Build Coastguard Worker 
232*ec779b8eSAndroid Build Coastguard Worker     mVolumeSIMD[0] = 0;
233*ec779b8eSAndroid Build Coastguard Worker     mVolumeSIMD[1] = 0;
234*ec779b8eSAndroid Build Coastguard Worker 
235*ec779b8eSAndroid Build Coastguard Worker     // Load the constants for coefficients
236*ec779b8eSAndroid Build Coastguard Worker     int ok = pthread_once(&once_control, init_routine);
237*ec779b8eSAndroid Build Coastguard Worker     if (ok != 0) {
238*ec779b8eSAndroid Build Coastguard Worker         ALOGE("%s pthread_once failed: %d", __func__, ok);
239*ec779b8eSAndroid Build Coastguard Worker     }
240*ec779b8eSAndroid Build Coastguard Worker     mConstants = (quality == VERY_HIGH_QUALITY) ?
241*ec779b8eSAndroid Build Coastguard Worker             &veryHighQualityConstants : &highQualityConstants;
242*ec779b8eSAndroid Build Coastguard Worker }
243*ec779b8eSAndroid Build Coastguard Worker 
244*ec779b8eSAndroid Build Coastguard Worker 
~AudioResamplerSinc()245*ec779b8eSAndroid Build Coastguard Worker AudioResamplerSinc::~AudioResamplerSinc() {
246*ec779b8eSAndroid Build Coastguard Worker     free(mState);
247*ec779b8eSAndroid Build Coastguard Worker }
248*ec779b8eSAndroid Build Coastguard Worker 
init()249*ec779b8eSAndroid Build Coastguard Worker void AudioResamplerSinc::init() {
250*ec779b8eSAndroid Build Coastguard Worker     const Constants& c(*mConstants);
251*ec779b8eSAndroid Build Coastguard Worker     const size_t numCoefs = 2 * c.halfNumCoefs;
252*ec779b8eSAndroid Build Coastguard Worker     const size_t stateSize = numCoefs * mChannelCount * 2;
253*ec779b8eSAndroid Build Coastguard Worker     mState = (int16_t*)memalign(32, stateSize*sizeof(int16_t));
254*ec779b8eSAndroid Build Coastguard Worker     memset(mState, 0, sizeof(int16_t)*stateSize);
255*ec779b8eSAndroid Build Coastguard Worker     mImpulse  = mState   + (c.halfNumCoefs-1)*mChannelCount;
256*ec779b8eSAndroid Build Coastguard Worker     mRingFull = mImpulse + (numCoefs+1)*mChannelCount;
257*ec779b8eSAndroid Build Coastguard Worker }
258*ec779b8eSAndroid Build Coastguard Worker 
setVolume(float left,float right)259*ec779b8eSAndroid Build Coastguard Worker void AudioResamplerSinc::setVolume(float left, float right) {
260*ec779b8eSAndroid Build Coastguard Worker     AudioResampler::setVolume(left, right);
261*ec779b8eSAndroid Build Coastguard Worker     // convert to U4_28 (rounding down).
262*ec779b8eSAndroid Build Coastguard Worker     // integer volume values are clamped to 0 to UNITY_GAIN.
263*ec779b8eSAndroid Build Coastguard Worker     mVolumeSIMD[0] = u4_28_from_float(clampFloatVol(left));
264*ec779b8eSAndroid Build Coastguard Worker     mVolumeSIMD[1] = u4_28_from_float(clampFloatVol(right));
265*ec779b8eSAndroid Build Coastguard Worker }
266*ec779b8eSAndroid Build Coastguard Worker 
resample(int32_t * out,size_t outFrameCount,AudioBufferProvider * provider)267*ec779b8eSAndroid Build Coastguard Worker size_t AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
268*ec779b8eSAndroid Build Coastguard Worker             AudioBufferProvider* provider)
269*ec779b8eSAndroid Build Coastguard Worker {
270*ec779b8eSAndroid Build Coastguard Worker     // FIXME store current state (up or down sample) and only load the coefs when the state
271*ec779b8eSAndroid Build Coastguard Worker     // changes. Or load two pointers one for up and one for down in the init function.
272*ec779b8eSAndroid Build Coastguard Worker     // Not critical now since the read functions are fast, but would be important if read was slow.
273*ec779b8eSAndroid Build Coastguard Worker     if (mConstants == &veryHighQualityConstants && readResampleCoefficients) {
274*ec779b8eSAndroid Build Coastguard Worker         mFirCoefs = readResampleCoefficients( mInSampleRate <= mSampleRate );
275*ec779b8eSAndroid Build Coastguard Worker     } else {
276*ec779b8eSAndroid Build Coastguard Worker         mFirCoefs = (const int32_t *)
277*ec779b8eSAndroid Build Coastguard Worker                 ((mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown);
278*ec779b8eSAndroid Build Coastguard Worker     }
279*ec779b8eSAndroid Build Coastguard Worker 
280*ec779b8eSAndroid Build Coastguard Worker     // select the appropriate resampler
281*ec779b8eSAndroid Build Coastguard Worker     switch (mChannelCount) {
282*ec779b8eSAndroid Build Coastguard Worker     case 1:
283*ec779b8eSAndroid Build Coastguard Worker         return resample<1>(out, outFrameCount, provider);
284*ec779b8eSAndroid Build Coastguard Worker     case 2:
285*ec779b8eSAndroid Build Coastguard Worker         return resample<2>(out, outFrameCount, provider);
286*ec779b8eSAndroid Build Coastguard Worker     default:
287*ec779b8eSAndroid Build Coastguard Worker         LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount);
288*ec779b8eSAndroid Build Coastguard Worker         return 0;
289*ec779b8eSAndroid Build Coastguard Worker     }
290*ec779b8eSAndroid Build Coastguard Worker }
291*ec779b8eSAndroid Build Coastguard Worker 
292*ec779b8eSAndroid Build Coastguard Worker 
293*ec779b8eSAndroid Build Coastguard Worker template<int CHANNELS>
resample(int32_t * out,size_t outFrameCount,AudioBufferProvider * provider)294*ec779b8eSAndroid Build Coastguard Worker size_t AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
295*ec779b8eSAndroid Build Coastguard Worker         AudioBufferProvider* provider)
296*ec779b8eSAndroid Build Coastguard Worker {
297*ec779b8eSAndroid Build Coastguard Worker     const Constants& c(*mConstants);
298*ec779b8eSAndroid Build Coastguard Worker     const size_t headOffset = c.halfNumCoefs*CHANNELS;
299*ec779b8eSAndroid Build Coastguard Worker     int16_t* impulse = mImpulse;
300*ec779b8eSAndroid Build Coastguard Worker     uint32_t vRL = mVolumeRL;
301*ec779b8eSAndroid Build Coastguard Worker     size_t inputIndex = mInputIndex;
302*ec779b8eSAndroid Build Coastguard Worker     uint32_t phaseFraction = mPhaseFraction;
303*ec779b8eSAndroid Build Coastguard Worker     uint32_t phaseIncrement = mPhaseIncrement;
304*ec779b8eSAndroid Build Coastguard Worker     size_t outputIndex = 0;
305*ec779b8eSAndroid Build Coastguard Worker     size_t outputSampleCount = outFrameCount * 2;
306*ec779b8eSAndroid Build Coastguard Worker     size_t inFrameCount = getInFrameCountRequired(outFrameCount);
307*ec779b8eSAndroid Build Coastguard Worker 
308*ec779b8eSAndroid Build Coastguard Worker     while (outputIndex < outputSampleCount) {
309*ec779b8eSAndroid Build Coastguard Worker         // buffer is empty, fetch a new one
310*ec779b8eSAndroid Build Coastguard Worker         while (mBuffer.frameCount == 0) {
311*ec779b8eSAndroid Build Coastguard Worker             mBuffer.frameCount = inFrameCount;
312*ec779b8eSAndroid Build Coastguard Worker             provider->getNextBuffer(&mBuffer);
313*ec779b8eSAndroid Build Coastguard Worker             if (mBuffer.raw == NULL) {
314*ec779b8eSAndroid Build Coastguard Worker                 goto resample_exit;
315*ec779b8eSAndroid Build Coastguard Worker             }
316*ec779b8eSAndroid Build Coastguard Worker             const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
317*ec779b8eSAndroid Build Coastguard Worker             if (phaseIndex == 1) {
318*ec779b8eSAndroid Build Coastguard Worker                 // read one frame
319*ec779b8eSAndroid Build Coastguard Worker                 read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
320*ec779b8eSAndroid Build Coastguard Worker             } else if (phaseIndex == 2) {
321*ec779b8eSAndroid Build Coastguard Worker                 // read 2 frames
322*ec779b8eSAndroid Build Coastguard Worker                 read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
323*ec779b8eSAndroid Build Coastguard Worker                 inputIndex++;
324*ec779b8eSAndroid Build Coastguard Worker                 if (inputIndex >= mBuffer.frameCount) {
325*ec779b8eSAndroid Build Coastguard Worker                     inputIndex -= mBuffer.frameCount;
326*ec779b8eSAndroid Build Coastguard Worker                     provider->releaseBuffer(&mBuffer);
327*ec779b8eSAndroid Build Coastguard Worker                 } else {
328*ec779b8eSAndroid Build Coastguard Worker                     read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
329*ec779b8eSAndroid Build Coastguard Worker                 }
330*ec779b8eSAndroid Build Coastguard Worker             }
331*ec779b8eSAndroid Build Coastguard Worker         }
332*ec779b8eSAndroid Build Coastguard Worker         int16_t const * const in = mBuffer.i16;
333*ec779b8eSAndroid Build Coastguard Worker         const size_t frameCount = mBuffer.frameCount;
334*ec779b8eSAndroid Build Coastguard Worker 
335*ec779b8eSAndroid Build Coastguard Worker         // Always read-in the first samples from the input buffer
336*ec779b8eSAndroid Build Coastguard Worker         int16_t* head = impulse + headOffset;
337*ec779b8eSAndroid Build Coastguard Worker         for (size_t i=0 ; i<CHANNELS ; i++) {
338*ec779b8eSAndroid Build Coastguard Worker             head[i] = in[inputIndex*CHANNELS + i];
339*ec779b8eSAndroid Build Coastguard Worker         }
340*ec779b8eSAndroid Build Coastguard Worker 
341*ec779b8eSAndroid Build Coastguard Worker         // handle boundary case
342*ec779b8eSAndroid Build Coastguard Worker         while (CC_LIKELY(outputIndex < outputSampleCount)) {
343*ec779b8eSAndroid Build Coastguard Worker             filterCoefficient<CHANNELS>(&out[outputIndex], phaseFraction, impulse, vRL);
344*ec779b8eSAndroid Build Coastguard Worker             outputIndex += 2;
345*ec779b8eSAndroid Build Coastguard Worker 
346*ec779b8eSAndroid Build Coastguard Worker             phaseFraction += phaseIncrement;
347*ec779b8eSAndroid Build Coastguard Worker             const size_t phaseIndex = phaseFraction >> kNumPhaseBits;
348*ec779b8eSAndroid Build Coastguard Worker             for (size_t i=0 ; i<phaseIndex ; i++) {
349*ec779b8eSAndroid Build Coastguard Worker                 inputIndex++;
350*ec779b8eSAndroid Build Coastguard Worker                 if (inputIndex >= frameCount) {
351*ec779b8eSAndroid Build Coastguard Worker                     goto done;  // need a new buffer
352*ec779b8eSAndroid Build Coastguard Worker                 }
353*ec779b8eSAndroid Build Coastguard Worker                 read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
354*ec779b8eSAndroid Build Coastguard Worker             }
355*ec779b8eSAndroid Build Coastguard Worker         }
356*ec779b8eSAndroid Build Coastguard Worker done:
357*ec779b8eSAndroid Build Coastguard Worker         // if done with buffer, save samples
358*ec779b8eSAndroid Build Coastguard Worker         if (inputIndex >= frameCount) {
359*ec779b8eSAndroid Build Coastguard Worker             inputIndex -= frameCount;
360*ec779b8eSAndroid Build Coastguard Worker             provider->releaseBuffer(&mBuffer);
361*ec779b8eSAndroid Build Coastguard Worker         }
362*ec779b8eSAndroid Build Coastguard Worker     }
363*ec779b8eSAndroid Build Coastguard Worker 
364*ec779b8eSAndroid Build Coastguard Worker resample_exit:
365*ec779b8eSAndroid Build Coastguard Worker     mImpulse = impulse;
366*ec779b8eSAndroid Build Coastguard Worker     mInputIndex = inputIndex;
367*ec779b8eSAndroid Build Coastguard Worker     mPhaseFraction = phaseFraction;
368*ec779b8eSAndroid Build Coastguard Worker     return outputIndex / CHANNELS;
369*ec779b8eSAndroid Build Coastguard Worker }
370*ec779b8eSAndroid Build Coastguard Worker 
371*ec779b8eSAndroid Build Coastguard Worker template<int CHANNELS>
372*ec779b8eSAndroid Build Coastguard Worker /***
373*ec779b8eSAndroid Build Coastguard Worker * read()
374*ec779b8eSAndroid Build Coastguard Worker *
375*ec779b8eSAndroid Build Coastguard Worker * This function reads only one frame from input buffer and writes it in
376*ec779b8eSAndroid Build Coastguard Worker * state buffer
377*ec779b8eSAndroid Build Coastguard Worker *
378*ec779b8eSAndroid Build Coastguard Worker **/
read(int16_t * & impulse,uint32_t & phaseFraction,const int16_t * in,size_t inputIndex)379*ec779b8eSAndroid Build Coastguard Worker void AudioResamplerSinc::read(
380*ec779b8eSAndroid Build Coastguard Worker         int16_t*& impulse, uint32_t& phaseFraction,
381*ec779b8eSAndroid Build Coastguard Worker         const int16_t* in, size_t inputIndex)
382*ec779b8eSAndroid Build Coastguard Worker {
383*ec779b8eSAndroid Build Coastguard Worker     impulse += CHANNELS;
384*ec779b8eSAndroid Build Coastguard Worker     phaseFraction -= 1LU<<kNumPhaseBits;
385*ec779b8eSAndroid Build Coastguard Worker 
386*ec779b8eSAndroid Build Coastguard Worker     const Constants& c(*mConstants);
387*ec779b8eSAndroid Build Coastguard Worker     if (CC_UNLIKELY(impulse >= mRingFull)) {
388*ec779b8eSAndroid Build Coastguard Worker         const size_t stateSize = (c.halfNumCoefs*2)*CHANNELS;
389*ec779b8eSAndroid Build Coastguard Worker         memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize);
390*ec779b8eSAndroid Build Coastguard Worker         impulse -= stateSize;
391*ec779b8eSAndroid Build Coastguard Worker     }
392*ec779b8eSAndroid Build Coastguard Worker 
393*ec779b8eSAndroid Build Coastguard Worker     int16_t* head = impulse + c.halfNumCoefs*CHANNELS;
394*ec779b8eSAndroid Build Coastguard Worker     for (size_t i=0 ; i<CHANNELS ; i++) {
395*ec779b8eSAndroid Build Coastguard Worker         head[i] = in[inputIndex*CHANNELS + i];
396*ec779b8eSAndroid Build Coastguard Worker     }
397*ec779b8eSAndroid Build Coastguard Worker }
398*ec779b8eSAndroid Build Coastguard Worker 
399*ec779b8eSAndroid Build Coastguard Worker template<int CHANNELS>
filterCoefficient(int32_t * out,uint32_t phase,const int16_t * samples,uint32_t vRL)400*ec779b8eSAndroid Build Coastguard Worker void AudioResamplerSinc::filterCoefficient(int32_t* out, uint32_t phase,
401*ec779b8eSAndroid Build Coastguard Worker          const int16_t *samples, uint32_t vRL)
402*ec779b8eSAndroid Build Coastguard Worker {
403*ec779b8eSAndroid Build Coastguard Worker     // NOTE: be very careful when modifying the code here. register
404*ec779b8eSAndroid Build Coastguard Worker     // pressure is very high and a small change might cause the compiler
405*ec779b8eSAndroid Build Coastguard Worker     // to generate far less efficient code.
406*ec779b8eSAndroid Build Coastguard Worker     // Always validate the result with objdump or test-resample.
407*ec779b8eSAndroid Build Coastguard Worker 
408*ec779b8eSAndroid Build Coastguard Worker     // compute the index of the coefficient on the positive side and
409*ec779b8eSAndroid Build Coastguard Worker     // negative side
410*ec779b8eSAndroid Build Coastguard Worker     const Constants& c(*mConstants);
411*ec779b8eSAndroid Build Coastguard Worker     const int32_t ONE = c.cMask | c.pMask;
412*ec779b8eSAndroid Build Coastguard Worker     uint32_t indexP = ( phase & c.cMask) >> c.cShift;
413*ec779b8eSAndroid Build Coastguard Worker     uint32_t lerpP  = ( phase & c.pMask) >> c.pShift;
414*ec779b8eSAndroid Build Coastguard Worker     uint32_t indexN = ((ONE-phase) & c.cMask) >> c.cShift;
415*ec779b8eSAndroid Build Coastguard Worker     uint32_t lerpN  = ((ONE-phase) & c.pMask) >> c.pShift;
416*ec779b8eSAndroid Build Coastguard Worker 
417*ec779b8eSAndroid Build Coastguard Worker     const size_t offset = c.halfNumCoefs;
418*ec779b8eSAndroid Build Coastguard Worker     indexP *= offset;
419*ec779b8eSAndroid Build Coastguard Worker     indexN *= offset;
420*ec779b8eSAndroid Build Coastguard Worker 
421*ec779b8eSAndroid Build Coastguard Worker     int32_t const* coefsP = mFirCoefs + indexP;
422*ec779b8eSAndroid Build Coastguard Worker     int32_t const* coefsN = mFirCoefs + indexN;
423*ec779b8eSAndroid Build Coastguard Worker     int16_t const* sP = samples;
424*ec779b8eSAndroid Build Coastguard Worker     int16_t const* sN = samples + CHANNELS;
425*ec779b8eSAndroid Build Coastguard Worker 
426*ec779b8eSAndroid Build Coastguard Worker     size_t count = offset;
427*ec779b8eSAndroid Build Coastguard Worker 
428*ec779b8eSAndroid Build Coastguard Worker #if !USE_NEON
429*ec779b8eSAndroid Build Coastguard Worker     int32_t l = 0;
430*ec779b8eSAndroid Build Coastguard Worker     int32_t r = 0;
431*ec779b8eSAndroid Build Coastguard Worker     for (size_t i=0 ; i<count ; i++) {
432*ec779b8eSAndroid Build Coastguard Worker         interpolate<CHANNELS>(l, r, coefsP++, offset, lerpP, sP);
433*ec779b8eSAndroid Build Coastguard Worker         sP -= CHANNELS;
434*ec779b8eSAndroid Build Coastguard Worker         interpolate<CHANNELS>(l, r, coefsN++, offset, lerpN, sN);
435*ec779b8eSAndroid Build Coastguard Worker         sN += CHANNELS;
436*ec779b8eSAndroid Build Coastguard Worker     }
437*ec779b8eSAndroid Build Coastguard Worker     out[0] += 2 * mulRL(1, l, vRL);
438*ec779b8eSAndroid Build Coastguard Worker     out[1] += 2 * mulRL(0, r, vRL);
439*ec779b8eSAndroid Build Coastguard Worker #else
440*ec779b8eSAndroid Build Coastguard Worker     UNUSED(vRL);
441*ec779b8eSAndroid Build Coastguard Worker     if (CHANNELS == 1) {
442*ec779b8eSAndroid Build Coastguard Worker         int32_t const* coefsP1 = coefsP + offset;
443*ec779b8eSAndroid Build Coastguard Worker         int32_t const* coefsN1 = coefsN + offset;
444*ec779b8eSAndroid Build Coastguard Worker         sP -= CHANNELS*3;
445*ec779b8eSAndroid Build Coastguard Worker 
446*ec779b8eSAndroid Build Coastguard Worker         int32x4_t sum;
447*ec779b8eSAndroid Build Coastguard Worker         int32x2_t lerpPN;
448*ec779b8eSAndroid Build Coastguard Worker         lerpPN = vdup_n_s32(0);
449*ec779b8eSAndroid Build Coastguard Worker         lerpPN = vld1_lane_s32((int32_t *)&lerpP, lerpPN, 0);
450*ec779b8eSAndroid Build Coastguard Worker         lerpPN = vld1_lane_s32((int32_t *)&lerpN, lerpPN, 1);
451*ec779b8eSAndroid Build Coastguard Worker         lerpPN = vshl_n_s32(lerpPN, 16);
452*ec779b8eSAndroid Build Coastguard Worker         sum = vdupq_n_s32(0);
453*ec779b8eSAndroid Build Coastguard Worker 
454*ec779b8eSAndroid Build Coastguard Worker         int16x4_t sampleP, sampleN;
455*ec779b8eSAndroid Build Coastguard Worker         int32x4_t samplePExt, sampleNExt;
456*ec779b8eSAndroid Build Coastguard Worker         int32x4_t coefsPV0, coefsPV1, coefsNV0, coefsNV1;
457*ec779b8eSAndroid Build Coastguard Worker 
458*ec779b8eSAndroid Build Coastguard Worker         coefsP = (const int32_t*)__builtin_assume_aligned(coefsP, 16);
459*ec779b8eSAndroid Build Coastguard Worker         coefsN = (const int32_t*)__builtin_assume_aligned(coefsN, 16);
460*ec779b8eSAndroid Build Coastguard Worker         coefsP1 = (const int32_t*)__builtin_assume_aligned(coefsP1, 16);
461*ec779b8eSAndroid Build Coastguard Worker         coefsN1 = (const int32_t*)__builtin_assume_aligned(coefsN1, 16);
462*ec779b8eSAndroid Build Coastguard Worker         for (; count > 0; count -= 4) {
463*ec779b8eSAndroid Build Coastguard Worker             sampleP = vld1_s16(sP);
464*ec779b8eSAndroid Build Coastguard Worker             sampleN = vld1_s16(sN);
465*ec779b8eSAndroid Build Coastguard Worker             coefsPV0 = vld1q_s32(coefsP);
466*ec779b8eSAndroid Build Coastguard Worker             coefsNV0 = vld1q_s32(coefsN);
467*ec779b8eSAndroid Build Coastguard Worker             coefsPV1 = vld1q_s32(coefsP1);
468*ec779b8eSAndroid Build Coastguard Worker             coefsNV1 = vld1q_s32(coefsN1);
469*ec779b8eSAndroid Build Coastguard Worker             sP -= 4;
470*ec779b8eSAndroid Build Coastguard Worker             sN += 4;
471*ec779b8eSAndroid Build Coastguard Worker             coefsP += 4;
472*ec779b8eSAndroid Build Coastguard Worker             coefsN += 4;
473*ec779b8eSAndroid Build Coastguard Worker             coefsP1 += 4;
474*ec779b8eSAndroid Build Coastguard Worker             coefsN1 += 4;
475*ec779b8eSAndroid Build Coastguard Worker 
476*ec779b8eSAndroid Build Coastguard Worker             sampleP = vrev64_s16(sampleP);
477*ec779b8eSAndroid Build Coastguard Worker 
478*ec779b8eSAndroid Build Coastguard Worker             // interpolate (step1)
479*ec779b8eSAndroid Build Coastguard Worker             coefsPV1 = vsubq_s32(coefsPV1, coefsPV0);
480*ec779b8eSAndroid Build Coastguard Worker             coefsNV1 = vsubq_s32(coefsNV1, coefsNV0);
481*ec779b8eSAndroid Build Coastguard Worker             samplePExt = vshll_n_s16(sampleP, 15);
482*ec779b8eSAndroid Build Coastguard Worker             // interpolate (step2)
483*ec779b8eSAndroid Build Coastguard Worker             coefsPV1 = vqrdmulhq_lane_s32(coefsPV1, lerpPN, 0);
484*ec779b8eSAndroid Build Coastguard Worker             coefsNV1 = vqrdmulhq_lane_s32(coefsNV1, lerpPN, 1);
485*ec779b8eSAndroid Build Coastguard Worker             sampleNExt = vshll_n_s16(sampleN, 15);
486*ec779b8eSAndroid Build Coastguard Worker             // interpolate (step3)
487*ec779b8eSAndroid Build Coastguard Worker             coefsPV0 = vaddq_s32(coefsPV0, coefsPV1);
488*ec779b8eSAndroid Build Coastguard Worker             coefsNV0 = vaddq_s32(coefsNV0, coefsNV1);
489*ec779b8eSAndroid Build Coastguard Worker 
490*ec779b8eSAndroid Build Coastguard Worker             samplePExt = vqrdmulhq_s32(samplePExt, coefsPV0);
491*ec779b8eSAndroid Build Coastguard Worker             sampleNExt = vqrdmulhq_s32(sampleNExt, coefsNV0);
492*ec779b8eSAndroid Build Coastguard Worker             sum = vaddq_s32(sum, samplePExt);
493*ec779b8eSAndroid Build Coastguard Worker             sum = vaddq_s32(sum, sampleNExt);
494*ec779b8eSAndroid Build Coastguard Worker         }
495*ec779b8eSAndroid Build Coastguard Worker         int32x2_t volumesV, outV;
496*ec779b8eSAndroid Build Coastguard Worker         volumesV = vld1_s32(mVolumeSIMD);
497*ec779b8eSAndroid Build Coastguard Worker         outV = vld1_s32(out);
498*ec779b8eSAndroid Build Coastguard Worker 
499*ec779b8eSAndroid Build Coastguard Worker         //add all 4 partial sums
500*ec779b8eSAndroid Build Coastguard Worker         int32x2_t sumLow, sumHigh;
501*ec779b8eSAndroid Build Coastguard Worker         sumLow = vget_low_s32(sum);
502*ec779b8eSAndroid Build Coastguard Worker         sumHigh = vget_high_s32(sum);
503*ec779b8eSAndroid Build Coastguard Worker         sumLow = vpadd_s32(sumLow, sumHigh);
504*ec779b8eSAndroid Build Coastguard Worker         sumLow = vpadd_s32(sumLow, sumLow);
505*ec779b8eSAndroid Build Coastguard Worker 
506*ec779b8eSAndroid Build Coastguard Worker         sumLow = vqrdmulh_s32(sumLow, volumesV);
507*ec779b8eSAndroid Build Coastguard Worker         outV = vadd_s32(outV, sumLow);
508*ec779b8eSAndroid Build Coastguard Worker         vst1_s32(out, outV);
509*ec779b8eSAndroid Build Coastguard Worker     } else if (CHANNELS == 2) {
510*ec779b8eSAndroid Build Coastguard Worker         int32_t const* coefsP1 = coefsP + offset;
511*ec779b8eSAndroid Build Coastguard Worker         int32_t const* coefsN1 = coefsN + offset;
512*ec779b8eSAndroid Build Coastguard Worker         sP -= CHANNELS*3;
513*ec779b8eSAndroid Build Coastguard Worker 
514*ec779b8eSAndroid Build Coastguard Worker         int32x4_t sum0, sum1;
515*ec779b8eSAndroid Build Coastguard Worker         int32x2_t lerpPN;
516*ec779b8eSAndroid Build Coastguard Worker 
517*ec779b8eSAndroid Build Coastguard Worker         lerpPN = vdup_n_s32(0);
518*ec779b8eSAndroid Build Coastguard Worker         lerpPN = vld1_lane_s32((int32_t *)&lerpP, lerpPN, 0);
519*ec779b8eSAndroid Build Coastguard Worker         lerpPN = vld1_lane_s32((int32_t *)&lerpN, lerpPN, 1);
520*ec779b8eSAndroid Build Coastguard Worker         lerpPN = vshl_n_s32(lerpPN, 16);
521*ec779b8eSAndroid Build Coastguard Worker         sum0 = vdupq_n_s32(0);
522*ec779b8eSAndroid Build Coastguard Worker         sum1 = vdupq_n_s32(0);
523*ec779b8eSAndroid Build Coastguard Worker 
524*ec779b8eSAndroid Build Coastguard Worker         int16x4x2_t sampleP, sampleN;
525*ec779b8eSAndroid Build Coastguard Worker         int32x4x2_t samplePExt, sampleNExt;
526*ec779b8eSAndroid Build Coastguard Worker         int32x4_t coefsPV0, coefsPV1, coefsNV0, coefsNV1;
527*ec779b8eSAndroid Build Coastguard Worker 
528*ec779b8eSAndroid Build Coastguard Worker         coefsP = (const int32_t*)__builtin_assume_aligned(coefsP, 16);
529*ec779b8eSAndroid Build Coastguard Worker         coefsN = (const int32_t*)__builtin_assume_aligned(coefsN, 16);
530*ec779b8eSAndroid Build Coastguard Worker         coefsP1 = (const int32_t*)__builtin_assume_aligned(coefsP1, 16);
531*ec779b8eSAndroid Build Coastguard Worker         coefsN1 = (const int32_t*)__builtin_assume_aligned(coefsN1, 16);
532*ec779b8eSAndroid Build Coastguard Worker         for (; count > 0; count -= 4) {
533*ec779b8eSAndroid Build Coastguard Worker             sampleP = vld2_s16(sP);
534*ec779b8eSAndroid Build Coastguard Worker             sampleN = vld2_s16(sN);
535*ec779b8eSAndroid Build Coastguard Worker             coefsPV0 = vld1q_s32(coefsP);
536*ec779b8eSAndroid Build Coastguard Worker             coefsNV0 = vld1q_s32(coefsN);
537*ec779b8eSAndroid Build Coastguard Worker             coefsPV1 = vld1q_s32(coefsP1);
538*ec779b8eSAndroid Build Coastguard Worker             coefsNV1 = vld1q_s32(coefsN1);
539*ec779b8eSAndroid Build Coastguard Worker             sP -= 8;
540*ec779b8eSAndroid Build Coastguard Worker             sN += 8;
541*ec779b8eSAndroid Build Coastguard Worker             coefsP += 4;
542*ec779b8eSAndroid Build Coastguard Worker             coefsN += 4;
543*ec779b8eSAndroid Build Coastguard Worker             coefsP1 += 4;
544*ec779b8eSAndroid Build Coastguard Worker             coefsN1 += 4;
545*ec779b8eSAndroid Build Coastguard Worker 
546*ec779b8eSAndroid Build Coastguard Worker             sampleP.val[0] = vrev64_s16(sampleP.val[0]);
547*ec779b8eSAndroid Build Coastguard Worker             sampleP.val[1] = vrev64_s16(sampleP.val[1]);
548*ec779b8eSAndroid Build Coastguard Worker 
549*ec779b8eSAndroid Build Coastguard Worker             // interpolate (step1)
550*ec779b8eSAndroid Build Coastguard Worker             coefsPV1 = vsubq_s32(coefsPV1, coefsPV0);
551*ec779b8eSAndroid Build Coastguard Worker             coefsNV1 = vsubq_s32(coefsNV1, coefsNV0);
552*ec779b8eSAndroid Build Coastguard Worker             samplePExt.val[0] = vshll_n_s16(sampleP.val[0], 15);
553*ec779b8eSAndroid Build Coastguard Worker             samplePExt.val[1] = vshll_n_s16(sampleP.val[1], 15);
554*ec779b8eSAndroid Build Coastguard Worker             // interpolate (step2)
555*ec779b8eSAndroid Build Coastguard Worker             coefsPV1 = vqrdmulhq_lane_s32(coefsPV1, lerpPN, 0);
556*ec779b8eSAndroid Build Coastguard Worker             coefsNV1 = vqrdmulhq_lane_s32(coefsNV1, lerpPN, 1);
557*ec779b8eSAndroid Build Coastguard Worker             sampleNExt.val[0] = vshll_n_s16(sampleN.val[0], 15);
558*ec779b8eSAndroid Build Coastguard Worker             sampleNExt.val[1] = vshll_n_s16(sampleN.val[1], 15);
559*ec779b8eSAndroid Build Coastguard Worker             // interpolate (step3)
560*ec779b8eSAndroid Build Coastguard Worker             coefsPV0 = vaddq_s32(coefsPV0, coefsPV1);
561*ec779b8eSAndroid Build Coastguard Worker             coefsNV0 = vaddq_s32(coefsNV0, coefsNV1);
562*ec779b8eSAndroid Build Coastguard Worker 
563*ec779b8eSAndroid Build Coastguard Worker             samplePExt.val[0] = vqrdmulhq_s32(samplePExt.val[0], coefsPV0);
564*ec779b8eSAndroid Build Coastguard Worker             samplePExt.val[1] = vqrdmulhq_s32(samplePExt.val[1], coefsPV0);
565*ec779b8eSAndroid Build Coastguard Worker             sampleNExt.val[0] = vqrdmulhq_s32(sampleNExt.val[0], coefsNV0);
566*ec779b8eSAndroid Build Coastguard Worker             sampleNExt.val[1] = vqrdmulhq_s32(sampleNExt.val[1], coefsNV0);
567*ec779b8eSAndroid Build Coastguard Worker             sum0 = vaddq_s32(sum0, samplePExt.val[0]);
568*ec779b8eSAndroid Build Coastguard Worker             sum1 = vaddq_s32(sum1, samplePExt.val[1]);
569*ec779b8eSAndroid Build Coastguard Worker             sum0 = vaddq_s32(sum0, sampleNExt.val[0]);
570*ec779b8eSAndroid Build Coastguard Worker             sum1 = vaddq_s32(sum1, sampleNExt.val[1]);
571*ec779b8eSAndroid Build Coastguard Worker         }
572*ec779b8eSAndroid Build Coastguard Worker         int32x2_t volumesV, outV;
573*ec779b8eSAndroid Build Coastguard Worker         volumesV = vld1_s32(mVolumeSIMD);
574*ec779b8eSAndroid Build Coastguard Worker         outV = vld1_s32(out);
575*ec779b8eSAndroid Build Coastguard Worker 
576*ec779b8eSAndroid Build Coastguard Worker         //add all 4 partial sums
577*ec779b8eSAndroid Build Coastguard Worker         int32x2_t sumLow0, sumHigh0, sumLow1, sumHigh1;
578*ec779b8eSAndroid Build Coastguard Worker         sumLow0 = vget_low_s32(sum0);
579*ec779b8eSAndroid Build Coastguard Worker         sumHigh0 = vget_high_s32(sum0);
580*ec779b8eSAndroid Build Coastguard Worker         sumLow1 = vget_low_s32(sum1);
581*ec779b8eSAndroid Build Coastguard Worker         sumHigh1 = vget_high_s32(sum1);
582*ec779b8eSAndroid Build Coastguard Worker         sumLow0 = vpadd_s32(sumLow0, sumHigh0);
583*ec779b8eSAndroid Build Coastguard Worker         sumLow0 = vpadd_s32(sumLow0, sumLow0);
584*ec779b8eSAndroid Build Coastguard Worker         sumLow1 = vpadd_s32(sumLow1, sumHigh1);
585*ec779b8eSAndroid Build Coastguard Worker         sumLow1 = vpadd_s32(sumLow1, sumLow1);
586*ec779b8eSAndroid Build Coastguard Worker 
587*ec779b8eSAndroid Build Coastguard Worker         sumLow0 = vtrn_s32(sumLow0, sumLow1).val[0];
588*ec779b8eSAndroid Build Coastguard Worker         sumLow0 = vqrdmulh_s32(sumLow0, volumesV);
589*ec779b8eSAndroid Build Coastguard Worker         outV = vadd_s32(outV, sumLow0);
590*ec779b8eSAndroid Build Coastguard Worker         vst1_s32(out, outV);
591*ec779b8eSAndroid Build Coastguard Worker     }
592*ec779b8eSAndroid Build Coastguard Worker #endif
593*ec779b8eSAndroid Build Coastguard Worker }
594*ec779b8eSAndroid Build Coastguard Worker 
595*ec779b8eSAndroid Build Coastguard Worker template<int CHANNELS>
interpolate(int32_t & l,int32_t & r,const int32_t * coefs,size_t offset,int32_t lerp,const int16_t * samples)596*ec779b8eSAndroid Build Coastguard Worker void AudioResamplerSinc::interpolate(
597*ec779b8eSAndroid Build Coastguard Worker         int32_t& l, int32_t& r,
598*ec779b8eSAndroid Build Coastguard Worker         const int32_t* coefs, size_t offset,
599*ec779b8eSAndroid Build Coastguard Worker         int32_t lerp, const int16_t* samples)
600*ec779b8eSAndroid Build Coastguard Worker {
601*ec779b8eSAndroid Build Coastguard Worker     int32_t c0 = coefs[0];
602*ec779b8eSAndroid Build Coastguard Worker     int32_t c1 = coefs[offset];
603*ec779b8eSAndroid Build Coastguard Worker     int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0);
604*ec779b8eSAndroid Build Coastguard Worker     if (CHANNELS == 2) {
605*ec779b8eSAndroid Build Coastguard Worker         uint32_t rl = *reinterpret_cast<const uint32_t*>(samples);
606*ec779b8eSAndroid Build Coastguard Worker         l = mulAddRL(1, rl, sinc, l);
607*ec779b8eSAndroid Build Coastguard Worker         r = mulAddRL(0, rl, sinc, r);
608*ec779b8eSAndroid Build Coastguard Worker     } else {
609*ec779b8eSAndroid Build Coastguard Worker         r = l = mulAdd(samples[0], sinc, l);
610*ec779b8eSAndroid Build Coastguard Worker     }
611*ec779b8eSAndroid Build Coastguard Worker }
612*ec779b8eSAndroid Build Coastguard Worker // ----------------------------------------------------------------------------
613*ec779b8eSAndroid Build Coastguard Worker } // namespace android
614