Issue 2667801: [Audio Effect Framework] AudioFlinger, AudioMixer AudioTrack modifications.

First drop of audio framework modifications for audio effects support.

- AudioTrack/AudioRecord:
Added support for auxiliary effects in AudioTrack
Added support for audio sessions
Fixed left right channel inversion in setVolume()

- IAudioFlinger:
Added interface methods for effect enumeraiton and instantiation
Added support for audio sessions.

- IAudioTrack:
Added method to attach auxiliary effect.

- AudioFlinger
Created new classes to control effect engines in effect library and manage effect connections to tracks or
output mix:
  EffectModule: wrapper object controlling the effect engine implementation in the effect library. There
	is one EffectModule per instance of an effect in a given audio session
  EffectChain: group of effects associated to one audio session. There is one EffectChain per audio session.
	EffectChain for session 0 is for output mix effects, other chains are attached to audio tracks
	with same session ID. Each chain contains a variable number of EffectModules
  EffectHandle: implements the IEffect interface. There is one EffectHandle object for each application
	controlling (or using) an effect module. THe EffectModule maintians a list of EffectHandles.

Added support for effect modules and effect chains creation in PlaybackThread.
modified mixer thread loop to allow track volume control by effect modules and call effect processing.

-AudioMixer
Each track now specifies its output buffer used by mixer for accumulation
Modified mixer process functions to process tracks by groups of tracks with same buffer
Modified track process functions to support accumulation to auxiliary channel

Change-Id: I26d5f7c9e070a89bdd383e1a659f8b7ca150379c
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 92bc126..d956882 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -142,7 +142,8 @@
                                     uint32_t flags      = 0,
                                     callback_t cbf = 0,
                                     void* user = 0,
-                                    int notificationFrames = 0);
+                                    int notificationFrames = 0,
+                                    int sessionId = 0);
 
 
     /* Terminates the AudioRecord and unregisters it from AudioFlinger.
@@ -168,7 +169,8 @@
                             callback_t cbf = 0,
                             void* user = 0,
                             int notificationFrames = 0,
-                            bool threadCanCallJava = false);
+                            bool threadCanCallJava = false,
+                            int sessionId = 0);
 
 
     /* Result of constructing the AudioRecord. This must be checked
@@ -270,6 +272,16 @@
      */
             audio_io_handle_t    getInput();
 
+    /* returns the audio session ID associated to this AudioRecord.
+     *
+     * Parameters:
+     *  none.
+     *
+     * Returned value:
+     *  AudioRecord session ID.
+     */
+            int    getSessionId();
+
     /* obtains a buffer of "frameCount" frames. The buffer must be
      * filled entirely. If the track is stopped, obtainBuffer() returns
      * STOPPED instead of NO_ERROR as long as there are buffers availlable,
@@ -356,6 +368,7 @@
     uint32_t                mFlags;
     uint32_t                mChannels;
     audio_io_handle_t       mInput;
+    int                     mSessionId;
 };
 
 }; // namespace android
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 9caef8f..f21e83d 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -230,6 +230,8 @@
     static status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int stream = DEFAULT);
 
     static unsigned int  getInputFramesLost(audio_io_handle_t ioHandle);
+
+    static int newAudioSessionId();
     //
     // AudioPolicyService interface
     //
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index cc4ab74..c46df1e 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -138,7 +138,8 @@
                                     uint32_t flags       = 0,
                                     callback_t cbf       = 0,
                                     void* user           = 0,
-                                    int notificationFrames = 0);
+                                    int notificationFrames = 0,
+                                    int sessionId = 0);
 
     /* Creates an audio track and registers it with AudioFlinger. With this constructor,
      * The PCM data to be rendered by AudioTrack is passed in a shared memory buffer
@@ -157,7 +158,8 @@
                                     uint32_t flags      = 0,
                                     callback_t cbf      = 0,
                                     void* user          = 0,
-                                    int notificationFrames = 0);
+                                    int notificationFrames = 0,
+                                    int sessionId = 0);
 
     /* Terminates the AudioTrack and unregisters it from AudioFlinger.
      * Also destroys all resources assotiated with the AudioTrack.
@@ -182,7 +184,8 @@
                             void* user          = 0,
                             int notificationFrames = 0,
                             const sp<IMemory>& sharedBuffer = 0,
-                            bool threadCanCallJava = false);
+                            bool threadCanCallJava = false,
+                            int sessionId = 0);
 
 
     /* Result of constructing the AudioTrack. This must be checked
@@ -239,10 +242,17 @@
 
 
     /* set volume for this track, mostly used for games' sound effects
+     * left and right volumes. Levels must be <= 1.0.
      */
-            void        setVolume(float left, float right);
+            status_t    setVolume(float left, float right);
             void        getVolume(float* left, float* right);
 
+    /* set the send level for this track. An auxiliary effect should be attached
+     * to the track with attachEffect(). Level must be <= 1.0.
+     */
+            status_t    setSendLevel(float level);
+            void        getSendLevel(float* level);
+
     /* set sample rate for this track, mostly used for games' sound effects
      */
             status_t    setSampleRate(int sampleRate);
@@ -340,6 +350,31 @@
      */
             audio_io_handle_t    getOutput();
 
+    /* returns the unique ID associated to this track.
+     *
+     * Parameters:
+     *  none.
+     *
+     * Returned value:
+     *  AudioTrack ID.
+     */
+            int    getSessionId();
+
+
+    /* Attach track auxiliary output to specified effect. Used effectId = 0
+     * to detach track from effect.
+     *
+     * Parameters:
+     *
+     * effectId:  effectId obtained from AudioEffect::id().
+     *
+     * Returned status (from utils/Errors.h) can be:
+     *  - NO_ERROR: successful operation
+     *  - INVALID_OPERATION: the effect is not an auxiliary effect.
+     *  - BAD_VALUE: The specified effect ID is invalid
+     */
+            status_t    attachAuxEffect(int effectId);
+
     /* obtains a buffer of "frameCount" frames. The buffer must be
      * filled entirely. If the track is stopped, obtainBuffer() returns
      * STOPPED instead of NO_ERROR as long as there are buffers availlable,
@@ -406,6 +441,7 @@
     sp<AudioTrackThread>    mAudioTrackThread;
 
     float                   mVolume[2];
+    float                   mSendLevel;
     uint32_t                mFrameCount;
 
     audio_track_cblk_t*     mCblk;
@@ -431,6 +467,7 @@
     uint32_t                mNewPosition;
     uint32_t                mUpdatePeriod;
     uint32_t                mFlags;
+    int                     mSessionId;
 };
 
 
diff --git a/include/media/EffectApi.h b/include/media/EffectApi.h
index a1bc3b82..97874f7 100644
--- a/include/media/EffectApi.h
+++ b/include/media/EffectApi.h
@@ -114,7 +114,8 @@
 //  +---------------------------+-----------+-----------------------------------
 //  | Volume management         | 5..6      | 0 none
 //  |                           |           | 1 implements volume control
-//  |                           |           | 2..3 reserved
+//  |                           |           | 2 requires volume indication
+//  |                           |           | 3 reserved
 //  +---------------------------+-----------+-----------------------------------
 //  | Device management         | 7..8      | 0 none
 //  |                           |           | 1 requires device updates
@@ -154,6 +155,7 @@
 // volume control
 #define EFFECT_FLAG_VOLUME_MASK         0x00000060
 #define EFFECT_FLAG_VOLUME_CTRL         0x00000020
+#define EFFECT_FLAG_VOLUME_IND          0x00000040
 #define EFFECT_FLAG_VOLUME_NONE         0x00000000
 
 // device control
@@ -296,10 +298,12 @@
 //  | Set and get volume. Used by    | EFFECT_CMD_SET_VOLUME         | size: n * sizeof(uint32_t)    | size: n * sizeof(uint32_t)
 //  | audio framework to delegate    |                               | data: volume for each channel | data: volume for each channel
 //  | volume control to effect engine|                               | defined in effect_config_t in | defined in effect_config_t in
-//  | The engine must return the     |                               | 8.24 fixed point format       | 8.24 fixed point format
-//  | volume that should be applied  |                               |                               |
-//  | before the effect is processed |                               |                               |
-//  | The overall volume (the volume |                               |                               |
+//  | If volume control flag is set  |                               | 8.24 fixed point format       | 8.24 fixed point format
+//  | in the effect descriptor, the  |                               |                               | It is legal to receive a null
+//  | effect engine must return the  |                               |                               | pointer as pReplyData in which
+//  | volume that should be applied  |                               |                               | case the effect framework has
+//  | before the effect is processed |                               |                               | delegated volume control to
+//  | The overall volume (the volume |                               |                               | another effect.
 //  | actually applied by the effect |                               |                               |
 //  | multiplied by the returned     |                               |                               |
 //  | value) should match the        |                               |                               |
@@ -370,7 +374,7 @@
 // structure that defines both input and output buffer configurations and is
 // passed by the EFFECT_CMD_CONFIGURE command.
 typedef struct buffer_config_s {
-    audio_buffer_t  buffer;     // buffer for use by process() function is not passed explicitly
+    audio_buffer_t  buffer;     // buffer for use by process() function if not passed explicitly
     uint32_t   samplingRate;    // sampling rate
     uint32_t   channels;        // channel mask (see audio_channels_e in AudioCommon.h)
     buffer_provider_t bufferProvider;   // buffer provider
@@ -457,7 +461,7 @@
 //
 //    Function:       EffectQueryNumberEffects
 //
-//    Description:    Returns the number of different effect exposed by the
+//    Description:    Returns the number of different effects exposed by the
 //          library. Each effect must have a unique effect uuid (see
 //          effect_descriptor_t). This function together with EffectQueryNext()
 //          is used to enumerate all effects present in the library.
@@ -475,7 +479,7 @@
 //        *pNumEffects:     updated with number of effects in library
 //
 ////////////////////////////////////////////////////////////////////////////////
-typedef int32_t (*effect_QueryNumberEffects_t)(int32_t *pNumEffects);
+typedef int32_t (*effect_QueryNumberEffects_t)(uint32_t *pNumEffects);
 
 ////////////////////////////////////////////////////////////////////////////////
 //
@@ -521,7 +525,7 @@
 //        returned value:    0          successful operation.
 //                          -ENODEV     library failed to initialize
 //                          -EINVAL     invalid pEffectUuid or pInterface
-//                          -ENOENT     No effect with this uuid found
+//                          -ENOENT     no effect with this uuid found
 //        *pInterface:     updated with the effect interface handle.
 //
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/include/media/EffectFactoryApi.h b/include/media/EffectFactoryApi.h
index 8179c234..6cc9932 100644
--- a/include/media/EffectFactoryApi.h
+++ b/include/media/EffectFactoryApi.h
@@ -34,7 +34,7 @@
 //
 //    Function:       EffectQueryNumberEffects
 //
-//    Description:    Returns the number of different effect in all loaded libraries.
+//    Description:    Returns the number of different effects in all loaded libraries.
 //          Each effect must have a different effect uuid (see
 //          effect_descriptor_t). This function together with EffectQueryNext()
 //          is used to enumerate all effects present in all loaded libraries.
@@ -52,7 +52,7 @@
 //        *pNumEffects:     updated with number of effects in factory
 //
 ////////////////////////////////////////////////////////////////////////////////
-int EffectQueryNumberEffects(int *pNumEffects);
+int EffectQueryNumberEffects(uint32_t *pNumEffects);
 
 ////////////////////////////////////////////////////////////////////////////////
 //
@@ -98,7 +98,7 @@
 //        returned value:    0          successful operation.
 //                          -ENODEV     factory failed to initialize
 //                          -EINVAL     invalid pEffectUuid or pInterface
-//                          -ENOENT     No effect with this uuid found
+//                          -ENOENT     no effect with this uuid found
 //        *pInterface:     updated with the effect interface.
 //
 ////////////////////////////////////////////////////////////////////////////////
@@ -140,7 +140,7 @@
 //
 //    Output:
 //        returned value:    0          successful operation.
-//                          -ENODEV     Effect factory not initialized or
+//                          -ENODEV     effect factory not initialized or
 //                                      library could not be loaded or
 //                                      library does not implement required functions
 //                          -EINVAL     invalid libPath string or handle
@@ -159,7 +159,7 @@
 //
 //    Output:
 //        returned value:    0          successful operation.
-//                          -ENODEV     Effect factory not initialized
+//                          -ENODEV     effect factory not initialized
 //                          -ENOENT     invalid handle
 //
 ////////////////////////////////////////////////////////////////////////////////
@@ -184,7 +184,7 @@
 //        returned value:    0          successful operation.
 //                          -ENODEV     factory failed to initialize
 //                          -EINVAL     invalid pEffectUuid or pDescriptor
-//                          -ENOENT     No effect with this uuid found
+//                          -ENOENT     no effect with this uuid found
 //        *pDescriptor:     updated with the effect descriptor.
 //
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index c147632..ccfa530 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -27,6 +27,9 @@
 #include <media/IAudioTrack.h>
 #include <media/IAudioRecord.h>
 #include <media/IAudioFlingerClient.h>
+#include <media/EffectApi.h>
+#include <media/IEffect.h>
+#include <media/IEffectClient.h>
 #include <utils/String8.h>
 
 namespace android {
@@ -51,6 +54,7 @@
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
                                 int output,
+                                int *sessionId,
                                 status_t *status) = 0;
 
     virtual sp<IAudioRecord> openRecord(
@@ -61,6 +65,7 @@
                                 int channelCount,
                                 int frameCount,
                                 uint32_t flags,
+                                int *sessionId,
                                 status_t *status) = 0;
 
     /* query the audio hardware state. This state never changes,
@@ -134,6 +139,28 @@
     virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output) = 0;
 
     virtual unsigned int  getInputFramesLost(int ioHandle) = 0;
+
+    virtual int newAudioSessionId() = 0;
+
+    virtual status_t loadEffectLibrary(const char *libPath, int *handle) = 0;
+
+    virtual status_t unloadEffectLibrary(int handle) = 0;
+
+    virtual status_t queryNumberEffects(uint32_t *numEffects) = 0;
+
+    virtual status_t queryNextEffect(effect_descriptor_t *pDescriptor) = 0;
+
+    virtual status_t getEffectDescriptor(effect_uuid_t *pEffectUUID, effect_descriptor_t *pDescriptor) = 0;
+
+    virtual sp<IEffect> createEffect(pid_t pid,
+                                    effect_descriptor_t *pDesc,
+                                    const sp<IEffectClient>& client,
+                                    int32_t priority,
+                                    int output,
+                                    int sessionId,
+                                    status_t *status,
+                                    int *id,
+                                    int *enabled) = 0;
 };
 
 
diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h
index de6426a..47d530b 100644
--- a/include/media/IAudioTrack.h
+++ b/include/media/IAudioTrack.h
@@ -62,6 +62,11 @@
      */
     virtual void        pause() = 0;
 
+    /* Attach track auxiliary output to specified effect. Use effectId = 0
+     * to detach track from effect.
+     */
+    virtual status_t    attachAuxEffect(int effectId) = 0;
+
     /* get this tracks control block */
     virtual sp<IMemory> getCblk() const = 0;    
 };
diff --git a/include/private/media/AudioEffectShared.h b/include/private/media/AudioEffectShared.h
new file mode 100644
index 0000000..a3a99a4
--- /dev/null
+++ b/include/private/media/AudioEffectShared.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_EFFECTCBASESHARED_H
+#define ANDROID_EFFECTCBASESHARED_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/threads.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+// Size of buffer used to exchange parameters between application and mediaserver processes.
+#define EFFECT_PARAM_BUFFER_SIZE 1024
+
+
+// Shared memory area used to exchange parameters between application and mediaserver
+// process.
+struct effect_param_cblk_t
+{
+                Mutex       lock;
+    volatile    uint32_t    clientIndex;    // Current read/write index for application
+    volatile    uint32_t    serverIndex;    // Current read/write index for mediaserver
+                uint8_t*    buffer;         // start of parameter buffer
+
+                effect_param_cblk_t()
+                    : lock(Mutex::SHARED), clientIndex(0), serverIndex(0) {}
+};
+
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_EFFECTCBASESHARED_H
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index cd47fdf..1510f87 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -78,7 +78,8 @@
                 uint16_t    bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger
                 uint16_t    waitTimeMs;      // Cumulated wait time
 
-                uint32_t    reserved;
+                uint16_t    sendLevel;
+                uint16_t    reserved;
                 // Cache line boundary (32 bytes)
                             audio_track_cblk_t();
                 uint32_t    stepUser(uint32_t frameCount);
diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk
index 870c0b8..22ecc54 100644
--- a/libs/audioflinger/Android.mk
+++ b/libs/audioflinger/Android.mk
@@ -87,7 +87,8 @@
     libutils \
     libbinder \
     libmedia \
-    libhardware_legacy
+    libhardware_legacy \
+    libeffects
 
 ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
   LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 3b38d83..1860793 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -37,7 +37,7 @@
 #include <media/AudioRecord.h>
 
 #include <private/media/AudioTrackShared.h>
-
+#include <private/media/AudioEffectShared.h>
 #include <hardware_legacy/AudioHardwareInterface.h>
 
 #include "AudioMixer.h"
@@ -51,6 +51,8 @@
 #include "lifevibes.h"
 #endif
 
+#include <media/EffectFactoryApi.h>
+
 // ----------------------------------------------------------------------------
 // the sim build doesn't have gettid
 
@@ -67,6 +69,7 @@
 
 //static const nsecs_t kStandbyTimeInNsecs = seconds(3);
 static const float MAX_GAIN = 4096.0f;
+static const float MAX_GAIN_INT = 0x1000;
 
 // retry counts for buffer fill timeout
 // 50 * ~20msecs = 1 second
@@ -123,7 +126,7 @@
 
 AudioFlinger::AudioFlinger()
     : BnAudioFlinger(),
-        mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextThreadId(0)
+        mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
 {
     mHardwareStatus = AUDIO_HW_IDLE;
 
@@ -282,6 +285,7 @@
         uint32_t flags,
         const sp<IMemory>& sharedBuffer,
         int output,
+        int *sessionId,
         status_t *status)
 {
     sp<PlaybackThread::Track> track;
@@ -289,6 +293,7 @@
     sp<Client> client;
     wp<Client> wclient;
     status_t lStatus;
+    int lSessionId;
 
     if (streamType >= AudioSystem::NUM_STREAM_TYPES) {
         LOGE("invalid stream type");
@@ -313,8 +318,23 @@
             client = new Client(this, pid);
             mClients.add(pid, client);
         }
+
+        // If no audio session id is provided, create one here
+        // TODO: enforce same stream type for all tracks in same audio session?
+        // TODO: prevent same audio session on different output threads
+        LOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
+        if (sessionId != NULL && *sessionId != 0) {
+            lSessionId = *sessionId;
+        } else {
+            lSessionId = nextUniqueId();
+            if (sessionId != NULL) {
+                *sessionId = lSessionId;
+            }
+        }
+        LOGV("createTrack() lSessionId: %d", lSessionId);
+
         track = thread->createTrack_l(client, streamType, sampleRate, format,
-                channelCount, frameCount, sharedBuffer, &lStatus);
+                channelCount, frameCount, sharedBuffer, lSessionId, &lStatus);
     }
     if (lStatus == NO_ERROR) {
         trackHandle = new TrackHandle(track);
@@ -940,10 +960,11 @@
 
 // ----------------------------------------------------------------------------
 
-AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
+AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
     :   ThreadBase(audioFlinger, id),
         mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output),
-        mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
+        mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
+        mDevice(device)
 {
     readOutputParameters();
 
@@ -965,6 +986,7 @@
 {
     dumpInternals(fd, args);
     dumpTracks(fd, args);
+    dumpEffectChains(fd, args);
     return NO_ERROR;
 }
 
@@ -976,7 +998,7 @@
 
     snprintf(buffer, SIZE, "Output thread %p tracks\n", this);
     result.append(buffer);
-    result.append("   Name Clien Typ Fmt Chn Buf  S M F SRate  LeftV RighV Serv     User\n");
+    result.append("   Name  Clien Typ Fmt Chn Session Buf  S M F SRate LeftV RighV  Serv       User       Main buf   Aux Buf\n");
     for (size_t i = 0; i < mTracks.size(); ++i) {
         sp<Track> track = mTracks[i];
         if (track != 0) {
@@ -987,7 +1009,7 @@
 
     snprintf(buffer, SIZE, "Output thread %p active tracks\n", this);
     result.append(buffer);
-    result.append("   Name Clien Typ Fmt Chn Buf  S M F SRate  LeftV RighV Serv     User\n");
+    result.append("   Name  Clien Typ Fmt Chn Session Buf  S M F SRate LeftV RighV  Serv       User       Main buf   Aux Buf\n");
     for (size_t i = 0; i < mActiveTracks.size(); ++i) {
         wp<Track> wTrack = mActiveTracks[i];
         if (wTrack != 0) {
@@ -1002,6 +1024,24 @@
     return NO_ERROR;
 }
 
+status_t AudioFlinger::PlaybackThread::dumpEffectChains(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, "\n- %d Effect Chains:\n", mEffectChains.size());
+    write(fd, buffer, strlen(buffer));
+
+    for (size_t i = 0; i < mEffectChains.size(); ++i) {
+        sp<EffectChain> chain = mEffectChains[i];
+        if (chain != 0) {
+            chain->dump(fd, args);
+        }
+    }
+    return NO_ERROR;
+}
+
 status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
 {
     const size_t SIZE = 256;
@@ -1020,6 +1060,8 @@
     result.append(buffer);
     snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended);
     result.append(buffer);
+    snprintf(buffer, SIZE, "mix buffer : %p\n", mMixBuffer);
+    result.append(buffer);
     write(fd, result.string(), result.size());
 
     dumpBase(fd, args);
@@ -1057,6 +1099,7 @@
         int channelCount,
         int frameCount,
         const sp<IMemory>& sharedBuffer,
+        int sessionId,
         status_t *status)
 {
     sp<Track> track;
@@ -1087,12 +1130,18 @@
     { // scope for mLock
         Mutex::Autolock _l(mLock);
         track = new Track(this, client, streamType, sampleRate, format,
-                channelCount, frameCount, sharedBuffer);
+                channelCount, frameCount, sharedBuffer, sessionId);
         if (track->getCblk() == NULL || track->name() < 0) {
             lStatus = NO_MEMORY;
             goto Exit;
         }
         mTracks.add(track);
+
+        sp<EffectChain> chain = getEffectChain_l(sessionId);
+        if (chain != 0) {
+            LOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
+            track->setMainBuffer(chain->inBuffer());
+        }
     }
     lStatus = NO_ERROR;
 
@@ -1209,6 +1258,14 @@
         track->mFillingUpStatus = Track::FS_FILLING;
         track->mResetDone = false;
         mActiveTracks.add(track);
+        if (track->mainBuffer() != mMixBuffer) {
+            sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+            if (chain != 0) {
+                LOGV("addTrack_l() starting track on chain %p for session %d", chain.get(), track->sessionId());
+                chain->startTrack();
+            }
+        }
+
         status = NO_ERROR;
     }
 
@@ -1271,9 +1328,11 @@
 
     // FIXME - Current mixer implementation only supports stereo output: Always
     // Allocate a stereo buffer even if HW output is mono.
-    if (mMixBuffer != NULL) delete mMixBuffer;
+    if (mMixBuffer != NULL) delete[] mMixBuffer;
     mMixBuffer = new int16_t[mFrameCount * 2];
     memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
+
+    //TODO handle effects reconfig
 }
 
 status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
