使用 IKsControl 介面存取音訊屬性

在罕見的情況下,特製化音訊應用程式可能需要使用 IKsControl 介面來存取 DeviceTopology API 或 MMDevice API 未公開之音訊適配卡的特定硬體功能。 IKsControl 介面可讓核心串流 (KS) 裝置的屬性、事件和方法可供使用者模式應用程式使用。 音訊應用程式的主要興趣是 KS 屬性。 IKsControl 介面可與 DeviceTopology API 和 MMDevice API 搭配使用,以存取音頻適配卡的 KS 屬性。

IKsControl 介面主要供撰寫控制面板應用程式來管理其音訊硬體的硬體廠商使用。 IKsControl 對於未系結至特定硬體裝置的一般用途音訊應用程式,不太有用。 原因是硬體廠商經常實作專屬機制來存取其裝置的音訊屬性。 相較於 DeviceTopology API,它隱藏硬體特定的驅動程式古怪,並提供相對統一的介面來存取音訊屬性,應用程式會使用 IKsControl 直接與驅動程序通訊。 如需 IKsControl 的詳細資訊,請參閱 Windows DDK 檔。

如裝置拓撲中所述,配接器裝置拓撲中的子單位可能支援下表左欄中所示的一或多個函式特定控制介面。 數據表右欄中的每個專案都是對應至左側控制項介面的 KS 屬性。 控制項介面可讓您方便存取 屬性。 大部分音訊驅動程式都會使用 KS 屬性來代表其音訊配接器拓撲中子單位(也稱為 KS 節點)的函式特定處理功能。 如需 KS 屬性和 KS 節點的詳細資訊,請參閱 Windows DDK 檔。

控制介面 KS 屬性
IAudioAutoGainControl KSPROPERTY_AUDIO_AGC
IAudioBass KSPROPERTY_AUDIO_BASS
IAudioChannelConfig KSPROPERTY_AUDIO_CHANNEL_CONFIG
IAudioInputSelector KSPROPERTY_AUDIO_MUX_SOURCE
IAudioLoudness KSPROPERTY_AUDIO_LOUDNESS
IAudioMidrange KSPROPERTY_AUDIO_MID
IAudioMute KSPROPERTY_AUDIO_MUTE
IAudioOutputSelector KSPROPERTY_AUDIO_DEMUX_DEST
IAudioPeakMeter KSPROPERTY_AUDIO_PEAKMETER
IAudioTreble KSPROPERTY_AUDIO_TREBLE
IAudioVolumeLevel KSPROPERTY_AUDIO_VOLUMELEVEL
IDeviceSpecificProperty KSPROPERTY_AUDIO_DEV_SPECIFIC

 

某些音訊配接器的拓撲可能包含具有上表未列出的 KS 屬性的子單位。 例如,假設特定子單位的 IPart::GetSubType 方法呼叫會擷取 GUID 值KSNODETYPE_TONE。 這個子類型 GUID 表示子單位支援下列一或多個 KS 屬性:

  • KSPROPERTY_AUDIO_BASS
  • KSPROPERTY_AUDIO_MID
  • KSPROPERTY_AUDIO_TREBLE
  • KSPROPERTY_AUDIO_BASS_BOOST

此清單中的前三個屬性可以透過上表中出現的控件介面來存取,但 deviceTopology API 中沒有對應控件介面的KSPROPERTY_AUDIO_BASS_BOOST屬性。 不過,如果子單位支援 屬性,應用程式可以使用 IKsControl 介面來存取這個屬性。

