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