potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

C++11&Win API MMDevice APIを利用してマスター音量を操作する

Windows Vistaまではマスター音量(システム全体の音量)を変更するにはmixer~系のAPIが使われていました。しかし諸々の事情により、Windows VistaからはCOMベースのMMDevice API (Windows Multimedia Device API)が用いられているそうです。この投稿ではサンプルコードを淡々と列挙します。

以下のコードではこのMMDevice APIを使用するサンプルコードとしてWRLを用いてミュートの切り替えを行います。例外処理等は手を抜いています。

#include "stdafx.hpp"

using namespace::Microsoft::WRL;
using namespace::Microsoft::WRL::Details;

class ComInitializer
{
private:
    HRESULT m_hr;
public:
    ComInitializer() { m_hr = CoInitialize(nullptr); }
    ComInitializer(LPVOID pvReserved) { m_hr = CoInitialize(pvReserved); }
    ~ComInitializer() { CoUninitialize(); }
    operator HRESULT() const { return m_hr; }
};

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    ComInitializer initializer;
    if (FAILED(initializer))
        return -1;

    // デバイス列挙オブジェクトを取得する
    ComPtr<IMMDeviceEnumerator> deviceEnumerator;
    HRESULT hr = CoCreateInstance(
        __uuidof(MMDeviceEnumerator),
        nullptr,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&deviceEnumerator));
    if (FAILED(initializer))
        RaiseException(hr);

    // TODO:ここでデバイス列挙オブジェクトに対する処理を行います
    //

    // 既定のマルチメディア出力デバイス(スピーカー)を取得する
    ComPtr<IMMDevice> device;
    hr = deviceEnumerator->GetDefaultAudioEndpoint(
        EDataFlow::eRender,
        ERole::eMultimedia,
        &device);
    if (FAILED(initializer))
        RaiseException(hr);

    // TODO:ここで既定のマルチメディア出力デバイス(スピーカー)に対する処理を行います
    //

    // オーディオエンドポイントのボリュームオブジェクトを作成する
    ComPtr<IAudioEndpointVolume> audioEndpointVolume;
    hr = device->Activate(
        __uuidof(IAudioEndpointVolume),
        CLSCTX_INPROC_SERVER,
        nullptr,
        &audioEndpointVolume);
    if (FAILED(hr))
        RaiseException(hr);

    // TODO:ここでボリュームオブジェクトに対する処理を行います
    //

    // ミュート設定を反転する
    BOOL mute = FALSE;
    hr = audioEndpointVolume->GetMute(&mute);
    if (FAILED(hr))
        RaiseException(hr);
    hr = audioEndpointVolume->SetMute(!mute, nullptr);
    if (FAILED(hr))
        RaiseException(hr);

    return 0;
}

いくつかの変更を施すことでその他の情報も手軽に取得することができます。例えば既定の出力マルチメディアデバイスの表示名を取得するには以下のように書き換えます。

 // TODO:ここで既定のマルチメディア出力デバイス(スピーカー)に対する処理を行います
    //

    // 表示名の取得
    ComPtr<IPropertyStore> properties;
    device->OpenPropertyStore(STGM_READ, &properties);
    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd370812(v=vs.85).aspx
    PROPVARIANT propvar;
    PropVariantInit(&propvar);
    hr = properties->GetValue(PKEY_Device_FriendlyName, &propvar);
    if (FAILED(initializer))
        RaiseException(hr);
    LPWSTR friendlyName = nullptr;
    hr = PropVariantToStringAlloc(propvar, &friendlyName);
    PropVariantClear(&propvar);
    CoTaskMemFree(friendlyName);

また、以下のコードでボリュームの範囲と現在値を取得することができます。

 // TODO:ここでボリュームオブジェクトに対する処理を行います
    //

    // 音量と範囲の取得
    float volumeLevel = 0;
    float volumeMin = 0;
    float volumeMax = 0;
    float volumeInc = 0;
    hr = audioEndpointVolume->GetMasterVolumeLevel(&volumeLevel);
    if (FAILED(hr))
        RaiseException(hr);
    hr = audioEndpointVolume->GetVolumeRange(&volumeMin, &volumeMax, &volumeInc);
    if (FAILED(hr))
        RaiseException(hr);

参考

http://msdn.microsoft.com/en-us/library/windows/desktop/dd370793(v=vs.85).aspx