android audio effects框架笔记

android audio effects框架笔记

标签(空格分隔): android sound effect


全文不涉及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类

先不看方法,里面关键的成员主要就是下面四个
    // Automatic input effects are configured per audio_source_t
    KeyedVector< audio_source_t, EffectDescVector* > mInputSources;
    // Automatic input effects are unique for audio_io_handle_t
    KeyedVector< audio_io_handle_t, EffectVector* > mInputs;

    // Automatic output effects are organized per audio_stream_type_t
    KeyedVector< audio_stream_type_t, EffectDescVector* > mOutputStreams;
    // Automatic output effects are unique for audiosession ID
    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)的对应是通过函数
audio_stream_type_t AudioPolicyEffects::streamNameToEnum(const char *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,语言不好描述,这里用一张图来记录:
effectDescVector.jpg-189.3kB
最下面的键值对实际存储方式与图上画的是不一样的,图上那样画是为了体现他们是键值对,实际的存储方式详见:”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类的核心方法,这里就简单的分析一下。

status_t AudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output,
                         audio_stream_type_t stream,
                         audio_session_t audioSession)
{
……
    /* 从mOutputStreams中找到对应stream type的预置effects */
    ssize_t index = mOutputStreams.indexOfKey(stream);
……
    /* 查找新加的session是否已经添加过了 */
    ssize_t idx = mOutputSessions.indexOfKey(audioSession);
    /* 与该session想对应的,实际的effect的向量列表 */
    EffectVector *procDesc;
    /* 如果新加的session没有添加过,则创建新的session与EffectVector的对应关系,并添加到mOutputSessions中 */
    if (idx < 0) {
        procDesc = new EffectVector(audioSession);
        mOutputSessions.add(audioSession, procDesc);
    } else {
        // EffectVector is existing and we just need to increase ref count
        procDesc = mOutputSessions.valueAt(idx);
    }
    procDesc->mRefCount++;

/* 如果该session对应的effects描述向量procDesc是新建的,则实际创建procDesc中所描述的每一个effects */
    if (procDesc->mRefCount == 1) {
        /* 从mOutputStreams中取出对应流类型的effects描述符 */
        Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects;
        /* 把每个描述符所描述的effects实际创建出来,并挂载到对应的threads中 */
        for (size_t i = 0; i < effects.size(); i++) {
            EffectDesc *effect = effects[i];
            /* 根据effect的描述符,实际创建一个effect */
            sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, 0, 0, 0,
                                                 audioSession, output);
            ……
            /* 把创建好的effect添加到EffectVector向量中 */
            procDesc->mEffects.add(fx);
        }
        /* 设置该向量为使能 */
        procDesc->setProcessorEnabled(true);
    }
    return status;
}

总结来说,上述函数就做了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()方法中完成,这里简要分析一下:

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)
{
/* 创建一个effectClient */
    mIEffectClient = new EffectClient(this);
/* 创建一个effect实体,并且把该effect的实体接口返回给iEffect,这里其实就是返回了一个EffectHandle类型,这里面提供了对该effect实体的实际操作方法 */
    iEffect = audioFlinger->createEffect((effect_descriptor_t *)&mDescriptor,
            mIEffectClient, priority, io, mSessionId, mOpPackageName, &mStatus, &mId, &enabled);
/* 设置该effect为使能 */
    mEnabled = (volatile int32_t)enabled;
/* effect内存相关操作,暂时不分析*/
    cblk = iEffect->getCblk();

/* 保存该effect实体接口 */
    mIEffect = iEffect;

    /* effect内存相关操作,暂时不分析*/
    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;
/* binder相关操作,暂时不分析 */
    IInterface::asBinder(iEffect)->linkToDeath(mIEffectClient);
    mClientPid = IPCThreadState::self()->getCallingPid();

}

这里面最核心的一部就是audioFlinger->createEffect()这句话,这句话里面最有意义的一句话又是:handle = thread->createEffect_l(),所以这里稍微来分析一下createEffect_l()这个函数:

sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
        const sp<AudioFlinger::Client>& client,
        const sp<IEffectClient>& effectClient,
        int32_t priority,
        audio_session_t sessionId,
        effect_descriptor_t *desc,
        int *enabled,
        status_t *status,
        bool pinned)
{
    sp<EffectModule> effect;
    sp<EffectHandle> handle;
    sp<EffectChain> chain;

    { 
        // check for existing effect chain with the requested audio session
        chain = getEffectChain_l(sessionId);
        if (chain == 0) {
            // create a new chain for this session
            chain = new EffectChain(this, sessionId);
            addEffectChain_l(chain);
            chain->setStrategy(getStrategyForSession_l(sessionId));
            chainCreated = true;
        } else {
        /* 如果该effect chain已经存在,则在chain里面找一下,看看这个effect module是不是已经被创建了 */
            effect = chain->getEffectFromDesc_l(desc);
        }

/* 如果该effect module没有被创建,则创建这个effect的实体,即:effectModule */
        if (effect == 0) {
        /* 为即将被创建的effectModule获取一个唯一识别号 */
            audio_unique_id_t id = mAudioFlinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT);
            // Check CPU and memory usage
            /* 这里是因为AudioPolicyManager类里面有一个成员:EffectDescriptorCollection mEffects 
            这里的注册,其实就是把这个effect封装成一个EffectDescriptor类型,并保存到mEffects中,
            该函数的具体实现为:status_t AudioPolicyManager::registerEffect(),EffectDescriptor
            主要在AudioPolicyManager中做一些判断的时候使用*/
            lStatus = AudioSystem::registerEffect(desc, mId, chain->strategy(), sessionId, id);

            // create a new effect module if none present in the chain
            lStatus = chain->createEffect_l(effect, this, desc, id, sessionId, pinned);
/* 给effect module设置一些必要参数 */
            effect->setDevice(mOutDevice);
            effect->setDevice(mInDevice);
            effect->setMode(mAudioFlinger->getMode());
            effect->setAudioSource(mAudioSource);
        }
/* 创建effectHandle,这里可以看出,其实一个effectModule可以对应多个effectHandle,
所以effectHandle是针对每个使用者而独立存在的,而多个使用者公用一个effectModule */
        handle = new EffectHandle(effect, client, effectClient, priority);
/* 把新创建的effectHandle加入到该effectModule所维护的handle列表中 */
        if (lStatus == OK) {
            lStatus = effect->addHandle(handle.get());
        }
    }

Exit:
    return handle;
}

上述代码中其实还是没有看到effectModule被创建的地方,只看到了是被effect chain所创建的,这里继续看一下effect chain中的createEffect_l函数:

status_t AudioFlinger::EffectChain::createEffect_l(sp<EffectModule>& effect,
                                                   ThreadBase *thread,
                                                   effect_descriptor_t *desc,
                                                   int id,
                                                   audio_session_t sessionId,
                                                   bool pinned)
{
    Mutex::Autolock _l(mLock);
    /* 实例化一个EffectModule,这里才是真正创建一个effectModule,但是这里还并不是真正把effect创建出来的
    地方,effectModule其实还不是真正的effect实体,它应该算是effect实体的一个代理,effect实体是在
    EffectModule的构造函数中被真正创建的 */
    effect = new EffectModule(thread, this, desc, id, sessionId, pinned);
    status_t lStatus = effect->status();
    if (lStatus == NO_ERROR) {
    /* 把创建的effectModule存入effect链中 */
        lStatus = addEffect_ll(effect);
    }
    if (lStatus != NO_ERROR) {
        effect.clear();
    }
    return lStatus;
}

根据代码里面写的注释,这里需要继续看看EffectModule的构造函数:

AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,
                                        const wp<AudioFlinger::EffectChain>& chain,
                                        effect_descriptor_t *desc,
                                        int id,
                                        audio_session_t sessionId,
                                        bool pinned)
    : mPinned(pinned),
      mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
      mDescriptor(*desc),
      // mConfig is set by configure() and not used before then
      mEffectInterface(NULL),
      mStatus(NO_INIT), mState(IDLE),
      // mMaxDisableWaitCnt is set by configure() and not used before then
      // mDisableWaitCnt is set by process() and updateState() and not used before then
      mSuspended(false),
      mAudioFlinger(thread->mAudioFlinger)
{
    // create effect engine from effect factory
    mStatus = EffectCreate(&desc->uuid, sessionId, thread->id(), &mEffectInterface);

    setOffloaded((thread->type() == ThreadBase::OFFLOAD ||
                 (thread->type() == ThreadBase::DIRECT && thread->mIsDirectPcm)), thread->id());
}

其实EffectModule的构造函数里面真正起作用的就两句话,第一句话,真正的创建effect实体,第二句话设置offload属性,至于第二句话的意义暂时不去深究,总之就是想effect实体发送了一条设置offload的command。

