簡體   English   中英

如何檢測默認音頻 output 設備更改 Windows

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM