/*****************************************************************************************************/
聲明:本文內容是基於Android 8.1的源碼分析
https://blog.csdn.net/xuechwtx原創,轉載請註明出處,謝謝!
/*****************************************************************************************************/1. VolumeCurvesCollection
(1) IVolumeCurvesCollection
IVolumeCurvesCollection *mVolumeCurves;
mVolumeCurves = new VolumeCurvesCollection()
從AudioPolicymanager中可以看到,在操作音量的函數中,大都會調用mVolumeCurves的一些方法。比如在函數getStreamVolumeIndex中就調用了 mVolumeCurves->getVolumeIndex()的方法。所以我們可以猜測, mVolumeCurves可能保存着音量的信息, 並且有一些操作音量的方法. 在頭文件中,mVolumeCurves被聲明爲IVolumeCurvesCollection類型。
IVolumeCurvesCollection,這個類的定義在文件<IVolumeCurvesCollection.h>。在IVolumeCurvesCollection類中的public方法都被聲明爲虛函數,這些方法都是對AudioPolicy的接口,這些接口的實現應該都是在VolumeCurvesCollection類中。
(2) VolumeCurvesCollection
class VolumeCurvesCollection :
public KeyedVector<audio_stream_type_t, VolumeCurvesForStream>,
public IVolumeCurvesCollection
VolumeCurvesCollection首先繼承於IVolumeCurvesCollection,並且實現了父類中的虛函數。另外VolumeCurvesCollection繼承於一個容器KeyedVector<audio_stream_type_t, VolumeCurvesForStream>,容器的下標是流類型,容器的元素是VolumeCurvesForStream類型的對象。從VolumeCurvesCollection構造函數可以看出,構造時會每一種流類型創建一個VolumeCurvesForStream。所以我們可以猜測VolumeCurvesForStream中保存着每個流的音量信息。這樣做可以對不同的流使用各自的策略。
class VolumeCurvesForStream : public KeyedVector<device_category, sp<VolumeCurve> >
{
private:
KeyedVector<device_category, sp<VolumeCurve> > mOriginVolumeCurves;
KeyedVector<audio_devices_t, int> 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。mIndexCur也是一個容器,下標是設備號audio_devices_t,元素是音量值(際調節的音量. 在VolumeCurvesCollection中,對於每個流每一個設備都保存着一個音量值。這樣做的目的主要是可以存儲不同設備的音量值,來滿足用戶對不同設備不同音量的需求。
VolumeCurvesForStream繼承了KeyedVector<device_category, sp<VolumeCurve> >,另外內部又有一個該容器類型的屬性mOriginVolumeCurves。所以可以認爲VolumeCurvesForStream內部有兩個KeyedVector<device_category, sp<VolumeCurve> >類型的對象。其中mOriginVolumeCurves是原始值,不會被改變。KeyedVector<device_category, sp<VolumeCurve> >下標是device_category(設備的類別),元素是VolumeCurve. VolumeCurve代表一個音量曲線,即音量值與分貝值或者增益的對應關係。device_category是把設備分成了四類DEVICE_CATEGORY_HEADSET, DEVICE_CATEGORY_SPEAKER,DEVICE_CATEGORY_EARPIECE,DEVICE_CATEGORY_EXT_MEDIA. getDeviceCategory(audio_devices_t device)函數可以獲得設備的分類。mOriginVolumeCurves提供了針對4類設備的音量值到分貝值的轉換關係。
class VolumeCurve : public RefBase
{
private:
SortedVector<CurvePoint> 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之間的轉換關係我們稍後會具體分析.
<reference name="DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE">
<!-- Default is Speaker Media Volume Curve -->
<point>1,-5800</point>
<point>20,-4000</point>
<point>60,-1700</point>
<point>100,0</point>
</reference>
接下來總結一下VolumeCurvesCollection的結構:
2. 音量策略配置文件的解析
(1) 配置文件的結構
首先我們看一下音量配置文件的結構。首先default_volume_tables.xml文件中保存着一些默認的音量配置,這些配置可以被audio_policy_volumes.xml中具體的音量配置引用,例如, AUDIO_STREAM_MUSIC流DEVICE_CATEGORY_SPEAKER類的設備使用的音量曲線的配置是DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE。
<!-- In the filse default_volume_tables.xml -->
<reference name="DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE">
<!-- Default is Speaker Media Volume Curve -->
<point>1,-5800</point>
<point>20,-4000</point>
<point>60,-1700</point>
<point>100,0</point>
</reference>
<!-- In the filse audio_policy_volumes.xml -->
<volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER"
ref="DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE"/>
(2) 選擇配置文件
音量相關的配置文件包括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一起解析。
- 創建一個VolumeCurvesCollection對象,構造函數會爲每一個流創建VolumeCurvesForStream。
- 把mVolumeCurves對象傳入AudioPolicyConfig的構造函數。作爲AudioPolicyConfig的一個屬性mVolumeCurves
- 解析AudioPolicy的配置文件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<VolumeCurvesCollection *>(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);
(3) 配置文件的解析過程
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<VolumeTraits>(doc, cur, volumes, &config);
config.setVolumes(volumes);
deserializeCollection函數最終會調用VolumeTraits::deserialize函數解析每一個音量曲線。VolumeTraits::deserialize函數根據 “stream” “deviceCategory” “point”解析每條曲線配置。
<volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_EXT_MEDIA"
ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_SYSTEM" deviceCategory="DEVICE_CATEGORY_HEADSET">
<point>1,-3000</point>
<point>33,-2600</point>
<point>66,-2200</point>
<point>100,-1800</point>
</volume>
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<VolumeTraits>(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<int32_t> point;
collectionFromString<DefaultTraits<int32_t> >((const char*)pointDefinition, point, ",");
// 添加解析出來的 Point
element->add(CurvePoint(point[0], point[1]));
xmlFree(pointDefinition);
}
child = child->next;
}
}
3. IVolumeCurvesCollection 的接口
clearCurrentVolumeIndex,清空某個流的所有音量記錄,AudioPolicyManager沒有調到該接口void clearCurrentVolumeIndex(audio_stream_type_t stream)
editCurvesFor(stream).clearCurrentVolumeIndex();
// 清空當前音量的容器 KeyedVector<audio_devices_t, int> 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);
}
4. 音量(衰減)的計算
上層設置到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. 計算音量值對應的橫座標音量值和橫座標是線性關係, 所以計算的方法是
(indexInUi - volIndexMin) / (volIndexMax - volIndexMin) * 100
// 傳入特定流的音量最大值,最小值
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)) );
5. 設置音量值的接口
setStreamVolumeIndex,設置特定流特定設備的音量
其中參數device是AudioServer.java通過AudioSystem,AudioPlicy的接口 getStrategyForStream獲得, 即Stream->Strategy->Device的方式, 這也是AudioPolicy獲得設備的標準方式。
- 判斷傳入的參數
- 更新與傳入的流,設備的音量值
- 遍歷已經打開的所有的output,對所有的符合條件的output和流設置音量
(1) 該output中,請求的流必須正在播放,音量無法設置,很大情況下,都是該條件不符合
(2) 判斷請求的設備是否跟當前根據流類型獲得的設備curStreamDevice相匹配
因爲兩個設備獲得的方式相同,如果設備不同,表示在設置音量的過程中, 已經切換了設備
(3) OutPut的當前設備是否與請求的設備或者請求的設備的子設備相同
子設備的情況考慮到了Duplicating的情況,不相同代表OutPut需要切換設備?
(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<SwAudioOutputDescriptor> 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);
6. 調節音量
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函數。
- 如果請求的流已經被Mute, 則不能調節該流的音量
- 判斷AUDIO_STREAM_VOICE_CALL或AUDIO_POLICY_FORCE_BT_SCO的情況
- 如果傳入的設備是空,使用output當前使用的音量
- 獲得需要調節音量的分貝值
- 把音量傳到AudioFlinger
- 計算並設置Voice的音量
status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
int index,
const sp<AudioOutputDescriptor>& 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傳到AudioFlingerbool 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);
}