diff options
author | Chris Robinson <[email protected]> | 2019-07-29 15:40:17 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2019-07-29 15:40:17 -0700 |
commit | 0a26bab14e0100b883f59958f3ce417888cebc62 (patch) | |
tree | 2ef9bb6a2f100366eed14bcbaf51edecab6211ca /al/event.cpp | |
parent | 8ccb7604d30147583fda134e220807f3dc2f07e5 (diff) |
Rename the OpenAL32 directory to al
Diffstat (limited to 'al/event.cpp')
-rw-r--r-- | al/event.cpp | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/al/event.cpp b/al/event.cpp new file mode 100644 index 00000000..d50cef2e --- /dev/null +++ b/al/event.cpp @@ -0,0 +1,216 @@ + +#include "config.h" + +#include <algorithm> +#include <atomic> +#include <cstring> +#include <exception> +#include <memory> +#include <mutex> +#include <new> +#include <string> +#include <thread> + +#include "AL/al.h" +#include "AL/alc.h" + +#include "alError.h" +#include "albyte.h" +#include "alcmain.h" +#include "alcontext.h" +#include "alexcpt.h" +#include "almalloc.h" +#include "effects/base.h" +#include "inprogext.h" +#include "logging.h" +#include "opthelpers.h" +#include "ringbuffer.h" +#include "threads.h" + + +static int EventThread(ALCcontext *context) +{ + RingBuffer *ring{context->AsyncEvents.get()}; + bool quitnow{false}; + while(LIKELY(!quitnow)) + { + auto evt_data = ring->getReadVector().first; + if(evt_data.len == 0) + { + context->EventSem.wait(); + continue; + } + + std::lock_guard<std::mutex> _{context->EventCbLock}; + do { + auto &evt = *reinterpret_cast<AsyncEvent*>(evt_data.buf); + evt_data.buf += sizeof(AsyncEvent); + evt_data.len -= 1; + /* This automatically destructs the event object and advances the + * ringbuffer's read offset at the end of scope. + */ + const struct EventAutoDestructor { + AsyncEvent &evt_; + RingBuffer *ring_; + ~EventAutoDestructor() + { + al::destroy_at(&evt_); + ring_->readAdvance(1); + } + } _{evt, ring}; + + quitnow = evt.EnumType == EventType_KillThread; + if(UNLIKELY(quitnow)) break; + + if(evt.EnumType == EventType_ReleaseEffectState) + { + evt.u.mEffectState->DecRef(); + continue; + } + + ALbitfieldSOFT enabledevts{context->EnabledEvts.load(std::memory_order_acquire)}; + if(!context->EventCb) continue; + + if(evt.EnumType == EventType_SourceStateChange) + { + if(!(enabledevts&EventType_SourceStateChange)) + continue; + std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)}; + msg += " state has changed to "; + msg += (evt.u.srcstate.state==AL_INITIAL) ? "AL_INITIAL" : + (evt.u.srcstate.state==AL_PLAYING) ? "AL_PLAYING" : + (evt.u.srcstate.state==AL_PAUSED) ? "AL_PAUSED" : + (evt.u.srcstate.state==AL_STOPPED) ? "AL_STOPPED" : "<unknown>"; + context->EventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id, + evt.u.srcstate.state, static_cast<ALsizei>(msg.length()), msg.c_str(), + context->EventParam + ); + } + else if(evt.EnumType == EventType_BufferCompleted) + { + if(!(enabledevts&EventType_BufferCompleted)) + continue; + std::string msg{std::to_string(evt.u.bufcomp.count)}; + if(evt.u.bufcomp.count == 1) msg += " buffer completed"; + else msg += " buffers completed"; + context->EventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.u.bufcomp.id, + evt.u.bufcomp.count, static_cast<ALsizei>(msg.length()), msg.c_str(), + context->EventParam + ); + } + else if((enabledevts&evt.EnumType) == evt.EnumType) + context->EventCb(evt.u.user.type, evt.u.user.id, evt.u.user.param, + static_cast<ALsizei>(strlen(evt.u.user.msg)), evt.u.user.msg, + context->EventParam + ); + } while(evt_data.len != 0); + } + return 0; +} + +void StartEventThrd(ALCcontext *ctx) +{ + try { + ctx->EventThread = std::thread{EventThread, ctx}; + } + catch(std::exception& e) { + ERR("Failed to start event thread: %s\n", e.what()); + } + catch(...) { + ERR("Failed to start event thread! Expect problems.\n"); + } +} + +void StopEventThrd(ALCcontext *ctx) +{ + static constexpr AsyncEvent kill_evt{EventType_KillThread}; + RingBuffer *ring{ctx->AsyncEvents.get()}; + auto evt_data = ring->getWriteVector().first; + if(evt_data.len == 0) + { + do { + std::this_thread::yield(); + evt_data = ring->getWriteVector().first; + } while(evt_data.len == 0); + } + new (evt_data.buf) AsyncEvent{kill_evt}; + ring->writeAdvance(1); + + ctx->EventSem.post(); + if(ctx->EventThread.joinable()) + ctx->EventThread.join(); +} + +AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable) +START_API_FUNC +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; + + if(count < 0) SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Controlling %d events", count); + if(count == 0) return; + if(!types) SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "NULL pointer"); + + ALbitfieldSOFT flags{0}; + const ALenum *types_end = types+count; + auto bad_type = std::find_if_not(types, types_end, + [&flags](ALenum type) noexcept -> bool + { + if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT) + flags |= EventType_BufferCompleted; + else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT) + flags |= EventType_SourceStateChange; + else if(type == AL_EVENT_TYPE_ERROR_SOFT) + flags |= EventType_Error; + else if(type == AL_EVENT_TYPE_PERFORMANCE_SOFT) + flags |= EventType_Performance; + else if(type == AL_EVENT_TYPE_DEPRECATED_SOFT) + flags |= EventType_Deprecated; + else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT) + flags |= EventType_Disconnected; + else + return false; + return true; + } + ); + if(bad_type != types_end) + SETERR_RETURN(context.get(), AL_INVALID_ENUM,, "Invalid event type 0x%04x", *bad_type); + + if(enable) + { + ALbitfieldSOFT enabledevts{context->EnabledEvts.load(std::memory_order_relaxed)}; + while(context->EnabledEvts.compare_exchange_weak(enabledevts, enabledevts|flags, + std::memory_order_acq_rel, std::memory_order_acquire) == 0) + { + /* enabledevts is (re-)filled with the current value on failure, so + * just try again. + */ + } + } + else + { + ALbitfieldSOFT enabledevts{context->EnabledEvts.load(std::memory_order_relaxed)}; + while(context->EnabledEvts.compare_exchange_weak(enabledevts, enabledevts&~flags, + std::memory_order_acq_rel, std::memory_order_acquire) == 0) + { + } + /* Wait to ensure the event handler sees the changed flags before + * returning. + */ + std::lock_guard<std::mutex>{context->EventCbLock}; + } +} +END_API_FUNC + +AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam) +START_API_FUNC +{ + ContextRef context{GetContextRef()}; + if(UNLIKELY(!context)) return; + + std::lock_guard<std::mutex> _{context->PropLock}; + std::lock_guard<std::mutex> __{context->EventCbLock}; + context->EventCb = callback; + context->EventParam = userParam; +} +END_API_FUNC |