blob: 82a3d66fbc11ebf00dcf4d91581013c7a06d7326 [file] [log] [blame]
Mathias Agopian65ab4712010-07-14 17:59:35 -07001/*
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
SathishKumar Mani76b11162012-01-17 10:49:47 -080017#define LOG_TAG "AudioResamplerSinc"
18//#define LOG_NDEBUG 0
19
Zhongwei Yao12b44bd2014-04-10 17:23:42 +010020#define __STDC_CONSTANT_MACROS
Mathias Agopian7aa7ed72012-11-05 01:51:37 -080021#include <malloc.h>
Mathias Agopian65ab4712010-07-14 17:59:35 -070022#include <string.h>
SathishKumar Mani76b11162012-01-17 10:49:47 -080023#include <stdlib.h>
Mathias Agopian46afbec2012-11-04 02:03:49 -080024#include <dlfcn.h>
25
Mathias Agopiana798c972012-11-03 23:37:53 -070026#include <cutils/compiler.h>
Mathias Agopian46afbec2012-11-04 02:03:49 -080027#include <cutils/properties.h>
28
29#include <utils/Log.h>
Andy Hung5e58b0a2014-06-23 19:07:29 -070030#include <audio_utils/primitives.h>
Mathias Agopian46afbec2012-11-04 02:03:49 -080031
32#include "AudioResamplerSinc.h"
33
Bernhard Rosenkraenzer4fbf2322014-09-19 01:50:16 +020034#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
Mathias Agopianad9af032012-11-04 15:16:13 -080038
39#if defined(__arm__) && !defined(__thumb__)
40#define USE_INLINE_ASSEMBLY (true)
41#else
42#define USE_INLINE_ASSEMBLY (false)
43#endif
44
Zhongwei Yao12b44bd2014-04-10 17:23:42 +010045#if defined(__aarch64__) || defined(__ARM_NEON__)
46#include <arm_neon.h>
47#define USE_NEON
Mathias Agopianad9af032012-11-04 15:16:13 -080048#else
Zhongwei Yao12b44bd2014-04-10 17:23:42 +010049#undef USE_NEON
Mathias Agopianad9af032012-11-04 15:16:13 -080050#endif
51
Zhongwei Yao12b44bd2014-04-10 17:23:42 +010052#define UNUSED(x) ((void)(x))
Mathias Agopianad9af032012-11-04 15:16:13 -080053
Mathias Agopian65ab4712010-07-14 17:59:35 -070054namespace android {
55// ----------------------------------------------------------------------------
56
57
58/*
59 * These coeficients are computed with the "fir" utility found in
60 * tools/resampler_tools
Mathias Agopiand88a0512012-10-30 12:49:07 -070061 * cmd-line: fir -l 7 -s 48000 -c 20478
Mathias Agopian65ab4712010-07-14 17:59:35 -070062 */
Glenn Kastenc4974312012-12-14 07:13:28 -080063const uint32_t AudioResamplerSinc::mFirCoefsUp[] __attribute__ ((aligned (32))) = {
Glenn Kasten675933b2015-02-17 14:23:04 -080064#include "AudioResamplerSincUp.h"
Mathias Agopian65ab4712010-07-14 17:59:35 -070065};
66
67/*
Mathias Agopian443e6962012-10-26 13:48:42 -070068 * These coefficients are optimized for 48KHz -> 44.1KHz
Mathias Agopian4ed475d2012-11-01 21:03:46 -070069 * cmd-line: fir -l 7 -s 48000 -c 17189
Mathias Agopian65ab4712010-07-14 17:59:35 -070070 */
Glenn Kastenc4974312012-12-14 07:13:28 -080071const uint32_t AudioResamplerSinc::mFirCoefsDown[] __attribute__ ((aligned (32))) = {
Glenn Kasten675933b2015-02-17 14:23:04 -080072#include "AudioResamplerSincDown.h"
Mathias Agopian65ab4712010-07-14 17:59:35 -070073};
74
Glenn Kastenac602052012-10-01 14:04:31 -070075// we use 15 bits to interpolate between these samples
76// this cannot change because the mul below rely on it.
77static const int pLerpBits = 15;
78
79static pthread_once_t once_control = PTHREAD_ONCE_INIT;
80static readCoefficientsFn readResampleCoefficients = NULL;
81
82/*static*/ AudioResamplerSinc::Constants AudioResamplerSinc::highQualityConstants;
83/*static*/ AudioResamplerSinc::Constants AudioResamplerSinc::veryHighQualityConstants;
84
85void AudioResamplerSinc::init_routine()
86{
87 // for high quality resampler, the parameters for coefficients are compile-time constants
88 Constants *c = &highQualityConstants;
89 c->coefsBits = RESAMPLE_FIR_LERP_INT_BITS;
90 c->cShift = kNumPhaseBits - c->coefsBits;
91 c->cMask = ((1<< c->coefsBits)-1) << c->cShift;
92 c->pShift = kNumPhaseBits - c->coefsBits - pLerpBits;
93 c->pMask = ((1<< pLerpBits)-1) << c->pShift;
94 c->halfNumCoefs = RESAMPLE_FIR_NUM_COEF;
95
96 // for very high quality resampler, the parameters are load-time constants
97 veryHighQualityConstants = highQualityConstants;
98
99 // Open the dll to get the coefficients for VERY_HIGH_QUALITY
100 void *resampleCoeffLib = dlopen("libaudio-resampler.so", RTLD_NOW);
101 ALOGV("Open libaudio-resampler library = %p", resampleCoeffLib);
102 if (resampleCoeffLib == NULL) {
103 ALOGE("Could not open audio-resampler library: %s", dlerror());
104 return;
105 }
106
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800107 readResampleFirNumCoeffFn readResampleFirNumCoeff;
108 readResampleFirLerpIntBitsFn readResampleFirLerpIntBits;
109
110 readResampleCoefficients = (readCoefficientsFn)
111 dlsym(resampleCoeffLib, "readResamplerCoefficients");
112 readResampleFirNumCoeff = (readResampleFirNumCoeffFn)
Glenn Kastenac602052012-10-01 14:04:31 -0700113 dlsym(resampleCoeffLib, "readResampleFirNumCoeff");
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800114 readResampleFirLerpIntBits = (readResampleFirLerpIntBitsFn)
Glenn Kastenac602052012-10-01 14:04:31 -0700115 dlsym(resampleCoeffLib, "readResampleFirLerpIntBits");
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800116
Glenn Kastenac602052012-10-01 14:04:31 -0700117 if (!readResampleCoefficients || !readResampleFirNumCoeff || !readResampleFirLerpIntBits) {
118 readResampleCoefficients = NULL;
119 dlclose(resampleCoeffLib);
120 resampleCoeffLib = NULL;
121 ALOGE("Could not find symbol: %s", dlerror());
122 return;
123 }
124
125 c = &veryHighQualityConstants;
Glenn Kastenac602052012-10-01 14:04:31 -0700126 c->coefsBits = readResampleFirLerpIntBits();
Glenn Kastenac602052012-10-01 14:04:31 -0700127 c->cShift = kNumPhaseBits - c->coefsBits;
128 c->cMask = ((1<<c->coefsBits)-1) << c->cShift;
129 c->pShift = kNumPhaseBits - c->coefsBits - pLerpBits;
130 c->pMask = ((1<<pLerpBits)-1) << c->pShift;
131 // number of zero-crossing on each side
132 c->halfNumCoefs = readResampleFirNumCoeff();
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800133 ALOGV("coefsBits = %d", c->coefsBits);
Glenn Kastenac602052012-10-01 14:04:31 -0700134 ALOGV("halfNumCoefs = %d", c->halfNumCoefs);
135 // note that we "leak" resampleCoeffLib until the process exits
136}
SathishKumar Mani76b11162012-01-17 10:49:47 -0800137
Mathias Agopian65ab4712010-07-14 17:59:35 -0700138// ----------------------------------------------------------------------------
139
140static inline
141int32_t mulRL(int left, int32_t in, uint32_t vRL)
142{
Mathias Agopianad9af032012-11-04 15:16:13 -0800143#if USE_INLINE_ASSEMBLY
Mathias Agopian65ab4712010-07-14 17:59:35 -0700144 int32_t out;
145 if (left) {
146 asm( "smultb %[out], %[in], %[vRL] \n"
147 : [out]"=r"(out)
148 : [in]"%r"(in), [vRL]"r"(vRL)
149 : );
150 } else {
151 asm( "smultt %[out], %[in], %[vRL] \n"
152 : [out]"=r"(out)
153 : [in]"%r"(in), [vRL]"r"(vRL)
154 : );
155 }
156 return out;
157#else
Mathias Agopian1f09b4a2012-10-30 13:51:44 -0700158 int16_t v = left ? int16_t(vRL) : int16_t(vRL>>16);
159 return int32_t((int64_t(in) * v) >> 16);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700160#endif
161}
162
163static inline
164int32_t mulAdd(int16_t in, int32_t v, int32_t a)
165{
Mathias Agopianad9af032012-11-04 15:16:13 -0800166#if USE_INLINE_ASSEMBLY
Mathias Agopian65ab4712010-07-14 17:59:35 -0700167 int32_t out;
168 asm( "smlawb %[out], %[v], %[in], %[a] \n"
169 : [out]"=r"(out)
170 : [in]"%r"(in), [v]"r"(v), [a]"r"(a)
171 : );
172 return out;
173#else
Mathias Agopian1f09b4a2012-10-30 13:51:44 -0700174 return a + int32_t((int64_t(v) * in) >> 16);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700175#endif
176}
177
178static inline
179int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
180{
Mathias Agopianad9af032012-11-04 15:16:13 -0800181#if USE_INLINE_ASSEMBLY
Mathias Agopian65ab4712010-07-14 17:59:35 -0700182 int32_t out;
183 if (left) {
184 asm( "smlawb %[out], %[v], %[inRL], %[a] \n"
185 : [out]"=r"(out)
186 : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
187 : );
188 } else {
189 asm( "smlawt %[out], %[v], %[inRL], %[a] \n"
190 : [out]"=r"(out)
191 : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
192 : );
193 }
194 return out;
195#else
Mathias Agopian1f09b4a2012-10-30 13:51:44 -0700196 int16_t s = left ? int16_t(inRL) : int16_t(inRL>>16);
197 return a + int32_t((int64_t(v) * s) >> 16);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700198#endif
199}
200
201// ----------------------------------------------------------------------------
202
Andy Hung3348e362014-07-07 10:21:44 -0700203AudioResamplerSinc::AudioResamplerSinc(
Glenn Kastenac602052012-10-01 14:04:31 -0700204 int inChannelCount, int32_t sampleRate, src_quality quality)
Andy Hung3348e362014-07-07 10:21:44 -0700205 : AudioResampler(inChannelCount, sampleRate, quality),
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800206 mState(0), mImpulse(0), mRingFull(0), mFirCoefs(0)
Mathias Agopian65ab4712010-07-14 17:59:35 -0700207{
208 /*
209 * Layout of the state buffer for 32 tap:
210 *
211 * "present" sample beginning of 2nd buffer
212 * v v
213 * 0 01 2 23 3
214 * 0 F0 0 F0 F
215 * [pppppppppppppppInnnnnnnnnnnnnnnnpppppppppppppppInnnnnnnnnnnnnnnn]
216 * ^ ^ head
217 *
218 * p = past samples, convoluted with the (p)ositive side of sinc()
219 * n = future samples, convoluted with the (n)egative side of sinc()
220 * r = extra space for implementing the ring buffer
221 *
222 */
223
Mathias Agopian0d585c82012-11-10 03:26:39 -0800224 mVolumeSIMD[0] = 0;
225 mVolumeSIMD[1] = 0;
226
Glenn Kastenac602052012-10-01 14:04:31 -0700227 // Load the constants for coefficients
228 int ok = pthread_once(&once_control, init_routine);
229 if (ok != 0) {
230 ALOGE("%s pthread_once failed: %d", __func__, ok);
SathishKumar Mani76b11162012-01-17 10:49:47 -0800231 }
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800232 mConstants = (quality == VERY_HIGH_QUALITY) ?
233 &veryHighQualityConstants : &highQualityConstants;
Mathias Agopian65ab4712010-07-14 17:59:35 -0700234}
235
SathishKumar Mani76b11162012-01-17 10:49:47 -0800236
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800237AudioResamplerSinc::~AudioResamplerSinc() {
238 free(mState);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700239}
240
241void AudioResamplerSinc::init() {
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800242 const Constants& c(*mConstants);
243 const size_t numCoefs = 2 * c.halfNumCoefs;
SathishKumar Mani76b11162012-01-17 10:49:47 -0800244 const size_t stateSize = numCoefs * mChannelCount * 2;
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800245 mState = (int16_t*)memalign(32, stateSize*sizeof(int16_t));
SathishKumar Mani76b11162012-01-17 10:49:47 -0800246 memset(mState, 0, sizeof(int16_t)*stateSize);
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800247 mImpulse = mState + (c.halfNumCoefs-1)*mChannelCount;
SathishKumar Mani76b11162012-01-17 10:49:47 -0800248 mRingFull = mImpulse + (numCoefs+1)*mChannelCount;
Mathias Agopian65ab4712010-07-14 17:59:35 -0700249}
250
Andy Hung5e58b0a2014-06-23 19:07:29 -0700251void AudioResamplerSinc::setVolume(float left, float right) {
Mathias Agopian0d585c82012-11-10 03:26:39 -0800252 AudioResampler::setVolume(left, right);
Andy Hung5e58b0a2014-06-23 19:07:29 -0700253 // convert to U4_28 (rounding down).
254 // integer volume values are clamped to 0 to UNITY_GAIN.
255 mVolumeSIMD[0] = u4_28_from_float(clampFloatVol(left));
256 mVolumeSIMD[1] = u4_28_from_float(clampFloatVol(right));
Mathias Agopian0d585c82012-11-10 03:26:39 -0800257}
258
Mathias Agopian65ab4712010-07-14 17:59:35 -0700259void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
260 AudioBufferProvider* provider)
261{
Glenn Kastenac602052012-10-01 14:04:31 -0700262 // FIXME store current state (up or down sample) and only load the coefs when the state
263 // changes. Or load two pointers one for up and one for down in the init function.
264 // Not critical now since the read functions are fast, but would be important if read was slow.
Mathias Agopian61ea1172012-10-21 03:04:05 -0700265 if (mConstants == &veryHighQualityConstants && readResampleCoefficients) {
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800266 mFirCoefs = readResampleCoefficients( mInSampleRate <= mSampleRate );
Glenn Kastenac602052012-10-01 14:04:31 -0700267 } else {
Glenn Kastenc4974312012-12-14 07:13:28 -0800268 mFirCoefs = (const int32_t *) ((mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown);
SathishKumar Mani76b11162012-01-17 10:49:47 -0800269 }
Mathias Agopian65ab4712010-07-14 17:59:35 -0700270
271 // select the appropriate resampler
272 switch (mChannelCount) {
273 case 1:
274 resample<1>(out, outFrameCount, provider);
275 break;
276 case 2:
277 resample<2>(out, outFrameCount, provider);
278 break;
279 }
280}
281
282
283template<int CHANNELS>
284void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
285 AudioBufferProvider* provider)
286{
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800287 const Constants& c(*mConstants);
288 const size_t headOffset = c.halfNumCoefs*CHANNELS;
Mathias Agopian65ab4712010-07-14 17:59:35 -0700289 int16_t* impulse = mImpulse;
290 uint32_t vRL = mVolumeRL;
291 size_t inputIndex = mInputIndex;
292 uint32_t phaseFraction = mPhaseFraction;
293 uint32_t phaseIncrement = mPhaseIncrement;
294 size_t outputIndex = 0;
295 size_t outputSampleCount = outFrameCount * 2;
Andy Hung24781ff2014-02-19 12:45:19 -0800296 size_t inFrameCount = getInFrameCountRequired(outFrameCount);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700297
Mathias Agopian65ab4712010-07-14 17:59:35 -0700298 while (outputIndex < outputSampleCount) {
299 // buffer is empty, fetch a new one
Glenn Kastend198b612012-02-02 14:09:43 -0800300 while (mBuffer.frameCount == 0) {
301 mBuffer.frameCount = inFrameCount;
John Grossman4ff14ba2012-02-08 16:37:41 -0800302 provider->getNextBuffer(&mBuffer,
303 calculateOutputPTS(outputIndex / 2));
Glenn Kastend198b612012-02-02 14:09:43 -0800304 if (mBuffer.raw == NULL) {
Mathias Agopian65ab4712010-07-14 17:59:35 -0700305 goto resample_exit;
306 }
307 const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
308 if (phaseIndex == 1) {
309 // read one frame
Glenn Kastend198b612012-02-02 14:09:43 -0800310 read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700311 } else if (phaseIndex == 2) {
312 // read 2 frames
Glenn Kastend198b612012-02-02 14:09:43 -0800313 read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700314 inputIndex++;
315 if (inputIndex >= mBuffer.frameCount) {
316 inputIndex -= mBuffer.frameCount;
Glenn Kastend198b612012-02-02 14:09:43 -0800317 provider->releaseBuffer(&mBuffer);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700318 } else {
Glenn Kastend198b612012-02-02 14:09:43 -0800319 read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700320 }
Glenn Kastene53b9ea2012-03-12 16:29:55 -0700321 }
Mathias Agopian65ab4712010-07-14 17:59:35 -0700322 }
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800323 int16_t const * const in = mBuffer.i16;
Glenn Kastend198b612012-02-02 14:09:43 -0800324 const size_t frameCount = mBuffer.frameCount;
Mathias Agopian65ab4712010-07-14 17:59:35 -0700325
326 // Always read-in the first samples from the input buffer
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800327 int16_t* head = impulse + headOffset;
Mathias Agopiana798c972012-11-03 23:37:53 -0700328 for (size_t i=0 ; i<CHANNELS ; i++) {
329 head[i] = in[inputIndex*CHANNELS + i];
330 }
Mathias Agopian65ab4712010-07-14 17:59:35 -0700331
332 // handle boundary case
Mathias Agopiana798c972012-11-03 23:37:53 -0700333 while (CC_LIKELY(outputIndex < outputSampleCount)) {
Mathias Agopian0d585c82012-11-10 03:26:39 -0800334 filterCoefficient<CHANNELS>(&out[outputIndex], phaseFraction, impulse, vRL);
335 outputIndex += 2;
Mathias Agopian65ab4712010-07-14 17:59:35 -0700336
337 phaseFraction += phaseIncrement;
Mathias Agopiana798c972012-11-03 23:37:53 -0700338 const size_t phaseIndex = phaseFraction >> kNumPhaseBits;
339 for (size_t i=0 ; i<phaseIndex ; i++) {
Mathias Agopian65ab4712010-07-14 17:59:35 -0700340 inputIndex++;
Mathias Agopiana798c972012-11-03 23:37:53 -0700341 if (inputIndex >= frameCount) {
342 goto done; // need a new buffer
343 }
Mathias Agopian65ab4712010-07-14 17:59:35 -0700344 read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
345 }
346 }
Mathias Agopiana798c972012-11-03 23:37:53 -0700347done:
Mathias Agopian65ab4712010-07-14 17:59:35 -0700348 // if done with buffer, save samples
349 if (inputIndex >= frameCount) {
350 inputIndex -= frameCount;
Glenn Kastend198b612012-02-02 14:09:43 -0800351 provider->releaseBuffer(&mBuffer);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700352 }
353 }
354
355resample_exit:
356 mImpulse = impulse;
357 mInputIndex = inputIndex;
358 mPhaseFraction = phaseFraction;
359}
360
361template<int CHANNELS>
362/***
363* read()
364*
365* This function reads only one frame from input buffer and writes it in
366* state buffer
367*
368**/
369void AudioResamplerSinc::read(
370 int16_t*& impulse, uint32_t& phaseFraction,
Glenn Kasten54c3b662012-01-06 07:46:30 -0800371 const int16_t* in, size_t inputIndex)
Mathias Agopian65ab4712010-07-14 17:59:35 -0700372{
Mathias Agopian65ab4712010-07-14 17:59:35 -0700373 impulse += CHANNELS;
374 phaseFraction -= 1LU<<kNumPhaseBits;
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800375
376 const Constants& c(*mConstants);
Mathias Agopiana798c972012-11-03 23:37:53 -0700377 if (CC_UNLIKELY(impulse >= mRingFull)) {
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800378 const size_t stateSize = (c.halfNumCoefs*2)*CHANNELS;
Mathias Agopian65ab4712010-07-14 17:59:35 -0700379 memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize);
380 impulse -= stateSize;
381 }
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800382
383 int16_t* head = impulse + c.halfNumCoefs*CHANNELS;
Mathias Agopiana798c972012-11-03 23:37:53 -0700384 for (size_t i=0 ; i<CHANNELS ; i++) {
385 head[i] = in[inputIndex*CHANNELS + i];
386 }
Mathias Agopian65ab4712010-07-14 17:59:35 -0700387}
388
389template<int CHANNELS>
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100390void AudioResamplerSinc::filterCoefficient(int32_t* out, uint32_t phase,
391 const int16_t *samples, uint32_t vRL)
Mathias Agopian65ab4712010-07-14 17:59:35 -0700392{
Mathias Agopian7492a7f2012-11-10 04:44:30 -0800393 // NOTE: be very careful when modifying the code here. register
394 // pressure is very high and a small change might cause the compiler
395 // to generate far less efficient code.
396 // Always sanity check the result with objdump or test-resample.
397
Mathias Agopian65ab4712010-07-14 17:59:35 -0700398 // compute the index of the coefficient on the positive side and
399 // negative side
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800400 const Constants& c(*mConstants);
Mathias Agopian7492a7f2012-11-10 04:44:30 -0800401 const int32_t ONE = c.cMask | c.pMask;
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800402 uint32_t indexP = ( phase & c.cMask) >> c.cShift;
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800403 uint32_t lerpP = ( phase & c.pMask) >> c.pShift;
Mathias Agopian7492a7f2012-11-10 04:44:30 -0800404 uint32_t indexN = ((ONE-phase) & c.cMask) >> c.cShift;
405 uint32_t lerpN = ((ONE-phase) & c.pMask) >> c.pShift;
406
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800407 const size_t offset = c.halfNumCoefs;
Mathias Agopian46afbec2012-11-04 02:03:49 -0800408 indexP *= offset;
409 indexN *= offset;
410
Mathias Agopian7aa7ed72012-11-05 01:51:37 -0800411 int32_t const* coefsP = mFirCoefs + indexP;
412 int32_t const* coefsN = mFirCoefs + indexN;
Mathias Agopian46afbec2012-11-04 02:03:49 -0800413 int16_t const* sP = samples;
414 int16_t const* sN = samples + CHANNELS;
Mathias Agopian65ab4712010-07-14 17:59:35 -0700415
Mathias Agopian46afbec2012-11-04 02:03:49 -0800416 size_t count = offset;
Mathias Agopianad9af032012-11-04 15:16:13 -0800417
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100418#ifndef USE_NEON
419 int32_t l = 0;
420 int32_t r = 0;
421 for (size_t i=0 ; i<count ; i++) {
422 interpolate<CHANNELS>(l, r, coefsP++, offset, lerpP, sP);
423 sP -= CHANNELS;
424 interpolate<CHANNELS>(l, r, coefsN++, offset, lerpN, sN);
425 sN += CHANNELS;
426 }
427 out[0] += 2 * mulRL(1, l, vRL);
428 out[1] += 2 * mulRL(0, r, vRL);
429#else
430 UNUSED(vRL);
431 if (CHANNELS == 1) {
Mathias Agopianad9af032012-11-04 15:16:13 -0800432 int32_t const* coefsP1 = coefsP + offset;
433 int32_t const* coefsN1 = coefsN + offset;
434 sP -= CHANNELS*3;
Mathias Agopianad9af032012-11-04 15:16:13 -0800435
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100436 int32x4_t sum;
437 int32x2_t lerpPN;
438 lerpPN = vdup_n_s32(0);
439 lerpPN = vld1_lane_s32((int32_t *)&lerpP, lerpPN, 0);
440 lerpPN = vld1_lane_s32((int32_t *)&lerpN, lerpPN, 1);
441 lerpPN = vshl_n_s32(lerpPN, 16);
442 sum = vdupq_n_s32(0);
Mathias Agopianad9af032012-11-04 15:16:13 -0800443
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100444 int16x4_t sampleP, sampleN;
445 int32x4_t samplePExt, sampleNExt;
446 int32x4_t coefsPV0, coefsPV1, coefsNV0, coefsNV1;
Mathias Agopianad9af032012-11-04 15:16:13 -0800447
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100448 coefsP = (const int32_t*)__builtin_assume_aligned(coefsP, 16);
449 coefsN = (const int32_t*)__builtin_assume_aligned(coefsN, 16);
450 coefsP1 = (const int32_t*)__builtin_assume_aligned(coefsP1, 16);
451 coefsN1 = (const int32_t*)__builtin_assume_aligned(coefsN1, 16);
452 for (; count > 0; count -= 4) {
453 sampleP = vld1_s16(sP);
454 sampleN = vld1_s16(sN);
455 coefsPV0 = vld1q_s32(coefsP);
456 coefsNV0 = vld1q_s32(coefsN);
457 coefsPV1 = vld1q_s32(coefsP1);
458 coefsNV1 = vld1q_s32(coefsN1);
459 sP -= 4;
460 sN += 4;
461 coefsP += 4;
462 coefsN += 4;
463 coefsP1 += 4;
464 coefsN1 += 4;
Mathias Agopianad9af032012-11-04 15:16:13 -0800465
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100466 sampleP = vrev64_s16(sampleP);
Mathias Agopianad9af032012-11-04 15:16:13 -0800467
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100468 // interpolate (step1)
469 coefsPV1 = vsubq_s32(coefsPV1, coefsPV0);
470 coefsNV1 = vsubq_s32(coefsNV1, coefsNV0);
471 samplePExt = vshll_n_s16(sampleP, 15);
472 // interpolate (step2)
473 coefsPV1 = vqrdmulhq_lane_s32(coefsPV1, lerpPN, 0);
474 coefsNV1 = vqrdmulhq_lane_s32(coefsNV1, lerpPN, 1);
475 sampleNExt = vshll_n_s16(sampleN, 15);
476 // interpolate (step3)
477 coefsPV0 = vaddq_s32(coefsPV0, coefsPV1);
478 coefsNV0 = vaddq_s32(coefsNV0, coefsNV1);
Mathias Agopianad9af032012-11-04 15:16:13 -0800479
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100480 samplePExt = vqrdmulhq_s32(samplePExt, coefsPV0);
481 sampleNExt = vqrdmulhq_s32(sampleNExt, coefsNV0);
482 sum = vaddq_s32(sum, samplePExt);
483 sum = vaddq_s32(sum, sampleNExt);
484 }
485 int32x2_t volumesV, outV;
486 volumesV = vld1_s32(mVolumeSIMD);
487 outV = vld1_s32(out);
Mathias Agopianad9af032012-11-04 15:16:13 -0800488
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100489 //add all 4 partial sums
490 int32x2_t sumLow, sumHigh;
491 sumLow = vget_low_s32(sum);
492 sumHigh = vget_high_s32(sum);
493 sumLow = vpadd_s32(sumLow, sumHigh);
494 sumLow = vpadd_s32(sumLow, sumLow);
Mathias Agopianad9af032012-11-04 15:16:13 -0800495
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100496 sumLow = vqrdmulh_s32(sumLow, volumesV);
497 outV = vadd_s32(outV, sumLow);
498 vst1_s32(out, outV);
Mathias Agopianad9af032012-11-04 15:16:13 -0800499 } else if (CHANNELS == 2) {
500 int32_t const* coefsP1 = coefsP + offset;
501 int32_t const* coefsN1 = coefsN + offset;
502 sP -= CHANNELS*3;
Mathias Agopianad9af032012-11-04 15:16:13 -0800503
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100504 int32x4_t sum0, sum1;
505 int32x2_t lerpPN;
Mathias Agopianad9af032012-11-04 15:16:13 -0800506
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100507 lerpPN = vdup_n_s32(0);
508 lerpPN = vld1_lane_s32((int32_t *)&lerpP, lerpPN, 0);
509 lerpPN = vld1_lane_s32((int32_t *)&lerpN, lerpPN, 1);
510 lerpPN = vshl_n_s32(lerpPN, 16);
511 sum0 = vdupq_n_s32(0);
512 sum1 = vdupq_n_s32(0);
Mathias Agopianad9af032012-11-04 15:16:13 -0800513
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100514 int16x4x2_t sampleP, sampleN;
515 int32x4x2_t samplePExt, sampleNExt;
516 int32x4_t coefsPV0, coefsPV1, coefsNV0, coefsNV1;
Mathias Agopianad9af032012-11-04 15:16:13 -0800517
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100518 coefsP = (const int32_t*)__builtin_assume_aligned(coefsP, 16);
519 coefsN = (const int32_t*)__builtin_assume_aligned(coefsN, 16);
520 coefsP1 = (const int32_t*)__builtin_assume_aligned(coefsP1, 16);
521 coefsN1 = (const int32_t*)__builtin_assume_aligned(coefsN1, 16);
522 for (; count > 0; count -= 4) {
523 sampleP = vld2_s16(sP);
524 sampleN = vld2_s16(sN);
525 coefsPV0 = vld1q_s32(coefsP);
526 coefsNV0 = vld1q_s32(coefsN);
527 coefsPV1 = vld1q_s32(coefsP1);
528 coefsNV1 = vld1q_s32(coefsN1);
529 sP -= 8;
530 sN += 8;
531 coefsP += 4;
532 coefsN += 4;
533 coefsP1 += 4;
534 coefsN1 += 4;
Mathias Agopianad9af032012-11-04 15:16:13 -0800535
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100536 sampleP.val[0] = vrev64_s16(sampleP.val[0]);
537 sampleP.val[1] = vrev64_s16(sampleP.val[1]);
Mathias Agopianad9af032012-11-04 15:16:13 -0800538
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100539 // interpolate (step1)
540 coefsPV1 = vsubq_s32(coefsPV1, coefsPV0);
541 coefsNV1 = vsubq_s32(coefsNV1, coefsNV0);
542 samplePExt.val[0] = vshll_n_s16(sampleP.val[0], 15);
543 samplePExt.val[1] = vshll_n_s16(sampleP.val[1], 15);
544 // interpolate (step2)
545 coefsPV1 = vqrdmulhq_lane_s32(coefsPV1, lerpPN, 0);
546 coefsNV1 = vqrdmulhq_lane_s32(coefsNV1, lerpPN, 1);
547 sampleNExt.val[0] = vshll_n_s16(sampleN.val[0], 15);
548 sampleNExt.val[1] = vshll_n_s16(sampleN.val[1], 15);
549 // interpolate (step3)
550 coefsPV0 = vaddq_s32(coefsPV0, coefsPV1);
551 coefsNV0 = vaddq_s32(coefsNV0, coefsNV1);
Mathias Agopianad9af032012-11-04 15:16:13 -0800552
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100553 samplePExt.val[0] = vqrdmulhq_s32(samplePExt.val[0], coefsPV0);
554 samplePExt.val[1] = vqrdmulhq_s32(samplePExt.val[1], coefsPV0);
555 sampleNExt.val[0] = vqrdmulhq_s32(sampleNExt.val[0], coefsNV0);
556 sampleNExt.val[1] = vqrdmulhq_s32(sampleNExt.val[1], coefsNV0);
557 sum0 = vaddq_s32(sum0, samplePExt.val[0]);
558 sum1 = vaddq_s32(sum1, samplePExt.val[1]);
559 sum0 = vaddq_s32(sum0, sampleNExt.val[0]);
560 sum1 = vaddq_s32(sum1, sampleNExt.val[1]);
561 }
562 int32x2_t volumesV, outV;
563 volumesV = vld1_s32(mVolumeSIMD);
564 outV = vld1_s32(out);
Mathias Agopianad9af032012-11-04 15:16:13 -0800565
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100566 //add all 4 partial sums
567 int32x2_t sumLow0, sumHigh0, sumLow1, sumHigh1;
568 sumLow0 = vget_low_s32(sum0);
569 sumHigh0 = vget_high_s32(sum0);
570 sumLow1 = vget_low_s32(sum1);
571 sumHigh1 = vget_high_s32(sum1);
572 sumLow0 = vpadd_s32(sumLow0, sumHigh0);
573 sumLow0 = vpadd_s32(sumLow0, sumLow0);
574 sumLow1 = vpadd_s32(sumLow1, sumHigh1);
575 sumLow1 = vpadd_s32(sumLow1, sumLow1);
Mathias Agopianad9af032012-11-04 15:16:13 -0800576
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100577 sumLow0 = vtrn_s32(sumLow0, sumLow1).val[0];
578 sumLow0 = vqrdmulh_s32(sumLow0, volumesV);
579 outV = vadd_s32(outV, sumLow0);
580 vst1_s32(out, outV);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700581 }
Zhongwei Yao12b44bd2014-04-10 17:23:42 +0100582#endif
Mathias Agopian65ab4712010-07-14 17:59:35 -0700583}
584
585template<int CHANNELS>
586void AudioResamplerSinc::interpolate(
587 int32_t& l, int32_t& r,
Mathias Agopian46afbec2012-11-04 02:03:49 -0800588 const int32_t* coefs, size_t offset,
589 int32_t lerp, const int16_t* samples)
Mathias Agopian65ab4712010-07-14 17:59:35 -0700590{
591 int32_t c0 = coefs[0];
Mathias Agopian46afbec2012-11-04 02:03:49 -0800592 int32_t c1 = coefs[offset];
Mathias Agopian65ab4712010-07-14 17:59:35 -0700593 int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0);
594 if (CHANNELS == 2) {
Glenn Kasten54c3b662012-01-06 07:46:30 -0800595 uint32_t rl = *reinterpret_cast<const uint32_t*>(samples);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700596 l = mulAddRL(1, rl, sinc, l);
597 r = mulAddRL(0, rl, sinc, r);
598 } else {
599 r = l = mulAdd(samples[0], sinc, l);
600 }
601}
Mathias Agopian65ab4712010-07-14 17:59:35 -0700602// ----------------------------------------------------------------------------
603}; // namespace android