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