android audio effects笔记

幼敏悟过人,读书辄成诵。这篇文章主要讲述android audio effects笔记相关的知识,希望能为你提供帮助。
 
这里的内部跳转链接好像无效……这里会好一点:https://blog.csdn.net/whshiyun/article/details/79806870
#wmd-preview h1 { color: #0077bb }    android effects笔记android sound effect

  • android effects笔记
    • 1 相关类及成员说明
      • 1.1 AudioPolicyService类
      • 1.2 AudioPolicyEffects类
      • 1.3 audioEffect相关
        • 1.3.1 audioEffect的创建位置
        • 1.3.2 audioEffect的关键方法及属性
        • 1.3.3 audioEffect的创建过程
        • 1.3.4 audioEffect的作用
    • 2 effect数据结构及关系
      • 2.1 AudioPolicyService与AudioEffect关系图
      • 2.2 AudioEffect与AudioModule关系图
      • 2.3 audioModule数据结构图
    • 3 effect启动加载过程
    • 4 effect挂载到音频流过程
    • 5 effect工作过程
    • 附1 audio_effects.conf文件
    • 附2 effect_entry结构分析
    • 附3 effectsFactory分析
    • 附4 addOutputSessionEffects调用过程
    • 附5 effectClient分析
全文不涉及effect buf相关的内容,这里只记录effect的创建以及接口调用相关内容,effect buf的内容下次有时间单独来记录一下
如果不关心audio effect相关类或者刚开始接触这块的话,可以直接看后面--> effect数据结构及关系,先有个感官上的认识
1 相关类及成员说明   1.1 AudioPolicyService类
AudioPolicyService类中与effect相关的仅仅只有一个成员变量:
sp< AudioPolicyEffects> mAudioPolicyEffects;
AudioPolicyService在创建时会一起创建一个AudioPolicyEffects,并把创建的AudioPolicyEffects保存在mAudioPolicyEffects变量中。
AudioPolicyService在提供给其他地方调用的一些方法中,通过mAudioPolicyEffects,调用了AudioPolicyEffects中的方法,这些方法都在AudioPolicyInterfaceImpl.cpp文件中实现。
对于其他模块,AudioPolicyService提供的对effect的操作仅仅只有几个查询接口,其余再就没什么用了……(AudioPolicyEffects中最核心的创建effect的方法还是AudioPolicyService自己调用的)
【android audio effects笔记】所以,这里可以看出,其实真正对于audio effect的操作,根本就跟AudioPolicyService、AudioPolicyEffects没有什么关系……
  1.2 AudioPolicyEffects类
先不看方法,里面关键的成员主要就是下面四个

 
  1. // Automatic input effects are configured per audio_source_t
  2. KeyedVector< audio_source_t,EffectDescVector*> mInputSources;
  3. // Automatic input effects are unique for audio_io_handle_t
  4. KeyedVector< audio_io_handle_t,EffectVector*> mInputs;
  5. // Automatic output effects are organized per audio_stream_type_t
  6. KeyedVector< audio_stream_type_t,EffectDescVector*> mOutputStreams;
  7. // Automatic output effects are unique for audiosession ID
  8. KeyedVector< audio_session_t,EffectVector*> mOutputSessions;
这里主要记录一下mOutputStreams和mOutputSessions两个,另外两个其实就是对应的input。
  • EffectDescVector:effect的描述向量,里面记录的是从config文件中读取的effect信息,不是真实可执行的effect
  • EffectVector:可执行的effect向量,这里面记录的所有effect都是根据effect描述而创建的实际可执行的effect
  • mOutputStreams:把effect与stream type对应绑定的一个键值对。这里要明确三个问题:1、stream type是什么,2、effect是哪来的,里面有些什么。3、这个键值对有什么用?
1、stream type
定义在文件:audio.h中,streamtype定义了android所支持的音频流类型,在app侧打开一个音频流时需要指定该stream的类型。
stream type与effect的描述文件(audio_effects.conf)的对应是通过函数
 
  1. audio_stream_type_tAudioPolicyEffects::streamNameToEnum(constchar*name)
实现的。把描述文件中的字符串如“ring”等,翻译成stream type类型,然后进行匹配,实现stream type与effect的对应绑定
2、effect
effect的类型为:EffectDescVector
在了解EffectDescVector之前需要对audio_effects.conf文件有所了解,关于audio_effects.conf的记录,见后面的附录:audio_effects.conf文件简析,熟悉audio_effects.conf的话,可以直接跳过……
effect里面主要就是存了effet的name,uuid以及params,其中name和uuid比较简单,这里主要记录一下parameters,语言不好描述,这里用一张图来记录:
android audio effects笔记

文章图片

