一上层的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"

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

  1. spi总线 上层调用_spi总线设备驱动分析

    今天折腾了一天的SPI设备的驱动加载,甚至动用了逻辑分析仪来查看spi总线的波形,主要包括两个SPI设备,at45db321d和mcp2515,一个是串行的dataflash,一个是can总线设备芯片 ...

  2. Android系统架构与分层

    Android系统架构与分层如下: 图1 Android系统架构与分层 软件从底层到上层分为:Uboot,Linux Kernel和Android OS. 1 Uboot 将flash中的kernel ...

  3. Android零基础入门第2节:Android 系统架构和应用组件那些事

    继上一期浅谈了Android的前世今生,这一期一起来大致回顾一下Android 系统架构和应用组件. 一.Android 系统架构 Android系统的底层建立在Linux系统之上,该平台由操作系统. ...

  4. Android平台架构简介

    Android系统介绍 Linux内核层 系统运行库层 提供Android系统特性的函数库 Android运行时库 Android虚拟机与Java虚拟机 应用框架层 应用层 Android系统介绍 A ...

  5. linux底层把值传给上层,Android上层如何调用一个底层函数

    Android上层如何调用一个底层函数 1. 背景 本文讲的是调用流程,如何找到相应代码位置,更多的是一种分析代码的方式. 此处将从ZygoteInit调用Zygote.forkSystemServe ...

  6. java手机电池充电代码,android电池管理系统从上层的java到底层驱动的调用(转载)...

    1.概述 随着移动智能设备的快速发屏,电池的续航能力在很大情况下诱导了大众消费者的购买选择,android系统对电源管理的合理与否直接影响到电池的续航能力,而电池系统作为其中的一部分,主要用于对电池状 ...

  7. android底层 考试 华清,Android开发架构你真的了解吗—华清创客学院

    原标题:Android开发架构你真的了解吗-华清创客学院 华清创客学院讲师:我在网上翻过很多关于架构的文章,android也好,iOS也好,谈的更多的都是对工程结构的划分,涉及架构的部分非常少. 很多 ...

  8. Android系统架构-[Android取经之路]

    摘要:本节主要来讲解Android的系统架构 阅读本文大约需要花费10分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢! 欢 ...

  9. android phone驱动_一文带你掌握 Android 系统架构

    引言 本文作为Android系统架构的开篇,起到提纲挈领的作用,从系统整体架构角度概要讲解Android系统的核心技术点,带领大家初探Android系统全貌以及内部运作机制.虽然Android系统非常 ...

最新文章

  1. 讲解AI三大方向的模型与算法!
  2. DeepMind Nando(原牛津大学教授)强化学习最新进展,含图文、公式和代码,附102页PPT下载...
  3. oracle startup mount nomount 区别
  4. 在python中使用csv库以字典格式读写csv文件
  5. [Python人工智能] 二十四.易学智能GPU搭建Keras环境实现LSTM恶意URL请求分类
  6. wpf调用其他项目界面_WPF开发Prism框架实现一个简单播放器
  7. 7大最重要的管理方法
  8. iview tag 标签点击事件
  9. jenkins 集成java搅拌_如何将Gauge与Jenkins集成
  10. android ui自动化框架选型,Appium UI 自动化框架之我见 (开源)
  11. af_netlink_Linux Netlink通信机制详解(上)
  12. library的英语怎么读音_英语元音与辅音
  13. CNN图像处理常用损失函数对比评测
  14. 基于LM2733升压电路设计
  15. 计算机论文英语单词,英语单词
  16. 杜鹏的个人博客 Flex使用Blazeds与Java交互及自定义对象转换详解
  17. Splitter之带隔离电阻的功分器
  18. 装完黑苹果怎么装windows_苹果都说双系统好 黑苹果装windows教程
  19. PS青山绿水婚片处理
  20. 文献调研-存算一体的实现

热门文章

  1. Linux运维 第三阶段 (一) 网络配置及openssl加密
  2. 算法研发---对宏定义#define相关研究
  3. xcode 4,2 for Mac 10.6.8
  4. ESX下修改时间和时区
  5. 空位補零,你會選擇哪種方式?
  6. 生成BAPI的ALE接口
  7. Kaldi AMI数据集脚本学习6---转移模型(Transition Model)
  8. htc服务器更新系统,HTC U11刷机教程_HTC U11卡刷官方ruu升级更新系统
  9. Javascript第四章函数function也是数据类型第六课
  10. Javascript加号的作用、数据类型、输入用户的输入第四课