Avoid some pointer arithmetic
[openal-soft.git] / al / auxeffectslot.cpp
blobd5a9e6403a90c3041e9913288b6e21813c869879
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.
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.
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
21 #include "config.h"
23 #include "auxeffectslot.h"
25 #include <algorithm>
26 #include <cassert>
27 #include <cstddef>
28 #include <cstdint>
29 #include <functional>
30 #include <iterator>
31 #include <memory>
32 #include <mutex>
33 #include <numeric>
34 #include <stdexcept>
35 #include <string>
36 #include <tuple>
37 #include <unordered_map>
38 #include <vector>
40 #include "AL/al.h"
41 #include "AL/alc.h"
42 #include "AL/alext.h"
43 #include "AL/efx.h"
45 #include "albit.h"
46 #include "alc/alu.h"
47 #include "alc/context.h"
48 #include "alc/device.h"
49 #include "alc/effects/base.h"
50 #include "alc/inprogext.h"
51 #include "almalloc.h"
52 #include "alnumeric.h"
53 #include "alspan.h"
54 #include "atomic.h"
55 #include "buffer.h"
56 #include "core/buffer_storage.h"
57 #include "core/device.h"
58 #include "core/fpu_ctrl.h"
59 #include "core/logging.h"
60 #include "direct_defs.h"
61 #include "effects/effects.h"
62 #include "effect.h"
63 #include "flexarray.h"
64 #include "opthelpers.h"
66 #ifdef ALSOFT_EAX
67 #include "eax/api.h"
68 #include "eax/call.h"
69 #include "eax/effect.h"
70 #include "eax/fx_slot_index.h"
71 #include "eax/utils.h"
72 #endif
74 namespace {
76 using SubListAllocator = al::allocator<std::array<ALeffectslot,64>>;
78 EffectStateFactory *getFactoryByType(EffectSlotType type)
80 switch(type)
82 case EffectSlotType::None: return NullStateFactory_getFactory();
83 case EffectSlotType::EAXReverb: return ReverbStateFactory_getFactory();
84 case EffectSlotType::Reverb: return StdReverbStateFactory_getFactory();
85 case EffectSlotType::Autowah: return AutowahStateFactory_getFactory();
86 case EffectSlotType::Chorus: return ChorusStateFactory_getFactory();
87 case EffectSlotType::Compressor: return CompressorStateFactory_getFactory();
88 case EffectSlotType::Distortion: return DistortionStateFactory_getFactory();
89 case EffectSlotType::Echo: return EchoStateFactory_getFactory();
90 case EffectSlotType::Equalizer: return EqualizerStateFactory_getFactory();
91 case EffectSlotType::Flanger: return FlangerStateFactory_getFactory();
92 case EffectSlotType::FrequencyShifter: return FshifterStateFactory_getFactory();
93 case EffectSlotType::RingModulator: return ModulatorStateFactory_getFactory();
94 case EffectSlotType::PitchShifter: return PshifterStateFactory_getFactory();
95 case EffectSlotType::VocalMorpher: return VmorpherStateFactory_getFactory();
96 case EffectSlotType::DedicatedDialog: return DedicatedDialogStateFactory_getFactory();
97 case EffectSlotType::DedicatedLFE: return DedicatedLfeStateFactory_getFactory();
98 case EffectSlotType::Convolution: return ConvolutionStateFactory_getFactory();
100 return nullptr;
104 inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
106 const size_t lidx{(id-1) >> 6};
107 const ALuint slidx{(id-1) & 0x3f};
109 if(lidx >= context->mEffectSlotList.size()) UNLIKELY
110 return nullptr;
111 EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
112 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
113 return nullptr;
114 return al::to_address(sublist.EffectSlots->begin() + slidx);
117 inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept
119 const size_t lidx{(id-1) >> 6};
120 const ALuint slidx{(id-1) & 0x3f};
122 if(lidx >= device->EffectList.size()) UNLIKELY
123 return nullptr;
124 EffectSubList &sublist = device->EffectList[lidx];
125 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
126 return nullptr;
127 return al::to_address(sublist.Effects->begin() + slidx);
130 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
132 const size_t lidx{(id-1) >> 6};
133 const ALuint slidx{(id-1) & 0x3f};
135 if(lidx >= device->BufferList.size()) UNLIKELY
136 return nullptr;
137 BufferSubList &sublist = device->BufferList[lidx];
138 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
139 return nullptr;
140 return al::to_address(sublist.Buffers->begin() + slidx);
144 void AddActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
146 if(auxslots.empty()) return;
147 EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
148 size_t newcount{curarray->size() + auxslots.size()};
150 /* Insert the new effect slots into the head of the array, followed by the
151 * existing ones.
153 auto newarray = EffectSlot::CreatePtrArray(newcount);
154 auto slotiter = std::transform(auxslots.begin(), auxslots.end(), newarray->begin(),
155 std::mem_fn(&ALeffectslot::mSlot));
156 std::copy(curarray->begin(), curarray->end(), slotiter);
158 /* Remove any duplicates (first instance of each will be kept). */
159 auto last = newarray->end();
160 for(auto start=newarray->begin()+1;;)
162 last = std::remove(start, last, *(start-1));
163 if(start == last) break;
164 ++start;
166 newcount = static_cast<size_t>(std::distance(newarray->begin(), last));
168 /* Reallocate newarray if the new size ended up smaller from duplicate
169 * removal.
171 if(newcount < newarray->size()) UNLIKELY
173 auto oldarray = std::move(newarray);
174 newarray = EffectSlot::CreatePtrArray(newcount);
175 std::copy_n(oldarray->begin(), newcount, newarray->begin());
177 std::uninitialized_fill_n(newarray->end(), newcount, nullptr);
179 auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray),
180 std::memory_order_acq_rel);
181 std::ignore = context->mDevice->waitForMix();
183 std::destroy_n(oldarray->end(), oldarray->size());
186 void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
188 if(auxslots.empty()) return;
189 EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
191 /* Don't shrink the allocated array size since we don't know how many (if
192 * any) of the effect slots to remove are in the array.
194 auto newarray = EffectSlot::CreatePtrArray(curarray->size());
196 auto new_end = std::copy(curarray->begin(), curarray->end(), newarray->begin());
197 /* Remove elements from newarray that match any ID in slotids. */
198 for(const ALeffectslot *auxslot : auxslots)
200 auto slot_match = [auxslot](EffectSlot *slot) noexcept -> bool
201 { return (slot == auxslot->mSlot); };
202 new_end = std::remove_if(newarray->begin(), new_end, slot_match);
205 /* Reallocate with the new size. */
206 auto newsize = static_cast<size_t>(std::distance(newarray->begin(), new_end));
207 if(newsize != newarray->size()) LIKELY
209 auto oldarray = std::move(newarray);
210 newarray = EffectSlot::CreatePtrArray(newsize);
211 std::copy_n(oldarray->begin(), newsize, newarray->begin());
213 std::uninitialized_fill_n(newarray->end(), newsize, nullptr);
215 auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray),
216 std::memory_order_acq_rel);
217 std::ignore = context->mDevice->waitForMix();
219 std::destroy_n(curarray->end(), curarray->size());
223 EffectSlotType EffectSlotTypeFromEnum(ALenum type)
225 switch(type)
227 case AL_EFFECT_NULL: return EffectSlotType::None;
228 case AL_EFFECT_REVERB: return EffectSlotType::Reverb;
229 case AL_EFFECT_CHORUS: return EffectSlotType::Chorus;
230 case AL_EFFECT_DISTORTION: return EffectSlotType::Distortion;
231 case AL_EFFECT_ECHO: return EffectSlotType::Echo;
232 case AL_EFFECT_FLANGER: return EffectSlotType::Flanger;
233 case AL_EFFECT_FREQUENCY_SHIFTER: return EffectSlotType::FrequencyShifter;
234 case AL_EFFECT_VOCAL_MORPHER: return EffectSlotType::VocalMorpher;
235 case AL_EFFECT_PITCH_SHIFTER: return EffectSlotType::PitchShifter;
236 case AL_EFFECT_RING_MODULATOR: return EffectSlotType::RingModulator;
237 case AL_EFFECT_AUTOWAH: return EffectSlotType::Autowah;
238 case AL_EFFECT_COMPRESSOR: return EffectSlotType::Compressor;
239 case AL_EFFECT_EQUALIZER: return EffectSlotType::Equalizer;
240 case AL_EFFECT_EAXREVERB: return EffectSlotType::EAXReverb;
241 case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::DedicatedLFE;
242 case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::DedicatedDialog;
243 case AL_EFFECT_CONVOLUTION_SOFT: return EffectSlotType::Convolution;
245 ERR("Unhandled effect enum: 0x%04x\n", type);
246 return EffectSlotType::None;
249 bool EnsureEffectSlots(ALCcontext *context, size_t needed)
251 size_t count{std::accumulate(context->mEffectSlotList.cbegin(),
252 context->mEffectSlotList.cend(), 0_uz,
253 [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
254 { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
256 try {
257 while(needed > count)
259 if(context->mEffectSlotList.size() >= 1<<25) UNLIKELY
260 return false;
262 EffectSlotSubList sublist{};
263 sublist.FreeMask = ~0_u64;
264 sublist.EffectSlots = SubListAllocator{}.allocate(1);
265 context->mEffectSlotList.emplace_back(std::move(sublist));
266 count += std::tuple_size_v<SubListAllocator::value_type>;
269 catch(...) {
270 return false;
272 return true;
275 ALeffectslot *AllocEffectSlot(ALCcontext *context)
277 auto sublist = std::find_if(context->mEffectSlotList.begin(), context->mEffectSlotList.end(),
278 [](const EffectSlotSubList &entry) noexcept -> bool
279 { return entry.FreeMask != 0; });
280 auto lidx = static_cast<ALuint>(std::distance(context->mEffectSlotList.begin(), sublist));
281 auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
282 ASSUME(slidx < 64);
284 ALeffectslot *slot{al::construct_at(al::to_address(sublist->EffectSlots->begin() + slidx),
285 context)};
286 aluInitEffectPanning(slot->mSlot, context);
288 /* Add 1 to avoid ID 0. */
289 slot->id = ((lidx<<6) | slidx) + 1;
291 context->mNumEffectSlots += 1;
292 sublist->FreeMask &= ~(1_u64 << slidx);
294 return slot;
297 void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
299 context->mEffectSlotNames.erase(slot->id);
301 const ALuint id{slot->id - 1};
302 const size_t lidx{id >> 6};
303 const ALuint slidx{id & 0x3f};
305 std::destroy_at(slot);
307 context->mEffectSlotList[lidx].FreeMask |= 1_u64 << slidx;
308 context->mNumEffectSlots--;
312 inline void UpdateProps(ALeffectslot *slot, ALCcontext *context)
314 if(!context->mDeferUpdates && slot->mState == SlotState::Playing)
316 slot->updateProps(context);
317 return;
319 slot->mPropsDirty = true;
322 } // namespace
325 AL_API DECL_FUNC2(void, alGenAuxiliaryEffectSlots, ALsizei,n, ALuint*,effectslots)
326 FORCE_ALIGN void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n,
327 ALuint *effectslots) noexcept
329 if(n < 0) UNLIKELY
330 context->setError(AL_INVALID_VALUE, "Generating %d effect slots", n);
331 if(n <= 0) UNLIKELY return;
333 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
334 ALCdevice *device{context->mALDevice.get()};
335 if(static_cast<ALuint>(n) > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
337 context->setError(AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)",
338 device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n);
339 return;
341 if(!EnsureEffectSlots(context, static_cast<ALuint>(n)))
343 context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n,
344 (n==1) ? "" : "s");
345 return;
348 std::vector<ALeffectslot*> slots;
349 try {
350 if(n == 1)
352 /* Special handling for the easy and normal case. */
353 *effectslots = AllocEffectSlot(context)->id;
355 else
357 slots.reserve(static_cast<ALuint>(n));
358 std::generate_n(std::back_inserter(slots), static_cast<ALuint>(n),
359 [context]{ return AllocEffectSlot(context); });
361 const al::span eids{effectslots, static_cast<ALuint>(n)};
362 std::transform(slots.cbegin(), slots.cend(), eids.begin(),
363 [](ALeffectslot *slot) -> ALuint { return slot->id; });
366 catch(std::exception& e) {
367 ERR("Exception allocating effectslot %zu of %d: %s\n", slots.size()+1, n, e.what());
368 auto delete_effectslot = [context](ALeffectslot *slot) -> void
369 { FreeEffectSlot(context, slot); };
370 std::for_each(slots.begin(), slots.end(), delete_effectslot);
371 context->setError(AL_INVALID_OPERATION, "Exception allocating %d effectslots: %s", n,
372 e.what());
376 AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei,n, const ALuint*,effectslots)
377 FORCE_ALIGN void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n,
378 const ALuint *effectslots) noexcept
380 if(n < 0) UNLIKELY
381 context->setError(AL_INVALID_VALUE, "Deleting %d effect slots", n);
382 if(n <= 0) UNLIKELY return;
384 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
385 if(n == 1)
387 ALeffectslot *slot{LookupEffectSlot(context, *effectslots)};
388 if(!slot) UNLIKELY
390 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", *effectslots);
391 return;
393 if(slot->ref.load(std::memory_order_relaxed) != 0) UNLIKELY
395 context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
396 *effectslots);
397 return;
399 RemoveActiveEffectSlots({&slot, 1u}, context);
400 FreeEffectSlot(context, slot);
402 else
404 const al::span eids{effectslots, static_cast<ALuint>(n)};
405 auto slots = std::vector<ALeffectslot*>(static_cast<ALuint>(n));
406 for(size_t i{0};i < slots.size();++i)
408 ALeffectslot *slot{LookupEffectSlot(context, eids[i])};
409 if(!slot) UNLIKELY
411 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", eids[i]);
412 return;
414 if(slot->ref.load(std::memory_order_relaxed) != 0) UNLIKELY
416 context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u", eids[i]);
417 return;
419 slots[i] = slot;
421 /* All effectslots are valid, remove and delete them */
422 RemoveActiveEffectSlots(slots, context);
424 auto delete_effectslot = [context](const ALuint eid) -> void
426 if(ALeffectslot *slot{LookupEffectSlot(context, eid)})
427 FreeEffectSlot(context, slot);
429 std::for_each(eids.begin(), eids.end(), delete_effectslot);
433 AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint,effectslot)
434 FORCE_ALIGN ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context,
435 ALuint effectslot) noexcept
437 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
438 if(LookupEffectSlot(context, effectslot) != nullptr)
439 return AL_TRUE;
440 return AL_FALSE;
444 AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) noexcept
446 ContextRef context{GetContextRef()};
447 if(!context) UNLIKELY return;
449 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
450 ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
451 if(!slot) UNLIKELY
453 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
454 return;
456 if(slot->mState == SlotState::Playing)
457 return;
459 slot->mPropsDirty = false;
460 slot->updateProps(context.get());
462 AddActiveEffectSlots({&slot, 1}, context.get());
463 slot->mState = SlotState::Playing;
466 AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) noexcept
468 ContextRef context{GetContextRef()};
469 if(!context) UNLIKELY return;
471 if(n < 0) UNLIKELY
472 context->setError(AL_INVALID_VALUE, "Playing %d effect slots", n);
473 if(n <= 0) UNLIKELY return;
475 auto ids = al::span{slotids, static_cast<ALuint>(n)};
476 auto slots = std::vector<ALeffectslot*>(ids.size());
477 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
478 try {
479 auto lookupslot = [&context](const ALuint id) -> ALeffectslot*
481 ALeffectslot *slot{LookupEffectSlot(context.get(), id)};
482 if(!slot)
483 throw effect_exception{AL_INVALID_NAME, "Invalid effect slot ID %u", id};
485 if(slot->mState != SlotState::Playing)
487 slot->mPropsDirty = false;
488 slot->updateProps(context.get());
490 return slot;
492 std::transform(ids.cbegin(), ids.cend(), slots.begin(), lookupslot);
494 catch(effect_exception& e) {
495 context->setError(e.errorCode(), "%s", e.what());
496 return;
499 AddActiveEffectSlots(slots, context.get());
500 for(auto slot : slots)
501 slot->mState = SlotState::Playing;
504 AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) noexcept
506 ContextRef context{GetContextRef()};
507 if(!context) UNLIKELY return;
509 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
510 ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
511 if(!slot) UNLIKELY
513 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
514 return;
517 RemoveActiveEffectSlots({&slot, 1}, context.get());
518 slot->mState = SlotState::Stopped;
521 AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) noexcept
523 ContextRef context{GetContextRef()};
524 if(!context) UNLIKELY return;
526 if(n < 0) UNLIKELY
527 context->setError(AL_INVALID_VALUE, "Stopping %d effect slots", n);
528 if(n <= 0) UNLIKELY return;
530 auto ids = al::span{slotids, static_cast<ALuint>(n)};
531 auto slots = std::vector<ALeffectslot*>(ids.size());
532 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
533 try {
534 auto lookupslot = [&context](const ALuint id) -> ALeffectslot*
536 if(ALeffectslot *slot{LookupEffectSlot(context.get(), id)})
537 return slot;
538 throw effect_exception{AL_INVALID_NAME, "Invalid effect slot ID %u", id};
540 std::transform(ids.cbegin(), ids.cend(), slots.begin(), lookupslot);
542 catch(effect_exception& e) {
543 context->setError(e.errorCode(), "%s", e.what());
544 return;
547 RemoveActiveEffectSlots(slots, context.get());
548 for(auto slot : slots)
549 slot->mState = SlotState::Stopped;
553 AL_API DECL_FUNC3(void, alAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint,value)
554 FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot,
555 ALenum param, ALint value) noexcept
557 std::lock_guard<std::mutex> proplock{context->mPropLock};
558 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
559 ALeffectslot *slot = LookupEffectSlot(context, effectslot);
560 if(!slot) UNLIKELY
561 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
563 ALeffectslot *target{};
564 ALCdevice *device{};
565 ALenum err{};
566 switch(param)
568 case AL_EFFECTSLOT_EFFECT:
569 device = context->mALDevice.get();
572 std::lock_guard<std::mutex> effectlock{device->EffectLock};
573 ALeffect *effect{value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr};
574 if(effect)
575 err = slot->initEffect(effect->id, effect->type, effect->Props, context);
576 else
578 if(value != 0)
579 return context->setError(AL_INVALID_VALUE, "Invalid effect ID %u", value);
580 err = slot->initEffect(0, AL_EFFECT_NULL, EffectProps{}, context);
583 if(err != AL_NO_ERROR) UNLIKELY
585 context->setError(err, "Effect initialization failed");
586 return;
588 if(slot->mState == SlotState::Initial) UNLIKELY
590 slot->mPropsDirty = false;
591 slot->updateProps(context);
593 AddActiveEffectSlots({&slot, 1}, context);
594 slot->mState = SlotState::Playing;
595 return;
597 break;
599 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
600 if(!(value == AL_TRUE || value == AL_FALSE))
601 return context->setError(AL_INVALID_VALUE,
602 "Effect slot auxiliary send auto out of range");
603 if(slot->AuxSendAuto == !!value) UNLIKELY
604 return;
605 slot->AuxSendAuto = !!value;
606 break;
608 case AL_EFFECTSLOT_TARGET_SOFT:
609 target = LookupEffectSlot(context, static_cast<ALuint>(value));
610 if(value && !target)
611 return context->setError(AL_INVALID_VALUE, "Invalid effect slot target ID");
612 if(slot->Target == target) UNLIKELY
613 return;
614 if(target)
616 ALeffectslot *checker{target};
617 while(checker && checker != slot)
618 checker = checker->Target;
619 if(checker)
620 return context->setError(AL_INVALID_OPERATION,
621 "Setting target of effect slot ID %u to %u creates circular chain", slot->id,
622 target->id);
625 if(ALeffectslot *oldtarget{slot->Target})
627 /* We must force an update if there was an existing effect slot
628 * target, in case it's about to be deleted.
630 if(target) IncrementRef(target->ref);
631 DecrementRef(oldtarget->ref);
632 slot->Target = target;
633 slot->updateProps(context);
634 return;
637 if(target) IncrementRef(target->ref);
638 slot->Target = target;
639 break;
641 case AL_BUFFER:
642 device = context->mALDevice.get();
644 if(slot->mState == SlotState::Playing)
645 return context->setError(AL_INVALID_OPERATION,
646 "Setting buffer on playing effect slot %u", slot->id);
648 if(ALbuffer *buffer{slot->Buffer})
650 if(buffer->id == static_cast<ALuint>(value)) UNLIKELY
651 return;
653 else if(value == 0) UNLIKELY
654 return;
657 std::lock_guard<std::mutex> bufferlock{device->BufferLock};
658 ALbuffer *buffer{};
659 if(value)
661 buffer = LookupBuffer(device, static_cast<ALuint>(value));
662 if(!buffer) return context->setError(AL_INVALID_VALUE, "Invalid buffer ID");
663 if(buffer->mCallback)
664 return context->setError(AL_INVALID_OPERATION,
665 "Callback buffer not valid for effects");
667 IncrementRef(buffer->ref);
670 if(ALbuffer *oldbuffer{slot->Buffer})
671 DecrementRef(oldbuffer->ref);
672 slot->Buffer = buffer;
674 FPUCtl mixer_mode{};
675 auto *state = slot->Effect.State.get();
676 state->deviceUpdate(device, buffer);
678 break;
680 case AL_EFFECTSLOT_STATE_SOFT:
681 return context->setError(AL_INVALID_OPERATION, "AL_EFFECTSLOT_STATE_SOFT is read-only");
683 default:
684 return context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x",
685 param);
687 UpdateProps(slot, context);
690 AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, const ALint*,values)
691 FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot,
692 ALenum param, const ALint *values) noexcept
694 switch(param)
696 case AL_EFFECTSLOT_EFFECT:
697 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
698 case AL_EFFECTSLOT_TARGET_SOFT:
699 case AL_EFFECTSLOT_STATE_SOFT:
700 case AL_BUFFER:
701 alAuxiliaryEffectSlotiDirect(context, effectslot, param, *values);
702 return;
705 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
706 ALeffectslot *slot = LookupEffectSlot(context, effectslot);
707 if(!slot) UNLIKELY
708 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
710 switch(param)
712 default:
713 return context->setError(AL_INVALID_ENUM,
714 "Invalid effect slot integer-vector property 0x%04x", param);
718 AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat,value)
719 FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot,
720 ALenum param, ALfloat value) noexcept
722 std::lock_guard<std::mutex> proplock{context->mPropLock};
723 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
724 ALeffectslot *slot = LookupEffectSlot(context, effectslot);
725 if(!slot) UNLIKELY
726 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
728 switch(param)
730 case AL_EFFECTSLOT_GAIN:
731 if(!(value >= 0.0f && value <= 1.0f))
732 return context->setError(AL_INVALID_VALUE, "Effect slot gain out of range");
733 if(slot->Gain == value) UNLIKELY
734 return;
735 slot->Gain = value;
736 break;
738 default:
739 return context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x",
740 param);
742 UpdateProps(slot, context);
745 AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, const ALfloat*,values)
746 FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot,
747 ALenum param, const ALfloat *values) noexcept
749 switch(param)
751 case AL_EFFECTSLOT_GAIN:
752 alAuxiliaryEffectSlotfDirect(context, effectslot, param, *values);
753 return;
756 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
757 ALeffectslot *slot = LookupEffectSlot(context, effectslot);
758 if(!slot) UNLIKELY
759 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
761 switch(param)
763 default:
764 return context->setError(AL_INVALID_ENUM,
765 "Invalid effect slot float-vector property 0x%04x", param);
770 AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint*,value)
771 FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context,
772 ALuint effectslot, ALenum param, ALint *value) noexcept
774 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
775 ALeffectslot *slot = LookupEffectSlot(context, effectslot);
776 if(!slot) UNLIKELY
777 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
779 switch(param)
781 case AL_EFFECTSLOT_EFFECT:
782 *value = static_cast<ALint>(slot->EffectId);
783 break;
785 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
786 *value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE;
787 break;
789 case AL_EFFECTSLOT_TARGET_SOFT:
790 if(auto *target = slot->Target)
791 *value = static_cast<ALint>(target->id);
792 else
793 *value = 0;
794 break;
796 case AL_EFFECTSLOT_STATE_SOFT:
797 *value = static_cast<int>(slot->mState);
798 break;
800 case AL_BUFFER:
801 if(auto *buffer = slot->Buffer)
802 *value = static_cast<ALint>(buffer->id);
803 else
804 *value = 0;
805 break;
807 default:
808 context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param);
812 AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, ALint*,values)
813 FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context,
814 ALuint effectslot, ALenum param, ALint *values) noexcept
816 switch(param)
818 case AL_EFFECTSLOT_EFFECT:
819 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
820 case AL_EFFECTSLOT_TARGET_SOFT:
821 case AL_EFFECTSLOT_STATE_SOFT:
822 case AL_BUFFER:
823 alGetAuxiliaryEffectSlotiDirect(context, effectslot, param, values);
824 return;
827 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
828 ALeffectslot *slot = LookupEffectSlot(context, effectslot);
829 if(!slot) UNLIKELY
830 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
832 switch(param)
834 default:
835 context->setError(AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
836 param);
840 AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat*,value)
841 FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context,
842 ALuint effectslot, ALenum param, ALfloat *value) noexcept
844 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
845 ALeffectslot *slot = LookupEffectSlot(context, effectslot);
846 if(!slot) UNLIKELY
847 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
849 switch(param)
851 case AL_EFFECTSLOT_GAIN:
852 *value = slot->Gain;
853 break;
855 default:
856 context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param);
860 AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, ALfloat*,values)
861 FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context,
862 ALuint effectslot, ALenum param, ALfloat *values) noexcept
864 switch(param)
866 case AL_EFFECTSLOT_GAIN:
867 alGetAuxiliaryEffectSlotfDirect(context, effectslot, param, values);
868 return;
871 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
872 ALeffectslot *slot = LookupEffectSlot(context, effectslot);
873 if(!slot) UNLIKELY
874 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
876 switch(param)
878 default:
879 context->setError(AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
880 param);
885 ALeffectslot::ALeffectslot(ALCcontext *context)
887 EffectStateFactory *factory{getFactoryByType(EffectSlotType::None)};
888 if(!factory) throw std::runtime_error{"Failed to get null effect factory"};
890 al::intrusive_ptr<EffectState> state{factory->create()};
891 Effect.State = state;
893 mSlot = context->getEffectSlot();
894 mSlot->InUse = true;
895 mSlot->mEffectState = std::move(state);
898 ALeffectslot::~ALeffectslot()
900 if(Target)
901 DecrementRef(Target->ref);
902 Target = nullptr;
903 if(Buffer)
904 DecrementRef(Buffer->ref);
905 Buffer = nullptr;
907 if(auto *slot = mSlot->Update.exchange(nullptr, std::memory_order_relaxed))
908 slot->State = nullptr;
910 mSlot->mEffectState = nullptr;
911 mSlot->InUse = false;
914 ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
915 ALCcontext *context)
917 EffectSlotType newtype{EffectSlotTypeFromEnum(effectType)};
918 if(newtype != Effect.Type)
920 EffectStateFactory *factory{getFactoryByType(newtype)};
921 if(!factory)
923 ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype));
924 return AL_INVALID_ENUM;
926 al::intrusive_ptr<EffectState> state{factory->create()};
928 ALCdevice *device{context->mALDevice.get()};
929 std::unique_lock<std::mutex> statelock{device->StateLock};
930 state->mOutTarget = device->Dry.Buffer;
932 FPUCtl mixer_mode{};
933 state->deviceUpdate(device, Buffer);
936 Effect.Type = newtype;
937 Effect.Props = effectProps;
939 Effect.State = std::move(state);
941 else if(newtype != EffectSlotType::None)
942 Effect.Props = effectProps;
943 EffectId = effectId;
945 /* Remove state references from old effect slot property updates. */
946 EffectSlotProps *props{context->mFreeEffectSlotProps.load()};
947 while(props)
949 props->State = nullptr;
950 props = props->next.load(std::memory_order_relaxed);
953 return AL_NO_ERROR;
956 void ALeffectslot::updateProps(ALCcontext *context) const
958 /* Get an unused property container, or allocate a new one as needed. */
959 EffectSlotProps *props{context->mFreeEffectSlotProps.load(std::memory_order_acquire)};
960 if(!props)
962 context->allocEffectSlotProps();
963 props = context->mFreeEffectSlotProps.load(std::memory_order_acquire);
965 EffectSlotProps *next;
966 do {
967 next = props->next.load(std::memory_order_relaxed);
968 } while(!context->mFreeEffectSlotProps.compare_exchange_weak(props, next,
969 std::memory_order_acq_rel, std::memory_order_acquire));
971 /* Copy in current property values. */
972 props->Gain = Gain;
973 props->AuxSendAuto = AuxSendAuto;
974 props->Target = Target ? Target->mSlot : nullptr;
976 props->Type = Effect.Type;
977 props->Props = Effect.Props;
978 props->State = Effect.State;
980 /* Set the new container for updating internal parameters. */
981 props = mSlot->Update.exchange(props, std::memory_order_acq_rel);
982 if(props)
984 /* If there was an unused update container, put it back in the
985 * freelist.
987 props->State = nullptr;
988 AtomicReplaceHead(context->mFreeEffectSlotProps, props);
992 void ALeffectslot::SetName(ALCcontext* context, ALuint id, std::string_view name)
994 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
996 auto slot = LookupEffectSlot(context, id);
997 if(!slot) UNLIKELY
998 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", id);
1000 context->mEffectSlotNames.insert_or_assign(id, name);
1003 void UpdateAllEffectSlotProps(ALCcontext *context)
1005 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
1006 for(auto &sublist : context->mEffectSlotList)
1008 uint64_t usemask{~sublist.FreeMask};
1009 while(usemask)
1011 const auto idx = static_cast<uint>(al::countr_zero(usemask));
1012 usemask &= ~(1_u64 << idx);
1013 auto &slot = (*sublist.EffectSlots)[idx];
1015 if(slot.mState != SlotState::Stopped && std::exchange(slot.mPropsDirty, false))
1016 slot.updateProps(context);
1021 EffectSlotSubList::~EffectSlotSubList()
1023 if(!EffectSlots)
1024 return;
1026 uint64_t usemask{~FreeMask};
1027 while(usemask)
1029 const int idx{al::countr_zero(usemask)};
1030 std::destroy_at(al::to_address(EffectSlots->begin() + idx));
1031 usemask &= ~(1_u64 << idx);
1033 FreeMask = ~usemask;
1034 SubListAllocator{}.deallocate(EffectSlots, 1);
1035 EffectSlots = nullptr;
1038 #ifdef ALSOFT_EAX
1039 void ALeffectslot::eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index)
1041 if(index >= EAX_MAX_FXSLOTS)
1042 eax_fail("Index out of range.");
1044 eax_al_context_ = &al_context;
1045 eax_fx_slot_index_ = index;
1046 eax_fx_slot_set_defaults();
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>();
1054 void ALeffectslot::eax_commit()
1056 if(eax_df_ != EaxDirtyFlags{})
1058 auto df = EaxDirtyFlags{};
1059 switch(eax_version_)
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;
1073 eax_df_ = EaxDirtyFlags{};
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();
1081 if(eax_effect_->commit(eax_version_))
1082 eax_set_efx_slot_effect(*eax_effect_);
1085 [[noreturn]] void ALeffectslot::eax_fail(const char* message)
1087 throw Exception{message};
1090 [[noreturn]] void ALeffectslot::eax_fail_unknown_effect_id()
1092 eax_fail("Unknown effect ID.");
1095 [[noreturn]] void ALeffectslot::eax_fail_unknown_property_id()
1097 eax_fail("Unknown property ID.");
1100 [[noreturn]] void ALeffectslot::eax_fail_unknown_version()
1102 eax_fail("Unknown version.");
1105 void ALeffectslot::eax4_fx_slot_ensure_unlocked() const
1107 if(eax4_fx_slot_is_legacy())
1108 eax_fail("Locked legacy slot.");
1111 ALenum ALeffectslot::eax_get_efx_effect_type(const GUID& guid)
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;
1140 eax_fail_unknown_effect_id();
1143 const GUID& ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
1145 switch(eax_fx_slot_index_)
1147 case 0: return EAX_REVERB_EFFECT;
1148 case 1: return EAX_CHORUS_EFFECT;
1149 default: return EAX_NULL_GUID;
1153 long ALeffectslot::eax_get_eax_default_lock() const noexcept
1155 return eax4_fx_slot_is_legacy() ? EAXFXSLOT_LOCKED : EAXFXSLOT_UNLOCKED;
1158 void ALeffectslot::eax4_fx_slot_set_defaults(Eax4Props& props) noexcept
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;
1166 void ALeffectslot::eax5_fx_slot_set_defaults(Eax5Props& props) noexcept
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;
1176 void ALeffectslot::eax_fx_slot_set_defaults()
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{};
1185 void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props)
1187 switch(call.get_property_id())
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();
1209 void ALeffectslot::eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props)
1211 switch(call.get_property_id())
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();
1239 void ALeffectslot::eax_fx_slot_get(const EaxCall& call) const
1241 switch(call.get_version())
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();
1249 bool ALeffectslot::eax_get(const EaxCall& call)
1251 switch(call.get_property_set_id())
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();
1263 return false;
1266 void ALeffectslot::eax_fx_slot_load_effect(int version, ALenum altype)
1268 if(!IsValidEffectType(altype))
1269 altype = AL_EFFECT_NULL;
1270 eax_effect_->set_defaults(version, altype);
1273 void ALeffectslot::eax_fx_slot_set_volume()
1275 const auto volume = std::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);
1280 void ALeffectslot::eax_fx_slot_set_environment_flag()
1282 eax_set_efx_slot_send_auto((eax_.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0u);
1285 void ALeffectslot::eax_fx_slot_set_flags()
1287 eax_fx_slot_set_environment_flag();
1290 void ALeffectslot::eax4_fx_slot_set_all(const EaxCall& call)
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;
1303 void ALeffectslot::eax5_fx_slot_set_all(const EaxCall& call)
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;
1317 bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept
1319 static constexpr auto dirty_bits =
1320 eax_occlusion_dirty_bit |
1321 eax_occlusion_lf_ratio_dirty_bit |
1322 eax_flags_dirty_bit;
1324 return (eax_df_ & dirty_bits) != EaxDirtyFlags{};
1327 // Returns `true` if all sources should be updated, or `false` otherwise.
1328 bool ALeffectslot::eax4_fx_slot_set(const EaxCall& call)
1330 auto& dst = eax4_.i;
1332 switch(call.get_property_id())
1334 case EAXFXSLOT_NONE:
1335 break;
1336 case EAXFXSLOT_ALLPARAMETERS:
1337 eax4_fx_slot_set_all(call);
1338 if((eax_df_ & eax_load_effect_dirty_bit))
1339 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
1340 break;
1341 case EAXFXSLOT_LOADEFFECT:
1342 eax4_fx_slot_ensure_unlocked();
1343 eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call, dst.guidLoadEffect, eax_df_);
1344 if((eax_df_ & eax_load_effect_dirty_bit))
1345 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
1346 break;
1347 case EAXFXSLOT_VOLUME:
1348 eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, eax_df_);
1349 break;
1350 case EAXFXSLOT_LOCK:
1351 eax4_fx_slot_ensure_unlocked();
1352 eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, eax_df_);
1353 break;
1354 case EAXFXSLOT_FLAGS:
1355 eax_fx_slot_set<Eax4FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, eax_df_);
1356 break;
1357 default:
1358 eax_fail_unknown_property_id();
1361 return eax_fx_slot_should_update_sources();
1364 // Returns `true` if all sources should be updated, or `false` otherwise.
1365 bool ALeffectslot::eax5_fx_slot_set(const EaxCall& call)
1367 auto& dst = eax5_.i;
1369 switch(call.get_property_id())
1371 case EAXFXSLOT_NONE:
1372 break;
1373 case EAXFXSLOT_ALLPARAMETERS:
1374 eax5_fx_slot_set_all(call);
1375 if((eax_df_ & eax_load_effect_dirty_bit))
1376 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
1377 break;
1378 case EAXFXSLOT_LOADEFFECT:
1379 eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call, dst.guidLoadEffect, eax_df_);
1380 if((eax_df_ & eax_load_effect_dirty_bit))
1381 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
1382 break;
1383 case EAXFXSLOT_VOLUME:
1384 eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, eax_df_);
1385 break;
1386 case EAXFXSLOT_LOCK:
1387 eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, eax_df_);
1388 break;
1389 case EAXFXSLOT_FLAGS:
1390 eax_fx_slot_set<Eax5FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, eax_df_);
1391 break;
1392 case EAXFXSLOT_OCCLUSION:
1393 eax_fx_slot_set<Eax5OcclusionValidator, eax_occlusion_dirty_bit>(call, dst.lOcclusion, eax_df_);
1394 break;
1395 case EAXFXSLOT_OCCLUSIONLFRATIO:
1396 eax_fx_slot_set<Eax5OcclusionLfRatioValidator, eax_occlusion_lf_ratio_dirty_bit>(call, dst.flOcclusionLFRatio, eax_df_);
1397 break;
1398 default:
1399 eax_fail_unknown_property_id();
1402 return eax_fx_slot_should_update_sources();
1405 // Returns `true` if all sources should be updated, or `false` otherwise.
1406 bool ALeffectslot::eax_fx_slot_set(const EaxCall& call)
1408 switch (call.get_version())
1410 case 4: return eax4_fx_slot_set(call);
1411 case 5: return eax5_fx_slot_set(call);
1412 default: eax_fail_unknown_version();
1416 // Returns `true` if all sources should be updated, or `false` otherwise.
1417 bool ALeffectslot::eax_set(const EaxCall& call)
1419 bool ret{false};
1421 switch(call.get_property_set_id())
1423 case EaxCallPropertySetId::fx_slot: ret = eax_fx_slot_set(call); break;
1424 case EaxCallPropertySetId::fx_slot_effect: eax_effect_->set(call); break;
1425 default: eax_fail_unknown_property_id();
1428 const auto version = call.get_version();
1429 if(eax_version_ != version)
1430 eax_df_ = ~EaxDirtyFlags{};
1431 eax_version_ = version;
1433 return ret;
1436 void ALeffectslot::eax4_fx_slot_commit(EaxDirtyFlags& dst_df)
1438 eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::guidLoadEffect);
1439 eax_fx_slot_commit_property<eax_volume_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::lVolume);
1440 eax_fx_slot_commit_property<eax_lock_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::lLock);
1441 eax_fx_slot_commit_property<eax_flags_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::ulFlags);
1443 auto& dst_i = eax_;
1445 if(dst_i.lOcclusion != EAXFXSLOT_DEFAULTOCCLUSION) {
1446 dst_df |= eax_occlusion_dirty_bit;
1447 dst_i.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
1450 if(dst_i.flOcclusionLFRatio != EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO) {
1451 dst_df |= eax_occlusion_lf_ratio_dirty_bit;
1452 dst_i.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
1456 void ALeffectslot::eax5_fx_slot_commit(Eax5State& state, EaxDirtyFlags& dst_df)
1458 eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::guidLoadEffect);
1459 eax_fx_slot_commit_property<eax_volume_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lVolume);
1460 eax_fx_slot_commit_property<eax_lock_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lLock);
1461 eax_fx_slot_commit_property<eax_flags_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::ulFlags);
1462 eax_fx_slot_commit_property<eax_occlusion_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lOcclusion);
1463 eax_fx_slot_commit_property<eax_occlusion_lf_ratio_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::flOcclusionLFRatio);
1466 void ALeffectslot::eax_set_efx_slot_effect(EaxEffect &effect)
1468 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
1470 const auto error = initEffect(0, effect.al_effect_type_, effect.al_effect_props_,
1471 eax_al_context_);
1473 if(error != AL_NO_ERROR) {
1474 ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect.");
1475 return;
1478 if(mState == SlotState::Initial) {
1479 mPropsDirty = false;
1480 updateProps(eax_al_context_);
1481 auto effect_slot_ptr = this;
1482 AddActiveEffectSlots({&effect_slot_ptr, 1}, eax_al_context_);
1483 mState = SlotState::Playing;
1484 return;
1487 mPropsDirty = true;
1489 #undef EAX_PREFIX
1492 void ALeffectslot::eax_set_efx_slot_send_auto(bool is_send_auto)
1494 if(AuxSendAuto == is_send_auto)
1495 return;
1497 AuxSendAuto = is_send_auto;
1498 mPropsDirty = true;
1501 void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain)
1503 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
1505 if(gain == Gain)
1506 return;
1507 if(gain < 0.0f || gain > 1.0f)
1508 ERR(EAX_PREFIX "Gain out of range (%f)\n", gain);
1510 Gain = std::clamp(gain, 0.0f, 1.0f);
1511 mPropsDirty = true;
1513 #undef EAX_PREFIX
1516 void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot)
1518 assert(effect_slot);
1519 eax_delete_al_effect_slot(*effect_slot->eax_al_context_, *effect_slot);
1522 EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context)
1524 #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
1526 std::lock_guard<std::mutex> slotlock{context.mEffectSlotLock};
1527 auto& device = *context.mALDevice;
1529 if(context.mNumEffectSlots == device.AuxiliaryEffectSlotMax) {
1530 ERR(EAX_PREFIX "%s\n", "Out of memory.");
1531 return nullptr;
1534 if(!EnsureEffectSlots(&context, 1)) {
1535 ERR(EAX_PREFIX "%s\n", "Failed to ensure.");
1536 return nullptr;
1539 return EaxAlEffectSlotUPtr{AllocEffectSlot(&context)};
1541 #undef EAX_PREFIX
1544 void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot)
1546 #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
1548 std::lock_guard<std::mutex> slotlock{context.mEffectSlotLock};
1550 if(effect_slot.ref.load(std::memory_order_relaxed) != 0)
1552 ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id);
1553 return;
1556 auto effect_slot_ptr = &effect_slot;
1557 RemoveActiveEffectSlots({&effect_slot_ptr, 1}, &context);
1558 FreeEffectSlot(&context, &effect_slot);
1560 #undef EAX_PREFIX
1562 #endif // ALSOFT_EAX