From 517bb94c0953de768f6aacca45456fd63bb2c96d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 30 May 2023 21:16:41 -0700 Subject: Add a callback to report system device changes Devices being added or removed, or the default device changing. Not all backends report this (none do currently), but it'll be supported where it can. --- CMakeLists.txt | 2 ++ alc/alc.cpp | 54 +++++++++++++++++-------------- alc/device.h | 3 ++ alc/events.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ alc/events.h | 36 +++++++++++++++++++++ alc/inprogext.h | 15 +++++++++ 6 files changed, 184 insertions(+), 24 deletions(-) create mode 100644 alc/events.cpp create mode 100644 alc/events.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bc8f2e2..e1c6db76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -813,6 +813,8 @@ set(ALC_OBJS alc/effects/pshifter.cpp alc/effects/reverb.cpp alc/effects/vmorpher.cpp + alc/events.cpp + alc/events.h alc/inprogext.h alc/panning.cpp) diff --git a/alc/alc.cpp b/alc/alc.cpp index 88aa73c5..c5d4b9d3 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -616,6 +616,9 @@ const struct { DECL(alPushDebugGroupDirectEXT), DECL(alPopDebugGroupDirectEXT), DECL(alGetDebugMessageLogDirectEXT), + + DECL(alcEventControlSOFT), + DECL(alcEventCallbackSOFT), #ifdef ALSOFT_EAX }, eaxFunctions[] = { DECL(EAXGet), @@ -1174,7 +1177,8 @@ constexpr ALCchar alcNoDeviceExtList[] = "ALC_EXT_thread_local_context " "ALC_SOFT_loopback " "ALC_SOFT_loopback_bformat " - "ALC_SOFT_reopen_device"; + "ALC_SOFT_reopen_device " + "ALC_SOFTX_system_events"; constexpr ALCchar alcExtensionList[] = "ALC_ENUMERATE_ALL_EXT " "ALC_ENUMERATION_EXT " @@ -1192,7 +1196,8 @@ constexpr ALCchar alcExtensionList[] = "ALC_SOFT_output_limiter " "ALC_SOFT_output_mode " "ALC_SOFT_pause_device " - "ALC_SOFT_reopen_device"; + "ALC_SOFT_reopen_device " + "ALC_SOFTX_system_events"; constexpr int alcMajorVersion{1}; constexpr int alcMinorVersion{1}; @@ -1815,28 +1820,6 @@ const std::array X71Downmix{{ }}; -/** Stores the latest ALC device error. */ -void alcSetError(ALCdevice *device, ALCenum errorCode) -{ - WARN("Error generated on device %p, code 0x%04x\n", voidp{device}, errorCode); - if(TrapALCError) - { -#ifdef _WIN32 - /* DebugBreak() will cause an exception if there is no debugger */ - if(IsDebuggerPresent()) - DebugBreak(); -#elif defined(SIGTRAP) - raise(SIGTRAP); -#endif - } - - if(device) - device->LastError.store(errorCode); - else - LastNullDeviceError.store(errorCode); -} - - std::unique_ptr CreateDeviceLimiter(const ALCdevice *device, const float threshold) { static constexpr bool AutoKnee{true}; @@ -2754,6 +2737,25 @@ ContextRef GetContextRef(void) return ContextRef{context}; } +void alcSetError(ALCdevice *device, ALCenum errorCode) +{ + WARN("Error generated on device %p, code 0x%04x\n", voidp{device}, errorCode); + if(TrapALCError) + { +#ifdef _WIN32 + /* DebugBreak() will cause an exception if there is no debugger */ + if(IsDebuggerPresent()) + DebugBreak(); +#elif defined(SIGTRAP) + raise(SIGTRAP); +#endif + } + + if(device) + device->LastError.store(errorCode); + else + LastNullDeviceError.store(errorCode); +} /************************************************ * Standard ALC functions @@ -3436,6 +3438,8 @@ ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar alcSetError(dev.get(), ALC_INVALID_VALUE); return nullptr; } + + InitConfig(); #ifdef ALSOFT_EAX if(eax_g_is_enabled) { @@ -3463,6 +3467,8 @@ ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *e alcSetError(dev.get(), ALC_INVALID_VALUE); return 0; } + + InitConfig(); #ifdef ALSOFT_EAX if(eax_g_is_enabled) { diff --git a/alc/device.h b/alc/device.h index 1274e287..c346dc9e 100644 --- a/alc/device.h +++ b/alc/device.h @@ -163,4 +163,7 @@ template<> inline std::optional ALCdevice::configValue(const char *block, const char *key) { return ConfigValueBool(DeviceName.c_str(), block, key); } +/** Stores the latest ALC device error. */ +void alcSetError(ALCdevice *device, ALCenum errorCode); + #endif diff --git a/alc/events.cpp b/alc/events.cpp new file mode 100644 index 00000000..3c9c59ee --- /dev/null +++ b/alc/events.cpp @@ -0,0 +1,98 @@ + +#include "config.h" + +#include "events.h" + +#include + +#include "alspan.h" +#include "common/threads.h" +#include "core/logging.h" +#include "device.h" + + +namespace { + +std::optional GetEventType(ALCenum type) +{ + switch(type) + { + case ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT: return alc::EventType::DefaultDeviceChanged; + case ALC_EVENT_TYPE_DEVICE_ADDED_SOFT: return alc::EventType::DeviceAdded; + case ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT: return alc::EventType::DeviceRemoved; + } + return std::nullopt; +} + +ALCenum EnumFromEventType(const alc::EventType type) +{ + switch(type) + { + case alc::EventType::DefaultDeviceChanged: return ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT; + case alc::EventType::DeviceAdded: return ALC_EVENT_TYPE_DEVICE_ADDED_SOFT; + case alc::EventType::DeviceRemoved: return ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT; + case alc::EventType::Count: break; + } + throw std::runtime_error{"Invalid EventType: "+std::to_string(al::to_underlying(type))}; +} + +} // namespace + +namespace alc { + +void Event(EventType eventType, ALCdevice *device, std::string_view message) noexcept +{ + auto eventlock = std::unique_lock{EventMutex}; + if(EventCallback && EventsEnabled.test(al::to_underlying(eventType))) + EventCallback(EnumFromEventType(eventType), device, + static_cast(message.length()), message.data(), EventUserPtr); +} + +} // namespace alc + +FORCE_ALIGN ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const ALCenum *types, + ALCboolean enable) noexcept +{ + if(enable != ALC_FALSE && enable != ALC_TRUE) + { + alcSetError(nullptr, ALC_INVALID_ENUM); + return ALC_FALSE; + } + if(count < 0) + { + alcSetError(nullptr, ALC_INVALID_VALUE); + return ALC_FALSE; + } + if(count == 0) + return ALC_TRUE; + if(!types) + { + alcSetError(nullptr, ALC_INVALID_VALUE); + return ALC_FALSE; + } + + std::bitset eventSet{0}; + for(ALCenum type : al::span{types, static_cast(count)}) + { + auto etype = GetEventType(type); + if(!etype) + { + WARN("Invalid event type: 0x%04x\n", type); + alcSetError(nullptr, ALC_INVALID_ENUM); + return ALC_FALSE; + } + eventSet.set(al::to_underlying(*etype)); + } + + auto eventlock = std::unique_lock{alc::EventMutex}; + if(enable) alc::EventsEnabled |= eventSet; + else alc::EventsEnabled &= eventSet; + return ALC_TRUE; +} + +FORCE_ALIGN void ALC_APIENTRY alcEventCallbackSOFT(ALCEVENTPROCTYPESOFT callback, void *userParam) noexcept +{ + auto eventlock = std::unique_lock{alc::EventMutex}; + alc::EventCallback = callback; + alc::EventUserPtr = userParam; +} diff --git a/alc/events.h b/alc/events.h new file mode 100644 index 00000000..51f32e57 --- /dev/null +++ b/alc/events.h @@ -0,0 +1,36 @@ +#ifndef ALC_EVENTS_H +#define ALC_EVENTS_H + +#include "inprogext.h" +#include "opthelpers.h" + +#include +#include +#include + + +namespace alc { + +enum class EventType : uint8_t { + DefaultDeviceChanged, + DeviceAdded, + DeviceRemoved, + + Count +}; + +inline std::bitset EventsEnabled{0}; + +inline std::mutex EventMutex; + +inline ALCEVENTPROCTYPESOFT EventCallback{}; +inline void *EventUserPtr{}; + +void Event(ALCenum eventType, ALCdevice *device, std::string_view message) noexcept; + +inline void Event(ALCenum eventType, std::string_view message) noexcept +{ Event(eventType, nullptr, message); } + +} // namespace alc + +#endif /* ALC_EVENTS_H */ diff --git a/alc/inprogext.h b/alc/inprogext.h index 38ef2bf8..7cf49868 100644 --- a/alc/inprogext.h +++ b/alc/inprogext.h @@ -443,6 +443,21 @@ ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer, AL #endif #endif +#ifndef ALC_SOFT_system_events +#define ALC_SOFT_system_events +#define ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT 0x19CF +#define ALC_EVENT_TYPE_DEVICE_ADDED_SOFT 0x19D0 +#define ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT 0x19D1 +typedef void (ALC_APIENTRY*ALCEVENTPROCTYPESOFT)(ALCenum eventType, ALCdevice *device, + ALCsizei length, const ALCchar *message, void *userParam) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY*LPALCEVENTCONTROLSOFT)(ALCsizei count, const ALCenum *types, ALCboolean enable) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY*LPALCEVENTCALLBACKSOFT)(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT17; +#ifdef AL_ALEXT_PROTOTYPES +ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const ALCenum *types, ALCboolean enable) ALC_API_NOEXCEPT; +void ALC_APIENTRY alcEventCallbackSOFT(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT; +#endif +#endif + /* Non-standard export. Not part of any extension. */ AL_API const ALchar* AL_APIENTRY alsoft_get_version(void) noexcept; -- cgit v1.2.3