IKsControl インターフェイスを使用したオーディオ プロパティへのアクセス

まれに、特殊なオーディオ アプリケーションでは、DeviceTopology API または MMDevice API によって公開されていないオーディオ アダプターの特定のハードウェア機能にアクセスするために、IKsControl インターフェイスを使用する必要があります。 IKsControl インターフェイスを使用すると、カーネル ストリーミング (KS) デバイスのプロパティ、イベント、メソッドをユーザー モード アプリケーションで使用できるようになります。 オーディオ アプリケーションにとって最も重要なのは KS プロパティです。 IKsControl インターフェイスは、DeviceTopology API と MMDevice API と組み合わせて使用することで、オーディオ アダプターの KS プロパティにアクセスすることができます。

IKsControl インターフェイスは、主に、オーディオ ハードウェアを管理するためのコントロール パネル アプリケーションを記述するハードウェア ベンダーによる使用を目的としています。 IKsControl は、特定のハードウェア デバイスに関連付けられていない汎用オーディオ アプリケーションにはあまり役に立ちません。 この理由は、ハードウェア ベンダーは、デバイスのオーディオ プロパティにアクセスするための独自のメカニズムを頻繁に実装するためです。 ハードウェア固有のドライバーの癖を隠し、オーディオ プロパティにアクセスするための比較的統一されたインターフェイスを提供する DeviceTopology API とは対照的に、アプリケーションは IKsControl を使用してドライバーと直接通信します。 IKsControl の詳細については、Windows DDK のドキュメントを参照してください。

デバイス トポロジ」で説明したように、アダプター デバイスのトポロジのサブユニットは、次の表の左側の列に示す 1 つ以上の関数固有のコントロール インターフェイスをサポートする場合があります。 テーブルの右列の各エントリは、左側のコントロール インターフェイスに対応する 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 メソッドを呼び出すと、KSNODETYPE_TONE の GUID 値が取得されるとします。 このサブタイプ GUID は、サブユニットが次の KS プロパティの 1 つ以上をサポートしていることを示します。

  • KSPROPERTY_AUDIO_BASS
  • KSPROPERTY_AUDIO_MID
  • KSPROPERTY_AUDIO_TREBLE
  • KSPROPERTY_AUDIO_BASS_BOOST

この一覧の最初の 3 つのプロパティは、前の表に示したコントロール インターフェイスを介してアクセスできますが、KSPROPERTY_AUDIO_BASS_BOOST プロパティには DeviceTopology API に対応するコントロール インターフェイスがありません。 ただし、サブユニットがこのプロパティをサポートしている場合は、アプリケーションは IKsControl インターフェイスを使用してこのプロパティにアクセスできます。

サブタイプ KSNODETYPE_TONE のサブユニットの KSPROPERTY_AUDIO_BASS_BOOST プロパティにアクセスするには、次の手順が必要です。

  1. IConnector::GetDeviceIdConnectedTo メソッド、または IDeviceTopology::GetDeviceId メソッドを呼び出して、アダプター デバイスを識別するデバイス ID 文字列を取得します。 この文字列はエンドポイント ID 文字列に似ていますが、エンドポイント デバイスではなくアダプター デバイスを識別する点が異なります。 アダプター デバイスとエンドポイント デバイスの違いの詳細については、「オーディオ エンドポイント デバイス」を参照してください。
  2. デバイス ID 文字列を使用して IMMDeviceEnumerator::GetDevice メソッドを呼び出すことで、アダプター デバイスの IMMDevice インターフェイスを取得します。 この IMMDevice インターフェイスは、「IMMDevice インターフェイス」で説明されているインターフェイスと同じですが、エンドポイント デバイスではなくアダプター デバイスを表しています。
  3. パラメーター iidREFIID IID_IKsControl に設定して IMMDevice::Activate メソッドを呼び出すことによって、サブユニットの IKsControl インターフェイスを取得します。 この Activate メソッドでサポートされるインターフェイス (アダプター デバイス用) は、エンドポイント デバイス用の Activate メソッドでサポートされるインターフェイスとは異なる点に注意してください。 特に、アダプター デバイス用の Activate メソッドでは、IKsControl がサポートされています。
  4. IPart::GetLocalId メソッドを呼び出して、サブユニットのローカル ID を取得します。
  5. KS プロパティ要求を作成します。 要求に必要な KS ノード ID は、前の手順で取得したローカル ID の 16 ビットの最下位ビットに含まれています。
  6. IKsControl::KsProperty メソッドを呼び出して、KS プロパティ要求をオーディオ ドライバーに送信します。 このメソッドの詳細については、Windows DDK ドキュメントを参照してください。