这里EffectCreate()函数引出了一个非常重要的东西EffectFactory。关于effectFactory详见:effectFactory分析。EffectCreate()函数源码如下:

int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle)
{

/* 初始化真个effectFactory,该函数在多出被调用,只有第一次被调用时才实际执行初始化
其余时候再次被调用则直接返回*/
    ret = init();

/* 从配置文件(audio_effects.conf)读入的effects信息中查找所要创建的effect
如果找不到该effect则退出,找到了侧继续创建effect */
    ret = findEffect(NULL, uuid, &l, &d);
    if (ret < 0){
        // Sub effects are not associated with the library->effects,
        // so, findEffect will fail. Search for the effect in gSubEffectList.
        ret = findSubEffect(uuid, &l, &d);
        if (ret < 0 ) {
            goto exit;
        }
    }

/* 这里就是调用的effect文件中的create_effect接口(详见audio_effect.h文件,里面的注释已经说的很清楚了)
该接口是每个effect都必须实现的标准接口,这里的返回值其实是itfe,itfe是一个effect_handle_t类型的结构体
该结构体就是这个effect的实际操作接口,该接口的定义也在audio_effect.h文件中,也是每个effect必须实现的 */
    // create effect in library
    ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe);

/* 创建并填写一个effect_entry_t结构,该结构中就存储了itfe */
    // add entry to effect list
    fx = (effect_entry_t *)malloc(sizeof(effect_entry_t));
    fx->subItfe = itfe;
    /* 这里其实是把itfe封装了一次,让effectFactory统一调用itfe,其实落脚点还是itfe…… */
    if ((*itfe)->process_reverse != NULL) {
        fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse;
        ALOGV("EffectCreate() gInterfaceWithReverse");
    }   else {
        fx->itfe = (struct effect_interface_s *)&gInterface;
        ALOGV("EffectCreate() gInterface");
    }
    fx->lib = l;

/* 把fx存入gEffectList链表中,该链表为effectFactory自己的全局链表 */
    e = (list_elem_t *)malloc(sizeof(list_elem_t));
    e->object = fx;
    e->next = gEffectList;
    gEffectList = e;

/* 其实就是把itfe传了出去 */
    *pHandle = (effect_handle_t)fx;
}

至此,一个完整的effect就创建完成了……也与effect lib所提供的接口关联上了。

1.3.4 audioEffect的作用

那么总结下来,audioEffect除了创建了effect实体(即EffectModule)外也没有什么实质性的作用,主要是充当了接口功能,为真正的effect实体提供一个外部接口,并通过mIEffect来调用EffectModule的相关操作,EffectModule通过mIEffectClient来通知外界操作执行完毕。

2 effect数据结构及关系

宏观上来说,android的音效其实可以分为三层:音效框架、音效工厂和plugin音效。这里的名称是我自己起的……不是官方的,大概表述一下应该是这样:
effectOverView.jpg-37kB

  • 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关系图

这一部分是最顶层的,直接面对外部接口调用的,其关系图如下:
audioPolicyService.jpg-204kB
这里就简单记录一下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的结构如下图所示:
effectChain.jpg-90.5kB
在一个音频thread当中,一个session对应一个effectChain,每个effectChain可以对应若干个track,只要这些track具有相同的session值。每个effectChain是否真正执行除了要看该effect module的状态外还依赖于effectChain是否有关联的track,即:mTrackCnt是否为零,其关系如下图所示:
mTrackCnt.jpg-119.9kB
只有当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的加载,加载过程如下:
loadEffects1.jpg-100.1kB
其实这一部分主要是利用audio_effects.conf建立输入输出流与stream type对应的EffectDesc,EffectDesc在创建AudioEffect时会被用到。关于AudioPolicyService、AudioEffect相关内容详见:相关类及成员说明

2、EffectsFactory的加载,加载过程如下:
loadEffects2.jpg-49.6kB
这里主要是为了创建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文件进行绑定,例如:

  volume_listener {
    path /system/lib/soundfx/libvolumelistener.so
  }

这里定义了一个名为volume_listener的音效库,该库的路径为/system/lib/soundfx/libvolumelistener.so
2、音效定义:定义一个音效名称,然后指定该音效使用哪个音效库,例如:

  music_helper {
    library volume_listener
    uuid 08b8b058-0590-11e5-ac71-0025b32654a0
  }
  ring_helper {
    library volume_listener
    uuid 0956df94-0590-11e5-bdbe-0025b32654a0
  }

