Android Effect 解析

 

目录

1.简介... 4

2. Android Effect 解析... 4

2.1 音效库的创建.... 4

2.2 音效的加载.... 8

2.3 音效的调用.... 13

3总结... 14

 

1.简介

 

最近学习audio effect机制,了解了一下effect运行流程,写下学习过程,用于备忘

 

2. Android Effect 解析

 Android 系统在设计是就充分考虑了接入第三方的音效问题,所以提供了标准的接入接口,方便第三方的开发与调试,下面我们就来分析一下Android 的effect的创建加载与处理过程。

2.1 音效库的创建

       Google 为第三方音效算法设计了标准API接口audio_effect_library_t,第三方音效算法只要按照该API接口,完成各个函数功能即可挂载到Android的系统中来,如下:

 

主要是create_effect、release_effect和get_descriptor三个函数,下面以android提供的Equalizer音效为例,展示一下如何创建算法结构体,以及各函数做了哪些工作;

       Equalizer的结构体实现如下,其中AUDIO_EFFECT_LIBRARY_TAG和EFFECT_LIBRARY_API_VERSION是android 定义好的直接引用,其它name和implementor可以按照客制化需求描述,然后是本地实现的create/release/getdescriptor函数赋值到对应位置。

     Create_effect 顾名思义是用于创建effect,主要实现初始化,以及effect_handle_t接口实现,如下可以看到,首先创建了EqualizerContext结构体并赋值,记住gEqualizerInterface这个结构体,后面系统对于effect的调用都是基于这个接口实现的,然后紧跟着是effect的初始化动作,最后将EqualizerContext赋值给pHandle,实际上phandle的数据类型effect_handle_是gEqualizerInterface的数据类型effect_interface_s的指针:

 

gEqualizerInterface的实现如下,process即data主处理函数,command函数则是系统与算法的通讯函数,通过command设置effect的参数、状态等告知effect当前处理的track信息以及设备信息,getdescriptor是获取effect的描述结构体。

Process就是算法处理函数,不多做说明想了解可以阅读相关代码,command函数是系统与effect的通讯关键函数,可以看到系统通过cmdCode识别系统指令然后做出相应处理:

 

  其中重要的如EFFECT_CMD_INIT 、EFFECT_CMD_GET_PARAM、EFFECT_CMD_SET_PARAM、EFFECT_CMD_ENABLE、EFFECT_CMD_DISABLE、EFFECT_CMD_SET_DEVICE、EFFECT_CMD_SET_AUDIO_MODE都是比较重要的指令。

  而Equalizer_getDescriptor主要功能就是将effect_descriptor_t反馈给系统:

 

其中最重要的就是UUID和TYPE ID,音效的加载就是通过UUID识别的。到这create_effect的函数工作就结束了。

  EffectRelease的主要工作就是释放effect占用的资源:

 

EffectGetDescriptor 和 Equalizer_getDescriptor类似,不过这边需要先通过UUID对比识别:

 

   目前为止,一个android的标准音效库就实现了。然后编译生成.so文件;

  在系统Audio_effects.conf文件加入相关配置:

或者audio_effects.xml:

系统会根据配置去加载effect。

 

 

2.2 音效的加载

         本节我们来看一下音效是如何加载到android系统的,首先需要在应用层创建应用或者通过其它应用创建,我们还是以Equalizer音效为例,应用层代码在Equalizer.java

EFFECT_TYPE_EQUALIZER 就是对应的UUID,audioSession则是指定的effect seeeionID,全局音效设置为0。

然后就跳转到AudioEffect.java中:

通过native_setup函数利用jni接口调用native的AudioEffect类的set函数(AudioEffect.cpp):

