Merge "threadLoop merge"
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 8f7b35c..b972548 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1919,10 +1919,10 @@
 
 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 @@
     }
 }
 
-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 @@
             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 @@
                     // 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 @@
                     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 @@
             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 @@
             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 @@
                 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 @@
 {
 }
 
-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 @@
 
     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 @@
     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 @@
                         // 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 @@
                     // 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 @@
                             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 @@
             // 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 @@
             }
             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 @@
                 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 @@
     }
 }
 
-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 @@
             }
             sleepTime = 0;
             writeFrames = mFrameCount;
-        } else {
+}
+
+void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
+{
             if (sleepTime == 0) {
                 if (mixerStatus == MIXER_TRACKS_ENABLED) {
                     sleepTime = activeSleepTime;
@@ -3269,58 +3168,23 @@
                     }
                 }
             }
-        }
+}
 
-        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)
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index bdaf97c..c7ac0a8 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -207,7 +207,9 @@
     // 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 @@
             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 @@
 
         // 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 @@
         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 @@
         virtual             ~MixerThread();
 
         // Thread virtuals
-        virtual     bool        threadLoop();
 
                     void        invalidateTracks(audio_stream_type_t streamType);
         virtual     bool        checkForNewParameters_l();
@@ -920,14 +974,17 @@
                     // 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 @@
         virtual                 ~DirectOutputThread();
 
         // Thread virtuals
-        virtual     bool        threadLoop();
 
         virtual     bool        checkForNewParameters_l();
 
@@ -949,8 +1005,11 @@
         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 @@
         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 @@
         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:
                     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 @@
                         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; }