• Android AudioEffect
  • 前言
  • Android AudioEffect框架
    • AudioEffect Framework Java框架
    • AudioEffect CC
      • AudioEffect 的调用关系
      • EffectModule
      • EffectHandle
      • EffectChain
  • 如何启动
  • Audio Effect 如何实现

Android AudioEffect

前言

支持多媒体是Android设备的一个重要功能,可以想象一台不支持多媒体的设备是何等枯燥。通常意义上的多媒体(Multimedia)通常是指图片、视频、音频、文本等等。这其中对于音频的需求最高。可以通过种种手段来提升音频的体验,总体分为软件和硬件两大类。例如:HiFi耳机、smartPA、音源修复算法、音效算法等等。软件层面,可以大致分为基于音频数据流的处理方式以及基于Android的音效框架的处理方式。基于音频数据流的处理方式比较容易理解,基本原理就是先把数据丢给3rd的库进行处理,接着将处理完成的数据重新写入原有数据流的节点即可。这里主要探讨下基于Android的音效框架的处理方式。

  1. 通过本文可以有如下的初步了解
  2. 可以指导修复基于Android原生audido effect架构的音效问题
  3. 可以知道集成类似audio effect的集成、开发等工作 可以了解Android 原生effect架构

Android AudioEffect框架

AudioEffect Framework Java框架

Framework java层的结构如下图所示。向上层提供了一系列基础的用于控制audio effect的类。需要注意的是,不建议直接使用AudioEffect这个父类;父类只是提供了统一的方法,并不是‘音效’的具体实现。
Android Audio Effect 机制初探_第1张图片

AudioEffect C/C++

这部分是其主要实现,主要从如下方面入手进行介绍。

AudioEffect 的调用关系

从初始化说起;所有的具体音效的java接口都继承自AudioEffect.java,AudioEffect或调用native_setup 进行初始化。接下来的流程如下图所示,一步步的跟就可以。其中Threads /AudioFilger/AudioSystem用的比较多了,不针对去进行说明了。这里主要针对EffectModule /EffectChain/EffectHandle 这三个类进行一下介绍。EffectChain EffectModule EffectHandle

EffectModule

EffectModule是一个封装类,封装了3rd音效引擎的实现;可以控制类似process()\command()通过不同进程的并发调用。保存着一个用于同所有客户端进行同步的EffectHandle用于处理音效状态、参数更改;EffectModule同时管控着音效引擎的状态机:重置、使能、以及在状态切换过程中的声音淡入淡出效果。

EffectHandle

EffectHandleIEffect接口的具体实现,他提供了一系列资源来接受参数更新、跟踪效果控制的所有权和状态;并具有一个指向EffectModule的指针控制着EffectModule对象。每个应用只能使用一个EffectHandle来控制。EffectHandle由AudioFlinger::createEffect()创建。

EffectChain

EffectChain展示了一系列音效同audio session之间的关系;每一个output mixer thread (playbackthread)可以关联任意多个EffectChain对象。当EffectChain同ID为0的session关联时,EffectChain作用于所有的output mix。
EffectChain EffectMode EffectHandle这三者的关系类似于MVC.当然这里的view说起来可能有些牵强。但可以这么来理解EffectMode就算这里的Model,EffectHandle就是这里的control,EffectChain就是这里的View。总的来说:EffectChain代表了一系列音效之前的关系,EffectMode是所有音效实现的抽象,EffectHandle是所有音效的操作的抽象。
Android Audio Effect 机制初探_第2张图片
图1-1 audio effect常见类时序图
从上图看到,我们不仅仅需要将上层的信息应用到音效库中(setParamater),我们还需要感知下层的状态;这里选择了一个统一的函数指针作为信息传递的中介。先来看看签名:
typedef void (*effect_callback_t)(int32_t event,void *user,void *info);event 代表不同的消息类型,user代表传递给的不同客户端,info代表所携带的信息
这里写图片描述
图1-2 effectCallback 函数签名
当下层的音效引擎的相关状态发生改变时,由该机制通知客户端。同一音效引擎可以被多个客户端复用,但是在同一时刻只能有一个是处于活动状态。不同的状态取值如event_type所示:
Android Audio Effect 机制初探_第3张图片
图1-3 传递event类型
EVENT_CONTROL_STATUS_CHANGE:
当另一客户端尝试使用相同的type、优先级更高的参数创建AudioEffect时,当前客户端会获得effect engine的控制权;对应的另一客户端就丢失了控制权并发出该状态
EVENT_ENABLE_STATUS_CHANGED:
当disable/enable时,所有没有获得effect控制的,或者说不是在激活状态的effect都会收到该状态
EVENT_PARAMETER_CHANGE:
当参数更新时,会收到该状态
EVENT_ERROR:
当media server process dies时会受到该状态
通常情况下,绝大部分APPS是由java语言编写,但通过函数指针的方式我们最多可以将状态从底层的so库传递到JNI层;如何将so库中的状态传递到上层的APPS中,是接下来要讨论的。这里APPS,我们先简单的理解成AudioEffect.java的一个实例,audioEffect_class是AudioEffect.class的引用;audioEffect_ref则是指AudioEffect的一个实例;相关数据结构如下所示:
Android Audio Effect 机制初探_第4张图片
图1-4 JNI存储java对象数据结构
接下来了解下初始化的过程,很简单获得AudioEffect.class的一个引用,保存AudioEffect的一个实例;然后将保存有AudioEffect.class引用和AudioEffect实例的结构体的地址给AudioEffect的构造函数。如下图所示
Android Audio Effect 机制初探_第5张图片
图1-5 JNI中保存java对象的初始化
AudioEffect构造函数如下所示:
Android Audio Effect 机制初探_第6张图片
图1-6 AudioEffect构造函数

