auxeffectslot.cpp 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530
  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 <cstddef>
  24. #include <cstdint>
  25. #include <functional>
  26. #include <iterator>
  27. #include <memory>
  28. #include <mutex>
  29. #include <numeric>
  30. #include <stdexcept>
  31. #include <string>
  32. #include <tuple>
  33. #include <unordered_map>
  34. #include <vector>
  35. #include "AL/al.h"
  36. #include "AL/alc.h"
  37. #include "AL/alext.h"
  38. #include "AL/efx.h"
  39. #include "albit.h"
  40. #include "alc/alu.h"
  41. #include "alc/context.h"
  42. #include "alc/device.h"
  43. #include "alc/effects/base.h"
  44. #include "alc/inprogext.h"
  45. #include "almalloc.h"
  46. #include "alnumeric.h"
  47. #include "alspan.h"
  48. #include "atomic.h"
  49. #include "buffer.h"
  50. #include "core/buffer_storage.h"
  51. #include "core/device.h"
  52. #include "core/fpu_ctrl.h"
  53. #include "core/logging.h"
  54. #include "direct_defs.h"
  55. #include "effect.h"
  56. #include "error.h"
  57. #include "flexarray.h"
  58. #include "opthelpers.h"
  59. #ifdef ALSOFT_EAX
  60. #include "eax/api.h"
  61. #include "eax/call.h"
  62. #include "eax/effect.h"
  63. #include "eax/fx_slot_index.h"
  64. #include "eax/utils.h"
  65. #endif
  66. namespace {
  67. using SubListAllocator = al::allocator<std::array<ALeffectslot,64>>;
  68. EffectStateFactory *getFactoryByType(EffectSlotType type)
  69. {
  70. switch(type)
  71. {
  72. case EffectSlotType::None: return NullStateFactory_getFactory();
  73. case EffectSlotType::Reverb: return ReverbStateFactory_getFactory();
  74. case EffectSlotType::Chorus: return ChorusStateFactory_getFactory();
  75. case EffectSlotType::Autowah: return AutowahStateFactory_getFactory();
  76. case EffectSlotType::Compressor: return CompressorStateFactory_getFactory();
  77. case EffectSlotType::Convolution: return ConvolutionStateFactory_getFactory();
  78. case EffectSlotType::Dedicated: return DedicatedStateFactory_getFactory();
  79. case EffectSlotType::Distortion: return DistortionStateFactory_getFactory();
  80. case EffectSlotType::Echo: return EchoStateFactory_getFactory();
  81. case EffectSlotType::Equalizer: return EqualizerStateFactory_getFactory();
  82. case EffectSlotType::Flanger: return ChorusStateFactory_getFactory();
  83. case EffectSlotType::FrequencyShifter: return FshifterStateFactory_getFactory();
  84. case EffectSlotType::RingModulator: return ModulatorStateFactory_getFactory();
  85. case EffectSlotType::PitchShifter: return PshifterStateFactory_getFactory();
  86. case EffectSlotType::VocalMorpher: return VmorpherStateFactory_getFactory();
  87. }
  88. return nullptr;
  89. }
  90. auto LookupEffectSlot(ALCcontext *context, ALuint id) noexcept -> ALeffectslot*
  91. {
  92. const size_t lidx{(id-1) >> 6};
  93. const ALuint slidx{(id-1) & 0x3f};
  94. if(lidx >= context->mEffectSlotList.size()) UNLIKELY
  95. return nullptr;
  96. EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
  97. if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
  98. return nullptr;
  99. return al::to_address(sublist.EffectSlots->begin() + slidx);
  100. }
  101. inline auto LookupEffect(ALCdevice *device, ALuint id) noexcept -> ALeffect*
  102. {
  103. const size_t lidx{(id-1) >> 6};
  104. const ALuint slidx{(id-1) & 0x3f};
  105. if(lidx >= device->EffectList.size()) UNLIKELY
  106. return nullptr;
  107. EffectSubList &sublist = device->EffectList[lidx];
  108. if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
  109. return nullptr;
  110. return al::to_address(sublist.Effects->begin() + slidx);
  111. }
  112. inline auto LookupBuffer(ALCdevice *device, ALuint id) noexcept -> ALbuffer*
  113. {
  114. const size_t lidx{(id-1) >> 6};
  115. const ALuint slidx{(id-1) & 0x3f};
  116. if(lidx >= device->BufferList.size()) UNLIKELY
  117. return nullptr;
  118. BufferSubList &sublist = device->BufferList[lidx];
  119. if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
  120. return nullptr;
  121. return al::to_address(sublist.Buffers->begin() + slidx);
  122. }
  123. void AddActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
  124. {
  125. if(auxslots.empty()) return;
  126. EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
  127. if((curarray->size()>>1) > std::numeric_limits<size_t>::max()-auxslots.size())
  128. throw std::runtime_error{"Too many active effect slots"};
  129. size_t newcount{(curarray->size()>>1) + auxslots.size()};
  130. if(newcount > std::numeric_limits<size_t>::max()>>1)
  131. throw std::runtime_error{"Too many active effect slots"};
  132. /* Insert the new effect slots into the head of the new array, followed by
  133. * the existing ones.
  134. */
  135. auto newarray = EffectSlot::CreatePtrArray(newcount<<1);
  136. auto new_end = std::transform(auxslots.begin(), auxslots.end(), newarray->begin(),
  137. std::mem_fn(&ALeffectslot::mSlot));
  138. new_end = std::copy_n(curarray->begin(), curarray->size()>>1, new_end);
  139. /* Remove any duplicates (first instance of each will be kept). */
  140. for(auto start=newarray->begin()+1;;)
  141. {
  142. new_end = std::remove(start, new_end, *(start-1));
  143. if(start == new_end) break;
  144. ++start;
  145. }
  146. newcount = static_cast<size_t>(std::distance(newarray->begin(), new_end));
  147. /* Reallocate newarray if the new size ended up smaller from duplicate
  148. * removal.
  149. */
  150. if(newcount < newarray->size()>>1) UNLIKELY
  151. {
  152. auto oldarray = std::move(newarray);
  153. newarray = EffectSlot::CreatePtrArray(newcount<<1);
  154. new_end = std::copy_n(oldarray->begin(), newcount, newarray->begin());
  155. }
  156. std::fill(new_end, newarray->end(), nullptr);
  157. auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray),
  158. std::memory_order_acq_rel);
  159. std::ignore = context->mDevice->waitForMix();
  160. }
  161. void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
  162. {
  163. if(auxslots.empty()) return;
  164. EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
  165. /* Don't shrink the allocated array size since we don't know how many (if
  166. * any) of the effect slots to remove are in the array.
  167. */
  168. auto newarray = EffectSlot::CreatePtrArray(curarray->size());
  169. auto new_end = std::copy_n(curarray->begin(), curarray->size()>>1, newarray->begin());
  170. /* Remove elements from newarray that match any ID in slotids. */
  171. for(const ALeffectslot *auxslot : auxslots)
  172. {
  173. auto slot_match = [auxslot](EffectSlot *slot) noexcept -> bool
  174. { return (slot == auxslot->mSlot); };
  175. new_end = std::remove_if(newarray->begin(), new_end, slot_match);
  176. }
  177. /* Reallocate with the new size. */
  178. auto newsize = static_cast<size_t>(std::distance(newarray->begin(), new_end));
  179. if(newsize < newarray->size()>>1) LIKELY
  180. {
  181. auto oldarray = std::move(newarray);
  182. newarray = EffectSlot::CreatePtrArray(newsize<<1);
  183. new_end = std::copy_n(oldarray->begin(), newsize, newarray->begin());
  184. }
  185. std::fill(new_end, newarray->end(), nullptr);
  186. auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray),
  187. std::memory_order_acq_rel);
  188. std::ignore = context->mDevice->waitForMix();
  189. }
  190. constexpr auto EffectSlotTypeFromEnum(ALenum type) noexcept -> EffectSlotType
  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::Reverb;
  208. case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::Dedicated;
  209. case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::Dedicated;
  210. case AL_EFFECT_CONVOLUTION_SOFT: return EffectSlotType::Convolution;
  211. }
  212. ERR("Unhandled effect enum: 0x%04x\n", type);
  213. return EffectSlotType::None;
  214. }
  215. auto EnsureEffectSlots(ALCcontext *context, size_t needed) noexcept -> bool
  216. try {
  217. size_t count{std::accumulate(context->mEffectSlotList.cbegin(),
  218. context->mEffectSlotList.cend(), 0_uz,
  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(context->mEffectSlotList.size() >= 1<<25) UNLIKELY
  224. return false;
  225. EffectSlotSubList sublist{};
  226. sublist.FreeMask = ~0_u64;
  227. sublist.EffectSlots = SubListAllocator{}.allocate(1);
  228. context->mEffectSlotList.emplace_back(std::move(sublist));
  229. count += std::tuple_size_v<SubListAllocator::value_type>;
  230. }
  231. return true;
  232. }
  233. catch(...) {
  234. return false;
  235. }
  236. ALeffectslot *AllocEffectSlot(ALCcontext *context)
  237. {
  238. auto sublist = std::find_if(context->mEffectSlotList.begin(), context->mEffectSlotList.end(),
  239. [](const EffectSlotSubList &entry) noexcept -> bool
  240. { return entry.FreeMask != 0; });
  241. auto lidx = static_cast<ALuint>(std::distance(context->mEffectSlotList.begin(), sublist));
  242. auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
  243. ASSUME(slidx < 64);
  244. ALeffectslot *slot{al::construct_at(al::to_address(sublist->EffectSlots->begin() + slidx),
  245. context)};
  246. aluInitEffectPanning(slot->mSlot, context);
  247. /* Add 1 to avoid ID 0. */
  248. slot->id = ((lidx<<6) | slidx) + 1;
  249. context->mNumEffectSlots += 1;
  250. sublist->FreeMask &= ~(1_u64 << slidx);
  251. return slot;
  252. }
  253. void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
  254. {
  255. context->mEffectSlotNames.erase(slot->id);
  256. const ALuint id{slot->id - 1};
  257. const size_t lidx{id >> 6};
  258. const ALuint slidx{id & 0x3f};
  259. std::destroy_at(slot);
  260. context->mEffectSlotList[lidx].FreeMask |= 1_u64 << slidx;
  261. context->mNumEffectSlots--;
  262. }
  263. inline void UpdateProps(ALeffectslot *slot, ALCcontext *context)
  264. {
  265. if(!context->mDeferUpdates && slot->mState == SlotState::Playing)
  266. {
  267. slot->updateProps(context);
  268. return;
  269. }
  270. slot->mPropsDirty = true;
  271. }
  272. } // namespace
  273. AL_API DECL_FUNC2(void, alGenAuxiliaryEffectSlots, ALsizei,n, ALuint*,effectslots)
  274. FORCE_ALIGN void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n,
  275. ALuint *effectslots) noexcept
  276. try {
  277. if(n < 0)
  278. throw al::context_error{AL_INVALID_VALUE, "Generating %d effect slots", n};
  279. if(n <= 0) UNLIKELY return;
  280. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  281. ALCdevice *device{context->mALDevice.get()};
  282. const al::span eids{effectslots, static_cast<ALuint>(n)};
  283. if(eids.size() > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
  284. throw al::context_error{AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)",
  285. device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n};
  286. if(!EnsureEffectSlots(context, eids.size()))
  287. throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n,
  288. (n == 1) ? "" : "s"};
  289. std::vector<ALeffectslot*> slots;
  290. try {
  291. if(eids.size() == 1)
  292. {
  293. /* Special handling for the easy and normal case. */
  294. eids[0] = AllocEffectSlot(context)->id;
  295. }
  296. else
  297. {
  298. slots.reserve(eids.size());
  299. std::generate_n(std::back_inserter(slots), eids.size(),
  300. [context]{ return AllocEffectSlot(context); });
  301. std::transform(slots.cbegin(), slots.cend(), eids.begin(),
  302. [](ALeffectslot *slot) -> ALuint { return slot->id; });
  303. }
  304. }
  305. catch(std::exception& e) {
  306. ERR("Exception allocating effectslot %zu of %d: %s\n", slots.size()+1, n, e.what());
  307. auto delete_effectslot = [context](ALeffectslot *slot) -> void
  308. { FreeEffectSlot(context, slot); };
  309. std::for_each(slots.begin(), slots.end(), delete_effectslot);
  310. throw al::context_error{AL_INVALID_OPERATION, "Exception allocating %d effectslots: %s", n,
  311. e.what()};
  312. }
  313. }
  314. catch(al::context_error& e) {
  315. context->setError(e.errorCode(), "%s", e.what());
  316. }
  317. AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei,n, const ALuint*,effectslots)
  318. FORCE_ALIGN void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n,
  319. const ALuint *effectslots) noexcept
  320. try {
  321. if(n < 0) UNLIKELY
  322. throw al::context_error{AL_INVALID_VALUE, "Deleting %d effect slots", n};
  323. if(n <= 0) UNLIKELY return;
  324. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  325. if(n == 1)
  326. {
  327. ALeffectslot *slot{LookupEffectSlot(context, *effectslots)};
  328. if(!slot)
  329. throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", *effectslots};
  330. if(slot->ref.load(std::memory_order_relaxed) != 0)
  331. throw al::context_error{AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
  332. *effectslots};
  333. RemoveActiveEffectSlots({&slot, 1u}, context);
  334. FreeEffectSlot(context, slot);
  335. }
  336. else
  337. {
  338. const al::span eids{effectslots, static_cast<ALuint>(n)};
  339. std::vector<ALeffectslot*> slots;
  340. slots.reserve(eids.size());
  341. auto lookupslot = [context](const ALuint eid) -> ALeffectslot*
  342. {
  343. ALeffectslot *slot{LookupEffectSlot(context, eid)};
  344. if(!slot)
  345. throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", eid};
  346. if(slot->ref.load(std::memory_order_relaxed) != 0)
  347. throw al::context_error{AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
  348. eid};
  349. return slot;
  350. };
  351. std::transform(eids.cbegin(), eids.cend(), std::back_inserter(slots), lookupslot);
  352. /* All effectslots are valid, remove and delete them */
  353. RemoveActiveEffectSlots(slots, context);
  354. auto delete_effectslot = [context](const ALuint eid) -> void
  355. {
  356. if(ALeffectslot *slot{LookupEffectSlot(context, eid)})
  357. FreeEffectSlot(context, slot);
  358. };
  359. std::for_each(eids.begin(), eids.end(), delete_effectslot);
  360. }
  361. }
  362. catch(al::context_error& e) {
  363. context->setError(e.errorCode(), "%s", e.what());
  364. }
  365. AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint,effectslot)
  366. FORCE_ALIGN ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context,
  367. ALuint effectslot) noexcept
  368. {
  369. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  370. if(LookupEffectSlot(context, effectslot) != nullptr)
  371. return AL_TRUE;
  372. return AL_FALSE;
  373. }
  374. AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint) noexcept
  375. {
  376. ContextRef context{GetContextRef()};
  377. if(!context) UNLIKELY return;
  378. context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlaySOFT not supported");
  379. }
  380. AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei, const ALuint*) noexcept
  381. {
  382. ContextRef context{GetContextRef()};
  383. if(!context) UNLIKELY return;
  384. context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlayvSOFT not supported");
  385. }
  386. AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint) noexcept
  387. {
  388. ContextRef context{GetContextRef()};
  389. if(!context) UNLIKELY return;
  390. context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopSOFT not supported");
  391. }
  392. AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei, const ALuint*) noexcept
  393. {
  394. ContextRef context{GetContextRef()};
  395. if(!context) UNLIKELY return;
  396. context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopvSOFT not supported");
  397. }
  398. AL_API DECL_FUNC3(void, alAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint,value)
  399. FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot,
  400. ALenum param, ALint value) noexcept
  401. try {
  402. std::lock_guard<std::mutex> proplock{context->mPropLock};
  403. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  404. ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
  405. if(!slot) UNLIKELY
  406. throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
  407. ALeffectslot *target{};
  408. ALenum err{};
  409. switch(param)
  410. {
  411. case AL_EFFECTSLOT_EFFECT:
  412. {
  413. ALCdevice *device{context->mALDevice.get()};
  414. std::lock_guard<std::mutex> effectlock{device->EffectLock};
  415. ALeffect *effect{value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr};
  416. if(effect)
  417. err = slot->initEffect(effect->id, effect->type, effect->Props, context);
  418. else
  419. {
  420. if(value != 0)
  421. throw al::context_error{AL_INVALID_VALUE, "Invalid effect ID %u", value};
  422. err = slot->initEffect(0, AL_EFFECT_NULL, EffectProps{}, context);
  423. }
  424. }
  425. if(err != AL_NO_ERROR)
  426. throw al::context_error{err, "Effect initialization failed"};
  427. if(slot->mState == SlotState::Initial) UNLIKELY
  428. {
  429. slot->mPropsDirty = false;
  430. slot->updateProps(context);
  431. AddActiveEffectSlots({&slot, 1}, context);
  432. slot->mState = SlotState::Playing;
  433. return;
  434. }
  435. UpdateProps(slot, context);
  436. return;
  437. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  438. if(!(value == AL_TRUE || value == AL_FALSE))
  439. throw al::context_error{AL_INVALID_VALUE,
  440. "Effect slot auxiliary send auto out of range"};
  441. if(!(slot->AuxSendAuto == !!value)) LIKELY
  442. {
  443. slot->AuxSendAuto = !!value;
  444. UpdateProps(slot, context);
  445. }
  446. return;
  447. case AL_EFFECTSLOT_TARGET_SOFT:
  448. target = LookupEffectSlot(context, static_cast<ALuint>(value));
  449. if(value && !target)
  450. throw al::context_error{AL_INVALID_VALUE, "Invalid effect slot target ID"};
  451. if(slot->Target == target) UNLIKELY
  452. return;
  453. if(target)
  454. {
  455. ALeffectslot *checker{target};
  456. while(checker && checker != slot)
  457. checker = checker->Target;
  458. if(checker)
  459. throw al::context_error{AL_INVALID_OPERATION,
  460. "Setting target of effect slot ID %u to %u creates circular chain", slot->id,
  461. target->id};
  462. }
  463. if(ALeffectslot *oldtarget{slot->Target})
  464. {
  465. /* We must force an update if there was an existing effect slot
  466. * target, in case it's about to be deleted.
  467. */
  468. if(target) IncrementRef(target->ref);
  469. DecrementRef(oldtarget->ref);
  470. slot->Target = target;
  471. slot->updateProps(context);
  472. return;
  473. }
  474. if(target) IncrementRef(target->ref);
  475. slot->Target = target;
  476. UpdateProps(slot, context);
  477. return;
  478. case AL_BUFFER:
  479. if(ALbuffer *buffer{slot->Buffer})
  480. {
  481. if(buffer->id == static_cast<ALuint>(value))
  482. return;
  483. }
  484. else if(value == 0)
  485. return;
  486. if(slot->mState == SlotState::Playing)
  487. {
  488. EffectStateFactory *factory{getFactoryByType(slot->Effect.Type)};
  489. assert(factory);
  490. al::intrusive_ptr<EffectState> state{factory->create()};
  491. ALCdevice *device{context->mALDevice.get()};
  492. auto bufferlock = std::unique_lock{device->BufferLock};
  493. ALbuffer *buffer{};
  494. if(value)
  495. {
  496. buffer = LookupBuffer(device, static_cast<ALuint>(value));
  497. if(!buffer)
  498. throw al::context_error{AL_INVALID_VALUE, "Invalid buffer ID %u", value};
  499. if(buffer->mCallback)
  500. throw al::context_error{AL_INVALID_OPERATION,
  501. "Callback buffer not valid for effects"};
  502. IncrementRef(buffer->ref);
  503. }
  504. /* Stop the effect slot from processing while we switch buffers. */
  505. RemoveActiveEffectSlots({&slot, 1}, context);
  506. if(ALbuffer *oldbuffer{slot->Buffer})
  507. DecrementRef(oldbuffer->ref);
  508. slot->Buffer = buffer;
  509. bufferlock.unlock();
  510. state->mOutTarget = device->Dry.Buffer;
  511. {
  512. FPUCtl mixer_mode{};
  513. state->deviceUpdate(device, buffer);
  514. }
  515. slot->Effect.State = std::move(state);
  516. slot->mPropsDirty = false;
  517. slot->updateProps(context);
  518. AddActiveEffectSlots({&slot, 1}, context);
  519. }
  520. else
  521. {
  522. ALCdevice *device{context->mALDevice.get()};
  523. auto bufferlock = std::unique_lock{device->BufferLock};
  524. ALbuffer *buffer{};
  525. if(value)
  526. {
  527. buffer = LookupBuffer(device, static_cast<ALuint>(value));
  528. if(!buffer)
  529. throw al::context_error{AL_INVALID_VALUE, "Invalid buffer ID %u", value};
  530. if(buffer->mCallback)
  531. throw al::context_error{AL_INVALID_OPERATION,
  532. "Callback buffer not valid for effects"};
  533. IncrementRef(buffer->ref);
  534. }
  535. if(ALbuffer *oldbuffer{slot->Buffer})
  536. DecrementRef(oldbuffer->ref);
  537. slot->Buffer = buffer;
  538. bufferlock.unlock();
  539. FPUCtl mixer_mode{};
  540. auto *state = slot->Effect.State.get();
  541. state->deviceUpdate(device, buffer);
  542. slot->mPropsDirty = true;
  543. }
  544. return;
  545. case AL_EFFECTSLOT_STATE_SOFT:
  546. throw al::context_error{AL_INVALID_OPERATION, "AL_EFFECTSLOT_STATE_SOFT is read-only"};
  547. }
  548. throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param};
  549. }
  550. catch(al::context_error& e) {
  551. context->setError(e.errorCode(), "%s", e.what());
  552. }
  553. AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, const ALint*,values)
  554. FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot,
  555. ALenum param, const ALint *values) noexcept
  556. try {
  557. switch(param)
  558. {
  559. case AL_EFFECTSLOT_EFFECT:
  560. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  561. case AL_EFFECTSLOT_TARGET_SOFT:
  562. case AL_EFFECTSLOT_STATE_SOFT:
  563. case AL_BUFFER:
  564. alAuxiliaryEffectSlotiDirect(context, effectslot, param, *values);
  565. return;
  566. }
  567. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  568. ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
  569. if(!slot)
  570. throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
  571. switch(param)
  572. {
  573. }
  574. throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
  575. param};
  576. }
  577. catch(al::context_error& e) {
  578. context->setError(e.errorCode(), "%s", e.what());
  579. }
  580. AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat,value)
  581. FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot,
  582. ALenum param, ALfloat value) noexcept
  583. try {
  584. std::lock_guard<std::mutex> proplock{context->mPropLock};
  585. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  586. ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
  587. if(!slot)
  588. throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
  589. switch(param)
  590. {
  591. case AL_EFFECTSLOT_GAIN:
  592. if(!(value >= 0.0f && value <= 1.0f))
  593. throw al::context_error{AL_INVALID_VALUE, "Effect slot gain out of range"};
  594. if(!(slot->Gain == value)) LIKELY
  595. {
  596. slot->Gain = value;
  597. UpdateProps(slot, context);
  598. }
  599. return;
  600. }
  601. throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param};
  602. }
  603. catch(al::context_error& e) {
  604. context->setError(e.errorCode(), "%s", e.what());
  605. }
  606. AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, const ALfloat*,values)
  607. FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot,
  608. ALenum param, const ALfloat *values) noexcept
  609. try {
  610. switch(param)
  611. {
  612. case AL_EFFECTSLOT_GAIN:
  613. alAuxiliaryEffectSlotfDirect(context, effectslot, param, *values);
  614. return;
  615. }
  616. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  617. ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
  618. if(!slot)
  619. throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
  620. switch(param)
  621. {
  622. }
  623. throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
  624. param};
  625. }
  626. catch(al::context_error& e) {
  627. context->setError(e.errorCode(), "%s", e.what());
  628. }
  629. AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint*,value)
  630. FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context,
  631. ALuint effectslot, ALenum param, ALint *value) noexcept
  632. try {
  633. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  634. ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
  635. if(!slot)
  636. throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
  637. switch(param)
  638. {
  639. case AL_EFFECTSLOT_EFFECT:
  640. *value = static_cast<ALint>(slot->EffectId);
  641. return;
  642. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  643. *value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE;
  644. return;
  645. case AL_EFFECTSLOT_TARGET_SOFT:
  646. if(auto *target = slot->Target)
  647. *value = static_cast<ALint>(target->id);
  648. else
  649. *value = 0;
  650. return;
  651. case AL_EFFECTSLOT_STATE_SOFT:
  652. *value = static_cast<int>(slot->mState);
  653. return;
  654. case AL_BUFFER:
  655. if(auto *buffer = slot->Buffer)
  656. *value = static_cast<ALint>(buffer->id);
  657. else
  658. *value = 0;
  659. return;
  660. }
  661. throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param};
  662. }
  663. catch(al::context_error& e) {
  664. context->setError(e.errorCode(), "%s", e.what());
  665. }
  666. AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, ALint*,values)
  667. FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context,
  668. ALuint effectslot, ALenum param, ALint *values) noexcept
  669. try {
  670. switch(param)
  671. {
  672. case AL_EFFECTSLOT_EFFECT:
  673. case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
  674. case AL_EFFECTSLOT_TARGET_SOFT:
  675. case AL_EFFECTSLOT_STATE_SOFT:
  676. case AL_BUFFER:
  677. alGetAuxiliaryEffectSlotiDirect(context, effectslot, param, values);
  678. return;
  679. }
  680. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  681. ALeffectslot *slot = LookupEffectSlot(context, effectslot);
  682. if(!slot)
  683. throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
  684. switch(param)
  685. {
  686. }
  687. throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
  688. param};
  689. }
  690. catch(al::context_error& e) {
  691. context->setError(e.errorCode(), "%s", e.what());
  692. }
  693. AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat*,value)
  694. FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context,
  695. ALuint effectslot, ALenum param, ALfloat *value) noexcept
  696. try {
  697. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  698. ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
  699. if(!slot)
  700. throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
  701. switch(param)
  702. {
  703. case AL_EFFECTSLOT_GAIN:
  704. *value = slot->Gain;
  705. return;
  706. }
  707. throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param};
  708. }
  709. catch(al::context_error& e) {
  710. context->setError(e.errorCode(), "%s", e.what());
  711. }
  712. AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, ALfloat*,values)
  713. FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context,
  714. ALuint effectslot, ALenum param, ALfloat *values) noexcept
  715. try {
  716. switch(param)
  717. {
  718. case AL_EFFECTSLOT_GAIN:
  719. alGetAuxiliaryEffectSlotfDirect(context, effectslot, param, values);
  720. return;
  721. }
  722. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  723. ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
  724. if(!slot)
  725. throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
  726. switch(param)
  727. {
  728. }
  729. throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
  730. param};
  731. }
  732. catch(al::context_error& e) {
  733. context->setError(e.errorCode(), "%s", e.what());
  734. }
  735. ALeffectslot::ALeffectslot(ALCcontext *context)
  736. {
  737. EffectStateFactory *factory{getFactoryByType(EffectSlotType::None)};
  738. if(!factory) throw std::runtime_error{"Failed to get null effect factory"};
  739. al::intrusive_ptr<EffectState> state{factory->create()};
  740. Effect.State = state;
  741. mSlot = context->getEffectSlot();
  742. mSlot->InUse = true;
  743. mSlot->mEffectState = std::move(state);
  744. }
  745. ALeffectslot::~ALeffectslot()
  746. {
  747. if(Target)
  748. DecrementRef(Target->ref);
  749. Target = nullptr;
  750. if(Buffer)
  751. DecrementRef(Buffer->ref);
  752. Buffer = nullptr;
  753. if(auto *slot = mSlot->Update.exchange(nullptr, std::memory_order_relaxed))
  754. slot->State = nullptr;
  755. mSlot->mEffectState = nullptr;
  756. mSlot->InUse = false;
  757. }
  758. ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
  759. ALCcontext *context)
  760. {
  761. EffectSlotType newtype{EffectSlotTypeFromEnum(effectType)};
  762. if(newtype != Effect.Type)
  763. {
  764. EffectStateFactory *factory{getFactoryByType(newtype)};
  765. if(!factory)
  766. {
  767. ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype));
  768. return AL_INVALID_ENUM;
  769. }
  770. al::intrusive_ptr<EffectState> state{factory->create()};
  771. ALCdevice *device{context->mALDevice.get()};
  772. state->mOutTarget = device->Dry.Buffer;
  773. {
  774. FPUCtl mixer_mode{};
  775. state->deviceUpdate(device, Buffer);
  776. }
  777. Effect.Type = newtype;
  778. Effect.Props = effectProps;
  779. Effect.State = std::move(state);
  780. }
  781. else if(newtype != EffectSlotType::None)
  782. Effect.Props = effectProps;
  783. EffectId = effectId;
  784. /* Remove state references from old effect slot property updates. */
  785. EffectSlotProps *props{context->mFreeEffectSlotProps.load()};
  786. while(props)
  787. {
  788. props->State = nullptr;
  789. props = props->next.load(std::memory_order_relaxed);
  790. }
  791. return AL_NO_ERROR;
  792. }
  793. void ALeffectslot::updateProps(ALCcontext *context) const
  794. {
  795. /* Get an unused property container, or allocate a new one as needed. */
  796. EffectSlotProps *props{context->mFreeEffectSlotProps.load(std::memory_order_acquire)};
  797. if(!props)
  798. {
  799. context->allocEffectSlotProps();
  800. props = context->mFreeEffectSlotProps.load(std::memory_order_acquire);
  801. }
  802. EffectSlotProps *next;
  803. do {
  804. next = props->next.load(std::memory_order_relaxed);
  805. } while(!context->mFreeEffectSlotProps.compare_exchange_weak(props, next,
  806. std::memory_order_acq_rel, std::memory_order_acquire));
  807. /* Copy in current property values. */
  808. props->Gain = Gain;
  809. props->AuxSendAuto = AuxSendAuto;
  810. props->Target = Target ? Target->mSlot : nullptr;
  811. props->Type = Effect.Type;
  812. props->Props = Effect.Props;
  813. props->State = Effect.State;
  814. /* Set the new container for updating internal parameters. */
  815. props = mSlot->Update.exchange(props, std::memory_order_acq_rel);
  816. if(props)
  817. {
  818. /* If there was an unused update container, put it back in the
  819. * freelist.
  820. */
  821. props->State = nullptr;
  822. AtomicReplaceHead(context->mFreeEffectSlotProps, props);
  823. }
  824. }
  825. void ALeffectslot::SetName(ALCcontext* context, ALuint id, std::string_view name)
  826. {
  827. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  828. auto slot = LookupEffectSlot(context, id);
  829. if(!slot)
  830. throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", id};
  831. context->mEffectSlotNames.insert_or_assign(id, name);
  832. }
  833. void UpdateAllEffectSlotProps(ALCcontext *context)
  834. {
  835. std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
  836. for(auto &sublist : context->mEffectSlotList)
  837. {
  838. uint64_t usemask{~sublist.FreeMask};
  839. while(usemask)
  840. {
  841. const auto idx = static_cast<uint>(al::countr_zero(usemask));
  842. usemask &= ~(1_u64 << idx);
  843. auto &slot = (*sublist.EffectSlots)[idx];
  844. if(std::exchange(slot.mPropsDirty, false))
  845. slot.updateProps(context);
  846. }
  847. }
  848. }
  849. EffectSlotSubList::~EffectSlotSubList()
  850. {
  851. if(!EffectSlots)
  852. return;
  853. uint64_t usemask{~FreeMask};
  854. while(usemask)
  855. {
  856. const int idx{al::countr_zero(usemask)};
  857. std::destroy_at(al::to_address(EffectSlots->begin() + idx));
  858. usemask &= ~(1_u64 << idx);
  859. }
  860. FreeMask = ~usemask;
  861. SubListAllocator{}.deallocate(EffectSlots, 1);
  862. EffectSlots = nullptr;
  863. }
  864. #ifdef ALSOFT_EAX
  865. void ALeffectslot::eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index)
  866. {
  867. if(index >= EAX_MAX_FXSLOTS)
  868. eax_fail("Index out of range.");
  869. eax_al_context_ = &al_context;
  870. eax_fx_slot_index_ = index;
  871. eax_fx_slot_set_defaults();
  872. eax_effect_ = std::make_unique<EaxEffect>();
  873. if(index == 0) eax_effect_->init<EaxReverbCommitter>();
  874. else if(index == 1) eax_effect_->init<EaxChorusCommitter>();
  875. else eax_effect_->init<EaxNullCommitter>();
  876. }
  877. void ALeffectslot::eax_commit()
  878. {
  879. if(eax_df_ != EaxDirtyFlags{})
  880. {
  881. auto df = EaxDirtyFlags{};
  882. switch(eax_version_)
  883. {
  884. case 1:
  885. case 2:
  886. case 3:
  887. eax5_fx_slot_commit(eax123_, df);
  888. break;
  889. case 4:
  890. eax4_fx_slot_commit(df);
  891. break;
  892. case 5:
  893. eax5_fx_slot_commit(eax5_, df);
  894. break;
  895. }
  896. eax_df_ = EaxDirtyFlags{};
  897. if((df & eax_volume_dirty_bit) != EaxDirtyFlags{})
  898. eax_fx_slot_set_volume();
  899. if((df & eax_flags_dirty_bit) != EaxDirtyFlags{})
  900. eax_fx_slot_set_flags();
  901. }
  902. if(eax_effect_->commit(eax_version_))
  903. eax_set_efx_slot_effect(*eax_effect_);
  904. }
  905. [[noreturn]] void ALeffectslot::eax_fail(const char* message)
  906. {
  907. throw Exception{message};
  908. }
  909. [[noreturn]] void ALeffectslot::eax_fail_unknown_effect_id()
  910. {
  911. eax_fail("Unknown effect ID.");
  912. }
  913. [[noreturn]] void ALeffectslot::eax_fail_unknown_property_id()
  914. {
  915. eax_fail("Unknown property ID.");
  916. }
  917. [[noreturn]] void ALeffectslot::eax_fail_unknown_version()
  918. {
  919. eax_fail("Unknown version.");
  920. }
  921. void ALeffectslot::eax4_fx_slot_ensure_unlocked() const
  922. {
  923. if(eax4_fx_slot_is_legacy())
  924. eax_fail("Locked legacy slot.");
  925. }
  926. ALenum ALeffectslot::eax_get_efx_effect_type(const GUID& guid)
  927. {
  928. if(guid == EAX_NULL_GUID)
  929. return AL_EFFECT_NULL;
  930. if(guid == EAX_AUTOWAH_EFFECT)
  931. return AL_EFFECT_AUTOWAH;
  932. if(guid == EAX_CHORUS_EFFECT)
  933. return AL_EFFECT_CHORUS;
  934. if(guid == EAX_AGCCOMPRESSOR_EFFECT)
  935. return AL_EFFECT_COMPRESSOR;
  936. if(guid == EAX_DISTORTION_EFFECT)
  937. return AL_EFFECT_DISTORTION;
  938. if(guid == EAX_REVERB_EFFECT)
  939. return AL_EFFECT_EAXREVERB;
  940. if(guid == EAX_ECHO_EFFECT)
  941. return AL_EFFECT_ECHO;
  942. if(guid == EAX_EQUALIZER_EFFECT)
  943. return AL_EFFECT_EQUALIZER;
  944. if(guid == EAX_FLANGER_EFFECT)
  945. return AL_EFFECT_FLANGER;
  946. if(guid == EAX_FREQUENCYSHIFTER_EFFECT)
  947. return AL_EFFECT_FREQUENCY_SHIFTER;
  948. if(guid == EAX_PITCHSHIFTER_EFFECT)
  949. return AL_EFFECT_PITCH_SHIFTER;
  950. if(guid == EAX_RINGMODULATOR_EFFECT)
  951. return AL_EFFECT_RING_MODULATOR;
  952. if(guid == EAX_VOCALMORPHER_EFFECT)
  953. return AL_EFFECT_VOCAL_MORPHER;
  954. eax_fail_unknown_effect_id();
  955. }
  956. const GUID& ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
  957. {
  958. switch(eax_fx_slot_index_)
  959. {
  960. case 0: return EAX_REVERB_EFFECT;
  961. case 1: return EAX_CHORUS_EFFECT;
  962. default: return EAX_NULL_GUID;
  963. }
  964. }
  965. long ALeffectslot::eax_get_eax_default_lock() const noexcept
  966. {
  967. return eax4_fx_slot_is_legacy() ? EAXFXSLOT_LOCKED : EAXFXSLOT_UNLOCKED;
  968. }
  969. void ALeffectslot::eax4_fx_slot_set_defaults(Eax4Props& props) noexcept
  970. {
  971. props.guidLoadEffect = eax_get_eax_default_effect_guid();
  972. props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
  973. props.lLock = eax_get_eax_default_lock();
  974. props.ulFlags = EAX40FXSLOT_DEFAULTFLAGS;
  975. }
  976. void ALeffectslot::eax5_fx_slot_set_defaults(Eax5Props& props) noexcept
  977. {
  978. props.guidLoadEffect = eax_get_eax_default_effect_guid();
  979. props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
  980. props.lLock = EAXFXSLOT_UNLOCKED;
  981. props.ulFlags = EAX50FXSLOT_DEFAULTFLAGS;
  982. props.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
  983. props.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
  984. }
  985. void ALeffectslot::eax_fx_slot_set_defaults()
  986. {
  987. eax5_fx_slot_set_defaults(eax123_.i);
  988. eax4_fx_slot_set_defaults(eax4_.i);
  989. eax5_fx_slot_set_defaults(eax5_.i);
  990. eax_ = eax5_.i;
  991. eax_df_ = EaxDirtyFlags{};
  992. }
  993. void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props)
  994. {
  995. switch(call.get_property_id())
  996. {
  997. case EAXFXSLOT_ALLPARAMETERS:
  998. call.set_value<Exception>(props);
  999. break;
  1000. case EAXFXSLOT_LOADEFFECT:
  1001. call.set_value<Exception>(props.guidLoadEffect);
  1002. break;
  1003. case EAXFXSLOT_VOLUME:
  1004. call.set_value<Exception>(props.lVolume);
  1005. break;
  1006. case EAXFXSLOT_LOCK:
  1007. call.set_value<Exception>(props.lLock);
  1008. break;
  1009. case EAXFXSLOT_FLAGS:
  1010. call.set_value<Exception>(props.ulFlags);
  1011. break;
  1012. default:
  1013. eax_fail_unknown_property_id();
  1014. }
  1015. }
  1016. void ALeffectslot::eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props)
  1017. {
  1018. switch(call.get_property_id())
  1019. {
  1020. case EAXFXSLOT_ALLPARAMETERS:
  1021. call.set_value<Exception>(props);
  1022. break;
  1023. case EAXFXSLOT_LOADEFFECT:
  1024. call.set_value<Exception>(props.guidLoadEffect);
  1025. break;
  1026. case EAXFXSLOT_VOLUME:
  1027. call.set_value<Exception>(props.lVolume);
  1028. break;
  1029. case EAXFXSLOT_LOCK:
  1030. call.set_value<Exception>(props.lLock);
  1031. break;
  1032. case EAXFXSLOT_FLAGS:
  1033. call.set_value<Exception>(props.ulFlags);
  1034. break;
  1035. case EAXFXSLOT_OCCLUSION:
  1036. call.set_value<Exception>(props.lOcclusion);
  1037. break;
  1038. case EAXFXSLOT_OCCLUSIONLFRATIO:
  1039. call.set_value<Exception>(props.flOcclusionLFRatio);
  1040. break;
  1041. default:
  1042. eax_fail_unknown_property_id();
  1043. }
  1044. }
  1045. void ALeffectslot::eax_fx_slot_get(const EaxCall& call) const
  1046. {
  1047. switch(call.get_version())
  1048. {
  1049. case 4: eax4_fx_slot_get(call, eax4_.i); break;
  1050. case 5: eax5_fx_slot_get(call, eax5_.i); break;
  1051. default: eax_fail_unknown_version();
  1052. }
  1053. }
  1054. bool ALeffectslot::eax_get(const EaxCall& call)
  1055. {
  1056. switch(call.get_property_set_id())
  1057. {
  1058. case EaxCallPropertySetId::fx_slot:
  1059. eax_fx_slot_get(call);
  1060. break;
  1061. case EaxCallPropertySetId::fx_slot_effect:
  1062. eax_effect_->get(call);
  1063. break;
  1064. default:
  1065. eax_fail_unknown_property_id();
  1066. }
  1067. return false;
  1068. }
  1069. void ALeffectslot::eax_fx_slot_load_effect(int version, ALenum altype)
  1070. {
  1071. if(!IsValidEffectType(altype))
  1072. altype = AL_EFFECT_NULL;
  1073. eax_effect_->set_defaults(version, altype);
  1074. }
  1075. void ALeffectslot::eax_fx_slot_set_volume()
  1076. {
  1077. const auto volume = std::clamp(eax_.lVolume, EAXFXSLOT_MINVOLUME, EAXFXSLOT_MAXVOLUME);
  1078. const auto gain = level_mb_to_gain(static_cast<float>(volume));
  1079. eax_set_efx_slot_gain(gain);
  1080. }
  1081. void ALeffectslot::eax_fx_slot_set_environment_flag()
  1082. {
  1083. eax_set_efx_slot_send_auto((eax_.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0u);
  1084. }
  1085. void ALeffectslot::eax_fx_slot_set_flags()
  1086. {
  1087. eax_fx_slot_set_environment_flag();
  1088. }
  1089. void ALeffectslot::eax4_fx_slot_set_all(const EaxCall& call)
  1090. {
  1091. eax4_fx_slot_ensure_unlocked();
  1092. const auto& src = call.get_value<Exception, const EAX40FXSLOTPROPERTIES>();
  1093. Eax4AllValidator{}(src);
  1094. auto& dst = eax4_.i;
  1095. eax_df_ |= eax_load_effect_dirty_bit; // Always reset the effect.
  1096. eax_df_ |= (dst.lVolume != src.lVolume ? eax_volume_dirty_bit : EaxDirtyFlags{});
  1097. eax_df_ |= (dst.lLock != src.lLock ? eax_lock_dirty_bit : EaxDirtyFlags{});
  1098. eax_df_ |= (dst.ulFlags != src.ulFlags ? eax_flags_dirty_bit : EaxDirtyFlags{});
  1099. dst = src;
  1100. }
  1101. void ALeffectslot::eax5_fx_slot_set_all(const EaxCall& call)
  1102. {
  1103. const auto& src = call.get_value<Exception, const EAX50FXSLOTPROPERTIES>();
  1104. Eax5AllValidator{}(src);
  1105. auto& dst = eax5_.i;
  1106. eax_df_ |= eax_load_effect_dirty_bit; // Always reset the effect.
  1107. eax_df_ |= (dst.lVolume != src.lVolume ? eax_volume_dirty_bit : EaxDirtyFlags{});
  1108. eax_df_ |= (dst.lLock != src.lLock ? eax_lock_dirty_bit : EaxDirtyFlags{});
  1109. eax_df_ |= (dst.ulFlags != src.ulFlags ? eax_flags_dirty_bit : EaxDirtyFlags{});
  1110. eax_df_ |= (dst.lOcclusion != src.lOcclusion ? eax_flags_dirty_bit : EaxDirtyFlags{});
  1111. eax_df_ |= (dst.flOcclusionLFRatio != src.flOcclusionLFRatio ? eax_flags_dirty_bit : EaxDirtyFlags{});
  1112. dst = src;
  1113. }
  1114. bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept
  1115. {
  1116. static constexpr auto dirty_bits =
  1117. eax_occlusion_dirty_bit |
  1118. eax_occlusion_lf_ratio_dirty_bit |
  1119. eax_flags_dirty_bit;
  1120. return (eax_df_ & dirty_bits) != EaxDirtyFlags{};
  1121. }
  1122. // Returns `true` if all sources should be updated, or `false` otherwise.
  1123. bool ALeffectslot::eax4_fx_slot_set(const EaxCall& call)
  1124. {
  1125. auto& dst = eax4_.i;
  1126. switch(call.get_property_id())
  1127. {
  1128. case EAXFXSLOT_NONE:
  1129. break;
  1130. case EAXFXSLOT_ALLPARAMETERS:
  1131. eax4_fx_slot_set_all(call);
  1132. if((eax_df_ & eax_load_effect_dirty_bit))
  1133. eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
  1134. break;
  1135. case EAXFXSLOT_LOADEFFECT:
  1136. eax4_fx_slot_ensure_unlocked();
  1137. eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call, dst.guidLoadEffect, eax_df_);
  1138. if((eax_df_ & eax_load_effect_dirty_bit))
  1139. eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
  1140. break;
  1141. case EAXFXSLOT_VOLUME:
  1142. eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, eax_df_);
  1143. break;
  1144. case EAXFXSLOT_LOCK:
  1145. eax4_fx_slot_ensure_unlocked();
  1146. eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, eax_df_);
  1147. break;
  1148. case EAXFXSLOT_FLAGS:
  1149. eax_fx_slot_set<Eax4FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, eax_df_);
  1150. break;
  1151. default:
  1152. eax_fail_unknown_property_id();
  1153. }
  1154. return eax_fx_slot_should_update_sources();
  1155. }
  1156. // Returns `true` if all sources should be updated, or `false` otherwise.
  1157. bool ALeffectslot::eax5_fx_slot_set(const EaxCall& call)
  1158. {
  1159. auto& dst = eax5_.i;
  1160. switch(call.get_property_id())
  1161. {
  1162. case EAXFXSLOT_NONE:
  1163. break;
  1164. case EAXFXSLOT_ALLPARAMETERS:
  1165. eax5_fx_slot_set_all(call);
  1166. if((eax_df_ & eax_load_effect_dirty_bit))
  1167. eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
  1168. break;
  1169. case EAXFXSLOT_LOADEFFECT:
  1170. eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call, dst.guidLoadEffect, eax_df_);
  1171. if((eax_df_ & eax_load_effect_dirty_bit))
  1172. eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
  1173. break;
  1174. case EAXFXSLOT_VOLUME:
  1175. eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, eax_df_);
  1176. break;
  1177. case EAXFXSLOT_LOCK:
  1178. eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, eax_df_);
  1179. break;
  1180. case EAXFXSLOT_FLAGS:
  1181. eax_fx_slot_set<Eax5FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, eax_df_);
  1182. break;
  1183. case EAXFXSLOT_OCCLUSION:
  1184. eax_fx_slot_set<Eax5OcclusionValidator, eax_occlusion_dirty_bit>(call, dst.lOcclusion, eax_df_);
  1185. break;
  1186. case EAXFXSLOT_OCCLUSIONLFRATIO:
  1187. eax_fx_slot_set<Eax5OcclusionLfRatioValidator, eax_occlusion_lf_ratio_dirty_bit>(call, dst.flOcclusionLFRatio, eax_df_);
  1188. break;
  1189. default:
  1190. eax_fail_unknown_property_id();
  1191. }
  1192. return eax_fx_slot_should_update_sources();
  1193. }
  1194. // Returns `true` if all sources should be updated, or `false` otherwise.
  1195. bool ALeffectslot::eax_fx_slot_set(const EaxCall& call)
  1196. {
  1197. switch (call.get_version())
  1198. {
  1199. case 4: return eax4_fx_slot_set(call);
  1200. case 5: return eax5_fx_slot_set(call);
  1201. default: eax_fail_unknown_version();
  1202. }
  1203. }
  1204. // Returns `true` if all sources should be updated, or `false` otherwise.
  1205. bool ALeffectslot::eax_set(const EaxCall& call)
  1206. {
  1207. bool ret{false};
  1208. switch(call.get_property_set_id())
  1209. {
  1210. case EaxCallPropertySetId::fx_slot: ret = eax_fx_slot_set(call); break;
  1211. case EaxCallPropertySetId::fx_slot_effect: eax_effect_->set(call); break;
  1212. default: eax_fail_unknown_property_id();
  1213. }
  1214. const auto version = call.get_version();
  1215. if(eax_version_ != version)
  1216. eax_df_ = ~EaxDirtyFlags{};
  1217. eax_version_ = version;
  1218. return ret;
  1219. }
  1220. void ALeffectslot::eax4_fx_slot_commit(EaxDirtyFlags& dst_df)
  1221. {
  1222. eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::guidLoadEffect);
  1223. eax_fx_slot_commit_property<eax_volume_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::lVolume);
  1224. eax_fx_slot_commit_property<eax_lock_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::lLock);
  1225. eax_fx_slot_commit_property<eax_flags_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::ulFlags);
  1226. auto& dst_i = eax_;
  1227. if(dst_i.lOcclusion != EAXFXSLOT_DEFAULTOCCLUSION) {
  1228. dst_df |= eax_occlusion_dirty_bit;
  1229. dst_i.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
  1230. }
  1231. if(dst_i.flOcclusionLFRatio != EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO) {
  1232. dst_df |= eax_occlusion_lf_ratio_dirty_bit;
  1233. dst_i.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
  1234. }
  1235. }
  1236. void ALeffectslot::eax5_fx_slot_commit(Eax5State& state, EaxDirtyFlags& dst_df)
  1237. {
  1238. eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::guidLoadEffect);
  1239. eax_fx_slot_commit_property<eax_volume_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lVolume);
  1240. eax_fx_slot_commit_property<eax_lock_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lLock);
  1241. eax_fx_slot_commit_property<eax_flags_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::ulFlags);
  1242. eax_fx_slot_commit_property<eax_occlusion_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lOcclusion);
  1243. eax_fx_slot_commit_property<eax_occlusion_lf_ratio_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::flOcclusionLFRatio);
  1244. }
  1245. void ALeffectslot::eax_set_efx_slot_effect(EaxEffect &effect)
  1246. {
  1247. #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
  1248. const auto error = initEffect(0, effect.al_effect_type_, effect.al_effect_props_,
  1249. eax_al_context_);
  1250. if(error != AL_NO_ERROR) {
  1251. ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect.");
  1252. return;
  1253. }
  1254. if(mState == SlotState::Initial) {
  1255. mPropsDirty = false;
  1256. updateProps(eax_al_context_);
  1257. auto effect_slot_ptr = this;
  1258. AddActiveEffectSlots({&effect_slot_ptr, 1}, eax_al_context_);
  1259. mState = SlotState::Playing;
  1260. return;
  1261. }
  1262. mPropsDirty = true;
  1263. #undef EAX_PREFIX
  1264. }
  1265. void ALeffectslot::eax_set_efx_slot_send_auto(bool is_send_auto)
  1266. {
  1267. if(AuxSendAuto == is_send_auto)
  1268. return;
  1269. AuxSendAuto = is_send_auto;
  1270. mPropsDirty = true;
  1271. }
  1272. void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain)
  1273. {
  1274. #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
  1275. if(gain == Gain)
  1276. return;
  1277. if(gain < 0.0f || gain > 1.0f)
  1278. ERR(EAX_PREFIX "Gain out of range (%f)\n", gain);
  1279. Gain = std::clamp(gain, 0.0f, 1.0f);
  1280. mPropsDirty = true;
  1281. #undef EAX_PREFIX
  1282. }
  1283. void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot)
  1284. {
  1285. eax_delete_al_effect_slot(*effect_slot->eax_al_context_, *effect_slot);
  1286. }
  1287. EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context)
  1288. {
  1289. #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
  1290. std::lock_guard<std::mutex> slotlock{context.mEffectSlotLock};
  1291. auto& device = *context.mALDevice;
  1292. if(context.mNumEffectSlots == device.AuxiliaryEffectSlotMax) {
  1293. ERR(EAX_PREFIX "%s\n", "Out of memory.");
  1294. return nullptr;
  1295. }
  1296. if(!EnsureEffectSlots(&context, 1)) {
  1297. ERR(EAX_PREFIX "%s\n", "Failed to ensure.");
  1298. return nullptr;
  1299. }
  1300. return EaxAlEffectSlotUPtr{AllocEffectSlot(&context)};
  1301. #undef EAX_PREFIX
  1302. }
  1303. void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot)
  1304. {
  1305. #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
  1306. std::lock_guard<std::mutex> slotlock{context.mEffectSlotLock};
  1307. if(effect_slot.ref.load(std::memory_order_relaxed) != 0)
  1308. {
  1309. ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id);
  1310. return;
  1311. }
  1312. auto effect_slot_ptr = &effect_slot;
  1313. RemoveActiveEffectSlots({&effect_slot_ptr, 1}, &context);
  1314. FreeEffectSlot(&context, &effect_slot);
  1315. #undef EAX_PREFIX
  1316. }
  1317. #endif // ALSOFT_EAX