下面以Android 6.0为例来说明。
一、音频流、音频设备、音量三角关系
AudioSystem.java中定义的音频流格式:
int STREAM_VOICE_CALL = 0; 电话
int STREAM_SYSTEM = 1; 系统
int STREAM_RING = 2; 响铃和消息
int STREAM_MUSIC = 3; 音乐
int STREAM_ALARM = 4; 闹钟
int STREAM_NOTIFICATION = 5; 通知
int STREAM_BLUETOOTH_SCO = 6; 蓝牙
int STREAM_SYSTEM_ENFORCED = 7; 强制系统声音
int STREAM_DTMF = 8; 双音多频
int STREAM_TTS = 9; 语音
总共10种音频流,因Android版本不同可能存在差异。
音量与音频流是息息相关的。每种音频流至少对应一种音量,当然也可以多种音频流对应一种音量。
在AudioService.java中定义了这种对应关系,
private final int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {
// STREAM_VOICE_CALL AudioSystem.STREAM_VOICE_CALL,
// STREAM_SYSTEM AudioSystem.STREAM_RING,
// STREAM_RING AudioSystem.STREAM_RING,
// STREAM_MUSIC AudioSystem.STREAM_MUSIC,
// STREAM_ALARM AudioSystem.STREAM_ALARM,
// STREAM_NOTIFICATION AudioSystem.STREAM_RING,
// STREAM_BLUETOOTH_SCO AudioSystem.STREAM_BLUETOOTH_SCO,
// STREAM_SYSTEM_ENFORCED AudioSystem.STREAM_RING,
// STREAM_DTMF AudioSystem.STREAM_RING,
// STREAM_TTS AudioSystem.STREAM_MUSIC };
从上面定义可以看到系统音频流,响铃与消息音频流,通知音频流,强制声音音频流,DTMF这五种音频流共用一个音量,音乐与语音是共用一个音量( AudioSystem.STREAM_MUSIC)
上面这个定义是用于通话的Android平台上的(比如手机),Android还定义了两种,分别用在电视或者机顶盒上的定义:。。。
不同设备调整音量后互不影响
我们知道在使用手机扬声器播放音乐时调整音量后,如果插入耳机,从耳机听到的音量并没有变化。在Android系统中,定义了一系统输入和输出设备,针对每个输入与输出设备的音量也是不一样的。下面是Android系统在audio.h定义的部份音频设备。
输出设备:
AUDIO_DEVICE_OUT_EARPIECE = 0x1,// 听筒AUDIO_DEVICE_OUT_SPEAKER = 0x2,// 扬声器AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4,//线控耳机AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8,//普通耳机AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10,//单声道蓝牙耳机AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,//蓝牙电话 AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40, //车载免提蓝牙设备AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80, //立体声蓝牙耳机
输入设备,比如:
AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4, //手机自带MICAUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN| 0x40,//电话MIC
可以说每个音频流对应到每种设备都有一个音量。比如,对于同一个STREAM_MUSIC流,对扬声器和耳机的音量是分开存储的。不考虑相同的情况,音量个数=音频流*音频设备。
二、音量的缓存与持久化
音量的缓存是通过AudioService.java的内部类VolumeStreamState来设置。
音量的持久化在Android 6.0以前是保存到设置数据库setting.db的System表中
android6.0之后在data/data/com.android.providers.settings/databases目录下找不到settings.db数据库原因:
在6.0上面,Google修改了SettingsProvider,这次修改,涉及到了
global,secure,system 三个表;并且实现方式从之前的数据库,改为异步性能更加优
良的xml,每个用户都有自己的一份SettingsProvider设置xml文档。通常位于
/data/system/users/userid/ 下面。
结论:
AudioManager mAudioManager = (AudioManager) FRApplication.getInstance().currentActivity().getSystemService(FRApplication.getInstance().currentActivity().AUDIO_SERVICE);
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, voice, 0);
以上代码执行会影响 setting.db的System表中的 字段 volume_music_speaker ,测试设备android5.1
三、音量的设置流程
设置音量通常有以下方法:
通过AudioManager来设置
通过AudioTrack/MediaPlayer来设置
1.通过AudioManager来设置
我们先看一下AudioManager音量的设置过程
图3.1AudioManager音量设置流程
AudioManager只是一个轻量级的封装类,由Context创建,工作在APK进程中,通过IBinder的机制,负责与JAVA层的音频服务AudioService进行交互。
AudioManager类提供了setStreamVolume方法来对一种stream type对应的音量进行设置:
public void setStreamVolume(int streamType, int index, int flags) {
IAudioService service = getService();
try {
service.setStreamVolume(streamType, index, flags, getContext().getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setStreamVolume", e);
}
}
从代码中可以看出,setStreamVolume就是通过IPC调用AudioService的方法,用一个类图来表示AudioManager和AudioService的关系:
图3.2 AudioManager &; AudioService
AudioManager通过代理对象访问工作在SystemServer中的AudioService服务,调用其setStreamVolume方法来设置音量。
上面说过AudioService通过VolumeStreamState来缓存各种音频流的音量,并且通过mStreamStates来记录各种音频流的音量。设置音量最终是通过 VolumeStreamState. applyDeviceVolume_syncVSS函数调用AudioSystem.setStreamVolumeIndex函数来传入device类型、音量index以及stream类型,告知音频系统,“使用这种device播放这种stream类型的音频播放操作,都将使用这个音量index”。代码如下:
status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
const sp<IAudioPolicyService>&; aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->setStreamVolumeIndex(stream, index, device);
}
AudioSystem主要对AudioPolicyService进行了封装,所以接下来的操作都是由AudioPolicyService来完成的。
setStreamVolumeIndex是AudioSystem通过IBinder调用了AudioPolicyService的setStreamVolumeIndex函数,AudioPolicyService继承了AudioPolicyClientInterface类,他有一个AudioPolicyInterface类的成员指针mpPolicyManager,实际上就是指向了AudioPolicyManager,最终是调用了AudioPolicyManager的setStreamVolumeIndex函数。(实际上AudioPolicyService是通过成员指针mpPolicyManager访问AudioPolicyManager,而AudioPolicyManager则通过AudioPolicyClientInterface(mpClientInterface)访问AudioPolicyService)。
AudioPolicyManager调用setStreamVolumeIndex后会引发AudioPolicyService执行一个SET_VOLUME的CommandThread,在这个CommandThread中调用了AudioSystem的静态方法setStreamVolume,具体如下:
status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output)
{
if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp<IAudioFlinger>&; af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
af->setStreamVolume(stream, value, output);
return NO_ERROR;
}
在这个函数里调用了AudioFlinger的setStreamVolume。在AudioFlinger的setStreamVolume中调用了PlaybackThread的setStreamVolume.
AudioFlinger通过checkPlaybachTread方法,通过AudioPolicy传入IO句柄(audio_io_handle_t),来定位到具体的PlaybackThread,调用其setStreamVolume方法,这个方法将音量值缓存到stream对应的stream_type_t对象中,这样,PlaybackThread便知道每种stream对应的音量了。具体如下:
status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output)
{
......
if (thread == NULL) {
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
}
} else {
thread->setStreamVolume(stream, value);
}
return NO_ERROR;
}
在PlaybackThread的setStreamVolume中只是保存当前音量值,然后发送通知在输出音频时按新的音量计算。
2. 通过AudioTrack/MediaPlayer来设置
Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例。每个AudioTrack在创建时会注册到AudioFlinger中,AudioFlinger在AudioPolicy的辅助下,为每个AudioTrack对象建立与某个具体的工作线程的对应关系,并通知这个工作线程创建了一个Track对象与这个AudioTrack进行对应。由AudioFlinger把所有的AudioTrack进行混合(Mixer),然后输送到AudioHardware中进行播放。最多可以创建32个音频流。
AudioMixer进行混音的时候,需要知道每个Track播放音频的音量,这个音量是由stream音量、master音量和track音量相乘出来的,stream音量就是AudioPolicy设置进来的,master volume由用户设置,track volume由调用者通过AudioTrack.setVolume来设置。AudioTrack.setVolume所设置的track volume,是一个取值为0~1.0的浮点数
通常,AudioTrack和AudioFlinger并不在同一个进程中,它们通过android中的binder机制建立联系。
AudioTrack通过setVolume设置音量后,会记录入共享内存中,然后由AudioFlinger去读取。
四、小结
整个Android音量设置还是比较复杂,其中包括持久化及各个模块的缓存及同步更新。所涉及到的音频系统子模块包括AudioService、AudioPolicy和AudioFlinger,每个子模块都用各自的数据结构缓存了stream音量,持久化在设置数据库的system表中或者XML中。
参考网址:http://www.vccoo.com/v/b5ghyx_4