/* * 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"