Merge "Effect for multichannel PCM downmix to stereo"
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 9f2bd3a..95b9d86 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -135,8 +135,10 @@
      * format:             Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
      *                     16 bits per sample).
      * channelMask:        Channel mask: see audio_channels_t.
-     * frameCount:         Total size of track PCM buffer in frames. This defines the
-     *                     latency of the track.
+     * frameCount:         Minimum size of track PCM buffer in frames. This defines the
+     *                     latency of the track. The actual size selected by the AudioTrack could be
+     *                     larger if the requested size is not compatible with current audio HAL
+     *                     latency.
      * flags:              Reserved for future use.
      * cbf:                Callback function. If not null, this function is called periodically
      *                     to request new PCM data.
diff --git a/include/media/stagefright/MediaSourceSplitter.h b/include/media/stagefright/MediaSourceSplitter.h
deleted file mode 100644
index 568f4c2..0000000
--- a/include/media/stagefright/MediaSourceSplitter.h
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// This class provides a way to split a single media source into multiple sources.
-// The constructor takes in the real mediaSource and createClient() can then be
-// used to create multiple sources served from this real mediaSource.
-//
-// Usage:
-// - Create MediaSourceSplitter by passing in a real mediaSource from which
-// multiple duplicate channels are needed.
-// - Create a client using createClient() and use it as any other mediaSource.
-//
-// Note that multiple clients can be created using createClient() and
-// started/stopped in any order. MediaSourceSplitter stops the real source only
-// when all clients have been stopped.
-//
-// If a new client is created/started after some existing clients have already
-// started, the new client will start getting its read frames from the current
-// time.
-
-#ifndef MEDIA_SOURCE_SPLITTER_H_
-
-#define MEDIA_SOURCE_SPLITTER_H_
-
-#include <media/stagefright/MediaSource.h>
-#include <utils/threads.h>
-#include <utils/Vector.h>
-#include <utils/RefBase.h>
-
-namespace android {
-
-class MediaBuffer;
-class MetaData;
-
-class MediaSourceSplitter : public RefBase {
-public:
-    // Constructor
-    // mediaSource: The real mediaSource. The class keeps a reference to it to
-    // implement the various clients.
-    MediaSourceSplitter(sp<MediaSource> mediaSource);
-
-    ~MediaSourceSplitter();
-
-    // Creates a new client of base type MediaSource. Multiple clients can be
-    // created which get their data through the same real mediaSource. These
-    // clients can then be used like any other MediaSource, all of which provide
-    // data from the same real source.
-    sp<MediaSource> createClient();
-
-private:
-    // Total number of clients created through createClient().
-    int32_t mNumberOfClients;
-
-    // reference to the real MediaSource passed to the constructor.
-    sp<MediaSource> mSource;
-
-    // Stores pointer to the MediaBuffer read from the real MediaSource.
-    // All clients use this to implement the read() call.
-    MediaBuffer *mLastReadMediaBuffer;
-
-    // Status code for read from the real MediaSource. All clients return
-    // this for their read().
-    status_t mLastReadStatus;
-
-    // Boolean telling whether the real MediaSource has started.
-    bool mSourceStarted;
-
-    // List of booleans, one for each client, storing whether the corresponding
-    // client's start() has been called.
-    Vector<bool> mClientsStarted;
-
-    // Stores the number of clients which are currently started.
-    int32_t mNumberOfClientsStarted;
-
-    // Since different clients call read() asynchronously, we need to keep track
-    // of what data is currently read into the mLastReadMediaBuffer.
-    // mCurrentReadBit stores the bit for the current read buffer. This bit
-    // flips each time a new buffer is read from the source.
-    // mClientsDesiredReadBit stores the bit for the next desired read buffer
-    // for each client. This bit flips each time read() is completed for this
-    // client.
-    bool mCurrentReadBit;
-    Vector<bool> mClientsDesiredReadBit;
-
-    // Number of clients whose current read has been completed.
-    int32_t mNumberOfCurrentReads;
-
-    // Boolean telling whether the last read has been completed for all clients.
-    // The variable is reset to false each time buffer is read from the real
-    // source.
-    bool mLastReadCompleted;
-
-    // A global mutex for access to critical sections.
-    Mutex mLock;
-
-    // Condition variable for waiting on read from source to complete.
-    Condition mReadFromSourceCondition;
-
-    // Condition variable for waiting on all client's last read to complete.
-    Condition mAllReadsCompleteCondition;
-
-    // Functions used by Client to implement the MediaSource interface.
-
-    // If the real source has not been started yet by any client, starts it.
-    status_t start(int clientId, MetaData *params);
-
-    // Stops the real source after all clients have called stop().
-    status_t stop(int clientId);
-
-    // returns the real source's getFormat().
-    sp<MetaData> getFormat(int clientId);
-
-    // If the client's desired buffer has already been read into
-    // mLastReadMediaBuffer, points the buffer to that. Otherwise if it is the
-    // master client, reads the buffer from source or else waits for the master
-    // client to read the buffer and uses that.
-    status_t read(int clientId,
-            MediaBuffer **buffer, const MediaSource::ReadOptions *options = NULL);
-
-    // Not implemented right now.
-    status_t pause(int clientId);
-
-    // Function which reads a buffer from the real source into
-    // mLastReadMediaBuffer
-    void readFromSource_lock(const MediaSource::ReadOptions *options);
-
-    // Waits until read from the real source has been completed.
-    // _lock means that the function should be called when the thread has already
-    // obtained the lock for the mutex mLock.
-    void waitForReadFromSource_lock(int32_t clientId);
-
-    // Waits until all clients have read the current buffer in
-    // mLastReadCompleted.
-    void waitForAllClientsLastRead_lock(int32_t clientId);
-
-    // Each client calls this after it completes its read(). Once all clients
-    // have called this for the current buffer, the function calls
-    // mAllReadsCompleteCondition.broadcast() to signal the waiting clients.
-    void signalReadComplete_lock(bool readAborted);
-
-    // Make these constructors private.
-    MediaSourceSplitter();
-    MediaSourceSplitter(const MediaSourceSplitter &);
-    MediaSourceSplitter &operator=(const MediaSourceSplitter &);
-
-    // This class implements the MediaSource interface. Each client stores a
-    // reference to the parent MediaSourceSplitter and uses it to complete the
-    // various calls.
-    class Client : public MediaSource {
-    public:
-        // Constructor stores reference to the parent MediaSourceSplitter and it
-        // client id.
-        Client(sp<MediaSourceSplitter> splitter, int32_t clientId);
-
-        // MediaSource interface
-        virtual status_t start(MetaData *params = NULL);
-
-        virtual status_t stop();
-
-        virtual sp<MetaData> getFormat();
-
-        virtual status_t read(
-                MediaBuffer **buffer, const ReadOptions *options = NULL);
-
-        virtual status_t pause();
-
-    private:
-        // Refernce to the parent MediaSourceSplitter
-        sp<MediaSourceSplitter> mSplitter;
-
-        // Id of this client.
-        int32_t mClientId;
-    };
-
-    friend class Client;
-};
-
-}  // namespace android
-
-#endif  // MEDIA_SOURCE_SPLITTER_H_
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 4890f05..a1c99e5 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -784,12 +784,9 @@
                 mNotificationFramesAct = frameCount/2;
             }
             if (frameCount < minFrameCount) {
-                if (enforceFrameCount) {
-                    ALOGE("Invalid buffer size: minFrameCount %d, frameCount %d", minFrameCount, frameCount);
-                    return BAD_VALUE;
-                } else {
-                    frameCount = minFrameCount;
-                }
+                ALOGW_IF(enforceFrameCount, "Minimum buffer size corrected from %d to %d",
+                         frameCount, minFrameCount);
+                frameCount = minFrameCount;
             }
         } else {
             // Ensure that buffer alignment matches channelCount
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index bd3e07a..1a85c9c 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -325,7 +325,7 @@
             mStreamType, mLeftVolume, mRightVolume);
     result.append(buffer);
     snprintf(buffer, 255, "  msec per frame(%f), latency (%d)\n",
-            mMsecsPerFrame, mLatency);
+            mMsecsPerFrame, (mTrack != 0) ? mTrack->latency() : -1);
     result.append(buffer);
     snprintf(buffer, 255, "  aux effect id(%d), send level (%f)\n",
             mAuxEffectId, mSendLevel);
