AudioEffect与Equalizer解析(Java侧)
Posted wkw1125
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AudioEffect与Equalizer解析(Java侧)相关的知识,希望对你有一定的参考价值。
##AudioEffect
android2.3增加了对音频混响的支持,通过AudioEffect可以方便地对AudioTrack和MediaPlayer播放的音乐进行音效控制。AudioEffect是音效控制基类,开发者不应直接使用此类,应该使用它的派生类:
- Equalizer 均衡器:增加或降低某一频率的声音响度来达到想要的效果
- Virtualizer 频谱(示波器):音频频谱可视化
- BassBoost 重低音控制器:增加低音的强度
- PresetReverb 预设混响(推荐用于音乐):使音乐通过声音在不同路径传播下造成的反射叠加产生的声音特效,比如流行,古典,爵士等。
- EnvironmentalReverb 环境混响(推荐用于游戏):比如马路,走廊,室内,大厅等
以上音效包含在android.media.audiofx包中,具体使用可以参考 Android实现音乐示波器、均衡器、重低音和音场功能
这里以Equalizer为例,主要使用步骤如下:
//创建MediaPlayer,音频源为/res/raw/audio.mp3
mEqualizer = new Equalizer(0, mMediaPlayer.getAudiosessionId());
//创建Equalizer,通过AudioSessionId绑定到MediaPlayer
Equalizer mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId());
//启用、获取/设置参数
mEqualizer.setEnabled(true);
short bands = mEqualizer.getNumberOfBands();
mEqualizer.setBandLevel(band, level);
//MP播放,创建Equalizer
mMediaPlayer.start();
AudioEffect的具体效果作用在音频数据上,MediaPlayer只管播放音频,二者通过AudioSessionId关联起来。可以说,音效的处理对MediaPlayer是透明的,具体的处理由Android框架进行。
效果图:
(图中几个滑动条是Equalizer的控制,顶部的示波器是Virtualizer)
##Equalizer.java解析
Equalizer继承AudioEffect,二者的Java代码都十分简单,实际处理是通过AudioEffect构造函数中的JNI接口native_setup,调用了C++的代码实现。
(以下代码有删减)
Equalizer是AudioEffect的子类:
public class Equalizer extends AudioEffect
**参数序号定义。**这部分定义了相关参数的序号PARAM_XXX,作为C++代码中参数数组的下标进行访问:
//Band level. Parameter ID for OnParameterChangeListener
public static final int PARAM_BAND_LEVEL = 2;
**成员变量定义。**其中特别的一个成员mParamListenerLock用于参数监听器的线程同步的锁定?:
//Lock for access to mParamListener
private final Object mParamListenerLock = new Object();
**构造函数。**将优先级、音频会话ID、音效类型与实现引擎信息传递给父类AudioEffect构造函数进行初始化。其中,音效类型EFFECT_TYPE_EQUALIZER
、音效实现引擎EFFECT_TYPE_NULL
都由AudioEffect中定义,将在后文介绍。
参数:
- int priority:优先级,多个应用可以共享同一Equalizer引擎,该参数指出控制优先权,默认为0.
- int audioSession:音频会话ID,系统范围内唯一,Equalizer将被附加在拥有相同音频会话ID的MediaPlayer或AudioTrack上生效。
public Equalizer(int priority, int audioSession)
throws IllegalStateException, IllegalArgumentException,
UnsupportedOperationException, RuntimeException
super(EFFECT_TYPE_EQUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
if (audioSession == 0)
Log.w(TAG, "WARNING: attaching an Equalizer to global output mix is deprecated!");
//...
如果要应用全局音频输出的混响效果必须指定audioSession=0
,并且要求有MODIFY_AUDIO_SETTINGS
权限。但是,注意到api的提示:
attaching an Equalizer to the global audio output mix by use of session 0 is deprecated.
使用session=0进行全局音频混响,已经被废弃
**参数get/set。**传入参数,包装为参数数组后调用父类AudioEffect的getParameter/setParameter
进行参数设置,其中参数数组中第一个数是改变参数的序号,如之前设置的PARAM_BAND_LEVEL
。getParameter/setParameter
返回值是操作的结果,传入父类AudioEffect的checkStatus
进行状态检查,若不成功则抛出异常。
/**
* Gets the gain set for the given equalizer band.
* @param band frequency band whose gain is requested.
* @return the gain in millibels of the given band.
*/
public short getBandLevel(short band)
throws IllegalStateException, IllegalArgumentException,
UnsupportedOperationException
int[] param = new int[2];//参数数组
short[] result = new short[1];//返回值数组
param[0] = PARAM_BAND_LEVEL;//第一个参数为参数序号
param[1] = (int)band;//第二个参数为参数值band
checkStatus(getParameter(param, result));//获取gain
return result[0];
**定义音效参数监听器:**面向Equalizer的使用者的监听器Equalizer.OnParameterChangeListener
//The OnParameterChangeListener interface defines a method called by the Equalizer when a parameter value has changed.
public interface OnParameterChangeListener
void onParameterChange(Equalizer effect, int status, int param1, int param2, int value);
**实现基类参数监听:**继承AudioEffect.OnParameterChangeListener
,实现具体的监听方法,对基类传来的参数变化的原始数据进行包装,并传递给Equalizer.OnParameterChangeListener
//Listener used internally to receive unformatted parameter change events from AudioEffect super class.
private class BaseParameterListener implements AudioEffect.OnParameterChangeListener
private BaseParameterListener()
public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value)
OnParameterChangeListener l = null;
//线程同步:锁机制
synchronized(mParamListenerLock)
if (mParamListener != null)
l = mParamListener;
if (l != null)
int p1 = -1;
int p2 = -1;
int v = -1;
// 按字节长度判断参数、值个数
if (param.length >= 4)
p1 = byteArrayToInt(param, 0);
if (param.length >= 8)
p2 = byteArrayToInt(param, 4);
if (value.length == 2)
v = (int)byteArrayToShort(value, 0); ;
else if (value.length == 4)
v = byteArrayToInt(value, 0);
if (p1 != -1 && v != -1)
//包装参数传递给Equalizer.OnParameterChangeListener
l.onParameterChange(Equalizer.this, status, p1, p2, v);
**定义参数包装类Settings:**将音效器的各种参数包装为类,方便访问
public static class Settings
//...
;
public Equalizer.Settings getProperties()
public void setProperties(Equalizer.Settings settings)
##AudioEffect.java解析
- AudioEffect是由Android音频框架提供的音频效果控制的基类,开发者不应直接使用该类,而是该使用该类的派生类,如Equalizer。
- 将AudioEffect应用于特定AudioTrack或MediaPlayer实例时,需要在创建AudioEffect时指定播放器实例的AudioSessionId。共用一个AudioSessionId的AudioTrack和MediaPlayer会共用一个AudioEffect。
- 通过指定session=0对全局音频输出的混响效果已经废弃。
- 创建AudioEffect对象时将会在Android框架中创建对应的音效引擎。当指定audio session中不存在该音效实例时创建,存在时复用。
- 多个应用共享同一个音效引擎。若A正在使用音效引擎,而B用比A更高的优先级创建音效,则B将从A获得该音效引擎的控制权;若B的优先级比A低,则控制权还在A上,B将被告知音效引擎状态或者控制权的变更信息。
**载入so库。**AudioEffect的具体实现是通过JNI调用Android框架so库中C++的代码实现。
public class AudioEffect
static
System.loadLibrary("audioeffect_jni");
native_init();
**定义音效类型Type。**指定了由Android音频框架实现的音效类型Type的128位标识符,与框架中的C++实现挂钩(识别作用)。
//UUID for equalizer effect
public static final UUID EFFECT_TYPE_EQUALIZER = UUID
.fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b");
//Null effect UUID. Used when the UUID for effect type of
public static final UUID EFFECT_TYPE_NULL = UUID
.fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
在框架源码中,音效的C++代码实际上指定了两个UUID类型的属性:Effect_Type、Effect_UUID。Effect_Type指定音效的类型,每种类型可以有多种实现,而Effect_UUID指定某类音效的具体实现引擎。
上述代码中常量名为EFFECT_TYPE,而注释却是“UUID”有点误导,实际上就是指音效类型(而不是音效引擎)。
需要留意一个特别的UUID:UUID EFFECT_TYPE_NULL
UUID(Universally Unique Identifier,通用唯一识别码),这是一个128位的唯一识别码,其生成涉及网卡地址、纳秒级时间、芯片ID等信息。标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12),其中每个x是一个十六进制数字(0-9 a-f)。
Java中的uuid生成:
import java.util.UUID;
UUID uuid = UUID.randomUUID();
**状态值定义:**指定操作的状态,与底层C++代码对应
//State of an AudioEffect object that was not successfully initialized upon creation
public static final int STATE_UNINITIALIZED = 0;
**音效描述类定义:**该类是对音效引擎的描述,在底层C++实现中也有该类型结构体。
//The effect descriptor contains information on a particular effect implemented in the audio framework
public static class Descriptor
public Descriptor(String type, String uuid, String connectMode,
String name, String implementor)
//...
;
连接模式connectMode的说明:
待补充:
EFFECT_INSERT = “Insert”
EFFECT_AUXILIARY = “Auxiliary”
EFFECT_PRE_PROCESSING = “Pre Processing”
**成员变量定义。**其中特别的一个成员mParamListenerLock用于参数监听器的线程同步的锁定?
OnEnableStatusChangeListener //启用状态
OnControlStatusChangeListener //控制权
OnParameterChangeListener //参数变化
Object mListenerLock //同步锁
构造函数。将优先级、音频会话ID、音效类型与实现引擎信息传递给本地方法native_setup,并根据返回值判断初始化操作的结果。
参数:
- UUID type:内置音效类型,若指定了不支持的音效类型将抛出
IllegalArgumentException
异常。扩展的音效类型可以通过新的UUID指定,并且音效需在平台上可用(引入so库)。将该参数设置为EFFECT_TYPE_NULL
可以只使用uuid来指定音效。 - UUID uuid:音效类型的特定实现,将该参数设置为
EFFECT_TYPE_NULL
可以只使用type来指定音效。 - int priority:优先级,多个应用可以共享同一音效引擎,该参数指出控制优先权,默认为0.
- int audioSession:音频会话ID,系统范围内唯一,音效将被附加在拥有相同音频会话ID的MediaPlayer或AudioTrack上生效。
public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
throws IllegalArgumentException, UnsupportedOperationException,
RuntimeException
//...
// native initialization
int initResult = native_setup(new WeakReference<AudioEffect>(this), type.toString(), uuid.toString(), priority, audioSession, id, desc);
if (initResult != SUCCESS && initResult != ALREADY_EXISTS)
switch (initResult)
case ERROR_BAD_VALUE://...
case ERROR_INVALID_OPERATION://...
default://...
//...
如果要应用全局音频输出的混响效果必须指定audioSession=0
,并且要求有MODIFY_AUDIO_SETTINGS
权限。但是,注意到api的提示:
attaching an Equalizer to the global audio output mix by use of session 0 is deprecated.
使用session=0进行全局音频混响,已经被废弃
**参数get/set。**传入参数数组给本地方法native_setParameter。param数组中包含修改参数的序号(修改哪些音效参数),value数组是修改的音效参数值。其他get/set是对setParameter(byte[], byte[])
的重载。
public int setParameter(byte[] param, byte[] value)
throws IllegalStateException
checkState("setParameter()");
return native_setParameter(param.length, param, value.length, value);
public int getParameter(byte[] param, byte[] value)
throws IllegalStateException
checkState("getParameter()");
return native_getParameter(param.length, param, value.length, value);
**引擎命令:**向音效引擎发送命令,与底层C++实现对接。
public int command(int cmdCode, byte[] command, byte[] reply)
throws IllegalStateException
checkState("command()");
return native_command(cmdCode, command.length, command, reply.length, reply);
**其他:**监听定义、广播定义、本地方法与Java方法互相调用的声明、以及checkState、byteArrayToInt等工具方法的定义。
##AudioEffect的底层实现(native侧)
业务要求实现新的AudioEffect引擎,进行音频升降调处理。从Equalizer、AudioEffect的Java代码可以知道,具体的实现其实都是在JNI本地方法中,所以要实现新的音效引擎,**需编写底层引擎代码(c++)**并在Android框架下编译。因此,得了解AudioEffect在框架中的具体实现。
本文主要记录AudioEffect的Java层实现,底层框架C++的实现请移步:
AudioEffect底层框架代码跟踪(native侧)
##参考
以上是关于AudioEffect与Equalizer解析(Java侧)的主要内容,如果未能解决你的问题,请参考以下文章