Use AudioPlaybackRate to hold TimestretchBufferProvider parameters

Use this struct to handle the parameters for TimestretchBufferProvider all
across the system.
Add stretch mode and fallback mode to TimestretchBuffer Provider.

Change-Id: I19099924a7003c62e48bb6ead56c785cb129fba2
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index c2c791f..18ce1d0 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -38,7 +38,6 @@
 #include <audio_utils/format.h>
 #include <common_time/local_clock.h>
 #include <common_time/cc_helper.h>
-#include <media/AudioResamplerPublic.h>
 
 #include "AudioMixerOps.h"
 #include "AudioMixer.h"
@@ -223,8 +222,7 @@
         t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
                 AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
         t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
-        t->mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
-        t->mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
+        t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
         // Check the downmixing (or upmixing) requirements.
         status_t status = t->prepareForDownmix();
         if (status != OK) {
@@ -668,19 +666,25 @@
         case TIMESTRETCH:
             switch (param) {
             case PLAYBACK_RATE: {
-                const float speed = reinterpret_cast<float*>(value)[0];
-                const float pitch = reinterpret_cast<float*>(value)[1];
-                ALOG_ASSERT(AUDIO_TIMESTRETCH_SPEED_MIN <= speed
-                        && speed <= AUDIO_TIMESTRETCH_SPEED_MAX,
-                        "bad speed %f", speed);
-                ALOG_ASSERT(AUDIO_TIMESTRETCH_PITCH_MIN <= pitch
-                        && pitch <= AUDIO_TIMESTRETCH_PITCH_MAX,
-                        "bad pitch %f", pitch);
-                if (track.setPlaybackRate(speed, pitch)) {
-                    ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, %f %f", speed, pitch);
+                const AudioPlaybackRate *playbackRate =
+                        reinterpret_cast<AudioPlaybackRate*>(value);
+                ALOG_ASSERT(AUDIO_TIMESTRETCH_SPEED_MIN <= playbackRate->mSpeed
+                        && playbackRate->mSpeed <= AUDIO_TIMESTRETCH_SPEED_MAX,
+                        "bad speed %f", playbackRate->mSpeed);
+                ALOG_ASSERT(AUDIO_TIMESTRETCH_PITCH_MIN <= playbackRate->mPitch
+                        && playbackRate->mPitch <= AUDIO_TIMESTRETCH_PITCH_MAX,
+                        "bad pitch %f", playbackRate->mPitch);
+                //TODO: use function from AudioResamplerPublic.h to test validity.
+                if (track.setPlaybackRate(*playbackRate)) {
+                    ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, STRETCH_MODE, FALLBACK_MODE "
+                            "%f %f %d %d",
+                            playbackRate->mSpeed,
+                            playbackRate->mPitch,
+                            playbackRate->mStretchMode,
+                            playbackRate->mFallbackMode);
                     // invalidateState(1 << name);
                 }
-                } break;
+            } break;
             default:
                 LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
             }
@@ -730,24 +734,26 @@
     return false;
 }
 
-bool AudioMixer::track_t::setPlaybackRate(float speed, float pitch)
+bool AudioMixer::track_t::setPlaybackRate(const AudioPlaybackRate &playbackRate)
 {
-    if (speed == mSpeed && pitch == mPitch) {
+    if ((mTimestretchBufferProvider == NULL &&
+            fabs(playbackRate.mSpeed - mPlaybackRate.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA &&
+            fabs(playbackRate.mPitch - mPlaybackRate.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA) ||
+            isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) {
         return false;
     }
-    mSpeed = speed;
-    mPitch = pitch;
+    mPlaybackRate = playbackRate;
     if (mTimestretchBufferProvider == NULL) {
         // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
         // but if none exists, it is the channel count (1 for mono).
         const int timestretchChannelCount = downmixerBufferProvider != NULL
                 ? mMixerChannelCount : channelCount;
         mTimestretchBufferProvider = new TimestretchBufferProvider(timestretchChannelCount,
-                mMixerInFormat, sampleRate, speed, pitch);
+                mMixerInFormat, sampleRate, playbackRate);
         reconfigureBufferProviders();
     } else {
         reinterpret_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider)
-                ->setPlaybackRate(speed, pitch);
+                ->setPlaybackRate(playbackRate);
     }
     return true;
 }
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index e27a0d1..7165c6c 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -23,6 +23,7 @@
 
 #include <hardware/audio_effect.h>
 #include <media/AudioBufferProvider.h>
+#include <media/AudioResamplerPublic.h>
 #include <media/nbaio/NBLog.h>
 #include <system/audio.h>
 #include <utils/Compat.h>
@@ -259,8 +260,7 @@
         audio_channel_mask_t mMixerChannelMask;
         uint32_t             mMixerChannelCount;
 
-        float          mSpeed;
-        float          mPitch;
+        AudioPlaybackRate    mPlaybackRate;
 
         bool        needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
         bool        setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
@@ -274,7 +274,7 @@
         void        unprepareForDownmix();
         status_t    prepareForReformat();
         void        unprepareForReformat();
-        bool        setPlaybackRate(float speed, float pitch);
+        bool        setPlaybackRate(const AudioPlaybackRate &playbackRate);
         void        reconfigureBufferProviders();
     };
 
