diff options
| -rw-r--r-- | include/media/AudioParameter.h | 2 | ||||
| -rw-r--r-- | media/libeffects/visualizer/EffectVisualizer.cpp | 97 | ||||
| -rw-r--r-- | media/libmedia/AudioParameter.cpp | 1 | ||||
| -rw-r--r-- | services/audioflinger/Android.mk | 3 | ||||
| -rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 69 | ||||
| -rw-r--r-- | services/audioflinger/AudioFlinger.h | 3 | ||||
| -rw-r--r-- | services/audioflinger/FastMixer.cpp | 5 | ||||
| -rw-r--r-- | services/audioflinger/MonoPipe.cpp | 71 | ||||
| -rw-r--r-- | services/audioflinger/MonoPipe.h | 7 |
9 files changed, 197 insertions, 61 deletions
diff --git a/include/media/AudioParameter.h b/include/media/AudioParameter.h index 8cb2fa7..891bc4b 100644 --- a/include/media/AudioParameter.h +++ b/include/media/AudioParameter.h @@ -40,12 +40,14 @@ public: // keyFrameCount: to change audio output frame count, value is an int // keyInputSource: to change audio input source, value is an int in audio_source_t // (defined in media/mediarecorder.h) + // keyScreenState: either "on" or "off" static const char * const keyRouting; static const char * const keySamplingRate; static const char * const keyFormat; static const char * const keyChannels; static const char * const keyFrameCount; static const char * const keyInputSource; + static const char * const keyScreenState; String8 toString(); diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp index 44d05cd..d3c69f4 100644 --- a/media/libeffects/visualizer/EffectVisualizer.cpp +++ b/media/libeffects/visualizer/EffectVisualizer.cpp @@ -52,6 +52,8 @@ enum visualizer_state_e { // that the framework has stopped playing audio and we must start returning silence #define MAX_STALL_TIME_MS 1000 +#define CAPTURE_BUF_SIZE 65536 // "64k should be enough for everyone" + struct VisualizerContext { const struct effect_interface_s *mItfe; effect_config_t mConfig; @@ -59,10 +61,10 @@ struct VisualizerContext { uint32_t mCaptureSize; uint32_t mScalingMode; uint8_t mState; - uint8_t mCurrentBuf; - uint8_t mLastBuf; + uint8_t mLastCaptureIdx; + uint32_t mLatency; struct timespec mBufferUpdateTime; - uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX]; + uint8_t mCaptureBuf[CAPTURE_BUF_SIZE]; }; // @@ -72,11 +74,10 @@ struct VisualizerContext { void Visualizer_reset(VisualizerContext *pContext) { pContext->mCaptureIdx = 0; - pContext->mCurrentBuf = 0; - pContext->mLastBuf = 1; + pContext->mLastCaptureIdx = 0; pContext->mBufferUpdateTime.tv_sec = 0; - memset(pContext->mCaptureBuf[0], 0x80, VISUALIZER_CAPTURE_SIZE_MAX); - memset(pContext->mCaptureBuf[1], 0x80, VISUALIZER_CAPTURE_SIZE_MAX); + pContext->mLatency = 0; + memset(pContext->mCaptureBuf, 0x80, CAPTURE_BUF_SIZE); } //---------------------------------------------------------------------------- @@ -316,25 +317,25 @@ int Visualizer_process( uint32_t captIdx; uint32_t inIdx; - uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf]; + uint8_t *buf = pContext->mCaptureBuf; for (inIdx = 0, captIdx = pContext->mCaptureIdx; - inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize; + inIdx < inBuffer->frameCount; inIdx++, captIdx++) { + if (captIdx >= CAPTURE_BUF_SIZE) { + // wrap around + captIdx = 0; + } int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1]; smp = smp >> shift; buf[captIdx] = ((uint8_t)smp)^0x80; } - pContext->mCaptureIdx = captIdx; - // go to next buffer when buffer full - if (pContext->mCaptureIdx == pContext->mCaptureSize) { - pContext->mCurrentBuf ^= 1; - pContext->mCaptureIdx = 0; - - // update last buffer update time stamp - if (clock_gettime(CLOCK_MONOTONIC, &pContext->mBufferUpdateTime) < 0) { - pContext->mBufferUpdateTime.tv_sec = 0; - } + // XXX the following two should really be atomic, though it probably doesn't + // matter much for visualization purposes + pContext->mCaptureIdx = captIdx; + // update last buffer update time stamp + if (clock_gettime(CLOCK_MONOTONIC, &pContext->mBufferUpdateTime) < 0) { + pContext->mBufferUpdateTime.tv_sec = 0; } if (inBuffer->raw != outBuffer->raw) { @@ -464,6 +465,10 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, pContext->mScalingMode = *((uint32_t *)p->data + 1); ALOGV("set mScalingMode = %d", pContext->mScalingMode); break; + case VISUALIZER_PARAM_LATENCY: + pContext->mLatency = *((uint32_t *)p->data + 1); + ALOGV("set mLatency = %d", pContext->mLatency); + break; default: *(int32_t *)pReplyData = -EINVAL; } @@ -481,13 +486,9 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, return -EINVAL; } if (pContext->mState == VISUALIZER_STATE_ACTIVE) { - memcpy(pReplyData, - pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1], - pContext->mCaptureSize); - // if audio framework has stopped playing audio although the effect is still - // active we must clear the capture buffer to return silence - if ((pContext->mLastBuf == pContext->mCurrentBuf) && - (pContext->mBufferUpdateTime.tv_sec != 0)) { + int32_t latencyMs = pContext->mLatency; + uint32_t deltaMs = 0; + if (pContext->mBufferUpdateTime.tv_sec != 0) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { time_t secs = ts.tv_sec - pContext->mBufferUpdateTime.tv_sec; @@ -496,17 +497,45 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, --secs; nsec += 1000000000; } - uint32_t deltaMs = secs * 1000 + nsec / 1000000; - if (deltaMs > MAX_STALL_TIME_MS) { - ALOGV("capture going to idle"); - pContext->mBufferUpdateTime.tv_sec = 0; - memset(pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1], - 0x80, - pContext->mCaptureSize); + deltaMs = secs * 1000 + nsec / 1000000; + latencyMs -= deltaMs; + if (latencyMs < 0) { + latencyMs = 0; } } } - pContext->mLastBuf = pContext->mCurrentBuf; + uint32_t deltaSmpl = pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000; + + int32_t capturePoint = pContext->mCaptureIdx - pContext->mCaptureSize - deltaSmpl; + int32_t captureSize = pContext->mCaptureSize; + if (capturePoint < 0) { + int32_t size = -capturePoint; + if (size > captureSize) { + size = captureSize; + } + memcpy(pReplyData, + pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint, + size); + pReplyData += size; + captureSize -= size; + capturePoint = 0; + } + memcpy(pReplyData, + pContext->mCaptureBuf + capturePoint, + captureSize); + + + // if audio framework has stopped playing audio although the effect is still + // active we must clear the capture buffer to return silence + if ((pContext->mLastCaptureIdx == pContext->mCaptureIdx) && + (pContext->mBufferUpdateTime.tv_sec != 0)) { + if (deltaMs > MAX_STALL_TIME_MS) { + ALOGV("capture going to idle"); + pContext->mBufferUpdateTime.tv_sec = 0; + memset(pReplyData, 0x80, pContext->mCaptureSize); + } + } + pContext->mLastCaptureIdx = pContext->mCaptureIdx; } else { memset(pReplyData, 0x80, pContext->mCaptureSize); } diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp index 9766ee6..e3fea77 100644 --- a/media/libmedia/AudioParameter.cpp +++ b/media/libmedia/AudioParameter.cpp @@ -31,6 +31,7 @@ const char * const AudioParameter::keyFormat = AUDIO_PARAMETER_STREAM_FORMAT; const char * const AudioParameter::keyChannels = AUDIO_PARAMETER_STREAM_CHANNELS; const char * const AudioParameter::keyFrameCount = AUDIO_PARAMETER_STREAM_FRAME_COUNT; const char * const AudioParameter::keyInputSource = AUDIO_PARAMETER_STREAM_INPUT_SOURCE; +const char * const AudioParameter::keyScreenState = AUDIO_PARAMETER_KEY_SCREEN_STATE; AudioParameter::AudioParameter(const String8& keyValuePairs) { diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 14b7fc1..ee843aa 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 2cfc3e8..be59ca0 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<AudioFlinger>& 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) { @@ -1818,6 +1829,10 @@ uint32_t AudioFlinger::PlaybackThread::correctLatency(uint32_t latency) const uint32_t AudioFlinger::PlaybackThread::latency() const { Mutex::Autolock _l(mLock); + return latency_l(); +} +uint32_t AudioFlinger::PlaybackThread::latency_l() const +{ if (initCheck() == NO_ERROR) { return correctLatency(mOutput->stream->get_latency(mOutput->stream)); } else { @@ -2220,6 +2235,8 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& 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 @@ -2682,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); @@ -3887,6 +3914,7 @@ void AudioFlinger::DuplicatingThread::threadLoop_mix() } sleepTime = 0; writeFrames = mNormalFrameCount; + standbyTime = systemTime() + standbyDelay; } void AudioFlinger::DuplicatingThread::threadLoop_sleepTime() @@ -3898,21 +3926,19 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime() sleepTime = idleSleepTime; } } else if (mBytesWritten != 0) { - // flush remaining overflow buffers in output tracks - for (size_t i = 0; i < outputTracks.size(); i++) { - if (outputTracks[i]->isActive()) { - sleepTime = 0; - writeFrames = 0; - memset(mMixBuffer, 0, mixBufferSize); - break; - } + if (mMixerStatus == MIXER_TRACKS_ENABLED) { + writeFrames = mNormalFrameCount; + memset(mMixBuffer, 0, mixBufferSize); + } else { + // flush remaining overflow buffers in output tracks + writeFrames = 0; } + sleepTime = 0; } } void AudioFlinger::DuplicatingThread::threadLoop_write() { - standbyTime = systemTime() + standbyDelay; for (size_t i = 0; i < outputTracks.size(); i++) { outputTracks[i]->write(mMixBuffer, writeFrames); } @@ -8187,6 +8213,31 @@ status_t AudioFlinger::EffectModule::configure() status = cmdStatus; } + if (status == 0 && + (memcmp(&mDescriptor.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0)) { + uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2]; + effect_param_t *p = (effect_param_t *)buf32; + + p->psize = sizeof(uint32_t); + p->vsize = sizeof(uint32_t); + size = sizeof(int); + *(int32_t *)p->data = VISUALIZER_PARAM_LATENCY; + + uint32_t latency = 0; + PlaybackThread *pbt = thread->mAudioFlinger->checkPlaybackThread_l(thread->mId); + if (pbt != NULL) { + latency = pbt->latency_l(); + } + + *((int32_t *)p->data + 1)= latency; + (*mEffectInterface)->command(mEffectInterface, + EFFECT_CMD_SET_PARAM, + sizeof(effect_param_t) + 8, + &buf32, + &size, + &cmdStatus); + } + mMaxDisableWaitCnt = (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate) / (1000 * mConfig.outputCfg.buffer.frameCount); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index a0e0ea5..677d466 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -965,6 +965,8 @@ public: // return estimated latency in milliseconds, as reported by HAL uint32_t latency() const; + // same, but lock must already be held + uint32_t latency_l() const; void setMasterVolume(float value); void setMasterMute(bool muted); @@ -1117,6 +1119,7 @@ public: // For dumpsys sp<NBAIO_Sink> mTeeSink; sp<NBAIO_Source> 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/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index d8bed40..3bb7b44 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -483,7 +483,10 @@ bool FastMixer::threadLoop() (int) sec, nsec / 1000000L); dumpState->mOverruns++; } - sleepNs = periodNs - overrunNs; + // Code for non blocking audio HAL. Sleep time must be tuned to allow + // catching up after an underrun + // sleepNs = periodNs - overrunNs; + sleepNs = -1; } else { sleepNs = -1; ignoreNextOverrun = false; diff --git a/services/audioflinger/MonoPipe.cpp b/services/audioflinger/MonoPipe.cpp index 6efb8b1..f3fc19a 100644 --- a/services/audioflinger/MonoPipe.cpp +++ b/services/audioflinger/MonoPipe.cpp @@ -20,6 +20,7 @@ #include <cutils/atomic.h> #include <cutils/compiler.h> #include <utils/Log.h> +#include <utils/Trace.h> #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 aaaa51f..f6e2cb3 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 }; |
