AudioEffect构造流程跟踪
为了编写新的音效实现,需要了解Android底层在AudioEffect的底层实现: 在Java层new Equalizer();后,通过JNI进入底层C/C++的实现过程。在底层,通过层层调用,由音控中枢AudioFlinger.cpp负责音效的管理,在线程中使用音效工厂EffectFactory.c读取.conf配置文件完成音效实例的创建。
AudioEffect构造流程图
详细代码跟踪参考: 安卓音效AudioEffect源码剖析1——构造流程 根据上文,我做了张图帮助理解,其中将涉及到的类、头文件的关系列出,并标明了代码路径方便查找。当然,图中只列出了构造流程的关键代码。
音效工厂EffectsFactory.c中的EffectCreate方法中的三个调用:
ret
= init();
ret
= findEffect(
NULL, uuid,
&l,
&d);
ret
= l
->desc
->create_effect(uuid, sessionId, ioId,
&itfe);
配置文件路径
init()读取运行环境的音效配置文件,如果vendor/etc/audio_effects.conf存在则使用该配置,若不存在则使用系统的system/etc/audio_effects.conf。
配置文件的路径定义在 /system/media/audio_effects/include/audio_effects/audio_effects_conf.h,有以下值:
常量值顺序
AUDIO_EFFECT_VENDOR_CONFIG_FILEvendor/etc/audio_effects.conf优先AUDIO_EFFECT_DEFAULT_CONFIG_FILEsystem/etc/audio_effects.conf其次
配置文件audio_effects.conf
audio_effects.conf配置文件内声明了平台所支持的音效库,新增音效库时需对该文件进行修改。 AOSP路径:/frameworks/av/media/libeffects/data/audio_effects.conf
配置内容如下:
libraries {
...
bundle {
path /system/lib/soundfx/libbundlewrapper.so
}
...
}
effects {
...
bassboost {
library bundle
uuid 8631f300-
72e2-11df-b57e-0002a5d5c51b
}
equalizer {
library bundle
uuid ce772f20-847d-11df-bb17-0002a5d5c51b
}
...
}
libraries指出了音效库.so文件路径,默认是在平台的/system/lib/soundfx/目录下,新增的音效库so也要放在此处。 effects定义了音效名、音效库、实现引擎uuid的关系。注意到,同一个音效库so可以包含多种音效引擎。
到此,Java层到底层C/C++层层调用,找到.so库完成对音效的创建。
AudioEffect音效库实现
公司导师要求做一个新的AudioEffect音效库实现音频的升降调,目前已经使用SoundTouch实现。 这里解读Android自带的音效库Visualizer实现,因为这个音效实现代码最为简约,模仿这个音效库容易写出新库。
Visualizer相关
Visualizer路径
音效库.so(运行平台)/system/lib/soundfx/libvisualizer.so编译脚本/frameworks/av/media/libeffects/visualizer/Android.mk实现.cpp/frameworks/av/media/libeffects/visualizer/EffectVisualizer.cpp头文件.h/system/media/audio_effects/include/audio_effects/effect_visualizer.h
创建新库的过程是编写EffectVisualizer.cpp实现effect_visualizer.h中的接口,并用编译脚本Android.mk编译为音效库libvisualizer.so。前3个对象是要编写的内容。
头文件effect_visualizer.h
#ifndef ANDROID_EFFECT_VISUALIZER_H_
#define ANDROID_EFFECT_VISUALIZER_H_
#include <hardware/audio_effect.h>
#if __cplusplus
extern
"C" {
#endif
#define VISUALIZER_SCALING_MODE_NORMALIZED 0
#define VISUALIZER_SCALING_MODE_AS_PLAYED 1
typedef
enum
{
VISUALIZER_PARAM_CAPTURE_SIZE,
VISUALIZER_PARAM_SCALING_MODE,
VISUALIZER_PARAM_LATENCY,
} t_visualizer_params;
typedef
enum
{
VISUALIZER_CMD_CAPTURE = EFFECT_CMD_FIRST_PROPRIETARY,
}t_visualizer_cmds;
#if __cplusplus
}
#endif
#endif /*ANDROID_EFFECT_VISUALIZER_H_*/
实现EffectVisualizer.cpp
#define LOG_TAG "EffectVisualizer"
#include <cutils/log.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <new>
#include <time.h>
#include <audio_effects/effect_visualizer.h>
extern "C" {
extern const struct effect_interface_s gVisualizerInterface;
const effect_descriptor_t gVisualizerDescriptor = {
{
0xe46b26a0,
0xdddd,
0x11db,
0x8afd, {
0x00,
0x02,
0xa5,
0xd5,
0xc5,
0x1b}},
{
0xd069d9e0,
0x8329,
0x11df,
0x9168, {
0x00,
0x02,
0xa5,
0xd5,
0xc5,
0x1b}},
EFFECT_CONTROL_API_VERSION,
(EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
0,
1,
"Visualizer",
"The Android Open Source Project",
};
enum visualizer_state_e {
VISUALIZER_STATE_UNINITIALIZED,
VISUALIZER_STATE_INITIALIZED,
VISUALIZER_STATE_ACTIVE,
};
struct VisualizerContext {
const struct effect_interface_s *mItfe;
effect_config_t mConfig;
uint32_t mCaptureIdx;
uint32_t mCaptureSize;
uint32_t mScalingMode;
uint8_t mState;
uint8_t mLastCaptureIdx;
uint32_t mLatency;
struct timespec mBufferUpdateTime;
uint8_t mCaptureBuf[CAPTURE_BUF_SIZE];
};
void Visualizer_reset(VisualizerContext *pContext)
int Visualizer_setConfig(VisualizerContext *pContext, effect_config_t *pConfig)
int Visualizer_init(VisualizerContext *pContext)
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
tag : AUDIO_EFFECT_LIBRARY_TAG,
version : EFFECT_LIBRARY_API_VERSION,
name :
"Visualizer Library",
implementor :
"The Android Open Source Project",
#if EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION) <= 2
query_num_effects : VisualizerLib_QueryNumberEffects,
query_effect : VisualizerLib_QueryEffect,
#endif
create_effect : VisualizerLib_Create,
release_effect : VisualizerLib_Release,
get_descriptor : VisualizerLib_GetDescriptor,
};
#if EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION) <= 2
int VisualizerLib_QueryNumberEffects(uint32_t *pNumEffects)
int VisualizerLib_QueryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
#endif
int VisualizerLib_Create(
const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle)
int VisualizerLib_Release(effect_handle_t handle)
int VisualizerLib_GetDescriptor(
const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor)
const struct effect_interface_s gVisualizerInterface = {
Visualizer_process,
Visualizer_command,
Visualizer_getDescriptor,
NULL,
};
int Visualizer_process(effect_handle_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
void *pCmdData, uint32_t *replySize,
void *pReplyData) {
VisualizerContext * pContext = (VisualizerContext *)self;
int retsize;
if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) {
return -EINVAL;
}
switch (cmdCode) {
case EFFECT_CMD_INIT:
if (pReplyData == NULL || *replySize !=
sizeof(
int)) {
return -EINVAL;
}
*(
int *) pReplyData = Visualizer_init(pContext);
break;
case EFFECT_CMD_SET_CONFIG:
case EFFECT_CMD_GET_CONFIG:
case EFFECT_CMD_RESET:
case EFFECT_CMD_ENABLE:
case EFFECT_CMD_DISABLE:
case EFFECT_CMD_GET_PARAM: {
switch (*(uint32_t *)p->data) {
case VISUALIZER_PARAM_CAPTURE_SIZE:
ALOGV(
"get mCaptureSize = %d", pContext->mCaptureSize);
*((uint32_t *)p->data +
1) = pContext->mCaptureSize;
p->vsize =
sizeof(uint32_t);
*replySize +=
sizeof(uint32_t);
break;
case VISUALIZER_PARAM_SCALING_MODE:
default:
}
}
break;
case EFFECT_CMD_SET_PARAM: {
switch (*(uint32_t *)p->data) {
case VISUALIZER_PARAM_CAPTURE_SIZE:
pContext->mCaptureSize = *((uint32_t *)p->data +
1);
ALOGV(
"set mCaptureSize = %d", pContext->mCaptureSize);
break;
case VISUALIZER_PARAM_SCALING_MODE:
case VISUALIZER_PARAM_LATENCY:
default:
}
}
break;
case EFFECT_CMD_SET_DEVICE:
case EFFECT_CMD_SET_VOLUME:
case EFFECT_CMD_SET_AUDIO_MODE:
break;
case VISUALIZER_CMD_CAPTURE:
break;
default:
}
return 0;
}
int Visualizer_getDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
编译脚本Android.mk
LOCAL_PATH:=
$(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
EffectVisualizer.cpp
LOCAL_CFLAGS+= -
O2
LOCAL_SHARED_LIBRARIES := \
libcutils \
libdl
LOCAL_MODULE_PATH :=
$(TARGET_OUT_SHARED_LIBRARIES)/soundfx
LOCAL_MODULE:= libvisualizer
LOCAL_C_INCLUDES := \
$(call
include-path-
for, graphics corecg) \
$(call
include-path-
for, audio-effects)
include $(BUILD_SHARED_LIBRARY)
编译音效库.so
将编写的音效文件EffectVisualizer.cpp、effect_visualizer.h、Android.mk置于AOSP的/external/目录下,使用Android.mk进行编译,成功的话会在/system/lib/soundfx/目录中生成libvisualizer.so。
P.S. 在NDK中编译音效库是不够的,缺乏audio_effect.h相关库。
编写中介类Visualizer.java
完成了底层的音效库实现后,在Java层编写对应的类进行调用。Visualizer.java只是“中介”,代码十分简单,继承音效基类AudioEffect.java,实现相关的调用即可。具体编写可参考: AudioEffect与Equalizer解析(Java侧)中对中介类的解读。
type和uuid的区别? 对于Android预设的Equalizer等音效,在AudioEffect基类中定义的EFFECT_TYPE_EQUALIZER对应底层音效引擎.cpp中effect_descriptor_t结构体的type字段。因此,在Equalizer等预设音效类的构造函数中,均指定type参数而将uuid参数指定为EFFECT_TYPE_NULL,如:
public Equalizer(
int priority,
int audioSession)
throws IllegalStateException, IllegalArgumentException,
UnsupportedOperationException, RuntimeException {
super(EFFECT_TYPE_EQUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
按我的理解,如果只指定type,系统会自动查找支持该type音效的一个音效引擎进行实现;如果只指定uuid,那么不管是什么类型的音效,系统直接使用指定uuid的引擎进行实现。一开始在这上面弄糊涂了,导致系统一直找不到编写好的音效库。
如何使用@hide? 在Java层继承AudioEffect实现新的音效中介类时,会遇到AudioEffect的几乎所有的public字段、方法都被@hide 标记导致无法在子类中调用的问题。
原因与解决方法参考:Android中使用@hide成员
导入classes.jar包(注意:要勾上System library(addedto the boot class path),否则“Java heap space”爆满)Java反射机制
修改配置文件audio_effects.conf
如果只是单独编译了新的音效库用于现有Android平台(手机)的使用,那么需要修改平台的system/etc/audio_effects.conf配置文件,加入新音效库的.so库路径以及新音效的effect uuid。 (猜测)如果是重编译整个Android版本的话,修改/frameworks/av/media/libeffects/data/audio_effects.conf文件后进行编译即可自动修改平台运行配置文件。
在手机上使用新音效
假设单独编译了新音效库libnew.so,也正确编写新应用.apk,那么要在手机上让新音效库生效,需要以下步骤:
将新音效库libnew.so放到/system/lib/soundfx/目录下修改配置文件:system/etc/audio_effects.conf,加入新音效库路径path以及effect uuid重启手机,运行apk
P.S. 修改手机system/下目录和文件需要root权限
adb root
adb remount
adb shell cp
...
参考
安卓音效AudioEffect源码剖析1——构造流程安卓音效AudioEffect源码剖析2——音效库接口Android AudioEffect机制初探Android源码分析:AudioEffectAudioEffect与Equalizer解析(Java侧)Android中使用@hide成员