Minor code refactor for the frequency shifter effect
[openal-soft.git] / alc / context.h
blobd93d63d65dd8e1bd652a1522cc4ca36400028aac
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};
107 ALCcontext(al::intrusive_ptr<ALCdevice> device);
108 ALCcontext(const ALCcontext&) = delete;
109 ALCcontext& operator=(const ALCcontext&) = delete;
110 ~ALCcontext();
112 void init();
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.
118 bool deinit();
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
129 * when called.
131 void processUpdates()
133 if(std::exchange(mDeferUpdates, false))
134 applyAllUpdates();
138 * Applies all pending updates for the context, listener, effect slots, and
139 * sources.
141 void applyAllUpdates();
143 #ifdef __USE_MINGW_ANSI_STDIO
144 [[gnu::format(gnu_printf, 3, 4)]]
145 #else
146 [[gnu::format(printf, 3, 4)]]
147 #endif
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;
154 private:
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.
161 class ThreadCtx {
162 public:
163 ~ThreadCtx();
164 void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
166 static thread_local ThreadCtx sThreadContext;
168 public:
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
171 * defined.
173 #ifdef __MINGW32__
174 static ALCcontext *getThreadContext() noexcept;
175 static void setThreadContext(ALCcontext *context) noexcept;
176 #else
177 static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
178 static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
179 #endif
181 /* Default effect that applies to sources that don't have an effect on send 0. */
182 static ALeffect sDefaultEffect;
184 DEF_NEWDEL(ALCcontext)
186 #ifdef ALSOFT_EAX
187 public:
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;
194 ALenum eax_eax_set(
195 const GUID* property_set_id,
196 ALuint property_id,
197 ALuint property_source_id,
198 ALvoid* property_value,
199 ALuint property_value_size);
201 ALenum eax_eax_get(
202 const GUID* property_set_id,
203 ALuint property_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(); }
222 private:
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;
231 struct Eax4State {
232 Eax4Props i; // Immediate.
233 Eax4Props d; // Deferred.
236 using Eax5Props = EAX50CONTEXTPROPERTIES;
238 struct Eax5State {
239 Eax5Props i; // Immediate.
240 Eax5Props d; // Deferred.
243 class ContextException : public EaxException
245 public:
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>(
269 "Distance Factor",
270 flDistanceFactor,
271 EAXCONTEXT_MINDISTANCEFACTOR,
272 EAXCONTEXT_MAXDISTANCEFACTOR);
276 struct Eax4AirAbsorptionHfValidator {
277 void operator()(float flAirAbsorptionHF) const
279 eax_validate_range<ContextException>(
280 "Air Absorption HF",
281 flAirAbsorptionHF,
282 EAXCONTEXT_MINAIRABSORPTIONHF,
283 EAXCONTEXT_MAXAIRABSORPTIONHF);
287 struct Eax4HfReferenceValidator {
288 void operator()(float flHFReference) const
290 eax_validate_range<ContextException>(
291 "HF Reference",
292 flHFReference,
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>(
326 "Macro FX Factor",
327 flMacroFXFactor,
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>(
348 "EAX version",
349 ulEAXVersion,
350 EAXCONTEXT_MINEAXSESSION,
351 EAXCONTEXT_MAXEAXSESSION);
355 struct Eax5MaxActiveSendsValidator {
356 void operator()(unsigned long ulMaxActiveSends) const
358 eax_validate_range<ContextException>(
359 "Max Active Sends",
360 ulMaxActiveSends,
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>(
378 "Speaker Config",
379 ulSpeakerConfig,
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,
411 // validates it,
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>();
417 TValidator{}(value);
418 property = value;
421 // Gets a new value from EAX call,
422 // validates it,
423 // updates the deferred value,
424 // updates a dirty flag.
425 template<
426 typename TValidator,
427 EaxDirtyFlags TDirtyBit,
428 typename TMemberResult,
429 typename TProps,
430 typename TState>
431 void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member) noexcept
433 const auto& src = call.get_value<ContextException, const TMemberResult>();
434 TValidator{}(src);
435 const auto& dst_i = state.i.*member;
436 auto& dst_d = state.d.*member;
437 dst_d = src;
439 if(dst_i != dst_d)
440 eax_df_ |= TDirtyBit;
443 template<
444 EaxDirtyFlags TDirtyBit,
445 typename TMemberResult,
446 typename TProps,
447 typename TState>
448 void eax_context_commit_property(TState& state, EaxDirtyFlags& dst_df,
449 TMemberResult TProps::*member) noexcept
451 if((eax_df_ & TDirtyBit) != EaxDirtyFlags{})
453 dst_df |= TDirtyBit;
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();
514 void eax_commit();
515 #endif // ALSOFT_EAX
518 using ContextRef = al::intrusive_ptr<ALCcontext>;
520 ContextRef GetContextRef(void);
522 void UpdateContextProps(ALCcontext *context);
525 extern bool TrapALError;
528 #ifdef ALSOFT_EAX
529 ALenum AL_APIENTRY EAXSet(
530 const GUID* property_set_id,
531 ALuint property_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,
538 ALuint property_id,
539 ALuint property_source_id,
540 ALvoid* property_value,
541 ALuint property_value_size) noexcept;
542 #endif // ALSOFT_EAX
544 #endif /* ALC_CONTEXT_H */