最下面的键值对实际存储方式与图上画的是不一样的,图上那样画是为了体现他们是键值对,实际的存储方式详见:"audio_effect.h"文件中的typedef struct effect_param_s结构体的注释。
最后,mOutputStreams里面所存储的params信息好像完全没有被使用?!!
至于param的作用,感觉是记录的该effect部分参数的初始值,理论上应该是创建该effect后通过command()配置下去才对
3、作用
其实,audioPolicyEffects类里面解析audio_effects.conf文件我觉得从软件架构上来说是不合理的……不知道google那帮人是怎么想的,也许是我还没有领会他们的用意。
既然我觉得他不合理,是有我个人的理由的,虽然不一定对。从代码中可以看到,在audioPolicyEffects类中,解析了audio_effects.conf文件,并且记录了一个简单的EffectDescVector,这里做解析的函数是audioPolicyEffects类中自己的方法:loadAudioEffectConfig()。可是“effectsFactory.c”中也有一个类似的函数loadEffectConfigFile(),里面实现的内容几乎一样,只是effectsFactory中对audio_effect.conf文件的解析更加彻底,并且建立了一套自己的管理体系,具体分析见这里:effect_entry结构分析。既然这里有了effectsFactory这套体系,是不是可以考虑把audioPolicyEffects类里面的mOutputStreams东西纳入effectsFactory中?这里仅为个人揣测……
那么整个audioPolicyEffects类或者mOutputStreams存在意义到底在哪里?从代码上看,好像最大的作用就是为了支持status_t AudioPolicyEffects::addOutputSessionEffects()这个函数,这个函数的具体作用就是实际的为指定的session加载音效了,是整个effect中实际创建音效的地方,该函数在doStartOutput()(audioPolicyInterfaceImpl.cpp)中被调用,这里具体的调用过程见附录:addOutputSessionEffects调用过程。
既然addOutputSessionEffects是整个audioPolicyEffects类的核心方法,这里就简单的分析一下。
 
  1. status_tAudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output,
  2. audio_stream_type_t stream,
  3. audio_session_t audioSession)
  4. {
  5. ……
  6. /* 从mOutputStreams中找到对应stream type的预置effects */
  7. ssize_t index = mOutputStreams.indexOfKey(stream);
  8. ……
  9. /* 查找新加的session是否已经添加过了 */
  10. ssize_t idx = mOutputSessions.indexOfKey(audioSession);
  11. /* 与该session想对应的,实际的effect的向量列表 */
  12. EffectVector*procDesc;
  13. /* 如果新加的session没有添加过,则创建新的session与EffectVector的对应关系,并添加到mOutputSessions中 */
  14. if(idx < 0){
  15. procDesc =newEffectVector(audioSession);
  16. mOutputSessions.add(audioSession, procDesc);
  17. }else{
  18. // EffectVector is existing and we just need to increase ref count
  19. procDesc = mOutputSessions.valueAt(idx);
  20. }
  21. procDesc-> mRefCount++;
  22. /* 如果该session对应的effects描述向量procDesc是新建的,则实际创建procDesc中所描述的每一个effects */
  23. if(procDesc-> mRefCount ==1){
  24. /* 从mOutputStreams中取出对应流类型的effects描述符 */
  25. Vector< EffectDesc*> effects = mOutputStreams.valueAt(index)-> mEffects;
  26. /* 把每个描述符所描述的effects实际创建出来,并挂载到对应的threads中 */
  27. for(size_t i =0; i < effects.size(); i++){
  28. EffectDesc*effect = effects[i];
  29. /* 根据effect的描述符,实际创建一个effect */
  30. sp< AudioEffect> fx =newAudioEffect(NULL,String16("android"),& effect-> mUuid,0,0,0,
  31. audioSession, output);
  32. ……
  33. /* 把创建好的effect添加到EffectVector向量中 */
  34. procDesc-> mEffects.add(fx);
  35. }
  36. /* 设置该向量为使能 */
  37. procDesc-> setProcessorEnabled(true);
  38. }
  39. return status;
  40. }
总结来说,上述函数就做了3件事:
1、检查mOutputSessions中,对应session的EffectVector是否已经创建过了,如果没有创建,则创建EffectVector,并且在mOutputSessions中建立当前session与EffectVector的映射
2、根据输入的stream type,从mOutputStreams中创建所有相应的effect
3、把创建的effect添加到EffectVector列表中
关于mOutputSessions,到底有什么用?目前来看,只是在函数
status_t AudioPolicyEffects::queryDefaultOutputSessionEffects()
中被调用,这个函数应该是AudioPolicyEffects提供给上层查询使用的,底层没有调用。除此之外,mOutputSessions就再也没有什么用了……
所以,AudioPolicyEffects类主要就是充当了创建effect的接口,其余再没有什么实质性的作用……包括上层对于这个东西其实也没有什么依赖性,仅仅能够从AudioPolicyEffects中查询一下默认被挂载的音效,至于对音效的控制什么的,完全跟AudioPolicyEffects没有一点关系。
1.3 audioEffect相关
  1.3.1 audioEffect的创建位置1、对于默认挂载的effect,对应的audioEffect是被AudioPolicyEffects所创建的,个人感觉这也是AudioPolicyEffects最大的作用了……
