/* * 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 #include #define LOG_TAG "PreProcessing" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include // 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 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"