From 28ed2f93324988767b5658eba7c1fa781a275183 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 7 Jun 2012 10:17:54 -0700 Subject: Reduce underruns in screen off, esp. with EQ Add MonoPipe APIs to specify setpoint. Use screen state to configure pipe setpoint. Fix a long-standing bug where pipe sleep time was excessive, which interacted poorly with governor and low clock frequencies. Now it deducts the elapsed time since last write(), which was significant when there was EQ and low clock frequency. Bug: 6618373 Change-Id: I6f3b0072c2244aeb033ef0795ad164491a164ff5 --- services/audioflinger/Android.mk | 3 ++ services/audioflinger/AudioFlinger.cpp | 23 +++++++++++ services/audioflinger/AudioFlinger.h | 1 + services/audioflinger/MonoPipe.cpp | 71 ++++++++++++++++++++++++++-------- services/audioflinger/MonoPipe.h | 7 +++- 5 files changed, 88 insertions(+), 17 deletions(-) (limited to 'services/audioflinger') diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 14b7fc12..ee843aa6 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -30,6 +30,9 @@ LOCAL_SRC_FILES := \ #LOCAL_C_INCLUDES += path/to/libsndfile/src #LOCAL_STATIC_LIBRARIES += libsndfile +# uncomment for systrace +# LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_AUDIO + LOCAL_MODULE := libnbaio include $(BUILD_STATIC_LIBRARY) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 67dbfe9d..be59ca01 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -164,6 +164,9 @@ static const enum { // up large writes into smaller ones, and the wrapper would need to deal with scheduler. } kUseFastMixer = FastMixer_Static; +static uint32_t gScreenState; // incremented by 2 when screen state changes, bit 0 == 1 means "off" + // AudioFlinger::setParameters() updates, other threads read w/o lock + // ---------------------------------------------------------------------------- #ifdef ADD_BATTERY_DATA @@ -889,6 +892,13 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& mBtNrecIsOff = btNrecIsOff; } } + String8 screenState; + if (param.get(String8(AudioParameter::keyScreenState), screenState) == NO_ERROR) { + bool isOff = screenState == "off"; + if (isOff != (gScreenState & 1)) { + gScreenState = ((gScreenState & ~1) + 2) | isOff; + } + } return final_result; } @@ -1501,6 +1511,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp& audioFlinge mMixerStatus(MIXER_IDLE), mMixerStatusIgnoringFastTracks(MIXER_IDLE), standbyDelay(AudioFlinger::mStandbyTimeInNsecs), + mScreenState(gScreenState), // index 0 is reserved for normal mixer's submix mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1) { @@ -2224,6 +2235,8 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud size_t numCounterOffers = 0; ssize_t index = monoPipe->negotiate(offers, 1, NULL, numCounterOffers); ALOG_ASSERT(index == 0); + monoPipe->setAvgFrames((mScreenState & 1) ? + (monoPipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2); mPipeSink = monoPipe; #ifdef TEE_SINK_FRAMES @@ -2686,6 +2699,16 @@ void AudioFlinger::PlaybackThread::threadLoop_write() #if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER) Tracer::traceBegin(ATRACE_TAG, "write"); #endif + // update the setpoint when gScreenState changes + uint32_t screenState = gScreenState; + if (screenState != mScreenState) { + mScreenState = screenState; + MonoPipe *pipe = (MonoPipe *)mPipeSink.get(); + if (pipe != NULL) { + pipe->setAvgFrames((mScreenState & 1) ? + (pipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2); + } + } ssize_t framesWritten = mNormalSink->write(mMixBuffer, count); #if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER) Tracer::traceEnd(ATRACE_TAG); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 1c44f2f7..677d466e 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -1119,6 +1119,7 @@ public: // For dumpsys sp mTeeSink; sp mTeeSource; + uint32_t mScreenState; // cached copy of gScreenState public: virtual bool hasFastMixer() const = 0; virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const diff --git a/services/audioflinger/MonoPipe.cpp b/services/audioflinger/MonoPipe.cpp index 6efb8b1d..f3fc19a7 100644 --- a/services/audioflinger/MonoPipe.cpp +++ b/services/audioflinger/MonoPipe.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "MonoPipe.h" #include "roundup.h" @@ -32,6 +33,9 @@ MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) : mBuffer(malloc(mMaxFrames * Format_frameSize(format))), mFront(0), mRear(0), + mWriteTsValid(false), + // mWriteTs + mSetpoint((reqFrames * 11) / 16), mWriteCanBlock(writeCanBlock) { } @@ -87,40 +91,75 @@ ssize_t MonoPipe::write(const void *buffer, size_t count) count -= written; buffer = (char *) buffer + (written << mBitShift); // Simulate blocking I/O by sleeping at different rates, depending on a throttle. - // The throttle tries to keep the pipe about 11/16 full on average, with a slight jitter. + // The throttle tries to keep the mean pipe depth near the setpoint, with a slight jitter. uint32_t ns; if (written > 0) { size_t filled = (mMaxFrames - avail) + written; // FIXME cache these values to avoid re-computation - if (filled <= mReqFrames / 4) { + if (filled <= mSetpoint / 2) { // pipe is (nearly) empty, fill quickly ns = written * ( 500000000 / Format_sampleRate(mFormat)); - } else if (filled <= mReqFrames / 2) { - // pipe is normal, fill at slightly faster rate + } else if (filled <= (mSetpoint * 3) / 4) { + // pipe is below setpoint, fill at slightly faster rate ns = written * ( 750000000 / Format_sampleRate(mFormat)); - } else if (filled <= (mReqFrames * 5) / 8) { - // pipe is normal, fill at nominal rate + } else if (filled <= (mSetpoint * 5) / 4) { + // pipe is at setpoint, fill at nominal rate ns = written * (1000000000 / Format_sampleRate(mFormat)); - } else if (filled <= (mReqFrames * 3) / 4) { - // pipe is normal, fill at slightly slower rate - ns = written * (1100000000 / Format_sampleRate(mFormat)); + } else if (filled <= (mSetpoint * 3) / 2) { + // pipe is above setpoint, fill at slightly slower rate + ns = written * (1150000000 / Format_sampleRate(mFormat)); + } else if (filled <= (mSetpoint * 7) / 4) { + // pipe is overflowing, fill slowly + ns = written * (1350000000 / Format_sampleRate(mFormat)); } else { - // pipe is (nearly) full, fill slowly - ns = written * (1250000000 / Format_sampleRate(mFormat)); + // pipe is severely overflowing + ns = written * (1750000000 / Format_sampleRate(mFormat)); } } else { - ns = mReqFrames * (250000000 / Format_sampleRate(mFormat)); + ns = count * (1350000000 / Format_sampleRate(mFormat)); } if (ns > 999999999) { ns = 999999999; } - struct timespec sleep; - sleep.tv_sec = 0; - sleep.tv_nsec = ns; - nanosleep(&sleep, NULL); + struct timespec nowTs; + bool nowTsValid = !clock_gettime(CLOCK_MONOTONIC, &nowTs); + // deduct the elapsed time since previous write() completed + if (nowTsValid && mWriteTsValid) { + time_t sec = nowTs.tv_sec - mWriteTs.tv_sec; + long nsec = nowTs.tv_nsec - mWriteTs.tv_nsec; + if (nsec < 0) { + --sec; + nsec += 1000000000; + } + if (sec == 0) { + if ((long) ns > nsec) { + ns -= nsec; + } else { + ns = 0; + } + } + } + if (ns > 0) { + const struct timespec req = {0, ns}; + nanosleep(&req, NULL); + } + // record the time that this write() completed + if (nowTsValid) { + mWriteTs = nowTs; + if ((mWriteTs.tv_nsec += ns) >= 1000000000) { + mWriteTs.tv_nsec -= 1000000000; + ++mWriteTs.tv_sec; + } + } + mWriteTsValid = nowTsValid; } mFramesWritten += totalFramesWritten; return totalFramesWritten; } +void MonoPipe::setAvgFrames(size_t setpoint) +{ + mSetpoint = setpoint; +} + } // namespace android diff --git a/services/audioflinger/MonoPipe.h b/services/audioflinger/MonoPipe.h index aaaa51ff..f6e2cb35 100644 --- a/services/audioflinger/MonoPipe.h +++ b/services/audioflinger/MonoPipe.h @@ -58,7 +58,9 @@ public: // average number of frames present in the pipe under normal conditions. // See throttling mechanism in MonoPipe::write() - size_t getAvgFrames() const { return (mReqFrames * 11) / 16; } + size_t getAvgFrames() const { return mSetpoint; } + void setAvgFrames(size_t setpoint); + size_t maxFrames() const { return mMaxFrames; } private: const size_t mReqFrames; // as requested in constructor, unrounded @@ -71,6 +73,9 @@ private: // read by writer with android_atomic_acquire_load volatile int32_t mRear; // written by writer with android_atomic_release_store, // read by reader with android_atomic_acquire_load + bool mWriteTsValid; // whether mWriteTs is valid + struct timespec mWriteTs; // time that the previous write() completed + size_t mSetpoint; // target value for pipe fill depth const bool mWriteCanBlock; // whether write() should block if the pipe is full }; -- cgit v1.2.3