13 #include <string_view>
14 #include <unordered_map>
22 #include "al/listener.h"
23 #include "althreads.h"
24 #include "core/context.h"
25 #include "intrusive_ptr.h"
26 #include "opthelpers.h"
29 #include "al/eax/api.h"
30 #include "al/eax/exception.h"
31 #include "al/eax/fx_slot_index.h"
32 #include "al/eax/fx_slots.h"
33 #include "al/eax/utils.h"
41 struct EffectSlotSubList
;
44 enum class DebugSource
: std::uint8_t;
45 enum class DebugType
: std::uint8_t;
46 enum class DebugSeverity
: std::uint8_t;
48 using uint
= unsigned int;
52 DebugBit
= 0, /* ALC_CONTEXT_DEBUG_BIT_EXT */
54 using ContextFlagBitset
= std::bitset
<sizeof(ALuint
)*8>;
57 struct DebugLogEntry
{
58 const DebugSource mSource
;
59 const DebugType mType
;
60 const DebugSeverity mSeverity
;
66 DebugLogEntry(DebugSource source
, DebugType type
, uint id
, DebugSeverity severity
, T
&& message
)
67 : mSource
{source
}, mType
{type
}, mSeverity
{severity
}, mId
{id
}
68 , mMessage
{std::forward
<T
>(message
)}
70 DebugLogEntry(const DebugLogEntry
&) = default;
71 DebugLogEntry(DebugLogEntry
&&) = default;
75 struct ALCcontext final
: public al::intrusive_ref
<ALCcontext
>, ContextBase
{
76 const al::intrusive_ptr
<ALCdevice
> mALDevice
;
79 bool mPropsDirty
{true};
80 bool mDeferUpdates
{false};
84 al::tss
<ALenum
> mLastThreadError
{AL_NO_ERROR
};
86 const ContextFlagBitset mContextFlags
;
87 std::atomic
<bool> mDebugEnabled
{false};
89 DistanceModel mDistanceModel
{DistanceModel::Default
};
90 bool mSourceDistanceModel
{false};
92 float mDopplerFactor
{1.0f
};
93 float mDopplerVelocity
{1.0f
};
94 float mSpeedOfSound
{SpeedOfSoundMetersPerSec
};
95 float mAirAbsorptionGainHF
{AirAbsorbGainHF
};
97 std::mutex mEventCbLock
;
98 ALEVENTPROCSOFT mEventCb
{};
99 void *mEventParam
{nullptr};
101 std::mutex mDebugCbLock
;
102 ALDEBUGPROCEXT mDebugCb
{};
103 void *mDebugParam
{nullptr};
104 std::vector
<DebugGroup
> mDebugGroups
;
105 std::deque
<DebugLogEntry
> mDebugLog
;
107 ALlistener mListener
{};
109 std::vector
<SourceSubList
> mSourceList
;
110 ALuint mNumSources
{0};
111 std::mutex mSourceLock
;
113 std::vector
<EffectSlotSubList
> mEffectSlotList
;
114 ALuint mNumEffectSlots
{0u};
115 std::mutex mEffectSlotLock
;
117 /* Default effect slot */
118 std::unique_ptr
<ALeffectslot
> mDefaultSlot
;
120 std::vector
<std::string_view
> mExtensions
;
121 std::string mExtensionsString
{};
123 std::unordered_map
<ALuint
,std::string
> mSourceNames
;
124 std::unordered_map
<ALuint
,std::string
> mEffectSlotNames
;
126 ALCcontext(al::intrusive_ptr
<ALCdevice
> device
, ContextFlagBitset flags
);
127 ALCcontext(const ALCcontext
&) = delete;
128 ALCcontext
& operator=(const ALCcontext
&) = delete;
133 * Removes the context from its device and removes it from being current on
134 * the running thread or globally. Stops device playback if this was the
135 * last context on its device.
140 * Defers/suspends updates for the given context's listener and sources.
141 * This does *NOT* stop mixing, but rather prevents certain property
142 * changes from taking effect. mPropLock must be held when called.
144 void deferUpdates() noexcept
{ mDeferUpdates
= true; }
147 * Resumes update processing after being deferred. mPropLock must be held
150 void processUpdates()
152 if(std::exchange(mDeferUpdates
, false))
157 * Applies all pending updates for the context, listener, effect slots, and
160 void applyAllUpdates();
163 [[gnu::format(__MINGW_PRINTF_FORMAT
, 3, 4)]]
165 [[gnu::format(printf
, 3, 4)]]
167 void setError(ALenum errorCode
, const char *msg
, ...);
169 void sendDebugMessage(std::unique_lock
<std::mutex
> &debuglock
, DebugSource source
,
170 DebugType type
, ALuint id
, DebugSeverity severity
, std::string_view message
);
172 void debugMessage(DebugSource source
, DebugType type
, ALuint id
, DebugSeverity severity
,
173 std::string_view message
)
175 if(!mDebugEnabled
.load(std::memory_order_relaxed
)) LIKELY
177 std::unique_lock
<std::mutex
> debuglock
{mDebugCbLock
};
178 sendDebugMessage(debuglock
, source
, type
, id
, severity
, message
);
181 /* Process-wide current context */
182 static std::atomic
<bool> sGlobalContextLock
;
183 static std::atomic
<ALCcontext
*> sGlobalContext
;
186 /* Thread-local current context. */
187 static inline thread_local ALCcontext
*sLocalContext
{};
189 /* Thread-local context handling. This handles attempting to release the
190 * context which may have been left current when the thread is destroyed.
194 ThreadCtx() = default;
195 ThreadCtx(const ThreadCtx
&) = delete;
196 auto operator=(const ThreadCtx
&) -> ThreadCtx
& = delete;
199 /* NOLINTBEGIN(readability-convert-member-functions-to-static)
200 * This should be non-static to invoke construction of the thread-local
201 * sThreadContext, so that it's destructor gets run at thread exit to
202 * clear sLocalContext (which isn't a member variable to make read
205 void set(ALCcontext
*ctx
) const noexcept
{ sLocalContext
= ctx
; }
206 /* NOLINTEND(readability-convert-member-functions-to-static) */
208 static thread_local ThreadCtx sThreadContext
;
211 static ALCcontext
*getThreadContext() noexcept
{ return sLocalContext
; }
212 static void setThreadContext(ALCcontext
*context
) noexcept
{ sThreadContext
.set(context
); }
214 /* Default effect that applies to sources that don't have an effect on send 0. */
215 static ALeffect sDefaultEffect
;
218 bool hasEax() const noexcept
{ return mEaxIsInitialized
; }
219 bool eaxIsCapable() const noexcept
;
221 void eaxUninitialize() noexcept
;
224 const GUID
* property_set_id
,
226 ALuint property_source_id
,
227 ALvoid
* property_value
,
228 ALuint property_value_size
);
231 const GUID
* property_set_id
,
233 ALuint property_source_id
,
234 ALvoid
* property_value
,
235 ALuint property_value_size
);
237 void eaxSetLastError() noexcept
;
240 auto eaxGetDistanceFactor() const noexcept
-> float { return mEax
.flDistanceFactor
; }
243 auto eaxGetPrimaryFxSlotIndex() const noexcept
-> EaxFxSlotIndex
244 { return mEaxPrimaryFxSlotIndex
; }
246 const ALeffectslot
& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index
) const
247 { return mEaxFxSlots
.get(fx_slot_index
); }
248 ALeffectslot
& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index
)
249 { return mEaxFxSlots
.get(fx_slot_index
); }
251 bool eaxNeedsCommit() const noexcept
{ return mEaxNeedsCommit
; }
254 void eaxCommitFxSlots()
255 { mEaxFxSlots
.commit(); }
258 static constexpr auto eax_primary_fx_slot_id_dirty_bit
= EaxDirtyFlags
{1} << 0;
259 static constexpr auto eax_distance_factor_dirty_bit
= EaxDirtyFlags
{1} << 1;
260 static constexpr auto eax_air_absorption_hf_dirty_bit
= EaxDirtyFlags
{1} << 2;
261 static constexpr auto eax_hf_reference_dirty_bit
= EaxDirtyFlags
{1} << 3;
262 static constexpr auto eax_macro_fx_factor_dirty_bit
= EaxDirtyFlags
{1} << 4;
264 using Eax4Props
= EAX40CONTEXTPROPERTIES
;
267 Eax4Props i
; // Immediate.
268 Eax4Props d
; // Deferred.
271 using Eax5Props
= EAX50CONTEXTPROPERTIES
;
274 Eax5Props i
; // Immediate.
275 Eax5Props d
; // Deferred.
278 class ContextException final
: public EaxException
{
280 explicit ContextException(const char *message
)
281 : EaxException
{"EAX_CONTEXT", message
}
285 struct Eax4PrimaryFxSlotIdValidator
{
286 void operator()(const GUID
& guidPrimaryFXSlotID
) const
288 if(guidPrimaryFXSlotID
!= EAX_NULL_GUID
&&
289 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX40_FXSlot0
&&
290 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX40_FXSlot1
&&
291 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX40_FXSlot2
&&
292 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX40_FXSlot3
)
294 eax_fail_unknown_primary_fx_slot_id();
299 struct Eax4DistanceFactorValidator
{
300 void operator()(float flDistanceFactor
) const
302 eax_validate_range
<ContextException
>(
305 EAXCONTEXT_MINDISTANCEFACTOR
,
306 EAXCONTEXT_MAXDISTANCEFACTOR
);
310 struct Eax4AirAbsorptionHfValidator
{
311 void operator()(float flAirAbsorptionHF
) const
313 eax_validate_range
<ContextException
>(
316 EAXCONTEXT_MINAIRABSORPTIONHF
,
317 EAXCONTEXT_MAXAIRABSORPTIONHF
);
321 struct Eax4HfReferenceValidator
{
322 void operator()(float flHFReference
) const
324 eax_validate_range
<ContextException
>(
327 EAXCONTEXT_MINHFREFERENCE
,
328 EAXCONTEXT_MAXHFREFERENCE
);
332 struct Eax4AllValidator
{
333 void operator()(const EAX40CONTEXTPROPERTIES
& all
) const
335 Eax4PrimaryFxSlotIdValidator
{}(all
.guidPrimaryFXSlotID
);
336 Eax4DistanceFactorValidator
{}(all
.flDistanceFactor
);
337 Eax4AirAbsorptionHfValidator
{}(all
.flAirAbsorptionHF
);
338 Eax4HfReferenceValidator
{}(all
.flHFReference
);
342 struct Eax5PrimaryFxSlotIdValidator
{
343 void operator()(const GUID
& guidPrimaryFXSlotID
) const
345 if(guidPrimaryFXSlotID
!= EAX_NULL_GUID
&&
346 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX50_FXSlot0
&&
347 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX50_FXSlot1
&&
348 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX50_FXSlot2
&&
349 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX50_FXSlot3
)
351 eax_fail_unknown_primary_fx_slot_id();
356 struct Eax5MacroFxFactorValidator
{
357 void operator()(float flMacroFXFactor
) const
359 eax_validate_range
<ContextException
>(
362 EAXCONTEXT_MINMACROFXFACTOR
,
363 EAXCONTEXT_MAXMACROFXFACTOR
);
367 struct Eax5AllValidator
{
368 void operator()(const EAX50CONTEXTPROPERTIES
& all
) const
370 Eax5PrimaryFxSlotIdValidator
{}(all
.guidPrimaryFXSlotID
);
371 Eax4DistanceFactorValidator
{}(all
.flDistanceFactor
);
372 Eax4AirAbsorptionHfValidator
{}(all
.flAirAbsorptionHF
);
373 Eax4HfReferenceValidator
{}(all
.flHFReference
);
374 Eax5MacroFxFactorValidator
{}(all
.flMacroFXFactor
);
378 struct Eax5EaxVersionValidator
{
379 void operator()(unsigned long ulEAXVersion
) const
381 eax_validate_range
<ContextException
>(
384 EAXCONTEXT_MINEAXSESSION
,
385 EAXCONTEXT_MAXEAXSESSION
);
389 struct Eax5MaxActiveSendsValidator
{
390 void operator()(unsigned long ulMaxActiveSends
) const
392 eax_validate_range
<ContextException
>(
395 EAXCONTEXT_MINMAXACTIVESENDS
,
396 EAXCONTEXT_MAXMAXACTIVESENDS
);
400 struct Eax5SessionAllValidator
{
401 void operator()(const EAXSESSIONPROPERTIES
& all
) const
403 Eax5EaxVersionValidator
{}(all
.ulEAXVersion
);
404 Eax5MaxActiveSendsValidator
{}(all
.ulMaxActiveSends
);
408 struct Eax5SpeakerConfigValidator
{
409 void operator()(unsigned long ulSpeakerConfig
) const
411 eax_validate_range
<ContextException
>(
414 EAXCONTEXT_MINSPEAKERCONFIG
,
415 EAXCONTEXT_MAXSPEAKERCONFIG
);
419 bool mEaxIsInitialized
{};
422 long mEaxLastError
{};
423 unsigned long mEaxSpeakerConfig
{};
425 EaxFxSlotIndex mEaxPrimaryFxSlotIndex
{};
426 EaxFxSlots mEaxFxSlots
{};
428 int mEaxVersion
{}; // Current EAX version.
429 bool mEaxNeedsCommit
{};
430 EaxDirtyFlags mEaxDf
{}; // Dirty flags for the current EAX version.
431 Eax5State mEax123
{}; // EAX1/EAX2/EAX3 state.
432 Eax4State mEax4
{}; // EAX4 state.
433 Eax5State mEax5
{}; // EAX5 state.
434 Eax5Props mEax
{}; // Current EAX state.
435 EAXSESSIONPROPERTIES mEaxSession
{};
437 [[noreturn
]] static void eax_fail(const char* message
);
438 [[noreturn
]] static void eax_fail_unknown_property_set_id();
439 [[noreturn
]] static void eax_fail_unknown_primary_fx_slot_id();
440 [[noreturn
]] static void eax_fail_unknown_property_id();
441 [[noreturn
]] static void eax_fail_unknown_version();
443 // Gets a value from EAX call,
445 // and updates the current value.
446 template<typename TValidator
, typename TProperty
>
447 static void eax_set(const EaxCall
& call
, TProperty
& property
)
449 const auto& value
= call
.get_value
<ContextException
, const TProperty
>();
454 // Gets a new value from EAX call,
456 // updates the deferred value,
457 // updates a dirty flag.
460 EaxDirtyFlags TDirtyBit
,
461 typename TMemberResult
,
464 void eax_defer(const EaxCall
& call
, TState
& state
, TMemberResult
TProps::*member
)
466 const auto& src
= call
.get_value
<ContextException
, const TMemberResult
>();
468 const auto& dst_i
= state
.i
.*member
;
469 auto& dst_d
= state
.d
.*member
;
477 EaxDirtyFlags TDirtyBit
,
478 typename TMemberResult
,
481 void eax_context_commit_property(TState
& state
, EaxDirtyFlags
& dst_df
,
482 TMemberResult
TProps::*member
) noexcept
484 if((mEaxDf
& TDirtyBit
) != EaxDirtyFlags
{})
487 const auto& src_d
= state
.d
.*member
;
488 state
.i
.*member
= src_d
;
489 mEax
.*member
= src_d
;
493 void eax_initialize_extensions();
494 void eax_initialize();
496 bool eax_has_no_default_effect_slot() const noexcept
;
497 void eax_ensure_no_default_effect_slot() const;
498 bool eax_has_enough_aux_sends() const noexcept
;
499 void eax_ensure_enough_aux_sends() const;
500 void eax_ensure_compatibility();
502 unsigned long eax_detect_speaker_configuration() const;
503 void eax_update_speaker_configuration();
505 void eax_set_last_error_defaults() noexcept
;
506 void eax_session_set_defaults() noexcept
;
507 static void eax4_context_set_defaults(Eax4Props
& props
) noexcept
;
508 static void eax4_context_set_defaults(Eax4State
& state
) noexcept
;
509 static void eax5_context_set_defaults(Eax5Props
& props
) noexcept
;
510 static void eax5_context_set_defaults(Eax5State
& state
) noexcept
;
511 void eax_context_set_defaults();
512 void eax_set_defaults();
514 void eax_dispatch_fx_slot(const EaxCall
& call
);
515 void eax_dispatch_source(const EaxCall
& call
);
517 void eax_get_misc(const EaxCall
& call
);
518 void eax4_get(const EaxCall
& call
, const Eax4Props
& props
);
519 void eax5_get(const EaxCall
& call
, const Eax5Props
& props
);
520 void eax_get(const EaxCall
& call
);
522 void eax_context_commit_primary_fx_slot_id();
523 void eax_context_commit_distance_factor();
524 void eax_context_commit_air_absorbtion_hf();
525 void eax_context_commit_hf_reference();
526 void eax_context_commit_macro_fx_factor();
528 void eax_initialize_fx_slots();
530 void eax_update_sources();
532 void eax_set_misc(const EaxCall
& call
);
533 void eax4_defer_all(const EaxCall
& call
, Eax4State
& state
);
534 void eax4_defer(const EaxCall
& call
, Eax4State
& state
);
535 void eax5_defer_all(const EaxCall
& call
, Eax5State
& state
);
536 void eax5_defer(const EaxCall
& call
, Eax5State
& state
);
537 void eax_set(const EaxCall
& call
);
539 void eax4_context_commit(Eax4State
& state
, EaxDirtyFlags
& dst_df
);
540 void eax5_context_commit(Eax5State
& state
, EaxDirtyFlags
& dst_df
);
541 void eax_context_commit();
545 using ContextRef
= al::intrusive_ptr
<ALCcontext
>;
547 ContextRef
GetContextRef() noexcept
;
549 void UpdateContextProps(ALCcontext
*context
);
552 inline bool TrapALError
{false};
556 auto AL_APIENTRY
EAXSet(const GUID
*property_set_id
, ALuint property_id
,
557 ALuint source_id
, ALvoid
*value
, ALuint value_size
) noexcept
-> ALenum
;
559 auto AL_APIENTRY
EAXGet(const GUID
*property_set_id
, ALuint property_id
,
560 ALuint source_id
, ALvoid
*value
, ALuint value_size
) noexcept
-> ALenum
;
563 #endif /* ALC_CONTEXT_H */