🛠️🐜 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.

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