XAudio2_7 не вызывает onCriticalError, когда наушники отключены и приложение может зависнуть - PullRequest
0 голосов
/ 17 апреля 2020
#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(&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;
}

Мы используем XAudio2_7 lib в нашем проекте. Я должен воссоздавать аудио движок каждый раз, когда я подключаю / отключаю наушники. Иногда при отключении наушников OnCriticalError не поднимается. IMMNotificationClient дает мне возможность обнаружить, что аудиоустройство по умолчанию изменено, и я пытаюсь воссоздать движок. Вызов DestroyVoice для основного или исходного голоса иногда приводит к зависанию приложения.

Это минимальный рабочий пример, в котором я могу воспроизвести проблему. Вместо использования IMMNotificationClient (для обнаружения изменения устройства по умолчанию) я воссоздаю движок каждые 300 мс. Когда я начинаю подключать / отключать наушники, иногда приложение зависает при вызове dev-> StopEngine (). Такое случается редко.

...