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(!quitnow
) [[likely
]]
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(quitnow
) [[unlikely
]] break;
60 if(evt
.EnumType
== AsyncEvent::ReleaseEffectState
)
62 al::intrusive_ptr
<EffectState
>{evt
.u
.mEffectState
};
66 auto enabledevts
= context
->mEnabledEvts
.load(std::memory_order_acquire
);
67 if(!context
->mEventCb
|| !enabledevts
.test(evt
.EnumType
))
70 if(evt
.EnumType
== AsyncEvent::SourceStateChange
)
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
)
77 case AsyncEvent::SrcState::Reset
:
81 case AsyncEvent::SrcState::Stop
:
85 case AsyncEvent::SrcState::Play
:
89 case AsyncEvent::SrcState::Pause
:
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
);
97 else if(evt
.EnumType
== AsyncEvent::BufferCompleted
)
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
);
106 else if(evt
.EnumType
== AsyncEvent::Disconnected
)
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
);
112 } while(evt_data
.len
!= 0);
117 void StartEventThrd(ALCcontext
*ctx
)
120 ctx
->mEventThread
= std::thread
{EventThread
, ctx
};
122 catch(std::exception
& e
) {
123 ERR("Failed to start event thread: %s\n", e
.what());
126 ERR("Failed to start event thread! Expect problems.\n");
130 void StopEventThrd(ALCcontext
*ctx
)
132 RingBuffer
*ring
{ctx
->mAsyncEvents
.get()};
133 auto evt_data
= ring
->getWriteVector().first
;
134 if(evt_data
.len
== 0)
137 std::this_thread::yield();
138 evt_data
= ring
->getWriteVector().first
;
139 } while(evt_data
.len
== 0);
141 al::construct_at(reinterpret_cast<AsyncEvent
*>(evt_data
.buf
), AsyncEvent::KillThread
);
142 ring
->writeAdvance(1);
144 ctx
->mEventSem
.post();
145 if(ctx
->mEventThread
.joinable())
146 ctx
->mEventThread
.join();
149 AL_API
void AL_APIENTRY
alEventControlSOFT(ALsizei count
, const ALenum
*types
, ALboolean enable
)
152 ContextRef context
{GetContextRef()};
153 if(!context
) [[unlikely
]] return;
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");
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
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
);
175 if(bad_type
!= types_end
)
176 return context
->setError(AL_INVALID_ENUM
, "Invalid event type 0x%04x", *bad_type
);
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)
184 /* enabledevts is (re-)filled with the current value on failure, so
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)
196 /* Wait to ensure the event handler sees the changed flags before
199 std::lock_guard
<std::mutex
> _
{context
->mEventCbLock
};
204 AL_API
void AL_APIENTRY
alEventCallbackSOFT(ALEVENTPROCSOFT callback
, void *userParam
)
207 ContextRef context
{GetContextRef()};
208 if(!context
) [[unlikely
]] return;
210 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
211 std::lock_guard
<std::mutex
> __
{context
->mEventCbLock
};
212 context
->mEventCb
= callback
;
213 context
->mEventParam
= userParam
;