@@ -1289,10 +1348,47 @@
     return mOutput->getRenderPosition(dspFrames);
 }
 
+bool AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
+{
+    Mutex::Autolock _l(mLock);
+    if (getEffectChain_l(sessionId) != 0) {
+        return true;
+    }
+
+    for (size_t i = 0; i < mTracks.size(); ++i) {
+        sp<Track> track = mTracks[i];
+        if (sessionId == track->sessionId()) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain(int sessionId)
+{
+    Mutex::Autolock _l(mLock);
+    return getEffectChain_l(sessionId);
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain_l(int sessionId)
+{
+    sp<EffectChain> chain;
+
+    size_t size = mEffectChains.size();
+    for (size_t i = 0; i < size; i++) {
+        if (mEffectChains[i]->sessionId() == sessionId) {
+            chain = mEffectChains[i];
+            break;
+        }
+    }
+    return chain;
+}
+
 // ----------------------------------------------------------------------------
 
-AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
-    :   PlaybackThread(audioFlinger, output, id),
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
+    :   PlaybackThread(audioFlinger, output, id, device),
         mAudioMixer(0)
 {
     mType = PlaybackThread::MIXER;
@@ -1311,7 +1407,6 @@
 
 bool AudioFlinger::MixerThread::threadLoop()
 {
-    int16_t* curBuf = mMixBuffer;
     Vector< sp<Track> > tracksToRemove;
     uint32_t mixerStatus = MIXER_IDLE;
     nsecs_t standbyTime = systemTime();
@@ -1324,6 +1419,7 @@
     uint32_t activeSleepTime = activeSleepTimeUs();
     uint32_t idleSleepTime = idleSleepTimeUs();
     uint32_t sleepTime = idleSleepTime;
+    Vector< sp<EffectChain> > effectChains;
 
     while (!exitPending())
     {
@@ -1382,13 +1478,20 @@
             }
 
             mixerStatus = prepareTracks_l(activeTracks, &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
+            effectChains = mEffectChains;
+            lockEffectChains_l();
        }
 
         if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
             // mix buffers...
-            mAudioMixer->process(curBuf);
+            mAudioMixer->process();
             sleepTime = 0;
             standbyTime = systemTime() + kStandbyTimeInNsecs;
+            //TODO: delay standby when effects have a tail
         } else {
             // If no tracks are ready, sleep once for the duration of an output
             // buffer size, then write 0s to the output
@@ -1400,10 +1503,11 @@
                 }
             } else if (mBytesWritten != 0 ||
                        (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) {
-                memset (curBuf, 0, mixBufferSize);
+                memset (mMixBuffer, 0, mixBufferSize);
                 sleepTime = 0;
                 LOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
             }
+            // TODO add standby time extension fct of effect tail
         }
 
         if (mSuspended) {
@@ -1411,16 +1515,22 @@
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
-            mLastWriteTime = systemTime();
-            mInWrite = true;
-            mBytesWritten += mixBufferSize;
+             for (size_t i = 0; i < effectChains.size(); i ++) {
+                 effectChains[i]->process_l();
+             }
+             // enable changes in effect chain
+             unlockEffectChains();
 #ifdef LVMX
             int audioOutputType = LifeVibes::getMixerType(mId, mType);
             if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
-               LifeVibes::process(audioOutputType, curBuf, mixBufferSize);
+               LifeVibes::process(audioOutputType, mMixBuffer, mixBufferSize);
             }
 #endif
-            int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize);
+            mLastWriteTime = systemTime();
+            mInWrite = true;
+            mBytesWritten += mixBufferSize;
+
+            int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
             if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
             mNumWrites++;
             mInWrite = false;
@@ -1439,6 +1549,8 @@
             }
             mStandby = false;
         } else {
+            // enable changes in effect chain
+            unlockEffectChains();
             usleep(sleepTime);
         }
 
@@ -1446,6 +1558,10 @@
         // 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();
     }
 
     if (!mStandby) {
@@ -1463,6 +1579,8 @@
     uint32_t mixerStatus = MIXER_IDLE;
     // find out which tracks need to be processed
     size_t count = activeTracks.size();
+    size_t mixedTracks = 0;
+    size_t tracksWithEffect = 0;
 
     float masterVolume = mMasterVolume;
     bool  masterMute = mMasterMute;
@@ -1485,6 +1603,14 @@
         LifeVibes::computeVolumes(audioOutputType, activeTypes, tracksConnectedChanged, stateChanged, masterVolume, masterMute);
     }
 #endif
+    // Delegate master volume control to effect in output mix effect chain if needed
+    sp<EffectChain> chain = getEffectChain_l(0);
+    if (chain != 0) {
+        uint32_t v = (uint32_t)(masterVolume * (1 << 24));
+        chain->setVolume(&v, &v);
+        masterVolume = (float)((v + (1 << 23)) >> 24);
+        chain.clear();
+    }
 
     for (size_t i=0 ; i<count ; i++) {
         sp<Track> t = activeTracks[i].promote();
@@ -1501,11 +1627,42 @@
         {
             //LOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this);
 
+            mixedTracks++;
+
+            // track->mainBuffer() != mMixBuffer means there is an effect chain
+            // connected to the track
+            chain.clear();
+            if (track->mainBuffer() != mMixBuffer) {
+                chain = getEffectChain_l(track->sessionId());
+                // Delegate volume control to effect in track effect chain if needed
+                if (chain != 0) {
+                    tracksWithEffect++;
+                } else {
+                    LOGW("prepareTracks_l(): track %08x attached to effect but no chain found on session %d",
+                            track->name(), track->sessionId());
+                }
+            }
+
+
+            int param = AudioMixer::VOLUME;
+            if (track->mFillingUpStatus == Track::FS_FILLED) {
+                // no ramp for the first volume setting
+                track->mFillingUpStatus = Track::FS_ACTIVE;
+                if (track->mState == TrackBase::RESUMING) {
+                    track->mState = TrackBase::ACTIVE;
+                    param = AudioMixer::RAMP_VOLUME;
+                }
+            } else if (cblk->server != 0) {
+                // If the track is stopped before the first frame was mixed,
+                // do not apply ramp
+                param = AudioMixer::RAMP_VOLUME;
+            }
+
             // compute volume for this track
-            int16_t left, right;
+            int16_t left, right, aux;
             if (track->isMuted() || masterMute || track->isPausing() ||
                 mStreamTypes[track->type()].mute) {
-                left = right = 0;
+                left = right = aux = 0;
                 if (track->isPausing()) {
                     track->setPaused();
                 }
@@ -1524,31 +1681,28 @@
                 }
 #endif
                 float v = masterVolume * typeVolume;
-                float v_clamped = v * cblk->volume[0];
-                if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
-                left = int16_t(v_clamped);
-                v_clamped = v * cblk->volume[1];
-                if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
-                right = int16_t(v_clamped);
-            }
+                uint32_t vl = (uint32_t)(v * cblk->volume[0]) << 12;
+                uint32_t vr = (uint32_t)(v * cblk->volume[1]) << 12;
 
-            // XXX: these things DON'T need to be done each time
-            mAudioMixer->setBufferProvider(track);
-            mAudioMixer->enable(AudioMixer::MIXING);
-
-            int param = AudioMixer::VOLUME;
-            if (track->mFillingUpStatus == Track::FS_FILLED) {
-                // no ramp for the first volume setting
-                track->mFillingUpStatus = Track::FS_ACTIVE;
-                if (track->mState == TrackBase::RESUMING) {
-                    track->mState = TrackBase::ACTIVE;
-                    param = AudioMixer::RAMP_VOLUME;
+                // Delegate volume control to effect in track effect chain if needed
+                if (chain != 0 && chain->setVolume(&vl, &vr)) {
+                    // Do not ramp volume is volume is controlled by effect
+                    param = AudioMixer::VOLUME;
                 }
-            } else if (cblk->server != 0) {
-                // If the track is stopped before the first frame was mixed,
-                // do not apply ramp
-                param = AudioMixer::RAMP_VOLUME;
+
+                // 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;
+                left = int16_t(v_clamped);
+                v_clamped = (vr + (1 << 11)) >> 12;
+                if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+                right = int16_t(v_clamped);
+
+                v_clamped = (uint32_t)(v * cblk->sendLevel);
+                if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+                aux = int16_t(v_clamped);
             }
+
 #ifdef LVMX
             if ( tracksConnectedChanged || stateChanged )
             {
@@ -1556,18 +1710,30 @@
                  param = AudioMixer::VOLUME;
             }
 #endif
-            mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left);
-            mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right);
+
+            // XXX: these things DON'T need to be done each time
+            mAudioMixer->setBufferProvider(track);
+            mAudioMixer->enable(AudioMixer::MIXING);
+
+            mAudioMixer->setParameter(param, AudioMixer::VOLUME0, (void *)left);
+            mAudioMixer->setParameter(param, AudioMixer::VOLUME1, (void *)right);
+            mAudioMixer->setParameter(param, AudioMixer::AUXLEVEL, (void *)aux);
             mAudioMixer->setParameter(
                 AudioMixer::TRACK,
-                AudioMixer::FORMAT, track->format());
+                AudioMixer::FORMAT, (void *)track->format());
             mAudioMixer->setParameter(
                 AudioMixer::TRACK,
-                AudioMixer::CHANNEL_COUNT, track->channelCount());
+                AudioMixer::CHANNEL_COUNT, (void *)track->channelCount());
             mAudioMixer->setParameter(
                 AudioMixer::RESAMPLE,
                 AudioMixer::SAMPLE_RATE,
-                int(cblk->sampleRate));
+                (void *)(cblk->sampleRate));
+            mAudioMixer->setParameter(
+                AudioMixer::TRACK,
+                AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
+            mAudioMixer->setParameter(
+                AudioMixer::TRACK,
+                AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());
 
             // reset retry count
             track->mRetryCount = kMaxTrackRetries;
@@ -1581,7 +1747,6 @@
                 // We have consumed all the buffers of this track.
                 // Remove it from the list of active tracks.
                 tracksToRemove->add(track);
-                mAudioMixer->disable(AudioMixer::MIXING);
             } else {
                 // No buffers for this track. Give it a few chances to
                 // fill a buffer, then remove it from active list.
@@ -1591,9 +1756,8 @@
                 } else if (mixerStatus != MIXER_TRACKS_READY) {
                     mixerStatus = MIXER_TRACKS_ENABLED;
                 }
-
-                mAudioMixer->disable(AudioMixer::MIXING);
             }
+            mAudioMixer->disable(AudioMixer::MIXING);
         }
     }
 
@@ -1603,6 +1767,13 @@
         for (size_t i=0 ; i<count ; i++) {
             const sp<Track>& track = tracksToRemove->itemAt(i);
             mActiveTracks.remove(track);
+            if (track->mainBuffer() != mMixBuffer) {
+                chain = getEffectChain_l(track->sessionId());
+                if (chain != 0) {
+                    LOGV("stopping track on chain %p for session Id: %d", chain.get(), track->sessionId());
+                    chain->stopTrack();
+                }
+            }
             if (track->isTerminated()) {
                 mTracks.remove(track);
                 deleteTrackName_l(track->mName);
@@ -1610,6 +1781,13 @@
         }
     }
 
+    // mix buffer must be cleared if all tracks are connected to an
+    // effect chain as in this case the mixer will not write to
+    // mix buffer and track effects will accumulate into it
+    if (mixedTracks != 0 && mixedTracks == tracksWithEffect) {
+        memset(mMixBuffer, 0, mFrameCount * mChannelCount * sizeof(int16_t));
+    }
+
     return mixerStatus;
 }
 
@@ -1681,6 +1859,15 @@
                 reconfig = true;
             }
         }
+        if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
+            // forward device change to effects that have requested to be
+            // aware of attached audio device.
+            mDevice = (uint32_t)value;
+            for (size_t i = 0; i < mEffectChains.size(); i++) {
+                mEffectChains[i]->setDevice(mDevice);
+            }
+        }
+
         if (status == NO_ERROR) {
             status = mOutput->setParameters(keyValuePair);
             if (!mStandby && status == INVALID_OPERATION) {
@@ -1740,9 +1927,8 @@
 }
 
 // ----------------------------------------------------------------------------
-AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
-    :   PlaybackThread(audioFlinger, output, id),
-    mLeftVolume (1.0), mRightVolume(1.0)
+AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
+    :   PlaybackThread(audioFlinger, output, id, device)
 {
     mType = PlaybackThread::DIRECT;
 }
@@ -1752,6 +1938,102 @@
 }
 
 
+static inline int16_t clamp16(int32_t sample)
+{
+    if ((sample>>15) ^ (sample>>31))
+        sample = 0x7FFF ^ (sample>>31);
+    return sample;
+}
+
+static inline
+int32_t mul(int16_t in, int16_t v)
+{
+#if defined(__arm__) && !defined(__thumb__)
+    int32_t out;
+    asm( "smulbb %[out], %[in], %[v] \n"
+         : [out]"=r"(out)
+         : [in]"%r"(in), [v]"r"(v)
+         : );
+    return out;
+#else
+    return in * int32_t(v);
+#endif
+}
+
+void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp)
+{
+    // Do not apply volume on compressed audio
+    if (!AudioSystem::isLinearPCM(mFormat)) {
+        return;
+    }
+
+    // convert to signed 16 bit before volume calculation
+    if (mFormat == AudioSystem::PCM_8_BIT) {
+        size_t count = mFrameCount * mChannelCount;
+        uint8_t *src = (uint8_t *)mMixBuffer + count-1;
+        int16_t *dst = mMixBuffer + count-1;
+        while(count--) {
+            *dst-- = (int16_t)(*src--^0x80) << 8;
+        }
+    }
+
+    size_t frameCount = mFrameCount;
+    int16_t *out = mMixBuffer;
+    if (ramp) {
+        if (mChannelCount == 1) {
+            int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16;
+            int32_t vlInc = d / (int32_t)frameCount;
+            int32_t vl = ((int32_t)mLeftVolShort << 16);
+            do {
+                out[0] = clamp16(mul(out[0], vl >> 16) >> 12);
+                out++;
+                vl += vlInc;
+            } while (--frameCount);
+
+        } else {
+            int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16;
+            int32_t vlInc = d / (int32_t)frameCount;
+            d = ((int32_t)rightVol - (int32_t)mRightVolShort) << 16;
+            int32_t vrInc = d / (int32_t)frameCount;
+            int32_t vl = ((int32_t)mLeftVolShort << 16);
+            int32_t vr = ((int32_t)mRightVolShort << 16);
+            do {
+                out[0] = clamp16(mul(out[0], vl >> 16) >> 12);
+                out[1] = clamp16(mul(out[1], vr >> 16) >> 12);
+                out += 2;
+                vl += vlInc;
+                vr += vrInc;
+            } while (--frameCount);
+        }
+    } else {
+        if (mChannelCount == 1) {
+            do {
+                out[0] = clamp16(mul(out[0], leftVol) >> 12);
+                out++;
+            } while (--frameCount);
+        } else {
+            do {
+                out[0] = clamp16(mul(out[0], leftVol) >> 12);
+                out[1] = clamp16(mul(out[1], rightVol) >> 12);
+                out += 2;
+            } while (--frameCount);
+        }
+    }
+
+    // convert back to unsigned 8 bit after volume calculation
+    if (mFormat == AudioSystem::PCM_8_BIT) {
+        size_t count = mFrameCount * mChannelCount;
+        int16_t *src = mMixBuffer;
+        uint8_t *dst = (uint8_t *)mMixBuffer;
+        while(count--) {
+            *dst++ = (uint8_t)(((int32_t)*src++ + (1<<7)) >> 8)^0x80;
+        }
+    }
+
+    mLeftVolShort = leftVol;
+    mRightVolShort = rightVol;
+}
+
 bool AudioFlinger::DirectOutputThread::threadLoop()
 {
     uint32_t mixerStatus = MIXER_IDLE;
@@ -1770,6 +2052,11 @@
 
     while (!exitPending())
     {
+        bool rampVolume;
+        uint16_t leftVol;
+        uint16_t rightVol;
+        Vector< sp<EffectChain> > effectChains;
+
         processConfigEvents();
 
         mixerStatus = MIXER_IDLE;
@@ -1821,6 +2108,8 @@
                 }
             }
 
+            effectChains = mEffectChains;
+
             // find out which tracks need to be processed
             if (mActiveTracks.size() != 0) {
                 sp<Track> t = mActiveTracks[0].promote();
@@ -1836,6 +2125,19 @@
                 {
                     //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
 
+                    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() ||
@@ -1855,17 +2157,42 @@
                         right = v_clamped/MAX_GAIN;
                     }
 
-                    if (left != mLeftVolume || right != mRightVolume) {
-                        mOutput->setVolume(left, right);
-                        left = mLeftVolume;
-                        right = mRightVolume;
-                    }
+                    if (left != mLeftVolFloat || right != mRightVolFloat) {
+                        mLeftVolFloat = left;
+                        mRightVolFloat = right;
 
-                    if (track->mFillingUpStatus == Track::FS_FILLED) {
-                        track->mFillingUpStatus = Track::FS_ACTIVE;
-                        if (track->mState == TrackBase::RESUMING) {
-                            track->mState = TrackBase::ACTIVE;
+                        // If audio HAL implements volume control,
+                        // force software volume to nominal value
+                        if (mOutput->setVolume(left, right) == NO_ERROR) {
+                            left = 1.0f;
+                            right = 1.0f;
                         }
+
+                        // Convert volumes from float to 8.24
+                        uint32_t vl = (uint32_t)(left * (1 << 24));
+                        uint32_t vr = (uint32_t)(right * (1 << 24));
+
+                        // 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()) {
+                            // Do not ramp volume is volume is controlled by effect
+                            if(effectChains[0]->setVolume(&vl, &vr)) {
+                                rampVolume = false;
+                            }
+                        }
+
+                        // 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
@@ -1897,11 +2224,17 @@
             // remove all the tracks that need to be...
             if (UNLIKELY(trackToRemove != 0)) {
                 mActiveTracks.remove(trackToRemove);
+                if (!effectChains.isEmpty()) {
+                    LOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(), trackToRemove->sessionId());
+                    effectChains[0]->stopTrack();
+                }
                 if (trackToRemove->isTerminated()) {
                     mTracks.remove(trackToRemove);
                     deleteTrackName_l(trackToRemove->mName);
                 }
             }
+
+            lockEffectChains_l();
        }
 
         if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
@@ -1909,7 +2242,7 @@
             size_t frameCount = mFrameCount;
             curBuf = (int8_t *)mMixBuffer;
             // output audio to hardware
-            while(frameCount) {
+            while (frameCount) {
                 buffer.frameCount = frameCount;
                 activeTrack->getNextBuffer(&buffer);
                 if (UNLIKELY(buffer.raw == 0)) {
@@ -1941,6 +2274,14 @@
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
+            if (mixerStatus == MIXER_TRACKS_READY) {
+                applyVolume(leftVol, rightVol, rampVolume);
+            }
+            for (size_t i = 0; i < effectChains.size(); i ++) {
+                effectChains[i]->process_l();
+            }
+            unlockEffectChains();
+
             mLastWriteTime = systemTime();
             mInWrite = true;
             mBytesWritten += mixBufferSize;
@@ -1950,6 +2291,7 @@
             mInWrite = false;
             mStandby = false;
         } else {
+            unlockEffectChains();
             usleep(sleepTime);
         }
 
@@ -1958,6 +2300,10 @@
         // 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();
     }
 
     if (!mStandby) {
@@ -2048,7 +2394,7 @@
 // ----------------------------------------------------------------------------
 
 AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
-    :   MixerThread(audioFlinger, mainThread->getOutput(), id), mWaitTimeMs(UINT_MAX)
+    :   MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device()), mWaitTimeMs(UINT_MAX)
 {
     mType = PlaybackThread::DUPLICATING;
     addOutputTrack(mainThread);
@@ -2064,7 +2410,6 @@
 
 bool AudioFlinger::DuplicatingThread::threadLoop()
 {
-    int16_t* curBuf = mMixBuffer;
     Vector< sp<Track> > tracksToRemove;
     uint32_t mixerStatus = MIXER_IDLE;
     nsecs_t standbyTime = systemTime();
@@ -2074,6 +2419,7 @@
     uint32_t activeSleepTime = activeSleepTimeUs();
     uint32_t idleSleepTime = idleSleepTimeUs();
     uint32_t sleepTime = idleSleepTime;
+    Vector< sp<EffectChain> > effectChains;
 
     while (!exitPending())
     {
@@ -2134,14 +2480,20 @@
             }
 
             mixerStatus = prepareTracks_l(activeTracks, &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
+            effectChains = mEffectChains;
+            lockEffectChains_l();
         }
 
         if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
             // mix buffers...
             if (outputsReady(outputTracks)) {
-                mAudioMixer->process(curBuf);
+                mAudioMixer->process();
             } else {
-                memset(curBuf, 0, mixBufferSize);
+                memset(mMixBuffer, 0, mixBufferSize);
             }
             sleepTime = 0;
             writeFrames = mFrameCount;
@@ -2158,6 +2510,7 @@
                     if (outputTracks[i]->isActive()) {
                         sleepTime = 0;
                         writeFrames = 0;
+                        memset(mMixBuffer, 0, mixBufferSize);
                         break;
                     }
                 }
@@ -2169,13 +2522,21 @@
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
+            for (size_t i = 0; i < effectChains.size(); i ++) {
+                effectChains[i]->process_l();
+            }
+            // enable changes in effect chain
+            unlockEffectChains();
+
             standbyTime = systemTime() + kStandbyTimeInNsecs;
             for (size_t i = 0; i < outputTracks.size(); i++) {
-                outputTracks[i]->write(curBuf, writeFrames);
+                outputTracks[i]->write(mMixBuffer, writeFrames);
             }
             mStandby = false;
             mBytesWritten += mixBufferSize;
         } else {
+            // enable changes in effect chain
+            unlockEffectChains();
             usleep(sleepTime);
         }
 
@@ -2184,6 +2545,10 @@
         // 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();
     }
 
     return false;
@@ -2268,7 +2633,8 @@
             int channelCount,
             int frameCount,
             uint32_t flags,
-            const sp<IMemory>& sharedBuffer)
+            const sp<IMemory>& sharedBuffer,
+            int sessionId)
     :   RefBase(),
         mThread(thread),
         mClient(client),
