Separate ALCdevice from the implementation
[openal-soft.git] / alc / context.h
blobe9d8db6ac442d5fdc3d8e71a66cad0a83de0b2e0
1 #ifndef ALC_CONTEXT_H
2 #define ALC_CONTEXT_H
4 #include "config.h"
6 #include <atomic>
7 #include <bitset>
8 #include <cstdint>
9 #include <deque>
10 #include <memory>
11 #include <mutex>
12 #include <string>
13 #include <string_view>
14 #include <unordered_map>
15 #include <utility>
16 #include <vector>
18 #include "AL/al.h"
19 #include "AL/alc.h"
20 #include "AL/alext.h"
22 #include "al/listener.h"
23 #include "althreads.h"
24 #include "core/context.h"
25 #include "intrusive_ptr.h"
26 #include "opthelpers.h"
28 #if ALSOFT_EAX
29 #include "al/eax/api.h"
30 #include "al/eax/exception.h"
31 #include "al/eax/fx_slot_index.h"
32 #include "al/eax/fx_slots.h"
33 #include "al/eax/utils.h"
35 class EaxCall;
36 #endif // ALSOFT_EAX
38 struct ALeffect;
39 struct ALeffectslot;
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 namespace al {
76 struct Device;
77 } // namespace al
79 struct ALCcontext final : public al::intrusive_ref<ALCcontext>, ContextBase {
80 const al::intrusive_ptr<al::Device> mALDevice;
82 bool mPropsDirty{true};
83 bool mDeferUpdates{false};
85 std::mutex mPropLock;
87 al::tss<ALenum> mLastThreadError{AL_NO_ERROR};
89 const ContextFlagBitset mContextFlags;
90 std::atomic<bool> mDebugEnabled{false};
92 DistanceModel mDistanceModel{DistanceModel::Default};
93 bool mSourceDistanceModel{false};
95 float mDopplerFactor{1.0f};
96 float mDopplerVelocity{1.0f};
97 float mSpeedOfSound{SpeedOfSoundMetersPerSec};
98 float mAirAbsorptionGainHF{AirAbsorbGainHF};
100 std::mutex mEventCbLock;
101 ALEVENTPROCSOFT mEventCb{};
102 void *mEventParam{nullptr};
104 std::mutex mDebugCbLock;
105 ALDEBUGPROCEXT mDebugCb{};
106 void *mDebugParam{nullptr};
107 std::vector<DebugGroup> mDebugGroups;
108 std::deque<DebugLogEntry> mDebugLog;
110 ALlistener mListener{};
112 std::vector<SourceSubList> mSourceList;
113 ALuint mNumSources{0};
114 std::mutex mSourceLock;
116 std::vector<EffectSlotSubList> mEffectSlotList;
117 ALuint mNumEffectSlots{0u};
118 std::mutex mEffectSlotLock;
120 /* Default effect slot */
121 std::unique_ptr<ALeffectslot> mDefaultSlot;
123 std::vector<std::string_view> mExtensions;
124 std::string mExtensionsString{};
126 std::unordered_map<ALuint,std::string> mSourceNames;
127 std::unordered_map<ALuint,std::string> mEffectSlotNames;
129 ALCcontext(al::intrusive_ptr<al::Device> device, ContextFlagBitset flags);
130 ALCcontext(const ALCcontext&) = delete;
131 ALCcontext& operator=(const ALCcontext&) = delete;
132 ~ALCcontext() final;
134 void init();
136 * Removes the context from its device and removes it from being current on
137 * the running thread or globally. Stops device playback if this was the
138 * last context on its device.
140 void deinit();
143 * Defers/suspends updates for the given context's listener and sources.
144 * This does *NOT* stop mixing, but rather prevents certain property
145 * changes from taking effect. mPropLock must be held when called.
147 void deferUpdates() noexcept { mDeferUpdates = true; }
150 * Resumes update processing after being deferred. mPropLock must be held
151 * when called.
153 void processUpdates()
155 if(std::exchange(mDeferUpdates, false))
156 applyAllUpdates();
160 * Applies all pending updates for the context, listener, effect slots, and
161 * sources.
163 void applyAllUpdates();
165 #ifdef __MINGW32__
166 [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
167 #else
168 [[gnu::format(printf, 3, 4)]]
169 #endif
170 void setError(ALenum errorCode, const char *msg, ...);
172 void sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
173 DebugType type, ALuint id, DebugSeverity severity, std::string_view message);
175 void debugMessage(DebugSource source, DebugType type, ALuint id, DebugSeverity severity,
176 std::string_view message)
178 if(!mDebugEnabled.load(std::memory_order_relaxed)) LIKELY
179 return;
180 std::unique_lock<std::mutex> debuglock{mDebugCbLock};
181 sendDebugMessage(debuglock, source, type, id, severity, message);
184 /* Process-wide current context */
185 static std::atomic<bool> sGlobalContextLock;
186 static std::atomic<ALCcontext*> sGlobalContext;
188 private:
189 /* Thread-local current context. */
190 static inline thread_local ALCcontext *sLocalContext{};
192 /* Thread-local context handling. This handles attempting to release the
193 * context which may have been left current when the thread is destroyed.
195 class ThreadCtx {
196 public:
197 ThreadCtx() = default;
198 ThreadCtx(const ThreadCtx&) = delete;
199 auto operator=(const ThreadCtx&) -> ThreadCtx& = delete;
201 ~ThreadCtx();
202 /* NOLINTBEGIN(readability-convert-member-functions-to-static)
203 * This should be non-static to invoke construction of the thread-local
204 * sThreadContext, so that it's destructor gets run at thread exit to
205 * clear sLocalContext (which isn't a member variable to make read
206 * access efficient).
208 void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
209 /* NOLINTEND(readability-convert-member-functions-to-static) */
211 static thread_local ThreadCtx sThreadContext;
213 public:
214 static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
215 static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
217 /* Default effect that applies to sources that don't have an effect on send 0. */
218 static ALeffect sDefaultEffect;
220 #if ALSOFT_EAX
221 bool hasEax() const noexcept { return mEaxIsInitialized; }
222 bool eaxIsCapable() const noexcept;
224 void eaxUninitialize() noexcept;
226 ALenum eax_eax_set(
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 ALenum eax_eax_get(
234 const GUID* property_set_id,
235 ALuint property_id,
236 ALuint property_source_id,
237 ALvoid* property_value,
238 ALuint property_value_size);
240 void eaxSetLastError() noexcept;
242 [[nodiscard]]
243 auto eaxGetDistanceFactor() const noexcept -> float { return mEax.flDistanceFactor; }
245 [[nodiscard]]
246 auto eaxGetPrimaryFxSlotIndex() const noexcept -> EaxFxSlotIndex
247 { return mEaxPrimaryFxSlotIndex; }
249 const ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index) const
250 { return mEaxFxSlots.get(fx_slot_index); }
251 ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index)
252 { return mEaxFxSlots.get(fx_slot_index); }
254 bool eaxNeedsCommit() const noexcept { return mEaxNeedsCommit; }
255 void eaxCommit();
257 void eaxCommitFxSlots()
258 { mEaxFxSlots.commit(); }
260 private:
261 static constexpr auto eax_primary_fx_slot_id_dirty_bit = EaxDirtyFlags{1} << 0;
262 static constexpr auto eax_distance_factor_dirty_bit = EaxDirtyFlags{1} << 1;
263 static constexpr auto eax_air_absorption_hf_dirty_bit = EaxDirtyFlags{1} << 2;
264 static constexpr auto eax_hf_reference_dirty_bit = EaxDirtyFlags{1} << 3;
265 static constexpr auto eax_macro_fx_factor_dirty_bit = EaxDirtyFlags{1} << 4;
267 using Eax4Props = EAX40CONTEXTPROPERTIES;
269 struct Eax4State {
270 Eax4Props i; // Immediate.
271 Eax4Props d; // Deferred.
274 using Eax5Props = EAX50CONTEXTPROPERTIES;
276 struct Eax5State {
277 Eax5Props i; // Immediate.
278 Eax5Props d; // Deferred.
281 class ContextException final : public EaxException {
282 public:
283 explicit ContextException(const char *message)
284 : EaxException{"EAX_CONTEXT", message}
288 struct Eax4PrimaryFxSlotIdValidator {
289 void operator()(const GUID& guidPrimaryFXSlotID) const
291 if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
292 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot0 &&
293 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot1 &&
294 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot2 &&
295 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot3)
297 eax_fail_unknown_primary_fx_slot_id();
302 struct Eax4DistanceFactorValidator {
303 void operator()(float flDistanceFactor) const
305 eax_validate_range<ContextException>(
306 "Distance Factor",
307 flDistanceFactor,
308 EAXCONTEXT_MINDISTANCEFACTOR,
309 EAXCONTEXT_MAXDISTANCEFACTOR);
313 struct Eax4AirAbsorptionHfValidator {
314 void operator()(float flAirAbsorptionHF) const
316 eax_validate_range<ContextException>(
317 "Air Absorption HF",
318 flAirAbsorptionHF,
319 EAXCONTEXT_MINAIRABSORPTIONHF,
320 EAXCONTEXT_MAXAIRABSORPTIONHF);
324 struct Eax4HfReferenceValidator {
325 void operator()(float flHFReference) const
327 eax_validate_range<ContextException>(
328 "HF Reference",
329 flHFReference,
330 EAXCONTEXT_MINHFREFERENCE,
331 EAXCONTEXT_MAXHFREFERENCE);
335 struct Eax4AllValidator {
336 void operator()(const EAX40CONTEXTPROPERTIES& all) const
338 Eax4PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
339 Eax4DistanceFactorValidator{}(all.flDistanceFactor);
340 Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
341 Eax4HfReferenceValidator{}(all.flHFReference);
345 struct Eax5PrimaryFxSlotIdValidator {
346 void operator()(const GUID& guidPrimaryFXSlotID) const
348 if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
349 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot0 &&
350 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot1 &&
351 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot2 &&
352 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot3)
354 eax_fail_unknown_primary_fx_slot_id();
359 struct Eax5MacroFxFactorValidator {
360 void operator()(float flMacroFXFactor) const
362 eax_validate_range<ContextException>(
363 "Macro FX Factor",
364 flMacroFXFactor,
365 EAXCONTEXT_MINMACROFXFACTOR,
366 EAXCONTEXT_MAXMACROFXFACTOR);
370 struct Eax5AllValidator {
371 void operator()(const EAX50CONTEXTPROPERTIES& all) const
373 Eax5PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
374 Eax4DistanceFactorValidator{}(all.flDistanceFactor);
375 Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
376 Eax4HfReferenceValidator{}(all.flHFReference);
377 Eax5MacroFxFactorValidator{}(all.flMacroFXFactor);
381 struct Eax5EaxVersionValidator {
382 void operator()(unsigned long ulEAXVersion) const
384 eax_validate_range<ContextException>(
385 "EAX version",
386 ulEAXVersion,
387 EAXCONTEXT_MINEAXSESSION,
388 EAXCONTEXT_MAXEAXSESSION);
392 struct Eax5MaxActiveSendsValidator {
393 void operator()(unsigned long ulMaxActiveSends) const
395 eax_validate_range<ContextException>(
396 "Max Active Sends",
397 ulMaxActiveSends,
398 EAXCONTEXT_MINMAXACTIVESENDS,
399 EAXCONTEXT_MAXMAXACTIVESENDS);
403 struct Eax5SessionAllValidator {
404 void operator()(const EAXSESSIONPROPERTIES& all) const
406 Eax5EaxVersionValidator{}(all.ulEAXVersion);
407 Eax5MaxActiveSendsValidator{}(all.ulMaxActiveSends);
411 struct Eax5SpeakerConfigValidator {
412 void operator()(unsigned long ulSpeakerConfig) const
414 eax_validate_range<ContextException>(
415 "Speaker Config",
416 ulSpeakerConfig,
417 EAXCONTEXT_MINSPEAKERCONFIG,
418 EAXCONTEXT_MAXSPEAKERCONFIG);
422 bool mEaxIsInitialized{};
423 bool mEaxIsTried{};
425 long mEaxLastError{};
426 unsigned long mEaxSpeakerConfig{};
428 EaxFxSlotIndex mEaxPrimaryFxSlotIndex{};
429 EaxFxSlots mEaxFxSlots{};
431 int mEaxVersion{}; // Current EAX version.
432 bool mEaxNeedsCommit{};
433 EaxDirtyFlags mEaxDf{}; // Dirty flags for the current EAX version.
434 Eax5State mEax123{}; // EAX1/EAX2/EAX3 state.
435 Eax4State mEax4{}; // EAX4 state.
436 Eax5State mEax5{}; // EAX5 state.
437 Eax5Props mEax{}; // Current EAX state.
438 EAXSESSIONPROPERTIES mEaxSession{};
440 [[noreturn]] static void eax_fail(const char* message);
441 [[noreturn]] static void eax_fail_unknown_property_set_id();
442 [[noreturn]] static void eax_fail_unknown_primary_fx_slot_id();
443 [[noreturn]] static void eax_fail_unknown_property_id();
444 [[noreturn]] static void eax_fail_unknown_version();
446 // Gets a value from EAX call,
447 // validates it,
448 // and updates the current value.
449 template<typename TValidator, typename TProperty>
450 static void eax_set(const EaxCall& call, TProperty& property)
452 const auto& value = call.get_value<ContextException, const TProperty>();
453 TValidator{}(value);
454 property = value;
457 // Gets a new value from EAX call,
458 // validates it,
459 // updates the deferred value,
460 // updates a dirty flag.
461 template<
462 typename TValidator,
463 EaxDirtyFlags TDirtyBit,
464 typename TMemberResult,
465 typename TProps,
466 typename TState>
467 void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member)
469 const auto& src = call.get_value<ContextException, const TMemberResult>();
470 TValidator{}(src);
471 const auto& dst_i = state.i.*member;
472 auto& dst_d = state.d.*member;
473 dst_d = src;
475 if(dst_i != dst_d)
476 mEaxDf |= TDirtyBit;
479 template<
480 EaxDirtyFlags TDirtyBit,
481 typename TMemberResult,
482 typename TProps,
483 typename TState>
484 void eax_context_commit_property(TState& state, EaxDirtyFlags& dst_df,
485 TMemberResult TProps::*member) noexcept
487 if((mEaxDf & TDirtyBit) != EaxDirtyFlags{})
489 dst_df |= TDirtyBit;
490 const auto& src_d = state.d.*member;
491 state.i.*member = src_d;
492 mEax.*member = src_d;
496 void eax_initialize_extensions();
497 void eax_initialize();
499 bool eax_has_no_default_effect_slot() const noexcept;
500 void eax_ensure_no_default_effect_slot() const;
501 bool eax_has_enough_aux_sends() const noexcept;
502 void eax_ensure_enough_aux_sends() const;
503 void eax_ensure_compatibility();
505 unsigned long eax_detect_speaker_configuration() const;
506 void eax_update_speaker_configuration();
508 void eax_set_last_error_defaults() noexcept;
509 void eax_session_set_defaults() noexcept;
510 static void eax4_context_set_defaults(Eax4Props& props) noexcept;
511 static void eax4_context_set_defaults(Eax4State& state) noexcept;
512 static void eax5_context_set_defaults(Eax5Props& props) noexcept;
513 static void eax5_context_set_defaults(Eax5State& state) noexcept;
514 void eax_context_set_defaults();
515 void eax_set_defaults();
517 void eax_dispatch_fx_slot(const EaxCall& call);
518 void eax_dispatch_source(const EaxCall& call);
520 void eax_get_misc(const EaxCall& call);
521 void eax4_get(const EaxCall& call, const Eax4Props& props);
522 void eax5_get(const EaxCall& call, const Eax5Props& props);
523 void eax_get(const EaxCall& call);
525 void eax_context_commit_primary_fx_slot_id();
526 void eax_context_commit_distance_factor();
527 void eax_context_commit_air_absorbtion_hf();
528 void eax_context_commit_hf_reference();
529 void eax_context_commit_macro_fx_factor();
531 void eax_initialize_fx_slots();
533 void eax_update_sources();
535 void eax_set_misc(const EaxCall& call);
536 void eax4_defer_all(const EaxCall& call, Eax4State& state);
537 void eax4_defer(const EaxCall& call, Eax4State& state);
538 void eax5_defer_all(const EaxCall& call, Eax5State& state);
539 void eax5_defer(const EaxCall& call, Eax5State& state);
540 void eax_set(const EaxCall& call);
542 void eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df);
543 void eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df);
544 void eax_context_commit();
545 #endif // ALSOFT_EAX
548 using ContextRef = al::intrusive_ptr<ALCcontext>;
550 ContextRef GetContextRef() noexcept;
552 void UpdateContextProps(ALCcontext *context);
555 inline bool TrapALError{false};
558 #if ALSOFT_EAX
559 auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id,
560 ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
562 auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id,
563 ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
564 #endif // ALSOFT_EAX
566 #endif /* ALC_CONTEXT_H */