@@ -1384,7 +1384,6 @@
     mRightVolume = 1.0;
     mPlaybackRatePermille = 1000;
     mSampleRateHz = 0;
-    mLatency = 0;
     mMsecsPerFrame = 0;
     mAuxEffectId = 0;
     mSendLevel = 0.0;
@@ -1443,7 +1442,8 @@
 
 uint32_t MediaPlayerService::AudioOutput::latency () const
 {
-    return mLatency;
+    if (mTrack == 0) return 0;
+    return mTrack->latency();
 }
 
 float MediaPlayerService::AudioOutput::msecsPerFrame() const
@@ -1533,7 +1533,6 @@
 
     mSampleRateHz = sampleRate;
     mMsecsPerFrame = mPlaybackRatePermille / (float) sampleRate;
-    mLatency = t->latency();
     mTrack = t;
 
     status_t res = t->setSampleRate(mPlaybackRatePermille * mSampleRateHz / 1000);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 681ecab..85cec22 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -118,7 +118,6 @@
         int32_t                 mPlaybackRatePermille;
         uint32_t                mSampleRateHz; // sample rate of the content, as set in open()
         float                   mMsecsPerFrame;
-        uint32_t                mLatency;
         int                     mSessionId;
         float                   mSendLevel;
         int                     mAuxEffectId;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 21d6866..5aea8d0 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -34,7 +34,6 @@
         MediaDefs.cpp                     \
         MediaExtractor.cpp                \
         MediaSource.cpp                   \
-        MediaSourceSplitter.cpp           \
         MetaData.cpp                      \
         NuCachedSource2.cpp               \
         NuMediaExtractor.cpp              \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 9427ef7..650b6c4 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -427,6 +427,12 @@
                 break;
             }
 
+            if (mAudioSink != NULL) {
+                mLatencyUs = (int64_t)mAudioSink->latency() * 1000;
+            } else {
+                mLatencyUs = (int64_t)mAudioTrack->latency() * 1000;
+            }
+
             CHECK(mInputBuffer->meta_data()->findInt64(
                         kKeyTime, &mPositionTimeMediaUs));
 
