Move EAX files to their own sub-directory
[openal-soft.git] / al / auxeffectslot.cpp
blob455a107200e34e927a3578f1fdbbbb79b58930c7
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 <cstdint>
28 #include <iterator>
29 #include <memory>
30 #include <mutex>
31 #include <numeric>
32 #include <thread>
34 #include "AL/al.h"
35 #include "AL/alc.h"
36 #include "AL/efx.h"
38 #include "albit.h"
39 #include "alc/alu.h"
40 #include "alc/context.h"
41 #include "alc/device.h"
42 #include "alc/inprogext.h"
43 #include "almalloc.h"
44 #include "alnumeric.h"
45 #include "alspan.h"
46 #include "buffer.h"
47 #include "core/except.h"
48 #include "core/fpu_ctrl.h"
49 #include "core/logging.h"
50 #include "effect.h"
51 #include "opthelpers.h"
53 #ifdef ALSOFT_EAX
54 #include "eax/exception.h"
55 #include "eax/utils.h"
56 #endif // ALSOFT_EAX
58 namespace {
60 struct FactoryItem {
61 EffectSlotType Type;
62 EffectStateFactory* (&GetFactory)(void);
64 constexpr FactoryItem FactoryList[] = {
65 { EffectSlotType::None, NullStateFactory_getFactory },
66 { EffectSlotType::EAXReverb, ReverbStateFactory_getFactory },
67 { EffectSlotType::Reverb, StdReverbStateFactory_getFactory },
68 { EffectSlotType::Autowah, AutowahStateFactory_getFactory },
69 { EffectSlotType::Chorus, ChorusStateFactory_getFactory },
70 { EffectSlotType::Compressor, CompressorStateFactory_getFactory },
71 { EffectSlotType::Distortion, DistortionStateFactory_getFactory },
72 { EffectSlotType::Echo, EchoStateFactory_getFactory },
73 { EffectSlotType::Equalizer, EqualizerStateFactory_getFactory },
74 { EffectSlotType::Flanger, FlangerStateFactory_getFactory },
75 { EffectSlotType::FrequencyShifter, FshifterStateFactory_getFactory },
76 { EffectSlotType::RingModulator, ModulatorStateFactory_getFactory },
77 { EffectSlotType::PitchShifter, PshifterStateFactory_getFactory },
78 { EffectSlotType::VocalMorpher, VmorpherStateFactory_getFactory },
79 { EffectSlotType::DedicatedDialog, DedicatedStateFactory_getFactory },
80 { EffectSlotType::DedicatedLFE, DedicatedStateFactory_getFactory },
81 { EffectSlotType::Convolution, ConvolutionStateFactory_getFactory },
84 EffectStateFactory *getFactoryByType(EffectSlotType type)
86 auto iter = std::find_if(std::begin(FactoryList), std::end(FactoryList),
87 [type](const FactoryItem &item) noexcept -> bool
88 { return item.Type == type; });
89 return (iter != std::end(FactoryList)) ? iter->GetFactory() : nullptr;
93 inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
95 const size_t lidx{(id-1) >> 6};
96 const ALuint slidx{(id-1) & 0x3f};
98 if UNLIKELY(lidx >= context->mEffectSlotList.size())
99 return nullptr;
100 EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
101 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
102 return nullptr;
103 return sublist.EffectSlots + slidx;
106 inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept
108 const size_t lidx{(id-1) >> 6};
109 const ALuint slidx{(id-1) & 0x3f};
111 if UNLIKELY(lidx >= device->EffectList.size())
112 return nullptr;
113 EffectSubList &sublist = device->EffectList[lidx];
114 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
115 return nullptr;
116 return sublist.Effects + slidx;
119 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
121 const size_t lidx{(id-1) >> 6};
122 const ALuint slidx{(id-1) & 0x3f};
124 if UNLIKELY(lidx >= device->BufferList.size())
125 return nullptr;
126 BufferSubList &sublist = device->BufferList[lidx];
127 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
128 return nullptr;
129 return sublist.Buffers + slidx;
133 inline auto GetEffectBuffer(ALbuffer *buffer) noexcept -> EffectState::Buffer
135 if(!buffer) return EffectState::Buffer{};
136 return EffectState::Buffer{buffer, buffer->mData};
140 void AddActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
142 if(auxslots.empty()) return;
143 EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
144 size_t newcount{curarray->size() + auxslots.size()};
146 /* Insert the new effect slots into the head of the array, followed by the
147 * existing ones.
149 EffectSlotArray *newarray = EffectSlot::CreatePtrArray(newcount);
150 auto slotiter = std::transform(auxslots.begin(), auxslots.end(), newarray->begin(),
151 [](ALeffectslot *auxslot) noexcept { return &auxslot->mSlot; });
152 std::copy(curarray->begin(), curarray->end(), slotiter);
154 /* Remove any duplicates (first instance of each will be kept). */
155 auto last = newarray->end();
156 for(auto start=newarray->begin()+1;;)
158 last = std::remove(start, last, *(start-1));
159 if(start == last) break;
160 ++start;
162 newcount = static_cast<size_t>(std::distance(newarray->begin(), last));
164 /* Reallocate newarray if the new size ended up smaller from duplicate
165 * removal.
167 if UNLIKELY(newcount < newarray->size())
169 curarray = newarray;
170 newarray = EffectSlot::CreatePtrArray(newcount);
171 std::copy_n(curarray->begin(), newcount, newarray->begin());
172 delete curarray;
173 curarray = nullptr;
175 std::uninitialized_fill_n(newarray->end(), newcount, nullptr);
177 curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
178 context->mDevice->waitForMix();
180 al::destroy_n(curarray->end(), curarray->size());
181 delete curarray;
184 void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
186 if(auxslots.empty()) return;
187 EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
189 /* Don't shrink the allocated array size since we don't know how many (if
190 * any) of the effect slots to remove are in the array.
192 EffectSlotArray *newarray = EffectSlot::CreatePtrArray(curarray->size());
194 auto new_end = std::copy(curarray->begin(), curarray->end(), newarray->begin());
195 /* Remove elements from newarray that match any ID in slotids. */
196 for(const ALeffectslot *auxslot : auxslots)
198 auto slot_match = [auxslot](EffectSlot *slot) noexcept -> bool
199 { return (slot == &auxslot->mSlot); };
200 new_end = std::remove_if(newarray->begin(), new_end, slot_match);
203 /* Reallocate with the new size. */
204 auto newsize = static_cast<size_t>(std::distance(newarray->begin(), new_end));
205 if LIKELY(newsize != newarray->size())
207 curarray = newarray;
208 newarray = EffectSlot::CreatePtrArray(newsize);
209 std::copy_n(curarray->begin(), newsize, newarray->begin());
211 delete curarray;
212 curarray = nullptr;
214 std::uninitialized_fill_n(newarray->end(), newsize, nullptr);
216 curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
217 context->mDevice->waitForMix();
219 al::destroy_n(curarray->end(), curarray->size());
220 delete curarray;
224 EffectSlotType EffectSlotTypeFromEnum(ALenum type)
226 switch(type)
228 case AL_EFFECT_NULL: return EffectSlotType::None;
229 case AL_EFFECT_REVERB: return EffectSlotType::Reverb;
230 case AL_EFFECT_CHORUS: return EffectSlotType::Chorus;
231 case AL_EFFECT_DISTORTION: return EffectSlotType::Distortion;
232 case AL_EFFECT_ECHO: return EffectSlotType::Echo;
233 case AL_EFFECT_FLANGER: return EffectSlotType::Flanger;
234 case AL_EFFECT_FREQUENCY_SHIFTER: return EffectSlotType::FrequencyShifter;
235 case AL_EFFECT_VOCAL_MORPHER: return EffectSlotType::VocalMorpher;
236 case AL_EFFECT_PITCH_SHIFTER: return EffectSlotType::PitchShifter;
237 case AL_EFFECT_RING_MODULATOR: return EffectSlotType::RingModulator;
238 case AL_EFFECT_AUTOWAH: return EffectSlotType::Autowah;
239 case AL_EFFECT_COMPRESSOR: return EffectSlotType::Compressor;
240 case AL_EFFECT_EQUALIZER: return EffectSlotType::Equalizer;
241 case AL_EFFECT_EAXREVERB: return EffectSlotType::EAXReverb;
242 case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::DedicatedLFE;
243 case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::DedicatedDialog;
244 case AL_EFFECT_CONVOLUTION_REVERB_SOFT: return EffectSlotType::Convolution;
246 ERR("Unhandled effect enum: 0x%04x\n", type);
247 return EffectSlotType::None;
250 bool EnsureEffectSlots(ALCcontext *context, size_t needed)
252 size_t count{std::accumulate(context->mEffectSlotList.cbegin(),
253 context->mEffectSlotList.cend(), size_t{0},
254 [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
255 { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
257 while(needed > count)
259 if UNLIKELY(context->mEffectSlotList.size() >= 1<<25)
260 return false;
262 context->mEffectSlotList.emplace_back();
263 auto sublist = context->mEffectSlotList.end() - 1;
264 sublist->FreeMask = ~0_u64;
265 sublist->EffectSlots = static_cast<ALeffectslot*>(
266 al_calloc(alignof(ALeffectslot), sizeof(ALeffectslot)*64));
267 if UNLIKELY(!sublist->EffectSlots)
269 context->mEffectSlotList.pop_back();
270 return false;
272 count += 64;
274 return true;
277 ALeffectslot *AllocEffectSlot(ALCcontext *context)
279 auto sublist = std::find_if(context->mEffectSlotList.begin(), context->mEffectSlotList.end(),
280 [](const EffectSlotSubList &entry) noexcept -> bool
281 { return entry.FreeMask != 0; });
282 auto lidx = static_cast<ALuint>(std::distance(context->mEffectSlotList.begin(), sublist));
283 auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
284 ASSUME(slidx < 64);
286 ALeffectslot *slot{al::construct_at(sublist->EffectSlots + slidx)};
287 aluInitEffectPanning(&slot->mSlot, context);
289 /* Add 1 to avoid source ID 0. */
290 slot->id = ((lidx<<6) | slidx) + 1;
292 context->mNumEffectSlots += 1;
293 sublist->FreeMask &= ~(1_u64 << slidx);
295 return slot;
298 void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
300 const ALuint id{slot->id - 1};
301 const size_t lidx{id >> 6};
302 const ALuint slidx{id & 0x3f};
304 al::destroy_at(slot);
306 context->mEffectSlotList[lidx].FreeMask |= 1_u64 << slidx;
307 context->mNumEffectSlots--;
311 inline void UpdateProps(ALeffectslot *slot, ALCcontext *context)
313 if(!context->mDeferUpdates && slot->mState == SlotState::Playing)
315 slot->updateProps(context);
316 return;
318 slot->mPropsDirty = true;
321 } // namespace
324 AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
325 START_API_FUNC
327 ContextRef context{GetContextRef()};
328 if UNLIKELY(!context) return;
330 if UNLIKELY(n < 0)
331 context->setError(AL_INVALID_VALUE, "Generating %d effect slots", n);
332 if UNLIKELY(n <= 0) return;
334 std::unique_lock<std::mutex> slotlock{context->mEffectSlotLock};
335 ALCdevice *device{context->mALDevice.get()};
336 if(static_cast<ALuint>(n) > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
338 context->setError(AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)",
339 device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n);
340 return;
342 if(!EnsureEffectSlots(context.get(), static_cast<ALuint>(n)))
344 context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n,
345 (n==1) ? "" : "s");
346 return;
349 if(n == 1)
351 ALeffectslot *slot{AllocEffectSlot(context.get())};
352 if(!slot) return;
353 effectslots[0] = slot->id;
355 else
357 al::vector<ALuint> ids;
358 ALsizei count{n};
359 ids.reserve(static_cast<ALuint>(count));
360 do {
361 ALeffectslot *slot{AllocEffectSlot(context.get())};
362 if(!slot)
364 slotlock.unlock();
365 alDeleteAuxiliaryEffectSlots(static_cast<ALsizei>(ids.size()), ids.data());
366 return;
368 ids.emplace_back(slot->id);
369 } while(--count);
370 std::copy(ids.cbegin(), ids.cend(), effectslots);
373 END_API_FUNC
375 AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots)
376 START_API_FUNC
378 ContextRef context{GetContextRef()};
379 if UNLIKELY(!context) return;
381 if UNLIKELY(n < 0)
382 context->setError(AL_INVALID_VALUE, "Deleting %d effect slots", n);
383 if UNLIKELY(n <= 0) return;
385 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
386 if(n == 1)
388 ALeffectslot *slot{LookupEffectSlot(context.get(), effectslots[0])};
389 if UNLIKELY(!slot)
391 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[0]);
392 return;
394 if UNLIKELY(ReadRef(slot->ref) != 0)
396 context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
397 effectslots[0]);
398 return;
400 RemoveActiveEffectSlots({&slot, 1u}, context.get());
401 FreeEffectSlot(context.get(), slot);
403 else
405 auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
406 for(size_t i{0};i < slots.size();++i)
408 ALeffectslot *slot{LookupEffectSlot(context.get(), effectslots[i])};
409 if UNLIKELY(!slot)
411 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[i]);
412 return;
414 if UNLIKELY(ReadRef(slot->ref) != 0)
416 context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
417 effectslots[i]);
418 return;
420 slots[i] = slot;
422 /* Remove any duplicates. */
423 auto slots_end = slots.end();
424 for(auto start=slots.begin()+1;start != slots_end;++start)
426 slots_end = std::remove(start, slots_end, *(start-1));
427 if(start == slots_end) break;
429 slots.erase(slots_end, slots.end());
431 /* All effectslots are valid, remove and delete them */
432 RemoveActiveEffectSlots(slots, context.get());
433 for(ALeffectslot *slot : slots)
434 FreeEffectSlot(context.get(), slot);
437 END_API_FUNC
439 AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
440 START_API_FUNC
442 ContextRef context{GetContextRef()};
443 if LIKELY(context)
445 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
446 if(LookupEffectSlot(context.get(), effectslot) != nullptr)
447 return AL_TRUE;
449 return AL_FALSE;
451 END_API_FUNC
454 AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid)
455 START_API_FUNC
457 ContextRef context{GetContextRef()};
458 if UNLIKELY(!context) return;
460 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
461 ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
462 if UNLIKELY(!slot)
464 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
465 return;
467 if(slot->mState == SlotState::Playing)
468 return;
470 slot->mPropsDirty = false;
471 slot->updateProps(context.get());
473 AddActiveEffectSlots({&slot, 1}, context.get());
474 slot->mState = SlotState::Playing;
476 END_API_FUNC
478 AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids)
479 START_API_FUNC
481 ContextRef context{GetContextRef()};
482 if UNLIKELY(!context) return;
484 if UNLIKELY(n < 0)
485 context->setError(AL_INVALID_VALUE, "Playing %d effect slots", n);
486 if UNLIKELY(n <= 0) return;
488 auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
489 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
490 for(size_t i{0};i < slots.size();++i)
492 ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])};
493 if UNLIKELY(!slot)
495 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]);
496 return;
499 if(slot->mState != SlotState::Playing)
501 slot->mPropsDirty = false;
502 slot->updateProps(context.get());
504 slots[i] = slot;
507 AddActiveEffectSlots(slots, context.get());
508 for(auto slot : slots)
509 slot->mState = SlotState::Playing;
511 END_API_FUNC
513 AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid)
514 START_API_FUNC
516 ContextRef context{GetContextRef()};
517 if UNLIKELY(!context) return;
519 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
520 ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
521 if UNLIKELY(!slot)
523 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
524 return;
527 RemoveActiveEffectSlots({&slot, 1}, context.get());
528 slot->mState = SlotState::Stopped;
530 END_API_FUNC
532 AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids)
533 START_API_FUNC
535 ContextRef context{GetContextRef()};
536 if UNLIKELY(!context) return;
538 if UNLIKELY(n < 0)
539 context->setError(AL_INVALID_VALUE, "Stopping %d effect slots", n);
540 if UNLIKELY(n <= 0) return;
542 auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
543 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
544 for(size_t i{0};i < slots.size();++i)
546 ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])};
547 if UNLIKELY(!slot)
549 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]);
550 return;
553 slots[i] = slot;
556 RemoveActiveEffectSlots(slots, context.get());
557 for(auto slot : slots)
558 slot->mState = SlotState::Stopped;
560 END_API_FUNC
563 AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value)
564 START_API_FUNC
566 ContextRef context{GetContextRef()};
567 if UNLIKELY(!context) return;
569 std::lock_guard<std::mutex> _{context->mPropLock};
570 std::lock_guard<std::mutex> __{context->mEffectSlotLock};
571 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
572 if UNLIKELY(!slot)
573 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
575 ALeffectslot *target{};
576 ALCdevice *device{};
577 ALenum err{};
578 switch(param)
580 case AL_EFFECTSLOT_EFFECT:
581 device = context->mALDevice.get();
584 std::lock_guard<std::mutex> ___{device->EffectLock};
585 ALeffect *effect{value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr};
586 if(effect)
587 err = slot->initEffect(effect->type, effect->Props, context.get());
588 else
590 if(value != 0)
591 SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid effect ID %u", value);
592 err = slot->initEffect(AL_EFFECT_NULL, EffectProps{}, context.get());
595 if UNLIKELY(err != AL_NO_ERROR)
597 context->setError(err, "Effect initialization failed");
598 return;
600 if UNLIKELY(slot->mState == SlotState::Initial)
602 slot->mPropsDirty = false;
603 slot->updateProps(context.get());
605 AddActiveEffectSlots({&slot, 1}, context.get());
606 slot->mState = SlotState::Playing;
607 return;
609 break;
611 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
612 if(!(value == AL_TRUE || value == AL_FALSE))
613 SETERR_RETURN(context, AL_INVALID_VALUE,,
614 "Effect slot auxiliary send auto out of range");
615 if UNLIKELY(slot->AuxSendAuto == !!value)
616 return;
617 slot->AuxSendAuto = !!value;
618 break;
620 case AL_EFFECTSLOT_TARGET_SOFT:
621 target = LookupEffectSlot(context.get(), static_cast<ALuint>(value));
622 if(value && !target)
623 SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid effect slot target ID");
624 if UNLIKELY(slot->Target == target)
625 return;
626 if(target)
628 ALeffectslot *checker{target};
629 while(checker && checker != slot)
630 checker = checker->Target;
631 if(checker)
632 SETERR_RETURN(context, AL_INVALID_OPERATION,,
633 "Setting target of effect slot ID %u to %u creates circular chain", slot->id,
634 target->id);
637 if(ALeffectslot *oldtarget{slot->Target})
639 /* We must force an update if there was an existing effect slot
640 * target, in case it's about to be deleted.
642 if(target) IncrementRef(target->ref);
643 DecrementRef(oldtarget->ref);
644 slot->Target = target;
645 slot->updateProps(context.get());
646 return;
649 if(target) IncrementRef(target->ref);
650 slot->Target = target;
651 break;
653 case AL_BUFFER:
654 device = context->mALDevice.get();
656 if(slot->mState == SlotState::Playing)
657 SETERR_RETURN(context, AL_INVALID_OPERATION,,
658 "Setting buffer on playing effect slot %u", slot->id);
660 if(ALbuffer *buffer{slot->Buffer})
662 if UNLIKELY(buffer->id == static_cast<ALuint>(value))
663 return;
665 else if UNLIKELY(value == 0)
666 return;
669 std::lock_guard<std::mutex> ___{device->BufferLock};
670 ALbuffer *buffer{};
671 if(value)
673 buffer = LookupBuffer(device, static_cast<ALuint>(value));
674 if(!buffer) SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid buffer ID");
675 if(buffer->mCallback)
676 SETERR_RETURN(context, AL_INVALID_OPERATION,,
677 "Callback buffer not valid for effects");
679 IncrementRef(buffer->ref);
682 if(ALbuffer *oldbuffer{slot->Buffer})
683 DecrementRef(oldbuffer->ref);
684 slot->Buffer = buffer;
686 FPUCtl mixer_mode{};
687 auto *state = slot->Effect.State.get();
688 state->deviceUpdate(device, GetEffectBuffer(buffer));
690 break;
692 case AL_EFFECTSLOT_STATE_SOFT:
693 SETERR_RETURN(context, AL_INVALID_OPERATION,, "AL_EFFECTSLOT_STATE_SOFT is read-only");
695 default:
696 SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid effect slot integer property 0x%04x",
697 param);
699 UpdateProps(slot, context.get());
701 END_API_FUNC
703 AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
704 START_API_FUNC
706 switch(param)
708 case AL_EFFECTSLOT_EFFECT:
709 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
710 case AL_EFFECTSLOT_TARGET_SOFT:
711 case AL_EFFECTSLOT_STATE_SOFT:
712 case AL_BUFFER:
713 alAuxiliaryEffectSloti(effectslot, param, values[0]);
714 return;
717 ContextRef context{GetContextRef()};
718 if UNLIKELY(!context) return;
720 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
721 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
722 if UNLIKELY(!slot)
723 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
725 switch(param)
727 default:
728 SETERR_RETURN(context, AL_INVALID_ENUM,,
729 "Invalid effect slot integer-vector property 0x%04x", param);
732 END_API_FUNC
734 AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
735 START_API_FUNC
737 ContextRef context{GetContextRef()};
738 if UNLIKELY(!context) return;
740 std::lock_guard<std::mutex> _{context->mPropLock};
741 std::lock_guard<std::mutex> __{context->mEffectSlotLock};
742 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
743 if UNLIKELY(!slot)
744 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
746 switch(param)
748 case AL_EFFECTSLOT_GAIN:
749 if(!(value >= 0.0f && value <= 1.0f))
750 SETERR_RETURN(context, AL_INVALID_VALUE,, "Effect slot gain out of range");
751 if UNLIKELY(slot->Gain == value)
752 return;
753 slot->Gain = value;
754 break;
756 default:
757 SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid effect slot float property 0x%04x",
758 param);
760 UpdateProps(slot, context.get());
762 END_API_FUNC
764 AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
765 START_API_FUNC
767 switch(param)
769 case AL_EFFECTSLOT_GAIN:
770 alAuxiliaryEffectSlotf(effectslot, param, values[0]);
771 return;
774 ContextRef context{GetContextRef()};
775 if UNLIKELY(!context) return;
777 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
778 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
779 if UNLIKELY(!slot)
780 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
782 switch(param)
784 default:
785 SETERR_RETURN(context, AL_INVALID_ENUM,,
786 "Invalid effect slot float-vector property 0x%04x", param);
789 END_API_FUNC
792 AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
793 START_API_FUNC
795 ContextRef context{GetContextRef()};
796 if UNLIKELY(!context) return;
798 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
799 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
800 if UNLIKELY(!slot)
801 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
803 switch(param)
805 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
806 *value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE;
807 break;
809 case AL_EFFECTSLOT_TARGET_SOFT:
810 if(auto *target = slot->Target)
811 *value = static_cast<ALint>(target->id);
812 else
813 *value = 0;
814 break;
816 case AL_EFFECTSLOT_STATE_SOFT:
817 *value = static_cast<int>(slot->mState);
818 break;
820 case AL_BUFFER:
821 if(auto *buffer = slot->Buffer)
822 *value = static_cast<ALint>(buffer->id);
823 else
824 *value = 0;
825 break;
827 default:
828 context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param);
831 END_API_FUNC
833 AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
834 START_API_FUNC
836 switch(param)
838 case AL_EFFECTSLOT_EFFECT:
839 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
840 case AL_EFFECTSLOT_TARGET_SOFT:
841 case AL_EFFECTSLOT_STATE_SOFT:
842 case AL_BUFFER:
843 alGetAuxiliaryEffectSloti(effectslot, param, values);
844 return;
847 ContextRef context{GetContextRef()};
848 if UNLIKELY(!context) return;
850 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
851 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
852 if UNLIKELY(!slot)
853 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
855 switch(param)
857 default:
858 context->setError(AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
859 param);
862 END_API_FUNC
864 AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
865 START_API_FUNC
867 ContextRef context{GetContextRef()};
868 if UNLIKELY(!context) return;
870 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
871 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
872 if UNLIKELY(!slot)
873 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
875 switch(param)
877 case AL_EFFECTSLOT_GAIN:
878 *value = slot->Gain;
879 break;
881 default:
882 context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param);
885 END_API_FUNC
887 AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
888 START_API_FUNC
890 switch(param)
892 case AL_EFFECTSLOT_GAIN:
893 alGetAuxiliaryEffectSlotf(effectslot, param, values);
894 return;
897 ContextRef context{GetContextRef()};
898 if UNLIKELY(!context) return;
900 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
901 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
902 if UNLIKELY(!slot)
903 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
905 switch(param)
907 default:
908 context->setError(AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
909 param);
912 END_API_FUNC
915 ALeffectslot::ALeffectslot()
917 EffectStateFactory *factory{getFactoryByType(EffectSlotType::None)};
918 if(!factory) throw std::runtime_error{"Failed to get null effect factory"};
920 al::intrusive_ptr<EffectState> state{factory->create()};
921 Effect.State = state;
922 mSlot.mEffectState = state.release();
925 ALeffectslot::~ALeffectslot()
927 if(Target)
928 DecrementRef(Target->ref);
929 Target = nullptr;
930 if(Buffer)
931 DecrementRef(Buffer->ref);
932 Buffer = nullptr;
934 EffectSlotProps *props{mSlot.Update.exchange(nullptr)};
935 if(props)
937 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n",
938 decltype(std::declval<void*>()){props});
939 delete props;
942 if(mSlot.mEffectState)
943 mSlot.mEffectState->release();
946 ALenum ALeffectslot::initEffect(ALenum effectType, const EffectProps &effectProps,
947 ALCcontext *context)
949 EffectSlotType newtype{EffectSlotTypeFromEnum(effectType)};
950 if(newtype != Effect.Type)
952 EffectStateFactory *factory{getFactoryByType(newtype)};
953 if(!factory)
955 ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype));
956 return AL_INVALID_ENUM;
958 al::intrusive_ptr<EffectState> state{factory->create()};
960 ALCdevice *device{context->mALDevice.get()};
961 std::unique_lock<std::mutex> statelock{device->StateLock};
962 state->mOutTarget = device->Dry.Buffer;
964 FPUCtl mixer_mode{};
965 state->deviceUpdate(device, GetEffectBuffer(Buffer));
968 Effect.Type = newtype;
969 Effect.Props = effectProps;
971 Effect.State = std::move(state);
973 else if(newtype != EffectSlotType::None)
974 Effect.Props = effectProps;
976 /* Remove state references from old effect slot property updates. */
977 EffectSlotProps *props{context->mFreeEffectslotProps.load()};
978 while(props)
980 props->State = nullptr;
981 props = props->next.load(std::memory_order_relaxed);
984 return AL_NO_ERROR;
987 void ALeffectslot::updateProps(ALCcontext *context)
989 /* Get an unused property container, or allocate a new one as needed. */
990 EffectSlotProps *props{context->mFreeEffectslotProps.load(std::memory_order_relaxed)};
991 if(!props)
992 props = new EffectSlotProps{};
993 else
995 EffectSlotProps *next;
996 do {
997 next = props->next.load(std::memory_order_relaxed);
998 } while(context->mFreeEffectslotProps.compare_exchange_weak(props, next,
999 std::memory_order_seq_cst, std::memory_order_acquire) == 0);
1002 /* Copy in current property values. */
1003 props->Gain = Gain;
1004 props->AuxSendAuto = AuxSendAuto;
1005 props->Target = Target ? &Target->mSlot : nullptr;
1007 props->Type = Effect.Type;
1008 props->Props = Effect.Props;
1009 props->State = Effect.State;
1011 /* Set the new container for updating internal parameters. */
1012 props = mSlot.Update.exchange(props, std::memory_order_acq_rel);
1013 if(props)
1015 /* If there was an unused update container, put it back in the
1016 * freelist.
1018 props->State = nullptr;
1019 AtomicReplaceHead(context->mFreeEffectslotProps, props);
1023 void UpdateAllEffectSlotProps(ALCcontext *context)
1025 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
1026 #ifdef ALSOFT_EAX
1027 if(context->has_eax())
1028 context->eax_commit_fx_slots();
1029 #endif
1030 for(auto &sublist : context->mEffectSlotList)
1032 uint64_t usemask{~sublist.FreeMask};
1033 while(usemask)
1035 const int idx{al::countr_zero(usemask)};
1036 usemask &= ~(1_u64 << idx);
1037 ALeffectslot *slot{sublist.EffectSlots + idx};
1039 if(slot->mState != SlotState::Stopped && std::exchange(slot->mPropsDirty, false))
1040 slot->updateProps(context);
1045 EffectSlotSubList::~EffectSlotSubList()
1047 uint64_t usemask{~FreeMask};
1048 while(usemask)
1050 const int idx{al::countr_zero(usemask)};
1051 al::destroy_at(EffectSlots+idx);
1052 usemask &= ~(1_u64 << idx);
1054 FreeMask = ~usemask;
1055 al_free(EffectSlots);
1056 EffectSlots = nullptr;
1059 #ifdef ALSOFT_EAX
1060 namespace {
1062 class EaxFxSlotException :
1063 public EaxException
1065 public:
1066 explicit EaxFxSlotException(
1067 const char* message)
1069 EaxException{"EAX_FX_SLOT", message}
1072 }; // EaxFxSlotException
1075 } // namespace
1078 void ALeffectslot::eax_initialize(
1079 ALCcontext& al_context,
1080 EaxFxSlotIndexValue index)
1082 eax_al_context_ = &al_context;
1084 if (index >= EAX_MAX_FXSLOTS)
1086 eax_fail("Index out of range.");
1089 eax_fx_slot_index_ = index;
1091 eax_initialize_eax();
1092 eax_initialize_lock();
1093 eax_initialize_effects();
1096 const EAX50FXSLOTPROPERTIES& ALeffectslot::eax_get_eax_fx_slot() const noexcept
1098 return eax_eax_fx_slot_;
1101 void ALeffectslot::eax_ensure_is_unlocked() const
1103 if (eax_is_locked_)
1104 eax_fail("Locked.");
1107 void ALeffectslot::eax_validate_fx_slot_effect(
1108 const GUID& eax_effect_id)
1110 eax_ensure_is_unlocked();
1112 if (eax_effect_id != EAX_NULL_GUID &&
1113 eax_effect_id != EAX_REVERB_EFFECT &&
1114 eax_effect_id != EAX_AGCCOMPRESSOR_EFFECT &&
1115 eax_effect_id != EAX_AUTOWAH_EFFECT &&
1116 eax_effect_id != EAX_CHORUS_EFFECT &&
1117 eax_effect_id != EAX_DISTORTION_EFFECT &&
1118 eax_effect_id != EAX_ECHO_EFFECT &&
1119 eax_effect_id != EAX_EQUALIZER_EFFECT &&
1120 eax_effect_id != EAX_FLANGER_EFFECT &&
1121 eax_effect_id != EAX_FREQUENCYSHIFTER_EFFECT &&
1122 eax_effect_id != EAX_VOCALMORPHER_EFFECT &&
1123 eax_effect_id != EAX_PITCHSHIFTER_EFFECT &&
1124 eax_effect_id != EAX_RINGMODULATOR_EFFECT)
1126 eax_fail("Unsupported EAX effect GUID.");
1130 void ALeffectslot::eax_validate_fx_slot_volume(
1131 long eax_volume)
1133 eax_validate_range<EaxFxSlotException>(
1134 "Volume",
1135 eax_volume,
1136 EAXFXSLOT_MINVOLUME,
1137 EAXFXSLOT_MAXVOLUME);
1140 void ALeffectslot::eax_validate_fx_slot_lock(
1141 long eax_lock)
1143 eax_ensure_is_unlocked();
1145 eax_validate_range<EaxFxSlotException>(
1146 "Lock",
1147 eax_lock,
1148 EAXFXSLOT_MINLOCK,
1149 EAXFXSLOT_MAXLOCK);
1152 void ALeffectslot::eax_validate_fx_slot_flags(
1153 unsigned long eax_flags,
1154 int eax_version)
1156 eax_validate_range<EaxFxSlotException>(
1157 "Flags",
1158 eax_flags,
1159 0UL,
1160 ~(eax_version == 4 ? EAX40FXSLOTFLAGS_RESERVED : EAX50FXSLOTFLAGS_RESERVED));
1163 void ALeffectslot::eax_validate_fx_slot_occlusion(
1164 long eax_occlusion)
1166 eax_validate_range<EaxFxSlotException>(
1167 "Occlusion",
1168 eax_occlusion,
1169 EAXFXSLOT_MINOCCLUSION,
1170 EAXFXSLOT_MAXOCCLUSION);
1173 void ALeffectslot::eax_validate_fx_slot_occlusion_lf_ratio(
1174 float eax_occlusion_lf_ratio)
1176 eax_validate_range<EaxFxSlotException>(
1177 "Occlusion LF Ratio",
1178 eax_occlusion_lf_ratio,
1179 EAXFXSLOT_MINOCCLUSIONLFRATIO,
1180 EAXFXSLOT_MAXOCCLUSIONLFRATIO);
1183 void ALeffectslot::eax_validate_fx_slot_all(
1184 const EAX40FXSLOTPROPERTIES& fx_slot,
1185 int eax_version)
1187 eax_validate_fx_slot_effect(fx_slot.guidLoadEffect);
1188 eax_validate_fx_slot_volume(fx_slot.lVolume);
1189 eax_validate_fx_slot_lock(fx_slot.lLock);
1190 eax_validate_fx_slot_flags(fx_slot.ulFlags, eax_version);
1193 void ALeffectslot::eax_validate_fx_slot_all(
1194 const EAX50FXSLOTPROPERTIES& fx_slot,
1195 int eax_version)
1197 eax_validate_fx_slot_all(static_cast<const EAX40FXSLOTPROPERTIES&>(fx_slot), eax_version);
1199 eax_validate_fx_slot_occlusion(fx_slot.lOcclusion);
1200 eax_validate_fx_slot_occlusion_lf_ratio(fx_slot.flOcclusionLFRatio);
1203 void ALeffectslot::eax_set_fx_slot_effect(
1204 const GUID& eax_effect_id)
1206 if (eax_eax_fx_slot_.guidLoadEffect == eax_effect_id)
1208 return;
1211 eax_eax_fx_slot_.guidLoadEffect = eax_effect_id;
1213 eax_set_fx_slot_effect();
1216 void ALeffectslot::eax_set_fx_slot_volume(
1217 long eax_volume)
1219 if (eax_eax_fx_slot_.lVolume == eax_volume)
1221 return;
1224 eax_eax_fx_slot_.lVolume = eax_volume;
1226 eax_set_fx_slot_volume();
1229 void ALeffectslot::eax_set_fx_slot_lock(
1230 long eax_lock)
1232 if (eax_eax_fx_slot_.lLock == eax_lock)
1234 return;
1237 eax_eax_fx_slot_.lLock = eax_lock;
1240 void ALeffectslot::eax_set_fx_slot_flags(
1241 unsigned long eax_flags)
1243 if (eax_eax_fx_slot_.ulFlags == eax_flags)
1245 return;
1248 eax_eax_fx_slot_.ulFlags = eax_flags;
1250 eax_set_fx_slot_flags();
1253 // [[nodiscard]]
1254 bool ALeffectslot::eax_set_fx_slot_occlusion(
1255 long eax_occlusion)
1257 if (eax_eax_fx_slot_.lOcclusion == eax_occlusion)
1259 return false;
1262 eax_eax_fx_slot_.lOcclusion = eax_occlusion;
1264 return true;
1267 // [[nodiscard]]
1268 bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio(
1269 float eax_occlusion_lf_ratio)
1271 if (eax_eax_fx_slot_.flOcclusionLFRatio == eax_occlusion_lf_ratio)
1273 return false;
1276 eax_eax_fx_slot_.flOcclusionLFRatio = eax_occlusion_lf_ratio;
1278 return true;
1281 void ALeffectslot::eax_set_fx_slot_all(
1282 const EAX40FXSLOTPROPERTIES& eax_fx_slot)
1284 eax_set_fx_slot_effect(eax_fx_slot.guidLoadEffect);
1285 eax_set_fx_slot_volume(eax_fx_slot.lVolume);
1286 eax_set_fx_slot_lock(eax_fx_slot.lLock);
1287 eax_set_fx_slot_flags(eax_fx_slot.ulFlags);
1290 // [[nodiscard]]
1291 bool ALeffectslot::eax_set_fx_slot_all(
1292 const EAX50FXSLOTPROPERTIES& eax_fx_slot)
1294 eax_set_fx_slot_all(static_cast<const EAX40FXSLOTPROPERTIES&>(eax_fx_slot));
1296 const auto is_occlusion_modified = eax_set_fx_slot_occlusion(eax_fx_slot.lOcclusion);
1297 const auto is_occlusion_lf_ratio_modified = eax_set_fx_slot_occlusion_lf_ratio(eax_fx_slot.flOcclusionLFRatio);
1299 return is_occlusion_modified || is_occlusion_lf_ratio_modified;
1302 void ALeffectslot::eax_unlock_legacy() noexcept
1304 assert(eax_fx_slot_index_ < 2);
1305 eax_is_locked_ = false;
1306 eax_eax_fx_slot_.lLock = EAXFXSLOT_UNLOCKED;
1309 [[noreturn]]
1310 void ALeffectslot::eax_fail(
1311 const char* message)
1313 throw EaxFxSlotException{message};
1316 GUID ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
1318 switch (eax_fx_slot_index_)
1320 case 0: return EAX_REVERB_EFFECT;
1321 case 1: return EAX_CHORUS_EFFECT;
1322 default: return EAX_NULL_GUID;
1326 long ALeffectslot::eax_get_eax_default_lock() const noexcept
1328 return eax_fx_slot_index_ < 2 ? EAXFXSLOT_LOCKED : EAXFXSLOT_UNLOCKED;
1331 void ALeffectslot::eax_set_eax_fx_slot_defaults()
1333 eax_eax_fx_slot_.guidLoadEffect = eax_get_eax_default_effect_guid();
1334 eax_eax_fx_slot_.lVolume = EAXFXSLOT_DEFAULTVOLUME;
1335 eax_eax_fx_slot_.lLock = eax_get_eax_default_lock();
1336 eax_eax_fx_slot_.ulFlags = EAX40FXSLOT_DEFAULTFLAGS;
1337 eax_eax_fx_slot_.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
1338 eax_eax_fx_slot_.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
1341 void ALeffectslot::eax_initialize_eax()
1343 eax_set_eax_fx_slot_defaults();
1346 void ALeffectslot::eax_initialize_lock()
1348 eax_is_locked_ = (eax_fx_slot_index_ < 2);
1351 void ALeffectslot::eax_initialize_effects()
1353 eax_set_fx_slot_effect();
1356 void ALeffectslot::eax_get_fx_slot_all(
1357 const EaxEaxCall& eax_call) const
1359 switch (eax_call.get_version())
1361 case 4:
1362 eax_call.set_value<EaxFxSlotException, EAX40FXSLOTPROPERTIES>(eax_eax_fx_slot_);
1363 break;
1365 case 5:
1366 eax_call.set_value<EaxFxSlotException, EAX50FXSLOTPROPERTIES>(eax_eax_fx_slot_);
1367 break;
1369 default:
1370 eax_fail("Unsupported EAX version.");
1374 void ALeffectslot::eax_get_fx_slot(
1375 const EaxEaxCall& eax_call) const
1377 switch (eax_call.get_property_id())
1379 case EAXFXSLOT_ALLPARAMETERS:
1380 eax_get_fx_slot_all(eax_call);
1381 break;
1383 case EAXFXSLOT_LOADEFFECT:
1384 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.guidLoadEffect);
1385 break;
1387 case EAXFXSLOT_VOLUME:
1388 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lVolume);
1389 break;
1391 case EAXFXSLOT_LOCK:
1392 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lLock);
1393 break;
1395 case EAXFXSLOT_FLAGS:
1396 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.ulFlags);
1397 break;
1399 case EAXFXSLOT_OCCLUSION:
1400 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lOcclusion);
1401 break;
1403 case EAXFXSLOT_OCCLUSIONLFRATIO:
1404 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.flOcclusionLFRatio);
1405 break;
1407 default:
1408 eax_fail("Unsupported FX slot property id.");
1412 // [[nodiscard]]
1413 bool ALeffectslot::eax_get(
1414 const EaxEaxCall& eax_call)
1416 switch (eax_call.get_property_set_id())
1418 case EaxEaxCallPropertySetId::fx_slot:
1419 eax_get_fx_slot(eax_call);
1420 break;
1422 case EaxEaxCallPropertySetId::fx_slot_effect:
1423 eax_dispatch_effect(eax_call);
1424 break;
1426 default:
1427 eax_fail("Unsupported property id.");
1430 return false;
1433 void ALeffectslot::eax_set_fx_slot_effect(
1434 ALenum al_effect_type)
1436 if(!IsValidEffectType(al_effect_type))
1437 eax_fail("Unsupported effect.");
1439 eax_effect_ = nullptr;
1440 eax_effect_ = eax_create_eax_effect(al_effect_type);
1442 eax_set_effect_slot_effect(*eax_effect_);
1445 void ALeffectslot::eax_set_fx_slot_effect()
1447 auto al_effect_type = ALenum{};
1449 if (false)
1452 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_NULL_GUID)
1454 al_effect_type = AL_EFFECT_NULL;
1456 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AUTOWAH_EFFECT)
1458 al_effect_type = AL_EFFECT_AUTOWAH;
1460 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_CHORUS_EFFECT)
1462 al_effect_type = AL_EFFECT_CHORUS;
1464 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AGCCOMPRESSOR_EFFECT)
1466 al_effect_type = AL_EFFECT_COMPRESSOR;
1468 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_DISTORTION_EFFECT)
1470 al_effect_type = AL_EFFECT_DISTORTION;
1472 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_REVERB_EFFECT)
1474 al_effect_type = AL_EFFECT_EAXREVERB;
1476 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_ECHO_EFFECT)
1478 al_effect_type = AL_EFFECT_ECHO;
1480 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_EQUALIZER_EFFECT)
1482 al_effect_type = AL_EFFECT_EQUALIZER;
1484 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FLANGER_EFFECT)
1486 al_effect_type = AL_EFFECT_FLANGER;
1488 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FREQUENCYSHIFTER_EFFECT)
1490 al_effect_type = AL_EFFECT_FREQUENCY_SHIFTER;
1492 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_PITCHSHIFTER_EFFECT)
1494 al_effect_type = AL_EFFECT_PITCH_SHIFTER;
1496 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_RINGMODULATOR_EFFECT)
1498 al_effect_type = AL_EFFECT_RING_MODULATOR;
1500 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_VOCALMORPHER_EFFECT)
1502 al_effect_type = AL_EFFECT_VOCAL_MORPHER;
1504 else
1506 eax_fail("Unsupported effect.");
1509 eax_set_fx_slot_effect(al_effect_type);
1512 void ALeffectslot::eax_set_efx_effect_slot_gain()
1514 const auto gain = level_mb_to_gain(
1515 static_cast<float>(clamp(
1516 eax_eax_fx_slot_.lVolume,
1517 EAXFXSLOT_MINVOLUME,
1518 EAXFXSLOT_MAXVOLUME)));
1520 eax_set_effect_slot_gain(gain);
1523 void ALeffectslot::eax_set_fx_slot_volume()
1525 eax_set_efx_effect_slot_gain();
1528 void ALeffectslot::eax_set_effect_slot_send_auto()
1530 eax_set_effect_slot_send_auto((eax_eax_fx_slot_.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0);
1533 void ALeffectslot::eax_set_fx_slot_flags()
1535 eax_set_effect_slot_send_auto();
1538 void ALeffectslot::eax_set_fx_slot_effect(
1539 const EaxEaxCall& eax_call)
1541 const auto& eax_effect_id =
1542 eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::guidLoadEffect)>();
1544 eax_validate_fx_slot_effect(eax_effect_id);
1545 eax_set_fx_slot_effect(eax_effect_id);
1548 void ALeffectslot::eax_set_fx_slot_volume(
1549 const EaxEaxCall& eax_call)
1551 const auto& eax_volume =
1552 eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::lVolume)>();
1554 eax_validate_fx_slot_volume(eax_volume);
1555 eax_set_fx_slot_volume(eax_volume);
1558 void ALeffectslot::eax_set_fx_slot_lock(
1559 const EaxEaxCall& eax_call)
1561 const auto& eax_lock =
1562 eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::lLock)>();
1564 eax_validate_fx_slot_lock(eax_lock);
1565 eax_set_fx_slot_lock(eax_lock);
1568 void ALeffectslot::eax_set_fx_slot_flags(
1569 const EaxEaxCall& eax_call)
1571 const auto& eax_flags =
1572 eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::ulFlags)>();
1574 eax_validate_fx_slot_flags(eax_flags, eax_call.get_version());
1575 eax_set_fx_slot_flags(eax_flags);
1578 // [[nodiscard]]
1579 bool ALeffectslot::eax_set_fx_slot_occlusion(
1580 const EaxEaxCall& eax_call)
1582 const auto& eax_occlusion =
1583 eax_call.get_value<EaxFxSlotException, const decltype(EAX50FXSLOTPROPERTIES::lOcclusion)>();
1585 eax_validate_fx_slot_occlusion(eax_occlusion);
1587 return eax_set_fx_slot_occlusion(eax_occlusion);
1590 // [[nodiscard]]
1591 bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio(
1592 const EaxEaxCall& eax_call)
1594 const auto& eax_occlusion_lf_ratio =
1595 eax_call.get_value<EaxFxSlotException, const decltype(EAX50FXSLOTPROPERTIES::flOcclusionLFRatio)>();
1597 eax_validate_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio);
1599 return eax_set_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio);
1602 // [[nodiscard]]
1603 bool ALeffectslot::eax_set_fx_slot_all(
1604 const EaxEaxCall& eax_call)
1606 switch (eax_call.get_version())
1608 case 4:
1610 const auto& eax_all =
1611 eax_call.get_value<EaxFxSlotException, const EAX40FXSLOTPROPERTIES>();
1613 eax_validate_fx_slot_all(eax_all, eax_call.get_version());
1614 eax_set_fx_slot_all(eax_all);
1616 return false;
1619 case 5:
1621 const auto& eax_all =
1622 eax_call.get_value<EaxFxSlotException, const EAX50FXSLOTPROPERTIES>();
1624 eax_validate_fx_slot_all(eax_all, eax_call.get_version());
1625 return eax_set_fx_slot_all(eax_all);
1628 default:
1629 eax_fail("Unsupported EAX version.");
1633 bool ALeffectslot::eax_set_fx_slot(
1634 const EaxEaxCall& eax_call)
1636 switch (eax_call.get_property_id())
1638 case EAXFXSLOT_NONE:
1639 return false;
1641 case EAXFXSLOT_ALLPARAMETERS:
1642 return eax_set_fx_slot_all(eax_call);
1644 case EAXFXSLOT_LOADEFFECT:
1645 eax_set_fx_slot_effect(eax_call);
1646 return false;
1648 case EAXFXSLOT_VOLUME:
1649 eax_set_fx_slot_volume(eax_call);
1650 return false;
1652 case EAXFXSLOT_LOCK:
1653 eax_set_fx_slot_lock(eax_call);
1654 return false;
1656 case EAXFXSLOT_FLAGS:
1657 eax_set_fx_slot_flags(eax_call);
1658 return false;
1660 case EAXFXSLOT_OCCLUSION:
1661 return eax_set_fx_slot_occlusion(eax_call);
1663 case EAXFXSLOT_OCCLUSIONLFRATIO:
1664 return eax_set_fx_slot_occlusion_lf_ratio(eax_call);
1667 default:
1668 eax_fail("Unsupported FX slot property id.");
1672 // [[nodiscard]]
1673 bool ALeffectslot::eax_set(const EaxEaxCall& eax_call)
1675 switch(eax_call.get_property_set_id())
1677 case EaxEaxCallPropertySetId::fx_slot:
1678 return eax_set_fx_slot(eax_call);
1680 case EaxEaxCallPropertySetId::fx_slot_effect:
1681 eax_dispatch_effect(eax_call);
1682 break;
1684 default:
1685 eax_fail("Unsupported property id.");
1688 return false;
1691 void ALeffectslot::eax_dispatch_effect(const EaxEaxCall& eax_call)
1692 { if(eax_effect_) eax_effect_->dispatch(eax_call); }
1694 void ALeffectslot::eax_apply_deferred()
1696 /* The other FXSlot properties (volume, effect, etc) aren't deferred? */
1698 auto is_changed = false;
1699 if(eax_effect_)
1700 is_changed = eax_effect_->apply_deferred();
1701 if(is_changed)
1702 eax_set_effect_slot_effect(*eax_effect_);
1706 void ALeffectslot::eax_set_effect_slot_effect(EaxEffect &effect)
1708 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
1710 const auto error = initEffect(effect.al_effect_type_, effect.al_effect_props_, eax_al_context_);
1711 if (error != AL_NO_ERROR)
1713 ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect.");
1714 return;
1717 if (mState == SlotState::Initial)
1719 mPropsDirty = false;
1720 updateProps(eax_al_context_);
1722 auto effect_slot_ptr = this;
1724 AddActiveEffectSlots({&effect_slot_ptr, 1}, eax_al_context_);
1725 mState = SlotState::Playing;
1727 return;
1730 UpdateProps(this, eax_al_context_);
1732 #undef EAX_PREFIX
1735 void ALeffectslot::eax_set_effect_slot_send_auto(
1736 bool is_send_auto)
1738 if(AuxSendAuto == is_send_auto)
1739 return;
1741 AuxSendAuto = is_send_auto;
1742 UpdateProps(this, eax_al_context_);
1745 void ALeffectslot::eax_set_effect_slot_gain(
1746 ALfloat gain)
1748 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
1750 if(gain == Gain)
1751 return;
1752 if(gain < 0.0f || gain > 1.0f)
1753 ERR(EAX_PREFIX "Gain out of range (%f)\n", gain);
1755 Gain = clampf(gain, 0.0f, 1.0f);
1756 UpdateProps(this, eax_al_context_);
1758 #undef EAX_PREFIX
1762 void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot)
1764 assert(effect_slot);
1765 eax_delete_al_effect_slot(*effect_slot->eax_al_context_, *effect_slot);
1769 EaxAlEffectSlotUPtr eax_create_al_effect_slot(
1770 ALCcontext& context)
1772 #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
1774 std::unique_lock<std::mutex> effect_slot_lock{context.mEffectSlotLock};
1776 auto& device = *context.mALDevice;
1778 if (context.mNumEffectSlots == device.AuxiliaryEffectSlotMax)
1780 ERR(EAX_PREFIX "%s\n", "Out of memory.");
1781 return nullptr;
1784 if (!EnsureEffectSlots(&context, 1))
1786 ERR(EAX_PREFIX "%s\n", "Failed to ensure.");
1787 return nullptr;
1790 auto effect_slot = EaxAlEffectSlotUPtr{AllocEffectSlot(&context)};
1791 if (!effect_slot)
1793 ERR(EAX_PREFIX "%s\n", "Failed to allocate.");
1794 return nullptr;
1797 return effect_slot;
1799 #undef EAX_PREFIX
1802 void eax_delete_al_effect_slot(
1803 ALCcontext& context,
1804 ALeffectslot& effect_slot)
1806 #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
1808 std::lock_guard<std::mutex> effect_slot_lock{context.mEffectSlotLock};
1810 if (ReadRef(effect_slot.ref) != 0)
1812 ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id);
1813 return;
1816 auto effect_slot_ptr = &effect_slot;
1818 RemoveActiveEffectSlots({&effect_slot_ptr, 1}, &context);
1819 FreeEffectSlot(&context, &effect_slot);
1821 #undef EAX_PREFIX
1823 #endif // ALSOFT_EAX