summaryrefslogtreecommitdiff
path: root/indra/llaudio
diff options
context:
space:
mode:
authornibbbl <63519528+nibbbl@users.noreply.github.com>2026-07-21 11:10:37 -0700
committerGitHub <noreply@github.com>2026-07-21 21:10:37 +0300
commit70609524f38038f8d65810dcc088d9ff594982cc (patch)
tree9024c33e9de99cb24e9db48a9acfc305e4b9bdf6 /indra/llaudio
parentf37f129ae10248731febb748c03c545bcee5d974 (diff)
#6022 Detect OS default audio device changes
Diffstat (limited to 'indra/llaudio')
-rw-r--r--indra/llaudio/llaudioengine_openal.cpp104
-rw-r--r--indra/llaudio/llaudioengine_openal.h19
2 files changed, 122 insertions, 1 deletions
diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp
index 755547bfaa..4126c8ef80 100644
--- a/indra/llaudio/llaudioengine_openal.cpp
+++ b/indra/llaudio/llaudioengine_openal.cpp
@@ -42,7 +42,14 @@ LLAudioEngine_OpenAL::LLAudioEngine_OpenAL()
mWindBufSamples(0),
mWindBufBytes(0),
mWindSource(AL_NONE),
- mNumEmptyWindALBuffers(MAX_NUM_WIND_BUFFERS)
+ mNumEmptyWindALBuffers(MAX_NUM_WIND_BUFFERS),
+ mALDevice(NULL),
+ mHasReopenExt(false),
+ mReopenDeviceSOFT(NULL),
+ mHasSystemEventsExt(false),
+ mEventControlSOFT(NULL),
+ mEventCallbackSOFT(NULL),
+ mDefaultDeviceChanged(false)
{
}
@@ -87,6 +94,38 @@ bool LLAudioEngine_OpenAL::init(void* userdata, const std::string &app_title)
ALC_DEFAULT_DEVICE_SPECIFIER))
<< LL_ENDL;
+ mALDevice = device;
+
+ mHasReopenExt = alcIsExtensionPresent(mALDevice, "ALC_SOFT_reopen_device");
+ if (mHasReopenExt)
+ {
+ mReopenDeviceSOFT = (LPALCREOPENDEVICESOFT)alcGetProcAddress(mALDevice, "alcReopenDeviceSOFT");
+ mHasReopenExt = (mReopenDeviceSOFT != NULL);
+ }
+ LL_INFOS() << "ALC_SOFT_reopen_device " << (mHasReopenExt ? "supported" : "NOT supported") << LL_ENDL;
+
+ mHasSystemEventsExt = alcIsExtensionPresent(mALDevice, "ALC_SOFT_system_events");
+ if (mHasSystemEventsExt)
+ {
+ mEventControlSOFT = (LPALCEVENTCONTROLSOFT)alcGetProcAddress(mALDevice, "alcEventControlSOFT");
+ mEventCallbackSOFT = (LPALCEVENTCALLBACKSOFT)alcGetProcAddress(mALDevice, "alcEventCallbackSOFT");
+ mHasSystemEventsExt = (mEventControlSOFT != NULL && mEventCallbackSOFT != NULL);
+ }
+ LL_INFOS() << "ALC_SOFT_system_events " << (mHasSystemEventsExt ? "supported" : "NOT supported") << LL_ENDL;
+
+ if (mHasReopenExt && mHasSystemEventsExt)
+ {
+ mEventCallbackSOFT(&LLAudioEngine_OpenAL::onDeviceEventSOFT, this);
+ ALCenum events[] = { ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT };
+ mEventControlSOFT(1, events, ALC_TRUE);
+ LL_INFOS() << "Registered for ALC default-device-change notifications" << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS() << "Dynamic OS default output device following is not available; "
+ << "a viewer restart will be needed to pick up default device changes" << LL_ENDL;
+ }
+
return true;
}
@@ -130,9 +169,72 @@ void LLAudioEngine_OpenAL::allocateListener()
}
// virtual
+void LLAudioEngine_OpenAL::idle()
+{
+ LLAudioEngine::idle();
+
+ if (mDefaultDeviceChanged)
+ {
+ mDefaultDeviceChanged = false;
+ reopenOnDefaultDevice();
+ }
+}
+
+// static
+void ALC_APIENTRY LLAudioEngine_OpenAL::onDeviceEventSOFT(ALCenum eventType, ALCenum /*deviceType*/,
+ ALCdevice* /*device*/, ALCsizei /*length*/,
+ const ALCchar* /*message*/, void* userParam) noexcept
+{
+ // This may run on OpenAL's internal thread, so just flip the mDefaultDeviceChanged flag
+ // and handle device change in idle().
+ if (eventType == ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT)
+ {
+ LLAudioEngine_OpenAL* self = static_cast<LLAudioEngine_OpenAL*>(userParam);
+ if (self)
+ {
+ self->mDefaultDeviceChanged = true;
+ }
+ }
+}
+
+void LLAudioEngine_OpenAL::reopenOnDefaultDevice()
+{
+ if (!mHasReopenExt || !mReopenDeviceSOFT || !mALDevice)
+ {
+ return;
+ }
+
+ std::string old_name = ll_safe_string(alcGetString(mALDevice, ALC_DEVICE_SPECIFIER));
+ LL_INFOS() << "OS default audio device changed; reopening OpenAL device (was: "
+ << old_name << ")" << LL_ENDL;
+
+ // Clear any prior device error state so failures below report accurately.
+ (void)alcGetError(mALDevice);
+
+ if (mReopenDeviceSOFT(mALDevice, NULL, NULL))
+ {
+ LL_INFOS() << "OpenAL device reopened successfully, now on: "
+ << ll_safe_string(alcGetString(mALDevice, ALC_DEVICE_SPECIFIER)) << LL_ENDL;
+ }
+ else
+ {
+ ALCenum error = alcGetError(mALDevice);
+ LL_WARNS() << "alcReopenDeviceSOFT() failed: alc error " << error << LL_ENDL;
+ }
+}
+
+// virtual
void LLAudioEngine_OpenAL::shutdown()
{
LL_INFOS() << "About to LLAudioEngine::shutdown()" << LL_ENDL;
+
+ if (mHasSystemEventsExt && mEventControlSOFT && mEventCallbackSOFT)
+ {
+ ALCenum events[] = { ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT };
+ mEventControlSOFT(1, events, ALC_FALSE);
+ mEventCallbackSOFT(NULL, NULL);
+ }
+
LLAudioEngine::shutdown();
// If a subsequent error occurs while there is still an error recorded
diff --git a/indra/llaudio/llaudioengine_openal.h b/indra/llaudio/llaudioengine_openal.h
index 574bec416d..da1f376800 100644
--- a/indra/llaudio/llaudioengine_openal.h
+++ b/indra/llaudio/llaudioengine_openal.h
@@ -33,6 +33,7 @@
#include "llaudioengine.h"
#include "lllistener_openal.h"
#include "llwindgen.h"
+#include "llatomic.h"
class LLAudioEngine_OpenAL : public LLAudioEngine
{
@@ -46,6 +47,7 @@ class LLAudioEngine_OpenAL : public LLAudioEngine
virtual void allocateListener();
virtual void shutdown();
+ virtual void idle();
void setInternalGain(F32 gain);
@@ -68,6 +70,23 @@ class LLAudioEngine_OpenAL : public LLAudioEngine
static const int MAX_NUM_WIND_BUFFERS = 80;
static const float WIND_BUFFER_SIZE_SEC; // 1/20th sec
+
+ ALCdevice* mALDevice;
+
+ bool mHasReopenExt;
+ LPALCREOPENDEVICESOFT mReopenDeviceSOFT;
+
+ bool mHasSystemEventsExt;
+ LPALCEVENTCONTROLSOFT mEventControlSOFT;
+ LPALCEVENTCALLBACKSOFT mEventCallbackSOFT;
+
+ // Set by onDeviceEventSOFT(), consumed by idle()
+ LLAtomicBool mDefaultDeviceChanged;
+
+ static void ALC_APIENTRY onDeviceEventSOFT(ALCenum eventType, ALCenum deviceType,
+ ALCdevice* device, ALCsizei length,
+ const ALCchar* message, void* userParam) noexcept;
+ void reopenOnDefaultDevice();
};
class LLAudioChannelOpenAL : public LLAudioChannel