もくじ
https://tera1707.com/entry/2022/02/06/144447
やりたいこと
マイクやスピーカーの音量変更、ミュート状態が変更されたことを知りたい。
やり方
IAudioEndpointVolumeCallback
インターフェースクラスを実装したクラスをRegisterControlChangeNotify()
に渡してやる。
下記を参考にして実装。
サンプルコード
下記にコードを置いた。
https://github.com/tera1707/AudioDevice
IAudioEndpointVolumeCallback
インターフェースクラスを実装したクラス
#include <windows.h> #include <mmdeviceapi.h> #include <functiondiscoverykeys.h> #include <endpointvolume.h> #include <string> #include "CVolumeNotification.h" CVolumeNotification::CVolumeNotification(std::wstring targetEndpointName) : _cRef(1) { this->targetEndpointName = targetEndpointName; } CVolumeNotification::~CVolumeNotification() {} ULONG STDMETHODCALLTYPE CVolumeNotification::AddRef() { return InterlockedIncrement(&_cRef); } ULONG STDMETHODCALLTYPE CVolumeNotification::Release() { ULONG ulRef = InterlockedDecrement(&_cRef); if (0 == ulRef) { delete this; } return ulRef; } HRESULT STDMETHODCALLTYPE CVolumeNotification::QueryInterface(REFIID riid, VOID** ppvInterface) { if (IID_IUnknown == riid) { AddRef(); *ppvInterface = (IUnknown*)this; } else if (__uuidof(IAudioEndpointVolumeCallback) == riid) { AddRef(); *ppvInterface = (IAudioEndpointVolumeCallback*)this; } else { *ppvInterface = NULL; return E_NOINTERFACE; } return S_OK; } HRESULT STDMETHODCALLTYPE CVolumeNotification::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify) { if (!pNotify) { return E_INVALIDARG; } auto mute = pNotify->bMuted; auto volume = pNotify->fMasterVolume; OutputDebugString((targetEndpointName + L" mute:" + std::to_wstring(mute) + L" volume:" + std::to_wstring(volume) + L"\r\n").c_str()); return S_OK; }
IAudioEndpointVolumeCallback
インターフェースクラスを実装したクラスのヘッダー
#pragma once #include <windows.h> #include <mmdeviceapi.h> #include <functiondiscoverykeys.h> #include <endpointvolume.h> #include <string> class CVolumeNotification : public IAudioEndpointVolumeCallback { ULONG _cRef; std::wstring targetEndpointName; public: CVolumeNotification(std::wstring targetEndpointName); ~CVolumeNotification(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface); HRESULT STDMETHODCALLTYPE OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify); };
メイン
#include <windows.h> #include <mmdeviceapi.h> #include <functiondiscoverykeys.h> #include <endpointvolume.h> #include <string> #include "CMMNotificationClient.h" #include "CVolumeNotification.h" int main() { HRESULT hr; IMMDeviceEnumerator* pEnum = NULL; IMMDeviceCollection* pCollection = NULL; UINT deviceCount = 0; IMMDevice* pEndpoint[8] = { NULL }; IPropertyStore* pProperties[8] = { NULL }; IAudioEndpointVolume* pAudioEndVol[8] = { NULL }; CVolumeNotification* vn[8] = { NULL }; // COMの初期化(COMのお作法) hr = CoInitializeEx(0, COINIT_MULTITHREADED); // COMからMMDeviceEnumeratorを取ってくる hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, IID_PPV_ARGS(&pEnum)); auto nc = new CMMNotificationClient(pEnum); pEnum->RegisterEndpointNotificationCallback(nc); // オーディオエンドポイントの列挙を実行 hr = pEnum->EnumAudioEndpoints(EDataFlow::eCapture, DEVICE_STATE_ACTIVE, &pCollection); // とれた数を数える hr = pCollection->GetCount(&deviceCount); for (int i = 0; i < deviceCount; i++) { // デバイスの情報を取る pCollection->Item(i, &pEndpoint[i]); // デバイスのプロパティを取る pEndpoint[i]->OpenPropertyStore(STGM_READ, &pProperties[i]); // 取ったプロパティから、値を指定して取得する PROPVARIANT vName; PropVariantInit(&vName); pProperties[i]->GetValue(PKEY_Device_FriendlyName, &vName); // エンドポイントの情報を取る hr = pEndpoint[i]->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void**)&pAudioEndVol[i]); // 音量・ミュート状態の変更検知 vn[i] = new CVolumeNotification(vName.pwszVal); pAudioEndVol[i]->RegisterControlChangeNotify(vn[i]); // ★★ココが重要!!! //// エンドポイントに指示を出す(「IAudioEndpointVolume」のVolumeは、マイクの怨霊という意味の「ボリューム」とは別の意味っぽい) float getVolume = 0.0f; pAudioEndVol[i]->GetMasterVolumeLevelScalar(&getVolume); // 音量を取得 float setVolume = 0.25f; pAudioEndVol[i]->SetMasterVolumeLevelScalar(setVolume, &GUID_NULL); // 音量を設定 std::wstring outString = std::to_wstring(i) + L":" + vName.pwszVal + std::to_wstring(getVolume) + L"\r\n"; OutputDebugString(outString.c_str()); // 後処理系 if (pProperties) pProperties[i]->Release(); } while (1) { Sleep(100000); } // 後処理系 for (size_t i = 0; i < sizeof(pAudioEndVol) / sizeof(pAudioEndVol[0]); i++) { if (pAudioEndVol[i] != NULL) pAudioEndVol[i]->Release(); if (pEndpoint[i] != NULL) pEndpoint[i]->Release(); } if (pCollection) pCollection->Release(); if (pEnum) pEnum->Release(); // COMの後処理(COMのお作法) CoUninitialize(); }
参考
IAudioEndpointVolume::RegisterControlChangeNotify メソッド (endpointvolume.h)