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