次のコード例では、サブタイプ KSNODETYPE_TONE のサブユニットから KSPROPERTY_AUDIO_BASS_BOOST プロパティの値を取得します。

//-----------------------------------------------------------
// 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 関数は次の 3 つのパラメーターを受け取ります。

  • pEnumerator は、オーディオ エンドポイント列挙子の IMMDeviceEnumerator インターフェイスを指します。
  • pPart は、 KSNODETYPE_TONE のサブタイプを持つサブユニットの IPart インターフェイスを指します。
  • pbValue は、関数がプロパティ値を書き込む BOOL 変数を指します。

プログラムは、IPart::GetSubType を呼び出して、サブユニットのサブタイプが KSNODETYPE_TONE であることを判断した後にのみ、GetBassBoost を呼び出します。 低音ブーストが有効な場合、プロパティ値は TRUE です。 低音ブーストが無効になっている場合は、FALSE です。

GetBassBoost 関数の先頭で、IPart::GetTopologyObject メソッドを呼び出すと、KSNODETYPE_TONE サブユニットを含むアダプター デバイスの IDeviceTopology インターフェイスを取得します。 IDeviceTopology::GetDeviceId メソッドの呼び出しは、 アダプター デバイスを識別するデバイス ID 文字列を取得します。 IMMDeviceEnumerator::GetDevice メソッドの呼び出しは、入力パラメーターとしてデバイス ID 文字列を受け取り、アダプター デバイスの IMMDevice インターフェイスを取得します。 次に、IMMDevice::Activate メソッドの呼び出しによって、サブユニットの IKsControl インターフェイスを取得します。 IKsControl インターフェイスの詳細については、Windows DDK のドキュメントを参照してください。

次に、前のコード例では、低音ブースト プロパティを記述する KSNODEPROPERTY_AUDIO_CHANNEL 構造体が作成されます。 このコード例では、構造体へのポインターを IKsControl::KsProperty メソッドに渡します。このメソッドは、構造体内の情報を使用してプロパティ値を取得します。 KSNODEPROPERTY_AUDIO_CHANNEL 構造体と IKsControl::KsProperty メソッドの詳細については、Windows DDK のドキュメントを参照してください。

オーディオ ハードウェアは、通常、オーディオ ストリーム内の各チャネルに個別の低音ブースト状態を割り当てます。 原則として, 低音ブーストは、一部のチャンネルで有効にでき、それ以外のチャネルでは無効になります。 KSNODEPROPERTY_AUDIO_CHANNEL 構造体には、チャネル番号を指定する Channel メンバーが含まれています。 ストリームに N 個のチャネルが含まれている場合、チャネルには 0 から N— 1 の番号が付けられます。 前のコード例では、チャネル 0 のみの低音ブースト プロパティの値を取得します。 この実装では、すべてのチャネルの低音ブースト プロパティが同じ状態に一様に設定されていることを暗黙的に想定しています。 したがって、ストリームの低音ブースト プロパティ値を特定するには、チャネル 0 の低音ブースト プロパティを読み取るのみで十分です。 この仮定と整合させるために、低音ブースト プロパティを設定するための対応する関数は、すべてのチャネルを同じ低音ブースト プロパティ値に設定します。 これらは合理的な規則ですが、すべてのハードウェア ベンダーが必ずしも従っているわけではありません。 たとえば、ベンダーは、フルレンジ スピーカーを駆動するチャネルに対してのみ低音ブーストを有効にするコントロール パネル アプリケーションを提供する場合があります。 (フルレンジ スピーカーは、低音から高音まで、全範囲のサウンドを再生することができます。) その場合、前のコード例で取得したプロパティ値は、ストリームの低音ブースト状態を正確に表していない可能性があります。

前の表に示したコントロール インターフェイスを使用するクライアントは、IPart::RegisterControlChangeCallback メソッドを呼び出して、コントロール パラメーターの値が変更されたときの通知を登録できます。 IKsControl インターフェイスでは、プロパティ値が変更されたときの通知をクライアントが登録するための同様の手段は提供されないことに注意してください。 IKsControl がプロパティ変更通知をサポートしている場合、別のアプリケーションがプロパティ値を変更したときにアプリケーションに通知される可能性があります。 ただし、IPart 通知によって監視される一般的に使用されるコントロールとは異なり、IKsControl によって管理されるプロパティは、主にハードウェア ベンダーが使用することを目的としており、これらのプロパティはベンダーによって作成されたコントロール パネル アプリケーションによってのみ変更される可能性があります。 アプリケーションが別のアプリケーションによって行われたプロパティの変更を検出する必要がある場合は、IKsControl を介してプロパティ値を定期的にポーリングすることで、変更を検出することができます。

プログラミング ガイド