前言
文章就Android平台主机接不同的外放设备时的音量调节策略做简要介绍。
一、主机版本
主机 | 版本 |
---|---|
RK3399广告屏 | Android7 |
Skyworth平板 | Android9 |
Skyworth车机 | Android9 |
二、外设类型
- USB音箱底座
- 蓝牙音箱
- 3.5mm线控耳机
三、相关术语
- track volume: 单个App设置音量时设置的是这个,它只影响本App的音量。
- stream volume: 设置某一stream的音量,Android系统中支持的stream,可以在audio.h中找到。
- stream volume alias: 设置的是同一组stream的音量,比如使用某个音量调节滑动条设置的音量。比如设置媒体音,所有App的媒体音都受到影响(但是电话音,闹钟音不受影响)。
- master volume: 设置它等于设置所有的stream volume和track volume。它可以写到声卡里面去,控制所有声音的音量。也可以不写到声卡里面去,而是作为一个乘数因子来影响所有的音量。
四、音量调节流程
Android平台的设备,常见的手机、平板、车机、广告屏,调节音量基本有两种常用方式:音量键和滑动条。下面分别介绍写这两种方式的大致流程。
1.音量键
- 音量键的动作由 PhoneFallbackEventHandler 处理,它会调用AudioService.adjustSuggestedStreamVolume 调整“推荐的流”的音量。
- 获取“推荐的流”:stream = getActiveStreamType(…),就是获取当前活动的流。比如当前在通话,按音量键时,调节的就是话音的音量。
- 设置流的音量:adjustStreamVolume调用 sendMsg 发送 MSG_SET_DEVICE_VOLUME 消息,由handleMessage 来进行处理。
- 将设置后的StreamVolume保存到SettingsProvider中。
2.滑动条(SeekBar)
-
每一个SeekBar要绑定一个 SeekBarChangeListener 用来监听变化。
-
当SeekBar被滑动时,它的onProgressRefresh被调用,该函数会调用mOnSeekBarChangeListener.onProgressChanged 去调节设备音量。
-
每一个SeekBar对应一个stream,滑动SeekBar时会设置该stream的音量,也会去设置同属一个alias(同一分组)的其他stream的音量,伪代码如下:
if(mStreamVolumeAlias[other stream] == stream) setvolume forother stream;
-
设置StreamVolume:adjustStreamVolume调用sendMsg 发送 MSG_SET_DEVICE_VOLUME
消息,由 handleMessage 来进行处理。 -
将设置后的StreamVolume保存到SettingsProvider中。
能够看出按键调节和滑动音量条调节,最后的处理是一样的。最后都会保存到SettingsProvider中,可以用 adb shell settings get system volume_music_speaker 查看stream volume的值。
五、外设音量调节
当Android主机连接外放设备时,主要关心的是两者的音量同步问题。下面分别介绍几种常用的外接播放设备。
1.USB音箱、USB底座
此类设备,主机端的音量和设备端音量相互独立。
一般来说设备侧调节的是自身的功放参数,比如增益;主机端调节的是自身的输出音量,不会影响设备端音量。
2.蓝牙音箱、蓝牙耳机
- 主机端连接蓝牙设备,涉及到一个“蓝牙绝对音量”的开关。该开关决定了主机端调节音量时是否同步调节设备端。
- 当主机端开启了绝对音量:主机端音量调节会同步调节设备端;
- 当主机端未开启绝对音量:主机端音量调节不会同步调节设备端,设备端独立调节;
- 不管是否开启绝对音量,蓝牙设备端都不会反向同步音量变化给主机端。
3.3.5mm线控耳机
- 对于普通的线控耳机,调节音量的原理是:主机端会监测耳机按下音量+/-键时的阻抗变化,触发相应的音量调节动作。
- 实测发现,并不是所有的Android机型都会监测耳机的音量按键,比如RK3399的广告屏,插上3.5mm线控耳机,按耳机的音量键, 并不会触发音量调节;拿一部vivo手机测试,耳机可以控制手机音量。
- 总之要看主机端是否有监控耳机动作的处理操作。
六、“静音”
这里的“静音”不同于通过按音量键或者拉动音量条逐级的调节为静音状态,而是指设备的“MUTE”状态。对应的操作为:
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_MUTE, 1);
关于“静音”,分析下常见的一个问题:当主机端“静音”后,连接其它外放设备,再断开连接,主机端会自动变为“非静音”状态。 前面说了音量调节的一般流程,简化来说就两步:
第一步:设置StreamVolume;
第二步:保存到SettingsProvider;
当设置“静音”时,设备只执行第一步,不会执行第二步;
当连接外放设备时,系统监测到外放设备connected,会触发一次“UNMUTE”动作,同样是只执行第一步,不执行第二步。因为静音时未保存SettingsProvider,所以“UNMUTE”后的StreamVolume还是静音前的值。
七、常用命令
-
查看系统有哪些volume,以"volume_"开头的是音量相关的
adb shel settings list system
-
查看某一个volume的值,以volume_music_speaker为例
adb shell settings get system volume_music_speaker
-
使用media命令设置音量,会保存到SettingsProvider中
adb shell media volume --stream 3 --get // 获取音量 adb shell media volume --stream 3 --adj lower // 降低音量 adb shell media volume --stream 3 --set 0 // 设置音量为0
-
使用keyevent设置音量,不会保存到SettingsProvider中
adb shell input keyevent 164 // mute,不同平台键值可能有差异。