diff --git a/media/libstagefright/MediaSourceSplitter.cpp b/media/libstagefright/MediaSourceSplitter.cpp
deleted file mode 100644
index 3b64ded..0000000
--- a/media/libstagefright/MediaSourceSplitter.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaSourceSplitter"
-#include <utils/Log.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/MediaSourceSplitter.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MetaData.h>
-
-namespace android {
-
-MediaSourceSplitter::MediaSourceSplitter(sp<MediaSource> mediaSource) {
-    mNumberOfClients = 0;
-    mSource = mediaSource;
-    mSourceStarted = false;
-
-    mNumberOfClientsStarted = 0;
-    mNumberOfCurrentReads = 0;
-    mCurrentReadBit = 0;
-    mLastReadCompleted = true;
-}
-
-MediaSourceSplitter::~MediaSourceSplitter() {
-}
-
-sp<MediaSource> MediaSourceSplitter::createClient() {
-    Mutex::Autolock autoLock(mLock);
-
-    sp<MediaSource> client = new Client(this, mNumberOfClients++);
-    mClientsStarted.push(false);
-    mClientsDesiredReadBit.push(0);
-    return client;
-}
-
-status_t MediaSourceSplitter::start(int clientId, MetaData *params) {
-    Mutex::Autolock autoLock(mLock);
-
-    ALOGV("start client (%d)", clientId);
-    if (mClientsStarted[clientId]) {
-        return OK;
-    }
-
-    mNumberOfClientsStarted++;
-
-    if (!mSourceStarted) {
-        ALOGV("Starting real source from client (%d)", clientId);
-        status_t err = mSource->start(params);
-
-        if (err == OK) {
-            mSourceStarted = true;
-            mClientsStarted.editItemAt(clientId) = true;
-            mClientsDesiredReadBit.editItemAt(clientId) = !mCurrentReadBit;
-        }
-
-        return err;
-    } else {
-        mClientsStarted.editItemAt(clientId) = true;
-        if (mLastReadCompleted) {
-            // Last read was completed. So join in the threads for the next read.
-            mClientsDesiredReadBit.editItemAt(clientId) = !mCurrentReadBit;
-        } else {
-            // Last read is ongoing. So join in the threads for the current read.
-            mClientsDesiredReadBit.editItemAt(clientId) = mCurrentReadBit;
-        }
-        return OK;
-    }
-}
-
-status_t MediaSourceSplitter::stop(int clientId) {
-    Mutex::Autolock autoLock(mLock);
-
-    ALOGV("stop client (%d)", clientId);
-    CHECK(clientId >= 0 && clientId < mNumberOfClients);
-    CHECK(mClientsStarted[clientId]);
-
-    if (--mNumberOfClientsStarted == 0) {
-        ALOGV("Stopping real source from client (%d)", clientId);
-        status_t err = mSource->stop();
-        mSourceStarted = false;
-        mClientsStarted.editItemAt(clientId) = false;
-        return err;
-    } else {
-        mClientsStarted.editItemAt(clientId) = false;
-        if (!mLastReadCompleted && (mClientsDesiredReadBit[clientId] == mCurrentReadBit)) {
-            // !mLastReadCompleted implies that buffer has been read from source, but all
-            // clients haven't read it.
-            // mClientsDesiredReadBit[clientId] == mCurrentReadBit implies that this
-            // client would have wanted to read from this buffer. (i.e. it has not yet
-            // called read() for the current read buffer.)
-            // Since other threads may be waiting for all the clients' reads to complete,
-            // signal that this read has been aborted.
-            signalReadComplete_lock(true);
-        }
-        return OK;
-    }
-}
-
-sp<MetaData> MediaSourceSplitter::getFormat(int clientId) {
-    Mutex::Autolock autoLock(mLock);
-
-    ALOGV("getFormat client (%d)", clientId);
-    return mSource->getFormat();
-}
-
-status_t MediaSourceSplitter::read(int clientId,
-        MediaBuffer **buffer, const MediaSource::ReadOptions *options) {
-    Mutex::Autolock autoLock(mLock);
-
-    CHECK(clientId >= 0 && clientId < mNumberOfClients);
-
-    ALOGV("read client (%d)", clientId);
-    *buffer = NULL;
-
-    if (!mClientsStarted[clientId]) {
-        return OK;
-    }
-
-    if (mCurrentReadBit != mClientsDesiredReadBit[clientId]) {
-        // Desired buffer has not been read from source yet.
-
-        // If the current client is the special client with clientId = 0
-        // then read from source, else wait until the client 0 has finished
-        // reading from source.
-        if (clientId == 0) {
-            // Wait for all client's last read to complete first so as to not
-            // corrupt the buffer at mLastReadMediaBuffer.
-            waitForAllClientsLastRead_lock(clientId);
-
-            readFromSource_lock(options);
-            *buffer = mLastReadMediaBuffer;
-        } else {
-            waitForReadFromSource_lock(clientId);
-
-            *buffer = mLastReadMediaBuffer;
-            (*buffer)->add_ref();
-        }
-        CHECK(mCurrentReadBit == mClientsDesiredReadBit[clientId]);
-    } else {
-        // Desired buffer has already been read from source. Use the cached data.
-        CHECK(clientId != 0);
-
-        *buffer = mLastReadMediaBuffer;
-        (*buffer)->add_ref();
-    }
-
-    mClientsDesiredReadBit.editItemAt(clientId) = !mClientsDesiredReadBit[clientId];
-    signalReadComplete_lock(false);
-
-    return mLastReadStatus;
-}
-
-void MediaSourceSplitter::readFromSource_lock(const MediaSource::ReadOptions *options) {
-    mLastReadStatus = mSource->read(&mLastReadMediaBuffer , options);
-
-    mCurrentReadBit = !mCurrentReadBit;
-    mLastReadCompleted = false;
-    mReadFromSourceCondition.broadcast();
-}
-
-void MediaSourceSplitter::waitForReadFromSource_lock(int32_t clientId) {
-    mReadFromSourceCondition.wait(mLock);
-}
-
-void MediaSourceSplitter::waitForAllClientsLastRead_lock(int32_t clientId) {
-    if (mLastReadCompleted) {
-        return;
-    }
-    mAllReadsCompleteCondition.wait(mLock);
-    CHECK(mLastReadCompleted);
-}
-
-void MediaSourceSplitter::signalReadComplete_lock(bool readAborted) {
-    if (!readAborted) {
-        mNumberOfCurrentReads++;
-    }
-
-    if (mNumberOfCurrentReads == mNumberOfClientsStarted) {
-        mLastReadCompleted = true;
-        mNumberOfCurrentReads = 0;
-        mAllReadsCompleteCondition.broadcast();
-    }
-}
-
-status_t MediaSourceSplitter::pause(int clientId) {
-    return ERROR_UNSUPPORTED;
-}
-
-// Client
-
-MediaSourceSplitter::Client::Client(
-        sp<MediaSourceSplitter> splitter,
-        int32_t clientId) {
-    mSplitter = splitter;
-    mClientId = clientId;
-}
-
-status_t MediaSourceSplitter::Client::start(MetaData *params) {
-    return mSplitter->start(mClientId, params);
-}
-
-status_t MediaSourceSplitter::Client::stop() {
-    return mSplitter->stop(mClientId);
-}
-
-sp<MetaData> MediaSourceSplitter::Client::getFormat() {
-    return mSplitter->getFormat(mClientId);
-}
-
-status_t MediaSourceSplitter::Client::read(
-        MediaBuffer **buffer, const ReadOptions *options) {
-    return mSplitter->read(mClientId, buffer, options);
-}
-
-status_t MediaSourceSplitter::Client::pause() {
-    return mSplitter->pause(mClientId);
-}
-
-}  // namespace android
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index f4b5d4f..6d345bb 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -26,8 +26,6 @@
 
 #include "include/TimedEventQueue.h"
 