diff --git a/services/audioflinger/BufferProviders.cpp b/services/audioflinger/BufferProviders.cpp
index dcae5e7..77bf4ac 100644
--- a/services/audioflinger/BufferProviders.cpp
+++ b/services/audioflinger/BufferProviders.cpp
@@ -361,25 +361,25 @@
 }
 
 TimestretchBufferProvider::TimestretchBufferProvider(int32_t channelCount,
-        audio_format_t format, uint32_t sampleRate, float speed, float pitch) :
+        audio_format_t format, uint32_t sampleRate, const AudioPlaybackRate &playbackRate) :
         mChannelCount(channelCount),
         mFormat(format),
         mSampleRate(sampleRate),
         mFrameSize(channelCount * audio_bytes_per_sample(format)),
-        mSpeed(speed),
-        mPitch(pitch),
         mLocalBufferFrameCount(0),
         mLocalBufferData(NULL),
         mRemaining(0),
-        mSonicStream(sonicCreateStream(sampleRate, mChannelCount))
+        mSonicStream(sonicCreateStream(sampleRate, mChannelCount)),
+        mFallbackFailErrorShown(false)
 {
-    ALOGV("TimestretchBufferProvider(%p)(%u, %#x, %u %f %f)",
-            this, channelCount, format, sampleRate, speed, pitch);
-    mBuffer.frameCount = 0;
-
     LOG_ALWAYS_FATAL_IF(mSonicStream == NULL,
             "TimestretchBufferProvider can't allocate Sonic stream");
-    sonicSetSpeed(mSonicStream, speed);
+
+    setPlaybackRate(playbackRate);
+    ALOGV("TimestretchBufferProvider(%p)(%u, %#x, %u %f %f %d %d)",
+            this, channelCount, format, sampleRate, playbackRate.mSpeed,
+            playbackRate.mPitch, playbackRate.mStretchMode, playbackRate.mFallbackMode);
+    mBuffer.frameCount = 0;
 }
 
 TimestretchBufferProvider::~TimestretchBufferProvider()
@@ -423,8 +423,8 @@
 
     // need to fetch more data
     const size_t outputDesired = pBuffer->frameCount - mRemaining;
-    mBuffer.frameCount = mSpeed == AUDIO_TIMESTRETCH_SPEED_NORMAL
-            ? outputDesired : outputDesired * mSpeed + 1;
+    mBuffer.frameCount = mPlaybackRate.mSpeed == AUDIO_TIMESTRETCH_SPEED_NORMAL
+            ? outputDesired : outputDesired * mPlaybackRate.mSpeed + 1;
 
     status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
 
@@ -491,13 +491,13 @@
     mRemaining = 0;
 }
 
-status_t TimestretchBufferProvider::setPlaybackRate(float speed, float pitch)
+status_t TimestretchBufferProvider::setPlaybackRate(const AudioPlaybackRate &playbackRate)
 {
-    mSpeed = speed;
-    mPitch = pitch;
-
-    sonicSetSpeed(mSonicStream, speed);
+    mPlaybackRate = playbackRate;
+    mFallbackFailErrorShown = false;
+    sonicSetSpeed(mSonicStream, mPlaybackRate.mSpeed);
     //TODO: pitch is ignored for now
+    //TODO: optimize: if parameters are the same, don't do any extra computation.
     return OK;
 }
 
@@ -508,33 +508,68 @@
     // Note dstFrames is the required number of frames.
 
     // Ensure consumption from src is as expected.