在set函数中会根据传入参数创建mDescriptor,mIeffectClient等参数,最后调用audioFlinger->createEffect,实际上这是在client通过binder调用audioflinger中的createEffect函数:

      我们看到会利用UUID去查询是否有对应的UUID effect,如果有再利用SessionID获得当前需要添加音效的线程(mix/direct/offload),如果没有适配的则选着哦0个playbackthread一般为mix,在调用playbackthread的createEffect_l函数。到这有个疑问点,就是调用EffectGetDescriptor函数利用UUID去匹配相应的effect时,我们先前添加的libequalizer.so以及audio_effects.conf或者audio_effects.xml并未在任何地方加载啊,系统如何知道我当前有哪些effect呢,答案就在这个EffectGetDescriptor函数中,该函数紧接着会调用EffectGetDescriptor函数,该函数在Effectsfactory.c中:

看到吗,在find之前首先进行了初始化的动作,顾名思义这个函数一定是加载系统中设置的音效的:

首先调用了EffectLoadXmlEffectConfig在该函数失败后在调用EffectLoadEffectConfig函数,你可能已经意思到这两个函数与之前的audio_effects.conf和audio_effects.xml文件有密切的关系,是的这两个函数就是为了解析上述文件,然后加载effect到指定的数据链中,而且xml的优先级比conf高,xml是android 8.1以后的版本才有的,这里我们以EffectLoadXmlEffectConfig为例查看effect的加载:

两个函数loadLibraries和loadEffects,前者利用配置文件获取算法库,并将每个算法库的audio_effect_library_t数据结构做成链表放入gLibraryList中:

loadEffects主要作用这是确认effect配置有哪些找到了对应的lib,哪些没有并放置到对应的链表gSkippedEffects和gSubEffectList中。这样就完成了配置文件设置的音效的加载。

         好了现在我们回到thread->createEffect_l函数查找thread 中与sessionId对应的ChianEffect,没有则创建,然后查看effectchain中是否有与desc对应的(UUID匹配的)effect,如果没有需要创建调用new EffectModule,然后将effect加入到effectchain中,这时候创建EffectHandle类,该类封装了对相应的effect的操作比如enable/disable/command等等,最后返回这个handle。

 

 

         我们再看看new EffectModule的操作,操作比较简单调用EffectsFactory的函数,关键点是mEffectInterface,create函数会为其赋值,其值就是算法库中对应的effect_interface_s结构体:

 

   进入factory,查找对的effect获取effect_handle_t接口就是effect的process/command/getdescriptor函数

 

   将创建的effect挂载到gEffectList链上。返回的handle被添加到EffectModule的mEffectInterface中,EffectModule加入到thread对应的EffectChain,创建EffectHandle设置到effect的handle数组中,另返回个audioeffect的IEffect,java的command调用会调用IEffect->command最终调用的还是mEffectInterface->command,实际上就是Equalizer_command函数。

  导致Equalize加载过程就结束了,可以看出effect是依赖于playbackThread的没有playbackThread都有根据sessionId创建的EffectChain,EffectChain中有管理者多个effect。

2.3 音效的调用

       前面说明了音效的加载过程,也看到了effect的create、init以及command等操作,那么我们process函数在那边被调用的呢,答案就是PlaybackThread::threadLoop()函数,该函数用于接受audiotrack发送过来的数据,在该函数中找到effectChains[i]->process_l();调用playbackThread的effectChain数组每个成员的process_l函数,process_l做了什么呢,主要是调用effectChain中的每个effect的process:

 

       注意如果thread属于direct或者offload时,不做音效处理,然后调用mEffects->process即EffectModule::process:

首先做了一个32bità到16bit的转换,然后mEffectInterface->process处理,你一定记得这个接口是从音效库中获取的,这里是gEqualizerInterface,所以process就是Equalizer_process。到这音效的调用处理就完成了。处理后就调用output->stream.write送给hal处理。

 

 

3总结

   上诉为学习记录,用于备忘,参考了网上其他大神的文章,不一一列举

https://blog.csdn.net/Joymine/article/details/75317016?locationNum=6&fps=1

https://blog.csdn.net/peng_cao/article/details/52575945

 

 

  • 5
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值