🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

504 lines
12 KiB

  1. #ifndef ALC_CONTEXT_H
  2. #define ALC_CONTEXT_H
  3. #include <atomic>
  4. #include <memory>
  5. #include <mutex>
  6. #include <stdint.h>
  7. #include <utility>
  8. #include "AL/al.h"
  9. #include "AL/alc.h"
  10. #include "AL/alext.h"
  11. #include "al/listener.h"
  12. #include "almalloc.h"
  13. #include "alnumeric.h"
  14. #include "atomic.h"
  15. #include "core/context.h"
  16. #include "intrusive_ptr.h"
  17. #include "vector.h"
  18. #ifdef ALSOFT_EAX
  19. #include "al/eax_eax_call.h"
  20. #include "al/eax_fx_slot_index.h"
  21. #include "al/eax_fx_slots.h"
  22. #include "al/eax_utils.h"
  23. using EaxContextSharedDirtyFlagsValue = std::uint_least8_t;
  24. struct EaxContextSharedDirtyFlags
  25. {
  26. using EaxIsBitFieldStruct = bool;
  27. EaxContextSharedDirtyFlagsValue primary_fx_slot_id : 1;
  28. }; // EaxContextSharedDirtyFlags
  29. using ContextDirtyFlagsValue = std::uint_least8_t;
  30. struct ContextDirtyFlags
  31. {
  32. using EaxIsBitFieldStruct = bool;
  33. ContextDirtyFlagsValue guidPrimaryFXSlotID : 1;
  34. ContextDirtyFlagsValue flDistanceFactor : 1;
  35. ContextDirtyFlagsValue flAirAbsorptionHF : 1;
  36. ContextDirtyFlagsValue flHFReference : 1;
  37. ContextDirtyFlagsValue flMacroFXFactor : 1;
  38. }; // ContextDirtyFlags
  39. struct EaxAlIsExtensionPresentResult
  40. {
  41. ALboolean is_present;
  42. bool is_return;
  43. }; // EaxAlIsExtensionPresentResult
  44. #endif // ALSOFT_EAX
  45. struct ALeffect;
  46. struct ALeffectslot;
  47. struct ALsource;
  48. using uint = unsigned int;
  49. struct SourceSubList {
  50. uint64_t FreeMask{~0_u64};
  51. ALsource *Sources{nullptr}; /* 64 */
  52. SourceSubList() noexcept = default;
  53. SourceSubList(const SourceSubList&) = delete;
  54. SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
  55. { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
  56. ~SourceSubList();
  57. SourceSubList& operator=(const SourceSubList&) = delete;
  58. SourceSubList& operator=(SourceSubList&& rhs) noexcept
  59. { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
  60. };
  61. struct EffectSlotSubList {
  62. uint64_t FreeMask{~0_u64};
  63. ALeffectslot *EffectSlots{nullptr}; /* 64 */
  64. EffectSlotSubList() noexcept = default;
  65. EffectSlotSubList(const EffectSlotSubList&) = delete;
  66. EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
  67. : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
  68. { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
  69. ~EffectSlotSubList();
  70. EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
  71. EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
  72. { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
  73. };
  74. struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
  75. const al::intrusive_ptr<ALCdevice> mALDevice;
  76. /* Wet buffers used by effect slots. */
  77. al::vector<WetBufferPtr> mWetBuffers;
  78. bool mPropsDirty{true};
  79. bool mDeferUpdates{false};
  80. std::mutex mPropLock;
  81. std::atomic<ALenum> mLastError{AL_NO_ERROR};
  82. DistanceModel mDistanceModel{DistanceModel::Default};
  83. bool mSourceDistanceModel{false};
  84. float mDopplerFactor{1.0f};
  85. float mDopplerVelocity{1.0f};
  86. float mSpeedOfSound{SpeedOfSoundMetersPerSec};
  87. float mAirAbsorptionGainHF{AirAbsorbGainHF};
  88. std::mutex mEventCbLock;
  89. ALEVENTPROCSOFT mEventCb{};
  90. void *mEventParam{nullptr};
  91. ALlistener mListener{};
  92. al::vector<SourceSubList> mSourceList;
  93. ALuint mNumSources{0};
  94. std::mutex mSourceLock;
  95. al::vector<EffectSlotSubList> mEffectSlotList;
  96. ALuint mNumEffectSlots{0u};
  97. std::mutex mEffectSlotLock;
  98. /* Default effect slot */
  99. std::unique_ptr<ALeffectslot> mDefaultSlot;
  100. const char *mExtensionList{nullptr};
  101. ALCcontext(al::intrusive_ptr<ALCdevice> device);
  102. ALCcontext(const ALCcontext&) = delete;
  103. ALCcontext& operator=(const ALCcontext&) = delete;
  104. ~ALCcontext();
  105. void init();
  106. /**
  107. * Removes the context from its device and removes it from being current on
  108. * the running thread or globally. Returns true if other contexts still
  109. * exist on the device.
  110. */
  111. bool deinit();
  112. /**
  113. * Defers/suspends updates for the given context's listener and sources.
  114. * This does *NOT* stop mixing, but rather prevents certain property
  115. * changes from taking effect. mPropLock must be held when called.
  116. */
  117. void deferUpdates() noexcept { mDeferUpdates = true; }
  118. /**
  119. * Resumes update processing after being deferred. mPropLock must be held
  120. * when called.
  121. */
  122. void processUpdates()
  123. {
  124. if(std::exchange(mDeferUpdates, false))
  125. applyAllUpdates();
  126. }
  127. /**
  128. * Applies all pending updates for the context, listener, effect slots, and
  129. * sources.
  130. */
  131. void applyAllUpdates();
  132. #ifdef __USE_MINGW_ANSI_STDIO
  133. [[gnu::format(gnu_printf, 3, 4)]]
  134. #else
  135. [[gnu::format(printf, 3, 4)]]
  136. #endif
  137. void setError(ALenum errorCode, const char *msg, ...);
  138. /* Process-wide current context */
  139. static std::atomic<ALCcontext*> sGlobalContext;
  140. private:
  141. /* Thread-local current context. */
  142. static thread_local ALCcontext *sLocalContext;
  143. /* Thread-local context handling. This handles attempting to release the
  144. * context which may have been left current when the thread is destroyed.
  145. */
  146. class ThreadCtx {
  147. public:
  148. ~ThreadCtx();
  149. void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
  150. };
  151. static thread_local ThreadCtx sThreadContext;
  152. public:
  153. /* HACK: MinGW generates bad code when accessing an extern thread_local
  154. * object. Add a wrapper function for it that only accesses it where it's
  155. * defined.
  156. */
  157. #ifdef __MINGW32__
  158. static ALCcontext *getThreadContext() noexcept;
  159. static void setThreadContext(ALCcontext *context) noexcept;
  160. #else
  161. static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
  162. static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
  163. #endif
  164. /* Default effect that applies to sources that don't have an effect on send 0. */
  165. static ALeffect sDefaultEffect;
  166. DEF_NEWDEL(ALCcontext)
  167. #ifdef ALSOFT_EAX
  168. public:
  169. bool has_eax() const noexcept { return eax_is_initialized_; }
  170. bool eax_is_capable() const noexcept;
  171. void eax_uninitialize() noexcept;
  172. ALenum eax_eax_set(
  173. const GUID* property_set_id,
  174. ALuint property_id,
  175. ALuint property_source_id,
  176. ALvoid* property_value,
  177. ALuint property_value_size);
  178. ALenum eax_eax_get(
  179. const GUID* property_set_id,
  180. ALuint property_id,
  181. ALuint property_source_id,
  182. ALvoid* property_value,
  183. ALuint property_value_size);
  184. void eax_update_filters();
  185. void eax_commit_and_update_sources();
  186. void eax_set_last_error() noexcept;
  187. EaxFxSlotIndex eax_get_previous_primary_fx_slot_index() const noexcept
  188. { return eax_previous_primary_fx_slot_index_; }
  189. EaxFxSlotIndex eax_get_primary_fx_slot_index() const noexcept
  190. { return eax_primary_fx_slot_index_; }
  191. const ALeffectslot& eax_get_fx_slot(EaxFxSlotIndexValue fx_slot_index) const
  192. { return eax_fx_slots_.get(fx_slot_index); }
  193. ALeffectslot& eax_get_fx_slot(EaxFxSlotIndexValue fx_slot_index)
  194. { return eax_fx_slots_.get(fx_slot_index); }
  195. void eax_commit_fx_slots()
  196. { eax_fx_slots_.commit(); }
  197. private:
  198. struct Eax
  199. {
  200. EAX50CONTEXTPROPERTIES context{};
  201. }; // Eax
  202. bool eax_is_initialized_{};
  203. bool eax_is_tried_{};
  204. bool eax_are_legacy_fx_slots_unlocked_{};
  205. long eax_last_error_{};
  206. unsigned long eax_speaker_config_{};
  207. EaxFxSlotIndex eax_previous_primary_fx_slot_index_{};
  208. EaxFxSlotIndex eax_primary_fx_slot_index_{};
  209. EaxFxSlots eax_fx_slots_{};
  210. EaxContextSharedDirtyFlags eax_context_shared_dirty_flags_{};
  211. Eax eax_{};
  212. Eax eax_d_{};
  213. EAXSESSIONPROPERTIES eax_session_{};
  214. ContextDirtyFlags eax_context_dirty_flags_{};
  215. std::string eax_extension_list_{};
  216. [[noreturn]]
  217. static void eax_fail(
  218. const char* message);
  219. void eax_initialize_extensions();
  220. void eax_initialize();
  221. bool eax_has_no_default_effect_slot() const noexcept;
  222. void eax_ensure_no_default_effect_slot() const;
  223. bool eax_has_enough_aux_sends() const noexcept;
  224. void eax_ensure_enough_aux_sends() const;
  225. void eax_ensure_compatibility();
  226. unsigned long eax_detect_speaker_configuration() const;
  227. void eax_update_speaker_configuration();
  228. void eax_set_last_error_defaults() noexcept;
  229. void eax_set_session_defaults() noexcept;
  230. void eax_set_context_defaults() noexcept;
  231. void eax_set_defaults() noexcept;
  232. void eax_initialize_sources();
  233. void eax_unlock_legacy_fx_slots(const EaxEaxCall& eax_call) noexcept;
  234. void eax_dispatch_fx_slot(
  235. const EaxEaxCall& eax_call);
  236. void eax_dispatch_source(
  237. const EaxEaxCall& eax_call);
  238. void eax_get_primary_fx_slot_id(
  239. const EaxEaxCall& eax_call);
  240. void eax_get_distance_factor(
  241. const EaxEaxCall& eax_call);
  242. void eax_get_air_absorption_hf(
  243. const EaxEaxCall& eax_call);
  244. void eax_get_hf_reference(
  245. const EaxEaxCall& eax_call);
  246. void eax_get_last_error(
  247. const EaxEaxCall& eax_call);
  248. void eax_get_speaker_config(
  249. const EaxEaxCall& eax_call);
  250. void eax_get_session(
  251. const EaxEaxCall& eax_call);
  252. void eax_get_macro_fx_factor(
  253. const EaxEaxCall& eax_call);
  254. void eax_get_context_all(
  255. const EaxEaxCall& eax_call);
  256. void eax_get(
  257. const EaxEaxCall& eax_call);
  258. void eax_set_primary_fx_slot_id();
  259. void eax_set_distance_factor();
  260. void eax_set_air_absorbtion_hf();
  261. void eax_set_hf_reference();
  262. void eax_set_macro_fx_factor();
  263. void eax_set_context();
  264. void eax_initialize_fx_slots();
  265. void eax_update_sources();
  266. void eax_validate_primary_fx_slot_id(
  267. const GUID& primary_fx_slot_id);
  268. void eax_validate_distance_factor(
  269. float distance_factor);
  270. void eax_validate_air_absorption_hf(
  271. float air_absorption_hf);
  272. void eax_validate_hf_reference(
  273. float hf_reference);
  274. void eax_validate_speaker_config(
  275. unsigned long speaker_config);
  276. void eax_validate_session_eax_version(
  277. unsigned long eax_version);
  278. void eax_validate_session_max_active_sends(
  279. unsigned long max_active_sends);
  280. void eax_validate_session(
  281. const EAXSESSIONPROPERTIES& eax_session);
  282. void eax_validate_macro_fx_factor(
  283. float macro_fx_factor);
  284. void eax_validate_context_all(
  285. const EAX40CONTEXTPROPERTIES& context_all);
  286. void eax_validate_context_all(
  287. const EAX50CONTEXTPROPERTIES& context_all);
  288. void eax_defer_primary_fx_slot_id(
  289. const GUID& primary_fx_slot_id);
  290. void eax_defer_distance_factor(
  291. float distance_factor);
  292. void eax_defer_air_absorption_hf(
  293. float air_absorption_hf);
  294. void eax_defer_hf_reference(
  295. float hf_reference);
  296. void eax_defer_macro_fx_factor(
  297. float macro_fx_factor);
  298. void eax_defer_context_all(
  299. const EAX40CONTEXTPROPERTIES& context_all);
  300. void eax_defer_context_all(
  301. const EAX50CONTEXTPROPERTIES& context_all);
  302. void eax_defer_context_all(
  303. const EaxEaxCall& eax_call);
  304. void eax_defer_primary_fx_slot_id(
  305. const EaxEaxCall& eax_call);
  306. void eax_defer_distance_factor(
  307. const EaxEaxCall& eax_call);
  308. void eax_defer_air_absorption_hf(
  309. const EaxEaxCall& eax_call);
  310. void eax_defer_hf_reference(
  311. const EaxEaxCall& eax_call);
  312. void eax_set_session(
  313. const EaxEaxCall& eax_call);
  314. void eax_defer_macro_fx_factor(
  315. const EaxEaxCall& eax_call);
  316. void eax_set(
  317. const EaxEaxCall& eax_call);
  318. void eax_apply_deferred();
  319. #endif // ALSOFT_EAX
  320. };
  321. #define SETERR_RETURN(ctx, err, retval, ...) do { \
  322. (ctx)->setError((err), __VA_ARGS__); \
  323. return retval; \
  324. } while(0)
  325. using ContextRef = al::intrusive_ptr<ALCcontext>;
  326. ContextRef GetContextRef(void);
  327. void UpdateContextProps(ALCcontext *context);
  328. extern bool TrapALError;
  329. #ifdef ALSOFT_EAX
  330. ALenum AL_APIENTRY EAXSet(
  331. const GUID* property_set_id,
  332. ALuint property_id,
  333. ALuint property_source_id,
  334. ALvoid* property_value,
  335. ALuint property_value_size) noexcept;
  336. ALenum AL_APIENTRY EAXGet(
  337. const GUID* property_set_id,
  338. ALuint property_id,
  339. ALuint property_source_id,
  340. ALvoid* property_value,
  341. ALuint property_value_size) noexcept;
  342. #endif // ALSOFT_EAX
  343. #endif /* ALC_CONTEXT_H */