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};
107 ALCcontext(al::intrusive_ptr
<ALCdevice
> device
);
108 ALCcontext(const ALCcontext
&) = delete;
109 ALCcontext
& operator=(const ALCcontext
&) = delete;
114 * Removes the context from its device and removes it from being current on
115 * the running thread or globally. Returns true if other contexts still
116 * exist on the device.
121 * Defers/suspends updates for the given context's listener and sources.
122 * This does *NOT* stop mixing, but rather prevents certain property
123 * changes from taking effect. mPropLock must be held when called.
125 void deferUpdates() noexcept
{ mDeferUpdates
= true; }
128 * Resumes update processing after being deferred. mPropLock must be held
131 void processUpdates()
133 if(std::exchange(mDeferUpdates
, false))
138 * Applies all pending updates for the context, listener, effect slots, and
141 void applyAllUpdates();
143 #ifdef __USE_MINGW_ANSI_STDIO
144 [[gnu::format(gnu_printf
, 3, 4)]]
146 [[gnu::format(printf
, 3, 4)]]
148 void setError(ALenum errorCode
, const char *msg
, ...);
150 /* Process-wide current context */
151 static std::atomic
<bool> sGlobalContextLock
;
152 static std::atomic
<ALCcontext
*> sGlobalContext
;
155 /* Thread-local current context. */
156 static thread_local ALCcontext
*sLocalContext
;
158 /* Thread-local context handling. This handles attempting to release the
159 * context which may have been left current when the thread is destroyed.
164 void set(ALCcontext
*ctx
) const noexcept
{ sLocalContext
= ctx
; }
166 static thread_local ThreadCtx sThreadContext
;
169 /* HACK: MinGW generates bad code when accessing an extern thread_local
170 * object. Add a wrapper function for it that only accesses it where it's
174 static ALCcontext
*getThreadContext() noexcept
;
175 static void setThreadContext(ALCcontext
*context
) noexcept
;
177 static ALCcontext
*getThreadContext() noexcept
{ return sLocalContext
; }
178 static void setThreadContext(ALCcontext
*context
) noexcept
{ sThreadContext
.set(context
); }
181 /* Default effect that applies to sources that don't have an effect on send 0. */
182 static ALeffect sDefaultEffect
;
184 DEF_NEWDEL(ALCcontext
)
188 bool has_eax() const noexcept
{ return eax_is_initialized_
; }
189 bool eax_is_capable() const noexcept
;
190 int eax_get_version() const noexcept
{ return eax_version_
; }
192 void eax_uninitialize() noexcept
;
195 const GUID
* property_set_id
,
197 ALuint property_source_id
,
198 ALvoid
* property_value
,
199 ALuint property_value_size
);
202 const GUID
* property_set_id
,
204 ALuint property_source_id
,
205 ALvoid
* property_value
,
206 ALuint property_value_size
);
208 void eax_commit_and_update_sources();
209 void eax_set_last_error() noexcept
;
211 EaxFxSlotIndex
eax_get_primary_fx_slot_index() const noexcept
212 { return eax_primary_fx_slot_index_
; }
214 const ALeffectslot
& eax_get_fx_slot(EaxFxSlotIndexValue fx_slot_index
) const
215 { return eax_fx_slots_
.get(fx_slot_index
); }
216 ALeffectslot
& eax_get_fx_slot(EaxFxSlotIndexValue fx_slot_index
)
217 { return eax_fx_slots_
.get(fx_slot_index
); }
219 void eax_commit_fx_slots()
220 { eax_fx_slots_
.commit(); }
223 static constexpr auto eax_primary_fx_slot_id_dirty_bit
= EaxDirtyFlags
{1} << 0;
224 static constexpr auto eax_distance_factor_dirty_bit
= EaxDirtyFlags
{1} << 1;
225 static constexpr auto eax_air_absorption_hf_dirty_bit
= EaxDirtyFlags
{1} << 2;
226 static constexpr auto eax_hf_reference_dirty_bit
= EaxDirtyFlags
{1} << 3;
227 static constexpr auto eax_macro_fx_factor_dirty_bit
= EaxDirtyFlags
{1} << 4;
229 using Eax4Props
= EAX40CONTEXTPROPERTIES
;
232 Eax4Props i
; // Immediate.
233 Eax4Props d
; // Deferred.
236 using Eax5Props
= EAX50CONTEXTPROPERTIES
;
239 Eax5Props i
; // Immediate.
240 Eax5Props d
; // Deferred.
243 class ContextException
: public EaxException
246 explicit ContextException(const char* message
)
247 : EaxException
{"EAX_CONTEXT", message
}
251 struct Eax4PrimaryFxSlotIdValidator
{
252 void operator()(const GUID
& guidPrimaryFXSlotID
) const
254 if(guidPrimaryFXSlotID
!= EAX_NULL_GUID
&&
255 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX40_FXSlot0
&&
256 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX40_FXSlot1
&&
257 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX40_FXSlot2
&&
258 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX40_FXSlot3
)
260 eax_fail_unknown_primary_fx_slot_id();
265 struct Eax4DistanceFactorValidator
{
266 void operator()(float flDistanceFactor
) const
268 eax_validate_range
<ContextException
>(
271 EAXCONTEXT_MINDISTANCEFACTOR
,
272 EAXCONTEXT_MAXDISTANCEFACTOR
);
276 struct Eax4AirAbsorptionHfValidator
{
277 void operator()(float flAirAbsorptionHF
) const
279 eax_validate_range
<ContextException
>(
282 EAXCONTEXT_MINAIRABSORPTIONHF
,
283 EAXCONTEXT_MAXAIRABSORPTIONHF
);
287 struct Eax4HfReferenceValidator
{
288 void operator()(float flHFReference
) const
290 eax_validate_range
<ContextException
>(
293 EAXCONTEXT_MINHFREFERENCE
,
294 EAXCONTEXT_MAXHFREFERENCE
);
298 struct Eax4AllValidator
{
299 void operator()(const EAX40CONTEXTPROPERTIES
& all
) const
301 Eax4PrimaryFxSlotIdValidator
{}(all
.guidPrimaryFXSlotID
);
302 Eax4DistanceFactorValidator
{}(all
.flDistanceFactor
);
303 Eax4AirAbsorptionHfValidator
{}(all
.flAirAbsorptionHF
);
304 Eax4HfReferenceValidator
{}(all
.flHFReference
);
308 struct Eax5PrimaryFxSlotIdValidator
{
309 void operator()(const GUID
& guidPrimaryFXSlotID
) const
311 if(guidPrimaryFXSlotID
!= EAX_NULL_GUID
&&
312 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX50_FXSlot0
&&
313 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX50_FXSlot1
&&
314 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX50_FXSlot2
&&
315 guidPrimaryFXSlotID
!= EAXPROPERTYID_EAX50_FXSlot3
)
317 eax_fail_unknown_primary_fx_slot_id();
322 struct Eax5MacroFxFactorValidator
{
323 void operator()(float flMacroFXFactor
) const
325 eax_validate_range
<ContextException
>(
328 EAXCONTEXT_MINMACROFXFACTOR
,
329 EAXCONTEXT_MAXMACROFXFACTOR
);
333 struct Eax5AllValidator
{
334 void operator()(const EAX50CONTEXTPROPERTIES
& all
) const
336 Eax5PrimaryFxSlotIdValidator
{}(all
.guidPrimaryFXSlotID
);
337 Eax4DistanceFactorValidator
{}(all
.flDistanceFactor
);
338 Eax4AirAbsorptionHfValidator
{}(all
.flAirAbsorptionHF
);
339 Eax4HfReferenceValidator
{}(all
.flHFReference
);
340 Eax5MacroFxFactorValidator
{}(all
.flMacroFXFactor
);
344 struct Eax5EaxVersionValidator
{
345 void operator()(unsigned long ulEAXVersion
) const
347 eax_validate_range
<ContextException
>(
350 EAXCONTEXT_MINEAXSESSION
,
351 EAXCONTEXT_MAXEAXSESSION
);
355 struct Eax5MaxActiveSendsValidator
{
356 void operator()(unsigned long ulMaxActiveSends
) const
358 eax_validate_range
<ContextException
>(
361 EAXCONTEXT_MINMAXACTIVESENDS
,
362 EAXCONTEXT_MAXMAXACTIVESENDS
);
366 struct Eax5SessionAllValidator
{
367 void operator()(const EAXSESSIONPROPERTIES
& all
) const
369 Eax5EaxVersionValidator
{}(all
.ulEAXVersion
);
370 Eax5MaxActiveSendsValidator
{}(all
.ulMaxActiveSends
);
374 struct Eax5SpeakerConfigValidator
{
375 void operator()(unsigned long ulSpeakerConfig
) const
377 eax_validate_range
<ContextException
>(
380 EAXCONTEXT_MINSPEAKERCONFIG
,
381 EAXCONTEXT_MAXSPEAKERCONFIG
);
385 bool eax_is_initialized_
{};
386 bool eax_is_tried_
{};
388 long eax_last_error_
{};
389 unsigned long eax_speaker_config_
{};
391 EaxFxSlotIndex eax_primary_fx_slot_index_
{};
392 EaxFxSlots eax_fx_slots_
{};
394 int eax_version_
{}; // Current EAX version.
395 EaxDirtyFlags eax_df_
{}; // Dirty flags for the current EAX version.
396 Eax5State eax123_
{}; // EAX1/EAX2/EAX3 state.
397 Eax4State eax4_
{}; // EAX4 state.
398 Eax5State eax5_
{}; // EAX5 state.
399 Eax5Props eax_
{}; // Current EAX state.
400 EAXSESSIONPROPERTIES eax_session_
{};
402 std::string eax_extension_list_
{};
404 [[noreturn
]] static void eax_fail(const char* message
);
405 [[noreturn
]] static void eax_fail_unknown_property_set_id();
406 [[noreturn
]] static void eax_fail_unknown_primary_fx_slot_id();
407 [[noreturn
]] static void eax_fail_unknown_property_id();
408 [[noreturn
]] static void eax_fail_unknown_version();
410 // Gets a value from EAX call,
412 // and updates the current value.
413 template<typename TValidator
, typename TProperty
>
414 static void eax_set(const EaxCall
& call
, TProperty
& property
)
416 const auto& value
= call
.get_value
<ContextException
, const TProperty
>();
421 // Gets a new value from EAX call,
423 // updates the deferred value,
424 // updates a dirty flag.
427 EaxDirtyFlags TDirtyBit
,
428 typename TMemberResult
,
431 void eax_defer(const EaxCall
& call
, TState
& state
, TMemberResult
TProps::*member
) noexcept
433 const auto& src
= call
.get_value
<ContextException
, const TMemberResult
>();
435 const auto& dst_i
= state
.i
.*member
;
436 auto& dst_d
= state
.d
.*member
;
440 eax_df_
|= TDirtyBit
;
444 EaxDirtyFlags TDirtyBit
,
445 typename TMemberResult
,
448 void eax_context_commit_property(TState
& state
, EaxDirtyFlags
& dst_df
,
449 TMemberResult
TProps::*member
) noexcept
451 if((eax_df_
& TDirtyBit
) != EaxDirtyFlags
{})
454 const auto& src_d
= state
.d
.*member
;
455 state
.i
.*member
= src_d
;
456 eax_
.*member
= src_d
;
460 void eax_initialize_extensions();
461 void eax_initialize();
463 bool eax_has_no_default_effect_slot() const noexcept
;
464 void eax_ensure_no_default_effect_slot() const;
465 bool eax_has_enough_aux_sends() const noexcept
;
466 void eax_ensure_enough_aux_sends() const;
467 void eax_ensure_compatibility();
469 unsigned long eax_detect_speaker_configuration() const;
470 void eax_update_speaker_configuration();
472 void eax_set_last_error_defaults() noexcept
;
473 void eax_session_set_defaults() noexcept
;
474 static void eax4_context_set_defaults(Eax4Props
& props
) noexcept
;
475 static void eax4_context_set_defaults(Eax4State
& state
) noexcept
;
476 static void eax5_context_set_defaults(Eax5Props
& props
) noexcept
;
477 static void eax5_context_set_defaults(Eax5State
& state
) noexcept
;
478 void eax4_context_set_current_defaults(const Eax4Props
& props
) noexcept
;
479 void eax5_context_set_current_defaults(const Eax5Props
& props
) noexcept
;
480 void eax_context_set_current_defaults();
481 void eax_context_set_defaults();
482 void eax_set_defaults();
484 void eax_initialize_sources();
486 void eax_dispatch_fx_slot(const EaxCall
& call
);
487 void eax_dispatch_source(const EaxCall
& call
);
489 void eax_get_misc(const EaxCall
& call
);
490 void eax4_get(const EaxCall
& call
, const Eax4Props
& props
);
491 void eax5_get(const EaxCall
& call
, const Eax5Props
& props
);
492 void eax_get(const EaxCall
& call
);
494 void eax_context_commit_primary_fx_slot_id();
495 void eax_context_commit_distance_factor();
496 void eax_context_commit_air_absorbtion_hf();
497 void eax_context_commit_hf_reference();
498 void eax_context_commit_macro_fx_factor();
500 void eax_initialize_fx_slots();
502 void eax_update_sources();
504 void eax_set_misc(const EaxCall
& call
);
505 void eax4_defer_all(const EaxCall
& call
, Eax4State
& state
);
506 void eax4_defer(const EaxCall
& call
, Eax4State
& state
);
507 void eax5_defer_all(const EaxCall
& call
, Eax5State
& state
);
508 void eax5_defer(const EaxCall
& call
, Eax5State
& state
);
509 void eax_set(const EaxCall
& call
);
511 void eax4_context_commit(Eax4State
& state
, EaxDirtyFlags
& dst_df
);
512 void eax5_context_commit(Eax5State
& state
, EaxDirtyFlags
& dst_df
);
513 void eax_context_commit();
518 using ContextRef
= al::intrusive_ptr
<ALCcontext
>;
520 ContextRef
GetContextRef(void);
522 void UpdateContextProps(ALCcontext
*context
);
525 extern bool TrapALError
;
529 ALenum AL_APIENTRY
EAXSet(
530 const GUID
* property_set_id
,
532 ALuint property_source_id
,
533 ALvoid
* property_value
,
534 ALuint property_value_size
) noexcept
;
536 ALenum AL_APIENTRY
EAXGet(
537 const GUID
* property_set_id
,
539 ALuint property_source_id
,
540 ALvoid
* property_value
,
541 ALuint property_value_size
) noexcept
;
544 #endif /* ALC_CONTEXT_H */