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

1271 lines
31 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. const auto call = create_eax_call(
  278. EaxCallType::set,
  279. property_set_id,
  280. property_id,
  281. property_source_id,
  282. property_value,
  283. property_value_size);
  284. eax_version_ = call.get_version();
  285. eax_initialize(call);
  286. eax_unlock_legacy_fx_slots(call);
  287. switch (call.get_property_set_id())
  288. {
  289. case EaxCallPropertySetId::context:
  290. eax_set(call);
  291. break;
  292. case EaxCallPropertySetId::fx_slot:
  293. case EaxCallPropertySetId::fx_slot_effect:
  294. eax_dispatch_fx_slot(call);
  295. break;
  296. case EaxCallPropertySetId::source:
  297. eax_dispatch_source(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. const auto call = create_eax_call(
  315. EaxCallType::get,
  316. property_set_id,
  317. property_id,
  318. property_source_id,
  319. property_value,
  320. property_value_size);
  321. eax_version_ = call.get_version();
  322. eax_initialize(call);
  323. eax_unlock_legacy_fx_slots(call);
  324. switch (call.get_property_set_id())
  325. {
  326. case EaxCallPropertySetId::context:
  327. eax_get(call);
  328. break;
  329. case EaxCallPropertySetId::fx_slot:
  330. case EaxCallPropertySetId::fx_slot_effect:
  331. eax_dispatch_fx_slot(call);
  332. break;
  333. case EaxCallPropertySetId::source:
  334. eax_dispatch_source(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, [](ALsource& source){ source.eax_commit(); });
  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(const EaxCall& call)
  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(call);
  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. /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
  463. * suggest full-sphere surround sound (like HRTF).
  464. */
  465. case DevFmtX3D71: return SPEAKERS_5;
  466. /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
  467. * provide full-sphere surround sound. Depends if apps are more likely to
  468. * consider headphones or 7.1 for surround sound support.
  469. */
  470. case DevFmtAmbi3D: return SPEAKERS_7;
  471. }
  472. ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans);
  473. return HEADPHONES;
  474. #undef EAX_PREFIX
  475. }
  476. void ALCcontext::eax_update_speaker_configuration()
  477. {
  478. eax_speaker_config_ = eax_detect_speaker_configuration();
  479. }
  480. void ALCcontext::eax_set_last_error_defaults() noexcept
  481. {
  482. eax_last_error_ = EAX_OK;
  483. }
  484. void ALCcontext::eax_set_session_defaults() noexcept
  485. {
  486. eax_session_.ulEAXVersion = EAXCONTEXT_MINEAXSESSION;
  487. eax_session_.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS;
  488. }
  489. void ALCcontext::eax_set_context_defaults() noexcept
  490. {
  491. eax_.context.guidPrimaryFXSlotID = EAXCONTEXT_DEFAULTPRIMARYFXSLOTID;
  492. eax_.context.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
  493. eax_.context.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
  494. eax_.context.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
  495. }
  496. void ALCcontext::eax_set_defaults() noexcept
  497. {
  498. eax_set_last_error_defaults();
  499. eax_set_session_defaults();
  500. eax_set_context_defaults();
  501. eax_d_ = eax_;
  502. }
  503. void ALCcontext::eax_unlock_legacy_fx_slots(const EaxCall& call) noexcept
  504. {
  505. if (call.get_version() != 5 || eax_are_legacy_fx_slots_unlocked_)
  506. return;
  507. eax_are_legacy_fx_slots_unlocked_ = true;
  508. eax_fx_slots_.unlock_legacy();
  509. }
  510. void ALCcontext::eax_dispatch_fx_slot(const EaxCall& call)
  511. {
  512. const auto fx_slot_index = call.get_fx_slot_index();
  513. if(!fx_slot_index.has_value())
  514. eax_fail("Invalid fx slot index.");
  515. auto& fx_slot = eax_get_fx_slot(*fx_slot_index);
  516. if(fx_slot.eax_dispatch(call))
  517. {
  518. std::lock_guard<std::mutex> source_lock{mSourceLock};
  519. eax_update_filters();
  520. }
  521. }
  522. void ALCcontext::eax_dispatch_source(const EaxCall& call)
  523. {
  524. const auto source_id = call.get_property_al_name();
  525. std::lock_guard<std::mutex> source_lock{mSourceLock};
  526. const auto source = ALsource::eax_lookup_source(*this, source_id);
  527. if (source == nullptr)
  528. eax_fail("Source not found.");
  529. source->eax_dispatch(call);
  530. }
  531. void ALCcontext::eax_get_primary_fx_slot_id(const EaxCall& call)
  532. {
  533. call.set_value<ContextException>(eax_.context.guidPrimaryFXSlotID);
  534. }
  535. void ALCcontext::eax_get_distance_factor(const EaxCall& call)
  536. {
  537. call.set_value<ContextException>(eax_.context.flDistanceFactor);
  538. }
  539. void ALCcontext::eax_get_air_absorption_hf(const EaxCall& call)
  540. {
  541. call.set_value<ContextException>(eax_.context.flAirAbsorptionHF);
  542. }
  543. void ALCcontext::eax_get_hf_reference(const EaxCall& call)
  544. {
  545. call.set_value<ContextException>(eax_.context.flHFReference);
  546. }
  547. void ALCcontext::eax_get_last_error(const EaxCall& call)
  548. {
  549. const auto eax_last_error = eax_last_error_;
  550. eax_last_error_ = EAX_OK;
  551. call.set_value<ContextException>(eax_last_error);
  552. }
  553. void ALCcontext::eax_get_speaker_config(const EaxCall& call)
  554. {
  555. call.set_value<ContextException>(eax_speaker_config_);
  556. }
  557. void ALCcontext::eax_get_session(const EaxCall& call)
  558. {
  559. call.set_value<ContextException>(eax_session_);
  560. }
  561. void ALCcontext::eax_get_macro_fx_factor(const EaxCall& call)
  562. {
  563. call.set_value<ContextException>(eax_.context.flMacroFXFactor);
  564. }
  565. void ALCcontext::eax_get_context_all(const EaxCall& call)
  566. {
  567. switch (call.get_version())
  568. {
  569. case 4:
  570. call.set_value<ContextException>(static_cast<const EAX40CONTEXTPROPERTIES&>(eax_.context));
  571. break;
  572. case 5:
  573. call.set_value<ContextException>(static_cast<const EAX50CONTEXTPROPERTIES&>(eax_.context));
  574. break;
  575. default:
  576. eax_fail("Unsupported EAX version.");
  577. }
  578. }
  579. void ALCcontext::eax_get(const EaxCall& call)
  580. {
  581. switch (call.get_property_id())
  582. {
  583. case EAXCONTEXT_NONE:
  584. break;
  585. case EAXCONTEXT_ALLPARAMETERS:
  586. eax_get_context_all(call);
  587. break;
  588. case EAXCONTEXT_PRIMARYFXSLOTID:
  589. eax_get_primary_fx_slot_id(call);
  590. break;
  591. case EAXCONTEXT_DISTANCEFACTOR:
  592. eax_get_distance_factor(call);
  593. break;
  594. case EAXCONTEXT_AIRABSORPTIONHF:
  595. eax_get_air_absorption_hf(call);
  596. break;
  597. case EAXCONTEXT_HFREFERENCE:
  598. eax_get_hf_reference(call);
  599. break;
  600. case EAXCONTEXT_LASTERROR:
  601. eax_get_last_error(call);
  602. break;
  603. case EAXCONTEXT_SPEAKERCONFIG:
  604. eax_get_speaker_config(call);
  605. break;
  606. case EAXCONTEXT_EAXSESSION:
  607. eax_get_session(call);
  608. break;
  609. case EAXCONTEXT_MACROFXFACTOR:
  610. eax_get_macro_fx_factor(call);
  611. break;
  612. default:
  613. eax_fail("Unsupported property id.");
  614. }
  615. }
  616. void ALCcontext::eax_set_primary_fx_slot_id()
  617. {
  618. eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
  619. }
  620. void ALCcontext::eax_set_distance_factor()
  621. {
  622. mListener.mMetersPerUnit = eax_.context.flDistanceFactor;
  623. mPropsDirty = true;
  624. }
  625. void ALCcontext::eax_set_air_absorbtion_hf()
  626. {
  627. mAirAbsorptionGainHF = level_mb_to_gain(eax_.context.flAirAbsorptionHF);
  628. mPropsDirty = true;
  629. }
  630. void ALCcontext::eax_set_hf_reference()
  631. {
  632. // TODO
  633. }
  634. void ALCcontext::eax_set_macro_fx_factor()
  635. {
  636. // TODO
  637. }
  638. void ALCcontext::eax_set_context()
  639. {
  640. eax_set_primary_fx_slot_id();
  641. eax_set_distance_factor();
  642. eax_set_air_absorbtion_hf();
  643. eax_set_hf_reference();
  644. }
  645. void ALCcontext::eax_initialize_fx_slots(const EaxCall& call)
  646. {
  647. eax_fx_slots_.initialize(call, *this);
  648. eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
  649. }
  650. void ALCcontext::eax_initialize_sources()
  651. {
  652. std::unique_lock<std::mutex> source_lock{mSourceLock};
  653. auto init_source = [this](ALsource &source) noexcept
  654. { source.eax_initialize(this); };
  655. ForEachSource(this, init_source);
  656. }
  657. void ALCcontext::eax_update_sources()
  658. {
  659. std::unique_lock<std::mutex> source_lock{mSourceLock};
  660. auto update_source = [](ALsource &source)
  661. { source.eax_commit(); };
  662. ForEachSource(this, update_source);
  663. }
  664. void ALCcontext::eax_validate_primary_fx_slot_id(
  665. const GUID& primary_fx_slot_id)
  666. {
  667. if (primary_fx_slot_id != EAX_NULL_GUID &&
  668. primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot0 &&
  669. primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot0 &&
  670. primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot1 &&
  671. primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot1 &&
  672. primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot2 &&
  673. primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot2 &&
  674. primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot3 &&
  675. primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot3)
  676. {
  677. eax_fail("Unsupported primary FX slot id.");
  678. }
  679. }
  680. void ALCcontext::eax_validate_distance_factor(
  681. float distance_factor)
  682. {
  683. eax_validate_range<ContextException>(
  684. "Distance Factor",
  685. distance_factor,
  686. EAXCONTEXT_MINDISTANCEFACTOR,
  687. EAXCONTEXT_MAXDISTANCEFACTOR);
  688. }
  689. void ALCcontext::eax_validate_air_absorption_hf(
  690. float air_absorption_hf)
  691. {
  692. eax_validate_range<ContextException>(
  693. "Air Absorption HF",
  694. air_absorption_hf,
  695. EAXCONTEXT_MINAIRABSORPTIONHF,
  696. EAXCONTEXT_MAXAIRABSORPTIONHF);
  697. }
  698. void ALCcontext::eax_validate_hf_reference(
  699. float hf_reference)
  700. {
  701. eax_validate_range<ContextException>(
  702. "HF Reference",
  703. hf_reference,
  704. EAXCONTEXT_MINHFREFERENCE,
  705. EAXCONTEXT_MAXHFREFERENCE);
  706. }
  707. void ALCcontext::eax_validate_speaker_config(
  708. unsigned long speaker_config)
  709. {
  710. switch (speaker_config)
  711. {
  712. case HEADPHONES:
  713. case SPEAKERS_2:
  714. case SPEAKERS_4:
  715. case SPEAKERS_5:
  716. case SPEAKERS_6:
  717. case SPEAKERS_7:
  718. break;
  719. default:
  720. eax_fail("Unsupported speaker configuration.");
  721. }
  722. }
  723. void ALCcontext::eax_validate_session_eax_version(
  724. unsigned long eax_version)
  725. {
  726. switch (eax_version)
  727. {
  728. case EAX_40:
  729. case EAX_50:
  730. break;
  731. default:
  732. eax_fail("Unsupported session EAX version.");
  733. }
  734. }
  735. void ALCcontext::eax_validate_session_max_active_sends(
  736. unsigned long max_active_sends)
  737. {
  738. eax_validate_range<ContextException>(
  739. "Max Active Sends",
  740. max_active_sends,
  741. EAXCONTEXT_MINMAXACTIVESENDS,
  742. EAXCONTEXT_MAXMAXACTIVESENDS);
  743. }
  744. void ALCcontext::eax_validate_session(
  745. const EAXSESSIONPROPERTIES& eax_session)
  746. {
  747. eax_validate_session_eax_version(eax_session.ulEAXVersion);
  748. eax_validate_session_max_active_sends(eax_session.ulMaxActiveSends);
  749. }
  750. void ALCcontext::eax_validate_macro_fx_factor(
  751. float macro_fx_factor)
  752. {
  753. eax_validate_range<ContextException>(
  754. "Macro FX Factor",
  755. macro_fx_factor,
  756. EAXCONTEXT_MINMACROFXFACTOR,
  757. EAXCONTEXT_MAXMACROFXFACTOR);
  758. }
  759. void ALCcontext::eax_validate_context_all(
  760. const EAX40CONTEXTPROPERTIES& context_all)
  761. {
  762. eax_validate_primary_fx_slot_id(context_all.guidPrimaryFXSlotID);
  763. eax_validate_distance_factor(context_all.flDistanceFactor);
  764. eax_validate_air_absorption_hf(context_all.flAirAbsorptionHF);
  765. eax_validate_hf_reference(context_all.flHFReference);
  766. }
  767. void ALCcontext::eax_validate_context_all(
  768. const EAX50CONTEXTPROPERTIES& context_all)
  769. {
  770. eax_validate_context_all(static_cast<const EAX40CONTEXTPROPERTIES>(context_all));
  771. eax_validate_macro_fx_factor(context_all.flMacroFXFactor);
  772. }
  773. void ALCcontext::eax_defer_primary_fx_slot_id(
  774. const GUID& primary_fx_slot_id)
  775. {
  776. eax_d_.context.guidPrimaryFXSlotID = primary_fx_slot_id;
  777. eax_context_dirty_flags_.guidPrimaryFXSlotID =
  778. (eax_.context.guidPrimaryFXSlotID != eax_d_.context.guidPrimaryFXSlotID);
  779. }
  780. void ALCcontext::eax_defer_distance_factor(
  781. float distance_factor)
  782. {
  783. eax_d_.context.flDistanceFactor = distance_factor;
  784. eax_context_dirty_flags_.flDistanceFactor =
  785. (eax_.context.flDistanceFactor != eax_d_.context.flDistanceFactor);
  786. }
  787. void ALCcontext::eax_defer_air_absorption_hf(
  788. float air_absorption_hf)
  789. {
  790. eax_d_.context.flAirAbsorptionHF = air_absorption_hf;
  791. eax_context_dirty_flags_.flAirAbsorptionHF =
  792. (eax_.context.flAirAbsorptionHF != eax_d_.context.flAirAbsorptionHF);
  793. }
  794. void ALCcontext::eax_defer_hf_reference(
  795. float hf_reference)
  796. {
  797. eax_d_.context.flHFReference = hf_reference;
  798. eax_context_dirty_flags_.flHFReference =
  799. (eax_.context.flHFReference != eax_d_.context.flHFReference);
  800. }
  801. void ALCcontext::eax_defer_macro_fx_factor(
  802. float macro_fx_factor)
  803. {
  804. eax_d_.context.flMacroFXFactor = macro_fx_factor;
  805. eax_context_dirty_flags_.flMacroFXFactor =
  806. (eax_.context.flMacroFXFactor != eax_d_.context.flMacroFXFactor);
  807. }
  808. void ALCcontext::eax_defer_context_all(
  809. const EAX40CONTEXTPROPERTIES& context_all)
  810. {
  811. eax_defer_primary_fx_slot_id(context_all.guidPrimaryFXSlotID);
  812. eax_defer_distance_factor(context_all.flDistanceFactor);
  813. eax_defer_air_absorption_hf(context_all.flAirAbsorptionHF);
  814. eax_defer_hf_reference(context_all.flHFReference);
  815. }
  816. void ALCcontext::eax_defer_context_all(
  817. const EAX50CONTEXTPROPERTIES& context_all)
  818. {
  819. eax_defer_context_all(static_cast<const EAX40CONTEXTPROPERTIES&>(context_all));
  820. eax_defer_macro_fx_factor(context_all.flMacroFXFactor);
  821. }
  822. void ALCcontext::eax_defer_context_all(const EaxCall& call)
  823. {
  824. switch(call.get_version())
  825. {
  826. case 4:
  827. {
  828. const auto& context_all =
  829. call.get_value<ContextException, EAX40CONTEXTPROPERTIES>();
  830. eax_validate_context_all(context_all);
  831. eax_defer_context_all(context_all);
  832. }
  833. break;
  834. case 5:
  835. {
  836. const auto& context_all =
  837. call.get_value<ContextException, EAX50CONTEXTPROPERTIES>();
  838. eax_validate_context_all(context_all);
  839. eax_defer_context_all(context_all);
  840. }
  841. break;
  842. default:
  843. eax_fail("Unsupported EAX version.");
  844. }
  845. }
  846. void ALCcontext::eax_defer_primary_fx_slot_id(const EaxCall& call)
  847. {
  848. const auto& primary_fx_slot_id =
  849. call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID)>();
  850. eax_validate_primary_fx_slot_id(primary_fx_slot_id);
  851. eax_defer_primary_fx_slot_id(primary_fx_slot_id);
  852. }
  853. void ALCcontext::eax_defer_distance_factor(const EaxCall& call)
  854. {
  855. const auto& distance_factor =
  856. call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flDistanceFactor)>();
  857. eax_validate_distance_factor(distance_factor);
  858. eax_defer_distance_factor(distance_factor);
  859. }
  860. void ALCcontext::eax_defer_air_absorption_hf(const EaxCall& call)
  861. {
  862. const auto& air_absorption_hf =
  863. call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flAirAbsorptionHF)>();
  864. eax_validate_air_absorption_hf(air_absorption_hf);
  865. eax_defer_air_absorption_hf(air_absorption_hf);
  866. }
  867. void ALCcontext::eax_defer_hf_reference(const EaxCall& call)
  868. {
  869. const auto& hf_reference =
  870. call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flHFReference)>();
  871. eax_validate_hf_reference(hf_reference);
  872. eax_defer_hf_reference(hf_reference);
  873. }
  874. void ALCcontext::eax_set_session(const EaxCall& call)
  875. {
  876. const auto& eax_session =
  877. call.get_value<ContextException, const EAXSESSIONPROPERTIES>();
  878. eax_validate_session(eax_session);
  879. eax_session_ = eax_session;
  880. }
  881. void ALCcontext::eax_defer_macro_fx_factor(const EaxCall& call)
  882. {
  883. const auto& macro_fx_factor =
  884. call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flMacroFXFactor)>();
  885. eax_validate_macro_fx_factor(macro_fx_factor);
  886. eax_defer_macro_fx_factor(macro_fx_factor);
  887. }
  888. void ALCcontext::eax_set(const EaxCall& call)
  889. {
  890. switch (call.get_property_id())
  891. {
  892. case EAXCONTEXT_NONE:
  893. break;
  894. case EAXCONTEXT_ALLPARAMETERS:
  895. eax_defer_context_all(call);
  896. break;
  897. case EAXCONTEXT_PRIMARYFXSLOTID:
  898. eax_defer_primary_fx_slot_id(call);
  899. break;
  900. case EAXCONTEXT_DISTANCEFACTOR:
  901. eax_defer_distance_factor(call);
  902. break;
  903. case EAXCONTEXT_AIRABSORPTIONHF:
  904. eax_defer_air_absorption_hf(call);
  905. break;
  906. case EAXCONTEXT_HFREFERENCE:
  907. eax_defer_hf_reference(call);
  908. break;
  909. case EAXCONTEXT_LASTERROR:
  910. eax_fail("Last error is read-only.");
  911. case EAXCONTEXT_SPEAKERCONFIG:
  912. eax_fail("Speaker configuration is read-only.");
  913. case EAXCONTEXT_EAXSESSION:
  914. eax_set_session(call);
  915. break;
  916. case EAXCONTEXT_MACROFXFACTOR:
  917. eax_defer_macro_fx_factor(call);
  918. break;
  919. default:
  920. eax_fail("Unsupported property id.");
  921. }
  922. }
  923. void ALCcontext::eax_apply_deferred()
  924. {
  925. if (eax_context_dirty_flags_ == ContextDirtyFlags{})
  926. {
  927. return;
  928. }
  929. eax_ = eax_d_;
  930. if (eax_context_dirty_flags_.guidPrimaryFXSlotID)
  931. {
  932. eax_set_primary_fx_slot_id();
  933. }
  934. if (eax_context_dirty_flags_.flDistanceFactor)
  935. {
  936. eax_set_distance_factor();
  937. }
  938. if (eax_context_dirty_flags_.flAirAbsorptionHF)
  939. {
  940. eax_set_air_absorbtion_hf();
  941. }
  942. if (eax_context_dirty_flags_.flHFReference)
  943. {
  944. eax_set_hf_reference();
  945. }
  946. if (eax_context_dirty_flags_.flMacroFXFactor)
  947. {
  948. eax_set_macro_fx_factor();
  949. }
  950. if (eax_context_dirty_flags_.guidPrimaryFXSlotID)
  951. {
  952. eax_update_sources();
  953. }
  954. eax_context_dirty_flags_ = ContextDirtyFlags{};
  955. }
  956. namespace
  957. {
  958. class EaxSetException :
  959. public EaxException
  960. {
  961. public:
  962. explicit EaxSetException(
  963. const char* message)
  964. :
  965. EaxException{"EAX_SET", message}
  966. {
  967. }
  968. }; // EaxSetException
  969. [[noreturn]]
  970. void eax_fail_set(
  971. const char* message)
  972. {
  973. throw EaxSetException{message};
  974. }
  975. class EaxGetException :
  976. public EaxException
  977. {
  978. public:
  979. explicit EaxGetException(
  980. const char* message)
  981. :
  982. EaxException{"EAX_GET", message}
  983. {
  984. }
  985. }; // EaxGetException
  986. [[noreturn]]
  987. void eax_fail_get(
  988. const char* message)
  989. {
  990. throw EaxGetException{message};
  991. }
  992. } // namespace
  993. FORCE_ALIGN ALenum AL_APIENTRY EAXSet(
  994. const GUID* property_set_id,
  995. ALuint property_id,
  996. ALuint property_source_id,
  997. ALvoid* property_value,
  998. ALuint property_value_size) noexcept
  999. try
  1000. {
  1001. auto context = GetContextRef();
  1002. if (!context)
  1003. {
  1004. eax_fail_set("No current context.");
  1005. }
  1006. std::lock_guard<std::mutex> prop_lock{context->mPropLock};
  1007. return context->eax_eax_set(
  1008. property_set_id,
  1009. property_id,
  1010. property_source_id,
  1011. property_value,
  1012. property_value_size
  1013. );
  1014. }
  1015. catch (...)
  1016. {
  1017. eax_log_exception(__func__);
  1018. return AL_INVALID_OPERATION;
  1019. }
  1020. FORCE_ALIGN ALenum AL_APIENTRY EAXGet(
  1021. const GUID* property_set_id,
  1022. ALuint property_id,
  1023. ALuint property_source_id,
  1024. ALvoid* property_value,
  1025. ALuint property_value_size) noexcept
  1026. try
  1027. {
  1028. auto context = GetContextRef();
  1029. if (!context)
  1030. {
  1031. eax_fail_get("No current context.");
  1032. }
  1033. std::lock_guard<std::mutex> prop_lock{context->mPropLock};
  1034. return context->eax_eax_get(
  1035. property_set_id,
  1036. property_id,
  1037. property_source_id,
  1038. property_value,
  1039. property_value_size
  1040. );
  1041. }
  1042. catch (...)
  1043. {
  1044. eax_log_exception(__func__);
  1045. return AL_INVALID_OPERATION;
  1046. }
  1047. #endif // ALSOFT_EAX