🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

XAudio2_7 don't raise onCriticalError when headphones unplugged and app may hangs

Started by
0 comments, last by poljak181 4 years, 2 months ago
#include <iostream>
#include <wrl.h>
#include <mfapi.h>
#include <XAudio2.h>
#pragma comment(lib, "mfplat.lib")

struct MyXAudio2EngineCallback : public IXAudio2EngineCallback {
    void STDMETHODCALLTYPE OnProcessingPassEnd() override {}
    void STDMETHODCALLTYPE OnProcessingPassStart() override {}
    void STDMETHODCALLTYPE OnCriticalError(HRESULT Error) {
        std::cerr << "OnCriticalError finished\n";
    }
};

class AudioEngine {
public:
    AudioEngine() {
        auto mDLL = LoadLibraryEx(L"XAudio2_7.DLL", nullptr, 0x00000800 /* LOAD_LIBRARY_SEARCH_SYSTEM32 */);
        if (!mDLL) {
            std::cerr << "Error: XAudio 2.7 not installed on system (June 2010)\n";
        }
        auto hr = MFStartup(MF_VERSION);
        if (FAILED(hr)) {
            std::cerr << "Error: Unable to start the Windows Media Foundation!\n";
        }

        engineCallback = std::make_unique<MyXAudio2EngineCallback>();
        createEngine();
    }

    bool recreateEngine() {
        destroyEngine();
        return createEngine();
    }

private:
    bool createEngine() {
        auto hr = XAudio2Create(dev.ReleaseAndGetAddressOf(), 0, XAUDIO2_DEFAULT_PROCESSOR);
        if (FAILED(hr)) {
            std::cerr << "Error: Unable to create the XAudio2 engine!\n";
            return false;
        }
        hr = dev->CreateMasteringVoice(&amp;masterVoice, 0U, 0U, 0U, 0);
        if (FAILED(hr)) {
            std::cerr << "Error: Unable to create the XAudio2 mastering voice!\n";
            dev.Reset();
            return false;
        }
        dev->RegisterForCallbacks(engineCallback.get());
        return true;
    }

    void destroyEngine() {
        if (dev) {
            dev->UnregisterForCallbacks(engineCallback.get());
            dev->StopEngine();
        }
        if (masterVoice) {
            masterVoice->DestroyVoice();
            masterVoice = nullptr;
        }
        dev.Reset();
    }

private:
    Microsoft::WRL::ComPtr<IXAudio2> dev;
    IXAudio2MasteringVoice* masterVoice = nullptr;
    std::unique_ptr<MyXAudio2EngineCallback> engineCallback;
};

int main() {
    auto* engine = new AudioEngine();
    while (true) {
        Sleep(300);
        engine->recreateEngine();
    }
    return 0;
}

We use XAudio2_7 lib in our project. I have to recreate audio engine every time I plug/unplug headphones. Sometimes when I unplug headphones OnCriticalError not raised. IMMNotificationClient gives me ability to detect that default audio device changed and I try to recreate engine. Calling of DestroyVoice for master or source voices sometimes hang app.

This is minimal working example where I can reproduce problem. Instead of using IMMNotificationClient (to detect changing of default device) I recreate engine every 300ms. When I start plug/unplug headphones sometimes app hangs in calling of dev->StopEngine(). This happens rarely.

This topic is closed to new replies.

Advertisement