1、Android音量的保存
存放到数据库
AudioHandler.persistVolume
private void persistVolume(VolumeStreamState streamState, int device) {
...
System.putIntForUser(mContentResolver,
streamState.getSettingNameForDevice(device),
(streamState.getIndex(device) + 5)/ 10,
UserHandle.USER_CURRENT);
...
}
VolumeStreamState .getSettingNameForDevice
public @Nullable String getSettingNameForDevice(int device) {
...
final String suffix = AudioSystem.getOutputDeviceName(device);
...
return mVolumeIndexSettingName + "_" + suffix;
}
mVolumeIndexSettingName 为初始化多个stream type的实例时给定,每个stream type的name不一样(根据System.VOLUME_SETTINGS_INT数组中返回)
AudioService.createStreamStates
private void createStreamStates() {
int numStreamTypes = AudioSystem.getNumStreamTypes();
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
for (int i = 0; i < numStreamTypes; i++) {
streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS_INT[mStreamVolumeAlias[i]], i);
}
}
switch(getDeviceForVolume(deviceTypes)) {
case AUDIO_DEVICE_OUT_EARPIECE:
return DEVICE_CATEGORY_EARPIECE;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
case AUDIO_DEVICE_OUT_USB_HEADSET:
return DEVICE_CATEGORY_HEADSET;
case AUDIO_DEVICE_OUT_HEARING_AID:
return DEVICE_CATEGORY_HEARING_AID;
case AUDIO_DEVICE_OUT_LINE:
case AUDIO_DEVICE_OUT_AUX_DIGITAL:
case AUDIO_DEVICE_OUT_USB_DEVICE:
return DEVICE_CATEGORY_EXT_MEDIA;
case AUDIO_DEVICE_OUT_SPEAKER:
case AUDIO_DEVICE_OUT_SPEAKER_SAFE:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
case AUDIO_DEVICE_OUT_USB_ACCESSORY:
case AUDIO_DEVICE_OUT_REMOTE_SUBMIX:
default:
return DEVICE_CATEGORY_SPEAKER;
每个stream中的每个device的音量都是分开保存,
数据库的字串为: mVolumeIndexSettingName (System.VOLUME_SETTINGS_INT字符数组) + AudioSystem.getOutputDeviceName设备名称 。
例如:
Music 类型的speaker音量存储的string为:volume_music_speaker
Music 类型的hdmi arc音量存储的string为:volume_music_hmdi_arc
音量存储在android数据库的system区,可通过命令获取
settings list system |grep music
2、特殊音量
固定音量的设备
音量为最大或者0
mFixedVolumeDevices
// Devices for which the volume is fixed (volume is either max or muted)
Set<Integer> mFixedVolumeDevices = new HashSet<>(Arrays.asList(
AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
AudioSystem.DEVICE_OUT_HDMI_ARC,
AudioSystem.DEVICE_OUT_AUX_LINE));
音量总是最大的设备
mFullVolumeDevices
3、音量的加载
每个type拥有一个VolumeStreamState实例
private void createStreamStates() {
int numStreamTypes = AudioSystem.getNumStreamTypes();
// 加载多条type的音量实例
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
for (int i = 0; i < numStreamTypes; i++) {
streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS_INT[mStreamVolumeAlias[i]], i);
}
checkAllFixedVolumeDevices();
checkAllAliasStreamVolumes();
checkMuteAffectedStreams();
updateDefaultVolumes();
}
创建实例时会将每一个type的各个device的音量加载到该实例的map mIndexMap中去
VolumeStreamState.readSettings
for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
// retrieve current volume for device
// if no volume stored for current stream and device, use default volume if default
// device, continue otherwise
int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
AudioSystem.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
int index;
if (!hasValidSettingsName()) {
index = defaultIndex;
} else {
String name = getSettingNameForDevice(device);
index = Settings.System.getIntForUser(
mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
}
if (index == -1) {
continue;
}
mIndexMap.put(device, getValidIndex(10 * index, true /*hasModifyAudioSettings*/));
}
4、音量设置
单板目录 /product/build.prop (Android R)
4.1、默认音量
开机起来AUDIO_STREAM_MUSIC的默认音量prop设置为,见上图
ro.config.media_vol_default
不配置该prop的逻辑处理如下:
int defaultMusicVolume = SystemProperties.getInt("ro.config.media_vol_default", -1);
if (defaultMusicVolume != -1 &&
defaultMusicVolume <= MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] &&
defaultMusicVolume >= MIN_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]) {
AudioSystem.DEFAULT_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] = defaultMusicVolume;
} else {
if (isPlatformTelevision()) {
AudioSystem.DEFAULT_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] =
MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] / 4;
} else {
AudioSystem.DEFAULT_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] =
MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] / 3;
}
}
4.2、最大音量
AUDIO_STREAM_MUSIC的UI可设置的最大音量prop为,见上图
ro.config.media_vol_steps
例如:UI可设置最大音量为0-100
不配置该prop的逻辑处理如下:
int maxMusicVolume = SystemProperties.getInt("ro.config.media_vol_steps", -1);
if (maxMusicVolume != -1) {
MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] = maxMusicVolume;
}
4、音量曲线
这里以AUDIO_STREAM_ACCESSIBILITY(10)类型的music在Speaker播放为例
hippoaudio是小编的sample可执行文件,-t后面的数字是type
则,使用的音量曲线为:通过/vendor/etc/audio_policy_volumes.xml查询可知,使用的为"DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE"数组,路径为/vendor/etc/default_volume_tables.xml
float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const
{
ALOG_ASSERT(!mCurvePoints.isEmpty(), "Invalid volume curve");
if (volIndexMin < 0 || volIndexMax < 0) {
// In order to let AudioService initialize the min and max, convention is to use -1
return NAN;
}
ALOGI("[%s:%d]index:%d, min:%d, max:%d, dev:%d", __func__, __LINE__, indexInUi, volIndexMin, volIndexMax, mDeviceCategory);
if (indexInUi < volIndexMin) {
// an index of 0 means mute request when volIndexMin > 0
if (indexInUi == 0) {
ALOGV("VOLUME forcing mute for index 0 with min index %d", volIndexMin);
return VOLUME_MIN_DB;
}
ALOGV("VOLUME remapping index from %d to min index %d", indexInUi, volIndexMin);
indexInUi = volIndexMin;
} else if (indexInUi > volIndexMax) {
ALOGV("VOLUME remapping index from %d to max index %d", indexInUi, volIndexMax);
indexInUi = volIndexMax;
}
size_t nbCurvePoints = mCurvePoints.size();
// the volume index in the UI is relative to the min and max volume indices for this stream
int nbSteps = 1 + mCurvePoints[nbCurvePoints - 1].mIndex - mCurvePoints[0].mIndex;
int volIdx = (nbSteps * (indexInUi - volIndexMin)) / (volIndexMax - volIndexMin);
ALOGI("[%s:%d] point_size:%d, nbSteps:%d,volIdx:%d max:%d, min:%d", __func__, __LINE__, nbCurvePoints, nbSteps, volIdx, mCurvePoints[nbCurvePoints - 1].mIndex,
mCurvePoints[0].mIndex);
// Where would this volume index been inserted in the curve point
size_t indexInUiPosition = mCurvePoints.orderOf(CurvePoint(volIdx, 0));
ALOGI("[%s:%d]indexInUiPosition:%d", __func__, __LINE__,
indexInUiPosition);
if (indexInUiPosition >= nbCurvePoints) {
//use last point of table
return mCurvePoints[nbCurvePoints - 1].mAttenuationInMb / 100.0f;
}
if (indexInUiPosition == 0) {
if (indexInUiPosition != mCurvePoints[0].mIndex) {
return VOLUME_MIN_DB; // out of bounds
}
return mCurvePoints[0].mAttenuationInMb / 100.0f;
}
// linear interpolation in the attenuation table in dB
float decibels = (mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f) +
((float)(volIdx - mCurvePoints[indexInUiPosition - 1].mIndex)) *
( ((mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f) -
(mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f)) /
((float)(mCurvePoints[indexInUiPosition].mIndex -
mCurvePoints[indexInUiPosition - 1].mIndex)) );
ALOGD("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
mCurvePoints[indexInUiPosition - 1].mIndex, volIdx,
mCurvePoints[indexInUiPosition].mIndex,
((float)mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f), decibels,
((float)mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f));
return decibels;
}