@@ -2277,7 +2643,8 @@
         mState(IDLE),
         mClientTid(-1),
         mFormat(format),
-        mFlags(flags & ~SYSTEM_FLAGS_MASK)
+        mFlags(flags & ~SYSTEM_FLAGS_MASK),
+        mSessionId(sessionId)
 {
     LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
 
@@ -2420,15 +2787,17 @@
             int format,
             int channelCount,
             int frameCount,
-            const sp<IMemory>& sharedBuffer)
-    :   TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer),
-    mMute(false), mSharedBuffer(sharedBuffer), mName(-1)
+            const sp<IMemory>& sharedBuffer,
+            int sessionId)
+    :   TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer, sessionId),
+    mMute(false), mSharedBuffer(sharedBuffer), mName(-1), mMainBuffer(NULL), mAuxBuffer(NULL), mAuxEffectId(0)
 {
     if (mCblk != NULL) {
         sp<ThreadBase> baseThread = thread.promote();
         if (baseThread != 0) {
             PlaybackThread *playbackThread = (PlaybackThread *)baseThread.get();
             mName = playbackThread->getTrackName_l();
+            mMainBuffer = playbackThread->mixBuffer();
         }
         LOGV("Track constructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid());
         if (mName < 0) {
@@ -2482,12 +2851,13 @@
 
 void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
 {
-    snprintf(buffer, size, "  %5d %5d %3u %3u %3u %04u %1d %1d %1d %5u %5u %5u  %08x %08x\n",
+    snprintf(buffer, size, "   %05d %05d %03u %03u %03u %05u   %04u %1d %1d %1d %05u %05u %05u  0x%08x 0x%08x 0x%08x 0x%08x\n",
             mName - AudioMixer::TRACK0,
             (mClient == NULL) ? getpid() : mClient->pid(),
             mStreamType,
             mFormat,
             mCblk->channelCount,
+            mSessionId,
             mFrameCount,
             mState,
             mMute,
@@ -2496,7 +2866,9 @@
             mCblk->volume[0],
             mCblk->volume[1],
             mCblk->server,
-            mCblk->user);
+            mCblk->user,
+            (int)mMainBuffer,
+            (int)mAuxBuffer);
 }
 
 status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
@@ -2679,6 +3051,23 @@
     mVolume[1] = right;
 }
 
+status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
+{
+    status_t status = DEAD_OBJECT;
+    sp<ThreadBase> thread = mThread.promote();
+    if (thread != 0) {
+       PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+       status = playbackThread->attachAuxEffect(this, EffectId);
+    }
+    return status;
+}
+
+void AudioFlinger::PlaybackThread::Track::setAuxBuffer(int EffectId, int32_t *buffer)
+{
+    mAuxEffectId = EffectId;
+    mAuxBuffer = buffer;
+}
+
 // ----------------------------------------------------------------------------
 
 // RecordTrack constructor must be called with AudioFlinger::mLock held
@@ -2689,9 +3078,10 @@
             int format,
             int channelCount,
             int frameCount,
-            uint32_t flags)
+            uint32_t flags,
+            int sessionId)
     :   TrackBase(thread, client, sampleRate, format,
-                  channelCount, frameCount, flags, 0),
+                  channelCount, frameCount, flags, 0, sessionId),
         mOverflow(false)
 {
     if (mCblk != NULL) {
@@ -2779,10 +3169,11 @@
 
 void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
 {
-    snprintf(buffer, size, "   %05d %03u %03u %04u %01d %05u  %08x %08x\n",
+    snprintf(buffer, size, "   %05d %03u %03u %05d   %04u %01d %05u  %08x %08x\n",
             (mClient == NULL) ? getpid() : mClient->pid(),
             mFormat,
             mCblk->channelCount,
+            mSessionId,
             mFrameCount,
             mState,
             mCblk->sampleRate,
@@ -2800,7 +3191,7 @@
             int format,
             int channelCount,
             int frameCount)
-    :   Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL),
+    :   Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL, 0),
     mActive(false), mSourceThread(sourceThread)
 {
 
@@ -3115,6 +3506,11 @@
     return mTrack->getCblk();
 }
 
+status_t AudioFlinger::TrackHandle::attachAuxEffect(int EffectId)
+{
+    return mTrack->attachAuxEffect(EffectId);
+}
+
 status_t AudioFlinger::TrackHandle::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
@@ -3131,6 +3527,7 @@
         int channelCount,
         int frameCount,
         uint32_t flags,
+        int *sessionId,
         status_t *status)
 {
     sp<RecordThread::RecordTrack> recordTrack;
@@ -3140,6 +3537,7 @@
     status_t lStatus;
     RecordThread *thread;
     size_t inFrameCount;
+    int lSessionId;
 
     // check calling permissions
     if (!recordingAllowed()) {
@@ -3164,9 +3562,18 @@
             mClients.add(pid, client);
         }
 
+        // If no audio session id is provided, create one here
+        if (sessionId != NULL && *sessionId != 0) {
+            lSessionId = *sessionId;
+        } else {
+            lSessionId = nextUniqueId();
+            if (sessionId != NULL) {
+                *sessionId = lSessionId;
+            }
+        }
         // create new record track. The record track uses one track in mHardwareMixerThread by convention.
         recordTrack = new RecordThread::RecordTrack(thread, client, sampleRate,
-                                                   format, channelCount, frameCount, flags);
+                                                   format, channelCount, frameCount, flags, lSessionId);
     }
     if (recordTrack->getCblk() == NULL) {
         // remove local strong reference to Client before deleting the RecordTrack so that the Client
@@ -3504,7 +3911,7 @@
 
     if (mActiveTrack != 0) {
         result.append("Active Track:\n");
-        result.append("   Clien Fmt Chn Buf  S SRate  Serv     User\n");
+        result.append("   Clien Fmt Chn Session Buf  S SRate  Serv     User\n");
         mActiveTrack->dump(buffer, SIZE);
         result.append(buffer);
 
@@ -3753,14 +4160,15 @@
 
     mHardwareStatus = AUDIO_HW_IDLE;
     if (output != 0) {
+        int id = nextUniqueId();
         if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
             (format != AudioSystem::PCM_16_BIT) ||
             (channels != AudioSystem::CHANNEL_OUT_STEREO)) {
-            thread = new DirectOutputThread(this, output, ++mNextThreadId);
-            LOGV("openOutput() created direct output: ID %d thread %p", mNextThreadId, thread);
+            thread = new DirectOutputThread(this, output, id, *pDevices);
+            LOGV("openOutput() created direct output: ID %d thread %p", id, thread);
         } else {
-            thread = new MixerThread(this, output, ++mNextThreadId);
-            LOGV("openOutput() created mixer output: ID %d thread %p", mNextThreadId, thread);
+            thread = new MixerThread(this, output, id, *pDevices);
+            LOGV("openOutput() created mixer output: ID %d thread %p", id, thread);
 
 #ifdef LVMX
             unsigned bitsPerSample =
@@ -3774,7 +4182,7 @@
 #endif
 
         }
-        mPlaybackThreads.add(mNextThreadId, thread);
+        mPlaybackThreads.add(id, thread);
 
         if (pSamplingRate) *pSamplingRate = samplingRate;
         if (pFormat) *pFormat = format;
@@ -3783,7 +4191,7 @@
 
         // notify client processes of the new output creation
         thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
-        return mNextThreadId;
+        return id;
     }
 
     return 0;
@@ -3800,13 +4208,13 @@
         return 0;
     }
 
-
-    DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId);
+    int id = nextUniqueId();
+    DuplicatingThread *thread = new DuplicatingThread(this, thread1, id);
     thread->addOutputTrack(thread2);
-    mPlaybackThreads.add(mNextThreadId, thread);
+    mPlaybackThreads.add(id, thread);
     // notify client processes of the new output creation
     thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
-    return mNextThreadId;
+    return id;
 }
 
 status_t AudioFlinger::closeOutput(int output)
@@ -3925,10 +4333,11 @@
     }
 
     if (input != 0) {
+        int id = nextUniqueId();
          // Start record thread
-        thread = new RecordThread(this, input, reqSamplingRate, reqChannels, ++mNextThreadId);
-        mRecordThreads.add(mNextThreadId, thread);
-        LOGV("openInput() created record thread: ID %d thread %p", mNextThreadId, thread);
+        thread = new RecordThread(this, input, reqSamplingRate, reqChannels, id);
+        mRecordThreads.add(id, thread);
+        LOGV("openInput() created record thread: ID %d thread %p", id, thread);
         if (pSamplingRate) *pSamplingRate = reqSamplingRate;
         if (pFormat) *pFormat = format;
         if (pChannels) *pChannels = reqChannels;
@@ -3937,7 +4346,7 @@
 
         // notify client processes of the new input creation
         thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED);
-        return mNextThreadId;
+        return id;
     }
 
     return 0;
@@ -3991,6 +4400,12 @@
     return NO_ERROR;
 }
 
+
+int AudioFlinger::newAudioSessionId()
+{
+    return nextUniqueId();
+}
+
 // checkPlaybackThread_l() must be called with AudioFlinger::mLock held
 AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(int output) const
 {
@@ -4023,6 +4438,1475 @@
     return thread;
 }
 