-#include <cutils/sched_policy.h>
-
 #include <sys/prctl.h>
 #include <sys/time.h>
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index b972548..fe068af 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -310,10 +310,10 @@
     }
 
     result.append("Global session refs:\n");
-    result.append(" session pid cnt\n");
+    result.append(" session pid count\n");
     for (size_t i = 0; i < mAudioSessionRefs.size(); i++) {
         AudioSessionRef *r = mAudioSessionRefs[i];
-        snprintf(buffer, SIZE, " %7d %3d %3d\n", r->sessionid, r->pid, r->cnt);
+        snprintf(buffer, SIZE, " %7d %3d %3d\n", r->mSessionid, r->mPid, r->mCnt);
         result.append(buffer);
     }
     write(fd, result.string(), result.size());
@@ -1036,9 +1036,9 @@
     bool removed = false;
     for (size_t i = 0; i< num; ) {
         AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
-        ALOGV(" pid %d @ %d", ref->pid, i);
-        if (ref->pid == pid) {
-            ALOGV(" removing entry for pid %d session %d", pid, ref->sessionid);
+        ALOGV(" pid %d @ %d", ref->mPid, i);
+        if (ref->mPid == pid) {
+            ALOGV(" removing entry for pid %d session %d", pid, ref->mSessionid);
             mAudioSessionRefs.removeAt(i);
             delete ref;
             removed = true;
@@ -1993,12 +1993,8 @@
 
 bool AudioFlinger::PlaybackThread::threadLoop()
 {
-    // MIXER || DUPLICATING
     Vector< sp<Track> > tracksToRemove;
 
-    // DIRECT
-    sp<Track> trackToRemove;
-
     standbyTime = systemTime();
     mixBufferSize = mFrameCount * mFrameSize;
 
@@ -2067,9 +2063,7 @@
                 maxPeriod = seconds(mFrameCount) / mSampleRate * 15;
 }
 
-if (mType == DUPLICATING) {
-                updateWaitTime();
-}
+                updateWaitTime_l();
 
                 activeSleepTime = activeSleepTimeUs();
                 idleSleepTime = idleSleepTimeUs();
@@ -2144,17 +2138,11 @@
                 }
             }
 
-// 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
@@ -2178,14 +2166,6 @@
 
         // 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();
             }
@@ -2226,17 +2206,13 @@
         // 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();
 
 // FIXME merge these
-if (mType == MIXER) {
-        tracksToRemove.clear();
-}
 if (mType == DIRECT) {
-        trackToRemove.clear();
         activeTrack.clear();
 }
 if (mType == DUPLICATING) {
-        tracksToRemove.clear();
         outputTracks.clear();
 }
 
@@ -2267,79 +2243,79 @@
 // 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;
+    // 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);
+    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;
+    // obtain the presentation timestamp of the next output buffer
+    int64_t pts;
+    status_t status = INVALID_OPERATION;
 
-            if (NULL != mOutput->stream->get_next_write_timestamp) {
-                status = mOutput->stream->get_next_write_timestamp(
-                        mOutput->stream, &pts);
-            }
+    if (NULL != mOutput->stream->get_next_write_timestamp) {
+        status = mOutput->stream->get_next_write_timestamp(
+                mOutput->stream, &pts);
+    }
 
-            if (status != NO_ERROR) {
-                pts = AudioBufferProvider::kInvalidPTS;
-            }
+    if (status != NO_ERROR) {
+        pts = AudioBufferProvider::kInvalidPTS;
+    }
 
