[Android] AudioEffect架构:从上层调用到底层音效驱动

来源:互联网 发布:mac os官方下载 编辑:程序博客网 时间:2024/06/06 15:36

    • 一上层的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 signaturesstatic 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   工厂-代理模式,这个比常用,而且较为复杂,下面细说

工厂代理使用音效的模式,调用架构如下

Created with Raphaël 2.1.0EffectFactoryEffectFactoryproxyproxylibswlibswlibhwlibhwEffectProxyCreateeffect_interface_seffect_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: 0V Bundle  :     V Bundle  : Effect_command startV Bundle  :     Effect_command setting command for LVM_EQUALIZERV Bundle  :     Effect_command INPUTS are: command 5 cmdSize 22V Bundle  :     Equalizer_command cmdCode Case: EFFECT_CMD_SET_PARAM startV Bundle  :     Equalizer_setParameter startV Bundle  :     Equalizer_setParameter() EQ_PARAM_BAND_LEVEL band 0, level -125V Bundle  :     EqualizerSetBandLevel(-125)->(-1)V Bundle  :  TOTAL energy estimation: 1.29V Bundle  :     Vol:-37, GainCorrection: 0, Actual vol: -37V Bundle  :     Equalizer_setParameter endV Bundle  :     Effect_command cmdCode Case: EFFECT_CMD_SET_PARAM endV offload_effect_bundle: effect_command: ctxt 0xa67e4500, cmd 5V offload_effect_equalizer: equalizer_set_parameter: ctxt 0xa67e4500, param 2V offload_effect_equalizer: equalizer_set_band_level: ctxt 0xa67e4500, band: 0, level: -125V offload_effect_api: offload_eq_set_preset: preset -1V offload_effect_api: offload_eq_set_bands_levelV 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::initEffectModule::configureEffectModule::start_lEffectModule::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_outputint 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.cstatic 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.cint 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.cLOCAL_MODULE_RELATIVE_PATH := soundfxLOCAL_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 definitionstruct 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_ttypedef 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"