potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++&Win32 API MMDevice API (Core Audio APIs)によるデバイス情報の取得

トップC++言語関係

Windows Vistaから導入されたMMDevice API(Core Audio APIs)を使用してデバイス情報を取得するサンプルです。WIL(NuGetのMicrosoft.Windows.ImplementationLibraryパッケージ)を使用した場合はコードにバージョンを記載しています。

マルチメディアデバイスのプロパティを列挙する

コンソールアプリケーションとして実行すると標準出力へマルチメディアデバイスのプロパティを列挙します。

#include <iostream>
#include <string>
#include <sstream>

#define STRICT
#define NOMINMAX
#include <Windows.h>
#pragma comment(lib, "propsys.lib")
#include <propvarutil.h>
#include <mmdeviceapi.h>

// Microsoft.Windows.ImplementationLibrary 1.0.210204.1
#include "wil/com.h"
#include "wil/resource.h"

// PROPERTYKEYを文字列へ変換します。
std::wstring FormatPROPERTYKEY(const PROPERTYKEY& key)
{
    wil::unique_cotaskmem_string guid_str;
    THROW_IF_FAILED(::StringFromCLSID(key.fmtid, guid_str.put()));

    std::wstringstream ss;
    ss << L"(" << guid_str.get() << L", " << key.pid << L")";
    return ss.str();
}

int main()
{
    // 標準出力の日本語設定
    std::wcout.imbue(std::locale("Japanese", std::locale::ctype));

    // COMの初期化と解放の準備
    auto coinit = wil::CoInitializeEx();

    // IMMDeviceEnumeratorの作成
    wil::com_ptr<IMMDeviceEnumerator> deviceEnumerator;
    THROW_IF_FAILED(::CoCreateInstance(__uuidof(MMDeviceEnumerator),
        nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(deviceEnumerator.put())));

    // IMMDeviceCollectionの作成とデバイス数の取得
    wil::com_ptr<IMMDeviceCollection> deviceCollection;
    THROW_IF_FAILED(deviceEnumerator->EnumAudioEndpoints(
        EDataFlow::eAll, ERole::eMultimedia, deviceCollection.put()));
    UINT count;
    THROW_IF_FAILED(deviceCollection->GetCount(&count));

    // デバイスの処理
    for (UINT i = 0; i < count; i++)
    {
        // 現在位置のデバイスの取得
        wil::com_ptr<IMMDevice> device;
        THROW_IF_FAILED(deviceCollection->Item(i, device.put()));

        // デバイスIDの取得
        wil::unique_cotaskmem_string deviceId;
        THROW_IF_FAILED(device->GetId(deviceId.put()));

        // デバイスのプロパティストアの取得とプロパティ数の取得
        wil::com_ptr<IPropertyStore> propStore;
        THROW_IF_FAILED(device->OpenPropertyStore(STGM_READ, propStore.put()));
        DWORD propCount;
        THROW_IF_FAILED(propStore->GetCount(&propCount));

        // プロパティの処理
        for (DWORD propIndex = 0; propIndex < propCount; propIndex++)
        {
            // プロパティのキーと値の取得
            PROPERTYKEY propKey;
            THROW_IF_FAILED(propStore->GetAt(propIndex, &propKey));
            wil::unique_prop_variant propValue;
            THROW_IF_FAILED(propStore->GetValue(propKey, propValue.reset_and_addressof()));

            // プロパティの文字列変換
            wil::unique_cotaskmem_string propValueString;
            THROW_IF_FAILED(::PropVariantToStringAlloc(propValue, propValueString.put()));

            // デバイスID、プロパティキー・値の出力
            std::wcout
                << deviceId.get() << L", "
                << FormatPROPERTYKEY(propKey) << L", "
                << propValueString.get() << std::endl;
        }
    }
}

マルチメディアデバイスのフレンドリー名を取得する

#include <iostream>
#include <string>
#include <sstream>

#define STRICT
#define NOMINMAX
#include <Windows.h>
#pragma comment(lib, "propsys.lib")
#include <propvarutil.h>
#include <mmdeviceapi.h>
#include <InitGUID.h>
#include <functiondiscoverykeys_devpkey.h>

// Microsoft.Windows.ImplementationLibrary 1.0.210204.1
#include "wil/com.h"
#include "wil/resource.h"

// PROPERTYKEYを文字列へ変換します。
std::wstring FormatPROPERTYKEY(const PROPERTYKEY& key)
{
    wil::unique_cotaskmem_string guid_str;
    THROW_IF_FAILED(::StringFromCLSID(key.fmtid, guid_str.put()));

    std::wstringstream ss;
    ss << L"(" << guid_str.get() << L", " << key.pid << L")";
    return ss.str();
}

