auxeffectslot.cpp 24 KB

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