• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <machine/cpu-features.h>
33 #endif
34 
35 namespace android {
36 
37 #ifdef __ARM_HAVE_HALFWORD_MULTIPLY // optimized asm option
38     #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1
39 #endif // __ARM_HAVE_HALFWORD_MULTIPLY
40 // ----------------------------------------------------------------------------
41 
42 class AudioResamplerOrder1 : public AudioResampler {
43 public:
AudioResamplerOrder1(int inChannelCount,int32_t sampleRate)44     AudioResamplerOrder1(int inChannelCount, int32_t sampleRate) :
45         AudioResampler(inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) {
46     }
47     virtual void resample(int32_t* out, size_t outFrameCount,
48             AudioBufferProvider* provider);
49 private:
50     // number of bits used in interpolation multiply - 15 bits avoids overflow
51     static const int kNumInterpBits = 15;
52 
53     // bits to shift the phase fraction down to avoid overflow
54     static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits;
55 
init()56     void init() {}
57     void resampleMono16(int32_t* out, size_t outFrameCount,
58             AudioBufferProvider* provider);
59     void resampleStereo16(int32_t* out, size_t outFrameCount,
60             AudioBufferProvider* provider);
61 #ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
62     void AsmMono16Loop(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     void AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
66             size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
67             uint32_t &phaseFraction, uint32_t phaseIncrement);
68 #endif  // ASM_ARM_RESAMP1
69 
Interp(int32_t x0,int32_t x1,uint32_t f)70     static inline int32_t Interp(int32_t x0, int32_t x1, uint32_t f) {
71         return x0 + (((x1 - x0) * (int32_t)(f >> kPreInterpShift)) >> kNumInterpBits);
72     }
Advance(size_t * index,uint32_t * frac,uint32_t inc)73     static inline void Advance(size_t* index, uint32_t* frac, uint32_t inc) {
74         *frac += inc;
75         *index += (size_t)(*frac >> kNumPhaseBits);
76         *frac &= kPhaseMask;
77     }
78     int mX0L;
79     int mX0R;
80 };
81 
82 /*static*/
83 const double AudioResampler::kPhaseMultiplier = 1L << AudioResampler::kNumPhaseBits;
84 
qualityIsSupported(src_quality quality)85 bool AudioResampler::qualityIsSupported(src_quality quality)
86 {
87     switch (quality) {
88     case DEFAULT_QUALITY:
89     case LOW_QUALITY:
90     case MED_QUALITY:
91     case HIGH_QUALITY:
92     case VERY_HIGH_QUALITY:
93     case DYN_LOW_QUALITY:
94     case DYN_MED_QUALITY:
95     case DYN_HIGH_QUALITY:
96         return true;
97     default:
98         return false;
99     }
100 }
101 
102 // ----------------------------------------------------------------------------
103 
104 static pthread_once_t once_control = PTHREAD_ONCE_INIT;
105 static AudioResampler::src_quality defaultQuality = AudioResampler::DEFAULT_QUALITY;
106 
init_routine()107 void AudioResampler::init_routine()
108 {
109     char value[PROPERTY_VALUE_MAX];
110     if (property_get("af.resampler.quality", value, NULL) > 0) {
111         char *endptr;
112         unsigned long l = strtoul(value, &endptr, 0);
113         if (*endptr == '\0') {
114             defaultQuality = (src_quality) l;
115             ALOGD("forcing AudioResampler quality to %d", defaultQuality);
116             if (defaultQuality < DEFAULT_QUALITY || defaultQuality > DYN_HIGH_QUALITY) {
117                 defaultQuality = DEFAULT_QUALITY;
118             }
119         }
120     }
121 }
122 
qualityMHz(src_quality quality)123 uint32_t AudioResampler::qualityMHz(src_quality quality)
124 {
125     switch (quality) {
126     default:
127     case DEFAULT_QUALITY:
128     case LOW_QUALITY:
129         return 3;
130     case MED_QUALITY:
131         return 6;
132     case HIGH_QUALITY:
133         return 20;
134     case VERY_HIGH_QUALITY:
135         return 34;
136     case DYN_LOW_QUALITY:
137         return 4;
138     case DYN_MED_QUALITY:
139         return 6;
140     case DYN_HIGH_QUALITY:
141         return 12;
142     }
143 }
144 
145 static const uint32_t maxMHz = 130; // an arbitrary number that permits 3 VHQ, should be tunable
146 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
147 static uint32_t currentMHz = 0;
148 
create(audio_format_t format,int inChannelCount,int32_t sampleRate,src_quality quality)149 AudioResampler* AudioResampler::create(audio_format_t format, int inChannelCount,
150         int32_t sampleRate, src_quality quality) {
151 
152     bool atFinalQuality;
153     if (quality == DEFAULT_QUALITY) {
154         // read the resampler default quality property the first time it is needed
155         int ok = pthread_once(&once_control, init_routine);
156         if (ok != 0) {
157             ALOGE("%s pthread_once failed: %d", __func__, ok);
158         }
159         quality = defaultQuality;
160         atFinalQuality = false;
161     } else {
162         atFinalQuality = true;
163     }
164 
165     /* if the caller requests DEFAULT_QUALITY and af.resampler.property
166      * has not been set, the target resampler quality is set to DYN_MED_QUALITY,
167      * and allowed to "throttle" down to DYN_LOW_QUALITY if necessary
168      * due to estimated CPU load of having too many active resamplers
169      * (the code below the if).
170      */
171     if (quality == DEFAULT_QUALITY) {
172         quality = DYN_MED_QUALITY;
173     }
174 
175     // naive implementation of CPU load throttling doesn't account for whether resampler is active
176     pthread_mutex_lock(&mutex);
177     for (;;) {
178         uint32_t deltaMHz = qualityMHz(quality);
179         uint32_t newMHz = currentMHz + deltaMHz;
180         if ((qualityIsSupported(quality) && newMHz <= maxMHz) || atFinalQuality) {
181             ALOGV("resampler load %u -> %u MHz due to delta +%u MHz from quality %d",
182                     currentMHz, newMHz, deltaMHz, quality);
183             currentMHz = newMHz;
184             break;
185         }
186         // not enough CPU available for proposed quality level, so try next lowest level
187         switch (quality) {
188         default:
189         case LOW_QUALITY:
190             atFinalQuality = true;
191             break;
192         case MED_QUALITY:
193             quality = LOW_QUALITY;
194             break;
195         case HIGH_QUALITY:
196             quality = MED_QUALITY;
197             break;
198         case VERY_HIGH_QUALITY:
199             quality = HIGH_QUALITY;
200             break;
201         case DYN_LOW_QUALITY:
202             atFinalQuality = true;
203             break;
204         case DYN_MED_QUALITY:
205             quality = DYN_LOW_QUALITY;
206             break;
207         case DYN_HIGH_QUALITY:
208             quality = DYN_MED_QUALITY;
209             break;
210         }
211     }
212     pthread_mutex_unlock(&mutex);
213 
214     AudioResampler* resampler;
215 
216     switch (quality) {
217     default:
218     case LOW_QUALITY:
219         ALOGV("Create linear Resampler");
220         LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
221         resampler = new AudioResamplerOrder1(inChannelCount, sampleRate);
222         break;
223     case MED_QUALITY:
224         ALOGV("Create cubic Resampler");
225         LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
226         resampler = new AudioResamplerCubic(inChannelCount, sampleRate);
227         break;
228     case HIGH_QUALITY:
229         ALOGV("Create HIGH_QUALITY sinc Resampler");
230         LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
231         resampler = new AudioResamplerSinc(inChannelCount, sampleRate);
232         break;
233     case VERY_HIGH_QUALITY:
234         ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality);
235         LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
236         resampler = new AudioResamplerSinc(inChannelCount, sampleRate, quality);
237         break;
238     case DYN_LOW_QUALITY:
239     case DYN_MED_QUALITY:
240     case DYN_HIGH_QUALITY:
241         ALOGV("Create dynamic Resampler = %d", quality);
242         if (format == AUDIO_FORMAT_PCM_FLOAT) {
243             resampler = new AudioResamplerDyn<float, float, float>(inChannelCount,
244                     sampleRate, quality);
245         } else {
246             LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
247             if (quality == DYN_HIGH_QUALITY) {
248                 resampler = new AudioResamplerDyn<int32_t, int16_t, int32_t>(inChannelCount,
249                         sampleRate, quality);
250             } else {
251                 resampler = new AudioResamplerDyn<int16_t, int16_t, int32_t>(inChannelCount,
252                         sampleRate, quality);
253             }
254         }
255         break;
256     }
257 
258     // initialize resampler
259     resampler->init();
260     return resampler;
261 }
262 
AudioResampler(int inChannelCount,int32_t sampleRate,src_quality quality)263 AudioResampler::AudioResampler(int inChannelCount,
264         int32_t sampleRate, src_quality quality) :
265         mChannelCount(inChannelCount),
266         mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0),
267         mPhaseFraction(0), mLocalTimeFreq(0),
268         mPTS(AudioBufferProvider::kInvalidPTS), mQuality(quality) {
269 
270     const int maxChannels = quality < DYN_LOW_QUALITY ? 2 : 8;
271     if (inChannelCount < 1
272             || inChannelCount > maxChannels) {
273         LOG_ALWAYS_FATAL("Unsupported sample format %d quality %d channels",
274                 quality, inChannelCount);
275     }
276     if (sampleRate <= 0) {
277         LOG_ALWAYS_FATAL("Unsupported sample rate %d Hz", sampleRate);
278     }
279 
280     // initialize common members
281     mVolume[0] = mVolume[1] = 0;
282     mBuffer.frameCount = 0;
283 }
284 
~AudioResampler()285 AudioResampler::~AudioResampler() {
286     pthread_mutex_lock(&mutex);
287     src_quality quality = getQuality();
288     uint32_t deltaMHz = qualityMHz(quality);
289     int32_t newMHz = currentMHz - deltaMHz;
290     ALOGV("resampler load %u -> %d MHz due to delta -%u MHz from quality %d",
291             currentMHz, newMHz, deltaMHz, quality);
292     LOG_ALWAYS_FATAL_IF(newMHz < 0, "negative resampler load %d MHz", newMHz);
293     currentMHz = newMHz;
294     pthread_mutex_unlock(&mutex);
295 }
296 
setSampleRate(int32_t inSampleRate)297 void AudioResampler::setSampleRate(int32_t inSampleRate) {
298     mInSampleRate = inSampleRate;
299     mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate);
300 }
301 
setVolume(float left,float right)302 void AudioResampler::setVolume(float left, float right) {
303     // TODO: Implement anti-zipper filter
304     // convert to U4.12 for internal integer use (round down)
305     // integer volume values are clamped to 0 to UNITY_GAIN.
306     mVolume[0] = u4_12_from_float(clampFloatVol(left));
307     mVolume[1] = u4_12_from_float(clampFloatVol(right));
308 }
309 
setLocalTimeFreq(uint64_t freq)310 void AudioResampler::setLocalTimeFreq(uint64_t freq) {
311     mLocalTimeFreq = freq;
312 }
313 
setPTS(int64_t pts)314 void AudioResampler::setPTS(int64_t pts) {
315     mPTS = pts;
316 }
317 
calculateOutputPTS(int outputFrameIndex)318 int64_t AudioResampler::calculateOutputPTS(int outputFrameIndex) {
319 
320     if (mPTS == AudioBufferProvider::kInvalidPTS) {
321         return AudioBufferProvider::kInvalidPTS;
322     } else {
323         return mPTS + ((outputFrameIndex * mLocalTimeFreq) / mSampleRate);
324     }
325 }
326 
reset()327 void AudioResampler::reset() {
328     mInputIndex = 0;
329     mPhaseFraction = 0;
330     mBuffer.frameCount = 0;
331 }
332 
333 // ----------------------------------------------------------------------------
334 
resample(int32_t * out,size_t outFrameCount,AudioBufferProvider * provider)335 void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
336         AudioBufferProvider* provider) {
337 
338     // should never happen, but we overflow if it does
339     // ALOG_ASSERT(outFrameCount < 32767);
340 
341     // select the appropriate resampler
342     switch (mChannelCount) {
343     case 1:
344         resampleMono16(out, outFrameCount, provider);
345         break;
346     case 2:
347         resampleStereo16(out, outFrameCount, provider);
348         break;
349     }
350 }
351 
resampleStereo16(int32_t * out,size_t outFrameCount,AudioBufferProvider * provider)352 void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
353         AudioBufferProvider* provider) {
354 
355     int32_t vl = mVolume[0];
356     int32_t vr = mVolume[1];
357 
358     size_t inputIndex = mInputIndex;
359     uint32_t phaseFraction = mPhaseFraction;
360     uint32_t phaseIncrement = mPhaseIncrement;
361     size_t outputIndex = 0;
362     size_t outputSampleCount = outFrameCount * 2;
363     size_t inFrameCount = getInFrameCountRequired(outFrameCount);
364 
365     // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
366     //      outFrameCount, inputIndex, phaseFraction, phaseIncrement);
367 
368     while (outputIndex < outputSampleCount) {
369 
370         // buffer is empty, fetch a new one
371         while (mBuffer.frameCount == 0) {
372             mBuffer.frameCount = inFrameCount;
373             provider->getNextBuffer(&mBuffer,
374                                     calculateOutputPTS(outputIndex / 2));
375             if (mBuffer.raw == NULL) {
376                 goto resampleStereo16_exit;
377             }
378 
379             // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount);
380             if (mBuffer.frameCount > inputIndex) break;
381 
382             inputIndex -= mBuffer.frameCount;
383             mX0L = mBuffer.i16[mBuffer.frameCount*2-2];
384             mX0R = mBuffer.i16[mBuffer.frameCount*2-1];
385             provider->releaseBuffer(&mBuffer);
386             // mBuffer.frameCount == 0 now so we reload a new buffer
387         }
388 
389         int16_t *in = mBuffer.i16;
390 
391         // handle boundary case
392         while (inputIndex == 0) {
393             // ALOGE("boundary case");
394             out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction);
395             out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction);
396             Advance(&inputIndex, &phaseFraction, phaseIncrement);
397             if (outputIndex == outputSampleCount) {
398                 break;
399             }
400         }
401 
402         // process input samples
403         // ALOGE("general case");
404 
405 #ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
406         if (inputIndex + 2 < mBuffer.frameCount) {
407             int32_t* maxOutPt;
408             int32_t maxInIdx;
409 
410             maxOutPt = out + (outputSampleCount - 2);   // 2 because 2 frames per loop
411             maxInIdx = mBuffer.frameCount - 2;
412             AsmStereo16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr,
413                     phaseFraction, phaseIncrement);
414         }
415 #endif  // ASM_ARM_RESAMP1
416 
417         while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) {
418             out[outputIndex++] += vl * Interp(in[inputIndex*2-2],
419                     in[inputIndex*2], phaseFraction);
420             out[outputIndex++] += vr * Interp(in[inputIndex*2-1],
421                     in[inputIndex*2+1], phaseFraction);
422             Advance(&inputIndex, &phaseFraction, phaseIncrement);
423         }
424 
425         // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
426 
427         // if done with buffer, save samples
428         if (inputIndex >= mBuffer.frameCount) {
429             inputIndex -= mBuffer.frameCount;
430 
431             // ALOGE("buffer done, new input index %d", inputIndex);
432 
433             mX0L = mBuffer.i16[mBuffer.frameCount*2-2];
434             mX0R = mBuffer.i16[mBuffer.frameCount*2-1];
435             provider->releaseBuffer(&mBuffer);
436 
437             // verify that the releaseBuffer resets the buffer frameCount
438             // ALOG_ASSERT(mBuffer.frameCount == 0);
439         }
440     }
441 
442     // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
443 
444 resampleStereo16_exit:
445     // save state
446     mInputIndex = inputIndex;
447     mPhaseFraction = phaseFraction;
448 }
449 
resampleMono16(int32_t * out,size_t outFrameCount,AudioBufferProvider * provider)450 void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
451         AudioBufferProvider* provider) {
452 
453     int32_t vl = mVolume[0];
454     int32_t vr = mVolume[1];
455 
456     size_t inputIndex = mInputIndex;
457     uint32_t phaseFraction = mPhaseFraction;
458     uint32_t phaseIncrement = mPhaseIncrement;
459     size_t outputIndex = 0;
460     size_t outputSampleCount = outFrameCount * 2;
461     size_t inFrameCount = getInFrameCountRequired(outFrameCount);
462 
463     // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
464     //      outFrameCount, inputIndex, phaseFraction, phaseIncrement);
465     while (outputIndex < outputSampleCount) {
466         // buffer is empty, fetch a new one
467         while (mBuffer.frameCount == 0) {
468             mBuffer.frameCount = inFrameCount;
469             provider->getNextBuffer(&mBuffer,
470                                     calculateOutputPTS(outputIndex / 2));
471             if (mBuffer.raw == NULL) {
472                 mInputIndex = inputIndex;
473                 mPhaseFraction = phaseFraction;
474                 goto resampleMono16_exit;
475             }
476             // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount);
477             if (mBuffer.frameCount >  inputIndex) break;
478 
479             inputIndex -= mBuffer.frameCount;
480             mX0L = mBuffer.i16[mBuffer.frameCount-1];
481             provider->releaseBuffer(&mBuffer);
482             // mBuffer.frameCount == 0 now so we reload a new buffer
483         }
484         int16_t *in = mBuffer.i16;
485 
486         // handle boundary case
487         while (inputIndex == 0) {
488             // ALOGE("boundary case");
489             int32_t sample = Interp(mX0L, in[0], phaseFraction);
490             out[outputIndex++] += vl * sample;
491             out[outputIndex++] += vr * sample;
492             Advance(&inputIndex, &phaseFraction, phaseIncrement);
493             if (outputIndex == outputSampleCount) {
494                 break;
495             }
496         }
497 
498         // process input samples
499         // ALOGE("general case");
500 
501 #ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
502         if (inputIndex + 2 < mBuffer.frameCount) {
503             int32_t* maxOutPt;
504             int32_t maxInIdx;
505 
506             maxOutPt = out + (outputSampleCount - 2);
507             maxInIdx = (int32_t)mBuffer.frameCount - 2;
508                 AsmMono16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr,
509                         phaseFraction, phaseIncrement);
510         }
511 #endif  // ASM_ARM_RESAMP1
512 
513         while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) {
514             int32_t sample = Interp(in[inputIndex-1], in[inputIndex],
515                     phaseFraction);
516             out[outputIndex++] += vl * sample;
517             out[outputIndex++] += vr * sample;
518             Advance(&inputIndex, &phaseFraction, phaseIncrement);
519         }
520 
521 
522         // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
523 
524         // if done with buffer, save samples
525         if (inputIndex >= mBuffer.frameCount) {
526             inputIndex -= mBuffer.frameCount;
527 
528             // ALOGE("buffer done, new input index %d", inputIndex);
529 
530             mX0L = mBuffer.i16[mBuffer.frameCount-1];
531             provider->releaseBuffer(&mBuffer);
532 
533             // verify that the releaseBuffer resets the buffer frameCount
534             // ALOG_ASSERT(mBuffer.frameCount == 0);
535         }
536     }
537 
538     // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
539 
540 resampleMono16_exit:
541     // save state
542     mInputIndex = inputIndex;
543     mPhaseFraction = phaseFraction;
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