/*****************************************************************************************************/
声明:本文内容是基于Android 8.1的源码分析
https://blog.csdn.net/xuechwtx原创,转载请注明出处,谢谢!
/*****************************************************************************************************/IVolumeCurvesCollection *mVolumeCurves;
mVolumeCurves = new VolumeCurvesCollection()
从AudioPolicymanager中可以看到,在操作音量的函数中,大都会调用mVolumeCurves的一些方法。比如在函数getStreamVolumeIndex中就调用了 mVolumeCurves->getVolumeIndex()的方法。所以我们可以猜测, mVolumeCurves可能保存着音量的信息, 并且有一些操作音量的方法. 在头文件中,mVolumeCurves被声明为IVolumeCurvesCollection类型。
IVolumeCurvesCollection,这个类的定义在文件
class VolumeCurvesCollection :
public KeyedVector,
public IVolumeCurvesCollection
VolumeCurvesCollection首先继承于IVolumeCurvesCollection,并且实现了父类中的虚函数。另外VolumeCurvesCollection继承于一个容器KeyedVector
class VolumeCurvesForStream : public KeyedVector >
{
private:
KeyedVector > mOriginVolumeCurves;
KeyedVector mIndexCur; /**< current volume index per device. */
int mIndexMin; /**< min volume index. */
int mIndexMax; /**< max volume index. */
bool mCanBeMuted; /**< true is the stream can be muted. */
};
mIndexMin,mIndexMax这两个属性代表着该流可以调节音量的最大值和最小值,这里的音量值就是我们实际调节音量键的时候所调节的音量值。最大值,最小值保存在AudioService.java中的MAX_STREAM_VOLUME,MIN_STREAM_VOLUME数组内。在AudioService构造的时候会调用AudioSystem的接口initStreamVolume传入每个流的最大值和最小值。
private 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
};
private static int[] MIN_STREAM_VOLUME = new int[] {
1, // STREAM_VOICE_CALL
0, // STREAM_SYSTEM
0, // STREAM_RING
0, // STREAM_MUSIC
0, // STREAM_ALARM
0, // STREAM_NOTIFICATION
0, // STREAM_BLUETOOTH_SCO
0, // STREAM_SYSTEM_ENFORCED
0, // STREAM_DTMF
0, // STREAM_TTS
0 // STREAM_ACCESSIBILITY
};
mCanBeMuted
表示该流是否可以被Mute,现在代码环境下,都是ture。class VolumeCurve : public RefBase
{
private:
SortedVector mCurvePoints;
device_category mDeviceCategory;
audio_stream_type_t mStreamType;
};
struct CurvePoint
{
uint32_t mIndex;
int mAttenuationInMb;
};
VolumeCurve里面保存一个CurvePoint(曲线点)的容器,曲线点的横坐标是Index,纵坐标是mAttenuationInMb(单位是mdB).接下来看一下其中一条音量曲线. 我们可以看到该曲线包含四个点, index是从0~100,Attenuation的大小是-58dB~0dB。具体的音量值, Index和Attenuation之间的转换关系我们稍后会具体分析.
1,-5800
20,-4000
60,-1700
100,0
接下来总结一下VolumeCurvesCollection的结构:
首先我们看一下音量配置文件的结构。首先default_volume_tables.xml文件中保存着一些默认的音量配置,这些配置可以被audio_policy_volumes.xml中具体的音量配置引用,例如, AUDIO_STREAM_MUSIC流DEVICE_CATEGORY_SPEAKER类的设备使用的音量曲线的配置是DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE。
1,-5800
20,-4000
60,-1700
100,0
音量相关的配置文件包括audio_policy_volumes.xml和default_volume_tables.xml,默认在frameworks/av/services/audiopolicy/config/目录下, 配置文件的位置在不同平台不同。通常跟AudioPolicy的配置文件audio_policy_configuration.xml在同一个目录下。我们可以看到audio_policy_configuration.xml中引用了音量的配置文件。所以音频配置文件在AudioPolicyManager的构造函数中,跟audio_policy_configuration.xml一起解析。
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
// 1. 创建一个VolumeCurvesCollection对象
mVolumeCurves = new VolumeCurvesCollection();
// 2. 把mVolumeCurves对象传入AudioPolicyConfig的构造函数
AudioPolicyConfig config(mHwModules, mAvailableOutputDevices, mAvailableInputDevices,
mDefaultOutputDevice, speakerDrcEnabled,
static_cast(mVolumeCurves));
// 保存传入的mVolumeCurves对象
config.mVolumeCurves = mVolumeCurves;
// 3. 尝试解析Audiolicy的配置文件 audio_policy_configuration.xml
deserializeAudioPolicyXmlConfig(config);
// 依次尝试查找下面目录下的audio_policy_configuration.xml配置文件,
// {"/odm/etc", "/vendor/etc/audio", "/vendor/etc", "/system/etc"};
// audioPolicyXmlConfigFile = “audio_policy_configuration.xml”
PolicySerializer serializer;
serializer.deserialize(audioPolicyXmlConfigFile, config)
// 4. 初始化 mVolumeCurves,在当前代码中initializeVolumeCurves没有做什么操作
mVolumeCurves->initializeVolumeCurves(speakerDrcEnabled);
AudioPolicyManager通过PolicySerializer::deserialize来解析Audio Policy 的配置文件其中包括audio_policy_configuration.xml
status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig &config)
// 解析文件xml文件,找到跟节点xmlNodePtr cur
xml文件,DocPtr doc = xmlParseFile(configFile);
xmlNodePtr cur = xmlDocGetRootElement(doc);
// 调用 deserializeCollection 函数解析Volume类型的节点
// 设置AudioPolicyConfig中的属性mVolumeCurves = volumes;
VolumeTraits::Collection volumes;
deserializeCollection(doc, cur, volumes, &config);
config.setVolumes(volumes);
deserializeCollection函数最终会调用VolumeTraits::deserialize函数解析每一个音量曲线。VolumeTraits::deserialize函数根据 “stream” “deviceCategory” “point”解析每条曲线配置。
1,-3000
33,-2600
66,-2200
100,-1800
status_t VolumeTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, PtrElement &element,
PtrSerializingCtx /*serializingContext*/)
{
// 解析 “stream” Tag
string streamTypeLiteral = getXmlAttribute(root, Attributes::stream);
audio_stream_type_t streamType;
StreamTypeConverter::fromString(streamTypeLiteral, streamType);
// 解析 “deviceCategory” Tag
string deviceCategoryLiteral = getXmlAttribute(root, Attributes::deviceCategory);
device_category deviceCategory;
DeviceCategoryConverter::fromString(deviceCategoryLiteral, deviceCategory);
// 解析 “ref” Tag
string referenceName = getXmlAttribute(root, Attributes::reference);
const _xmlNode *ref = NULL;
if (!referenceName.empty()) {
getReference(root->parent, ref, referenceName);
}
element = new Element(deviceCategory, streamType);
const xmlNode *child = referenceName.empty() ? root->xmlChildrenNode : ref->xmlChildrenNode;
while (child != NULL) {
if (!xmlStrcmp(child->name, (const xmlChar *)volumePointTag)) {
xmlChar *pointDefinition = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);;
// 解析 “point”
Vector point;
collectionFromString >((const char*)pointDefinition, point, ",");
// 添加解析出来的 Point
element->add(CurvePoint(point[0], point[1]));
xmlFree(pointDefinition);
}
child = child->next;
}
}
void clearCurrentVolumeIndex(audio_stream_type_t stream)
editCurvesFor(stream).clearCurrentVolumeIndex();
// 清空当前音量的容器 KeyedVector mIndexCur
mIndexCur.clear();
addCurrentVolumeIndex,更新某个流某个设备的音量 AudioPolicyManager在函数setStreamVolumeIndex时会调用该接口
void addCurrentVolumeIndex(audio_stream_type_t stream, audio_devices_t device, int index)
// VolumeCurvesForStream::addCurrentVolumeIndex
editCurvesFor(stream).addCurrentVolumeIndex(device, index);
// 向当前音量的容器添加一项,替换当前保存的音量值
mIndexCur.add(device, index);
canBeMuted,判断该流是否可以静音
当前代码没有setcanBeMuted的接口。 mCanBeMuted默认为ture, 该接口返回ture
bool canBeMuted(audio_stream_type_t stream)
return getCurvesFor(stream).canBeMuted()
// 返回 流的音量信息中的 mCanBeMuted
// VolumeCurvesForStream.mCanBeMuted
VolumeCurvesForStream::canBeMuted()
return mCanBeMuted;
getVolumeIndexMin,获取该流的最小音量
没有set的接口, Min和Max只有被接口initializeVolumeCurves设置
int getVolumeIndexMin(audio_stream_type_t stream)
return getCurvesFor(stream).getVolumeIndexMin()
// 返回 流的音量信息中的 mIndexMin
// VolumeCurvesForStream.mIndexMin
VolumeCurvesForStream::getVolumeIndexMin()
return mIndexMin
getVolumeIndexMax,获取该流的最大音量
int getVolumeIndexMax(audio_stream_type_t stream)
return getCurvesFor(stream).getVolumeIndexMax()
// 返回 流的音量信息中的 mIndexMax
// VolumeCurvesForStream.mIndexMax
VolumeCurvesForStream::getVolumeIndexMax()
return mIndexMax
initStreamVolume,初始化特定流的VolumeCurvesForStream
这里只是设置 VolumeCurvesForStream 的最大音量和最小音量
这也是唯一可以设置最大最小值的接口
virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax)
editValueAt(stream).setVolumeIndexMin(indexMin);
// VolumeCurvesForStream.mIndexMin = indexMin;
editValueAt(stream).setVolumeIndexMax(indexMax);
// VolumeCurvesForStream.mIndexMax = indexMax;
getVolumeIndex,获取该特定流特定设备的音量
返回VolumeCurvesForStream.mIndexCur.valueFor(device)
int getVolumeIndex(audio_stream_type_t stream, audio_devices_t device)
return getCurvesFor(stream).getVolumeIndex(device)
VolumeCurvesForStream::getVolumeIndex()
// 从多个设备选择中提取一个的设备
// getCurvesFor(stream).mIndexCur.valueFor(device)
device = Volume::getDeviceForVolume(device)
return mIndexCur.valueFor(device)
hasVolumeIndexForDevice,有没有为特定的流特定的设备设置音量值
virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream,
audio_devices_t device) const
// VolumeCurvesForStream::hasVolumeIndexForDevice(device)
return getCurvesFor(stream).hasVolumeIndexForDevice(device);
// mIndexCur.indexOfKey(device) < 0 表示mIndexCur没有该项
device = Volume::getDeviceForVolume(device);
return mIndexCur.indexOfKey(device) >= 0;
initializeVolumeCurves,解析配置文件的时候已经被初始化,不需要再初始化
void initializeVolumeCurves(bool /*isSpeakerDrcEnabled*/) {}
switchVolumeCurve, 用src流的mOriginVolumeCurves音量曲线替换dst的音量曲线
这样dst会暂时使用跟src流相同的音量曲线,dst的mOriginVolumeCurves音量曲线不变
virtual void switchVolumeCurve(audio_stream_type_t src流的, audio_stream_type_t dst)
restoreOriginVolumeCurve,从mOriginVolumeCurves音量曲线恢复该流的音量曲线
与switchVolumeCurve配合使用,用来回复switchVolumeCurve的修改
virtual void restoreOriginVolumeCurve(audio_stream_type_t stream)
{
switchVolumeCurve(stream, stream);
}
上层设置到AudioPolicy的音量是用户设置的音量值, 而底层把音量值转换成分贝值才能处理该音量。音量曲线的作用就是做这种转换。具体的转换流程在函数volIndexToDb内完成的。
1. 找到特定的音量曲线
传入的参数是:特定的流,特定的设备类型,需要计算的音量值。
因为音量的计算依赖于特定的音量曲线,需要根据stream,category找到该音量曲线,调用音量曲线的函数VolumeCurve::volIndexToDb
virtual float volIndexToDb(audio_stream_type_t stream, device_category cat, int indexInUi) const
return getCurvesFor(stream).volIndexToDb(cat, indexInUi);
float volIndexToDb(device_category deviceCat, int indexInUi) const
return getCurvesFor(deviceCat)->volIndexToDb(indexInUi, mIndexMin, mIndexMax);
2. 计算音量值对应的横坐标// 传入特定流的音量最大值,最小值
float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const
{
// 确定曲线的点的个数
size_t nbCurvePoints = mCurvePoints.size();
// 曲线两个端点横坐标的的差值,一般情况下是100
int nbSteps = 1 + mCurvePoints[nbCurvePoints - 1].mIndex - mCurvePoints[0].mIndex;
// 计算出横坐标
if (indexInUi >= volIndexMin)
volIdx = (nbSteps * (indexInUi - volIndexMin)) / (volIndexMax - volIndexMin);
3. 计算横坐标对应的分贝值(纵坐标)p[n-1].y + (x-p[n-1].x) * ( (p[n].y - p[n-1].y) / (p[n].x - p[n-1].x) )
// 计算出横坐标在哪一条线段,即上面公式中的n
size_t indexInUiPosition = mCurvePoints.orderOf(CurvePoint(volIdx, 0));
// 如果横坐标大于最大值,使用最大的音量
if (indexInUiPosition >= nbCurvePoints) {
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;
}
// 根据线性关系算出音量(衰减)
// 因为配置文件中存入的值不是以dB为单位,所以计算过程中需要做“/100”的操作
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)) );
setStreamVolumeIndex,设置特定流特定设备的音量
其中参数device是AudioServer.java通过AudioSystem,AudioPlicy的接口 getStrategyForStream获得, 即Stream->Strategy->Device的方式, 这也是AudioPolicy获得设备的标准方式。
(4) 如果请求的设备是默认设备,需要curStreamDevice没有音量配置
status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
int index, audio_devices_t device)
// 1. 判断传入的参数
// 音量不可大于流的最大音量,小于最小音量值
if ((index < mVolumeCurves->getVolumeIndexMin(stream)) ||
(index > mVolumeCurves->getVolumeIndexMax(stream)))
return BAD_VALUE;
// 设备需要是输出设备
if (!audio_is_output_device(device))
return BAD_VALUE;
// 如果传入的流不能被Mute, 强制使用该流的最高音量
// canBeMuted,现在代码中,没有设置canBeMuted的接口,默认被设置为true
if (!mVolumeCurves->canBeMuted的接口,默认被设置为true(stream)) index = mVolumeCurves->getVolumeIndexMax(stream);
// 2. 更新与传入的流,设备的音量值
for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
// return (stream1 == stream2)
if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
continue;
}
// 更新特定流,特定设备的音量值
mVolumeCurves->addCurrentVolumeIndex((audio_stream_type_t)curStream, device, index);
}
// 3. 遍历已经打开的所有的output,对所有的符合条件的output和流设置音量
status_t status = NO_ERROR;
for (size_t i = 0; i < mOutputs.size(); i++) {
// 获得该output的配置信息
sp desc = mOutputs.valueAt(i);
// curDevice 是根据当前output使用的设备得出的
// 其中主要对双设备做了处理,一般双设备只选取了speaker
audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device());
for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
// (1) 请求的流必须在当前output中Active(可以理解为正在播放)
// 遍历所有的流,仅对跟请求的流符合的流(当前的代码下可以认为自有请求的流)
if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
continue;
}
// 判断流是不是Active
if (!(desc->isStreamActive((audio_stream_type_t)curStream) ||
(isInCall() && (curStream == AUDIO_STREAM_VOICE_CALL)))) {
continue;
}
// (2) 判断请求的设备是否跟当前获得的设备匹配
// 获得请求的流在当前场景下应该使用的设备
routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
audio_devices_t curStreamDevice = Volume::getDeviceForVolume(getDeviceForStrategy(
curStrategy, false /*fromCache*/));
// 请求的设备跟curStreamDevice是否有相同的设备, 是否是默认设备
// 如果两个条件都不符合,不会调整当前流的音量
if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) &&
((curStreamDevice & device) == 0)) {
continue;
}
bool applyVolume;
// (3) OutPut的当前设备是否与请求的设备或者请求的设备的子设备相同
if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
curStreamDevice |= device;
applyVolume = (curDevice & curStreamDevice) != 0;
} else {
// (4) 如果请求的设备是默认设备,需要curStreamDevice没有音量配置
applyVolume = !mVolumeCurves->hasVolumeIndexForDevice(
stream, curStreamDevice);
}
if (applyVolume) {
// 调用checkAndSetVolume应用该音量值
status_t volStatus =
checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice,
(stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0);
Audiopoicy真正通知Audofinger调节音量的接口是checkAndSetVolume。需要传入的参数主要包括stream,outputDesc,index,force。对于AudioFlinger来说每一个output对应一个播放线程。因为每个output所用输出设备会不同,所以对于不同output需要有不同的音量, 每一个output内可以同时支持多种流播放,每个output中不同的流需要与不同的音量值. 而一个output在当前只能使用同一个输出设备, 所以AudioFlinger只需要关心每个output中每个流的音量. 在每个output都有mCurVolume[stream]保存着该流音量的分贝值。
接下来分析哪些因素会引起调节音量。首先是音量值的改变即主动调节音量setStreamVolumeIndex,其次就是设置静音setStreamMute。上面都是属于主动因素,接下来看一下被动因素,这里面主要就是输出设备的改变,可以导致输出设备改变的调用包括startOutput, stopOutput, setPhoneState, setDeviceConnectionState, setForceUse. 这些函数涉及到比较复杂的output和设备的管理。需呀逐个分析。这里暂时只分析checkAndSetVolume函数。
status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
int index,
const sp& outputDesc,
audio_devices_t device,
int delayMs,
bool force)
{
// 1. 如果请求的流已经被Mute, 则不能调节该流的音量
if (outputDesc->mMuteCount[stream] != 0) {
return NO_ERROR;
}
// 2. 如果设置Forceuse SCO,不能设置AUDIO_STREAM_VOICE_CALL的音量、
// 如果没有设置Forceuse SCO,不能设置AUDIO_STREAM_BLUETOOTH_SCO的音量
// AUDIO_STREAM_BLUETOOTH_SCO可以看作是是特殊的VOICE流,用于SCO通话
audio_policy_forced_cfg_t forceUseForComm =
mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION);
if ((stream == AUDIO_STREAM_VOICE_CALL && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) ||
(stream == AUDIO_STREAM_BLUETOOTH_SCO && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO)) {
return INVALID_OPERATION;
}
// 3. 如果传入的设备是空,使用output当前使用的音量
// 有待分析 ???
if (device == AUDIO_DEVICE_NONE) {
device = outputDesc->device();
}
// 4. 获得需要调节音量的分贝值
// 这里主要是调用volIndexToDb完成,另外还会这里还会针对一些特殊情况,调整获得音量
// 代码中有很详尽的注释,不再具体分析
float volumeDb = computeVolume(stream, index, device);
// 5. 把音量传到AudioFlinger
// AudioFlinger是音量调节的执行者,AudioPolicy是决策者
outputDesc->setVolume(volumeDb, stream, device, delayMs, force);
// 6. 计算并设置Voice的音量
// 对于Voice的数据不会经过AP侧,音量的调节一般需要在底层完成(Modem?)
if (stream == AUDIO_STREAM_VOICE_CALL ||
stream == AUDIO_STREAM_BLUETOOTH_SCO) {
float voiceVolume;
if (stream == AUDIO_STREAM_VOICE_CALL) {
// 计算出Voice流的音量
voiceVolume = (float)index/(float)mVolumeCurves->getVolumeIndexMax(stream);
} else {
// 对于AUDIO_STREAM_BLUETOOTH_SCO流,蓝牙侧会调节音量,所以这里会使用最大音量值
voiceVolume = 1.0;
}
if (voiceVolume != mLastVoiceVolume) {
// 直接调用AudioFinger的setVoiceVolume接口
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
mLastVoiceVolume = voiceVolume;
SwAudioOutputDescriptor::setVolume把计算出来的音量float volume传到AudioFlinger
bool SwAudioOutputDescriptor::setVolume(float volume,
audio_stream_type_t stream,
audio_devices_t device,
uint32_t delayMs,
bool force)
// 判断是否需要改变音量
// 这里需要改变音量的条件是当前Outut的请求的流的音量已经改变
// 或者force == true
// 这里的mCurVolume只是区别流的音量,没有区别设备的音量,所以在切换设备的过程中,也会应用音量
// 这里的原因大概是,一个output中所有的Track可能有不同的流类型,但是设备是相同的
bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force)
return (volume != mCurVolume[stream] || force)
if (changed) {
// 把计算出的音量的衰减传入到AudioFnger
// 分贝值转换为衰减,分贝值一般小于0, 所以这里计算出来的增益(理解为相对的电压值或压强值)是小于1的
float volume = Volume::DbToAmpl(mCurVolume[stream]);
// ???
if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
mClientInterface->setStreamVolume(
AUDIO_STREAM_VOICE_CALL, volume, mIoHandle, delayMs);
}
// 把音量传递给AudioFinger
mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs);
}