2、android_Effect.cpp里面,关于bassboost(audio_effects.conf里面有定义)之类的音效好像是在这里怎么搞出来的,因为暂时不关心,所以这里暂时不深入研究
3、android_media_AudioEffect.cpp,这里是给上层app提供的effect操作的接口了,这其实就是个jni的接口,里面提供了创建effect,设置effect,使能effect等等相关操作,如果app希望启动一个非默认effect或者去设置已经启动了的effect,应该就是从这里入手~这里面所有函数的操作都是通过调用AudioEffect类的对应方法来完成的。
  1.3.2 audioEffect的关键方法及属性属性:
  • sp< IEffect> mIEffect; // IEffect binder interface
    mIEffect这个属性应该是整个audioEffect中最核心的属性了,mIEffect指向该effect的实体接口。换句话说就是:audioEffect只是一张皮,用来给上层应用调用的(比如android_media_AudioEffect.cpp中),真正的执行者其实是另有其人,这个真实的effect实体实在创建audioEffect时伴随创建的,创建过程在audioEffect的创建过程中。

  • sp< EffectClient> mIEffectClient; // IEffectClient implementation
    记录该audioEffect对应的EffectClient,每一个audioEffect都有一个唯一的EffectClient,EffectClient的创建也是伴随audioEffect创建时一起创建的。EffectClient的作用从代码中看,好像是给底层提供audioEffect的调用接口的,effectClient相关的见:effectClient
方法:
  • 除了set()方法外,其余真正对effect产生作用的方法,其实都是调用的IEffect中对应的方法,所以这里就暂时不管,后面会简要记录set方法。
1.3.3 audioEffect的创建过程在addOutputSessionEffects()函数中最关键的一步就是new AudioEffect(),创建一个audioEffect。创建audioEffect的实际执行实在set()方法中完成,这里简要分析一下:
 
  1. status_tAudioEffect::set(consteffect_uuid_t*type,
  2. consteffect_uuid_t*uuid,
  3. int32_t priority,
  4. effect_callback_t cbf,
  5. void* user,
  6. audio_session_t sessionId,
  7. audio_io_handle_t io)
  8. {
  9. /* 创建一个effectClient */
  10. mIEffectClient =newEffectClient(this);
  11. /* 创建一个effect实体,并且把该effect的实体接口返回给iEffect,这里其实就是返回了一个EffectHandle类型,这里面提供了对该effect实体的实际操作方法 */
  12. iEffect = audioFlinger-> createEffect((effect_descriptor_t*)& mDescriptor,
  13. mIEffectClient, priority, io, mSessionId, mOpPackageName,& mStatus,& mId,& enabled);
  14. /* 设置该effect为使能 */
  15. mEnabled =(volatileint32_t)enabled;
  16. /* effect内存相关操作,暂时不分析*/
  17. cblk = iEffect-> getCblk();
  18. /* 保存该effect实体接口 */
  19. mIEffect = iEffect;
  20. /* effect内存相关操作,暂时不分析*/
  21. mCblkMemory = cblk;
  22. mCblk =static_cast< effect_param_cblk_t*> (cblk-> pointer());
  23. int bufOffset =((sizeof(effect_param_cblk_t)-1)/sizeof(int)+1)*sizeof(int);
  24. mCblk-> buffer =(uint8_t*)mCblk + bufOffset;
  25. /* binder相关操作,暂时不分析 */
  26. IInterface::asBinder(iEffect)-> linkToDeath(mIEffectClient);
  27. mClientPid =IPCThreadState::self()-> getCallingPid();
  28. }