-            // mix buffers...
-            mAudioMixer->process(pts);
-            // increase sleep time progressively when application underrun condition clears.
-            // Only increase sleep time if the mixer is ready for two consecutive times to avoid
-            // that a steady state of alternating ready/not ready conditions keeps the sleep time
-            // such that we would underrun the audio HAL.
-            if ((sleepTime == 0) && (sleepTimeShift > 0)) {
-                sleepTimeShift--;
-            }
-            sleepTime = 0;
-            standbyTime = systemTime() + mStandbyTimeInNsecs;
-            //TODO: delay standby when effects have a tail
+    // mix buffers...
+    mAudioMixer->process(pts);
+    // increase sleep time progressively when application underrun condition clears.
+    // Only increase sleep time if the mixer is ready for two consecutive times to avoid
+    // that a steady state of alternating ready/not ready conditions keeps the sleep time
+    // such that we would underrun the audio HAL.
+    if ((sleepTime == 0) && (sleepTimeShift > 0)) {
+        sleepTimeShift--;
+    }
+    sleepTime = 0;
+    standbyTime = systemTime() + mStandbyTimeInNsecs;
+    //TODO: delay standby when effects have a tail
 }
 
 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) {
-                if (mixerStatus == MIXER_TRACKS_ENABLED) {
-                    sleepTime = activeSleepTime >> sleepTimeShift;
-                    if (sleepTime < kMinThreadSleepTimeUs) {
-                        sleepTime = kMinThreadSleepTimeUs;
-                    }
-                    // reduce sleep time in case of consecutive application underruns to avoid
-                    // starving the audio HAL. As activeSleepTimeUs() is larger than a buffer
-                    // duration we would end up writing less data than needed by the audio HAL if
-                    // the condition persists.
-                    if (sleepTimeShift < kMaxThreadSleepTimeShift) {
-                        sleepTimeShift++;
-                    }
-                } else {
-                    sleepTime = idleSleepTime;
-                }
-            } else if (mBytesWritten != 0 ||
-                       (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) {
-                memset (mMixBuffer, 0, mixBufferSize);
-                sleepTime = 0;
-                ALOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
+    // If no tracks are ready, sleep once for the duration of an output
+    // buffer size, then write 0s to the output
+    if (sleepTime == 0) {
+        if (mixerStatus == MIXER_TRACKS_ENABLED) {
+            sleepTime = activeSleepTime >> sleepTimeShift;
+            if (sleepTime < kMinThreadSleepTimeUs) {
+                sleepTime = kMinThreadSleepTimeUs;
             }
-            // TODO add standby time extension fct of effect tail
+            // reduce sleep time in case of consecutive application underruns to avoid
+            // starving the audio HAL. As activeSleepTimeUs() is larger than a buffer
+            // duration we would end up writing less data than needed by the audio HAL if
+            // the condition persists.
+            if (sleepTimeShift < kMaxThreadSleepTimeShift) {
+                sleepTimeShift++;
+            }
+        } else {
+            sleepTime = idleSleepTime;
+        }
+    } else if (mBytesWritten != 0 ||
+               (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) {
+        memset (mMixBuffer, 0, mixBufferSize);
+        sleepTime = 0;
+        ALOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
+    }
+    // TODO add standby time extension fct of effect tail
 }
 
 // prepareTracks_l() must be called with ThreadBase::mLock held
@@ -2854,177 +2830,182 @@
     mRightVolShort = rightVol;
 }
 
-AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::threadLoop_prepareTracks_l(
-    sp<Track>& trackToRemove
+AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prepareTracks_l(
+    Vector< sp<Track> > *tracksToRemove
 )
 {
-// FIXME Temporarily renamed to avoid confusion with the member "mixerStatus"
-mixer_state mixerStatus_ = MIXER_IDLE;
+    sp<Track> trackToRemove;
 
-            // find out which tracks need to be processed
-            if (mActiveTracks.size() != 0) {
-                sp<Track> t = mActiveTracks[0].promote();
-                // see FIXME in AudioFlinger.h, return MIXER_IDLE might also work
-                if (t == 0) return MIXER_CONTINUE;
-                //if (t == 0) continue;
+    // FIXME Temporarily renamed to avoid confusion with the member "mixerStatus"
+    mixer_state mixerStatus_ = MIXER_IDLE;
 
-                Track* const track = t.get();
-                audio_track_cblk_t* cblk = track->cblk();
+    // find out which tracks need to be processed
+    if (mActiveTracks.size() != 0) {
+        sp<Track> t = mActiveTracks[0].promote();
+        // see FIXME in AudioFlinger.h, return MIXER_IDLE might also work
+        if (t == 0) return MIXER_CONTINUE;
+        //if (t == 0) continue;
 
-                // The first time a track is added we wait
-                // for all its buffers to be filled before processing it
-                if (cblk->framesReady() && track->isReady() &&
-                        !track->isPaused() && !track->isTerminated())
-                {
-                    //ALOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
+        Track* const track = t.get();
+        audio_track_cblk_t* cblk = track->cblk();
 
-                    if (track->mFillingUpStatus == Track::FS_FILLED) {
-                        track->mFillingUpStatus = Track::FS_ACTIVE;
-                        mLeftVolFloat = mRightVolFloat = 0;
-                        mLeftVolShort = mRightVolShort = 0;
-                        if (track->mState == TrackBase::RESUMING) {
-                            track->mState = TrackBase::ACTIVE;
-                            rampVolume = true;
-                        }
-                    } else if (cblk->server != 0) {
-                        // If the track is stopped before the first frame was mixed,
-                        // do not apply ramp
-                        rampVolume = true;
-                    }
-                    // compute volume for this track
-                    float left, right;
-                    if (track->isMuted() || mMasterMute || track->isPausing() ||
-                        mStreamTypes[track->streamType()].mute) {
-                        left = right = 0;
-                        if (track->isPausing()) {
-                            track->setPaused();
-                        }
-                    } else {
-                        float typeVolume = mStreamTypes[track->streamType()].volume;
-                        float v = mMasterVolume * typeVolume;
-                        uint32_t vlr = cblk->getVolumeLR();
-                        float v_clamped = v * (vlr & 0xFFFF);
-                        if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
-                        left = v_clamped/MAX_GAIN;
-                        v_clamped = v * (vlr >> 16);
-                        if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
-                        right = v_clamped/MAX_GAIN;
-                    }
+        // The first time a track is added we wait
+        // for all its buffers to be filled before processing it
+        if (cblk->framesReady() && track->isReady() &&
+                !track->isPaused() && !track->isTerminated())
+        {
+            //ALOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
 
-                    if (left != mLeftVolFloat || right != mRightVolFloat) {
-                        mLeftVolFloat = left;
-                        mRightVolFloat = right;
+            if (track->mFillingUpStatus == Track::FS_FILLED) {
+                track->mFillingUpStatus = Track::FS_ACTIVE;
+                mLeftVolFloat = mRightVolFloat = 0;
+                mLeftVolShort = mRightVolShort = 0;
+                if (track->mState == TrackBase::RESUMING) {
+                    track->mState = TrackBase::ACTIVE;
+                    rampVolume = true;
+                }
+            } else if (cblk->server != 0) {
+                // If the track is stopped before the first frame was mixed,
+                // do not apply ramp
+                rampVolume = true;
+            }
+            // compute volume for this track
+            float left, right;
+            if (track->isMuted() || mMasterMute || track->isPausing() ||
+                mStreamTypes[track->streamType()].mute) {
+                left = right = 0;
+                if (track->isPausing()) {
+                    track->setPaused();
+                }
+            } else {
+                float typeVolume = mStreamTypes[track->streamType()].volume;
+                float v = mMasterVolume * typeVolume;
+                uint32_t vlr = cblk->getVolumeLR();
+                float v_clamped = v * (vlr & 0xFFFF);
+                if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+                left = v_clamped/MAX_GAIN;
+                v_clamped = v * (vlr >> 16);
+                if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+                right = v_clamped/MAX_GAIN;
+            }
 
-                        // If audio HAL implements volume control,
-                        // force software volume to nominal value
-                        if (mOutput->stream->set_volume(mOutput->stream, left, right) == NO_ERROR) {
-                            left = 1.0f;
-                            right = 1.0f;
-                        }
+            if (left != mLeftVolFloat || right != mRightVolFloat) {
+                mLeftVolFloat = left;
+                mRightVolFloat = right;
 
-                        // Convert volumes from float to 8.24
-                        uint32_t vl = (uint32_t)(left * (1 << 24));
-                        uint32_t vr = (uint32_t)(right * (1 << 24));
+                // If audio HAL implements volume control,
+                // force software volume to nominal value
+                if (mOutput->stream->set_volume(mOutput->stream, left, right) == NO_ERROR) {
+                    left = 1.0f;
+                    right = 1.0f;
+                }
 
-                        // 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 (!mEffectChains.isEmpty()) {
-                            // Do not ramp volume if volume is controlled by effect
-                            if (mEffectChains[0]->setVolume_l(&vl, &vr)) {
-                                rampVolume = false;
-                            }
-                        }
+                // Convert volumes from float to 8.24
+                uint32_t vl = (uint32_t)(left * (1 << 24));
+                uint32_t vr = (uint32_t)(right * (1 << 24));
 
-                        // Convert volumes from 8.24 to 4.12 format
-                        uint32_t v_clamped = (vl + (1 << 11)) >> 12;
-                        if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
-                        leftVol = (uint16_t)v_clamped;
-                        v_clamped = (vr + (1 << 11)) >> 12;
-                        if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
-                        rightVol = (uint16_t)v_clamped;
-                    } else {
-                        leftVol = mLeftVolShort;
-                        rightVol = mRightVolShort;
+                // 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 (!mEffectChains.isEmpty()) {
+                    // Do not ramp volume if volume is controlled by effect
+                    if (mEffectChains[0]->setVolume_l(&vl, &vr)) {
                         rampVolume = false;
                     }
+                }
 
-                    // reset retry count
-                    track->mRetryCount = kMaxTrackRetriesDirect;
-                    activeTrack = t;
-                    mixerStatus_ = MIXER_TRACKS_READY;
+                // Convert volumes from 8.24 to 4.12 format
+                uint32_t v_clamped = (vl + (1 << 11)) >> 12;
+                if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+                leftVol = (uint16_t)v_clamped;
+                v_clamped = (vr + (1 << 11)) >> 12;
+                if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+                rightVol = (uint16_t)v_clamped;
+            } else {
+                leftVol = mLeftVolShort;
+                rightVol = mRightVolShort;
+                rampVolume = false;
+            }
+
+            // reset retry count
+            track->mRetryCount = kMaxTrackRetriesDirect;
+            activeTrack = t;
+            mixerStatus_ = MIXER_TRACKS_READY;
+        } else {
+            //ALOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
+            if (track->isStopped()) {
+                track->reset();
+            }
+            if (track->isTerminated() || track->isStopped() || track->isPaused()) {
+                // We have consumed all the buffers of this track.
+                // Remove it from the list of active tracks.
+                trackToRemove = track;
+            } else {
+                // No buffers for this track. Give it a few chances to
+                // fill a buffer, then remove it from active list.
+                if (--(track->mRetryCount) <= 0) {
+                    ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
+                    trackToRemove = track;
                 } else {
-                    //ALOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
-                    if (track->isStopped()) {
-                        track->reset();
-                    }
-                    if (track->isTerminated() || track->isStopped() || track->isPaused()) {
-                        // We have consumed all the buffers of this track.
-                        // Remove it from the list of active tracks.
-                        trackToRemove = track;
-                    } else {
-                        // No buffers for this track. Give it a few chances to
-                        // fill a buffer, then remove it from active list.
-                        if (--(track->mRetryCount) <= 0) {
-                            ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
-                            trackToRemove = track;
-                        } else {
-                            mixerStatus_ = MIXER_TRACKS_ENABLED;
-                        }
-                    }
+                    mixerStatus_ = MIXER_TRACKS_ENABLED;
                 }
             }
+        }
+    }
 
-            // remove all the tracks that need to be...
-            if (CC_UNLIKELY(trackToRemove != 0)) {
-                mActiveTracks.remove(trackToRemove);
-                if (!mEffectChains.isEmpty()) {
-                    ALOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(),
-                            trackToRemove->sessionId());
-                    mEffectChains[0]->decActiveTrackCnt();
-                }
-                if (trackToRemove->isTerminated()) {
-                    removeTrack_l(trackToRemove);
-                }
-            }
+    // FIXME merge this with similar code for removing multiple tracks
+    // remove all the tracks that need to be...
+    if (CC_UNLIKELY(trackToRemove != 0)) {
+        tracksToRemove->add(trackToRemove);
+        mActiveTracks.remove(trackToRemove);
+        if (!mEffectChains.isEmpty()) {
+            ALOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(),
+                    trackToRemove->sessionId());
+            mEffectChains[0]->decActiveTrackCnt();
+        }
+        if (trackToRemove->isTerminated()) {
+            removeTrack_l(trackToRemove);
+        }
+    }
 
-return mixerStatus_;
+    return mixerStatus_;
 }
 
 void AudioFlinger::DirectOutputThread::threadLoop_mix()
 {
-            AudioBufferProvider::Buffer buffer;
-            size_t frameCount = mFrameCount;
-            int8_t *curBuf = (int8_t *)mMixBuffer;
-            // output audio to hardware
-            while (frameCount) {
-                buffer.frameCount = frameCount;
-                activeTrack->getNextBuffer(&buffer);
-                if (CC_UNLIKELY(buffer.raw == NULL)) {
-                    memset(curBuf, 0, frameCount * mFrameSize);
-                    break;
-                }
-                memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
-                frameCount -= buffer.frameCount;
-                curBuf += buffer.frameCount * mFrameSize;
-                activeTrack->releaseBuffer(&buffer);
-            }
-            sleepTime = 0;
-            standbyTime = systemTime() + standbyDelay;
+    AudioBufferProvider::Buffer buffer;
+    size_t frameCount = mFrameCount;
+    int8_t *curBuf = (int8_t *)mMixBuffer;
+    // output audio to hardware
+    while (frameCount) {
+        buffer.frameCount = frameCount;
+        activeTrack->getNextBuffer(&buffer);
+        if (CC_UNLIKELY(buffer.raw == NULL)) {
+            memset(curBuf, 0, frameCount * mFrameSize);
+            break;
+        }
+        memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
+        frameCount -= buffer.frameCount;
+        curBuf += buffer.frameCount * mFrameSize;
+        activeTrack->releaseBuffer(&buffer);
+    }
+    sleepTime = 0;
+    standbyTime = systemTime() + standbyDelay;
+    applyVolume();
 }
 
 void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
 {
-            if (sleepTime == 0) {
-                if (mixerStatus == MIXER_TRACKS_ENABLED) {
-                    sleepTime = activeSleepTime;
-                } else {
-                    sleepTime = idleSleepTime;
-                }
-            } else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) {
-                memset (mMixBuffer, 0, mFrameCount * mFrameSize);
-                sleepTime = 0;
-            }
+    if (sleepTime == 0) {
+        if (mixerStatus == MIXER_TRACKS_ENABLED) {
+            sleepTime = activeSleepTime;
+        } else {
+            sleepTime = idleSleepTime;
+        }
+    } else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) {
+        memset (mMixBuffer, 0, mFrameCount * mFrameSize);
+        sleepTime = 0;
+    }
 }
 
 // getTrackName_l() must be called with ThreadBase::mLock held