+int AudioFlinger::nextUniqueId()
+{
+    return android_atomic_inc(&mNextUniqueId);
+}
+
+// ----------------------------------------------------------------------------
+//  Effect management
+// ----------------------------------------------------------------------------
+
+
+status_t AudioFlinger::loadEffectLibrary(const char *libPath, int *handle)
+{
+    Mutex::Autolock _l(mLock);
+    return EffectLoadLibrary(libPath, handle);
+}
+
+status_t AudioFlinger::unloadEffectLibrary(int handle)
+{
+    Mutex::Autolock _l(mLock);
+    return EffectUnloadLibrary(handle);
+}
+
+status_t AudioFlinger::queryNumberEffects(uint32_t *numEffects)
+{
+    Mutex::Autolock _l(mLock);
+    return EffectQueryNumberEffects(numEffects);
+}
+
+status_t AudioFlinger::queryNextEffect(effect_descriptor_t *descriptor)
+{
+    Mutex::Autolock _l(mLock);
+    return EffectQueryNext(descriptor);
+}
+
+status_t AudioFlinger::getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor)
+{
+    Mutex::Autolock _l(mLock);
+    return EffectGetDescriptor(pUuid, descriptor);
+}
+
+sp<IEffect> AudioFlinger::createEffect(pid_t pid,
+        effect_descriptor_t *pDesc,
+        const sp<IEffectClient>& effectClient,
+        int32_t priority,
+        int output,
+        int sessionId,
+        status_t *status,
+        int *id,
+        int *enabled)
+{
+    status_t lStatus = NO_ERROR;
+    sp<EffectHandle> handle;
+    effect_interface_t itfe;
+    effect_descriptor_t desc;
+    sp<Client> client;
+    wp<Client> wclient;
+
+    LOGV("createEffect pid %d, client %p, priority %d, sessionId %d, output %d", pid, effectClient.get(), priority, sessionId, output);
+
+    if (pDesc == NULL) {
+        lStatus = BAD_VALUE;
+        goto Exit;
+    }
+
+    {
+        Mutex::Autolock _l(mLock);
+
+        if (!EffectIsNullUuid(&pDesc->uuid)) {
+            // if uuid is specified, request effect descriptor
+            lStatus = EffectGetDescriptor(&pDesc->uuid, &desc);
+            if (lStatus < 0) {
+                LOGW("createEffect() error %d from EffectGetDescriptor", lStatus);
+                goto Exit;
+            }
+        } else {
+            // if uuid is not specified, look for an available implementation
+            // of the required type in effect factory
+            if (EffectIsNullUuid(&pDesc->type)) {
+                LOGW("createEffect() no effect type");
+                lStatus = BAD_VALUE;
+                goto Exit;
+            }
+            uint32_t numEffects = 0;
+            effect_descriptor_t d;
+            bool found = false;
+
+            lStatus = EffectQueryNumberEffects(&numEffects);
+            if (lStatus < 0) {
+                LOGW("createEffect() error %d from EffectQueryNumberEffects", lStatus);
+                goto Exit;
+            }
+            for (; numEffects > 0; numEffects--) {
+                lStatus = EffectQueryNext(&desc);
+                if (lStatus < 0) {
+                    LOGW("createEffect() error %d from EffectQueryNext", lStatus);
+                    continue;
+                }
+                if (memcmp(&desc.type, &pDesc->type, sizeof(effect_uuid_t)) == 0) {
+                    // If matching type found save effect descriptor. If the session is
+                    // 0 and the effect is not auxiliary, continue enumeration in case
+                    // an auxiliary version of this effect type is available
+                    found = true;
+                    memcpy(&d, &desc, sizeof(effect_descriptor_t));
+                    if (sessionId != 0 ||
+                            (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+                        break;
+                    }
+                }
+            }
+            if (!found) {
+                lStatus = BAD_VALUE;
+                LOGW("createEffect() effect not found");
+                goto Exit;
+            }
+            // For same effect type, chose auxiliary version over insert version if
+            // connect to output mix (Compliance to OpenSL ES)
+            if (sessionId == 0 &&
+                    (d.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_AUXILIARY) {
+                memcpy(&desc, &d, sizeof(effect_descriptor_t));
+            }
+        }
+
+        // Do not allow auxiliary effects on a session different from 0 (output mix)
+        if (sessionId != 0 &&
+             (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+            lStatus = INVALID_OPERATION;
+            goto Exit;
+        }
+
+        // return effect descriptor
+        memcpy(pDesc, &desc, sizeof(effect_descriptor_t));
+
+        // If output is not specified try to find a matching audio session ID in one of the
+        // output threads.
+        // TODO: allow attachment of effect to inputs
+        if (output == 0) {
+            if (sessionId == 0) {
+                // default to first output
+                // TODO: define criteria to choose output when not specified. Or
+                // receive output from audio policy manager
+                if (mPlaybackThreads.size() != 0) {
+                    output = mPlaybackThreads.keyAt(0);
+                }
+            } else {
+                 // look for the thread where the specified audio session is present
+                for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+                    if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId)) {
+                        output = mPlaybackThreads.keyAt(i);
+                        break;
+                    }
+                }
+            }
+        }
+        PlaybackThread *thread = checkPlaybackThread_l(output);
+        if (thread == NULL) {
+            LOGE("unknown output thread");
+            lStatus = BAD_VALUE;
+            goto Exit;
+        }
+
+        wclient = mClients.valueFor(pid);
+
+        if (wclient != NULL) {
+            client = wclient.promote();
+        } else {
+            client = new Client(this, pid);
+            mClients.add(pid, client);
+        }
+
+        // create effect on selected output trhead
+        handle = thread->createEffect_l(client, effectClient, priority, sessionId, &desc, enabled, &lStatus);
+        if (handle != 0 && id != NULL) {
+            *id = handle->id();
+        }
+    }
+
+Exit:
+    if(status) {
+        *status = lStatus;
+    }
+    return handle;
+}
+
+// PlaybackThread::createEffect_l() must be called with AudioFlinger::mLock held
+sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l(
+        const sp<AudioFlinger::Client>& client,
+        const sp<IEffectClient>& effectClient,
+        int32_t priority,
+        int sessionId,
+        effect_descriptor_t *desc,
+        int *enabled,
+        status_t *status
+        )
+{
+    sp<EffectModule> effect;
+    sp<EffectHandle> handle;
+    status_t lStatus;
+    sp<Track> track;
+    sp<EffectChain> chain;
+    bool effectCreated = false;
+
+    if (mOutput == 0) {
+        LOGW("createEffect_l() Audio driver not initialized.");
+        lStatus = NO_INIT;
+        goto Exit;
+    }
+
+    // Do not allow auxiliary effect on session other than 0
+    if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY &&
+        sessionId != 0) {
+        LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d", desc->name, sessionId);
+        lStatus = BAD_VALUE;
+        goto Exit;
+    }
+
+    // Do not allow effects with session ID 0 on direct output or duplicating threads
+    // TODO: add rule for hw accelerated effects on direct outputs with non PCM format
+    if (sessionId == 0 && mType != MIXER) {
+        LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d", desc->name, sessionId);
+        lStatus = BAD_VALUE;
+        goto Exit;
+    }
+
+    LOGV("createEffect_l() thread %p effect %s on session %d", this, desc->name, sessionId);
+
+    { // scope for mLock
+        Mutex::Autolock _l(mLock);
+
+        // check for existing effect chain with the requested audio session
+        chain = getEffectChain_l(sessionId);
+        if (chain == 0) {
+            // create a new chain for this session
+            LOGV("createEffect_l() new effect chain for session %d", sessionId);
+            chain = new EffectChain(this, sessionId);
+            addEffectChain_l(chain);
+        } else {
+            effect = chain->getEffectFromDesc(desc);
+        }
+
+        LOGV("createEffect_l() got effect %p on chain %p", effect == 0 ? 0 : effect.get(), chain.get());
+
+        if (effect == 0) {
+            // create a new effect module if none present in the chain
+            effectCreated = true;
+            effect = new EffectModule(this, chain, desc, mAudioFlinger->nextUniqueId(), sessionId);
+            lStatus = effect->status();
+            if (lStatus != NO_ERROR) {
+                goto Exit;
+            }
+            //TODO: handle CPU load and memory usage here
+            lStatus = chain->addEffect(effect);
+            if (lStatus != NO_ERROR) {
+                goto Exit;
+            }
+
+            effect->setDevice(mDevice);
+        }
+        // create effect handle and connect it to effect module
+        handle = new EffectHandle(effect, client, effectClient, priority);
+        lStatus = effect->addHandle(handle);
+        if (enabled) {
+            *enabled = (int)effect->isEnabled();
+        }
+    }
+
+Exit:
+    if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
+        if (chain != 0 && effectCreated) {
+            if (chain->removeEffect(effect) == 0) {
+                removeEffectChain_l(chain);
+            }
+        }
+        handle.clear();
+    }
+
+    if(status) {
+        *status = lStatus;
+    }
+    return handle;
+}
+
+status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
+{
+    int session = chain->sessionId();
+    int16_t *buffer = mMixBuffer;
+
+    LOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
+    if (session == 0) {
+        chain->setInBuffer(buffer, false);
+        chain->setOutBuffer(buffer);
+        // Effect chain for session 0 is inserted at end of effect chains list
+        // to be processed last as it contains output mix effects to apply after
+        // all track specific effects
+        mEffectChains.add(chain);
+    } else {
+        bool ownsBuffer = false;
+        // Only one effect chain can be present in direct output thread and it uses
+        // the mix buffer as input
+        if (mType != DIRECT) {
+            size_t numSamples = mFrameCount * mChannelCount;
+            buffer = new int16_t[numSamples];
+            memset(buffer, 0, numSamples * sizeof(int16_t));
+            LOGV("addEffectChain_l() creating new input buffer %p session %d", buffer, session);
+            ownsBuffer = true;
+        }
+        chain->setInBuffer(buffer, ownsBuffer);
+        chain->setOutBuffer(mMixBuffer);
+        // Effect chain for session other than 0 is inserted at beginning of effect
+        // chains list to be processed before output mix effects. Relative order between
+        // sessions other than 0 is not important
+        mEffectChains.insertAt(chain, 0);
+    }
+
+    // Attach all tracks with same session ID to this chain.
+    for (size_t i = 0; i < mTracks.size(); ++i) {
+        sp<Track> track = mTracks[i];
+        if (session == track->sessionId()) {
+            LOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(), buffer);
+            track->setMainBuffer(buffer);
+        }
+    }
+
+    // indicate all active tracks in the chain
+    for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
+        sp<Track> track = mActiveTracks[i].promote();
+        if (track == 0) continue;
+        if (session == track->sessionId()) {
+            LOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
+            chain->startTrack();
+        }
+    }
+
+    return NO_ERROR;
+}
+
+size_t AudioFlinger::PlaybackThread::removeEffectChain_l(const sp<EffectChain>& chain)
+{
+    int session = chain->sessionId();
+
+    LOGV("removeEffectChain_l() %p from thread %p for session %d", chain.get(), this, session);
+
+    for (size_t i = 0; i < mEffectChains.size(); i++) {
+        if (chain == mEffectChains[i]) {
+            mEffectChains.removeAt(i);
+            // detach all tracks with same session ID from this chain
+            for (size_t i = 0; i < mTracks.size(); ++i) {
+                sp<Track> track = mTracks[i];
+                if (session == track->sessionId()) {
+                    track->setMainBuffer(mMixBuffer);
+                }
+            }
+        }
+    }
+    return mEffectChains.size();
+}
+
+void AudioFlinger::PlaybackThread::lockEffectChains_l()
+{
+    for (size_t i = 0; i < mEffectChains.size(); i++) {
+        mEffectChains[i]->lock();
+    }
+}
+
+void AudioFlinger::PlaybackThread::unlockEffectChains()
+{
+    Mutex::Autolock _l(mLock);
+    for (size_t i = 0; i < mEffectChains.size(); i++) {
+        mEffectChains[i]->unlock();
+    }
+}
+
+sp<AudioFlinger::EffectModule> AudioFlinger::PlaybackThread::getEffect_l(int sessionId, int effectId)
+{
+    sp<EffectModule> effect;
+
+    sp<EffectChain> chain = getEffectChain_l(sessionId);
+    if (chain != 0) {
+        effect = chain->getEffectFromId(effectId);
+    }
+    return effect;
+}
+
+status_t AudioFlinger::PlaybackThread::attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId)
+{
+    Mutex::Autolock _l(mLock);
+    return attachAuxEffect_l(track, EffectId);
+}
+
+status_t AudioFlinger::PlaybackThread::attachAuxEffect_l(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId)
+{
+    status_t status = NO_ERROR;
+
+    if (EffectId == 0) {
+        track->setAuxBuffer(0, NULL);
+    } else {
+        // Auxiliary effects are always in audio session 0
+        sp<EffectModule> effect = getEffect_l(0, EffectId);
+        if (effect != 0) {
+            if ((effect->desc().flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+                track->setAuxBuffer(EffectId, (int32_t *)effect->inBuffer());
+            } else {
+                status = INVALID_OPERATION;
+            }
+        } else {
+            status = BAD_VALUE;
+        }
+    }
+    return status;
+}
+
+void AudioFlinger::PlaybackThread::detachAuxEffect_l(int effectId)
+{
+     for (size_t i = 0; i < mTracks.size(); ++i) {
+        sp<Track> track = mTracks[i];
+        if (track->auxEffectId() == effectId) {
+            attachAuxEffect_l(track, 0);
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+//  EffectModule implementation
+// ----------------------------------------------------------------------------
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectModule"
+
+AudioFlinger::EffectModule::EffectModule(const wp<ThreadBase>& wThread,
+                                        const wp<AudioFlinger::EffectChain>& chain,
+                                        effect_descriptor_t *desc,
+                                        int id,
+                                        int sessionId)
+    : mThread(wThread), mChain(chain), mId(id), mSessionId(sessionId), mEffectInterface(NULL),
+      mStatus(NO_INIT), mState(IDLE)
+{
+    LOGV("Constructor %p", this);
+    int lStatus;
+    sp<ThreadBase> thread = mThread.promote();
+    if (thread == 0) {
+        return;
+    }
+    PlaybackThread *p = (PlaybackThread *)thread.get();
+
+    memcpy(&mDescriptor, desc, sizeof(effect_descriptor_t));
+
+    // create effect engine from effect factory
+    mStatus = EffectCreate(&desc->uuid, &mEffectInterface);
+    if (mStatus != NO_ERROR) {
+        return;
+    }
+    lStatus = init();
+    if (lStatus < 0) {
+        mStatus = lStatus;
+        goto Error;
+    }
+
+    LOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface);
+    return;
+Error:
+    EffectRelease(mEffectInterface);
+    mEffectInterface = NULL;
+    LOGV("Constructor Error %d", mStatus);
+}
+
+AudioFlinger::EffectModule::~EffectModule()
+{
+    LOGV("Destructor %p", this);
+    if (mEffectInterface != NULL) {
+        // release effect engine
+        EffectRelease(mEffectInterface);
+    }
+}
+
+status_t AudioFlinger::EffectModule::addHandle(sp<EffectHandle>& handle)
+{
+    status_t status;
+
+    Mutex::Autolock _l(mLock);
+    // First handle in mHandles has highest priority and controls the effect module
+    int priority = handle->priority();
+    size_t size = mHandles.size();
+    sp<EffectHandle> h;
+    size_t i;
+    for (i = 0; i < size; i++) {
+        h = mHandles[i].promote();
+        if (h == 0) continue;
+        if (h->priority() <= priority) break;
+    }
+    // if inserted in first place, move effect control from previous owner to this handle
+    if (i == 0) {
+        if (h != 0) {
+            h->setControl(false, true);
+        }
+        handle->setControl(true, false);
+        status = NO_ERROR;
+    } else {
+        status = ALREADY_EXISTS;
+    }
+    mHandles.insertAt(handle, i);
+    return status;
+}
+
+size_t AudioFlinger::EffectModule::removeHandle(const wp<EffectHandle>& handle)
+{
+    Mutex::Autolock _l(mLock);
+    size_t size = mHandles.size();
+    size_t i;
+    for (i = 0; i < size; i++) {
+        if (mHandles[i] == handle) break;
+    }
+    if (i == size) {
+        return size;
+    }
+    mHandles.removeAt(i);
+    size = mHandles.size();
+    // if removed from first place, move effect control from this handle to next in line
+    if (i == 0 && size != 0) {
+        sp<EffectHandle> h = mHandles[0].promote();
+        if (h != 0) {
+            h->setControl(true, true);
+        }
+    }
+
+    return size;
+}
+
+void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle)
+{
+    // keep a strong reference on this EffectModule to avoid calling the
+    // destructor before we exit
+    sp<EffectModule> keep(this);
+    sp<ThreadBase> thread = mThread.promote();
+    if (thread != 0) {
+        Mutex::Autolock _l(thread->mLock);
+        // delete the effect module if removing last handle on it
+        if (removeHandle(handle) == 0) {
+            PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+            if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+                playbackThread->detachAuxEffect_l(mId);
+            }
+            sp<EffectChain> chain = mChain.promote();
+            if (chain != 0) {
+                // remove effect chain if remove last effect
+                if (chain->removeEffect(keep) == 0) {
+                    playbackThread->removeEffectChain_l(chain);
+                }
+            }
+        }
+    }
+}
+
+void AudioFlinger::EffectModule::process()
+{
+    Mutex::Autolock _l(mLock);
+
+    if (mEffectInterface == NULL || mConfig.inputCfg.buffer.raw == NULL || mConfig.outputCfg.buffer.raw == NULL) {
+        return;
+    }
+
+    if (mState != IDLE) {
+        // do 32 bit to 16 bit conversion for auxiliary effect input buffer
+        if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+            AudioMixer::ditherAndClamp(mConfig.inputCfg.buffer.s32,
+                                        mConfig.inputCfg.buffer.s32,
+                                        mConfig.inputCfg.buffer.frameCount);
+        }
+
+        // TODO: handle effects with buffer provider
+        if (mState != ACTIVE) {
+            uint32_t count = mConfig.inputCfg.buffer.frameCount;
+            int32_t amp = 32767L << 16;
+            int32_t step = amp / count;
+            int16_t *pIn = mConfig.inputCfg.buffer.s16;
+            int16_t *pOut = mConfig.outputCfg.buffer.s16;
+            int inChannels;
+            int outChannels;
+
+            if (mConfig.inputCfg.channels == CHANNEL_MONO) {
+                inChannels = 1;
+            } else {
+                inChannels = 2;
+            }
+            if (mConfig.outputCfg.channels == CHANNEL_MONO) {
+                outChannels = 1;
+            } else {
+                outChannels = 2;
+            }
+
+            switch (mState) {
+            case RESET:
+                reset();
+                // clear auxiliary effect input buffer for next accumulation
+                if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+                    memset(mConfig.inputCfg.buffer.raw, 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
+                }
+                step = -step;
+                mState = STARTING;
+                break;
+            case STARTING:
+                start();
+                amp = 0;
+                pOut = mConfig.inputCfg.buffer.s16;
+                outChannels = inChannels;
+                mState = ACTIVE;
+                break;
+            case STOPPING:
+                step = -step;
+                pOut = mConfig.inputCfg.buffer.s16;
+                outChannels = inChannels;
+                mState = STOPPED;
+                break;
+            case STOPPED:
+                stop();
+                amp = 0;
+                mState = IDLE;
+                break;
+            }
+
+            // ramp volume down or up before activating or deactivating the effect
+            if (inChannels == 1) {
+                if (outChannels == 1) {
+                    while (count--) {
+                        *pOut++ = (int16_t)(((int32_t)*pIn++ * (amp >> 16)) >> 15);
+                        amp += step;
+                    }
+                } else {
+                    while (count--) {
+                        int32_t smp = (int16_t)(((int32_t)*pIn++ * (amp >> 16)) >> 15);
+                        *pOut++ = smp;
+                        *pOut++ = smp;
+                        amp += step;
+                    }
+                }
+            } else {
+                if (outChannels == 1) {
+                    while (count--) {
+                        int32_t smp = (((int32_t)*pIn * (amp >> 16)) >> 16) +
+                                      (((int32_t)*(pIn + 1) * (amp >> 16)) >> 16);
+                        pIn += 2;
+                        *pOut++ = (int16_t)smp;
+                        amp += step;
+                    }
+                } else {
+                    while (count--) {
+                        *pOut++ = (int16_t)((int32_t)*pIn++ * (amp >> 16)) >> 15;
+                        *pOut++ = (int16_t)((int32_t)*pIn++ * (amp >> 16)) >> 15;
+                         amp += step;
+                    }
+                }
+            }
+            if (mState == STARTING || mState == IDLE) {
+                return;
+            }
+        }
+
+        // do the actual processing in the effect engine
+        (*mEffectInterface)->process(mEffectInterface, &mConfig.inputCfg.buffer, &mConfig.outputCfg.buffer);
+
+        // clear auxiliary effect input buffer for next accumulation
+        if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+            memset(mConfig.inputCfg.buffer.raw, 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
+        }
+    } else if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT &&
+                mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw){
+        // If an insert effect is idle and input buffer is different from output buffer, copy input to
+        // output
+        sp<EffectChain> chain = mChain.promote();
+        if (chain != 0 && chain->activeTracks() != 0) {
+            size_t size = mConfig.inputCfg.buffer.frameCount * sizeof(int16_t);
+            if (mConfig.inputCfg.channels == CHANNEL_STEREO) {
+                size *= 2;
+            }
+            memcpy(mConfig.outputCfg.buffer.raw, mConfig.inputCfg.buffer.raw, size);
+        }
+    }
+}
+
+void AudioFlinger::EffectModule::reset()
+{
+    if (mEffectInterface == NULL) {
+        return;
+    }
+    (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_RESET, 0, NULL, 0, NULL);
+}
+
+status_t AudioFlinger::EffectModule::configure()
+{
+    uint32_t channels;
+    if (mEffectInterface == NULL) {
+        return NO_INIT;
+    }
+
+    sp<ThreadBase> thread = mThread.promote();
+    if (thread == 0) {
+        return DEAD_OBJECT;
+    }
+
+    // TODO: handle configuration of effects replacing track process
+    if (thread->channelCount() == 1) {
+        channels = CHANNEL_MONO;
+    } else {
+        channels = CHANNEL_STEREO;
+    }
+
+    if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+        mConfig.inputCfg.channels = CHANNEL_MONO;
+    } else {
+        mConfig.inputCfg.channels = channels;
+    }
+    mConfig.outputCfg.channels = channels;
+    mConfig.inputCfg.format = PCM_FORMAT_S15;
+    mConfig.outputCfg.format = PCM_FORMAT_S15;
+    mConfig.inputCfg.samplingRate = thread->sampleRate();
+    mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;
+    mConfig.inputCfg.bufferProvider.cookie = NULL;
+    mConfig.inputCfg.bufferProvider.getBuffer = NULL;
+    mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
+    mConfig.outputCfg.bufferProvider.cookie = NULL;
+    mConfig.outputCfg.bufferProvider.getBuffer = NULL;
+    mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
+    mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+    // Insert effect:
+    // - in session 0, always overwrites output buffer: input buffer == output buffer
+    // - in other sessions:
+    //      last effect in the chain accumulates in output buffer: input buffer != output buffer
+    //      other effect: overwrites output buffer: input buffer == output buffer
+    // Auxiliary effect:
+    //      accumulates in output buffer: input buffer != output buffer
+    // Therefore: accumulate <=> input buffer != output buffer
+    if (mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) {
+        mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+    } else {
+        mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
+    }
+    mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
+    mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
+    mConfig.inputCfg.buffer.frameCount = thread->frameCount();
+    mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;
+
+    status_t cmdStatus;
+    int size = sizeof(int);
+    status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_CONFIGURE, sizeof(effect_config_t), &mConfig, &size, &cmdStatus);
+    if (status == 0) {
+        status = cmdStatus;
+    }
+    return status;
+}
+
+status_t AudioFlinger::EffectModule::init()
+{
+    if (mEffectInterface == NULL) {
+        return NO_INIT;
+    }
+    status_t cmdStatus;
+    int size = sizeof(status_t);
+    status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_INIT, 0, NULL, &size, &cmdStatus);
+    if (status == 0) {
+        status = cmdStatus;
+    }
+    return status;
+}
+
+status_t AudioFlinger::EffectModule::start()
+{
+    if (mEffectInterface == NULL) {
+        return NO_INIT;
+    }
+    status_t cmdStatus;
+    int size = sizeof(status_t);
+    status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_ENABLE, 0, NULL, &size, &cmdStatus);
+    if (status == 0) {
+        status = cmdStatus;
+    }
+    return status;
+}
+
+status_t AudioFlinger::EffectModule::stop()
+{
+    if (mEffectInterface == NULL) {
+        return NO_INIT;
+    }
+    status_t cmdStatus;
+    int size = sizeof(status_t);
+    status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_DISABLE, 0, NULL, &size, &cmdStatus);
+    if (status == 0) {
+        status = cmdStatus;
+    }
+    return status;
+}
+
+status_t AudioFlinger::EffectModule::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData)
+{
+    LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface);
+
+    if (mEffectInterface == NULL) {
+        return NO_INIT;
+    }
+    status_t status = (*mEffectInterface)->command(mEffectInterface, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+    if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) {
+        int size = (replySize == NULL) ? 0 : *replySize;
+        Mutex::Autolock _l(mLock);
+        for (size_t i = 1; i < mHandles.size(); i++) {
+            sp<EffectHandle> h = mHandles[i].promote();
+            if (h != 0) {
+                h->commandExecuted(cmdCode, cmdSize, pCmdData, size, pReplyData);
+            }
+        }
+    }
+    return status;
+}
+
+status_t AudioFlinger::EffectModule::setEnabled(bool enabled)
+{
+    Mutex::Autolock _l(mLock);
+    LOGV("setEnabled %p enabled %d", this, enabled);
+
+    if (enabled != isEnabled()) {
+        switch (mState) {
+        // going from disabled to enabled
+        case IDLE:
+            mState = RESET;
+            break;
+        case STOPPING:
+            mState = ACTIVE;
+            break;
+        case STOPPED:
+            mState = STARTING;
+            break;
+
+        // going from enabled to disabled
+        case RESET:
+            mState = IDLE;
+            break;
+        case STARTING:
+            mState = STOPPED;
+            break;
+        case ACTIVE:
+            mState = STOPPING;
+            break;
+        }
+        for (size_t i = 1; i < mHandles.size(); i++) {
+            sp<EffectHandle> h = mHandles[i].promote();
+            if (h != 0) {
+                h->setEnabled(enabled);
+            }
+        }
+    }
+    return NO_ERROR;
+}
+
+bool AudioFlinger::EffectModule::isEnabled()
+{
+    switch (mState) {
+    case RESET:
+    case STARTING:
+    case ACTIVE:
+        return true;
+    case IDLE:
+    case STOPPING:
+    case STOPPED:
+    default:
+        return false;
+    }
+}
+
+status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
+{
+    status_t status = NO_ERROR;
+
+    // Send volume indication if EFFECT_FLAG_VOLUME_IND is set and read back altered volume
+    // if controller flag is set (Note that controller == TRUE => EFFECT_FLAG_VOLUME_CTRL set)
+    if ((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) & (EFFECT_FLAG_VOLUME_CTRL|EFFECT_FLAG_VOLUME_IND)) {
+        status_t cmdStatus;
+        uint32_t volume[2];
+        uint32_t *pVolume = NULL;
+        int size = sizeof(volume);
+        volume[0] = *left;
+        volume[1] = *right;
+        if (controller) {
+            pVolume = volume;
+        }
+        status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_VOLUME, size, volume, &size, pVolume);
+        if (controller && status == NO_ERROR && size == sizeof(volume)) {
+            *left = volume[0];
+            *right = volume[1];
+        }
+    }
+    return status;
+}
+
+status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
+{
+    status_t status = NO_ERROR;
+    if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_MASK) {
+        status_t cmdStatus;
+        int size = sizeof(status_t);
+        status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_DEVICE, sizeof(uint32_t), &device, &size, &cmdStatus);
+        if (status == NO_ERROR) {
+            status = cmdStatus;
+        }
+    }
+    return status;
+}
+
+
+status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, "\tEffect ID %d:\n", mId);
+    result.append(buffer);
+
+    bool locked = tryLock(mLock);
+    // failed to lock - AudioFlinger is probably deadlocked
+    if (!locked) {
+        result.append("\t\tCould not lock Fx mutex:\n");
+    }
+
+    result.append("\t\tSession Status State Engine:\n");
+    snprintf(buffer, SIZE, "\t\t%05d   %03d    %03d   0x%08x\n",
+            mSessionId, mStatus, mState, (uint32_t)mEffectInterface);
+    result.append(buffer);
+
+    result.append("\t\tDescriptor:\n");
+    snprintf(buffer, SIZE, "\t\t- UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
+            mDescriptor.uuid.timeLow, mDescriptor.uuid.timeMid, mDescriptor.uuid.timeHiAndVersion,
+            mDescriptor.uuid.clockSeq, mDescriptor.uuid.node[0], mDescriptor.uuid.node[1],mDescriptor.uuid.node[2],
+            mDescriptor.uuid.node[3],mDescriptor.uuid.node[4],mDescriptor.uuid.node[5]);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\t\t- TYPE: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
+                mDescriptor.type.timeLow, mDescriptor.type.timeMid, mDescriptor.type.timeHiAndVersion,
+                mDescriptor.type.clockSeq, mDescriptor.type.node[0], mDescriptor.type.node[1],mDescriptor.type.node[2],
+                mDescriptor.type.node[3],mDescriptor.type.node[4],mDescriptor.type.node[5]);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\t\t- apiVersion: %04X\n\t\t- flags: %08X\n",
+            mDescriptor.apiVersion,
+            mDescriptor.flags);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\t\t- name: %s\n",
+            mDescriptor.name);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\t\t- implementor: %s\n",
+            mDescriptor.implementor);
+    result.append(buffer);
+
+    result.append("\t\t- Input configuration:\n");
+    result.append("\t\t\tBuffer     Frames  Smp rate Channels Format\n");
+    snprintf(buffer, SIZE, "\t\t\t0x%08x %05d   %05d    %08x %d\n",
+            (uint32_t)mConfig.inputCfg.buffer.raw,
+            mConfig.inputCfg.buffer.frameCount,
+            mConfig.inputCfg.samplingRate,
+            mConfig.inputCfg.channels,
+            mConfig.inputCfg.format);
+    result.append(buffer);
+
+    result.append("\t\t- Output configuration:\n");
+    result.append("\t\t\tBuffer     Frames  Smp rate Channels Format\n");
+    snprintf(buffer, SIZE, "\t\t\t0x%08x %05d   %05d    %08x %d\n",
+            (uint32_t)mConfig.outputCfg.buffer.raw,
+            mConfig.outputCfg.buffer.frameCount,
+            mConfig.outputCfg.samplingRate,
+            mConfig.outputCfg.channels,
+            mConfig.outputCfg.format);
+    result.append(buffer);
+
+    snprintf(buffer, SIZE, "\t\t%d Clients:\n", mHandles.size());
+    result.append(buffer);
+    result.append("\t\t\tPid   Priority Ctrl Locked client server\n");
+    for (size_t i = 0; i < mHandles.size(); ++i) {
+        sp<EffectHandle> handle = mHandles[i].promote();
+        if (handle != 0) {
+            handle->dump(buffer, SIZE);
+            result.append(buffer);
+        }
+    }
+
+    result.append("\n");
+
+    write(fd, result.string(), result.length());
+
+    if (locked) {
+        mLock.unlock();
+    }
+
+    return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+//  EffectHandle implementation
+// ----------------------------------------------------------------------------
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectHandle"
+
+AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect,
+                                        const sp<AudioFlinger::Client>& client,
+                                        const sp<IEffectClient>& effectClient,
+                                        int32_t priority)
+    : BnEffect(),
+    mEffect(effect), mEffectClient(effectClient), mClient(client), mPriority(priority), mHasControl(false)
+{
+    LOGV("constructor %p", this);
+
+    int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
+    mCblkMemory = client->heap()->allocate(EFFECT_PARAM_BUFFER_SIZE + bufOffset);
+    if (mCblkMemory != 0) {
+        mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer());
+
+        if (mCblk) {
+            new(mCblk) effect_param_cblk_t();
+            mBuffer = (uint8_t *)mCblk + bufOffset;
+         }
+    } else {
+        LOGE("not enough memory for Effect size=%u", EFFECT_PARAM_BUFFER_SIZE + sizeof(effect_param_cblk_t));
+        return;
+    }
+}
+
+AudioFlinger::EffectHandle::~EffectHandle()
+{
+    LOGV("Destructor %p", this);
+    disconnect();
+}
+
+status_t AudioFlinger::EffectHandle::enable()
+{
+    if (!mHasControl) return INVALID_OPERATION;
+    if (mEffect == 0) return DEAD_OBJECT;
+
+    return mEffect->setEnabled(true);
+}
+
+status_t AudioFlinger::EffectHandle::disable()
+{
+    if (!mHasControl) return INVALID_OPERATION;
+    if (mEffect == NULL) return DEAD_OBJECT;
+
+    return mEffect->setEnabled(false);
+}
+
+void AudioFlinger::EffectHandle::disconnect()
+{
+    if (mEffect == 0) {
+        return;
+    }
+    mEffect->disconnect(this);
+    // release sp on module => module destructor can be called now
+    mEffect.clear();
+    if (mCblk) {
+        mCblk->~effect_param_cblk_t();   // destroy our shared-structure.
+    }
+    mCblkMemory.clear();            // and free the shared memory
+    if (mClient != 0) {
+        Mutex::Autolock _l(mClient->audioFlinger()->mLock);
+        mClient.clear();
+    }
+}
+
+status_t AudioFlinger::EffectHandle::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData)
+{
+    LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p", cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get());
+
+    // only get parameter command is permitted for applications not controlling the effect
+    if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) {
+        return INVALID_OPERATION;
+    }
+    if (mEffect == 0) return DEAD_OBJECT;
+
+    // handle commands that are not forwarded transparently to effect engine
+    if (cmdCode == EFFECT_CMD_SET_PARAM_COMMIT) {
+        // No need to trylock() here as this function is executed in the binder thread serving a particular client process:
+        // no risk to block the whole media server process or mixer threads is we are stuck here
+        Mutex::Autolock _l(mCblk->lock);
+        if (mCblk->clientIndex > EFFECT_PARAM_BUFFER_SIZE ||
+            mCblk->serverIndex > EFFECT_PARAM_BUFFER_SIZE) {
+            mCblk->serverIndex = 0;
+            mCblk->clientIndex = 0;
+            return BAD_VALUE;
+        }
+        status_t status = NO_ERROR;
+        while (mCblk->serverIndex < mCblk->clientIndex) {
+            int reply;
+            int rsize = sizeof(int);
+            int *p = (int *)(mBuffer + mCblk->serverIndex);
+            int size = *p++;
+            effect_param_t *param = (effect_param_t *)p;
+            int psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
+            status_t ret = mEffect->command(EFFECT_CMD_SET_PARAM, psize, p, &rsize, &reply);
+            if (ret == NO_ERROR) {
+                if (reply != NO_ERROR) {
+                    status = reply;
+                }
+            } else {
+                status = ret;
+            }
+            mCblk->serverIndex += size;
+        }
+        mCblk->serverIndex = 0;
+        mCblk->clientIndex = 0;
+        return status;
+    } else if (cmdCode == EFFECT_CMD_ENABLE) {
+        return enable();
+    } else if (cmdCode == EFFECT_CMD_DISABLE) {
+        return disable();
+    }
+
+    return mEffect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+}
+
+sp<IMemory> AudioFlinger::EffectHandle::getCblk() const {
+    return mCblkMemory;
+}
+
+void AudioFlinger::EffectHandle::setControl(bool hasControl, bool signal)
+{
+    LOGV("setControl %p control %d", this, hasControl);
+
+    mHasControl = hasControl;
+    if (signal && mEffectClient != 0) {
+        mEffectClient->controlStatusChanged(hasControl);
+    }
+}
+
+void AudioFlinger::EffectHandle::commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData)
+{
+    if (mEffectClient != 0) {
+        mEffectClient->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+    }
+}
+
+
+
+void AudioFlinger::EffectHandle::setEnabled(bool enabled)
+{
+    if (mEffectClient != 0) {
+        mEffectClient->enableStatusChanged(enabled);
+    }
+}
+
+status_t AudioFlinger::EffectHandle::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    return BnEffect::onTransact(code, data, reply, flags);
+}
+
+
+void AudioFlinger::EffectHandle::dump(char* buffer, size_t size)
+{
+    bool locked = tryLock(mCblk->lock);
+
+    snprintf(buffer, size, "\t\t\t%05d %05d    %01u    %01u      %05u  %05u\n",
+            (mClient == NULL) ? getpid() : mClient->pid(),
+            mPriority,
+            mHasControl,
+            !locked,
+            mCblk->clientIndex,
+            mCblk->serverIndex
+            );
+
+    if (locked) {
+        mCblk->lock.unlock();
+    }
+}
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectChain"
+
+AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread,
+                                        int sessionId)
+    : mThread(wThread), mSessionId(sessionId), mVolumeCtrlIdx(-1), mActiveTrackCnt(0), mOwnInBuffer(false)
+{
+
+}
+
+AudioFlinger::EffectChain::~EffectChain()
+{
+    if (mOwnInBuffer) {
+        delete mInBuffer;
+    }
+
+}
+
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromDesc(effect_descriptor_t *descriptor)
+{
+    sp<EffectModule> effect;
+    size_t size = mEffects.size();
+
+    for (size_t i = 0; i < size; i++) {
+        if (memcmp(&mEffects[i]->desc().uuid, &descriptor->uuid, sizeof(effect_uuid_t)) == 0) {
+            effect = mEffects[i];
+            break;
+        }
+    }
+    return effect;
+}
+
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromId(int id)
+{
+    sp<EffectModule> effect;
+    size_t size = mEffects.size();
+
+    for (size_t i = 0; i < size; i++) {
+        if (mEffects[i]->id() == id) {
+            effect = mEffects[i];
+            break;
+        }
+    }
+    return effect;
+}
+
+// Must be called with EffectChain::mLock locked
+void AudioFlinger::EffectChain::process_l()
+{
+    size_t size = mEffects.size();
+    for (size_t i = 0; i < size; i++) {
+        mEffects[i]->process();
+    }
+    // if no track is active, input buffer must be cleared here as the mixer process
+    // will not do it
+    if (mSessionId != 0 && activeTracks() == 0) {
+        sp<ThreadBase> thread = mThread.promote();
+        if (thread != 0) {
+            size_t numSamples = thread->frameCount() * thread->channelCount();
+            memset(mInBuffer, 0, numSamples * sizeof(int16_t));
+        }
+    }
+}
+
+status_t AudioFlinger::EffectChain::addEffect(sp<EffectModule>& effect)
+{
+    effect_descriptor_t desc = effect->desc();
+    uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;
+
+    Mutex::Autolock _l(mLock);
+
+    if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+        // Auxiliary effects are inserted at the beginning of mEffects vector as
+        // they are processed first and accumulated in chain input buffer
+        mEffects.insertAt(effect, 0);
+        sp<ThreadBase> thread = mThread.promote();
+        if (thread == 0) {
+            return NO_INIT;
+        }
+        // the input buffer for auxiliary effect contains mono samples in
+        // 32 bit format. This is to avoid saturation in AudoMixer
+        // accumulation stage. Saturation is done in EffectModule::process() before
+        // calling the process in effect engine
+        size_t numSamples = thread->frameCount();
+        int32_t *buffer = new int32_t[numSamples];
+        memset(buffer, 0, numSamples * sizeof(int32_t));
+        effect->setInBuffer((int16_t *)buffer);
+        // auxiliary effects output samples to chain input buffer for further processing
+        // by insert effects
+        effect->setOutBuffer(mInBuffer);
+    } else {
+        // Insert effects are inserted at the end of mEffects vector as they are processed
+        //  after track and auxiliary effects.
+        // Insert effect order:
+        //  if EFFECT_FLAG_INSERT_FIRST or EFFECT_FLAG_INSERT_EXCLUSIVE insert as first insert effect
+        //  else if EFFECT_FLAG_INSERT_ANY insert after first or before last
+        //  else insert as last insert effect
+        // Reject insertion if:
+        //  - EFFECT_FLAG_INSERT_EXCLUSIVE and another effect is present
+        //  - an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is present
+        //  - EFFECT_FLAG_INSERT_FIRST or EFFECT_FLAG_INSERT_LAST and an effect with same
+        //  preference is present
+
+        int size = (int)mEffects.size();
+        int idx_insert = size;
+        int idx_insert_first = -1;
+        int idx_insert_last = -1;
+
+        for (int i = 0; i < size; i++) {
+            effect_descriptor_t d = mEffects[i]->desc();
+            uint32_t iMode = d.flags & EFFECT_FLAG_TYPE_MASK;
+            uint32_t iPref = d.flags & EFFECT_FLAG_INSERT_MASK;
+            if (iMode == EFFECT_FLAG_TYPE_INSERT) {
+                // check invalid effect chaining combinations
+                if (insertPref == EFFECT_FLAG_INSERT_EXCLUSIVE ||
+                    iPref == EFFECT_FLAG_INSERT_EXCLUSIVE ||
+                    (insertPref != EFFECT_FLAG_INSERT_ANY
+                                && insertPref == iPref)) {
+                    return INVALID_OPERATION;
+                }
+                // remember position of first insert effect
+                if (idx_insert == size) {
+                    idx_insert = i;
+                }
+                // remember position of insert effect claiming
+                // first place
+                if (iPref == EFFECT_FLAG_INSERT_FIRST) {
+                    idx_insert_first = i;
+                }
+                // remember position of insert effect claiming
+                // last place
+                if (iPref == EFFECT_FLAG_INSERT_LAST) {
+                    idx_insert_last = i;
+                }
+            }
+        }
+
+        // modify idx_insert from first place if needed
+        if (idx_insert_first != -1) {
+            idx_insert = idx_insert_first + 1;
+        } else if (idx_insert_last != -1) {
+            idx_insert = idx_insert_last;
+        } else if (insertPref == EFFECT_FLAG_INSERT_LAST) {
+            idx_insert = size;
+        }
+
+        // always read samples from chain input buffer
+        effect->setInBuffer(mInBuffer);
+
+        // if last effect in the chain, output samples to chain
+        // output buffer, otherwise to chain input buffer
+        if (idx_insert == size) {
+            if (idx_insert != 0) {
+                mEffects[idx_insert-1]->setOutBuffer(mInBuffer);
+                mEffects[idx_insert-1]->configure();
+            }
+            effect->setOutBuffer(mOutBuffer);
+        } else {
+            effect->setOutBuffer(mInBuffer);
+        }
+        status_t status = mEffects.insertAt(effect, idx_insert);
+        // Always give volume control to last effect in chain with volume control capability
+        if (((desc.flags & EFFECT_FLAG_VOLUME_MASK) & EFFECT_FLAG_VOLUME_CTRL) &&
+                mVolumeCtrlIdx < idx_insert) {
+            mVolumeCtrlIdx = idx_insert;
+        }
+
+        LOGV("addEffect() effect %p, added in chain %p at rank %d status %d", effect.get(), this, idx_insert, status);
+    }
+    effect->configure();
+    return NO_ERROR;
+}
+
+size_t AudioFlinger::EffectChain::removeEffect(const sp<EffectModule>& effect)
+{
+    Mutex::Autolock _l(mLock);
+
+    int size = (int)mEffects.size();
+    int i;
+    uint32_t type = effect->desc().flags & EFFECT_FLAG_TYPE_MASK;
+
+    for (i = 0; i < size; i++) {
+        if (effect == mEffects[i]) {
+            if (type == EFFECT_FLAG_TYPE_AUXILIARY) {
+                delete[] effect->inBuffer();
+            } else {
+                if (i == size - 1 && i != 0) {
+                    mEffects[i - 1]->setOutBuffer(mOutBuffer);
+                    mEffects[i - 1]->configure();
+                }
+            }
+            mEffects.removeAt(i);
+            LOGV("removeEffect() effect %p, removed from chain %p at rank %d", effect.get(), this, i);
+            break;
+        }
+    }
+    // Return volume control to last effect in chain with volume control capability
+    if (mVolumeCtrlIdx == i) {
+        size = (int)mEffects.size();
+        for (i = size; i > 0; i--) {
+            if ((mEffects[i - 1]->desc().flags & EFFECT_FLAG_VOLUME_MASK) & EFFECT_FLAG_VOLUME_CTRL) {
+                break;
+            }
+        }
+        // mVolumeCtrlIdx reset to -1 if no effect found with volume control flag set
+        mVolumeCtrlIdx = i - 1;
+    }
+
+    return mEffects.size();
+}
+
+void AudioFlinger::EffectChain::setDevice(uint32_t device)
+{
+    size_t size = mEffects.size();
+    for (size_t i = 0; i < size; i++) {
+        mEffects[i]->setDevice(device);
+    }
+}
+
+bool AudioFlinger::EffectChain::setVolume(uint32_t *left, uint32_t *right)
+{
+    uint32_t newLeft = *left;
+    uint32_t newRight = *right;
+    bool hasControl = false;
+
+    // first get volume update from volume controller
+    if (mVolumeCtrlIdx >= 0) {
+        mEffects[mVolumeCtrlIdx]->setVolume(&newLeft, &newRight, true);
+        hasControl = true;
+    }
+    // then indicate volume to all other effects in chain.
+    // Pass altered volume to effects before volume controller
+    // and requested volume to effects after controller
+    uint32_t lVol = newLeft;
+    uint32_t rVol = newRight;
+    size_t size = mEffects.size();
+    for (size_t i = 0; i < size; i++) {
+        if ((int)i == mVolumeCtrlIdx) continue;
+        // this also works for mVolumeCtrlIdx == -1 when there is no volume controller
+        if ((int)i > mVolumeCtrlIdx) {
+            lVol = *left;
+            rVol = *right;
+        }
+        mEffects[i]->setVolume(&lVol, &rVol, false);
+    }
+    *left = newLeft;
+    *right = newRight;
+
+    return hasControl;
+}
+
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getVolumeController()
+{
+    sp<EffectModule> effect;
+    if (mVolumeCtrlIdx >= 0) {
+        effect = mEffects[mVolumeCtrlIdx];
+    }
+    return effect;
+}
+
+
+status_t AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, "Effects for session %d:\n", mSessionId);
+    result.append(buffer);
+
+    bool locked = tryLock(mLock);
+    // failed to lock - AudioFlinger is probably deadlocked
+    if (!locked) {
+        result.append("\tCould not lock mutex:\n");
+    }
+
+    result.append("\tNum fx In buffer   Out buffer   Vol ctrl Active tracks:\n");
+    snprintf(buffer, SIZE, "\t%02d     0x%08x  0x%08x   %02d       %d\n",
+            mEffects.size(),
+            (uint32_t)mInBuffer,
+            (uint32_t)mOutBuffer,
+            (mVolumeCtrlIdx == -1) ? 0 : mEffects[mVolumeCtrlIdx]->id(),
+            mActiveTrackCnt);
+    result.append(buffer);
+    write(fd, result.string(), result.size());
+
+    for (size_t i = 0; i < mEffects.size(); ++i) {
+        sp<EffectModule> effect = mEffects[i];
+        if (effect != 0) {
+            effect->dump(fd, args);
+        }
+    }
+
+    if (locked) {
+        mLock.unlock();
+    }
+
+    return NO_ERROR;
+}
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger"
+
 // ----------------------------------------------------------------------------
 
 status_t AudioFlinger::onTransact(
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index f35f38b..e543334 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -42,6 +42,7 @@
 namespace android {
 
 class audio_track_cblk_t;
+class effect_param_cblk_t;
 class AudioMixer;
 class AudioBuffer;
 class AudioResampler;
@@ -75,6 +76,7 @@
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
                                 int output,
+                                int *sessionId,
                                 status_t *status);
 
     virtual     uint32_t    sampleRate(int output) const;
@@ -139,6 +141,28 @@
 
     virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output);
 
