Home | History | Annotate | Download | only in audioflinger
      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 
     35 namespace android {
     36 
     37 #ifdef __ARM_HAVE_HALFWORD_MULTIPLY // optimized asm option
     38     #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1
     39 #endif // __ARM_HAVE_HALFWORD_MULTIPLY
     40 // ----------------------------------------------------------------------------
     41 
     42 class AudioResamplerOrder1 : public AudioResampler {
     43 public:
     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);
     49 private:
     50     // number of bits used in interpolation multiply - 15 bits avoids overflow
     51     static const int kNumInterpBits = 15;
     52 
     53     // bits to shift the phase fraction down to avoid overflow
     54     static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits;
     55 
     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 // ----------------------------------------------------------------------------
     83 AudioResampler* 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 
    121 AudioResampler::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 
    140 AudioResampler::~AudioResampler() {
    141 }
    142 
    143 void AudioResampler::setSampleRate(int32_t inSampleRate) {
    144     mInSampleRate = inSampleRate;
    145     mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate);
    146 }
    147 
    148 void AudioResampler::setVolume(int16_t left, int16_t right) {
    149     // TODO: Implement anti-zipper filter
    150     mVolume[0] = left;
    151     mVolume[1] = right;
    152 }
    153 
    154 void AudioResampler::setLocalTimeFreq(uint64_t freq) {
    155     mLocalTimeFreq = freq;
    156 }
    157 
    158 void AudioResampler::setPTS(int64_t pts) {
    159     mPTS = pts;
    160 }
    161 
    162 int64_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 
    171 void AudioResampler::reset() {
    172     mInputIndex = 0;
    173     mPhaseFraction = 0;
    174     mBuffer.frameCount = 0;
    175 }
    176 
    177 // ----------------------------------------------------------------------------
    178 
    179 void 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 
    196 void 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 
    287 resampleStereo16_exit:
    288     // save state
    289     mInputIndex = inputIndex;
    290     mPhaseFraction = phaseFraction;
    291 }
    292 
    293 void 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 
    382 resampleMono16_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))
    412 void 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))
    523 void 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