Handle AL effect properties with a variant
[openal-soft.git] / alc / context.h
blobe1437fb31f8fc1165b9676a720add724e384c091
1 #ifndef ALC_CONTEXT_H
2 #define ALC_CONTEXT_H
4 #include <array>
5 #include <atomic>
6 #include <cstdint>
7 #include <deque>
8 #include <memory>
9 #include <mutex>
10 #include <string>
11 #include <string_view>
12 #include <unordered_map>
13 #include <utility>
14 #include <vector>
16 #include "AL/al.h"
17 #include "AL/alc.h"
18 #include "AL/alext.h"
20 #include "al/listener.h"
21 #include "almalloc.h"
22 #include "alnumeric.h"
23 #include "althreads.h"
24 #include "atomic.h"
25 #include "core/context.h"
26 #include "inprogext.h"
27 #include "intrusive_ptr.h"
29 #ifdef ALSOFT_EAX
30 #include "al/eax/call.h"
31 #include "al/eax/exception.h"
32 #include "al/eax/fx_slot_index.h"
33 #include "al/eax/fx_slots.h"
34 #include "al/eax/utils.h"
35 #endif // ALSOFT_EAX
37 struct ALeffect;
38 struct ALeffectslot;
39 struct ALsource;
40 struct DebugGroup;
41 struct EffectSlotSubList;
42 struct SourceSubList;
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;
51 enum ContextFlags {
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;
61 const uint mId;
63 std::string mMessage;
65 template<typename T>
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)}
69 { }
70 DebugLogEntry(const DebugLogEntry&) = default;
71 DebugLogEntry(DebugLogEntry&&) = default;
75 struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
76 const al::intrusive_ptr<ALCdevice> mALDevice;
79 bool mPropsDirty{true};
80 bool mDeferUpdates{false};
82 std::mutex mPropLock;
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;
129 ~ALCcontext();
131 void init();
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.
137 void deinit();
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
148 * when called.
150 void processUpdates()
152 if(std::exchange(mDeferUpdates, false))
153 applyAllUpdates();
157 * Applies all pending updates for the context, listener, effect slots, and
158 * sources.
160 void applyAllUpdates();
162 #ifdef __MINGW32__
163 [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
164 #else
165 [[gnu::format(printf, 3, 4)]]
166 #endif
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
176 return;
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;
185 private:
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.
192 class ThreadCtx {
193 public:
194 ~ThreadCtx();
195 /* NOLINTBEGIN(readability-convert-member-functions-to-static)
196 * This should be non-static to invoke construction of the thread-local
197 * sThreadContext, so that it's destructor gets run at thread exit to
198 * clear sLocalContext (which isn't a member variable to make read
199 * access efficient).
201 void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
202 /* NOLINTEND(readability-convert-member-functions-to-static) */
204 static thread_local ThreadCtx sThreadContext;
206 public:
207 static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
208 static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
210 /* Default effect that applies to sources that don't have an effect on send 0. */
211 static ALeffect sDefaultEffect;
213 #ifdef ALSOFT_EAX
214 bool hasEax() const noexcept { return mEaxIsInitialized; }
215 bool eaxIsCapable() const noexcept;
217 void eaxUninitialize() noexcept;
219 ALenum eax_eax_set(
220 const GUID* property_set_id,
221 ALuint property_id,
222 ALuint property_source_id,
223 ALvoid* property_value,
224 ALuint property_value_size);
226 ALenum eax_eax_get(
227 const GUID* property_set_id,
228 ALuint property_id,
229 ALuint property_source_id,
230 ALvoid* property_value,
231 ALuint property_value_size);
233 void eaxSetLastError() noexcept;
235 EaxFxSlotIndex eaxGetPrimaryFxSlotIndex() const noexcept
236 { return mEaxPrimaryFxSlotIndex; }
238 const ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index) const
239 { return mEaxFxSlots.get(fx_slot_index); }
240 ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index)
241 { return mEaxFxSlots.get(fx_slot_index); }
243 bool eaxNeedsCommit() const noexcept { return mEaxNeedsCommit; }
244 void eaxCommit();
246 void eaxCommitFxSlots()
247 { mEaxFxSlots.commit(); }
249 private:
250 static constexpr auto eax_primary_fx_slot_id_dirty_bit = EaxDirtyFlags{1} << 0;
251 static constexpr auto eax_distance_factor_dirty_bit = EaxDirtyFlags{1} << 1;
252 static constexpr auto eax_air_absorption_hf_dirty_bit = EaxDirtyFlags{1} << 2;
253 static constexpr auto eax_hf_reference_dirty_bit = EaxDirtyFlags{1} << 3;
254 static constexpr auto eax_macro_fx_factor_dirty_bit = EaxDirtyFlags{1} << 4;
256 using Eax4Props = EAX40CONTEXTPROPERTIES;
258 struct Eax4State {
259 Eax4Props i; // Immediate.
260 Eax4Props d; // Deferred.
263 using Eax5Props = EAX50CONTEXTPROPERTIES;
265 struct Eax5State {
266 Eax5Props i; // Immediate.
267 Eax5Props d; // Deferred.
270 class ContextException : public EaxException
272 public:
273 explicit ContextException(const char* message)
274 : EaxException{"EAX_CONTEXT", message}
278 struct Eax4PrimaryFxSlotIdValidator {
279 void operator()(const GUID& guidPrimaryFXSlotID) const
281 if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
282 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot0 &&
283 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot1 &&
284 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot2 &&
285 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot3)
287 eax_fail_unknown_primary_fx_slot_id();
292 struct Eax4DistanceFactorValidator {
293 void operator()(float flDistanceFactor) const
295 eax_validate_range<ContextException>(
296 "Distance Factor",
297 flDistanceFactor,
298 EAXCONTEXT_MINDISTANCEFACTOR,
299 EAXCONTEXT_MAXDISTANCEFACTOR);
303 struct Eax4AirAbsorptionHfValidator {
304 void operator()(float flAirAbsorptionHF) const
306 eax_validate_range<ContextException>(
307 "Air Absorption HF",
308 flAirAbsorptionHF,
309 EAXCONTEXT_MINAIRABSORPTIONHF,
310 EAXCONTEXT_MAXAIRABSORPTIONHF);
314 struct Eax4HfReferenceValidator {
315 void operator()(float flHFReference) const
317 eax_validate_range<ContextException>(
318 "HF Reference",
319 flHFReference,
320 EAXCONTEXT_MINHFREFERENCE,
321 EAXCONTEXT_MAXHFREFERENCE);
325 struct Eax4AllValidator {
326 void operator()(const EAX40CONTEXTPROPERTIES& all) const
328 Eax4PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
329 Eax4DistanceFactorValidator{}(all.flDistanceFactor);
330 Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
331 Eax4HfReferenceValidator{}(all.flHFReference);
335 struct Eax5PrimaryFxSlotIdValidator {
336 void operator()(const GUID& guidPrimaryFXSlotID) const
338 if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
339 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot0 &&
340 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot1 &&
341 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot2 &&
342 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot3)
344 eax_fail_unknown_primary_fx_slot_id();
349 struct Eax5MacroFxFactorValidator {
350 void operator()(float flMacroFXFactor) const
352 eax_validate_range<ContextException>(
353 "Macro FX Factor",
354 flMacroFXFactor,
355 EAXCONTEXT_MINMACROFXFACTOR,
356 EAXCONTEXT_MAXMACROFXFACTOR);
360 struct Eax5AllValidator {
361 void operator()(const EAX50CONTEXTPROPERTIES& all) const
363 Eax5PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
364 Eax4DistanceFactorValidator{}(all.flDistanceFactor);
365 Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
366 Eax4HfReferenceValidator{}(all.flHFReference);
367 Eax5MacroFxFactorValidator{}(all.flMacroFXFactor);
371 struct Eax5EaxVersionValidator {
372 void operator()(unsigned long ulEAXVersion) const
374 eax_validate_range<ContextException>(
375 "EAX version",
376 ulEAXVersion,
377 EAXCONTEXT_MINEAXSESSION,
378 EAXCONTEXT_MAXEAXSESSION);
382 struct Eax5MaxActiveSendsValidator {
383 void operator()(unsigned long ulMaxActiveSends) const
385 eax_validate_range<ContextException>(
386 "Max Active Sends",
387 ulMaxActiveSends,
388 EAXCONTEXT_MINMAXACTIVESENDS,
389 EAXCONTEXT_MAXMAXACTIVESENDS);
393 struct Eax5SessionAllValidator {
394 void operator()(const EAXSESSIONPROPERTIES& all) const
396 Eax5EaxVersionValidator{}(all.ulEAXVersion);
397 Eax5MaxActiveSendsValidator{}(all.ulMaxActiveSends);
401 struct Eax5SpeakerConfigValidator {
402 void operator()(unsigned long ulSpeakerConfig) const
404 eax_validate_range<ContextException>(
405 "Speaker Config",
406 ulSpeakerConfig,
407 EAXCONTEXT_MINSPEAKERCONFIG,
408 EAXCONTEXT_MAXSPEAKERCONFIG);
412 bool mEaxIsInitialized{};
413 bool mEaxIsTried{};
415 long mEaxLastError{};
416 unsigned long mEaxSpeakerConfig{};
418 EaxFxSlotIndex mEaxPrimaryFxSlotIndex{};
419 EaxFxSlots mEaxFxSlots{};
421 int mEaxVersion{}; // Current EAX version.
422 bool mEaxNeedsCommit{};
423 EaxDirtyFlags mEaxDf{}; // Dirty flags for the current EAX version.
424 Eax5State mEax123{}; // EAX1/EAX2/EAX3 state.
425 Eax4State mEax4{}; // EAX4 state.
426 Eax5State mEax5{}; // EAX5 state.
427 Eax5Props mEax{}; // Current EAX state.
428 EAXSESSIONPROPERTIES mEaxSession{};
430 [[noreturn]] static void eax_fail(const char* message);
431 [[noreturn]] static void eax_fail_unknown_property_set_id();
432 [[noreturn]] static void eax_fail_unknown_primary_fx_slot_id();
433 [[noreturn]] static void eax_fail_unknown_property_id();
434 [[noreturn]] static void eax_fail_unknown_version();
436 // Gets a value from EAX call,
437 // validates it,
438 // and updates the current value.
439 template<typename TValidator, typename TProperty>
440 static void eax_set(const EaxCall& call, TProperty& property)
442 const auto& value = call.get_value<ContextException, const TProperty>();
443 TValidator{}(value);
444 property = value;
447 // Gets a new value from EAX call,
448 // validates it,
449 // updates the deferred value,
450 // updates a dirty flag.
451 template<
452 typename TValidator,
453 EaxDirtyFlags TDirtyBit,
454 typename TMemberResult,
455 typename TProps,
456 typename TState>
457 void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member)
459 const auto& src = call.get_value<ContextException, const TMemberResult>();
460 TValidator{}(src);
461 const auto& dst_i = state.i.*member;
462 auto& dst_d = state.d.*member;
463 dst_d = src;
465 if(dst_i != dst_d)
466 mEaxDf |= TDirtyBit;
469 template<
470 EaxDirtyFlags TDirtyBit,
471 typename TMemberResult,
472 typename TProps,
473 typename TState>
474 void eax_context_commit_property(TState& state, EaxDirtyFlags& dst_df,
475 TMemberResult TProps::*member) noexcept
477 if((mEaxDf & TDirtyBit) != EaxDirtyFlags{})
479 dst_df |= TDirtyBit;
480 const auto& src_d = state.d.*member;
481 state.i.*member = src_d;
482 mEax.*member = src_d;
486 void eax_initialize_extensions();
487 void eax_initialize();
489 bool eax_has_no_default_effect_slot() const noexcept;
490 void eax_ensure_no_default_effect_slot() const;
491 bool eax_has_enough_aux_sends() const noexcept;
492 void eax_ensure_enough_aux_sends() const;
493 void eax_ensure_compatibility();
495 unsigned long eax_detect_speaker_configuration() const;
496 void eax_update_speaker_configuration();
498 void eax_set_last_error_defaults() noexcept;
499 void eax_session_set_defaults() noexcept;
500 static void eax4_context_set_defaults(Eax4Props& props) noexcept;
501 static void eax4_context_set_defaults(Eax4State& state) noexcept;
502 static void eax5_context_set_defaults(Eax5Props& props) noexcept;
503 static void eax5_context_set_defaults(Eax5State& state) noexcept;
504 void eax_context_set_defaults();
505 void eax_set_defaults();
507 void eax_dispatch_fx_slot(const EaxCall& call);
508 void eax_dispatch_source(const EaxCall& call);
510 void eax_get_misc(const EaxCall& call);
511 void eax4_get(const EaxCall& call, const Eax4Props& props);
512 void eax5_get(const EaxCall& call, const Eax5Props& props);
513 void eax_get(const EaxCall& call);
515 void eax_context_commit_primary_fx_slot_id();
516 void eax_context_commit_distance_factor();
517 void eax_context_commit_air_absorbtion_hf();
518 void eax_context_commit_hf_reference();
519 void eax_context_commit_macro_fx_factor();
521 void eax_initialize_fx_slots();
523 void eax_update_sources();
525 void eax_set_misc(const EaxCall& call);
526 void eax4_defer_all(const EaxCall& call, Eax4State& state);
527 void eax4_defer(const EaxCall& call, Eax4State& state);
528 void eax5_defer_all(const EaxCall& call, Eax5State& state);
529 void eax5_defer(const EaxCall& call, Eax5State& state);
530 void eax_set(const EaxCall& call);
532 void eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df);
533 void eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df);
534 void eax_context_commit();
535 #endif // ALSOFT_EAX
538 using ContextRef = al::intrusive_ptr<ALCcontext>;
540 ContextRef GetContextRef() noexcept;
542 void UpdateContextProps(ALCcontext *context);
545 inline bool TrapALError{false};
548 #ifdef ALSOFT_EAX
549 auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id,
550 ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
552 auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id,
553 ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
554 #endif // ALSOFT_EAX
556 #endif /* ALC_CONTEXT_H */