这里定义了两个音效,music_helper和ring_helper,都使用了同一个音效库,但是使用的uuid是不同的,一个音效库中可以存在若干个uuid,在做音效处理的时候可以根据不同的uuid采取不同的处理策略,使得音效处理可以针对不同使用者拥有不同的处理方式,而不需要去重新写多个音效库。
3、流类型与音效绑定:定义一个流类型,并且把需要默认挂载到该流类型上的音效绑定到该流上,其中第三部分为可选的,也就是说如果没有针对流类型的默认音效的话,可以没有第三部分,其中第三部分分为两个子模块:output_session_processing和pre_processing,分别对应的其实是输出流和输入流。第三部分的例子:

输出流:

output_session_processing {
    music {
        music_helper {
        }
        sixth_music_helper {
        }
    }
}

输入流:

pre_processing {
  voice_communication {
    aec {
    }
    ns  {
    }
  }
}

这里,在music流类型上挂载了两个音效,music_helper和sixth_music_helper,可以看到,这两个音效的名称都是在第二部分定义好的。
这里有一点要注意:就是流类型的名称,例如本例中的music,这个名字不是随便起的,必须是在代码“audio_effects_conf.h”文件中定义好的!!这里的定义如下:

// audio_source_t
#define MIC_SRC_TAG "mic"                           // AUDIO_SOURCE_MIC
#define VOICE_UL_SRC_TAG "voice_uplink"             // AUDIO_SOURCE_VOICE_UPLINK
#define VOICE_DL_SRC_TAG "voice_downlink"           // AUDIO_SOURCE_VOICE_DOWNLINK
#define VOICE_CALL_SRC_TAG "voice_call"             // AUDIO_SOURCE_VOICE_CALL
#define CAMCORDER_SRC_TAG "camcorder"               // AUDIO_SOURCE_CAMCORDER
#define VOICE_REC_SRC_TAG "voice_recognition"       // AUDIO_SOURCE_VOICE_RECOGNITION
#define VOICE_COMM_SRC_TAG "voice_communication"    // AUDIO_SOURCE_VOICE_COMMUNICATION
#define UNPROCESSED_SRC_TAG "unprocessed"           // AUDIO_SOURCE_UNPROCESSED

// audio_stream_type_t
#define AUDIO_STREAM_DEFAULT_TAG "default"
#define AUDIO_STREAM_VOICE_CALL_TAG "voice_call"
#define AUDIO_STREAM_SYSTEM_TAG "system"
#define AUDIO_STREAM_RING_TAG "ring"
#define AUDIO_STREAM_MUSIC_TAG "music"
#define AUDIO_STREAM_ALARM_TAG "alarm"
#define AUDIO_STREAM_NOTIFICATION_TAG "notification"
#define AUDIO_STREAM_BLUETOOTH_SCO_TAG "bluetooth_sco"
#define AUDIO_STREAM_ENFORCED_AUDIBLE_TAG "enforced_audible"
#define AUDIO_STREAM_DTMF_TAG "dtmf"
#define AUDIO_STREAM_TTS_TAG "tts"

这里所使用的music字符就是对应的#define AUDIO_STREAM_MUSIC_TAG "music"这句话

附2 effect_entry结构分析

相关文件:EffectsFactory.c、EffectsFactory.h
整个effect_entry或者说整个plugin音效与android音效框架的纽带全在这里,这里面关键是要弄清下面三个全局变量的结构:

static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
static list_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的数据结构如下图所示:
effectLists.jpg-426.2kB
其中有关于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的结构如下所示:
gEffectList.jpg-133.2kB
这个比较简单就不多记录了,这个链表是在每次调用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分析

    class EffectClient :
    public android::BnEffectClient, public android::IBinder::DeathRecipient
    {
    public:
        EffectClient(AudioEffect *effect) : mEffect(effect){}
        // IEffectClient
        virtual void controlStatusChanged(bool controlGranted) {
            sp<AudioEffect> effect = mEffect.promote();
            if (effect != 0) {
                effect->controlStatusChanged(controlGranted);
            }
        }
        virtual void enableStatusChanged(bool enabled) 
        virtual void commandExecuted()
    private:
        wp<AudioEffect> mEffect;
    };

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相关的类视图:
EffectClient.jpg-176.7kB
commandExecuted的调用过程:
EffectClientSeq.jpg-89.5kB
这里有一点,为了简便,我把IEffect类和EffectHandle给等价了,其实在mIEffect应该是IEffect类型,但是EffectHandle是继承自IEffect,并且后面都是用的EffectHandle,所以这里就混用了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值