-    const size_t targetSrc = *dstFrames * mSpeed;
+    //TODO: add logic to track "very accurate" consumption related to speed, original sampling
+    //rate, actual frames processed.
+    const size_t targetSrc = *dstFrames * mPlaybackRate.mSpeed;
     if (*srcFrames < targetSrc) { // limit dst frames to that possible
-        *dstFrames = *srcFrames / mSpeed;
+        *dstFrames = *srcFrames / mPlaybackRate.mSpeed;
     } else if (*srcFrames > targetSrc + 1) {
         *srcFrames = targetSrc + 1;
     }
 
-    switch (mFormat) {
-    case AUDIO_FORMAT_PCM_FLOAT:
-        if (sonicWriteFloatToStream(mSonicStream, (float*)srcBuffer, *srcFrames) != 1) {
-            ALOGE("sonicWriteFloatToStream cannot realloc");
-            *srcFrames = 0; // cannot consume all of srcBuffer
+    if (mPlaybackRate.mSpeed< TIMESTRETCH_SONIC_SPEED_MIN  ||
+            mPlaybackRate.mSpeed >  TIMESTRETCH_SONIC_SPEED_MAX ) {
+        //fallback mode
+        if (*dstFrames > 0) {
+            switch(mPlaybackRate.mFallbackMode) {
+            case AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT:
+                if (*dstFrames <= *srcFrames) {
+                      size_t copySize = mFrameSize * *dstFrames;
+                      memcpy(dstBuffer, srcBuffer, copySize);
+                  } else {
+                      // cyclically repeat the source.
+                      for (size_t count = 0; count < *dstFrames; count += *srcFrames) {
+                          size_t remaining = min(*srcFrames, *dstFrames - count);
+                          memcpy((uint8_t*)dstBuffer + mFrameSize * count,
+                                  srcBuffer, mFrameSize * remaining);
+                      }
+                  }
+                break;
+            case AUDIO_TIMESTRETCH_FALLBACK_DEFAULT:
+            case AUDIO_TIMESTRETCH_FALLBACK_MUTE:
+                memset(dstBuffer,0, mFrameSize * *dstFrames);
+                break;
+            case AUDIO_TIMESTRETCH_FALLBACK_FAIL:
+            default:
+                if(!mFallbackFailErrorShown) {
+                    ALOGE("invalid parameters in TimestretchBufferProvider fallbackMode:%d",
+                            mPlaybackRate.mFallbackMode);
+                    mFallbackFailErrorShown = true;
+                }
+                break;
+            }
         }
-        *dstFrames = sonicReadFloatFromStream(mSonicStream, (float*)dstBuffer, *dstFrames);
-        break;
-    case AUDIO_FORMAT_PCM_16_BIT:
-        if (sonicWriteShortToStream(mSonicStream, (short*)srcBuffer, *srcFrames) != 1) {
-            ALOGE("sonicWriteShortToStream cannot realloc");
-            *srcFrames = 0; // cannot consume all of srcBuffer
+    } else {
+        switch (mFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            if (sonicWriteFloatToStream(mSonicStream, (float*)srcBuffer, *srcFrames) != 1) {
+                ALOGE("sonicWriteFloatToStream cannot realloc");
+                *srcFrames = 0; // cannot consume all of srcBuffer
+            }
+            *dstFrames = sonicReadFloatFromStream(mSonicStream, (float*)dstBuffer, *dstFrames);
+            break;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            if (sonicWriteShortToStream(mSonicStream, (short*)srcBuffer, *srcFrames) != 1) {
+                ALOGE("sonicWriteShortToStream cannot realloc");
+                *srcFrames = 0; // cannot consume all of srcBuffer
+            }
+            *dstFrames = sonicReadShortFromStream(mSonicStream, (short*)dstBuffer, *dstFrames);
+            break;
+        default:
+            // could also be caught on construction
+            LOG_ALWAYS_FATAL("invalid format %#x for TimestretchBufferProvider", mFormat);
         }
-        *dstFrames = sonicReadShortFromStream(mSonicStream, (short*)dstBuffer, *dstFrames);
-        break;
-    default:
-        // could also be caught on construction
-        LOG_ALWAYS_FATAL("invalid format %#x for TimestretchBufferProvider", mFormat);
     }
 }
-
 // ----------------------------------------------------------------------------
 } // namespace android
diff --git a/services/audioflinger/BufferProviders.h b/services/audioflinger/BufferProviders.h
index 42030c0..4970b6c 100644
--- a/services/audioflinger/BufferProviders.h
+++ b/services/audioflinger/BufferProviders.h
@@ -151,7 +151,8 @@
 class TimestretchBufferProvider : public PassthruBufferProvider {
 public:
     TimestretchBufferProvider(int32_t channelCount,
-            audio_format_t format, uint32_t sampleRate, float speed, float pitch);
+            audio_format_t format, uint32_t sampleRate,
+            const AudioPlaybackRate &playbackRate);
     virtual ~TimestretchBufferProvider();
 
     // Overrides AudioBufferProvider methods
@@ -161,7 +162,7 @@
     // Overrides PassthruBufferProvider
     virtual void reset();
 
-    virtual status_t setPlaybackRate(float speed, float pitch);
+    virtual status_t setPlaybackRate(const AudioPlaybackRate &playbackRate);
 
     // processes frames
     // dstBuffer is where to place the data
@@ -176,15 +177,17 @@
     const audio_format_t mFormat;
     const uint32_t       mSampleRate; // const for now (TODO change this)
     const size_t         mFrameSize;
-    float                mSpeed;
-    float                mPitch;
+    AudioPlaybackRate    mPlaybackRate;
 
 private:
-    AudioBufferProvider::Buffer mBuffer;
-    size_t               mLocalBufferFrameCount;
-    void                *mLocalBufferData;
-    size_t               mRemaining;
-    sonicStream          mSonicStream;
+    AudioBufferProvider::Buffer mBuffer;          // for upstream request
+    size_t               mLocalBufferFrameCount;  // size of local buffer
+    void                *mLocalBufferData;        // internally allocated buffer for data returned
+                                                  // to caller
+    size_t               mRemaining;              // remaining data in local buffer
+    sonicStream          mSonicStream;            // handle to sonic timestretch object
+    //FIXME: this dependency should be abstracted out
+    bool                 mFallbackFailErrorShown; // log fallback error only once
 };
 
 // ----------------------------------------------------------------------------
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index b30fd20..4eaeda3 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3599,11 +3599,10 @@
         // during last round
         size_t desiredFrames;
         const uint32_t sampleRate = track->mAudioTrackServerProxy->getSampleRate();
