Avoid a divide-by-zero in UhjDecoder::decodeStereo
[openal-soft.git] / alc / context.h
blob72b259e976695a8104cf06cf94d6188c30609451
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_eax_call.h"
24 #include "al/eax_fx_slot_index.h"
25 #include "al/eax_fx_slots.h"
26 #include "al/eax_utils.h"
29 using EaxContextSharedDirtyFlagsValue = std::uint_least8_t;
31 struct EaxContextSharedDirtyFlags
33 using EaxIsBitFieldStruct = bool;
35 EaxContextSharedDirtyFlagsValue primary_fx_slot_id : 1;
36 }; // EaxContextSharedDirtyFlags
39 using ContextDirtyFlagsValue = std::uint_least8_t;
41 struct ContextDirtyFlags
43 using EaxIsBitFieldStruct = bool;
45 ContextDirtyFlagsValue guidPrimaryFXSlotID : 1;
46 ContextDirtyFlagsValue flDistanceFactor : 1;
47 ContextDirtyFlagsValue flAirAbsorptionHF : 1;
48 ContextDirtyFlagsValue flHFReference : 1;
49 ContextDirtyFlagsValue flMacroFXFactor : 1;
50 }; // ContextDirtyFlags
53 struct EaxAlIsExtensionPresentResult
55 ALboolean is_present;
56 bool is_return;
57 }; // EaxAlIsExtensionPresentResult
58 #endif // ALSOFT_EAX
60 struct ALeffect;
61 struct ALeffectslot;
62 struct ALsource;
64 using uint = unsigned int;
67 struct SourceSubList {
68 uint64_t FreeMask{~0_u64};
69 ALsource *Sources{nullptr}; /* 64 */
71 SourceSubList() noexcept = default;
72 SourceSubList(const SourceSubList&) = delete;
73 SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
74 { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
75 ~SourceSubList();
77 SourceSubList& operator=(const SourceSubList&) = delete;
78 SourceSubList& operator=(SourceSubList&& rhs) noexcept
79 { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
82 struct EffectSlotSubList {
83 uint64_t FreeMask{~0_u64};
84 ALeffectslot *EffectSlots{nullptr}; /* 64 */
86 EffectSlotSubList() noexcept = default;
87 EffectSlotSubList(const EffectSlotSubList&) = delete;
88 EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
89 : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
90 { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
91 ~EffectSlotSubList();
93 EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
94 EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
95 { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
98 struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
99 const al::intrusive_ptr<ALCdevice> mALDevice;
101 /* Wet buffers used by effect slots. */
102 al::vector<WetBufferPtr> mWetBuffers;
105 bool mPropsDirty{true};
106 bool mDeferUpdates{false};
108 std::mutex mPropLock;
110 std::atomic<ALenum> mLastError{AL_NO_ERROR};
112 DistanceModel mDistanceModel{DistanceModel::Default};
113 bool mSourceDistanceModel{false};
115 float mDopplerFactor{1.0f};
116 float mDopplerVelocity{1.0f};
117 float mSpeedOfSound{SpeedOfSoundMetersPerSec};
118 float mAirAbsorptionGainHF{AirAbsorbGainHF};
120 std::mutex mEventCbLock;
121 ALEVENTPROCSOFT mEventCb{};
122 void *mEventParam{nullptr};
124 ALlistener mListener{};
126 al::vector<SourceSubList> mSourceList;
127 ALuint mNumSources{0};
128 std::mutex mSourceLock;
130 al::vector<EffectSlotSubList> mEffectSlotList;
131 ALuint mNumEffectSlots{0u};
132 std::mutex mEffectSlotLock;
134 /* Default effect slot */
135 std::unique_ptr<ALeffectslot> mDefaultSlot;
137 const char *mExtensionList{nullptr};
140 ALCcontext(al::intrusive_ptr<ALCdevice> device);
141 ALCcontext(const ALCcontext&) = delete;
142 ALCcontext& operator=(const ALCcontext&) = delete;
143 ~ALCcontext();
145 void init();
147 * Removes the context from its device and removes it from being current on
148 * the running thread or globally. Returns true if other contexts still
149 * exist on the device.
151 bool deinit();
154 * Defers/suspends updates for the given context's listener and sources.
155 * This does *NOT* stop mixing, but rather prevents certain property
156 * changes from taking effect. mPropLock must be held when called.
158 void deferUpdates() noexcept { mDeferUpdates = true; }
161 * Resumes update processing after being deferred. mPropLock must be held
162 * when called.
164 void processUpdates()
166 if(std::exchange(mDeferUpdates, false))
167 applyAllUpdates();
171 * Applies all pending updates for the context, listener, effect slots, and
172 * sources.
174 void applyAllUpdates();
176 #ifdef __USE_MINGW_ANSI_STDIO
177 [[gnu::format(gnu_printf, 3, 4)]]
178 #else
179 [[gnu::format(printf, 3, 4)]]
180 #endif
181 void setError(ALenum errorCode, const char *msg, ...);
183 /* Process-wide current context */
184 static std::atomic<ALCcontext*> sGlobalContext;
186 private:
187 /* Thread-local current context. */
188 static thread_local ALCcontext *sLocalContext;
190 /* Thread-local context handling. This handles attempting to release the
191 * context which may have been left current when the thread is destroyed.
193 class ThreadCtx {
194 public:
195 ~ThreadCtx();
196 void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
198 static thread_local ThreadCtx sThreadContext;
200 public:
201 /* HACK: MinGW generates bad code when accessing an extern thread_local
202 * object. Add a wrapper function for it that only accesses it where it's
203 * defined.
205 #ifdef __MINGW32__
206 static ALCcontext *getThreadContext() noexcept;
207 static void setThreadContext(ALCcontext *context) noexcept;
208 #else
209 static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
210 static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
211 #endif
213 /* Default effect that applies to sources that don't have an effect on send 0. */
214 static ALeffect sDefaultEffect;
216 DEF_NEWDEL(ALCcontext)
218 #ifdef ALSOFT_EAX
219 public:
220 bool has_eax() const noexcept { return eax_is_initialized_; }
222 bool eax_is_capable() const noexcept;
225 void eax_uninitialize() noexcept;
228 ALenum eax_eax_set(
229 const GUID* property_set_id,
230 ALuint property_id,
231 ALuint property_source_id,
232 ALvoid* property_value,
233 ALuint property_value_size);
235 ALenum eax_eax_get(
236 const GUID* property_set_id,
237 ALuint property_id,
238 ALuint property_source_id,
239 ALvoid* property_value,
240 ALuint property_value_size);
243 void eax_update_filters();
245 void eax_commit_and_update_sources();
248 void eax_set_last_error() noexcept;
251 EaxFxSlotIndex eax_get_previous_primary_fx_slot_index() const noexcept
252 { return eax_previous_primary_fx_slot_index_; }
253 EaxFxSlotIndex eax_get_primary_fx_slot_index() const noexcept
254 { return eax_primary_fx_slot_index_; }
256 const ALeffectslot& eax_get_fx_slot(EaxFxSlotIndexValue fx_slot_index) const
257 { return eax_fx_slots_.get(fx_slot_index); }
258 ALeffectslot& eax_get_fx_slot(EaxFxSlotIndexValue fx_slot_index)
259 { return eax_fx_slots_.get(fx_slot_index); }
261 void eax_commit_fx_slots()
262 { eax_fx_slots_.commit(); }
264 private:
265 struct Eax
267 EAX50CONTEXTPROPERTIES context{};
268 }; // Eax
271 bool eax_is_initialized_{};
272 bool eax_is_tried_{};
273 bool eax_are_legacy_fx_slots_unlocked_{};
275 long eax_last_error_{};
276 unsigned long eax_speaker_config_{};
278 EaxFxSlotIndex eax_previous_primary_fx_slot_index_{};
279 EaxFxSlotIndex eax_primary_fx_slot_index_{};
280 EaxFxSlots eax_fx_slots_{};
282 EaxContextSharedDirtyFlags eax_context_shared_dirty_flags_{};
284 Eax eax_{};
285 Eax eax_d_{};
286 EAXSESSIONPROPERTIES eax_session_{};
288 ContextDirtyFlags eax_context_dirty_flags_{};
290 std::string eax_extension_list_{};
293 [[noreturn]]
294 static void eax_fail(
295 const char* message);
298 void eax_initialize_extensions();
300 void eax_initialize();
303 bool eax_has_no_default_effect_slot() const noexcept;
305 void eax_ensure_no_default_effect_slot() const;
307 bool eax_has_enough_aux_sends() const noexcept;
309 void eax_ensure_enough_aux_sends() const;
311 void eax_ensure_compatibility();
314 unsigned long eax_detect_speaker_configuration() const;
315 void eax_update_speaker_configuration();
318 void eax_set_last_error_defaults() noexcept;
320 void eax_set_session_defaults() noexcept;
322 void eax_set_context_defaults() noexcept;
324 void eax_set_defaults() noexcept;
326 void eax_initialize_sources();
329 void eax_unlock_legacy_fx_slots(const EaxEaxCall& eax_call) noexcept;
332 void eax_dispatch_fx_slot(
333 const EaxEaxCall& eax_call);
335 void eax_dispatch_source(
336 const EaxEaxCall& eax_call);
339 void eax_get_primary_fx_slot_id(
340 const EaxEaxCall& eax_call);
342 void eax_get_distance_factor(
343 const EaxEaxCall& eax_call);
345 void eax_get_air_absorption_hf(
346 const EaxEaxCall& eax_call);
348 void eax_get_hf_reference(
349 const EaxEaxCall& eax_call);
351 void eax_get_last_error(
352 const EaxEaxCall& eax_call);
354 void eax_get_speaker_config(
355 const EaxEaxCall& eax_call);
357 void eax_get_session(
358 const EaxEaxCall& eax_call);
360 void eax_get_macro_fx_factor(
361 const EaxEaxCall& eax_call);
363 void eax_get_context_all(
364 const EaxEaxCall& eax_call);
366 void eax_get(
367 const EaxEaxCall& eax_call);
370 void eax_set_primary_fx_slot_id();
372 void eax_set_distance_factor();
374 void eax_set_air_absorbtion_hf();
376 void eax_set_hf_reference();
378 void eax_set_macro_fx_factor();
380 void eax_set_context();
382 void eax_initialize_fx_slots();
385 void eax_update_sources();
388 void eax_validate_primary_fx_slot_id(
389 const GUID& primary_fx_slot_id);
391 void eax_validate_distance_factor(
392 float distance_factor);
394 void eax_validate_air_absorption_hf(
395 float air_absorption_hf);
397 void eax_validate_hf_reference(
398 float hf_reference);
400 void eax_validate_speaker_config(
401 unsigned long speaker_config);
403 void eax_validate_session_eax_version(
404 unsigned long eax_version);
406 void eax_validate_session_max_active_sends(
407 unsigned long max_active_sends);
409 void eax_validate_session(
410 const EAXSESSIONPROPERTIES& eax_session);
412 void eax_validate_macro_fx_factor(
413 float macro_fx_factor);
415 void eax_validate_context_all(
416 const EAX40CONTEXTPROPERTIES& context_all);
418 void eax_validate_context_all(
419 const EAX50CONTEXTPROPERTIES& context_all);
422 void eax_defer_primary_fx_slot_id(
423 const GUID& primary_fx_slot_id);
425 void eax_defer_distance_factor(
426 float distance_factor);
428 void eax_defer_air_absorption_hf(
429 float air_absorption_hf);
431 void eax_defer_hf_reference(
432 float hf_reference);
434 void eax_defer_macro_fx_factor(
435 float macro_fx_factor);
437 void eax_defer_context_all(
438 const EAX40CONTEXTPROPERTIES& context_all);
440 void eax_defer_context_all(
441 const EAX50CONTEXTPROPERTIES& context_all);
444 void eax_defer_context_all(
445 const EaxEaxCall& eax_call);
447 void eax_defer_primary_fx_slot_id(
448 const EaxEaxCall& eax_call);
450 void eax_defer_distance_factor(
451 const EaxEaxCall& eax_call);
453 void eax_defer_air_absorption_hf(
454 const EaxEaxCall& eax_call);
456 void eax_defer_hf_reference(
457 const EaxEaxCall& eax_call);
459 void eax_set_session(
460 const EaxEaxCall& eax_call);
462 void eax_defer_macro_fx_factor(
463 const EaxEaxCall& eax_call);
465 void eax_set(
466 const EaxEaxCall& eax_call);
468 void eax_apply_deferred();
469 #endif // ALSOFT_EAX
472 #define SETERR_RETURN(ctx, err, retval, ...) do { \
473 (ctx)->setError((err), __VA_ARGS__); \
474 return retval; \
475 } while(0)
478 using ContextRef = al::intrusive_ptr<ALCcontext>;
480 ContextRef GetContextRef(void);
482 void UpdateContextProps(ALCcontext *context);
485 extern bool TrapALError;
488 #ifdef ALSOFT_EAX
489 ALenum AL_APIENTRY EAXSet(
490 const GUID* property_set_id,
491 ALuint property_id,
492 ALuint property_source_id,
493 ALvoid* property_value,
494 ALuint property_value_size) noexcept;
496 ALenum AL_APIENTRY EAXGet(
497 const GUID* property_set_id,
498 ALuint property_id,
499 ALuint property_source_id,
500 ALvoid* property_value,
501 ALuint property_value_size) noexcept;
502 #endif // ALSOFT_EAX
504 #endif /* ALC_CONTEXT_H */