AudioResampler.cpp revision e53b9ead781c36e96d6b6f012ddffc93a3d80f0d
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#if 0
27#include "AudioResamplerSinc.h"
28#include "AudioResamplerCubic.h"
29#endif
30
31#ifdef __arm__
32#include <machine/cpu-features.h>
33#endif
34
35namespace 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
42class AudioResamplerOrder1 : public AudioResampler {
43public:
44    AudioResamplerOrder1(int bitDepth, int inChannelCount, int32_t sampleRate) :
45        AudioResampler(bitDepth, inChannelCount, sampleRate), mX0L(0), mX0R(0) {
46    }
47    virtual void resample(int32_t* out, size_t outFrameCount,
48            AudioBufferProvider* provider);
49private:
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
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
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    }
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// ----------------------------------------------------------------------------
83AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
84        int32_t sampleRate, int quality) {
85
86    // can only create low quality resample now
87    AudioResampler* resampler;
88
89    char value[PROPERTY_VALUE_MAX];
90    if (property_get("af.resampler.quality", value, 0)) {
91        quality = atoi(value);
92        ALOGD("forcing AudioResampler quality to %d", quality);
93    }
94
95    if (quality == DEFAULT)
96        quality = LOW_QUALITY;
97
98    switch (quality) {
99    default:
100    case LOW_QUALITY:
101        ALOGV("Create linear Resampler");
102        resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate);
103        break;
104#if 0
105    case MED_QUALITY:
106        ALOGV("Create cubic Resampler");
107        resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate);
108        break;
109    case HIGH_QUALITY:
110        ALOGV("Create sinc Resampler");
111        resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate);
112        break;
113#endif
114    }
115
116    // initialize resampler
117    resampler->init();
118    return resampler;
119}
120
121AudioResampler::AudioResampler(int bitDepth, int inChannelCount,
122        int32_t sampleRate) :
123    mBitDepth(bitDepth), mChannelCount(inChannelCount),
124            mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0),
125            mPhaseFraction(0), mLocalTimeFreq(0),
126            mPTS(AudioBufferProvider::kInvalidPTS) {
127    // sanity check on format
128    if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) {
129        ALOGE("Unsupported sample format, %d bits, %d channels", bitDepth,
130                inChannelCount);
131        // ALOG_ASSERT(0);
132    }
133
134    // initialize common members
135    mVolume[0] = mVolume[1] = 0;
136    mBuffer.frameCount = 0;
137
138}
139
140AudioResampler::~AudioResampler() {
141}
142
143void AudioResampler::setSampleRate(int32_t inSampleRate) {
144    mInSampleRate = inSampleRate;
145    mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate);
146}
147
148void AudioResampler::setVolume(int16_t left, int16_t right) {
149    // TODO: Implement anti-zipper filter
150    mVolume[0] = left;
151    mVolume[1] = right;
152}
153
154void AudioResampler::setLocalTimeFreq(uint64_t freq) {
155    mLocalTimeFreq = freq;
156}
157
158void AudioResampler::setPTS(int64_t pts) {
159    mPTS = pts;
160}
161
162int64_t AudioResampler::calculateOutputPTS(int outputFrameIndex) {
163
164    if (mPTS == AudioBufferProvider::kInvalidPTS) {
165        return AudioBufferProvider::kInvalidPTS;
166    } else {
167        return mPTS + ((outputFrameIndex * mLocalTimeFreq) / mSampleRate);
168    }
169}
170
171void AudioResampler::reset() {
172    mInputIndex = 0;
173    mPhaseFraction = 0;
174    mBuffer.frameCount = 0;
175}
176
177// ----------------------------------------------------------------------------
178
179void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
180        AudioBufferProvider* provider) {
181
182    // should never happen, but we overflow if it does
183    // ALOG_ASSERT(outFrameCount < 32767);
184
185    // select the appropriate resampler
186    switch (mChannelCount) {
187    case 1:
188        resampleMono16(out, outFrameCount, provider);
189        break;
190    case 2:
191        resampleStereo16(out, outFrameCount, provider);
192        break;
193    }
194}
195
196void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
197        AudioBufferProvider* provider) {
198
199    int32_t vl = mVolume[0];
200    int32_t vr = mVolume[1];
201
202    size_t inputIndex = mInputIndex;
203    uint32_t phaseFraction = mPhaseFraction;
204    uint32_t phaseIncrement = mPhaseIncrement;
205    size_t outputIndex = 0;
206    size_t outputSampleCount = outFrameCount * 2;
207    size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
208
209    // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
210    //      outFrameCount, inputIndex, phaseFraction, phaseIncrement);
211
212    while (outputIndex < outputSampleCount) {
213
214        // buffer is empty, fetch a new one
215        while (mBuffer.frameCount == 0) {
216            mBuffer.frameCount = inFrameCount;
217            provider->getNextBuffer(&mBuffer,
218                                    calculateOutputPTS(outputIndex / 2));
219            if (mBuffer.raw == NULL) {
220                goto resampleStereo16_exit;
221            }
222
223            // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount);
224            if (mBuffer.frameCount > inputIndex) break;
225
226            inputIndex -= mBuffer.frameCount;
227            mX0L = mBuffer.i16[mBuffer.frameCount*2-2];
228            mX0R = mBuffer.i16[mBuffer.frameCount*2-1];
229            provider->releaseBuffer(&mBuffer);
230            // mBuffer.frameCount == 0 now so we reload a new buffer
231        }
232
233        int16_t *in = mBuffer.i16;
234
235        // handle boundary case
236        while (inputIndex == 0) {
237            // ALOGE("boundary case");
238            out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction);
239            out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction);
240            Advance(&inputIndex, &phaseFraction, phaseIncrement);
241            if (outputIndex == outputSampleCount)
242                break;
243        }
244
245        // process input samples
246        // ALOGE("general case");
247
248#ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
249        if (inputIndex + 2 < mBuffer.frameCount) {
250            int32_t* maxOutPt;
251            int32_t maxInIdx;
252
253            maxOutPt = out + (outputSampleCount - 2);   // 2 because 2 frames per loop
254            maxInIdx = mBuffer.frameCount - 2;
255            AsmStereo16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr,
256                    phaseFraction, phaseIncrement);
257        }
258#endif  // ASM_ARM_RESAMP1
259
260        while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) {
261            out[outputIndex++] += vl * Interp(in[inputIndex*2-2],
262                    in[inputIndex*2], phaseFraction);
263            out[outputIndex++] += vr * Interp(in[inputIndex*2-1],
264                    in[inputIndex*2+1], phaseFraction);
265            Advance(&inputIndex, &phaseFraction, phaseIncrement);
266        }
267
268        // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
269
270        // if done with buffer, save samples
271        if (inputIndex >= mBuffer.frameCount) {
272            inputIndex -= mBuffer.frameCount;
273
274            // ALOGE("buffer done, new input index %d", inputIndex);
275
276            mX0L = mBuffer.i16[mBuffer.frameCount*2-2];
277            mX0R = mBuffer.i16[mBuffer.frameCount*2-1];
278            provider->releaseBuffer(&mBuffer);
279
280            // verify that the releaseBuffer resets the buffer frameCount
281            // ALOG_ASSERT(mBuffer.frameCount == 0);
282        }
283    }
284
285    // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
286
287resampleStereo16_exit:
288    // save state
289    mInputIndex = inputIndex;
290    mPhaseFraction = phaseFraction;
291}
292
293void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
294        AudioBufferProvider* provider) {
295
296    int32_t vl = mVolume[0];
297    int32_t vr = mVolume[1];
298
299    size_t inputIndex = mInputIndex;
300    uint32_t phaseFraction = mPhaseFraction;
301    uint32_t phaseIncrement = mPhaseIncrement;
302    size_t outputIndex = 0;
303    size_t outputSampleCount = outFrameCount * 2;
304    size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
305
306    // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
307    //      outFrameCount, inputIndex, phaseFraction, phaseIncrement);
308    while (outputIndex < outputSampleCount) {
309        // buffer is empty, fetch a new one
310        while (mBuffer.frameCount == 0) {
311            mBuffer.frameCount = inFrameCount;
312            provider->getNextBuffer(&mBuffer,
313                                    calculateOutputPTS(outputIndex / 2));
314            if (mBuffer.raw == NULL) {
315                mInputIndex = inputIndex;
316                mPhaseFraction = phaseFraction;
317                goto resampleMono16_exit;
318            }
319            // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount);
320            if (mBuffer.frameCount >  inputIndex) break;
321
322            inputIndex -= mBuffer.frameCount;
323            mX0L = mBuffer.i16[mBuffer.frameCount-1];
324            provider->releaseBuffer(&mBuffer);
325            // mBuffer.frameCount == 0 now so we reload a new buffer
326        }
327        int16_t *in = mBuffer.i16;
328
329        // handle boundary case
330        while (inputIndex == 0) {
331            // ALOGE("boundary case");
332            int32_t sample = Interp(mX0L, in[0], phaseFraction);
333            out[outputIndex++] += vl * sample;
334            out[outputIndex++] += vr * sample;
335            Advance(&inputIndex, &phaseFraction, phaseIncrement);
336            if (outputIndex == outputSampleCount)
337                break;
338        }
339
340        // process input samples
341        // ALOGE("general case");
342
343#ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
344        if (inputIndex + 2 < mBuffer.frameCount) {
345            int32_t* maxOutPt;
346            int32_t maxInIdx;
347
348            maxOutPt = out + (outputSampleCount - 2);
349            maxInIdx = (int32_t)mBuffer.frameCount - 2;
350                AsmMono16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr,
351                        phaseFraction, phaseIncrement);
352        }
353#endif  // ASM_ARM_RESAMP1
354
355        while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) {
356            int32_t sample = Interp(in[inputIndex-1], in[inputIndex],
357                    phaseFraction);
358            out[outputIndex++] += vl * sample;
359            out[outputIndex++] += vr * sample;
360            Advance(&inputIndex, &phaseFraction, phaseIncrement);
361        }
362
363
364        // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
365
366        // if done with buffer, save samples
367        if (inputIndex >= mBuffer.frameCount) {
368            inputIndex -= mBuffer.frameCount;
369
370            // ALOGE("buffer done, new input index %d", inputIndex);
371
372            mX0L = mBuffer.i16[mBuffer.frameCount-1];
373            provider->releaseBuffer(&mBuffer);
374
375            // verify that the releaseBuffer resets the buffer frameCount
376            // ALOG_ASSERT(mBuffer.frameCount == 0);
377        }
378    }
379
380    // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
381
382resampleMono16_exit:
383    // save state
384    mInputIndex = inputIndex;
385    mPhaseFraction = phaseFraction;
386}
387
388#ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
389
390/*******************************************************************
391*
392*   AsmMono16Loop
393*   asm optimized monotonic loop version; one loop is 2 frames
394*   Input:
395*       in : pointer on input samples
396*       maxOutPt : pointer on first not filled
397*       maxInIdx : index on first not used
398*       outputIndex : pointer on current output index
399*       out : pointer on output buffer
400*       inputIndex : pointer on current input index
401*       vl, vr : left and right gain
402*       phaseFraction : pointer on current phase fraction
403*       phaseIncrement
404*   Ouput:
405*       outputIndex :
406*       out : updated buffer
407*       inputIndex : index of next to use
408*       phaseFraction : phase fraction for next interpolation
409*
410*******************************************************************/
411__attribute__((noinline))
412void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
413            size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
414            uint32_t &phaseFraction, uint32_t phaseIncrement)
415{
416#define MO_PARAM5   "36"        // offset of parameter 5 (outputIndex)
417
418    asm(
419        "stmfd  sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n"
420        // get parameters
421        "   ldr r6, [sp, #" MO_PARAM5 " + 20]\n"    // &phaseFraction
422        "   ldr r6, [r6]\n"                         // phaseFraction
423        "   ldr r7, [sp, #" MO_PARAM5 " + 8]\n"     // &inputIndex
424        "   ldr r7, [r7]\n"                         // inputIndex
425        "   ldr r8, [sp, #" MO_PARAM5 " + 4]\n"     // out
426        "   ldr r0, [sp, #" MO_PARAM5 " + 0]\n"     // &outputIndex
427        "   ldr r0, [r0]\n"                         // outputIndex
428        "   add r8, r0, asl #2\n"                   // curOut
429        "   ldr r9, [sp, #" MO_PARAM5 " + 24]\n"    // phaseIncrement
430        "   ldr r10, [sp, #" MO_PARAM5 " + 12]\n"   // vl
431        "   ldr r11, [sp, #" MO_PARAM5 " + 16]\n"   // vr
432
433        // r0 pin, x0, Samp
434
435        // r1 in
436        // r2 maxOutPt
437        // r3 maxInIdx
438
439        // r4 x1, i1, i3, Out1
440        // r5 out0
441
442        // r6 frac
443        // r7 inputIndex
444        // r8 curOut
445
446        // r9 inc
447        // r10 vl
448        // r11 vr
449
450        // r12
451        // r13 sp
452        // r14
453
454        // the following loop works on 2 frames
455
456        "1:\n"
457        "   cmp r8, r2\n"                   // curOut - maxCurOut
458        "   bcs 2f\n"
459
460#define MO_ONE_FRAME \
461    "   add r0, r1, r7, asl #1\n"       /* in + inputIndex */\
462    "   ldrsh r4, [r0]\n"               /* in[inputIndex] */\
463    "   ldr r5, [r8]\n"                 /* out[outputIndex] */\
464    "   ldrsh r0, [r0, #-2]\n"          /* in[inputIndex-1] */\
465    "   bic r6, r6, #0xC0000000\n"      /* phaseFraction & ... */\
466    "   sub r4, r4, r0\n"               /* in[inputIndex] - in[inputIndex-1] */\
467    "   mov r4, r4, lsl #2\n"           /* <<2 */\
468    "   smulwt r4, r4, r6\n"            /* (x1-x0)*.. */\
469    "   add r6, r6, r9\n"               /* phaseFraction + phaseIncrement */\
470    "   add r0, r0, r4\n"               /* x0 - (..) */\
471    "   mla r5, r0, r10, r5\n"          /* vl*interp + out[] */\
472    "   ldr r4, [r8, #4]\n"             /* out[outputIndex+1] */\
473    "   str r5, [r8], #4\n"             /* out[outputIndex++] = ... */\
474    "   mla r4, r0, r11, r4\n"          /* vr*interp + out[] */\
475    "   add r7, r7, r6, lsr #30\n"      /* inputIndex + phaseFraction>>30 */\
476    "   str r4, [r8], #4\n"             /* out[outputIndex++] = ... */
477
478        MO_ONE_FRAME    // frame 1
479        MO_ONE_FRAME    // frame 2
480
481        "   cmp r7, r3\n"                   // inputIndex - maxInIdx
482        "   bcc 1b\n"
483        "2:\n"
484
485        "   bic r6, r6, #0xC0000000\n"             // phaseFraction & ...
486        // save modified values
487        "   ldr r0, [sp, #" MO_PARAM5 " + 20]\n"    // &phaseFraction
488        "   str r6, [r0]\n"                         // phaseFraction
489        "   ldr r0, [sp, #" MO_PARAM5 " + 8]\n"     // &inputIndex
490        "   str r7, [r0]\n"                         // inputIndex
491        "   ldr r0, [sp, #" MO_PARAM5 " + 4]\n"     // out
492        "   sub r8, r0\n"                           // curOut - out
493        "   asr r8, #2\n"                           // new outputIndex
494        "   ldr r0, [sp, #" MO_PARAM5 " + 0]\n"     // &outputIndex
495        "   str r8, [r0]\n"                         // save outputIndex
496
497        "   ldmfd   sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}\n"
498    );
499}
500
501/*******************************************************************
502*
503*   AsmStereo16Loop
504*   asm optimized stereo loop version; one loop is 2 frames
505*   Input:
506*       in : pointer on input samples
507*       maxOutPt : pointer on first not filled
508*       maxInIdx : index on first not used
509*       outputIndex : pointer on current output index
510*       out : pointer on output buffer
511*       inputIndex : pointer on current input index
512*       vl, vr : left and right gain
513*       phaseFraction : pointer on current phase fraction
514*       phaseIncrement
515*   Ouput:
516*       outputIndex :
517*       out : updated buffer
518*       inputIndex : index of next to use
519*       phaseFraction : phase fraction for next interpolation
520*
521*******************************************************************/
522__attribute__((noinline))
523void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
524            size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
525            uint32_t &phaseFraction, uint32_t phaseIncrement)
526{
527#define ST_PARAM5    "40"     // offset of parameter 5 (outputIndex)
528    asm(
529        "stmfd  sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n"
530        // get parameters
531        "   ldr r6, [sp, #" ST_PARAM5 " + 20]\n"    // &phaseFraction
532        "   ldr r6, [r6]\n"                         // phaseFraction
533        "   ldr r7, [sp, #" ST_PARAM5 " + 8]\n"     // &inputIndex
534        "   ldr r7, [r7]\n"                         // inputIndex
535        "   ldr r8, [sp, #" ST_PARAM5 " + 4]\n"     // out
536        "   ldr r0, [sp, #" ST_PARAM5 " + 0]\n"     // &outputIndex
537        "   ldr r0, [r0]\n"                         // outputIndex
538        "   add r8, r0, asl #2\n"                   // curOut
539        "   ldr r9, [sp, #" ST_PARAM5 " + 24]\n"    // phaseIncrement
540        "   ldr r10, [sp, #" ST_PARAM5 " + 12]\n"   // vl
541        "   ldr r11, [sp, #" ST_PARAM5 " + 16]\n"   // vr
542
543        // r0 pin, x0, Samp
544
545        // r1 in
546        // r2 maxOutPt
547        // r3 maxInIdx
548
549        // r4 x1, i1, i3, out1
550        // r5 out0
551
552        // r6 frac
553        // r7 inputIndex
554        // r8 curOut
555
556        // r9 inc
557        // r10 vl
558        // r11 vr
559
560        // r12 temporary
561        // r13 sp
562        // r14
563
564        "3:\n"
565        "   cmp r8, r2\n"                   // curOut - maxCurOut
566        "   bcs 4f\n"
567
568#define ST_ONE_FRAME \
569    "   bic r6, r6, #0xC0000000\n"      /* phaseFraction & ... */\
570\
571    "   add r0, r1, r7, asl #2\n"       /* in + 2*inputIndex */\
572\
573    "   ldrsh r4, [r0]\n"               /* in[2*inputIndex] */\
574    "   ldr r5, [r8]\n"                 /* out[outputIndex] */\
575    "   ldrsh r12, [r0, #-4]\n"         /* in[2*inputIndex-2] */\
576    "   sub r4, r4, r12\n"              /* in[2*InputIndex] - in[2*InputIndex-2] */\
577    "   mov r4, r4, lsl #2\n"           /* <<2 */\
578    "   smulwt r4, r4, r6\n"            /* (x1-x0)*.. */\
579    "   add r12, r12, r4\n"             /* x0 - (..) */\
580    "   mla r5, r12, r10, r5\n"         /* vl*interp + out[] */\
581    "   ldr r4, [r8, #4]\n"             /* out[outputIndex+1] */\
582    "   str r5, [r8], #4\n"             /* out[outputIndex++] = ... */\
583\
584    "   ldrsh r12, [r0, #+2]\n"         /* in[2*inputIndex+1] */\
585    "   ldrsh r0, [r0, #-2]\n"          /* in[2*inputIndex-1] */\
586    "   sub r12, r12, r0\n"             /* in[2*InputIndex] - in[2*InputIndex-2] */\
587    "   mov r12, r12, lsl #2\n"         /* <<2 */\
588    "   smulwt r12, r12, r6\n"          /* (x1-x0)*.. */\
589    "   add r12, r0, r12\n"             /* x0 - (..) */\
590    "   mla r4, r12, r11, r4\n"         /* vr*interp + out[] */\
591    "   str r4, [r8], #4\n"             /* out[outputIndex++] = ... */\
592\
593    "   add r6, r6, r9\n"               /* phaseFraction + phaseIncrement */\
594    "   add r7, r7, r6, lsr #30\n"      /* inputIndex + phaseFraction>>30 */
595
596    ST_ONE_FRAME    // frame 1
597    ST_ONE_FRAME    // frame 1
598
599        "   cmp r7, r3\n"                       // inputIndex - maxInIdx
600        "   bcc 3b\n"
601        "4:\n"
602
603        "   bic r6, r6, #0xC0000000\n"              // phaseFraction & ...
604        // save modified values
605        "   ldr r0, [sp, #" ST_PARAM5 " + 20]\n"    // &phaseFraction
606        "   str r6, [r0]\n"                         // phaseFraction
607        "   ldr r0, [sp, #" ST_PARAM5 " + 8]\n"     // &inputIndex
608        "   str r7, [r0]\n"                         // inputIndex
609        "   ldr r0, [sp, #" ST_PARAM5 " + 4]\n"     // out
610        "   sub r8, r0\n"                           // curOut - out
611        "   asr r8, #2\n"                           // new outputIndex
612        "   ldr r0, [sp, #" ST_PARAM5 " + 0]\n"     // &outputIndex
613        "   str r8, [r0]\n"                         // save outputIndex
614
615        "   ldmfd   sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, pc}\n"
616    );
617}
618
619#endif  // ASM_ARM_RESAMP1
620
621
622// ----------------------------------------------------------------------------
623
624} // namespace android
625