Merge "threadLoop merge"

This commit is contained in:
Glenn Kasten
2012-03-05 12:18:14 -08:00
committed by Android (Google) Code Review
2 changed files with 342 additions and 405 deletions

View File

@@ -1919,10 +1919,10 @@ uint32_t AudioFlinger::PlaybackThread::activeSleepTimeUs()
AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
audio_io_handle_t id, uint32_t device, type_t type)
: PlaybackThread(audioFlinger, output, id, device, type),
mAudioMixer(new AudioMixer(mFrameCount, mSampleRate)),
mPrevMixerStatus(MIXER_IDLE)
: PlaybackThread(audioFlinger, output, id, device, type)
{
mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
mPrevMixerStatus = MIXER_IDLE;
// FIXME - Current mixer implementation only supports stereo output
if (mChannelCount == 1) {
ALOGE("Invalid audio hardware channel count");
@@ -1991,44 +1991,67 @@ void AudioFlinger::PlaybackThread::checkSilentMode_l()
}
}
bool AudioFlinger::MixerThread::threadLoop()
bool AudioFlinger::PlaybackThread::threadLoop()
{
// DirectOutputThread has single trackToRemove instead of Vector
// MIXER || DUPLICATING
Vector< sp<Track> > tracksToRemove;
// DirectOutputThread has activeTrack here
nsecs_t standbyTime = systemTime();
size_t mixBufferSize = mFrameCount * mFrameSize;
// DIRECT
sp<Track> trackToRemove;
standbyTime = systemTime();
mixBufferSize = mFrameCount * mFrameSize;
// MIXER
// FIXME: Relaxed timing because of a certain device that can't meet latency
// Should be reduced to 2x after the vendor fixes the driver issue
// increase threshold again due to low power audio mode. The way this warning threshold is
// calculated and its usefulness should be reconsidered anyway.
nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 15;
nsecs_t lastWarning = 0;
bool longStandbyExit = false;
if (mType == MIXER) {
longStandbyExit = false;
}
uint32_t activeSleepTime = activeSleepTimeUs();
uint32_t idleSleepTime = idleSleepTimeUs();
uint32_t sleepTime = idleSleepTime;
// DUPLICATING
// FIXME could this be made local to while loop?
writeFrames = 0;
uint32_t sleepTimeShift = 0;
activeSleepTime = activeSleepTimeUs();
idleSleepTime = idleSleepTimeUs();
sleepTime = idleSleepTime;
if (mType == MIXER) {
sleepTimeShift = 0;
}
// MIXER
CpuStats cpuStats;
// DirectOutputThread has shorter standbyDelay
// DIRECT
if (mType == DIRECT) {
// use shorter standby delay as on normal output to release
// hardware resources as soon as possible
standbyDelay = microseconds(activeSleepTime*2);
}
acquireWakeLock();
while (!exitPending())
{
if (mType == MIXER) {
cpuStats.sample();
// DirectOutputThread has rampVolume, leftVol, rightVol
}
Vector< sp<EffectChain> > effectChains;
processConfigEvents();
mixer_state mixerStatus = MIXER_IDLE;
if (mType == DIRECT) {
activeTrack.clear();
}
mixerStatus = MIXER_IDLE;
{ // scope for mLock
Mutex::Autolock _l(mLock);
@@ -2036,23 +2059,46 @@ bool AudioFlinger::MixerThread::threadLoop()
if (checkForNewParameters_l()) {
mixBufferSize = mFrameCount * mFrameSize;
if (mType == MIXER) {
// FIXME: Relaxed timing because of a certain device that can't meet latency
// Should be reduced to 2x after the vendor fixes the driver issue
// increase threshold again due to low power audio mode. The way this warning
// threshold is calculated and its usefulness should be reconsidered anyway.
maxPeriod = seconds(mFrameCount) / mSampleRate * 15;
}
if (mType == DUPLICATING) {
updateWaitTime();
}
activeSleepTime = activeSleepTimeUs();
idleSleepTime = idleSleepTimeUs();
// DirectOutputThread updates standbyDelay also
if (mType == DIRECT) {
standbyDelay = microseconds(activeSleepTime*2);
}
}
if (mType == DUPLICATING) {
#if 0 // see earlier FIXME
// Now that this is a field instead of local variable,
// clear it so it is empty the first time through the loop,
// and later an assignment could combine the clear with the loop below
outputTracks.clear();
#endif
for (size_t i = 0; i < mOutputTracks.size(); i++) {
outputTracks.add(mOutputTracks[i]);
}
}
// put audio hardware into standby after short delay
if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
mSuspended > 0)) {
if (!mStandby) {
ALOGV("Audio hardware entering standby, mixer %p, suspend count %u", this, mSuspended);
mOutput->stream->common.standby(&mOutput->stream->common);
threadLoop_standby();
mStandby = true;
mBytesWritten = 0;
}
@@ -2061,6 +2107,10 @@ bool AudioFlinger::MixerThread::threadLoop()
// we're about to wait, flush the binder command buffer
IPCThreadState::self()->flushCommands();
if (mType == DUPLICATING) {
outputTracks.clear();
}
if (exitPending()) break;
releaseWakeLock_l();
@@ -2070,17 +2120,41 @@ bool AudioFlinger::MixerThread::threadLoop()
ALOGV("Thread %p type %d TID %d waking up", this, mType, gettid());
acquireWakeLock_l();
if (mType == MIXER || mType == DUPLICATING) {
mPrevMixerStatus = MIXER_IDLE;
}
checkSilentMode_l();
if (mType == MIXER || mType == DUPLICATING) {
standbyTime = systemTime() + mStandbyTimeInNsecs;
}
if (mType == DIRECT) {
standbyTime = systemTime() + standbyDelay;
}
sleepTime = idleSleepTime;
if (mType == MIXER) {
sleepTimeShift = 0;
}
continue;
}
}
// FIXME merge these
if (mType == MIXER || mType == DUPLICATING) {
mixerStatus = prepareTracks_l(&tracksToRemove);
}
if (mType == DIRECT) {
mixerStatus = threadLoop_prepareTracks_l(trackToRemove);
// see FIXME in AudioFlinger.h
if (mixerStatus == MIXER_CONTINUE) {
continue;
}
}
// prevent any changes in effect chain list and in each effect chain
// during mixing and effect process as the audio buffers could be deleted
@@ -2088,7 +2162,130 @@ bool AudioFlinger::MixerThread::threadLoop()
lockEffectChains_l(effectChains);
}
if (mType == DIRECT) {
// For DirectOutputThread, this test is equivalent to "activeTrack != 0"
}
if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
threadLoop_mix();
} else {
threadLoop_sleepTime();
}
if (mSuspended > 0) {
sleepTime = suspendSleepTimeUs();
}
// only process effects if we're going to write
if (sleepTime == 0) {
if (mixerStatus == MIXER_TRACKS_READY) {
// Non-trivial for DIRECT only
applyVolume();
}
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();
}
}
// enable changes in effect chain
unlockEffectChains(effectChains);
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
threadLoop_write();
if (mType == MIXER) {
// write blocked detection
nsecs_t now = systemTime();
nsecs_t delta = now - mLastWriteTime;
if (!mStandby && delta > maxPeriod) {
mNumDelayedWrites++;
if ((now - lastWarning) > kWarningThrottleNs) {
ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
ns2ms(delta), mNumDelayedWrites, this);
lastWarning = now;
}
// FIXME this is broken: longStandbyExit should be handled out of the if() and with
// a different threshold. Or completely removed for what it is worth anyway...
if (mStandby) {
longStandbyExit = true;
}
}
}
mStandby = false;
} else {
usleep(sleepTime);
}
// finally let go of removed track(s), without the lock held
// since we can't guarantee the destructors won't acquire that
// same lock.
// FIXME merge these
if (mType == MIXER) {
tracksToRemove.clear();
}
if (mType == DIRECT) {
trackToRemove.clear();
activeTrack.clear();
}
if (mType == DUPLICATING) {
tracksToRemove.clear();
outputTracks.clear();
}
// Effect chains will be actually deleted here if they were removed from
// mEffectChains list during mixing or effects processing
effectChains.clear();
// FIXME Note that the above .clear() is no longer necessary since effectChains
// is now local to this block, but will keep it for now (at least until merge done).
}
if (mType == MIXER || mType == DIRECT) {
// put output stream into standby mode
if (!mStandby) {
mOutput->stream->common.standby(&mOutput->stream->common);
}
}
if (mType == DUPLICATING) {
// for DuplicatingThread, standby mode is handled by the outputTracks
}
releaseWakeLock();
ALOGV("Thread %p type %d exiting", this, mType);
return false;
}
// shared by MIXER and DIRECT, overridden by DUPLICATING
void AudioFlinger::PlaybackThread::threadLoop_write()
{
// FIXME rewrite to reduce number of system calls
mLastWriteTime = systemTime();
mInWrite = true;
mBytesWritten += mixBufferSize;
int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
mNumWrites++;
mInWrite = false;
}
// shared by MIXER and DIRECT, overridden by DUPLICATING
void AudioFlinger::PlaybackThread::threadLoop_standby()
{
ALOGV("Audio hardware entering standby, mixer %p, suspend count %u", this, mSuspended);
mOutput->stream->common.standby(&mOutput->stream->common);
}
void AudioFlinger::MixerThread::threadLoop_mix()
{
// obtain the presentation timestamp of the next output buffer
int64_t pts;
status_t status = INVALID_OPERATION;
@@ -2114,7 +2311,10 @@ bool AudioFlinger::MixerThread::threadLoop()
sleepTime = 0;
standbyTime = systemTime() + mStandbyTimeInNsecs;
//TODO: delay standby when effects have a tail
} else {
}
void AudioFlinger::MixerThread::threadLoop_sleepTime()
{
// If no tracks are ready, sleep once for the duration of an output
// buffer size, then write 0s to the output
if (sleepTime == 0) {
@@ -2140,79 +2340,6 @@ bool AudioFlinger::MixerThread::threadLoop()
ALOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
}
// TODO add standby time extension fct of effect tail
}
if (mSuspended > 0) {
sleepTime = suspendSleepTimeUs();
}
// only process effects if we're going to write
if (sleepTime == 0) {
// DirectOutputThread adds applyVolume here
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();
}
}
// enable changes in effect chain
unlockEffectChains(effectChains);
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
// FIXME Only in MixerThread, and rewrite to reduce number of system calls
mLastWriteTime = systemTime();
mInWrite = true;
mBytesWritten += mixBufferSize;
int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
mNumWrites++;
mInWrite = false;
// Only in MixerThread: start of write blocked detection
nsecs_t now = systemTime();
nsecs_t delta = now - mLastWriteTime;
if (!mStandby && delta > maxPeriod) {
mNumDelayedWrites++;
if ((now - lastWarning) > kWarningThrottleNs) {
ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
ns2ms(delta), mNumDelayedWrites, this);
lastWarning = now;
}
if (mStandby) {
longStandbyExit = true;
}
}
// end of write blocked detection
mStandby = false;
} else {
usleep(sleepTime);
}
// finally let go of removed track(s), without the lock held
// since we can't guarantee the destructors won't acquire that
// same lock.
tracksToRemove.clear();
// Effect chains will be actually deleted here if they were removed from
// mEffectChains list during mixing or effects processing
effectChains.clear();
// FIXME Note that the above .clear() is no longer necessary since effectChains
// is now local to this block, but will keep it for now (at least until merge done).
}
// put output stream into standby mode
if (!mStandby) {
mOutput->stream->common.standby(&mOutput->stream->common);
}
releaseWakeLock();
ALOGV("Thread %p type %d exiting", this, mType);
return false;
}
// prepareTracks_l() must be called with ThreadBase::mLock held
@@ -2653,7 +2780,7 @@ AudioFlinger::DirectOutputThread::~DirectOutputThread()
{
}
void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp)
void AudioFlinger::DirectOutputThread::applyVolume()
{
// Do not apply volume on compressed audio
if (!audio_is_linear_pcm(mFormat)) {
@@ -2672,7 +2799,7 @@ void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t ri
size_t frameCount = mFrameCount;
int16_t *out = mMixBuffer;
if (ramp) {
if (rampVolume) {
if (mChannelCount == 1) {
int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16;
int32_t vlInc = d / (int32_t)frameCount;
@@ -2727,103 +2854,19 @@ void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t ri
mRightVolShort = rightVol;
}
bool AudioFlinger::DirectOutputThread::threadLoop()
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::threadLoop_prepareTracks_l(
sp<Track>& trackToRemove
)
{
// MixerThread has Vector instead of single trackToRemove
sp<Track> trackToRemove;
nsecs_t standbyTime = systemTime();
size_t mixBufferSize = mFrameCount * mFrameSize;
// MixerThread has relaxed timing: maxPeriod, lastWarning, longStandbyExit
uint32_t activeSleepTime = activeSleepTimeUs();
uint32_t idleSleepTime = idleSleepTimeUs();
uint32_t sleepTime = idleSleepTime;
// MixerThread has sleepTimeShift and cpuStats
// use shorter standby delay as on normal output to release
// hardware resources as soon as possible
nsecs_t standbyDelay = microseconds(activeSleepTime*2);
acquireWakeLock();
while (!exitPending())
{
// MixerThread has cpuStats.sample()
bool rampVolume;
uint16_t leftVol;
uint16_t rightVol;
Vector< sp<EffectChain> > effectChains;
processConfigEvents();
// MixerThread does not have activeTrack here
sp<Track> activeTrack;
mixer_state mixerStatus = MIXER_IDLE;
{ // scope for the mLock
Mutex::Autolock _l(mLock);
if (checkForNewParameters_l()) {
mixBufferSize = mFrameCount * mFrameSize;
// different calculations here
standbyDelay = microseconds(activeSleepTime*2);
activeSleepTime = activeSleepTimeUs();
idleSleepTime = idleSleepTimeUs();
standbyDelay = microseconds(activeSleepTime*2);
}
// put audio hardware into standby after short delay
if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
mSuspended > 0)) {
if (!mStandby) {
ALOGV("Audio hardware entering standby, mixer %p, suspend count %u", this, mSuspended);
mOutput->stream->common.standby(&mOutput->stream->common);
mStandby = true;
mBytesWritten = 0;
}
if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
// we're about to wait, flush the binder command buffer
IPCThreadState::self()->flushCommands();
if (exitPending()) break;
releaseWakeLock_l();
// wait until we have something to do...
ALOGV("Thread %p type %d TID %d going to sleep", this, mType, gettid());
mWaitWorkCV.wait(mLock);
ALOGV("Thread %p type %d TID %d waking up", this, mType, gettid());
acquireWakeLock_l();
// MixerThread has "mPrevMixerStatus = MIXER_IDLE"
checkSilentMode_l();
// MixerThread has different standbyDelay
standbyTime = systemTime() + standbyDelay;
sleepTime = idleSleepTime;
// MixerThread has "sleepTimeShift = 0"
continue;
}
}
// MixerThread has "mixerStatus = prepareTracks_l(...)"
// equivalent to MixerThread's lockEffectChains_l, but without the lock
// FIXME - is it OK to omit the lock here?
effectChains = mEffectChains;
// FIXME Temporarily renamed to avoid confusion with the member "mixerStatus"
mixer_state mixerStatus_ = MIXER_IDLE;
// find out which tracks need to be processed
if (mActiveTracks.size() != 0) {
sp<Track> t = mActiveTracks[0].promote();
if (t == 0) continue;
// see FIXME in AudioFlinger.h, return MIXER_IDLE might also work
if (t == 0) return MIXER_CONTINUE;
//if (t == 0) continue;
Track* const track = t.get();
audio_track_cblk_t* cblk = track->cblk();
@@ -2886,9 +2929,9 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
// Delegate volume control to effect in track effect chain if needed
// only one effect chain can be present on DirectOutputThread, so if
// there is one, the track is connected to it
if (!effectChains.isEmpty()) {
if (!mEffectChains.isEmpty()) {
// Do not ramp volume if volume is controlled by effect
if(effectChains[0]->setVolume_l(&vl, &vr)) {
if (mEffectChains[0]->setVolume_l(&vl, &vr)) {
rampVolume = false;
}
}
@@ -2909,7 +2952,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
// reset retry count
track->mRetryCount = kMaxTrackRetriesDirect;
activeTrack = t;
mixerStatus = MIXER_TRACKS_READY;
mixerStatus_ = MIXER_TRACKS_READY;
} else {
//ALOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
if (track->isStopped()) {
@@ -2926,7 +2969,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
trackToRemove = track;
} else {
mixerStatus = MIXER_TRACKS_ENABLED;
mixerStatus_ = MIXER_TRACKS_ENABLED;
}
}
}
@@ -2935,21 +2978,21 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
// remove all the tracks that need to be...
if (CC_UNLIKELY(trackToRemove != 0)) {
mActiveTracks.remove(trackToRemove);
if (!effectChains.isEmpty()) {
if (!mEffectChains.isEmpty()) {
ALOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(),
trackToRemove->sessionId());
effectChains[0]->decActiveTrackCnt();
mEffectChains[0]->decActiveTrackCnt();
}
if (trackToRemove->isTerminated()) {
removeTrack_l(trackToRemove);
}
}
lockEffectChains_l(effectChains);
}
return mixerStatus_;
}
// For DirectOutputThread, this test is equivalent to "activeTrack != 0"
if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
void AudioFlinger::DirectOutputThread::threadLoop_mix()
{
AudioBufferProvider::Buffer buffer;
size_t frameCount = mFrameCount;
int8_t *curBuf = (int8_t *)mMixBuffer;
@@ -2968,7 +3011,10 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
}
sleepTime = 0;
standbyTime = systemTime() + standbyDelay;
} else {
}
void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
{
if (sleepTime == 0) {
if (mixerStatus == MIXER_TRACKS_ENABLED) {
sleepTime = activeSleepTime;
@@ -2979,68 +3025,6 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
memset (mMixBuffer, 0, mFrameCount * mFrameSize);
sleepTime = 0;
}
}
if (mSuspended > 0) {
sleepTime = suspendSleepTimeUs();
}
// only process effects if we're going to write
if (sleepTime == 0) {
// MixerThread does not have applyVolume
if (mixerStatus == MIXER_TRACKS_READY) {
applyVolume(leftVol, rightVol, rampVolume);
}
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();
}
}
// enable changes in effect chain
unlockEffectChains(effectChains);
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
mLastWriteTime = systemTime();
mInWrite = true;
mBytesWritten += mixBufferSize;
int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
mNumWrites++;
mInWrite = false;
// MixerThread has write blocked detection here
mStandby = false;
} else {
usleep(sleepTime);
}
// finally let go of removed track(s), without the lock held
// since we can't guarantee the destructors won't acquire that
// same lock.
trackToRemove.clear();
activeTrack.clear();
// Effect chains will be actually deleted here if they were removed from
// mEffectChains list during mixing or effects processing
effectChains.clear();
// FIXME Note that the above .clear() is no longer necessary since effectChains
// is now local to this block, but will keep it for now (at least until merge done).
}
// put output stream into standby mode
if (!mStandby) {
mOutput->stream->common.standby(&mOutput->stream->common);
}
releaseWakeLock();
ALOGV("Thread %p type %d exiting", this, mType);
return false;
}
// getTrackName_l() must be called with ThreadBase::mLock held
@@ -3153,96 +3137,8 @@ AudioFlinger::DuplicatingThread::~DuplicatingThread()
}
}
bool AudioFlinger::DuplicatingThread::threadLoop()
void AudioFlinger::DuplicatingThread::threadLoop_mix()
{
Vector< sp<Track> > tracksToRemove;
nsecs_t standbyTime = systemTime();
size_t mixBufferSize = mFrameCount * mFrameSize;
// Only in DuplicatingThread
SortedVector< sp<OutputTrack> > outputTracks;
uint32_t writeFrames = 0;
uint32_t activeSleepTime = activeSleepTimeUs();
uint32_t idleSleepTime = idleSleepTimeUs();
uint32_t sleepTime = idleSleepTime;
acquireWakeLock();
while (!exitPending())
{
// MixerThread has cpuStats.sample
Vector< sp<EffectChain> > effectChains;
processConfigEvents();
mixer_state mixerStatus = MIXER_IDLE;
{ // scope for the mLock
Mutex::Autolock _l(mLock);
if (checkForNewParameters_l()) {
mixBufferSize = mFrameCount * mFrameSize;
// Only in DuplicatingThread
updateWaitTime();
activeSleepTime = activeSleepTimeUs();
idleSleepTime = idleSleepTimeUs();
}
// Only in DuplicatingThread
for (size_t i = 0; i < mOutputTracks.size(); i++) {
outputTracks.add(mOutputTracks[i]);
}
// put audio hardware into standby after short delay
if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
mSuspended > 0)) {
if (!mStandby) {
// DuplicatingThread implements standby by stopping all tracks
for (size_t i = 0; i < outputTracks.size(); i++) {
outputTracks[i]->stop();
}
mStandby = true;
mBytesWritten = 0;
}
if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
// we're about to wait, flush the binder command buffer
IPCThreadState::self()->flushCommands();
outputTracks.clear();
if (exitPending()) break;
releaseWakeLock_l();
// wait until we have something to do...
ALOGV("Thread %p type %d TID %d going to sleep", this, mType, gettid());
mWaitWorkCV.wait(mLock);
ALOGV("Thread %p type %d TID %d waking up", this, mType, gettid());
acquireWakeLock_l();
// MixerThread has "mPrevMixerStatus = MIXER_IDLE"
checkSilentMode_l();
standbyTime = systemTime() + mStandbyTimeInNsecs;
sleepTime = idleSleepTime;
// MixerThread has sleepTimeShift
continue;
}
}
mixerStatus = prepareTracks_l(&tracksToRemove);
// prevent any changes in effect chain list and in each effect chain
// during mixing and effect process as the audio buffers could be deleted
// or modified if an effect is created or deleted
lockEffectChains_l(effectChains);
}
// Duplicating Thread is completely different here
if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
// mix buffers...
if (outputsReady(outputTracks)) {
mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
@@ -3251,7 +3147,10 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
}
sleepTime = 0;
writeFrames = mFrameCount;
} else {
}
void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
{
if (sleepTime == 0) {
if (mixerStatus == MIXER_TRACKS_ENABLED) {
sleepTime = activeSleepTime;
@@ -3269,58 +3168,23 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
}
}
}
}
}
if (mSuspended > 0) {
sleepTime = suspendSleepTimeUs();
}
// only process effects if we're going to write
if (sleepTime == 0) {
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();
}
}
// enable changes in effect chain
unlockEffectChains(effectChains);
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
void AudioFlinger::DuplicatingThread::threadLoop_write()
{
standbyTime = systemTime() + mStandbyTimeInNsecs;
for (size_t i = 0; i < outputTracks.size(); i++) {
outputTracks[i]->write(mMixBuffer, writeFrames);
}
mStandby = false;
mBytesWritten += mixBufferSize;
}
// MixerThread has write blocked detection here
} else {
usleep(sleepTime);
}
// finally let go of removed track(s), without the lock held
// since we can't guarantee the destructors won't acquire that
// same lock.
tracksToRemove.clear();
outputTracks.clear();
// Effect chains will be actually deleted here if they were removed from
// mEffectChains list during mixing or effects processing
effectChains.clear();
// FIXME Note that the above .clear() is no longer necessary since effectChains
// is now local to this block, but will keep it for now (at least until merge done).
}
// MixerThread and DirectOutpuThread have standby here,
// but for DuplicatingThread this is handled by the outputTracks
releaseWakeLock();
ALOGV("Thread %p type %d exiting", this, mType);
return false;
void AudioFlinger::DuplicatingThread::threadLoop_standby()
{
// DuplicatingThread implements standby by stopping all tracks
for (size_t i = 0; i < outputTracks.size(); i++) {
outputTracks[i]->stop();
}
}
void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)

