前言
关于音量调节我们知道有AudioManager的软件调节和CarAudioManager的硬件调节,今天先聊聊AudioManager的软件音量调节。
正文
关于AudioManager中音量调节的API主要有如下两个:
- adjustVolume
- setStreamVolume
adjustVolume偏向一些按键的音量调节,比如手机上音量+-的硬按键控制的音量调节。
setStreamVolume更像是settings中的音量bar进行的音量调节。
说这俩函数前先看下volume几个相关的数组。
- Max Volume
所有streamType对应的最大音量
/** Maximum volume index values for audio streams */
protected static int[] MAX_STREAM_VOLUME = new int[] {
5, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
7, // STREAM_RING
15, // STREAM_MUSIC
7, // STREAM_ALARM
7, // STREAM_NOTIFICATION
15, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
15, // STREAM_DTMF
15, // STREAM_TTS
15, // STREAM_ACCESSIBILITY
15 // STREAM_ASSISTANT
};
- Min Volume
所有streamType对应的最小音量
/** Minimum volume index values for audio streams */
protected static int[] MIN_STREAM_VOLUME = new int[] {
1, // STREAM_VOICE_CALL
0, // STREAM_SYSTEM
0, // STREAM_RING
0, // STREAM_MUSIC
1, // STREAM_ALARM
0, // STREAM_NOTIFICATION
0, // STREAM_BLUETOOTH_SCO
0, // STREAM_SYSTEM_ENFORCED
0, // STREAM_DTMF
0, // STREAM_TTS
1, // STREAM_ACCESSIBILITY
0 // STREAM_ASSISTANT
};
- Default Volume
注意默认音量定义在AudioSystem中
public static int[] DEFAULT_STREAM_VOLUME = new int[] {
4, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
5, // STREAM_RING
5, // STREAM_MUSIC
6, // STREAM_ALARM
5, // STREAM_NOTIFICATION
7, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
5, // STREAM_DTMF
5, // STREAM_TTS
5, // STREAM_ACCESSIBILITY
5, // STREAM_ASSISTANT
};
我们知道了每个streamType对应的最大、最小以及默认的音量,还有个很重的数组
- Volume Alias
private final int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_RING, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
AudioSystem.STREAM_ALARM, // STREAM_ALARM
AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
AudioSystem.STREAM_RING, // STREAM_DTMF
AudioSystem.STREAM_MUSIC, // STREAM_TTS
AudioSystem.STREAM_MUSIC, // STREAM_ACCESSIBILITY
AudioSystem.STREAM_MUSIC // STREAM_ASSISTANT
};
ALIAS别名,这个主要是将streamType进行了分组,相同别名的音量一组。如streamType是TTS和Music的就是一组,同一组的音源音量一起调节,我们调节了STREAM_MUSIC的音量,那么STREAM_TTS的音量也会同步变更。
接下来就看下具体音量调节的流程吧。
adjustVolume
调节当前音源的音量,AudioService中实现adjustStreamVolume的逻辑比较复杂,因为随着Android版本的不断迭代,功能的不断增多。因此逻辑也是越来越复杂,比如震动下的铃声调节、勿扰模式下的铃声调节,以及特殊StreamType的特殊处理,这里只说下大体的流程吧。
首先是step的计算,即每次调节音量的步长。
step = rescaleStep(10, streamType, streamTypeAlias);
看下rescaleStep这个函数:
private int rescaleStep(int step, int srcStream, int dstStream) {
int srcRange = getIndexRange(srcStream);
int dstRange = getIndexRange(dstStream);
if (srcRange == 0) {
Log.e(TAG, "rescaleStep : index range should not be zero");
return 0;
}
return ((step * dstRange + srcRange / 2) / srcRange);
}
srcStream表示调节的streamType,dstStream表示当前音量组的streamType,举个例子如果当前调节的srcStream是STREAM_TTS那么通过STREAM_VOLUME_ALIAS_VOICE 我们可以知道dstStream是STREAM_MUSIC。
private int getIndexRange(int streamType) {
return (mStreamStates[streamType].getMaxIndex() - mStreamStates[streamType].getMinIndex());
}
如果srcStream音量最大值减去最小值不是0,那么就说明这个streamType的音量是可以调节的,那么它的步长就是((step * dstRange + srcRange / 2) / srcRange)根据上面的数组就很容易算出step来了。
拿到step,在根据direction然后调用VolumeStreamState的adjustIndex,而adjustIndex又会调用setIndex
public boolean adjustIndex(int deltaIndex, int device, String caller,
boolean hasModifyAudioSettings) {
return setIndex(getIndex(device) + deltaIndex, device, caller,
hasModifyAudioSettings);
}
setIndex中主要做音量处理,以及发送音量变化的广播。这里多说一句音量变更后存储在mIndexMap中。
mIndexMap.put(device, index)
可以看到音量的存储并不是根据streamType来的,而是根据device存储的,这就解释了我们在播放音乐的时候外放和插入耳机播放时音量不一致的原因了。
因为一个streamType可能对应多个Device即,一个streamType可能存在多个音量。
音量更新完后便是给子线程发 MSG_SET_DEVICE_VOLUME消息来调节音量。 子线程收到音量调节消息后调用VolumeStreamState的setDeviceVolume
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
synchronized (VolumeStreamState.class) {
// Apply volume
streamState.applyDeviceVolume_syncVSS(device);
// Apply change to all streams using this one as alias
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
if (streamType != streamState.mStreamType &&
mStreamVolumeAlias[streamType] == streamState.mStreamType) {
// Make sure volume is also maxed out on A2DP device for aliased stream
// that may have a different device selected
int streamDevice = getDeviceForStream(streamType);
if ((device != streamDevice) && mAvrcpAbsVolSupported
&& AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)) {
mStreamStates[streamType].applyDeviceVolume_syncVSS(device);
}
mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
}
}
}
// Post a persist volume msg
sendMsg(mAudioHandler,
MSG_PERSIST_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
PERSIST_DELAY);
}
这里主要做了三步,先看第一步即设置音量applyDeviceVolume_syncVSS将音量通过AudioSystem设置到native的AudioPolicyManager中;第二步是 mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice)即设置相同组别的其他streamType的音量;最后一步发送MSG_PERSIST_VOLUME消息。将音量保存起来。
这样adjustVolume的流程就走完了。
setStreamVolume
设置指定streamType的音量。和adjustVolume的流程类似,但是相对adjustVolume而言setStreamVolume直接在AudioManager中调用到AudioService中的setStreamVolume。然后一些特殊的判断后会调用到onSetStreamVolume
private void onSetStreamVolume(int streamType, int index, int flags, int device,
String caller, boolean hasModifyAudioSettings) {
final int stream = mStreamVolumeAlias[streamType];
setStreamVolumeInt(stream, index, device, false, caller, hasModifyAudioSettings);
// setting volume on ui sounds stream type also controls silent mode
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
(stream == getUiSoundsStreamType())) {
setRingerMode(getNewRingerMode(stream, index, flags),
TAG + ".onSetStreamVolume", false /*external*/);
}
// setting non-zero volume for a muted stream unmutes the stream and vice versa,
// except for BT SCO stream where only explicit mute is allowed to comply to BT requirements
if (streamType != AudioSystem.STREAM_BLUETOOTH_SCO) {
mStreamStates[stream].mute(index == 0);
}
}
然后是setStreamVolumeInt,然后streamState.setIndex到这里就和adjustVolume的逻辑相同了。
总结
AudioManager的音量调节无论是adjustVolume还是setStreamVolume其实核心逻辑都是一样的,
- 更新VolumeStreamState中index,并存入mIndexMap中(主要为了getStreamVolume使用),将index通过AudioSystem设置下去
- 更新同组别的其他streamType的音量,音量组别可以通过int[] STREAM_VOLUME_ALIAS_VOICE查看
- 音量存储到数据库中
以上,欢迎大家一起沟通讨论喜欢就点下关注吧