+    virtual int newAudioSessionId();
+
+    virtual status_t loadEffectLibrary(const char *libPath, int *handle);
+
+    virtual status_t unloadEffectLibrary(int handle);
+
+    virtual status_t queryNumberEffects(uint32_t *numEffects);
+
+    virtual status_t queryNextEffect(effect_descriptor_t *descriptor);
+
+    virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor);
+
+    virtual sp<IEffect> createEffect(pid_t pid,
+                        effect_descriptor_t *pDesc,
+                        const sp<IEffectClient>& effectClient,
+                        int32_t priority,
+                        int output,
+                        int sessionId,
+                        status_t *status,
+                        int *id,
+                        int *enabled);
+
     enum hardware_call_state {
         AUDIO_HW_IDLE = 0,
         AUDIO_HW_INIT,
@@ -167,6 +191,7 @@
                                 int channelCount,
                                 int frameCount,
                                 uint32_t flags,
+                                int *sessionId,
                                 status_t *status);
 
     virtual     status_t    onTransact(
@@ -233,6 +258,9 @@
     class DuplicatingThread;
     class Track;
     class RecordTrack;
+    class EffectModule;
+    class EffectHandle;
+    class EffectChain;
 
     class ThreadBase : public Thread {
     public:
@@ -268,13 +296,15 @@
                                         int channelCount,
                                         int frameCount,
                                         uint32_t flags,
-                                        const sp<IMemory>& sharedBuffer);
+                                        const sp<IMemory>& sharedBuffer,
+                                        int sessionId);
                                 ~TrackBase();
 
             virtual status_t    start() = 0;
             virtual void        stop() = 0;
                     sp<IMemory> getCblk() const;
                     audio_track_cblk_t* cblk() const { return mCblk; }
+                    int         sessionId() { return mSessionId; }
 
         protected:
             friend class ThreadBase;
@@ -323,6 +353,7 @@
             int                 mClientTid;
             uint8_t             mFormat;
             uint32_t            mFlags;
+            int                 mSessionId;
         };
 
         class ConfigEvent {
@@ -405,7 +436,8 @@
                                         int format,
                                         int channelCount,
                                         int frameCount,
-                                        const sp<IMemory>& sharedBuffer);
+                                        const sp<IMemory>& sharedBuffer,
+                                        int sessionId);
                                 ~Track();
 
                     void        dump(char* buffer, size_t size);
@@ -424,6 +456,12 @@
                     int type() const {
                         return mStreamType;
                     }
+                    status_t    attachAuxEffect(int EffectId);
+                    void        setAuxBuffer(int EffectId, int32_t *buffer);
+                    int32_t     *auxBuffer() { return mAuxBuffer; }
+                    void        setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }
+                    int16_t     *mainBuffer() { return mMainBuffer; }
+                    int         auxEffectId() { return mAuxEffectId; }
 
 
         protected:
@@ -464,6 +502,9 @@
             bool                mResetDone;
             int                 mStreamType;
             int                 mName;
+            int16_t             *mMainBuffer;
+            int32_t             *mAuxBuffer;
+            int                 mAuxEffectId;
         };  // end of Track
 
 
@@ -505,7 +546,7 @@
             DuplicatingThread*          mSourceThread;
         };  // end of OutputTrack
 
-        PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+        PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
         virtual             ~PlaybackThread();
 
         virtual     status_t    dump(int fd, const Vector<String16>& args);
@@ -538,6 +579,7 @@
                                     int channelCount,
                                     int frameCount,
                                     const sp<IMemory>& sharedBuffer,
+                                    int sessionId,
                                     status_t *status);
 
                     AudioStreamOut* getOutput() { return mOutput; }
@@ -549,6 +591,29 @@
         virtual     String8     getParameters(const String8& keys);
         virtual     void        audioConfigChanged_l(int event, int param = 0);
         virtual     status_t    getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
+                    int16_t     *mixBuffer() { return mMixBuffer; };
+
+                    sp<EffectHandle> createEffect_l(
+                                        const sp<AudioFlinger::Client>& client,
+                                        const sp<IEffectClient>& effectClient,
+                                        int32_t priority,
+                                        int sessionId,
+                                        effect_descriptor_t *desc,
+                                        int *enabled,
+                                        status_t *status);
+
+                    bool hasAudioSession(int sessionId);
+                    sp<EffectChain> getEffectChain(int sessionId);
+                    sp<EffectChain> getEffectChain_l(int sessionId);
+                    status_t addEffectChain_l(const sp<EffectChain>& chain);
+                    size_t removeEffectChain_l(const sp<EffectChain>& chain);
+                    void lockEffectChains_l();
+                    void unlockEffectChains();
+
+                    sp<AudioFlinger::EffectModule> getEffect_l(int sessionId, int effectId);
+                    void detachAuxEffect_l(int effectId);
+                    status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId);
+                    status_t attachAuxEffect_l(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId);
 
         struct  stream_type_t {
             stream_type_t()
@@ -591,8 +656,11 @@
 
         void        readOutputParameters();
 
+        uint32_t    device() { return mDevice; }
+
         virtual status_t    dumpInternals(int fd, const Vector<String16>& args);
         status_t    dumpTracks(int fd, const Vector<String16>& args);
+        status_t    dumpEffectChains(int fd, const Vector<String16>& args);
 
         SortedVector< sp<Track> >       mTracks;
         // mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread
@@ -603,11 +671,13 @@
         int                             mNumWrites;
         int                             mNumDelayedWrites;
         bool                            mInWrite;
+        Vector< sp<EffectChain> >       mEffectChains;
+        uint32_t                        mDevice;
     };
 
     class MixerThread : public PlaybackThread {
     public:
-        MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+        MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
         virtual             ~MixerThread();
 
         // Thread virtuals
@@ -630,7 +700,7 @@
     class DirectOutputThread : public PlaybackThread {
     public:
 
-        DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+        DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
         ~DirectOutputThread();
 
         // Thread virtuals
@@ -645,8 +715,12 @@
         virtual     uint32_t    idleSleepTimeUs();
 
     private:
-        float mLeftVolume;
-        float mRightVolume;
+        void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
+
+        float mLeftVolFloat;
+        float mRightVolFloat;
+        uint16_t mLeftVolShort;
+        uint16_t mRightVolShort;
     };
 
     class DuplicatingThread : public MixerThread {
@@ -676,6 +750,8 @@
               float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; }
               void audioConfigChanged_l(int event, int ioHandle, void *param2);
 
+              int  nextUniqueId();
+
     friend class AudioBuffer;
 
     class TrackHandle : public android::BnAudioTrack {
@@ -689,6 +765,7 @@
         virtual void        pause();
         virtual void        setVolume(float left, float right);
         virtual sp<IMemory> getCblk() const;
+        virtual status_t    attachAuxEffect(int effectId);
         virtual status_t onTransact(
             uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
     private:
@@ -717,7 +794,8 @@
                                         int format,
                                         int channelCount,
                                         int frameCount,
-                                        uint32_t flags);
+                                        uint32_t flags,
+                                        int sessionId);
                                 ~RecordTrack();
 
             virtual status_t    start();
@@ -792,6 +870,215 @@
         sp<RecordThread::RecordTrack> mRecordTrack;
     };
 
