| /* |
| * Copyright (C) 2011 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 <stdlib.h> |
| #include <string.h> |
| #define LOG_TAG "PreProcessing" |
| //#define LOG_NDEBUG 0 |
| #include <audio_effects/effect_aec.h> |
| #include <audio_effects/effect_agc.h> |
| #include <hardware/audio_effect.h> |
| #include <utils/Log.h> |
| #include <utils/Timers.h> |
| #include <audio_effects/effect_agc2.h> |
| #include <audio_effects/effect_ns.h> |
| #include <audio_processing.h> |
| #include <module_common_types.h> |
| |
| // undefine to perform multi channels API functional tests |
| //#define DUAL_MIC_TEST |
| |
| //------------------------------------------------------------------------------ |
| // local definitions |
| //------------------------------------------------------------------------------ |
| |
| // maximum number of sessions |
| #define PREPROC_NUM_SESSIONS 8 |
| |
| // types of pre processing modules |
| enum preproc_id { |
| PREPROC_AGC, // Automatic Gain Control |
| PREPROC_AGC2, // Automatic Gain Control 2 |
| PREPROC_AEC, // Acoustic Echo Canceler |
| PREPROC_NS, // Noise Suppressor |
| PREPROC_NUM_EFFECTS |
| }; |
| |
| // Session state |
| enum preproc_session_state { |
| PREPROC_SESSION_STATE_INIT, // initialized |
| PREPROC_SESSION_STATE_CONFIG // configuration received |
| }; |
| |
| // Effect/Preprocessor state |
| enum preproc_effect_state { |
| PREPROC_EFFECT_STATE_INIT, // initialized |
| PREPROC_EFFECT_STATE_CREATED, // webRTC engine created |
| PREPROC_EFFECT_STATE_CONFIG, // configuration received/disabled |
| PREPROC_EFFECT_STATE_ACTIVE // active/enabled |
| }; |
| |
| // handle on webRTC engine |
| typedef void* preproc_fx_handle_t; |
| |
| typedef struct preproc_session_s preproc_session_t; |
| typedef struct preproc_effect_s preproc_effect_t; |
| typedef struct preproc_ops_s preproc_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 preproc_ops_s { |
| int (*create)(preproc_effect_t* fx); |
| int (*init)(preproc_effect_t* fx); |
| int (*reset)(preproc_effect_t* fx); |
| void (*enable)(preproc_effect_t* fx); |
| void (*disable)(preproc_effect_t* fx); |
| int (*set_parameter)(preproc_effect_t* fx, void* param, void* value); |
| int (*get_parameter)(preproc_effect_t* fx, void* param, uint32_t* size, void* value); |
| int (*set_device)(preproc_effect_t* fx, uint32_t device); |
| }; |
| |
| // Effect context |
| struct preproc_effect_s { |
| const struct effect_interface_s* itfe; |
| uint32_t procId; // type of pre processor (enum preproc_id) |
| uint32_t state; // current state (enum preproc_effect_state) |
| preproc_session_t* session; // session the effect is on |
| const preproc_ops_t* ops; // effect ops table |
| preproc_fx_handle_t engine; // handle on webRTC engine |
| uint32_t type; // subtype of effect |
| #ifdef DUAL_MIC_TEST |
| bool aux_channels_on; // support auxiliary channels |
| size_t cur_channel_config; // current auciliary channel configuration |
| #endif |
| }; |
| |
| // Session context |
| struct preproc_session_s { |
| struct preproc_effect_s effects[PREPROC_NUM_EFFECTS]; // effects in this session |
| uint32_t state; // current state (enum preproc_session_state) |
| int id; // audio session ID |
| int io; // handle of input stream this session is on |
| rtc::scoped_refptr<webrtc::AudioProcessing> |
| apm; // handle on webRTC audio processing module (APM) |
| // Audio Processing module builder |
| webrtc::AudioProcessingBuilder ap_builder; |
| // frameCount represents the size of the buffers used for processing, and must represent 10ms. |
| size_t frameCount; |
| uint32_t samplingRate; // sampling rate at effect process interface |
| uint32_t inChannelCount; // input channel count |
| uint32_t outChannelCount; // output channel count |
| uint32_t createdMsk; // bit field containing IDs of crested pre processors |
| uint32_t enabledMsk; // bit field containing IDs of enabled pre processors |
| uint32_t processedMsk; // bit field containing IDs of pre processors already |
| // processed in current round |
| // audio config strucutre |
| webrtc::AudioProcessing::Config config; |
| webrtc::StreamConfig inputConfig; // input stream configuration |
| webrtc::StreamConfig outputConfig; // output stream configuration |
| uint32_t revChannelCount; // number of channels on reverse stream |
| uint32_t revEnabledMsk; // bit field containing IDs of enabled pre processors |
| // with reverse channel |
| uint32_t revProcessedMsk; // bit field containing IDs of pre processors with reverse |
| // channel already processed in current round |
| webrtc::StreamConfig revConfig; // reverse stream configuration. |
| }; |
| |
| #ifdef DUAL_MIC_TEST |
| enum { |
| PREPROC_CMD_DUAL_MIC_ENABLE = EFFECT_CMD_FIRST_PROPRIETARY, // enable dual mic mode |
| PREPROC_CMD_DUAL_MIC_PCM_DUMP_START, // start pcm capture |
| PREPROC_CMD_DUAL_MIC_PCM_DUMP_STOP // stop pcm capture |
| }; |
| |
| enum { |
| CHANNEL_CFG_MONO, |
| CHANNEL_CFG_STEREO, |
| CHANNEL_CFG_MONO_AUX, |
| CHANNEL_CFG_STEREO_AUX, |
| CHANNEL_CFG_CNT, |
| CHANNEL_CFG_FIRST_AUX = CHANNEL_CFG_MONO_AUX, |
| }; |
| |
| const channel_config_t sDualMicConfigs[CHANNEL_CFG_CNT] = { |
| {AUDIO_CHANNEL_IN_MONO, 0}, |
| {AUDIO_CHANNEL_IN_STEREO, 0}, |
| {AUDIO_CHANNEL_IN_FRONT, AUDIO_CHANNEL_IN_BACK}, |
| {AUDIO_CHANNEL_IN_STEREO, AUDIO_CHANNEL_IN_RIGHT}}; |
| |
| bool sHasAuxChannels[PREPROC_NUM_EFFECTS] = { |
| false, // PREPROC_AGC |
| false, // PREPROC_AGC2 |
| true, // PREPROC_AEC |
| true, // PREPROC_NS |
| }; |
| |
| bool gDualMicEnabled; |
| FILE* gPcmDumpFh; |
| static pthread_mutex_t gPcmDumpLock = PTHREAD_MUTEX_INITIALIZER; |
| #endif |
| |
| //------------------------------------------------------------------------------ |
| // 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 |
| |
| // Automatic Gain Control |
| static const effect_descriptor_t sAgcDescriptor = { |
| {0x0a8abfe0, 0x654c, 0x11e0, 0xba26, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type |
| {0xaa8130e0, 0x66fc, 0x11e0, 0xbad0, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // 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", |
| "The Android Open Source Project"}; |
| |
| // Automatic Gain Control 2 |
| static const effect_descriptor_t sAgc2Descriptor = { |
| {0xae3c653b, 0xbe18, 0x4ab8, 0x8938, {0x41, 0x8f, 0x0a, 0x7f, 0x06, 0xac}}, // type |
| {0x89f38e65, 0xd4d2, 0x4d64, 0xad0e, {0x2b, 0x3e, 0x79, 0x9e, 0xa8, 0x86}}, // 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 2", |
| "The Android Open Source Project"}; |
| |
| // Acoustic Echo Cancellation |
| static const effect_descriptor_t sAecDescriptor = { |
| {0x7b491460, 0x8d4d, 0x11e0, 0xbd61, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type |
| {0xbb392ec0, 0x8d4d, 0x11e0, 0xa896, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // 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", |
| "The Android Open Source Project"}; |
| |
| // Noise suppression |
| static const effect_descriptor_t sNsDescriptor = { |
| {0x58b4b260, 0x8e06, 0x11e0, 0xaa8e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type |
| {0xc06c8400, 0x8e06, 0x11e0, 0x9cb6, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // 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", |
| "The Android Open Source Project"}; |
| |
| static const effect_descriptor_t* sDescriptors[PREPROC_NUM_EFFECTS] = {&sAgcDescriptor, |
| &sAgc2Descriptor, |
| &sAecDescriptor, |
| &sNsDescriptor}; |
| |
| //------------------------------------------------------------------------------ |
| // Helper functions |
| //------------------------------------------------------------------------------ |
| |
| const effect_uuid_t* const sUuidToPreProcTable[PREPROC_NUM_EFFECTS] = {FX_IID_AGC, |
| FX_IID_AGC2, |
| FX_IID_AEC, FX_IID_NS}; |
| |
| const effect_uuid_t* ProcIdToUuid(int procId) { |
| if (procId >= PREPROC_NUM_EFFECTS) { |
| return EFFECT_UUID_NULL; |
| } |
| return sUuidToPreProcTable[procId]; |
| } |
| |
| uint32_t UuidToProcId(const effect_uuid_t* uuid) { |
| size_t i; |
| for (i = 0; i < PREPROC_NUM_EFFECTS; i++) { |
| if (memcmp(uuid, sUuidToPreProcTable[i], sizeof(*uuid)) == 0) { |
| break; |
| } |
| } |
| return i; |
| } |
| |
| bool HasReverseStream(uint32_t procId) { |
| if (procId == PREPROC_AEC) { |
| return true; |
| } |
| return false; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Automatic Gain Control (AGC) |
| //------------------------------------------------------------------------------ |
| |
| static const int kAgcDefaultTargetLevel = 3; |
| static const int kAgcDefaultCompGain = 9; |
| static const bool kAgcDefaultLimiter = true; |
| |
| int Agc2Init(preproc_effect_t* effect) { |
| ALOGV("Agc2Init"); |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.gain_controller2.fixed_digital.gain_db = 0.f; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| return 0; |
| } |
| |
| int AgcInit(preproc_effect_t* effect) { |
| ALOGV("AgcInit"); |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.gain_controller1.target_level_dbfs = kAgcDefaultTargetLevel; |
| effect->session->config.gain_controller1.compression_gain_db = kAgcDefaultCompGain; |
| effect->session->config.gain_controller1.enable_limiter = kAgcDefaultLimiter; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| return 0; |
| } |
| |
| int Agc2Create(preproc_effect_t* effect) { |
| Agc2Init(effect); |
| return 0; |
| } |
| |
| int AgcCreate(preproc_effect_t* effect) { |
| AgcInit(effect); |
| return 0; |
| } |
| |
| int Agc2GetParameter(preproc_effect_t* effect, void* pParam, uint32_t* pValueSize, void* pValue) { |
| int status = 0; |
| uint32_t param = *(uint32_t*)pParam; |
| agc2_settings_t* pProperties = (agc2_settings_t*)pValue; |
| |
| switch (param) { |
| case AGC2_PARAM_FIXED_DIGITAL_GAIN: |
| if (*pValueSize < sizeof(float)) { |
| *pValueSize = 0.f; |
| return -EINVAL; |
| } |
| break; |
| case AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR: |
| if (*pValueSize < sizeof(int32_t)) { |
| *pValueSize = 0; |
| return -EINVAL; |
| } |
| break; |
| case AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN: |
| if (*pValueSize < sizeof(float)) { |
| *pValueSize = 0.f; |
| return -EINVAL; |
| } |
| break; |
| case AGC2_PARAM_PROPERTIES: |
| if (*pValueSize < sizeof(agc2_settings_t)) { |
| *pValueSize = 0; |
| return -EINVAL; |
| } |
| break; |
| |
| default: |
| ALOGW("Agc2GetParameter() unknown param %08x", param); |
| status = -EINVAL; |
| break; |
| } |
| |
| effect->session->config = effect->session->apm->GetConfig(); |
| switch (param) { |
| case AGC2_PARAM_FIXED_DIGITAL_GAIN: |
| *(float*)pValue = |
| (float)(effect->session->config.gain_controller2.fixed_digital.gain_db); |
| ALOGV("Agc2GetParameter() target level %f dB", *(float*)pValue); |
| break; |
| case AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR: |
| // WebRTC only supports RMS level estimator now |
| *(uint32_t*)pValue = (uint32_t)(0); |
| ALOGV("Agc2GetParameter() level estimator RMS"); |
| break; |
| case AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN: |
| *(float*)pValue = (float)(2.0); |
| ALOGV("Agc2GetParameter() extra saturation margin %f dB", *(float*)pValue); |
| break; |
| case AGC2_PARAM_PROPERTIES: |
| pProperties->fixedDigitalGain = |
| (float)(effect->session->config.gain_controller2.fixed_digital.gain_db); |
| pProperties->level_estimator = 0; |
| pProperties->extraSaturationMargin = 2.0; |
| break; |
| default: |
| ALOGW("Agc2GetParameter() unknown param %d", param); |
| status = -EINVAL; |
| break; |
| } |
| |
| return status; |
| } |
| |
| int AgcGetParameter(preproc_effect_t* effect, void* pParam, uint32_t* pValueSize, void* pValue) { |
| int status = 0; |
| uint32_t param = *(uint32_t*)pParam; |
| t_agc_settings* pProperties = (t_agc_settings*)pValue; |
| |
| switch (param) { |
| case AGC_PARAM_TARGET_LEVEL: |
| case AGC_PARAM_COMP_GAIN: |
| if (*pValueSize < sizeof(int16_t)) { |
| *pValueSize = 0; |
| return -EINVAL; |
| } |
| break; |
| case AGC_PARAM_LIMITER_ENA: |
| if (*pValueSize < sizeof(bool)) { |
| *pValueSize = 0; |
| return -EINVAL; |
| } |
| break; |
| case AGC_PARAM_PROPERTIES: |
| if (*pValueSize < sizeof(t_agc_settings)) { |
| *pValueSize = 0; |
| return -EINVAL; |
| } |
| break; |
| |
| default: |
| ALOGW("AgcGetParameter() unknown param %08x", param); |
| status = -EINVAL; |
| break; |
| } |
| |
| effect->session->config = effect->session->apm->GetConfig(); |
| switch (param) { |
| case AGC_PARAM_TARGET_LEVEL: |
| *(int16_t*)pValue = |
| (int16_t)(effect->session->config.gain_controller1.target_level_dbfs * -100); |
| ALOGV("AgcGetParameter() target level %d milliBels", *(int16_t*)pValue); |
| break; |
| case AGC_PARAM_COMP_GAIN: |
| *(int16_t*)pValue = |
| (int16_t)(effect->session->config.gain_controller1.compression_gain_db * -100); |
| ALOGV("AgcGetParameter() comp gain %d milliBels", *(int16_t*)pValue); |
| break; |
| case AGC_PARAM_LIMITER_ENA: |
| *(bool*)pValue = (bool)(effect->session->config.gain_controller1.enable_limiter); |
| ALOGV("AgcGetParameter() limiter enabled %s", |
| (*(int16_t*)pValue != 0) ? "true" : "false"); |
| break; |
| case AGC_PARAM_PROPERTIES: |
| pProperties->targetLevel = |
| (int16_t)(effect->session->config.gain_controller1.target_level_dbfs * -100); |
| pProperties->compGain = |
| (int16_t)(effect->session->config.gain_controller1.compression_gain_db * -100); |
| pProperties->limiterEnabled = |
| (bool)(effect->session->config.gain_controller1.enable_limiter); |
| break; |
| default: |
| ALOGW("AgcGetParameter() unknown param %d", param); |
| status = -EINVAL; |
| break; |
| } |
| return status; |
| } |
| |
| int Agc2SetParameter(preproc_effect_t* effect, void* pParam, void* pValue) { |
| int status = 0; |
| uint32_t param = *(uint32_t*)pParam; |
| float valueFloat = 0.f; |
| agc2_settings_t* pProperties = (agc2_settings_t*)pValue; |
| effect->session->config = effect->session->apm->GetConfig(); |
| switch (param) { |
| case AGC2_PARAM_FIXED_DIGITAL_GAIN: |
| valueFloat = (float)(*(int32_t*)pValue); |
| ALOGV("Agc2SetParameter() fixed digital gain %f dB", valueFloat); |
| effect->session->config.gain_controller2.fixed_digital.gain_db = valueFloat; |
| break; |
| case AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR: |
| ALOGV("Agc2SetParameter() level estimator %d", *(uint32_t*)pValue); |
| if (*(uint32_t*)pValue != 0) { |
| // only RMS is supported |
| status = -EINVAL; |
| } |
| break; |
| case AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN: |
| valueFloat = (float)(*(int32_t*)pValue); |
| ALOGV("Agc2SetParameter() extra saturation margin %f dB", valueFloat); |
| if (valueFloat != 2.0) { |
| // extra_staturation_margin_db is no longer configurable in webrtc |
| status = -EINVAL; |
| } |
| break; |
| case AGC2_PARAM_PROPERTIES: |
| ALOGV("Agc2SetParameter() properties gain %f, level %d margin %f", |
| pProperties->fixedDigitalGain, pProperties->level_estimator, |
| pProperties->extraSaturationMargin); |
| effect->session->config.gain_controller2.fixed_digital.gain_db = |
| pProperties->fixedDigitalGain; |
| if (pProperties->level_estimator != 0 || pProperties->extraSaturationMargin != 2.0) { |
| status = -EINVAL; |
| } |
| break; |
| default: |
| ALOGW("Agc2SetParameter() unknown param %08x value %08x", param, *(uint32_t*)pValue); |
| status = -EINVAL; |
| break; |
| } |
| effect->session->apm->ApplyConfig(effect->session->config); |
| |
| ALOGV("Agc2SetParameter() done status %d", status); |
| |
| return status; |
| } |
| |
| int AgcSetParameter(preproc_effect_t* effect, void* pParam, void* pValue) { |
| int status = 0; |
| uint32_t param = *(uint32_t*)pParam; |
| t_agc_settings* pProperties = (t_agc_settings*)pValue; |
| effect->session->config = effect->session->apm->GetConfig(); |
| switch (param) { |
| case AGC_PARAM_TARGET_LEVEL: |
| ALOGV("AgcSetParameter() target level %d milliBels", *(int16_t*)pValue); |
| effect->session->config.gain_controller1.target_level_dbfs = |
| (-(*(int16_t*)pValue / 100)); |
| break; |
| case AGC_PARAM_COMP_GAIN: |
| ALOGV("AgcSetParameter() comp gain %d milliBels", *(int16_t*)pValue); |
| effect->session->config.gain_controller1.compression_gain_db = |
| (*(int16_t*)pValue / 100); |
| break; |
| case AGC_PARAM_LIMITER_ENA: |
| ALOGV("AgcSetParameter() limiter enabled %s", *(bool*)pValue ? "true" : "false"); |
| effect->session->config.gain_controller1.enable_limiter = (*(bool*)pValue); |
| break; |
| case AGC_PARAM_PROPERTIES: |
| ALOGV("AgcSetParameter() properties level %d, gain %d limiter %d", |
| pProperties->targetLevel, pProperties->compGain, pProperties->limiterEnabled); |
| effect->session->config.gain_controller1.target_level_dbfs = |
| -(pProperties->targetLevel / 100); |
| effect->session->config.gain_controller1.compression_gain_db = |
| pProperties->compGain / 100; |
| effect->session->config.gain_controller1.enable_limiter = pProperties->limiterEnabled; |
| break; |
| default: |
| ALOGW("AgcSetParameter() unknown param %08x value %08x", param, *(uint32_t*)pValue); |
| status = -EINVAL; |
| break; |
| } |
| effect->session->apm->ApplyConfig(effect->session->config); |
| |
| ALOGV("AgcSetParameter() done status %d", status); |
| |
| return status; |
| } |
| |
| void Agc2Enable(preproc_effect_t* effect) { |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.gain_controller2.enabled = true; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| } |
| |
| void AgcEnable(preproc_effect_t* effect) { |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.gain_controller1.enabled = true; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| } |
| |
| void Agc2Disable(preproc_effect_t* effect) { |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.gain_controller2.enabled = false; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| } |
| |
| void AgcDisable(preproc_effect_t* effect) { |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.gain_controller1.enabled = false; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| } |
| |
| static const preproc_ops_t sAgcOps = {AgcCreate, AgcInit, NULL, AgcEnable, AgcDisable, |
| AgcSetParameter, AgcGetParameter, NULL}; |
| |
| static const preproc_ops_t sAgc2Ops = {Agc2Create, Agc2Init, NULL, |
| Agc2Enable, Agc2Disable, Agc2SetParameter, |
| Agc2GetParameter, NULL}; |
| |
| //------------------------------------------------------------------------------ |
| // Acoustic Echo Canceler (AEC) |
| //------------------------------------------------------------------------------ |
| |
| |
| int AecInit(preproc_effect_t* effect) { |
| ALOGV("AecInit"); |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.echo_canceller.mobile_mode = true; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| return 0; |
| } |
| |
| int AecCreate(preproc_effect_t* effect) { |
| AecInit(effect); |
| return 0; |
| } |
| |
| int AecGetParameter(preproc_effect_t* effect, void* pParam, uint32_t* pValueSize, void* pValue) { |
| int status = 0; |
| uint32_t param = *(uint32_t*)pParam; |
| |
| if (*pValueSize < sizeof(uint32_t)) { |
| return -EINVAL; |
| } |
| switch (param) { |
| case AEC_PARAM_ECHO_DELAY: |
| case AEC_PARAM_PROPERTIES: |
| *(uint32_t*)pValue = 1000 * effect->session->apm->stream_delay_ms(); |
| ALOGV("AecGetParameter() echo delay %d us", *(uint32_t*)pValue); |
| break; |
| case AEC_PARAM_MOBILE_MODE: |
| effect->session->config = effect->session->apm->GetConfig(); |
| *(uint32_t*)pValue = effect->session->config.echo_canceller.mobile_mode; |
| ALOGV("AecGetParameter() mobile mode %d us", *(uint32_t*)pValue); |
| break; |
| default: |
| ALOGW("AecGetParameter() unknown param %08x value %08x", param, *(uint32_t*)pValue); |
| status = -EINVAL; |
| break; |
| } |
| return status; |
| } |
| |
| int AecSetParameter(preproc_effect_t* effect, void* pParam, void* pValue) { |
| int status = 0; |
| uint32_t param = *(uint32_t*)pParam; |
| uint32_t value = *(uint32_t*)pValue; |
| |
| switch (param) { |
| case AEC_PARAM_ECHO_DELAY: |
| case AEC_PARAM_PROPERTIES: |
| status = effect->session->apm->set_stream_delay_ms(value / 1000); |
| ALOGV("AecSetParameter() echo delay %d us, status %d", value, status); |
| break; |
| case AEC_PARAM_MOBILE_MODE: |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.echo_canceller.mobile_mode = value; |
| ALOGV("AecSetParameter() mobile mode %d us", value); |
| effect->session->apm->ApplyConfig(effect->session->config); |
| break; |
| default: |
| ALOGW("AecSetParameter() unknown param %08x value %08x", param, *(uint32_t*)pValue); |
| status = -EINVAL; |
| break; |
| } |
| return status; |
| } |
| |
| void AecEnable(preproc_effect_t* effect) { |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.echo_canceller.enabled = true; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| } |
| |
| void AecDisable(preproc_effect_t* effect) { |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.echo_canceller.enabled = false; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| } |
| |
| int AecSetDevice(preproc_effect_t* effect, uint32_t device) { |
| ALOGV("AecSetDevice %08x", device); |
| |
| if (audio_is_input_device(device)) { |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| static const preproc_ops_t sAecOps = {AecCreate, AecInit, NULL, |
| AecEnable, AecDisable, AecSetParameter, |
| AecGetParameter, AecSetDevice}; |
| |
| //------------------------------------------------------------------------------ |
| // Noise Suppression (NS) |
| //------------------------------------------------------------------------------ |
| |
| static const webrtc::AudioProcessing::Config::NoiseSuppression::Level kNsDefaultLevel = |
| webrtc::AudioProcessing::Config::NoiseSuppression::kModerate; |
| |
| int NsInit(preproc_effect_t* effect) { |
| ALOGV("NsInit"); |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.noise_suppression.level = kNsDefaultLevel; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| effect->type = NS_TYPE_SINGLE_CHANNEL; |
| return 0; |
| } |
| |
| int NsCreate(preproc_effect_t* effect) { |
| NsInit(effect); |
| return 0; |
| } |
| |
| int NsGetParameter(preproc_effect_t* /*effect __unused*/, void* /*pParam __unused*/, |
| uint32_t* /*pValueSize __unused*/, void* /*pValue __unused*/) { |
| int status = 0; |
| return status; |
| } |
| |
| int NsSetParameter(preproc_effect_t* effect, void* pParam, void* pValue) { |
| int status = 0; |
| uint32_t param = *(uint32_t*)pParam; |
| uint32_t value = *(uint32_t*)pValue; |
| effect->session->config = effect->session->apm->GetConfig(); |
| switch (param) { |
| case NS_PARAM_LEVEL: |
| effect->session->config.noise_suppression.level = |
| (webrtc::AudioProcessing::Config::NoiseSuppression::Level)value; |
| ALOGV("NsSetParameter() level %d", value); |
| break; |
| default: |
| ALOGW("NsSetParameter() unknown param %08x value %08x", param, value); |
| status = -EINVAL; |
| } |
| effect->session->apm->ApplyConfig(effect->session->config); |
| |
| return status; |
| } |
| |
| void NsEnable(preproc_effect_t* effect) { |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.noise_suppression.enabled = true; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| } |
| |
| void NsDisable(preproc_effect_t* effect) { |
| ALOGV("NsDisable"); |
| effect->session->config = effect->session->apm->GetConfig(); |
| effect->session->config.noise_suppression.enabled = false; |
| effect->session->apm->ApplyConfig(effect->session->config); |
| } |
| |
| static const preproc_ops_t sNsOps = {NsCreate, NsInit, NULL, NsEnable, |
| NsDisable, NsSetParameter, NsGetParameter, NULL}; |
| |
| static const preproc_ops_t* sPreProcOps[PREPROC_NUM_EFFECTS] = {&sAgcOps, |
| &sAgc2Ops, |
| &sAecOps, &sNsOps}; |
| |
| //------------------------------------------------------------------------------ |
| // Effect functions |
| //------------------------------------------------------------------------------ |
| |
| void Session_SetProcEnabled(preproc_session_t* session, uint32_t procId, bool enabled); |
| |
| extern "C" const struct effect_interface_s sEffectInterface; |
| extern "C" const struct effect_interface_s sEffectInterfaceReverse; |
| |
| #define BAD_STATE_ABORT(from, to) LOG_ALWAYS_FATAL("Bad state transition from %d to %d", from, to); |
| |
| int Effect_SetState(preproc_effect_t* effect, uint32_t state) { |
| int status = 0; |
| ALOGV("Effect_SetState proc %d, new %d old %d", effect->procId, state, effect->state); |
| switch (state) { |
| case PREPROC_EFFECT_STATE_INIT: |
| switch (effect->state) { |
| case PREPROC_EFFECT_STATE_ACTIVE: |
| effect->ops->disable(effect); |
| Session_SetProcEnabled(effect->session, effect->procId, false); |
| break; |
| case PREPROC_EFFECT_STATE_CONFIG: |
| case PREPROC_EFFECT_STATE_CREATED: |
| case PREPROC_EFFECT_STATE_INIT: |
| break; |
| default: |
| BAD_STATE_ABORT(effect->state, state); |
| } |
| break; |
| case PREPROC_EFFECT_STATE_CREATED: |
| switch (effect->state) { |
| case PREPROC_EFFECT_STATE_INIT: |
| status = effect->ops->create(effect); |
| break; |
| case PREPROC_EFFECT_STATE_CREATED: |
| case PREPROC_EFFECT_STATE_ACTIVE: |
| case PREPROC_EFFECT_STATE_CONFIG: |
| ALOGE("Effect_SetState invalid transition"); |
| status = -ENOSYS; |
| break; |
| default: |
| BAD_STATE_ABORT(effect->state, state); |
| } |
| break; |
| case PREPROC_EFFECT_STATE_CONFIG: |
| switch (effect->state) { |
| case PREPROC_EFFECT_STATE_INIT: |
| ALOGE("Effect_SetState invalid transition"); |
| status = -ENOSYS; |
| break; |
| case PREPROC_EFFECT_STATE_ACTIVE: |
| effect->ops->disable(effect); |
| Session_SetProcEnabled(effect->session, effect->procId, false); |
| break; |
| case PREPROC_EFFECT_STATE_CREATED: |
| case PREPROC_EFFECT_STATE_CONFIG: |
| break; |
| default: |
| BAD_STATE_ABORT(effect->state, state); |
| } |
| break; |
| case PREPROC_EFFECT_STATE_ACTIVE: |
| switch (effect->state) { |
| case PREPROC_EFFECT_STATE_INIT: |
| case PREPROC_EFFECT_STATE_CREATED: |
| ALOGE("Effect_SetState invalid transition"); |
| status = -ENOSYS; |
| break; |
| case PREPROC_EFFECT_STATE_ACTIVE: |
| // enabling an already enabled effect is just ignored |
| break; |
| case PREPROC_EFFECT_STATE_CONFIG: |
| effect->ops->enable(effect); |
| Session_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 Effect_Init(preproc_effect_t* effect, uint32_t procId) { |
| if (HasReverseStream(procId)) { |
| effect->itfe = &sEffectInterfaceReverse; |
| } else { |
| effect->itfe = &sEffectInterface; |
| } |
| effect->ops = sPreProcOps[procId]; |
| effect->procId = procId; |
| effect->state = PREPROC_EFFECT_STATE_INIT; |
| return 0; |
| } |
| |
| int Effect_Create(preproc_effect_t* effect, preproc_session_t* session, |
| effect_handle_t* interface) { |
| effect->session = session; |
| *interface = (effect_handle_t)&effect->itfe; |
| return Effect_SetState(effect, PREPROC_EFFECT_STATE_CREATED); |
| } |
| |
| int Effect_Release(preproc_effect_t* effect) { |
| return Effect_SetState(effect, PREPROC_EFFECT_STATE_INIT); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Session functions |
| //------------------------------------------------------------------------------ |
| |
| #define RESAMPLER_QUALITY SPEEX_RESAMPLER_QUALITY_VOIP |
| |
| static const int kPreprocDefaultSr = 16000; |
| static const int kPreProcDefaultCnl = 1; |
| |
| int Session_Init(preproc_session_t* session) { |
| size_t i; |
| int status = 0; |
| |
| session->state = PREPROC_SESSION_STATE_INIT; |
| session->id = 0; |
| session->io = 0; |
| session->createdMsk = 0; |
| for (i = 0; i < PREPROC_NUM_EFFECTS && status == 0; i++) { |
| status = Effect_Init(&session->effects[i], i); |
| } |
| return status; |
| } |
| |
| extern "C" int Session_CreateEffect(preproc_session_t* session, int32_t procId, |
| effect_handle_t* interface) { |
| int status = -ENOMEM; |
| |
| ALOGV("Session_CreateEffect procId %d, createdMsk %08x", procId, session->createdMsk); |
| |
| if (session->createdMsk == 0) { |
| session->apm = session->ap_builder.Create(); |
| if (session->apm == NULL) { |
| ALOGW("Session_CreateEffect could not get apm engine"); |
| goto error; |
| } |
| session->frameCount = kPreprocDefaultSr / 100; |
| session->samplingRate = kPreprocDefaultSr; |
| session->inChannelCount = kPreProcDefaultCnl; |
| session->outChannelCount = kPreProcDefaultCnl; |
| session->inputConfig.set_sample_rate_hz(kPreprocDefaultSr); |
| session->inputConfig.set_num_channels(kPreProcDefaultCnl); |
| session->outputConfig.set_sample_rate_hz(kPreprocDefaultSr); |
| session->outputConfig.set_num_channels(kPreProcDefaultCnl); |
| session->revChannelCount = kPreProcDefaultCnl; |
| session->revConfig.set_sample_rate_hz(kPreprocDefaultSr); |
| session->revConfig.set_num_channels(kPreProcDefaultCnl); |
| session->enabledMsk = 0; |
| session->processedMsk = 0; |
| session->revEnabledMsk = 0; |
| session->revProcessedMsk = 0; |
| } |
| status = Effect_Create(&session->effects[procId], session, interface); |
| if (status < 0) { |
| goto error; |
| } |
| ALOGV("Session_CreateEffect OK"); |
| session->createdMsk |= (1 << procId); |
| return status; |
| |
| error: |
| if (session->createdMsk == 0) { |
| // Scoped_refptr will handle reference counting here |
| session->apm = nullptr; |
| } |
| return status; |
| } |
| |
| int Session_ReleaseEffect(preproc_session_t* session, preproc_effect_t* fx) { |
| ALOGW_IF(Effect_Release(fx) != 0, " Effect_Release() failed for proc ID %d", fx->procId); |
| session->createdMsk &= ~(1 << fx->procId); |
| if (session->createdMsk == 0) { |
| // Scoped_refptr will handle reference counting here |
| session->apm = nullptr; |
| session->id = 0; |
| } |
| |
| return 0; |
| } |
| |
| int Session_SetConfig(preproc_session_t* session, effect_config_t* config) { |
| uint32_t inCnl = audio_channel_count_from_in_mask(config->inputCfg.channels); |
| uint32_t outCnl = audio_channel_count_from_in_mask(config->outputCfg.channels); |
| |
| if (config->inputCfg.samplingRate != config->outputCfg.samplingRate || |
| config->inputCfg.format != config->outputCfg.format || |
| config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) { |
| return -EINVAL; |
| } |
| |
| ALOGV("Session_SetConfig sr %d cnl %08x", config->inputCfg.samplingRate, |
| config->inputCfg.channels); |
| |
| session->samplingRate = config->inputCfg.samplingRate; |
| session->frameCount = session->samplingRate / 100; |
| session->inChannelCount = inCnl; |
| session->outChannelCount = outCnl; |
| session->inputConfig.set_sample_rate_hz(session->samplingRate); |
| session->inputConfig.set_num_channels(inCnl); |
| session->outputConfig.set_sample_rate_hz(session->samplingRate); |
| session->outputConfig.set_num_channels(inCnl); |
| |
| session->revChannelCount = inCnl; |
| session->revConfig.set_sample_rate_hz(session->samplingRate); |
| session->revConfig.set_num_channels(inCnl); |
| |
| session->state = PREPROC_SESSION_STATE_CONFIG; |
| return 0; |
| } |
| |
| void Session_GetConfig(preproc_session_t* session, effect_config_t* config) { |
| memset(config, 0, sizeof(effect_config_t)); |
| config->inputCfg.samplingRate = config->outputCfg.samplingRate = session->samplingRate; |
| config->inputCfg.format = config->outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; |
| config->inputCfg.channels = audio_channel_in_mask_from_count(session->inChannelCount); |
| // "out" doesn't mean output device, so this is the correct API to convert channel count to mask |
| config->outputCfg.channels = audio_channel_in_mask_from_count(session->outChannelCount); |
| config->inputCfg.mask = config->outputCfg.mask = |
| (EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT); |
| } |
| |
| int Session_SetReverseConfig(preproc_session_t* session, effect_config_t* config) { |
| if (config->inputCfg.samplingRate != config->outputCfg.samplingRate || |
| config->inputCfg.format != config->outputCfg.format || |
| config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) { |
| return -EINVAL; |
| } |
| |
| ALOGV("Session_SetReverseConfig sr %d cnl %08x", config->inputCfg.samplingRate, |
| config->inputCfg.channels); |
| |
| if (session->state < PREPROC_SESSION_STATE_CONFIG) { |
| return -ENOSYS; |
| } |
| if (config->inputCfg.samplingRate != session->samplingRate || |
| config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) { |
| return -EINVAL; |
| } |
| uint32_t inCnl = audio_channel_count_from_out_mask(config->inputCfg.channels); |
| session->revChannelCount = inCnl; |
| |
| return 0; |
| } |
| |
| void Session_GetReverseConfig(preproc_session_t* session, effect_config_t* config) { |
| memset(config, 0, sizeof(effect_config_t)); |
| config->inputCfg.samplingRate = config->outputCfg.samplingRate = session->samplingRate; |
| config->inputCfg.format = config->outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; |
| config->inputCfg.channels = config->outputCfg.channels = |
| audio_channel_in_mask_from_count(session->revChannelCount); |
| config->inputCfg.mask = config->outputCfg.mask = |
| (EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT); |
| } |
| |
| void Session_SetProcEnabled(preproc_session_t* session, uint32_t procId, bool enabled) { |
| if (enabled) { |
| session->enabledMsk |= (1 << procId); |
| if (HasReverseStream(procId)) { |
| session->revEnabledMsk |= (1 << procId); |
| } |
| } else { |
| session->enabledMsk &= ~(1 << procId); |
| if (HasReverseStream(procId)) { |
| session->revEnabledMsk &= ~(1 << procId); |
| } |
| } |
| ALOGV("Session_SetProcEnabled proc %d, enabled %d enabledMsk %08x revEnabledMsk %08x", procId, |
| enabled, session->enabledMsk, session->revEnabledMsk); |
| session->processedMsk = 0; |
| if (HasReverseStream(procId)) { |
| session->revProcessedMsk = 0; |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Bundle functions |
| //------------------------------------------------------------------------------ |
| |
| static int sInitStatus = 1; |
| static preproc_session_t sSessions[PREPROC_NUM_SESSIONS]; |
| |
| preproc_session_t* PreProc_GetSession(int32_t procId, int32_t sessionId, int32_t ioId) { |
| size_t i; |
| for (i = 0; i < PREPROC_NUM_SESSIONS; i++) { |
| if (sSessions[i].id == sessionId) { |
| if (sSessions[i].createdMsk & (1 << procId)) { |
| return NULL; |
| } |
| return &sSessions[i]; |
| } |
| } |
| for (i = 0; i < PREPROC_NUM_SESSIONS; i++) { |
| if (sSessions[i].id == 0) { |
| sSessions[i].id = sessionId; |
| sSessions[i].io = ioId; |
| return &sSessions[i]; |
| } |
| } |
| return NULL; |
| } |
| |
| int PreProc_Init() { |
| size_t i; |
| int status = 0; |
| |
| if (sInitStatus <= 0) { |
| return sInitStatus; |
| } |
| for (i = 0; i < PREPROC_NUM_SESSIONS && status == 0; i++) { |
| status = Session_Init(&sSessions[i]); |
| } |
| sInitStatus = status; |
| return sInitStatus; |
| } |
| |
| const effect_descriptor_t* PreProc_GetDescriptor(const effect_uuid_t* uuid) { |
| size_t i; |
| for (i = 0; i < PREPROC_NUM_EFFECTS; i++) { |
| if (memcmp(&sDescriptors[i]->uuid, uuid, sizeof(effect_uuid_t)) == 0) { |
| return sDescriptors[i]; |
| } |
| } |
| return NULL; |
| } |
| |
| extern "C" { |
| |
| //------------------------------------------------------------------------------ |
| // Effect Control Interface Implementation |
| //------------------------------------------------------------------------------ |
| |
| int PreProcessingFx_Process(effect_handle_t self, audio_buffer_t* inBuffer, |
| audio_buffer_t* outBuffer) { |
| preproc_effect_t* effect = (preproc_effect_t*)self; |
| |
| if (effect == NULL) { |
| ALOGV("PreProcessingFx_Process() ERROR effect == NULL"); |
| return -EINVAL; |
| } |
| preproc_session_t* session = (preproc_session_t*)effect->session; |
| |
| if (inBuffer == NULL || inBuffer->raw == NULL || outBuffer == NULL || outBuffer->raw == NULL) { |
| ALOGW("PreProcessingFx_Process() ERROR bad pointer"); |
| return -EINVAL; |
| } |
| |
| if (inBuffer->frameCount != outBuffer->frameCount) { |
| ALOGW("inBuffer->frameCount %zu is not equal to outBuffer->frameCount %zu", |
| inBuffer->frameCount, outBuffer->frameCount); |
| return -EINVAL; |
| } |
| |
| if (inBuffer->frameCount != session->frameCount) { |
| ALOGW("inBuffer->frameCount %zu != %zu representing 10ms at sampling rate %d", |
| inBuffer->frameCount, session->frameCount, session->samplingRate); |
| return -EINVAL; |
| } |
| |
| session->processedMsk |= (1 << effect->procId); |
| |
| // ALOGV("PreProcessingFx_Process In %d frames enabledMsk %08x processedMsk %08x", |
| // inBuffer->frameCount, session->enabledMsk, session->processedMsk); |
| if ((session->processedMsk & session->enabledMsk) == session->enabledMsk) { |
| effect->session->processedMsk = 0; |
| if (int status = effect->session->apm->ProcessStream( |
| (const int16_t* const)inBuffer->s16, |
| (const webrtc::StreamConfig)effect->session->inputConfig, |
| (const webrtc::StreamConfig)effect->session->outputConfig, |
| (int16_t* const)outBuffer->s16); |
| status != 0) { |
| ALOGE("Process Stream failed with error %d\n", status); |
| return status; |
| } |
| return 0; |
| } else { |
| return -ENODATA; |
| } |
| } |
| |
| int PreProcessingFx_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, |
| void* pCmdData, uint32_t* replySize, void* pReplyData) { |
| preproc_effect_t* effect = (preproc_effect_t*)self; |
| |
| if (effect == NULL) { |
| return -EINVAL; |
| } |
| |
| // ALOGV("PreProcessingFx_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("PreProcessingFx_Command cmdCode Case: " |
| "EFFECT_CMD_SET_CONFIG: ERROR"); |
| return -EINVAL; |
| } |
| #ifdef DUAL_MIC_TEST |
| // make sure that the config command is accepted by making as if all effects were |
| // disabled: this is OK for functional tests |
| uint32_t enabledMsk = effect->session->enabledMsk; |
| if (gDualMicEnabled) { |
| effect->session->enabledMsk = 0; |
| } |
| #endif |
| *(int*)pReplyData = Session_SetConfig(effect->session, (effect_config_t*)pCmdData); |
| #ifdef DUAL_MIC_TEST |
| if (gDualMicEnabled) { |
| effect->session->enabledMsk = enabledMsk; |
| } |
| #endif |
| if (*(int*)pReplyData != 0) { |
| break; |
| } |
| if (effect->state != PREPROC_EFFECT_STATE_ACTIVE) { |
| *(int*)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_CONFIG); |
| } |
| } break; |
| |
| case EFFECT_CMD_GET_CONFIG: |
| if (pReplyData == NULL || *replySize != sizeof(effect_config_t)) { |
| ALOGV("\tLVM_ERROR : PreProcessingFx_Command cmdCode Case: " |
| "EFFECT_CMD_GET_CONFIG: ERROR"); |
| return -EINVAL; |
| } |
| |
| Session_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("PreProcessingFx_Command cmdCode Case: " |
| "EFFECT_CMD_SET_CONFIG_REVERSE: ERROR"); |
| return -EINVAL; |
| } |
| *(int*)pReplyData = |
| Session_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("PreProcessingFx_Command cmdCode Case: " |
| "EFFECT_CMD_GET_CONFIG_REVERSE: ERROR"); |
| return -EINVAL; |
| } |
| Session_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: { |
| effect_param_t* p = (effect_param_t*)pCmdData; |
| |
| if (pCmdData == NULL || cmdSize < sizeof(effect_param_t) || |
| cmdSize < (sizeof(effect_param_t) + p->psize) || pReplyData == NULL || |
| replySize == NULL || *replySize < (sizeof(effect_param_t) + p->psize)) { |
| ALOGV("PreProcessingFx_Command cmdCode Case: " |
| "EFFECT_CMD_GET_PARAM: ERROR"); |
| return -EINVAL; |
| } |
| |
| 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, &p->vsize, p->data + voffset); |
| *replySize = sizeof(effect_param_t) + voffset + p->vsize; |
| } |
| } break; |
| |
| case EFFECT_CMD_SET_PARAM: { |
| if (pCmdData == NULL || cmdSize < sizeof(effect_param_t) || pReplyData == NULL || |
| replySize == NULL || *replySize != sizeof(int32_t)) { |
| ALOGV("PreProcessingFx_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("PreProcessingFx_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 == NULL || *replySize != sizeof(int)) { |
| ALOGV("PreProcessingFx_Command cmdCode Case: EFFECT_CMD_ENABLE: ERROR"); |
| return -EINVAL; |
| } |
| *(int*)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_ACTIVE); |
| break; |
| |
| case EFFECT_CMD_DISABLE: |
| if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) { |
| ALOGV("PreProcessingFx_Command cmdCode Case: EFFECT_CMD_DISABLE: ERROR"); |
| return -EINVAL; |
| } |
| *(int*)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_CONFIG); |
| break; |
| |
| case EFFECT_CMD_SET_DEVICE: |
| case EFFECT_CMD_SET_INPUT_DEVICE: |
| if (pCmdData == NULL || cmdSize != sizeof(uint32_t)) { |
| ALOGV("PreProcessingFx_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: |
| break; |
| |
| #ifdef DUAL_MIC_TEST |
| ///// test commands start |
| case PREPROC_CMD_DUAL_MIC_ENABLE: { |
| if (pCmdData == NULL || cmdSize != sizeof(uint32_t) || pReplyData == NULL || |
| replySize == NULL) { |
| ALOGE("PreProcessingFx_Command cmdCode Case: " |
| "PREPROC_CMD_DUAL_MIC_ENABLE: ERROR"); |
| *replySize = 0; |
| return -EINVAL; |
| } |
| gDualMicEnabled = *(bool*)pCmdData; |
| if (gDualMicEnabled) { |
| effect->aux_channels_on = sHasAuxChannels[effect->procId]; |
| } else { |
| effect->aux_channels_on = false; |
| } |
| effect->cur_channel_config = |
| (effect->session->inChannelCount == 1) ? CHANNEL_CFG_MONO : CHANNEL_CFG_STEREO; |
| |
| ALOGV("PREPROC_CMD_DUAL_MIC_ENABLE: %s", gDualMicEnabled ? "enabled" : "disabled"); |
| *replySize = sizeof(int); |
| *(int*)pReplyData = 0; |
| } break; |
| case PREPROC_CMD_DUAL_MIC_PCM_DUMP_START: { |
| if (pCmdData == NULL || pReplyData == NULL || replySize == NULL) { |
| ALOGE("PreProcessingFx_Command cmdCode Case: " |
| "PREPROC_CMD_DUAL_MIC_PCM_DUMP_START: ERROR"); |
| *replySize = 0; |
| return -EINVAL; |
| } |
| pthread_mutex_lock(&gPcmDumpLock); |
| if (gPcmDumpFh != NULL) { |
| fclose(gPcmDumpFh); |
| gPcmDumpFh = NULL; |
| } |
| char* path = strndup((char*)pCmdData, cmdSize); |
| gPcmDumpFh = fopen((char*)path, "wb"); |
| pthread_mutex_unlock(&gPcmDumpLock); |
| ALOGV("PREPROC_CMD_DUAL_MIC_PCM_DUMP_START: path %s gPcmDumpFh %p", path, gPcmDumpFh); |
| ALOGE_IF(gPcmDumpFh <= 0, "gPcmDumpFh open error %d %s", errno, strerror(errno)); |
| free(path); |
| *replySize = sizeof(int); |
| *(int*)pReplyData = 0; |
| } break; |
| case PREPROC_CMD_DUAL_MIC_PCM_DUMP_STOP: { |
| if (pReplyData == NULL || replySize == NULL) { |
| ALOGE("PreProcessingFx_Command cmdCode Case: " |
| "PREPROC_CMD_DUAL_MIC_PCM_DUMP_STOP: ERROR"); |
| *replySize = 0; |
| return -EINVAL; |
| } |
| pthread_mutex_lock(&gPcmDumpLock); |
| if (gPcmDumpFh != NULL) { |
| fclose(gPcmDumpFh); |
| gPcmDumpFh = NULL; |
| } |
| pthread_mutex_unlock(&gPcmDumpLock); |
| ALOGV("PREPROC_CMD_DUAL_MIC_PCM_DUMP_STOP"); |
| *replySize = sizeof(int); |
| *(int*)pReplyData = 0; |
| } break; |
| ///// test commands end |
| |
| case EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS: { |
| if (!gDualMicEnabled) { |
| return -EINVAL; |
| } |
| if (pCmdData == NULL || cmdSize != 2 * sizeof(uint32_t) || pReplyData == NULL || |
| replySize == NULL) { |
| ALOGE("PreProcessingFx_Command cmdCode Case: " |
| "EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS: ERROR"); |
| *replySize = 0; |
| return -EINVAL; |
| } |
| if (*(uint32_t*)pCmdData != EFFECT_FEATURE_AUX_CHANNELS || !effect->aux_channels_on) { |
| ALOGV("PreProcessingFx_Command feature EFFECT_FEATURE_AUX_CHANNELS not supported by" |
| " fx %d", |
| effect->procId); |
| *(uint32_t*)pReplyData = -ENOSYS; |
| *replySize = sizeof(uint32_t); |
| break; |
| } |
| size_t num_configs = *((uint32_t*)pCmdData + 1); |
| if (*replySize < (2 * sizeof(uint32_t) + num_configs * sizeof(channel_config_t))) { |
| *replySize = 0; |
| return -EINVAL; |
| } |
| |
| *((uint32_t*)pReplyData + 1) = CHANNEL_CFG_CNT; |
| if (num_configs < CHANNEL_CFG_CNT || |
| *replySize < (2 * sizeof(uint32_t) + CHANNEL_CFG_CNT * sizeof(channel_config_t))) { |
| *(uint32_t*)pReplyData = -ENOMEM; |
| } else { |
| num_configs = CHANNEL_CFG_CNT; |
| *(uint32_t*)pReplyData = 0; |
| } |
| ALOGV("PreProcessingFx_Command EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS num config %d", |
| num_configs); |
| |
| *replySize = 2 * sizeof(uint32_t) + num_configs * sizeof(channel_config_t); |
| *((uint32_t*)pReplyData + 1) = num_configs; |
| memcpy((uint32_t*)pReplyData + 2, &sDualMicConfigs, |
| num_configs * sizeof(channel_config_t)); |
| } break; |
| case EFFECT_CMD_GET_FEATURE_CONFIG: |
| if (!gDualMicEnabled) { |
| return -EINVAL; |
| } |
| if (pCmdData == NULL || cmdSize != sizeof(uint32_t) || pReplyData == NULL || |
| replySize == NULL || *replySize < sizeof(uint32_t) + sizeof(channel_config_t)) { |
| ALOGE("PreProcessingFx_Command cmdCode Case: " |
| "EFFECT_CMD_GET_FEATURE_CONFIG: ERROR"); |
| return -EINVAL; |
| } |
| if (*(uint32_t*)pCmdData != EFFECT_FEATURE_AUX_CHANNELS || !effect->aux_channels_on) { |
| *(uint32_t*)pReplyData = -ENOSYS; |
| *replySize = sizeof(uint32_t); |
| break; |
| } |
| ALOGV("PreProcessingFx_Command EFFECT_CMD_GET_FEATURE_CONFIG"); |
| *(uint32_t*)pReplyData = 0; |
| *replySize = sizeof(uint32_t) + sizeof(channel_config_t); |
| memcpy((uint32_t*)pReplyData + 1, &sDualMicConfigs[effect->cur_channel_config], |
| sizeof(channel_config_t)); |
| break; |
| case EFFECT_CMD_SET_FEATURE_CONFIG: { |
| ALOGV("PreProcessingFx_Command EFFECT_CMD_SET_FEATURE_CONFIG: " |
| "gDualMicEnabled %d effect->aux_channels_on %d", |
| gDualMicEnabled, effect->aux_channels_on); |
| if (!gDualMicEnabled) { |
| return -EINVAL; |
| } |
| if (pCmdData == NULL || cmdSize != (sizeof(uint32_t) + sizeof(channel_config_t)) || |
| pReplyData == NULL || replySize == NULL || *replySize < sizeof(uint32_t)) { |
| ALOGE("PreProcessingFx_Command cmdCode Case: " |
| "EFFECT_CMD_SET_FEATURE_CONFIG: ERROR\n" |
| "pCmdData %p cmdSize %d pReplyData %p replySize %p *replySize %d", |
| pCmdData, cmdSize, pReplyData, replySize, replySize ? *replySize : -1); |
| return -EINVAL; |
| } |
| *replySize = sizeof(uint32_t); |
| if (*(uint32_t*)pCmdData != EFFECT_FEATURE_AUX_CHANNELS || !effect->aux_channels_on) { |
| *(uint32_t*)pReplyData = -ENOSYS; |
| ALOGV("PreProcessingFx_Command cmdCode Case: " |
| "EFFECT_CMD_SET_FEATURE_CONFIG: ERROR\n" |
| "CmdData %d effect->aux_channels_on %d", |
| *(uint32_t*)pCmdData, effect->aux_channels_on); |
| break; |
| } |
| size_t i; |
| for (i = 0; i < CHANNEL_CFG_CNT; i++) { |
| if (memcmp((uint32_t*)pCmdData + 1, &sDualMicConfigs[i], |
| sizeof(channel_config_t)) == 0) { |
| break; |
| } |
| } |
| if (i == CHANNEL_CFG_CNT) { |
| *(uint32_t*)pReplyData = -EINVAL; |
| ALOGW("PreProcessingFx_Command EFFECT_CMD_SET_FEATURE_CONFIG invalid config" |
| "[%08x].[%08x]", |
| *((uint32_t*)pCmdData + 1), *((uint32_t*)pCmdData + 2)); |
| } else { |
| effect->cur_channel_config = i; |
| *(uint32_t*)pReplyData = 0; |
| ALOGV("PreProcessingFx_Command EFFECT_CMD_SET_FEATURE_CONFIG New config" |
| "[%08x].[%08x]", |
| sDualMicConfigs[i].main_channels, sDualMicConfigs[i].aux_channels); |
| } |
| } break; |
| #endif |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| int PreProcessingFx_GetDescriptor(effect_handle_t self, effect_descriptor_t* pDescriptor) { |
| preproc_effect_t* effect = (preproc_effect_t*)self; |
| |
| if (effect == NULL || pDescriptor == NULL) { |
| return -EINVAL; |
| } |
| |
| *pDescriptor = *sDescriptors[effect->procId]; |
| |
| return 0; |
| } |
| |
| int PreProcessingFx_ProcessReverse(effect_handle_t self, audio_buffer_t* inBuffer, |
| audio_buffer_t* outBuffer) { |
| preproc_effect_t* effect = (preproc_effect_t*)self; |
| |
| if (effect == NULL) { |
| ALOGW("PreProcessingFx_ProcessReverse() ERROR effect == NULL"); |
| return -EINVAL; |
| } |
| preproc_session_t* session = (preproc_session_t*)effect->session; |
| |
| if (inBuffer == NULL || inBuffer->raw == NULL) { |
| ALOGW("PreProcessingFx_ProcessReverse() ERROR bad pointer"); |
| return -EINVAL; |
| } |
| |
| if (inBuffer->frameCount != outBuffer->frameCount) { |
| ALOGW("inBuffer->frameCount %zu is not equal to outBuffer->frameCount %zu", |
| inBuffer->frameCount, outBuffer->frameCount); |
| return -EINVAL; |
| } |
| |
| if (inBuffer->frameCount != session->frameCount) { |
| ALOGW("inBuffer->frameCount %zu != %zu representing 10ms at sampling rate %d", |
| inBuffer->frameCount, session->frameCount, session->samplingRate); |
| return -EINVAL; |
| } |
| |
| session->revProcessedMsk |= (1 << effect->procId); |
| |
| // ALOGV("PreProcessingFx_ProcessReverse In %d frames revEnabledMsk %08x revProcessedMsk |
| // %08x", |
| // inBuffer->frameCount, session->revEnabledMsk, session->revProcessedMsk); |
| |
| if ((session->revProcessedMsk & session->revEnabledMsk) == session->revEnabledMsk) { |
| effect->session->revProcessedMsk = 0; |
| if (int status = effect->session->apm->ProcessReverseStream( |
| (const int16_t* const)inBuffer->s16, |
| (const webrtc::StreamConfig)effect->session->revConfig, |
| (const webrtc::StreamConfig)effect->session->revConfig, |
| (int16_t* const)outBuffer->s16); |
| status != 0) { |
| ALOGE("Process Reverse Stream failed with error %d\n", status); |
| return status; |
| } |
| return 0; |
| } else { |
| return -ENODATA; |
| } |
| } |
| |
| // effect_handle_t interface implementation for effect |
| const struct effect_interface_s sEffectInterface = { |
| PreProcessingFx_Process, PreProcessingFx_Command, PreProcessingFx_GetDescriptor, NULL}; |
| |
| const struct effect_interface_s sEffectInterfaceReverse = { |
| PreProcessingFx_Process, PreProcessingFx_Command, PreProcessingFx_GetDescriptor, |
| PreProcessingFx_ProcessReverse}; |
| |
| //------------------------------------------------------------------------------ |
| // Effect Library Interface Implementation |
| //------------------------------------------------------------------------------ |
| |
| int PreProcessingLib_Create(const effect_uuid_t* uuid, int32_t sessionId, int32_t ioId, |
| effect_handle_t* pInterface) { |
| ALOGV("EffectCreate: uuid: %08x session %d IO: %d", uuid->timeLow, sessionId, ioId); |
| |
| int status; |
| const effect_descriptor_t* desc; |
| preproc_session_t* session; |
| uint32_t procId; |
| |
| if (PreProc_Init() != 0) { |
| return sInitStatus; |
| } |
| desc = PreProc_GetDescriptor(uuid); |
| if (desc == NULL) { |
| ALOGW("EffectCreate: fx not found uuid: %08x", uuid->timeLow); |
| return -EINVAL; |
| } |
| procId = UuidToProcId(&desc->type); |
| |
| session = PreProc_GetSession(procId, sessionId, ioId); |
| if (session == NULL) { |
| ALOGW("EffectCreate: no more session available"); |
| return -EINVAL; |
| } |
| |
| status = Session_CreateEffect(session, procId, pInterface); |
| |
| if (status < 0 && session->createdMsk == 0) { |
| session->id = 0; |
| } |
| return status; |
| } |
| |
| int PreProcessingLib_Release(effect_handle_t interface) { |
| ALOGV("EffectRelease start %p", interface); |
| if (PreProc_Init() != 0) { |
| return sInitStatus; |
| } |
| |
| preproc_effect_t* fx = (preproc_effect_t*)interface; |
| |
| if (fx->session->id == 0) { |
| return -EINVAL; |
| } |
| return Session_ReleaseEffect(fx->session, fx); |
| } |
| |
| int PreProcessingLib_GetDescriptor(const effect_uuid_t* uuid, effect_descriptor_t* pDescriptor) { |
| if (pDescriptor == NULL || uuid == NULL) { |
| return -EINVAL; |
| } |
| |
| const effect_descriptor_t* desc = PreProc_GetDescriptor(uuid); |
| if (desc == NULL) { |
| ALOGV("PreProcessingLib_GetDescriptor() not found"); |
| return -EINVAL; |
| } |
| |
| ALOGV("PreProcessingLib_GetDescriptor() got fx %s", desc->name); |
| |
| *pDescriptor = *desc; |
| return 0; |
| } |
| |
| // This is the only symbol that needs to be exported |
| __attribute__((visibility("default"))) audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { |
| .tag = AUDIO_EFFECT_LIBRARY_TAG, |
| .version = EFFECT_LIBRARY_API_VERSION, |
| .name = "Audio Preprocessing Library", |
| .implementor = "The Android Open Source Project", |
| .create_effect = PreProcessingLib_Create, |
| .release_effect = PreProcessingLib_Release, |
| .get_descriptor = PreProcessingLib_GetDescriptor}; |
| |
| }; // extern "C" |