From 5a8a95de6dad1a3bcf3da5a37b35766e89086e13 Mon Sep 17 00:00:00 2001 From: Ricardo Garcia Date: Sat, 18 Apr 2015 14:47:04 -0700 Subject: 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 --- include/media/AudioResamplerPublic.h | 78 ++++++++++++++++++++-- include/media/AudioTrack.h | 8 +-- include/private/media/AudioTrackShared.h | 26 +++----- media/libmedia/AudioTrack.cpp | 55 +++++++++------- media/libmedia/AudioTrackShared.cpp | 10 +-- services/audioflinger/AudioMixer.cpp | 46 +++++++------ services/audioflinger/AudioMixer.h | 6 +- services/audioflinger/BufferProviders.cpp | 105 ++++++++++++++++++++---------- services/audioflinger/BufferProviders.h | 21 +++--- services/audioflinger/Threads.cpp | 12 ++-- services/audioflinger/Tracks.cpp | 8 +-- 11 files changed, 239 insertions(+), 136 deletions(-) diff --git a/include/media/AudioResamplerPublic.h b/include/media/AudioResamplerPublic.h index 07d946d..53b8c13 100644 --- a/include/media/AudioResamplerPublic.h +++ b/include/media/AudioResamplerPublic.h @@ -18,6 +18,9 @@ #define ANDROID_AUDIO_RESAMPLER_PUBLIC_H #include +#include + +namespace android { // AUDIO_RESAMPLER_DOWN_RATIO_MAX is the maximum ratio between the original // audio sample rate and the target rate when downsampling, @@ -34,13 +37,76 @@ // an int32_t of the phase increments, making the resulting sample rate inexact. #define AUDIO_RESAMPLER_UP_RATIO_MAX 65536 -#define AUDIO_TIMESTRETCH_SPEED_MIN 0.5f -#define AUDIO_TIMESTRETCH_SPEED_MAX 2.0f +// AUDIO_TIMESTRETCH_SPEED_MIN and AUDIO_TIMESTRETCH_SPEED_MAX define the min and max time stretch +// speeds supported by the system. These are enforced by the system and values outside this range +// will result in a runtime error. +// Depending on the AudioPlaybackRate::mStretchMode, the effective limits might be narrower than +// the ones specified here +// AUDIO_TIMESTRETCH_SPEED_MIN_DELTA is the minimum absolute speed difference that might trigger a +// parameter update +#define AUDIO_TIMESTRETCH_SPEED_MIN 0.01f +#define AUDIO_TIMESTRETCH_SPEED_MAX 20.0f #define AUDIO_TIMESTRETCH_SPEED_NORMAL 1.0f +#define AUDIO_TIMESTRETCH_SPEED_MIN_DELTA 0.0001f -#define AUDIO_TIMESTRETCH_PITCH_MIN 0.5f -#define AUDIO_TIMESTRETCH_PITCH_MAX 2.0f +// AUDIO_TIMESTRETCH_PITCH_MIN and AUDIO_TIMESTRETCH_PITCH_MAX define the min and max time stretch +// pitch shifting supported by the system. These are not enforced by the system and values +// outside this range might result in a pitch different than the one requested. +// Depending on the AudioPlaybackRate::mStretchMode, the effective limits might be narrower than +// the ones specified here. +// AUDIO_TIMESTRETCH_PITCH_MIN_DELTA is the minimum absolute pitch difference that might trigger a +// parameter update +#define AUDIO_TIMESTRETCH_PITCH_MIN 0.25f +#define AUDIO_TIMESTRETCH_PITCH_MAX 4.0f #define AUDIO_TIMESTRETCH_PITCH_NORMAL 1.0f +#define AUDIO_TIMESTRETCH_PITCH_MIN_DELTA 0.0001f + + +//Determines the current algorithm used for stretching +enum AudioTimestretchStretchMode : int32_t { + AUDIO_TIMESTRETCH_STRETCH_DEFAULT = 0, + AUDIO_TIMESTRETCH_STRETCH_SPEECH = 1, + //TODO: add more stretch modes/algorithms +}; + +//Limits for AUDIO_TIMESTRETCH_STRETCH_SPEECH mode +#define TIMESTRETCH_SONIC_SPEED_MIN 0.1f +#define TIMESTRETCH_SONIC_SPEED_MAX 6.0f + +//Determines behavior of Timestretch if current algorithm can't perform +//with current parameters. +// FALLBACK_CUT_REPEAT: (internal only) for speed <1.0 will truncate frames +// for speed > 1.0 will repeat frames +// FALLBACK_MUTE: will set all processed frames to zero +// FALLBACK_FAIL: will stop program execution and log a fatal error +enum AudioTimestretchFallbackMode : int32_t { + AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT = -1, + AUDIO_TIMESTRETCH_FALLBACK_DEFAULT = 0, + AUDIO_TIMESTRETCH_FALLBACK_MUTE = 1, + AUDIO_TIMESTRETCH_FALLBACK_FAIL = 2, +}; + +struct AudioPlaybackRate { + float mSpeed; + float mPitch; + enum AudioTimestretchStretchMode mStretchMode; + enum AudioTimestretchFallbackMode mFallbackMode; +}; + +static const AudioPlaybackRate AUDIO_PLAYBACK_RATE_DEFAULT = { + AUDIO_TIMESTRETCH_SPEED_NORMAL, + AUDIO_TIMESTRETCH_PITCH_NORMAL, + AUDIO_TIMESTRETCH_STRETCH_DEFAULT, + AUDIO_TIMESTRETCH_FALLBACK_DEFAULT +}; + +static inline bool isAudioPlaybackRateEqual(const AudioPlaybackRate &pr1, + const AudioPlaybackRate &pr2) { + return fabs(pr1.mSpeed - pr2.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA && + fabs(pr1.mPitch - pr2.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA && + pr2.mStretchMode == pr2.mStretchMode && + pr2.mFallbackMode == pr2.mFallbackMode; +} // TODO: Consider putting these inlines into a class scope @@ -77,4 +143,8 @@ static inline size_t sourceFramesNeededWithTimestretch( return required * (double)speed + 1 + 1; // accounting for rounding dependencies } +} // namespace android + +// --------------------------------------------------------------------------- + #endif // ANDROID_AUDIO_RESAMPLER_PUBLIC_H diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index a06197f..2d34c02 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -21,6 +21,7 @@ #include #include #include +#include #include namespace android { @@ -369,10 +370,10 @@ public: * Speed increases the playback rate of media, but does not alter pitch. * Pitch increases the "tonal frequency" of media, but does not affect the playback rate. */ - status_t setPlaybackRate(float speed, float pitch); + status_t setPlaybackRate(const AudioPlaybackRate &playbackRate); /* Return current playback rate */ - void getPlaybackRate(float *speed, float *pitch) const; + const AudioPlaybackRate& getPlaybackRate() const; /* Enables looping and sets the start and end points of looping. * Only supported for static buffer mode. @@ -748,8 +749,7 @@ protected: float mVolume[2]; float mSendLevel; mutable uint32_t mSampleRate; // mutable because getSampleRate() can update it - float mSpeed; // timestretch: 1.0f for normal speed. - float mPitch; // timestretch: 1.0f for normal pitch. + AudioPlaybackRate mPlaybackRate; size_t mFrameCount; // corresponds to current IAudioTrack, value is // reported back by AudioFlinger to the client size_t mReqFrameCount; // frame count to request the first or next time diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h index 6cc2e2b..1e5064f 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -114,13 +114,7 @@ struct AudioTrackSharedStatic { mPosLoopQueue; }; - -struct AudioTrackPlaybackRate { - float mSpeed; - float mPitch; -}; - -typedef SingleStateQueue AudioTrackPlaybackRateQueue; +typedef SingleStateQueue PlaybackRateQueue; // ---------------------------------------------------------------------------- @@ -168,7 +162,7 @@ private: uint32_t mSampleRate; // AudioTrack only: client's requested sample rate in Hz // or 0 == default. Write-only client, read-only server. - AudioTrackPlaybackRateQueue::Shared mPlaybackRateQueue; + PlaybackRateQueue::Shared mPlaybackRateQueue; // client write-only, server read-only uint16_t mSendLevel; // Fixed point U4.12 so 0x1000 means 1.0 @@ -345,10 +339,7 @@ public: mCblk->mSampleRate = sampleRate; } - void setPlaybackRate(float speed, float pitch) { - AudioTrackPlaybackRate playbackRate; - playbackRate.mSpeed = speed; - playbackRate.mPitch = pitch; + void setPlaybackRate(const AudioPlaybackRate& playbackRate) { mPlaybackRateMutator.push(playbackRate); } @@ -365,7 +356,7 @@ public: status_t waitStreamEndDone(const struct timespec *requested); private: - AudioTrackPlaybackRateQueue::Mutator mPlaybackRateMutator; + PlaybackRateQueue::Mutator mPlaybackRateMutator; }; class StaticAudioTrackClientProxy : public AudioTrackClientProxy { @@ -483,8 +474,7 @@ public: : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer), mPlaybackRateObserver(&cblk->mPlaybackRateQueue) { mCblk->mSampleRate = sampleRate; - mPlaybackRate.mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL; - mPlaybackRate.mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL; + mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; } protected: virtual ~AudioTrackServerProxy() { } @@ -520,11 +510,11 @@ public: virtual size_t framesReleased() const { return mCblk->mServer; } // Return the playback speed and pitch read atomically. Not multi-thread safe on server side. - void getPlaybackRate(float *speed, float *pitch); + AudioPlaybackRate getPlaybackRate(); private: - AudioTrackPlaybackRate mPlaybackRate; // last observed playback rate - AudioTrackPlaybackRateQueue::Observer mPlaybackRateObserver; + AudioPlaybackRate mPlaybackRate; // last observed playback rate + PlaybackRateQueue::Observer mPlaybackRateObserver; }; class StaticAudioTrackServerProxy : public AudioTrackServerProxy { diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 055556f..b51b94f 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -393,8 +393,7 @@ status_t AudioTrack::set( return BAD_VALUE; } mSampleRate = sampleRate; - mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL; - mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL; + mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; // Make copy of input parameter offloadInfo so that in the future: // (a) createTrack_l doesn't need it as an input parameter @@ -722,7 +721,7 @@ status_t AudioTrack::setSampleRate(uint32_t rate) return NO_INIT; } // pitch is emulated by adjusting speed and sampleRate - const uint32_t effectiveSampleRate = adjustSampleRate(rate, mPitch); + const uint32_t effectiveSampleRate = adjustSampleRate(rate, mPlaybackRate.mPitch); if (rate == 0 || effectiveSampleRate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) { return BAD_VALUE; } @@ -757,10 +756,10 @@ uint32_t AudioTrack::getSampleRate() const return mSampleRate; } -status_t AudioTrack::setPlaybackRate(float speed, float pitch) +status_t AudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) { AutoMutex lock(mLock); - if (speed == mSpeed && pitch == mPitch) { + if (isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) { return NO_ERROR; } if (mIsTimed || isOffloadedOrDirect_l()) { @@ -770,32 +769,37 @@ status_t AudioTrack::setPlaybackRate(float speed, float pitch) return INVALID_OPERATION; } // pitch is emulated by adjusting speed and sampleRate - const uint32_t effectiveRate = adjustSampleRate(mSampleRate, pitch); - const float effectiveSpeed = adjustSpeed(speed, pitch); - const float effectivePitch = adjustPitch(pitch); + const uint32_t effectiveRate = adjustSampleRate(mSampleRate, playbackRate.mPitch); + const float effectiveSpeed = adjustSpeed(playbackRate.mSpeed, playbackRate.mPitch); + const float effectivePitch = adjustPitch(playbackRate.mPitch); if (effectiveSpeed < AUDIO_TIMESTRETCH_SPEED_MIN || effectiveSpeed > AUDIO_TIMESTRETCH_SPEED_MAX || effectivePitch < AUDIO_TIMESTRETCH_PITCH_MIN || effectivePitch > AUDIO_TIMESTRETCH_PITCH_MAX) { return BAD_VALUE; + //TODO: add function in AudioResamplerPublic.h to check for validity. } // Check if the buffer size is compatible. if (!isSampleRateSpeedAllowed_l(effectiveRate, effectiveSpeed)) { - ALOGV("setPlaybackRate(%f, %f) failed", speed, pitch); + ALOGV("setPlaybackRate(%f, %f) failed", playbackRate.mSpeed, playbackRate.mPitch); return BAD_VALUE; } - mSpeed = speed; - mPitch = pitch; - mProxy->setPlaybackRate(effectiveSpeed, effectivePitch); + mPlaybackRate = playbackRate; + mProxy->setPlaybackRate(playbackRate); + + //modify this + AudioPlaybackRate playbackRateTemp = playbackRate; + playbackRateTemp.mSpeed = effectiveSpeed; + playbackRateTemp.mPitch = effectivePitch; + mProxy->setPlaybackRate(playbackRateTemp); mProxy->setSampleRate(effectiveRate); // FIXME: not quite "atomic" with setPlaybackRate return NO_ERROR; } -void AudioTrack::getPlaybackRate(float *speed, float *pitch) const +const AudioPlaybackRate& AudioTrack::getPlaybackRate() const { AutoMutex lock(mLock); - *speed = mSpeed; - *pitch = mPitch; + return mPlaybackRate; } status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) @@ -1170,7 +1174,8 @@ status_t AudioTrack::createTrack_l() if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) { // for normal tracks precompute the frame count based on speed. const size_t minFrameCount = calculateMinFrameCount( - afLatency, afFrameCount, afSampleRate, mSampleRate, mSpeed); + afLatency, afFrameCount, afSampleRate, mSampleRate, + mPlaybackRate.mSpeed); if (frameCount < minFrameCount) { frameCount = minFrameCount; } @@ -1342,11 +1347,15 @@ status_t AudioTrack::createTrack_l() gain_from_float(mVolume[AUDIO_INTERLEAVE_RIGHT]))); mProxy->setSendLevel(mSendLevel); - const uint32_t effectiveSampleRate = adjustSampleRate(mSampleRate, mPitch); - const float effectiveSpeed = adjustSpeed(mSpeed, mPitch); - const float effectivePitch = adjustPitch(mPitch); + const uint32_t effectiveSampleRate = adjustSampleRate(mSampleRate, mPlaybackRate.mPitch); + const float effectiveSpeed = adjustSpeed(mPlaybackRate.mSpeed, mPlaybackRate.mPitch); + const float effectivePitch = adjustPitch(mPlaybackRate.mPitch); mProxy->setSampleRate(effectiveSampleRate); - mProxy->setPlaybackRate(effectiveSpeed, effectivePitch); + + AudioPlaybackRate playbackRateTemp = mPlaybackRate; + playbackRateTemp.mSpeed = effectiveSpeed; + playbackRateTemp.mPitch = effectivePitch; + mProxy->setPlaybackRate(playbackRateTemp); mProxy->setMinimum(mNotificationFramesAct); mDeathNotifier = new DeathNotifier(this); @@ -1716,7 +1725,7 @@ nsecs_t AudioTrack::processAudioBuffer() // Cache other fields that will be needed soon uint32_t sampleRate = mSampleRate; - float speed = mSpeed; + float speed = mPlaybackRate.mSpeed; uint32_t notificationFrames = mNotificationFramesAct; if (mRefreshRemaining) { mRefreshRemaining = false; @@ -2138,7 +2147,7 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) } const int64_t deltaTimeUs = timestampTimeUs - mStartUs; const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000 - / ((double)mSampleRate * mSpeed); + / ((double)mSampleRate * mPlaybackRate.mSpeed); if (deltaPositionByUs > deltaTimeUs + kTimeJitterUs) { // Verify that the counter can't count faster than the sample rate @@ -2226,7 +2235,7 @@ status_t AudioTrack::dump(int fd, const Vector& args __unused) const mChannelCount, mFrameCount); result.append(buffer); snprintf(buffer, 255, " sample rate(%u), speed(%f), status(%d)\n", - mSampleRate, mSpeed, mStatus); + mSampleRate, mPlaybackRate.mSpeed, mStatus); result.append(buffer); snprintf(buffer, 255, " state(%d), latency (%d)\n", mState, mLatency); result.append(buffer); diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index aee9fc2..1d7aed2 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -794,14 +794,10 @@ void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount) (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags); } -void AudioTrackServerProxy::getPlaybackRate(float *speed, float *pitch) +AudioPlaybackRate AudioTrackServerProxy::getPlaybackRate() { // do not call from multiple threads without holding lock - AudioTrackPlaybackRate playbackRate; - if (mPlaybackRateObserver.poll(playbackRate)) { - mPlaybackRate = playbackRate; - } - *speed = mPlaybackRate.mSpeed; - *pitch = mPlaybackRate.mPitch; + mPlaybackRateObserver.poll(mPlaybackRate); + return mPlaybackRate; } // --------------------------------------------------------------------------- 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 #include #include -#include #include "AudioMixerOps.h" #include "AudioMixer.h" @@ -223,8 +222,7 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, 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 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) case TIMESTRETCH: switch (param) { case PLAYBACK_RATE: { - const float speed = reinterpret_cast(value)[0]; - const float pitch = reinterpret_cast(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(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 @@ bool AudioMixer::track_t::setResampler(uint32_t trackSampleRate, uint32_t devSam 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(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 #include +#include #include #include #include @@ -259,8 +260,7 @@ private: 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 @@ private: 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 @@ void ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frame } 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 @@ status_t TimestretchBufferProvider::getNextBuffer( // 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 @@ void TimestretchBufferProvider::reset() 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 @@ void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames // 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 @@ protected: 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 @@ public: // 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 @@ protected: 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 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // 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 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac 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 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times // 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. -- cgit v1.1