/* * 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 "speex/speex_resampler.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_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 webrtc::AudioProcessing* apm; // handle on webRTC audio processing module (APM) size_t apmFrameCount; // buffer size for webRTC process (10 ms) uint32_t apmSamplingRate; // webRTC APM sampling rate (8/16 or 32 kHz) size_t frameCount; // buffer size before input resampler ( <=> apmFrameCount) 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 webrtc::AudioFrame *procFrame; // audio frame passed to webRTC AMP ProcessStream() int16_t *inBuf; // input buffer used when resampling size_t inBufSize; // input buffer size in frames size_t framesIn; // number of frames in input buffer SpeexResamplerState *inResampler; // handle on input speex resampler int16_t *outBuf; // output buffer used when resampling size_t outBufSize; // output buffer size in frames size_t framesOut; // number of frames in output buffer SpeexResamplerState *outResampler; // handle on output speex resampler 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::AudioFrame *revFrame; // audio frame passed to webRTC AMP AnalyzeReverseStream() int16_t *revBuf; // reverse channel input buffer size_t revBufSize; // reverse channel input buffer size size_t framesRev; // number of frames in reverse channel input buffer SpeexResamplerState *revResampler; // handle on reverse channel input speex resampler }; #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 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" }; // 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, &sAecDescriptor, &sNsDescriptor }; //------------------------------------------------------------------------------ // Helper functions //------------------------------------------------------------------------------ const effect_uuid_t * const sUuidToPreProcTable[PREPROC_NUM_EFFECTS] = { FX_IID_AGC, 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 AgcInit (preproc_effect_t *effect) { ALOGV("AgcInit"); webrtc::GainControl *agc = static_cast(effect->engine); agc->set_mode(webrtc::GainControl::kFixedDigital); agc->set_target_level_dbfs(kAgcDefaultTargetLevel); agc->set_compression_gain_db(kAgcDefaultCompGain); agc->enable_limiter(kAgcDefaultLimiter); return 0; } int AgcCreate(preproc_effect_t *effect) { webrtc::GainControl *agc = effect->session->apm->gain_control(); ALOGV("AgcCreate got agc %p", agc); if (agc == NULL) { ALOGW("AgcCreate Error"); return -ENOMEM; } effect->engine = static_cast(agc); AgcInit(effect); return 0; } 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; webrtc::GainControl *agc = static_cast(effect->engine); 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; } switch (param) { case AGC_PARAM_TARGET_LEVEL: *(int16_t *) pValue = (int16_t)(agc->target_level_dbfs() * -100); ALOGV("AgcGetParameter() target level %d milliBels", *(int16_t *) pValue); break; case AGC_PARAM_COMP_GAIN: *(int16_t *) pValue = (int16_t)(agc->compression_gain_db() * 100); ALOGV("AgcGetParameter() comp gain %d milliBels", *(int16_t *) pValue); break; case AGC_PARAM_LIMITER_ENA: *(bool *) pValue = (bool)agc->is_limiter_enabled(); ALOGV("AgcGetParameter() limiter enabled %s", (*(int16_t *) pValue != 0) ? "true" : "false"); break; case AGC_PARAM_PROPERTIES: pProperties->targetLevel = (int16_t)(agc->target_level_dbfs() * -100); pProperties->compGain = (int16_t)(agc->compression_gain_db() * 100); pProperties->limiterEnabled = (bool)agc->is_limiter_enabled(); break; default: ALOGW("AgcGetParameter() unknown param %d", param); status = -EINVAL; break; } 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; webrtc::GainControl *agc = static_cast(effect->engine); switch (param) { case AGC_PARAM_TARGET_LEVEL: ALOGV("AgcSetParameter() target level %d milliBels", *(int16_t *)pValue); status = agc->set_target_level_dbfs(-(*(int16_t *)pValue / 100)); break; case AGC_PARAM_COMP_GAIN: ALOGV("AgcSetParameter() comp gain %d milliBels", *(int16_t *)pValue); status = agc->set_compression_gain_db(*(int16_t *)pValue / 100); break; case AGC_PARAM_LIMITER_ENA: ALOGV("AgcSetParameter() limiter enabled %s", *(bool *)pValue ? "true" : "false"); status = agc->enable_limiter(*(bool *)pValue); break; case AGC_PARAM_PROPERTIES: ALOGV("AgcSetParameter() properties level %d, gain %d limiter %d", pProperties->targetLevel, pProperties->compGain, pProperties->limiterEnabled); status = agc->set_target_level_dbfs(-(pProperties->targetLevel / 100)); if (status != 0) break; status = agc->set_compression_gain_db(pProperties->compGain / 100); if (status != 0) break; status = agc->enable_limiter(pProperties->limiterEnabled); break; default: ALOGW("AgcSetParameter() unknown param %08x value %08x", param, *(uint32_t *)pValue); status = -EINVAL; break; } ALOGV("AgcSetParameter() done status %d", status); return status; } void AgcEnable(preproc_effect_t *effect) { webrtc::GainControl *agc = static_cast(effect->engine); ALOGV("AgcEnable agc %p", agc); agc->Enable(true); } void AgcDisable(preproc_effect_t *effect) { ALOGV("AgcDisable"); webrtc::GainControl *agc = static_cast(effect->engine); agc->Enable(false); } static const preproc_ops_t sAgcOps = { AgcCreate, AgcInit, NULL, AgcEnable, AgcDisable, AgcSetParameter, AgcGetParameter, NULL }; //------------------------------------------------------------------------------ // Acoustic Echo Canceler (AEC) //------------------------------------------------------------------------------ static const webrtc::EchoControlMobile::RoutingMode kAecDefaultMode = webrtc::EchoControlMobile::kEarpiece; static const bool kAecDefaultComfortNoise = true; int AecInit (preproc_effect_t *effect) { ALOGV("AecInit"); webrtc::EchoControlMobile *aec = static_cast(effect->engine); aec->set_routing_mode(kAecDefaultMode); aec->enable_comfort_noise(kAecDefaultComfortNoise); return 0; } int AecCreate(preproc_effect_t *effect) { webrtc::EchoControlMobile *aec = effect->session->apm->echo_control_mobile(); ALOGV("AecCreate got aec %p", aec); if (aec == NULL) { ALOGW("AgcCreate Error"); return -ENOMEM; } effect->engine = static_cast(aec); 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; 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; default: ALOGW("AecSetParameter() unknown param %08x value %08x", param, *(uint32_t *)pValue); status = -EINVAL; break; } return status; } void AecEnable(preproc_effect_t *effect) { webrtc::EchoControlMobile *aec = static_cast(effect->engine); ALOGV("AecEnable aec %p", aec); aec->Enable(true); } void AecDisable(preproc_effect_t *effect) { ALOGV("AecDisable"); webrtc::EchoControlMobile *aec = static_cast(effect->engine); aec->Enable(false); } int AecSetDevice(preproc_effect_t *effect, uint32_t device) { ALOGV("AecSetDevice %08x", device); webrtc::EchoControlMobile *aec = static_cast(effect->engine); webrtc::EchoControlMobile::RoutingMode mode = webrtc::EchoControlMobile::kQuietEarpieceOrHeadset; if (audio_is_input_device(device)) { return 0; } switch(device) { case AUDIO_DEVICE_OUT_EARPIECE: mode = webrtc::EchoControlMobile::kEarpiece; break; case AUDIO_DEVICE_OUT_SPEAKER: mode = webrtc::EchoControlMobile::kSpeakerphone; break; case AUDIO_DEVICE_OUT_WIRED_HEADSET: case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: case AUDIO_DEVICE_OUT_USB_HEADSET: default: break; } aec->set_routing_mode(mode); return 0; } static const preproc_ops_t sAecOps = { AecCreate, AecInit, NULL, AecEnable, AecDisable, AecSetParameter, AecGetParameter, AecSetDevice }; //------------------------------------------------------------------------------ // Noise Suppression (NS) //------------------------------------------------------------------------------ static const webrtc::NoiseSuppression::Level kNsDefaultLevel = webrtc::NoiseSuppression::kModerate; int NsInit (preproc_effect_t *effect) { ALOGV("NsInit"); webrtc::NoiseSuppression *ns = static_cast(effect->engine); ns->set_level(kNsDefaultLevel); webrtc::Config config; std::vector geometry; // TODO(aluebs): Make the geometry settable. geometry.push_back(webrtc::Point(-0.03f, 0.f, 0.f)); geometry.push_back(webrtc::Point(-0.01f, 0.f, 0.f)); geometry.push_back(webrtc::Point(0.01f, 0.f, 0.f)); geometry.push_back(webrtc::Point(0.03f, 0.f, 0.f)); // The geometry needs to be set with Beamforming enabled. config.Set( new webrtc::Beamforming(true, geometry)); effect->session->apm->SetExtraOptions(config); config.Set( new webrtc::Beamforming(false, geometry)); effect->session->apm->SetExtraOptions(config); effect->type = NS_TYPE_SINGLE_CHANNEL; return 0; } int NsCreate(preproc_effect_t *effect) { webrtc::NoiseSuppression *ns = effect->session->apm->noise_suppression(); ALOGV("NsCreate got ns %p", ns); if (ns == NULL) { ALOGW("AgcCreate Error"); return -ENOMEM; } effect->engine = static_cast(ns); 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; webrtc::NoiseSuppression *ns = static_cast(effect->engine); uint32_t param = *(uint32_t *)pParam; uint32_t value = *(uint32_t *)pValue; switch(param) { case NS_PARAM_LEVEL: ns->set_level((webrtc::NoiseSuppression::Level)value); ALOGV("NsSetParameter() level %d", value); break; case NS_PARAM_TYPE: { webrtc::Config config; std::vector geometry; bool is_beamforming_enabled = value == NS_TYPE_MULTI_CHANNEL && ns->is_enabled(); config.Set( new webrtc::Beamforming(is_beamforming_enabled, geometry)); effect->session->apm->SetExtraOptions(config); effect->type = value; ALOGV("NsSetParameter() type %d", value); break; } default: ALOGW("NsSetParameter() unknown param %08x value %08x", param, value); status = -EINVAL; } return status; } void NsEnable(preproc_effect_t *effect) { webrtc::NoiseSuppression *ns = static_cast(effect->engine); ALOGV("NsEnable ns %p", ns); ns->Enable(true); if (effect->type == NS_TYPE_MULTI_CHANNEL) { webrtc::Config config; std::vector geometry; config.Set(new webrtc::Beamforming(true, geometry)); effect->session->apm->SetExtraOptions(config); } } void NsDisable(preproc_effect_t *effect) { ALOGV("NsDisable"); webrtc::NoiseSuppression *ns = static_cast(effect->engine); ns->Enable(false); webrtc::Config config; std::vector geometry; config.Set(new webrtc::Beamforming(false, geometry)); effect->session->apm->SetExtraOptions(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, &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; session->apm = NULL; 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 = webrtc::AudioProcessing::Create(); if (session->apm == NULL) { ALOGW("Session_CreateEffect could not get apm engine"); goto error; } const webrtc::ProcessingConfig processing_config = { {{kPreprocDefaultSr, kPreProcDefaultCnl}, {kPreprocDefaultSr, kPreProcDefaultCnl}, {kPreprocDefaultSr, kPreProcDefaultCnl}, {kPreprocDefaultSr, kPreProcDefaultCnl}}}; session->apm->Initialize(processing_config); session->procFrame = new webrtc::AudioFrame(); if (session->procFrame == NULL) { ALOGW("Session_CreateEffect could not allocate audio frame"); goto error; } session->revFrame = new webrtc::AudioFrame(); if (session->revFrame == NULL) { ALOGW("Session_CreateEffect could not allocate reverse audio frame"); goto error; } session->apmSamplingRate = kPreprocDefaultSr; session->apmFrameCount = (kPreprocDefaultSr) / 100; session->frameCount = session->apmFrameCount; session->samplingRate = kPreprocDefaultSr; session->inChannelCount = kPreProcDefaultCnl; session->outChannelCount = kPreProcDefaultCnl; session->procFrame->sample_rate_hz_ = kPreprocDefaultSr; session->procFrame->num_channels_ = kPreProcDefaultCnl; session->revChannelCount = kPreProcDefaultCnl; session->revFrame->sample_rate_hz_ = kPreprocDefaultSr; session->revFrame->num_channels_ = kPreProcDefaultCnl; session->enabledMsk = 0; session->processedMsk = 0; session->revEnabledMsk = 0; session->revProcessedMsk = 0; session->inResampler = NULL; session->inBuf = NULL; session->inBufSize = 0; session->outResampler = NULL; session->outBuf = NULL; session->outBufSize = 0; session->revResampler = NULL; session->revBuf = NULL; session->revBufSize = 0; } status = Effect_Create(&session->effects[procId], session, interface); if (status < 0) { goto error; } ALOGV("Session_CreateEffect OK"); session->createdMsk |= (1<createdMsk == 0) { delete session->revFrame; session->revFrame = NULL; delete session->procFrame; session->procFrame = NULL; delete session->apm; session->apm = NULL; // NOLINT(clang-analyzer-cplusplus.NewDelete) } 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<procId); if (session->createdMsk == 0) { delete session->apm; session->apm = NULL; delete session->procFrame; session->procFrame = NULL; delete session->revFrame; session->revFrame = NULL; if (session->inResampler != NULL) { speex_resampler_destroy(session->inResampler); session->inResampler = NULL; } if (session->outResampler != NULL) { speex_resampler_destroy(session->outResampler); session->outResampler = NULL; } if (session->revResampler != NULL) { speex_resampler_destroy(session->revResampler); session->revResampler = NULL; } delete session->inBuf; session->inBuf = NULL; delete session->outBuf; session->outBuf = NULL; delete session->revBuf; session->revBuf = NULL; 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); int status; // AEC implementation is limited to 16kHz if (config->inputCfg.samplingRate >= 32000 && !(session->createdMsk & (1 << PREPROC_AEC))) { session->apmSamplingRate = 32000; } else if (config->inputCfg.samplingRate >= 16000) { session->apmSamplingRate = 16000; } else if (config->inputCfg.samplingRate >= 8000) { session->apmSamplingRate = 8000; } const webrtc::ProcessingConfig processing_config = { {{static_cast(session->apmSamplingRate), inCnl}, {static_cast(session->apmSamplingRate), outCnl}, {static_cast(session->apmSamplingRate), inCnl}, {static_cast(session->apmSamplingRate), inCnl}}}; status = session->apm->Initialize(processing_config); if (status < 0) { return -EINVAL; } session->samplingRate = config->inputCfg.samplingRate; session->apmFrameCount = session->apmSamplingRate / 100; if (session->samplingRate == session->apmSamplingRate) { session->frameCount = session->apmFrameCount; } else { session->frameCount = (session->apmFrameCount * session->samplingRate) / session->apmSamplingRate + 1; } session->inChannelCount = inCnl; session->outChannelCount = outCnl; session->procFrame->num_channels_ = inCnl; session->procFrame->sample_rate_hz_ = session->apmSamplingRate; session->revChannelCount = inCnl; session->revFrame->num_channels_ = inCnl; session->revFrame->sample_rate_hz_ = session->apmSamplingRate; // force process buffer reallocation session->inBufSize = 0; session->outBufSize = 0; session->framesIn = 0; session->framesOut = 0; if (session->inResampler != NULL) { speex_resampler_destroy(session->inResampler); session->inResampler = NULL; } if (session->outResampler != NULL) { speex_resampler_destroy(session->outResampler); session->outResampler = NULL; } if (session->revResampler != NULL) { speex_resampler_destroy(session->revResampler); session->revResampler = NULL; } if (session->samplingRate != session->apmSamplingRate) { int error; session->inResampler = speex_resampler_init(session->inChannelCount, session->samplingRate, session->apmSamplingRate, RESAMPLER_QUALITY, &error); if (session->inResampler == NULL) { ALOGW("Session_SetConfig Cannot create speex resampler: %s", speex_resampler_strerror(error)); return -EINVAL; } session->outResampler = speex_resampler_init(session->outChannelCount, session->apmSamplingRate, session->samplingRate, RESAMPLER_QUALITY, &error); if (session->outResampler == NULL) { ALOGW("Session_SetConfig Cannot create speex resampler: %s", speex_resampler_strerror(error)); speex_resampler_destroy(session->inResampler); session->inResampler = NULL; return -EINVAL; } session->revResampler = speex_resampler_init(session->inChannelCount, session->samplingRate, session->apmSamplingRate, RESAMPLER_QUALITY, &error); if (session->revResampler == NULL) { ALOGW("Session_SetConfig Cannot create speex resampler: %s", speex_resampler_strerror(error)); speex_resampler_destroy(session->inResampler); session->inResampler = NULL; speex_resampler_destroy(session->outResampler); session->outResampler = NULL; return -EINVAL; } } 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); const webrtc::ProcessingConfig processing_config = { {{static_cast(session->apmSamplingRate), session->inChannelCount}, {static_cast(session->apmSamplingRate), session->outChannelCount}, {static_cast(session->apmSamplingRate), inCnl}, {static_cast(session->apmSamplingRate), inCnl}}}; int status = session->apm->Initialize(processing_config); if (status < 0) { return -EINVAL; } session->revChannelCount = inCnl; session->revFrame->num_channels_ = inCnl; session->revFrame->sample_rate_hz_ = session->apmSamplingRate; // force process buffer reallocation session->revBufSize = 0; session->framesRev = 0; 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) { if(session->enabledMsk == 0) { session->framesIn = 0; if (session->inResampler != NULL) { speex_resampler_reset_mem(session->inResampler); } session->framesOut = 0; if (session->outResampler != NULL) { speex_resampler_reset_mem(session->outResampler); } } session->enabledMsk |= (1 << procId); if (HasReverseStream(procId)) { session->framesRev = 0; if (session->revResampler != NULL) { speex_resampler_reset_mem(session->revResampler); } 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; } session->processedMsk |= (1<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; size_t framesRq = outBuffer->frameCount; size_t framesWr = 0; if (session->framesOut) { size_t fr = session->framesOut; if (outBuffer->frameCount < fr) { fr = outBuffer->frameCount; } memcpy(outBuffer->s16, session->outBuf, fr * session->outChannelCount * sizeof(int16_t)); memmove(session->outBuf, session->outBuf + fr * session->outChannelCount, (session->framesOut - fr) * session->outChannelCount * sizeof(int16_t)); session->framesOut -= fr; framesWr += fr; } outBuffer->frameCount = framesWr; if (framesWr == framesRq) { inBuffer->frameCount = 0; return 0; } if (session->inResampler != NULL) { size_t fr = session->frameCount - session->framesIn; if (inBuffer->frameCount < fr) { fr = inBuffer->frameCount; } if (session->inBufSize < session->framesIn + fr) { int16_t *buf; session->inBufSize = session->framesIn + fr; buf = (int16_t *)realloc(session->inBuf, session->inBufSize * session->inChannelCount * sizeof(int16_t)); if (buf == NULL) { session->framesIn = 0; free(session->inBuf); session->inBuf = NULL; return -ENOMEM; } session->inBuf = buf; } memcpy(session->inBuf + session->framesIn * session->inChannelCount, inBuffer->s16, fr * session->inChannelCount * sizeof(int16_t)); #ifdef DUAL_MIC_TEST pthread_mutex_lock(&gPcmDumpLock); if (gPcmDumpFh != NULL) { fwrite(inBuffer->raw, fr * session->inChannelCount * sizeof(int16_t), 1, gPcmDumpFh); } pthread_mutex_unlock(&gPcmDumpLock); #endif session->framesIn += fr; inBuffer->frameCount = fr; if (session->framesIn < session->frameCount) { return 0; } spx_uint32_t frIn = session->framesIn; spx_uint32_t frOut = session->apmFrameCount; if (session->inChannelCount == 1) { speex_resampler_process_int(session->inResampler, 0, session->inBuf, &frIn, session->procFrame->data_, &frOut); } else { speex_resampler_process_interleaved_int(session->inResampler, session->inBuf, &frIn, session->procFrame->data_, &frOut); } memmove(session->inBuf, session->inBuf + frIn * session->inChannelCount, (session->framesIn - frIn) * session->inChannelCount * sizeof(int16_t)); session->framesIn -= frIn; } else { size_t fr = session->frameCount - session->framesIn; if (inBuffer->frameCount < fr) { fr = inBuffer->frameCount; } memcpy(session->procFrame->data_ + session->framesIn * session->inChannelCount, inBuffer->s16, fr * session->inChannelCount * sizeof(int16_t)); #ifdef DUAL_MIC_TEST pthread_mutex_lock(&gPcmDumpLock); if (gPcmDumpFh != NULL) { fwrite(inBuffer->raw, fr * session->inChannelCount * sizeof(int16_t), 1, gPcmDumpFh); } pthread_mutex_unlock(&gPcmDumpLock); #endif session->framesIn += fr; inBuffer->frameCount = fr; if (session->framesIn < session->frameCount) { return 0; } session->framesIn = 0; } session->procFrame->samples_per_channel_ = session->apmFrameCount; effect->session->apm->ProcessStream(session->procFrame); if (session->outBufSize < session->framesOut + session->frameCount) { int16_t *buf; session->outBufSize = session->framesOut + session->frameCount; buf = (int16_t *)realloc(session->outBuf, session->outBufSize * session->outChannelCount * sizeof(int16_t)); if (buf == NULL) { session->framesOut = 0; free(session->outBuf); session->outBuf = NULL; return -ENOMEM; } session->outBuf = buf; } if (session->outResampler != NULL) { spx_uint32_t frIn = session->apmFrameCount; spx_uint32_t frOut = session->frameCount; if (session->inChannelCount == 1) { speex_resampler_process_int(session->outResampler, 0, session->procFrame->data_, &frIn, session->outBuf + session->framesOut * session->outChannelCount, &frOut); } else { speex_resampler_process_interleaved_int(session->outResampler, session->procFrame->data_, &frIn, session->outBuf + session->framesOut * session->outChannelCount, &frOut); } session->framesOut += frOut; } else { memcpy(session->outBuf + session->framesOut * session->outChannelCount, session->procFrame->data_, session->frameCount * session->outChannelCount * sizeof(int16_t)); session->framesOut += session->frameCount; } size_t fr = session->framesOut; if (framesRq - framesWr < fr) { fr = framesRq - framesWr; } memcpy(outBuffer->s16 + framesWr * session->outChannelCount, session->outBuf, fr * session->outChannelCount * sizeof(int16_t)); memmove(session->outBuf, session->outBuf + fr * session->outChannelCount, (session->framesOut - fr) * session->outChannelCount * sizeof(int16_t)); session->framesOut -= fr; outBuffer->frameCount += fr; 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 __unused) { 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; } session->revProcessedMsk |= (1<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 (session->revResampler != NULL) { size_t fr = session->frameCount - session->framesRev; if (inBuffer->frameCount < fr) { fr = inBuffer->frameCount; } if (session->revBufSize < session->framesRev + fr) { int16_t *buf; session->revBufSize = session->framesRev + fr; buf = (int16_t *)realloc(session->revBuf, session->revBufSize * session->inChannelCount * sizeof(int16_t)); if (buf == NULL) { session->framesRev = 0; free(session->revBuf); session->revBuf = NULL; return -ENOMEM; } session->revBuf = buf; } memcpy(session->revBuf + session->framesRev * session->inChannelCount, inBuffer->s16, fr * session->inChannelCount * sizeof(int16_t)); session->framesRev += fr; inBuffer->frameCount = fr; if (session->framesRev < session->frameCount) { return 0; } spx_uint32_t frIn = session->framesRev; spx_uint32_t frOut = session->apmFrameCount; if (session->inChannelCount == 1) { speex_resampler_process_int(session->revResampler, 0, session->revBuf, &frIn, session->revFrame->data_, &frOut); } else { speex_resampler_process_interleaved_int(session->revResampler, session->revBuf, &frIn, session->revFrame->data_, &frOut); } memmove(session->revBuf, session->revBuf + frIn * session->inChannelCount, (session->framesRev - frIn) * session->inChannelCount * sizeof(int16_t)); session->framesRev -= frIn; } else { size_t fr = session->frameCount - session->framesRev; if (inBuffer->frameCount < fr) { fr = inBuffer->frameCount; } memcpy(session->revFrame->data_ + session->framesRev * session->inChannelCount, inBuffer->s16, fr * session->inChannelCount * sizeof(int16_t)); session->framesRev += fr; inBuffer->frameCount = fr; if (session->framesRev < session->frameCount) { return 0; } session->framesRev = 0; } session->revFrame->samples_per_channel_ = session->apmFrameCount; effect->session->apm->AnalyzeReverseStream(session->revFrame); 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"