View File

@@ -207,7 +207,9 @@ private:
// call in any IAudioFlinger method that accesses mPrimaryHardwareDev
status_t initCheck() const { return mPrimaryHardwareDev == NULL ? NO_INIT : NO_ERROR; }
// RefBase
virtual void onFirstRef();
audio_hw_device_t* findSuitableHwDev_l(uint32_t devices);
void purgeStaleEffects_l();
@@ -581,6 +583,10 @@ private:
MIXER_TRACKS_READY // at least one active track, and at least one track has data
// standby mode does not have an enum value
// suspend by audio policy manager is orthogonal to mixer state
#if 1
// FIXME remove these hacks for threadLoop_prepareTracks_l
, MIXER_CONTINUE // "continue;"
#endif
};
// playback track
@@ -793,8 +799,33 @@ private:
// Thread virtuals
virtual status_t readyToRun();
virtual bool threadLoop();
// RefBase
virtual void onFirstRef();
protected:
// Code snippets that were lifted up out of threadLoop()
virtual void threadLoop_mix() = 0;
virtual void threadLoop_sleepTime() = 0;
virtual void threadLoop_write();
virtual void threadLoop_standby();
// Non-trivial for DUPLICATING only
virtual void updateWaitTime() { }
// Non-trivial for DIRECT only
virtual void applyVolume() { }
// FIXME merge these
// Non-trivial for MIXER and DUPLICATING only
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove) { return MIXER_IDLE; }
// Non-trivial for DIRECT only
virtual mixer_state threadLoop_prepareTracks_l(sp<Track>& trackToRemove)
{ return MIXER_IDLE; }
public:
virtual status_t initCheck() const { return (mOutput == NULL) ? NO_INIT : NO_ERROR; }
virtual uint32_t latency() const;
@@ -897,6 +928,30 @@ private:
int mNumWrites;
int mNumDelayedWrites;
bool mInWrite;
// FIXME rename these former local variables of threadLoop to standard "m" names
nsecs_t standbyTime;
size_t mixBufferSize;
uint32_t activeSleepTime;
uint32_t idleSleepTime;
uint32_t sleepTime;
// mixerStatus was local to the while !exitingPending loop
mixer_state mixerStatus;
// FIXME move these declarations into the specific sub-class that needs them
// MIXER only
bool longStandbyExit;
uint32_t sleepTimeShift;
// MIXER and DUPLICATING only
mixer_state mPrevMixerStatus; // previous status returned by prepareTracks_l()
// DIRECT only
nsecs_t standbyDelay;
// activeTrack was local to the while !exitingPending loop
sp<Track> activeTrack;
// DUPLICATING only
SortedVector < sp<OutputTrack> > outputTracks;
uint32_t writeFrames;
SortedVector < sp<OutputTrack> > mOutputTracks;
};
class MixerThread : public PlaybackThread {
@@ -909,7 +964,6 @@ private:
virtual ~MixerThread();
// Thread virtuals
virtual bool threadLoop();
void invalidateTracks(audio_stream_type_t streamType);
virtual bool checkForNewParameters_l();
@@ -920,14 +974,17 @@ private:
// pending set of tracks to remove via Vector 'tracksToRemove'. The caller is
// responsible for clearing or destroying this Vector later on, when it
// is safe to do so. That will drop the final ref count and destroy the tracks.
mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
virtual int getTrackName_l();
virtual void deleteTrackName_l(int name);
virtual uint32_t idleSleepTimeUs();
virtual uint32_t suspendSleepTimeUs();
// threadLoop snippets
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
AudioMixer* mAudioMixer;
mixer_state mPrevMixerStatus; // previous status returned by prepareTracks_l()
};
class DirectOutputThread : public PlaybackThread {
@@ -938,7 +995,6 @@ private:
virtual ~DirectOutputThread();
// Thread virtuals
virtual bool threadLoop();
virtual bool checkForNewParameters_l();
@@ -949,8 +1005,11 @@ private:
virtual uint32_t idleSleepTimeUs();
virtual uint32_t suspendSleepTimeUs();
private:
void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
// threadLoop snippets
virtual mixer_state threadLoop_prepareTracks_l(sp<Track>& trackToRemove);
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
virtual void applyVolume();
// volumes last sent to audio HAL with stream->set_volume()
// FIXME use standard representation and names
@@ -958,6 +1017,12 @@ private:
float mRightVolFloat;
uint16_t mLeftVolShort;
uint16_t mRightVolShort;
// FIXME rename these former local variables of threadLoop to standard names
// next 3 were local to the while !exitingPending loop
bool rampVolume;
uint16_t leftVol;
uint16_t rightVol;
};
class DuplicatingThread : public MixerThread {
@@ -967,7 +1032,6 @@ private:
virtual ~DuplicatingThread();
// Thread virtuals
virtual bool threadLoop();
void addOutputTrack(MixerThread* thread);
void removeOutputTrack(MixerThread* thread);
uint32_t waitTimeMs() { return mWaitTimeMs; }
@@ -976,9 +1040,15 @@ private:
private:
bool outputsReady(const SortedVector<sp<OutputTrack> > &outputTracks);
void updateWaitTime();
protected:
// threadLoop snippets
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
virtual void threadLoop_write();
virtual void threadLoop_standby();
virtual void updateWaitTime();
private:
SortedVector < sp<OutputTrack> > mOutputTracks;
uint32_t mWaitTimeMs;
};
@@ -1086,8 +1156,11 @@ private:
uint32_t device);
virtual ~RecordThread();
// Thread
virtual bool threadLoop();
virtual status_t readyToRun();
// RefBase
virtual void onFirstRef();
virtual status_t initCheck() const { return (mInput == NULL) ? NO_INIT : NO_ERROR; }