@@ -3139,52 +3120,52 @@
 
 void AudioFlinger::DuplicatingThread::threadLoop_mix()
 {
-            // mix buffers...
-            if (outputsReady(outputTracks)) {
-                mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
-            } else {
-                memset(mMixBuffer, 0, mixBufferSize);
-            }
-            sleepTime = 0;
-            writeFrames = mFrameCount;
+    // mix buffers...
+    if (outputsReady(outputTracks)) {
+        mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
+    } else {
+        memset(mMixBuffer, 0, mixBufferSize);
+    }
+    sleepTime = 0;
+    writeFrames = mFrameCount;
 }
 
 void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
 {
-            if (sleepTime == 0) {
-                if (mixerStatus == MIXER_TRACKS_ENABLED) {
-                    sleepTime = activeSleepTime;
-                } else {
-                    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 (sleepTime == 0) {
+        if (mixerStatus == MIXER_TRACKS_ENABLED) {
+            sleepTime = activeSleepTime;
+        } else {
+            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;
             }
+        }
+    }
 }
 
 void AudioFlinger::DuplicatingThread::threadLoop_write()
 {
-            standbyTime = systemTime() + mStandbyTimeInNsecs;
-            for (size_t i = 0; i < outputTracks.size(); i++) {
-                outputTracks[i]->write(mMixBuffer, writeFrames);
-            }
-            mBytesWritten += mixBufferSize;
+    standbyTime = systemTime() + mStandbyTimeInNsecs;
+    for (size_t i = 0; i < outputTracks.size(); i++) {
+        outputTracks[i]->write(mMixBuffer, writeFrames);
+    }
+    mBytesWritten += mixBufferSize;
 }
 
 void AudioFlinger::DuplicatingThread::threadLoop_standby()
 {
-                    // DuplicatingThread implements standby by stopping all tracks
-                    for (size_t i = 0; i < outputTracks.size(); i++) {
-                        outputTracks[i]->stop();
-                    }
+    // 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)
@@ -3202,7 +3183,7 @@
         thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
         mOutputTracks.add(outputTrack);
         ALOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread);
-        updateWaitTime();
+        updateWaitTime_l();
     }
 }
 