+    //--- Audio Effect Management
+
+    // EffectModule and EffectChain classes both have their own mutex to protect
+    // state changes or resource modifications. Always respect the following order
+    // if multiple mutexes must be acquired to avoid cross deadlock:
+    // AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
+
+    // The EffectModule class is a wrapper object controlling the effect engine implementation
+    // in the effect library. It prevents concurrent calls to process() and command() functions
+    // from different client threads. It keeps a list of EffectHandle objects corresponding
+    // to all client applications using this effect and notifies applications of effect state,
+    // control or parameter changes. It manages the activation state machine to send appropriate
+    // reset, enable, disable commands to effect engine and provide volume
+    // ramping when effects are activated/deactivated.
+    // When controlling an auxiliary effect, the EffectModule also provides an input buffer used by
+    // the attached track(s) to accumulate their auxiliary channel.
+    class EffectModule: public RefBase {
+    public:
+        EffectModule(const wp<ThreadBase>& wThread,
+                        const wp<AudioFlinger::EffectChain>& chain,
+                        effect_descriptor_t *desc,
+                        int id,
+                        int sessionId);
+        ~EffectModule();
+
+        enum effect_state {
+            IDLE,
+            RESET,
+            STARTING,
+            ACTIVE,
+            STOPPING,
+            STOPPED
+        };
+
+        int         id() { return mId; }
+        void process();
+        status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
+
+        void reset();
+        status_t configure();
+        status_t init();
+        uint32_t state() {
+            return mState;
+        }
+        uint32_t status() {
+            return mStatus;
+        }
+        status_t    setEnabled(bool enabled);
+        bool isEnabled();
+
+        void        setInBuffer(int16_t *buffer) { mConfig.inputCfg.buffer.s16 = buffer; }
+        int16_t     *inBuffer() { return mConfig.inputCfg.buffer.s16; }
+        void        setOutBuffer(int16_t *buffer) { mConfig.outputCfg.buffer.s16 = buffer; }
+        int16_t     *outBuffer() { return mConfig.outputCfg.buffer.s16; }
+
+        status_t addHandle(sp<EffectHandle>& handle);
+        void disconnect(const wp<EffectHandle>& handle);
+        size_t removeHandle (const wp<EffectHandle>& handle);
+
+        effect_descriptor_t& desc() { return mDescriptor; }
+
+        status_t         setDevice(uint32_t device);
+        status_t         setVolume(uint32_t *left, uint32_t *right, bool controller);
+
+        status_t         dump(int fd, const Vector<String16>& args);
+
+    protected:
+
+        EffectModule(const EffectModule&);
+        EffectModule& operator = (const EffectModule&);
+
+        status_t start();
+        status_t stop();
+
+        Mutex               mLock;      // mutex for process, commands and handles list protection
+        wp<ThreadBase>      mThread;    // parent thread
+        wp<EffectChain>     mChain;     // parent effect chain
+        int                 mId;        // this instance unique ID
+        int                 mSessionId; // audio session ID
+        effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
+        effect_config_t     mConfig;    // input and output audio configuration
+        effect_interface_t  mEffectInterface; // Effect module C API
+        status_t mStatus;               // initialization status
+        uint32_t mState;                // current activation state (effect_state)
+        Vector< wp<EffectHandle> > mHandles;    // list of client handles
+    };
+
+    // The EffectHandle class implements the IEffect interface. It provides resources
+    // to receive parameter updates, keeps track of effect control
+    // ownership and state and has a pointer to the EffectModule object it is controlling.
+    // There is one EffectHandle object for each application controlling (or using)
+    // an effect module.
+    // The EffectHandle is obtained by calling AudioFlinger::createEffect().
+    class EffectHandle: public android::BnEffect {
+    public:
+
+        EffectHandle(const sp<EffectModule>& effect,
+                const sp<AudioFlinger::Client>& client,
+                const sp<IEffectClient>& effectClient,
+                int32_t priority);
+        virtual ~EffectHandle();
+
+        // IEffect
+        virtual status_t enable();
+        virtual status_t disable();
+        virtual status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
+        virtual void disconnect();
+        virtual sp<IMemory> getCblk() const;
+        virtual status_t onTransact(uint32_t code, const Parcel& data,
+                Parcel* reply, uint32_t flags);
+
+
+        // Give or take control of effect module
+        void setControl(bool hasControl, bool signal);
+        void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData);
+        void setEnabled(bool enabled);
+
+        // Getters
+        int id() { return mEffect->id(); }
+        int priority() { return mPriority; }
+        bool hasControl() { return mHasControl; }
+        sp<EffectModule> effect() { return mEffect; }
+
+        void dump(char* buffer, size_t size);
+
+    protected:
+
+        EffectHandle(const EffectHandle&);
+        EffectHandle& operator =(const EffectHandle&);
+
+        sp<EffectModule> mEffect;           // pointer to controlled EffectModule
+        sp<IEffectClient> mEffectClient;    // callback interface for client notifications
+        sp<Client>          mClient;        // client for shared memory allocation
+        sp<IMemory>         mCblkMemory;    // shared memory for control block
+        effect_param_cblk_t* mCblk;         // control block for deferred parameter setting via shared memory
+        uint8_t*            mBuffer;        // pointer to parameter area in shared memory
+        int mPriority;                      // client application priority to control the effect
+        bool mHasControl;                   // true if this handle is controlling the effect
+    };
+
+    // the EffectChain class represents a group of effects associated to one audio session.
+    // There can be any number of EffectChain objects per output mixer thread (PlaybackThread).
+    // The EffecChain with session ID 0 contains global effects applied to the output mix.
+    // Effects in this chain can be insert or auxiliary. Effects in other chains (attached to tracks)
+    // are insert only. The EffectChain maintains an ordered list of effect module, the order corresponding
+    // in the effect process order. When attached to a track (session ID != 0), it also provide it's own
+    // input buffer used by the track as accumulation buffer.
+    class EffectChain: public RefBase {
+    public:
+        EffectChain(const wp<ThreadBase>& wThread, int sessionId);
+        ~EffectChain();
+
+        void process_l();
+
+        void lock() {
+            mLock.lock();
+        }
+        void unlock() {
+            mLock.unlock();
+        }
+
+        status_t addEffect(sp<EffectModule>& handle);
+        size_t removeEffect(const sp<EffectModule>& handle);
+
+        int sessionId() {
+            return mSessionId;
+        }
+        sp<EffectModule> getEffectFromDesc(effect_descriptor_t *descriptor);
+        sp<EffectModule> getEffectFromId(int id);
+        sp<EffectModule> getVolumeController();
+        bool setVolume(uint32_t *left, uint32_t *right);
+        void setDevice(uint32_t device);
+
+        void setInBuffer(int16_t *buffer, bool ownsBuffer = false) {
+            mInBuffer = buffer;
+            mOwnInBuffer = ownsBuffer;
+        }
+        int16_t *inBuffer() {
+            return mInBuffer;
+        }
+        void setOutBuffer(int16_t *buffer) {
+            mOutBuffer = buffer;
+        }
+        int16_t *outBuffer() {
+            return mOutBuffer;
+        }
+
+        void startTrack() {mActiveTrackCnt++;}
+        void stopTrack() {mActiveTrackCnt--;}
+        int activeTracks() { return mActiveTrackCnt;}
+
+        status_t dump(int fd, const Vector<String16>& args);
+
+    protected:
+
+        EffectChain(const EffectChain&);
+        EffectChain& operator =(const EffectChain&);
+
+        wp<ThreadBase> mThread;     // parent mixer thread
+        Mutex mLock;                // mutex protecting effect list
+        Vector<sp<EffectModule> > mEffects; // list of effect modules
+        int mSessionId;             // audio session ID
+        int16_t *mInBuffer;         // chain input buffer
+        int16_t *mOutBuffer;        // chain output buffer
+        int mVolumeCtrlIdx;         // index of insert effect having control over volume
+        int mActiveTrackCnt;        // number of active tracks connected
+        bool mOwnInBuffer;          // true if the chain owns its input buffer
+    };
+
     friend class RecordThread;
     friend class PlaybackThread;
 
@@ -813,7 +1100,7 @@
                 DefaultKeyedVector< int, sp<RecordThread> >    mRecordThreads;
 
                 DefaultKeyedVector< pid_t, sp<NotificationClient> >    mNotificationClients;
-                int                                 mNextThreadId;
+                volatile int32_t                    mNextUniqueId;
 #ifdef LVMX
                 int mLifeVibesClientPid;
 #endif
diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp
index 19a442a8..8aaa325 100644
--- a/libs/audioflinger/AudioMixer.cpp
+++ b/libs/audioflinger/AudioMixer.cpp
@@ -56,6 +56,8 @@
         t->volume[1] = UNITY_GAIN;
         t->volumeInc[0] = 0;
         t->volumeInc[1] = 0;
+        t->auxLevel = 0;
+        t->auxInc = 0;
         t->channelCount = 2;
         t->enabled = 0;
         t->format = 16;
@@ -65,6 +67,8 @@
         t->resampler = 0;
         t->sampleRate = mSampleRate;
         t->in = 0;
+        t->mainBuffer = NULL;
+        t->auxBuffer = NULL;
         t++;
     }
 }
@@ -169,28 +173,48 @@
     return NO_ERROR;
 }
 
-status_t AudioMixer::setParameter(int target, int name, int value)
+status_t AudioMixer::setParameter(int target, int name, void *value)
 {
+    int valueInt = (int)value;
+    int32_t *valueBuf = (int32_t *)value;
+
     switch (target) {
     case TRACK:
         if (name == CHANNEL_COUNT) {
-            if ((uint32_t(value) <= MAX_NUM_CHANNELS) && (value)) {
-                if (mState.tracks[ mActiveTrack ].channelCount != value) {
-                    mState.tracks[ mActiveTrack ].channelCount = value;
-                    LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", value);
+            if ((uint32_t(valueInt) <= MAX_NUM_CHANNELS) && (valueInt)) {
+                if (mState.tracks[ mActiveTrack ].channelCount != valueInt) {
+                    mState.tracks[ mActiveTrack ].channelCount = valueInt;
+                    LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", valueInt);
                     invalidateState(1<<mActiveTrack);
                 }
                 return NO_ERROR;
             }
         }
+        if (name == MAIN_BUFFER) {
+            if (mState.tracks[ mActiveTrack ].mainBuffer != valueBuf) {
+                mState.tracks[ mActiveTrack ].mainBuffer = valueBuf;
+                LOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
+                invalidateState(1<<mActiveTrack);
+            }
+            return NO_ERROR;
+        }
+        if (name == AUX_BUFFER) {
+            if (mState.tracks[ mActiveTrack ].auxBuffer != valueBuf) {
+                mState.tracks[ mActiveTrack ].auxBuffer = valueBuf;
+                LOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf);
+                invalidateState(1<<mActiveTrack);
+            }
+            return NO_ERROR;
+        }
+
         break;
     case RESAMPLE:
         if (name == SAMPLE_RATE) {
-            if (value > 0) {
+            if (valueInt > 0) {
                 track_t& track = mState.tracks[ mActiveTrack ];
-                if (track.setResampler(uint32_t(value), mSampleRate)) {
+                if (track.setResampler(uint32_t(valueInt), mSampleRate)) {
                     LOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)",
-                            uint32_t(value));
+                            uint32_t(valueInt));
                     invalidateState(1<<mActiveTrack);
                 }
                 return NO_ERROR;
@@ -201,18 +225,39 @@
     case VOLUME:
         if ((uint32_t(name-VOLUME0) < MAX_NUM_CHANNELS)) {
             track_t& track = mState.tracks[ mActiveTrack ];
-            if (track.volume[name-VOLUME0] != value) {
+            if (track.volume[name-VOLUME0] != valueInt) {
+                LOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt);
                 track.prevVolume[name-VOLUME0] = track.volume[name-VOLUME0] << 16;
-                track.volume[name-VOLUME0] = value;
+                track.volume[name-VOLUME0] = valueInt;
                 if (target == VOLUME) {
-                    track.prevVolume[name-VOLUME0] = value << 16;
+                    track.prevVolume[name-VOLUME0] = valueInt << 16;
                     track.volumeInc[name-VOLUME0] = 0;
                 } else {
-                    int32_t d = (value<<16) - track.prevVolume[name-VOLUME0];
+                    int32_t d = (valueInt<<16) - track.prevVolume[name-VOLUME0];
                     int32_t volInc = d / int32_t(mState.frameCount);
                     track.volumeInc[name-VOLUME0] = volInc;
                     if (volInc == 0) {
-                        track.prevVolume[name-VOLUME0] = value << 16;
+                        track.prevVolume[name-VOLUME0] = valueInt << 16;
+                    }
+                }
+                invalidateState(1<<mActiveTrack);
+            }
+            return NO_ERROR;
+        } else if (name == AUXLEVEL) {
+            track_t& track = mState.tracks[ mActiveTrack ];
+            if (track.auxLevel != valueInt) {
+                LOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt);
+                track.prevAuxLevel = track.auxLevel << 16;
+                track.auxLevel = valueInt;
+                if (target == VOLUME) {
+                    track.prevAuxLevel = valueInt << 16;
+                    track.auxInc = 0;
+                } else {
+                    int32_t d = (valueInt<<16) - track.prevAuxLevel;
+                    int32_t volInc = d / int32_t(mState.frameCount);
+                    track.auxInc = volInc;
+                    if (volInc == 0) {
+                        track.prevAuxLevel = valueInt << 16;
                     }
                 }
                 invalidateState(1<<mActiveTrack);
@@ -245,7 +290,7 @@
 }
 
 inline
-void AudioMixer::track_t::adjustVolumeRamp()
+void AudioMixer::track_t::adjustVolumeRamp(bool aux)
 {
     for (int i=0 ; i<2 ; i++) {
         if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) ||
@@ -254,6 +299,13 @@
             prevVolume[i] = volume[i]<<16;
         }
     }
+    if (aux) {
+        if (((auxInc>0) && (((prevAuxLevel+auxInc)>>16) >= auxLevel)) ||
+            ((auxInc<0) && (((prevAuxLevel+auxInc)>>16) <= auxLevel))) {
+            auxInc = 0;
+            prevAuxLevel = auxLevel<<16;
+        }
+    }
 }
 
 
@@ -265,13 +317,13 @@
 
 
 
-void AudioMixer::process(void* output)
+void AudioMixer::process()
 {
-    mState.hook(&mState, output);
+    mState.hook(&mState);
 }
 
 
-void AudioMixer::process__validate(state_t* state, void* output)
+void AudioMixer::process__validate(state_t* state)
 {
     LOGW_IF(!state->needsChanged,
         "in process__validate() but nothing's invalid");
@@ -308,7 +360,10 @@
         n |= NEEDS_CHANNEL_1 + t.channelCount - 1;
         n |= NEEDS_FORMAT_16;
         n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED;
-       
+        if (t.auxLevel != 0 && t.auxBuffer != NULL) {
+            n |= NEEDS_AUX_ENABLED;
+        }
+
         if (t.volumeInc[0]|t.volumeInc[1]) {
             volumeRamp = 1;
         } else if (!t.doesResample() && t.volumeRL == 0) {
@@ -319,6 +374,9 @@
         if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) {
             t.hook = track__nop;
         } else {
+            if ((n & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+                all16BitsStereoNoResample = 0;
+            }
             if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
                 all16BitsStereoNoResample = 0;
                 resampling = 1;
@@ -369,7 +427,7 @@
         countActiveTracks, state->enabledTracks,
         all16BitsStereoNoResample, resampling, volumeRamp);
 
-   state->hook(state, output);
+   state->hook(state);
 
    // Now that the volume ramp has been done, set optimal state and
    // track hooks for subsequent mixer process
@@ -390,7 +448,7 @@
        }
        if (allMuted) {
            state->hook = process__nop;
-       } else if (!resampling && all16BitsStereoNoResample) {
+       } else if (all16BitsStereoNoResample) {
            if (countActiveTracks == 1) {
               state->hook = process__OneTrack16BitsStereoNoResampling;
            }
@@ -481,30 +539,44 @@
 }
 
 
-void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp)
+void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
 {
     t->resampler->setSampleRate(t->sampleRate);
 
     // ramp gain - resample to temp buffer and scale/mix in 2nd step
-    if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+    if (aux != NULL) {
+        // always resample with unity gain when sending to auxiliary buffer to be able
+        // to apply send level after resampling
+        // TODO: modify each resampler to support aux channel?
         t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
         memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
         t->resampler->resample(temp, outFrameCount, t->bufferProvider);
-        volumeRampStereo(t, out, outFrameCount, temp);
-    }
+        if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
+            volumeRampStereo(t, out, outFrameCount, temp, aux);
+        } else {
+            volumeStereo(t, out, outFrameCount, temp, aux);
+        }
+    } else {
+        if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+            t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
+            memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
+            t->resampler->resample(temp, outFrameCount, t->bufferProvider);
+            volumeRampStereo(t, out, outFrameCount, temp, aux);
+        }
 
-    // constant gain
-    else {
-        t->resampler->setVolume(t->volume[0], t->volume[1]);
-        t->resampler->resample(out, outFrameCount, t->bufferProvider);
+        // constant gain
+        else {
+            t->resampler->setVolume(t->volume[0], t->volume[1]);
+            t->resampler->resample(out, outFrameCount, t->bufferProvider);
+        }
     }
 }
 
-void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp)
+void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
 {
 }
 
-void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
+void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
 {
     int32_t vl = t->prevVolume[0];
     int32_t vr = t->prevVolume[1];
@@ -514,98 +586,238 @@
     //LOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
     //        t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
     //       (vl + vlInc*frameCount)/65536.0f, frameCount);
-   
+
     // ramp volume
-    do {
-        *out++ += (vl >> 16) * (*temp++ >> 12);
-        *out++ += (vr >> 16) * (*temp++ >> 12);
-        vl += vlInc;
-        vr += vrInc;
-    } while (--frameCount);
-
-    t->prevVolume[0] = vl;
-    t->prevVolume[1] = vr;
-    t->adjustVolumeRamp();
-}
-
-void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
-{
-    int16_t const *in = static_cast<int16_t const *>(t->in);
-
-    // ramp gain
-    if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
-        int32_t vl = t->prevVolume[0];
-        int32_t vr = t->prevVolume[1];
-        const int32_t vlInc = t->volumeInc[0];
-        const int32_t vrInc = t->volumeInc[1];
-
-        // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
-        //        t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
-        //        (vl + vlInc*frameCount)/65536.0f, frameCount);
+    if UNLIKELY(aux != NULL) {
+        int32_t va = t->prevAuxLevel;
+        const int32_t vaInc = t->auxInc;
+        int32_t l;
+        int32_t r;
 
         do {
-            *out++ += (vl >> 16) * (int32_t) *in++;
-            *out++ += (vr >> 16) * (int32_t) *in++;
+            l = (*temp++ >> 12);
+            r = (*temp++ >> 12);
+            *out++ += (vl >> 16) * l;
+            *out++ += (vr >> 16) * r;
+            *aux++ += (va >> 17) * (l + r);
+            vl += vlInc;
+            vr += vrInc;
+            va += vaInc;
+        } while (--frameCount);
+        t->prevAuxLevel = va;
+    } else {
+        do {
+            *out++ += (vl >> 16) * (*temp++ >> 12);
+            *out++ += (vr >> 16) * (*temp++ >> 12);
             vl += vlInc;
             vr += vrInc;
         } while (--frameCount);
-       
-        t->prevVolume[0] = vl;
-        t->prevVolume[1] = vr;
-        t->adjustVolumeRamp();
     }
+    t->prevVolume[0] = vl;
+    t->prevVolume[1] = vr;
+    t->adjustVolumeRamp((aux != NULL));
+}
 
