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

803 lines
25 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 <cstdlib>
  22. #include <cmath>
  23. #include <thread>
  24. #include <algorithm>
  25. #include "AL/al.h"
  26. #include "AL/alc.h"
  27. #include "alMain.h"
  28. #include "alcontext.h"
  29. #include "alAuxEffectSlot.h"
  30. #include "alError.h"
  31. #include "alListener.h"
  32. #include "alSource.h"
  33. #include "fpu_modes.h"
  34. #include "alexcpt.h"
  35. #include "almalloc.h"
  36. namespace {
  37. inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
  38. {
  39. ALuint lidx = (id-1) >> 6;
  40. ALsizei slidx = (id-1) & 0x3f;
  41. if(UNLIKELY(lidx >= context->EffectSlotList.size()))
  42. return nullptr;
  43. EffectSlotSubList &sublist{context->EffectSlotList[lidx]};
  44. if(UNLIKELY(sublist.FreeMask & (1_u64 << slidx)))
  45. return nullptr;
  46. return sublist.EffectSlots + slidx;
  47. }
  48. inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept
  49. {
  50. ALuint lidx = (id-1) >> 6;
  51. ALsizei slidx = (id-1) & 0x3f;
  52. if(UNLIKELY(lidx >= device->EffectList.size()))
  53. return nullptr;
  54. EffectSubList &sublist = device->EffectList[lidx];
  55. if(UNLIKELY(sublist.FreeMask & (1_u64 << slidx)))
  56. return nullptr;
  57. return sublist.Effects + slidx;
  58. }
  59. void AddActiveEffectSlots(const ALuint *slotids, ALsizei count, ALCcontext *context)
  60. {
  61. if(count < 1) return;
  62. ALeffectslotArray *curarray{context->ActiveAuxSlots.load(std::memory_order_acquire)};
  63. size_t newcount{curarray->size() + count};
  64. /* Insert the new effect slots into the head of the array, followed by the
  65. * existing ones.
  66. */
  67. ALeffectslotArray *newarray = ALeffectslot::CreatePtrArray(newcount);
  68. auto slotiter = std::transform(slotids, slotids+count, newarray->begin(),
  69. [context](ALuint id) noexcept -> ALeffectslot*
  70. { return LookupEffectSlot(context, id); }
  71. );
  72. std::copy(curarray->begin(), curarray->end(), slotiter);
  73. /* Remove any duplicates (first instance of each will be kept). */
  74. auto last = newarray->end();
  75. for(auto start=newarray->begin()+1;;)
  76. {
  77. last = std::remove(start, last, *(start-1));
  78. if(start == last) break;
  79. ++start;
  80. }
  81. newcount = static_cast<size_t>(std::distance(newarray->begin(), last));
  82. /* Reallocate newarray if the new size ended up smaller from duplicate
  83. * removal.
  84. */
  85. if(UNLIKELY(newcount < newarray->size()))
  86. {
  87. curarray = newarray;
  88. newarray = ALeffectslot::CreatePtrArray(newcount);
  89. std::copy_n(curarray->begin(), newcount, newarray->begin());
  90. delete curarray;
  91. curarray = nullptr;
  92. }
  93. curarray = context->ActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
  94. ALCdevice *device{context->Device};
  95. while((device->MixCount.load(std::memory_order_acquire)&1))
  96. std::this_thread::yield();
  97. delete curarray;
  98. }
  99. void RemoveActiveEffectSlots(const ALuint *slotids, ALsizei count, ALCcontext *context)
  100. {
  101. if(count < 1) return;
  102. ALeffectslotArray *curarray{context->ActiveAuxSlots.load(std::memory_order_acquire)};
  103. /* Don't shrink the allocated array size since we don't know how many (if
  104. * any) of the effect slots to remove are in the array.
  105. */
  106. ALeffectslotArray *newarray = ALeffectslot::CreatePtrArray(curarray->size());
  107. /* Copy each element in curarray to newarray whose ID is not in slotids. */
  108. const ALuint *slotids_end{slotids + count};
  109. auto slotiter = std::copy_if(curarray->begin(), curarray->end(), newarray->begin(),
  110. [slotids, slotids_end](const ALeffectslot *slot) -> bool
  111. { return std::find(slotids, slotids_end, slot->id) == slotids_end; }
  112. );
  113. /* Reallocate with the new size. */
  114. auto newsize = static_cast<size_t>(std::distance(newarray->begin(), slotiter));
  115. if(LIKELY(newsize != newarray->size()))
  116. {
  117. curarray = newarray;
  118. newarray = ALeffectslot::CreatePtrArray(newsize);
  119. std::copy_n(curarray->begin(), newsize, newarray->begin());
  120. delete curarray;
  121. curarray = nullptr;
  122. }
  123. curarray = context->ActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
  124. ALCdevice *device{context->Device};
  125. while((device->MixCount.load(std::memory_order_acquire)&1))
  126. std::this_thread::yield();
  127. delete curarray;
  128. }
  129. ALeffectslot *AllocEffectSlot(ALCcontext *context)
  130. {
  131. ALCdevice *device{context->Device};
  132. std::lock_guard<std::mutex> _{context->EffectSlotLock};
  133. if(context->NumEffectSlots >= device->AuxiliaryEffectSlotMax)
  134. {
  135. alSetError(context, AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit",
  136. device->AuxiliaryEffectSlotMax);
  137. return nullptr;
  138. }
  139. auto sublist = std::find_if(context->EffectSlotList.begin(), context->EffectSlotList.end(),
  140. [](const EffectSlotSubList &entry) noexcept -> bool
  141. { return entry.FreeMask != 0; }
  142. );
  143. auto lidx = static_cast<ALsizei>(std::distance(context->EffectSlotList.begin(), sublist));
  144. ALeffectslot *slot;
  145. ALsizei slidx;
  146. if(LIKELY(sublist != context->EffectSlotList.end()))
  147. {
  148. slidx = CTZ64(sublist->FreeMask);
  149. slot = sublist->EffectSlots + slidx;
  150. }
  151. else
  152. {
  153. /* Don't allocate so many list entries that the 32-bit ID could
  154. * overflow...
  155. */
  156. if(UNLIKELY(context->EffectSlotList.size() >= 1<<25))
  157. {
  158. alSetError(context, AL_OUT_OF_MEMORY, "Too many effect slots allocated");
  159. return nullptr;
  160. }
  161. context->EffectSlotList.emplace_back();
  162. sublist = context->EffectSlotList.end() - 1;
  163. sublist->FreeMask = ~0_u64;
  164. sublist->EffectSlots = static_cast<ALeffectslot*>(al_calloc(16, sizeof(ALeffectslot)*64));
  165. if(UNLIKELY(!sublist->EffectSlots))
  166. {
  167. context->EffectSlotList.pop_back();
  168. alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate effect slot batch");
  169. return nullptr;
  170. }
  171. slidx = 0;
  172. slot = sublist->EffectSlots + slidx;
  173. }
  174. slot = new (slot) ALeffectslot{};
  175. ALenum err{InitEffectSlot(slot)};
  176. if(err != AL_NO_ERROR)
  177. {
  178. slot->~ALeffectslot();
  179. alSetError(context, err, "Effect slot object initialization failed");
  180. return nullptr;
  181. }
  182. aluInitEffectPanning(slot, device);
  183. /* Add 1 to avoid source ID 0. */
  184. slot->id = ((lidx<<6) | slidx) + 1;
  185. context->NumEffectSlots += 1;
  186. sublist->FreeMask &= ~(1_u64 << slidx);
  187. return slot;
  188. }
  189. void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
  190. {
  191. ALuint id = slot->id - 1;
  192. ALsizei lidx = id >> 6;
  193. ALsizei slidx = id & 0x3f;
  194. slot->~ALeffectslot();
  195. context->EffectSlotList[lidx].FreeMask |= 1_u64 << slidx;
  196. context->NumEffectSlots--;
  197. }
  198. #define DO_UPDATEPROPS() do { \
  199. if(!context->DeferUpdates.load(std::memory_order_acquire)) \
  200. UpdateEffectSlotProps(slot, context.get()); \
  201. else \
  202. slot->PropsClean.clear(std::memory_order_release); \
  203. } while(0)
  204. } // namespace
  205. ALeffectslotArray *ALeffectslot::CreatePtrArray(size_t count) noexcept
  206. {
  207. /* Allocate space for twice as many pointers, so the mixer has scratch
  208. * space to store a sorted list during mixing.
  209. */
  210. void *ptr{al_calloc(DEF_ALIGN, ALeffectslotArray::Sizeof(count*2))};
  211. return new (ptr) ALeffectslotArray{count};
  212. }
  213. AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
  214. START_API_FUNC
  215. {
  216. ContextRef context{GetContextRef()};
  217. if(UNLIKELY(!context)) return;
  218. if(n < 0)
  219. SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Generating %d effect slots", n);
  220. if(n == 0) return;
  221. if(n == 1)
  222. {
  223. ALeffectslot *slot{AllocEffectSlot(context.get())};
  224. if(!slot) return;
  225. effectslots[0] = slot->id;
  226. }
  227. else
  228. {
  229. auto tempids = al::vector<ALuint>(n);
  230. auto alloc_end = std::find_if_not(tempids.begin(), tempids.end(),
  231. [&context](ALuint &id) -> bool
  232. {
  233. ALeffectslot *slot{AllocEffectSlot(context.get())};
  234. if(!slot) return false;
  235. id = slot->id;
  236. return true;
  237. }
  238. );
  239. if(alloc_end != tempids.end())
  240. {
  241. auto count = static_cast<ALsizei>(std::distance(tempids.begin(), alloc_end));
  242. alDeleteAuxiliaryEffectSlots(count, tempids.data());
  243. return;
  244. }
  245. std::copy(tempids.cbegin(), tempids.cend(), effectslots);
  246. }
  247. std::unique_lock<std::mutex> slotlock{context->EffectSlotLock};
  248. AddActiveEffectSlots(effectslots, n, context.get());
  249. }
  250. END_API_FUNC
  251. AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots)
  252. START_API_FUNC
  253. {
  254. ContextRef context{GetContextRef()};
  255. if(UNLIKELY(!context)) return;
  256. if(n < 0)
  257. SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Deleting %d effect slots", n);
  258. if(n == 0) return;
  259. std::lock_guard<std::mutex> _{context->EffectSlotLock};
  260. auto effectslots_end = effectslots + n;
  261. auto bad_slot = std::find_if(effectslots, effectslots_end,
  262. [&context](ALuint id) -> bool
  263. {
  264. ALeffectslot *slot{LookupEffectSlot(context.get(), id)};
  265. if(!slot)
  266. {
  267. alSetError(context.get(), AL_INVALID_NAME, "Invalid effect slot ID %u", id);
  268. return true;
  269. }
  270. if(ReadRef(&slot->ref) != 0)
  271. {
  272. alSetError(context.get(), AL_INVALID_NAME, "Deleting in-use effect slot %u", id);
  273. return true;
  274. }
  275. return false;
  276. }
  277. );
  278. if(bad_slot != effectslots_end)
  279. return;
  280. // All effectslots are valid, remove and delete them
  281. RemoveActiveEffectSlots(effectslots, n, context.get());
  282. std::for_each(effectslots, effectslots_end,
  283. [&context](ALuint sid) -> void
  284. {
  285. ALeffectslot *slot{LookupEffectSlot(context.get(), sid)};
  286. if(slot) FreeEffectSlot(context.get(), slot);
  287. }
  288. );
  289. }
  290. END_API_FUNC
  291. AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
  292. START_API_FUNC
  293. {
  294. ContextRef context{GetContextRef()};
  295. if(LIKELY(context))
  296. {
  297. std::lock_guard<std::mutex> _{context->EffectSlotLock};
  298. if(LookupEffectSlot(context.get(), effectslot) != nullptr)
  299. return AL_TRUE;
  300. }
  301. return AL_FALSE;
  302. }
  303. END_API_FUNC
  304. AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value)
  305. START_API_FUNC
  306. {
  307. ContextRef context{GetContextRef()};
  308. if(UNLIKELY(!context)) return;
  309. std::lock_guard<std::mutex> _{context->PropLock};
  310. std::lock_guard<std::mutex> __{context->EffectSlotLock};
  311. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  312. if(UNLIKELY(!slot))
  313. SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  314. ALeffectslot *target{};
  315. ALCdevice *device{};
  316. ALenum err{};
  317. switch(param)
  318. {
  319. case AL_EFFECTSLOT_EFFECT:
  320. device = context->Device;
  321. { std::lock_guard<std::mutex> ___{device->EffectLock};
  322. ALeffect *effect{value ? LookupEffect(device, value) : nullptr};
  323. if(!(value == 0 || effect != nullptr))
  324. SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Invalid effect ID %u", value);
  325. err = InitializeEffect(context.get(), slot, effect);
  326. }
  327. if(err != AL_NO_ERROR)
  328. {
  329. alSetError(context.get(), err, "Effect initialization failed");
  330. return;
  331. }
  332. break;
  333. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  334. if(!(value == AL_TRUE || value == AL_FALSE))
  335. SETERR_RETURN(context.get(), AL_INVALID_VALUE,,
  336. "Effect slot auxiliary send auto out of range");
  337. slot->AuxSendAuto = value;
  338. break;
  339. case AL_EFFECTSLOT_TARGET_SOFT:
  340. target = (value ? LookupEffectSlot(context.get(), value) : nullptr);
  341. if(value && !target)
  342. SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Invalid effect slot target ID");
  343. if(target)
  344. {
  345. ALeffectslot *checker{target};
  346. while(checker && checker != slot)
  347. checker = checker->Target;
  348. if(checker)
  349. SETERR_RETURN(context.get(), AL_INVALID_OPERATION,,
  350. "Setting target of effect slot ID %u to %u creates circular chain", slot->id,
  351. target->id);
  352. }
  353. if(ALeffectslot *oldtarget{slot->Target})
  354. {
  355. /* We must force an update if there was an existing effect slot
  356. * target, in case it's about to be deleted.
  357. */
  358. if(target) IncrementRef(&target->ref);
  359. DecrementRef(&oldtarget->ref);
  360. slot->Target = target;
  361. UpdateEffectSlotProps(slot, context.get());
  362. return;
  363. }
  364. if(target) IncrementRef(&target->ref);
  365. slot->Target = target;
  366. break;
  367. default:
  368. SETERR_RETURN(context.get(), AL_INVALID_ENUM,,
  369. "Invalid effect slot integer property 0x%04x", param);
  370. }
  371. DO_UPDATEPROPS();
  372. }
  373. END_API_FUNC
  374. AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
  375. START_API_FUNC
  376. {
  377. switch(param)
  378. {
  379. case AL_EFFECTSLOT_EFFECT:
  380. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  381. case AL_EFFECTSLOT_TARGET_SOFT:
  382. alAuxiliaryEffectSloti(effectslot, param, values[0]);
  383. return;
  384. }
  385. ContextRef context{GetContextRef()};
  386. if(UNLIKELY(!context)) return;
  387. std::lock_guard<std::mutex> _{context->EffectSlotLock};
  388. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  389. if(UNLIKELY(!slot))
  390. SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  391. switch(param)
  392. {
  393. default:
  394. SETERR_RETURN(context.get(), AL_INVALID_ENUM,,
  395. "Invalid effect slot integer-vector property 0x%04x", param);
  396. }
  397. }
  398. END_API_FUNC
  399. AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
  400. START_API_FUNC
  401. {
  402. ContextRef context{GetContextRef()};
  403. if(UNLIKELY(!context)) return;
  404. std::lock_guard<std::mutex> _{context->PropLock};
  405. std::lock_guard<std::mutex> __{context->EffectSlotLock};
  406. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  407. if(UNLIKELY(!slot))
  408. SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  409. switch(param)
  410. {
  411. case AL_EFFECTSLOT_GAIN:
  412. if(!(value >= 0.0f && value <= 1.0f))
  413. SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Effect slot gain out of range");
  414. slot->Gain = value;
  415. break;
  416. default:
  417. SETERR_RETURN(context.get(), AL_INVALID_ENUM,, "Invalid effect slot float property 0x%04x",
  418. param);
  419. }
  420. DO_UPDATEPROPS();
  421. }
  422. END_API_FUNC
  423. AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
  424. START_API_FUNC
  425. {
  426. switch(param)
  427. {
  428. case AL_EFFECTSLOT_GAIN:
  429. alAuxiliaryEffectSlotf(effectslot, param, values[0]);
  430. return;
  431. }
  432. ContextRef context{GetContextRef()};
  433. if(UNLIKELY(!context)) return;
  434. std::lock_guard<std::mutex> _{context->EffectSlotLock};
  435. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  436. if(UNLIKELY(!slot))
  437. SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  438. switch(param)
  439. {
  440. default:
  441. SETERR_RETURN(context.get(), AL_INVALID_ENUM,,
  442. "Invalid effect slot float-vector property 0x%04x", param);
  443. }
  444. }
  445. END_API_FUNC
  446. AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
  447. START_API_FUNC
  448. {
  449. ContextRef context{GetContextRef()};
  450. if(UNLIKELY(!context)) return;
  451. std::lock_guard<std::mutex> _{context->EffectSlotLock};
  452. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  453. if(UNLIKELY(!slot))
  454. SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  455. switch(param)
  456. {
  457. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  458. *value = slot->AuxSendAuto;
  459. break;
  460. case AL_EFFECTSLOT_TARGET_SOFT:
  461. *value = slot->Target ? slot->Target->id : 0;
  462. break;
  463. default:
  464. SETERR_RETURN(context.get(), AL_INVALID_ENUM,,
  465. "Invalid effect slot integer property 0x%04x", param);
  466. }
  467. }
  468. END_API_FUNC
  469. AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
  470. START_API_FUNC
  471. {
  472. switch(param)
  473. {
  474. case AL_EFFECTSLOT_EFFECT:
  475. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  476. case AL_EFFECTSLOT_TARGET_SOFT:
  477. alGetAuxiliaryEffectSloti(effectslot, param, values);
  478. return;
  479. }
  480. ContextRef context{GetContextRef()};
  481. if(UNLIKELY(!context)) return;
  482. std::lock_guard<std::mutex> _{context->EffectSlotLock};
  483. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  484. if(UNLIKELY(!slot))
  485. SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  486. switch(param)
  487. {
  488. default:
  489. SETERR_RETURN(context.get(), AL_INVALID_ENUM,,
  490. "Invalid effect slot integer-vector property 0x%04x", param);
  491. }
  492. }
  493. END_API_FUNC
  494. AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
  495. START_API_FUNC
  496. {
  497. ContextRef context{GetContextRef()};
  498. if(UNLIKELY(!context)) return;
  499. std::lock_guard<std::mutex> _{context->EffectSlotLock};
  500. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  501. if(UNLIKELY(!slot))
  502. SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  503. switch(param)
  504. {
  505. case AL_EFFECTSLOT_GAIN:
  506. *value = slot->Gain;
  507. break;
  508. default:
  509. SETERR_RETURN(context.get(), AL_INVALID_ENUM,,
  510. "Invalid effect slot float property 0x%04x", param);
  511. }
  512. }
  513. END_API_FUNC
  514. AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
  515. START_API_FUNC
  516. {
  517. switch(param)
  518. {
  519. case AL_EFFECTSLOT_GAIN:
  520. alGetAuxiliaryEffectSlotf(effectslot, param, values);
  521. return;
  522. }
  523. ContextRef context{GetContextRef()};
  524. if(UNLIKELY(!context)) return;
  525. std::lock_guard<std::mutex> _{context->EffectSlotLock};
  526. ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
  527. if(UNLIKELY(!slot))
  528. SETERR_RETURN(context.get(), AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
  529. switch(param)
  530. {
  531. default:
  532. SETERR_RETURN(context.get(), AL_INVALID_ENUM,,
  533. "Invalid effect slot float-vector property 0x%04x", param);
  534. }
  535. }
  536. END_API_FUNC
  537. ALenum InitializeEffect(ALCcontext *Context, ALeffectslot *EffectSlot, ALeffect *effect)
  538. {
  539. ALenum newtype{effect ? effect->type : AL_EFFECT_NULL};
  540. if(newtype != EffectSlot->Effect.Type)
  541. {
  542. EffectStateFactory *factory{getFactoryByType(newtype)};
  543. if(!factory)
  544. {
  545. ERR("Failed to find factory for effect type 0x%04x\n", newtype);
  546. return AL_INVALID_ENUM;
  547. }
  548. EffectState *State{factory->create()};
  549. if(!State) return AL_OUT_OF_MEMORY;
  550. FPUCtl mixer_mode{};
  551. ALCdevice *Device{Context->Device};
  552. std::unique_lock<std::mutex> statelock{Device->StateLock};
  553. State->mOutBuffer = Device->Dry.Buffer;
  554. State->mOutChannels = Device->Dry.NumChannels;
  555. if(State->deviceUpdate(Device) == AL_FALSE)
  556. {
  557. statelock.unlock();
  558. mixer_mode.leave();
  559. State->DecRef();
  560. return AL_OUT_OF_MEMORY;
  561. }
  562. mixer_mode.leave();
  563. if(!effect)
  564. {
  565. EffectSlot->Effect.Type = AL_EFFECT_NULL;
  566. EffectSlot->Effect.Props = EffectProps {};
  567. }
  568. else
  569. {
  570. EffectSlot->Effect.Type = effect->type;
  571. EffectSlot->Effect.Props = effect->Props;
  572. }
  573. EffectSlot->Effect.State->DecRef();
  574. EffectSlot->Effect.State = State;
  575. }
  576. else if(effect)
  577. EffectSlot->Effect.Props = effect->Props;
  578. /* Remove state references from old effect slot property updates. */
  579. ALeffectslotProps *props{Context->FreeEffectslotProps.load()};
  580. while(props)
  581. {
  582. if(props->State)
  583. props->State->DecRef();
  584. props->State = nullptr;
  585. props = props->next.load(std::memory_order_relaxed);
  586. }
  587. return AL_NO_ERROR;
  588. }
  589. void EffectState::IncRef() noexcept
  590. {
  591. auto ref = IncrementRef(&mRef);
  592. TRACEREF("%p increasing refcount to %u\n", this, ref);
  593. }
  594. void EffectState::DecRef() noexcept
  595. {
  596. auto ref = DecrementRef(&mRef);
  597. TRACEREF("%p decreasing refcount to %u\n", this, ref);
  598. if(ref == 0) delete this;
  599. }
  600. ALenum InitEffectSlot(ALeffectslot *slot)
  601. {
  602. EffectStateFactory *factory{getFactoryByType(slot->Effect.Type)};
  603. if(!factory) return AL_INVALID_VALUE;
  604. slot->Effect.State = factory->create();
  605. if(!slot->Effect.State) return AL_OUT_OF_MEMORY;
  606. slot->Effect.State->IncRef();
  607. slot->Params.mEffectState = slot->Effect.State;
  608. return AL_NO_ERROR;
  609. }
  610. ALeffectslot::~ALeffectslot()
  611. {
  612. if(Target)
  613. DecrementRef(&Target->ref);
  614. Target = nullptr;
  615. ALeffectslotProps *props{Update.load()};
  616. if(props)
  617. {
  618. if(props->State) props->State->DecRef();
  619. TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props);
  620. al_free(props);
  621. }
  622. if(Effect.State)
  623. Effect.State->DecRef();
  624. if(Params.mEffectState)
  625. Params.mEffectState->DecRef();
  626. }
  627. void UpdateEffectSlotProps(ALeffectslot *slot, ALCcontext *context)
  628. {
  629. /* Get an unused property container, or allocate a new one as needed. */
  630. ALeffectslotProps *props{context->FreeEffectslotProps.load(std::memory_order_relaxed)};
  631. if(!props)
  632. props = static_cast<ALeffectslotProps*>(al_calloc(16, sizeof(*props)));
  633. else
  634. {
  635. ALeffectslotProps *next;
  636. do {
  637. next = props->next.load(std::memory_order_relaxed);
  638. } while(context->FreeEffectslotProps.compare_exchange_weak(props, next,
  639. std::memory_order_seq_cst, std::memory_order_acquire) == 0);
  640. }
  641. /* Copy in current property values. */
  642. props->Gain = slot->Gain;
  643. props->AuxSendAuto = slot->AuxSendAuto;
  644. props->Target = slot->Target;
  645. props->Type = slot->Effect.Type;
  646. props->Props = slot->Effect.Props;
  647. /* Swap out any stale effect state object there may be in the container, to
  648. * delete it.
  649. */
  650. EffectState *oldstate{props->State};
  651. slot->Effect.State->IncRef();
  652. props->State = slot->Effect.State;
  653. /* Set the new container for updating internal parameters. */
  654. props = slot->Update.exchange(props, std::memory_order_acq_rel);
  655. if(props)
  656. {
  657. /* If there was an unused update container, put it back in the
  658. * freelist.
  659. */
  660. if(props->State)
  661. props->State->DecRef();
  662. props->State = nullptr;
  663. AtomicReplaceHead(context->FreeEffectslotProps, props);
  664. }
  665. if(oldstate)
  666. oldstate->DecRef();
  667. }
  668. void UpdateAllEffectSlotProps(ALCcontext *context)
  669. {
  670. std::lock_guard<std::mutex> _{context->EffectSlotLock};
  671. ALeffectslotArray *auxslots{context->ActiveAuxSlots.load(std::memory_order_acquire)};
  672. for(ALeffectslot *slot : *auxslots)
  673. {
  674. if(!slot->PropsClean.test_and_set(std::memory_order_acq_rel))
  675. UpdateEffectSlotProps(slot, context);
  676. }
  677. }
  678. EffectSlotSubList::~EffectSlotSubList()
  679. {
  680. uint64_t usemask{~FreeMask};
  681. while(usemask)
  682. {
  683. ALsizei idx{CTZ64(usemask)};
  684. EffectSlots[idx].~ALeffectslot();
  685. usemask &= ~(1_u64 << idx);
  686. }
  687. FreeMask = ~usemask;
  688. al_free(EffectSlots);
  689. EffectSlots = nullptr;
  690. }