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

1797 lines
51 KiB

  1. /**
  2. * OpenAL cross platform audio library
  3. * Copyright (C) 1999-2007 by authors.
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Library General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Library General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Library General Public
  15. * License along with this library; if not, write to the
  16. * Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18. * Or go to http://www.gnu.org/copyleft/lgpl.html
  19. */
  20. #include "config.h"
  21. #include "auxeffectslot.h"
  22. #include <algorithm>
  23. #include <cassert>
  24. #include <cstdint>
  25. #include <iterator>
  26. #include <memory>
  27. #include <mutex>
  28. #include <numeric>
  29. #include <thread>
  30. #include "AL/al.h"
  31. #include "AL/alc.h"
  32. #include "AL/efx.h"
  33. #include "albit.h"
  34. #include "alc/alu.h"
  35. #include "alc/context.h"
  36. #include "alc/device.h"
  37. #include "alc/inprogext.h"
  38. #include "almalloc.h"
  39. #include "alnumeric.h"
  40. #include "alspan.h"
  41. #include "buffer.h"
  42. #include "core/except.h"
  43. #include "core/fpu_ctrl.h"
  44. #include "core/logging.h"
  45. #include "effect.h"
  46. #include "opthelpers.h"
  47. #ifdef ALSOFT_EAX
  48. #include "eax/exception.h"
  49. #include "eax/utils.h"
  50. #endif // ALSOFT_EAX
  51. namespace {
  52. struct FactoryItem {
  53. EffectSlotType Type;
  54. EffectStateFactory* (&GetFactory)(void);
  55. };
  56. constexpr FactoryItem FactoryList[] = {
  57. { EffectSlotType::None, NullStateFactory_getFactory },
  58. { EffectSlotType::EAXReverb, ReverbStateFactory_getFactory },
  59. { EffectSlotType::Reverb, StdReverbStateFactory_getFactory },
  60. { EffectSlotType::Autowah, AutowahStateFactory_getFactory },
  61. { EffectSlotType::Chorus, ChorusStateFactory_getFactory },
  62. { EffectSlotType::Compressor, CompressorStateFactory_getFactory },
  63. { EffectSlotType::Distortion, DistortionStateFactory_getFactory },
  64. { EffectSlotType::Echo, EchoStateFactory_getFactory },
  65. { EffectSlotType::Equalizer, EqualizerStateFactory_getFactory },
  66. { EffectSlotType::Flanger, FlangerStateFactory_getFactory },
  67. { EffectSlotType::FrequencyShifter, FshifterStateFactory_getFactory },
  68. { EffectSlotType::RingModulator, ModulatorStateFactory_getFactory },
  69. { EffectSlotType::PitchShifter, PshifterStateFactory_getFactory },
  70. { EffectSlotType::VocalMorpher, VmorpherStateFactory_getFactory },
  71. { EffectSlotType::DedicatedDialog, DedicatedStateFactory_getFactory },
  72. { EffectSlotType::DedicatedLFE, DedicatedStateFactory_getFactory },
  73. { EffectSlotType::Convolution, ConvolutionStateFactory_getFactory },
  74. };
  75. EffectStateFactory *getFactoryByType(EffectSlotType type)
  76. {
  77. auto iter = std::find_if(std::begin(FactoryList), std::end(FactoryList),
  78. [type](const FactoryItem &item) noexcept -> bool
  79. { return item.Type == type; });
  80. return (iter != std::end(FactoryList)) ? iter->GetFactory() : nullptr;
  81. }
  82. inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
  83. {
  84. const size_t lidx{(id-1) >> 6};
  85. const ALuint slidx{(id-1) & 0x3f};
  86. if UNLIKELY(lidx >= context->mEffectSlotList.size())
  87. return nullptr;
  88. EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
  89. if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
  90. return nullptr;
  91. return sublist.EffectSlots + slidx;
  92. }
  93. inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept
  94. {
  95. const size_t lidx{(id-1) >> 6};
  96. const ALuint slidx{(id-1) & 0x3f};
  97. if UNLIKELY(lidx >= device->EffectList.size())
  98. return nullptr;
  99. EffectSubList &sublist = device->EffectList[lidx];
  100. if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
  101. return nullptr;
  102. return sublist.Effects + slidx;
  103. }
  104. inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
  105. {
  106. const size_t lidx{(id-1) >> 6};
  107. const ALuint slidx{(id-1) & 0x3f};
  108. if UNLIKELY(lidx >= device->BufferList.size())
  109. return nullptr;
  110. BufferSubList &sublist = device->BufferList[lidx];
  111. if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
  112. return nullptr;
  113. return sublist.Buffers + slidx;
  114. }
  115. inline auto GetEffectBuffer(ALbuffer *buffer) noexcept -> EffectState::Buffer
  116. {
  117. if(!buffer) return EffectState::Buffer{};
  118. return EffectState::Buffer{buffer, buffer->mData};
  119. }
  120. void AddActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
  121. {
  122. if(auxslots.empty()) return;
  123. EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
  124. size_t newcount{curarray->size() + auxslots.size()};
  125. /* Insert the new effect slots into the head of the array, followed by the
  126. * existing ones.
  127. */
  128. EffectSlotArray *newarray = EffectSlot::CreatePtrArray(newcount);
  129. auto slotiter = std::transform(auxslots.begin(), auxslots.end(), newarray->begin(),
  130. [](ALeffectslot *auxslot) noexcept { return &auxslot->mSlot; });
  131. std::copy(curarray->begin(), curarray->end(), slotiter);
  132. /* Remove any duplicates (first instance of each will be kept). */
  133. auto last = newarray->end();
  134. for(auto start=newarray->begin()+1;;)
  135. {
  136. last = std::remove(start, last, *(start-1));
  137. if(start == last) break;
  138. ++start;
  139. }
  140. newcount = static_cast<size_t>(std::distance(newarray->begin(), last));
  141. /* Reallocate newarray if the new size ended up smaller from duplicate
  142. * removal.
  143. */
  144. if UNLIKELY(newcount < newarray->size())
  145. {
  146. curarray = newarray;
  147. newarray = EffectSlot::CreatePtrArray(newcount);
  148. std::copy_n(curarray->begin(), newcount, newarray->begin());
  149. delete curarray;
  150. curarray = nullptr;
  151. }
  152. std::uninitialized_fill_n(newarray->end(), newcount, nullptr);
  153. curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
  154. context->mDevice->waitForMix();
  155. al::destroy_n(curarray->end(), curarray->size());
  156. delete curarray;
  157. }
  158. void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
  159. {
  160. if(auxslots.empty()) return;
  161. EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
  162. /* Don't shrink the allocated array size since we don't know how many (if
  163. * any) of the effect slots to remove are in the array.
  164. */
  165. EffectSlotArray *newarray = EffectSlot::CreatePtrArray(curarray->size());
  166. auto new_end = std::copy(curarray->begin(), curarray->end(), newarray->begin());
  167. /* Remove elements from newarray that match any ID in slotids. */
  168. for(const ALeffectslot *auxslot : auxslots)
  169. {
  170. auto slot_match = [auxslot](EffectSlot *slot) noexcept -> bool
  171. { return (slot == &auxslot->mSlot); };
  172. new_end = std::remove_if(newarray->begin(), new_end, slot_match);
  173. }
  174. /* Reallocate with the new size. */
  175. auto newsize = static_cast<size_t>(std::distance(newarray->begin(), new_end));
  176. if LIKELY(newsize != newarray->size())
  177. {
  178. curarray = newarray;
  179. newarray = EffectSlot::CreatePtrArray(newsize);
  180. std::copy_n(curarray->begin(), newsize, newarray->begin());
  181. delete curarray;
  182. curarray = nullptr;
  183. }
  184. std::uninitialized_fill_n(newarray->end(), newsize, nullptr);
  185. curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
  186. context->mDevice->waitForMix();
  187. al::destroy_n(curarray->end(), curarray->size());
  188. delete curarray;
  189. }
  190. EffectSlotType EffectSlotTypeFromEnum(ALenum type)
  191. {
  192. switch(type)
  193. {
  194. case AL_EFFECT_NULL: return EffectSlotType::None;
  195. case AL_EFFECT_REVERB: return EffectSlotType::Reverb;
  196. case AL_EFFECT_CHORUS: return EffectSlotType::Chorus;
  197. case AL_EFFECT_DISTORTION: return EffectSlotType::Distortion;
  198. case AL_EFFECT_ECHO: return EffectSlotType::Echo;
  199. case AL_EFFECT_FLANGER: return EffectSlotType::Flanger;
  200. case AL_EFFECT_FREQUENCY_SHIFTER: return EffectSlotType::FrequencyShifter;
  201. case AL_EFFECT_VOCAL_MORPHER: return EffectSlotType::VocalMorpher;
  202. case AL_EFFECT_PITCH_SHIFTER: return EffectSlotType::PitchShifter;
  203. case AL_EFFECT_RING_MODULATOR: return EffectSlotType::RingModulator;
  204. case AL_EFFECT_AUTOWAH: return EffectSlotType::Autowah;
  205. case AL_EFFECT_COMPRESSOR: return EffectSlotType::Compressor;
  206. case AL_EFFECT_EQUALIZER: return EffectSlotType::Equalizer;
  207. case AL_EFFECT_EAXREVERB: return EffectSlotType::EAXReverb;
  208. case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::DedicatedLFE;
  209. case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::DedicatedDialog;
  210. case AL_EFFECT_CONVOLUTION_REVERB_SOFT: return EffectSlotType::Convolution;
  211. }
  212. ERR("Unhandled effect enum: 0x%04x\n", type);
  213. return EffectSlotType::None;
  214. }
  215. bool EnsureEffectSlots(ALCcontext *context, size_t needed)
  216. {
  217. size_t count{std::accumulate(context->mEffectSlotList.cbegin(),
  218. context->mEffectSlotList.cend(), size_t{0},
  219. [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
  220. { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
  221. while(needed > count)
  222. {
  223. if UNLIKELY(context->mEffectSlotList.size() >= 1<<25)
  224. return false;
  225. context->mEffectSlotList.emplace_back();
  226. auto sublist = context->mEffectSlotList.end() - 1;
  227. sublist->FreeMask = ~0_u64;
  228. sublist->EffectSlots = static_cast<ALeffectslot*>(
  229. al_calloc(alignof(ALeffectslot), sizeof(ALeffectslot)*64));
  230. if UNLIKELY(!sublist->EffectSlots)
  231. {
  232. context->mEffectSlotList.pop_back();
  233. return false;
  234. }
  235. count += 64;
  236. }
  237. return true;
  238. }
  239. ALeffectslot *AllocEffectSlot(ALCcontext *context)
  240. {
  241. auto sublist = std::find_if(context->mEffectSlotList.begin(), context->mEffectSlotList.end(),
  242. [](const EffectSlotSubList &entry) noexcept -> bool
  243. { return entry.FreeMask != 0; });
  244. auto lidx = static_cast<ALuint>(std::distance(context->mEffectSlotList.begin(), sublist));
  245. auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
  246. ASSUME(slidx < 64);
  247. ALeffectslot *slot{al::construct_at(sublist->EffectSlots + slidx)};
  248. aluInitEffectPanning(&slot->mSlot, context);
  249. /* Add 1 to avoid source ID 0. */
  250. slot->id = ((lidx<<6) | slidx) + 1;
  251. context->mNumEffectSlots += 1;
  252. sublist->FreeMask &= ~(1_u64 << slidx);
  253. return slot;
  254. }
  255. void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
  256. {
  257. const ALuint id{slot->id - 1};
  258. const size_t lidx{id >> 6};
  259. const ALuint slidx{id & 0x3f};
  260. al::destroy_at(slot);
  261. context->mEffectSlotList[lidx].FreeMask |= 1_u64 << slidx;
  262. context->mNumEffectSlots--;
  263. }
  264. inline void UpdateProps(ALeffectslot *slot, ALCcontext *context)
  265. {
  266. if(!context->mDeferUpdates && slot->mState == SlotState::Playing)
  267. {
  268. slot->updateProps(context);
  269. return;
  270. }
  271. slot->mPropsDirty = true;
  272. }
  273. } // namespace
  274. AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
  275. START_API_FUNC
  276. {
  277. ContextRef context{GetContextRef()};
  278. if UNLIKELY(!context) return;
  279. if UNLIKELY(n < 0)
  280. context->setError(AL_INVALID_VALUE, "Generating %d effect slots", n);
  281. if UNLIKELY(n <= 0) return;
  282. std::unique_lock<std::mutex> slotlock{context->mEffectSlotLock};
  283. ALCdevice *device{context->mALDevice.get()};
  284. if(static_cast<ALuint>(n) > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
  285. {
  286. context->setError(AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)",
  287. device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n);
  288. return;
  289. }
  290. if(!EnsureEffectSlots(context.get(), static_cast<ALuint>(n)))
  291. {
  292. context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n,
  293. (n==1) ? "" : "s");
  294. return;
  295. }
  296. if(n == 1)
  297. {
  298. ALeffectslot *slot{AllocEffectSlot(context.get())};
  299. if(!slot) return;
  300. effectslots[0] = slot->id;
  301. }
  302. else
  303. {
  304. al::vector<ALuint> ids;
  305. ALsizei count{n};
  306. ids.reserve(static_cast<ALuint>(count));
  307. do {
  308. ALeffectslot *slot{AllocEffectSlot(context.get())};
  309. if(!slot)
  310. {
  311. slotlock.unlock();
  312. alDeleteAuxiliaryEffectSlots(static_cast<ALsizei>(ids.size()), ids.data());
  313. return;
  314. }
  315. ids.emplace_back(slot->id);
  316. } while(--count);
  317. std::copy(ids.cbegin(), ids.cend(), effectslots);
  318. }
  319. }
  320. END_API_FUNC
  321. AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots)
  322. START_API_FUNC
  323. {
  324. ContextRef context{GetContextRef()};
  325. if UNLIKELY(!context) return;
  326. if UNLIKELY(n < 0)
  327. context->setError(AL_INVALID_VALUE, "Deleting %d effect slots", n);
  328. if UNLIKELY(n <= 0) return;
  329. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  330. if(n == 1)
  331. {
  332. ALeffectslot *slot{LookupEffectSlot(context.get(), effectslots[0])};
  333. if UNLIKELY(!slot)
  334. {
  335. context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[0]);
  336. return;
  337. }
  338. if UNLIKELY(ReadRef(slot->ref) != 0)
  339. {
  340. context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
  341. effectslots[0]);
  342. return;
  343. }
  344. RemoveActiveEffectSlots({&slot, 1u}, context.get());
  345. FreeEffectSlot(context.get(), slot);
  346. }
  347. else
  348. {
  349. auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
  350. for(size_t i{0};i < slots.size();++i)
  351. {
  352. ALeffectslot *slot{LookupEffectSlot(context.get(), effectslots[i])};
  353. if UNLIKELY(!slot)
  354. {
  355. context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[i]);
  356. return;
  357. }
  358. if UNLIKELY(ReadRef(slot->ref) != 0)
  359. {
  360. context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
  361. effectslots[i]);
  362. return;
  363. }
  364. slots[i] = slot;
  365. }
  366. /* Remove any duplicates. */
  367. auto slots_end = slots.end();
  368. for(auto start=slots.begin()+1;start != slots_end;++start)
  369. {
  370. slots_end = std::remove(start, slots_end, *(start-1));
  371. if(start == slots_end) break;
  372. }
  373. slots.erase(slots_end, slots.end());
  374. /* All effectslots are valid, remove and delete them */
  375. RemoveActiveEffectSlots(slots, context.get());
  376. for(ALeffectslot *slot : slots)
  377. FreeEffectSlot(context.get(), slot);
  378. }
  379. }
  380. END_API_FUNC
  381. AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
  382. START_API_FUNC
  383. {
  384. ContextRef context{GetContextRef()};
  385. if LIKELY(context)
  386. {
  387. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  388. if(LookupEffectSlot(context.get(), effectslot) != nullptr)
  389. return AL_TRUE;
  390. }
  391. return AL_FALSE;
  392. }
  393. END_API_FUNC
  394. AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid)
  395. START_API_FUNC
  396. {
  397. ContextRef context{GetContextRef()};
  398. if UNLIKELY(!context) return;
  399. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  400. ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
  401. if UNLIKELY(!slot)
  402. {
  403. context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
  404. return;
  405. }
  406. if(slot->mState == SlotState::Playing)
  407. return;
  408. slot->mPropsDirty = false;
  409. slot->updateProps(context.get());
  410. AddActiveEffectSlots({&slot, 1}, context.get());
  411. slot->mState = SlotState::Playing;
  412. }
  413. END_API_FUNC
  414. AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids)
  415. START_API_FUNC
  416. {
  417. ContextRef context{GetContextRef()};
  418. if UNLIKELY(!context) return;
  419. if UNLIKELY(n < 0)
  420. context->setError(AL_INVALID_VALUE, "Playing %d effect slots", n);
  421. if UNLIKELY(n <= 0) return;
  422. auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
  423. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  424. for(size_t i{0};i < slots.size();++i)
  425. {
  426. ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])};
  427. if UNLIKELY(!slot)
  428. {
  429. context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]);
  430. return;
  431. }
  432. if(slot->mState != SlotState::Playing)
  433. {
  434. slot->mPropsDirty = false;
  435. slot->updateProps(context.get());
  436. }
  437. slots[i] = slot;
  438. };
  439. AddActiveEffectSlots(slots, context.get());
  440. for(auto slot : slots)
  441. slot->mState = SlotState::Playing;
  442. }
  443. END_API_FUNC
  444. AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid)
  445. START_API_FUNC
  446. {
  447. ContextRef context{GetContextRef()};
  448. if UNLIKELY(!context) return;
  449. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  450. ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
  451. if UNLIKELY(!slot)
  452. {
  453. context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
  454. return;
  455. }
  456. RemoveActiveEffectSlots({&slot, 1}, context.get());
  457. slot->mState = SlotState::Stopped;
  458. }
  459. END_API_FUNC
  460. AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids)
  461. START_API_FUNC
  462. {
  463. ContextRef context{GetContextRef()};
  464. if UNLIKELY(!context) return;
  465. if UNLIKELY(n < 0)
  466. context->setError(AL_INVALID_VALUE, "Stopping %d effect slots", n);
  467. if UNLIKELY(n <= 0) return;
  468. auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
  469. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  470. for(size_t i{0};i < slots.size();++i)
  471. {
  472. ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])};
  473. if UNLIKELY(!slot)
  474. {
  475. context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]);
  476. return;
  477. }
  478. slots[i] = slot;
  479. };
  480. RemoveActiveEffectSlots(slots, context.get());
  481. for(auto slot : slots)
  482. slot->mState = SlotState::Stopped;
  483. }
  484. END_API_FUNC
  485. AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value)
  486. START_API_FUNC
  487. {
  488. ContextRef context{GetContextRef()};
  489. if UNLIKELY(!context) return;
  490. std::lock_guard<std::mutex> _{context->mPropLock};
  491. std::lock_guard<std::mutex> __{context->mEffectSlotLock};
  492. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  493. if UNLIKELY(!slot)
  494. SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  495. ALeffectslot *target{};
  496. ALCdevice *device{};
  497. ALenum err{};
  498. switch(param)
  499. {
  500. case AL_EFFECTSLOT_EFFECT:
  501. device = context->mALDevice.get();
  502. {
  503. std::lock_guard<std::mutex> ___{device->EffectLock};
  504. ALeffect *effect{value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr};
  505. if(effect)
  506. err = slot->initEffect(effect->type, effect->Props, context.get());
  507. else
  508. {
  509. if(value != 0)
  510. SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid effect ID %u", value);
  511. err = slot->initEffect(AL_EFFECT_NULL, EffectProps{}, context.get());
  512. }
  513. }
  514. if UNLIKELY(err != AL_NO_ERROR)
  515. {
  516. context->setError(err, "Effect initialization failed");
  517. return;
  518. }
  519. if UNLIKELY(slot->mState == SlotState::Initial)
  520. {
  521. slot->mPropsDirty = false;
  522. slot->updateProps(context.get());
  523. AddActiveEffectSlots({&slot, 1}, context.get());
  524. slot->mState = SlotState::Playing;
  525. return;
  526. }
  527. break;
  528. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  529. if(!(value == AL_TRUE || value == AL_FALSE))
  530. SETERR_RETURN(context, AL_INVALID_VALUE,,
  531. "Effect slot auxiliary send auto out of range");
  532. if UNLIKELY(slot->AuxSendAuto == !!value)
  533. return;
  534. slot->AuxSendAuto = !!value;
  535. break;
  536. case AL_EFFECTSLOT_TARGET_SOFT:
  537. target = LookupEffectSlot(context.get(), static_cast<ALuint>(value));
  538. if(value && !target)
  539. SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid effect slot target ID");
  540. if UNLIKELY(slot->Target == target)
  541. return;
  542. if(target)
  543. {
  544. ALeffectslot *checker{target};
  545. while(checker && checker != slot)
  546. checker = checker->Target;
  547. if(checker)
  548. SETERR_RETURN(context, AL_INVALID_OPERATION,,
  549. "Setting target of effect slot ID %u to %u creates circular chain", slot->id,
  550. target->id);
  551. }
  552. if(ALeffectslot *oldtarget{slot->Target})
  553. {
  554. /* We must force an update if there was an existing effect slot
  555. * target, in case it's about to be deleted.
  556. */
  557. if(target) IncrementRef(target->ref);
  558. DecrementRef(oldtarget->ref);
  559. slot->Target = target;
  560. slot->updateProps(context.get());
  561. return;
  562. }
  563. if(target) IncrementRef(target->ref);
  564. slot->Target = target;
  565. break;
  566. case AL_BUFFER:
  567. device = context->mALDevice.get();
  568. if(slot->mState == SlotState::Playing)
  569. SETERR_RETURN(context, AL_INVALID_OPERATION,,
  570. "Setting buffer on playing effect slot %u", slot->id);
  571. if(ALbuffer *buffer{slot->Buffer})
  572. {
  573. if UNLIKELY(buffer->id == static_cast<ALuint>(value))
  574. return;
  575. }
  576. else if UNLIKELY(value == 0)
  577. return;
  578. {
  579. std::lock_guard<std::mutex> ___{device->BufferLock};
  580. ALbuffer *buffer{};
  581. if(value)
  582. {
  583. buffer = LookupBuffer(device, static_cast<ALuint>(value));
  584. if(!buffer) SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid buffer ID");
  585. if(buffer->mCallback)
  586. SETERR_RETURN(context, AL_INVALID_OPERATION,,
  587. "Callback buffer not valid for effects");
  588. IncrementRef(buffer->ref);
  589. }
  590. if(ALbuffer *oldbuffer{slot->Buffer})
  591. DecrementRef(oldbuffer->ref);
  592. slot->Buffer = buffer;
  593. FPUCtl mixer_mode{};
  594. auto *state = slot->Effect.State.get();
  595. state->deviceUpdate(device, GetEffectBuffer(buffer));
  596. }
  597. break;
  598. case AL_EFFECTSLOT_STATE_SOFT:
  599. SETERR_RETURN(context, AL_INVALID_OPERATION,, "AL_EFFECTSLOT_STATE_SOFT is read-only");
  600. default:
  601. SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid effect slot integer property 0x%04x",
  602. param);
  603. }
  604. UpdateProps(slot, context.get());
  605. }
  606. END_API_FUNC
  607. AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
  608. START_API_FUNC
  609. {
  610. switch(param)
  611. {
  612. case AL_EFFECTSLOT_EFFECT:
  613. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  614. case AL_EFFECTSLOT_TARGET_SOFT:
  615. case AL_EFFECTSLOT_STATE_SOFT:
  616. case AL_BUFFER:
  617. alAuxiliaryEffectSloti(effectslot, param, values[0]);
  618. return;
  619. }
  620. ContextRef context{GetContextRef()};
  621. if UNLIKELY(!context) return;
  622. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  623. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  624. if UNLIKELY(!slot)
  625. SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  626. switch(param)
  627. {
  628. default:
  629. SETERR_RETURN(context, AL_INVALID_ENUM,,
  630. "Invalid effect slot integer-vector property 0x%04x", param);
  631. }
  632. }
  633. END_API_FUNC
  634. AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
  635. START_API_FUNC
  636. {
  637. ContextRef context{GetContextRef()};
  638. if UNLIKELY(!context) return;
  639. std::lock_guard<std::mutex> _{context->mPropLock};
  640. std::lock_guard<std::mutex> __{context->mEffectSlotLock};
  641. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  642. if UNLIKELY(!slot)
  643. SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  644. switch(param)
  645. {
  646. case AL_EFFECTSLOT_GAIN:
  647. if(!(value >= 0.0f && value <= 1.0f))
  648. SETERR_RETURN(context, AL_INVALID_VALUE,, "Effect slot gain out of range");
  649. if UNLIKELY(slot->Gain == value)
  650. return;
  651. slot->Gain = value;
  652. break;
  653. default:
  654. SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid effect slot float property 0x%04x",
  655. param);
  656. }
  657. UpdateProps(slot, context.get());
  658. }
  659. END_API_FUNC
  660. AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
  661. START_API_FUNC
  662. {
  663. switch(param)
  664. {
  665. case AL_EFFECTSLOT_GAIN:
  666. alAuxiliaryEffectSlotf(effectslot, param, values[0]);
  667. return;
  668. }
  669. ContextRef context{GetContextRef()};
  670. if UNLIKELY(!context) return;
  671. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  672. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  673. if UNLIKELY(!slot)
  674. SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  675. switch(param)
  676. {
  677. default:
  678. SETERR_RETURN(context, AL_INVALID_ENUM,,
  679. "Invalid effect slot float-vector property 0x%04x", param);
  680. }
  681. }
  682. END_API_FUNC
  683. AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
  684. START_API_FUNC
  685. {
  686. ContextRef context{GetContextRef()};
  687. if UNLIKELY(!context) return;
  688. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  689. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  690. if UNLIKELY(!slot)
  691. SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  692. switch(param)
  693. {
  694. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  695. *value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE;
  696. break;
  697. case AL_EFFECTSLOT_TARGET_SOFT:
  698. if(auto *target = slot->Target)
  699. *value = static_cast<ALint>(target->id);
  700. else
  701. *value = 0;
  702. break;
  703. case AL_EFFECTSLOT_STATE_SOFT:
  704. *value = static_cast<int>(slot->mState);
  705. break;
  706. case AL_BUFFER:
  707. if(auto *buffer = slot->Buffer)
  708. *value = static_cast<ALint>(buffer->id);
  709. else
  710. *value = 0;
  711. break;
  712. default:
  713. context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param);
  714. }
  715. }
  716. END_API_FUNC
  717. AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
  718. START_API_FUNC
  719. {
  720. switch(param)
  721. {
  722. case AL_EFFECTSLOT_EFFECT:
  723. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  724. case AL_EFFECTSLOT_TARGET_SOFT:
  725. case AL_EFFECTSLOT_STATE_SOFT:
  726. case AL_BUFFER:
  727. alGetAuxiliaryEffectSloti(effectslot, param, values);
  728. return;
  729. }
  730. ContextRef context{GetContextRef()};
  731. if UNLIKELY(!context) return;
  732. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  733. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  734. if UNLIKELY(!slot)
  735. SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  736. switch(param)
  737. {
  738. default:
  739. context->setError(AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
  740. param);
  741. }
  742. }
  743. END_API_FUNC
  744. AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
  745. START_API_FUNC
  746. {
  747. ContextRef context{GetContextRef()};
  748. if UNLIKELY(!context) return;
  749. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  750. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  751. if UNLIKELY(!slot)
  752. SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  753. switch(param)
  754. {
  755. case AL_EFFECTSLOT_GAIN:
  756. *value = slot->Gain;
  757. break;
  758. default:
  759. context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param);
  760. }
  761. }
  762. END_API_FUNC
  763. AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
  764. START_API_FUNC
  765. {
  766. switch(param)
  767. {
  768. case AL_EFFECTSLOT_GAIN:
  769. alGetAuxiliaryEffectSlotf(effectslot, param, values);
  770. return;
  771. }
  772. ContextRef context{GetContextRef()};
  773. if UNLIKELY(!context) return;
  774. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  775. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  776. if UNLIKELY(!slot)
  777. SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  778. switch(param)
  779. {
  780. default:
  781. context->setError(AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
  782. param);
  783. }
  784. }
  785. END_API_FUNC
  786. ALeffectslot::ALeffectslot()
  787. {
  788. EffectStateFactory *factory{getFactoryByType(EffectSlotType::None)};
  789. if(!factory) throw std::runtime_error{"Failed to get null effect factory"};
  790. al::intrusive_ptr<EffectState> state{factory->create()};
  791. Effect.State = state;
  792. mSlot.mEffectState = state.release();
  793. }
  794. ALeffectslot::~ALeffectslot()
  795. {
  796. if(Target)
  797. DecrementRef(Target->ref);
  798. Target = nullptr;
  799. if(Buffer)
  800. DecrementRef(Buffer->ref);
  801. Buffer = nullptr;
  802. EffectSlotProps *props{mSlot.Update.exchange(nullptr)};
  803. if(props)
  804. {
  805. TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n",
  806. decltype(std::declval<void*>()){props});
  807. delete props;
  808. }
  809. if(mSlot.mEffectState)
  810. mSlot.mEffectState->release();
  811. }
  812. ALenum ALeffectslot::initEffect(ALenum effectType, const EffectProps &effectProps,
  813. ALCcontext *context)
  814. {
  815. EffectSlotType newtype{EffectSlotTypeFromEnum(effectType)};
  816. if(newtype != Effect.Type)
  817. {
  818. EffectStateFactory *factory{getFactoryByType(newtype)};
  819. if(!factory)
  820. {
  821. ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype));
  822. return AL_INVALID_ENUM;
  823. }
  824. al::intrusive_ptr<EffectState> state{factory->create()};
  825. ALCdevice *device{context->mALDevice.get()};
  826. std::unique_lock<std::mutex> statelock{device->StateLock};
  827. state->mOutTarget = device->Dry.Buffer;
  828. {
  829. FPUCtl mixer_mode{};
  830. state->deviceUpdate(device, GetEffectBuffer(Buffer));
  831. }
  832. Effect.Type = newtype;
  833. Effect.Props = effectProps;
  834. Effect.State = std::move(state);
  835. }
  836. else if(newtype != EffectSlotType::None)
  837. Effect.Props = effectProps;
  838. /* Remove state references from old effect slot property updates. */
  839. EffectSlotProps *props{context->mFreeEffectslotProps.load()};
  840. while(props)
  841. {
  842. props->State = nullptr;
  843. props = props->next.load(std::memory_order_relaxed);
  844. }
  845. return AL_NO_ERROR;
  846. }
  847. void ALeffectslot::updateProps(ALCcontext *context)
  848. {
  849. /* Get an unused property container, or allocate a new one as needed. */
  850. EffectSlotProps *props{context->mFreeEffectslotProps.load(std::memory_order_relaxed)};
  851. if(!props)
  852. props = new EffectSlotProps{};
  853. else
  854. {
  855. EffectSlotProps *next;
  856. do {
  857. next = props->next.load(std::memory_order_relaxed);
  858. } while(context->mFreeEffectslotProps.compare_exchange_weak(props, next,
  859. std::memory_order_seq_cst, std::memory_order_acquire) == 0);
  860. }
  861. /* Copy in current property values. */
  862. props->Gain = Gain;
  863. props->AuxSendAuto = AuxSendAuto;
  864. props->Target = Target ? &Target->mSlot : nullptr;
  865. props->Type = Effect.Type;
  866. props->Props = Effect.Props;
  867. props->State = Effect.State;
  868. /* Set the new container for updating internal parameters. */
  869. props = mSlot.Update.exchange(props, std::memory_order_acq_rel);
  870. if(props)
  871. {
  872. /* If there was an unused update container, put it back in the
  873. * freelist.
  874. */
  875. props->State = nullptr;
  876. AtomicReplaceHead(context->mFreeEffectslotProps, props);
  877. }
  878. }
  879. void UpdateAllEffectSlotProps(ALCcontext *context)
  880. {
  881. std::lock_guard<std::mutex> _{context->mEffectSlotLock};
  882. #ifdef ALSOFT_EAX
  883. if(context->has_eax())
  884. context->eax_commit_fx_slots();
  885. #endif
  886. for(auto &sublist : context->mEffectSlotList)
  887. {
  888. uint64_t usemask{~sublist.FreeMask};
  889. while(usemask)
  890. {
  891. const int idx{al::countr_zero(usemask)};
  892. usemask &= ~(1_u64 << idx);
  893. ALeffectslot *slot{sublist.EffectSlots + idx};
  894. if(slot->mState != SlotState::Stopped && std::exchange(slot->mPropsDirty, false))
  895. slot->updateProps(context);
  896. }
  897. }
  898. }
  899. EffectSlotSubList::~EffectSlotSubList()
  900. {
  901. uint64_t usemask{~FreeMask};
  902. while(usemask)
  903. {
  904. const int idx{al::countr_zero(usemask)};
  905. al::destroy_at(EffectSlots+idx);
  906. usemask &= ~(1_u64 << idx);
  907. }
  908. FreeMask = ~usemask;
  909. al_free(EffectSlots);
  910. EffectSlots = nullptr;
  911. }
  912. #ifdef ALSOFT_EAX
  913. namespace {
  914. class EaxFxSlotException :
  915. public EaxException
  916. {
  917. public:
  918. explicit EaxFxSlotException(
  919. const char* message)
  920. :
  921. EaxException{"EAX_FX_SLOT", message}
  922. {
  923. }
  924. }; // EaxFxSlotException
  925. } // namespace
  926. void ALeffectslot::eax_initialize(
  927. const EaxCall& call,
  928. ALCcontext& al_context,
  929. EaxFxSlotIndexValue index)
  930. {
  931. eax_al_context_ = &al_context;
  932. if (index >= EAX_MAX_FXSLOTS)
  933. {
  934. eax_fail("Index out of range.");
  935. }
  936. eax_fx_slot_index_ = index;
  937. eax_initialize_eax();
  938. eax_initialize_lock();
  939. eax_initialize_effects(call);
  940. }
  941. const EAX50FXSLOTPROPERTIES& ALeffectslot::eax_get_eax_fx_slot() const noexcept
  942. {
  943. return eax_eax_fx_slot_;
  944. }
  945. void ALeffectslot::eax_ensure_is_unlocked() const
  946. {
  947. if (eax_is_locked_)
  948. eax_fail("Locked.");
  949. }
  950. void ALeffectslot::eax_validate_fx_slot_effect(const GUID& eax_effect_id)
  951. {
  952. eax_ensure_is_unlocked();
  953. if (eax_effect_id != EAX_NULL_GUID &&
  954. eax_effect_id != EAX_REVERB_EFFECT &&
  955. eax_effect_id != EAX_AGCCOMPRESSOR_EFFECT &&
  956. eax_effect_id != EAX_AUTOWAH_EFFECT &&
  957. eax_effect_id != EAX_CHORUS_EFFECT &&
  958. eax_effect_id != EAX_DISTORTION_EFFECT &&
  959. eax_effect_id != EAX_ECHO_EFFECT &&
  960. eax_effect_id != EAX_EQUALIZER_EFFECT &&
  961. eax_effect_id != EAX_FLANGER_EFFECT &&
  962. eax_effect_id != EAX_FREQUENCYSHIFTER_EFFECT &&
  963. eax_effect_id != EAX_VOCALMORPHER_EFFECT &&
  964. eax_effect_id != EAX_PITCHSHIFTER_EFFECT &&
  965. eax_effect_id != EAX_RINGMODULATOR_EFFECT)
  966. {
  967. eax_fail("Unsupported EAX effect GUID.");
  968. }
  969. }
  970. void ALeffectslot::eax_validate_fx_slot_volume(long eax_volume)
  971. {
  972. eax_validate_range<EaxFxSlotException>(
  973. "Volume",
  974. eax_volume,
  975. EAXFXSLOT_MINVOLUME,
  976. EAXFXSLOT_MAXVOLUME);
  977. }
  978. void ALeffectslot::eax_validate_fx_slot_lock(long eax_lock)
  979. {
  980. eax_ensure_is_unlocked();
  981. eax_validate_range<EaxFxSlotException>(
  982. "Lock",
  983. eax_lock,
  984. EAXFXSLOT_MINLOCK,
  985. EAXFXSLOT_MAXLOCK);
  986. }
  987. void ALeffectslot::eax_validate_fx_slot_flags(const EaxCall& call, unsigned long eax_flags)
  988. {
  989. eax_validate_range<EaxFxSlotException>(
  990. "Flags",
  991. eax_flags,
  992. 0UL,
  993. ~(call.get_version() == 4 ? EAX40FXSLOTFLAGS_RESERVED : EAX50FXSLOTFLAGS_RESERVED));
  994. }
  995. void ALeffectslot::eax_validate_fx_slot_occlusion(long eax_occlusion)
  996. {
  997. eax_validate_range<EaxFxSlotException>(
  998. "Occlusion",
  999. eax_occlusion,
  1000. EAXFXSLOT_MINOCCLUSION,
  1001. EAXFXSLOT_MAXOCCLUSION);
  1002. }
  1003. void ALeffectslot::eax_validate_fx_slot_occlusion_lf_ratio(float eax_occlusion_lf_ratio)
  1004. {
  1005. eax_validate_range<EaxFxSlotException>(
  1006. "Occlusion LF Ratio",
  1007. eax_occlusion_lf_ratio,
  1008. EAXFXSLOT_MINOCCLUSIONLFRATIO,
  1009. EAXFXSLOT_MAXOCCLUSIONLFRATIO);
  1010. }
  1011. void ALeffectslot::eax_validate_fx_slot_all(const EaxCall& call, const EAX40FXSLOTPROPERTIES& fx_slot)
  1012. {
  1013. eax_validate_fx_slot_effect(fx_slot.guidLoadEffect);
  1014. eax_validate_fx_slot_volume(fx_slot.lVolume);
  1015. eax_validate_fx_slot_lock(fx_slot.lLock);
  1016. eax_validate_fx_slot_flags(call, fx_slot.ulFlags);
  1017. }
  1018. void ALeffectslot::eax_validate_fx_slot_all(const EaxCall& call, const EAX50FXSLOTPROPERTIES& fx_slot)
  1019. {
  1020. eax_validate_fx_slot_all(call, static_cast<const EAX40FXSLOTPROPERTIES&>(fx_slot));
  1021. eax_validate_fx_slot_occlusion(fx_slot.lOcclusion);
  1022. eax_validate_fx_slot_occlusion_lf_ratio(fx_slot.flOcclusionLFRatio);
  1023. }
  1024. void ALeffectslot::eax_set_fx_slot_effect(const EaxCall& call, const GUID& eax_effect_id)
  1025. {
  1026. if (eax_eax_fx_slot_.guidLoadEffect == eax_effect_id)
  1027. {
  1028. return;
  1029. }
  1030. eax_eax_fx_slot_.guidLoadEffect = eax_effect_id;
  1031. eax_set_fx_slot_effect(call);
  1032. }
  1033. void ALeffectslot::eax_set_fx_slot_volume(
  1034. long eax_volume)
  1035. {
  1036. if (eax_eax_fx_slot_.lVolume == eax_volume)
  1037. {
  1038. return;
  1039. }
  1040. eax_eax_fx_slot_.lVolume = eax_volume;
  1041. eax_set_fx_slot_volume();
  1042. }
  1043. void ALeffectslot::eax_set_fx_slot_lock(
  1044. long eax_lock)
  1045. {
  1046. if (eax_eax_fx_slot_.lLock == eax_lock)
  1047. {
  1048. return;
  1049. }
  1050. eax_eax_fx_slot_.lLock = eax_lock;
  1051. }
  1052. void ALeffectslot::eax_set_fx_slot_flags(
  1053. unsigned long eax_flags)
  1054. {
  1055. if (eax_eax_fx_slot_.ulFlags == eax_flags)
  1056. {
  1057. return;
  1058. }
  1059. eax_eax_fx_slot_.ulFlags = eax_flags;
  1060. eax_set_fx_slot_flags();
  1061. }
  1062. // [[nodiscard]]
  1063. bool ALeffectslot::eax_set_fx_slot_occlusion(
  1064. long eax_occlusion)
  1065. {
  1066. if (eax_eax_fx_slot_.lOcclusion == eax_occlusion)
  1067. {
  1068. return false;
  1069. }
  1070. eax_eax_fx_slot_.lOcclusion = eax_occlusion;
  1071. return true;
  1072. }
  1073. // [[nodiscard]]
  1074. bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio(
  1075. float eax_occlusion_lf_ratio)
  1076. {
  1077. if (eax_eax_fx_slot_.flOcclusionLFRatio == eax_occlusion_lf_ratio)
  1078. {
  1079. return false;
  1080. }
  1081. eax_eax_fx_slot_.flOcclusionLFRatio = eax_occlusion_lf_ratio;
  1082. return true;
  1083. }
  1084. void ALeffectslot::eax_set_fx_slot_all(const EaxCall& call, const EAX40FXSLOTPROPERTIES& eax_fx_slot)
  1085. {
  1086. eax_set_fx_slot_effect(call, eax_fx_slot.guidLoadEffect);
  1087. eax_set_fx_slot_volume(eax_fx_slot.lVolume);
  1088. eax_set_fx_slot_lock(eax_fx_slot.lLock);
  1089. eax_set_fx_slot_flags(eax_fx_slot.ulFlags);
  1090. }
  1091. // [[nodiscard]]
  1092. bool ALeffectslot::eax_set_fx_slot_all(const EaxCall& call, const EAX50FXSLOTPROPERTIES& eax_fx_slot)
  1093. {
  1094. eax_set_fx_slot_all(call, static_cast<const EAX40FXSLOTPROPERTIES&>(eax_fx_slot));
  1095. const auto is_occlusion_modified = eax_set_fx_slot_occlusion(eax_fx_slot.lOcclusion);
  1096. const auto is_occlusion_lf_ratio_modified = eax_set_fx_slot_occlusion_lf_ratio(eax_fx_slot.flOcclusionLFRatio);
  1097. return is_occlusion_modified || is_occlusion_lf_ratio_modified;
  1098. }
  1099. void ALeffectslot::eax_unlock_legacy() noexcept
  1100. {
  1101. assert(eax_fx_slot_index_ < 2);
  1102. eax_is_locked_ = false;
  1103. eax_eax_fx_slot_.lLock = EAXFXSLOT_UNLOCKED;
  1104. }
  1105. [[noreturn]]
  1106. void ALeffectslot::eax_fail(
  1107. const char* message)
  1108. {
  1109. throw EaxFxSlotException{message};
  1110. }
  1111. GUID ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
  1112. {
  1113. switch (eax_fx_slot_index_)
  1114. {
  1115. case 0: return EAX_REVERB_EFFECT;
  1116. case 1: return EAX_CHORUS_EFFECT;
  1117. default: return EAX_NULL_GUID;
  1118. }
  1119. }
  1120. long ALeffectslot::eax_get_eax_default_lock() const noexcept
  1121. {
  1122. return eax_fx_slot_index_ < 2 ? EAXFXSLOT_LOCKED : EAXFXSLOT_UNLOCKED;
  1123. }
  1124. void ALeffectslot::eax_set_eax_fx_slot_defaults()
  1125. {
  1126. eax_eax_fx_slot_.guidLoadEffect = eax_get_eax_default_effect_guid();
  1127. eax_eax_fx_slot_.lVolume = EAXFXSLOT_DEFAULTVOLUME;
  1128. eax_eax_fx_slot_.lLock = eax_get_eax_default_lock();
  1129. eax_eax_fx_slot_.ulFlags = EAX40FXSLOT_DEFAULTFLAGS;
  1130. eax_eax_fx_slot_.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
  1131. eax_eax_fx_slot_.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
  1132. }
  1133. void ALeffectslot::eax_initialize_eax()
  1134. {
  1135. eax_set_eax_fx_slot_defaults();
  1136. }
  1137. void ALeffectslot::eax_initialize_lock()
  1138. {
  1139. eax_is_locked_ = (eax_fx_slot_index_ < 2);
  1140. }
  1141. void ALeffectslot::eax_initialize_effects(const EaxCall& call)
  1142. {
  1143. eax_set_fx_slot_effect(call);
  1144. }
  1145. void ALeffectslot::eax_get_fx_slot_all(const EaxCall& call) const
  1146. {
  1147. switch (call.get_version())
  1148. {
  1149. case 4:
  1150. call.set_value<EaxFxSlotException, EAX40FXSLOTPROPERTIES>(eax_eax_fx_slot_);
  1151. break;
  1152. case 5:
  1153. call.set_value<EaxFxSlotException, EAX50FXSLOTPROPERTIES>(eax_eax_fx_slot_);
  1154. break;
  1155. default:
  1156. eax_fail("Unsupported EAX version.");
  1157. }
  1158. }
  1159. void ALeffectslot::eax_get_fx_slot(const EaxCall& call) const
  1160. {
  1161. switch (call.get_property_id())
  1162. {
  1163. case EAXFXSLOT_ALLPARAMETERS:
  1164. eax_get_fx_slot_all(call);
  1165. break;
  1166. case EAXFXSLOT_LOADEFFECT:
  1167. call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.guidLoadEffect);
  1168. break;
  1169. case EAXFXSLOT_VOLUME:
  1170. call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lVolume);
  1171. break;
  1172. case EAXFXSLOT_LOCK:
  1173. call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lLock);
  1174. break;
  1175. case EAXFXSLOT_FLAGS:
  1176. call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.ulFlags);
  1177. break;
  1178. case EAXFXSLOT_OCCLUSION:
  1179. call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lOcclusion);
  1180. break;
  1181. case EAXFXSLOT_OCCLUSIONLFRATIO:
  1182. call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.flOcclusionLFRatio);
  1183. break;
  1184. default:
  1185. eax_fail("Unsupported FX slot property id.");
  1186. }
  1187. }
  1188. // [[nodiscard]]
  1189. bool ALeffectslot::eax_get(const EaxCall& call)
  1190. {
  1191. switch (call.get_property_set_id())
  1192. {
  1193. case EaxCallPropertySetId::fx_slot:
  1194. eax_get_fx_slot(call);
  1195. break;
  1196. case EaxCallPropertySetId::fx_slot_effect:
  1197. eax_dispatch_effect(call);
  1198. break;
  1199. default:
  1200. eax_fail("Unsupported property id.");
  1201. }
  1202. return false;
  1203. }
  1204. void ALeffectslot::eax_set_fx_slot_effect(const EaxCall& call, ALenum al_effect_type)
  1205. {
  1206. if(!IsValidEffectType(al_effect_type))
  1207. eax_fail("Unsupported effect.");
  1208. eax_effect_ = nullptr;
  1209. eax_effect_ = eax_create_eax_effect(al_effect_type, call);
  1210. eax_set_effect_slot_effect(*eax_effect_);
  1211. }
  1212. void ALeffectslot::eax_set_fx_slot_effect(const EaxCall& call)
  1213. {
  1214. auto al_effect_type = ALenum{};
  1215. if (false)
  1216. {
  1217. }
  1218. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_NULL_GUID)
  1219. {
  1220. al_effect_type = AL_EFFECT_NULL;
  1221. }
  1222. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AUTOWAH_EFFECT)
  1223. {
  1224. al_effect_type = AL_EFFECT_AUTOWAH;
  1225. }
  1226. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_CHORUS_EFFECT)
  1227. {
  1228. al_effect_type = AL_EFFECT_CHORUS;
  1229. }
  1230. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AGCCOMPRESSOR_EFFECT)
  1231. {
  1232. al_effect_type = AL_EFFECT_COMPRESSOR;
  1233. }
  1234. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_DISTORTION_EFFECT)
  1235. {
  1236. al_effect_type = AL_EFFECT_DISTORTION;
  1237. }
  1238. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_REVERB_EFFECT)
  1239. {
  1240. al_effect_type = AL_EFFECT_EAXREVERB;
  1241. }
  1242. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_ECHO_EFFECT)
  1243. {
  1244. al_effect_type = AL_EFFECT_ECHO;
  1245. }
  1246. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_EQUALIZER_EFFECT)
  1247. {
  1248. al_effect_type = AL_EFFECT_EQUALIZER;
  1249. }
  1250. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FLANGER_EFFECT)
  1251. {
  1252. al_effect_type = AL_EFFECT_FLANGER;
  1253. }
  1254. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FREQUENCYSHIFTER_EFFECT)
  1255. {
  1256. al_effect_type = AL_EFFECT_FREQUENCY_SHIFTER;
  1257. }
  1258. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_PITCHSHIFTER_EFFECT)
  1259. {
  1260. al_effect_type = AL_EFFECT_PITCH_SHIFTER;
  1261. }
  1262. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_RINGMODULATOR_EFFECT)
  1263. {
  1264. al_effect_type = AL_EFFECT_RING_MODULATOR;
  1265. }
  1266. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_VOCALMORPHER_EFFECT)
  1267. {
  1268. al_effect_type = AL_EFFECT_VOCAL_MORPHER;
  1269. }
  1270. else
  1271. {
  1272. eax_fail("Unsupported effect.");
  1273. }
  1274. eax_set_fx_slot_effect(call, al_effect_type);
  1275. }
  1276. void ALeffectslot::eax_set_efx_effect_slot_gain()
  1277. {
  1278. const auto gain = level_mb_to_gain(
  1279. static_cast<float>(clamp(
  1280. eax_eax_fx_slot_.lVolume,
  1281. EAXFXSLOT_MINVOLUME,
  1282. EAXFXSLOT_MAXVOLUME)));
  1283. eax_set_effect_slot_gain(gain);
  1284. }
  1285. void ALeffectslot::eax_set_fx_slot_volume()
  1286. {
  1287. eax_set_efx_effect_slot_gain();
  1288. }
  1289. void ALeffectslot::eax_set_effect_slot_send_auto()
  1290. {
  1291. eax_set_effect_slot_send_auto((eax_eax_fx_slot_.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0);
  1292. }
  1293. void ALeffectslot::eax_set_fx_slot_flags()
  1294. {
  1295. eax_set_effect_slot_send_auto();
  1296. }
  1297. void ALeffectslot::eax_defer_fx_slot_effect(const EaxCall& call)
  1298. {
  1299. const auto& eax_effect_id =
  1300. call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::guidLoadEffect)>();
  1301. eax_validate_fx_slot_effect(eax_effect_id);
  1302. eax_set_fx_slot_effect(call, eax_effect_id);
  1303. }
  1304. void ALeffectslot::eax_defer_fx_slot_volume(const EaxCall& call)
  1305. {
  1306. const auto& eax_volume =
  1307. call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::lVolume)>();
  1308. eax_validate_fx_slot_volume(eax_volume);
  1309. eax_set_fx_slot_volume(eax_volume);
  1310. }
  1311. void ALeffectslot::eax_defer_fx_slot_lock(const EaxCall& call)
  1312. {
  1313. const auto& eax_lock =
  1314. call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::lLock)>();
  1315. eax_validate_fx_slot_lock(eax_lock);
  1316. eax_set_fx_slot_lock(eax_lock);
  1317. }
  1318. void ALeffectslot::eax_defer_fx_slot_flags(const EaxCall& call)
  1319. {
  1320. const auto& eax_flags =
  1321. call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::ulFlags)>();
  1322. eax_validate_fx_slot_flags(call, eax_flags);
  1323. eax_set_fx_slot_flags(eax_flags);
  1324. }
  1325. // [[nodiscard]]
  1326. bool ALeffectslot::eax_defer_fx_slot_occlusion(const EaxCall& call)
  1327. {
  1328. const auto& eax_occlusion =
  1329. call.get_value<EaxFxSlotException, const decltype(EAX50FXSLOTPROPERTIES::lOcclusion)>();
  1330. eax_validate_fx_slot_occlusion(eax_occlusion);
  1331. return eax_set_fx_slot_occlusion(eax_occlusion);
  1332. }
  1333. // [[nodiscard]]
  1334. bool ALeffectslot::eax_defer_fx_slot_occlusion_lf_ratio(const EaxCall& call)
  1335. {
  1336. const auto& eax_occlusion_lf_ratio =
  1337. call.get_value<EaxFxSlotException, const decltype(EAX50FXSLOTPROPERTIES::flOcclusionLFRatio)>();
  1338. eax_validate_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio);
  1339. return eax_set_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio);
  1340. }
  1341. // [[nodiscard]]
  1342. bool ALeffectslot::eax_defer_fx_slot_all(const EaxCall& call)
  1343. {
  1344. switch (call.get_version())
  1345. {
  1346. case 4:
  1347. {
  1348. const auto& eax_all =
  1349. call.get_value<EaxFxSlotException, const EAX40FXSLOTPROPERTIES>();
  1350. eax_validate_fx_slot_all(call, eax_all);
  1351. eax_set_fx_slot_all(call, eax_all);
  1352. return false;
  1353. }
  1354. case 5:
  1355. {
  1356. const auto& eax_all =
  1357. call.get_value<EaxFxSlotException, const EAX50FXSLOTPROPERTIES>();
  1358. eax_validate_fx_slot_all(call, eax_all);
  1359. return eax_set_fx_slot_all(call, eax_all);
  1360. }
  1361. default:
  1362. eax_fail("Unsupported EAX version.");
  1363. }
  1364. }
  1365. bool ALeffectslot::eax_set_fx_slot(const EaxCall& call)
  1366. {
  1367. switch (call.get_property_id())
  1368. {
  1369. case EAXFXSLOT_NONE:
  1370. return false;
  1371. case EAXFXSLOT_ALLPARAMETERS:
  1372. return eax_defer_fx_slot_all(call);
  1373. case EAXFXSLOT_LOADEFFECT:
  1374. eax_defer_fx_slot_effect(call);
  1375. return false;
  1376. case EAXFXSLOT_VOLUME:
  1377. eax_defer_fx_slot_volume(call);
  1378. return false;
  1379. case EAXFXSLOT_LOCK:
  1380. eax_defer_fx_slot_lock(call);
  1381. return false;
  1382. case EAXFXSLOT_FLAGS:
  1383. eax_defer_fx_slot_flags(call);
  1384. return false;
  1385. case EAXFXSLOT_OCCLUSION:
  1386. return eax_defer_fx_slot_occlusion(call);
  1387. case EAXFXSLOT_OCCLUSIONLFRATIO:
  1388. return eax_defer_fx_slot_occlusion_lf_ratio(call);
  1389. default:
  1390. eax_fail("Unsupported FX slot property id.");
  1391. }
  1392. }
  1393. // [[nodiscard]]
  1394. bool ALeffectslot::eax_set(const EaxCall& call)
  1395. {
  1396. switch(call.get_property_set_id())
  1397. {
  1398. case EaxCallPropertySetId::fx_slot:
  1399. return eax_set_fx_slot(call);
  1400. case EaxCallPropertySetId::fx_slot_effect:
  1401. eax_dispatch_effect(call);
  1402. break;
  1403. default:
  1404. eax_fail("Unsupported property id.");
  1405. }
  1406. return false;
  1407. }
  1408. void ALeffectslot::eax_dispatch_effect(const EaxCall& call)
  1409. { if(eax_effect_) eax_effect_->dispatch(call); }
  1410. void ALeffectslot::eax_apply_deferred()
  1411. {
  1412. /* The other FXSlot properties (volume, effect, etc) aren't deferred? */
  1413. auto is_changed = false;
  1414. if(eax_effect_)
  1415. is_changed = eax_effect_->commit();
  1416. if(is_changed)
  1417. eax_set_effect_slot_effect(*eax_effect_);
  1418. }
  1419. void ALeffectslot::eax_set_effect_slot_effect(EaxEffect &effect)
  1420. {
  1421. #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
  1422. const auto error = initEffect(effect.al_effect_type_, effect.al_effect_props_, eax_al_context_);
  1423. if (error != AL_NO_ERROR)
  1424. {
  1425. ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect.");
  1426. return;
  1427. }
  1428. if (mState == SlotState::Initial)
  1429. {
  1430. mPropsDirty = false;
  1431. updateProps(eax_al_context_);
  1432. auto effect_slot_ptr = this;
  1433. AddActiveEffectSlots({&effect_slot_ptr, 1}, eax_al_context_);
  1434. mState = SlotState::Playing;
  1435. return;
  1436. }
  1437. UpdateProps(this, eax_al_context_);
  1438. #undef EAX_PREFIX
  1439. }
  1440. void ALeffectslot::eax_set_effect_slot_send_auto(
  1441. bool is_send_auto)
  1442. {
  1443. if(AuxSendAuto == is_send_auto)
  1444. return;
  1445. AuxSendAuto = is_send_auto;
  1446. UpdateProps(this, eax_al_context_);
  1447. }
  1448. void ALeffectslot::eax_set_effect_slot_gain(
  1449. ALfloat gain)
  1450. {
  1451. #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
  1452. if(gain == Gain)
  1453. return;
  1454. if(gain < 0.0f || gain > 1.0f)
  1455. ERR(EAX_PREFIX "Gain out of range (%f)\n", gain);
  1456. Gain = clampf(gain, 0.0f, 1.0f);
  1457. UpdateProps(this, eax_al_context_);
  1458. #undef EAX_PREFIX
  1459. }
  1460. void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot)
  1461. {
  1462. assert(effect_slot);
  1463. eax_delete_al_effect_slot(*effect_slot->eax_al_context_, *effect_slot);
  1464. }
  1465. EaxAlEffectSlotUPtr eax_create_al_effect_slot(
  1466. ALCcontext& context)
  1467. {
  1468. #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
  1469. std::unique_lock<std::mutex> effect_slot_lock{context.mEffectSlotLock};
  1470. auto& device = *context.mALDevice;
  1471. if (context.mNumEffectSlots == device.AuxiliaryEffectSlotMax)
  1472. {
  1473. ERR(EAX_PREFIX "%s\n", "Out of memory.");
  1474. return nullptr;
  1475. }
  1476. if (!EnsureEffectSlots(&context, 1))
  1477. {
  1478. ERR(EAX_PREFIX "%s\n", "Failed to ensure.");
  1479. return nullptr;
  1480. }
  1481. auto effect_slot = EaxAlEffectSlotUPtr{AllocEffectSlot(&context)};
  1482. if (!effect_slot)
  1483. {
  1484. ERR(EAX_PREFIX "%s\n", "Failed to allocate.");
  1485. return nullptr;
  1486. }
  1487. return effect_slot;
  1488. #undef EAX_PREFIX
  1489. }
  1490. void eax_delete_al_effect_slot(
  1491. ALCcontext& context,
  1492. ALeffectslot& effect_slot)
  1493. {
  1494. #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
  1495. std::lock_guard<std::mutex> effect_slot_lock{context.mEffectSlotLock};
  1496. if (ReadRef(effect_slot.ref) != 0)
  1497. {
  1498. ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id);
  1499. return;
  1500. }
  1501. auto effect_slot_ptr = &effect_slot;
  1502. RemoveActiveEffectSlots({&effect_slot_ptr, 1}, &context);
  1503. FreeEffectSlot(&context, &effect_slot);
  1504. #undef EAX_PREFIX
  1505. }
  1506. #endif // ALSOFT_EAX