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 */