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

1823 lines
52 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. ALCcontext& al_context,
  928. EaxFxSlotIndexValue index)
  929. {
  930. eax_al_context_ = &al_context;
  931. if (index >= EAX_MAX_FXSLOTS)
  932. {
  933. eax_fail("Index out of range.");
  934. }
  935. eax_fx_slot_index_ = index;
  936. eax_initialize_eax();
  937. eax_initialize_lock();
  938. eax_initialize_effects();
  939. }
  940. const EAX50FXSLOTPROPERTIES& ALeffectslot::eax_get_eax_fx_slot() const noexcept
  941. {
  942. return eax_eax_fx_slot_;
  943. }
  944. void ALeffectslot::eax_ensure_is_unlocked() const
  945. {
  946. if (eax_is_locked_)
  947. eax_fail("Locked.");
  948. }
  949. void ALeffectslot::eax_validate_fx_slot_effect(
  950. 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(
  971. long eax_volume)
  972. {
  973. eax_validate_range<EaxFxSlotException>(
  974. "Volume",
  975. eax_volume,
  976. EAXFXSLOT_MINVOLUME,
  977. EAXFXSLOT_MAXVOLUME);
  978. }
  979. void ALeffectslot::eax_validate_fx_slot_lock(
  980. long eax_lock)
  981. {
  982. eax_ensure_is_unlocked();
  983. eax_validate_range<EaxFxSlotException>(
  984. "Lock",
  985. eax_lock,
  986. EAXFXSLOT_MINLOCK,
  987. EAXFXSLOT_MAXLOCK);
  988. }
  989. void ALeffectslot::eax_validate_fx_slot_flags(
  990. unsigned long eax_flags,
  991. int eax_version)
  992. {
  993. eax_validate_range<EaxFxSlotException>(
  994. "Flags",
  995. eax_flags,
  996. 0UL,
  997. ~(eax_version == 4 ? EAX40FXSLOTFLAGS_RESERVED : EAX50FXSLOTFLAGS_RESERVED));
  998. }
  999. void ALeffectslot::eax_validate_fx_slot_occlusion(
  1000. long eax_occlusion)
  1001. {
  1002. eax_validate_range<EaxFxSlotException>(
  1003. "Occlusion",
  1004. eax_occlusion,
  1005. EAXFXSLOT_MINOCCLUSION,
  1006. EAXFXSLOT_MAXOCCLUSION);
  1007. }
  1008. void ALeffectslot::eax_validate_fx_slot_occlusion_lf_ratio(
  1009. float eax_occlusion_lf_ratio)
  1010. {
  1011. eax_validate_range<EaxFxSlotException>(
  1012. "Occlusion LF Ratio",
  1013. eax_occlusion_lf_ratio,
  1014. EAXFXSLOT_MINOCCLUSIONLFRATIO,
  1015. EAXFXSLOT_MAXOCCLUSIONLFRATIO);
  1016. }
  1017. void ALeffectslot::eax_validate_fx_slot_all(
  1018. const EAX40FXSLOTPROPERTIES& fx_slot,
  1019. int eax_version)
  1020. {
  1021. eax_validate_fx_slot_effect(fx_slot.guidLoadEffect);
  1022. eax_validate_fx_slot_volume(fx_slot.lVolume);
  1023. eax_validate_fx_slot_lock(fx_slot.lLock);
  1024. eax_validate_fx_slot_flags(fx_slot.ulFlags, eax_version);
  1025. }
  1026. void ALeffectslot::eax_validate_fx_slot_all(
  1027. const EAX50FXSLOTPROPERTIES& fx_slot,
  1028. int eax_version)
  1029. {
  1030. eax_validate_fx_slot_all(static_cast<const EAX40FXSLOTPROPERTIES&>(fx_slot), eax_version);
  1031. eax_validate_fx_slot_occlusion(fx_slot.lOcclusion);
  1032. eax_validate_fx_slot_occlusion_lf_ratio(fx_slot.flOcclusionLFRatio);
  1033. }
  1034. void ALeffectslot::eax_set_fx_slot_effect(
  1035. const GUID& eax_effect_id)
  1036. {
  1037. if (eax_eax_fx_slot_.guidLoadEffect == eax_effect_id)
  1038. {
  1039. return;
  1040. }
  1041. eax_eax_fx_slot_.guidLoadEffect = eax_effect_id;
  1042. eax_set_fx_slot_effect();
  1043. }
  1044. void ALeffectslot::eax_set_fx_slot_volume(
  1045. long eax_volume)
  1046. {
  1047. if (eax_eax_fx_slot_.lVolume == eax_volume)
  1048. {
  1049. return;
  1050. }
  1051. eax_eax_fx_slot_.lVolume = eax_volume;
  1052. eax_set_fx_slot_volume();
  1053. }
  1054. void ALeffectslot::eax_set_fx_slot_lock(
  1055. long eax_lock)
  1056. {
  1057. if (eax_eax_fx_slot_.lLock == eax_lock)
  1058. {
  1059. return;
  1060. }
  1061. eax_eax_fx_slot_.lLock = eax_lock;
  1062. }
  1063. void ALeffectslot::eax_set_fx_slot_flags(
  1064. unsigned long eax_flags)
  1065. {
  1066. if (eax_eax_fx_slot_.ulFlags == eax_flags)
  1067. {
  1068. return;
  1069. }
  1070. eax_eax_fx_slot_.ulFlags = eax_flags;
  1071. eax_set_fx_slot_flags();
  1072. }
  1073. // [[nodiscard]]
  1074. bool ALeffectslot::eax_set_fx_slot_occlusion(
  1075. long eax_occlusion)
  1076. {
  1077. if (eax_eax_fx_slot_.lOcclusion == eax_occlusion)
  1078. {
  1079. return false;
  1080. }
  1081. eax_eax_fx_slot_.lOcclusion = eax_occlusion;
  1082. return true;
  1083. }
  1084. // [[nodiscard]]
  1085. bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio(
  1086. float eax_occlusion_lf_ratio)
  1087. {
  1088. if (eax_eax_fx_slot_.flOcclusionLFRatio == eax_occlusion_lf_ratio)
  1089. {
  1090. return false;
  1091. }
  1092. eax_eax_fx_slot_.flOcclusionLFRatio = eax_occlusion_lf_ratio;
  1093. return true;
  1094. }
  1095. void ALeffectslot::eax_set_fx_slot_all(
  1096. const EAX40FXSLOTPROPERTIES& eax_fx_slot)
  1097. {
  1098. eax_set_fx_slot_effect(eax_fx_slot.guidLoadEffect);
  1099. eax_set_fx_slot_volume(eax_fx_slot.lVolume);
  1100. eax_set_fx_slot_lock(eax_fx_slot.lLock);
  1101. eax_set_fx_slot_flags(eax_fx_slot.ulFlags);
  1102. }
  1103. // [[nodiscard]]
  1104. bool ALeffectslot::eax_set_fx_slot_all(
  1105. const EAX50FXSLOTPROPERTIES& eax_fx_slot)
  1106. {
  1107. eax_set_fx_slot_all(static_cast<const EAX40FXSLOTPROPERTIES&>(eax_fx_slot));
  1108. const auto is_occlusion_modified = eax_set_fx_slot_occlusion(eax_fx_slot.lOcclusion);
  1109. const auto is_occlusion_lf_ratio_modified = eax_set_fx_slot_occlusion_lf_ratio(eax_fx_slot.flOcclusionLFRatio);
  1110. return is_occlusion_modified || is_occlusion_lf_ratio_modified;
  1111. }
  1112. void ALeffectslot::eax_unlock_legacy() noexcept
  1113. {
  1114. assert(eax_fx_slot_index_ < 2);
  1115. eax_is_locked_ = false;
  1116. eax_eax_fx_slot_.lLock = EAXFXSLOT_UNLOCKED;
  1117. }
  1118. [[noreturn]]
  1119. void ALeffectslot::eax_fail(
  1120. const char* message)
  1121. {
  1122. throw EaxFxSlotException{message};
  1123. }
  1124. GUID ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
  1125. {
  1126. switch (eax_fx_slot_index_)
  1127. {
  1128. case 0: return EAX_REVERB_EFFECT;
  1129. case 1: return EAX_CHORUS_EFFECT;
  1130. default: return EAX_NULL_GUID;
  1131. }
  1132. }
  1133. long ALeffectslot::eax_get_eax_default_lock() const noexcept
  1134. {
  1135. return eax_fx_slot_index_ < 2 ? EAXFXSLOT_LOCKED : EAXFXSLOT_UNLOCKED;
  1136. }
  1137. void ALeffectslot::eax_set_eax_fx_slot_defaults()
  1138. {
  1139. eax_eax_fx_slot_.guidLoadEffect = eax_get_eax_default_effect_guid();
  1140. eax_eax_fx_slot_.lVolume = EAXFXSLOT_DEFAULTVOLUME;
  1141. eax_eax_fx_slot_.lLock = eax_get_eax_default_lock();
  1142. eax_eax_fx_slot_.ulFlags = EAX40FXSLOT_DEFAULTFLAGS;
  1143. eax_eax_fx_slot_.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
  1144. eax_eax_fx_slot_.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
  1145. }
  1146. void ALeffectslot::eax_initialize_eax()
  1147. {
  1148. eax_set_eax_fx_slot_defaults();
  1149. }
  1150. void ALeffectslot::eax_initialize_lock()
  1151. {
  1152. eax_is_locked_ = (eax_fx_slot_index_ < 2);
  1153. }
  1154. void ALeffectslot::eax_initialize_effects()
  1155. {
  1156. eax_set_fx_slot_effect();
  1157. }
  1158. void ALeffectslot::eax_get_fx_slot_all(
  1159. const EaxEaxCall& eax_call) const
  1160. {
  1161. switch (eax_call.get_version())
  1162. {
  1163. case 4:
  1164. eax_call.set_value<EaxFxSlotException, EAX40FXSLOTPROPERTIES>(eax_eax_fx_slot_);
  1165. break;
  1166. case 5:
  1167. eax_call.set_value<EaxFxSlotException, EAX50FXSLOTPROPERTIES>(eax_eax_fx_slot_);
  1168. break;
  1169. default:
  1170. eax_fail("Unsupported EAX version.");
  1171. }
  1172. }
  1173. void ALeffectslot::eax_get_fx_slot(
  1174. const EaxEaxCall& eax_call) const
  1175. {
  1176. switch (eax_call.get_property_id())
  1177. {
  1178. case EAXFXSLOT_ALLPARAMETERS:
  1179. eax_get_fx_slot_all(eax_call);
  1180. break;
  1181. case EAXFXSLOT_LOADEFFECT:
  1182. eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.guidLoadEffect);
  1183. break;
  1184. case EAXFXSLOT_VOLUME:
  1185. eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lVolume);
  1186. break;
  1187. case EAXFXSLOT_LOCK:
  1188. eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lLock);
  1189. break;
  1190. case EAXFXSLOT_FLAGS:
  1191. eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.ulFlags);
  1192. break;
  1193. case EAXFXSLOT_OCCLUSION:
  1194. eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lOcclusion);
  1195. break;
  1196. case EAXFXSLOT_OCCLUSIONLFRATIO:
  1197. eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.flOcclusionLFRatio);
  1198. break;
  1199. default:
  1200. eax_fail("Unsupported FX slot property id.");
  1201. }
  1202. }
  1203. // [[nodiscard]]
  1204. bool ALeffectslot::eax_get(
  1205. const EaxEaxCall& eax_call)
  1206. {
  1207. switch (eax_call.get_property_set_id())
  1208. {
  1209. case EaxEaxCallPropertySetId::fx_slot:
  1210. eax_get_fx_slot(eax_call);
  1211. break;
  1212. case EaxEaxCallPropertySetId::fx_slot_effect:
  1213. eax_dispatch_effect(eax_call);
  1214. break;
  1215. default:
  1216. eax_fail("Unsupported property id.");
  1217. }
  1218. return false;
  1219. }
  1220. void ALeffectslot::eax_set_fx_slot_effect(
  1221. ALenum al_effect_type)
  1222. {
  1223. if(!IsValidEffectType(al_effect_type))
  1224. eax_fail("Unsupported effect.");
  1225. eax_effect_ = nullptr;
  1226. eax_effect_ = eax_create_eax_effect(al_effect_type);
  1227. eax_set_effect_slot_effect(*eax_effect_);
  1228. }
  1229. void ALeffectslot::eax_set_fx_slot_effect()
  1230. {
  1231. auto al_effect_type = ALenum{};
  1232. if (false)
  1233. {
  1234. }
  1235. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_NULL_GUID)
  1236. {
  1237. al_effect_type = AL_EFFECT_NULL;
  1238. }
  1239. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AUTOWAH_EFFECT)
  1240. {
  1241. al_effect_type = AL_EFFECT_AUTOWAH;
  1242. }
  1243. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_CHORUS_EFFECT)
  1244. {
  1245. al_effect_type = AL_EFFECT_CHORUS;
  1246. }
  1247. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AGCCOMPRESSOR_EFFECT)
  1248. {
  1249. al_effect_type = AL_EFFECT_COMPRESSOR;
  1250. }
  1251. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_DISTORTION_EFFECT)
  1252. {
  1253. al_effect_type = AL_EFFECT_DISTORTION;
  1254. }
  1255. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_REVERB_EFFECT)
  1256. {
  1257. al_effect_type = AL_EFFECT_EAXREVERB;
  1258. }
  1259. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_ECHO_EFFECT)
  1260. {
  1261. al_effect_type = AL_EFFECT_ECHO;
  1262. }
  1263. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_EQUALIZER_EFFECT)
  1264. {
  1265. al_effect_type = AL_EFFECT_EQUALIZER;
  1266. }
  1267. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FLANGER_EFFECT)
  1268. {
  1269. al_effect_type = AL_EFFECT_FLANGER;
  1270. }
  1271. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FREQUENCYSHIFTER_EFFECT)
  1272. {
  1273. al_effect_type = AL_EFFECT_FREQUENCY_SHIFTER;
  1274. }
  1275. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_PITCHSHIFTER_EFFECT)
  1276. {
  1277. al_effect_type = AL_EFFECT_PITCH_SHIFTER;
  1278. }
  1279. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_RINGMODULATOR_EFFECT)
  1280. {
  1281. al_effect_type = AL_EFFECT_RING_MODULATOR;
  1282. }
  1283. else if (eax_eax_fx_slot_.guidLoadEffect == EAX_VOCALMORPHER_EFFECT)
  1284. {
  1285. al_effect_type = AL_EFFECT_VOCAL_MORPHER;
  1286. }
  1287. else
  1288. {
  1289. eax_fail("Unsupported effect.");
  1290. }
  1291. eax_set_fx_slot_effect(al_effect_type);
  1292. }
  1293. void ALeffectslot::eax_set_efx_effect_slot_gain()
  1294. {
  1295. const auto gain = level_mb_to_gain(
  1296. static_cast<float>(clamp(
  1297. eax_eax_fx_slot_.lVolume,
  1298. EAXFXSLOT_MINVOLUME,
  1299. EAXFXSLOT_MAXVOLUME)));
  1300. eax_set_effect_slot_gain(gain);
  1301. }
  1302. void ALeffectslot::eax_set_fx_slot_volume()
  1303. {
  1304. eax_set_efx_effect_slot_gain();
  1305. }
  1306. void ALeffectslot::eax_set_effect_slot_send_auto()
  1307. {
  1308. eax_set_effect_slot_send_auto((eax_eax_fx_slot_.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0);
  1309. }
  1310. void ALeffectslot::eax_set_fx_slot_flags()
  1311. {
  1312. eax_set_effect_slot_send_auto();
  1313. }
  1314. void ALeffectslot::eax_set_fx_slot_effect(
  1315. const EaxEaxCall& eax_call)
  1316. {
  1317. const auto& eax_effect_id =
  1318. eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::guidLoadEffect)>();
  1319. eax_validate_fx_slot_effect(eax_effect_id);
  1320. eax_set_fx_slot_effect(eax_effect_id);
  1321. }
  1322. void ALeffectslot::eax_set_fx_slot_volume(
  1323. const EaxEaxCall& eax_call)
  1324. {
  1325. const auto& eax_volume =
  1326. eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::lVolume)>();
  1327. eax_validate_fx_slot_volume(eax_volume);
  1328. eax_set_fx_slot_volume(eax_volume);
  1329. }
  1330. void ALeffectslot::eax_set_fx_slot_lock(
  1331. const EaxEaxCall& eax_call)
  1332. {
  1333. const auto& eax_lock =
  1334. eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::lLock)>();
  1335. eax_validate_fx_slot_lock(eax_lock);
  1336. eax_set_fx_slot_lock(eax_lock);
  1337. }
  1338. void ALeffectslot::eax_set_fx_slot_flags(
  1339. const EaxEaxCall& eax_call)
  1340. {
  1341. const auto& eax_flags =
  1342. eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::ulFlags)>();
  1343. eax_validate_fx_slot_flags(eax_flags, eax_call.get_version());
  1344. eax_set_fx_slot_flags(eax_flags);
  1345. }
  1346. // [[nodiscard]]
  1347. bool ALeffectslot::eax_set_fx_slot_occlusion(
  1348. const EaxEaxCall& eax_call)
  1349. {
  1350. const auto& eax_occlusion =
  1351. eax_call.get_value<EaxFxSlotException, const decltype(EAX50FXSLOTPROPERTIES::lOcclusion)>();
  1352. eax_validate_fx_slot_occlusion(eax_occlusion);
  1353. return eax_set_fx_slot_occlusion(eax_occlusion);
  1354. }
  1355. // [[nodiscard]]
  1356. bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio(
  1357. const EaxEaxCall& eax_call)
  1358. {
  1359. const auto& eax_occlusion_lf_ratio =
  1360. eax_call.get_value<EaxFxSlotException, const decltype(EAX50FXSLOTPROPERTIES::flOcclusionLFRatio)>();
  1361. eax_validate_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio);
  1362. return eax_set_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio);
  1363. }
  1364. // [[nodiscard]]
  1365. bool ALeffectslot::eax_set_fx_slot_all(
  1366. const EaxEaxCall& eax_call)
  1367. {
  1368. switch (eax_call.get_version())
  1369. {
  1370. case 4:
  1371. {
  1372. const auto& eax_all =
  1373. eax_call.get_value<EaxFxSlotException, const EAX40FXSLOTPROPERTIES>();
  1374. eax_validate_fx_slot_all(eax_all, eax_call.get_version());
  1375. eax_set_fx_slot_all(eax_all);
  1376. return false;
  1377. }
  1378. case 5:
  1379. {
  1380. const auto& eax_all =
  1381. eax_call.get_value<EaxFxSlotException, const EAX50FXSLOTPROPERTIES>();
  1382. eax_validate_fx_slot_all(eax_all, eax_call.get_version());
  1383. return eax_set_fx_slot_all(eax_all);
  1384. }
  1385. default:
  1386. eax_fail("Unsupported EAX version.");
  1387. }
  1388. }
  1389. bool ALeffectslot::eax_set_fx_slot(
  1390. const EaxEaxCall& eax_call)
  1391. {
  1392. switch (eax_call.get_property_id())
  1393. {
  1394. case EAXFXSLOT_NONE:
  1395. return false;
  1396. case EAXFXSLOT_ALLPARAMETERS:
  1397. return eax_set_fx_slot_all(eax_call);
  1398. case EAXFXSLOT_LOADEFFECT:
  1399. eax_set_fx_slot_effect(eax_call);
  1400. return false;
  1401. case EAXFXSLOT_VOLUME:
  1402. eax_set_fx_slot_volume(eax_call);
  1403. return false;
  1404. case EAXFXSLOT_LOCK:
  1405. eax_set_fx_slot_lock(eax_call);
  1406. return false;
  1407. case EAXFXSLOT_FLAGS:
  1408. eax_set_fx_slot_flags(eax_call);
  1409. return false;
  1410. case EAXFXSLOT_OCCLUSION:
  1411. return eax_set_fx_slot_occlusion(eax_call);
  1412. case EAXFXSLOT_OCCLUSIONLFRATIO:
  1413. return eax_set_fx_slot_occlusion_lf_ratio(eax_call);
  1414. default:
  1415. eax_fail("Unsupported FX slot property id.");
  1416. }
  1417. }
  1418. // [[nodiscard]]
  1419. bool ALeffectslot::eax_set(const EaxEaxCall& eax_call)
  1420. {
  1421. switch(eax_call.get_property_set_id())
  1422. {
  1423. case EaxEaxCallPropertySetId::fx_slot:
  1424. return eax_set_fx_slot(eax_call);
  1425. case EaxEaxCallPropertySetId::fx_slot_effect:
  1426. eax_dispatch_effect(eax_call);
  1427. break;
  1428. default:
  1429. eax_fail("Unsupported property id.");
  1430. }
  1431. return false;
  1432. }
  1433. void ALeffectslot::eax_dispatch_effect(const EaxEaxCall& eax_call)
  1434. { if(eax_effect_) eax_effect_->dispatch(eax_call); }
  1435. void ALeffectslot::eax_apply_deferred()
  1436. {
  1437. /* The other FXSlot properties (volume, effect, etc) aren't deferred? */
  1438. auto is_changed = false;
  1439. if(eax_effect_)
  1440. is_changed = eax_effect_->apply_deferred();
  1441. if(is_changed)
  1442. eax_set_effect_slot_effect(*eax_effect_);
  1443. }
  1444. void ALeffectslot::eax_set_effect_slot_effect(EaxEffect &effect)
  1445. {
  1446. #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
  1447. const auto error = initEffect(effect.al_effect_type_, effect.al_effect_props_, eax_al_context_);
  1448. if (error != AL_NO_ERROR)
  1449. {
  1450. ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect.");
  1451. return;
  1452. }
  1453. if (mState == SlotState::Initial)
  1454. {
  1455. mPropsDirty = false;
  1456. updateProps(eax_al_context_);
  1457. auto effect_slot_ptr = this;
  1458. AddActiveEffectSlots({&effect_slot_ptr, 1}, eax_al_context_);
  1459. mState = SlotState::Playing;
  1460. return;
  1461. }
  1462. UpdateProps(this, eax_al_context_);
  1463. #undef EAX_PREFIX
  1464. }
  1465. void ALeffectslot::eax_set_effect_slot_send_auto(
  1466. bool is_send_auto)
  1467. {
  1468. if(AuxSendAuto == is_send_auto)
  1469. return;
  1470. AuxSendAuto = is_send_auto;
  1471. UpdateProps(this, eax_al_context_);
  1472. }
  1473. void ALeffectslot::eax_set_effect_slot_gain(
  1474. ALfloat gain)
  1475. {
  1476. #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
  1477. if(gain == Gain)
  1478. return;
  1479. if(gain < 0.0f || gain > 1.0f)
  1480. ERR(EAX_PREFIX "Gain out of range (%f)\n", gain);
  1481. Gain = clampf(gain, 0.0f, 1.0f);
  1482. UpdateProps(this, eax_al_context_);
  1483. #undef EAX_PREFIX
  1484. }
  1485. void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot)
  1486. {
  1487. assert(effect_slot);
  1488. eax_delete_al_effect_slot(*effect_slot->eax_al_context_, *effect_slot);
  1489. }
  1490. EaxAlEffectSlotUPtr eax_create_al_effect_slot(
  1491. ALCcontext& context)
  1492. {
  1493. #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
  1494. std::unique_lock<std::mutex> effect_slot_lock{context.mEffectSlotLock};
  1495. auto& device = *context.mALDevice;
  1496. if (context.mNumEffectSlots == device.AuxiliaryEffectSlotMax)
  1497. {
  1498. ERR(EAX_PREFIX "%s\n", "Out of memory.");
  1499. return nullptr;
  1500. }
  1501. if (!EnsureEffectSlots(&context, 1))
  1502. {
  1503. ERR(EAX_PREFIX "%s\n", "Failed to ensure.");
  1504. return nullptr;
  1505. }
  1506. auto effect_slot = EaxAlEffectSlotUPtr{AllocEffectSlot(&context)};
  1507. if (!effect_slot)
  1508. {
  1509. ERR(EAX_PREFIX "%s\n", "Failed to allocate.");
  1510. return nullptr;
  1511. }
  1512. return effect_slot;
  1513. #undef EAX_PREFIX
  1514. }
  1515. void eax_delete_al_effect_slot(
  1516. ALCcontext& context,
  1517. ALeffectslot& effect_slot)
  1518. {
  1519. #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
  1520. std::lock_guard<std::mutex> effect_slot_lock{context.mEffectSlotLock};
  1521. if (ReadRef(effect_slot.ref) != 0)
  1522. {
  1523. ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id);
  1524. return;
  1525. }
  1526. auto effect_slot_ptr = &effect_slot;
  1527. RemoveActiveEffectSlots({&effect_slot_ptr, 1}, &context);
  1528. FreeEffectSlot(&context, &effect_slot);
  1529. #undef EAX_PREFIX
  1530. }
  1531. #endif // ALSOFT_EAX