前言
音量最终下派到native是通过
public static native int setStreamVolumeIndex(int stream, int index, int device);
所以设置音量关键找到 (stream, index, device) 的一个组合
音量值保存在
private final ConcurrentHashMap<Integer, Integer> mIndex =
new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
那么在下派参数前,如何在VolumeStreamState中修改和获得某个device的音量呢?
VolumeStreamState.setIndex
参数便是index, device
public boolean setIndex(int index, int device) {
synchronized (VolumeStreamState.class) {
int oldIndex = getIndex(device);
index = getValidIndex(index);
synchronized (mCameraSoundForced) {
if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
index = mIndexMax;
}
}
//存入device -> index
mIndex.put(device, index);
if (oldIndex != index) {
下面的注释讲了两点:
// Apply change to all streams using this one as alias
// if changing volume of current device, also change volume of current
// device on aliased stream
boolean currentDevice = (device == getDeviceForStream(mStreamType));
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
if (streamType != mStreamType &&
mStreamVolumeAlias[streamType] == mStreamType) {
int scaledIndex = rescaleIndex(index, mStreamType, streamType);
mStreamStates[streamType].setIndex(scaledIndex,
device);
if (currentDevice) {
mStreamStates[streamType].setIndex(scaledIndex,
getDeviceForStream(streamType));
}
}
}
return true;
} else {
return false;
}
}
}
目前对上面注释的第二点还理解不到
相关函数
getDeviceForStream
private int getDeviceForStream(int stream) {
//先从native取device for stream
int device = AudioSystem.getDevicesForStream(stream);
if ((device & (device - 1)) != 0) {
<span style="font-family: Arial, Helvetica, sans-serif;">//如果native返回多个设备,上层只作为一个设备处理</span>
<pre name="code" class="java"><pre name="code" class="java">//选择的原则根据priority
//这里定义了三种情况:
//1.speaker + one other devie
//2.one A2DP device + another device
//3.HDMI-CEC system audio mode // Multiple device selection is either: // - speaker + one other device: give priority to speaker in this case. // - one A2DP device + another device: happens with duplicated output. In this case // retain the device on the A2DP output as the other must not correspond to an active // selection if not the speaker. // - HDMI-CEC system audio mode only output: give priority to available item in order. if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) { device = AudioSystem.DEVICE_OUT_SPEAKER; } else if ((device & AudioSystem.DEVICE_OUT_HDMI_ARC) != 0) { device = AudioSystem.DEVICE_OUT_HDMI_ARC; } else if ((device & AudioSystem.DEVICE_OUT_SPDIF) != 0) { device = AudioSystem.DEVICE_OUT_SPDIF; } else if ((device & AudioSystem.DEVICE_OUT_AUX_LINE) != 0) { device = AudioSystem.DEVICE_OUT_AUX_LINE; } else { device &= AudioSystem.DEVICE_OUT_ALL_A2DP; //最后是与运算 } } return device; }
VolumeStreamState.adjustIndex
还是调用setIndex(),殊途同归
public boolean adjustIndex(int deltaIndex, int device) {
return setIndex(getIndex(device) + deltaIndex,
device);
}
VolumeStreamState.getIndex
public int getIndex(int device) { synchronized (VolumeStreamState.class) { Integer index = mIndex.get(device); if (index == null) { // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT); } return index.intValue(); } }
VolumeStreamState.setAllIndexes
<pre style="border: 0px; margin-top: 0px; margin-bottom: 0px; background-color: rgb(255, 255, 255);"><pre name="code" class="java"> public void setAllIndexes(VolumeStreamState srcStream) {
synchronized (VolumeStreamState.class) {
int srcStreamType = srcStream.getStreamType();
<pre name="code" class="java">有些device 在当前的stream state但是不在src stream state
// apply default device volume from source stream to all devices first in case // some devices are present in this stream state but not in source stream state int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT); //取得 srcStream default device index index = rescaleIndex(index, srcStreamType, mStreamType); Set set = mIndex.entrySet(); Iterator i = set.iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry)i.next(); entry.setValue(index); } // Now apply actual volume for devices in source stream state set = srcStream.mIndex.entrySet(); //用 src mIndex 的值处理 i = set.iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry)i.next(); int device = ((Integer)entry.getKey()).intValue(); index = ((Integer)entry.getValue()).intValue(); index = rescaleIndex(index, srcStreamType, mStreamType); setIndex(index, device); } } }
setStreamVolumeInt
调用setIndex, 然后发送 MSG_SET_DEVICE_VOLUME 广播
private void setStreamVolumeInt(int streamType,
int index,
int device,
boolean force) {
VolumeStreamState streamState = mStreamStates[streamType];
if (streamState.setIndex(index, device) || force) {
// Post message to set system volume (it in turn will post a message
// to persist).
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
}
public void handleMessage(Message msg) { switch (msg.what) { case MSG_SET_DEVICE_VOLUME: setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1); break;
private void setDeviceVolume(VolumeStreamState streamState, int device) {
synchronized (VolumeStreamState.class) {
// Apply volume
//调回VolumeStreamState, 只传入device即可
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 &&
((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0)) {
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);
}
public void applyDeviceVolume_syncVSS(int device) {
int index;
if (isMuted_syncVSS()) {
index = 0;
} else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported)
|| ((device & mFullVolumeDevices) != 0)) {
index = (mIndexMax + 5)/10;
} else {
index = (getIndex(device) + 5)/10;
}
AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
}