如何启动

Audio effect的启动,或者说所有audio effect so的加载是基于audiopolicy services来进行的。当AudioPolicyServices启动之后,会创建一个AudioPolicyEffect对象;在AudioPolicyEffect构造函数中对所有的effect so进行加载。时序图如下图所示

Android Audio Effect 机制初探_第7张图片
图1-7 audio effect so 加载过程

Audio Effect 如何实现

说到3rd audio effect的实现,必然会考虑到如何对其进行统一的管理,如何将其抽象出统一的接口。接下来参照如下数据结构audio_effect_library_t定义了所有effect的一个统一接口。

// 所有的音效库必须实现一个名为AUDIO_EFFECT_LIBRARY_INFO_SYM的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 APIuint32_t version;    // Name of this library    const char *name;    // Author/owner/implementor of the library    const char *implementor;    int32_t (*create_effect)(const effect_uuid_t *uuid,                             int32_t sessionId,                             int32_t ioId,                             effect_handle_t *pHandle);    int32_t (*release_effect)(effect_handle_t handle);    int32_t (*get_descriptor)(const effect_uuid_t *uuid,                              effect_descriptor_t *pDescriptor);} audio_effect_library_t;

看具体的实例,downmix(frameworks/av/media/libeffects/downmix/)。将create_effect\release_effect\get_descriptor函数指针具体化。

// This is the only symbol that needs to be exported__attribute__ ((visibility ("default")))audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {    .tag = AUDIO_EFFECT_LIBRARY_TAG,    .version = EFFECT_LIBRARY_API_VERSION,    .name = "Downmix Library",    .implementor = "The Android Open Source Project",    .create_effect = DownmixLib_Create,    .release_effect = DownmixLib_Release,    .get_descriptor = DownmixLib_GetDescriptor,};

audio_buffer_t定义了音效输入输出的数据格式

struct audio_buffer_s {    size_t   frameCount;// number of frames in buffer    union {        void*       raw;// raw pointer to start of buffer        int32_t*    s32;// pointer to signed 32 bit data at start of buffer        int16_t*    s16;// pointer to signed 16 bit data at start of buffer        uint8_t*    u8; // pointer to unsigned 8 bit data at start of buffer    };};

effect_param_t定义了音效之间、系统上下之间的通信协议(数据、格式等等)

// effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM// command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command.// psize and vsize represent the actual size of parameter and value.typedef struct effect_param_s {// Transaction status (unused for command, used for reply)    int32_t     status;    uint32_t    psize;      // Parameter size    uint32_t    vsize;      // Value size    char        data[];     // Start of Parameter + Value data} effect_param_t;

更多相关文章

  1. Android P SystemUI下拉时,状态栏和通知栏显示位置不一致。
  2. Android Studio中隐藏状态栏和标题栏的方法
  3. 修改frameworks源码去掉android的下拉通知状态栏
  4. Android中的状态选择器
  5. Android中状态栏的隐藏
  6. Android Service判断设备联网状态详解
  7. android 设置 button 不同状态的图片
  8. Android的状态栏通知(Notification)
  9. C语言函数以及函数的使用

随机推荐

  1. 转载:Android中如何修改系统时间(应用程序
  2. Android(安卓)5.0(Lollipop)中的SurfaceT
  3. 【android】 百度地图应用开发(一)
  4. 初学Android——闪光灯当做手电筒使用
  5. Ruby实现Android自动化屏幕适配
  6. 深入浅出Android(安卓)Handler
  7. Android(安卓)之Sub Menu案例
  8. Android判断机器默认屏幕方向
  9. android常用包介绍
  10. Android(安卓)美化之Toolbar控件使用