C++程序  |  1532行  |  46.93 KB

/*
 * Copyright (C) 2012 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.
 */

#include <errno.h>
#include <fcntl.h>

#define LOG_TAG "eS305VoiceProcessing"
//#define LOG_NDEBUG 0
#include <cutils/log.h>

#include "eS305VoiceProcessing.h"
#include <audio_effects/effect_aec.h>
#include <audio_effects/effect_ns.h>
#include <audio_effects/effect_agc.h>

extern "C" {

//------------------------------------------------------------------------------
// local definitions
//------------------------------------------------------------------------------

// number of sessions this effect bundle can be used for
#define ADNC_PFX_NUM_SESSION 8

// types of pre processing modules
enum adnc_pfx_id
{
    PFX_ID_AEC = 0,  // Acoustic Echo Cancellation
    PFX_ID_NS,       // Noise Suppression
    PFX_ID_AGC,      // Automatic Gain Control
    PFX_ID_CNT
};

// Session state
enum adnc_pfx_session_state {
    PFX_SESSION_STATE_INIT,        // initialized
    PFX_SESSION_STATE_CONFIG       // configuration received
};

// Effect/Preprocessor state
enum adnc_pfx_effect_state {
    PFX_EFFECT_STATE_INIT,         // initialized
    PFX_EFFECT_STATE_CREATED,      // effect created
    PFX_EFFECT_STATE_CONFIG,       // configuration received/disabled
    PFX_EFFECT_STATE_ACTIVE        // active/enabled
};

typedef struct adnc_pfx_session_s adnc_pfx_session_t;
typedef struct adnc_pfx_effect_s  adnc_pfx_effect_t;
typedef struct adnc_pfx_ops_s     adnc_pfx_ops_t;

// Effect operation table. Functions for all pre processors are declared in sPreProcOps[] table.
// Function pointer can be null if no action required.
struct adnc_pfx_ops_s {
    int (* create)(adnc_pfx_effect_t *fx);
    int (* init)(adnc_pfx_effect_t *fx);
    int (* reset)(adnc_pfx_effect_t *fx);
    void (* enable)(adnc_pfx_effect_t *fx);
    void (* disable)(adnc_pfx_effect_t *fx);
    int (* set_parameter)(adnc_pfx_effect_t *fx, void *param, void *value);
    int (* get_parameter)(adnc_pfx_effect_t *fx, void *param, size_t *size, void *value);
    int (* set_device)(adnc_pfx_effect_t *fx, uint32_t device);
};

// Effect context
struct adnc_pfx_effect_s {
    const struct effect_interface_s *itfe;
    uint32_t procId;                // type of pre processor (enum adnc_pfx_id)
    uint32_t state;                 // current state (enum adnc_pfx_effect_state)
    adnc_pfx_session_t *session;     // session the effect is on
    const adnc_pfx_ops_t *ops;       // effect ops table
};

// Session context
struct adnc_pfx_session_s {
    uint32_t state;                     // current state (enum adnc_pfx_session_state)
    audio_source_t audioSource;
    // FIXME not used, delete?
    //int audioSessionId;             // audio session ID
    int ioHandle;                     // handle of input stream this session is on
    uint32_t createdMsk;              // bit field containing IDs of created pre processors
    uint32_t activeMsk;               // bit field containing IDs of pre processors currently active
    struct adnc_pfx_effect_s effects[PFX_ID_CNT]; // effects in this session

    // effect settings
    //   none controllable from public API here
};

//-----------------------------------------
// forward declarations
//-----------------------------------------
int Adnc_SetNoiseSuppressionInt_l(bool);
int Adnc_SetAutomaticGainControlInt_l(bool);
int Adnc_SetEchoCancellationInt_l(bool);
int Adnc_ReevaluateUsageInt_l(audio_io_handle_t);
int Adnc_SleepInt_l();

//------------------------------------------------------------------------------
// eS305 control
//------------------------------------------------------------------------------
#define ES305_SYSFS_PATH "/sys/class/i2c-dev/i2c-4/device/4-003e/"
#define ES305_VOICE_PROCESSING_PATH ES305_SYSFS_PATH "voice_processing"
#define ES305_PRESET_PATH           ES305_SYSFS_PATH "preset"
#define ES305_TX_NS_LEVEL_PATH      ES305_SYSFS_PATH "tx_ns_level"
#define ES305_TX_AGC_ENABLE_PATH    ES305_SYSFS_PATH "tx_agc_enable"
#define ES305_AEC_ENABLE_PATH       ES305_SYSFS_PATH "aec_enable"
#define ES305_SLEEP_PATH            ES305_SYSFS_PATH "sleep"

enum eS305_controls {
    ES305_CTRL_VOICE_PROCESSING = 0,
    ES305_CTRL_PRESET,
    ES305_CTRL_TX_NS_LEVEL,
    ES305_CTRL_TX_AGC_ENABLE,
    ES305_CTRL_AEC_ENABLE,
    ES305_CTRL_SLEEP,
    ES305_NUM_CTRL
};

struct eS305_ctrl_s {
    int fd[ES305_NUM_CTRL];
    int current_preset;
    int requested_preset;
    int ioHandle;
};
typedef struct eS305_ctrl_s eS305_ctrl_t;

static eS305_ctrl_t eS305_ctrl = {
        { -1/*vp*/, -1/*preset*/, -1/*ns*/, -1/*agc*/, -1/*aec*/, -1/*sleep*/},
        ES305_PRESET_OFF  /*current_preset*/,
        ES305_PRESET_INIT /*requested_preset, an invalid preset, different from current_preset*/,
        ES305_IO_HANDLE_NONE
};

//------------------------------------------------------------------------------
// Effect descriptors
//------------------------------------------------------------------------------

// UUIDs for effect types have been generated from http://www.itu.int/ITU-T/asn1/uuid.html
// as the pre processing effects are not defined by OpenSL ES

// Acoustic Echo Cancellation
static const effect_descriptor_t aec_descriptor = {
        FX_IID_AEC_, // type
        { 0xfd90ff00, 0x0b55, 0x11e2, 0x892e, { 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }, // uuid
        EFFECT_CONTROL_API_VERSION,
        (EFFECT_FLAG_TYPE_PRE_PROC|EFFECT_FLAG_DEVICE_IND),
        0, //FIXME indicate CPU load
        0, //FIXME indicate memory usage
        "Acoustic Echo Canceler",
        "Audience"
};

// Noise suppression
static const effect_descriptor_t ns_descriptor = {
        FX_IID_NS_, // type
        { 0x08fa98b0, 0x0b56, 0x11e2, 0x892e, { 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }, // uuid
        EFFECT_CONTROL_API_VERSION,
        (EFFECT_FLAG_TYPE_PRE_PROC|EFFECT_FLAG_DEVICE_IND),
        0, //FIXME indicate CPU load
        0, //FIXME indicate memory usage
        "Noise Suppression",
        "Audience"
};

// Automatic Gain Control
static const effect_descriptor_t agc_descriptor = {
        FX_IID_AGC_, // type
        { 0xe9e87eb0, 0x0b55, 0x11e2, 0x892e, { 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }, // uuid
        EFFECT_CONTROL_API_VERSION,
        (EFFECT_FLAG_TYPE_PRE_PROC|EFFECT_FLAG_DEVICE_IND),
        0, //FIXME indicate CPU load
        0, //FIXME indicate memory usage
        "Automatic Gain Control",
        "Audience"
};

static const effect_descriptor_t *adnc_pfx_descriptors[PFX_ID_CNT] = {
        &aec_descriptor,
        &ns_descriptor,
        &agc_descriptor
};


//------------------------------------------------------------------------------
// Helper functions
//------------------------------------------------------------------------------
static const effect_uuid_t * const sAdncUuidTable[PFX_ID_CNT] = {
        FX_IID_AEC,
        FX_IID_NS,
        FX_IID_AGC
};

const effect_uuid_t * Adnc_ProcIdToUuid(int procId)
{
    if (procId >= PFX_ID_CNT) {
        return EFFECT_UUID_NULL;
    }
    return sAdncUuidTable[procId];
}

uint32_t Adnc_UuidToProcId(const effect_uuid_t * uuid)
{
    size_t i;
    for (i = 0; i < PFX_ID_CNT; i++) {
        if (memcmp(uuid, sAdncUuidTable[i], sizeof(*uuid)) == 0) {
            break;
        }
    }
    return i;
}


//------------------------------------------------------------------------------
// Acoustic Echo Canceler (AEC)
//------------------------------------------------------------------------------
int aec_init (adnc_pfx_effect_t *effect)
{
    ALOGV("aec_init [noop]");
    return 0;
}

int aec_create(adnc_pfx_effect_t *effect)
{
    ALOGV("aec_create [noop]");
    return aec_init (effect);
}

int aec_reset(adnc_pfx_effect_t *effect)
{
    ALOGV("aec_reset [noop]");
    return 0;
}

int aec_get_parameter(adnc_pfx_effect_t     *effect,
                    void              *pParam,
                    size_t            *pValueSize,
                    void              *pValue)
{
    int status = 0;
    uint32_t param = *(uint32_t *)pParam;

    if (*pValueSize < sizeof(uint32_t)) {
        return -EINVAL;
    }
    /* NOT SUPPORTED
    switch (param) {
    case AEC_PARAM_ECHO_DELAY:
    case AEC_PARAM_PROPERTIES:
        break;
    default:
        ALOGW("aec_get_parameter() unknown param %08x value %08x", param, *(uint32_t *)pValue);
        status = -EINVAL;
        break;
    }
    return status;
    */
    return -EINVAL;
}

int aec_set_parameter (adnc_pfx_effect_t *effect, void *pParam, void *pValue)
{
    int status = 0;
    uint32_t param = *(uint32_t *)pParam;
    uint32_t value = *(uint32_t *)pValue;

    /* NOT SUPPORTED
    switch (param) {
    case AEC_PARAM_ECHO_DELAY:
    case AEC_PARAM_PROPERTIES:
        ALOGV("aec_setParameter() echo delay %d us, status %d", value, status);
        break;
    default:
        ALOGW("aec_setParameter() unknown param %08x value %08x", param, *(uint32_t *)pValue);
        status = -EINVAL;
        break;
    }
    */
    return status;
}

void aec_enable(adnc_pfx_effect_t *effect)
{
    ALOGV("aec_enable [noop]");
}

void aec_disable(adnc_pfx_effect_t *effect)
{
    ALOGV("aec_disable [noop]");
}

int aec_set_device(adnc_pfx_effect_t *effect, uint32_t device)
{
    ALOGV("aec_set_device(device=%08x) [noop]", device);

    /*
    switch(device) {
    case AUDIO_DEVICE_OUT_EARPIECE:
        break;
    case AUDIO_DEVICE_OUT_SPEAKER:
        break;
    case AUDIO_DEVICE_OUT_WIRED_HEADSET:
    case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
    default:
        break;
    }
    */

    return 0;
}

static const adnc_pfx_ops_t aec_ops = {
        aec_create,
        aec_init,
        aec_reset,
        aec_enable,
        aec_disable,
        aec_set_parameter,
        aec_get_parameter,
        aec_set_device,
};


//------------------------------------------------------------------------------
// Noise Suppression (NS)
//------------------------------------------------------------------------------
int ns_init (adnc_pfx_effect_t *effect)
{
    ALOGV("ns_init [noop]");

    return 0;
}

int ns_create(adnc_pfx_effect_t *effect)
{
    ALOGV("ns_create %p", effect);

    return ns_init (effect);
}

int ns_get_parameter(adnc_pfx_effect_t     *effect,
                   void              *pParam,
                   size_t            *pValueSize,
                   void              *pValue)
{
    int status = 0;
    return status;
}

int ns_set_parameter(adnc_pfx_effect_t *effect, void *pParam, void *pValue)
{
    int status = 0;
    return status;
}

void ns_enable(adnc_pfx_effect_t *effect)
{
    ALOGV("ns_enable [noop]");
}

void ns_disable(adnc_pfx_effect_t *effect)
{
    ALOGV("ns_disable [noop]");
}

static const adnc_pfx_ops_t ns_ops = {
        ns_create,
        ns_init,
        NULL,
        ns_enable,
        ns_disable,
        ns_set_parameter,
        ns_get_parameter,
        NULL,
};


//------------------------------------------------------------------------------
// Automatic Gain Control (AGC)
//------------------------------------------------------------------------------
int agc_init (adnc_pfx_effect_t *effect)
{
    ALOGV("agc_init  [noop]");

    return 0;
}

int agc_create(adnc_pfx_effect_t *effect)
{
    ALOGV("agc_create %p", effect);

    return agc_init (effect);
}

int agc_get_parameter(adnc_pfx_effect_t     *effect,
                   void              *pParam,
                   size_t            *pValueSize,
                   void              *pValue)
{
    int status = 0;
    return status;
}

int agc_set_parameter(adnc_pfx_effect_t *effect, void *pParam, void *pValue)
{
    int status = 0;
    return status;
}

void agc_enable(adnc_pfx_effect_t *effect)
{
    ALOGV("agc_enable [noop]");
}

void agc_disable(adnc_pfx_effect_t *effect)
{
    ALOGV("agc_disable [noop]");
}

static const adnc_pfx_ops_t agc_ops = {
        agc_create,
        agc_init,
        NULL,
        agc_enable,
        agc_disable,
        agc_set_parameter,
        agc_get_parameter,
        NULL,
};

//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
static const adnc_pfx_ops_t *sPreProcOps[PFX_ID_CNT] = {
        &aec_ops,
        &ns_ops,
        &agc_ops
};

//------------------------------------------------------------------------------
// Pre-processing effect functions
//------------------------------------------------------------------------------
extern const struct effect_interface_s sEffectInterface;

#define BAD_STATE_ABORT(from, to) \
        LOG_ALWAYS_FATAL("Bad state transition from %d to %d", from, to);

void AdncSession_SetProcEnabled(adnc_pfx_session_t *session, uint32_t procId, bool enabled);

int AdncPreProFx_SetState(adnc_pfx_effect_t *effect, uint32_t state)
{
    int status = 0;
    ALOGV("AdncPreProFx_SetState procId %d, new %d old %d", effect->procId, state, effect->state);
    switch(state) {
    case PFX_EFFECT_STATE_INIT:
        switch(effect->state) {
        case PFX_EFFECT_STATE_ACTIVE:
            effect->ops->disable(effect);
            AdncSession_SetProcEnabled(effect->session, effect->procId, false);
        case PFX_EFFECT_STATE_CONFIG:
        case PFX_EFFECT_STATE_CREATED:
        case PFX_EFFECT_STATE_INIT:
            break;
        default:
            BAD_STATE_ABORT(effect->state, state);
        }
        break;
    case PFX_EFFECT_STATE_CREATED:
        switch(effect->state) {
        case PFX_EFFECT_STATE_INIT:
            status = effect->ops->create(effect);
            break;
        case PFX_EFFECT_STATE_CREATED:
        case PFX_EFFECT_STATE_ACTIVE:
        case PFX_EFFECT_STATE_CONFIG:
            ALOGE("Effect_SetState invalid transition");
            status = -ENOSYS;
            break;
        default:
            BAD_STATE_ABORT(effect->state, state);
        }
        break;
    case PFX_EFFECT_STATE_CONFIG:
        switch(effect->state) {
        case PFX_EFFECT_STATE_INIT:
            ALOGE("Effect_SetState invalid transition");
            status = -ENOSYS;
            break;
        case PFX_EFFECT_STATE_ACTIVE:
            effect->ops->disable(effect);
            AdncSession_SetProcEnabled(effect->session, effect->procId, false);
            break;
        case PFX_EFFECT_STATE_CREATED:
        case PFX_EFFECT_STATE_CONFIG:
            break;
        default:
            BAD_STATE_ABORT(effect->state, state);
        }
        break;
    case PFX_EFFECT_STATE_ACTIVE:
        switch(effect->state) {
        case PFX_EFFECT_STATE_INIT:
        case PFX_EFFECT_STATE_CREATED:
            ALOGE("Effect_SetState invalid transition");
            status = -ENOSYS;
            break;
        case PFX_EFFECT_STATE_ACTIVE:
            // enabling an already enabled effect is just ignored
            break;
        case PFX_EFFECT_STATE_CONFIG:
            effect->ops->enable(effect);
            AdncSession_SetProcEnabled(effect->session, effect->procId, true);
            break;
        default:
            BAD_STATE_ABORT(effect->state, state);
        }
        break;
    default:
        BAD_STATE_ABORT(effect->state, state);
    }
    if (status == 0) {
        effect->state = state;
    }
    return status;
}

int AdncPreProFx_Init(adnc_pfx_effect_t *effect, uint32_t procId)
{
    ALOGV(" AdncPreProFx_Init(procId=%d)", procId);
    effect->itfe = &sEffectInterface;
    effect->ops = sPreProcOps[procId];
    effect->procId = procId;
    effect->state = PFX_EFFECT_STATE_INIT;
    return 0;
}

int AdncPreProFx_Create(adnc_pfx_effect_t *effect,
               adnc_pfx_session_t *session,
               effect_handle_t  *interface)
{
    ALOGV(" AdncPreProFx_Create(effect=%p)", effect);
    effect->session = session;
    *interface = (effect_handle_t)&effect->itfe;
    return AdncPreProFx_SetState(effect, PFX_EFFECT_STATE_CREATED);
}

int AdncPreProFx_Release(adnc_pfx_effect_t *effect)
{
    return AdncPreProFx_SetState(effect, PFX_EFFECT_STATE_INIT);
}


//------------------------------------------------------------------------------
// Session functions
//------------------------------------------------------------------------------
/*
 *  Initialize a session context.
 *  Must be called with a lock on sAdncBundleLock.
 */
int AdncSession_Init_l(adnc_pfx_session_t *session)
{
    ALOGV("AdncSession_Init()");
    size_t i;
    int status = 0;

    session->state = PFX_SESSION_STATE_INIT;
    session->audioSource = AUDIO_SOURCE_DEFAULT;
    //session->audioSessionId = ES305_SESSION_ID_NONE;  // FIXME not used delete?
    session->ioHandle = ES305_IO_HANDLE_NONE;
    session->createdMsk = 0;
    session->activeMsk  = 0;
    // initialize each effect for this session context
    for (i = 0; i < PFX_ID_CNT && status == 0; i++) {
        status = AdncPreProFx_Init(&session->effects[i], i);
    }
    return status;
}

/*
 * Must be called with a lock on sAdncBundleLock.
 */
int AdncSession_CreateEffect_l(adnc_pfx_session_t *session,
                             int32_t procId,
                             effect_handle_t  *interface)
{
    int status = -ENOMEM;
    ALOGV("AdncSession_CreateEffect handle=%d procId %d, old createdMsk %08x",
            session->ioHandle, procId, session->createdMsk);

    status = AdncPreProFx_Create(&session->effects[procId], session, interface);
    if (status >= 0) {
        ALOGV("  AdncSession_CreateEffect OK");
        session->createdMsk |= (1 << procId);
    }
    return status;
}

int AdncSession_SetConfig(adnc_pfx_session_t *session, effect_config_t *config)
{
    ALOGV("AdncSession_SetConfig [noop]");
    return 0;
}

void AdncSession_GetConfig(adnc_pfx_session_t *session, effect_config_t *config)
{
    ALOGV("AdncSession_GetConfig [noop]");
}

int AdncSession_SetReverseConfig(adnc_pfx_session_t *session, effect_config_t *config)
{
    ALOGV("AdncSession_SetReverseConfig [noop]");
    return 0;
}

void AdncSession_GetReverseConfig(adnc_pfx_session_t *session, effect_config_t *config)
{
    ALOGV("AdncSession_GetReverseConfig [noop]");
}

void AdncSession_SetProcEnabled(adnc_pfx_session_t *session, uint32_t procId, bool enabled)
{
    ALOGV("AdncSession_SetProcEnabled [noop] proc %d, enabled %d", procId, enabled);
    //no need to reevaluate session settings, if recording is currently ongoing, we'll reevaluate
    //  through eS305_AddEffect()
}

int AdncSession_SetSource(adnc_pfx_session_t *session, audio_source_t source)
{
    session->audioSource = source;
    return 0;
}

//------------------------------------------------------------------------------
// Bundle functions
//------------------------------------------------------------------------------
#define ADNC_BUNDLE_NO_INIT 1
static int sAdncBundleInitStatus = ADNC_BUNDLE_NO_INIT;
static adnc_pfx_session_t sAdncSessions[ADNC_PFX_NUM_SESSION];
static pthread_mutex_t sAdncBundleLock;

/* Returns a session context for the given IO handle
 * Returns an existing session context if the IO handle matches, initializes a new one otherwise.
 * Returns NULL if no more session contexts are available
 * Must be called with a lock on sAdncBundleLock
 */
adnc_pfx_session_t *AdncBundle_GetSession_l(int32_t procId, int32_t sessionId, int32_t ioId)
{
    size_t i;
    for (i = 0; i < ADNC_PFX_NUM_SESSION; i++) {
        if (sAdncSessions[i].ioHandle == ioId) {
            return &sAdncSessions[i];
        }
    }
    for (i = 0; i < ADNC_PFX_NUM_SESSION; i++) {
        if (sAdncSessions[i].ioHandle == ES305_IO_HANDLE_NONE) {
            //sAdncSessions[i].audioSessionId = sessionId; // FIXME not used delete?
            sAdncSessions[i].ioHandle = ioId;
            return &sAdncSessions[i];
        }
    }
    ALOGV("AdncBundle_GetSession_l");
    return NULL;
}

/*
 * Must be called with a lock on sAdncBundleLock.
 */
int AdncBundle_Init_l() {
    size_t i;
    int status = 0;

    if (sAdncBundleInitStatus <= 0) {
        return sAdncBundleInitStatus;
    }
    // initialize all the session contexts that this bundle supports
    for (i = 0; i < ADNC_PFX_NUM_SESSION && status == 0; i++) {
        status = AdncSession_Init_l(&sAdncSessions[i]);
    }
    sAdncBundleInitStatus = status;
    return sAdncBundleInitStatus;
}

/*
 * Must be called with a lock on sAdncBundleLock.
 */
int AdncBundle_Release_l() {
    ALOGV("AdncBundle_Release_l()");

    Adnc_SleepInt_l();

    for (int i = 0 ; i < ES305_NUM_CTRL ; i++) {
        if (eS305_ctrl.fd[i] >= 0) {
            close(eS305_ctrl.fd[i]);
        }
        eS305_ctrl.fd[i] = -1;
    }
    return 0;
}

const effect_descriptor_t *AdncBundle_GetDescriptor(const effect_uuid_t *uuid)
{
    size_t i;
    for (i = 0; i < PFX_ID_CNT; i++) {
        if (memcmp(&adnc_pfx_descriptors[i]->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
            return adnc_pfx_descriptors[i];
        }
    }
    return NULL;
}

/*
 * Debug only: display session contexts
 */
void AdncBundle_logv_dumpSessions() {
    ALOGV("Sessions:");
    for (int i=0 ; i<ADNC_PFX_NUM_SESSION ; i++) {
        ALOGV(" session %d handle=%d cre=%2x act=%2x",
           i, sAdncSessions[i].ioHandle, sAdncSessions[i].createdMsk, sAdncSessions[i].activeMsk);
    }
}

//------------------------------------------------------------------------------
// Effect Control Interface Implementation
//------------------------------------------------------------------------------
int AdncVoiceProcessingFx_Command(effect_handle_t  self,
                            uint32_t            cmdCode,
                            uint32_t            cmdSize,
                            void                *pCmdData,
                            uint32_t            *replySize,
                            void                *pReplyData)
{
    adnc_pfx_effect_t * effect = (adnc_pfx_effect_t *) self;
    int retsize;
    int status;

    if (effect == NULL){
        return -EINVAL;
    }

    ALOGV("AdncVoiceProcessingFx_Command: command %d cmdSize %d",cmdCode, cmdSize);

    switch (cmdCode){
        case EFFECT_CMD_INIT:
            if (pReplyData == NULL || *replySize != sizeof(int)){
                return -EINVAL;
            }
            if (effect->ops->init) {
                effect->ops->init(effect);
            }
            *(int *)pReplyData = 0;
            break;

        case EFFECT_CMD_SET_CONFIG: {
            if (pCmdData    == NULL||
                cmdSize     != sizeof(effect_config_t)||
                pReplyData  == NULL||
                *replySize  != sizeof(int)){
                ALOGV("AdncVoiceProcessingFx_Command cmdCode Case: "
                        "EFFECT_CMD_SET_CONFIG: ERROR");
                return -EINVAL;
            }

            *(int *)pReplyData = AdncSession_SetConfig(effect->session, (effect_config_t *)pCmdData);

            if (*(int *)pReplyData != 0) {
                break;
            }
            if (effect->state != PFX_EFFECT_STATE_ACTIVE) {
                *(int *)pReplyData = AdncPreProFx_SetState(effect, PFX_EFFECT_STATE_CONFIG);
            }
            } break;

        case EFFECT_CMD_GET_CONFIG:
            if (pReplyData == NULL ||
                *replySize != sizeof(effect_config_t)) {
                ALOGV("\tLVM_ERROR : AdncVoiceProcessingFx_Command cmdCode Case: "
                        "EFFECT_CMD_GET_CONFIG: ERROR");
                return -EINVAL;
            }

            AdncSession_GetConfig(effect->session, (effect_config_t *)pReplyData);
            break;

        case EFFECT_CMD_SET_CONFIG_REVERSE:
            if (pCmdData == NULL ||
                cmdSize != sizeof(effect_config_t) ||
                pReplyData == NULL ||
                *replySize != sizeof(int)) {
                ALOGV("AdncVoiceProcessingFx_Command cmdCode Case: "
                        "EFFECT_CMD_SET_CONFIG_REVERSE: ERROR");
                return -EINVAL;
            }
            *(int *)pReplyData = AdncSession_SetReverseConfig(effect->session,
                                                          (effect_config_t *)pCmdData);
            if (*(int *)pReplyData != 0) {
                break;
            }
            break;

        case EFFECT_CMD_GET_CONFIG_REVERSE:
            if (pReplyData == NULL ||
                *replySize != sizeof(effect_config_t)){
                ALOGV("AdncVoiceProcessingFx_Command cmdCode Case: "
                        "EFFECT_CMD_GET_CONFIG_REVERSE: ERROR");
                return -EINVAL;
            }
            AdncSession_GetReverseConfig(effect->session, (effect_config_t *)pCmdData);
            break;

        case EFFECT_CMD_RESET:
            if (effect->ops->reset) {
                effect->ops->reset(effect);
            }
            break;

        case EFFECT_CMD_GET_PARAM:{
            if (pCmdData == NULL ||
                    cmdSize < (int)sizeof(effect_param_t) ||
                    pReplyData == NULL ||
                    *replySize < (int)sizeof(effect_param_t)){
                ALOGV("AdncVoiceProcessingFx_Command cmdCode Case: "
                        "EFFECT_CMD_GET_PARAM: ERROR");
                return -EINVAL;
            }
            effect_param_t *p = (effect_param_t *)pCmdData;

            memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + p->psize);

            p = (effect_param_t *)pReplyData;

            int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);

            if (effect->ops->get_parameter) {
                p->status = effect->ops->get_parameter(effect, p->data,
                                                       (size_t  *)&p->vsize,
                                                       p->data + voffset);
                *replySize = sizeof(effect_param_t) + voffset + p->vsize;
            }
        } break;

        case EFFECT_CMD_SET_PARAM:{
            if (pCmdData == NULL||
                    cmdSize < (int)sizeof(effect_param_t) ||
                    pReplyData == NULL ||
                    *replySize != sizeof(int32_t)){
                ALOGV("AdncVoiceProcessingFx_Command cmdCode Case: "
                        "EFFECT_CMD_SET_PARAM: ERROR");
                return -EINVAL;
            }
            effect_param_t *p = (effect_param_t *) pCmdData;

            if (p->psize != sizeof(int32_t)){
                ALOGV("AdncVoiceProcessingFx_Command cmdCode Case: "
                        "EFFECT_CMD_SET_PARAM: ERROR, psize is not sizeof(int32_t)");
                return -EINVAL;
            }
            if (effect->ops->set_parameter) {
                *(int *)pReplyData = effect->ops->set_parameter(effect,
                                                                (void *)p->data,
                                                                p->data + p->psize);
            }
        } break;

        case EFFECT_CMD_ENABLE:
            if (pReplyData == NULL || *replySize != sizeof(int)){
                ALOGV("AdncVoiceProcessingFx_Command cmdCode Case: EFFECT_CMD_ENABLE: ERROR");
                return -EINVAL;
            }
            *(int *)pReplyData = AdncPreProFx_SetState(effect, PFX_EFFECT_STATE_ACTIVE);
            break;

        case EFFECT_CMD_DISABLE:
            if (pReplyData == NULL || *replySize != sizeof(int)){
                ALOGV("AdncVoiceProcessingFx_Command cmdCode Case: EFFECT_CMD_DISABLE: ERROR");
                return -EINVAL;
            }
            *(int *)pReplyData  = AdncPreProFx_SetState(effect, PFX_EFFECT_STATE_CONFIG);
            break;

        case EFFECT_CMD_SET_DEVICE:
        case EFFECT_CMD_SET_INPUT_DEVICE:
            if (pCmdData == NULL ||
                cmdSize != sizeof(uint32_t)) {
                ALOGV("AdncVoiceProcessingFx_Command cmdCode Case: EFFECT_CMD_SET_DEVICE: ERROR");
                return -EINVAL;
            }

            if (effect->ops->set_device) {
                effect->ops->set_device(effect, *(uint32_t *)pCmdData);
            }
            break;

        case EFFECT_CMD_SET_VOLUME:
        case EFFECT_CMD_SET_AUDIO_MODE:
        case EFFECT_CMD_SET_FEATURE_CONFIG:
            break;

        case EFFECT_CMD_SET_AUDIO_SOURCE:
            if (pCmdData == NULL ||
                    cmdSize != sizeof(uint32_t)) {
                ALOGV("AdncVoiceProcessingFx_Command cmdCode Case: EFFECT_CMD_SET_AUDIO_SOURCE: ERROR");
                return -EINVAL;
            }
            return AdncSession_SetSource(effect->session, (audio_source_t) *(uint32_t *)pCmdData);
            break;

        default:
            return -EINVAL;
    }
    return 0;
}


int AdncVoiceProcessingFx_GetDescriptor(effect_handle_t   self,
                                  effect_descriptor_t *pDescriptor)
{
    adnc_pfx_effect_t * effect = (adnc_pfx_effect_t *) self;

    if (effect == NULL || pDescriptor == NULL) {
        return -EINVAL;
    }

    memcpy(pDescriptor, adnc_pfx_descriptors[effect->procId], sizeof(effect_descriptor_t));

    return 0;
}


// effect_handle_t interface implementation for effect
const struct effect_interface_s sEffectInterface = {
        NULL, /* Process */
        AdncVoiceProcessingFx_Command,
        AdncVoiceProcessingFx_GetDescriptor,
        NULL
};
//------------------------------------------------------------------------------
// Effect Library Interface Implementation
//------------------------------------------------------------------------------

int adnc_create(const effect_uuid_t *uuid,
            int32_t         sessionId,
            int32_t         ioId,
            effect_handle_t *pInterface)
{
    ALOGV("adnc_create: uuid: %08x session %d IO: %d", uuid->timeLow, sessionId, ioId);

    int status = 0;
    const effect_descriptor_t *desc;
    adnc_pfx_session_t *session;
    uint32_t procId;

    pthread_mutex_lock(&sAdncBundleLock);

    if (AdncBundle_Init_l() != 0) {
        status = sAdncBundleInitStatus;
        goto exit;
    }

    desc =  AdncBundle_GetDescriptor(uuid);
    if (desc == NULL) {
        ALOGW("  adnc_create: fx not found uuid: %08x", uuid->timeLow);
        status = -EINVAL;
        goto exit;
    }
    procId = Adnc_UuidToProcId(&desc->type);

    session = AdncBundle_GetSession_l(procId, sessionId, ioId);
    if (session == NULL) {
        ALOGW("  adnc_create: no more session available");
        status = -EINVAL;
        goto exit;
    }

    status = AdncSession_CreateEffect_l(session, procId, pInterface);

    if (status < 0 && session->createdMsk == 0) {
        session->ioHandle = ES305_IO_HANDLE_NONE;
    }

exit:
    pthread_mutex_unlock(&sAdncBundleLock);
    return status;
}

int adnc_release(effect_handle_t interface)
{
    int i, status = 0;
    ALOGV("adnc_release %p", interface);

    // the effect handle comes from the effect framework, ok to cast
    const adnc_pfx_effect_t * fx = (adnc_pfx_effect_t *) interface;

    const uint32_t removalMsk = ~(1 << fx->procId);

    pthread_mutex_lock(&sAdncBundleLock);

    if (AdncBundle_Init_l() != 0) {
        status = sAdncBundleInitStatus;
        goto exit;
    }

    if (fx->session->ioHandle == 0) {
        status = -EINVAL;
        goto exit;
    }

    // effect is released, flag it as inactive and not created
    fx->session->createdMsk &= removalMsk;
    fx->session->activeMsk  &= removalMsk;

    // configuration has changed, reevaluate
    status = Adnc_ReevaluateUsageInt_l(fx->session->ioHandle);
    // not checking the return status here: if there was an error,
    //    we still need to free the session and wouldn't exit here

    // free session if it has no more effects
    if (fx->session->createdMsk == 0) {
        ALOGV(" resetting session on handle %d after effect release", fx->session->ioHandle);
        const int statusInit = AdncSession_Init_l(fx->session);
        if (status == 0) {
            status = statusInit;
        }
    }

exit:
    pthread_mutex_unlock(&sAdncBundleLock);
    return status;
}

int adnc_get_descriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
    if (pDescriptor == NULL || uuid == NULL){
        ALOGV("adnc_get_descriptor() invalid params");
        return -EINVAL;
    }

    const effect_descriptor_t *desc = AdncBundle_GetDescriptor(uuid);
    if (desc == NULL) {
        ALOGV("adnc_get_descriptor() not found");
        return -EINVAL;
    }

    ALOGV("adnc_get_descriptor() got fx %s", desc->name);

    memcpy(pDescriptor, desc, sizeof(effect_descriptor_t));
    return 0;
}

audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
    tag : AUDIO_EFFECT_LIBRARY_TAG,
    version : EFFECT_LIBRARY_API_VERSION,
    name : "Audience Voice Preprocessing Library",
    implementor : "The Android Open Source Project",
    create_effect : adnc_create,
    release_effect : adnc_release,
    get_descriptor : adnc_get_descriptor
};

//-------------------------------------------------------
// eS305 control interface
//-------------------------------------------------------
int Adnc_SetAutomaticGainControlInt_l(bool agc_on)
{
    ALOGV("Adnc_SetAutomaticGainControlInt_l(%d)", agc_on);

    if (eS305_ctrl.fd[ES305_CTRL_TX_AGC_ENABLE] < 0) {
        ALOGV("  opening eS305 path for agc");
        eS305_ctrl.fd[ES305_CTRL_TX_AGC_ENABLE] = open(ES305_TX_AGC_ENABLE_PATH, O_RDWR);
        if (eS305_ctrl.fd[ES305_CTRL_TX_AGC_ENABLE] < 0) {
            ALOGE("  Cannot open eS305 path for agc: %s", strerror(errno));
            return -ENODEV;
        }
    }

    if (agc_on) {
        write(eS305_ctrl.fd[ES305_CTRL_TX_AGC_ENABLE], ES305_AGC_ON, strlen(ES305_AGC_ON));
    } else {
        write(eS305_ctrl.fd[ES305_CTRL_TX_AGC_ENABLE], ES305_AEC_OFF, strlen(ES305_AGC_OFF));
    }
    return 0;
}

int Adnc_SetEchoCancellationInt_l(bool aec_on)
{
    ALOGV("Adnc_SetEchoCancellationInt_l(%d)", aec_on);

    if (eS305_ctrl.fd[ES305_CTRL_AEC_ENABLE] < 0) {
        ALOGV("  opening eS305 path for aec");
        eS305_ctrl.fd[ES305_CTRL_AEC_ENABLE] = open(ES305_AEC_ENABLE_PATH, O_RDWR);
        if (eS305_ctrl.fd[ES305_CTRL_AEC_ENABLE] < 0) {
            ALOGE("  Cannot open eS305 path for aec: %s", strerror(errno));
            return -ENODEV;
        }
    }

    if (aec_on) {
        write(eS305_ctrl.fd[ES305_CTRL_AEC_ENABLE], ES305_AEC_ON, strlen(ES305_AEC_ON));
    } else {
        write(eS305_ctrl.fd[ES305_CTRL_AEC_ENABLE], ES305_AEC_OFF, strlen(ES305_AEC_OFF));
    }
    return 0;
}

int Adnc_SetNoiseSuppressionInt_l(bool ns_on)
{
    ALOGV("Adnc_SetNoiseSuppressionInt(%d)", ns_on);

    if (eS305_ctrl.fd[ES305_CTRL_TX_NS_LEVEL] < 0) {
        ALOGV("  opening eS305 path for ns");
        eS305_ctrl.fd[ES305_CTRL_TX_NS_LEVEL] = open(ES305_TX_NS_LEVEL_PATH, O_RDWR);
        if (eS305_ctrl.fd[ES305_CTRL_TX_NS_LEVEL] < 0) {
            ALOGE("  Cannot open eS305 path for ns: %s", strerror(errno));
            return -ENODEV;
        }
    }

    if (ns_on) {
        if (eS305_ctrl.requested_preset == ES305_PRESET_ASRA_HANDHELD) {
            ALOGV("  setting ns to %s", ES305_NS_VOICE_REC_HANDHELD_ON);
            write(eS305_ctrl.fd[ES305_CTRL_TX_NS_LEVEL],
                    ES305_NS_VOICE_REC_HANDHELD_ON, strlen(ES305_NS_VOICE_REC_HANDHELD_ON));
        } else if ((eS305_ctrl.requested_preset == ES305_PRESET_ASRA_DESKTOP)
                || (eS305_ctrl.requested_preset == ES305_PRESET_ASRA_HEADSET)) {
            ALOGV("  setting ns to %s", ES305_NS_VOICE_REC_SINGLE_MIC_ON);
            write(eS305_ctrl.fd[ES305_CTRL_TX_NS_LEVEL],
                    ES305_NS_VOICE_REC_SINGLE_MIC_ON, strlen(ES305_NS_VOICE_REC_SINGLE_MIC_ON));
        } else {
            ALOGV("  setting ns to %s", ES305_NS_DEFAULT_ON);
            write(eS305_ctrl.fd[ES305_CTRL_TX_NS_LEVEL],
                    ES305_NS_DEFAULT_ON, strlen(ES305_NS_DEFAULT_ON));
        }
    } else {
        ALOGV("  setting ns to %s", ES305_NS_OFF);
        write(eS305_ctrl.fd[ES305_CTRL_TX_NS_LEVEL], ES305_NS_OFF, strlen(ES305_NS_OFF));
    }
    return 0;
}

int Adnc_SetVoiceProcessingInt_l(bool vp_on)
{
    if (eS305_ctrl.fd[ES305_CTRL_VOICE_PROCESSING] < 0) {
        ALOGV("  opening eS305 path for VP");
        eS305_ctrl.fd[ES305_CTRL_VOICE_PROCESSING] = open(ES305_VOICE_PROCESSING_PATH, O_RDWR);
        if (eS305_ctrl.fd[ES305_CTRL_VOICE_PROCESSING] < 0) {
            ALOGE("    cannot open eS305 path for VP: %s", strerror(errno));
            return -ENODEV;
        }
    }
    if (vp_on) {
        write(eS305_ctrl.fd[ES305_CTRL_VOICE_PROCESSING], ES305_ON, strlen(ES305_ON));
    } else {
        write(eS305_ctrl.fd[ES305_CTRL_VOICE_PROCESSING], ES305_OFF, strlen(ES305_OFF));
    }
    return 0;
}

/*
 * Put the eS305 to sleep
 * Post condition when no error: eS305_ctrl.current_preset == ES305_PRESET_OFF
 */
int Adnc_SleepInt_l()
{
    if (eS305_ctrl.current_preset == ES305_PRESET_OFF) {
        return 0;
    }

    ALOGV(" Adnc_SleepInt()_l setting VP off + sleep 1");

    Adnc_SetVoiceProcessingInt_l(false /*vp_on*/);

    ALOGV("  Adnc_SetSleepInt_l");
    if (eS305_ctrl.fd[ES305_CTRL_SLEEP] < 0) {
        ALOGV("  opening eS305 path for sleep");
        eS305_ctrl.fd[ES305_CTRL_SLEEP] = open(ES305_SLEEP_PATH, O_RDWR);
        if (eS305_ctrl.fd[ES305_CTRL_SLEEP] < 0) {
            ALOGE("    cannot open eS305 path for sleep: %s", strerror(errno));
            return -ENODEV;
        }
    }

    write(eS305_ctrl.fd[ES305_CTRL_SLEEP], ES305_ON, strlen(ES305_ON));

    eS305_ctrl.current_preset = ES305_PRESET_OFF;

    return 0;
}

/*
 * Apply the eS305_ctrl.requested_preset preset after turning VP on
 * Post condition when no error: eS305_ctrl.current_preset == eS305_ctrl.requested_preset
 */
int Adnc_ApplyPresetInt_l()
{
    ALOGV("Adnc_ApplyPresetInt() current_preset=%d, requested_preset=%d",
            eS305_ctrl.current_preset, eS305_ctrl.requested_preset);

    if (eS305_ctrl.requested_preset == eS305_ctrl.current_preset) {
        ALOGV("  nothing to do, preset %d is current", eS305_ctrl.requested_preset);
        return 0;
    }

    // preset off implies going to sleep
    if (eS305_ctrl.requested_preset == ES305_PRESET_OFF) {
        return Adnc_SleepInt_l();
    }

    // voice processing must be on before setting the preset
    if ((eS305_ctrl.current_preset == ES305_PRESET_OFF)
            || (eS305_ctrl.current_preset == ES305_PRESET_INIT)) {
        const int status = Adnc_SetVoiceProcessingInt_l(true /*vp_on*/);
        if (status != 0) {
            return status;
        }
    }

    if (eS305_ctrl.fd[ES305_CTRL_PRESET] < 0) {
        ALOGV("  opening eS305 path for PRESET");
        eS305_ctrl.fd[ES305_CTRL_PRESET] = open(ES305_PRESET_PATH, O_RDWR);
    }
    if (eS305_ctrl.fd[ES305_CTRL_PRESET] < 0) {
        ALOGE("  Cannot open eS305 path for PRESET: %s", strerror(errno));
        return -ENODEV;
    }

    char str[8];
    sprintf(str, "%d", eS305_ctrl.requested_preset);
    write(eS305_ctrl.fd[ES305_CTRL_PRESET], str, strlen(str));

    eS305_ctrl.current_preset = eS305_ctrl.requested_preset;

    return 0;
}


/*
 * Apply the settings of given the session context
 */
int Adnc_ApplySettingsFromSessionContextInt_l(adnc_pfx_session_t * session)
{
    ALOGV("Adnc_ApplySettingsFromSessionContextInt_l cre=%2x ac=%2x handle=%d",
                  session->createdMsk, session->activeMsk, session->ioHandle);
    int status = 0;

    if (session->ioHandle != eS305_ctrl.ioHandle) {
        return status;
    }

    // NS: special case of noise suppression, always reset according to effect state
    //     as default desirable value might differ from the preset
    const bool ns_on = ((session->activeMsk & (1 << PFX_ID_NS)) != 0);
    status = Adnc_SetNoiseSuppressionInt_l(ns_on /*ns_on*/);

    // AEC
    if ((session->createdMsk & (1 << PFX_ID_AEC))         /* the effect has been created */
            && (session->activeMsk  & (1 << PFX_ID_AEC))) /* the effect is active        */
    {
        Adnc_SetEchoCancellationInt_l(true /*aec_on*/);
    }

    // AGC
    if ((session->createdMsk & (1 << PFX_ID_AGC))         /* the effect has been created */
            && (session->activeMsk  & (1 << PFX_ID_AGC))) /* the effect is active        */
    {
        Adnc_SetAutomaticGainControlInt_l(true /*agc_on*/);
    }

    return status;
}

/*
 * Return a value between 0 and ADNC_PFX_NUM_SESSION-1 if a session context has the given handle,
 *        -1 if the handle isn't in handled by one of the sessions.
 * Must be called with a lock on sAdncBundleLock
 */
int Adnc_SessionNumberForHandle_l(audio_io_handle_t handle)
{
    for (int i = 0 ; i < ADNC_PFX_NUM_SESSION ; i++) {
        if (sAdncSessions[i].ioHandle == handle) {
            return i;
        }
    }
    return -1;
}


/*
 * Apply the settings of the session matching the given IO handle.
 * Must be called with a lock on sAdncBundleLock
 */
int Adnc_ApplySettingsForHandleInt_l(audio_io_handle_t handle)
{
    ALOGV(" Adnc_ApplySettingsForHandleInt_l(handle=%d)", handle);
    // indicates whether this effect bundle currently has a session context for this IO handle
    bool hasSession = false;
    int status = 0;
    int i;

    if (sAdncBundleInitStatus != 0) {
        // This assumes that the default config of the eS305 after setting a preset
        //    is the correct configuration.
        ALOGV(" no effect settings to apply for IO handle %d, no effect bundle", handle);
        return status;
    }

    const int sessionId = Adnc_SessionNumberForHandle_l(handle);
    if (sessionId >= 0) {
        ALOGV("  applying settings from session num %d", sessionId);
        status = Adnc_ApplySettingsFromSessionContextInt_l( &sAdncSessions[sessionId] );
    }
    else {
        ALOGV("  no session found for handle %d", handle);
    }

    return status;
}

/*
 * Reevaluate the usage of the eS305 based on the given IO handle.
 * Must be called with a lock on sAdncBundleLock
 */
int Adnc_ReevaluateUsageInt_l(audio_io_handle_t handle)
{
    ALOGV(" Adnc_ReevaluateUsageInt_l(handle=%d) current_preset=%d requested_preset=%d",
            handle, eS305_ctrl.current_preset, eS305_ctrl.requested_preset);
    int status = 0;
    if ((eS305_ctrl.requested_preset == ES305_PRESET_OFF) || (handle == ES305_IO_HANDLE_NONE)) {
        status = Adnc_SleepInt_l();
    } else {
        const int sessionId = Adnc_SessionNumberForHandle_l(handle);
        if (sessionId >= 0) {
            // recording active, use the preset only if there is an effect,
            //                   reset preset to off otherwise
            if (sAdncSessions[sessionId].activeMsk != 0) {
                status = Adnc_ApplyPresetInt_l();
                if (status == 0) {
                    //apply the settings of the session associated with the handle (if any)
                    status = Adnc_ApplySettingsForHandleInt_l(handle);
                }
            } else {
                status = Adnc_SleepInt_l();
            }
        }
    }
    return status;
}


//-------------------------------------------------------
// eS305 public control interface from HAL
//-------------------------------------------------------
int eS305_UsePreset(int preset)
{
    ALOGV("eS305_UsePreset(%d) current=%d handle=%d",
            preset, eS305_ctrl.current_preset, eS305_ctrl.ioHandle);

    int status = 0;

    pthread_mutex_lock(&sAdncBundleLock);

    //if (preset != -1) { AdncBundle_logv_dumpSessions(); }

    // allow preset transition from any preset to any other during recording,
    //    except from one ASRA preset to another
    if (eS305_ctrl.ioHandle != ES305_IO_HANDLE_NONE) {
        switch(eS305_ctrl.current_preset) {
        case ES305_PRESET_ASRA_HANDHELD:
        case ES305_PRESET_ASRA_DESKTOP:
        case ES305_PRESET_ASRA_HEADSET:
            switch(preset) {
            case ES305_PRESET_ASRA_HANDHELD:
            case ES305_PRESET_ASRA_DESKTOP:
            case ES305_PRESET_ASRA_HEADSET:
                ALOGV("  not switching from ASRA preset %d to %d during voice recognition",
                        eS305_ctrl.current_preset, preset);
                status = -EINVAL;
                goto exit;
            default:
                // transitioning from ASRA to non-ASRA: valid
                break;
            }
            break;
        default:
            // transitioning from non-ASRA: valid
            break;
        }
    }

    eS305_ctrl.requested_preset = preset;

    status = AdncBundle_Init_l();
    if (status != 0) {
        ALOGE(" error applying preset, bundle failed to initialize");
        goto exit;
    }

    status = Adnc_ReevaluateUsageInt_l(eS305_ctrl.ioHandle);

exit:
    pthread_mutex_unlock(&sAdncBundleLock);
    return status;
}


int eS305_SetActiveIoHandle(audio_io_handle_t handle)
{
    ALOGV("eS305_SetActiveIoHandle(%d)", handle);

    pthread_mutex_lock(&sAdncBundleLock);

    int status = AdncBundle_Init_l();
    if (status != 0) {
        ALOGE(" error setting active handle, bundle failed to initialize");
        pthread_mutex_unlock(&sAdncBundleLock);
        return status;
    }

    status = Adnc_ReevaluateUsageInt_l(handle);

    if (status == 0) {
        eS305_ctrl.ioHandle = handle;
    } else {
        ALOGE("  failed to update for new handle %d (current preset = %d)",
                handle, eS305_ctrl.current_preset);
    }

    pthread_mutex_unlock(&sAdncBundleLock);

    return status;
}


int eS305_AddEffect(effect_descriptor_t * descr, audio_io_handle_t handle)
{
    ALOGV("eS305_AddEffect(handle=%d)", handle);

    pthread_mutex_lock(&sAdncBundleLock);

    int status = AdncBundle_Init_l();
    if (status != 0) {
        ALOGE(" error setting adding effect, bundle failed to initialize");
        pthread_mutex_unlock(&sAdncBundleLock);
        return status;
    }

    if (descr == NULL){
        ALOGV(" eS305_AddEffect() ERROR effect descriptor is NULL");
        pthread_mutex_unlock(&sAdncBundleLock);
        return -EINVAL;
    }

    uint32_t procId = Adnc_UuidToProcId(&descr->type);

    adnc_pfx_session_t * session = AdncBundle_GetSession_l(
            procId, ES305_SESSION_ID_NONE, handle/*ioId*/);

    if (session != NULL) {
        // mark the effect as active
        session->activeMsk  |= (1 << procId);

        // update settings if necessary
        Adnc_ReevaluateUsageInt_l(session->ioHandle);
    }

    pthread_mutex_unlock(&sAdncBundleLock);

    return status;
}


int eS305_RemoveEffect(effect_descriptor_t * descr, audio_io_handle_t handle)
{
    ALOGV("eS305_RemoveEffect()");

    pthread_mutex_lock(&sAdncBundleLock);

    int status = AdncBundle_Init_l();
    if (status != 0) {
        ALOGE(" error setting removing effect, bundle failed to initialize");
        pthread_mutex_unlock(&sAdncBundleLock);
        return status;
    }

    if (descr == NULL){
        ALOGV(" eS305_AddEffect() ERROR effect descriptor is NULL");
        pthread_mutex_unlock(&sAdncBundleLock);
        return -EINVAL;
    }

    uint32_t procId = Adnc_UuidToProcId(&descr->type);

    adnc_pfx_session_t * session = AdncBundle_GetSession_l(
            procId, ES305_SESSION_ID_NONE, handle/*ioId*/);

    if (session != NULL) {
        // mark the effect as inactive
        session->activeMsk  &= ~(1 << procId);

        // update settings if necessary
        Adnc_ReevaluateUsageInt_l(session->ioHandle);
    }

    pthread_mutex_unlock(&sAdncBundleLock);

    return status;
}


int eS305_Release() {
    ALOGV("eS305_Release()");

    pthread_mutex_lock(&sAdncBundleLock);

    AdncBundle_Release_l();

    pthread_mutex_unlock(&sAdncBundleLock);

    return 0;
}

} // extern "C"