这里面最核心的一部就是audioFlinger-> createEffect()这句话,这句话里面最有意义的一句话又是:handle = thread-> createEffect_l(),所以这里稍微来分析一下createEffect_l()这个函数:
 
  1. sp< AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
  2. const sp< AudioFlinger::Client> & client,
  3. const sp< IEffectClient> & effectClient,
  4. int32_t priority,
  5. audio_session_t sessionId,
  6. effect_descriptor_t*desc,
  7. int*enabled,
  8. status_t*status,
  9. bool pinned)
  10. {
  11. sp< EffectModule> effect;
  12. sp< EffectHandle> handle;
  13. sp< EffectChain> chain;
  14. {
  15. // check for existing effect chain with the requested audio session
  16. chain = getEffectChain_l(sessionId);
  17. if(chain ==0){
  18. // create a new chain for this session
  19. chain =newEffectChain(this, sessionId);
  20. addEffectChain_l(chain);
  21. chain-> setStrategy(getStrategyForSession_l(sessionId));
  22. chainCreated =true;
  23. }else{
  24. /* 如果该effect chain已经存在,则在chain里面找一下,看看这个effect module是不是已经被创建了 */
  25. effect = chain-> getEffectFromDesc_l(desc);
  26. }
  27. /* 如果该effect module没有被创建,则创建这个effect的实体,即:effectModule */
  28. if(effect ==0){
  29. /* 为即将被创建的effectModule获取一个唯一识别号 */
  30. audio_unique_id_t id = mAudioFlinger-> nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT);
  31. // Check CPU and memory usage
  32. /* 这里是因为AudioPolicyManager类里面有一个成员:EffectDescriptorCollection mEffects
  33. 这里的注册,其实就是把这个effect封装成一个EffectDescriptor类型,并保存到mEffects中,
  34. 该函数的具体实现为:status_t AudioPolicyManager::registerEffect(),EffectDescriptor
  35. 主要在AudioPolicyManager中做一些判断的时候使用*/
  36. lStatus =AudioSystem::registerEffect(desc, mId, chain-> strategy(), sessionId, id);
  37. // create a new effect module if none present in the chain
  38. lStatus = chain-> createEffect_l(effect,this, desc, id, sessionId, pinned);
  39. /* 给effect module设置一些必要参数 */
  40. effect-> setDevice(mOutDevice);
  41. effect-> setDevice(mInDevice);
  42. effect-> setMode(mAudioFlinger-> getMode());
  43. effect-> setAudioSource(mAudioSource);
  44. }
  45. /* 创建effectHandle,这里可以看出,其实一个effectModule可以对应多个effectHandle,
  46. 所以effectHandle是针对每个使用者而独立存在的,而多个使用者公用一个effectModule */
  47. handle =newEffectHandle(effect, client, effectClient, priority);
  48. /* 把新创建的effectHandle加入到该effectModule所维护的handle列表中 */
  49. if(lStatus == OK){
  50. lStatus = effect-> addHandle(handle.get());
  51. }
  52. }
  53. Exit:
  54. return handle;
  55. }
上述代码中其实还是没有看到effectModule被创建的地方,只看到了是被effect chain所创建的,这里继续看一下effect chain中的createEffect_l函数:
 
  1. status_tAudioFlinger::EffectChain::createEffect_l(sp< EffectModule> & effect,
  2. ThreadBase*thread,
  3. effect_descriptor_t*desc,
  4. int id,
  5. audio_session_t sessionId,
  6. bool pinned)
  7. {
  8. Mutex::Autolock _l(mLock);
  9. /* 实例化一个EffectModule,这里才是真正创建一个effectModule,但是这里还并不是真正把effect创建出来的
  10. 地方,effectModule其实还不是真正的effect实体,它应该算是effect实体的一个代理,effect实体是在
  11. EffectModule的构造函数中被真正创建的 */
  12. effect =newEffectModule(thread,this, desc, id, sessionId, pinned);
  13. status_t lStatus = effect-> status();
  14. if(lStatus == NO_ERROR){
  15. /* 把创建的effectModule存入effect链中 */
  16. lStatus = addEffect_ll(effect);
  17. }
  18. if(lStatus != NO_ERROR){
  19. effect.clear();
  20. }
  21. return lStatus;
  22. }
根据代码里面写的注释,这里需要继续看看EffectModule的构造函数:
 
  1. AudioFlinger::EffectModule::EffectModule(ThreadBase*thread,
  2. const wp< AudioFlinger::EffectChain> & chain,
  3. effect_descriptor_t*desc,
  4. int id,
  5. audio_session_t sessionId,
  6. bool pinned)
  7. : mPinned(pinned),
  8. mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
  9. mDescriptor(*desc),
  10. // mConfig is set by configure() and not used before then
  11. mEffectInterface(NULL),
  12. mStatus(NO_INIT), mState(IDLE),
  13. // mMaxDisableWaitCnt is set by configure() and not used before then
  14. // mDisableWaitCnt is set by process() and updateState() and not used before then
  15. mSuspended(false),
  16. mAudioFlinger(thread-> mAudioFlinger)
  17. {
  18. // create effect engine from effect factory
  19. mStatus =EffectCreate(& desc-> uuid, sessionId, thread-> id(),& mEffectInterface);
  20. setOffloaded((thread-> type()==ThreadBase::OFFLOAD ||
  21. (thread-> type()==ThreadBase::DIRECT & & thread-> mIsDirectPcm)), thread-> id());
  22. }
