下面以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.dbSystem表中

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.dbSystem表中的 字段 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&; 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&; 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

 

 

 

更多相关文章

  1. AndroidManifest.xml的android:name是否带.的区别
  2. Android---Gallery
  3. android 采集PCM音频数据并播放(支持USB摄像头MIC)
  4. Android——自定义自适应高度的GridView
  5. Android延迟执行 handler类的实现
  6. Graphics->Compass
  7. android MediaPlayer 简易播放器的实现 及类似斗地主音频池实现
  8. Android设置界面之Preference
  9. 改变分隔条,基于数组的ListView

随机推荐

  1. sql server 2000管理单元初始化失败的解
  2. 如何恢复SQL Server 2000损坏的数据库文
  3. sqlserver数据库导入数据操作详解(图)
  4. sql字符串函数大全和使用方法示例
  5. sql集合运算符使用方法
  6. sql表连接查询使用方法(sql多表连接查询)
  7. SQL一条语句统计记录总数及各状态数
  8. sqlserver服务器验证改为混合验证模式步
  9. c语言http请求解析表单内容
  10. SqlServer中用exec处理sql字符串中含有变