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