简体   繁体   English

如何检测默认音频 output 设备更改 Windows

[英]How to detect default audio output device change in Windows

I am developing an audio recording program in C++ (I use OpenAL and ImGui with OpenGL if that helps) and i want to know if i can detect if my default audio output device changes without running a loop that blocks my program.我正在 C++ 中开发一个录音程序(如果有帮助,我将 OpenAL 和 ImGui 与 OpenGL 一起使用),我想知道我是否可以检测到我的默认音频 output 设备是否发生变化,而无需运行阻止我的程序的循环。 Is there a way for me to detect it like with callbacks maybe?有没有办法像回调一样检测它?

I tried using我尝试使用

alcGetString(device, ALC_DEFAULT_DEVICE_SPECIFIER);

function to get the name of the default device and compare it with last one i used in a loop on another thread. function 获取默认设备的名称并将其与我在另一个线程的循环中使用的最后一个进行比较。 It did the job but it cost me lots of performance.它完成了工作,但它让我损失了很多性能。

Thanks to @PaulSanders i have found a solution.感谢@PaulSanders,我找到了解决方案。 It is not what i was looking for but i think it is still a good one.这不是我想要的,但我认为它仍然是一个好东西。 Here is the code:这是代码:

#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 inherits IMMNotificationClient so i can override its functions for custom handling its events. NotificationClient class 继承IMMNotificationClient ,所以我可以重写它的函数来自定义处理它的事件。 This code generates a thread to check if default device is changed, AudioDeviceNotificationListener::Start will start the thread and AudioDeviceNotificationListener::Close will terminate it.此代码生成一个线程来检查默认设备是否已更改, AudioDeviceNotificationListener::Start将启动该线程,而AudioDeviceNotificationListener::Close将终止它。 I am not happy with TerminateThread function but it didn't create a problem for me so i will use it for now.我对TerminateThread function 不满意,但它没有给我带来问题,所以我现在会使用它。 This thread checks for a device change message and if it doesn't receive any message the thread is suspended until a message is received so this solves busy-looping which has caused my problem of performance.此线程检查设备更改消息,如果它没有收到任何消息,则线程将暂停,直到收到消息,这样就解决了导致我的性能问题的忙循环。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM