484e320fe0
This wrapper implements the interworking layer between the audio effect interface defined by the effect framework for audio preprocessing and the native interface of webrtc audio processing module. Change-Id: I3f9319730c102599cdf0dda520a53d90b4165114
1610 lines
56 KiB
C++
Executable File
1610 lines
56 KiB
C++
Executable File
/*
|
|
* 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 "modules/interface/module_common_types.h"
|
|
#include "modules/audio_processing/main/interface/audio_processing.h"
|
|
#include "speex/speex_resampler.h"
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// 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
|
|
};
|
|
|
|
// 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
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
// 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 = 0;
|
|
static const int kAgcDefaultCompGain = 90;
|
|
static const bool kAgcDefaultLimiter = true;
|
|
|
|
int AgcInit (preproc_effect_t *effect)
|
|
{
|
|
LOGV("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();
|
|
LOGV("AgcCreate got agc %p", agc);
|
|
if (agc == NULL) {
|
|
LOGW("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:
|
|
LOGW("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);
|
|
LOGV("AgcGetParameter() target level %d milliBels", *(int16_t *) pValue);
|
|
break;
|
|
case AGC_PARAM_COMP_GAIN:
|
|
*(int16_t *) pValue = (int16_t)(agc->compression_gain_db() * 100);
|
|
LOGV("AgcGetParameter() comp gain %d milliBels", *(int16_t *) pValue);
|
|
break;
|
|
case AGC_PARAM_LIMITER_ENA:
|
|
*(bool *) pValue = (bool)agc->is_limiter_enabled();
|
|
LOGV("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:
|
|
LOGW("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:
|
|
LOGV("AgcSetParameter() target level %d milliBels", *(int16_t *)pValue);
|
|
status = agc->set_target_level_dbfs(-(*(int16_t *)pValue / 100));
|
|
break;
|
|
case AGC_PARAM_COMP_GAIN:
|
|
LOGV("AgcSetParameter() comp gain %d milliBels", *(int16_t *)pValue);
|
|
status = agc->set_compression_gain_db(*(int16_t *)pValue / 100);
|
|
break;
|
|
case AGC_PARAM_LIMITER_ENA:
|
|
LOGV("AgcSetParameter() limiter enabled %s", *(bool *)pValue ? "true" : "false");
|
|
status = agc->enable_limiter(*(bool *)pValue);
|
|
break;
|
|
case AGC_PARAM_PROPERTIES:
|
|
LOGV("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:
|
|
LOGW("AgcSetParameter() unknown param %08x value %08x", param, *(uint32_t *)pValue);
|
|
status = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
LOGV("AgcSetParameter() done status %d", status);
|
|
|
|
return status;
|
|
}
|
|
|
|
void AgcEnable(preproc_effect_t *effect)
|
|
{
|
|
webrtc::GainControl *agc = static_cast<webrtc::GainControl *>(effect->engine);
|
|
LOGV("AgcEnable agc %p", agc);
|
|
agc->Enable(true);
|
|
}
|
|
|
|
void AgcDisable(preproc_effect_t *effect)
|
|
{
|
|
LOGV("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)
|
|
{
|
|
LOGV("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();
|
|
LOGV("AecCreate got aec %p", aec);
|
|
if (aec == NULL) {
|
|
LOGW("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();
|
|
LOGV("AecGetParameter() echo delay %d us", *(uint32_t *)pValue);
|
|
break;
|
|
default:
|
|
LOGW("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);
|
|
LOGV("AecSetParameter() echo delay %d us, status %d", value, status);
|
|
break;
|
|
default:
|
|
LOGW("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);
|
|
LOGV("AecEnable aec %p", aec);
|
|
aec->Enable(true);
|
|
}
|
|
|
|
void AecDisable(preproc_effect_t *effect)
|
|
{
|
|
LOGV("AecDisable");
|
|
webrtc::EchoControlMobile *aec = static_cast<webrtc::EchoControlMobile *>(effect->engine);
|
|
aec->Enable(false);
|
|
}
|
|
|
|
int AecSetDevice(preproc_effect_t *effect, uint32_t device)
|
|
{
|
|
LOGV("AecSetDevice %08x", device);
|
|
webrtc::EchoControlMobile *aec = static_cast<webrtc::EchoControlMobile *>(effect->engine);
|
|
webrtc::EchoControlMobile::RoutingMode mode = webrtc::EchoControlMobile::kQuietEarpieceOrHeadset;
|
|
|
|
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)
|
|
{
|
|
LOGV("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();
|
|
LOGV("NsCreate got ns %p", ns);
|
|
if (ns == NULL) {
|
|
LOGW("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);
|
|
LOGV("NsEnable ns %p", ns);
|
|
ns->Enable(true);
|
|
}
|
|
|
|
void NsDisable(preproc_effect_t *effect)
|
|
{
|
|
LOGV("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;
|
|
LOGV("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:
|
|
LOGE("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:
|
|
LOGE("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:
|
|
case PREPROC_EFFECT_STATE_ACTIVE:
|
|
LOGE("Effect_SetState invalid transition");
|
|
status = -ENOSYS;
|
|
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;
|
|
|
|
LOGV("Session_CreateEffect procId %d, createdMsk %08x", procId, session->createdMsk);
|
|
|
|
if (session->createdMsk == 0) {
|
|
session->apm = webrtc::AudioProcessing::Create(session->io);
|
|
if (session->apm == NULL) {
|
|
LOGW("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) {
|
|
LOGW("Session_CreateEffect could not allocate audio frame");
|
|
goto error;
|
|
}
|
|
session->revFrame = new webrtc::AudioFrame();
|
|
if (session->revFrame == NULL) {
|
|
LOGW("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;
|
|
}
|
|
LOGV("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)
|
|
{
|
|
LOGW_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;
|
|
}
|
|
|
|
LOGV("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;
|
|
}
|
|
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;
|
|
|
|
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) {
|
|
LOGW("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) {
|
|
LOGW("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) {
|
|
LOGW("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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
LOGV("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;
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
LOGV("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(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){
|
|
LOGV("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){
|
|
LOGW("PreProcessingFx_Process() ERROR bad pointer");
|
|
return -EINVAL;
|
|
}
|
|
|
|
session->processedMsk |= (1<<effect->procId);
|
|
|
|
// LOGV("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));
|
|
|
|
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));
|
|
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;
|
|
}
|
|
|
|
//LOGV("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_CONFIGURE:
|
|
if (pCmdData == NULL||
|
|
cmdSize != sizeof(effect_config_t)||
|
|
pReplyData == NULL||
|
|
*replySize != sizeof(int)){
|
|
LOGV("PreProcessingFx_Command cmdCode Case: "
|
|
"EFFECT_CMD_CONFIGURE: ERROR");
|
|
return -EINVAL;
|
|
}
|
|
*(int *)pReplyData = Session_SetConfig(effect->session, (effect_config_t *)pCmdData);
|
|
if (*(int *)pReplyData != 0) {
|
|
break;
|
|
}
|
|
*(int *)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_CONFIG);
|
|
break;
|
|
|
|
case EFFECT_CMD_CONFIGURE_REVERSE:
|
|
if (pCmdData == NULL||
|
|
cmdSize != sizeof(effect_config_t)||
|
|
pReplyData == NULL||
|
|
*replySize != sizeof(int)){
|
|
LOGV("PreProcessingFx_Command cmdCode Case: "
|
|
"EFFECT_CMD_CONFIGURE_REVERSE: ERROR");
|
|
return -EINVAL;
|
|
}
|
|
*(int *)pReplyData = Session_SetReverseConfig(effect->session,
|
|
(effect_config_t *)pCmdData);
|
|
if (*(int *)pReplyData != 0) {
|
|
break;
|
|
}
|
|
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)){
|
|
LOGV("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)){
|
|
LOGV("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)){
|
|
LOGV("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)){
|
|
LOGV("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)){
|
|
LOGV("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)) {
|
|
LOGV("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;
|
|
|
|
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;
|
|
}
|
|
|
|
memcpy(pDescriptor, sDescriptors[effect->procId], sizeof(effect_descriptor_t));
|
|
|
|
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){
|
|
LOGW("PreProcessingFx_ProcessReverse() ERROR effect == NULL");
|
|
return -EINVAL;
|
|
}
|
|
preproc_session_t * session = (preproc_session_t *)effect->session;
|
|
|
|
if (inBuffer == NULL || inBuffer->raw == NULL){
|
|
LOGW("PreProcessingFx_ProcessReverse() ERROR bad pointer");
|
|
return -EINVAL;
|
|
}
|
|
|
|
session->revProcessedMsk |= (1<<effect->procId);
|
|
|
|
// LOGV("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_QueryNumberEffects(uint32_t *pNumEffects)
|
|
{
|
|
if (PreProc_Init() != 0) {
|
|
return sInitStatus;
|
|
}
|
|
if (pNumEffects == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
*pNumEffects = PREPROC_NUM_EFFECTS;
|
|
return sInitStatus;
|
|
}
|
|
|
|
int PreProcessingLib_QueryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
|
|
{
|
|
if (PreProc_Init() != 0) {
|
|
return sInitStatus;
|
|
}
|
|
if (index >= PREPROC_NUM_EFFECTS) {
|
|
return -EINVAL;
|
|
}
|
|
memcpy(pDescriptor, sDescriptors[index], sizeof(effect_descriptor_t));
|
|
return 0;
|
|
}
|
|
|
|
int PreProcessingLib_Create(effect_uuid_t *uuid,
|
|
int32_t sessionId,
|
|
int32_t ioId,
|
|
effect_handle_t *pInterface)
|
|
{
|
|
LOGV("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) {
|
|
LOGW("EffectCreate: fx not found uuid: %08x", uuid->timeLow);
|
|
return -EINVAL;
|
|
}
|
|
procId = UuidToProcId(&desc->type);
|
|
|
|
session = PreProc_GetSession(procId, sessionId, ioId);
|
|
if (session == NULL) {
|
|
LOGW("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;
|
|
LOGV("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(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) {
|
|
LOGV("PreProcessingLib_GetDescriptor() not found");
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOGV("PreProcessingLib_GetDescriptor() got fx %s", desc->name);
|
|
|
|
memcpy(pDescriptor, desc, sizeof(effect_descriptor_t));
|
|
return 0;
|
|
}
|
|
|
|
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
|
|
tag : AUDIO_EFFECT_LIBRARY_TAG,
|
|
version : EFFECT_LIBRARY_API_VERSION,
|
|
name : "Audio Preprocessing Library",
|
|
implementor : "The Android Open Source Project",
|
|
query_num_effects : PreProcessingLib_QueryNumberEffects,
|
|
query_effect : PreProcessingLib_QueryEffect,
|
|
create_effect : PreProcessingLib_Create,
|
|
release_effect : PreProcessingLib_Release,
|
|
get_descriptor : PreProcessingLib_GetDescriptor
|
|
};
|
|
|
|
}; // extern "C"
|