其实EffectModule的构造函数里面真正起作用的就两句话,第一句话,真正的创建effect实体,第二句话设置offload属性,至于第二句话的意义暂时不去深究,总之就是想effect实体发送了一条设置offload的command。
这里EffectCreate()函数引出了一个非常重要的东西EffectFactory。关于effectFactory详见:effectFactory分析。EffectCreate()函数源码如下:
 
  1. intEffectCreate(consteffect_uuid_t*uuid,int32_t sessionId,int32_t ioId,effect_handle_t*pHandle)
  2. {
  3. /* 初始化真个effectFactory,该函数在多出被调用,只有第一次被调用时才实际执行初始化
  4. 其余时候再次被调用则直接返回*/
  5. ret = init();
  6. /* 从配置文件(audio_effects.conf)读入的effects信息中查找所要创建的effect
  7. 如果找不到该effect则退出,找到了侧继续创建effect */
  8. ret = findEffect(NULL, uuid,& l,& d);
  9. if(ret < 0){
  10. // Sub effects are not associated with the library-> effects,
  11. // so, findEffect will fail. Search for the effect in gSubEffectList.
  12. ret = findSubEffect(uuid,& l,& d);
  13. if(ret < 0){
  14. gotoexit;
  15. }
  16. }
  17. /* 这里就是调用的effect文件中的create_effect接口(详见audio_effect.h文件,里面的注释已经说的很清楚了)
  18. 该接口是每个effect都必须实现的标准接口,这里的返回值其实是itfe,itfe是一个effect_handle_t类型的结构体
  19. 该结构体就是这个effect的实际操作接口,该接口的定义也在audio_effect.h文件中,也是每个effect必须实现的 */
  20. // create effect in library
  21. ret = l-> desc-> create_effect(uuid, sessionId, ioId,& itfe);
  22. /* 创建并填写一个effect_entry_t结构,该结构中就存储了itfe */
  23. // add entry to effect list
  24. fx =(effect_entry_t*)malloc(sizeof(effect_entry_t));
  25. fx-> subItfe = itfe;
  26. /* 这里其实是把itfe封装了一次,让effectFactory统一调用itfe,其实落脚点还是itfe…… */
  27. if((*itfe)-> process_reverse != NULL){
  28. fx-> itfe =(struct effect_interface_s *)& gInterfaceWithReverse;
  29. ALOGV("EffectCreate() gInterfaceWithReverse");
  30. }else{
  31. fx-> itfe =(struct effect_interface_s *)& gInterface;
  32. ALOGV("EffectCreate() gInterface");
  33. }
  34. fx-> lib = l;
  35. /* 把fx存入gEffectList链表中,该链表为effectFactory自己的全局链表 */
  36. e =(list_elem_t*)malloc(sizeof(list_elem_t));
  37. e-> object= fx;
  38. e-> next= gEffectList;
  39. gEffectList = e;
  40. /* 其实就是把itfe传了出去 */
  41. *pHandle =(effect_handle_t)fx;
  42. }
至此,一个完整的effect就创建完成了……也与effect lib所提供的接口关联上了。
  1.3.4 audioEffect的作用那么总结下来,audioEffect除了创建了effect实体(即EffectModule)外也没有什么实质性的作用,主要是充当了接口功能,为真正的effect实体提供一个外部接口,并通过mIEffect来调用EffectModule的相关操作,EffectModule通过mIEffectClient来通知外界操作执行完毕。
2 effect数据结构及关系宏观上来说,android的音效其实可以分为三层:音效框架、音效工厂和plugin音效。这里的名称是我自己起的……不是官方的,大概表述一下应该是这样:
android audio effects笔记

文章图片

  • android音效框架我认为主要职责是让effect系统嵌入到音频框架中,包括对上层app的接口封装,音效的加载,音效的调用、音效的配置以及音频数据流内存管理等等,自己不执行与音效算法相关的任何事情,为音效的plugin提供框架上的支撑
  • EffectFactory 这一层起到承上启下的作用,它不关心任何与音频框架相关的事物,只是负责管理好所有的plugin音效,提供对音效算法调用、加载的接口,为音效框架提供操作接口
  • plugin effect 这一层由第三方厂家实现(android自己也有部分,但其实都可以归结为外挂的东西,可以不属于android源码本身)。这一层里面,需要实现audio_effect.h中所描述的接口,并且在audio_effect.conf文件中增加相关内容,就可以通过android音效框架提供的接口调用自己的音效了。
这里,只记录和分析android音效框架的数据结构!!EffectFactory相关结构见:effectFactory分析
因为整个effect的数据关系图非常庞大,所以这里分成3个部分来呈现。这三个部分的划分是自上而下的,也就是从提供的对外接口一直到对effect lib的调用。所有关系图中,默认只画了output流,input跟output类似,暂时不画出来。
  2.1 AudioPolicyService与AudioEffect关系图
这一部分是最顶层的,直接面对外部接口调用的,其关系图如下:
android audio effects笔记

文章图片

这里就简单记录一下EffectDescr到AudioEffect的过程:
mOutputCommandThread线程收到START_OUTPUT消息,调用AudioPolicyService::doStartOutput()-> AudioPolicyEffects::addOutputSessionEffects()-> new AudioEffect()
  2.2 AudioEffect与AudioModule关系图
