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