Scale B-Format panning coefficients only when needed
[openal-soft.git] / alc / context.h
blobe8efdbf1d41965f0ef0aab4fd4654f099d5bc4e6
1 #ifndef ALC_CONTEXT_H
2 #define ALC_CONTEXT_H
4 #include <atomic>
5 #include <memory>
6 #include <mutex>
7 #include <stdint.h>
8 #include <utility>
10 #include "AL/al.h"
11 #include "AL/alc.h"
12 #include "AL/alext.h"
14 #include "al/listener.h"
15 #include "almalloc.h"
16 #include "alnumeric.h"
17 #include "atomic.h"
18 #include "core/context.h"
19 #include "intrusive_ptr.h"
20 #include "vector.h"
22 #ifdef ALSOFT_EAX
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"
28 #endif // ALSOFT_EAX
30 struct ALeffect;
31 struct ALeffectslot;
32 struct ALsource;
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; }
45 ~SourceSubList();
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; }
61 ~EffectSlotSubList();
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};
75 std::mutex mPropLock;
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;
112 ~ALCcontext();
114 void init();
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.
120 bool deinit();
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
131 * when called.
133 void processUpdates()
135 if(std::exchange(mDeferUpdates, false))
136 applyAllUpdates();
140 * Applies all pending updates for the context, listener, effect slots, and
141 * sources.
143 void applyAllUpdates();
145 #ifdef __USE_MINGW_ANSI_STDIO
146 [[gnu::format(gnu_printf, 3, 4)]]
147 #else
148 [[gnu::format(printf, 3, 4)]]
149 #endif
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;
156 private:
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.
163 class ThreadCtx {
164 public:
165 ~ThreadCtx();
166 void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
168 static thread_local ThreadCtx sThreadContext;
170 public:
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
173 * defined.
175 #ifdef __MINGW32__
176 static ALCcontext *getThreadContext() noexcept;
177 static void setThreadContext(ALCcontext *context) noexcept;
178 #else
179 static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
180 static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
181 #endif
183 /* Default effect that applies to sources that don't have an effect on send 0. */
184 static ALeffect sDefaultEffect;
186 DEF_NEWDEL(ALCcontext)
188 #ifdef ALSOFT_EAX
189 public:
190 bool hasEax() const noexcept { return mEaxIsInitialized; }
191 bool eaxIsCapable() const noexcept;
193 void eaxUninitialize() noexcept;
195 ALenum eax_eax_set(
196 const GUID* property_set_id,
197 ALuint property_id,
198 ALuint property_source_id,
199 ALvoid* property_value,
200 ALuint property_value_size);
202 ALenum eax_eax_get(
203 const GUID* property_set_id,
204 ALuint property_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; }
220 void eaxCommit();
222 void eaxCommitFxSlots()
223 { mEaxFxSlots.commit(); }
225 private:
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;
234 struct Eax4State {
235 Eax4Props i; // Immediate.
236 Eax4Props d; // Deferred.
239 using Eax5Props = EAX50CONTEXTPROPERTIES;
241 struct Eax5State {
242 Eax5Props i; // Immediate.
243 Eax5Props d; // Deferred.
246 class ContextException : public EaxException
248 public:
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>(
272 "Distance Factor",
273 flDistanceFactor,
274 EAXCONTEXT_MINDISTANCEFACTOR,
275 EAXCONTEXT_MAXDISTANCEFACTOR);
279 struct Eax4AirAbsorptionHfValidator {
280 void operator()(float flAirAbsorptionHF) const
282 eax_validate_range<ContextException>(
283 "Air Absorption HF",
284 flAirAbsorptionHF,
285 EAXCONTEXT_MINAIRABSORPTIONHF,
286 EAXCONTEXT_MAXAIRABSORPTIONHF);
290 struct Eax4HfReferenceValidator {
291 void operator()(float flHFReference) const
293 eax_validate_range<ContextException>(
294 "HF Reference",
295 flHFReference,
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>(
329 "Macro FX Factor",
330 flMacroFXFactor,
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>(
351 "EAX version",
352 ulEAXVersion,
353 EAXCONTEXT_MINEAXSESSION,
354 EAXCONTEXT_MAXEAXSESSION);
358 struct Eax5MaxActiveSendsValidator {
359 void operator()(unsigned long ulMaxActiveSends) const
361 eax_validate_range<ContextException>(
362 "Max Active Sends",
363 ulMaxActiveSends,
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>(
381 "Speaker Config",
382 ulSpeakerConfig,
383 EAXCONTEXT_MINSPEAKERCONFIG,
384 EAXCONTEXT_MAXSPEAKERCONFIG);
388 bool mEaxIsInitialized{};
389 bool mEaxIsTried{};
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,
413 // validates it,
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>();
419 TValidator{}(value);
420 property = value;
423 // Gets a new value from EAX call,
424 // validates it,
425 // updates the deferred value,
426 // updates a dirty flag.
427 template<
428 typename TValidator,
429 EaxDirtyFlags TDirtyBit,
430 typename TMemberResult,
431 typename TProps,
432 typename TState>
433 void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member) noexcept
435 const auto& src = call.get_value<ContextException, const TMemberResult>();
436 TValidator{}(src);
437 const auto& dst_i = state.i.*member;
438 auto& dst_d = state.d.*member;
439 dst_d = src;
441 if(dst_i != dst_d)
442 mEaxDf |= TDirtyBit;
445 template<
446 EaxDirtyFlags TDirtyBit,
447 typename TMemberResult,
448 typename TProps,
449 typename TState>
450 void eax_context_commit_property(TState& state, EaxDirtyFlags& dst_df,
451 TMemberResult TProps::*member) noexcept
453 if((mEaxDf & TDirtyBit) != EaxDirtyFlags{})
455 dst_df |= TDirtyBit;
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();
511 #endif // ALSOFT_EAX
514 using ContextRef = al::intrusive_ptr<ALCcontext>;
516 ContextRef GetContextRef(void);
518 void UpdateContextProps(ALCcontext *context);
521 extern bool TrapALError;
524 #ifdef ALSOFT_EAX
525 ALenum AL_APIENTRY EAXSet(
526 const GUID* property_set_id,
527 ALuint property_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,
534 ALuint property_id,
535 ALuint property_source_id,
536 ALvoid* property_value,
537 ALuint property_value_size) noexcept;
538 #endif // ALSOFT_EAX
540 #endif /* ALC_CONTEXT_H */