AudioEffect类关系图点击这里:AudioEffect类关系图,往下面拖一点就是。
  2.3 audioModule数据结构图
audioModule数据结构里面实际执行操作的其实是effect_entry_t,但effect_entry_t跟audioEffect结构关系不大,所以就不在这里列出,关于effect_entry_t见附录:effect_entry结构分析。
关于audioModule的结构如下图所示:
android audio effects笔记

文章图片

在一个音频thread当中,一个session对应一个effectChain,每个effectChain可以对应若干个track,只要这些track具有相同的session值。每个effectChain是否真正执行除了要看该effect module的状态外还依赖于effectChain是否有关联的track,即:mTrackCnt是否为零,其关系如下图所示:
android audio effects笔记

文章图片

只有当mTrackCnt不为零时,该effectChain才可能被执行,这点可以参见函数:void AudioFlinger::EffectChain::process_l()
至于effectChain是如何与track绑定上关系的,这个就跟buffer有关了,Track在创建时会从Thread那里拿到一个默认的MainBuffer,在createTrack_l()时回去匹配相应的effectChain,从effectChain中去获取effectChain的buffer,并设置为mainBuffer,另一方面,每个effectChain在addEffectChain_l()时会去创建(特殊情况下不创建)一个buffer,并去匹配相应的track,为匹配到的track设置mainBuffer。effectChain和track的buffer关系还有其他情况,这里暂时不详细记录,关于effectChain的buffer最终怎么与effectModule对应上的,这里是在EffectChain::addEffect_ll()中完成的,最终把buffer赋值给mConfig.inputCfg.buffer,在每次effectModule process时,都会把mConfig中记录的buffer作为in/out buffer来输入输出。
上面只是简单记录一下这里的关系纽带,其实buffer这块比上述的复杂的多,这里不对buffer进行详细记录,等这个整理完了再来整理buffer相关的内容。
3 effect启动加载过程启动加载其实分为两块,一块是AudioPolicyService发起的加载,两一块是EffectsFactory发起的加载。
1、AudioPolicyService的加载,加载过程如下:
android audio effects笔记

文章图片

其实这一部分主要是利用audio_effects.conf建立输入输出流与stream type对应的EffectDesc,EffectDesc在创建AudioEffect时会被用到。关于AudioPolicyService、AudioEffect相关内容详见:相关类及成员说明。
2、EffectsFactory的加载,加载过程如下:
android audio effects笔记

文章图片

这里主要是为了创建gLibraryList和gSubEffectList,关于这部分详见:effect_entry结构分析。
4 effect挂载到音频流过程effect挂载及创建过程从new AudioEffect()开始,调用过程如下:
AudioEffect() ---> audioFlinger-> createEffect() ---> thread-> createEffect_l ---> chain-> createEffect_l() ---> new EffectModule() ---> EffectCreate() ---> l-> desc-> create_effect()
effect的创建位置及更详细的创建过程见:audioEffect相关。
5 effect工作过程这部分内容参见:effectClient分析的后半部分,在这部分里面,用command进行了举例。
附1 audio_effects.conf文件音效描述文件其实是分为了3部分:
1、库定义:定义音效库的名称,并将名称与音效的so文件进行绑定,例如:
 
  1. volume_listener {
  2. path /system/lib/soundfx/libvolumelistener.so
  3. }
这里定义了一个名为volume_listener的音效库,该库的路径为/system/lib/soundfx/libvolumelistener.so
2、音效定义:定义一个音效名称,然后指定该音效使用哪个音效库,例如:
 
  1. music_helper {
  2. library volume_listener
  3. uuid 08b8b058-0590-11e5-ac71-0025b32654a0
  4. }
  5. ring_helper {
  6. library volume_listener
  7. uuid 0956df94-0590-11e5-bdbe-0025b32654a0
  8. }
这里定义了两个音效,music_helper和ring_helper,都使用了同一个音效库,但是使用的uuid是不同的,一个音效库中可以存在若干个uuid,在做音效处理的时候可以根据不同的uuid采取不同的处理策略,使得音效处理可以针对不同使用者拥有不同的处理方式,而不需要去重新写多个音效库。
3、流类型与音效绑定:定义一个流类型,并且把需要默认挂载到该流类型上的音效绑定到该流上,其中第三部分为可选的,也就是说如果没有针对流类型的默认音效的话,可以没有第三部分,其中第三部分分为两个子模块:output_session_processing和pre_processing,分别对应的其实是输出流和输入流。第三部分的例子:
输出流:
 
  1. output_session_processing {
  2. music {
  3. music_helper {
  4. }
  5. sixth_music_helper {
  6. }
  7. }
  8. }
输入流:
 
  1. pre_processing {
  2. voice_communication {
  3. aec {
  4. }
  5. ns{
  6. }
  7. }
  8. }