-        float speed, pitch;
-        track->mAudioTrackServerProxy->getPlaybackRate(&speed, &pitch);
+        AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate();
 
         desiredFrames = sourceFramesNeededWithTimestretch(
-                sampleRate, mNormalFrameCount, mSampleRate, speed);
+                sampleRate, mNormalFrameCount, mSampleRate, playbackRate.mSpeed);
         // TODO: ONLY USED FOR LEGACY RESAMPLERS, remove when they are removed.
         // add frames already consumed but not yet released by the resampler
         // because mAudioTrackServerProxy->framesReady() will include these frames
@@ -3772,15 +3771,12 @@
                 AudioMixer::SAMPLE_RATE,
                 (void *)(uintptr_t)reqSampleRate);
 
-            // set the playback rate as an float array {speed, pitch}
-            float playbackRate[2];
-            track->mAudioTrackServerProxy->getPlaybackRate(
-                    &playbackRate[0] /*speed*/, &playbackRate[1] /*pitch*/);
+            AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate();
             mAudioMixer->setParameter(
                 name,
                 AudioMixer::TIMESTRETCH,
                 AudioMixer::PLAYBACK_RATE,
-                playbackRate);
+                &playbackRate);
 
             /*
              * Select the appropriate output buffer for the track.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index da2d634..c6e9745 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -906,11 +906,9 @@
         // FIXME Not accurate under dynamic changes of sample rate and speed.
         // Do not use track's mSampleRate as it is not current for mixer tracks.
         uint32_t sampleRate = mAudioTrackServerProxy->getSampleRate();
-        float speed, pitch;
-        mAudioTrackServerProxy->getPlaybackRate(&speed, &pitch);
-        uint32_t unpresentedFrames =
-                ((double) playbackThread->mLatchQ.mUnpresentedFrames * sampleRate * speed)
-                / playbackThread->mSampleRate;
+        AudioPlaybackRate playbackRate = mAudioTrackServerProxy->getPlaybackRate();
+        uint32_t unpresentedFrames = ((double) playbackThread->mLatchQ.mUnpresentedFrames *
+                sampleRate * playbackRate.mSpeed)/ playbackThread->mSampleRate;
         // FIXME Since we're using a raw pointer as the key, it is theoretically possible
         //       for a brand new track to share the same address as a recently destroyed
         //       track, and thus for us to get the frames released of the wrong track.