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 "AudioResampler"
18 //#define LOG_NDEBUG 0
19 
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <cutils/log.h>
24 #include <cutils/properties.h>
25 #include <audio_utils/primitives.h>
26 #include "AudioResampler.h"
27 #include "AudioResamplerSinc.h"
28 #include "AudioResamplerCubic.h"
29 #include "AudioResamplerDyn.h"
30 
31 #ifdef __arm__
32     #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1
33 #endif
34 
35 namespace android {
36 
37 // ----------------------------------------------------------------------------
38 
39 class AudioResamplerOrder1 : public AudioResampler {
40 public:
AudioResamplerOrder1(int inChannelCount,int32_t sampleRate)41     AudioResamplerOrder1(int inChannelCount, int32_t sampleRate) :
42         AudioResampler(inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) {
43     }
44     virtual size_t resample(int32_t* out, size_t outFrameCount,
45             AudioBufferProvider* provider);
46 private:
47     // number of bits used in interpolation multiply - 15 bits avoids overflow
48     static const int kNumInterpBits = 15;
49 
50     // bits to shift the phase fraction down to avoid overflow
51     static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits;
52 
init()53     void init() {}
54     size_t resampleMono16(int32_t* out, size_t outFrameCount,
55             AudioBufferProvider* provider);
56     size_t resampleStereo16(int32_t* out, size_t outFrameCount,
57             AudioBufferProvider* provider);
58 #ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
59     void AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
60             size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
61             uint32_t &phaseFraction, uint32_t phaseIncrement);
62     void AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
63             size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
64             uint32_t &phaseFraction, uint32_t phaseIncrement);
65 #endif  // ASM_ARM_RESAMP1
66 
Interp(int32_t x0,int32_t x1,uint32_t f)67     static inline int32_t Interp(int32_t x0, int32_t x1, uint32_t f) {
68         return x0 + (((x1 - x0) * (int32_t)(f >> kPreInterpShift)) >> kNumInterpBits);
69     }
Advance(size_t * index,uint32_t * frac,uint32_t inc)70     static inline void Advance(size_t* index, uint32_t* frac, uint32_t inc) {
71         *frac += inc;
72         *index += (size_t)(*frac >> kNumPhaseBits);
73         *frac &= kPhaseMask;
74     }
75     int mX0L;
76     int mX0R;
77 };
78 
79 /*static*/
80 const double AudioResampler::kPhaseMultiplier = 1L << AudioResampler::kNumPhaseBits;
81 
qualityIsSupported(src_quality quality)82 bool AudioResampler::qualityIsSupported(src_quality quality)
83 {
84     switch (quality) {
85     case DEFAULT_QUALITY:
86     case LOW_QUALITY:
87     case MED_QUALITY:
88     case HIGH_QUALITY:
89     case VERY_HIGH_QUALITY:
90     case DYN_LOW_QUALITY:
91     case DYN_MED_QUALITY:
92     case DYN_HIGH_QUALITY:
93         return true;
94     default:
95         return false;
96     }
97 }
98 
99 // ----------------------------------------------------------------------------
100 
101 static pthread_once_t once_control = PTHREAD_ONCE_INIT;
102 static AudioResampler::src_quality defaultQuality = AudioResampler::DEFAULT_QUALITY;
103 
init_routine()104 void AudioResampler::init_routine()
105 {
106     char value[PROPERTY_VALUE_MAX];
107     if (property_get("af.resampler.quality", value, NULL) > 0) {
108         char *endptr;
109         unsigned long l = strtoul(value, &endptr, 0);
110         if (*endptr == '\0') {
111             defaultQuality = (src_quality) l;
112             ALOGD("forcing AudioResampler quality to %d", defaultQuality);
113             if (defaultQuality < DEFAULT_QUALITY || defaultQuality > DYN_HIGH_QUALITY) {
114                 defaultQuality = DEFAULT_QUALITY;
115             }
116         }
117     }
118 }
119 
qualityMHz(src_quality quality)120 uint32_t AudioResampler::qualityMHz(src_quality quality)
121 {
122     switch (quality) {
123     default:
124     case DEFAULT_QUALITY:
125     case LOW_QUALITY:
126         return 3;
127     case MED_QUALITY:
128         return 6;
129     case HIGH_QUALITY:
130         return 20;
131     case VERY_HIGH_QUALITY:
132         return 34;
133     case DYN_LOW_QUALITY:
134         return 4;
135     case DYN_MED_QUALITY:
136         return 6;
137     case DYN_HIGH_QUALITY:
138         return 12;
139     }
140 }
141 
142 static const uint32_t maxMHz = 130; // an arbitrary number that permits 3 VHQ, should be tunable
143 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
144 static uint32_t currentMHz = 0;
145 
create(audio_format_t format,int inChannelCount,int32_t sampleRate,src_quality quality)146 AudioResampler* AudioResampler::create(audio_format_t format, int inChannelCount,
147         int32_t sampleRate, src_quality quality) {
148 
149     bool atFinalQuality;
150     if (quality == DEFAULT_QUALITY) {
151         // read the resampler default quality property the first time it is needed
152         int ok = pthread_once(&once_control, init_routine);
153         if (ok != 0) {
154             ALOGE("%s pthread_once failed: %d", __func__, ok);
155         }
156         quality = defaultQuality;
157         atFinalQuality = false;
158     } else {
159         atFinalQuality = true;
160     }
161 
162     /* if the caller requests DEFAULT_QUALITY and af.resampler.property
163      * has not been set, the target resampler quality is set to DYN_MED_QUALITY,
164      * and allowed to "throttle" down to DYN_LOW_QUALITY if necessary
165      * due to estimated CPU load of having too many active resamplers
166      * (the code below the if).
167      */
168     if (quality == DEFAULT_QUALITY) {
169         quality = DYN_MED_QUALITY;
170     }
171 
172     // naive implementation of CPU load throttling doesn't account for whether resampler is active
173     pthread_mutex_lock(&mutex);
174     for (;;) {
175         uint32_t deltaMHz = qualityMHz(quality);
176         uint32_t newMHz = currentMHz + deltaMHz;
177         if ((qualityIsSupported(quality) && newMHz <= maxMHz) || atFinalQuality) {
178             ALOGV("resampler load %u -> %u MHz due to delta +%u MHz from quality %d",
179                     currentMHz, newMHz, deltaMHz, quality);
180             currentMHz = newMHz;
181             break;
182         }
183         // not enough CPU available for proposed quality level, so try next lowest level
184         switch (quality) {
185         default:
186         case LOW_QUALITY:
187             atFinalQuality = true;
188             break;
189         case MED_QUALITY:
190             quality = LOW_QUALITY;
191             break;
192         case HIGH_QUALITY:
193             quality = MED_QUALITY;
194             break;
195         case VERY_HIGH_QUALITY:
196             quality = HIGH_QUALITY;
197             break;
198         case DYN_LOW_QUALITY:
199             atFinalQuality = true;
200             break;
201         case DYN_MED_QUALITY:
202             quality = DYN_LOW_QUALITY;
203             break;
204         case DYN_HIGH_QUALITY:
205             quality = DYN_MED_QUALITY;
206             break;
207         }
208     }
209     pthread_mutex_unlock(&mutex);
210 
211     AudioResampler* resampler;
212 
213     switch (quality) {
214     default:
215     case LOW_QUALITY:
216         ALOGV("Create linear Resampler");
217         LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
218         resampler = new AudioResamplerOrder1(inChannelCount, sampleRate);
219         break;
220     case MED_QUALITY:
221         ALOGV("Create cubic Resampler");
222         LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
223         resampler = new AudioResamplerCubic(inChannelCount, sampleRate);
224         break;
225     case HIGH_QUALITY:
226         ALOGV("Create HIGH_QUALITY sinc Resampler");
227         LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
228         resampler = new AudioResamplerSinc(inChannelCount, sampleRate);
229         break;
230     case VERY_HIGH_QUALITY:
231         ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality);
232         LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
233         resampler = new AudioResamplerSinc(inChannelCount, sampleRate, quality);
234         break;
235     case DYN_LOW_QUALITY:
236     case DYN_MED_QUALITY:
237     case DYN_HIGH_QUALITY:
238         ALOGV("Create dynamic Resampler = %d", quality);
239         if (format == AUDIO_FORMAT_PCM_FLOAT) {
240             resampler = new AudioResamplerDyn<float, float, float>(inChannelCount,
241                     sampleRate, quality);
242         } else {
243             LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
244             if (quality == DYN_HIGH_QUALITY) {
245                 resampler = new AudioResamplerDyn<int32_t, int16_t, int32_t>(inChannelCount,
246                         sampleRate, quality);
247             } else {
248                 resampler = new AudioResamplerDyn<int16_t, int16_t, int32_t>(inChannelCount,
249                         sampleRate, quality);
250             }
251         }
252         break;
253     }
254 
255     // initialize resampler
256     resampler->init();
257     return resampler;
258 }
259 
AudioResampler(int inChannelCount,int32_t sampleRate,src_quality quality)260 AudioResampler::AudioResampler(int inChannelCount,
261         int32_t sampleRate, src_quality quality) :
262         mChannelCount(inChannelCount),
263         mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0),
264         mPhaseFraction(0), mLocalTimeFreq(0),
265         mPTS(AudioBufferProvider::kInvalidPTS), mQuality(quality) {
266 
267     const int maxChannels = quality < DYN_LOW_QUALITY ? 2 : 8;
268     if (inChannelCount < 1
269             || inChannelCount > maxChannels) {
270         LOG_ALWAYS_FATAL("Unsupported sample format %d quality %d channels",
271                 quality, inChannelCount);
272     }
273     if (sampleRate <= 0) {
274         LOG_ALWAYS_FATAL("Unsupported sample rate %d Hz", sampleRate);
275     }
276 
277     // initialize common members
278     mVolume[0] = mVolume[1] = 0;
279     mBuffer.frameCount = 0;
280 }
281 
~AudioResampler()282 AudioResampler::~AudioResampler() {
283     pthread_mutex_lock(&mutex);
284     src_quality quality = getQuality();
285     uint32_t deltaMHz = qualityMHz(quality);
286     int32_t newMHz = currentMHz - deltaMHz;
287     ALOGV("resampler load %u -> %d MHz due to delta -%u MHz from quality %d",
288             currentMHz, newMHz, deltaMHz, quality);
289     LOG_ALWAYS_FATAL_IF(newMHz < 0, "negative resampler load %d MHz", newMHz);
290     currentMHz = newMHz;
291     pthread_mutex_unlock(&mutex);
292 }
293 
setSampleRate(int32_t inSampleRate)294 void AudioResampler::setSampleRate(int32_t inSampleRate) {
295     mInSampleRate = inSampleRate;
296     mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate);
297 }
298 
setVolume(float left,float right)299 void AudioResampler::setVolume(float left, float right) {
300     // TODO: Implement anti-zipper filter
301     // convert to U4.12 for internal integer use (round down)
302     // integer volume values are clamped to 0 to UNITY_GAIN.
303     mVolume[0] = u4_12_from_float(clampFloatVol(left));
304     mVolume[1] = u4_12_from_float(clampFloatVol(right));
305 }
306 
setLocalTimeFreq(uint64_t freq)307 void AudioResampler::setLocalTimeFreq(uint64_t freq) {
308     mLocalTimeFreq = freq;
309 }
310 
setPTS(int64_t pts)311 void AudioResampler::setPTS(int64_t pts) {
312     mPTS = pts;
313 }
314 
calculateOutputPTS(int outputFrameIndex)315 int64_t AudioResampler::calculateOutputPTS(int outputFrameIndex) {
316 
317     if (mPTS == AudioBufferProvider::kInvalidPTS) {
318         return AudioBufferProvider::kInvalidPTS;
319     } else {
320         return mPTS + ((outputFrameIndex * mLocalTimeFreq) / mSampleRate);
321     }
322 }
323 
reset()324 void AudioResampler::reset() {
325     mInputIndex = 0;
326     mPhaseFraction = 0;
327     mBuffer.frameCount = 0;
328 }
329 
330 // ----------------------------------------------------------------------------
331 
resample(int32_t * out,size_t outFrameCount,AudioBufferProvider * provider)332 size_t AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
333         AudioBufferProvider* provider) {
334 
335     // should never happen, but we overflow if it does
336     // ALOG_ASSERT(outFrameCount < 32767);
337 
338     // select the appropriate resampler
339     switch (mChannelCount) {
340     case 1:
341         return resampleMono16(out, outFrameCount, provider);
342     case 2:
343         return resampleStereo16(out, outFrameCount, provider);
344     default:
345         LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount);
346         return 0;
347     }
348 }
349 
resampleStereo16(int32_t * out,size_t outFrameCount,AudioBufferProvider * provider)350 size_t AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
351         AudioBufferProvider* provider) {
352 
353     int32_t vl = mVolume[0];
354     int32_t vr = mVolume[1];
355 
356     size_t inputIndex = mInputIndex;
357     uint32_t phaseFraction = mPhaseFraction;
358     uint32_t phaseIncrement = mPhaseIncrement;
359     size_t outputIndex = 0;
360     size_t outputSampleCount = outFrameCount * 2;
361     size_t inFrameCount = getInFrameCountRequired(outFrameCount);
362 
363     // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
364     //      outFrameCount, inputIndex, phaseFraction, phaseIncrement);
365 
366     while (outputIndex < outputSampleCount) {
367 
368         // buffer is empty, fetch a new one
369         while (mBuffer.frameCount == 0) {
370             mBuffer.frameCount = inFrameCount;
371             provider->getNextBuffer(&mBuffer,
372                                     calculateOutputPTS(outputIndex / 2));
373             if (mBuffer.raw == NULL) {
374                 goto resampleStereo16_exit;
375             }
376 
377             // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount);
378             if (mBuffer.frameCount > inputIndex) break;
379 
380             inputIndex -= mBuffer.frameCount;
381             mX0L = mBuffer.i16[mBuffer.frameCount*2-2];
382             mX0R = mBuffer.i16[mBuffer.frameCount*2-1];
383             provider->releaseBuffer(&mBuffer);
384             // mBuffer.frameCount == 0 now so we reload a new buffer
385         }
386 
387         int16_t *in = mBuffer.i16;
388 
389         // handle boundary case
390         while (inputIndex == 0) {
391             // ALOGE("boundary case");
392             out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction);
393             out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction);
394             Advance(&inputIndex, &phaseFraction, phaseIncrement);
395             if (outputIndex == outputSampleCount) {
396                 break;
397             }
398         }
399 
400         // process input samples
401         // ALOGE("general case");
402 
403 #ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
404         if (inputIndex + 2 < mBuffer.frameCount) {
405             int32_t* maxOutPt;
406             int32_t maxInIdx;
407 
408             maxOutPt = out + (outputSampleCount - 2);   // 2 because 2 frames per loop
409             maxInIdx = mBuffer.frameCount - 2;
410             AsmStereo16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr,
411                     phaseFraction, phaseIncrement);
412         }
413 #endif  // ASM_ARM_RESAMP1
414 
415         while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) {
416             out[outputIndex++] += vl * Interp(in[inputIndex*2-2],
417                     in[inputIndex*2], phaseFraction);
418             out[outputIndex++] += vr * Interp(in[inputIndex*2-1],
419                     in[inputIndex*2+1], phaseFraction);
420             Advance(&inputIndex, &phaseFraction, phaseIncrement);
421         }
422 
423         // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
424 
425         // if done with buffer, save samples
426         if (inputIndex >= mBuffer.frameCount) {
427             inputIndex -= mBuffer.frameCount;
428 
429             // ALOGE("buffer done, new input index %d", inputIndex);
430 
431             mX0L = mBuffer.i16[mBuffer.frameCount*2-2];
432             mX0R = mBuffer.i16[mBuffer.frameCount*2-1];
433             provider->releaseBuffer(&mBuffer);
434 
435             // verify that the releaseBuffer resets the buffer frameCount
436             // ALOG_ASSERT(mBuffer.frameCount == 0);
437         }
438     }
439 
440     // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
441 
442 resampleStereo16_exit:
443     // save state
444     mInputIndex = inputIndex;
445     mPhaseFraction = phaseFraction;
446     return outputIndex / 2 /* channels for stereo */;
447 }
448 
resampleMono16(int32_t * out,size_t outFrameCount,AudioBufferProvider * provider)449 size_t AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
450         AudioBufferProvider* provider) {
451 
452     int32_t vl = mVolume[0];
453     int32_t vr = mVolume[1];
454 
455     size_t inputIndex = mInputIndex;
456     uint32_t phaseFraction = mPhaseFraction;
457     uint32_t phaseIncrement = mPhaseIncrement;
458     size_t outputIndex = 0;
459     size_t outputSampleCount = outFrameCount * 2;
460     size_t inFrameCount = getInFrameCountRequired(outFrameCount);
461 
462     // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
463     //      outFrameCount, inputIndex, phaseFraction, phaseIncrement);
464     while (outputIndex < outputSampleCount) {
465         // buffer is empty, fetch a new one
466         while (mBuffer.frameCount == 0) {
467             mBuffer.frameCount = inFrameCount;
468             provider->getNextBuffer(&mBuffer,
469                                     calculateOutputPTS(outputIndex / 2));
470             if (mBuffer.raw == NULL) {
471                 mInputIndex = inputIndex;
472                 mPhaseFraction = phaseFraction;
473                 goto resampleMono16_exit;
474             }
475             // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount);
476             if (mBuffer.frameCount >  inputIndex) break;
477 
478             inputIndex -= mBuffer.frameCount;
479             mX0L = mBuffer.i16[mBuffer.frameCount-1];
480             provider->releaseBuffer(&mBuffer);
481             // mBuffer.frameCount == 0 now so we reload a new buffer
482         }
483         int16_t *in = mBuffer.i16;
484 
485         // handle boundary case
486         while (inputIndex == 0) {
487             // ALOGE("boundary case");
488             int32_t sample = Interp(mX0L, in[0], phaseFraction);
489             out[outputIndex++] += vl * sample;
490             out[outputIndex++] += vr * sample;
491             Advance(&inputIndex, &phaseFraction, phaseIncrement);
492             if (outputIndex == outputSampleCount) {
493                 break;
494             }
495         }
496 
497         // process input samples
498         // ALOGE("general case");
499 
500 #ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
501         if (inputIndex + 2 < mBuffer.frameCount) {
502             int32_t* maxOutPt;
503             int32_t maxInIdx;
504 
505             maxOutPt = out + (outputSampleCount - 2);
506             maxInIdx = (int32_t)mBuffer.frameCount - 2;
507                 AsmMono16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr,
508                         phaseFraction, phaseIncrement);
509         }
510 #endif  // ASM_ARM_RESAMP1
511 
512         while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) {
513             int32_t sample = Interp(in[inputIndex-1], in[inputIndex],
514                     phaseFraction);
515             out[outputIndex++] += vl * sample;
516             out[outputIndex++] += vr * sample;
517             Advance(&inputIndex, &phaseFraction, phaseIncrement);
518         }
519 
520 
521         // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
522 
523         // if done with buffer, save samples
524         if (inputIndex >= mBuffer.frameCount) {
525             inputIndex -= mBuffer.frameCount;
526 
527             // ALOGE("buffer done, new input index %d", inputIndex);
528 
529             mX0L = mBuffer.i16[mBuffer.frameCount-1];
530             provider->releaseBuffer(&mBuffer);
531 
532             // verify that the releaseBuffer resets the buffer frameCount
533             // ALOG_ASSERT(mBuffer.frameCount == 0);
534         }
535     }
536 
537     // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
538 
539 resampleMono16_exit:
540     // save state
541     mInputIndex = inputIndex;
542     mPhaseFraction = phaseFraction;
543     return outputIndex;
544 }
545 
546 #ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
547 
548 /*******************************************************************
549 *
550 *   AsmMono16Loop
551 *   asm optimized monotonic loop version; one loop is 2 frames
552 *   Input:
553 *       in : pointer on input samples
554 *       maxOutPt : pointer on first not filled
555 *       maxInIdx : index on first not used
556 *       outputIndex : pointer on current output index
557 *       out : pointer on output buffer
558 *       inputIndex : pointer on current input index
559 *       vl, vr : left and right gain
560 *       phaseFraction : pointer on current phase fraction
561 *       phaseIncrement
562 *   Ouput:
563 *       outputIndex :
564 *       out : updated buffer
565 *       inputIndex : index of next to use
566 *       phaseFraction : phase fraction for next interpolation
567 *
568 *******************************************************************/
569 __attribute__((noinline))
AsmMono16Loop(int16_t * in,int32_t * maxOutPt,int32_t maxInIdx,size_t & outputIndex,int32_t * out,size_t & inputIndex,int32_t vl,int32_t vr,uint32_t & phaseFraction,uint32_t phaseIncrement)570 void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
571             size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
572             uint32_t &phaseFraction, uint32_t phaseIncrement)
573 {
574     (void)maxOutPt; // remove unused parameter warnings
575     (void)maxInIdx;
576     (void)outputIndex;
577     (void)out;
578     (void)inputIndex;
579     (void)vl;
580     (void)vr;
581     (void)phaseFraction;
582     (void)phaseIncrement;
583     (void)in;
584 #define MO_PARAM5   "36"        // offset of parameter 5 (outputIndex)
585 
586     asm(
587         "stmfd  sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n"
588         // get parameters
589         "   ldr r6, [sp, #" MO_PARAM5 " + 20]\n"    // &phaseFraction
590         "   ldr r6, [r6]\n"                         // phaseFraction
591         "   ldr r7, [sp, #" MO_PARAM5 " + 8]\n"     // &inputIndex
592         "   ldr r7, [r7]\n"                         // inputIndex
593         "   ldr r8, [sp, #" MO_PARAM5 " + 4]\n"     // out
594         "   ldr r0, [sp, #" MO_PARAM5 " + 0]\n"     // &outputIndex
595         "   ldr r0, [r0]\n"                         // outputIndex
596         "   add r8, r8, r0, asl #2\n"               // curOut
597         "   ldr r9, [sp, #" MO_PARAM5 " + 24]\n"    // phaseIncrement
598         "   ldr r10, [sp, #" MO_PARAM5 " + 12]\n"   // vl
599         "   ldr r11, [sp, #" MO_PARAM5 " + 16]\n"   // vr
600 
601         // r0 pin, x0, Samp
602 
603         // r1 in
604         // r2 maxOutPt
605         // r3 maxInIdx
606 
607         // r4 x1, i1, i3, Out1
608         // r5 out0
609 
610         // r6 frac
611         // r7 inputIndex
612         // r8 curOut
613 
614         // r9 inc
615         // r10 vl
616         // r11 vr
617 
618         // r12
619         // r13 sp
620         // r14
621 
622         // the following loop works on 2 frames
623 
624         "1:\n"
625         "   cmp r8, r2\n"                   // curOut - maxCurOut
626         "   bcs 2f\n"
627 
628 #define MO_ONE_FRAME \
629     "   add r0, r1, r7, asl #1\n"       /* in + inputIndex */\
630     "   ldrsh r4, [r0]\n"               /* in[inputIndex] */\
631     "   ldr r5, [r8]\n"                 /* out[outputIndex] */\
632     "   ldrsh r0, [r0, #-2]\n"          /* in[inputIndex-1] */\
633     "   bic r6, r6, #0xC0000000\n"      /* phaseFraction & ... */\
634     "   sub r4, r4, r0\n"               /* in[inputIndex] - in[inputIndex-1] */\
635     "   mov r4, r4, lsl #2\n"           /* <<2 */\
636     "   smulwt r4, r4, r6\n"            /* (x1-x0)*.. */\
637     "   add r6, r6, r9\n"               /* phaseFraction + phaseIncrement */\
638     "   add r0, r0, r4\n"               /* x0 - (..) */\
639     "   mla r5, r0, r10, r5\n"          /* vl*interp + out[] */\
640     "   ldr r4, [r8, #4]\n"             /* out[outputIndex+1] */\
641     "   str r5, [r8], #4\n"             /* out[outputIndex++] = ... */\
642     "   mla r4, r0, r11, r4\n"          /* vr*interp + out[] */\
643     "   add r7, r7, r6, lsr #30\n"      /* inputIndex + phaseFraction>>30 */\
644     "   str r4, [r8], #4\n"             /* out[outputIndex++] = ... */
645 
646         MO_ONE_FRAME    // frame 1
647         MO_ONE_FRAME    // frame 2
648 
649         "   cmp r7, r3\n"                   // inputIndex - maxInIdx
650         "   bcc 1b\n"
651         "2:\n"
652 
653         "   bic r6, r6, #0xC0000000\n"             // phaseFraction & ...
654         // save modified values
655         "   ldr r0, [sp, #" MO_PARAM5 " + 20]\n"    // &phaseFraction
656         "   str r6, [r0]\n"                         // phaseFraction
657         "   ldr r0, [sp, #" MO_PARAM5 " + 8]\n"     // &inputIndex
658         "   str r7, [r0]\n"                         // inputIndex
659         "   ldr r0, [sp, #" MO_PARAM5 " + 4]\n"     // out
660         "   sub r8, r0\n"                           // curOut - out
661         "   asr r8, #2\n"                           // new outputIndex
662         "   ldr r0, [sp, #" MO_PARAM5 " + 0]\n"     // &outputIndex
663         "   str r8, [r0]\n"                         // save outputIndex
664 
665         "   ldmfd   sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}\n"
666     );
667 }
668 
669 /*******************************************************************
670 *
671 *   AsmStereo16Loop
672 *   asm optimized stereo loop version; one loop is 2 frames
673 *   Input:
674 *       in : pointer on input samples
675 *       maxOutPt : pointer on first not filled
676 *       maxInIdx : index on first not used
677 *       outputIndex : pointer on current output index
678 *       out : pointer on output buffer
679 *       inputIndex : pointer on current input index
680 *       vl, vr : left and right gain
681 *       phaseFraction : pointer on current phase fraction
682 *       phaseIncrement
683 *   Ouput:
684 *       outputIndex :
685 *       out : updated buffer
686 *       inputIndex : index of next to use
687 *       phaseFraction : phase fraction for next interpolation
688 *
689 *******************************************************************/
690 __attribute__((noinline))
AsmStereo16Loop(int16_t * in,int32_t * maxOutPt,int32_t maxInIdx,size_t & outputIndex,int32_t * out,size_t & inputIndex,int32_t vl,int32_t vr,uint32_t & phaseFraction,uint32_t phaseIncrement)691 void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
692             size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
693             uint32_t &phaseFraction, uint32_t phaseIncrement)
694 {
695     (void)maxOutPt; // remove unused parameter warnings
696     (void)maxInIdx;
697     (void)outputIndex;
698     (void)out;
699     (void)inputIndex;
700     (void)vl;
701     (void)vr;
702     (void)phaseFraction;
703     (void)phaseIncrement;
704     (void)in;
705 #define ST_PARAM5    "40"     // offset of parameter 5 (outputIndex)
706     asm(
707         "stmfd  sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n"
708         // get parameters
709         "   ldr r6, [sp, #" ST_PARAM5 " + 20]\n"    // &phaseFraction
710         "   ldr r6, [r6]\n"                         // phaseFraction
711         "   ldr r7, [sp, #" ST_PARAM5 " + 8]\n"     // &inputIndex
712         "   ldr r7, [r7]\n"                         // inputIndex
713         "   ldr r8, [sp, #" ST_PARAM5 " + 4]\n"     // out
714         "   ldr r0, [sp, #" ST_PARAM5 " + 0]\n"     // &outputIndex
715         "   ldr r0, [r0]\n"                         // outputIndex
716         "   add r8, r8, r0, asl #2\n"               // curOut
717         "   ldr r9, [sp, #" ST_PARAM5 " + 24]\n"    // phaseIncrement
718         "   ldr r10, [sp, #" ST_PARAM5 " + 12]\n"   // vl
719         "   ldr r11, [sp, #" ST_PARAM5 " + 16]\n"   // vr
720 
721         // r0 pin, x0, Samp
722 
723         // r1 in
724         // r2 maxOutPt
725         // r3 maxInIdx
726 
727         // r4 x1, i1, i3, out1
728         // r5 out0
729 
730         // r6 frac
731         // r7 inputIndex
732         // r8 curOut
733 
734         // r9 inc
735         // r10 vl
736         // r11 vr
737 
738         // r12 temporary
739         // r13 sp
740         // r14
741 
742         "3:\n"
743         "   cmp r8, r2\n"                   // curOut - maxCurOut
744         "   bcs 4f\n"
745 
746 #define ST_ONE_FRAME \
747     "   bic r6, r6, #0xC0000000\n"      /* phaseFraction & ... */\
748 \
749     "   add r0, r1, r7, asl #2\n"       /* in + 2*inputIndex */\
750 \
751     "   ldrsh r4, [r0]\n"               /* in[2*inputIndex] */\
752     "   ldr r5, [r8]\n"                 /* out[outputIndex] */\
753     "   ldrsh r12, [r0, #-4]\n"         /* in[2*inputIndex-2] */\
754     "   sub r4, r4, r12\n"              /* in[2*InputIndex] - in[2*InputIndex-2] */\
755     "   mov r4, r4, lsl #2\n"           /* <<2 */\
756     "   smulwt r4, r4, r6\n"            /* (x1-x0)*.. */\
757     "   add r12, r12, r4\n"             /* x0 - (..) */\
758     "   mla r5, r12, r10, r5\n"         /* vl*interp + out[] */\
759     "   ldr r4, [r8, #4]\n"             /* out[outputIndex+1] */\
760     "   str r5, [r8], #4\n"             /* out[outputIndex++] = ... */\
761 \
762     "   ldrsh r12, [r0, #+2]\n"         /* in[2*inputIndex+1] */\
763     "   ldrsh r0, [r0, #-2]\n"          /* in[2*inputIndex-1] */\
764     "   sub r12, r12, r0\n"             /* in[2*InputIndex] - in[2*InputIndex-2] */\
765     "   mov r12, r12, lsl #2\n"         /* <<2 */\
766     "   smulwt r12, r12, r6\n"          /* (x1-x0)*.. */\
767     "   add r12, r0, r12\n"             /* x0 - (..) */\
768     "   mla r4, r12, r11, r4\n"         /* vr*interp + out[] */\
769     "   str r4, [r8], #4\n"             /* out[outputIndex++] = ... */\
770 \
771     "   add r6, r6, r9\n"               /* phaseFraction + phaseIncrement */\
772     "   add r7, r7, r6, lsr #30\n"      /* inputIndex + phaseFraction>>30 */
773 
774     ST_ONE_FRAME    // frame 1
775     ST_ONE_FRAME    // frame 1
776 
777         "   cmp r7, r3\n"                       // inputIndex - maxInIdx
778         "   bcc 3b\n"
779         "4:\n"
780 
781         "   bic r6, r6, #0xC0000000\n"              // phaseFraction & ...
782         // save modified values
783         "   ldr r0, [sp, #" ST_PARAM5 " + 20]\n"    // &phaseFraction
784         "   str r6, [r0]\n"                         // phaseFraction
785         "   ldr r0, [sp, #" ST_PARAM5 " + 8]\n"     // &inputIndex
786         "   str r7, [r0]\n"                         // inputIndex
787         "   ldr r0, [sp, #" ST_PARAM5 " + 4]\n"     // out
788         "   sub r8, r0\n"                           // curOut - out
789         "   asr r8, #2\n"                           // new outputIndex
790         "   ldr r0, [sp, #" ST_PARAM5 " + 0]\n"     // &outputIndex
791         "   str r8, [r0]\n"                         // save outputIndex
792 
793         "   ldmfd   sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, pc}\n"
794     );
795 }
796 
797 #endif  // ASM_ARM_RESAMP1
798 
799 
800 // ----------------------------------------------------------------------------
801 
802 } // namespace android
803