From 754dac61031bf38cfad79f80408278bb09cef66c Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 2 Mar 2023 22:57:00 +0100 Subject: [PATCH 1/4] Copy alext.h from OpenAL Soft 1.23 --- apps/openmw/mwsound/alext.h | 237 ++++++++++++++++++++++++++++++------ 1 file changed, 200 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwsound/alext.h b/apps/openmw/mwsound/alext.h index 9999665416..f30cbbdc63 100644 --- a/apps/openmw/mwsound/alext.h +++ b/apps/openmw/mwsound/alext.h @@ -1,38 +1,20 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2008 by authors. - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * Or go to https://www.gnu.org/copyleft/lgpl.html - */ - #ifndef AL_ALEXT_H #define AL_ALEXT_H #include -/* Define int64_t and uint64_t types */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -#include -#elif defined(_WIN32) && defined(__GNUC__) +/* Define int64 and uint64 types */ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined(__cplusplus) && __cplusplus >= 201103L) #include +typedef int64_t _alsoft_int64_t; +typedef uint64_t _alsoft_uint64_t; #elif defined(_WIN32) -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; +typedef __int64 _alsoft_int64_t; +typedef unsigned __int64 _alsoft_uint64_t; #else /* Fallback if nothing above works */ -#include +#include +typedef int64_t _alsoft_int64_t; +typedef uint64_t _alsoft_uint64_t; #endif #include "al.h" @@ -98,6 +80,31 @@ extern "C" #ifndef AL_EXT_MCFORMATS #define AL_EXT_MCFORMATS 1 +/* Provides support for surround sound buffer formats with 8, 16, and 32-bit + * samples. + * + * QUAD8: Unsigned 8-bit, Quadraphonic (Front Left, Front Right, Rear Left, + * Rear Right). + * QUAD16: Signed 16-bit, Quadraphonic. + * QUAD32: 32-bit float, Quadraphonic. + * REAR8: Unsigned 8-bit, Rear Stereo (Rear Left, Rear Right). + * REAR16: Signed 16-bit, Rear Stereo. + * REAR32: 32-bit float, Rear Stereo. + * 51CHN8: Unsigned 8-bit, 5.1 Surround (Front Left, Front Right, Front Center, + * LFE, Side Left, Side Right). Note that some audio systems may label + * 5.1's Side channels as Rear or Surround; they are equivalent for the + * purposes of this extension. + * 51CHN16: Signed 16-bit, 5.1 Surround. + * 51CHN32: 32-bit float, 5.1 Surround. + * 61CHN8: Unsigned 8-bit, 6.1 Surround (Front Left, Front Right, Front Center, + * LFE, Rear Center, Side Left, Side Right). + * 61CHN16: Signed 16-bit, 6.1 Surround. + * 61CHN32: 32-bit float, 6.1 Surround. + * 71CHN8: Unsigned 8-bit, 7.1 Surround (Front Left, Front Right, Front Center, + * LFE, Rear Left, Rear Right, Side Left, Side Right). + * 71CHN16: Signed 16-bit, 7.1 Surround. + * 71CHN32: 32-bit float, 7.1 Surround. + */ #define AL_FORMAT_QUAD8 0x1204 #define AL_FORMAT_QUAD16 0x1205 #define AL_FORMAT_QUAD32 0x1206 @@ -134,9 +141,9 @@ extern "C" #ifndef AL_EXT_STATIC_BUFFER #define AL_EXT_STATIC_BUFFER 1 - typedef ALvoid(AL_APIENTRY* PFNALBUFFERDATASTATICPROC)(const ALint, ALenum, ALvoid*, ALsizei, ALsizei); + typedef void(AL_APIENTRY* PFNALBUFFERDATASTATICPROC)(const ALint, ALenum, ALvoid*, ALsizei, ALsizei); #ifdef AL_ALEXT_PROTOTYPES - AL_API ALvoid AL_APIENTRY alBufferDataStatic( + AL_API void AL_APIENTRY alBufferDataStatic( const ALint buffer, ALenum format, ALvoid* data, ALsizei len, ALsizei freq); #endif #endif @@ -170,9 +177,9 @@ extern "C" #define AL_SOFT_buffer_sub_data 1 #define AL_BYTE_RW_OFFSETS_SOFT 0x1031 #define AL_SAMPLE_RW_OFFSETS_SOFT 0x1032 - typedef ALvoid(AL_APIENTRY* PFNALBUFFERSUBDATASOFTPROC)(ALuint, ALenum, const ALvoid*, ALsizei, ALsizei); + typedef void(AL_APIENTRY* PFNALBUFFERSUBDATASOFTPROC)(ALuint, ALenum, const ALvoid*, ALsizei, ALsizei); #ifdef AL_ALEXT_PROTOTYPES - AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT( + AL_API void AL_APIENTRY alBufferSubDataSOFT( ALuint buffer, ALenum format, const ALvoid* data, ALsizei offset, ALsizei length); #endif #endif @@ -326,8 +333,8 @@ extern "C" #define AL_SOFT_source_latency 1 #define AL_SAMPLE_OFFSET_LATENCY_SOFT 0x1200 #define AL_SEC_OFFSET_LATENCY_SOFT 0x1201 - typedef int64_t ALint64SOFT; - typedef uint64_t ALuint64SOFT; + typedef _alsoft_int64_t ALint64SOFT; + typedef _alsoft_uint64_t ALuint64SOFT; typedef void(AL_APIENTRY* LPALSOURCEDSOFT)(ALuint, ALenum, ALdouble); typedef void(AL_APIENTRY* LPALSOURCE3DSOFT)(ALuint, ALenum, ALdouble, ALdouble, ALdouble); typedef void(AL_APIENTRY* LPALSOURCEDVSOFT)(ALuint, ALenum, const ALdouble*); @@ -368,11 +375,11 @@ extern "C" #ifndef AL_SOFT_deferred_updates #define AL_SOFT_deferred_updates 1 #define AL_DEFERRED_UPDATES_SOFT 0xC002 - typedef ALvoid(AL_APIENTRY* LPALDEFERUPDATESSOFT)(void); - typedef ALvoid(AL_APIENTRY* LPALPROCESSUPDATESSOFT)(void); + typedef void(AL_APIENTRY* LPALDEFERUPDATESSOFT)(void); + typedef void(AL_APIENTRY* LPALPROCESSUPDATESSOFT)(void); #ifdef AL_ALEXT_PROTOTYPES - AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void); - AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void); + AL_API void AL_APIENTRY alDeferUpdatesSOFT(void); + AL_API void AL_APIENTRY alProcessUpdatesSOFT(void); #endif #endif @@ -407,6 +414,16 @@ extern "C" #ifndef AL_EXT_BFORMAT #define AL_EXT_BFORMAT 1 +/* Provides support for B-Format ambisonic buffers (first-order, FuMa scaling + * and layout). + * + * BFORMAT2D_8: Unsigned 8-bit, 3-channel non-periphonic (WXY). + * BFORMAT2D_16: Signed 16-bit, 3-channel non-periphonic (WXY). + * BFORMAT2D_FLOAT32: 32-bit float, 3-channel non-periphonic (WXY). + * BFORMAT3D_8: Unsigned 8-bit, 4-channel periphonic (WXYZ). + * BFORMAT3D_16: Signed 16-bit, 4-channel periphonic (WXYZ). + * BFORMAT3D_FLOAT32: 32-bit float, 4-channel periphonic (WXYZ). + */ #define AL_FORMAT_BFORMAT2D_8 0x20021 #define AL_FORMAT_BFORMAT2D_16 0x20022 #define AL_FORMAT_BFORMAT2D_FLOAT32 0x20023 @@ -471,6 +488,152 @@ extern "C" #define ALC_OUTPUT_LIMITER_SOFT 0x199A #endif +#ifndef ALC_SOFT_device_clock +#define ALC_SOFT_device_clock 1 + typedef _alsoft_int64_t ALCint64SOFT; + typedef _alsoft_uint64_t ALCuint64SOFT; +#define ALC_DEVICE_CLOCK_SOFT 0x1600 +#define ALC_DEVICE_LATENCY_SOFT 0x1601 +#define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602 +#define AL_SAMPLE_OFFSET_CLOCK_SOFT 0x1202 +#define AL_SEC_OFFSET_CLOCK_SOFT 0x1203 + typedef void(ALC_APIENTRY* LPALCGETINTEGER64VSOFT)( + ALCdevice* device, ALCenum pname, ALsizei size, ALCint64SOFT* values); +#ifdef AL_ALEXT_PROTOTYPES + ALC_API void ALC_APIENTRY alcGetInteger64vSOFT( + ALCdevice* device, ALCenum pname, ALsizei size, ALCint64SOFT* values); +#endif +#endif + +#ifndef AL_SOFT_direct_channels_remix +#define AL_SOFT_direct_channels_remix 1 +#define AL_DROP_UNMATCHED_SOFT 0x0001 +#define AL_REMIX_UNMATCHED_SOFT 0x0002 +#endif + +#ifndef AL_SOFT_bformat_ex +#define AL_SOFT_bformat_ex 1 +#define AL_AMBISONIC_LAYOUT_SOFT 0x1997 +#define AL_AMBISONIC_SCALING_SOFT 0x1998 + +/* Ambisonic layouts */ +#define AL_FUMA_SOFT 0x0000 +#define AL_ACN_SOFT 0x0001 + +/* Ambisonic scalings (normalization) */ +/*#define AL_FUMA_SOFT*/ +#define AL_SN3D_SOFT 0x0001 +#define AL_N3D_SOFT 0x0002 +#endif + +#ifndef ALC_SOFT_loopback_bformat +#define ALC_SOFT_loopback_bformat 1 +#define ALC_AMBISONIC_LAYOUT_SOFT 0x1997 +#define ALC_AMBISONIC_SCALING_SOFT 0x1998 +#define ALC_AMBISONIC_ORDER_SOFT 0x1999 +#define ALC_MAX_AMBISONIC_ORDER_SOFT 0x199B + +#define ALC_BFORMAT3D_SOFT 0x1507 + +/* Ambisonic layouts */ +#define ALC_FUMA_SOFT 0x0000 +#define ALC_ACN_SOFT 0x0001 + +/* Ambisonic scalings (normalization) */ +/*#define ALC_FUMA_SOFT*/ +#define ALC_SN3D_SOFT 0x0001 +#define ALC_N3D_SOFT 0x0002 +#endif + +#ifndef AL_SOFT_effect_target +#define AL_SOFT_effect_target +#define AL_EFFECTSLOT_TARGET_SOFT 0x199C +#endif + +#ifndef AL_SOFT_events +#define AL_SOFT_events 1 +#define AL_EVENT_CALLBACK_FUNCTION_SOFT 0x19A2 +#define AL_EVENT_CALLBACK_USER_PARAM_SOFT 0x19A3 +#define AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT 0x19A4 +#define AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT 0x19A5 +#define AL_EVENT_TYPE_DISCONNECTED_SOFT 0x19A6 + typedef void(AL_APIENTRY* ALEVENTPROCSOFT)( + ALenum eventType, ALuint object, ALuint param, ALsizei length, const ALchar* message, void* userParam); + typedef void(AL_APIENTRY* LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum* types, ALboolean enable); + typedef void(AL_APIENTRY* LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void* userParam); + typedef void*(AL_APIENTRY* LPALGETPOINTERSOFT)(ALenum pname); + typedef void(AL_APIENTRY* LPALGETPOINTERVSOFT)(ALenum pname, void** values); +#ifdef AL_ALEXT_PROTOTYPES + AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum* types, ALboolean enable); + AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void* userParam); + AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname); + AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void** values); +#endif +#endif + +#ifndef ALC_SOFT_reopen_device +#define ALC_SOFT_reopen_device + typedef ALCboolean(ALC_APIENTRY* LPALCREOPENDEVICESOFT)( + ALCdevice* device, const ALCchar* deviceName, const ALCint* attribs); +#ifdef AL_ALEXT_PROTOTYPES + ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice* device, const ALCchar* deviceName, const ALCint* attribs); +#endif +#endif + +#ifndef AL_SOFT_callback_buffer +#define AL_SOFT_callback_buffer +#define AL_BUFFER_CALLBACK_FUNCTION_SOFT 0x19A0 +#define AL_BUFFER_CALLBACK_USER_PARAM_SOFT 0x19A1 + typedef ALsizei(AL_APIENTRY* ALBUFFERCALLBACKTYPESOFT)(ALvoid* userptr, ALvoid* sampledata, ALsizei numbytes); + typedef void(AL_APIENTRY* LPALBUFFERCALLBACKSOFT)( + ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid* userptr); + typedef void(AL_APIENTRY* LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid** value); + typedef void(AL_APIENTRY* LPALGETBUFFER3PTRSOFT)( + ALuint buffer, ALenum param, ALvoid** value1, ALvoid** value2, ALvoid** value3); + typedef void(AL_APIENTRY* LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid** values); +#ifdef AL_ALEXT_PROTOTYPES + AL_API void AL_APIENTRY alBufferCallbackSOFT( + ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid* userptr); + AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid** ptr); + AL_API void AL_APIENTRY alGetBuffer3PtrSOFT( + ALuint buffer, ALenum param, ALvoid** ptr0, ALvoid** ptr1, ALvoid** ptr2); + AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid** ptr); +#endif +#endif + +#ifndef AL_SOFT_UHJ +#define AL_SOFT_UHJ +#define AL_FORMAT_UHJ2CHN8_SOFT 0x19A2 +#define AL_FORMAT_UHJ2CHN16_SOFT 0x19A3 +#define AL_FORMAT_UHJ2CHN_FLOAT32_SOFT 0x19A4 +#define AL_FORMAT_UHJ3CHN8_SOFT 0x19A5 +#define AL_FORMAT_UHJ3CHN16_SOFT 0x19A6 +#define AL_FORMAT_UHJ3CHN_FLOAT32_SOFT 0x19A7 +#define AL_FORMAT_UHJ4CHN8_SOFT 0x19A8 +#define AL_FORMAT_UHJ4CHN16_SOFT 0x19A9 +#define AL_FORMAT_UHJ4CHN_FLOAT32_SOFT 0x19AA + +#define AL_STEREO_MODE_SOFT 0x19B0 +#define AL_NORMAL_SOFT 0x0000 +#define AL_SUPER_STEREO_SOFT 0x0001 +#define AL_SUPER_STEREO_WIDTH_SOFT 0x19B1 +#endif + +#ifndef ALC_SOFT_output_mode +#define ALC_SOFT_output_mode +#define ALC_OUTPUT_MODE_SOFT 0x19AC +#define ALC_ANY_SOFT 0x19AD +/*#define ALC_MONO_SOFT 0x1500*/ +/*#define ALC_STEREO_SOFT 0x1501*/ +#define ALC_STEREO_BASIC_SOFT 0x19AE +#define ALC_STEREO_UHJ_SOFT 0x19AF +#define ALC_STEREO_HRTF_SOFT 0x19B2 +/*#define ALC_QUAD_SOFT 0x1503*/ +#define ALC_SURROUND_5_1_SOFT 0x1504 +#define ALC_SURROUND_6_1_SOFT 0x1505 +#define ALC_SURROUND_7_1_SOFT 0x1506 +#endif + #ifdef __cplusplus } #endif From b762807dfb0f820987ace0124ac8f17373ba65c9 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 2 Mar 2023 22:58:07 +0100 Subject: [PATCH 2/4] 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) From e020af8b4ae6e206709f4cea9d0ed9e8635fbe63 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Fri, 3 Mar 2023 17:31:09 +0100 Subject: [PATCH 3/4] Switch to new default device if default device changes --- CHANGELOG.md | 1 + apps/openmw/mwsound/openal_output.cpp | 110 ++++++++++++++++++++++---- apps/openmw/mwsound/openal_output.hpp | 3 + 3 files changed, 100 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 050767312e..fcb25447f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Bug #3842: Body part skeletons override the main skeleton Bug #4127: Weapon animation looks choppy Bug #4204: Dead slaughterfish doesn't float to water surface after loading saved game + Bug #4382: Sound output device does not change when it should Bug #4610: Casting a Bound Weapon spell cancels the casting animation by equipping the weapon prematurely Bug #4816: GetWeaponDrawn returns 1 before weapon is attached Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 48d473487d..b130dc5d6b 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -165,6 +166,17 @@ namespace getALError(); } + std::basic_string_view getDeviceName(ALCdevice* device) + { + const ALCchar* name = nullptr; + if (alcIsExtensionPresent(device, "ALC_ENUMERATE_ALL_EXT")) + name = alcGetString(device, ALC_ALL_DEVICES_SPECIFIER); + if (alcGetError(device) != AL_NO_ERROR || !name) + name = alcGetString(device, ALC_DEVICE_SPECIFIER); + if (name == nullptr) // Prevent assigning nullptr to std::string + return {}; + return name; + } } namespace MWSound @@ -314,8 +326,7 @@ namespace MWSound // struct OpenAL_Output::StreamThread { - typedef std::vector StreamVec; - StreamVec mStreams; + std::vector mStreams; std::atomic mQuitNow; std::mutex mMutex; @@ -342,7 +353,7 @@ namespace MWSound std::unique_lock lock(mMutex); while (!mQuitNow) { - StreamVec::iterator iter = mStreams.begin(); + auto iter = mStreams.begin(); while (iter != mStreams.end()) { if ((*iter)->process() == false) @@ -368,7 +379,7 @@ namespace MWSound void remove(OpenAL_SoundStream* stream) { std::lock_guard lock(mMutex); - StreamVec::iterator iter = std::find(mStreams.begin(), mStreams.end(), stream); + auto iter = std::find(mStreams.begin(), mStreams.end(), stream); if (iter != mStreams.end()) mStreams.erase(iter); } @@ -379,9 +390,69 @@ namespace MWSound mStreams.clear(); } + StreamThread(const StreamThread& rhs) = delete; + StreamThread& operator=(const StreamThread& rhs) = delete; + }; + + class OpenAL_Output::DefaultDeviceThread + { + public: + std::basic_string mCurrentName; + private: - StreamThread(const StreamThread& rhs); - StreamThread& operator=(const StreamThread& rhs); + OpenAL_Output& mOutput; + + std::atomic mQuitNow; + std::mutex mMutex; + std::condition_variable mCondVar; + std::thread mThread; + + DefaultDeviceThread(const DefaultDeviceThread&) = delete; + DefaultDeviceThread& operator=(const DefaultDeviceThread&) = delete; + + void run() + { + std::unique_lock lock(mMutex); + while (!mQuitNow) + { + { + const std::lock_guard openLock(mOutput.mReopenMutex); + auto defaultName = getDeviceName(nullptr); + if (mCurrentName != defaultName) + { + Log(Debug::Info) << "Default audio device changed"; + ALCboolean reopened + = alcReopenDeviceSOFT(mOutput.mDevice, nullptr, mOutput.mContextAttributes.data()); + if (reopened == AL_FALSE) + { + mCurrentName = defaultName; + Log(Debug::Warning) << "Failed to switch to new audio device"; + } + else + mCurrentName = getDeviceName(mOutput.mDevice); + } + } + mCondVar.wait_for(lock, std::chrono::seconds(2)); + } + } + + public: + DefaultDeviceThread(OpenAL_Output& output, std::basic_string_view name) + : mCurrentName(name) + , mOutput(output) + , mQuitNow(false) + , mThread([this] { run(); }) + { + } + + ~DefaultDeviceThread() + { + mQuitNow = true; + mMutex.lock(); + mMutex.unlock(); + mCondVar.notify_all(); + mThread.join(); + } }; OpenAL_SoundStream::OpenAL_SoundStream(ALuint src, DecoderPtr decoder) @@ -614,7 +685,7 @@ namespace MWSound void OpenAL_Output::onDisconnect() { - if (!mInitialized) + if (!mInitialized || !alcReopenDeviceSOFT) return; const std::lock_guard lock(mReopenMutex); Log(Debug::Warning) << "Audio device disconnected, attempting to reopen..."; @@ -623,6 +694,12 @@ namespace MWSound reopened = alcReopenDeviceSOFT(mDevice, nullptr, mContextAttributes.data()); if (reopened == AL_FALSE) Log(Debug::Error) << "Failed to reopen audio device"; + else + { + Log(Debug::Info) << "Reopened audio device"; + if (mDefaultDeviceThread) + mDefaultDeviceThread->mCurrentName = getDeviceName(mDevice); + } } bool OpenAL_Output::init(const std::string& devname, const std::string& hrtfname, HrtfMode hrtfmode) @@ -647,11 +724,7 @@ namespace MWSound return false; } - const ALCchar* name = nullptr; - if (alcIsExtensionPresent(mDevice, "ALC_ENUMERATE_ALL_EXT")) - name = alcGetString(mDevice, ALC_ALL_DEVICES_SPECIFIER); - if (alcGetError(mDevice) != AL_NO_ERROR || !name) - name = alcGetString(mDevice, ALC_DEVICE_SPECIFIER); + auto name = getDeviceName(mDevice); Log(Debug::Info) << "Opened \"" << name << "\""; ALCint major = 0, minor = 0; @@ -722,9 +795,9 @@ namespace MWSound { getALFunc(alEventControlSOFT, "alEventControlSOFT"); getALFunc(alEventCallbackSOFT, "alEventCallbackSOFT"); - if (alcIsExtensionPresent(mDevice, "ALC_SOFT_reopen_device")) - getALFunc(alcReopenDeviceSOFT, "alcReopenDeviceSOFT"); } + if (alcIsExtensionPresent(mDevice, "ALC_SOFT_reopen_device")) + getALFunc(alcReopenDeviceSOFT, "alcReopenDeviceSOFT"); if (alEventControlSOFT) { static const std::array events{ { AL_EVENT_TYPE_DISCONNECTED_SOFT } }; @@ -733,6 +806,14 @@ namespace MWSound } else Log(Debug::Warning) << "Cannot detect audio device changes"; + if (mDeviceName.empty() && !name.empty()) + { + // If we opened the default device, switch devices if a new default is selected + if (alcReopenDeviceSOFT) + mDefaultDeviceThread = std::make_unique(*this, name); + else + Log(Debug::Warning) << "Cannot switch audio devices if the default changes"; + } if (!ALC.SOFT_HRTF) Log(Debug::Warning) << "HRTF status unavailable"; @@ -892,6 +973,7 @@ namespace MWSound void OpenAL_Output::deinit() { mStreamThread->removeAll(); + mDefaultDeviceThread.release(); for (ALuint source : mFreeSources) alDeleteSources(1, &source); diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 79ceaf6ae6..eed23ac659 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -58,6 +58,9 @@ namespace MWSound std::vector mContextAttributes; std::mutex mReopenMutex; + class DefaultDeviceThread; + std::unique_ptr mDefaultDeviceThread; + 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); From 027d49cf28306e06efc876cccf0770371d53bc5b Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Fri, 3 Mar 2023 18:00:02 +0100 Subject: [PATCH 4/4] Set thread priority and start thread on fallback usage --- apps/openmw/mwsound/openal_output.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index b130dc5d6b..773f877ef3 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "loudness.hpp" @@ -412,6 +413,7 @@ namespace MWSound void run() { + Misc::setCurrentThreadIdlePriority(); std::unique_lock lock(mMutex); while (!mQuitNow) { @@ -437,7 +439,7 @@ namespace MWSound } public: - DefaultDeviceThread(OpenAL_Output& output, std::basic_string_view name) + DefaultDeviceThread(OpenAL_Output& output, std::basic_string_view name = {}) : mCurrentName(name) , mOutput(output) , mQuitNow(false) @@ -691,7 +693,11 @@ namespace MWSound 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_TRUE && !mDefaultDeviceThread) + mDefaultDeviceThread = std::make_unique(*this); + } if (reopened == AL_FALSE) Log(Debug::Error) << "Failed to reopen audio device"; else