-    // constant gain
-    else {
-        const uint32_t vrl = t->volumeRL;
+void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
+{
+    const int16_t vl = t->volume[0];
+    const int16_t vr = t->volume[1];
+
+    if UNLIKELY(aux != NULL) {
+        const int16_t va = (int16_t)t->auxLevel;
         do {
-            uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
-            in += 2;
-            out[0] = mulAddRL(1, rl, vrl, out[0]);
-            out[1] = mulAddRL(0, rl, vrl, out[1]);
+            int16_t l = (int16_t)(*temp++ >> 12);
+            int16_t r = (int16_t)(*temp++ >> 12);
+            out[0] = mulAdd(l, vl, out[0]);
+            int16_t a = (int16_t)(((int32_t)l + r) >> 1);
+            out[1] = mulAdd(r, vr, out[1]);
+            out += 2;
+            aux[0] = mulAdd(a, va, aux[0]);
+            aux++;
+        } while (--frameCount);
+    } else {
+        do {
+            int16_t l = (int16_t)(*temp++ >> 12);
+            int16_t r = (int16_t)(*temp++ >> 12);
+            out[0] = mulAdd(l, vl, out[0]);
+            out[1] = mulAdd(r, vr, out[1]);
             out += 2;
         } while (--frameCount);
     }
+}
+
+void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
+{
+    int16_t const *in = static_cast<int16_t const *>(t->in);
+
+    if UNLIKELY(aux != NULL) {
+        int32_t l;
+        int32_t r;
+        // ramp gain
+        if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
+            int32_t vl = t->prevVolume[0];
+            int32_t vr = t->prevVolume[1];
+            int32_t va = t->prevAuxLevel;
+            const int32_t vlInc = t->volumeInc[0];
+            const int32_t vrInc = t->volumeInc[1];
+            const int32_t vaInc = t->auxInc;
+            // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+            //        t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+            //        (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+            do {
+                l = (int32_t)*in++;
+                r = (int32_t)*in++;
+                *out++ += (vl >> 16) * l;
+                *out++ += (vr >> 16) * r;
+                *aux++ += (va >> 17) * (l + r);
+                vl += vlInc;
+                vr += vrInc;
+                va += vaInc;
+            } while (--frameCount);
+
+            t->prevVolume[0] = vl;
+            t->prevVolume[1] = vr;
+            t->prevAuxLevel = va;
+            t->adjustVolumeRamp(true);
+        }
+
+        // constant gain
+        else {
+            const uint32_t vrl = t->volumeRL;
+            const int16_t va = (int16_t)t->auxLevel;
+            do {
+                uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+                int16_t a = (int16_t)(((int32_t)in[0] + in[1]) >> 1);
+                in += 2;
+                out[0] = mulAddRL(1, rl, vrl, out[0]);
+                out[1] = mulAddRL(0, rl, vrl, out[1]);
+                out += 2;
+                aux[0] = mulAdd(a, va, aux[0]);
+                aux++;
+            } while (--frameCount);
+        }
+    } else {
+        // ramp gain
+        if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+            int32_t vl = t->prevVolume[0];
+            int32_t vr = t->prevVolume[1];
+            const int32_t vlInc = t->volumeInc[0];
+            const int32_t vrInc = t->volumeInc[1];
+
+            // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+            //        t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+            //        (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+            do {
+                *out++ += (vl >> 16) * (int32_t) *in++;
+                *out++ += (vr >> 16) * (int32_t) *in++;
+                vl += vlInc;
+                vr += vrInc;
+            } while (--frameCount);
+
+            t->prevVolume[0] = vl;
+            t->prevVolume[1] = vr;
+            t->adjustVolumeRamp(false);
+        }
+
+        // constant gain
+        else {
+            const uint32_t vrl = t->volumeRL;
+            do {
+                uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+                in += 2;
+                out[0] = mulAddRL(1, rl, vrl, out[0]);
+                out[1] = mulAddRL(0, rl, vrl, out[1]);
+                out += 2;
+            } while (--frameCount);
+        }
+    }
     t->in = in;
 }
 
-void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
+void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
 {
     int16_t const *in = static_cast<int16_t const *>(t->in);
 
-    // ramp gain
-    if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
-        int32_t vl = t->prevVolume[0];
-        int32_t vr = t->prevVolume[1];
-        const int32_t vlInc = t->volumeInc[0];
-        const int32_t vrInc = t->volumeInc[1];
+    if UNLIKELY(aux != NULL) {
+        // ramp gain
+        if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
+            int32_t vl = t->prevVolume[0];
+            int32_t vr = t->prevVolume[1];
+            int32_t va = t->prevAuxLevel;
+            const int32_t vlInc = t->volumeInc[0];
+            const int32_t vrInc = t->volumeInc[1];
+            const int32_t vaInc = t->auxInc;
 
-        // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
-        //         t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
-        //         (vl + vlInc*frameCount)/65536.0f, frameCount);
+            // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+            //         t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+            //         (vl + vlInc*frameCount)/65536.0f, frameCount);
 
-        do {
-            int32_t l = *in++;
-            *out++ += (vl >> 16) * l;
-            *out++ += (vr >> 16) * l;
-            vl += vlInc;
-            vr += vrInc;
-        } while (--frameCount);
-       
-        t->prevVolume[0] = vl;
-        t->prevVolume[1] = vr;
-        t->adjustVolumeRamp();
-    }
-    // constant gain
-    else {
-        const int16_t vl = t->volume[0];
-        const int16_t vr = t->volume[1];
-        do {
-            int16_t l = *in++;
-            out[0] = mulAdd(l, vl, out[0]);
-            out[1] = mulAdd(l, vr, out[1]);
-            out += 2;
-        } while (--frameCount);
+            do {
+                int32_t l = *in++;
+                *out++ += (vl >> 16) * l;
+                *out++ += (vr >> 16) * l;
+                *aux++ += (va >> 16) * l;
+                vl += vlInc;
+                vr += vrInc;
+                va += vaInc;
+            } while (--frameCount);
+
+            t->prevVolume[0] = vl;
+            t->prevVolume[1] = vr;
+            t->prevAuxLevel = va;
+            t->adjustVolumeRamp(true);
+        }
+        // constant gain
+        else {
+            const int16_t vl = t->volume[0];
+            const int16_t vr = t->volume[1];
+            const int16_t va = (int16_t)t->auxLevel;
+            do {
+                int16_t l = *in++;
+                out[0] = mulAdd(l, vl, out[0]);
+                out[1] = mulAdd(l, vr, out[1]);
+                out += 2;
+                aux[0] = mulAdd(l, va, aux[0]);
+                aux++;
+            } while (--frameCount);
+        }
+    } else {
+        // ramp gain
+        if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+            int32_t vl = t->prevVolume[0];
+            int32_t vr = t->prevVolume[1];
+            const int32_t vlInc = t->volumeInc[0];
+            const int32_t vrInc = t->volumeInc[1];
+
+            // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+            //         t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+            //         (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+            do {
+                int32_t l = *in++;
+                *out++ += (vl >> 16) * l;
+                *out++ += (vr >> 16) * l;
+                vl += vlInc;
+                vr += vrInc;
+            } while (--frameCount);
+
+            t->prevVolume[0] = vl;
+            t->prevVolume[1] = vr;
+            t->adjustVolumeRamp(false);
+        }
+        // constant gain
+        else {
+            const int16_t vl = t->volume[0];
+            const int16_t vr = t->volume[1];
+            do {
+                int16_t l = *in++;
+                out[0] = mulAdd(l, vl, out[0]);
+                out[1] = mulAdd(l, vr, out[1]);
+                out += 2;
+            } while (--frameCount);
+        }
     }
     t->in = in;
 }
@@ -624,37 +836,56 @@
 }
 
 // no-op case
-void AudioMixer::process__nop(state_t* state, void* output)
+void AudioMixer::process__nop(state_t* state)
 {
-    // this assumes output 16 bits stereo, no resampling
-    memset(output, 0, state->frameCount*4);
-    uint32_t en = state->enabledTracks;
-    while (en) {
-        const int i = 31 - __builtin_clz(en);
-        en &= ~(1<<i);
-        track_t& t = state->tracks[i];
-        size_t outFrames = state->frameCount;
-        while (outFrames) {
-            t.buffer.frameCount = outFrames;
-            t.bufferProvider->getNextBuffer(&t.buffer);
-            if (!t.buffer.raw) break;
-            outFrames -= t.buffer.frameCount;
-            t.bufferProvider->releaseBuffer(&t.buffer);
+    uint32_t e0 = state->enabledTracks;
+    size_t bufSize = state->frameCount * sizeof(int16_t) * MAX_NUM_CHANNELS;
+    while (e0) {
+        // process by group of tracks with same output buffer to
+        // avoid multiple memset() on same buffer
+        uint32_t e1 = e0, e2 = e0;
+        int i = 31 - __builtin_clz(e1);
+        track_t& t1 = state->tracks[i];
+        e2 &= ~(1<<i);
+        while (e2) {
+            i = 31 - __builtin_clz(e2);
+            e2 &= ~(1<<i);
+            track_t& t2 = state->tracks[i];
+            if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+                e1 &= ~(1<<i);
+            }
+        }
+        e0 &= ~(e1);
+
+        memset(t1.mainBuffer, 0, bufSize);
+
+        while (e1) {
+            i = 31 - __builtin_clz(e1);
+            e1 &= ~(1<<i);
+            t1 = state->tracks[i];
+            size_t outFrames = state->frameCount;
+            while (outFrames) {
+                t1.buffer.frameCount = outFrames;
+                t1.bufferProvider->getNextBuffer(&t1.buffer);
+                if (!t1.buffer.raw) break;
+                outFrames -= t1.buffer.frameCount;
+                t1.bufferProvider->releaseBuffer(&t1.buffer);
+            }
         }
     }
 }
 
 // generic code without resampling
-void AudioMixer::process__genericNoResampling(state_t* state, void* output)
+void AudioMixer::process__genericNoResampling(state_t* state)
 {
     int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32)));
 
     // acquire each track's buffer
     uint32_t enabledTracks = state->enabledTracks;
-    uint32_t en = enabledTracks;
-    while (en) {
-        const int i = 31 - __builtin_clz(en);
-        en &= ~(1<<i);
+    uint32_t e0 = enabledTracks;
+    while (e0) {
+        const int i = 31 - __builtin_clz(e0);
+        e0 &= ~(1<<i);
         track_t& t = state->tracks[i];
         t.buffer.frameCount = state->frameCount;
         t.bufferProvider->getNextBuffer(&t.buffer);
@@ -666,110 +897,156 @@
             enabledTracks &= ~(1<<i);
     }
 
-    // this assumes output 16 bits stereo, no resampling
-    int32_t* out = static_cast<int32_t*>(output);
-    size_t numFrames = state->frameCount;
-    do {
-        memset(outTemp, 0, sizeof(outTemp));
-
-        en = enabledTracks;
-        while (en) {
-            const int i = 31 - __builtin_clz(en);
-            en &= ~(1<<i);
-            track_t& t = state->tracks[i];
-            size_t outFrames = BLOCKSIZE;
-           
-            while (outFrames) {
-                size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
-                if (inFrames) {
-                    (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp);
-                    t.frameCount -= inFrames;
-                    outFrames -= inFrames;
-                }
-                if (t.frameCount == 0 && outFrames) {
-                    t.bufferProvider->releaseBuffer(&t.buffer);
-                    t.buffer.frameCount = numFrames - (BLOCKSIZE - outFrames);
-                    t.bufferProvider->getNextBuffer(&t.buffer);
-                    t.in = t.buffer.raw;
-                    if (t.in == NULL) {
-                        enabledTracks &= ~(1<<i);
-                        break;
-                    }
-                    t.frameCount = t.buffer.frameCount;
-                 }
+    e0 = enabledTracks;
+    while (e0) {
+        // process by group of tracks with same output buffer to
+        // optimize cache use
+        uint32_t e1 = e0, e2 = e0;
+        int j = 31 - __builtin_clz(e1);
+        track_t& t1 = state->tracks[j];
+        e2 &= ~(1<<j);
+        while (e2) {
+            j = 31 - __builtin_clz(e2);
+            e2 &= ~(1<<j);
+            track_t& t2 = state->tracks[j];
+            if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+                e1 &= ~(1<<j);
             }
         }
-
-        ditherAndClamp(out, outTemp, BLOCKSIZE);
-        out += BLOCKSIZE;
-        numFrames -= BLOCKSIZE;
-    } while (numFrames);
-
+        e0 &= ~(e1);
+        // this assumes output 16 bits stereo, no resampling
+        int32_t *out = t1.mainBuffer;
+        size_t numFrames = 0;
+        do {
+            memset(outTemp, 0, sizeof(outTemp));
+            e2 = e1;
+            while (e2) {
+                const int i = 31 - __builtin_clz(e2);
+                e2 &= ~(1<<i);
+                track_t& t = state->tracks[i];
+                size_t outFrames = BLOCKSIZE;
+                int32_t *aux = NULL;
+                if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+                    aux = t.auxBuffer + numFrames;
+                }
+                while (outFrames) {
+                    size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
+                    if (inFrames) {
+                        (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, aux);
+                        t.frameCount -= inFrames;
+                        outFrames -= inFrames;
+                        if UNLIKELY(aux != NULL) {
+                            aux += inFrames;
+                        }
+                    }
+                    if (t.frameCount == 0 && outFrames) {
+                        t.bufferProvider->releaseBuffer(&t.buffer);
+                        t.buffer.frameCount = (state->frameCount - numFrames) - (BLOCKSIZE - outFrames);
+                        t.bufferProvider->getNextBuffer(&t.buffer);
+                        t.in = t.buffer.raw;
+                        if (t.in == NULL) {
+                            enabledTracks &= ~(1<<i);
+                            e1 &= ~(1<<i);
+                            break;
+                        }
+                        t.frameCount = t.buffer.frameCount;
+                    }
+                }
+            }
+            ditherAndClamp(out, outTemp, BLOCKSIZE);
+            out += BLOCKSIZE;
+            numFrames += BLOCKSIZE;
+        } while (numFrames < state->frameCount);
+    }
 
     // release each track's buffer
-    en = enabledTracks;
-    while (en) {
-        const int i = 31 - __builtin_clz(en);
-        en &= ~(1<<i);
+    e0 = enabledTracks;
+    while (e0) {
+        const int i = 31 - __builtin_clz(e0);
+        e0 &= ~(1<<i);
         track_t& t = state->tracks[i];
         t.bufferProvider->releaseBuffer(&t.buffer);
     }
 }
 
-// generic code with resampling
-void AudioMixer::process__genericResampling(state_t* state, void* output)
+
+  // generic code with resampling
+void AudioMixer::process__genericResampling(state_t* state)
 {
     int32_t* const outTemp = state->outputTemp;
     const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
     memset(outTemp, 0, size);
 
-    int32_t* out = static_cast<int32_t*>(output);
     size_t numFrames = state->frameCount;
 
-    uint32_t en = state->enabledTracks;
-    while (en) {
-        const int i = 31 - __builtin_clz(en);
-        en &= ~(1<<i);
-        track_t& t = state->tracks[i];
-
-        // this is a little goofy, on the resampling case we don't
-        // acquire/release the buffers because it's done by
-        // the resampler.
-        if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
-            (t.hook)(&t, outTemp, numFrames, state->resampleTemp);
-        } else {
-
-            size_t outFrames = numFrames;
-           
-            while (outFrames) {
-                t.buffer.frameCount = outFrames;
-                t.bufferProvider->getNextBuffer(&t.buffer);
-                t.in = t.buffer.raw;
-                // t.in == NULL can happen if the track was flushed just after having
-                // been enabled for mixing.
-                if (t.in == NULL) break;
-
-                (t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp);
-                outFrames -= t.buffer.frameCount;
-                t.bufferProvider->releaseBuffer(&t.buffer);
+    uint32_t e0 = state->enabledTracks;
+    while (e0) {
+        // process by group of tracks with same output buffer
+        // to optimize cache use
+        uint32_t e1 = e0, e2 = e0;
+        int j = 31 - __builtin_clz(e1);
+        track_t& t1 = state->tracks[j];
+        e2 &= ~(1<<j);
+        while (e2) {
+            j = 31 - __builtin_clz(e2);
+            e2 &= ~(1<<j);
+            track_t& t2 = state->tracks[j];
+            if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+                e1 &= ~(1<<j);
             }
         }
-    }
+        e0 &= ~(e1);
+        int32_t *out = t1.mainBuffer;
+        while (e1) {
+            const int i = 31 - __builtin_clz(e1);
+            e1 &= ~(1<<i);
+            track_t& t = state->tracks[i];
+            int32_t *aux = NULL;
+            if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+                aux = t.auxBuffer;
+            }
 
-    ditherAndClamp(out, outTemp, numFrames);
+            // this is a little goofy, on the resampling case we don't
+            // acquire/release the buffers because it's done by
+            // the resampler.
+            if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
+                (t.hook)(&t, outTemp, numFrames, state->resampleTemp, aux);
+            } else {
+
+                size_t outFrames = 0;
+
+                while (outFrames < numFrames) {
+                    t.buffer.frameCount = numFrames - outFrames;
+                    t.bufferProvider->getNextBuffer(&t.buffer);
+                    t.in = t.buffer.raw;
+                    // t.in == NULL can happen if the track was flushed just after having
+                    // been enabled for mixing.
+                    if (t.in == NULL) break;
+
+                    if UNLIKELY(aux != NULL) {
+                        aux += outFrames;
+                    }
+                    (t.hook)(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, aux);
+                    outFrames += t.buffer.frameCount;
+                    t.bufferProvider->releaseBuffer(&t.buffer);
+                }
+            }
+        }
+        ditherAndClamp(out, outTemp, numFrames);
+    }
 }
 
 // one track, 16 bits stereo without resampling is the most common case
-void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* output)
+void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state)
 {
     const int i = 31 - __builtin_clz(state->enabledTracks);
     const track_t& t = state->tracks[i];
 
     AudioBufferProvider::Buffer& b(t.buffer);
-   
-    int32_t* out = static_cast<int32_t*>(output);
+
+    int32_t* out = t.mainBuffer;
     size_t numFrames = state->frameCount;
-  
+
     const int16_t vl = t.volume[0];
     const int16_t vr = t.volume[1];
     const uint32_t vrl = t.volumeRL;
@@ -787,7 +1064,7 @@
             return;
         }
         size_t outFrames = b.frameCount;
-       
+
         if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
             // volume is boosted, so we might need to clamp even though
             // we process only one track.
@@ -816,7 +1093,9 @@
 }
 
 // 2 tracks is also a common case
-void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output)
+// NEVER used in current implementation of process__validate()
+// only use if the 2 tracks have the same output buffer
+void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state)
 {
     int i;
     uint32_t en = state->enabledTracks;
@@ -829,24 +1108,25 @@
     i = 31 - __builtin_clz(en);
     const track_t& t1 = state->tracks[i];
     AudioBufferProvider::Buffer& b1(t1.buffer);
-   
+
     int16_t const *in0;
     const int16_t vl0 = t0.volume[0];
     const int16_t vr0 = t0.volume[1];
     size_t frameCount0 = 0;
-  
+
     int16_t const *in1;
     const int16_t vl1 = t1.volume[0];
     const int16_t vr1 = t1.volume[1];
     size_t frameCount1 = 0;
-   
-    int32_t* out = static_cast<int32_t*>(output);
+
+    //FIXME: only works if two tracks use same buffer
+    int32_t* out = t0.mainBuffer;
     size_t numFrames = state->frameCount;
     int16_t const *buff = NULL;
 
-  
+
     while (numFrames) {
-   
+
         if (frameCount0 == 0) {
             b0.frameCount = numFrames;
             t0.bufferProvider->getNextBuffer(&b0);
@@ -875,13 +1155,13 @@
             }
             frameCount1 = b1.frameCount;
         }
-       
+
         size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1;
 
         numFrames -= outFrames;
         frameCount0 -= outFrames;
         frameCount1 -= outFrames;
-       
+
         do {
             int32_t l0 = *in0++;
             int32_t r0 = *in0++;
@@ -896,17 +1176,17 @@
             r = clamp16(r);
             *out++ = (r<<16) | (l & 0xFFFF);
         } while (--outFrames);
-       
+
         if (frameCount0 == 0) {
             t0.bufferProvider->releaseBuffer(&b0);
         }
         if (frameCount1 == 0) {
             t1.bufferProvider->releaseBuffer(&b1);
         }
-    }   
-       
+    }
+
     if (buff != NULL) {
-        delete [] buff;       
+        delete [] buff;
     }
 }
 
diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h
index 15766cd..aee3e17 100644
--- a/libs/audioflinger/AudioMixer.h
+++ b/libs/audioflinger/AudioMixer.h
@@ -63,11 +63,14 @@
         // for target TRACK
         CHANNEL_COUNT   = 0x4000,
         FORMAT          = 0x4001,
+        MAIN_BUFFER     = 0x4002,
+        AUX_BUFFER      = 0x4003,
         // for TARGET RESAMPLE
         SAMPLE_RATE     = 0x4100,
         // for TARGET VOLUME (8 channels max)
         VOLUME0         = 0x4200,
         VOLUME1         = 0x4201,
+        AUXLEVEL        = 0x4210,
     };
 
 
@@ -78,10 +81,10 @@
     status_t    disable(int name);
 
     status_t    setActiveTrack(int track);
-    status_t    setParameter(int target, int name, int value);
+    status_t    setParameter(int target, int name, void *value);
 
     status_t    setBufferProvider(AudioBufferProvider* bufferProvider);
-    void        process(void* output);
+    void        process();
 
     uint32_t    trackNames() const { return mTrackNames; }
 
@@ -94,6 +97,7 @@
         NEEDS_FORMAT__MASK          = 0x000000F0,
         NEEDS_MUTE__MASK            = 0x00000100,
         NEEDS_RESAMPLE__MASK        = 0x00001000,
+        NEEDS_AUX__MASK             = 0x00010000,
     };
 
     enum {
@@ -107,6 +111,9 @@
 
         NEEDS_RESAMPLE_DISABLED     = 0x00000000,
         NEEDS_RESAMPLE_ENABLED      = 0x00001000,
+
+        NEEDS_AUX_DISABLED     = 0x00000000,
+        NEEDS_AUX_ENABLED      = 0x00010000,
     };
 
     static inline int32_t applyVolume(int32_t in, int32_t v) {
@@ -115,9 +122,10 @@
 
 
     struct state_t;
+    struct track_t;
 
-    typedef void (*mix_t)(state_t* state, void* output);
-
+    typedef void (*mix_t)(state_t* state);
+    typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
     static const int BLOCKSIZE = 16; // 4 cache lines
 
     struct track_t {
@@ -131,6 +139,9 @@
         int32_t     prevVolume[2];
 
         int32_t     volumeInc[2];
+        int32_t     auxLevel;
+        int32_t     auxInc;
+        int32_t     prevAuxLevel;
 
         uint16_t    frameCount;
 
@@ -142,15 +153,17 @@
         AudioBufferProvider*                bufferProvider;
         mutable AudioBufferProvider::Buffer buffer;
 
-        void (*hook)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp);
+        hook_t      hook;
         void const* in;             // current location in buffer
 
         AudioResampler*     resampler;
         uint32_t            sampleRate;
+        int32_t*           mainBuffer;
+        int32_t*           auxBuffer;
 
         bool        setResampler(uint32_t sampleRate, uint32_t devSampleRate);
         bool        doesResample() const;
-        void        adjustVolumeRamp();
+        void        adjustVolumeRamp(bool aux);
     };
 
     // pad to 32-bytes to fill cache line
@@ -173,18 +186,19 @@
 
     void invalidateState(uint32_t mask);
 