这里,在music流类型上挂载了两个音效,music_helper和sixth_music_helper,可以看到,这两个音效的名称都是在第二部分定义好的。
这里有一点要注意:就是流类型的名称,例如本例中的music,这个名字不是随便起的,必须是在代码“audio_effects_conf.h”文件中定义好的!!这里的定义如下:
 
  1. // audio_source_t
  2. #define MIC_SRC_TAG "mic"// AUDIO_SOURCE_MIC
  3. #define VOICE_UL_SRC_TAG "voice_uplink"// AUDIO_SOURCE_VOICE_UPLINK
  4. #define VOICE_DL_SRC_TAG "voice_downlink"// AUDIO_SOURCE_VOICE_DOWNLINK
  5. #define VOICE_CALL_SRC_TAG "voice_call"// AUDIO_SOURCE_VOICE_CALL
  6. #define CAMCORDER_SRC_TAG "camcorder"// AUDIO_SOURCE_CAMCORDER
  7. #define VOICE_REC_SRC_TAG "voice_recognition"// AUDIO_SOURCE_VOICE_RECOGNITION
  8. #define VOICE_COMM_SRC_TAG "voice_communication"// AUDIO_SOURCE_VOICE_COMMUNICATION
  9. #define UNPROCESSED_SRC_TAG "unprocessed"// AUDIO_SOURCE_UNPROCESSED
  10. // audio_stream_type_t
  11. #define AUDIO_STREAM_DEFAULT_TAG "default"
  12. #define AUDIO_STREAM_VOICE_CALL_TAG "voice_call"
  13. #define AUDIO_STREAM_SYSTEM_TAG "system"
  14. #define AUDIO_STREAM_RING_TAG "ring"
  15. #define AUDIO_STREAM_MUSIC_TAG "music"
  16. #define AUDIO_STREAM_ALARM_TAG "alarm"
  17. #define AUDIO_STREAM_NOTIFICATION_TAG "notification"
  18. #define AUDIO_STREAM_BLUETOOTH_SCO_TAG "bluetooth_sco"
  19. #define AUDIO_STREAM_ENFORCED_AUDIBLE_TAG "enforced_audible"
  20. #define AUDIO_STREAM_DTMF_TAG "dtmf"
  21. #define AUDIO_STREAM_TTS_TAG "tts"
这里所使用的music字符就是对应的#define AUDIO_STREAM_MUSIC_TAG "music"这句话
附2 effect_entry结构分析相关文件:EffectsFactory.c、EffectsFactory.h
整个effect_entry或者说整个plugin音效与android音效框架的纽带全在这里,这里面关键是要弄清下面三个全局变量的结构:
 
  1. staticlist_elem_t*gEffectList; // list of effect_entry_t: all currently created effects
  2. staticlist_elem_t*gLibraryList; // list of lib_entry_t: all currently loaded libraries
  3. staticlist_sub_elem_t*gSubEffectList; // list of list_sub_elem_t: all currently created sub effects
  • gLibraryList保存了所有已经加载的effect lib,这个lib就是plugin音效编译后的so文件,通过audio_effects.conf来获取lib的路径以及有哪些lib
  • gEffectList保存了所有在audio_effects.conf所定义的音效
  • gSubEffectList保存了所有在audio_effects.conf所定义的子音效,最常见的一个例子就是proxy lib,里面会再定义hw和sw两个子音效的lib
上述数据结构怎么被创建的在effectFactory分析中分析,这里不记录。
这里关于gLibraryList、gSubEffectList的数据结构如下图所示:
android audio effects笔记

文章图片

其中有关于subEffect这里,目前最常见的就是proxy effect底下会有两个subEffect,android暂时规定只允许有 两个 subeffect,一个是hw,一个是sw,分别对应offload effect和host effect,这样做的好处我暂时认为是方便effect切换,相当于proxy没有实际处理音频数据,仅仅接受上层配置,根据上层配置在两个effect间切换,同时,对于上层来说看到的只是一个effect,这样上层就完全可以不用关心底下的行为以及管理过多的effect。
还有一点,就是关于effect_descriptor_t这个成员,如果该effect没有sub effect时,descriptor是从自己的lib中获取的,如果存在sub effect则被sub effect中的sw effect给替换掉了,但是UUID仍然为原来的UUID。举个栗子:一个effect使用了proxy lib,那么这个proxy lib在该effect中对应的descriptor的内容将是从libsw中获取的descriptor,而非proxy lib中获取的,但是,UUID还是proxy lib的UUID,这部分可以从代码中看出来。
gEffectList的结构如下所示:
android audio effects笔记

文章图片

