下面以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<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

Android音量系统分析相关推荐

  1. Android音量控制曲线

    摘要:本文介绍了android音量的控制曲线的计算方法. 由于人耳对声音的听感具指数曲线型,也就是对小音量时比较敏感,随着声音的加大其听感随之变的不敏感,其变化近似指数函数曲线的形式.为了使听感变的近 ...

  2. Android编译系统分析四:实战-新增一个产品

    通过上一节"android编译系统(三)-make"的分析,初步理清楚了编译初期加载产品相关信息的流程,整个过程主要涉及三个文件:1.AndroidProducts.mk,2.具体 ...

  3. android音量键调节听筒音量的大小

    android音量键调节听筒音量的大小 最近发现微信的语音功能可以在听筒和喇叭间互相切换并且可以使用音量按键进行调节,之前在项目开发中只用到音频的多媒体类型播放音频文件但没用到听筒,所以就写了个dem ...

  4. Android 编译系统分析之lunch分析

    已开通新的博客,后续文字都会发到新博客 http://www.0xfree.top Android 编译系统解析系列文档 编译系统入口envsetup.sh解析 source build/envset ...

  5. Android音量调节的实现(RingtoneManager和RingerVolumePreference)

    Android音量调节的实现,分两种情况说明,一种是在settings的音量设置界面,按下侧键进行调节:另一种是在非settings界面,按下侧键进行音量调节.这两种调节方式的实现不一样,分别操作的是 ...

  6. Android音量调节(一)音量键的处理流程

    文章目录 1.Android音量键调节音量流程 2.Android流定义 3.按键的处理流程 3.1 adjustSuggestedStreamVolume 3.2 adjustStreamVolum ...

  7. Android音量控制

    0. Thanks To Android音量控制调节 android 音量控制setVolumeControlStream android 音量调节以及媒体音量界面 1. 音量调节 我们知道,在平常调 ...

  8. android 拦截音量键,Android 音量键增大减小捕捉

    Android 音量键增大减小捕捉: @Override public boolean onKeyDown( int keyCode, KeyEvent event) { // TODO Auto-g ...

  9. Android编译系统分析五:system.img的生成过程

    Android编译系统分析系列文章: android编译系统分析(一)-source build/envsetup.sh与lunch Android编译系统(二)-mm编译单个模块 android编译 ...

  10. Android View系统分析之二View与ViewGroup

    目录 在Android View系统分析之从setContentView说开来(一)一文中,我们从setContentView开始阐述了Android中的视图层次,从设置内容布局到整个视图层次的建立的 ...

最新文章

  1. docker mysql sock_docker mysql安装
  2. P1332,nssl1316-血色先锋军【bfs】
  3. css3中的background
  4. OPPO大数据平台运营研发实践分享
  5. UE4中Component和Subobject的区别
  6. wince6.0添加组件_WINCE6.0添加对viewrs(PDF,Word,Execel和PowerPoint)支持
  7. 【Java后台开发规范】--- 日志的输出
  8. 计算机在材料科学中的应用论文,计算机在材料科学中的应用论文(2)
  9. 【概率密度函数】简介概率论中的概率密度函数
  10. macOS Monterey 12.0.1 (21A559) 正式版发布,ISO、IPSW、PKG 下载
  11. 凸优化(Convex Optimization)是什么?
  12. 仿淘宝 阿里云登录 密码和扫码切换登录
  13. vue树形权限菜单_vue实现树形菜单步骤说明
  14. labview文件写入与读取
  15. EasyRecovery免费激活软件秘钥下载恢复教程及注意事项
  16. android 加速度计,Android加速度计校准?
  17. html div挤下去了,网站固定导航条挤动下面的DIV_html/css_WEB-ITnose
  18. vimeo 镜像_Vimeo的首席应用程序开发人员Kevin Sheurs访谈
  19. [Luogu1365] WJMZBMR打osu! / Easy
  20. 植物根系水力再分配测定与模拟

热门文章

  1. 神书《笛卡儿几何》读后感
  2. FPGA实现AXI4总线的读写
  3. Tableau详细安装教程【踩坑】
  4. 常见的平面设计风格有哪些?
  5. 熬夜爆肝!C++实现圣域之战!(修过码)
  6. 如何修改input提示文字样式
  7. 数字证书:签名证书加密证书
  8. vnc远程控制软件7款,7款非常好用的vnc远程控制软件
  9. java怎么画八卦图_八卦图怎么画 最正确的八卦图
  10. win10电脑插入U盘提示文件或目录损坏了如何解决