AudioResampler.cpp revision 86eae0e5931103e040ac2cdd023ef5db252e09f6
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    // naive implementation of CPU load throttling doesn't account for whether resampler is active
162    pthread_mutex_lock(&mutex);
163    for (;;) {
164        uint32_t deltaMHz = qualityMHz(quality);
165        uint32_t newMHz = currentMHz + deltaMHz;
166        if ((qualityIsSupported(quality) && newMHz <= maxMHz) || atFinalQuality) {
167            ALOGV("resampler load %u -> %u MHz due to delta +%u MHz from quality %d",
168                    currentMHz, newMHz, deltaMHz, quality);
169            currentMHz = newMHz;
170            break;
171        }
172        // not enough CPU available for proposed quality level, so try next lowest level
173        switch (quality) {
174        default:
175        case DEFAULT_QUALITY:
176        case LOW_QUALITY:
177            atFinalQuality = true;
178            break;
179        case MED_QUALITY:
180            quality = LOW_QUALITY;
181            break;
182        case HIGH_QUALITY:
183            quality = MED_QUALITY;
184            break;
185        case VERY_HIGH_QUALITY:
186            quality = HIGH_QUALITY;
187            break;
188        case DYN_LOW_QUALITY:
189            atFinalQuality = true;
190            break;
191        case DYN_MED_QUALITY:
192            quality = DYN_LOW_QUALITY;
193            break;
194        case DYN_HIGH_QUALITY:
195            quality = DYN_MED_QUALITY;
196            break;
197        }
198    }
199    pthread_mutex_unlock(&mutex);
200
201    AudioResampler* resampler;
202
203    switch (quality) {
204    default:
205    case DEFAULT_QUALITY:
206    case LOW_QUALITY:
207        ALOGV("Create linear Resampler");
208        resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate);
209        break;
210    case MED_QUALITY:
211        ALOGV("Create cubic Resampler");
212        resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate);
213        break;
214    case HIGH_QUALITY:
215        ALOGV("Create HIGH_QUALITY sinc Resampler");
216        resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate);
217        break;
218    case VERY_HIGH_QUALITY:
219        ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality);
220        resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate, quality);
221        break;
222    case DYN_LOW_QUALITY:
223    case DYN_MED_QUALITY:
224    case DYN_HIGH_QUALITY:
225        ALOGV("Create dynamic Resampler = %d", quality);
226        resampler = new AudioResamplerDyn(bitDepth, inChannelCount, sampleRate, quality);
227        break;
228    }
229
230    // initialize resampler
231    resampler->init();
232    return resampler;
233}
234
235AudioResampler::AudioResampler(int bitDepth, int inChannelCount,
236        int32_t sampleRate, src_quality quality) :
237    mBitDepth(bitDepth), mChannelCount(inChannelCount),
238            mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0),
239            mPhaseFraction(0), mLocalTimeFreq(0),
240            mPTS(AudioBufferProvider::kInvalidPTS), mQuality(quality) {
241    // sanity check on format
242    if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) {
243        ALOGE("Unsupported sample format, %d bits, %d channels", bitDepth,
244                inChannelCount);
245        // ALOG_ASSERT(0);
246    }
247    if (sampleRate <= 0) {
248        ALOGE("Unsupported sample rate %d Hz", sampleRate);
249    }
250
251    // initialize common members
252    mVolume[0] = mVolume[1] = 0;
253    mBuffer.frameCount = 0;
254
255}
256
257AudioResampler::~AudioResampler() {
258    pthread_mutex_lock(&mutex);
259    src_quality quality = getQuality();
260    uint32_t deltaMHz = qualityMHz(quality);
261    int32_t newMHz = currentMHz - deltaMHz;
262    ALOGV("resampler load %u -> %d MHz due to delta -%u MHz from quality %d",
263            currentMHz, newMHz, deltaMHz, quality);
264    LOG_ALWAYS_FATAL_IF(newMHz < 0, "negative resampler load %d MHz", newMHz);
265    currentMHz = newMHz;
266    pthread_mutex_unlock(&mutex);
267}
268
269void AudioResampler::setSampleRate(int32_t inSampleRate) {
270    mInSampleRate = inSampleRate;
271    mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate);
272}
273
274void AudioResampler::setVolume(int16_t left, int16_t right) {
275    // TODO: Implement anti-zipper filter
276    mVolume[0] = left;
277    mVolume[1] = right;
278}
279
280void AudioResampler::setLocalTimeFreq(uint64_t freq) {
281    mLocalTimeFreq = freq;
282}
283
284void AudioResampler::setPTS(int64_t pts) {
285    mPTS = pts;
286}
287
288int64_t AudioResampler::calculateOutputPTS(int outputFrameIndex) {
289
290    if (mPTS == AudioBufferProvider::kInvalidPTS) {
291        return AudioBufferProvider::kInvalidPTS;
292    } else {
293        return mPTS + ((outputFrameIndex * mLocalTimeFreq) / mSampleRate);
294    }
295}
296
297void AudioResampler::reset() {
298    mInputIndex = 0;
299    mPhaseFraction = 0;
300    mBuffer.frameCount = 0;
301}
302
303// ----------------------------------------------------------------------------
304
305void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
306        AudioBufferProvider* provider) {
307
308    // should never happen, but we overflow if it does
309    // ALOG_ASSERT(outFrameCount < 32767);
310
311    // select the appropriate resampler
312    switch (mChannelCount) {
313    case 1:
314        resampleMono16(out, outFrameCount, provider);
315        break;
316    case 2:
317        resampleStereo16(out, outFrameCount, provider);
318        break;
319    }
320}
321
322void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
323        AudioBufferProvider* provider) {
324
325    int32_t vl = mVolume[0];
326    int32_t vr = mVolume[1];
327
328    size_t inputIndex = mInputIndex;
329    uint32_t phaseFraction = mPhaseFraction;
330    uint32_t phaseIncrement = mPhaseIncrement;
331    size_t outputIndex = 0;
332    size_t outputSampleCount = outFrameCount * 2;
333    size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
334
335    // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
336    //      outFrameCount, inputIndex, phaseFraction, phaseIncrement);
337
338    while (outputIndex < outputSampleCount) {
339
340        // buffer is empty, fetch a new one
341        while (mBuffer.frameCount == 0) {
342            mBuffer.frameCount = inFrameCount;
343            provider->getNextBuffer(&mBuffer,
344                                    calculateOutputPTS(outputIndex / 2));
345            if (mBuffer.raw == NULL) {
346                goto resampleStereo16_exit;
347            }
348
349            // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount);
350            if (mBuffer.frameCount > inputIndex) break;
351
352            inputIndex -= mBuffer.frameCount;
353            mX0L = mBuffer.i16[mBuffer.frameCount*2-2];
354            mX0R = mBuffer.i16[mBuffer.frameCount*2-1];
355            provider->releaseBuffer(&mBuffer);
356            // mBuffer.frameCount == 0 now so we reload a new buffer
357        }
358
359        int16_t *in = mBuffer.i16;
360
361        // handle boundary case
362        while (inputIndex == 0) {
363            // ALOGE("boundary case");
364            out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction);
365            out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction);
366            Advance(&inputIndex, &phaseFraction, phaseIncrement);
367            if (outputIndex == outputSampleCount) {
368                break;
369            }
370        }
371
372        // process input samples
373        // ALOGE("general case");
374
375#ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
376        if (inputIndex + 2 < mBuffer.frameCount) {
377            int32_t* maxOutPt;
378            int32_t maxInIdx;
379
380            maxOutPt = out + (outputSampleCount - 2);   // 2 because 2 frames per loop
381            maxInIdx = mBuffer.frameCount - 2;
382            AsmStereo16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr,
383                    phaseFraction, phaseIncrement);
384        }
385#endif  // ASM_ARM_RESAMP1
386
387        while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) {
388            out[outputIndex++] += vl * Interp(in[inputIndex*2-2],
389                    in[inputIndex*2], phaseFraction);
390            out[outputIndex++] += vr * Interp(in[inputIndex*2-1],
391                    in[inputIndex*2+1], phaseFraction);
392            Advance(&inputIndex, &phaseFraction, phaseIncrement);
393        }
394
395        // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
396
397        // if done with buffer, save samples
398        if (inputIndex >= mBuffer.frameCount) {
399            inputIndex -= mBuffer.frameCount;
400
401            // ALOGE("buffer done, new input index %d", inputIndex);
402
403            mX0L = mBuffer.i16[mBuffer.frameCount*2-2];
404            mX0R = mBuffer.i16[mBuffer.frameCount*2-1];
405            provider->releaseBuffer(&mBuffer);
406
407            // verify that the releaseBuffer resets the buffer frameCount
408            // ALOG_ASSERT(mBuffer.frameCount == 0);
409        }
410    }
411
412    // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
413
414resampleStereo16_exit:
415    // save state
416    mInputIndex = inputIndex;
417    mPhaseFraction = phaseFraction;
418}
419
420void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
421        AudioBufferProvider* provider) {
422
423    int32_t vl = mVolume[0];
424    int32_t vr = mVolume[1];
425
426    size_t inputIndex = mInputIndex;
427    uint32_t phaseFraction = mPhaseFraction;
428    uint32_t phaseIncrement = mPhaseIncrement;
429    size_t outputIndex = 0;
430    size_t outputSampleCount = outFrameCount * 2;
431    size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
432
433    // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
434    //      outFrameCount, inputIndex, phaseFraction, phaseIncrement);
435    while (outputIndex < outputSampleCount) {
436        // buffer is empty, fetch a new one
437        while (mBuffer.frameCount == 0) {
438            mBuffer.frameCount = inFrameCount;
439            provider->getNextBuffer(&mBuffer,
440                                    calculateOutputPTS(outputIndex / 2));
441            if (mBuffer.raw == NULL) {
442                mInputIndex = inputIndex;
443                mPhaseFraction = phaseFraction;
444                goto resampleMono16_exit;
445            }
446            // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount);
447            if (mBuffer.frameCount >  inputIndex) break;
448
449            inputIndex -= mBuffer.frameCount;
450            mX0L = mBuffer.i16[mBuffer.frameCount-1];
451            provider->releaseBuffer(&mBuffer);
452            // mBuffer.frameCount == 0 now so we reload a new buffer
453        }
454        int16_t *in = mBuffer.i16;
455
456        // handle boundary case
457        while (inputIndex == 0) {
458            // ALOGE("boundary case");
459            int32_t sample = Interp(mX0L, in[0], phaseFraction);
460            out[outputIndex++] += vl * sample;
461            out[outputIndex++] += vr * sample;
462            Advance(&inputIndex, &phaseFraction, phaseIncrement);
463            if (outputIndex == outputSampleCount) {
464                break;
465            }
466        }
467
468        // process input samples
469        // ALOGE("general case");
470
471#ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
472        if (inputIndex + 2 < mBuffer.frameCount) {
473            int32_t* maxOutPt;
474            int32_t maxInIdx;
475
476            maxOutPt = out + (outputSampleCount - 2);
477            maxInIdx = (int32_t)mBuffer.frameCount - 2;
478                AsmMono16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr,
479                        phaseFraction, phaseIncrement);
480        }
481#endif  // ASM_ARM_RESAMP1
482
483        while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) {
484            int32_t sample = Interp(in[inputIndex-1], in[inputIndex],
485                    phaseFraction);
486            out[outputIndex++] += vl * sample;
487            out[outputIndex++] += vr * sample;
488            Advance(&inputIndex, &phaseFraction, phaseIncrement);
489        }
490
491
492        // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
493
494        // if done with buffer, save samples
495        if (inputIndex >= mBuffer.frameCount) {
496            inputIndex -= mBuffer.frameCount;
497
498            // ALOGE("buffer done, new input index %d", inputIndex);
499
500            mX0L = mBuffer.i16[mBuffer.frameCount-1];
501            provider->releaseBuffer(&mBuffer);
502
503            // verify that the releaseBuffer resets the buffer frameCount
504            // ALOG_ASSERT(mBuffer.frameCount == 0);
505        }
506    }
507
508    // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
509
510resampleMono16_exit:
511    // save state
512    mInputIndex = inputIndex;
513    mPhaseFraction = phaseFraction;
514}
515
516#ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
517
518/*******************************************************************
519*
520*   AsmMono16Loop
521*   asm optimized monotonic loop version; one loop is 2 frames
522*   Input:
523*       in : pointer on input samples
524*       maxOutPt : pointer on first not filled
525*       maxInIdx : index on first not used
526*       outputIndex : pointer on current output index
527*       out : pointer on output buffer
528*       inputIndex : pointer on current input index
529*       vl, vr : left and right gain
530*       phaseFraction : pointer on current phase fraction
531*       phaseIncrement
532*   Ouput:
533*       outputIndex :
534*       out : updated buffer
535*       inputIndex : index of next to use
536*       phaseFraction : phase fraction for next interpolation
537*
538*******************************************************************/
539__attribute__((noinline))
540void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
541            size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
542            uint32_t &phaseFraction, uint32_t phaseIncrement)
543{
544#define MO_PARAM5   "36"        // offset of parameter 5 (outputIndex)
545
546    asm(
547        "stmfd  sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n"
548        // get parameters
549        "   ldr r6, [sp, #" MO_PARAM5 " + 20]\n"    // &phaseFraction
550        "   ldr r6, [r6]\n"                         // phaseFraction
551        "   ldr r7, [sp, #" MO_PARAM5 " + 8]\n"     // &inputIndex
552        "   ldr r7, [r7]\n"                         // inputIndex
553        "   ldr r8, [sp, #" MO_PARAM5 " + 4]\n"     // out
554        "   ldr r0, [sp, #" MO_PARAM5 " + 0]\n"     // &outputIndex
555        "   ldr r0, [r0]\n"                         // outputIndex
556        "   add r8, r0, asl #2\n"                   // curOut
557        "   ldr r9, [sp, #" MO_PARAM5 " + 24]\n"    // phaseIncrement
558        "   ldr r10, [sp, #" MO_PARAM5 " + 12]\n"   // vl
559        "   ldr r11, [sp, #" MO_PARAM5 " + 16]\n"   // vr
560
561        // r0 pin, x0, Samp
562
563        // r1 in
564        // r2 maxOutPt
565        // r3 maxInIdx
566
567        // r4 x1, i1, i3, Out1
568        // r5 out0
569
570        // r6 frac
571        // r7 inputIndex
572        // r8 curOut
573
574        // r9 inc
575        // r10 vl
576        // r11 vr
577
578        // r12
579        // r13 sp
580        // r14
581
582        // the following loop works on 2 frames
583
584        "1:\n"
585        "   cmp r8, r2\n"                   // curOut - maxCurOut
586        "   bcs 2f\n"
587
588#define MO_ONE_FRAME \
589    "   add r0, r1, r7, asl #1\n"       /* in + inputIndex */\
590    "   ldrsh r4, [r0]\n"               /* in[inputIndex] */\
591    "   ldr r5, [r8]\n"                 /* out[outputIndex] */\
592    "   ldrsh r0, [r0, #-2]\n"          /* in[inputIndex-1] */\
593    "   bic r6, r6, #0xC0000000\n"      /* phaseFraction & ... */\
594    "   sub r4, r4, r0\n"               /* in[inputIndex] - in[inputIndex-1] */\
595    "   mov r4, r4, lsl #2\n"           /* <<2 */\
596    "   smulwt r4, r4, r6\n"            /* (x1-x0)*.. */\
597    "   add r6, r6, r9\n"               /* phaseFraction + phaseIncrement */\
598    "   add r0, r0, r4\n"               /* x0 - (..) */\
599    "   mla r5, r0, r10, r5\n"          /* vl*interp + out[] */\
600    "   ldr r4, [r8, #4]\n"             /* out[outputIndex+1] */\
601    "   str r5, [r8], #4\n"             /* out[outputIndex++] = ... */\
602    "   mla r4, r0, r11, r4\n"          /* vr*interp + out[] */\
603    "   add r7, r7, r6, lsr #30\n"      /* inputIndex + phaseFraction>>30 */\
604    "   str r4, [r8], #4\n"             /* out[outputIndex++] = ... */
605
606        MO_ONE_FRAME    // frame 1
607        MO_ONE_FRAME    // frame 2
608
609        "   cmp r7, r3\n"                   // inputIndex - maxInIdx
610        "   bcc 1b\n"
611        "2:\n"
612
613        "   bic r6, r6, #0xC0000000\n"             // phaseFraction & ...
614        // save modified values
615        "   ldr r0, [sp, #" MO_PARAM5 " + 20]\n"    // &phaseFraction
616        "   str r6, [r0]\n"                         // phaseFraction
617        "   ldr r0, [sp, #" MO_PARAM5 " + 8]\n"     // &inputIndex
618        "   str r7, [r0]\n"                         // inputIndex
619        "   ldr r0, [sp, #" MO_PARAM5 " + 4]\n"     // out
620        "   sub r8, r0\n"                           // curOut - out
621        "   asr r8, #2\n"                           // new outputIndex
622        "   ldr r0, [sp, #" MO_PARAM5 " + 0]\n"     // &outputIndex
623        "   str r8, [r0]\n"                         // save outputIndex
624
625        "   ldmfd   sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}\n"
626    );
627}
628
629/*******************************************************************
630*
631*   AsmStereo16Loop
632*   asm optimized stereo loop version; one loop is 2 frames
633*   Input:
634*       in : pointer on input samples
635*       maxOutPt : pointer on first not filled
636*       maxInIdx : index on first not used
637*       outputIndex : pointer on current output index
638*       out : pointer on output buffer
639*       inputIndex : pointer on current input index
640*       vl, vr : left and right gain
641*       phaseFraction : pointer on current phase fraction
642*       phaseIncrement
643*   Ouput:
644*       outputIndex :
645*       out : updated buffer
646*       inputIndex : index of next to use
647*       phaseFraction : phase fraction for next interpolation
648*
649*******************************************************************/
650__attribute__((noinline))
651void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
652            size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
653            uint32_t &phaseFraction, uint32_t phaseIncrement)
654{
655#define ST_PARAM5    "40"     // offset of parameter 5 (outputIndex)
656    asm(
657        "stmfd  sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n"
658        // get parameters
659        "   ldr r6, [sp, #" ST_PARAM5 " + 20]\n"    // &phaseFraction
660        "   ldr r6, [r6]\n"                         // phaseFraction
661        "   ldr r7, [sp, #" ST_PARAM5 " + 8]\n"     // &inputIndex
662        "   ldr r7, [r7]\n"                         // inputIndex
663        "   ldr r8, [sp, #" ST_PARAM5 " + 4]\n"     // out
664        "   ldr r0, [sp, #" ST_PARAM5 " + 0]\n"     // &outputIndex
665        "   ldr r0, [r0]\n"                         // outputIndex
666        "   add r8, r0, asl #2\n"                   // curOut
667        "   ldr r9, [sp, #" ST_PARAM5 " + 24]\n"    // phaseIncrement
668        "   ldr r10, [sp, #" ST_PARAM5 " + 12]\n"   // vl
669        "   ldr r11, [sp, #" ST_PARAM5 " + 16]\n"   // vr
670
671        // r0 pin, x0, Samp
672
673        // r1 in
674        // r2 maxOutPt
675        // r3 maxInIdx
676
677        // r4 x1, i1, i3, out1
678        // r5 out0
679
680        // r6 frac
681        // r7 inputIndex
682        // r8 curOut
683
684        // r9 inc
685        // r10 vl
686        // r11 vr
687
688        // r12 temporary
689        // r13 sp
690        // r14
691
692        "3:\n"
693        "   cmp r8, r2\n"                   // curOut - maxCurOut
694        "   bcs 4f\n"
695
696#define ST_ONE_FRAME \
697    "   bic r6, r6, #0xC0000000\n"      /* phaseFraction & ... */\
698\
699    "   add r0, r1, r7, asl #2\n"       /* in + 2*inputIndex */\
700\
701    "   ldrsh r4, [r0]\n"               /* in[2*inputIndex] */\
702    "   ldr r5, [r8]\n"                 /* out[outputIndex] */\
703    "   ldrsh r12, [r0, #-4]\n"         /* in[2*inputIndex-2] */\
704    "   sub r4, r4, r12\n"              /* in[2*InputIndex] - in[2*InputIndex-2] */\
705    "   mov r4, r4, lsl #2\n"           /* <<2 */\
706    "   smulwt r4, r4, r6\n"            /* (x1-x0)*.. */\
707    "   add r12, r12, r4\n"             /* x0 - (..) */\
708    "   mla r5, r12, r10, r5\n"         /* vl*interp + out[] */\
709    "   ldr r4, [r8, #4]\n"             /* out[outputIndex+1] */\
710    "   str r5, [r8], #4\n"             /* out[outputIndex++] = ... */\
711\
712    "   ldrsh r12, [r0, #+2]\n"         /* in[2*inputIndex+1] */\
713    "   ldrsh r0, [r0, #-2]\n"          /* in[2*inputIndex-1] */\
714    "   sub r12, r12, r0\n"             /* in[2*InputIndex] - in[2*InputIndex-2] */\
715    "   mov r12, r12, lsl #2\n"         /* <<2 */\
716    "   smulwt r12, r12, r6\n"          /* (x1-x0)*.. */\
717    "   add r12, r0, r12\n"             /* x0 - (..) */\
718    "   mla r4, r12, r11, r4\n"         /* vr*interp + out[] */\
719    "   str r4, [r8], #4\n"             /* out[outputIndex++] = ... */\
720\
721    "   add r6, r6, r9\n"               /* phaseFraction + phaseIncrement */\
722    "   add r7, r7, r6, lsr #30\n"      /* inputIndex + phaseFraction>>30 */
723
724    ST_ONE_FRAME    // frame 1
725    ST_ONE_FRAME    // frame 1
726
727        "   cmp r7, r3\n"                       // inputIndex - maxInIdx
728        "   bcc 3b\n"
729        "4:\n"
730
731        "   bic r6, r6, #0xC0000000\n"              // phaseFraction & ...
732        // save modified values
733        "   ldr r0, [sp, #" ST_PARAM5 " + 20]\n"    // &phaseFraction
734        "   str r6, [r0]\n"                         // phaseFraction
735        "   ldr r0, [sp, #" ST_PARAM5 " + 8]\n"     // &inputIndex
736        "   str r7, [r0]\n"                         // inputIndex
737        "   ldr r0, [sp, #" ST_PARAM5 " + 4]\n"     // out
738        "   sub r8, r0\n"                           // curOut - out
739        "   asr r8, #2\n"                           // new outputIndex
740        "   ldr r0, [sp, #" ST_PARAM5 " + 0]\n"     // &outputIndex
741        "   str r8, [r0]\n"                         // save outputIndex
742
743        "   ldmfd   sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, pc}\n"
744    );
745}
746
747#endif  // ASM_ARM_RESAMP1
748
749
750// ----------------------------------------------------------------------------
751
752} // namespace android
753