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

1299 lines
32 KiB

  1. #include "config.h"
  2. #include "context.h"
  3. #include <algorithm>
  4. #include <functional>
  5. #include <limits>
  6. #include <numeric>
  7. #include <stddef.h>
  8. #include <stdexcept>
  9. #include "AL/efx.h"
  10. #include "al/auxeffectslot.h"
  11. #include "al/source.h"
  12. #include "al/effect.h"
  13. #include "al/event.h"
  14. #include "al/listener.h"
  15. #include "albit.h"
  16. #include "alc/alu.h"
  17. #include "core/async_event.h"
  18. #include "core/device.h"
  19. #include "core/effectslot.h"
  20. #include "core/logging.h"
  21. #include "core/voice.h"
  22. #include "core/voice_change.h"
  23. #include "device.h"
  24. #include "ringbuffer.h"
  25. #include "vecmat.h"
  26. #ifdef ALSOFT_EAX
  27. #include <cassert>
  28. #include <cstring>
  29. #include "alstring.h"
  30. #include "al/eax_exception.h"
  31. #include "al/eax_globals.h"
  32. #endif // ALSOFT_EAX
  33. namespace {
  34. using namespace std::placeholders;
  35. using voidp = void*;
  36. /* Default context extensions */
  37. constexpr ALchar alExtList[] =
  38. "AL_EXT_ALAW "
  39. "AL_EXT_BFORMAT "
  40. "AL_EXT_DOUBLE "
  41. "AL_EXT_EXPONENT_DISTANCE "
  42. "AL_EXT_FLOAT32 "
  43. "AL_EXT_IMA4 "
  44. "AL_EXT_LINEAR_DISTANCE "
  45. "AL_EXT_MCFORMATS "
  46. "AL_EXT_MULAW "
  47. "AL_EXT_MULAW_BFORMAT "
  48. "AL_EXT_MULAW_MCFORMATS "
  49. "AL_EXT_OFFSET "
  50. "AL_EXT_source_distance_model "
  51. "AL_EXT_SOURCE_RADIUS "
  52. "AL_EXT_STEREO_ANGLES "
  53. "AL_LOKI_quadriphonic "
  54. "AL_SOFT_bformat_ex "
  55. "AL_SOFTX_bformat_hoa "
  56. "AL_SOFT_block_alignment "
  57. "AL_SOFT_callback_buffer "
  58. "AL_SOFTX_convolution_reverb "
  59. "AL_SOFT_deferred_updates "
  60. "AL_SOFT_direct_channels "
  61. "AL_SOFT_direct_channels_remix "
  62. "AL_SOFT_effect_target "
  63. "AL_SOFT_events "
  64. "AL_SOFT_gain_clamp_ex "
  65. "AL_SOFTX_hold_on_disconnect "
  66. "AL_SOFT_loop_points "
  67. "AL_SOFTX_map_buffer "
  68. "AL_SOFT_MSADPCM "
  69. "AL_SOFT_source_latency "
  70. "AL_SOFT_source_length "
  71. "AL_SOFT_source_resampler "
  72. "AL_SOFT_source_spatialize "
  73. "AL_SOFT_UHJ";
  74. } // namespace
  75. std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
  76. thread_local ALCcontext *ALCcontext::sLocalContext{nullptr};
  77. ALCcontext::ThreadCtx::~ThreadCtx()
  78. {
  79. if(ALCcontext *ctx{ALCcontext::sLocalContext})
  80. {
  81. const bool result{ctx->releaseIfNoDelete()};
  82. ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
  83. result ? "" : ", leak detected");
  84. }
  85. }
  86. thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
  87. ALeffect ALCcontext::sDefaultEffect;
  88. #ifdef __MINGW32__
  89. ALCcontext *ALCcontext::getThreadContext() noexcept
  90. { return sLocalContext; }
  91. void ALCcontext::setThreadContext(ALCcontext *context) noexcept
  92. { sThreadContext.set(context); }
  93. #endif
  94. ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device)
  95. : ContextBase{device.get()}, mALDevice{std::move(device)}
  96. {
  97. }
  98. ALCcontext::~ALCcontext()
  99. {
  100. TRACE("Freeing context %p\n", voidp{this});
  101. size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u},
  102. [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
  103. { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
  104. if(count > 0)
  105. WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
  106. mSourceList.clear();
  107. mNumSources = 0;
  108. #ifdef ALSOFT_EAX
  109. eax_uninitialize();
  110. #endif // ALSOFT_EAX
  111. mDefaultSlot = nullptr;
  112. count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u},
  113. [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
  114. { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
  115. if(count > 0)
  116. WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
  117. mEffectSlotList.clear();
  118. mNumEffectSlots = 0;
  119. }
  120. void ALCcontext::init()
  121. {
  122. if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback)
  123. {
  124. mDefaultSlot = std::make_unique<ALeffectslot>();
  125. aluInitEffectPanning(&mDefaultSlot->mSlot, this);
  126. }
  127. EffectSlotArray *auxslots;
  128. if(!mDefaultSlot)
  129. auxslots = EffectSlot::CreatePtrArray(0);
  130. else
  131. {
  132. auxslots = EffectSlot::CreatePtrArray(1);
  133. (*auxslots)[0] = &mDefaultSlot->mSlot;
  134. mDefaultSlot->mState = SlotState::Playing;
  135. }
  136. mActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
  137. allocVoiceChanges();
  138. {
  139. VoiceChange *cur{mVoiceChangeTail};
  140. while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
  141. cur = next;
  142. mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
  143. }
  144. mExtensionList = alExtList;
  145. #ifdef ALSOFT_EAX
  146. eax_initialize_extensions();
  147. #endif // ALSOFT_EAX
  148. mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
  149. mParams.Matrix = alu::Matrix::Identity();
  150. mParams.Velocity = alu::Vector{};
  151. mParams.Gain = mListener.Gain;
  152. mParams.MetersPerUnit = mListener.mMetersPerUnit;
  153. mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
  154. mParams.DopplerFactor = mDopplerFactor;
  155. mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity;
  156. mParams.SourceDistanceModel = mSourceDistanceModel;
  157. mParams.mDistanceModel = mDistanceModel;
  158. mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
  159. StartEventThrd(this);
  160. allocVoices(256);
  161. mActiveVoiceCount.store(64, std::memory_order_relaxed);
  162. }
  163. bool ALCcontext::deinit()
  164. {
  165. if(sLocalContext == this)
  166. {
  167. WARN("%p released while current on thread\n", voidp{this});
  168. sThreadContext.set(nullptr);
  169. release();
  170. }
  171. ALCcontext *origctx{this};
  172. if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
  173. release();
  174. bool ret{};
  175. /* First make sure this context exists in the device's list. */
  176. auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
  177. if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
  178. {
  179. using ContextArray = al::FlexArray<ContextBase*>;
  180. auto alloc_ctx_array = [](const size_t count) -> ContextArray*
  181. {
  182. if(count == 0) return &DeviceBase::sEmptyContextArray;
  183. return ContextArray::Create(count).release();
  184. };
  185. auto *newarray = alloc_ctx_array(oldarray->size() - toremove);
  186. /* Copy the current/old context handles to the new array, excluding the
  187. * given context.
  188. */
  189. std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
  190. std::bind(std::not_equal_to<>{}, _1, this));
  191. /* Store the new context array in the device. Wait for any current mix
  192. * to finish before deleting the old array.
  193. */
  194. mDevice->mContexts.store(newarray);
  195. if(oldarray != &DeviceBase::sEmptyContextArray)
  196. {
  197. mDevice->waitForMix();
  198. delete oldarray;
  199. }
  200. ret = !newarray->empty();
  201. }
  202. else
  203. ret = !oldarray->empty();
  204. StopEventThrd(this);
  205. return ret;
  206. }
  207. void ALCcontext::applyAllUpdates()
  208. {
  209. /* Tell the mixer to stop applying updates, then wait for any active
  210. * updating to finish, before providing updates.
  211. */
  212. mHoldUpdates.store(true, std::memory_order_release);
  213. while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
  214. /* busy-wait */
  215. }
  216. #ifdef ALSOFT_EAX
  217. eax_apply_deferred();
  218. #endif
  219. if(std::exchange(mPropsDirty, false))
  220. UpdateContextProps(this);
  221. UpdateAllEffectSlotProps(this);
  222. UpdateAllSourceProps(this);
  223. /* Now with all updates declared, let the mixer continue applying them so
  224. * they all happen at once.
  225. */
  226. mHoldUpdates.store(false, std::memory_order_release);
  227. }
  228. #ifdef ALSOFT_EAX
  229. namespace {
  230. class ContextException :
  231. public EaxException
  232. {
  233. public:
  234. explicit ContextException(
  235. const char* message)
  236. :
  237. EaxException{"EAX_CONTEXT", message}
  238. {
  239. }
  240. }; // ContextException
  241. template<typename F>
  242. void ForEachSource(ALCcontext *context, F func)
  243. {
  244. for(auto &sublist : context->mSourceList)
  245. {
  246. uint64_t usemask{~sublist.FreeMask};
  247. while(usemask)
  248. {
  249. const int idx{al::countr_zero(usemask)};
  250. usemask &= ~(1_u64 << idx);
  251. func(sublist.Sources[idx]);
  252. }
  253. }
  254. }
  255. } // namespace
  256. bool ALCcontext::eax_is_capable() const noexcept
  257. {
  258. return eax_has_enough_aux_sends();
  259. }
  260. void ALCcontext::eax_uninitialize() noexcept
  261. {
  262. if (!eax_is_initialized_)
  263. {
  264. return;
  265. }
  266. eax_is_initialized_ = true;
  267. eax_is_tried_ = false;
  268. eax_fx_slots_.uninitialize();
  269. }
  270. ALenum ALCcontext::eax_eax_set(
  271. const GUID* property_set_id,
  272. ALuint property_id,
  273. ALuint property_source_id,
  274. ALvoid* property_value,
  275. ALuint property_value_size)
  276. {
  277. eax_initialize();
  278. const auto eax_call = create_eax_call(
  279. false,
  280. property_set_id,
  281. property_id,
  282. property_source_id,
  283. property_value,
  284. property_value_size
  285. );
  286. eax_unlock_legacy_fx_slots(eax_call);
  287. switch (eax_call.get_property_set_id())
  288. {
  289. case EaxEaxCallPropertySetId::context:
  290. eax_set(eax_call);
  291. break;
  292. case EaxEaxCallPropertySetId::fx_slot:
  293. case EaxEaxCallPropertySetId::fx_slot_effect:
  294. eax_dispatch_fx_slot(eax_call);
  295. break;
  296. case EaxEaxCallPropertySetId::source:
  297. eax_dispatch_source(eax_call);
  298. break;
  299. default:
  300. eax_fail("Unsupported property set id.");
  301. }
  302. static constexpr auto deferred_flag = 0x80000000u;
  303. if(!(property_id&deferred_flag) && !mDeferUpdates)
  304. applyAllUpdates();
  305. return AL_NO_ERROR;
  306. }
  307. ALenum ALCcontext::eax_eax_get(
  308. const GUID* property_set_id,
  309. ALuint property_id,
  310. ALuint property_source_id,
  311. ALvoid* property_value,
  312. ALuint property_value_size)
  313. {
  314. eax_initialize();
  315. const auto eax_call = create_eax_call(
  316. true,
  317. property_set_id,
  318. property_id,
  319. property_source_id,
  320. property_value,
  321. property_value_size
  322. );
  323. eax_unlock_legacy_fx_slots(eax_call);
  324. switch (eax_call.get_property_set_id())
  325. {
  326. case EaxEaxCallPropertySetId::context:
  327. eax_get(eax_call);
  328. break;
  329. case EaxEaxCallPropertySetId::fx_slot:
  330. case EaxEaxCallPropertySetId::fx_slot_effect:
  331. eax_dispatch_fx_slot(eax_call);
  332. break;
  333. case EaxEaxCallPropertySetId::source:
  334. eax_dispatch_source(eax_call);
  335. break;
  336. default:
  337. eax_fail("Unsupported property set id.");
  338. }
  339. return AL_NO_ERROR;
  340. }
  341. void ALCcontext::eax_update_filters()
  342. {
  343. ForEachSource(this, std::mem_fn(&ALsource::eax_update_filters));
  344. }
  345. void ALCcontext::eax_commit_and_update_sources()
  346. {
  347. std::unique_lock<std::mutex> source_lock{mSourceLock};
  348. ForEachSource(this, std::mem_fn(&ALsource::eax_commit_and_update));
  349. }
  350. void ALCcontext::eax_set_last_error() noexcept
  351. {
  352. eax_last_error_ = EAXERR_INVALID_OPERATION;
  353. }
  354. [[noreturn]]
  355. void ALCcontext::eax_fail(
  356. const char* message)
  357. {
  358. throw ContextException{message};
  359. }
  360. void ALCcontext::eax_initialize_extensions()
  361. {
  362. if (!eax_g_is_enabled)
  363. {
  364. return;
  365. }
  366. const auto string_max_capacity =
  367. std::strlen(mExtensionList) + 1 +
  368. std::strlen(eax1_ext_name) + 1 +
  369. std::strlen(eax2_ext_name) + 1 +
  370. std::strlen(eax3_ext_name) + 1 +
  371. std::strlen(eax4_ext_name) + 1 +
  372. std::strlen(eax5_ext_name) + 1 +
  373. std::strlen(eax_x_ram_ext_name) + 1 +
  374. 0;
  375. eax_extension_list_.reserve(string_max_capacity);
  376. if (eax_is_capable())
  377. {
  378. eax_extension_list_ += eax1_ext_name;
  379. eax_extension_list_ += ' ';
  380. eax_extension_list_ += eax2_ext_name;
  381. eax_extension_list_ += ' ';
  382. eax_extension_list_ += eax3_ext_name;
  383. eax_extension_list_ += ' ';
  384. eax_extension_list_ += eax4_ext_name;
  385. eax_extension_list_ += ' ';
  386. eax_extension_list_ += eax5_ext_name;
  387. eax_extension_list_ += ' ';
  388. }
  389. eax_extension_list_ += eax_x_ram_ext_name;
  390. eax_extension_list_ += ' ';
  391. eax_extension_list_ += mExtensionList;
  392. mExtensionList = eax_extension_list_.c_str();
  393. }
  394. void ALCcontext::eax_initialize()
  395. {
  396. if (eax_is_initialized_)
  397. {
  398. return;
  399. }
  400. if (eax_is_tried_)
  401. {
  402. eax_fail("No EAX.");
  403. }
  404. eax_is_tried_ = true;
  405. if (!eax_g_is_enabled)
  406. {
  407. eax_fail("EAX disabled by a configuration.");
  408. }
  409. eax_ensure_compatibility();
  410. eax_set_defaults();
  411. eax_set_air_absorbtion_hf();
  412. eax_update_speaker_configuration();
  413. eax_initialize_fx_slots();
  414. eax_initialize_sources();
  415. eax_is_initialized_ = true;
  416. }
  417. bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
  418. {
  419. return mDefaultSlot == nullptr;
  420. }
  421. void ALCcontext::eax_ensure_no_default_effect_slot() const
  422. {
  423. if (!eax_has_no_default_effect_slot())
  424. {
  425. eax_fail("There is a default effect slot in the context.");
  426. }
  427. }
  428. bool ALCcontext::eax_has_enough_aux_sends() const noexcept
  429. {
  430. return mALDevice->NumAuxSends >= EAX_MAX_FXSLOTS;
  431. }
  432. void ALCcontext::eax_ensure_enough_aux_sends() const
  433. {
  434. if (!eax_has_enough_aux_sends())
  435. {
  436. eax_fail("Not enough aux sends.");
  437. }
  438. }
  439. void ALCcontext::eax_ensure_compatibility()
  440. {
  441. eax_ensure_enough_aux_sends();
  442. }
  443. unsigned long ALCcontext::eax_detect_speaker_configuration() const
  444. {
  445. #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
  446. switch(mDevice->FmtChans)
  447. {
  448. case DevFmtMono: return SPEAKERS_2;
  449. case DevFmtStereo:
  450. /* Pretend 7.1 if using UHJ output, since they both provide full
  451. * horizontal surround.
  452. */
  453. if(mDevice->mUhjEncoder)
  454. return SPEAKERS_7;
  455. if(mDevice->Flags.test(DirectEar))
  456. return HEADPHONES;
  457. return SPEAKERS_2;
  458. case DevFmtQuad: return SPEAKERS_4;
  459. case DevFmtX51: return SPEAKERS_5;
  460. case DevFmtX61: return SPEAKERS_6;
  461. case DevFmtX71: return SPEAKERS_7;
  462. /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
  463. * provide full-sphere surround sound. Depends if apps are more likely to
  464. * consider headphones or 7.1 for surround sound support.
  465. */
  466. case DevFmtAmbi3D: return SPEAKERS_7;
  467. }
  468. ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans);
  469. return HEADPHONES;
  470. #undef EAX_PREFIX
  471. }
  472. void ALCcontext::eax_update_speaker_configuration()
  473. {
  474. eax_speaker_config_ = eax_detect_speaker_configuration();
  475. }
  476. void ALCcontext::eax_set_last_error_defaults() noexcept
  477. {
  478. eax_last_error_ = EAX_OK;
  479. }
  480. void ALCcontext::eax_set_session_defaults() noexcept
  481. {
  482. eax_session_.ulEAXVersion = EAXCONTEXT_MINEAXSESSION;
  483. eax_session_.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS;
  484. }
  485. void ALCcontext::eax_set_context_defaults() noexcept
  486. {
  487. eax_.context.guidPrimaryFXSlotID = EAXCONTEXT_DEFAULTPRIMARYFXSLOTID;
  488. eax_.context.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
  489. eax_.context.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
  490. eax_.context.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
  491. }
  492. void ALCcontext::eax_set_defaults() noexcept
  493. {
  494. eax_set_last_error_defaults();
  495. eax_set_session_defaults();
  496. eax_set_context_defaults();
  497. eax_d_ = eax_;
  498. }
  499. void ALCcontext::eax_unlock_legacy_fx_slots(const EaxEaxCall& eax_call) noexcept
  500. {
  501. if (eax_call.get_version() != 5 || eax_are_legacy_fx_slots_unlocked_)
  502. return;
  503. eax_are_legacy_fx_slots_unlocked_ = true;
  504. eax_fx_slots_.unlock_legacy();
  505. }
  506. void ALCcontext::eax_dispatch_fx_slot(
  507. const EaxEaxCall& eax_call)
  508. {
  509. const auto fx_slot_index = eax_call.get_fx_slot_index();
  510. if(!fx_slot_index.has_value())
  511. eax_fail("Invalid fx slot index.");
  512. auto& fx_slot = eax_get_fx_slot(*fx_slot_index);
  513. if(fx_slot.eax_dispatch(eax_call))
  514. {
  515. std::lock_guard<std::mutex> source_lock{mSourceLock};
  516. eax_update_filters();
  517. }
  518. }
  519. void ALCcontext::eax_dispatch_source(
  520. const EaxEaxCall& eax_call)
  521. {
  522. const auto source_id = eax_call.get_property_al_name();
  523. std::lock_guard<std::mutex> source_lock{mSourceLock};
  524. const auto source = ALsource::eax_lookup_source(*this, source_id);
  525. if (!source)
  526. {
  527. eax_fail("Source not found.");
  528. }
  529. source->eax_dispatch(eax_call);
  530. }
  531. void ALCcontext::eax_get_primary_fx_slot_id(
  532. const EaxEaxCall& eax_call)
  533. {
  534. eax_call.set_value<ContextException>(eax_.context.guidPrimaryFXSlotID);
  535. }
  536. void ALCcontext::eax_get_distance_factor(
  537. const EaxEaxCall& eax_call)
  538. {
  539. eax_call.set_value<ContextException>(eax_.context.flDistanceFactor);
  540. }
  541. void ALCcontext::eax_get_air_absorption_hf(
  542. const EaxEaxCall& eax_call)
  543. {
  544. eax_call.set_value<ContextException>(eax_.context.flAirAbsorptionHF);
  545. }
  546. void ALCcontext::eax_get_hf_reference(
  547. const EaxEaxCall& eax_call)
  548. {
  549. eax_call.set_value<ContextException>(eax_.context.flHFReference);
  550. }
  551. void ALCcontext::eax_get_last_error(
  552. const EaxEaxCall& eax_call)
  553. {
  554. const auto eax_last_error = eax_last_error_;
  555. eax_last_error_ = EAX_OK;
  556. eax_call.set_value<ContextException>(eax_last_error);
  557. }
  558. void ALCcontext::eax_get_speaker_config(
  559. const EaxEaxCall& eax_call)
  560. {
  561. eax_call.set_value<ContextException>(eax_speaker_config_);
  562. }
  563. void ALCcontext::eax_get_session(
  564. const EaxEaxCall& eax_call)
  565. {
  566. eax_call.set_value<ContextException>(eax_session_);
  567. }
  568. void ALCcontext::eax_get_macro_fx_factor(
  569. const EaxEaxCall& eax_call)
  570. {
  571. eax_call.set_value<ContextException>(eax_.context.flMacroFXFactor);
  572. }
  573. void ALCcontext::eax_get_context_all(
  574. const EaxEaxCall& eax_call)
  575. {
  576. switch (eax_call.get_version())
  577. {
  578. case 4:
  579. eax_call.set_value<ContextException>(static_cast<const EAX40CONTEXTPROPERTIES&>(eax_.context));
  580. break;
  581. case 5:
  582. eax_call.set_value<ContextException>(static_cast<const EAX50CONTEXTPROPERTIES&>(eax_.context));
  583. break;
  584. default:
  585. eax_fail("Unsupported EAX version.");
  586. }
  587. }
  588. void ALCcontext::eax_get(
  589. const EaxEaxCall& eax_call)
  590. {
  591. switch (eax_call.get_property_id())
  592. {
  593. case EAXCONTEXT_NONE:
  594. break;
  595. case EAXCONTEXT_ALLPARAMETERS:
  596. eax_get_context_all(eax_call);
  597. break;
  598. case EAXCONTEXT_PRIMARYFXSLOTID:
  599. eax_get_primary_fx_slot_id(eax_call);
  600. break;
  601. case EAXCONTEXT_DISTANCEFACTOR:
  602. eax_get_distance_factor(eax_call);
  603. break;
  604. case EAXCONTEXT_AIRABSORPTIONHF:
  605. eax_get_air_absorption_hf(eax_call);
  606. break;
  607. case EAXCONTEXT_HFREFERENCE:
  608. eax_get_hf_reference(eax_call);
  609. break;
  610. case EAXCONTEXT_LASTERROR:
  611. eax_get_last_error(eax_call);
  612. break;
  613. case EAXCONTEXT_SPEAKERCONFIG:
  614. eax_get_speaker_config(eax_call);
  615. break;
  616. case EAXCONTEXT_EAXSESSION:
  617. eax_get_session(eax_call);
  618. break;
  619. case EAXCONTEXT_MACROFXFACTOR:
  620. eax_get_macro_fx_factor(eax_call);
  621. break;
  622. default:
  623. eax_fail("Unsupported property id.");
  624. }
  625. }
  626. void ALCcontext::eax_set_primary_fx_slot_id()
  627. {
  628. eax_previous_primary_fx_slot_index_ = eax_primary_fx_slot_index_;
  629. eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
  630. }
  631. void ALCcontext::eax_set_distance_factor()
  632. {
  633. mListener.mMetersPerUnit = eax_.context.flDistanceFactor;
  634. mPropsDirty = true;
  635. }
  636. void ALCcontext::eax_set_air_absorbtion_hf()
  637. {
  638. mAirAbsorptionGainHF = level_mb_to_gain(eax_.context.flAirAbsorptionHF);
  639. mPropsDirty = true;
  640. }
  641. void ALCcontext::eax_set_hf_reference()
  642. {
  643. // TODO
  644. }
  645. void ALCcontext::eax_set_macro_fx_factor()
  646. {
  647. // TODO
  648. }
  649. void ALCcontext::eax_set_context()
  650. {
  651. eax_set_primary_fx_slot_id();
  652. eax_set_distance_factor();
  653. eax_set_air_absorbtion_hf();
  654. eax_set_hf_reference();
  655. }
  656. void ALCcontext::eax_initialize_fx_slots()
  657. {
  658. eax_fx_slots_.initialize(*this);
  659. eax_previous_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
  660. eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
  661. }
  662. void ALCcontext::eax_initialize_sources()
  663. {
  664. std::unique_lock<std::mutex> source_lock{mSourceLock};
  665. auto init_source = [this](ALsource &source) noexcept
  666. { source.eax_initialize(this); };
  667. ForEachSource(this, init_source);
  668. }
  669. void ALCcontext::eax_update_sources()
  670. {
  671. std::unique_lock<std::mutex> source_lock{mSourceLock};
  672. auto update_source = [this](ALsource &source)
  673. { source.eax_update(eax_context_shared_dirty_flags_); };
  674. ForEachSource(this, update_source);
  675. }
  676. void ALCcontext::eax_validate_primary_fx_slot_id(
  677. const GUID& primary_fx_slot_id)
  678. {
  679. if (primary_fx_slot_id != EAX_NULL_GUID &&
  680. primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot0 &&
  681. primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot0 &&
  682. primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot1 &&
  683. primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot1 &&
  684. primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot2 &&
  685. primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot2 &&
  686. primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot3 &&
  687. primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot3)
  688. {
  689. eax_fail("Unsupported primary FX slot id.");
  690. }
  691. }
  692. void ALCcontext::eax_validate_distance_factor(
  693. float distance_factor)
  694. {
  695. eax_validate_range<ContextException>(
  696. "Distance Factor",
  697. distance_factor,
  698. EAXCONTEXT_MINDISTANCEFACTOR,
  699. EAXCONTEXT_MAXDISTANCEFACTOR);
  700. }
  701. void ALCcontext::eax_validate_air_absorption_hf(
  702. float air_absorption_hf)
  703. {
  704. eax_validate_range<ContextException>(
  705. "Air Absorption HF",
  706. air_absorption_hf,
  707. EAXCONTEXT_MINAIRABSORPTIONHF,
  708. EAXCONTEXT_MAXAIRABSORPTIONHF);
  709. }
  710. void ALCcontext::eax_validate_hf_reference(
  711. float hf_reference)
  712. {
  713. eax_validate_range<ContextException>(
  714. "HF Reference",
  715. hf_reference,
  716. EAXCONTEXT_MINHFREFERENCE,
  717. EAXCONTEXT_MAXHFREFERENCE);
  718. }
  719. void ALCcontext::eax_validate_speaker_config(
  720. unsigned long speaker_config)
  721. {
  722. switch (speaker_config)
  723. {
  724. case HEADPHONES:
  725. case SPEAKERS_2:
  726. case SPEAKERS_4:
  727. case SPEAKERS_5:
  728. case SPEAKERS_6:
  729. case SPEAKERS_7:
  730. break;
  731. default:
  732. eax_fail("Unsupported speaker configuration.");
  733. }
  734. }
  735. void ALCcontext::eax_validate_session_eax_version(
  736. unsigned long eax_version)
  737. {
  738. switch (eax_version)
  739. {
  740. case EAX_40:
  741. case EAX_50:
  742. break;
  743. default:
  744. eax_fail("Unsupported session EAX version.");
  745. }
  746. }
  747. void ALCcontext::eax_validate_session_max_active_sends(
  748. unsigned long max_active_sends)
  749. {
  750. eax_validate_range<ContextException>(
  751. "Max Active Sends",
  752. max_active_sends,
  753. EAXCONTEXT_MINMAXACTIVESENDS,
  754. EAXCONTEXT_MAXMAXACTIVESENDS);
  755. }
  756. void ALCcontext::eax_validate_session(
  757. const EAXSESSIONPROPERTIES& eax_session)
  758. {
  759. eax_validate_session_eax_version(eax_session.ulEAXVersion);
  760. eax_validate_session_max_active_sends(eax_session.ulMaxActiveSends);
  761. }
  762. void ALCcontext::eax_validate_macro_fx_factor(
  763. float macro_fx_factor)
  764. {
  765. eax_validate_range<ContextException>(
  766. "Macro FX Factor",
  767. macro_fx_factor,
  768. EAXCONTEXT_MINMACROFXFACTOR,
  769. EAXCONTEXT_MAXMACROFXFACTOR);
  770. }
  771. void ALCcontext::eax_validate_context_all(
  772. const EAX40CONTEXTPROPERTIES& context_all)
  773. {
  774. eax_validate_primary_fx_slot_id(context_all.guidPrimaryFXSlotID);
  775. eax_validate_distance_factor(context_all.flDistanceFactor);
  776. eax_validate_air_absorption_hf(context_all.flAirAbsorptionHF);
  777. eax_validate_hf_reference(context_all.flHFReference);
  778. }
  779. void ALCcontext::eax_validate_context_all(
  780. const EAX50CONTEXTPROPERTIES& context_all)
  781. {
  782. eax_validate_context_all(static_cast<const EAX40CONTEXTPROPERTIES>(context_all));
  783. eax_validate_macro_fx_factor(context_all.flMacroFXFactor);
  784. }
  785. void ALCcontext::eax_defer_primary_fx_slot_id(
  786. const GUID& primary_fx_slot_id)
  787. {
  788. eax_d_.context.guidPrimaryFXSlotID = primary_fx_slot_id;
  789. eax_context_dirty_flags_.guidPrimaryFXSlotID =
  790. (eax_.context.guidPrimaryFXSlotID != eax_d_.context.guidPrimaryFXSlotID);
  791. }
  792. void ALCcontext::eax_defer_distance_factor(
  793. float distance_factor)
  794. {
  795. eax_d_.context.flDistanceFactor = distance_factor;
  796. eax_context_dirty_flags_.flDistanceFactor =
  797. (eax_.context.flDistanceFactor != eax_d_.context.flDistanceFactor);
  798. }
  799. void ALCcontext::eax_defer_air_absorption_hf(
  800. float air_absorption_hf)
  801. {
  802. eax_d_.context.flAirAbsorptionHF = air_absorption_hf;
  803. eax_context_dirty_flags_.flAirAbsorptionHF =
  804. (eax_.context.flAirAbsorptionHF != eax_d_.context.flAirAbsorptionHF);
  805. }
  806. void ALCcontext::eax_defer_hf_reference(
  807. float hf_reference)
  808. {
  809. eax_d_.context.flHFReference = hf_reference;
  810. eax_context_dirty_flags_.flHFReference =
  811. (eax_.context.flHFReference != eax_d_.context.flHFReference);
  812. }
  813. void ALCcontext::eax_defer_macro_fx_factor(
  814. float macro_fx_factor)
  815. {
  816. eax_d_.context.flMacroFXFactor = macro_fx_factor;
  817. eax_context_dirty_flags_.flMacroFXFactor =
  818. (eax_.context.flMacroFXFactor != eax_d_.context.flMacroFXFactor);
  819. }
  820. void ALCcontext::eax_defer_context_all(
  821. const EAX40CONTEXTPROPERTIES& context_all)
  822. {
  823. eax_defer_primary_fx_slot_id(context_all.guidPrimaryFXSlotID);
  824. eax_defer_distance_factor(context_all.flDistanceFactor);
  825. eax_defer_air_absorption_hf(context_all.flAirAbsorptionHF);
  826. eax_defer_hf_reference(context_all.flHFReference);
  827. }
  828. void ALCcontext::eax_defer_context_all(
  829. const EAX50CONTEXTPROPERTIES& context_all)
  830. {
  831. eax_defer_context_all(static_cast<const EAX40CONTEXTPROPERTIES&>(context_all));
  832. eax_defer_macro_fx_factor(context_all.flMacroFXFactor);
  833. }
  834. void ALCcontext::eax_defer_context_all(
  835. const EaxEaxCall& eax_call)
  836. {
  837. switch(eax_call.get_version())
  838. {
  839. case 4:
  840. {
  841. const auto& context_all =
  842. eax_call.get_value<ContextException, EAX40CONTEXTPROPERTIES>();
  843. eax_validate_context_all(context_all);
  844. eax_defer_context_all(context_all);
  845. }
  846. break;
  847. case 5:
  848. {
  849. const auto& context_all =
  850. eax_call.get_value<ContextException, EAX50CONTEXTPROPERTIES>();
  851. eax_validate_context_all(context_all);
  852. eax_defer_context_all(context_all);
  853. }
  854. break;
  855. default:
  856. eax_fail("Unsupported EAX version.");
  857. }
  858. }
  859. void ALCcontext::eax_defer_primary_fx_slot_id(
  860. const EaxEaxCall& eax_call)
  861. {
  862. const auto& primary_fx_slot_id =
  863. eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID)>();
  864. eax_validate_primary_fx_slot_id(primary_fx_slot_id);
  865. eax_defer_primary_fx_slot_id(primary_fx_slot_id);
  866. }
  867. void ALCcontext::eax_defer_distance_factor(
  868. const EaxEaxCall& eax_call)
  869. {
  870. const auto& distance_factor =
  871. eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flDistanceFactor)>();
  872. eax_validate_distance_factor(distance_factor);
  873. eax_defer_distance_factor(distance_factor);
  874. }
  875. void ALCcontext::eax_defer_air_absorption_hf(
  876. const EaxEaxCall& eax_call)
  877. {
  878. const auto& air_absorption_hf =
  879. eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flAirAbsorptionHF)>();
  880. eax_validate_air_absorption_hf(air_absorption_hf);
  881. eax_defer_air_absorption_hf(air_absorption_hf);
  882. }
  883. void ALCcontext::eax_defer_hf_reference(
  884. const EaxEaxCall& eax_call)
  885. {
  886. const auto& hf_reference =
  887. eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flHFReference)>();
  888. eax_validate_hf_reference(hf_reference);
  889. eax_defer_hf_reference(hf_reference);
  890. }
  891. void ALCcontext::eax_set_session(
  892. const EaxEaxCall& eax_call)
  893. {
  894. const auto& eax_session =
  895. eax_call.get_value<ContextException, const EAXSESSIONPROPERTIES>();
  896. eax_validate_session(eax_session);
  897. eax_session_ = eax_session;
  898. }
  899. void ALCcontext::eax_defer_macro_fx_factor(
  900. const EaxEaxCall& eax_call)
  901. {
  902. const auto& macro_fx_factor =
  903. eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flMacroFXFactor)>();
  904. eax_validate_macro_fx_factor(macro_fx_factor);
  905. eax_defer_macro_fx_factor(macro_fx_factor);
  906. }
  907. void ALCcontext::eax_set(
  908. const EaxEaxCall& eax_call)
  909. {
  910. switch (eax_call.get_property_id())
  911. {
  912. case EAXCONTEXT_NONE:
  913. break;
  914. case EAXCONTEXT_ALLPARAMETERS:
  915. eax_defer_context_all(eax_call);
  916. break;
  917. case EAXCONTEXT_PRIMARYFXSLOTID:
  918. eax_defer_primary_fx_slot_id(eax_call);
  919. break;
  920. case EAXCONTEXT_DISTANCEFACTOR:
  921. eax_defer_distance_factor(eax_call);
  922. break;
  923. case EAXCONTEXT_AIRABSORPTIONHF:
  924. eax_defer_air_absorption_hf(eax_call);
  925. break;
  926. case EAXCONTEXT_HFREFERENCE:
  927. eax_defer_hf_reference(eax_call);
  928. break;
  929. case EAXCONTEXT_LASTERROR:
  930. eax_fail("Last error is read-only.");
  931. case EAXCONTEXT_SPEAKERCONFIG:
  932. eax_fail("Speaker configuration is read-only.");
  933. case EAXCONTEXT_EAXSESSION:
  934. eax_set_session(eax_call);
  935. break;
  936. case EAXCONTEXT_MACROFXFACTOR:
  937. eax_defer_macro_fx_factor(eax_call);
  938. break;
  939. default:
  940. eax_fail("Unsupported property id.");
  941. }
  942. }
  943. void ALCcontext::eax_apply_deferred()
  944. {
  945. if (eax_context_dirty_flags_ == ContextDirtyFlags{})
  946. {
  947. return;
  948. }
  949. eax_ = eax_d_;
  950. if (eax_context_dirty_flags_.guidPrimaryFXSlotID)
  951. {
  952. eax_context_shared_dirty_flags_.primary_fx_slot_id = true;
  953. eax_set_primary_fx_slot_id();
  954. }
  955. if (eax_context_dirty_flags_.flDistanceFactor)
  956. {
  957. eax_set_distance_factor();
  958. }
  959. if (eax_context_dirty_flags_.flAirAbsorptionHF)
  960. {
  961. eax_set_air_absorbtion_hf();
  962. }
  963. if (eax_context_dirty_flags_.flHFReference)
  964. {
  965. eax_set_hf_reference();
  966. }
  967. if (eax_context_dirty_flags_.flMacroFXFactor)
  968. {
  969. eax_set_macro_fx_factor();
  970. }
  971. if (eax_context_shared_dirty_flags_ != EaxContextSharedDirtyFlags{})
  972. {
  973. eax_update_sources();
  974. }
  975. eax_context_shared_dirty_flags_ = EaxContextSharedDirtyFlags{};
  976. eax_context_dirty_flags_ = ContextDirtyFlags{};
  977. }
  978. namespace
  979. {
  980. class EaxSetException :
  981. public EaxException
  982. {
  983. public:
  984. explicit EaxSetException(
  985. const char* message)
  986. :
  987. EaxException{"EAX_SET", message}
  988. {
  989. }
  990. }; // EaxSetException
  991. [[noreturn]]
  992. void eax_fail_set(
  993. const char* message)
  994. {
  995. throw EaxSetException{message};
  996. }
  997. class EaxGetException :
  998. public EaxException
  999. {
  1000. public:
  1001. explicit EaxGetException(
  1002. const char* message)
  1003. :
  1004. EaxException{"EAX_GET", message}
  1005. {
  1006. }
  1007. }; // EaxGetException
  1008. [[noreturn]]
  1009. void eax_fail_get(
  1010. const char* message)
  1011. {
  1012. throw EaxGetException{message};
  1013. }
  1014. } // namespace
  1015. FORCE_ALIGN ALenum AL_APIENTRY EAXSet(
  1016. const GUID* property_set_id,
  1017. ALuint property_id,
  1018. ALuint property_source_id,
  1019. ALvoid* property_value,
  1020. ALuint property_value_size) noexcept
  1021. try
  1022. {
  1023. auto context = GetContextRef();
  1024. if (!context)
  1025. {
  1026. eax_fail_set("No current context.");
  1027. }
  1028. std::lock_guard<std::mutex> prop_lock{context->mPropLock};
  1029. return context->eax_eax_set(
  1030. property_set_id,
  1031. property_id,
  1032. property_source_id,
  1033. property_value,
  1034. property_value_size
  1035. );
  1036. }
  1037. catch (...)
  1038. {
  1039. eax_log_exception(__func__);
  1040. return AL_INVALID_OPERATION;
  1041. }
  1042. FORCE_ALIGN ALenum AL_APIENTRY EAXGet(
  1043. const GUID* property_set_id,
  1044. ALuint property_id,
  1045. ALuint property_source_id,
  1046. ALvoid* property_value,
  1047. ALuint property_value_size) noexcept
  1048. try
  1049. {
  1050. auto context = GetContextRef();
  1051. if (!context)
  1052. {
  1053. eax_fail_get("No current context.");
  1054. }
  1055. std::lock_guard<std::mutex> prop_lock{context->mPropLock};
  1056. return context->eax_eax_get(
  1057. property_set_id,
  1058. property_id,
  1059. property_source_id,
  1060. property_value,
  1061. property_value_size
  1062. );
  1063. }
  1064. catch (...)
  1065. {
  1066. eax_log_exception(__func__);
  1067. return AL_INVALID_OPERATION;
  1068. }
  1069. #endif // ALSOFT_EAX