[英]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.