AudioPolicy--音量的控制

/*****************************************************************************************************/

聲明:本文內容是基於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一起解析。

  1. 創建一個VolumeCurvesCollection對象,構造函數會爲每一個流創建VolumeCurvesForStream。
  2. 把mVolumeCurves對象傳入AudioPolicyConfig的構造函數。作爲AudioPolicyConfig的一個屬性mVolumeCurves
  3. 解析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獲得設備的標準方式。

  1. 判斷傳入的參數
  2. 更新與傳入的流,設備的音量值
  3. 遍歷已經打開的所有的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函數。

  1. 如果請求的流已經被Mute, 則不能調節該流的音量
  2. 判斷AUDIO_STREAM_VOICE_CALL或AUDIO_POLICY_FORCE_BT_SCO的情況
  3. 如果傳入的設備是空,使用output當前使用的音量
  4. 獲得需要調節音量的分貝值
  5. 把音量傳到AudioFlinger
  6. 計算並設置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傳到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);
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章