2024年1月23日发(作者:)
ArcherMind Inc. Confidential
Audio音效的设置和控制
学习总结
History
V1
完成文档
12年04月23日
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 1 of 33
Author 王京阳
目录1.0 Audio系统综述 ............................................................................................................ 2
1.1 Audio系统结构 ........................................................ 3
1.2 Audio系统代码位置 .................................................... 4
1.3 Audio系统和上层接口 .................................................. 5
1.3.1 Audio系统的各个层次 ............................................ 5
1.3.2 media库中的Audio框架部分 ...................................... 6
1.3.3 AudioFlinger本地代码 ........................................... 8
1.3.4 Audio系统的JNI代码 ............................................ 9
1.3.5 Audio系统的Java代码 ........................................... 9
2.0 音效设置及控制 ........................................................................................................... 11
2.1 不同类型声音资源的设置 ............................................... 11
2.2 拍照/录音时候的声音文件 .............................................. 12
2.3 默认铃声的设置 ....................................................... 14
2.4 拨号界面专属拨号按键音设置 ........................................... 17
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 2 of 33
2.5 添加音频效果的设置项 ................................................. 19
2.6 音量控制 ............................................................. 24
2.7 音频系统 ............................................................. 27
2.7.1 音频类型 ...................................................... 28
2.7.2 输入输出设备 .................................................. 28
2.7.3 音频策略管理 .................................................. 30
2.8 RingerMode ........................................................... 31
2.8.1利用AudioManager使用RingerMode ............................... 31
2.8.2 RingerMode在AudioService中内部实现 ........................... 31
2.8.3 配置RingerMode对特定AudioStream的影响 ....................... 32
1.0 Audio系统综述
Audio系统在Android中负责音频方面输入/输出层次〃一般负责播放PCM声音输出和从外部获取PCM声音〃以及管理声音设备和设置。
1.1 Audio系统结构
主要分成如下几个层次:
(1)media库提供的Audio系统本地部分接口;
(2)AudioFlinger作为Audio系统的中间层;
(3)Audio的硬件抽象层提供底层支持;
(4)Audio接口通过JNI和Java框架提供给上层。
Audio系统的各个层次接口主要提供了两方面功能:放音(Track)和录音
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 3 of 33
(Recorder)。
Android的Audio系统结构如图1-1所示。
图1.1Android的audio系统结构
1.2 Audio系统代码位置
Android系统的代码分布情况如下所示:
(1)Audio的Java部分
代码路径:frameworks/base/media/java/android/media
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 4 of 33
与Audio相关的Java包是〃主要包含AudioManager和Audio系统的几个类。
(2)Audio的JNI部分
代码路径:frameworks/base/core/jni
生成库libandroid_〃Audio的JNI是其中的一个部分。
(3)Audio的框架部分
头文件路径:frameworks/base/include/media/
源代码路径:frameworks/base/media/libmedia/
Audio本地框架是media库的一部分〃本部分内容被编译成库〃提供Audio部分的接口(包括基于Binder的IPC机制)。
(4)Audio Flinger
代码路径:frameworks/base/services/audioflinger(注:android2.3)
这部分内容被编译成库〃它是Audio系统的本地服务部分。
(5)Audio的硬件抽象层接口
头文件路径:hardware/libhardware_legacy/include/hardware/
Audio硬件抽象层的实现在各个系统中可能是不同的〃需要使用代码去继承相应的类并实现它们〃作为Android系统本地框架层和驱动程序接口。
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 5 of 33
1.3 Audio系统和上层接口
在Android中〃Audio系统自上而下由Java的Audio类、Audio本地框架类、AudioFlinger和Audio的硬件抽象层几个部分组成。
1.3.1 Audio系统的各个层次
Audio本地框架类是的一个部分〃这些Audio接口对上层提供接口〃由下层的本地代码去实现。
AudioFlinger继承libmeida中的接口〃提供实现库。这部分内容没有自己的对外头文件〃上层调用的只是libmedia本部分的接口〃但实际调用的内容是。
Audio使用JNI和Java对上层提供接口〃JNI部分通过调用libmedia库提供的接口来实现。
Audio的硬件抽象层提供到硬件的接口〃供AudioFlinger调用。Audio的硬件抽象层实际上是各个平台开发过程中需要主要关注和独立完成的部分。
表 1-1 Android Audio各个层次的对应关系
Audio管理环节 Audio输出 Audio输入
在各个层次之间具有对应关系〃如表1-1所示所示。
Java层
System ioTrack ecorder
本地框架层
AudioSystem AudioTrack AudioRecorder
AudioFlIAudioFlinger IAudioTrack IAudioRecorder
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 6 of 33
inger
硬件抽AudioHardwareInter象层
face
AudioStreamOut AudioStreamIn
1.3.2 media库中的Audio框架部分
Android的Audio系统的核心框架在media库中提供〃对上面主要实现AudioSystem、AudioTrack和AudioRecorder三个类。
提供了IAudioFlinger类接口〃在这个类中〃可以获得IAudioTrack和IAudioRecorder两个接口〃分别用于声音的播放和录制。AudioTrack和AudioRecorder分别通过调用IAudioTrack和IAudioRecorder来实现。
Audio系统的头文件在frameworks/base/include/media/目录中〃主要的头文件如下:
AudioSystem.h:media库的Audio部分对上层的总管接口;
IAudioFlinger.h:需要下层实现的总管接口;
AudioTrack.h:放音部分对上接口;
IAudioTrack.h:放音部分需要下层实现的接口;
AudioRecorder.h:录音部分对上接口;
IaudioRecorder.h:录音部分需要下层实现的接口。
IAudioFlinger.h、IAudioTrack.h和IAudioRecorder.h这三个接口通过下层的继承来实现(即AudioFlinger)。AudioFlinger.h、AudioTrack.h和AudioRecorder.h是对上层提供的接口〃它们既供本地程序调用(例如声音的播放器、录制器等)〃也可以通过JNI向Java层提供接口。
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 7 of 33
meida库中Audio部分的结构如图1-2所示。
从功能上看〃AudioSystem负责的是Audio系统的综合管理功能〃而AudioTrack和AudioRecorder分别负责音频数据的输出和输入〃即播放和录制。
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 8 of 33
AudioSystem.h中主要定义了一些枚举值和set/get等一系列接口;在Audio系统的几个枚举值中〃audio_routes是由单独的位来表示的〃而不是由顺序的枚举值表示〃因此这个值在使用过程中可以使用"或"的方式。例如〃表示声音可以既从耳机(EARPIECE)输出〃也从扬声器(SPEAKER)输出〃这样是否能实现〃由下层提供支持。在这个类中〃set/get等接口控制的也是相关的内容〃例如Audio声音的大小、Audio的模式、路径等。
AudioTrack是Audio输出环节的类〃其中最重要的接口是write();
AudioRecord是Audio输入环节的类〃其中最重要的接口为read()
AudioTrack和AudioRecord的read/write函数的参数都是内存的指针及其大小〃内存中的内容一般表示的是Audio的原始数据(PCM数据)。这两个类还涉及Auido数据格式、通道数、帧数目等参数〃可以在建立时指定〃也可以在建立之后使用set()函数进行设置。
在libmedia库中提供的只是一个Audio系统框架〃AudioSystem、AudioTrack和AudioRecord分别调用下层的IAudioFlinger、IAudioTrack和IAudioRecord来实现。另外的一个接口是IAudioFlingerClient〃它作为向IAudioFlinger中注册的监听器〃相当于使用回调函数获取 IAudioFlinger运行时信息。
1.3.3 AudioFlinger本地代码
AudioFlinger是Audio系统的中间层〃在系统中起到服务作用〃它主要作为libmedia提供的Audio部分接口的实现〃其代码路径为:frameworks/base/libs/audioflinger
AudioFlinger的核心文件是AudioFlinger.h和〃提供了类AudioFlinger〃这个类是一个IAudioFlinger的实现
AudioFlinger主要提供createTrack()创建音频的输出设备IAudioTrack〃openRecord()创建音频的输入设备IAudioRecord。另外包含的就是一个get/set接口〃用于控制。
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 9 of 33
从工作的角度看〃AudioFlinger在初始化之后〃首先获得放音设备〃然后为混音器(Mixer)建立线程〃接着建立放音设备线程〃在线程中获得放音设备。
在AudioFlinger的AudioResampler.h中定义了一个音频重取样器工具类〃这个音频重取样工具包含3种质量:低等质量(LOW_QUALITY)将使用线性差值算法实现;中等质量(MED_QUALITY)将使用立方差值算法实现;高等质量(HIGH_ QUALITY)将使用FIR(有限阶滤波器)实现。AudioResampler中的AudioResamplerOrder1是线性实现〃AudioResamplerCubic.*文件提供立方实现方式〃AudioResamplerSinc.*提供FIR实现。
AudioMixer.h和中实现的是一个Audio系统混音器〃它被AudioFlinger调用〃一般用于在声音输出之前的处理〃提供多通道处理、声音缩放、重取样。AudioMixer调用了AudioResampler。
1.3.4 Audio系统的JNI代码
Android的Audio部分通过JNI向Java层提供接口〃在Java层可以通过JNI接口完成Audio系统的大部分操作。Audio JNI部分的代码路径为:frameworks/base/core/jni。
其中〃主要实现的3个文件为:android_media_、android_media_Audio 和android_media_〃它们分别对应了Android Java框架中的3个类的支持:
ystem:负责Audio系统的总体控制;
rack:负责Audio系统的输出环节;
ecorder:负责Audio系统的输入环节。
在Android的Java层中〃可以对Audio系统进行控制和数据流操作〃对于控制操作〃和底层的处理基本一致;但是对于数据流操作〃由于Java不支持指针〃因此接口被封装成了另外的形式。
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 10 of 33
向Java提供native_write_byte和native_write_short接口〃它们一般是通过调用AudioTrack的write()函数来完成的〃只是在Java的数据类型和C++的指针中做了一步转换。
1.3.5 Audio系统的Java代码
Android的Audio系统的相关类在包中〃Java部分的代码路径为:
frameworks/base/media/java/android/media
Audio系统主要实现了以下几个类:ystem、.
AudioTrack、ecorder、ormat。前面的3个类和本地代码是对应的〃AudioFormat提供了一些Audio相关类型的枚举值。
注意:在Audio系统的Java代码中〃虽然可以通过AudioTrack和AudioRecorder的write()和read()接口〃在 Java层对Audio的数据流进行操作。但是〃更多的时候并不需要这样做〃而是在本地代码中直接调用接口进行数据流的输入/输出〃而Java层只进行控制类操作〃不处理数据流。
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 11 of 33
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 12 of 33
2.0 音效设置及控制
2.1
不同类型声音资源的设置
(1)TCL项目的默认音频文件资源在
gin_wimdata_ng/wcustores/Audios/frameworks/base/data/sounds/下面〃其中有Alarm、Notification、Ringtones、Switch_On_Off、UI这几个文件夹〃分别存放不同类型的音频文件资源
(2)位置:frameworks/base/media/java/android/media/
/* Sound effect file names */
private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
private static final String[] SOUND_EFFECT_FILES = new String[] {
//PR-240541-Jingyang-Wang begin
//"Effect_",
"Effect_3",
//PR-240541-Jingyang-Wang end
"",
"",
"",
"",
""//CR231720-Xiaodan-Fan-001
};
/* Sound effect file name mapping sound effect id (_xxx) to
* file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
* uses soundpool (second column) */
private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
{0, -1}, // FX_KEY_CLICK
{0, -1}, // FX_FOCUS_NAVIGATION_UP
{0, -1}, // FX_FOCUS_NAVIGATION_DOWNa/audio/ui/ 目录下。
{0, -1}, // FX_FOCUS_NAVIGATION_LEFT
{0, -1}, // FX_FOCUS_NAVIGATION_RIGHT
{1, -1}, // FX_KEYPRESS_STANDARD
{2, -1}, // FX_KEYPRESS_SPACEBAR
{3, -1}, // FX_FOCUS_DELETE
{4, -1} // FX_FOCUS_RETURN
};
/**
* Loads samples into the soundpool.
* This method must be called at when sound effects are enabled
*/
public boolean loadSoundEffects(){
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 13 of 33
…
String filePath = tDirectory() + SOUND_EFFECTS_PATH +
SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
…
return true;
}
private void playSoundEffect(int effectType, int volume) {
…
}
音效文件放在:/system/media/audio/ui/ 目录下。
2.2 拍照/录音时候的声音文件
位置:frameworks/base/services/camera/libcameraservices/下
void CameraService::loadSound() {
Mutex::Autolock lock(mSoundLock);
LOG1("CameraService::loadSound ref=%d", mSoundRef);
if (mSoundRef++) return;
char value[PROPERTY_VALUE_MAX];
property_get("e", value, "0");
int disableSound = atoi(value);
// [camera], @, Sep 13, 2011,
// FR158416, add SDMID Def_shuttersound_enable
char soundPlayValue[PROPERTY_VALUE_MAX];
property_get("", soundPlayValue, "true");
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 14 of 33
if(!disableSound && !strncmp(soundPlayValue, "true", 4)) {
mSoundPlayer[SOUND_SHUTTER] = newMediaPlayer("/system/media/audio/ui/camera_3");
mSoundPlayer[SOUND_RECORDING] = newMediaPlayer("/system/media/audio/ui/");
}
// create listener object
mSoundListener = new ShutterSoundListener();
if(mSoundPlayer[SOUND_SHUTTER].get() && ())
{
mSoundPlayer[SOUND_SHUTTER]->setListener(mSoundListener); //setlistener
}
}
2.3 默认铃声的设置
文件:frameworks/base/media/java/android/media/
TCL项目中默认的设置位于
gin_wimdata_ng/wprocedures/plf〃具体到Setting〃就是isdm_和isdm_这两个文件
例如isdm_中以下定义定义了iIncoming call默认铃声
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 15 of 33
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 16 of 33
packages/apps/Settings/src/com/android/settings/audioprofile/中使用以上的设置
/**
* Persist the values of All the profiles for their default status
*/
void initProfiles() {
…
gtoneUri(mContext, _RINGTONE,
parsePath(ing(ntifier("ro_def_ringer_stream_uri_" + profileName, "string", packageName)),
SOUND_TYPE_RINGTONES));
}
…
gin_wimdata_ng/wprocedures下的isdm_sys_
例如isdm_sys_中以下定义定义了Alarm的默认铃声
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 17 of 33
开关机铃声比较特殊;
开关机是否有铃声是由 _switch_on_off控制〃如果这个值为0〃没有铃声;设为1有铃声;此默认值在perso中的isdm_sys_进行设置
开关机默认铃声由 _on和_off控制〃这两个值在perso中的isdm_sys_进行设置
2.4 拨号界面专属拨号按键音设置
同一行中〃使用逐渐升调的按键音。即:
a)1、2、3保持升调
b)4、5、6保持升调
c)7、8、9保持升调
d)×、0、#保持升调
同一列中〃保持同一音调的按键音。即:
a)1、4、7、×保持同一音调
b)2、5、8、0保持同一音调
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 18 of 33
c)3、6、9、#保持同一音调
位置:,
private static final HashMap
static {
("1", new Integer(_DTMF_1));
("2", new Integer(_DTMF_2));
("3", new Integer(_DTMF_3));
("4", new Integer(_DTMF_4));
("5", new Integer(_DTMF_5));
("6", new Integer(_DTMF_6));
("7", new Integer(_DTMF_7));
("8", new Integer(_DTMF_8));
("9", new Integer(_DTMF_9));
("0", new Integer(_DTMF_0));
("*", new Integer(_DTMF_S));
("#", new Integer(_DTMF_P));
}
protected void setFormattedDigits(String data) {
ssage(Message(PLAY_TONE_FISRTENTER, data));
。。。。。
char c = ch(TERS);
if (c != 0 && mDigits != null && !sed()) {
playTone4Key(f(c));
}
。。。。。
}
private Handler mHandler = new Handler() {
public void handleMessage(Message msg){
switch () {
case New_TONE_GENERATOR :
newToneGenerator();
break;
case PLAY_TONE_FISRTENTER :
String data = (String);
playTone4Key(data);
break;
。。。
}
}
private void playTone4Key(String key) {
Integer tone = (key);
if (tone != null) {
playTone(tone);
}
}
public boolean onKey(View view, int keyCode, KeyEvent event) {
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 19 of 33
char c = ch(TERS);
if (_DOWN == ion() && 0 == eatCount()) {
playTone4Key(f(c));
}
//onKey is only used to play key tones, so return false
// in order to let other listeners consume the event.
return false;
}
2.5 添加音频效果的设置项
(1)首先在设置界面添加这两个选项
代码 位置:
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 20 of 33
packages/apps/Settings/src/com/android/settings/audioprofile/
private SwitchOnRingtonePreference mPowerOn;
private SwitchOffRingtonePreference mPowerOff;
mPowerOn = (SwitchOnRingtonePreference) findPreference(KEY_SWITCH_ON_RINGTONE);
if(mPowerOn != null){
file(key);
if(silentMode){
Preference(mPowerOn);
}
}
mPowerOff = (SwitchOffRingtonePreference) findPreference(KEY_SWITCH_OFF_RINGTONE);
if(mPowerOff != null){
file(key);
if(silentMode){
Preference(mPowerOff);
}
}
(2)默认值的设置
代码 位置:
packages/apps/Settings/src/com/android/settings/audioprofile
/**
* Persist the values of All the profiles for their default status
*/
void initProfiles()
(3)SwitchOffRingtonePreference和继承于RingtonePreference
代码 位置:
packages/apps/Settings/src/com/android/settings/
RingtonePreference中
mRingtoneType = (nePreference_ringtoneType,
_RINGTONE);
mShowDefault = lean(nePreference_showDefault,
true);
mShowSilent = lean(nePreference_showSilent,
true);
分别表示Ringtone类型,是否设置为默认,是否选择静音。
当在设置界面选中此设置项时,会调用
_RINGTONE_PICKER,弹出SwitchOff音效设置对话框,供用户选择
(4)在mediatek/source/frameworks/media/audio/java/com/mediatek/audioprofile/中,
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 21 of 33
需要添加
/**
* Type that refers to sounds that are used for the power on.
*
* @see #getRingtoneUri(Context, int)
* @see #setRingtoneUri(Context, int, Uri)
*/
public static final int TYPE_POWER_ON = 100;
/**
* Type that refers to sounds that are used for the power off.
*
* @see #getRingtoneUri(Context, int)
* @see #setRingtoneUri(Context, int, Uri)
*/
public static final int TYPE_POWER_OFF = 101;
两种类型
(5)mediatek/source/frameworks/media/audio/java/com/mediatek/audioprofile/中的protected void persistValues(boolean overrideSystem)函数里面添加
tRingtoneUri(TYPE_POWER_ON);
tRingtoneUri(TYPE_POWER_OFF);
public Uri getRingtoneUri(Context context, int type)函数里面添加
} else if (type == _POWER_ON) {
return OnStream;
} else if (type == _POWER_OFF) {
return OffStream;
private void persistRingtoneUri(int type) 函数里面添加
} else if(type == _POWER_ON) {
persistValue(_PERSIST_POWERON_RINGTONE,
OnStream);
} else if(type == _POWER_OFF) {
persistValue(_PERSIST_POWEROFF_RINGTONE,
OffStream);
}
public void setRingtoneUri(Context context, int type, Uri ringtoneUri)函数里面添加
else if(type == _POWER_ON) {
if((OnStream == null && ringtoneUri != null)
|| (OnStream != null
&& !(ringtoneUri))) {
OnStream = ringtoneUri;
tStreamUri(mKey, mResolver, type);
if(isActive()) {
tRingtoneUri(/*context,*/ type);
}
}
} else if(type == _POWER_OFF) {
if((OffStream == null && ringtoneUri != null)
|| (OffStream != null
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 22 of 33
&& !(ringtoneUri))) {
OffStream = ringtoneUri;
tStreamUri(mKey, mResolver, type);
if(isActive()) {
tRingtoneUri(/*context,*/ type);
}
}
}
(6)mediatek/source/frameworks/media/audio/java/com/mediatek/audioprofile/中
protected static final int MSG_PERSIST_POWERON_RINGTONE = 9;
protected static final int MSG_PERSIST_POWEROFF_RINGTONE = 10;
@Override
public void handleMessage(Message msg)函数里面添加
case MSG_PERSIST_POWERON_RINGTONE:
persistPowerRingtone(msg, "_on");
break;
case MSG_PERSIST_POWEROFF_RINGTONE:
persistPowerRingtone(msg, "_off");
break;
此外,添加函数
private void persistPowerRingtone(Message msg, String key) {
if ( == null) {
(key, "silent");
return;
}
Uri ringtoneUri = (Uri);
Cursor c = tentResolver().query(ringtoneUri, new String[] {
._ID, ,
_KEY }, null, null, null);
if(c != null){
if(nt() > 0){
First();
String path = ing(umnIndex("_data"));
(key, path);
}
();
};
}
(7)mediatek/source/frameworks/media/audio/java/com/mediatek/audioprofile/
中
public static String getStreamUriKey(String profileKey, int type) 函数里面添加
} else if (type == _POWER_ON) {
return profileKey + SUFFIX_POWER_ON_URI;
} else if (type == _POWER_OFF) {
return profileKey + SUFFIX_POWER_OFF_URI;
}
public ProfileState(String key, HashMap
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 23 of 33
OnStream = sistedUri(key,
initValues, cr, _POWER_ON, null);
OffStream = sistedUri(key,
initValues, cr, _POWER_OFF, null);
public void persistStreamUri(String key, ContentResolver resolver, int type)函数里面添加
} else if(type == _POWER_ON) {
String value = OnStream == null ? null
: ng();
ing(resolver, name, value);
} else if(type == _POWER_OFF) {
String value = OffStream == null ? null
: ng();
ing(resolver, name, value);
}
(8)mediatek/source/frameworks/media/audio/java/com/mediatek/audioprofile/
public ProfileState(String key, HashMap
OnStream = sistedUri(key,
initValues, cr, _POWER_ON, null);
OffStream = sistedUri(key,
initValues, cr, _POWER_OFF, null);
public void persistStreamUri(String key, ContentResolver resolver, int type)函数里面添加
} else if(type == _POWER_ON) {
String value = OnStream == null ? null
: ng();
ing(resolver, name, value);
} else if(type == _POWER_OFF) {
String value = OffStream == null ? null
: ng();
ing(resolver, name, value);
}
2.6 音量控制
Android为不同的应用场合定义了不同的Audio Stream: Voice Call, Ring,
Music,Alarm, Notification, DTMF。 这些AudioStream是相互独立的〃所以也有各自的音量。AudioStream的定义在anager中
位置:frameworks/base/media/java/android/media/
/** @hide Default volume index values for audio streams */
public static final int[] DEFAULT_STREAM_VOLUME = new int[] {
4, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
4, // STREAM_RING
8, // STREAM_MUSIC
4, // STREAM_ALARM
4, // STREAM_NOTIFICATION
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 24 of 33
7, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
11, // STREAM_DTMF
11, // STREAM_TTS
4, // STREAM_FM
4 // STREAM_MATV
};
以上结构定义了默认音量。
用户按下音量控制的HardKey〃希望能调出音量调整的界面。缺省情况下〃按下音量控制的硬件控制键Vol+/-〃调节的是当前被激活的(Active)AudioStream的音量〃如果你的程序当前没有正在播放任何声音〃按下Vol+/-调节的是来电铃声的音量。
在某一个程序运行时〃希望按下Vol+/-调节的是当前所使用的AudioStream的音量〃Android在Activity中提供了setVolumeControlStream()方法用来指定你的应用程序使用的Audio Stream类型。所以〃如果你的程序用到Audio的播放〃你首先要知道你的程序所用的Audio Stream类型〃并在onCreate()中调用setVolumeControlStream()来设定Audio Stream的类型。
调用详细路径如下:
文件
原型
onCheckedChanged(CompoundButton buttonView,
boolean isChecked)
setStreamVolume(int streamType, int index, int
flags)
setStreamVolume(int streamType, int index, int
flags)
native setStreamVolumeIndex(int stream, int
index);
android_media_oid_media_AudioSystem_setStreamVolumeIndex
p
setStreamVolumeIndex(stream_type stream, int
index)
setStreamVolumeIndex(AudioSystem::stream_type
stream, int index)
setStreamVolumeIndex(AudioSystem::stream_type
stream, int index)
setStreamVolume(AudioSystem::stream_type stream,
float volume, audio_io_handle_t output, int
delayMs = 0);
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 25 of 33
当按音量调节按钮时,会调用RingerVolumePreference::onCheckedChanged〃见文件
packages/apps/Settings/res/layout/sound_and_display_,然后就会调用AudioManager::setStreamVolume,一路跟踪直至文件中的函数 setStreamVolumeIndex〃它会调用checkAndSetVolume〃进而调用
mpClientInterface->setStreamVolume〃这个mpClientInterface应该是由
AudioPolicyService来实现的〃所以调用中的setStreamVolume〃一直往下看 至中的setStreamVolume〃改变的值保存在数组mStreamTypes中。
另外〃当播放音乐时〃MixerThread::threadLoop()是一直在运行的〃当检测到待播放的track时〃就调用函数prepareTracks_l〃这个函数里会计算左右声道的音量〃并且设置到AudioMixer里〃代码如下:
......
float typeVolume = mStreamTypes[track->type()].volume;
float v = mMasterVolume * typeVolume;
float v_clamped = v * cblk->volume[0];
if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
left = int16_t(v_clamped);
AudioCommandThread::volumeCommand(int stream,
float volume, int output, int delayMs)
AudioCommandThread::threadLoop()
setStreamVolume(int stream, float value, int
output)
setStreamVolume(int stream, float value, int
output)
MixerThread::threadLoop()
MixerThread::prepareTracks_l(const SortedVector<
wp
*tracksToRemove)
process(void* output)
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 26 of 33
v_clamped = v * cblk->volume[1];
if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
right = int16_t(v_clamped);
......
mAudioMixer->setParameter(AudioMixer::RAMP_VOLUME,
AudioMixer::VOLUME0, left);
mAudioMixer->setParameter(AudioMixer::RAMP_VOLUME,
AudioMixer::VOLUME1, right);
......
调用函数prepareTracks_l后〃继续调用mAudioMixer->process(curBuf);从而综合处理音频数据.android是利用软件来调节音量〃仅仅设置一个硬件音量的最大值
AudioPolicyManager提供了一下几个与音量相关的函数:
initStreamVolume(AudioSystem::stream_type stream, int indexMin, int indexMax)
setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
getStreamVolumeIndex(AudioSystem::stream_type stream)
中定义了每一种音频流的最大音量级别:
private int[] MAX_STREAM_VOLUME = new int[] {
6, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
7, // STREAM_RING //PR 186088 @ 2011-11-18 because of the merge code is not
proper.
12, // STREAM_MUSIC
7, // STREAM_ALARM
7, // STREAM_NOTIFICATION
15, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
15, // STREAM_DTMF
15, // STREAM_TTS
13, //STREAM_FM
13 //stream_MATV
};
由此可见〃电话铃声可以有7个级别的音量〃而音乐则可以有15个音量级别〃java的代码通过jni〃最后调用 AudioPolicyManager的initStreamVolume()〃把这个数组的内容传入AudioPolicyManager中〃这样 AudioPolicyManager也就记住了每一个音频流的音量级别。应用程序可以调用setStreamVolumeIndex设置各个音频流的音 量级别〃
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 27 of 33
setStreamVolumeIndex会把这个整数的音量级别转化为适合人耳的对数级别〃然后通过AudioPolicyService的 AudioCommandThread〃最终会将设置应用到AudioFlinger的相应的Track中。
2.7 音频系统
文件:frameworks/base/media/java/android/media/
2.7.1 音频类型
/* The audio stream for phone calls */
public static final int STREAM_VOICE_CALL = 0;
/* The audio stream for system sounds */
public static final int STREAM_SYSTEM = 1;
/* The audio stream for the phone ring and message alerts */
public static final int STREAM_RING = 2;
/* The audio stream for music playback */
public static final int STREAM_MUSIC = 3;
/* The audio stream for alarms */
public static final int STREAM_ALARM = 4;
/* The audio stream for notifications */
public static final int STREAM_NOTIFICATION = 5;
/* @hide The audio stream for phone calls when connected on bluetooth */
public static final int STREAM_BLUETOOTH_SCO = 6;
/* @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
public static final int STREAM_SYSTEM_ENFORCED = 7;
/* @hide The audio stream for DTMF tones */
public static final int STREAM_DTMF = 8;
/* @hide The audio stream for text to speech (TTS) */
public static final int STREAM_TTS = 9;
/* @hide The audio stream for Fm */
public static final int STREAM_FM = 10;
/* @hide The audio stream for MATV */
public static final int STREAM_MATV = 11;
2.7.2 输入输出设备
音频系统为音频设备定义了一个枚举
// output devices
public static final int DEVICE_OUT_EARPIECE = 0x1;
public static final int DEVICE_OUT_SPEAKER = 0x2;
public static final int DEVICE_OUT_WIRED_HEADSET = 0x4;
public static final int DEVICE_OUT_WIRED_HEADPHONE = 0x8;
public static final int DEVICE_OUT_BLUETOOTH_SCO = 0x10;
public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20;
public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40;
public static final int DEVICE_OUT_BLUETOOTH_A2DP = 0x80;
public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
public static final int DEVICE_OUT_AUX_DIGITAL = 0x400;
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 28 of 33
public static final int DEVICE_OUT_DEFAULT = 0x8000;
// input devices
public static final int DEVICE_IN_COMMUNICATION = 0x10000;
public static final int DEVICE_IN_AMBIENT = 0x20000;
public static final int DEVICE_IN_BUILTIN_MIC1 = 0x40000;
public static final int DEVICE_IN_BUILTIN_MIC2 = 0x80000;
public static final int DEVICE_IN_MIC_ARRAY = 0x100000;
public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x200000;
public static final int DEVICE_IN_WIRED_HEADSET = 0x400000;
public static final int DEVICE_IN_AUX_DIGITAL = 0x800000;
public static final int DEVICE_IN_DEFAULT = 0x80000000;
每一个枚举值其实对应一个32bit整数的某一个位〃所以这些值是可以进行位或操作的〃例如我希望同时打开扬声器和耳机〃那么可以这样:
newDevice = DEVICE_OUT_SPEAKER|DEVICE_OUT_WIRED_HEADPHONE;setOutputDevice(mHardwareOutput,newDevice);
AudioPolicyManager中有两个成员变量:mAvailableOutputDevices和
mAvailableInputDevices〃他们记录了当前可用的输入和输出设备〃当系统检测到耳机或者蓝牙已连接好时〃会调用 AudioPolicyManager的成员函数:
status_t
AudioPolicyManager::setDeviceConnectionState(AudioSystem::audio_devices
device,AudioSystem::device_connection_state state,const char *device_address)
该函数根据传入的device值和 state(DEVICE_STATE_AVAILABLE/DEVICE_STATE_UNAVAILABLE)设置
mAvailableOutputDevices或者mAvailableInputDevices〃然后选择相应的输入或者输出设备。
其他一些相关的函数:
setForceUse() 设置某种场合强制使用某一设备〃例如setForceUse(FOR_MEDIA,
FORCE_SPEAKER)会在播放音乐时打开扬声器
startOutput()/stopOutput()
startInput()/stopInput()
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 29 of 33
2.7.3 音频策略管理
AudioSystem::stream_type 音频流的类型〃一共有10种类型
AudioSystem::audio_devices 音频输入输出设备〃每一个bit代表一种设备〃见前面的说明
AudioPolicyManager::routing_strategy 音频路由策略〃可以有4种策略
getStrategy(stream_type)根据stream type〃返回对应的routing strategy值〃getDeviceForStrategy()则是根据routing strategy〃返回可用的device。Android把10种stream type归纳为4种路由策略〃然后根据路由策略决定具体的输出设备。
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 30 of 33
2.8 RingerMode
2.8.1利用AudioManager使用RingerMode
RingerMode的获取与设置都是通过AudioManager来进行的。通过
temService(_SERVICE)获得AudioService的本地代理〃然后通过调 用AudioManager的getRingerMode()/setRingerMode()来获取当前的RingerMode或设置 RingerMode。
RingerMode有三种:RINGER_MODE_SILENT,RINGER_MODE_VIBRATE和RINGER_MODE_NORMAL。
RingerMode设置之后〃会有广播RINGER_MODE_CHANGED_ACTION发出〃并且新的RingerMode会被设置在EXTRA_RINGER_MODE里〃广播RINGER_MODE_CHANGED_ACTION的接收者时〃可以通过Extra(_RINGER_MODE)获得更改过的RingerMode。
2.8.2
RingerMode在AudioService中内部实现
AudioManager不过是在本地的一个代理〃具体的实现还是在AudioService中。
RingerMode在AudioService中通过mRingerMode记录;永久存储还是在中〃对应的设置项是MODE_RINGER〃可通过()/putInt()来操作。
AudioService中RingerMode设置的方法(方法setRingerModeInt()的实现)
private void setRingerModeInt(int ringerMode, boolean persist) {
mRingerMode = ringerMode;
// Mute stream if not previously muted by ringer mode and ringer mode
// is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
// Unmute stream if previously muted by ringer mode and ringer mode
// is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
Log.d(TAG, "setRingerModeInt: Ringermode = " + ringerMode);
int numStreamTypes = StreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
if (isStreamMutedByRingerMode(streamType)) {
if (!isStreamAffectedByRingerMode(streamType) ||
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 31 of 33
mRingerMode == _MODE_NORMAL) {
mStreamStates[streamType].mute(null, false);
mRingerModeMutedStreams &= ~(1 << streamType);
}
} else {
if (isStreamAffectedByRingerMode(streamType) &&
mRingerMode != _MODE_NORMAL) {
mStreamStates[streamType].mute(null, true);
mRingerModeMutedStreams |= (1 << streamType);
}
}
}
改变RingerMode是否影响某一个AudioStream的音量(静音)是由
isStreamAffectedByRingerMode(streamType:int) 决定的〃而该方法是检查mRingerModeAffectedStreams上针对该AudioStream的位上是否被置位〃亦即〃mRingerModeAffectedStreams是AudioStream是否受RingerMode影响的Bitmasks。
如果AudioStream被静音〃则在mRingerModeMutedStreams的相应位上置位做标志。所以mRingerModeMutedStreams是当前AudioStream是否因为RingerMode而静音的Bitmasks。
RingerMode记录在mRingerMode〃但永久存储是通过〃所以设置完具体的RingerMode之后〃通过(cr,_RINGER, mRingerMode)保存当前的RingerMode。
2.8.3 配置RingerMode对特定AudioStream的影响
前节中讲到〃某一个AudioStream是否受设置RingerMode影响是由mRingerModeAffectedStreams决定的〃也就是即便设置RingerMode为静音/震动模式(RINGER_MODE_SILENT/RINGER_MODE_VIBRATE)之后〃 某个AudioStream还不一定被设置为静音〃而是否起作用〃关键取决于mRingerModeAffectedStreams上对应于该AudioStream的位上取值。
mRingerModeAffectedStreams在AudioService中初始值是通过(mContentResolver,
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 32 of 33
_RINGER_STREAMS_AFFECTED,
0);获取的
mRingerModeAffectedStreams = (cr,
_RINGER_STREAMS_AFFECTED,
((1 << _RING)|(1 << _NOTIFICATION)|
(1 << _SYSTEM)|(1 << _SYSTEM_ENFORCED)));
可以看到〃默认有四个AudioStream是受RingerMode设置的影响的。
这样〃可以通过设置_RINGER_STREAM_AFFECTED来改变受RingerMode设置影响的AudioStream。
比如〃Alarm的“静音模式下是否闹铃”的设置就是通过对AudioStreamSTREAM_ALARM的置位/去位实现的:
代码位置:packages/apps/DeskClock/src/com/android/deskclock/
int ringerModeStreamTypes = (
getContentResolver(),
_RINGER_STREAMS_AFFECTED, 0);
if (ked()) {
ringerModeStreamTypes &= ~ALARM_STREAM_TYPE_BIT;
} else {
ringerModeStreamTypes |= ALARM_STREAM_TYPE_BIT;
}
(getContentResolver(),
_RINGER_STREAMS_AFFECTED,
ringerModeStreamTypes);
ArcherMind Inc. Proprietary – For Internal Use Only
CONFIDENTIAL
Date
Page 33 of 33


发布评论