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