int main()
{
    // 標準出力の日本語設定
    std::wcout.imbue(std::locale("Japanese", std::locale::ctype));

    // COMの初期化と解放の準備
    auto coinit = wil::CoInitializeEx();

    // IMMDeviceEnumeratorの作成
    wil::com_ptr<IMMDeviceEnumerator> deviceEnumerator;
    THROW_IF_FAILED(::CoCreateInstance(__uuidof(MMDeviceEnumerator),
        nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(deviceEnumerator.put())));

    // IMMDeviceCollectionの作成とデバイス数の取得
    wil::com_ptr<IMMDeviceCollection> deviceCollection;
    THROW_IF_FAILED(deviceEnumerator->EnumAudioEndpoints(
        EDataFlow::eAll, ERole::eMultimedia, deviceCollection.put()));
    UINT count;
    THROW_IF_FAILED(deviceCollection->GetCount(&count));

    // デバイスの処理
    for (UINT i = 0; i < count; i++)
    {
        // 現在位置のデバイスの取得
        wil::com_ptr<IMMDevice> device;
        THROW_IF_FAILED(deviceCollection->Item(i, device.put()));

        // デバイスIDの取得
        wil::unique_cotaskmem_string deviceId;
        THROW_IF_FAILED(device->GetId(deviceId.put()));

        // デバイスのプロパティストアの取得
        wil::com_ptr<IPropertyStore> propStore;
        THROW_IF_FAILED(device->OpenPropertyStore(STGM_READ, propStore.put()));

        // デバイスのフレンドリー名の取得
        wil::unique_prop_variant propValue;
        propStore->GetValue(PKEY_Device_FriendlyName, &propValue);
        wil::unique_cotaskmem_string propValueString;
        THROW_IF_FAILED(::PropVariantToStringAlloc(propValue, propValueString.put()));

        // デバイスID、フレンドリー名の出力
        std::wcout
            << deviceId.get() << L", "
            << propValueString.get() << std::endl;
    }
}

マルチメディアデバイスの音量情報を取得する

#include <iostream>
#include <string>
#include <sstream>

#define STRICT
#define NOMINMAX
#include <Windows.h>
#pragma comment(lib, "propsys.lib")
#include <propvarutil.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>

// Microsoft.Windows.ImplementationLibrary 1.0.210204.1
#include "wil/com.h"
#include "wil/resource.h"

int main()
{
    // 標準出力の日本語設定
    std::wcout.imbue(std::locale("Japanese", std::locale::ctype));

    // COMの初期化と解放の準備
    auto coinit = wil::CoInitializeEx();

    // IMMDeviceEnumeratorの作成
    wil::com_ptr<IMMDeviceEnumerator> deviceEnumerator;
    THROW_IF_FAILED(::CoCreateInstance(__uuidof(MMDeviceEnumerator),
        nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(deviceEnumerator.put())));

    // IMMDeviceCollectionの作成とデバイス数の取得
    wil::com_ptr<IMMDeviceCollection> deviceCollection;
    THROW_IF_FAILED(deviceEnumerator->EnumAudioEndpoints(
        EDataFlow::eAll, ERole::eMultimedia, deviceCollection.put()));
    UINT count;
    THROW_IF_FAILED(deviceCollection->GetCount(&count));

    // デバイスの処理
    std::wcout
        << L"Device ID,Mute, Min Volume db, Max Volume db, Increment Volume db, Master Volume Level db"
        << std::endl;
    for (UINT i = 0; i < count; i++)
    {
        // 現在位置のデバイスの取得
        wil::com_ptr<IMMDevice> device;
        THROW_IF_FAILED(deviceCollection->Item(i, device.put()));

        // デバイスIDの取得
        wil::unique_cotaskmem_string deviceId;
        THROW_IF_FAILED(device->GetId(deviceId.put()));

        wil::com_ptr<IAudioEndpointVolume> audioEndpointVolume;
        THROW_IF_FAILED(device->Activate(__uuidof(IAudioEndpointVolume),
            CLSCTX_INPROC_SERVER, nullptr, audioEndpointVolume.put_void()));

        BOOL mute;
        float minDb, maxDb, incDb;
        float levelDb;
        THROW_IF_FAILED(audioEndpointVolume->GetMute(&mute));
        THROW_IF_FAILED(audioEndpointVolume->GetVolumeRange(&minDb, &maxDb, &incDb));
        THROW_IF_FAILED(audioEndpointVolume->GetMasterVolumeLevel(&levelDb));

        std::wcout
            << deviceId.get() << L", "
            << mute << L", "
            << minDb << L", "
            << maxDb << L", "
            << incDb << L", "
            << levelDb << std::endl;
    }
}