Define the CoreAudio default name only when needed
[openal-soft.git] / al / auxeffectslot.cpp
blob50c5cda64dd6c36722dc995e7408d592cb3624fa
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 assert(factory != nullptr);
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 for(auto &sublist : context->mEffectSlotList)
1028 uint64_t usemask{~sublist.FreeMask};
1029 while(usemask)
1031 const int idx{al::countr_zero(usemask)};
1032 ALeffectslot *slot{sublist.EffectSlots + idx};
1033 usemask &= ~(1_u64 << idx);
1035 if(slot->mState != SlotState::Stopped && std::exchange(slot->mPropsDirty, false))
1036 slot->updateProps(context);
1041 EffectSlotSubList::~EffectSlotSubList()
1043 uint64_t usemask{~FreeMask};
1044 while(usemask)
1046 const int idx{al::countr_zero(usemask)};
1047 al::destroy_at(EffectSlots+idx);
1048 usemask &= ~(1_u64 << idx);
1050 FreeMask = ~usemask;
1051 al_free(EffectSlots);
1052 EffectSlots = nullptr;
1055 #ifdef ALSOFT_EAX
1056 namespace {
1058 class EaxFxSlotException :
1059 public EaxException
1061 public:
1062 explicit EaxFxSlotException(
1063 const char* message)
1065 EaxException{"EAX_FX_SLOT", message}
1068 }; // EaxFxSlotException
1071 } // namespace
1074 void ALeffectslot::eax_initialize(
1075 ALCcontext& al_context,
1076 EaxFxSlotIndexValue index)
1078 eax_al_context_ = &al_context;
1080 if (index >= EAX_MAX_FXSLOTS)
1082 eax_fail("Index out of range.");
1085 eax_fx_slot_index_ = index;
1087 eax_initialize_eax();
1088 eax_initialize_lock();
1089 eax_initialize_effects();
1092 const EAX50FXSLOTPROPERTIES& ALeffectslot::eax_get_eax_fx_slot() const noexcept
1094 return eax_eax_fx_slot_;
1097 void ALeffectslot::eax_ensure_is_unlocked() const
1099 if (eax_is_locked_)
1100 eax_fail("Locked.");
1103 void ALeffectslot::eax_validate_fx_slot_effect(
1104 const GUID& eax_effect_id)
1106 eax_ensure_is_unlocked();
1108 if (eax_effect_id != EAX_NULL_GUID &&
1109 eax_effect_id != EAX_REVERB_EFFECT &&
1110 eax_effect_id != EAX_AGCCOMPRESSOR_EFFECT &&
1111 eax_effect_id != EAX_AUTOWAH_EFFECT &&
1112 eax_effect_id != EAX_CHORUS_EFFECT &&
1113 eax_effect_id != EAX_DISTORTION_EFFECT &&
1114 eax_effect_id != EAX_ECHO_EFFECT &&
1115 eax_effect_id != EAX_EQUALIZER_EFFECT &&
1116 eax_effect_id != EAX_FLANGER_EFFECT &&
1117 eax_effect_id != EAX_FREQUENCYSHIFTER_EFFECT &&
1118 eax_effect_id != EAX_VOCALMORPHER_EFFECT &&
1119 eax_effect_id != EAX_PITCHSHIFTER_EFFECT &&
1120 eax_effect_id != EAX_RINGMODULATOR_EFFECT)
1122 eax_fail("Unsupported EAX effect GUID.");
1126 void ALeffectslot::eax_validate_fx_slot_volume(
1127 long eax_volume)
1129 eax_validate_range<EaxFxSlotException>(
1130 "Volume",
1131 eax_volume,
1132 EAXFXSLOT_MINVOLUME,
1133 EAXFXSLOT_MAXVOLUME);
1136 void ALeffectslot::eax_validate_fx_slot_lock(
1137 long eax_lock)
1139 eax_ensure_is_unlocked();
1141 eax_validate_range<EaxFxSlotException>(
1142 "Lock",
1143 eax_lock,
1144 EAXFXSLOT_MINLOCK,
1145 EAXFXSLOT_MAXLOCK);
1148 void ALeffectslot::eax_validate_fx_slot_flags(
1149 unsigned long eax_flags,
1150 int eax_version)
1152 eax_validate_range<EaxFxSlotException>(
1153 "Flags",
1154 eax_flags,
1155 0UL,
1156 ~(eax_version == 4 ? EAX40FXSLOTFLAGS_RESERVED : EAX50FXSLOTFLAGS_RESERVED));
1159 void ALeffectslot::eax_validate_fx_slot_occlusion(
1160 long eax_occlusion)
1162 eax_validate_range<EaxFxSlotException>(
1163 "Occlusion",
1164 eax_occlusion,
1165 EAXFXSLOT_MINOCCLUSION,
1166 EAXFXSLOT_MAXOCCLUSION);
1169 void ALeffectslot::eax_validate_fx_slot_occlusion_lf_ratio(
1170 float eax_occlusion_lf_ratio)
1172 eax_validate_range<EaxFxSlotException>(
1173 "Occlusion LF Ratio",
1174 eax_occlusion_lf_ratio,
1175 EAXFXSLOT_MINOCCLUSIONLFRATIO,
1176 EAXFXSLOT_MAXOCCLUSIONLFRATIO);
1179 void ALeffectslot::eax_validate_fx_slot_all(
1180 const EAX40FXSLOTPROPERTIES& fx_slot,
1181 int eax_version)
1183 eax_validate_fx_slot_effect(fx_slot.guidLoadEffect);
1184 eax_validate_fx_slot_volume(fx_slot.lVolume);
1185 eax_validate_fx_slot_lock(fx_slot.lLock);
1186 eax_validate_fx_slot_flags(fx_slot.ulFlags, eax_version);
1189 void ALeffectslot::eax_validate_fx_slot_all(
1190 const EAX50FXSLOTPROPERTIES& fx_slot,
1191 int eax_version)
1193 eax_validate_fx_slot_all(static_cast<const EAX40FXSLOTPROPERTIES&>(fx_slot), eax_version);
1195 eax_validate_fx_slot_occlusion(fx_slot.lOcclusion);
1196 eax_validate_fx_slot_occlusion_lf_ratio(fx_slot.flOcclusionLFRatio);
1199 void ALeffectslot::eax_set_fx_slot_effect(
1200 const GUID& eax_effect_id)
1202 if (eax_eax_fx_slot_.guidLoadEffect == eax_effect_id)
1204 return;
1207 eax_eax_fx_slot_.guidLoadEffect = eax_effect_id;
1209 eax_set_fx_slot_effect();
1212 void ALeffectslot::eax_set_fx_slot_volume(
1213 long eax_volume)
1215 if (eax_eax_fx_slot_.lVolume == eax_volume)
1217 return;
1220 eax_eax_fx_slot_.lVolume = eax_volume;
1222 eax_set_fx_slot_volume();
1225 void ALeffectslot::eax_set_fx_slot_lock(
1226 long eax_lock)
1228 if (eax_eax_fx_slot_.lLock == eax_lock)
1230 return;
1233 eax_eax_fx_slot_.lLock = eax_lock;
1236 void ALeffectslot::eax_set_fx_slot_flags(
1237 unsigned long eax_flags)
1239 if (eax_eax_fx_slot_.ulFlags == eax_flags)
1241 return;
1244 eax_eax_fx_slot_.ulFlags = eax_flags;
1246 eax_set_fx_slot_flags();
1249 // [[nodiscard]]
1250 bool ALeffectslot::eax_set_fx_slot_occlusion(
1251 long eax_occlusion)
1253 if (eax_eax_fx_slot_.lOcclusion == eax_occlusion)
1255 return false;
1258 eax_eax_fx_slot_.lOcclusion = eax_occlusion;
1260 return true;
1263 // [[nodiscard]]
1264 bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio(
1265 float eax_occlusion_lf_ratio)
1267 if (eax_eax_fx_slot_.flOcclusionLFRatio == eax_occlusion_lf_ratio)
1269 return false;
1272 eax_eax_fx_slot_.flOcclusionLFRatio = eax_occlusion_lf_ratio;
1274 return true;
1277 void ALeffectslot::eax_set_fx_slot_all(
1278 const EAX40FXSLOTPROPERTIES& eax_fx_slot)
1280 eax_set_fx_slot_effect(eax_fx_slot.guidLoadEffect);
1281 eax_set_fx_slot_volume(eax_fx_slot.lVolume);
1282 eax_set_fx_slot_lock(eax_fx_slot.lLock);
1283 eax_set_fx_slot_flags(eax_fx_slot.ulFlags);
1286 // [[nodiscard]]
1287 bool ALeffectslot::eax_set_fx_slot_all(
1288 const EAX50FXSLOTPROPERTIES& eax_fx_slot)
1290 eax_set_fx_slot_all(static_cast<const EAX40FXSLOTPROPERTIES&>(eax_fx_slot));
1292 const auto is_occlusion_modified = eax_set_fx_slot_occlusion(eax_fx_slot.lOcclusion);
1293 const auto is_occlusion_lf_ratio_modified = eax_set_fx_slot_occlusion_lf_ratio(eax_fx_slot.flOcclusionLFRatio);
1295 return is_occlusion_modified || is_occlusion_lf_ratio_modified;
1298 // [[nodiscard]]
1299 bool ALeffectslot::eax_dispatch(
1300 const EaxEaxCall& eax_call)
1302 return eax_call.is_get() ? eax_get(eax_call) : eax_set(eax_call);
1305 void ALeffectslot::eax_unlock_legacy() noexcept
1307 assert(eax_fx_slot_index_ < 2);
1308 eax_is_locked_ = false;
1309 eax_eax_fx_slot_.lLock = EAXFXSLOT_UNLOCKED;
1312 [[noreturn]]
1313 void ALeffectslot::eax_fail(
1314 const char* message)
1316 throw EaxFxSlotException{message};
1319 GUID ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
1321 switch (eax_fx_slot_index_)
1323 case 0: return EAX_REVERB_EFFECT;
1324 case 1: return EAX_CHORUS_EFFECT;
1325 default: return EAX_NULL_GUID;
1329 long ALeffectslot::eax_get_eax_default_lock() const noexcept
1331 return eax_fx_slot_index_ < 2 ? EAXFXSLOT_LOCKED : EAXFXSLOT_UNLOCKED;
1334 void ALeffectslot::eax_set_eax_fx_slot_defaults()
1336 eax_eax_fx_slot_.guidLoadEffect = eax_get_eax_default_effect_guid();
1337 eax_eax_fx_slot_.lVolume = EAXFXSLOT_DEFAULTVOLUME;
1338 eax_eax_fx_slot_.lLock = eax_get_eax_default_lock();
1339 eax_eax_fx_slot_.ulFlags = EAX40FXSLOT_DEFAULTFLAGS;
1340 eax_eax_fx_slot_.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
1341 eax_eax_fx_slot_.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
1344 void ALeffectslot::eax_initialize_eax()
1346 eax_set_eax_fx_slot_defaults();
1349 void ALeffectslot::eax_initialize_lock()
1351 eax_is_locked_ = (eax_fx_slot_index_ < 2);
1354 void ALeffectslot::eax_initialize_effects()
1356 eax_set_fx_slot_effect();
1359 void ALeffectslot::eax_get_fx_slot_all(
1360 const EaxEaxCall& eax_call) const
1362 switch (eax_call.get_version())
1364 case 4:
1365 eax_call.set_value<EaxFxSlotException, EAX40FXSLOTPROPERTIES>(eax_eax_fx_slot_);
1366 break;
1368 case 5:
1369 eax_call.set_value<EaxFxSlotException, EAX50FXSLOTPROPERTIES>(eax_eax_fx_slot_);
1370 break;
1372 default:
1373 eax_fail("Unsupported EAX version.");
1377 void ALeffectslot::eax_get_fx_slot(
1378 const EaxEaxCall& eax_call) const
1380 switch (eax_call.get_property_id())
1382 case EAXFXSLOT_ALLPARAMETERS:
1383 eax_get_fx_slot_all(eax_call);
1384 break;
1386 case EAXFXSLOT_LOADEFFECT:
1387 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.guidLoadEffect);
1388 break;
1390 case EAXFXSLOT_VOLUME:
1391 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lVolume);
1392 break;
1394 case EAXFXSLOT_LOCK:
1395 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lLock);
1396 break;
1398 case EAXFXSLOT_FLAGS:
1399 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.ulFlags);
1400 break;
1402 case EAXFXSLOT_OCCLUSION:
1403 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lOcclusion);
1404 break;
1406 case EAXFXSLOT_OCCLUSIONLFRATIO:
1407 eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.flOcclusionLFRatio);
1408 break;
1410 default:
1411 eax_fail("Unsupported FX slot property id.");
1415 // [[nodiscard]]
1416 bool ALeffectslot::eax_get(
1417 const EaxEaxCall& eax_call)
1419 switch (eax_call.get_property_set_id())
1421 case EaxEaxCallPropertySetId::fx_slot:
1422 eax_get_fx_slot(eax_call);
1423 break;
1425 case EaxEaxCallPropertySetId::fx_slot_effect:
1426 eax_dispatch_effect(eax_call);
1427 break;
1429 default:
1430 eax_fail("Unsupported property id.");
1433 return false;
1436 void ALeffectslot::eax_set_fx_slot_effect(
1437 ALenum al_effect_type)
1439 if(!IsValidEffectType(al_effect_type))
1440 eax_fail("Unsupported effect.");
1442 eax_effect_ = nullptr;
1443 eax_effect_ = eax_create_eax_effect(al_effect_type);
1445 eax_set_effect_slot_effect(*eax_effect_);
1448 void ALeffectslot::eax_set_fx_slot_effect()
1450 auto al_effect_type = ALenum{};
1452 if (false)
1455 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_NULL_GUID)
1457 al_effect_type = AL_EFFECT_NULL;
1459 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AUTOWAH_EFFECT)
1461 al_effect_type = AL_EFFECT_AUTOWAH;
1463 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_CHORUS_EFFECT)
1465 al_effect_type = AL_EFFECT_CHORUS;
1467 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AGCCOMPRESSOR_EFFECT)
1469 al_effect_type = AL_EFFECT_COMPRESSOR;
1471 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_DISTORTION_EFFECT)
1473 al_effect_type = AL_EFFECT_DISTORTION;
1475 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_REVERB_EFFECT)
1477 al_effect_type = AL_EFFECT_EAXREVERB;
1479 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_ECHO_EFFECT)
1481 al_effect_type = AL_EFFECT_ECHO;
1483 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_EQUALIZER_EFFECT)
1485 al_effect_type = AL_EFFECT_EQUALIZER;
1487 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FLANGER_EFFECT)
1489 al_effect_type = AL_EFFECT_FLANGER;
1491 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FREQUENCYSHIFTER_EFFECT)
1493 al_effect_type = AL_EFFECT_FREQUENCY_SHIFTER;
1495 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_PITCHSHIFTER_EFFECT)
1497 al_effect_type = AL_EFFECT_PITCH_SHIFTER;
1499 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_RINGMODULATOR_EFFECT)
1501 al_effect_type = AL_EFFECT_RING_MODULATOR;
1503 else if (eax_eax_fx_slot_.guidLoadEffect == EAX_VOCALMORPHER_EFFECT)
1505 al_effect_type = AL_EFFECT_VOCAL_MORPHER;
1507 else
1509 eax_fail("Unsupported effect.");
1512 eax_set_fx_slot_effect(al_effect_type);
1515 void ALeffectslot::eax_set_efx_effect_slot_gain()
1517 const auto gain = level_mb_to_gain(
1518 static_cast<float>(clamp(
1519 eax_eax_fx_slot_.lVolume,
1520 EAXFXSLOT_MINVOLUME,
1521 EAXFXSLOT_MAXVOLUME)));
1523 eax_set_effect_slot_gain(gain);
1526 void ALeffectslot::eax_set_fx_slot_volume()
1528 eax_set_efx_effect_slot_gain();
1531 void ALeffectslot::eax_set_effect_slot_send_auto()
1533 eax_set_effect_slot_send_auto((eax_eax_fx_slot_.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0);
1536 void ALeffectslot::eax_set_fx_slot_flags()
1538 eax_set_effect_slot_send_auto();
1541 void ALeffectslot::eax_set_fx_slot_effect(
1542 const EaxEaxCall& eax_call)
1544 const auto& eax_effect_id =
1545 eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::guidLoadEffect)>();
1547 eax_validate_fx_slot_effect(eax_effect_id);
1548 eax_set_fx_slot_effect(eax_effect_id);
1551 void ALeffectslot::eax_set_fx_slot_volume(
1552 const EaxEaxCall& eax_call)
1554 const auto& eax_volume =
1555 eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::lVolume)>();
1557 eax_validate_fx_slot_volume(eax_volume);
1558 eax_set_fx_slot_volume(eax_volume);
1561 void ALeffectslot::eax_set_fx_slot_lock(
1562 const EaxEaxCall& eax_call)
1564 const auto& eax_lock =
1565 eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::lLock)>();
1567 eax_validate_fx_slot_lock(eax_lock);
1568 eax_set_fx_slot_lock(eax_lock);
1571 void ALeffectslot::eax_set_fx_slot_flags(
1572 const EaxEaxCall& eax_call)
1574 const auto& eax_flags =
1575 eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::ulFlags)>();
1577 eax_validate_fx_slot_flags(eax_flags, eax_call.get_version());
1578 eax_set_fx_slot_flags(eax_flags);
1581 // [[nodiscard]]
1582 bool ALeffectslot::eax_set_fx_slot_occlusion(
1583 const EaxEaxCall& eax_call)
1585 const auto& eax_occlusion =
1586 eax_call.get_value<EaxFxSlotException, const decltype(EAX50FXSLOTPROPERTIES::lOcclusion)>();
1588 eax_validate_fx_slot_occlusion(eax_occlusion);
1590 return eax_set_fx_slot_occlusion(eax_occlusion);
1593 // [[nodiscard]]
1594 bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio(
1595 const EaxEaxCall& eax_call)
1597 const auto& eax_occlusion_lf_ratio =
1598 eax_call.get_value<EaxFxSlotException, const decltype(EAX50FXSLOTPROPERTIES::flOcclusionLFRatio)>();
1600 eax_validate_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio);
1602 return eax_set_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio);
1605 // [[nodiscard]]
1606 bool ALeffectslot::eax_set_fx_slot_all(
1607 const EaxEaxCall& eax_call)
1609 switch (eax_call.get_version())
1611 case 4:
1613 const auto& eax_all =
1614 eax_call.get_value<EaxFxSlotException, const EAX40FXSLOTPROPERTIES>();
1616 eax_validate_fx_slot_all(eax_all, eax_call.get_version());
1617 eax_set_fx_slot_all(eax_all);
1619 return false;
1622 case 5:
1624 const auto& eax_all =
1625 eax_call.get_value<EaxFxSlotException, const EAX50FXSLOTPROPERTIES>();
1627 eax_validate_fx_slot_all(eax_all, eax_call.get_version());
1628 return eax_set_fx_slot_all(eax_all);
1631 default:
1632 eax_fail("Unsupported EAX version.");
1636 bool ALeffectslot::eax_set_fx_slot(
1637 const EaxEaxCall& eax_call)
1639 switch (eax_call.get_property_id())
1641 case EAXFXSLOT_NONE:
1642 return false;
1644 case EAXFXSLOT_ALLPARAMETERS:
1645 return eax_set_fx_slot_all(eax_call);
1647 case EAXFXSLOT_LOADEFFECT:
1648 eax_set_fx_slot_effect(eax_call);
1649 return false;
1651 case EAXFXSLOT_VOLUME:
1652 eax_set_fx_slot_volume(eax_call);
1653 return false;
1655 case EAXFXSLOT_LOCK:
1656 eax_set_fx_slot_lock(eax_call);
1657 return false;
1659 case EAXFXSLOT_FLAGS:
1660 eax_set_fx_slot_flags(eax_call);
1661 return false;
1663 case EAXFXSLOT_OCCLUSION:
1664 return eax_set_fx_slot_occlusion(eax_call);
1666 case EAXFXSLOT_OCCLUSIONLFRATIO:
1667 return eax_set_fx_slot_occlusion_lf_ratio(eax_call);
1670 default:
1671 eax_fail("Unsupported FX slot property id.");
1675 // [[nodiscard]]
1676 bool ALeffectslot::eax_set(
1677 const EaxEaxCall& eax_call)
1679 switch (eax_call.get_property_set_id())
1681 case EaxEaxCallPropertySetId::fx_slot:
1682 return eax_set_fx_slot(eax_call);
1684 case EaxEaxCallPropertySetId::fx_slot_effect:
1685 eax_dispatch_effect(eax_call);
1686 return false;
1688 default:
1689 eax_fail("Unsupported property id.");
1693 void ALeffectslot::eax_dispatch_effect(
1694 const EaxEaxCall& eax_call)
1696 auto is_changed = false;
1697 if(eax_effect_)
1698 is_changed = eax_effect_->dispatch(eax_call);
1699 if(is_changed)
1700 eax_set_effect_slot_effect(*eax_effect_);
1703 void ALeffectslot::eax_set_effect_slot_effect(EaxEffect &effect)
1705 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
1707 const auto error = initEffect(effect.al_effect_type_, effect.al_effect_props_, eax_al_context_);
1708 if (error != AL_NO_ERROR)
1710 ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect.");
1711 return;
1714 if (mState == SlotState::Initial)
1716 mPropsDirty = false;
1717 updateProps(eax_al_context_);
1719 auto effect_slot_ptr = this;
1721 AddActiveEffectSlots({&effect_slot_ptr, 1}, eax_al_context_);
1722 mState = SlotState::Playing;
1724 return;
1727 UpdateProps(this, eax_al_context_);
1729 #undef EAX_PREFIX
1732 void ALeffectslot::eax_set_effect_slot_send_auto(
1733 bool is_send_auto)
1735 std::lock_guard<std::mutex> effect_slot_lock{eax_al_context_->mEffectSlotLock};
1737 const auto is_changed = (AuxSendAuto != is_send_auto);
1739 AuxSendAuto = is_send_auto;
1741 if (is_changed)
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 < 0.0F || gain > 1.0F)
1752 ERR(EAX_PREFIX "%s\n", "Gain out of range.");
1753 return;
1756 std::lock_guard<std::mutex> effect_slot_lock{eax_al_context_->mEffectSlotLock};
1758 const auto is_changed = (Gain != gain);
1760 Gain = gain;
1762 if (is_changed)
1763 UpdateProps(this, eax_al_context_);
1765 #undef EAX_PREFIX
1769 EaxAlEffectSlotDeleter::EaxAlEffectSlotDeleter(
1770 ALCcontext& context) noexcept
1772 context_{&context}
1776 void EaxAlEffectSlotDeleter::operator()(
1777 ALeffectslot* effect_slot)
1779 assert(effect_slot);
1780 eax_delete_al_effect_slot(*context_, *effect_slot);
1784 EaxAlEffectSlotUPtr eax_create_al_effect_slot(
1785 ALCcontext& context)
1787 #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
1789 std::unique_lock<std::mutex> effect_slot_lock{context.mEffectSlotLock};
1791 auto& device = *context.mALDevice;
1793 if (context.mNumEffectSlots == device.AuxiliaryEffectSlotMax)
1795 ERR(EAX_PREFIX "%s\n", "Out of memory.");
1796 return nullptr;
1799 if (!EnsureEffectSlots(&context, 1))
1801 ERR(EAX_PREFIX "%s\n", "Failed to ensure.");
1802 return nullptr;
1805 auto effect_slot = EaxAlEffectSlotUPtr{AllocEffectSlot(&context), EaxAlEffectSlotDeleter{context}};
1807 if (!effect_slot)
1809 ERR(EAX_PREFIX "%s\n", "Failed to allocate.");
1810 return nullptr;
1813 return effect_slot;
1815 #undef EAX_PREFIX
1818 void eax_delete_al_effect_slot(
1819 ALCcontext& context,
1820 ALeffectslot& effect_slot)
1822 #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
1824 std::lock_guard<std::mutex> effect_slot_lock{context.mEffectSlotLock};
1826 if (ReadRef(effect_slot.ref) != 0)
1828 ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id);
1829 return;
1832 auto effect_slot_ptr = &effect_slot;
1834 RemoveActiveEffectSlots({&effect_slot_ptr, 1}, &context);
1835 FreeEffectSlot(&context, &effect_slot);
1837 #undef EAX_PREFIX
1839 #endif // ALSOFT_EAX