context.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  1. #include "config.h"
  2. #include "context.h"
  3. #include <algorithm>
  4. #include <array>
  5. #include <cstddef>
  6. #include <cstring>
  7. #include <functional>
  8. #include <limits>
  9. #include <numeric>
  10. #include <stdexcept>
  11. #include <string_view>
  12. #include <utility>
  13. #include "AL/efx.h"
  14. #include "al/auxeffectslot.h"
  15. #include "al/debug.h"
  16. #include "al/source.h"
  17. #include "al/effect.h"
  18. #include "al/event.h"
  19. #include "al/listener.h"
  20. #include "albit.h"
  21. #include "alc/alu.h"
  22. #include "alc/backends/base.h"
  23. #include "alspan.h"
  24. #include "core/async_event.h"
  25. #include "core/device.h"
  26. #include "core/effectslot.h"
  27. #include "core/logging.h"
  28. #include "core/voice.h"
  29. #include "core/voice_change.h"
  30. #include "device.h"
  31. #include "flexarray.h"
  32. #include "ringbuffer.h"
  33. #include "vecmat.h"
  34. #ifdef ALSOFT_EAX
  35. #include "alstring.h"
  36. #include "al/eax/globals.h"
  37. #endif // ALSOFT_EAX
  38. namespace {
  39. using namespace std::string_view_literals;
  40. using voidp = void*;
  41. /* Default context extensions */
  42. std::vector<std::string_view> getContextExtensions() noexcept
  43. {
  44. return std::vector<std::string_view>{
  45. "AL_EXT_ALAW"sv,
  46. "AL_EXT_BFORMAT"sv,
  47. "AL_EXT_debug"sv,
  48. "AL_EXT_direct_context"sv,
  49. "AL_EXT_DOUBLE"sv,
  50. "AL_EXT_EXPONENT_DISTANCE"sv,
  51. "AL_EXT_FLOAT32"sv,
  52. "AL_EXT_IMA4"sv,
  53. "AL_EXT_LINEAR_DISTANCE"sv,
  54. "AL_EXT_MCFORMATS"sv,
  55. "AL_EXT_MULAW"sv,
  56. "AL_EXT_MULAW_BFORMAT"sv,
  57. "AL_EXT_MULAW_MCFORMATS"sv,
  58. "AL_EXT_OFFSET"sv,
  59. "AL_EXT_source_distance_model"sv,
  60. "AL_EXT_SOURCE_RADIUS"sv,
  61. "AL_EXT_STATIC_BUFFER"sv,
  62. "AL_EXT_STEREO_ANGLES"sv,
  63. "AL_LOKI_quadriphonic"sv,
  64. "AL_SOFT_bformat_ex"sv,
  65. "AL_SOFTX_bformat_hoa"sv,
  66. "AL_SOFT_block_alignment"sv,
  67. "AL_SOFT_buffer_length_query"sv,
  68. "AL_SOFT_callback_buffer"sv,
  69. "AL_SOFTX_convolution_effect"sv,
  70. "AL_SOFT_deferred_updates"sv,
  71. "AL_SOFT_direct_channels"sv,
  72. "AL_SOFT_direct_channels_remix"sv,
  73. "AL_SOFT_effect_target"sv,
  74. "AL_SOFT_events"sv,
  75. "AL_SOFT_gain_clamp_ex"sv,
  76. "AL_SOFTX_hold_on_disconnect"sv,
  77. "AL_SOFT_loop_points"sv,
  78. "AL_SOFTX_map_buffer"sv,
  79. "AL_SOFT_MSADPCM"sv,
  80. "AL_SOFT_source_latency"sv,
  81. "AL_SOFT_source_length"sv,
  82. "AL_SOFTX_source_panning"sv,
  83. "AL_SOFT_source_resampler"sv,
  84. "AL_SOFT_source_spatialize"sv,
  85. "AL_SOFT_source_start_delay"sv,
  86. "AL_SOFT_UHJ"sv,
  87. "AL_SOFT_UHJ_ex"sv,
  88. };
  89. }
  90. } // namespace
  91. std::atomic<bool> ALCcontext::sGlobalContextLock{false};
  92. std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
  93. ALCcontext::ThreadCtx::~ThreadCtx()
  94. {
  95. if(ALCcontext *ctx{std::exchange(ALCcontext::sLocalContext, nullptr)})
  96. {
  97. const bool result{ctx->releaseIfNoDelete()};
  98. ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
  99. result ? "" : ", leak detected");
  100. }
  101. }
  102. thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
  103. ALeffect ALCcontext::sDefaultEffect;
  104. ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device, ContextFlagBitset flags)
  105. : ContextBase{device.get()}, mALDevice{std::move(device)}, mContextFlags{flags}
  106. {
  107. mDebugGroups.emplace_back(DebugSource::Other, 0, std::string{});
  108. mDebugEnabled.store(mContextFlags.test(ContextFlags::DebugBit), std::memory_order_relaxed);
  109. }
  110. ALCcontext::~ALCcontext()
  111. {
  112. TRACE("Freeing context %p\n", voidp{this});
  113. size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), 0_uz,
  114. [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
  115. { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
  116. if(count > 0)
  117. WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
  118. mSourceList.clear();
  119. mNumSources = 0;
  120. #ifdef ALSOFT_EAX
  121. eaxUninitialize();
  122. #endif // ALSOFT_EAX
  123. mDefaultSlot = nullptr;
  124. count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), 0_uz,
  125. [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
  126. { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
  127. if(count > 0)
  128. WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
  129. mEffectSlotList.clear();
  130. mNumEffectSlots = 0;
  131. }
  132. void ALCcontext::init()
  133. {
  134. if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback)
  135. {
  136. mDefaultSlot = std::make_unique<ALeffectslot>(this);
  137. aluInitEffectPanning(mDefaultSlot->mSlot, this);
  138. }
  139. std::unique_ptr<EffectSlotArray> auxslots;
  140. if(!mDefaultSlot)
  141. auxslots = EffectSlot::CreatePtrArray(0);
  142. else
  143. {
  144. auxslots = EffectSlot::CreatePtrArray(1);
  145. (*auxslots)[0] = mDefaultSlot->mSlot;
  146. std::uninitialized_fill_n(al::to_address(auxslots->end()), 1_uz, nullptr);
  147. mDefaultSlot->mState = SlotState::Playing;
  148. }
  149. mActiveAuxSlots.store(std::move(auxslots), std::memory_order_relaxed);
  150. allocVoiceChanges();
  151. {
  152. VoiceChange *cur{mVoiceChangeTail};
  153. while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
  154. cur = next;
  155. mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
  156. }
  157. mExtensions = getContextExtensions();
  158. if(sBufferSubDataCompat)
  159. {
  160. auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS");
  161. if(iter != mExtensions.end()) mExtensions.erase(iter);
  162. /* TODO: Would be nice to sort this alphabetically. Needs case-
  163. * insensitive searching.
  164. */
  165. mExtensions.emplace_back("AL_SOFT_buffer_sub_data");
  166. }
  167. #ifdef ALSOFT_EAX
  168. eax_initialize_extensions();
  169. #endif // ALSOFT_EAX
  170. if(!mExtensions.empty())
  171. {
  172. const size_t len{std::accumulate(mExtensions.cbegin()+1, mExtensions.cend(),
  173. mExtensions.front().length(),
  174. [](size_t current, std::string_view ext) noexcept
  175. { return current + ext.length() + 1; })};
  176. std::string extensions;
  177. extensions.reserve(len);
  178. extensions += mExtensions.front();
  179. for(std::string_view ext : al::span{mExtensions}.subspan<1>())
  180. {
  181. extensions += ' ';
  182. extensions += ext;
  183. }
  184. mExtensionsString = std::move(extensions);
  185. }
  186. mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
  187. mParams.Matrix = alu::Matrix::Identity();
  188. mParams.Velocity = alu::Vector{};
  189. mParams.Gain = mListener.Gain;
  190. mParams.MetersPerUnit = mListener.mMetersPerUnit;
  191. mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
  192. mParams.DopplerFactor = mDopplerFactor;
  193. mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity;
  194. mParams.SourceDistanceModel = mSourceDistanceModel;
  195. mParams.mDistanceModel = mDistanceModel;
  196. mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
  197. StartEventThrd(this);
  198. allocVoices(256);
  199. mActiveVoiceCount.store(64, std::memory_order_relaxed);
  200. }
  201. void ALCcontext::deinit()
  202. {
  203. if(sLocalContext == this)
  204. {
  205. WARN("%p released while current on thread\n", voidp{this});
  206. sThreadContext.set(nullptr);
  207. dec_ref();
  208. }
  209. ALCcontext *origctx{this};
  210. if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
  211. {
  212. while(sGlobalContextLock.load()) {
  213. /* Wait to make sure another thread didn't get the context and is
  214. * trying to increment its refcount.
  215. */
  216. }
  217. dec_ref();
  218. }
  219. bool stopPlayback{};
  220. /* First make sure this context exists in the device's list. */
  221. auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
  222. if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
  223. {
  224. using ContextArray = al::FlexArray<ContextBase*>;
  225. const size_t newsize{oldarray->size() - toremove};
  226. auto newarray = ContextArray::Create(newsize);
  227. /* Copy the current/old context handles to the new array, excluding the
  228. * given context.
  229. */
  230. std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
  231. [this](ContextBase *ctx) { return ctx != this; });
  232. /* Store the new context array in the device. Wait for any current mix
  233. * to finish before deleting the old array.
  234. */
  235. auto prevarray = mDevice->mContexts.exchange(std::move(newarray));
  236. std::ignore = mDevice->waitForMix();
  237. stopPlayback = (newsize == 0);
  238. }
  239. else
  240. stopPlayback = oldarray->empty();
  241. StopEventThrd(this);
  242. if(stopPlayback && mALDevice->mDeviceState == DeviceState::Playing)
  243. {
  244. mALDevice->Backend->stop();
  245. mALDevice->mDeviceState = DeviceState::Configured;
  246. }
  247. }
  248. void ALCcontext::applyAllUpdates()
  249. {
  250. /* Tell the mixer to stop applying updates, then wait for any active
  251. * updating to finish, before providing updates.
  252. */
  253. mHoldUpdates.store(true, std::memory_order_release);
  254. while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
  255. /* busy-wait */
  256. }
  257. #ifdef ALSOFT_EAX
  258. if(mEaxNeedsCommit)
  259. eaxCommit();
  260. #endif
  261. if(std::exchange(mPropsDirty, false))
  262. UpdateContextProps(this);
  263. UpdateAllEffectSlotProps(this);
  264. UpdateAllSourceProps(this);
  265. /* Now with all updates declared, let the mixer continue applying them so
  266. * they all happen at once.
  267. */
  268. mHoldUpdates.store(false, std::memory_order_release);
  269. }
  270. #ifdef ALSOFT_EAX
  271. namespace {
  272. template<typename F>
  273. void ForEachSource(ALCcontext *context, F func)
  274. {
  275. for(auto &sublist : context->mSourceList)
  276. {
  277. uint64_t usemask{~sublist.FreeMask};
  278. while(usemask)
  279. {
  280. const auto idx = static_cast<uint>(al::countr_zero(usemask));
  281. usemask &= ~(1_u64 << idx);
  282. func((*sublist.Sources)[idx]);
  283. }
  284. }
  285. }
  286. } // namespace
  287. bool ALCcontext::eaxIsCapable() const noexcept
  288. {
  289. return eax_has_enough_aux_sends();
  290. }
  291. void ALCcontext::eaxUninitialize() noexcept
  292. {
  293. if(!mEaxIsInitialized)
  294. return;
  295. mEaxIsInitialized = false;
  296. mEaxIsTried = false;
  297. mEaxFxSlots.uninitialize();
  298. }
  299. ALenum ALCcontext::eax_eax_set(
  300. const GUID* property_set_id,
  301. ALuint property_id,
  302. ALuint property_source_id,
  303. ALvoid* property_value,
  304. ALuint property_value_size)
  305. {
  306. const auto call = create_eax_call(
  307. EaxCallType::set,
  308. property_set_id,
  309. property_id,
  310. property_source_id,
  311. property_value,
  312. property_value_size);
  313. eax_initialize();
  314. switch(call.get_property_set_id())
  315. {
  316. case EaxCallPropertySetId::context:
  317. eax_set(call);
  318. break;
  319. case EaxCallPropertySetId::fx_slot:
  320. case EaxCallPropertySetId::fx_slot_effect:
  321. eax_dispatch_fx_slot(call);
  322. break;
  323. case EaxCallPropertySetId::source:
  324. eax_dispatch_source(call);
  325. break;
  326. default:
  327. eax_fail_unknown_property_set_id();
  328. }
  329. mEaxNeedsCommit = true;
  330. if(!call.is_deferred())
  331. {
  332. eaxCommit();
  333. if(!mDeferUpdates)
  334. applyAllUpdates();
  335. }
  336. return AL_NO_ERROR;
  337. }
  338. ALenum ALCcontext::eax_eax_get(
  339. const GUID* property_set_id,
  340. ALuint property_id,
  341. ALuint property_source_id,
  342. ALvoid* property_value,
  343. ALuint property_value_size)
  344. {
  345. const auto call = create_eax_call(
  346. EaxCallType::get,
  347. property_set_id,
  348. property_id,
  349. property_source_id,
  350. property_value,
  351. property_value_size);
  352. eax_initialize();
  353. switch(call.get_property_set_id())
  354. {
  355. case EaxCallPropertySetId::context:
  356. eax_get(call);
  357. break;
  358. case EaxCallPropertySetId::fx_slot:
  359. case EaxCallPropertySetId::fx_slot_effect:
  360. eax_dispatch_fx_slot(call);
  361. break;
  362. case EaxCallPropertySetId::source:
  363. eax_dispatch_source(call);
  364. break;
  365. default:
  366. eax_fail_unknown_property_set_id();
  367. }
  368. return AL_NO_ERROR;
  369. }
  370. void ALCcontext::eaxSetLastError() noexcept
  371. {
  372. mEaxLastError = EAXERR_INVALID_OPERATION;
  373. }
  374. [[noreturn]] void ALCcontext::eax_fail(const char* message)
  375. {
  376. throw ContextException{message};
  377. }
  378. [[noreturn]] void ALCcontext::eax_fail_unknown_property_set_id()
  379. {
  380. eax_fail("Unknown property ID.");
  381. }
  382. [[noreturn]] void ALCcontext::eax_fail_unknown_primary_fx_slot_id()
  383. {
  384. eax_fail("Unknown primary FX Slot ID.");
  385. }
  386. [[noreturn]] void ALCcontext::eax_fail_unknown_property_id()
  387. {
  388. eax_fail("Unknown property ID.");
  389. }
  390. [[noreturn]] void ALCcontext::eax_fail_unknown_version()
  391. {
  392. eax_fail("Unknown version.");
  393. }
  394. void ALCcontext::eax_initialize_extensions()
  395. {
  396. if(!eax_g_is_enabled)
  397. return;
  398. mExtensions.emplace(mExtensions.begin(), "EAX-RAM"sv);
  399. if(eaxIsCapable())
  400. {
  401. mExtensions.emplace(mExtensions.begin(), "EAX5.0"sv);
  402. mExtensions.emplace(mExtensions.begin(), "EAX4.0"sv);
  403. mExtensions.emplace(mExtensions.begin(), "EAX3.0"sv);
  404. mExtensions.emplace(mExtensions.begin(), "EAX2.0"sv);
  405. mExtensions.emplace(mExtensions.begin(), "EAX"sv);
  406. }
  407. }
  408. void ALCcontext::eax_initialize()
  409. {
  410. if(mEaxIsInitialized)
  411. return;
  412. if(mEaxIsTried)
  413. eax_fail("No EAX.");
  414. mEaxIsTried = true;
  415. if(!eax_g_is_enabled)
  416. eax_fail("EAX disabled by a configuration.");
  417. eax_ensure_compatibility();
  418. eax_set_defaults();
  419. eax_context_commit_air_absorbtion_hf();
  420. eax_update_speaker_configuration();
  421. eax_initialize_fx_slots();
  422. mEaxIsInitialized = true;
  423. }
  424. bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
  425. {
  426. return mDefaultSlot == nullptr;
  427. }
  428. void ALCcontext::eax_ensure_no_default_effect_slot() const
  429. {
  430. if(!eax_has_no_default_effect_slot())
  431. eax_fail("There is a default effect slot in the context.");
  432. }
  433. bool ALCcontext::eax_has_enough_aux_sends() const noexcept
  434. {
  435. return mALDevice->NumAuxSends >= EAX_MAX_FXSLOTS;
  436. }
  437. void ALCcontext::eax_ensure_enough_aux_sends() const
  438. {
  439. if(!eax_has_enough_aux_sends())
  440. eax_fail("Not enough aux sends.");
  441. }
  442. void ALCcontext::eax_ensure_compatibility()
  443. {
  444. eax_ensure_enough_aux_sends();
  445. }
  446. unsigned long ALCcontext::eax_detect_speaker_configuration() const
  447. {
  448. #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
  449. switch(mDevice->FmtChans)
  450. {
  451. case DevFmtMono: return SPEAKERS_2;
  452. case DevFmtStereo:
  453. /* Pretend 7.1 if using UHJ output, since they both provide full
  454. * horizontal surround.
  455. */
  456. if(mDevice->mUhjEncoder)
  457. return SPEAKERS_7;
  458. if(mDevice->Flags.test(DirectEar))
  459. return HEADPHONES;
  460. return SPEAKERS_2;
  461. case DevFmtQuad: return SPEAKERS_4;
  462. case DevFmtX51: return SPEAKERS_5;
  463. case DevFmtX61: return SPEAKERS_6;
  464. case DevFmtX71: return SPEAKERS_7;
  465. /* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to
  466. * suggest with-height surround sound (like HRTF).
  467. */
  468. case DevFmtX714: return SPEAKERS_7;
  469. /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
  470. * suggest full-sphere surround sound (like HRTF).
  471. */
  472. case DevFmtX3D71: return SPEAKERS_5;
  473. /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
  474. * provide full-sphere surround sound. Depends if apps are more likely to
  475. * consider headphones or 7.1 for surround sound support.
  476. */
  477. case DevFmtAmbi3D: return SPEAKERS_7;
  478. }
  479. ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans);
  480. return HEADPHONES;
  481. #undef EAX_PREFIX
  482. }
  483. void ALCcontext::eax_update_speaker_configuration()
  484. {
  485. mEaxSpeakerConfig = eax_detect_speaker_configuration();
  486. }
  487. void ALCcontext::eax_set_last_error_defaults() noexcept
  488. {
  489. mEaxLastError = EAXCONTEXT_DEFAULTLASTERROR;
  490. }
  491. void ALCcontext::eax_session_set_defaults() noexcept
  492. {
  493. mEaxSession.ulEAXVersion = EAXCONTEXT_DEFAULTEAXSESSION;
  494. mEaxSession.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS;
  495. }
  496. void ALCcontext::eax4_context_set_defaults(Eax4Props& props) noexcept
  497. {
  498. props.guidPrimaryFXSlotID = EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID;
  499. props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
  500. props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
  501. props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
  502. }
  503. void ALCcontext::eax4_context_set_defaults(Eax4State& state) noexcept
  504. {
  505. eax4_context_set_defaults(state.i);
  506. state.d = state.i;
  507. }
  508. void ALCcontext::eax5_context_set_defaults(Eax5Props& props) noexcept
  509. {
  510. props.guidPrimaryFXSlotID = EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID;
  511. props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
  512. props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
  513. props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
  514. props.flMacroFXFactor = EAXCONTEXT_DEFAULTMACROFXFACTOR;
  515. }
  516. void ALCcontext::eax5_context_set_defaults(Eax5State& state) noexcept
  517. {
  518. eax5_context_set_defaults(state.i);
  519. state.d = state.i;
  520. }
  521. void ALCcontext::eax_context_set_defaults()
  522. {
  523. eax5_context_set_defaults(mEax123);
  524. eax4_context_set_defaults(mEax4);
  525. eax5_context_set_defaults(mEax5);
  526. mEax = mEax5.i;
  527. mEaxVersion = 5;
  528. mEaxDf = EaxDirtyFlags{};
  529. }
  530. void ALCcontext::eax_set_defaults()
  531. {
  532. eax_set_last_error_defaults();
  533. eax_session_set_defaults();
  534. eax_context_set_defaults();
  535. }
  536. void ALCcontext::eax_dispatch_fx_slot(const EaxCall& call)
  537. {
  538. const auto fx_slot_index = call.get_fx_slot_index();
  539. if(!fx_slot_index.has_value())
  540. eax_fail("Invalid fx slot index.");
  541. auto& fx_slot = eaxGetFxSlot(*fx_slot_index);
  542. if(fx_slot.eax_dispatch(call))
  543. {
  544. std::lock_guard<std::mutex> source_lock{mSourceLock};
  545. ForEachSource(this, std::mem_fn(&ALsource::eaxMarkAsChanged));
  546. }
  547. }
  548. void ALCcontext::eax_dispatch_source(const EaxCall& call)
  549. {
  550. const auto source_id = call.get_property_al_name();
  551. std::lock_guard<std::mutex> source_lock{mSourceLock};
  552. const auto source = ALsource::EaxLookupSource(*this, source_id);
  553. if (source == nullptr)
  554. eax_fail("Source not found.");
  555. source->eaxDispatch(call);
  556. }
  557. void ALCcontext::eax_get_misc(const EaxCall& call)
  558. {
  559. switch(call.get_property_id())
  560. {
  561. case EAXCONTEXT_NONE:
  562. break;
  563. case EAXCONTEXT_LASTERROR:
  564. call.set_value<ContextException>(mEaxLastError);
  565. mEaxLastError = EAX_OK;
  566. break;
  567. case EAXCONTEXT_SPEAKERCONFIG:
  568. call.set_value<ContextException>(mEaxSpeakerConfig);
  569. break;
  570. case EAXCONTEXT_EAXSESSION:
  571. call.set_value<ContextException>(mEaxSession);
  572. break;
  573. default:
  574. eax_fail_unknown_property_id();
  575. }
  576. }
  577. void ALCcontext::eax4_get(const EaxCall& call, const Eax4Props& props)
  578. {
  579. switch(call.get_property_id())
  580. {
  581. case EAXCONTEXT_ALLPARAMETERS:
  582. call.set_value<ContextException>(props);
  583. break;
  584. case EAXCONTEXT_PRIMARYFXSLOTID:
  585. call.set_value<ContextException>(props.guidPrimaryFXSlotID);
  586. break;
  587. case EAXCONTEXT_DISTANCEFACTOR:
  588. call.set_value<ContextException>(props.flDistanceFactor);
  589. break;
  590. case EAXCONTEXT_AIRABSORPTIONHF:
  591. call.set_value<ContextException>(props.flAirAbsorptionHF);
  592. break;
  593. case EAXCONTEXT_HFREFERENCE:
  594. call.set_value<ContextException>(props.flHFReference);
  595. break;
  596. default:
  597. eax_get_misc(call);
  598. break;
  599. }
  600. }
  601. void ALCcontext::eax5_get(const EaxCall& call, const Eax5Props& props)
  602. {
  603. switch(call.get_property_id())
  604. {
  605. case EAXCONTEXT_ALLPARAMETERS:
  606. call.set_value<ContextException>(props);
  607. break;
  608. case EAXCONTEXT_PRIMARYFXSLOTID:
  609. call.set_value<ContextException>(props.guidPrimaryFXSlotID);
  610. break;
  611. case EAXCONTEXT_DISTANCEFACTOR:
  612. call.set_value<ContextException>(props.flDistanceFactor);
  613. break;
  614. case EAXCONTEXT_AIRABSORPTIONHF:
  615. call.set_value<ContextException>(props.flAirAbsorptionHF);
  616. break;
  617. case EAXCONTEXT_HFREFERENCE:
  618. call.set_value<ContextException>(props.flHFReference);
  619. break;
  620. case EAXCONTEXT_MACROFXFACTOR:
  621. call.set_value<ContextException>(props.flMacroFXFactor);
  622. break;
  623. default:
  624. eax_get_misc(call);
  625. break;
  626. }
  627. }
  628. void ALCcontext::eax_get(const EaxCall& call)
  629. {
  630. switch(call.get_version())
  631. {
  632. case 4: eax4_get(call, mEax4.i); break;
  633. case 5: eax5_get(call, mEax5.i); break;
  634. default: eax_fail_unknown_version();
  635. }
  636. }
  637. void ALCcontext::eax_context_commit_primary_fx_slot_id()
  638. {
  639. mEaxPrimaryFxSlotIndex = mEax.guidPrimaryFXSlotID;
  640. }
  641. void ALCcontext::eax_context_commit_distance_factor()
  642. {
  643. if(mListener.mMetersPerUnit == mEax.flDistanceFactor)
  644. return;
  645. mListener.mMetersPerUnit = mEax.flDistanceFactor;
  646. mPropsDirty = true;
  647. }
  648. void ALCcontext::eax_context_commit_air_absorbtion_hf()
  649. {
  650. const auto new_value = level_mb_to_gain(mEax.flAirAbsorptionHF);
  651. if(mAirAbsorptionGainHF == new_value)
  652. return;
  653. mAirAbsorptionGainHF = new_value;
  654. mPropsDirty = true;
  655. }
  656. void ALCcontext::eax_context_commit_hf_reference()
  657. {
  658. // TODO
  659. }
  660. void ALCcontext::eax_context_commit_macro_fx_factor()
  661. {
  662. // TODO
  663. }
  664. void ALCcontext::eax_initialize_fx_slots()
  665. {
  666. mEaxFxSlots.initialize(*this);
  667. mEaxPrimaryFxSlotIndex = mEax.guidPrimaryFXSlotID;
  668. }
  669. void ALCcontext::eax_update_sources()
  670. {
  671. std::unique_lock<std::mutex> source_lock{mSourceLock};
  672. auto update_source = [](ALsource &source)
  673. { source.eaxCommit(); };
  674. ForEachSource(this, update_source);
  675. }
  676. void ALCcontext::eax_set_misc(const EaxCall& call)
  677. {
  678. switch(call.get_property_id())
  679. {
  680. case EAXCONTEXT_NONE:
  681. break;
  682. case EAXCONTEXT_SPEAKERCONFIG:
  683. eax_set<Eax5SpeakerConfigValidator>(call, mEaxSpeakerConfig);
  684. break;
  685. case EAXCONTEXT_EAXSESSION:
  686. eax_set<Eax5SessionAllValidator>(call, mEaxSession);
  687. break;
  688. default:
  689. eax_fail_unknown_property_id();
  690. }
  691. }
  692. void ALCcontext::eax4_defer_all(const EaxCall& call, Eax4State& state)
  693. {
  694. const auto& src = call.get_value<ContextException, const EAX40CONTEXTPROPERTIES>();
  695. Eax4AllValidator{}(src);
  696. const auto& dst_i = state.i;
  697. auto& dst_d = state.d;
  698. dst_d = src;
  699. if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
  700. mEaxDf |= eax_primary_fx_slot_id_dirty_bit;
  701. if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
  702. mEaxDf |= eax_distance_factor_dirty_bit;
  703. if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
  704. mEaxDf |= eax_air_absorption_hf_dirty_bit;
  705. if(dst_i.flHFReference != dst_d.flHFReference)
  706. mEaxDf |= eax_hf_reference_dirty_bit;
  707. }
  708. void ALCcontext::eax4_defer(const EaxCall& call, Eax4State& state)
  709. {
  710. switch(call.get_property_id())
  711. {
  712. case EAXCONTEXT_ALLPARAMETERS:
  713. eax4_defer_all(call, state);
  714. break;
  715. case EAXCONTEXT_PRIMARYFXSLOTID:
  716. eax_defer<Eax4PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
  717. call, state, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
  718. break;
  719. case EAXCONTEXT_DISTANCEFACTOR:
  720. eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
  721. call, state, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
  722. break;
  723. case EAXCONTEXT_AIRABSORPTIONHF:
  724. eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
  725. call, state, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
  726. break;
  727. case EAXCONTEXT_HFREFERENCE:
  728. eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
  729. call, state, &EAX40CONTEXTPROPERTIES::flHFReference);
  730. break;
  731. default:
  732. eax_set_misc(call);
  733. break;
  734. }
  735. }
  736. void ALCcontext::eax5_defer_all(const EaxCall& call, Eax5State& state)
  737. {
  738. const auto& src = call.get_value<ContextException, const EAX50CONTEXTPROPERTIES>();
  739. Eax4AllValidator{}(src);
  740. const auto& dst_i = state.i;
  741. auto& dst_d = state.d;
  742. dst_d = src;
  743. if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
  744. mEaxDf |= eax_primary_fx_slot_id_dirty_bit;
  745. if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
  746. mEaxDf |= eax_distance_factor_dirty_bit;
  747. if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
  748. mEaxDf |= eax_air_absorption_hf_dirty_bit;
  749. if(dst_i.flHFReference != dst_d.flHFReference)
  750. mEaxDf |= eax_hf_reference_dirty_bit;
  751. if(dst_i.flMacroFXFactor != dst_d.flMacroFXFactor)
  752. mEaxDf |= eax_macro_fx_factor_dirty_bit;
  753. }
  754. void ALCcontext::eax5_defer(const EaxCall& call, Eax5State& state)
  755. {
  756. switch(call.get_property_id())
  757. {
  758. case EAXCONTEXT_ALLPARAMETERS:
  759. eax5_defer_all(call, state);
  760. break;
  761. case EAXCONTEXT_PRIMARYFXSLOTID:
  762. eax_defer<Eax5PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
  763. call, state, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
  764. break;
  765. case EAXCONTEXT_DISTANCEFACTOR:
  766. eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
  767. call, state, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
  768. break;
  769. case EAXCONTEXT_AIRABSORPTIONHF:
  770. eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
  771. call, state, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
  772. break;
  773. case EAXCONTEXT_HFREFERENCE:
  774. eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
  775. call, state, &EAX50CONTEXTPROPERTIES::flHFReference);
  776. break;
  777. case EAXCONTEXT_MACROFXFACTOR:
  778. eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(
  779. call, state, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
  780. break;
  781. default:
  782. eax_set_misc(call);
  783. break;
  784. }
  785. }
  786. void ALCcontext::eax_set(const EaxCall& call)
  787. {
  788. const auto version = call.get_version();
  789. switch(version)
  790. {
  791. case 4: eax4_defer(call, mEax4); break;
  792. case 5: eax5_defer(call, mEax5); break;
  793. default: eax_fail_unknown_version();
  794. }
  795. if(version != mEaxVersion)
  796. mEaxDf = ~EaxDirtyFlags();
  797. mEaxVersion = version;
  798. }
  799. void ALCcontext::eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df)
  800. {
  801. if(mEaxDf == EaxDirtyFlags{})
  802. return;
  803. eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
  804. state, dst_df, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
  805. eax_context_commit_property<eax_distance_factor_dirty_bit>(
  806. state, dst_df, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
  807. eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
  808. state, dst_df, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
  809. eax_context_commit_property<eax_hf_reference_dirty_bit>(
  810. state, dst_df, &EAX40CONTEXTPROPERTIES::flHFReference);
  811. mEaxDf = EaxDirtyFlags{};
  812. }
  813. void ALCcontext::eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df)
  814. {
  815. if(mEaxDf == EaxDirtyFlags{})
  816. return;
  817. eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
  818. state, dst_df, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
  819. eax_context_commit_property<eax_distance_factor_dirty_bit>(
  820. state, dst_df, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
  821. eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
  822. state, dst_df, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
  823. eax_context_commit_property<eax_hf_reference_dirty_bit>(
  824. state, dst_df, &EAX50CONTEXTPROPERTIES::flHFReference);
  825. eax_context_commit_property<eax_macro_fx_factor_dirty_bit>(
  826. state, dst_df, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
  827. mEaxDf = EaxDirtyFlags{};
  828. }
  829. void ALCcontext::eax_context_commit()
  830. {
  831. auto dst_df = EaxDirtyFlags{};
  832. switch(mEaxVersion)
  833. {
  834. case 1:
  835. case 2:
  836. case 3:
  837. eax5_context_commit(mEax123, dst_df);
  838. break;
  839. case 4:
  840. eax4_context_commit(mEax4, dst_df);
  841. break;
  842. case 5:
  843. eax5_context_commit(mEax5, dst_df);
  844. break;
  845. }
  846. if(dst_df == EaxDirtyFlags{})
  847. return;
  848. if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
  849. eax_context_commit_primary_fx_slot_id();
  850. if((dst_df & eax_distance_factor_dirty_bit) != EaxDirtyFlags{})
  851. eax_context_commit_distance_factor();
  852. if((dst_df & eax_air_absorption_hf_dirty_bit) != EaxDirtyFlags{})
  853. eax_context_commit_air_absorbtion_hf();
  854. if((dst_df & eax_hf_reference_dirty_bit) != EaxDirtyFlags{})
  855. eax_context_commit_hf_reference();
  856. if((dst_df & eax_macro_fx_factor_dirty_bit) != EaxDirtyFlags{})
  857. eax_context_commit_macro_fx_factor();
  858. if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
  859. eax_update_sources();
  860. }
  861. void ALCcontext::eaxCommit()
  862. {
  863. mEaxNeedsCommit = false;
  864. eax_context_commit();
  865. eaxCommitFxSlots();
  866. eax_update_sources();
  867. }
  868. FORCE_ALIGN auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id,
  869. ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
  870. {
  871. auto context = GetContextRef();
  872. if(!context) UNLIKELY return AL_INVALID_OPERATION;
  873. return EAXSetDirect(context.get(), property_set_id, property_id, source_id, value, value_size);
  874. }
  875. FORCE_ALIGN auto AL_APIENTRY EAXSetDirect(ALCcontext *context, const GUID *property_set_id,
  876. ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
  877. try
  878. {
  879. std::lock_guard<std::mutex> prop_lock{context->mPropLock};
  880. return context->eax_eax_set(property_set_id, property_id, source_id, value, value_size);
  881. }
  882. catch(...)
  883. {
  884. context->eaxSetLastError();
  885. eax_log_exception(std::data(__func__));
  886. return AL_INVALID_OPERATION;
  887. }
  888. FORCE_ALIGN auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id,
  889. ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
  890. {
  891. auto context = GetContextRef();
  892. if(!context) UNLIKELY return AL_INVALID_OPERATION;
  893. return EAXGetDirect(context.get(), property_set_id, property_id, source_id, value, value_size);
  894. }
  895. FORCE_ALIGN auto AL_APIENTRY EAXGetDirect(ALCcontext *context, const GUID *property_set_id,
  896. ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
  897. try
  898. {
  899. std::lock_guard<std::mutex> prop_lock{context->mPropLock};
  900. return context->eax_eax_get(property_set_id, property_id, source_id, value, value_size);
  901. }
  902. catch(...)
  903. {
  904. context->eaxSetLastError();
  905. eax_log_exception(std::data(__func__));
  906. return AL_INVALID_OPERATION;
  907. }
  908. #endif // ALSOFT_EAX