[英]How to detect default audio output device change in Windows
我正在 C++ 中開發一個錄音程序(如果有幫助,我將 OpenAL 和 ImGui 與 OpenGL 一起使用),我想知道我是否可以檢測到我的默認音頻 output 設備是否發生變化,而無需運行阻止我的程序的循環。 有沒有辦法像回調一樣檢測它?
我嘗試使用
alcGetString(device, ALC_DEFAULT_DEVICE_SPECIFIER);
function 獲取默認設備的名稱並將其與我在另一個線程的循環中使用的最后一個進行比較。 它完成了工作,但它讓我損失了很多性能。
感謝@PaulSanders,我找到了解決方案。 這不是我想要的,但我認為它仍然是一個好東西。 這是代碼:
#include <Windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
// The notification client class
class NotificationClient : public IMMNotificationClient {
public:
// IUnknown methods
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) {
if (riid == IID_IUnknown || riid == __uuidof(IMMNotificationClient)) {
*ppvObject = this;
AddRef();
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef() { return InterlockedIncrement(&m_cRef); }
ULONG STDMETHODCALLTYPE Release() {
ULONG ulRef = InterlockedDecrement(&m_cRef);
if (ulRef == 0)
delete this;
return ulRef;
}
// IMMNotificationClient methods
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) {
// The default audio output device has changed
// Handle the device change event
// ...
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { return S_OK; }
// Constructor and destructor
NotificationClient() : m_cRef(1) {}
~NotificationClient() {}
private:
long m_cRef;
};
class AudioDeviceNotificationListener
{
public:
AudioDeviceNotificationListener() = default;
~AudioDeviceNotificationListener() { Close(); }
bool Start()
{
// Initialize the COM library for the current thread
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) {
return false;
}
// Create the device enumerator
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
if (FAILED(hr)) {
CoUninitialize();
return false;
}
// Create the notification client object
pNotificationClient = new NotificationClient();
// Register the notification client
hr = pEnumerator->RegisterEndpointNotificationCallback(pNotificationClient);
if (FAILED(hr)) {
pEnumerator->Release();
pNotificationClient->Release();
pNotificationClient = nullptr;
CoUninitialize();
return false;
}
// Create the notification thread
hNotificationThread = CreateThread(NULL, 0, &AudioDeviceNotificationListener::NotificationThreadProc, pNotificationClient, 0, NULL);
if (hNotificationThread == NULL) {
pEnumerator->UnregisterEndpointNotificationCallback(pNotificationClient);
pEnumerator->Release();
pNotificationClient->Release();
pNotificationClient = nullptr;
CoUninitialize();
return false;
}
bDidStart = true;
}
void Close()
{
if (bDidStart)
{
// Clean up
/* Not happy with TerminateThread but until another solution i will use this */
TerminateThread(hNotificationThread, 0);
pEnumerator->UnregisterEndpointNotificationCallback(pNotificationClient);
pEnumerator->Release();
pNotificationClient->Release();
pNotificationClient = nullptr;
CoUninitialize();
bDidStart = false;
}
}
private:
// Thread Function
static DWORD WINAPI NotificationThreadProc(LPVOID lpParameter)
{
// Run the message loop
MSG msg;
while (true) {
// Check for a message
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
// A message was received. Process it.
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
// No message was received. Suspend the thread until a message is received.
WaitMessage();
}
}
}
private:
bool bDidStart = false;
NotificationClient* pNotificationClient = nullptr;
IMMDeviceEnumerator* pEnumerator = NULL;
HANDLE hNotificationThread = NULL;
};
NotificationClient
class 繼承IMMNotificationClient
,所以我可以重寫它的函數來自定義處理它的事件。 此代碼生成一個線程來檢查默認設備是否已更改, AudioDeviceNotificationListener::Start
將啟動該線程,而AudioDeviceNotificationListener::Close
將終止它。 我對TerminateThread
function 不滿意,但它沒有給我帶來問題,所以我現在會使用它。 此線程檢查設備更改消息,如果它沒有收到任何消息,則線程將暫停,直到收到消息,這樣就解決了導致我的性能問題的忙循環。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.