Remove some unnecessary enums
[openal-soft.git] / al / auxeffectslot.cpp
blob8d05b405ba3b23a047620976959b654627420010
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 <cstddef>
27 #include <cstdint>
28 #include <functional>
29 #include <iterator>
30 #include <memory>
31 #include <mutex>
32 #include <numeric>
33 #include <stdexcept>
34 #include <string>
35 #include <tuple>
36 #include <unordered_map>
37 #include <vector>
39 #include "AL/al.h"
40 #include "AL/alc.h"
41 #include "AL/alext.h"
42 #include "AL/efx.h"
44 #include "albit.h"
45 #include "alc/alu.h"
46 #include "alc/context.h"
47 #include "alc/device.h"
48 #include "alc/effects/base.h"
49 #include "alc/inprogext.h"
50 #include "almalloc.h"
51 #include "alnumeric.h"
52 #include "alspan.h"
53 #include "atomic.h"
54 #include "buffer.h"
55 #include "core/buffer_storage.h"
56 #include "core/device.h"
57 #include "core/fpu_ctrl.h"
58 #include "core/logging.h"
59 #include "direct_defs.h"
60 #include "effect.h"
61 #include "error.h"
62 #include "flexarray.h"
63 #include "opthelpers.h"
65 #ifdef ALSOFT_EAX
66 #include "eax/api.h"
67 #include "eax/call.h"
68 #include "eax/effect.h"
69 #include "eax/fx_slot_index.h"
70 #include "eax/utils.h"
71 #endif
73 namespace {
75 using SubListAllocator = al::allocator<std::array<ALeffectslot,64>>;
77 EffectStateFactory *getFactoryByType(EffectSlotType type)
79 switch(type)
81 case EffectSlotType::None: return NullStateFactory_getFactory();
82 case EffectSlotType::Reverb: return ReverbStateFactory_getFactory();
83 case EffectSlotType::Chorus: return ChorusStateFactory_getFactory();
84 case EffectSlotType::Autowah: return AutowahStateFactory_getFactory();
85 case EffectSlotType::Compressor: return CompressorStateFactory_getFactory();
86 case EffectSlotType::Convolution: return ConvolutionStateFactory_getFactory();
87 case EffectSlotType::Dedicated: return DedicatedStateFactory_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 ChorusStateFactory_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();
97 return nullptr;
101 auto LookupEffectSlot(ALCcontext *context, ALuint id) noexcept -> ALeffectslot*
103 const size_t lidx{(id-1) >> 6};
104 const ALuint slidx{(id-1) & 0x3f};
106 if(lidx >= context->mEffectSlotList.size()) UNLIKELY
107 return nullptr;
108 EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
109 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
110 return nullptr;
111 return al::to_address(sublist.EffectSlots->begin() + slidx);
114 inline auto LookupEffect(ALCdevice *device, ALuint id) noexcept -> ALeffect*
116 const size_t lidx{(id-1) >> 6};
117 const ALuint slidx{(id-1) & 0x3f};
119 if(lidx >= device->EffectList.size()) UNLIKELY
120 return nullptr;
121 EffectSubList &sublist = device->EffectList[lidx];
122 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
123 return nullptr;
124 return al::to_address(sublist.Effects->begin() + slidx);
127 inline auto LookupBuffer(ALCdevice *device, ALuint id) noexcept -> ALbuffer*
129 const size_t lidx{(id-1) >> 6};
130 const ALuint slidx{(id-1) & 0x3f};
132 if(lidx >= device->BufferList.size()) UNLIKELY
133 return nullptr;
134 BufferSubList &sublist = device->BufferList[lidx];
135 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
136 return nullptr;
137 return al::to_address(sublist.Buffers->begin() + slidx);
141 void AddActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
143 if(auxslots.empty()) return;
144 EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
145 if((curarray->size()>>1) > std::numeric_limits<size_t>::max()-auxslots.size())
146 throw std::runtime_error{"Too many active effect slots"};
148 size_t newcount{(curarray->size()>>1) + auxslots.size()};
149 if(newcount > std::numeric_limits<size_t>::max()>>1)
150 throw std::runtime_error{"Too many active effect slots"};
152 /* Insert the new effect slots into the head of the new array, followed by
153 * the existing ones.
155 auto newarray = EffectSlot::CreatePtrArray(newcount<<1);
156 auto new_end = std::transform(auxslots.begin(), auxslots.end(), newarray->begin(),
157 std::mem_fn(&ALeffectslot::mSlot));
158 new_end = std::copy_n(curarray->begin(), curarray->size()>>1, new_end);
160 /* Remove any duplicates (first instance of each will be kept). */
161 for(auto start=newarray->begin()+1;;)
163 new_end = std::remove(start, new_end, *(start-1));
164 if(start == new_end) break;
165 ++start;
167 newcount = static_cast<size_t>(std::distance(newarray->begin(), new_end));
169 /* Reallocate newarray if the new size ended up smaller from duplicate
170 * removal.
172 if(newcount < newarray->size()>>1) UNLIKELY
174 auto oldarray = std::move(newarray);
175 newarray = EffectSlot::CreatePtrArray(newcount<<1);
176 new_end = std::copy_n(oldarray->begin(), newcount, newarray->begin());
178 std::fill(new_end, newarray->end(), nullptr);
180 auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray),
181 std::memory_order_acq_rel);
182 std::ignore = context->mDevice->waitForMix();
185 void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
187 if(auxslots.empty()) return;
188 EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
190 /* Don't shrink the allocated array size since we don't know how many (if
191 * any) of the effect slots to remove are in the array.
193 auto newarray = EffectSlot::CreatePtrArray(curarray->size());
195 auto new_end = std::copy_n(curarray->begin(), curarray->size()>>1, newarray->begin());
196 /* Remove elements from newarray that match any ID in slotids. */
197 for(const ALeffectslot *auxslot : auxslots)
199 auto slot_match = [auxslot](EffectSlot *slot) noexcept -> bool
200 { return (slot == auxslot->mSlot); };
201 new_end = std::remove_if(newarray->begin(), new_end, slot_match);
204 /* Reallocate with the new size. */
205 auto newsize = static_cast<size_t>(std::distance(newarray->begin(), new_end));
206 if(newsize < newarray->size()>>1) LIKELY
208 auto oldarray = std::move(newarray);
209 newarray = EffectSlot::CreatePtrArray(newsize<<1);
210 new_end = std::copy_n(oldarray->begin(), newsize, newarray->begin());
212 std::fill(new_end, newarray->end(), nullptr);
214 auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray),
215 std::memory_order_acq_rel);
216 std::ignore = context->mDevice->waitForMix();
220 constexpr auto EffectSlotTypeFromEnum(ALenum type) noexcept -> EffectSlotType
222 switch(type)
224 case AL_EFFECT_NULL: return EffectSlotType::None;
225 case AL_EFFECT_REVERB: return EffectSlotType::Reverb;
226 case AL_EFFECT_CHORUS: return EffectSlotType::Chorus;
227 case AL_EFFECT_DISTORTION: return EffectSlotType::Distortion;
228 case AL_EFFECT_ECHO: return EffectSlotType::Echo;
229 case AL_EFFECT_FLANGER: return EffectSlotType::Flanger;
230 case AL_EFFECT_FREQUENCY_SHIFTER: return EffectSlotType::FrequencyShifter;
231 case AL_EFFECT_VOCAL_MORPHER: return EffectSlotType::VocalMorpher;
232 case AL_EFFECT_PITCH_SHIFTER: return EffectSlotType::PitchShifter;
233 case AL_EFFECT_RING_MODULATOR: return EffectSlotType::RingModulator;
234 case AL_EFFECT_AUTOWAH: return EffectSlotType::Autowah;
235 case AL_EFFECT_COMPRESSOR: return EffectSlotType::Compressor;
236 case AL_EFFECT_EQUALIZER: return EffectSlotType::Equalizer;
237 case AL_EFFECT_EAXREVERB: return EffectSlotType::Reverb;
238 case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::Dedicated;
239 case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::Dedicated;
240 case AL_EFFECT_CONVOLUTION_SOFT: return EffectSlotType::Convolution;
242 ERR("Unhandled effect enum: 0x%04x\n", type);
243 return EffectSlotType::None;
246 auto EnsureEffectSlots(ALCcontext *context, size_t needed) noexcept -> bool
247 try {
248 size_t count{std::accumulate(context->mEffectSlotList.cbegin(),
249 context->mEffectSlotList.cend(), 0_uz,
250 [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
251 { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
253 while(needed > count)
255 if(context->mEffectSlotList.size() >= 1<<25) UNLIKELY
256 return false;
258 EffectSlotSubList sublist{};
259 sublist.FreeMask = ~0_u64;
260 sublist.EffectSlots = SubListAllocator{}.allocate(1);
261 context->mEffectSlotList.emplace_back(std::move(sublist));
262 count += std::tuple_size_v<SubListAllocator::value_type>;
264 return true;
266 catch(...) {
267 return false;
270 ALeffectslot *AllocEffectSlot(ALCcontext *context)
272 auto sublist = std::find_if(context->mEffectSlotList.begin(), context->mEffectSlotList.end(),
273 [](const EffectSlotSubList &entry) noexcept -> bool
274 { return entry.FreeMask != 0; });
275 auto lidx = static_cast<ALuint>(std::distance(context->mEffectSlotList.begin(), sublist));
276 auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
277 ASSUME(slidx < 64);
279 ALeffectslot *slot{al::construct_at(al::to_address(sublist->EffectSlots->begin() + slidx),
280 context)};
281 aluInitEffectPanning(slot->mSlot, context);
283 /* Add 1 to avoid ID 0. */
284 slot->id = ((lidx<<6) | slidx) + 1;
286 context->mNumEffectSlots += 1;
287 sublist->FreeMask &= ~(1_u64 << slidx);
289 return slot;
292 void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
294 context->mEffectSlotNames.erase(slot->id);
296 const ALuint id{slot->id - 1};
297 const size_t lidx{id >> 6};
298 const ALuint slidx{id & 0x3f};
300 std::destroy_at(slot);
302 context->mEffectSlotList[lidx].FreeMask |= 1_u64 << slidx;
303 context->mNumEffectSlots--;
307 inline void UpdateProps(ALeffectslot *slot, ALCcontext *context)
309 if(!context->mDeferUpdates && slot->mState == SlotState::Playing)
311 slot->updateProps(context);
312 return;
314 slot->mPropsDirty = true;
317 } // namespace
320 AL_API DECL_FUNC2(void, alGenAuxiliaryEffectSlots, ALsizei,n, ALuint*,effectslots)
321 FORCE_ALIGN void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n,
322 ALuint *effectslots) noexcept
323 try {
324 if(n < 0)
325 throw al::context_error{AL_INVALID_VALUE, "Generating %d effect slots", n};
326 if(n <= 0) UNLIKELY return;
328 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
329 ALCdevice *device{context->mALDevice.get()};
331 const al::span eids{effectslots, static_cast<ALuint>(n)};
332 if(eids.size() > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
333 throw al::context_error{AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)",
334 device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n};
336 if(!EnsureEffectSlots(context, eids.size()))
337 throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n,
338 (n == 1) ? "" : "s"};
340 std::vector<ALeffectslot*> slots;
341 try {
342 if(eids.size() == 1)
344 /* Special handling for the easy and normal case. */
345 eids[0] = AllocEffectSlot(context)->id;
347 else
349 slots.reserve(eids.size());
350 std::generate_n(std::back_inserter(slots), eids.size(),
351 [context]{ return AllocEffectSlot(context); });
353 std::transform(slots.cbegin(), slots.cend(), eids.begin(),
354 [](ALeffectslot *slot) -> ALuint { return slot->id; });
357 catch(std::exception& e) {
358 ERR("Exception allocating effectslot %zu of %d: %s\n", slots.size()+1, n, e.what());
359 auto delete_effectslot = [context](ALeffectslot *slot) -> void
360 { FreeEffectSlot(context, slot); };
361 std::for_each(slots.begin(), slots.end(), delete_effectslot);
362 throw al::context_error{AL_INVALID_OPERATION, "Exception allocating %d effectslots: %s", n,
363 e.what()};
366 catch(al::context_error& e) {
367 context->setError(e.errorCode(), "%s", e.what());
370 AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei,n, const ALuint*,effectslots)
371 FORCE_ALIGN void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n,
372 const ALuint *effectslots) noexcept
373 try {
374 if(n < 0) UNLIKELY
375 throw al::context_error{AL_INVALID_VALUE, "Deleting %d effect slots", n};
376 if(n <= 0) UNLIKELY return;
378 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
379 if(n == 1)
381 ALeffectslot *slot{LookupEffectSlot(context, *effectslots)};
382 if(!slot)
383 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", *effectslots};
384 if(slot->ref.load(std::memory_order_relaxed) != 0)
385 throw al::context_error{AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
386 *effectslots};
388 RemoveActiveEffectSlots({&slot, 1u}, context);
389 FreeEffectSlot(context, slot);
391 else
393 const al::span eids{effectslots, static_cast<ALuint>(n)};
394 std::vector<ALeffectslot*> slots;
395 slots.reserve(eids.size());
397 auto lookupslot = [context](const ALuint eid) -> ALeffectslot*
399 ALeffectslot *slot{LookupEffectSlot(context, eid)};
400 if(!slot)
401 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", eid};
402 if(slot->ref.load(std::memory_order_relaxed) != 0)
403 throw al::context_error{AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
404 eid};
405 return slot;
407 std::transform(eids.cbegin(), eids.cend(), std::back_inserter(slots), lookupslot);
409 /* All effectslots are valid, remove and delete them */
410 RemoveActiveEffectSlots(slots, context);
412 auto delete_effectslot = [context](const ALuint eid) -> void
414 if(ALeffectslot *slot{LookupEffectSlot(context, eid)})
415 FreeEffectSlot(context, slot);
417 std::for_each(eids.begin(), eids.end(), delete_effectslot);
420 catch(al::context_error& e) {
421 context->setError(e.errorCode(), "%s", e.what());
424 AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint,effectslot)
425 FORCE_ALIGN ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context,
426 ALuint effectslot) noexcept
428 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
429 if(LookupEffectSlot(context, effectslot) != nullptr)
430 return AL_TRUE;
431 return AL_FALSE;
435 AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) noexcept
437 ContextRef context{GetContextRef()};
438 if(!context) UNLIKELY return;
440 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
441 ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
442 if(!slot) UNLIKELY
444 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
445 return;
447 if(slot->mState == SlotState::Playing)
448 return;
450 slot->mPropsDirty = false;
451 slot->updateProps(context.get());
453 AddActiveEffectSlots({&slot, 1}, context.get());
454 slot->mState = SlotState::Playing;
457 AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) noexcept
459 ContextRef context{GetContextRef()};
460 if(!context) UNLIKELY return;
462 try {
463 if(n < 0)
464 throw al::context_error{AL_INVALID_VALUE, "Playing %d effect slots", n};
465 if(n <= 0) UNLIKELY return;
467 auto ids = al::span{slotids, static_cast<ALuint>(n)};
468 auto slots = std::vector<ALeffectslot*>(ids.size());
469 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
471 auto lookupslot = [&context](const ALuint id) -> ALeffectslot*
473 ALeffectslot *slot{LookupEffectSlot(context.get(), id)};
474 if(!slot)
475 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", id};
477 if(slot->mState != SlotState::Playing)
479 slot->mPropsDirty = false;
480 slot->updateProps(context.get());
482 return slot;
484 std::transform(ids.cbegin(), ids.cend(), slots.begin(), lookupslot);
486 AddActiveEffectSlots(slots, context.get());
487 for(auto slot : slots)
488 slot->mState = SlotState::Playing;
490 catch(al::context_error& e) {
491 context->setError(e.errorCode(), "%s", e.what());
492 return;
496 AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) noexcept
498 ContextRef context{GetContextRef()};
499 if(!context) UNLIKELY return;
501 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
502 ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
503 if(!slot) UNLIKELY
505 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
506 return;
509 RemoveActiveEffectSlots({&slot, 1}, context.get());
510 slot->mState = SlotState::Stopped;
513 AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) noexcept
515 ContextRef context{GetContextRef()};
516 if(!context) UNLIKELY return;
518 try {
519 if(n < 0)
520 throw al::context_error{AL_INVALID_VALUE, "Stopping %d effect slots", n};
521 if(n <= 0) UNLIKELY return;
523 auto ids = al::span{slotids, static_cast<ALuint>(n)};
524 auto slots = std::vector<ALeffectslot*>(ids.size());
525 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
527 auto lookupslot = [&context](const ALuint id) -> ALeffectslot*
529 if(ALeffectslot *slot{LookupEffectSlot(context.get(), id)})
530 return slot;
531 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", id};
533 std::transform(ids.cbegin(), ids.cend(), slots.begin(), lookupslot);
535 RemoveActiveEffectSlots(slots, context.get());
536 for(auto slot : slots)
537 slot->mState = SlotState::Stopped;
539 catch(al::context_error& e) {
540 context->setError(e.errorCode(), "%s", e.what());
541 return;
546 AL_API DECL_FUNC3(void, alAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint,value)
547 FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot,
548 ALenum param, ALint value) noexcept
549 try {
550 std::lock_guard<std::mutex> proplock{context->mPropLock};
551 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
553 ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
554 if(!slot) UNLIKELY
555 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
557 ALeffectslot *target{};
558 ALenum err{};
559 switch(param)
561 case AL_EFFECTSLOT_EFFECT:
563 ALCdevice *device{context->mALDevice.get()};
564 std::lock_guard<std::mutex> effectlock{device->EffectLock};
565 ALeffect *effect{value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr};
566 if(effect)
567 err = slot->initEffect(effect->id, effect->type, effect->Props, context);
568 else
570 if(value != 0)
571 throw al::context_error{AL_INVALID_VALUE, "Invalid effect ID %u", value};
572 err = slot->initEffect(0, AL_EFFECT_NULL, EffectProps{}, context);
575 if(err != AL_NO_ERROR)
576 throw al::context_error{err, "Effect initialization failed"};
578 if(slot->mState == SlotState::Initial) UNLIKELY
580 slot->mPropsDirty = false;
581 slot->updateProps(context);
583 AddActiveEffectSlots({&slot, 1}, context);
584 slot->mState = SlotState::Playing;
585 return;
587 UpdateProps(slot, context);
588 return;
590 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
591 if(!(value == AL_TRUE || value == AL_FALSE))
592 throw al::context_error{AL_INVALID_VALUE,
593 "Effect slot auxiliary send auto out of range"};
594 if(!(slot->AuxSendAuto == !!value)) LIKELY
596 slot->AuxSendAuto = !!value;
597 UpdateProps(slot, context);
599 return;
601 case AL_EFFECTSLOT_TARGET_SOFT:
602 target = LookupEffectSlot(context, static_cast<ALuint>(value));
603 if(value && !target)
604 throw al::context_error{AL_INVALID_VALUE, "Invalid effect slot target ID"};
605 if(slot->Target == target) UNLIKELY
606 return;
607 if(target)
609 ALeffectslot *checker{target};
610 while(checker && checker != slot)
611 checker = checker->Target;
612 if(checker)
613 throw al::context_error{AL_INVALID_OPERATION,
614 "Setting target of effect slot ID %u to %u creates circular chain", slot->id,
615 target->id};
618 if(ALeffectslot *oldtarget{slot->Target})
620 /* We must force an update if there was an existing effect slot
621 * target, in case it's about to be deleted.
623 if(target) IncrementRef(target->ref);
624 DecrementRef(oldtarget->ref);
625 slot->Target = target;
626 slot->updateProps(context);
627 return;
630 if(target) IncrementRef(target->ref);
631 slot->Target = target;
632 UpdateProps(slot, context);
633 return;
635 case AL_BUFFER:
636 if(slot->mState == SlotState::Playing)
637 throw al::context_error{AL_INVALID_OPERATION,
638 "Setting buffer on playing effect slot %u", slot->id};
640 if(ALbuffer *buffer{slot->Buffer})
642 if(buffer->id == static_cast<ALuint>(value)) UNLIKELY
643 return;
645 else if(value == 0) UNLIKELY
646 return;
649 ALCdevice *device{context->mALDevice.get()};
650 std::lock_guard<std::mutex> bufferlock{device->BufferLock};
651 ALbuffer *buffer{};
652 if(value)
654 buffer = LookupBuffer(device, static_cast<ALuint>(value));
655 if(!buffer)
656 throw al::context_error{AL_INVALID_VALUE, "Invalid buffer ID %u", value};
657 if(buffer->mCallback)
658 throw al::context_error{AL_INVALID_OPERATION,
659 "Callback buffer not valid for effects"};
661 IncrementRef(buffer->ref);
664 if(ALbuffer *oldbuffer{slot->Buffer})
665 DecrementRef(oldbuffer->ref);
666 slot->Buffer = buffer;
668 FPUCtl mixer_mode{};
669 auto *state = slot->Effect.State.get();
670 state->deviceUpdate(device, buffer);
672 UpdateProps(slot, context);
673 return;
675 case AL_EFFECTSLOT_STATE_SOFT:
676 throw al::context_error{AL_INVALID_OPERATION, "AL_EFFECTSLOT_STATE_SOFT is read-only"};
679 throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param};
681 catch(al::context_error& e) {
682 context->setError(e.errorCode(), "%s", e.what());
685 AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, const ALint*,values)
686 FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot,
687 ALenum param, const ALint *values) noexcept
688 try {
689 switch(param)
691 case AL_EFFECTSLOT_EFFECT:
692 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
693 case AL_EFFECTSLOT_TARGET_SOFT:
694 case AL_EFFECTSLOT_STATE_SOFT:
695 case AL_BUFFER:
696 alAuxiliaryEffectSlotiDirect(context, effectslot, param, *values);
697 return;
700 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
701 ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
702 if(!slot)
703 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
705 switch(param)
708 throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
709 param};
711 catch(al::context_error& e) {
712 context->setError(e.errorCode(), "%s", e.what());
715 AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat,value)
716 FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot,
717 ALenum param, ALfloat value) noexcept
718 try {
719 std::lock_guard<std::mutex> proplock{context->mPropLock};
720 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
722 ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
723 if(!slot)
724 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
726 switch(param)
728 case AL_EFFECTSLOT_GAIN:
729 if(!(value >= 0.0f && value <= 1.0f))
730 throw al::context_error{AL_INVALID_VALUE, "Effect slot gain out of range"};
731 if(!(slot->Gain == value)) LIKELY
733 slot->Gain = value;
734 UpdateProps(slot, context);
736 return;
739 throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param};
741 catch(al::context_error& e) {
742 context->setError(e.errorCode(), "%s", e.what());
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
748 try {
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)
759 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
761 switch(param)
764 throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
765 param};
767 catch(al::context_error& e) {
768 context->setError(e.errorCode(), "%s", e.what());
772 AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint*,value)
773 FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context,
774 ALuint effectslot, ALenum param, ALint *value) noexcept
775 try {
776 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
777 ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
778 if(!slot)
779 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
781 switch(param)
783 case AL_EFFECTSLOT_EFFECT:
784 *value = static_cast<ALint>(slot->EffectId);
785 return;
787 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
788 *value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE;
789 return;
791 case AL_EFFECTSLOT_TARGET_SOFT:
792 if(auto *target = slot->Target)
793 *value = static_cast<ALint>(target->id);
794 else
795 *value = 0;
796 return;
798 case AL_EFFECTSLOT_STATE_SOFT:
799 *value = static_cast<int>(slot->mState);
800 return;
802 case AL_BUFFER:
803 if(auto *buffer = slot->Buffer)
804 *value = static_cast<ALint>(buffer->id);
805 else
806 *value = 0;
807 return;
810 throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param};
812 catch(al::context_error& e) {
813 context->setError(e.errorCode(), "%s", e.what());
816 AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, ALint*,values)
817 FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context,
818 ALuint effectslot, ALenum param, ALint *values) noexcept
819 try {
820 switch(param)
822 case AL_EFFECTSLOT_EFFECT:
823 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
824 case AL_EFFECTSLOT_TARGET_SOFT:
825 case AL_EFFECTSLOT_STATE_SOFT:
826 case AL_BUFFER:
827 alGetAuxiliaryEffectSlotiDirect(context, effectslot, param, values);
828 return;
831 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
832 ALeffectslot *slot = LookupEffectSlot(context, effectslot);
833 if(!slot)
834 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
836 switch(param)
839 throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
840 param};
842 catch(al::context_error& e) {
843 context->setError(e.errorCode(), "%s", e.what());
846 AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat*,value)
847 FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context,
848 ALuint effectslot, ALenum param, ALfloat *value) noexcept
849 try {
850 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
851 ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
852 if(!slot)
853 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
855 switch(param)
857 case AL_EFFECTSLOT_GAIN:
858 *value = slot->Gain;
859 return;
862 throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param};
864 catch(al::context_error& e) {
865 context->setError(e.errorCode(), "%s", e.what());
868 AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, ALfloat*,values)
869 FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context,
870 ALuint effectslot, ALenum param, ALfloat *values) noexcept
871 try {
872 switch(param)
874 case AL_EFFECTSLOT_GAIN:
875 alGetAuxiliaryEffectSlotfDirect(context, effectslot, param, values);
876 return;
879 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
880 ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
881 if(!slot)
882 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
884 switch(param)
887 throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
888 param};
890 catch(al::context_error& e) {
891 context->setError(e.errorCode(), "%s", e.what());
895 ALeffectslot::ALeffectslot(ALCcontext *context)
897 EffectStateFactory *factory{getFactoryByType(EffectSlotType::None)};
898 if(!factory) throw std::runtime_error{"Failed to get null effect factory"};
900 al::intrusive_ptr<EffectState> state{factory->create()};
901 Effect.State = state;
903 mSlot = context->getEffectSlot();
904 mSlot->InUse = true;
905 mSlot->mEffectState = std::move(state);
908 ALeffectslot::~ALeffectslot()
910 if(Target)
911 DecrementRef(Target->ref);
912 Target = nullptr;
913 if(Buffer)
914 DecrementRef(Buffer->ref);
915 Buffer = nullptr;
917 if(auto *slot = mSlot->Update.exchange(nullptr, std::memory_order_relaxed))
918 slot->State = nullptr;
920 mSlot->mEffectState = nullptr;
921 mSlot->InUse = false;
924 ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
925 ALCcontext *context)
927 EffectSlotType newtype{EffectSlotTypeFromEnum(effectType)};
928 if(newtype != Effect.Type)
930 EffectStateFactory *factory{getFactoryByType(newtype)};
931 if(!factory)
933 ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype));
934 return AL_INVALID_ENUM;
936 al::intrusive_ptr<EffectState> state{factory->create()};
938 ALCdevice *device{context->mALDevice.get()};
939 std::unique_lock<std::mutex> statelock{device->StateLock};
940 state->mOutTarget = device->Dry.Buffer;
942 FPUCtl mixer_mode{};
943 state->deviceUpdate(device, Buffer);
946 Effect.Type = newtype;
947 Effect.Props = effectProps;
949 Effect.State = std::move(state);
951 else if(newtype != EffectSlotType::None)
952 Effect.Props = effectProps;
953 EffectId = effectId;
955 /* Remove state references from old effect slot property updates. */
956 EffectSlotProps *props{context->mFreeEffectSlotProps.load()};
957 while(props)
959 props->State = nullptr;
960 props = props->next.load(std::memory_order_relaxed);
963 return AL_NO_ERROR;
966 void ALeffectslot::updateProps(ALCcontext *context) const
968 /* Get an unused property container, or allocate a new one as needed. */
969 EffectSlotProps *props{context->mFreeEffectSlotProps.load(std::memory_order_acquire)};
970 if(!props)
972 context->allocEffectSlotProps();
973 props = context->mFreeEffectSlotProps.load(std::memory_order_acquire);
975 EffectSlotProps *next;
976 do {
977 next = props->next.load(std::memory_order_relaxed);
978 } while(!context->mFreeEffectSlotProps.compare_exchange_weak(props, next,
979 std::memory_order_acq_rel, std::memory_order_acquire));
981 /* Copy in current property values. */
982 props->Gain = Gain;
983 props->AuxSendAuto = AuxSendAuto;
984 props->Target = Target ? Target->mSlot : nullptr;
986 props->Type = Effect.Type;
987 props->Props = Effect.Props;
988 props->State = Effect.State;
990 /* Set the new container for updating internal parameters. */
991 props = mSlot->Update.exchange(props, std::memory_order_acq_rel);
992 if(props)
994 /* If there was an unused update container, put it back in the
995 * freelist.
997 props->State = nullptr;
998 AtomicReplaceHead(context->mFreeEffectSlotProps, props);
1002 void ALeffectslot::SetName(ALCcontext* context, ALuint id, std::string_view name)
1004 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
1006 auto slot = LookupEffectSlot(context, id);
1007 if(!slot)
1008 throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", id};
1010 context->mEffectSlotNames.insert_or_assign(id, name);
1013 void UpdateAllEffectSlotProps(ALCcontext *context)
1015 std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
1016 for(auto &sublist : context->mEffectSlotList)
1018 uint64_t usemask{~sublist.FreeMask};
1019 while(usemask)
1021 const auto idx = static_cast<uint>(al::countr_zero(usemask));
1022 usemask &= ~(1_u64 << idx);
1023 auto &slot = (*sublist.EffectSlots)[idx];
1025 if(slot.mState != SlotState::Stopped && std::exchange(slot.mPropsDirty, false))
1026 slot.updateProps(context);
1031 EffectSlotSubList::~EffectSlotSubList()
1033 if(!EffectSlots)
1034 return;
1036 uint64_t usemask{~FreeMask};
1037 while(usemask)
1039 const int idx{al::countr_zero(usemask)};
1040 std::destroy_at(al::to_address(EffectSlots->begin() + idx));
1041 usemask &= ~(1_u64 << idx);
1043 FreeMask = ~usemask;
1044 SubListAllocator{}.deallocate(EffectSlots, 1);
1045 EffectSlots = nullptr;
1048 #ifdef ALSOFT_EAX
1049 void ALeffectslot::eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index)
1051 if(index >= EAX_MAX_FXSLOTS)
1052 eax_fail("Index out of range.");
1054 eax_al_context_ = &al_context;
1055 eax_fx_slot_index_ = index;
1056 eax_fx_slot_set_defaults();
1058 eax_effect_ = std::make_unique<EaxEffect>();
1059 if(index == 0) eax_effect_->init<EaxReverbCommitter>();
1060 else if(index == 1) eax_effect_->init<EaxChorusCommitter>();
1061 else eax_effect_->init<EaxNullCommitter>();
1064 void ALeffectslot::eax_commit()
1066 if(eax_df_ != EaxDirtyFlags{})
1068 auto df = EaxDirtyFlags{};
1069 switch(eax_version_)
1071 case 1:
1072 case 2:
1073 case 3:
1074 eax5_fx_slot_commit(eax123_, df);
1075 break;
1076 case 4:
1077 eax4_fx_slot_commit(df);
1078 break;
1079 case 5:
1080 eax5_fx_slot_commit(eax5_, df);
1081 break;
1083 eax_df_ = EaxDirtyFlags{};
1085 if((df & eax_volume_dirty_bit) != EaxDirtyFlags{})
1086 eax_fx_slot_set_volume();
1087 if((df & eax_flags_dirty_bit) != EaxDirtyFlags{})
1088 eax_fx_slot_set_flags();
1091 if(eax_effect_->commit(eax_version_))
1092 eax_set_efx_slot_effect(*eax_effect_);
1095 [[noreturn]] void ALeffectslot::eax_fail(const char* message)
1097 throw Exception{message};
1100 [[noreturn]] void ALeffectslot::eax_fail_unknown_effect_id()
1102 eax_fail("Unknown effect ID.");
1105 [[noreturn]] void ALeffectslot::eax_fail_unknown_property_id()
1107 eax_fail("Unknown property ID.");
1110 [[noreturn]] void ALeffectslot::eax_fail_unknown_version()
1112 eax_fail("Unknown version.");
1115 void ALeffectslot::eax4_fx_slot_ensure_unlocked() const
1117 if(eax4_fx_slot_is_legacy())
1118 eax_fail("Locked legacy slot.");
1121 ALenum ALeffectslot::eax_get_efx_effect_type(const GUID& guid)
1123 if(guid == EAX_NULL_GUID)
1124 return AL_EFFECT_NULL;
1125 if(guid == EAX_AUTOWAH_EFFECT)
1126 return AL_EFFECT_AUTOWAH;
1127 if(guid == EAX_CHORUS_EFFECT)
1128 return AL_EFFECT_CHORUS;
1129 if(guid == EAX_AGCCOMPRESSOR_EFFECT)
1130 return AL_EFFECT_COMPRESSOR;
1131 if(guid == EAX_DISTORTION_EFFECT)
1132 return AL_EFFECT_DISTORTION;
1133 if(guid == EAX_REVERB_EFFECT)
1134 return AL_EFFECT_EAXREVERB;
1135 if(guid == EAX_ECHO_EFFECT)
1136 return AL_EFFECT_ECHO;
1137 if(guid == EAX_EQUALIZER_EFFECT)
1138 return AL_EFFECT_EQUALIZER;
1139 if(guid == EAX_FLANGER_EFFECT)
1140 return AL_EFFECT_FLANGER;
1141 if(guid == EAX_FREQUENCYSHIFTER_EFFECT)
1142 return AL_EFFECT_FREQUENCY_SHIFTER;
1143 if(guid == EAX_PITCHSHIFTER_EFFECT)
1144 return AL_EFFECT_PITCH_SHIFTER;
1145 if(guid == EAX_RINGMODULATOR_EFFECT)
1146 return AL_EFFECT_RING_MODULATOR;
1147 if(guid == EAX_VOCALMORPHER_EFFECT)
1148 return AL_EFFECT_VOCAL_MORPHER;
1150 eax_fail_unknown_effect_id();
1153 const GUID& ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
1155 switch(eax_fx_slot_index_)
1157 case 0: return EAX_REVERB_EFFECT;
1158 case 1: return EAX_CHORUS_EFFECT;
1159 default: return EAX_NULL_GUID;
1163 long ALeffectslot::eax_get_eax_default_lock() const noexcept
1165 return eax4_fx_slot_is_legacy() ? EAXFXSLOT_LOCKED : EAXFXSLOT_UNLOCKED;
1168 void ALeffectslot::eax4_fx_slot_set_defaults(Eax4Props& props) noexcept
1170 props.guidLoadEffect = eax_get_eax_default_effect_guid();
1171 props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
1172 props.lLock = eax_get_eax_default_lock();
1173 props.ulFlags = EAX40FXSLOT_DEFAULTFLAGS;
1176 void ALeffectslot::eax5_fx_slot_set_defaults(Eax5Props& props) noexcept
1178 props.guidLoadEffect = eax_get_eax_default_effect_guid();
1179 props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
1180 props.lLock = EAXFXSLOT_UNLOCKED;
1181 props.ulFlags = EAX50FXSLOT_DEFAULTFLAGS;
1182 props.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
1183 props.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
1186 void ALeffectslot::eax_fx_slot_set_defaults()
1188 eax5_fx_slot_set_defaults(eax123_.i);
1189 eax4_fx_slot_set_defaults(eax4_.i);
1190 eax5_fx_slot_set_defaults(eax5_.i);
1191 eax_ = eax5_.i;
1192 eax_df_ = EaxDirtyFlags{};
1195 void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props)
1197 switch(call.get_property_id())
1199 case EAXFXSLOT_ALLPARAMETERS:
1200 call.set_value<Exception>(props);
1201 break;
1202 case EAXFXSLOT_LOADEFFECT:
1203 call.set_value<Exception>(props.guidLoadEffect);
1204 break;
1205 case EAXFXSLOT_VOLUME:
1206 call.set_value<Exception>(props.lVolume);
1207 break;
1208 case EAXFXSLOT_LOCK:
1209 call.set_value<Exception>(props.lLock);
1210 break;
1211 case EAXFXSLOT_FLAGS:
1212 call.set_value<Exception>(props.ulFlags);
1213 break;
1214 default:
1215 eax_fail_unknown_property_id();
1219 void ALeffectslot::eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props)
1221 switch(call.get_property_id())
1223 case EAXFXSLOT_ALLPARAMETERS:
1224 call.set_value<Exception>(props);
1225 break;
1226 case EAXFXSLOT_LOADEFFECT:
1227 call.set_value<Exception>(props.guidLoadEffect);
1228 break;
1229 case EAXFXSLOT_VOLUME:
1230 call.set_value<Exception>(props.lVolume);
1231 break;
1232 case EAXFXSLOT_LOCK:
1233 call.set_value<Exception>(props.lLock);
1234 break;
1235 case EAXFXSLOT_FLAGS:
1236 call.set_value<Exception>(props.ulFlags);
1237 break;
1238 case EAXFXSLOT_OCCLUSION:
1239 call.set_value<Exception>(props.lOcclusion);
1240 break;
1241 case EAXFXSLOT_OCCLUSIONLFRATIO:
1242 call.set_value<Exception>(props.flOcclusionLFRatio);
1243 break;
1244 default:
1245 eax_fail_unknown_property_id();
1249 void ALeffectslot::eax_fx_slot_get(const EaxCall& call) const
1251 switch(call.get_version())
1253 case 4: eax4_fx_slot_get(call, eax4_.i); break;
1254 case 5: eax5_fx_slot_get(call, eax5_.i); break;
1255 default: eax_fail_unknown_version();
1259 bool ALeffectslot::eax_get(const EaxCall& call)
1261 switch(call.get_property_set_id())
1263 case EaxCallPropertySetId::fx_slot:
1264 eax_fx_slot_get(call);
1265 break;
1266 case EaxCallPropertySetId::fx_slot_effect:
1267 eax_effect_->get(call);
1268 break;
1269 default:
1270 eax_fail_unknown_property_id();
1273 return false;
1276 void ALeffectslot::eax_fx_slot_load_effect(int version, ALenum altype)
1278 if(!IsValidEffectType(altype))
1279 altype = AL_EFFECT_NULL;
1280 eax_effect_->set_defaults(version, altype);
1283 void ALeffectslot::eax_fx_slot_set_volume()
1285 const auto volume = std::clamp(eax_.lVolume, EAXFXSLOT_MINVOLUME, EAXFXSLOT_MAXVOLUME);
1286 const auto gain = level_mb_to_gain(static_cast<float>(volume));
1287 eax_set_efx_slot_gain(gain);
1290 void ALeffectslot::eax_fx_slot_set_environment_flag()
1292 eax_set_efx_slot_send_auto((eax_.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0u);
1295 void ALeffectslot::eax_fx_slot_set_flags()
1297 eax_fx_slot_set_environment_flag();
1300 void ALeffectslot::eax4_fx_slot_set_all(const EaxCall& call)
1302 eax4_fx_slot_ensure_unlocked();
1303 const auto& src = call.get_value<Exception, const EAX40FXSLOTPROPERTIES>();
1304 Eax4AllValidator{}(src);
1305 auto& dst = eax4_.i;
1306 eax_df_ |= eax_load_effect_dirty_bit; // Always reset the effect.
1307 eax_df_ |= (dst.lVolume != src.lVolume ? eax_volume_dirty_bit : EaxDirtyFlags{});
1308 eax_df_ |= (dst.lLock != src.lLock ? eax_lock_dirty_bit : EaxDirtyFlags{});
1309 eax_df_ |= (dst.ulFlags != src.ulFlags ? eax_flags_dirty_bit : EaxDirtyFlags{});
1310 dst = src;
1313 void ALeffectslot::eax5_fx_slot_set_all(const EaxCall& call)
1315 const auto& src = call.get_value<Exception, const EAX50FXSLOTPROPERTIES>();
1316 Eax5AllValidator{}(src);
1317 auto& dst = eax5_.i;
1318 eax_df_ |= eax_load_effect_dirty_bit; // Always reset the effect.
1319 eax_df_ |= (dst.lVolume != src.lVolume ? eax_volume_dirty_bit : EaxDirtyFlags{});
1320 eax_df_ |= (dst.lLock != src.lLock ? eax_lock_dirty_bit : EaxDirtyFlags{});
1321 eax_df_ |= (dst.ulFlags != src.ulFlags ? eax_flags_dirty_bit : EaxDirtyFlags{});
1322 eax_df_ |= (dst.lOcclusion != src.lOcclusion ? eax_flags_dirty_bit : EaxDirtyFlags{});
1323 eax_df_ |= (dst.flOcclusionLFRatio != src.flOcclusionLFRatio ? eax_flags_dirty_bit : EaxDirtyFlags{});
1324 dst = src;
1327 bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept
1329 static constexpr auto dirty_bits =
1330 eax_occlusion_dirty_bit |
1331 eax_occlusion_lf_ratio_dirty_bit |
1332 eax_flags_dirty_bit;
1334 return (eax_df_ & dirty_bits) != EaxDirtyFlags{};
1337 // Returns `true` if all sources should be updated, or `false` otherwise.
1338 bool ALeffectslot::eax4_fx_slot_set(const EaxCall& call)
1340 auto& dst = eax4_.i;
1342 switch(call.get_property_id())
1344 case EAXFXSLOT_NONE:
1345 break;
1346 case EAXFXSLOT_ALLPARAMETERS:
1347 eax4_fx_slot_set_all(call);
1348 if((eax_df_ & eax_load_effect_dirty_bit))
1349 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
1350 break;
1351 case EAXFXSLOT_LOADEFFECT:
1352 eax4_fx_slot_ensure_unlocked();
1353 eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call, dst.guidLoadEffect, eax_df_);
1354 if((eax_df_ & eax_load_effect_dirty_bit))
1355 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
1356 break;
1357 case EAXFXSLOT_VOLUME:
1358 eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, eax_df_);
1359 break;
1360 case EAXFXSLOT_LOCK:
1361 eax4_fx_slot_ensure_unlocked();
1362 eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, eax_df_);
1363 break;
1364 case EAXFXSLOT_FLAGS:
1365 eax_fx_slot_set<Eax4FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, eax_df_);
1366 break;
1367 default:
1368 eax_fail_unknown_property_id();
1371 return eax_fx_slot_should_update_sources();
1374 // Returns `true` if all sources should be updated, or `false` otherwise.
1375 bool ALeffectslot::eax5_fx_slot_set(const EaxCall& call)
1377 auto& dst = eax5_.i;
1379 switch(call.get_property_id())
1381 case EAXFXSLOT_NONE:
1382 break;
1383 case EAXFXSLOT_ALLPARAMETERS:
1384 eax5_fx_slot_set_all(call);
1385 if((eax_df_ & eax_load_effect_dirty_bit))
1386 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
1387 break;
1388 case EAXFXSLOT_LOADEFFECT:
1389 eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call, dst.guidLoadEffect, eax_df_);
1390 if((eax_df_ & eax_load_effect_dirty_bit))
1391 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
1392 break;
1393 case EAXFXSLOT_VOLUME:
1394 eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, eax_df_);
1395 break;
1396 case EAXFXSLOT_LOCK:
1397 eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, eax_df_);
1398 break;
1399 case EAXFXSLOT_FLAGS:
1400 eax_fx_slot_set<Eax5FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, eax_df_);
1401 break;
1402 case EAXFXSLOT_OCCLUSION:
1403 eax_fx_slot_set<Eax5OcclusionValidator, eax_occlusion_dirty_bit>(call, dst.lOcclusion, eax_df_);
1404 break;
1405 case EAXFXSLOT_OCCLUSIONLFRATIO:
1406 eax_fx_slot_set<Eax5OcclusionLfRatioValidator, eax_occlusion_lf_ratio_dirty_bit>(call, dst.flOcclusionLFRatio, eax_df_);
1407 break;
1408 default:
1409 eax_fail_unknown_property_id();
1412 return eax_fx_slot_should_update_sources();
1415 // Returns `true` if all sources should be updated, or `false` otherwise.
1416 bool ALeffectslot::eax_fx_slot_set(const EaxCall& call)
1418 switch (call.get_version())
1420 case 4: return eax4_fx_slot_set(call);
1421 case 5: return eax5_fx_slot_set(call);
1422 default: eax_fail_unknown_version();
1426 // Returns `true` if all sources should be updated, or `false` otherwise.
1427 bool ALeffectslot::eax_set(const EaxCall& call)
1429 bool ret{false};
1431 switch(call.get_property_set_id())
1433 case EaxCallPropertySetId::fx_slot: ret = eax_fx_slot_set(call); break;
1434 case EaxCallPropertySetId::fx_slot_effect: eax_effect_->set(call); break;
1435 default: eax_fail_unknown_property_id();
1438 const auto version = call.get_version();
1439 if(eax_version_ != version)
1440 eax_df_ = ~EaxDirtyFlags{};
1441 eax_version_ = version;
1443 return ret;
1446 void ALeffectslot::eax4_fx_slot_commit(EaxDirtyFlags& dst_df)
1448 eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::guidLoadEffect);
1449 eax_fx_slot_commit_property<eax_volume_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::lVolume);
1450 eax_fx_slot_commit_property<eax_lock_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::lLock);
1451 eax_fx_slot_commit_property<eax_flags_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::ulFlags);
1453 auto& dst_i = eax_;
1455 if(dst_i.lOcclusion != EAXFXSLOT_DEFAULTOCCLUSION) {
1456 dst_df |= eax_occlusion_dirty_bit;
1457 dst_i.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
1460 if(dst_i.flOcclusionLFRatio != EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO) {
1461 dst_df |= eax_occlusion_lf_ratio_dirty_bit;
1462 dst_i.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
1466 void ALeffectslot::eax5_fx_slot_commit(Eax5State& state, EaxDirtyFlags& dst_df)
1468 eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::guidLoadEffect);
1469 eax_fx_slot_commit_property<eax_volume_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lVolume);
1470 eax_fx_slot_commit_property<eax_lock_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lLock);
1471 eax_fx_slot_commit_property<eax_flags_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::ulFlags);
1472 eax_fx_slot_commit_property<eax_occlusion_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lOcclusion);
1473 eax_fx_slot_commit_property<eax_occlusion_lf_ratio_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::flOcclusionLFRatio);
1476 void ALeffectslot::eax_set_efx_slot_effect(EaxEffect &effect)
1478 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
1480 const auto error = initEffect(0, effect.al_effect_type_, effect.al_effect_props_,
1481 eax_al_context_);
1483 if(error != AL_NO_ERROR) {
1484 ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect.");
1485 return;
1488 if(mState == SlotState::Initial) {
1489 mPropsDirty = false;
1490 updateProps(eax_al_context_);
1491 auto effect_slot_ptr = this;
1492 AddActiveEffectSlots({&effect_slot_ptr, 1}, eax_al_context_);
1493 mState = SlotState::Playing;
1494 return;
1497 mPropsDirty = true;
1499 #undef EAX_PREFIX
1502 void ALeffectslot::eax_set_efx_slot_send_auto(bool is_send_auto)
1504 if(AuxSendAuto == is_send_auto)
1505 return;
1507 AuxSendAuto = is_send_auto;
1508 mPropsDirty = true;
1511 void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain)
1513 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
1515 if(gain == Gain)
1516 return;
1517 if(gain < 0.0f || gain > 1.0f)
1518 ERR(EAX_PREFIX "Gain out of range (%f)\n", gain);
1520 Gain = std::clamp(gain, 0.0f, 1.0f);
1521 mPropsDirty = true;
1523 #undef EAX_PREFIX
1526 void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot)
1528 eax_delete_al_effect_slot(*effect_slot->eax_al_context_, *effect_slot);
1531 EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context)
1533 #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
1535 std::lock_guard<std::mutex> slotlock{context.mEffectSlotLock};
1536 auto& device = *context.mALDevice;
1538 if(context.mNumEffectSlots == device.AuxiliaryEffectSlotMax) {
1539 ERR(EAX_PREFIX "%s\n", "Out of memory.");
1540 return nullptr;
1543 if(!EnsureEffectSlots(&context, 1)) {
1544 ERR(EAX_PREFIX "%s\n", "Failed to ensure.");
1545 return nullptr;
1548 return EaxAlEffectSlotUPtr{AllocEffectSlot(&context)};
1550 #undef EAX_PREFIX
1553 void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot)
1555 #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
1557 std::lock_guard<std::mutex> slotlock{context.mEffectSlotLock};
1559 if(effect_slot.ref.load(std::memory_order_relaxed) != 0)
1561 ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id);
1562 return;
1565 auto effect_slot_ptr = &effect_slot;
1566 RemoveActiveEffectSlots({&effect_slot_ptr, 1}, &context);
1567 FreeEffectSlot(&context, &effect_slot);
1569 #undef EAX_PREFIX
1571 #endif // ALSOFT_EAX