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