21 #include "alc/context.h"
22 #include "alc/effects/base.h"
23 #include "alc/inprogext.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"
34 static int EventThread(ALCcontext
*context
)
36 RingBuffer
*ring
{context
->mAsyncEvents
.get()};
38 while(likely(!quitnow
))
40 auto evt_data
= ring
->getReadVector().first
;
43 context
->mEventSem
.wait();
47 std::lock_guard
<std::mutex
> _
{context
->mEventCbLock
};
49 auto *evt_ptr
= reinterpret_cast<AsyncEvent
*>(evt_data
.buf
);
50 evt_data
.buf
+= sizeof(AsyncEvent
);
53 AsyncEvent evt
{*evt_ptr
};
54 al::destroy_at(evt_ptr
);
57 quitnow
= evt
.EnumType
== AsyncEvent::KillThread
;
58 if(unlikely(quitnow
)) break;
60 if(evt
.EnumType
== AsyncEvent::ReleaseEffectState
)
62 evt
.u
.mEffectState
->release();
66 uint enabledevts
{context
->mEnabledEvts
.load(std::memory_order_acquire
)};
67 if(!context
->mEventCb
) continue;
69 if(evt
.EnumType
== AsyncEvent::SourceStateChange
)
71 if(!(enabledevts
&AsyncEvent::SourceStateChange
))
74 std::string msg
{"Source ID " + std::to_string(evt
.u
.srcstate
.id
)};
75 msg
+= " state has changed to ";
76 switch(evt
.u
.srcstate
.state
)
78 case AsyncEvent::SrcState::Reset
:
82 case AsyncEvent::SrcState::Stop
:
86 case AsyncEvent::SrcState::Play
:
90 case AsyncEvent::SrcState::Pause
:
95 context
->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
, evt
.u
.srcstate
.id
,
96 state
, static_cast<ALsizei
>(msg
.length()), msg
.c_str(), context
->mEventParam
);
98 else if(evt
.EnumType
== AsyncEvent::BufferCompleted
)
100 if(!(enabledevts
&AsyncEvent::BufferCompleted
))
102 std::string msg
{std::to_string(evt
.u
.bufcomp
.count
)};
103 if(evt
.u
.bufcomp
.count
== 1) msg
+= " buffer completed";
104 else msg
+= " buffers completed";
105 context
->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
, evt
.u
.bufcomp
.id
,
106 evt
.u
.bufcomp
.count
, static_cast<ALsizei
>(msg
.length()), msg
.c_str(),
107 context
->mEventParam
);
109 else if(evt
.EnumType
== AsyncEvent::Disconnected
)
111 if(!(enabledevts
&AsyncEvent::Disconnected
))
113 context
->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT
, 0, 0,
114 static_cast<ALsizei
>(strlen(evt
.u
.disconnect
.msg
)), evt
.u
.disconnect
.msg
,
115 context
->mEventParam
);
117 } while(evt_data
.len
!= 0);
122 void StartEventThrd(ALCcontext
*ctx
)
125 ctx
->mEventThread
= std::thread
{EventThread
, ctx
};
127 catch(std::exception
& e
) {
128 ERR("Failed to start event thread: %s\n", e
.what());
131 ERR("Failed to start event thread! Expect problems.\n");
135 void StopEventThrd(ALCcontext
*ctx
)
137 RingBuffer
*ring
{ctx
->mAsyncEvents
.get()};
138 auto evt_data
= ring
->getWriteVector().first
;
139 if(evt_data
.len
== 0)
142 std::this_thread::yield();
143 evt_data
= ring
->getWriteVector().first
;
144 } while(evt_data
.len
== 0);
146 al::construct_at(reinterpret_cast<AsyncEvent
*>(evt_data
.buf
), AsyncEvent::KillThread
);
147 ring
->writeAdvance(1);
149 ctx
->mEventSem
.post();
150 if(ctx
->mEventThread
.joinable())
151 ctx
->mEventThread
.join();
154 AL_API
void AL_APIENTRY
alEventControlSOFT(ALsizei count
, const ALenum
*types
, ALboolean enable
)
157 ContextRef context
{GetContextRef()};
158 if(unlikely(!context
)) return;
160 if(count
< 0) context
->setError(AL_INVALID_VALUE
, "Controlling %d events", count
);
161 if(count
<= 0) return;
162 if(!types
) SETERR_RETURN(context
, AL_INVALID_VALUE
,, "NULL pointer");
165 const ALenum
*types_end
= types
+count
;
166 auto bad_type
= std::find_if_not(types
, types_end
,
167 [&flags
](ALenum type
) noexcept
-> bool
169 if(type
== AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
)
170 flags
|= AsyncEvent::BufferCompleted
;
171 else if(type
== AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
)
172 flags
|= AsyncEvent::SourceStateChange
;
173 else if(type
== AL_EVENT_TYPE_DISCONNECTED_SOFT
)
174 flags
|= AsyncEvent::Disconnected
;
180 if(bad_type
!= types_end
)
181 SETERR_RETURN(context
, AL_INVALID_ENUM
,, "Invalid event type 0x%04x", *bad_type
);
185 uint enabledevts
{context
->mEnabledEvts
.load(std::memory_order_relaxed
)};
186 while(context
->mEnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
|flags
,
187 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
189 /* enabledevts is (re-)filled with the current value on failure, so
196 uint enabledevts
{context
->mEnabledEvts
.load(std::memory_order_relaxed
)};
197 while(context
->mEnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
&~flags
,
198 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
201 /* Wait to ensure the event handler sees the changed flags before
204 std::lock_guard
<std::mutex
> _
{context
->mEventCbLock
};
209 AL_API
void AL_APIENTRY
alEventCallbackSOFT(ALEVENTPROCSOFT callback
, void *userParam
)
212 ContextRef context
{GetContextRef()};
213 if(unlikely(!context
)) return;
215 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
216 std::lock_guard
<std::mutex
> __
{context
->mEventCbLock
};
217 context
->mEventCb
= callback
;
218 context
->mEventParam
= userParam
;