这个比较简单就不多记录了,这个链表是在每次调用effectFactory.c中的EffectCreate()函数时才会向其中增加新的元素,也就是说这里面的元素就是当前正在使用effect实例列表。
附3 effectsFactory分析看这部分内容之前,要先确认已经了解了effect_entry结构分析里面的数据结构关系。
相关文件:EffectsFactory.c、EffectsFactory.h、audio_effect.h
EffectsFactory.c是链接plugin effect和android effect框架的纽带,两者之间所有打交道的部分全部都是通过EffectFactory来完成的。effectFactory主要职责如下:
  • 从audio_effects.conf文件中读取配置,建立gLibraryList及gSubEffectList两个数据列表
  • 提供创建、销毁、查询以及执行effect_handle_t的接口
1、建立gLibraryList及gSubEffectList数据列表
该部分由init()函数完成,该函数就是读取audio_effects.conf的配置,根据配置找到相应的动态链接库,然后加载动态链接库,再根据配置文件以及从动态链接库中恢复出来的数据,创建gLibraryList及gSubEffectList,由于此函数比较简单,就不详细记录,这里就记录一点,该函数在几乎所有外部接口中被调用了,同时该函数有一个全局变量标识,保证只实际执行一次,也就是说,effectFactory的外部接口中,哪个接口最先被调用,effectFactory就在哪里被初始化。不过比较遗憾的是暂时还没有找到实际初始化的时间点……
2、外部接口
外部接口调用其实也都比较简单,没有什么需要记录的,看代码可能比记录下来还要来的直接……
附4 addOutputSessionEffects调用过程addOutputSessionEffects()函数的调用源头在audioPolicyService.cpp提供的对外接口中,这里面的接口最终会被上层应用的api接口所调用,上层调用关系不在这里记录,总之audioPolicyService的主要作用就是音频框架对上提供的一层服务接口。
调用过程:
1、status_t AudioPolicyService::startOutput()被上层应用调用;
2、startOutput()中调用mOutputCommandThread-> startOutputCommand(output, stream, session);
3、mOutputCommandThread的创建是在AudioPolicyService::onFirstRef()函数中完成,mOutputCommandThread为一个线程,该线程具体的行为参见:AudioPolicyService解析
4、AudioPolicyService::AudioCommandThread::threadLoop()中收到START_OUTPUT命令后,执行svc-> doStartOutput()函数,这里的doStartOutput()函数在文件audioPolicyInterfaceImpl.cpp中。
5、doStartOutput()函数中调用audioPolicyEffects-> addOutputSessionEffects(),创建跟session对应的effects,这些effects就是预置的对应流类型的effects。
附5 effectClient分析  
  1. classEffectClient:
  2. public android::BnEffectClient,public android::IBinder::DeathRecipient
  3. {
  4. public:
  5. EffectClient(AudioEffect*effect): mEffect(effect){}
  6. // IEffectClient
  7. virtualvoid controlStatusChanged(bool controlGranted){
  8. sp< AudioEffect> effect = mEffect.promote();
  9. if(effect !=0){
  10. effect-> controlStatusChanged(controlGranted);
  11. }
  12. }
  13. virtualvoid enableStatusChanged(bool enabled)
  14. virtualvoid commandExecuted()
  15. private:
  16. wp< AudioEffect> mEffect;
  17. };
effectClient干了什么:
这里就简单列一下,其实就是实现了controlStatusChanged,enableStatusChanged,commandExecuted这三个方法,这三个方法简单来说应该是通知该effect的使用者某个动作或者某个消息被执行完成的回调函数,每个函数的实现其实最终还是调用的audioEffect类中的对应函数,在audioEffect类中,其实最终调用的是effect_callback_tmCbf; ,mcbf就是在创建audioeffect的时候外部传入的,如果外部需要获取实行完成的消息,则实现一个mcbf,并在创建audioeffect时传入进来,如果外部不关心,则可以不实现mcbf,那么最终EffectClient其实就是打了个酱油……啥也没干。
谁来使用effectClient的:
这里就用command来做说明。command的最终执行者是EffectModule类,比如说上层要给effect发送一条命令,则是先找到对应的AudioEffect,audioEffect通过自己的属性IEffect(也就是EffectHandle)中提供的command来执行这条命令,EffectHandle则调用其所对应的EffectModule类中的command来执行这条命令,最终effectMoudule将调用mEffectInterface中的command来执行这条命令。mEffectInterface的定义:effect_handle_tmEffectInterface; // Effect module C API,到这里就和外挂的音效文件接口给对上了。当EffectModule-> command被调用时,该函数会调用EffectHandle的commandExecuted(),EffectHandle中又调用了effectClient的commandExecuted(),最终调用AudioEffect的commandExecuted()。
AudioEffect相关的类视图:
android audio effects笔记

文章图片

commandExecuted的调用过程:
android audio effects笔记

文章图片

这里有一点,为了简便,我把IEffect类和EffectHandle给等价了,其实在mIEffect应该是IEffect类型,但是EffectHandle是继承自IEffect,并且后面都是用的EffectHandle,所以这里就混用了。
 





































































    推荐阅读