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自己调用的)

所以,这里可以看出,其实真正对于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中,stream type定义了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,语言不好描述,这里用一张图来记录:

最下面的键值对实际存储方式与图上画的是不一样的,图上那样画是为了体现他们是键值对,实际的存储方式详见:"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音效框架我认为主要职责是让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关系图

这一部分是最顶层的,直接面对外部接口调用的,其关系图如下:

这里就简单记录一下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的结构如下图所示:

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

只有当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的加载,加载过程如下:

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

2、EffectsFactory的加载,加载过程如下:

这里主要是为了创建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的数据结构如下图所示:

其中有关于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的结构如下所示:

这个比较简单就不多记录了,这个链表是在每次调用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_t mCbf;,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_t mEffectInterface; // Effect module C API,到这里就和外挂的音效文件接口给对上了。当EffectModule->command被调用时,该函数会调用EffectHandle的commandExecuted(),EffectHandle中又调用了effectClient的commandExecuted(),最终调用AudioEffect的commandExecuted()。

AudioEffect相关的类视图:

commandExecuted的调用过程:

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

原文地址:https://www.cnblogs.com/RunnigGift/p/8710087.html

时间: 2024-10-31 04:23:40

android audio effects笔记的相关文章

android audio开发的一些专用术语(待翻译)

Audio Terminology IN THIS DOCUMENT Generic Terms Digital Audio Hardware and Accessories Audio Signal Path Android-Specific Terms Sample Rate Conversion This document provides a glossary of audio-related terminology, including a list of widely used, g

java/android 设计模式学习笔记(14)---外观模式

这篇博客来介绍外观模式(Facade Pattern),外观模式也称为门面模式,它在开发过程中运用频率非常高,尤其是第三方 SDK 基本很大概率都会使用外观模式.通过一个外观类使得整个子系统只有一个统一的高层的接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节.当然,在我们的开发过程中,外观模式也是我们封装 API 的常用手段,例如网络模块.ImageLoader 模块等.其实我们在开发过程中可能已经使用过很多次外观模式,只是没有从理论层面去了解它. 转载请注明出处:http://bl

java/android 设计模式学习笔记(10)---建造者模式

这篇博客我们来介绍一下建造者模式(Builder Pattern),建造者模式又被称为生成器模式,是创造性模式之一,与工厂方法模式和抽象工厂模式不同,后两者的目的是为了实现多态性,而 Builder 模式的目的则是为了将对象的构建与展示分离.Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程.一个复杂的对象有大量的组成部分,比如汽车它有车轮.方向盘.发动机.以及各种各样的小零件,要将这些部件装配成一辆汽车,这个装配过

java/android 设计模式学习笔记(一)---单例模式

前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使用的时候也会有一些坑. PS:对技术感兴趣的同鞋加群544645972一起交流 设计模式总目录 java/android 设计模式学习笔记目录 特点 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式的使用很广泛,比如:线程池(threadpool).缓存(cache).对

Android NDK学习笔记(一) 为什么要用NDK?

NDK是什么 NDK是Native Development Kit的简称,即本地开发工具包.通过NDK,Android允许开发人员使用本地代码语言(例如C/C++)来完成应用的部分(甚至全部)功能.注意:由于翻译原因,有些地方也把Native翻译为"原生". NDK是SDK的一个补充,可以帮助你做这些事情: 生成可以在ARM CPU,Android 1.5(及以上)平台运行的JNI兼容的共享库. 将生成的共享库放置在应用程序项目路径的合适位置,使其能自动地添加进你最终的(和经过签名的)

Android:日常学习笔记(8)———探究UI开发(5)

Android:日常学习笔记(8)---探究UI开发(5) ListView控件的使用 ListView的简单用法 public class MainActivity extends AppCompatActivity { private String[] data={"Apple","Banana","Orange","Watermelon","Pear","Grape","

java/android 设计模式学习笔记(7)---装饰者模式

这篇将会介绍装饰者模式(Decorator Pattern),装饰者模式也称为包装模式(Wrapper Pattern),结构型模式之一,其使用一种对客户端透明的方式来动态的扩展对象的功能,同时它也是继承关系的一种替代方案之一,但比继承更加灵活.在现实生活中也可以看到很多装饰者模式的例子,或者可以大胆的说装饰者模式无处不在,就拿一件东西来说,可以给它披上无数层不一样的外壳,但是这件东西还是这件东西,外壳不过是用来扩展这个东西的功能而已,这就是装饰者模式,装饰者的这个角色也许各不相同但是被装饰的对

Android群英传笔记——第八章:Activity与Activity调用栈分析

Android群英传笔记--第八章:Activity与Activity调用栈分析 开篇,我们陈述一下Activity,Activity是整个应用用户交互的核心组件,了解Activity的工作模式,生命周期和管理方式,是了解Android的基础,本节主讲 Activity的生命周期与工作模式 Activity调用栈管理 一.Activity Activity作为四大组建出现平率最高的组件,我们在哪里都能看到他,就让我们一起先来了解一下他的生命周期 1.起源 Activity是用户交互的第一接口,他

疯狂Android讲义 - 学习笔记(四)

Android应用通常有多个Activity,多个Activity组成Activity栈,当前活动的Activity位于栈顶.Activity相当于Java Web开发的Servlet.当Activity处于Android应用运行时,同样受系统控制.有生命周期. 4.1 建立.配置和使用Activity 4.1.1 Activity 建立自己的Activity需要集成Activity基类,在不同的应用场景,有时也能集成Activity的子类. Activity间接或直接继承了Context.Co