-    static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
-    static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
-    static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp);
-    static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
-    static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
+    static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+    static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+    static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+    static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+    static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
+    static void volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
 
-    static void process__validate(state_t* state, void* output);
-    static void process__nop(state_t* state, void* output);
-    static void process__genericNoResampling(state_t* state, void* output);
-    static void process__genericResampling(state_t* state, void* output);
-    static void process__OneTrack16BitsStereoNoResampling(state_t* state, void* output);
-    static void process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output);
+    static void process__validate(state_t* state);
+    static void process__nop(state_t* state);
+    static void process__genericNoResampling(state_t* state);
+    static void process__genericResampling(state_t* state);
+    static void process__OneTrack16BitsStereoNoResampling(state_t* state);
+    static void process__TwoTracks16BitsStereoNoResampling(state_t* state);
 };
 
 // ----------------------------------------------------------------------------
diff --git a/media/libeffects/EffectEqualizer.cpp b/media/libeffects/EffectEqualizer.cpp
index c08f4f5..e39e595 100644
--- a/media/libeffects/EffectEqualizer.cpp
+++ b/media/libeffects/EffectEqualizer.cpp
@@ -114,7 +114,7 @@
 //--- Effect Library Interface Implementation
 //
 
-extern "C" int EffectQueryNumberEffects(int *pNumEffects) {
+extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) {
     *pNumEffects = 1;
     gEffectIndex = 0;
     return 0;
diff --git a/media/libeffects/EffectReverb.c b/media/libeffects/EffectReverb.c
index 3181504..202f50b 100644
--- a/media/libeffects/EffectReverb.c
+++ b/media/libeffects/EffectReverb.c
@@ -18,7 +18,8 @@
 //
 #define LOG_NDEBUG 0
 #include <cutils/log.h>
-
+#include <stdlib.h>
+#include <string.h>
 #include <stdbool.h>
 #include "EffectReverb.h"
 #include "EffectsMath.h"
@@ -86,7 +87,7 @@
 
 /*--- Effect Library Interface Implementation ---*/
 
-int EffectQueryNumberEffects(int *pNumEffects) {
+int EffectQueryNumberEffects(uint32_t *pNumEffects) {
     *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)
             - 1;
     gEffectIndex = 0;
diff --git a/media/libeffects/EffectReverb.h b/media/libeffects/EffectReverb.h
index cd14891..578e09e 100644
--- a/media/libeffects/EffectReverb.h
+++ b/media/libeffects/EffectReverb.h
@@ -292,7 +292,7 @@
  * Effect API
  *------------------------------------
 */
-int EffectQueryNumberEffects(int *pNumEffects);
+int EffectQueryNumberEffects(uint32_t *pNumEffects);
 int EffectQueryNext(effect_descriptor_t *pDescriptor);
 int EffectCreate(effect_uuid_t *effectUID, effect_interface_t *pInterface);
 int EffectRelease(effect_interface_t interface);
diff --git a/media/libeffects/EffectsFactory.c b/media/libeffects/EffectsFactory.c
index 35a1001..6800765 100644
--- a/media/libeffects/EffectsFactory.c
+++ b/media/libeffects/EffectsFactory.c
@@ -39,7 +39,7 @@
 static int init();
 static int loadLibrary(const char *libPath, int *handle);
 static int unloadLibrary(int handle);
-static int numEffectModules();
+static uint32_t numEffectModules();
 static int findEffect(effect_uuid_t *uuid, lib_entry_t **lib, effect_descriptor_t **desc);
 static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
 
@@ -96,7 +96,7 @@
 //      Effect Factory Interface functions
 /////////////////////////////////////////////////
 
-int EffectQueryNumberEffects(int *pNumEffects)
+int EffectQueryNumberEffects(uint32_t *pNumEffects)
 {
     int ret = init();
     if (ret < 0) {
@@ -353,8 +353,8 @@
     effect_QueryNextEffect_t queryFx;
     effect_CreateEffect_t createFx;
     effect_ReleaseEffect_t releaseFx;
-    int numFx;
-    int fx;
+    uint32_t numFx;
+    uint32_t fx;
     int ret;
     list_elem_t *e, *descHead = NULL;
     lib_entry_t *l;
@@ -525,9 +525,9 @@
 
 
 
-int numEffectModules() {
+uint32_t numEffectModules() {
     list_elem_t *e = gLibraryList;
-    int cnt = 0;
+    uint32_t cnt = 0;
 
     // Reset pointers for EffectQueryNext()
     gCurLib = e;
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index fd2b1ce..a2436ab 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -45,7 +45,7 @@
 // ---------------------------------------------------------------------------
 
 AudioRecord::AudioRecord()
-    : mStatus(NO_INIT)
+    : mStatus(NO_INIT), mSessionId(0)
 {
 }
 
@@ -58,11 +58,12 @@
         uint32_t flags,
         callback_t cbf,
         void* user,
-        int notificationFrames)
-    : mStatus(NO_INIT)
+        int notificationFrames,
+        int sessionId)
+    : mStatus(NO_INIT), mSessionId(0)
 {
     mStatus = set(inputSource, sampleRate, format, channels,
-            frameCount, flags, cbf, user, notificationFrames);
+            frameCount, flags, cbf, user, notificationFrames, sessionId);
 }
 
 AudioRecord::~AudioRecord()
@@ -91,7 +92,8 @@
         callback_t cbf,
         void* user,
         int notificationFrames,
-        bool threadCanCallJava)
+        bool threadCanCallJava,
+        int sessionId)
 {
 
     LOGV("set(): sampleRate %d, channels %d, frameCount %d",sampleRate, channels, frameCount);
@@ -119,6 +121,7 @@
     if (!AudioSystem::isInputChannel(channels)) {
         return BAD_VALUE;
     }
+
     int channelCount = AudioSystem::popCount(channels);
 
     audio_io_handle_t input = AudioSystem::getInput(inputSource,
@@ -164,6 +167,8 @@
         notificationFrames = frameCount/2;
     }
 
+    mSessionId = sessionId;
+
     // create the IAudioRecord
     status_t status = openRecord(sampleRate, format, channelCount,
                                  frameCount, flags, input);
@@ -414,6 +419,7 @@
                                                        channelCount,
                                                        frameCount,
                                                        ((uint16_t)flags) << 16,
+                                                       &mSessionId,
                                                        &status);
     if (record == 0) {
         LOGE("AudioFlinger could not create record track, status: %d", status);
@@ -532,6 +538,11 @@
     return mInput;
 }
 
+int AudioRecord::getSessionId()
+{
+    return mSessionId;
+}
+
 // -------------------------------------------------------------------------
 
 ssize_t AudioRecord::read(void* buffer, size_t userSize)
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 5e6ce42..c77f551 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -364,6 +364,12 @@
     return result;
 }
 
+int AudioSystem::newAudioSessionId() {
+    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+    if (af == 0) return 0;
+    return af->newAudioSessionId();
+}
+
 // ---------------------------------------------------------------------------
 
 void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index c350532..4b61131 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -58,7 +58,8 @@
         uint32_t flags,
         callback_t cbf,
         void* user,
-        int notificationFrames)
+        int notificationFrames,
+        int sessionId)
     : mStatus(NO_INIT)
 {
     mStatus = set(streamType, sampleRate, format, channels,
@@ -74,7 +75,8 @@
         uint32_t flags,
         callback_t cbf,
         void* user,
-        int notificationFrames)
+        int notificationFrames,
+        int sessionId)
     : mStatus(NO_INIT)
 {
     mStatus = set(streamType, sampleRate, format, channels,
@@ -110,7 +112,8 @@
         void* user,
         int notificationFrames,
         const sp<IMemory>& sharedBuffer,
-        bool threadCanCallJava)
+        bool threadCanCallJava,
+        int sessionId)
 {
 
     LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
@@ -171,8 +174,11 @@
 
     mVolume[LEFT] = 1.0f;
     mVolume[RIGHT] = 1.0f;
+    mSendLevel = 0;
     mFrameCount = frameCount;
     mNotificationFramesReq = notificationFrames;
+    mSessionId = sessionId;
+
     // create the IAudioTrack
     status_t status = createTrack(streamType, sampleRate, format, channelCount,
                                   frameCount, flags, sharedBuffer, output, true);
@@ -396,19 +402,49 @@
     return mMuted;
 }
 
-void AudioTrack::setVolume(float left, float right)
+status_t AudioTrack::setVolume(float left, float right)
 {
+    if (left > 1.0f || right > 1.0f) {
+        return BAD_VALUE;
+    }
+
     mVolume[LEFT] = left;
     mVolume[RIGHT] = right;
 
     // write must be atomic
-    mCblk->volumeLR = (int32_t(int16_t(left * 0x1000)) << 16) | int16_t(right * 0x1000);
+    mCblk->volumeLR = (uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000);
+
+    return NO_ERROR;
 }
 
 void AudioTrack::getVolume(float* left, float* right)
 {
-    *left  = mVolume[LEFT];
-    *right = mVolume[RIGHT];
+    if (left != NULL) {
+        *left  = mVolume[LEFT];
+    }
+    if (right != NULL) {
+        *right = mVolume[RIGHT];
+    }
+}
+
+status_t AudioTrack::setSendLevel(float level)
+{
+    if (level > 1.0f) {
+        return BAD_VALUE;
+    }
+
+    mSendLevel = level;
+
+    mCblk->sendLevel = uint16_t(level * 0x1000);
+
+    return NO_ERROR;
+}
+
+void AudioTrack::getSendLevel(float* level)
+{
+    if (level != NULL) {
+        *level  = mSendLevel;
+    }
 }
 
 status_t AudioTrack::setSampleRate(int rate)
@@ -563,6 +599,16 @@
             mCblk->sampleRate, mFormat, mChannels, (AudioSystem::output_flags)mFlags);
 }
 
+int AudioTrack::getSessionId()
+{
+    return mSessionId;
+}
+
+status_t AudioTrack::attachAuxEffect(int effectId)
+{
+    return mAudioTrack->attachAuxEffect(effectId);
+}
+
 // -------------------------------------------------------------------------
 
 status_t AudioTrack::createTrack(
@@ -647,6 +693,7 @@
                                                       ((uint16_t)flags) << 16,
                                                       sharedBuffer,
                                                       output,
+                                                      &mSessionId,
                                                       &status);
 
     if (track == 0) {
@@ -672,7 +719,8 @@
         mCblk->stepUser(mCblk->frameCount);
     }
 
-    mCblk->volumeLR = (int32_t(int16_t(mVolume[LEFT] * 0x1000)) << 16) | int16_t(mVolume[RIGHT] * 0x1000);
+    mCblk->volumeLR = (uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000);
+    mCblk->sendLevel = uint16_t(mSendLevel * 0x1000);
     mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
     mCblk->waitTimeMs = 0;
     mRemainingFrames = mNotificationFramesAct;
@@ -1016,7 +1064,7 @@
     : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
     userBase(0), serverBase(0), buffers(0), frameCount(0),
     loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
-    flags(0)
+    flags(0), sendLevel(0)
 {
 }
 
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 47bcc12..f2a8db3 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -62,7 +62,14 @@
     SET_STREAM_OUTPUT,
     SET_VOICE_VOLUME,
     GET_RENDER_POSITION,
-    GET_INPUT_FRAMES_LOST
+    GET_INPUT_FRAMES_LOST,
+    NEW_AUDIO_SESSION_ID,
+    LOAD_EFFECT_LIBRARY,
+    UNLOAD_EFFECT_LIBRARY,
+    QUERY_NUM_EFFECTS,
+    QUERY_NEXT_EFFECT,
+    GET_EFFECT_DESCRIPTOR,
+    CREATE_EFFECT
 };
 
 class BpAudioFlinger : public BpInterface<IAudioFlinger>
@@ -83,6 +90,7 @@
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
                                 int output,
+                                int *sessionId,
                                 status_t *status)
     {
         Parcel data, reply;
@@ -97,10 +105,19 @@
         data.writeInt32(flags);
         data.writeStrongBinder(sharedBuffer->asBinder());
         data.writeInt32(output);
+        int lSessionId = 0;
+        if (sessionId != NULL) {
+            lSessionId = *sessionId;
+        }
+        data.writeInt32(lSessionId);
         status_t lStatus = remote()->transact(CREATE_TRACK, data, &reply);
         if (lStatus != NO_ERROR) {
             LOGE("createTrack error: %s", strerror(-lStatus));
         } else {
+            lSessionId = reply.readInt32();
+            if (sessionId != NULL) {
+                *sessionId = lSessionId;
+            }
             lStatus = reply.readInt32();
             track = interface_cast<IAudioTrack>(reply.readStrongBinder());
         }
@@ -118,6 +135,7 @@
                                 int channelCount,
                                 int frameCount,
                                 uint32_t flags,
+                                int *sessionId,
                                 status_t *status)
     {
         Parcel data, reply;
@@ -130,10 +148,19 @@
         data.writeInt32(channelCount);
         data.writeInt32(frameCount);
         data.writeInt32(flags);
+        int lSessionId = 0;
+        if (sessionId != NULL) {
+            lSessionId = *sessionId;
+        }
+        data.writeInt32(lSessionId);
         status_t lStatus = remote()->transact(OPEN_RECORD, data, &reply);
         if (lStatus != NO_ERROR) {
             LOGE("openRecord error: %s", strerror(-lStatus));
         } else {
+            lSessionId = reply.readInt32();
+            if (sessionId != NULL) {
+                *sessionId = lSessionId;
+            }
             lStatus = reply.readInt32();
             record = interface_cast<IAudioRecord>(reply.readStrongBinder());
         }
@@ -497,6 +524,157 @@
         remote()->transact(GET_INPUT_FRAMES_LOST, data, &reply);
         return reply.readInt32();
     }
+
+    virtual int newAudioSessionId()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        status_t status = remote()->transact(NEW_AUDIO_SESSION_ID, data, &reply);
+        int id = 0;
+        if (status == NO_ERROR) {
+            id = reply.readInt32();
+        }
+        return id;
+    }
+
+    virtual status_t loadEffectLibrary(const char *libPath, int *handle)
+    {
+        if (libPath == NULL || handle == NULL) {
+            return BAD_VALUE;
+        }
+        *handle = 0;
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.writeCString(libPath);
+        status_t status = remote()->transact(LOAD_EFFECT_LIBRARY, data, &reply);
+        if (status == NO_ERROR) {
+            status = reply.readInt32();
+            if (status == NO_ERROR) {
+                *handle = reply.readInt32();
+            }
+        }
+        return status;
+    }
+
+    virtual status_t unloadEffectLibrary(int handle)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.writeInt32(handle);
+        status_t status = remote()->transact(UNLOAD_EFFECT_LIBRARY, data, &reply);
+        if (status == NO_ERROR) {
+            status = reply.readInt32();
+        }
+        return status;
+    }
+
+    virtual status_t queryNumberEffects(uint32_t *numEffects)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        status_t status = remote()->transact(QUERY_NUM_EFFECTS, data, &reply);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        status = reply.readInt32();
+        if (status != NO_ERROR) {
+            return status;
+        }
+        if (numEffects) {
+            *numEffects = (uint32_t)reply.readInt32();
+        }
+        return NO_ERROR;
+    }
+
+    virtual status_t queryNextEffect(effect_descriptor_t *pDescriptor)
+    {
+        if (pDescriptor == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        status_t status = remote()->transact(QUERY_NEXT_EFFECT, data, &reply);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        status = reply.readInt32();
+        if (status != NO_ERROR) {
+            return status;
+        }
+        reply.read(pDescriptor, sizeof(effect_descriptor_t));
+        return NO_ERROR;
+    }
+
+    virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *pDescriptor)
+    {
+        if (pUuid == NULL || pDescriptor == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.write(pUuid, sizeof(effect_uuid_t));
+        status_t status = remote()->transact(GET_EFFECT_DESCRIPTOR, data, &reply);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        status = reply.readInt32();
+        if (status != NO_ERROR) {
+            return status;
+        }
+        reply.read(pDescriptor, sizeof(effect_descriptor_t));
+        return NO_ERROR;
+    }
+
+    virtual sp<IEffect> createEffect(pid_t pid,
+                                    effect_descriptor_t *pDesc,
+                                    const sp<IEffectClient>& client,
+                                    int32_t priority,
+                                    int output,
+                                    int sessionId,
+                                    status_t *status,
+                                    int *id,
+                                    int *enabled)
+    {
+        Parcel data, reply;
+        sp<IEffect> effect;
+
+        if (pDesc == NULL) {
+             return effect;
+             if (status) {
+                 *status = BAD_VALUE;
+             }
+         }
+
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.writeInt32(pid);
+        data.write(pDesc, sizeof(effect_descriptor_t));
+        data.writeStrongBinder(client->asBinder());
+        data.writeInt32(priority);
+        data.writeInt32(output);
+        data.writeInt32(sessionId);
+
+        status_t lStatus = remote()->transact(CREATE_EFFECT, data, &reply);
+        if (lStatus != NO_ERROR) {
+            LOGE("createEffect error: %s", strerror(-lStatus));
+        } else {
+            lStatus = reply.readInt32();
+            int tmp = reply.readInt32();
+            if (id) {
+                *id = tmp;
+            }
+            tmp = reply.readInt32();
+            if (enabled) {
+                *enabled = tmp;
+            }
+            effect = interface_cast<IEffect>(reply.readStrongBinder());
+            reply.read(pDesc, sizeof(effect_descriptor_t));
+        }
+        if (status) {
+            *status = lStatus;
+        }
+
+        return effect;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger");
@@ -518,10 +696,12 @@
             uint32_t flags = data.readInt32();
             sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder());
             int output = data.readInt32();
+            int sessionId = data.readInt32();
             status_t status;
             sp<IAudioTrack> track = createTrack(pid,
                     streamType, sampleRate, format,
-                    channelCount, bufferCount, flags, buffer, output, &status);
+                    channelCount, bufferCount, flags, buffer, output, &sessionId, &status);
+            reply->writeInt32(sessionId);
             reply->writeInt32(status);
             reply->writeStrongBinder(track->asBinder());
             return NO_ERROR;
@@ -535,9 +715,11 @@
             int channelCount = data.readInt32();
             size_t bufferCount = data.readInt32();
             uint32_t flags = data.readInt32();
+            int sessionId = data.readInt32();
             status_t status;
             sp<IAudioRecord> record = openRecord(pid, input,
-                    sampleRate, format, channelCount, bufferCount, flags, &status);
+                    sampleRate, format, channelCount, bufferCount, flags, &sessionId, &status);
+            reply->writeInt32(sessionId);
             reply->writeInt32(status);
             reply->writeStrongBinder(record->asBinder());
             return NO_ERROR;
@@ -768,7 +950,79 @@
             reply->writeInt32(getInputFramesLost(ioHandle));
             return NO_ERROR;
         } break;
+        case NEW_AUDIO_SESSION_ID: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            reply->writeInt32(newAudioSessionId());
+            return NO_ERROR;
+        } break;
+        case LOAD_EFFECT_LIBRARY: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            int handle;
+            status_t status = loadEffectLibrary(data.readCString(), &handle);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->writeInt32(handle);
+            }
+            return NO_ERROR;
+        }
+        case UNLOAD_EFFECT_LIBRARY: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            reply->writeInt32(unloadEffectLibrary(data.readInt32()));
+            return NO_ERROR;
+        }
+        case QUERY_NUM_EFFECTS: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            uint32_t numEffects;
+            status_t status = queryNumberEffects(&numEffects);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->writeInt32((int32_t)numEffects);
+            }
+            return NO_ERROR;
+        }
+        case QUERY_NEXT_EFFECT: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            effect_descriptor_t desc;
+            status_t status = queryNextEffect(&desc);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->write(&desc, sizeof(effect_descriptor_t));
+            }
+            return NO_ERROR;
+        }
+        case GET_EFFECT_DESCRIPTOR: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            effect_uuid_t uuid;
+            data.read(&uuid, sizeof(effect_uuid_t));
+            effect_descriptor_t desc;
+            status_t status = getEffectDescriptor(&uuid, &desc);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->write(&desc, sizeof(effect_descriptor_t));
+            }
+            return NO_ERROR;
+        }
+        case CREATE_EFFECT: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            pid_t pid = data.readInt32();
+            effect_descriptor_t desc;
+            data.read(&desc, sizeof(effect_descriptor_t));
+            sp<IEffectClient> client = interface_cast<IEffectClient>(data.readStrongBinder());
+            int32_t priority = data.readInt32();
+            int output = data.readInt32();
+            int sessionId = data.readInt32();
+            status_t status;
+            int id;
+            int enabled;
 
+            sp<IEffect> effect = createEffect(pid, &desc, client, priority, output, sessionId, &status, &id, &enabled);
+            reply->writeInt32(status);
+            reply->writeInt32(id);
+            reply->writeInt32(enabled);
+            reply->writeStrongBinder(effect->asBinder());
+            reply->write(&desc, sizeof(effect_descriptor_t));
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index 01ffd75..bc8ff34 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -34,7 +34,8 @@
     STOP,
     FLUSH,
     MUTE,
-    PAUSE
+    PAUSE,
+    ATTACH_AUX_EFFECT
 };
 
 class BpAudioTrack : public BpInterface<IAudioTrack>
@@ -97,7 +98,21 @@
             cblk = interface_cast<IMemory>(reply.readStrongBinder());
         }
         return cblk;
-    }    
+    }
+
+    virtual status_t attachAuxEffect(int effectId)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+        data.writeInt32(effectId);
+        status_t status = remote()->transact(ATTACH_AUX_EFFECT, data, &reply);
+        if (status == NO_ERROR) {
+            status = reply.readInt32();
+        } else {
+            LOGW("attachAuxEffect() error: %s", strerror(-status));
+        }
+        return status;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack");
@@ -138,6 +153,11 @@
             pause();
             return NO_ERROR;
         }
+        case ATTACH_AUX_EFFECT: {
+            CHECK_INTERFACE(IAudioTrack, data, reply);
+            reply->writeInt32(attachAuxEffect(data.readInt32()));
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }