auxeffectslot.cpp 50 KB

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