需要執行下列步驟,才能存取子類型子單位的 KSPROPERTY_AUDIO_BASS_BOOST 屬性KSNODETYPE_TONE:

  1. 呼叫 I 連線 or::GetDeviceId 連線 edTo IDeviceTopology::GetDeviceId 方法來取得識別配接器裝置的裝置識別符字串。 此字串類似於 端點標識符字串,不同之處在於它會識別配接器裝置,而不是端點裝置。 如需適配卡裝置與端點裝置之間差異的詳細資訊,請參閱 音訊端點裝置
  2. 使用裝置標識符字串呼叫 IMMDeviceEnumerator::GetDevice 方法,以取得配接器裝置的 IMMDevice 介面。IMMDevice 介面與 IMMDevice 介面中所述的介面相同,但它代表配接器裝置,而不是端點裝置。
  3. 呼叫 IMMDevice::Activate 方法,並將參數 iid 設為 REFIID IID_IKsControl,以取得子單位上的 IKsControl 介面。 請注意,這個 Activate 方法所支援的介面,也就是適配卡裝置,與端點裝置 Activate 方法所支援的介面不同。 特別是適 配卡裝置的 Activate 方法支援 IKsControl
  4. 呼叫 IPart::GetLocalId 方法以取得子單位的本機標識碼。
  5. 建構 KS 屬性要求。 要求所需的 KS 節點識別碼包含在上一個步驟中取得之本機標識碼的 16 個最小有效位中。
  6. 呼叫 IKsControl::KsProperty 方法,將 KS 屬性要求傳送至音訊驅動程式。 如需此方法的詳細資訊,請參閱 Windows DDK 檔。

下列程式代碼範例會從子類型子單位擷取 KSPROPERTY_AUDIO_BASS_BOOST 屬性值KSNODETYPE_TONE:

//-----------------------------------------------------------
// This function calls the IKsControl::Property method to get
// the value of the KSPROPERTY_AUDIO_BASS_BOOST property of
// a subunit. Parameter pPart should point to a part that is
// a subunit with a subtype GUID value of KSNODETYPE_TONE.
//-----------------------------------------------------------
#define PARTID_MASK 0x0000ffff
#define EXIT_ON_ERROR(hres)  \
              if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

const IID IID_IKsControl = __uuidof(IKsControl);

HRESULT GetBassBoost(IMMDeviceEnumerator *pEnumerator,
                     IPart *pPart, BOOL *pbValue)
{
    HRESULT hr;
    IDeviceTopology *pTopology = NULL;
    IMMDevice *pPnpDevice = NULL;
    IKsControl *pKsControl = NULL;
    LPWSTR pwszDeviceId = NULL;

    if (pEnumerator == NULL || pPart == NULL || pbValue == NULL)
    {
        return E_INVALIDARG;
    }

    // Get the topology object for the adapter device that contains
    // the subunit represented by the IPart interface.
    hr = pPart->GetTopologyObject(&pTopology);
    EXIT_ON_ERROR(hr)

    // Get the device ID string that identifies the adapter device.
    hr = pTopology->GetDeviceId(&pwszDeviceId);
    EXIT_ON_ERROR(hr)

    // Get the IMMDevice interface of the adapter device object.
    hr = pEnumerator->GetDevice(pwszDeviceId, &pPnpDevice);
    EXIT_ON_ERROR(hr)

    // Activate an IKsControl interface on the adapter device object.
    hr = pPnpDevice->Activate(IID_IKsControl, CLSCTX_ALL, NULL, (void**)&pKsControl);
    EXIT_ON_ERROR(hr)

    // Get the local ID of the subunit (contains the KS node ID).
    UINT localId = 0;
    hr = pPart->GetLocalId(&localId);
    EXIT_ON_ERROR(hr)

    KSNODEPROPERTY_AUDIO_CHANNEL ksprop;
    ZeroMemory(&ksprop, sizeof(ksprop));
    ksprop.NodeProperty.Property.Set = KSPROPSETID_Audio;
    ksprop.NodeProperty.Property.Id = KSPROPERTY_AUDIO_BASS_BOOST;
    ksprop.NodeProperty.Property.Flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY;
    ksprop.NodeProperty.NodeId = localId & PARTID_MASK;
    ksprop.Channel = 0;

    // Send the property request.to the device driver.
    BOOL bValue = FALSE;
    ULONG valueSize;
    hr = pKsControl->KsProperty(
                         &ksprop.NodeProperty.Property, sizeof(ksprop),
                         &bValue, sizeof(bValue), &valueSize);
    EXIT_ON_ERROR(hr)

    *pbValue = bValue;

Exit:
    SAFE_RELEASE(pTopology)
    SAFE_RELEASE(pPnpDevice)
    SAFE_RELEASE(pKsControl)
    CoTaskMemFree(pwszDeviceId);
    return hr;
}