@@ -3213,14 +3194,15 @@
         if (mOutputTracks[i]->thread() == thread) {
             mOutputTracks[i]->destroy();
             mOutputTracks.removeAt(i);
-            updateWaitTime();
+            updateWaitTime_l();
             return;
         }
     }
     ALOGV("removeOutputTrack(): unkonwn thread: %p", thread);
 }
 
-void AudioFlinger::DuplicatingThread::updateWaitTime()
+// caller must hold mLock
+void AudioFlinger::DuplicatingThread::updateWaitTime_l()
 {
     mWaitTimeMs = UINT_MAX;
     for (size_t i = 0; i < mOutputTracks.size(); i++) {
@@ -5700,9 +5682,9 @@
     size_t num = mAudioSessionRefs.size();
     for (size_t i = 0; i< num; i++) {
         AudioSessionRef *ref = mAudioSessionRefs.editItemAt(i);
-        if (ref->sessionid == audioSession && ref->pid == caller) {
-            ref->cnt++;
-            ALOGV(" incremented refcount to %d", ref->cnt);
+        if (ref->mSessionid == audioSession && ref->mPid == caller) {
+            ref->mCnt++;
+            ALOGV(" incremented refcount to %d", ref->mCnt);
             return;
         }
     }
@@ -5718,10 +5700,10 @@
     size_t num = mAudioSessionRefs.size();
     for (size_t i = 0; i< num; i++) {
         AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
-        if (ref->sessionid == audioSession && ref->pid == caller) {
-            ref->cnt--;
-            ALOGV(" decremented refcount to %d", ref->cnt);
-            if (ref->cnt == 0) {
+        if (ref->mSessionid == audioSession && ref->mPid == caller) {
+            ref->mCnt--;
+            ALOGV(" decremented refcount to %d", ref->mCnt);
+            if (ref->mCnt == 0) {
                 mAudioSessionRefs.removeAt(i);
                 delete ref;
                 purgeStaleEffects_l();
@@ -5766,9 +5748,9 @@
         bool found = false;
         for (size_t k = 0; k < numsessionrefs; k++) {
             AudioSessionRef *ref = mAudioSessionRefs.itemAt(k);
-            if (ref->sessionid == sessionid) {
+            if (ref->mSessionid == sessionid) {
                 ALOGV(" session %d still exists for %d with %d refs",
-                     sessionid, ref->pid, ref->cnt);
+                     sessionid, ref->mPid, ref->mCnt);
                 found = true;
                 break;
             }
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c7ac0a8..8ca4f89 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -534,7 +534,10 @@
         friend class RecordTrack;
 
                     const type_t            mType;
+
+                    // Used by parameters, config events, addTrack_l, exit
                     Condition               mWaitWorkCV;
+
                     const sp<AudioFlinger>  mAudioFlinger;
                     uint32_t                mSampleRate;
                     size_t                  mFrameCount;
@@ -542,9 +545,30 @@
                     uint16_t                mChannelCount;
                     size_t                  mFrameSize;
                     audio_format_t          mFormat;
+
+                    // Parameter sequence by client: binder thread calling setParameters():
+                    //  1. Lock mLock
+                    //  2. Append to mNewParameters
+                    //  3. mWaitWorkCV.signal
+                    //  4. mParamCond.waitRelative with timeout
+                    //  5. read mParamStatus
+                    //  6. mWaitWorkCV.signal
+                    //  7. Unlock
+                    //
+                    // Parameter sequence by server: threadLoop calling checkForNewParameters_l():
+                    // 1. Lock mLock
+                    // 2. If there is an entry in mNewParameters proceed ...
+                    // 2. Read first entry in mNewParameters
+                    // 3. Process
+                    // 4. Remove first entry from mNewParameters
+                    // 5. Set mParamStatus
+                    // 6. mParamCond.signal
+                    // 7. mWaitWorkCV.wait with timeout (this is to avoid overwriting mParamStatus)
+                    // 8. Unlock
                     Condition               mParamCond;
                     Vector<String8>         mNewParameters;
                     status_t                mParamStatus;
+
                     Vector<ConfigEvent>     mConfigEvents;
                     bool                    mStandby;
                     const audio_io_handle_t mId;
@@ -584,7 +608,7 @@
             // 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
+            // FIXME remove this hack for prepareTracks_l()
             , MIXER_CONTINUE        // "continue;"
 #endif
         };
@@ -812,17 +836,16 @@
         virtual     void        threadLoop_standby();
 
         // Non-trivial for DUPLICATING only
-        virtual     void        updateWaitTime() { }
+        virtual     void        updateWaitTime_l() { }
 
         // 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; }
+                    // prepareTracks_l reads and writes mActiveTracks, and also returns the
+                    // 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.
+        virtual     mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove) = 0;
 
 public:
 
@@ -970,10 +993,6 @@
         virtual     status_t    dumpInternals(int fd, const Vector<String16>& args);
 
     protected:
-                    // prepareTracks_l reads and writes mActiveTracks, and also returns the
-                    // 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.
         virtual     mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
         virtual     int         getTrackName_l();
         virtual     void        deleteTrackName_l(int name);
@@ -1006,10 +1025,9 @@
         virtual     uint32_t    suspendSleepTimeUs();
 
         // threadLoop snippets
-        virtual     mixer_state threadLoop_prepareTracks_l(sp<Track>& trackToRemove);
+        virtual     mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
         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
@@ -1023,6 +1041,9 @@
         bool rampVolume;
         uint16_t leftVol;
         uint16_t rightVol;
+
+private:
+                    void        applyVolume();  // FIXME inline into threadLoop_mix()
     };
 
     class DuplicatingThread : public MixerThread {
@@ -1046,7 +1067,9 @@
         virtual     void        threadLoop_sleepTime();
         virtual     void        threadLoop_write();
         virtual     void        threadLoop_standby();
-        virtual     void        updateWaitTime();
+
+        // called from threadLoop, addOutputTrack, removeOutputTrack
+        virtual     void        updateWaitTime_l();
     private:
 
                     uint32_t    mWaitTimeMs;
@@ -1570,12 +1593,11 @@
 
     // for mAudioSessionRefs only
     struct AudioSessionRef {
-        // FIXME rename parameter names when fields get "m" prefix
-        AudioSessionRef(int sessionid_, pid_t pid_) :
-            sessionid(sessionid_), pid(pid_), cnt(1) {}
-        const int sessionid;
-        const pid_t pid;
-        int cnt;
+        AudioSessionRef(int sessionid, pid_t pid) :
+            mSessionid(sessionid), mPid(pid), mCnt(1) {}
+        const int   mSessionid;
+        const pid_t mPid;
+        int         mCnt;
     };
 
     friend class RecordThread;