#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;
}
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.