在上述程式代碼範例中,GetBassBoost 函式會採用下列三個參數:

只有在程式呼叫 IPart::GetSubType 之後,程式才會呼叫 GetBassBoost,並判斷子單位的子類型KSNODETYPE_TONE。 如果已啟用低音提升,屬性值為 TRUE 。 如果低音提升已停用,則為 FALSE

在 GetBassBoost 函式的開頭,對 IPart::GetTopologyObject 方法的呼叫取得包含KSNODETYPE_TONE子單位之配接器裝置的 IDeviceTopology 介面。 IDeviceTopology::GetDeviceId 方法呼叫會擷取識別配接器裝置的裝置標識符字串。 IMMDeviceEnumerator::GetDevice 方法呼叫會採用裝置標識符字串作為輸入參數,並擷取配接器裝置的 IMMDevice 介面。 接下來,IMMDevice::Activate 方法呼叫會擷取子單位的 IKsControl 介面。 如需 IKsControl 介面的詳細資訊,請參閱 Windows DDK 檔。

接下來,上述程式代碼範例會 建立描述bass-boost屬性的 KSNODEPROPERTY_AUDIO_CHANNEL結構。 程序代碼範例會將結構的指標傳遞至 IKsControl::KsProperty 方法,該方法會使用 結構中的資訊來擷取屬性值。 如需KSNODEPROPERTY_AUDIO_CHANNEL結構和 IKsControl::KsProperty 方法的詳細資訊,請參閱 Windows DDK 檔。

音訊硬體通常會將個別的低音提升狀態指派給音訊串流中的每個通道。 原則上,某些通道可以啟用低音提升,並停用其他通道。 KSNODEPROPERTY_AUDIO_CHANNEL 結構包含指定通道號碼的 Channel 成員。 如果數據流包含 N 個通道,則通道會從 0 編號為 N— 1。 上述程式代碼範例只會取得通道 0 的 bass-boost 屬性值。 此實作會隱含假設所有通道的Bass-boost屬性會統一設定為相同的狀態。 因此,讀取通道 0 的 bass-boost 屬性就足以判斷數據流的 bass-boost 屬性值。 為了符合此假設,設定bass-boost屬性的對應函式會將所有通道設定為相同的低音提升屬性值。 這些是合理的慣例,但並非所有硬體廠商都一定遵循這些慣例。 例如,廠商可能會提供控制面板應用程式,只針對驅動全範圍喇叭的通道啟用低音提升功能。 (全範圍喇叭能夠播放從低音到高音的全範圍音效。在此情況下,上述程式代碼範例所擷取的屬性值可能無法準確地表示數據流的低音提升狀態。

使用上表所列控件介面的用戶端可以呼叫 IPart::RegisterControlChangeCallback 方法,以在控件參數的值變更時註冊通知。 請注意, 當屬性值變更時,IKsControl 介面不會為用戶端提供類似的方式來註冊通知。 如果 IKsControl 確實支援屬性變更通知,當另一個應用程式變更屬性值時,它可能會通知應用程式。 不過,相較於透過 IPart 通知監視的較常用控件,IKsControl管理的屬性主要是供硬體廠商使用,而且這些屬性可能只由廠商撰寫的控制面板應用程式變更。 如果應用程式必須偵測另一個應用程式所做的屬性變更,則可以透過 IKsControl 定期輪詢屬性值來偵測變更。

程式設計指南