]> git.tdb.fi Git - ext/openal.git/blob - alc/context.h
Tweak some types to work around an MSVC compile error
[ext/openal.git] / alc / context.h
1 #ifndef ALC_CONTEXT_H
2 #define ALC_CONTEXT_H
3
4 #include <atomic>
5 #include <memory>
6 #include <mutex>
7 #include <stdint.h>
8 #include <utility>
9
10 #include "AL/al.h"
11 #include "AL/alc.h"
12 #include "AL/alext.h"
13
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"
21
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
29
30 struct ALeffect;
31 struct ALeffectslot;
32 struct ALsource;
33
34 using uint = unsigned int;
35
36
37 struct SourceSubList {
38     uint64_t FreeMask{~0_u64};
39     ALsource *Sources{nullptr}; /* 64 */
40
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();
46
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; }
50 };
51
52 struct EffectSlotSubList {
53     uint64_t FreeMask{~0_u64};
54     ALeffectslot *EffectSlots{nullptr}; /* 64 */
55
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();
62
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; }
66 };
67
68 struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
69     const al::intrusive_ptr<ALCdevice> mALDevice;
70
71
72     bool mPropsDirty{true};
73     bool mDeferUpdates{false};
74
75     std::mutex mPropLock;
76
77     std::atomic<ALenum> mLastError{AL_NO_ERROR};
78
79     DistanceModel mDistanceModel{DistanceModel::Default};
80     bool mSourceDistanceModel{false};
81
82     float mDopplerFactor{1.0f};
83     float mDopplerVelocity{1.0f};
84     float mSpeedOfSound{SpeedOfSoundMetersPerSec};
85     float mAirAbsorptionGainHF{AirAbsorbGainHF};
86
87     std::mutex mEventCbLock;
88     ALEVENTPROCSOFT mEventCb{};
89     void *mEventParam{nullptr};
90
91     ALlistener mListener{};
92
93     al::vector<SourceSubList> mSourceList;
94     ALuint mNumSources{0};
95     std::mutex mSourceLock;
96
97     al::vector<EffectSlotSubList> mEffectSlotList;
98     ALuint mNumEffectSlots{0u};
99     std::mutex mEffectSlotLock;
100
101     /* Default effect slot */
102     std::unique_ptr<ALeffectslot> mDefaultSlot;
103
104     const char *mExtensionList{nullptr};
105
106     std::string mExtensionListOverride{};
107
108
109     ALCcontext(al::intrusive_ptr<ALCdevice> device);
110     ALCcontext(const ALCcontext&) = delete;
111     ALCcontext& operator=(const ALCcontext&) = delete;
112     ~ALCcontext();
113
114     void init();
115     /**
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.
119      */
120     bool deinit();
121
122     /**
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.
126      */
127     void deferUpdates() noexcept { mDeferUpdates = true; }
128
129     /**
130      * Resumes update processing after being deferred. mPropLock must be held
131      * when called.
132      */
133     void processUpdates()
134     {
135         if(std::exchange(mDeferUpdates, false))
136             applyAllUpdates();
137     }
138
139     /**
140      * Applies all pending updates for the context, listener, effect slots, and
141      * sources.
142      */
143     void applyAllUpdates();
144
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, ...);
151
152     /* Process-wide current context */
153     static std::atomic<bool> sGlobalContextLock;
154     static std::atomic<ALCcontext*> sGlobalContext;
155
156 private:
157     /* Thread-local current context. */
158     static thread_local ALCcontext *sLocalContext;
159
160     /* Thread-local context handling. This handles attempting to release the
161      * context which may have been left current when the thread is destroyed.
162      */
163     class ThreadCtx {
164     public:
165         ~ThreadCtx();
166         void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
167     };
168     static thread_local ThreadCtx sThreadContext;
169
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.
174      */
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
182
183     /* Default effect that applies to sources that don't have an effect on send 0. */
184     static ALeffect sDefaultEffect;
185
186     DEF_NEWDEL(ALCcontext)
187
188 #ifdef ALSOFT_EAX
189 public:
190     bool hasEax() const noexcept { return mEaxIsInitialized; }
191     bool eaxIsCapable() const noexcept;
192
193     void eaxUninitialize() noexcept;
194
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);
201
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);
208
209     void eaxSetLastError() noexcept;
210
211     EaxFxSlotIndex eaxGetPrimaryFxSlotIndex() const noexcept
212     { return mEaxPrimaryFxSlotIndex; }
213
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); }
218
219     bool eaxNeedsCommit() const noexcept { return mEaxNeedsCommit; }
220     void eaxCommit();
221
222     void eaxCommitFxSlots()
223     { mEaxFxSlots.commit(); }
224
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;
231
232     using Eax4Props = EAX40CONTEXTPROPERTIES;
233
234     struct Eax4State {
235         Eax4Props i; // Immediate.
236         Eax4Props d; // Deferred.
237     };
238
239     using Eax5Props = EAX50CONTEXTPROPERTIES;
240
241     struct Eax5State {
242         Eax5Props i; // Immediate.
243         Eax5Props d; // Deferred.
244     };
245
246     class ContextException : public EaxException
247     {
248     public:
249         explicit ContextException(const char* message)
250             : EaxException{"EAX_CONTEXT", message}
251         {}
252     };
253
254     struct Eax4PrimaryFxSlotIdValidator {
255         void operator()(const GUID& guidPrimaryFXSlotID) const
256         {
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)
262             {
263                 eax_fail_unknown_primary_fx_slot_id();
264             }
265         }
266     };
267
268     struct Eax4DistanceFactorValidator {
269         void operator()(float flDistanceFactor) const
270         {
271             eax_validate_range<ContextException>(
272                 "Distance Factor",
273                 flDistanceFactor,
274                 EAXCONTEXT_MINDISTANCEFACTOR,
275                 EAXCONTEXT_MAXDISTANCEFACTOR);
276         }
277     };
278
279     struct Eax4AirAbsorptionHfValidator {
280         void operator()(float flAirAbsorptionHF) const
281         {
282             eax_validate_range<ContextException>(
283                 "Air Absorption HF",
284                 flAirAbsorptionHF,
285                 EAXCONTEXT_MINAIRABSORPTIONHF,
286                 EAXCONTEXT_MAXAIRABSORPTIONHF);
287         }
288     };
289
290     struct Eax4HfReferenceValidator {
291         void operator()(float flHFReference) const
292         {
293             eax_validate_range<ContextException>(
294                 "HF Reference",
295                 flHFReference,
296                 EAXCONTEXT_MINHFREFERENCE,
297                 EAXCONTEXT_MAXHFREFERENCE);
298         }
299     };
300
301     struct Eax4AllValidator {
302         void operator()(const EAX40CONTEXTPROPERTIES& all) const
303         {
304             Eax4PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
305             Eax4DistanceFactorValidator{}(all.flDistanceFactor);
306             Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
307             Eax4HfReferenceValidator{}(all.flHFReference);
308         }
309     };
310
311     struct Eax5PrimaryFxSlotIdValidator {
312         void operator()(const GUID& guidPrimaryFXSlotID) const
313         {
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)
319             {
320                 eax_fail_unknown_primary_fx_slot_id();
321             }
322         }
323     };
324
325     struct Eax5MacroFxFactorValidator {
326         void operator()(float flMacroFXFactor) const
327         {
328             eax_validate_range<ContextException>(
329                 "Macro FX Factor",
330                 flMacroFXFactor,
331                 EAXCONTEXT_MINMACROFXFACTOR,
332                 EAXCONTEXT_MAXMACROFXFACTOR);
333         }
334     };
335
336     struct Eax5AllValidator {
337         void operator()(const EAX50CONTEXTPROPERTIES& all) const
338         {
339             Eax5PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
340             Eax4DistanceFactorValidator{}(all.flDistanceFactor);
341             Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
342             Eax4HfReferenceValidator{}(all.flHFReference);
343             Eax5MacroFxFactorValidator{}(all.flMacroFXFactor);
344         }
345     };
346
347     struct Eax5EaxVersionValidator {
348         void operator()(unsigned long ulEAXVersion) const
349         {
350             eax_validate_range<ContextException>(
351                 "EAX version",
352                 ulEAXVersion,
353                 EAXCONTEXT_MINEAXSESSION,
354                 EAXCONTEXT_MAXEAXSESSION);
355         }
356     };
357
358     struct Eax5MaxActiveSendsValidator {
359         void operator()(unsigned long ulMaxActiveSends) const
360         {
361             eax_validate_range<ContextException>(
362                 "Max Active Sends",
363                 ulMaxActiveSends,
364                 EAXCONTEXT_MINMAXACTIVESENDS,
365                 EAXCONTEXT_MAXMAXACTIVESENDS);
366         }
367     };
368
369     struct Eax5SessionAllValidator {
370         void operator()(const EAXSESSIONPROPERTIES& all) const
371         {
372             Eax5EaxVersionValidator{}(all.ulEAXVersion);
373             Eax5MaxActiveSendsValidator{}(all.ulMaxActiveSends);
374         }
375     };
376
377     struct Eax5SpeakerConfigValidator {
378         void operator()(unsigned long ulSpeakerConfig) const
379         {
380             eax_validate_range<ContextException>(
381                 "Speaker Config",
382                 ulSpeakerConfig,
383                 EAXCONTEXT_MINSPEAKERCONFIG,
384                 EAXCONTEXT_MAXSPEAKERCONFIG);
385         }
386     };
387
388     bool mEaxIsInitialized{};
389     bool mEaxIsTried{};
390
391     long mEaxLastError{};
392     unsigned long mEaxSpeakerConfig{};
393
394     EaxFxSlotIndex mEaxPrimaryFxSlotIndex{};
395     EaxFxSlots mEaxFxSlots{};
396
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{};
405
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();
411
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)
417     {
418         const auto& value = call.get_value<ContextException, const TProperty>();
419         TValidator{}(value);
420         property = value;
421     }
422
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
434     {
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;
440
441         if(dst_i != dst_d)
442             mEaxDf |= TDirtyBit;
443     }
444
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
452     {
453         if((mEaxDf & TDirtyBit) != EaxDirtyFlags{})
454         {
455             dst_df |= TDirtyBit;
456             const auto& src_d = state.d.*member;
457             state.i.*member = src_d;
458             mEax.*member = src_d;
459         }
460     }
461
462     void eax_initialize_extensions();
463     void eax_initialize();
464
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();
470
471     unsigned long eax_detect_speaker_configuration() const;
472     void eax_update_speaker_configuration();
473
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();
482
483     void eax_dispatch_fx_slot(const EaxCall& call);
484     void eax_dispatch_source(const EaxCall& call);
485
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);
490
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();
496
497     void eax_initialize_fx_slots();
498
499     void eax_update_sources();
500
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);
507
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
512 };
513
514 using ContextRef = al::intrusive_ptr<ALCcontext>;
515
516 ContextRef GetContextRef(void);
517
518 void UpdateContextProps(ALCcontext *context);
519
520
521 extern bool TrapALError;
522
523
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;
531
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
539
540 #endif /* ALC_CONTEXT_H */