要求api level 28 (android 9)
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int minIndex = audioManager.getStreamMinVolume (STREAM_MUSIC);
int maxIndex = audioManager.getStreamMaxVolume (STREAM_MUSIC);
for(int i=minIndex; i<maxIndex; i++){
float db = audioManager.getStreamVolumeDb(STREAM_MUSIC, i, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP);
Log.d(TAG, "volume db = " + db + " at index= " + i);
}
以下是枯燥的android源码走读, 用作记录, 有兴趣的话可以看看.
getStreamVolumeDb简单调用如下:
// frameworks/base/media/java/android/media/AudioManager.java
public float getStreamVolumeDb(@PublicStreamTypes int streamType, int index,
@AudioDeviceInfo.AudioDeviceTypeOut int deviceType) {
// ...
final float gain = AudioSystem.getStreamVolumeDB(streamType, index,
AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType));
// ...
}
进JNI:
// frameworks/base/media/java/android/media/AudioSystem.java
/** @hide */
public static native float getStreamVolumeDB(int stream, int index, int device);
frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
float AudioPolicyManager::getStreamVolumeDB(
audio_stream_type_t stream, int index, audio_devices_t device)
{
return computeVolume(getVolumeCurves(stream), toVolumeSource(stream), index, {device});
}
1.computeVolume负责数值转换,
// frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
float AudioPolicyManager::computeVolume(IVolumeCurves &curves,
VolumeSource volumeSource,
int index,
const DeviceTypeSet& deviceTypes)
{
float volumeDb = curves.volIndexToDb(Volume::getDeviceCategory(deviceTypes), index);
// 删除了部分策略处理
return volumeDb;
}
其中的volIndexToDb细节如下
// frameworks/av/services/audiopolicy/engine/common/src/VolumeCurve.cpp
float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const
{
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);
// Where would this volume index been inserted in the curve point
size_t indexInUiPosition = mCurvePoints.orderOf(CurvePoint(volIdx, 0));
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)) );
return decibels;
}
2.getVolumeCurves获取音量曲线
// frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.h
IVolumeCurves &getVolumeCurves(audio_stream_type_t stream)
{
auto *curves = mEngine->getVolumeCurvesForStreamType(stream);
return *curves;
}
// frameworks/av/services/audiopolicy/engine/common/src/EngineBase.cpp
VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream) const
{
volume_group_t volGr = mProductStrategies.getVolumeGroupForStreamType(stream);
if (volGr == VOLUME_GROUP_NONE) {
volGr = mProductStrategies.getDefaultVolumeGroup();
}
const auto &iter = mVolumeGroups.find(volGr);
return mVolumeGroups.at(volGr)->getVolumeCurves();
}
// frameworks/av/services/audiopolicy/engine/common/include/VolumeGroup.h
VolumeCurves *getVolumeCurves() { return &mGroupVolumeCurves; }
VolumeCurves mGroupVolumeCurves;
可以注意到其中是从VolumeGroup(s)中获取的, 而VolumeGroup中的VolumeCurves是在loadAudioPolicyEngineConfig时从audio policy的相关xml中作引擎参数载入的
frameworks/av/services/audiopolicy/engine/common/src/EngineBase.cpp
engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{
// 部分省略
auto result = fileExists(engineConfig::DEFAULT_PATH) ?
engineConfig::parse(engineConfig::DEFAULT_PATH) : engineConfig::ParsingResult{};
if (result.parsedConfig == nullptr) {
ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__);
engineConfig::Config config = gDefaultEngineConfig;
android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups);
result = {std::make_unique<engineConfig::Config>(config),
static_cast<size_t>(ret == NO_ERROR ? 0 : 1)};
} else {
// ...
}
}
其中的engineConfig::DEFAULT_PATH指向/vendor/etc/audio_policy_engine_configuration.xml
// frameworks/av/services/audiopolicy/engine/config/include/EngineConfig.h
/** Default path of audio policy usages configuration file. */
constexpr char DEFAULT_PATH[] = "/vendor/etc/audio_policy_engine_configuration.xml";
而如果没有这个文件的时候回去读传统的配置数据,
而其中最重要的指向是audio_policy_configuration.xml, 我们知道这个xml通常还会再包含一些相关的xml
//system/media/audio/include/system/audio_config.h
static inline std::string audio_get_audio_policy_config_file() {
static constexpr const char *apmXmlConfigFileName = "audio_policy_configuration.xml";
static constexpr const char *apmA2dpOffloadDisabledXmlConfigFileName =
"audio_policy_configuration_a2dp_offload_disabled.xml";
static constexpr const char *apmBluetoothLegacyHalXmlConfigFileName =
"audio_policy_configuration_bluetooth_legacy_hal.xml";
std::string audioPolicyXmlConfigFile;
// First try alternative files if needed
if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false)) {
if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false) &&
property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
// Both BluetoothAudio@2.0 and BluetoothA2dp@1.0 (Offload) are disabled, and uses
// the legacy hardware module for A2DP and hearing aid.
audioPolicyXmlConfigFile = audio_find_readable_configuration_file(
apmBluetoothLegacyHalXmlConfigFileName);
} else if (property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
// A2DP offload supported but disabled: try to use special XML file
audioPolicyXmlConfigFile = audio_find_readable_configuration_file(
apmA2dpOffloadDisabledXmlConfigFileName);
}
} else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false)) {
audioPolicyXmlConfigFile = audio_find_readable_configuration_file(
apmBluetoothLegacyHalXmlConfigFileName);
}
return audioPolicyXmlConfigFile.empty() ?
audio_find_readable_configuration_file(apmXmlConfigFileName) : audioPolicyXmlConfigFile;
}
通常来说, audio_policy_configuration.xml中会这样引用一些附属文件
<!-- Volume section --> <xi:include href="audio_policy_volumes.xml"/> <xi:include href="default_volume_tables.xml"/>
而audio_policy_volumes.xml文件中会包含下面这样的音频曲线数据
<volumes>
<volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEADSET">
<point>0,-4200</point>
<point>33,-2800</point>
<point>66,-1400</point>
<point>100,0</point>
</volume>
我粗略的理解是这表示, 最高0dB, 最低-42dB, 而中间还有两个节点, 行程一个折线式的volume table表.