From b762807dfb0f820987ace0124ac8f17373ba65c9 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 2 Mar 2023 22:58:07 +0100 Subject: [PATCH] Reopen audio device on disconnect --- apps/openmw/mwsound/openal_output.cpp | 67 +++++++++++++++++++++---- apps/openmw/mwsound/openal_output.hpp | 10 ++++ apps/openmw/mwsound/soundmanagerimp.cpp | 2 +- 3 files changed, 68 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index a1679d0501..48d473487d 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -112,6 +112,10 @@ namespace LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; + LPALEVENTCONTROLSOFT alEventControlSOFT; + LPALEVENTCALLBACKSOFT alEventCallbackSOFT; + LPALCREOPENDEVICESOFT alcReopenDeviceSOFT; + void LoadEffect(ALuint effect, const EFXEAXREVERBPROPERTIES& props) { ALint type = AL_NONE; @@ -601,17 +605,40 @@ namespace MWSound return devlist; } + void OpenAL_Output::eventCallback( + ALenum eventType, ALuint object, ALuint param, ALsizei length, const ALchar* message, void* userParam) + { + if (eventType == AL_EVENT_TYPE_DISCONNECTED_SOFT) + static_cast(userParam)->onDisconnect(); + } + + void OpenAL_Output::onDisconnect() + { + if (!mInitialized) + return; + const std::lock_guard lock(mReopenMutex); + Log(Debug::Warning) << "Audio device disconnected, attempting to reopen..."; + ALCboolean reopened = alcReopenDeviceSOFT(mDevice, mDeviceName.c_str(), mContextAttributes.data()); + if (reopened == AL_FALSE && !mDeviceName.empty()) + reopened = alcReopenDeviceSOFT(mDevice, nullptr, mContextAttributes.data()); + if (reopened == AL_FALSE) + Log(Debug::Error) << "Failed to reopen audio device"; + } + bool OpenAL_Output::init(const std::string& devname, const std::string& hrtfname, HrtfMode hrtfmode) { + const std::lock_guard lock(mReopenMutex); deinit(); Log(Debug::Info) << "Initializing OpenAL..."; + mDeviceName = devname; mDevice = alcOpenDevice(devname.c_str()); if (!mDevice && !devname.empty()) { Log(Debug::Warning) << "Failed to open \"" << devname << "\", trying default"; mDevice = alcOpenDevice(nullptr); + mDeviceName.clear(); } if (!mDevice) @@ -636,17 +663,17 @@ namespace MWSound ALC.EXT_EFX = alcIsExtensionPresent(mDevice, "ALC_EXT_EFX"); ALC.SOFT_HRTF = alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF"); - std::vector attrs; - attrs.reserve(15); + mContextAttributes.clear(); + mContextAttributes.reserve(15); if (ALC.SOFT_HRTF) { LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr; getALCFunc(alcGetStringiSOFT, mDevice, "alcGetStringiSOFT"); - attrs.push_back(ALC_HRTF_SOFT); - attrs.push_back(hrtfmode == HrtfMode::Disable ? ALC_FALSE - : hrtfmode == HrtfMode::Enable ? ALC_TRUE - : + mContextAttributes.push_back(ALC_HRTF_SOFT); + mContextAttributes.push_back(hrtfmode == HrtfMode::Disable ? ALC_FALSE + : hrtfmode == HrtfMode::Enable ? ALC_TRUE + : /*hrtfmode == HrtfMode::Auto ?*/ ALC_DONT_CARE_SOFT); if (!hrtfname.empty()) { @@ -667,14 +694,14 @@ namespace MWSound Log(Debug::Warning) << "Failed to find HRTF \"" << hrtfname << "\", using default"; else { - attrs.push_back(ALC_HRTF_ID_SOFT); - attrs.push_back(index); + mContextAttributes.push_back(ALC_HRTF_ID_SOFT); + mContextAttributes.push_back(index); } } } - attrs.push_back(0); + mContextAttributes.push_back(0); - mContext = alcCreateContext(mDevice, attrs.data()); + mContext = alcCreateContext(mDevice, mContextAttributes.data()); if (!mContext || alcMakeContextCurrent(mContext) == ALC_FALSE) { Log(Debug::Error) << "Failed to setup audio context: " << alcGetString(mDevice, alcGetError(mDevice)); @@ -691,6 +718,22 @@ namespace MWSound << " Version: " << alGetString(AL_VERSION) << "\n" << " Extensions: " << alGetString(AL_EXTENSIONS); + if (alIsExtensionPresent("AL_SOFT_events")) + { + getALFunc(alEventControlSOFT, "alEventControlSOFT"); + getALFunc(alEventCallbackSOFT, "alEventCallbackSOFT"); + if (alcIsExtensionPresent(mDevice, "ALC_SOFT_reopen_device")) + getALFunc(alcReopenDeviceSOFT, "alcReopenDeviceSOFT"); + } + if (alEventControlSOFT) + { + static const std::array events{ { AL_EVENT_TYPE_DISCONNECTED_SOFT } }; + alEventControlSOFT(events.size(), events.data(), AL_TRUE); + alEventCallbackSOFT(&OpenAL_Output::eventCallback, this); + } + else + Log(Debug::Warning) << "Cannot detect audio device changes"; + if (!ALC.SOFT_HRTF) Log(Debug::Warning) << "HRTF status unavailable"; else @@ -867,6 +910,9 @@ namespace MWSound alDeleteFilters(1, &mWaterFilter); mWaterFilter = 0; + if (alEventCallbackSOFT) + alEventCallbackSOFT(nullptr, nullptr); + alcMakeContextCurrent(nullptr); if (mContext) alcDestroyContext(mContext); @@ -1526,6 +1572,7 @@ namespace MWSound OpenAL_Output::~OpenAL_Output() { + const std::lock_guard lock(mReopenMutex); OpenAL_Output::deinit(); } diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index b2d85f43a4..79ceaf6ae6 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -53,6 +54,10 @@ namespace MWSound struct StreamThread; std::unique_ptr mStreamThread; + std::string mDeviceName; + std::vector mContextAttributes; + std::mutex mReopenMutex; + void initCommon2D(ALuint source, const osg::Vec3f& pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv); void initCommon3D(ALuint source, const osg::Vec3f& pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv); @@ -65,6 +70,11 @@ namespace MWSound OpenAL_Output& operator=(const OpenAL_Output& rhs); OpenAL_Output(const OpenAL_Output& rhs); + static void eventCallback( + ALenum eventType, ALuint object, ALuint param, ALsizei length, const ALchar* message, void* userParam); + + void onDisconnect(); + public: std::vector enumerate() override; bool init(const std::string& devname, const std::string& hrtfname, HrtfMode hrtfmode) override; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 1a9680c7d2..2f7b38aad9 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -79,7 +79,7 @@ namespace MWSound SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound) : mVFS(vfs) - , mOutput(new OpenAL_Output(*this)) + , mOutput(std::make_unique(*this)) , mWaterSoundUpdater(makeWaterSoundUpdaterSettings()) , mSoundBuffers(*vfs, *mOutput) , mListenerUnderwater(false)