| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #define LOG_TAG "PreProcessing" |
| //#define LOG_NDEBUG 0 |
| #include <utils/Log.h> |
| #include <utils/Timers.h> |
| #include <hardware/audio_effect.h> |
| #include <audio_effects/effect_aec.h> |
| #include <audio_effects/effect_agc.h> |
| #include <audio_effects/effect_ns.h> |
| #include <module_common_types.h> |
| #include <audio_processing.h> |
| #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, size_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 |
| #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<webrtc::GainControl *>(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<preproc_fx_handle_t>(agc); |
| AgcInit(effect); |
| return 0; |
| } |
| |
| int AgcGetParameter(preproc_effect_t *effect, |
| void *pParam, |
| size_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<webrtc::GainControl *>(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<webrtc::GainControl *>(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<webrtc::GainControl *>(effect->engine); |
| ALOGV("AgcEnable agc %p", agc); |
| agc->Enable(true); |
| } |
| |
| void AgcDisable(preproc_effect_t *effect) |
| { |
| ALOGV("AgcDisable"); |
| webrtc::GainControl *agc = static_cast<webrtc::GainControl *>(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<webrtc::EchoControlMobile *>(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<preproc_fx_handle_t>(aec); |
| AecInit (effect); |
| return 0; |
| } |
| |
| int AecGetParameter(preproc_effect_t *effect, |
| void *pParam, |
| size_t *pValueSize, |
| void *pValue) |
| { |
| int status = 0; |
| uint32_t param = *(uint32_t *)pParam; |
| |
| if (*pValueSize < sizeof(uint32_t)) { |
| return -EINVAL; |
| } |
| 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<webrtc::EchoControlMobile *>(effect->engine); |
| ALOGV("AecEnable aec %p", aec); |
| aec->Enable(true); |
| } |
| |
| void AecDisable(preproc_effect_t *effect) |
| { |
| ALOGV("AecDisable"); |
| webrtc::EchoControlMobile *aec = static_cast<webrtc::EchoControlMobile *>(effect->engine); |
| aec->Enable(false); |
| } |
| |
| int AecSetDevice(preproc_effect_t *effect, uint32_t device) |
| { |
| ALOGV("AecSetDevice %08x", device); |
| webrtc::EchoControlMobile *aec = static_cast<webrtc::EchoControlMobile *>(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: |
| 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<webrtc::NoiseSuppression *>(effect->engine); |
| ns->set_level(kNsDefaultLevel); |
| 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<preproc_fx_handle_t>(ns); |
| NsInit (effect); |
| return 0; |
| } |
| |
| int NsGetParameter(preproc_effect_t *effect, |
| void *pParam, |
| size_t *pValueSize, |
| void *pValue) |
| { |
| int status = 0; |
| return status; |
| } |
| |
| int NsSetParameter (preproc_effect_t *effect, void *pParam, void *pValue) |
| { |
| int status = 0; |
| return status; |
| } |
| |
| void NsEnable(preproc_effect_t *effect) |
| { |
| webrtc::NoiseSuppression *ns = static_cast<webrtc::NoiseSuppression *>(effect->engine); |
| ALOGV("NsEnable ns %p", ns); |
| ns->Enable(true); |
| } |
| |
| void NsDisable(preproc_effect_t *effect) |
| { |
| ALOGV("NsDisable"); |
| webrtc::NoiseSuppression *ns = static_cast<webrtc::NoiseSuppression *>(effect->engine); |
| ns->Enable(false); |
| } |
| |
| 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); |
| 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(session->io); |
| if (session->apm == NULL) { |
| ALOGW("Session_CreateEffect could not get apm engine"); |
| goto error; |
| } |
| session->apm->set_sample_rate_hz(kPreprocDefaultSr); |
| session->apm->set_num_channels(kPreProcDefaultCnl, kPreProcDefaultCnl); |
| session->apm->set_num_reverse_channels(kPreProcDefaultCnl); |
| 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->_frequencyInHz = kPreprocDefaultSr; |
| session->procFrame->_audioChannel = kPreProcDefaultCnl; |
| session->revChannelCount = kPreProcDefaultCnl; |
| session->revFrame->_frequencyInHz = kPreprocDefaultSr; |
| session->revFrame->_audioChannel = 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<<procId); |
| return status; |
| |
| error: |
| if (session->createdMsk == 0) { |
| delete session->revFrame; |
| session->revFrame = NULL; |
| delete session->procFrame; |
| session->procFrame = NULL; |
| webrtc::AudioProcessing::Destroy(session->apm); |
| session->apm = NULL; |
| } |
| 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) { |
| webrtc::AudioProcessing::Destroy(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->io = 0; |
| } |
| |
| return 0; |
| } |
| |
| |
| int Session_SetConfig(preproc_session_t *session, effect_config_t *config) |
| { |
| uint32_t sr; |
| uint32_t inCnl = popcount(config->inputCfg.channels); |
| uint32_t outCnl = popcount(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; |
| |
| // if at least one process is enabled, do not accept configuration changes |
| if (session->enabledMsk) { |
| if (session->samplingRate != config->inputCfg.samplingRate || |
| session->inChannelCount != inCnl || |
| session->outChannelCount != outCnl) { |
| return -ENOSYS; |
| } else { |
| return 0; |
| } |
| } |
| |
| // 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; |
| } |
| status = session->apm->set_sample_rate_hz(session->apmSamplingRate); |
| if (status < 0) { |
| return -EINVAL; |
| } |
| status = session->apm->set_num_channels(inCnl, outCnl); |
| if (status < 0) { |
| return -EINVAL; |
| } |
| status = session->apm->set_num_reverse_channels(inCnl); |
| 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->_audioChannel = inCnl; |
| session->procFrame->_frequencyInHz = session->apmSamplingRate; |
| |
| session->revChannelCount = inCnl; |
| session->revFrame->_audioChannel = inCnl; |
| session->revFrame->_frequencyInHz = 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 = popcount(config->inputCfg.channels); |
| int status = session->apm->set_num_reverse_channels(inCnl); |
| if (status < 0) { |
| return -EINVAL; |
| } |
| session->revChannelCount = inCnl; |
| session->revFrame->_audioChannel = inCnl; |
| session->revFrame->_frequencyInHz = 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; |
| int free = -1; |
| for (i = 0; i < PREPROC_NUM_SESSIONS; i++) { |
| if (sSessions[i].io == ioId) { |
| if (sSessions[i].createdMsk & (1 << procId)) { |
| return NULL; |
| } |
| return &sSessions[i]; |
| } |
| } |
| for (i = 0; i < PREPROC_NUM_SESSIONS; i++) { |
| if (sSessions[i].io == 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; |
| int status = 0; |
| |
| 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<<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; |
| 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)); |
| memcpy(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) { |
| session->inBufSize = session->framesIn + fr; |
| session->inBuf = (int16_t *)realloc(session->inBuf, |
| session->inBufSize * session->inChannelCount * sizeof(int16_t)); |
| } |
| 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; |
| } |
| size_t frIn = session->framesIn; |
| size_t frOut = session->apmFrameCount; |
| if (session->inChannelCount == 1) { |
| speex_resampler_process_int(session->inResampler, |
| 0, |
| session->inBuf, |
| &frIn, |
| session->procFrame->_payloadData, |
| &frOut); |
| } else { |
| speex_resampler_process_interleaved_int(session->inResampler, |
| session->inBuf, |
| &frIn, |
| session->procFrame->_payloadData, |
| &frOut); |
| } |
| memcpy(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->_payloadData + 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->_payloadDataLengthInSamples = |
| session->apmFrameCount * session->inChannelCount; |
| |
| effect->session->apm->ProcessStream(session->procFrame); |
| |
| if (session->outBufSize < session->framesOut + session->frameCount) { |
| session->outBufSize = session->framesOut + session->frameCount; |
| session->outBuf = (int16_t *)realloc(session->outBuf, |
| session->outBufSize * session->outChannelCount * sizeof(int16_t)); |
| } |
| |
| if (session->outResampler != NULL) { |
| size_t frIn = session->apmFrameCount; |
| size_t frOut = session->frameCount; |
| if (session->inChannelCount == 1) { |
| speex_resampler_process_int(session->outResampler, |
| 0, |
| session->procFrame->_payloadData, |
| &frIn, |
| session->outBuf + session->framesOut * session->outChannelCount, |
| &frOut); |
| } else { |
| speex_resampler_process_interleaved_int(session->outResampler, |
| session->procFrame->_payloadData, |
| &frIn, |
| session->outBuf + session->framesOut * session->outChannelCount, |
| &frOut); |
| } |
| session->framesOut += frOut; |
| } else { |
| memcpy(session->outBuf + session->framesOut * session->outChannelCount, |
| session->procFrame->_payloadData, |
| 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)); |
| memcpy(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; |
| int retsize; |
| int status; |
| |
| 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:{ |
| if (pCmdData == NULL || |
| cmdSize < (int)sizeof(effect_param_t) || |
| pReplyData == NULL || |
| *replySize < (int)sizeof(effect_param_t)){ |
| ALOGV("PreProcessingFx_Command cmdCode Case: " |
| "EFFECT_CMD_GET_PARAM: ERROR"); |
| return -EINVAL; |
| } |
| effect_param_t *p = (effect_param_t *)pCmdData; |
| |
| memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + p->psize); |
| |
| p = (effect_param_t *)pReplyData; |
| |
| int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); |
| |
| if (effect->ops->get_parameter) { |
| p->status = effect->ops->get_parameter(effect, p->data, |
| (size_t *)&p->vsize, |
| p->data + voffset); |
| *replySize = sizeof(effect_param_t) + voffset + p->vsize; |
| } |
| } break; |
| |
| case EFFECT_CMD_SET_PARAM:{ |
| if (pCmdData == NULL|| |
| cmdSize < (int)sizeof(effect_param_t) || |
| pReplyData == NULL || |
| *replySize != sizeof(int32_t)){ |
| ALOGV("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 != 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 != 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; |
| int status = 0; |
| |
| 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<<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 (session->revResampler != NULL) { |
| size_t fr = session->frameCount - session->framesRev; |
| if (inBuffer->frameCount < fr) { |
| fr = inBuffer->frameCount; |
| } |
| if (session->revBufSize < session->framesRev + fr) { |
| session->revBufSize = session->framesRev + fr; |
| session->revBuf = (int16_t *)realloc(session->revBuf, |
| session->revBufSize * session->inChannelCount * sizeof(int16_t)); |
| } |
| 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; |
| } |
| size_t frIn = session->framesRev; |
| size_t frOut = session->apmFrameCount; |
| if (session->inChannelCount == 1) { |
| speex_resampler_process_int(session->revResampler, |
| 0, |
| session->revBuf, |
| &frIn, |
| session->revFrame->_payloadData, |
| &frOut); |
| } else { |
| speex_resampler_process_interleaved_int(session->revResampler, |
| session->revBuf, |
| &frIn, |
| session->revFrame->_payloadData, |
| &frOut); |
| } |
| memcpy(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->_payloadData + 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->_payloadDataLengthInSamples = |
| session->apmFrameCount * session->inChannelCount; |
| 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->io = 0; |
| } |
| return status; |
| } |
| |
| int PreProcessingLib_Release(effect_handle_t interface) |
| { |
| int status; |
| ALOGV("EffectRelease start %p", interface); |
| if (PreProc_Init() != 0) { |
| return sInitStatus; |
| } |
| |
| preproc_effect_t *fx = (preproc_effect_t *)interface; |
| |
| if (fx->session->io == 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" |