]> git.tdb.fi Git - ext/openal.git/blob - al/event.cpp
Tweak some types to work around an MSVC compile error
[ext/openal.git] / al / event.cpp
1
2 #include "config.h"
3
4 #include "event.h"
5
6 #include <algorithm>
7 #include <atomic>
8 #include <cstring>
9 #include <exception>
10 #include <memory>
11 #include <mutex>
12 #include <new>
13 #include <string>
14 #include <thread>
15 #include <utility>
16
17 #include "AL/al.h"
18 #include "AL/alc.h"
19
20 #include "albyte.h"
21 #include "alc/context.h"
22 #include "alc/effects/base.h"
23 #include "alc/inprogext.h"
24 #include "almalloc.h"
25 #include "core/async_event.h"
26 #include "core/except.h"
27 #include "core/logging.h"
28 #include "core/voice_change.h"
29 #include "opthelpers.h"
30 #include "ringbuffer.h"
31 #include "threads.h"
32
33
34 static int EventThread(ALCcontext *context)
35 {
36     RingBuffer *ring{context->mAsyncEvents.get()};
37     bool quitnow{false};
38     while(!quitnow)
39     {
40         auto evt_data = ring->getReadVector().first;
41         if(evt_data.len == 0)
42         {
43             context->mEventSem.wait();
44             continue;
45         }
46
47         std::lock_guard<std::mutex> _{context->mEventCbLock};
48         do {
49             auto *evt_ptr = reinterpret_cast<AsyncEvent*>(evt_data.buf);
50             evt_data.buf += sizeof(AsyncEvent);
51             evt_data.len -= 1;
52
53             AsyncEvent evt{*evt_ptr};
54             al::destroy_at(evt_ptr);
55             ring->readAdvance(1);
56
57             quitnow = evt.EnumType == AsyncEvent::KillThread;
58             if(quitnow) UNLIKELY break;
59
60             if(evt.EnumType == AsyncEvent::ReleaseEffectState)
61             {
62                 al::intrusive_ptr<EffectState>{evt.u.mEffectState};
63                 continue;
64             }
65
66             auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire);
67             if(!context->mEventCb || !enabledevts.test(evt.EnumType))
68                 continue;
69
70             if(evt.EnumType == AsyncEvent::SourceStateChange)
71             {
72                 ALuint state{};
73                 std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)};
74                 msg += " state has changed to ";
75                 switch(evt.u.srcstate.state)
76                 {
77                 case AsyncEvent::SrcState::Reset:
78                     msg += "AL_INITIAL";
79                     state = AL_INITIAL;
80                     break;
81                 case AsyncEvent::SrcState::Stop:
82                     msg += "AL_STOPPED";
83                     state = AL_STOPPED;
84                     break;
85                 case AsyncEvent::SrcState::Play:
86                     msg += "AL_PLAYING";
87                     state = AL_PLAYING;
88                     break;
89                 case AsyncEvent::SrcState::Pause:
90                     msg += "AL_PAUSED";
91                     state = AL_PAUSED;
92                     break;
93                 }
94                 context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
95                     state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
96             }
97             else if(evt.EnumType == AsyncEvent::BufferCompleted)
98             {
99                 std::string msg{std::to_string(evt.u.bufcomp.count)};
100                 if(evt.u.bufcomp.count == 1) msg += " buffer completed";
101                 else msg += " buffers completed";
102                 context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.u.bufcomp.id,
103                     evt.u.bufcomp.count, static_cast<ALsizei>(msg.length()), msg.c_str(),
104                     context->mEventParam);
105             }
106             else if(evt.EnumType == AsyncEvent::Disconnected)
107             {
108                 context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
109                     static_cast<ALsizei>(strlen(evt.u.disconnect.msg)), evt.u.disconnect.msg,
110                     context->mEventParam);
111             }
112         } while(evt_data.len != 0);
113     }
114     return 0;
115 }
116
117 void StartEventThrd(ALCcontext *ctx)
118 {
119     try {
120         ctx->mEventThread = std::thread{EventThread, ctx};
121     }
122     catch(std::exception& e) {
123         ERR("Failed to start event thread: %s\n", e.what());
124     }
125     catch(...) {
126         ERR("Failed to start event thread! Expect problems.\n");
127     }
128 }
129
130 void StopEventThrd(ALCcontext *ctx)
131 {
132     RingBuffer *ring{ctx->mAsyncEvents.get()};
133     auto evt_data = ring->getWriteVector().first;
134     if(evt_data.len == 0)
135     {
136         do {
137             std::this_thread::yield();
138             evt_data = ring->getWriteVector().first;
139         } while(evt_data.len == 0);
140     }
141     al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), AsyncEvent::KillThread);
142     ring->writeAdvance(1);
143
144     ctx->mEventSem.post();
145     if(ctx->mEventThread.joinable())
146         ctx->mEventThread.join();
147 }
148
149 AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable)
150 START_API_FUNC
151 {
152     ContextRef context{GetContextRef()};
153     if(!context) UNLIKELY return;
154
155     if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count);
156     if(count <= 0) return;
157     if(!types) return context->setError(AL_INVALID_VALUE, "NULL pointer");
158
159     ContextBase::AsyncEventBitset flags{};
160     const ALenum *types_end = types+count;
161     auto bad_type = std::find_if_not(types, types_end,
162         [&flags](ALenum type) noexcept -> bool
163         {
164             if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
165                 flags.set(AsyncEvent::BufferCompleted);
166             else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT)
167                 flags.set(AsyncEvent::SourceStateChange);
168             else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT)
169                 flags.set(AsyncEvent::Disconnected);
170             else
171                 return false;
172             return true;
173         }
174     );
175     if(bad_type != types_end)
176         return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", *bad_type);
177
178     if(enable)
179     {
180         auto enabledevts = context->mEnabledEvts.load(std::memory_order_relaxed);
181         while(context->mEnabledEvts.compare_exchange_weak(enabledevts, enabledevts|flags,
182             std::memory_order_acq_rel, std::memory_order_acquire) == 0)
183         {
184             /* enabledevts is (re-)filled with the current value on failure, so
185              * just try again.
186              */
187         }
188     }
189     else
190     {
191         auto enabledevts = context->mEnabledEvts.load(std::memory_order_relaxed);
192         while(context->mEnabledEvts.compare_exchange_weak(enabledevts, enabledevts&~flags,
193             std::memory_order_acq_rel, std::memory_order_acquire) == 0)
194         {
195         }
196         /* Wait to ensure the event handler sees the changed flags before
197          * returning.
198          */
199         std::lock_guard<std::mutex> _{context->mEventCbLock};
200     }
201 }
202 END_API_FUNC
203
204 AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam)
205 START_API_FUNC
206 {
207     ContextRef context{GetContextRef()};
208     if(!context) UNLIKELY return;
209
210     std::lock_guard<std::mutex> _{context->mPropLock};
211     std::lock_guard<std::mutex> __{context->mEventCbLock};
212     context->mEventCb = callback;
213     context->mEventParam = userParam;
214 }
215 END_API_FUNC