14 #include "al/listener.h"
16 #include "alnumeric.h"
18 #include "core/context.h"
19 #include "intrusive_ptr.h"
23 #include "al/eax/call.h"
24 #include "al/eax/exception.h"
25 #include "al/eax/fx_slot_index.h"
26 #include "al/eax/fx_slots.h"
27 #include "al/eax/utils.h"
34 using uint = unsigned int;
37 struct SourceSubList {
38 uint64_t FreeMask{~0_u64};
39 ALsource *Sources{nullptr}; /* 64 */
41 SourceSubList() noexcept = default;
42 SourceSubList(const SourceSubList&) = delete;
43 SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
44 { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
47 SourceSubList& operator=(const SourceSubList&) = delete;
48 SourceSubList& operator=(SourceSubList&& rhs) noexcept
49 { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
52 struct EffectSlotSubList {
53 uint64_t FreeMask{~0_u64};
54 ALeffectslot *EffectSlots{nullptr}; /* 64 */
56 EffectSlotSubList() noexcept = default;
57 EffectSlotSubList(const EffectSlotSubList&) = delete;
58 EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
59 : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
60 { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
63 EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
64 EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
65 { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
68 struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
69 const al::intrusive_ptr<ALCdevice> mALDevice;
72 bool mPropsDirty{true};
73 bool mDeferUpdates{false};
77 std::atomic<ALenum> mLastError{AL_NO_ERROR};
79 DistanceModel mDistanceModel{DistanceModel::Default};
80 bool mSourceDistanceModel{false};
82 float mDopplerFactor{1.0f};
83 float mDopplerVelocity{1.0f};
84 float mSpeedOfSound{SpeedOfSoundMetersPerSec};
85 float mAirAbsorptionGainHF{AirAbsorbGainHF};
87 std::mutex mEventCbLock;
88 ALEVENTPROCSOFT mEventCb{};
89 void *mEventParam{nullptr};
91 ALlistener mListener{};
93 al::vector<SourceSubList> mSourceList;
94 ALuint mNumSources{0};
95 std::mutex mSourceLock;
97 al::vector<EffectSlotSubList> mEffectSlotList;
98 ALuint mNumEffectSlots{0u};
99 std::mutex mEffectSlotLock;
101 /* Default effect slot */
102 std::unique_ptr<ALeffectslot> mDefaultSlot;
104 const char *mExtensionList{nullptr};
106 std::string mExtensionListOverride{};
109 ALCcontext(al::intrusive_ptr<ALCdevice> device);
110 ALCcontext(const ALCcontext&) = delete;
111 ALCcontext& operator=(const ALCcontext&) = delete;
116 * Removes the context from its device and removes it from being current on
117 * the running thread or globally. Returns true if other contexts still
118 * exist on the device.
123 * Defers/suspends updates for the given context's listener and sources.
124 * This does *NOT* stop mixing, but rather prevents certain property
125 * changes from taking effect. mPropLock must be held when called.
127 void deferUpdates() noexcept { mDeferUpdates = true; }
130 * Resumes update processing after being deferred. mPropLock must be held
133 void processUpdates()
135 if(std::exchange(mDeferUpdates, false))
140 * Applies all pending updates for the context, listener, effect slots, and
143 void applyAllUpdates();
145 #ifdef __USE_MINGW_ANSI_STDIO
146 [[gnu::format(gnu_printf, 3, 4)]]
148 [[gnu::format(printf, 3, 4)]]
150 void setError(ALenum errorCode, const char *msg, ...);
152 /* Process-wide current context */
153 static std::atomic<bool> sGlobalContextLock;
154 static std::atomic<ALCcontext*> sGlobalContext;
157 /* Thread-local current context. */
158 static thread_local ALCcontext *sLocalContext;
160 /* Thread-local context handling. This handles attempting to release the
161 * context which may have been left current when the thread is destroyed.
166 void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
168 static thread_local ThreadCtx sThreadContext;
171 /* HACK: MinGW generates bad code when accessing an extern thread_local
172 * object. Add a wrapper function for it that only accesses it where it's
176 static ALCcontext *getThreadContext() noexcept;
177 static void setThreadContext(ALCcontext *context) noexcept;
179 static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
180 static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
183 /* Default effect that applies to sources that don't have an effect on send 0. */
184 static ALeffect sDefaultEffect;
186 DEF_NEWDEL(ALCcontext)
190 bool hasEax() const noexcept { return mEaxIsInitialized; }
191 bool eaxIsCapable() const noexcept;
193 void eaxUninitialize() noexcept;
196 const GUID* property_set_id,
198 ALuint property_source_id,
199 ALvoid* property_value,
200 ALuint property_value_size);
203 const GUID* property_set_id,
205 ALuint property_source_id,
206 ALvoid* property_value,
207 ALuint property_value_size);
209 void eaxSetLastError() noexcept;
211 EaxFxSlotIndex eaxGetPrimaryFxSlotIndex() const noexcept
212 { return mEaxPrimaryFxSlotIndex; }
214 const ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index) const
215 { return mEaxFxSlots.get(fx_slot_index); }
216 ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index)
217 { return mEaxFxSlots.get(fx_slot_index); }
219 bool eaxNeedsCommit() const noexcept { return mEaxNeedsCommit; }
222 void eaxCommitFxSlots()
223 { mEaxFxSlots.commit(); }
226 static constexpr auto eax_primary_fx_slot_id_dirty_bit = EaxDirtyFlags{1} << 0;
227 static constexpr auto eax_distance_factor_dirty_bit = EaxDirtyFlags{1} << 1;
228 static constexpr auto eax_air_absorption_hf_dirty_bit = EaxDirtyFlags{1} << 2;
229 static constexpr auto eax_hf_reference_dirty_bit = EaxDirtyFlags{1} << 3;
230 static constexpr auto eax_macro_fx_factor_dirty_bit = EaxDirtyFlags{1} << 4;
232 using Eax4Props = EAX40CONTEXTPROPERTIES;
235 Eax4Props i; // Immediate.
236 Eax4Props d; // Deferred.
239 using Eax5Props = EAX50CONTEXTPROPERTIES;
242 Eax5Props i; // Immediate.
243 Eax5Props d; // Deferred.
246 class ContextException : public EaxException
249 explicit ContextException(const char* message)
250 : EaxException{"EAX_CONTEXT", message}
254 struct Eax4PrimaryFxSlotIdValidator {
255 void operator()(const GUID& guidPrimaryFXSlotID) const
257 if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
258 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot0 &&
259 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot1 &&
260 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot2 &&
261 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot3)
263 eax_fail_unknown_primary_fx_slot_id();
268 struct Eax4DistanceFactorValidator {
269 void operator()(float flDistanceFactor) const
271 eax_validate_range<ContextException>(
274 EAXCONTEXT_MINDISTANCEFACTOR,
275 EAXCONTEXT_MAXDISTANCEFACTOR);
279 struct Eax4AirAbsorptionHfValidator {
280 void operator()(float flAirAbsorptionHF) const
282 eax_validate_range<ContextException>(
285 EAXCONTEXT_MINAIRABSORPTIONHF,
286 EAXCONTEXT_MAXAIRABSORPTIONHF);
290 struct Eax4HfReferenceValidator {
291 void operator()(float flHFReference) const
293 eax_validate_range<ContextException>(
296 EAXCONTEXT_MINHFREFERENCE,
297 EAXCONTEXT_MAXHFREFERENCE);
301 struct Eax4AllValidator {
302 void operator()(const EAX40CONTEXTPROPERTIES& all) const
304 Eax4PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
305 Eax4DistanceFactorValidator{}(all.flDistanceFactor);
306 Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
307 Eax4HfReferenceValidator{}(all.flHFReference);
311 struct Eax5PrimaryFxSlotIdValidator {
312 void operator()(const GUID& guidPrimaryFXSlotID) const
314 if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
315 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot0 &&
316 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot1 &&
317 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot2 &&
318 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot3)
320 eax_fail_unknown_primary_fx_slot_id();
325 struct Eax5MacroFxFactorValidator {
326 void operator()(float flMacroFXFactor) const
328 eax_validate_range<ContextException>(
331 EAXCONTEXT_MINMACROFXFACTOR,
332 EAXCONTEXT_MAXMACROFXFACTOR);
336 struct Eax5AllValidator {
337 void operator()(const EAX50CONTEXTPROPERTIES& all) const
339 Eax5PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
340 Eax4DistanceFactorValidator{}(all.flDistanceFactor);
341 Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
342 Eax4HfReferenceValidator{}(all.flHFReference);
343 Eax5MacroFxFactorValidator{}(all.flMacroFXFactor);
347 struct Eax5EaxVersionValidator {
348 void operator()(unsigned long ulEAXVersion) const
350 eax_validate_range<ContextException>(
353 EAXCONTEXT_MINEAXSESSION,
354 EAXCONTEXT_MAXEAXSESSION);
358 struct Eax5MaxActiveSendsValidator {
359 void operator()(unsigned long ulMaxActiveSends) const
361 eax_validate_range<ContextException>(
364 EAXCONTEXT_MINMAXACTIVESENDS,
365 EAXCONTEXT_MAXMAXACTIVESENDS);
369 struct Eax5SessionAllValidator {
370 void operator()(const EAXSESSIONPROPERTIES& all) const
372 Eax5EaxVersionValidator{}(all.ulEAXVersion);
373 Eax5MaxActiveSendsValidator{}(all.ulMaxActiveSends);
377 struct Eax5SpeakerConfigValidator {
378 void operator()(unsigned long ulSpeakerConfig) const
380 eax_validate_range<ContextException>(
383 EAXCONTEXT_MINSPEAKERCONFIG,
384 EAXCONTEXT_MAXSPEAKERCONFIG);
388 bool mEaxIsInitialized{};
391 long mEaxLastError{};
392 unsigned long mEaxSpeakerConfig{};
394 EaxFxSlotIndex mEaxPrimaryFxSlotIndex{};
395 EaxFxSlots mEaxFxSlots{};
397 int mEaxVersion{}; // Current EAX version.
398 bool mEaxNeedsCommit{};
399 EaxDirtyFlags mEaxDf{}; // Dirty flags for the current EAX version.
400 Eax5State mEax123{}; // EAX1/EAX2/EAX3 state.
401 Eax4State mEax4{}; // EAX4 state.
402 Eax5State mEax5{}; // EAX5 state.
403 Eax5Props mEax{}; // Current EAX state.
404 EAXSESSIONPROPERTIES mEaxSession{};
406 [[noreturn]] static void eax_fail(const char* message);
407 [[noreturn]] static void eax_fail_unknown_property_set_id();
408 [[noreturn]] static void eax_fail_unknown_primary_fx_slot_id();
409 [[noreturn]] static void eax_fail_unknown_property_id();
410 [[noreturn]] static void eax_fail_unknown_version();
412 // Gets a value from EAX call,
414 // and updates the current value.
415 template<typename TValidator, typename TProperty>
416 static void eax_set(const EaxCall& call, TProperty& property)
418 const auto& value = call.get_value<ContextException, const TProperty>();
423 // Gets a new value from EAX call,
425 // updates the deferred value,
426 // updates a dirty flag.
429 EaxDirtyFlags TDirtyBit,
430 typename TMemberResult,
433 void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member) noexcept
435 const auto& src = call.get_value<ContextException, const TMemberResult>();
437 const auto& dst_i = state.i.*member;
438 auto& dst_d = state.d.*member;
446 EaxDirtyFlags TDirtyBit,
447 typename TMemberResult,
450 void eax_context_commit_property(TState& state, EaxDirtyFlags& dst_df,
451 TMemberResult TProps::*member) noexcept
453 if((mEaxDf & TDirtyBit) != EaxDirtyFlags{})
456 const auto& src_d = state.d.*member;
457 state.i.*member = src_d;
458 mEax.*member = src_d;
462 void eax_initialize_extensions();
463 void eax_initialize();
465 bool eax_has_no_default_effect_slot() const noexcept;
466 void eax_ensure_no_default_effect_slot() const;
467 bool eax_has_enough_aux_sends() const noexcept;
468 void eax_ensure_enough_aux_sends() const;
469 void eax_ensure_compatibility();
471 unsigned long eax_detect_speaker_configuration() const;
472 void eax_update_speaker_configuration();
474 void eax_set_last_error_defaults() noexcept;
475 void eax_session_set_defaults() noexcept;
476 static void eax4_context_set_defaults(Eax4Props& props) noexcept;
477 static void eax4_context_set_defaults(Eax4State& state) noexcept;
478 static void eax5_context_set_defaults(Eax5Props& props) noexcept;
479 static void eax5_context_set_defaults(Eax5State& state) noexcept;
480 void eax_context_set_defaults();
481 void eax_set_defaults();
483 void eax_dispatch_fx_slot(const EaxCall& call);
484 void eax_dispatch_source(const EaxCall& call);
486 void eax_get_misc(const EaxCall& call);
487 void eax4_get(const EaxCall& call, const Eax4Props& props);
488 void eax5_get(const EaxCall& call, const Eax5Props& props);
489 void eax_get(const EaxCall& call);
491 void eax_context_commit_primary_fx_slot_id();
492 void eax_context_commit_distance_factor();
493 void eax_context_commit_air_absorbtion_hf();
494 void eax_context_commit_hf_reference();
495 void eax_context_commit_macro_fx_factor();
497 void eax_initialize_fx_slots();
499 void eax_update_sources();
501 void eax_set_misc(const EaxCall& call);
502 void eax4_defer_all(const EaxCall& call, Eax4State& state);
503 void eax4_defer(const EaxCall& call, Eax4State& state);
504 void eax5_defer_all(const EaxCall& call, Eax5State& state);
505 void eax5_defer(const EaxCall& call, Eax5State& state);
506 void eax_set(const EaxCall& call);
508 void eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df);
509 void eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df);
510 void eax_context_commit();
514 using ContextRef = al::intrusive_ptr<ALCcontext>;
516 ContextRef GetContextRef(void);
518 void UpdateContextProps(ALCcontext *context);
521 extern bool TrapALError;
525 ALenum AL_APIENTRY EAXSet(
526 const GUID* property_set_id,
528 ALuint property_source_id,
529 ALvoid* property_value,
530 ALuint property_value_size) noexcept;
532 ALenum AL_APIENTRY EAXGet(
533 const GUID* property_set_id,
535 ALuint property_source_id,
536 ALvoid* property_value,
537 ALuint property_value_size) noexcept;
540 #endif /* ALC_CONTEXT_H */