[Android] AudioEffect架构:从上层调用到底层音效驱动
一上层的API说明
二JNI以及配置文件相关文件
三交互
四音效驱动
五分析AudioEffect架构的意图
六代码以及控制说明
本篇文章,只研究架构,不谈具体音效的实现算法、
一,上层的API说明
这个可以参考google文档
本地文档路径:
linux_x86/docs/reference/android/media/audiofx/AudioEffect.html
1.AudioEffect不可以直接使用,而需要实现其方法的子类,如Equlizer.java
2,每种音效都对应有一个UUID,具体请查阅AudioEffect.class中间的说明
二,JNI以及配置文件相关文件
源码:
就只有这个目录下面的两个文件:frameworks/base/media/jni/audioeffect/
android_media_Visualizer.cpp
android_media_AudioEffect.cpp
//android_media_AudioEffect.cpp
// Dalvik VM type signatures
static const JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_AudioEffect_native_init},
{"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;Ljava/lang/String;)I",
(void *)android_media_AudioEffect_native_setup},
{"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize},
{"native_release", "()V", (void *)android_media_AudioEffect_native_release},
{"native_setEnabled", "(Z)I", (void *)android_media_AudioEffect_native_setEnabled},
{"native_getEnabled", "()Z", (void *)android_media_AudioEffect_native_getEnabled},
{"native_hasControl", "()Z", (void *)android_media_AudioEffect_native_hasControl},
{"native_setParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_setParameter},
{"native_getParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_getParameter},
{"native_command", "(II[BI[B)I", (void *)android_media_AudioEffect_native_command},
{"native_query_effects", "()[Ljava/lang/Object;", (void *)android_media_AudioEffect_native_queryEffects},
{"native_query_pre_processing", "(I)[Ljava/lang/Object;",
(void *)android_media_AudioEffect_native_queryPreProcessings},
};
音效配置文件audio_effects.conf,可以看到音效的声明格式如下:
# Default pre-processing library. Add to audio_effect.conf "libraries" section if
# audio HAL implements support for default software audio pre-processing effects
#
# pre_processing {
# path /system/lib/soundfx/libaudiopreprocessing.so
# } 说明对应音效的所在库文件
# list of effects to load. Each effect element must contain a "library" and a "uuid" element.
# The value of the "library" element must correspond to the name of one library element in the
# "libraries" element.
# The name of the effect element is indicative, only the value of the "uuid" element
# designates the effect.
# The uuid is the implementation specific UUID as specified by the effect vendor. This is not the
# generic effect type UUID.
# effects {
# <fx name> {
# library <lib name>
# uuid <effect uuid>
# }
# ...
# } 这种声明的音效多为指定某一种效果,不需要proxy sw/hw库支持
effects {
# additions for the proxy implementation
# Proxy implementation
#effectname {
#library proxy
#uuid xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# SW implemetation of the effect. Added as a node under the proxy to
# indicate this as a sub effect.
#libsw {
#library libSW
#uuid yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
#} End of SW effect
# HW implementation of the effect. Added as a node under the proxy to
# indicate this as a sub effect.
#libhw {
#library libHW
#uuid zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
#}End of HW effect
#} End of effect proxy 工厂-代理模式,这个比常用,而且较为复杂,下面细说
工厂代理使用音效的模式,调用架构如下
EffectFactory
EffectFactory
proxy
proxy
libsw
libsw
libhw
libhw
EffectProxyCreate
effect_interface_s
effect_interface_s
EffectProxyCreate的时候,通过结构体audio_effect_library_t指定具体的操作库,通过音效type指定具体的音效,从而初始化一个EffectContext,返回一个指向EffectContext的handle指针,实际就是effect_interface_s的操作句柄
这里的proxy,bundle,offload_bundle都是使用结构体audio_effect_library_s,名字为AUDIO_EFFECT_LIBRARY_INFO_SYM,声明成一个音效库,
相关结构体说明见最底部
三,交互
分析内容包括:
音效架构的初始化
根据type和UUID初始化具体的AudioEffect
通过setParameter达到上下通信,发送控制信息与接收反馈
创建AudioEffect构造函数分析
status_t AudioEffect::set(const effect_uuid_t *type,
const effect_uuid_t *uuid,
int32_t priority,
effect_callback_t cbf,
void* user,
audio_session_t sessionId,
audio_io_handle_t io)
{
sp<IEffect> iEffect;
sp<IMemory> cblk;
int enabled;
ALOGV("set %p mUserData: %p uuid: %p timeLow %08x", this, user, type, type ? type->timeLow : 0);
if (type == NULL && uuid == NULL) {
//需要指定AudioEffect的UUID/type
ALOGW("Must specify at least type or uuid");
return BAD_VALUE;
}
...
...
mDescriptor.type = *(type != NULL ? type : EFFECT_UUID_NULL);
mDescriptor.uuid = *(uuid != NULL ? uuid : EFFECT_UUID_NULL);
mIEffectClient = new EffectClient(this);//内部类,这个是用来监控当前AudioEffect状态,以及处理控制等消息的,实质还是调用AudioEffect的相关操作函数
//还是通过AudioFlinger去创建,操作相关的不同的AudioEffect底层驱动库
iEffect = audioFlinger->createEffect((effect_descriptor_t *)&mDescriptor,
mIEffectClient, priority, io, mSessionId, mOpPackageName, &mStatus, &mId, &enabled);
...
cblk = iEffect->getCblk();//初始化控制单元
...
mIEffect = iEffect;
mCblkMemory = cblk;
mCblk = static_cast<effect_param_cblk_t*>(cblk->pointer());
int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
mCblk->buffer = (uint8_t *)mCblk + bufOffset;
...
return mStatus;
}
往下看AudioFlinger的创建流程:
sp<IEffect> AudioFlinger::createEffect(
effect_descriptor_t *pDesc,
const sp<IEffectClient>& effectClient,
int32_t priority,
audio_io_handle_t io,
audio_session_t sessionId,
const String16& opPackageName,
status_t *status,
int *id,
int *enabled)
{
status_t lStatus = NO_ERROR;
sp<EffectHandle> handle;
effect_descriptor_t desc;
//忽略检测权限,合法性的判断
//1,获取Effect描述符
...
{
if (!EffectIsNullUuid(&pDesc->uuid)) {
// if uuid is specified, request effect descriptor
//如果给定uuid,则直接获取描述符
lStatus = EffectGetDescriptor(&pDesc->uuid, &desc);
...
goto Exit;
}
} else {
//如果没有给定uuid,就根据给定的type到effect fectory中间去查找。
// if uuid is not specified, look for an available implementation
// of the required type in effect factory
...
uint32_t numEffects = 0;
effect_descriptor_t d;
d.flags = 0; // prevent compiler warning
bool found = false;
//获取并且遍历所有的音效,这里都是音效工厂提供的API
lStatus = EffectQueryNumberEffects(&numEffects);
...
}
//检查权限之后返回获取的描述符
// return effect descriptor
*pDesc = desc;
/*2,获取IO,thread,并根据情况创建EffectChain
*如果io为null,就调用接口创建一个output,如下
* io =AudioSystem::getOutputForEffect(&desc);获得output
* 或者根据seesion_id去所有的threads(play或者record都找)中间查找;
* 如果还找不到,就默认使用第一个output
* 遍历线程,获得一个线程之后,就创建EffectChain
* /
...
// create effect on selected output thread
//2,通过对应线程,创建effectHandle,并且返回
handle = thread->createEffect_l(client, effectClient, priority, sessionId,
&desc, enabled, &lStatus, pinned);
...
Exit:
*status = lStatus;
return handle;
}
下面通过实际使用音效Equilizer来说明音效调用流程
1,Java调用,创建一个小型播放器,播放一首音乐,期间使用均衡器设置各个频段的声音大小,达到均衡器的作用。APP关键代码如下:
private void setupEqualizerFxAndUI() {
//调用MediaPlayer的代码就不在这里说明
// 1,初始化一个均衡器默认使用 priority (0).
mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId());
mEqualizer.setEnabled(true); //2,enable这个均衡器
TextView eqTextView = new TextView(this);
eqTextView.setText("Equalizer:");
mLinearLayout.addView(eqTextView);
short bands = mEqualizer.getNumberOfBands();
//3,获取均衡器支持调节的频段 ,返回5,范围在60~14000HZ,具体查阅hw_sw,高通基线参考equalizer_band_presets_freq[NUM_EQ_BANDS] 路径hardware/qcom/audio/post_proc/equalizer.c
final short minEQLevel = mEqualizer.getBandLevelRange()[0];
final short maxEQLevel = mEqualizer.getBandLevelRange()[1];
Log.d(TAG, "getBandLevelRange min:"+minEQLevel+" max:"+maxEQLevel);
for (short i = 0; i < bands; i++) {
final short band = i;
。。。
//4这里主要是根据频bands以及每个band的区间,初始化可操作的seekbar,在事件响应函数中间操作设置参数到底层 frameworks/av/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
freqTextView.setText((mEqualizer.getCenterFreq(band) / 1000) + " Hz");
minDbTextView.setText((minEQLevel / 100) + " dB");
maxDbTextView.setText((maxEQLevel / 100) + " dB");
SeekBar bar = new SeekBar(this);
bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
mEqualizer.setBandLevel(band, (short) (progress + minEQLevel)); //seekbar响应函数中间的具体操作
Log.d(TAG, "seekbar operation: band: "+band+" value:"+(short) (progress + minEQLevel));
}
public void onStartTrackingTouch(SeekBar seekBar) {}
public void onStopTrackingTouch(SeekBar seekBar) {}
});
}
}
接下来我们重点关注1,4两个步骤,其他的类似
初始化一个音效
序列图如下:
交互1~17我们在上面已经提及,就不再具体描述,重点记录交互11:EffectCreate,这个函数位于lvm库中间
/* Effect Library Interface Implementation */
extern "C" int EffectCreate(const effect_uuid_t *uuid,
int32_t sessionId,
int32_t ioId __unused,
effect_handle_t *pHandle)
{
int ret = 0;
int sessionNo;
int i;
EffectContext *pContext = NULL;
bool newBundle = false;
SessionContext *pSessionContext;
...
pContext = new EffectContext;
//此处一大段对pContext的变量赋值,主要是根据uuid找到对应的音效库(sw/hw)
if (memcmp(uuid, &gEqualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0){
// Create Equalizer
ALOGV("\tEffectCreate - Effect to be created is LVM_EQUALIZER");
pSessionContext->bEqualizerInstantiated = LVM_TRUE;
pContext->pBundledContext->SamplesToExitCountEq = 0;
pContext->itfe = &gLvmEffectInterface;
pContext->EffectType = LVM_EQUALIZER;
}
...
*pHandle = (effect_handle_t)pContext;
..
return ret;
}
操作均衡器中间一个band,根据log和代码分析具体的流程:
V AudioEffect: setParameter: param: 2, param2: 0
V Bundle :
V Bundle : Effect_command start
V Bundle : Effect_command setting command for LVM_EQUALIZER
V Bundle : Effect_command INPUTS are: command 5 cmdSize 22
V Bundle : Equalizer_command cmdCode Case: EFFECT_CMD_SET_PARAM start
V Bundle : Equalizer_setParameter start
V Bundle : Equalizer_setParameter() EQ_PARAM_BAND_LEVEL band 0, level -125
V Bundle : EqualizerSetBandLevel(-125)->(-1)
V Bundle : TOTAL energy estimation: 1.29
V Bundle : Vol:-37, GainCorrection: 0, Actual vol: -37
V Bundle : Equalizer_setParameter end
V Bundle : Effect_command cmdCode Case: EFFECT_CMD_SET_PARAM end
V offload_effect_bundle: effect_command: ctxt 0xa67e4500, cmd 5
V offload_effect_equalizer: equalizer_set_parameter: ctxt 0xa67e4500, param 2
V offload_effect_equalizer: equalizer_set_band_level: ctxt 0xa67e4500, band: 0, level: -125
V offload_effect_api: offload_eq_set_preset: preset -1
V offload_effect_api: offload_eq_set_bands_level
V offload_effect_api: offload_eq_send_params: flags 0x5 control name :Audio Effects Config 9
从上面的log,就能看清前面提及的proxy->libsw(bundle proxy)->libhw(bundle proxy)->具体音效libhw(offload_effect_equalizer、offload_effect_api)
在这个案例中间,代码的调用流程就不再详细说明,大致流程如下,这个流程能很好的帮助理解EffectChain,EffectModule,EffectHandle ,AudioEffect之间的关系:
//音频数据buffer process()流程:
threadbase::threadloop()
|-EffectChain::process_l()
|_EffectModule::process()
|_hw interface process()
//控制信息改变的 command流程:
AudioFlinger::EffectHandle::command
|_EffectModule::command
|__hw interface command
//当然,配置信息改变,还有如下command调用
EffectModule::init
EffectModule::configure
EffectModule::start_l
EffectModule::reset
在均衡器设置band音量的过程中间,最终是调用如下:
effect_command (case EFFECT_CMD_SET_PARAM)
{
....
case EFFECT_CMD_SET_PARAM: {
...
*(int32_t *)pReplyData = context->ops.set_parameter(context, p,
*replySize);//这个赋值,看后面分析
....
}
/*
* Effect Library Interface Implementation
*/
int effect_lib_create(const effect_uuid_t *uuid,
int32_t sessionId,
int32_t ioId,
effect_handle_t *pHandle) {
...
context->ops.set_parameter = equalizer_set_parameter;//调用这个
...
}
//文件equlizer.c
//int equalizer_set_parameter()
// |_equalizer_set_band_level
// |_offload_eq_send_params 居然是使用mixer接口控制底层!!!
int equalizer_set_band_level(equalizer_context_t *context, int32_t band,
int32_t level)
{
ALOGV("%s: ctxt %p, band: %d, level: %d", __func__, context, band, level);
if (level > 0) {
level = (int)((level+50)/100);
} else {
level = (int)((level-50)/100);
}
context->band_levels[band] = level;
context->preset = PRESET_CUSTOM;
offload_eq_set_preset(&(context->offload_eq), PRESET_CUSTOM);
offload_eq_set_bands_level(&(context->offload_eq),
NUM_EQ_BANDS,
equalizer_band_presets_freq,
context->band_levels);
if (context->ctl)
offload_eq_send_params(context->ctl, context->offload_eq,
OFFLOAD_SEND_EQ_ENABLE_FLAG |
OFFLOAD_SEND_EQ_BANDS_LEVEL);
return 0;
}
int offload_eq_send_params(struct mixer_ctl *ctl, struct eq_params eq,
unsigned param_send_flags)
{
...
if (param_values[2] && ctl)
mixer_ctl_set_array(ctl, param_values, ARRAY_SIZE(param_values));//这里才是重点!!!
//通过增加log,查到qcom8909平台,这个mixer info是 “Audio Effects Config 9”可以使用tinymix工具查看这个信息,具体控制的什么呢?这就要开始分析驱动部分了
return 0;
}
四,音效驱动
从上一个部分,我们已经分析到,音效libhw实质是会通过pcm/mix接口和底层驱动通信吗,传递控制参数。我们带着如下几个疑问开始分析:
1,Audio Effects Config 9”是如何被赋值的?
2,发送给底层驱动的参数是如何生效的?
1,Audio Effects Config 9”是如何被赋值的?
初始化
static int adev_open(const hw_module_t *module, const char *name,
hw_device_t **device)
{
if (access(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, R_OK) == 0) {
adev->offload_effects_lib = dlopen(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, RTLD_NOW);
if (adev->offload_effects_lib == NULL) {
ALOGE("%s: DLOPEN failed for %s", __func__,
OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH);
} else {
ALOGV("%s: DLOPEN successful for %s", __func__,
OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH);
adev->offload_effects_start_output =
(int (*)(audio_io_handle_t, int))dlsym(adev->offload_effects_lib,
"offload_effects_bundle_hal_start_output");//这个函数中间会给相关ctrl赋值
//int (*offload_effects_start_output)(audio_io_handle_t, int);
adev->offload_effects_stop_output =
(int (*)(audio_io_handle_t, int))dlsym(adev->offload_effects_lib,
"offload_effects_bundle_hal_stop_output");
}
}
}
调用赋值流程
int start_output_stream(struct stream_out *out)
{
...
adev->offload_effects_start_output(out->handle, out->pcm_device_id);
}
//offload_effects_start_output
int offload_effects_bundle_hal_start_output(audio_io_handle_t output, int pcm_id)
{
/* populate the mixer control to send offload parameters */
snprintf(mixer_string, sizeof(mixer_string),
"%s %d", "Audio Effects Config", out_ctxt->pcm_device_id);
//和使用的pcm id组合成的ctl名字
//这个函数随后将这个effect放到链表,到这里Effect的名字就很清楚了
}
2,发送给底层驱动的参数是如何生效的?
这里我们以高通msm8909开始分析,毕竟具体的实现因平台而异,但是大致的原理都是一致的。
看到下面这个文件,这只文件是”Compress Offload platform driver”硬解码platform driver,什么是platform driver,这里需要了解BE、FE,platform,和CPU的关系,这个再其他博客再详细说明,这里就理解为是处理硬解码播放流的驱动就可以了。控制硬解码数据流的传输和路由。
kernel/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
下面这一支文件,就是集中处理所有的音效控制信息的,相当公共接口
msm-audio-effects-q6-v2.c
命令声明:
kernel/include/uapi/sound/audio_effects.h
操作band,打印的kernel log信息如下:
[Binder:2541_1 2577] msm_compr_audio_effects_config_put
[Binder:2541_1 2577] msm_compr_audio_effects_config_put: Effects supported for compr_type[0]
[Binder:2541_1 2577] msm_compr_audio_effects_config_put: EQ_MODULE
[Binder:2541_1 2577] msm_audio_effects_popless_eq_handler
[Binder:2541_1 2577] msm_audio_effects_popless_eq_handler: device: 0
[Binder:2541_1 2577] msm_audio_effects_popless_eq_handler: EQ_ENABLE prev:1 new:1
[Binder:2541_1 2577] msm_audio_effects_popless_eq_handler: EQ_CONFIG bands:5, pgain:134217728, pset:18
[Binder:5349_2 5369] compr_event_handler opcode =000110e8
通过这个log结合代码,我们可以看到的调用流程如下:
//msm-compress-q6-v2.c
static int msm_compr_audio_effects_config_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
//(根据control的赋值判断effect—module是EQ_MODULE:)
{
switch(effect_module):
case EQ_MODULE:
pr_debug("%s: EQ_MODULE\n", __func__);
if (msm_audio_effects_is_effmodule_supp_in_top(effects_module,
prtd->audio_client->topology))
msm_audio_effects_popless_eq_handler(prtd->audio_client,
&(audio_effects->equalizer),
values);
break;
}
//msm-audio-effects-q6-v2.c
int msm_audio_effects_popless_eq_handler(struct audio_client *ac,
struct eq_params *eq,
long *values)
{
for (i = 0; i < num_commands; i++)
{
switch (command_id) {
}
}
if (params_length && (rc == 0))
q6asm_send_audio_effects_params(ac, params,
params_length);
//上面根据指令赋值之后,这里开始向ASM发送effect控制参数
}
//q6asm.c ASM
int q6asm_send_audio_effects_params(struct audio_client *ac, char *params,
uint32_t params_length)
{
char *asm_params = NULL;
struct apr_hdr hdr;
struct asm_stream_cmd_set_pp_params_v2 payload_params;
//将pp参数,打包成asm参数,通过apr发出去
rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params);
}
/*kernel/drivers/soc/qcom/qdsp6v2/apr.c 这个是高通打包的*/
int apr_send_pkt(void *handle, uint32_t *buf)
{
struct apr_svc *svc = handle;
struct apr_client *clnt;
struct apr_hdr *hdr;
uint16_t dest_id;
uint16_t client_id;
uint16_t w_len;
unsigned long flags;
clnt = &client[dest_id][client_id];
w_len = apr_tal_write(clnt->handle, buf, hdr->pkt_size);//apr_tal.c
/*
*实质是调用smd.c "MSM Shared Memory Core"
*/
}
到这里,我们的分析节结束了
五,分析AudioEffect架构的意图
1,集成相关音效方法
2,当出现音质问题时,dump音效处理不同阶段的pcm数据,达到分析数据的目的
3,Android整体架构的理解与掌握
ps:这部分之后有空再补全
六,代码以及控制说明
1,相关代码说明
proxy:
路径 frameworks/av/media/libeffects/proxy/ EffectProxy.cpp
libeffectproxy 实质也是软件音效库
libsw:路径:/frameworks/av/media/libeffects/
data/
downmix/ 音效库libdownmix
factory/ 非音效 音效工厂,共用方法,库libeffects
loudness/ 音效库 libldnhncr
lvm/ 音效,非常重要,包括libmusicbundle libreverb libbundlewrapper libreverbwrapper
preprocessing/ 音效,前处理 libaudiopreprocessing
proxy/
testlibs/ 测试用的
visualizer/ 音效 ibvisualizer
PS:LVM中间包括很多音频数据换算的算法,可以自己研究一下
libhw :hardware/qcom/audio/
post_proc/ 后处理相关
LOCAL_SRC_FILES:= \
bundle.c \
equalizer.c \
bass_boost.c \
virtualizer.c \
reverb.c \
effect_api.c
LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_MODULE:= libqcompostprocbundle
...
visualizer/ 虚拟器的libhw libqcomvisualizer
voice_processing/ 通话后处理 libqcomvoiceprocessing
2,屏蔽所有的音效的开关节点:
PROPERTY_IGNORE_EFFECTS “ro.audio.ignore_effects”
3,相关结构体:
typedef struct output_context_s output_context_t;
typedef struct effect_ops_s effect_ops_t;
typedef struct effect_context_s effect_context_t;
/* effect specific operations. Only the init() and process() operations must be defined.
* Others are optional.
*/
typedef struct effect_ops_s {
int (*init)(effect_context_t *context);
int (*release)(effect_context_t *context);
int (*reset)(effect_context_t *context);
int (*enable)(effect_context_t *context);
int (*disable)(effect_context_t *context);
int (*start)(effect_context_t *context, output_context_t *output);
int (*stop)(effect_context_t *context, output_context_t *output);
int (*process)(effect_context_t *context, audio_buffer_t *in, audio_buffer_t *out);
int (*set_parameter)(effect_context_t *context, effect_param_t *param, uint32_t size);
int (*get_parameter)(effect_context_t *context, effect_param_t *param, uint32_t *size);
int (*command)(effect_context_t *context, uint32_t cmdCode, uint32_t cmdSize,
void *pCmdData, uint32_t *replySize, void *pReplyData);
} effect_ops_t;
struct effect_context_s {
const struct effect_interface_s *itfe;
struct listnode effects_list_node; /* node in created_effects_list */
struct listnode output_node; /* node in output_context_t.effects_list */
effect_config_t config;
const effect_descriptor_t *desc;
audio_io_handle_t out_handle; /* io handle of the output the effect is attached to */
uint32_t state;
bool offload_enabled; /* when offload is enabled we process VISUALIZER_CMD_CAPTURE command.
Otherwise non offloaded visualizer has already processed the command
and we must not overwrite the reply. */
effect_ops_t ops;
};
effect_interface_s,就是返回给AudioFlinger操作的句柄。
相关结构体如下:
//hardware/libhardware/include/hardware/audio_effect.h
// Effect control interface definition
struct effect_interface_s {
////////////////////////////////////////////////////////////////////////////////
//
// Function: process
//
// Description: Effect process function. Takes input samples as specified
// (count and location) in input buffer descriptor and output processed
// samples as specified in output buffer descriptor. If the buffer descriptor
// is not specified the function must use either the buffer or the
// buffer provider function installed by the EFFECT_CMD_SET_CONFIG command.
// The effect framework will call the process() function after the EFFECT_CMD_ENABLE
// command is received and until the EFFECT_CMD_DISABLE is received. When the engine
// receives the EFFECT_CMD_DISABLE command it should turn off the effect gracefully
// and when done indicate that it is OK to stop calling the process() function by
// returning the -ENODATA status.
//
// NOTE: the process() function implementation should be "real-time safe" that is
// it should not perform blocking calls: malloc/free, sleep, read/write/open/close,
// pthread_cond_wait/pthread_mutex_lock...
//
// Input:
// self: handle to the effect interface this function
// is called on.
// inBuffer: buffer descriptor indicating where to read samples to process.
// If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command.
//
// outBuffer: buffer descriptor indicating where to write processed samples.
// If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command.
//
// Output:
// returned value: 0 successful operation
// -ENODATA the engine has finished the disable phase and the framework
// can stop calling process()
// -EINVAL invalid interface handle or
// invalid input/output buffer description
////////////////////////////////////////////////////////////////////////////////
int32_t (*process)(effect_handle_t self,
audio_buffer_t *inBuffer,
audio_buffer_t *outBuffer);
////////////////////////////////////////////////////////////////////////////////
//
// Function: command
//
// Description: Send a command and receive a response to/from effect engine.
//
// Input:
// self: handle to the effect interface this function
// is called on.
// cmdCode: command code: the command can be a standardized command defined in
// effect_command_e (see below) or a proprietary command.
// cmdSize: size of command in bytes
// pCmdData: pointer to command data
// pReplyData: pointer to reply data
//
// Input/Output:
// replySize: maximum size of reply data as input
// actual size of reply data as output
//
// Output:
// returned value: 0 successful operation
// -EINVAL invalid interface handle or
// invalid command/reply size or format according to
// command code
// The return code should be restricted to indicate problems related to this API
// specification. Status related to the execution of a particular command should be
// indicated as part of the reply field.
//
// *pReplyData updated with command response
//
////////////////////////////////////////////////////////////////////////////////
int32_t (*command)(effect_handle_t self,
uint32_t cmdCode,
uint32_t cmdSize,
void *pCmdData,
uint32_t *replySize,
void *pReplyData);
////////////////////////////////////////////////////////////////////////////////
//
// Function: get_descriptor
//
// Description: Returns the effect descriptor
//
// Input:
// self: handle to the effect interface this function
// is called on.
//
// Input/Output:
// pDescriptor: address where to return the effect descriptor.
//
// Output:
// returned value: 0 successful operation.
// -EINVAL invalid interface handle or invalid pDescriptor
// *pDescriptor: updated with the effect descriptor.
//
////////////////////////////////////////////////////////////////////////////////
int32_t (*get_descriptor)(effect_handle_t self,
effect_descriptor_t *pDescriptor);
////////////////////////////////////////////////////////////////////////////////
//
// Function: process_reverse
//
// Description: Process reverse stream function. This function is used to pass
// a reference stream to the effect engine. If the engine does not need a reference
// stream, this function pointer can be set to NULL.
// This function would typically implemented by an Echo Canceler.
//
// Input:
// self: handle to the effect interface this function
// is called on.
// inBuffer: buffer descriptor indicating where to read samples to process.
// If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command.
//
// outBuffer: buffer descriptor indicating where to write processed samples.
// If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command.
// If the buffer and buffer provider in the configuration received by
// EFFECT_CMD_SET_CONFIG_REVERSE are also NULL, do not return modified reverse
// stream data
//
// Output:
// returned value: 0 successful operation
// -ENODATA the engine has finished the disable phase and the framework
// can stop calling process_reverse()
// -EINVAL invalid interface handle or
// invalid input/output buffer description
////////////////////////////////////////////////////////////////////////////////
int32_t (*process_reverse)(effect_handle_t self,
audio_buffer_t *inBuffer,
audio_buffer_t *outBuffer);
};
//<<<<<<<<<<<<<<<<<<<结构体分界线
// Every effect library must have a data structure named AUDIO_EFFECT_LIBRARY_INFO_SYM
// and the fields of this data structure must begin with audio_effect_library_t
typedef struct audio_effect_library_s {
// tag must be initialized to AUDIO_EFFECT_LIBRARY_TAG
uint32_t tag;
// Version of the effect library API : 0xMMMMmmmm MMMM: Major, mmmm: minor
uint32_t version;
// Name of this library
const char *name;
// Author/owner/implementor of the library
const char *implementor;
////////////////////////////////////////////////////////////////////////////////
//
// Function: create_effect
//
// Description: Creates an effect engine of the specified implementation uuid and
// returns an effect control interface on this engine. The function will allocate the
// resources for an instance of the requested effect engine and return
// a handle on the effect control interface.
//
// Input:
// uuid: pointer to the effect uuid.
// sessionId: audio session to which this effect instance will be attached.
// All effects created with the same session ID are connected in series and process
// the same signal stream. Knowing that two effects are part of the same effect
// chain can help the library implement some kind of optimizations.
// ioId: identifies the output or input stream this effect is directed to in
// audio HAL.
// For future use especially with tunneled HW accelerated effects
//
// Input/Output:
// pHandle: address where to return the effect interface handle.
//
// Output:
// returned value: 0 successful operation.
// -ENODEV library failed to initialize
// -EINVAL invalid pEffectUuid or pHandle
// -ENOENT no effect with this uuid found
// *pHandle: updated with the effect interface handle.
//
////////////////////////////////////////////////////////////////////////////////
int32_t (*create_effect)(const effect_uuid_t *uuid,
int32_t sessionId,
int32_t ioId,
effect_handle_t *pHandle);
////////////////////////////////////////////////////////////////////////////////
//
// Function: release_effect
//
// Description: Releases the effect engine whose handle is given as argument.
// All resources allocated to this particular instance of the effect are
// released.
//
// Input:
// handle: handle on the effect interface to be released.
//
// Output:
// returned value: 0 successful operation.
// -ENODEV library failed to initialize
// -EINVAL invalid interface handle
//
////////////////////////////////////////////////////////////////////////////////
int32_t (*release_effect)(effect_handle_t handle);
////////////////////////////////////////////////////////////////////////////////
//
// Function: get_descriptor
//
// Description: Returns the descriptor of the effect engine which implementation UUID is
// given as argument.
//
// Input/Output:
// uuid: pointer to the effect uuid.
// pDescriptor: address where to return the effect descriptor.
//
// Output:
// returned value: 0 successful operation.
// -ENODEV library failed to initialize
// -EINVAL invalid pDescriptor or uuid
// *pDescriptor: updated with the effect descriptor.
//
////////////////////////////////////////////////////////////////////////////////
int32_t (*get_descriptor)(const effect_uuid_t *uuid,
effect_descriptor_t *pDescriptor);
} audio_effect_library_t;
// Name of the hal_module_info
#define AUDIO_EFFECT_LIBRARY_INFO_SYM AELI
// Name of the hal_module_info as a string
#define AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR "AELI"