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
23 #include "auxeffectslot.h"
40 #include "alc/context.h"
41 #include "alc/device.h"
42 #include "alc/inprogext.h"
44 #include "alnumeric.h"
47 #include "core/except.h"
48 #include "core/fpu_ctrl.h"
49 #include "core/logging.h"
51 #include "opthelpers.h"
57 EffectStateFactory* (&GetFactory)(void);
59 constexpr FactoryItem FactoryList[] = {
60 { EffectSlotType::None, NullStateFactory_getFactory },
61 { EffectSlotType::EAXReverb, ReverbStateFactory_getFactory },
62 { EffectSlotType::Reverb, StdReverbStateFactory_getFactory },
63 { EffectSlotType::Autowah, AutowahStateFactory_getFactory },
64 { EffectSlotType::Chorus, ChorusStateFactory_getFactory },
65 { EffectSlotType::Compressor, CompressorStateFactory_getFactory },
66 { EffectSlotType::Distortion, DistortionStateFactory_getFactory },
67 { EffectSlotType::Echo, EchoStateFactory_getFactory },
68 { EffectSlotType::Equalizer, EqualizerStateFactory_getFactory },
69 { EffectSlotType::Flanger, FlangerStateFactory_getFactory },
70 { EffectSlotType::FrequencyShifter, FshifterStateFactory_getFactory },
71 { EffectSlotType::RingModulator, ModulatorStateFactory_getFactory },
72 { EffectSlotType::PitchShifter, PshifterStateFactory_getFactory },
73 { EffectSlotType::VocalMorpher, VmorpherStateFactory_getFactory },
74 { EffectSlotType::DedicatedDialog, DedicatedStateFactory_getFactory },
75 { EffectSlotType::DedicatedLFE, DedicatedStateFactory_getFactory },
76 { EffectSlotType::Convolution, ConvolutionStateFactory_getFactory },
79 EffectStateFactory *getFactoryByType(EffectSlotType type)
81 auto iter = std::find_if(std::begin(FactoryList), std::end(FactoryList),
82 [type](const FactoryItem &item) noexcept -> bool
83 { return item.Type == type; });
84 return (iter != std::end(FactoryList)) ? iter->GetFactory() : nullptr;
88 inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
90 const size_t lidx{(id-1) >> 6};
91 const ALuint slidx{(id-1) & 0x3f};
93 if(lidx >= context->mEffectSlotList.size()) UNLIKELY
95 EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
96 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
98 return sublist.EffectSlots + slidx;
101 inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept
103 const size_t lidx{(id-1) >> 6};
104 const ALuint slidx{(id-1) & 0x3f};
106 if(lidx >= device->EffectList.size()) UNLIKELY
108 EffectSubList &sublist = device->EffectList[lidx];
109 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
111 return sublist.Effects + slidx;
114 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
116 const size_t lidx{(id-1) >> 6};
117 const ALuint slidx{(id-1) & 0x3f};
119 if(lidx >= device->BufferList.size()) UNLIKELY
121 BufferSubList &sublist = device->BufferList[lidx];
122 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
124 return sublist.Buffers + slidx;
128 void AddActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
130 if(auxslots.empty()) return;
131 EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
132 size_t newcount{curarray->size() + auxslots.size()};
134 /* Insert the new effect slots into the head of the array, followed by the
137 EffectSlotArray *newarray = EffectSlot::CreatePtrArray(newcount);
138 auto slotiter = std::transform(auxslots.begin(), auxslots.end(), newarray->begin(),
139 [](ALeffectslot *auxslot) noexcept { return auxslot->mSlot; });
140 std::copy(curarray->begin(), curarray->end(), slotiter);
142 /* Remove any duplicates (first instance of each will be kept). */
143 auto last = newarray->end();
144 for(auto start=newarray->begin()+1;;)
146 last = std::remove(start, last, *(start-1));
147 if(start == last) break;
150 newcount = static_cast<size_t>(std::distance(newarray->begin(), last));
152 /* Reallocate newarray if the new size ended up smaller from duplicate
155 if(newcount < newarray->size()) UNLIKELY
158 newarray = EffectSlot::CreatePtrArray(newcount);
159 std::copy_n(curarray->begin(), newcount, newarray->begin());
163 std::uninitialized_fill_n(newarray->end(), newcount, nullptr);
165 curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
166 context->mDevice->waitForMix();
168 al::destroy_n(curarray->end(), curarray->size());
172 void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
174 if(auxslots.empty()) return;
175 EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
177 /* Don't shrink the allocated array size since we don't know how many (if
178 * any) of the effect slots to remove are in the array.
180 EffectSlotArray *newarray = EffectSlot::CreatePtrArray(curarray->size());
182 auto new_end = std::copy(curarray->begin(), curarray->end(), newarray->begin());
183 /* Remove elements from newarray that match any ID in slotids. */
184 for(const ALeffectslot *auxslot : auxslots)
186 auto slot_match = [auxslot](EffectSlot *slot) noexcept -> bool
187 { return (slot == auxslot->mSlot); };
188 new_end = std::remove_if(newarray->begin(), new_end, slot_match);
191 /* Reallocate with the new size. */
192 auto newsize = static_cast<size_t>(std::distance(newarray->begin(), new_end));
193 if(newsize != newarray->size()) LIKELY
196 newarray = EffectSlot::CreatePtrArray(newsize);
197 std::copy_n(curarray->begin(), newsize, newarray->begin());
202 std::uninitialized_fill_n(newarray->end(), newsize, nullptr);
204 curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
205 context->mDevice->waitForMix();
207 al::destroy_n(curarray->end(), curarray->size());
212 EffectSlotType EffectSlotTypeFromEnum(ALenum type)
216 case AL_EFFECT_NULL: return EffectSlotType::None;
217 case AL_EFFECT_REVERB: return EffectSlotType::Reverb;
218 case AL_EFFECT_CHORUS: return EffectSlotType::Chorus;
219 case AL_EFFECT_DISTORTION: return EffectSlotType::Distortion;
220 case AL_EFFECT_ECHO: return EffectSlotType::Echo;
221 case AL_EFFECT_FLANGER: return EffectSlotType::Flanger;
222 case AL_EFFECT_FREQUENCY_SHIFTER: return EffectSlotType::FrequencyShifter;
223 case AL_EFFECT_VOCAL_MORPHER: return EffectSlotType::VocalMorpher;
224 case AL_EFFECT_PITCH_SHIFTER: return EffectSlotType::PitchShifter;
225 case AL_EFFECT_RING_MODULATOR: return EffectSlotType::RingModulator;
226 case AL_EFFECT_AUTOWAH: return EffectSlotType::Autowah;
227 case AL_EFFECT_COMPRESSOR: return EffectSlotType::Compressor;
228 case AL_EFFECT_EQUALIZER: return EffectSlotType::Equalizer;
229 case AL_EFFECT_EAXREVERB: return EffectSlotType::EAXReverb;
230 case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::DedicatedLFE;
231 case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::DedicatedDialog;
232 case AL_EFFECT_CONVOLUTION_REVERB_SOFT: return EffectSlotType::Convolution;
234 ERR("Unhandled effect enum: 0x%04x\n", type);
235 return EffectSlotType::None;
238 bool EnsureEffectSlots(ALCcontext *context, size_t needed)
240 size_t count{std::accumulate(context->mEffectSlotList.cbegin(),
241 context->mEffectSlotList.cend(), size_t{0},
242 [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
243 { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
245 while(needed > count)
247 if(context->mEffectSlotList.size() >= 1<<25) UNLIKELY
250 context->mEffectSlotList.emplace_back();
251 auto sublist = context->mEffectSlotList.end() - 1;
252 sublist->FreeMask = ~0_u64;
253 sublist->EffectSlots = static_cast<ALeffectslot*>(
254 al_calloc(alignof(ALeffectslot), sizeof(ALeffectslot)*64));
255 if(!sublist->EffectSlots) UNLIKELY
257 context->mEffectSlotList.pop_back();
265 ALeffectslot *AllocEffectSlot(ALCcontext *context)
267 auto sublist = std::find_if(context->mEffectSlotList.begin(), context->mEffectSlotList.end(),
268 [](const EffectSlotSubList &entry) noexcept -> bool
269 { return entry.FreeMask != 0; });
270 auto lidx = static_cast<ALuint>(std::distance(context->mEffectSlotList.begin(), sublist));
271 auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
274 ALeffectslot *slot{al::construct_at(sublist->EffectSlots + slidx, context)};
275 aluInitEffectPanning(slot->mSlot, context);
277 /* Add 1 to avoid ID 0. */
278 slot->id = ((lidx<<6) | slidx) + 1;
280 context->mNumEffectSlots += 1;
281 sublist->FreeMask &= ~(1_u64 << slidx);
286 void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
288 const ALuint id{slot->id - 1};
289 const size_t lidx{id >> 6};
290 const ALuint slidx{id & 0x3f};
292 al::destroy_at(slot);
294 context->mEffectSlotList[lidx].FreeMask |= 1_u64 << slidx;
295 context->mNumEffectSlots--;
299 inline void UpdateProps(ALeffectslot *slot, ALCcontext *context)
301 if(!context->mDeferUpdates && slot->mState == SlotState::Playing)
303 slot->updateProps(context);
306 slot->mPropsDirty = true;
312 AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
315 ContextRef context{GetContextRef()};
316 if(!context) UNLIKELY return;
319 context->setError(AL_INVALID_VALUE, "Generating %d effect slots", n);
320 if(n <= 0) UNLIKELY return;
322 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
323 ALCdevice *device{context->mALDevice.get()};
324 if(static_cast<ALuint>(n) > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
326 context->setError(AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)",
327 device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n);
330 if(!EnsureEffectSlots(context.get(), static_cast<ALuint>(n)))
332 context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n,
339 ALeffectslot *slot{AllocEffectSlot(context.get())};
340 effectslots[0] = slot->id;
344 al::vector<ALuint> ids;
346 ids.reserve(static_cast<ALuint>(count));
348 ALeffectslot *slot{AllocEffectSlot(context.get())};
349 ids.emplace_back(slot->id);
351 std::copy(ids.cbegin(), ids.cend(), effectslots);
356 AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots)
359 ContextRef context{GetContextRef()};
360 if(!context) UNLIKELY return;
363 context->setError(AL_INVALID_VALUE, "Deleting %d effect slots", n);
364 if(n <= 0) UNLIKELY return;
366 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
369 ALeffectslot *slot{LookupEffectSlot(context.get(), effectslots[0])};
372 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[0]);
375 if(ReadRef(slot->ref) != 0) UNLIKELY
377 context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
381 RemoveActiveEffectSlots({&slot, 1u}, context.get());
382 FreeEffectSlot(context.get(), slot);
386 auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
387 for(size_t i{0};i < slots.size();++i)
389 ALeffectslot *slot{LookupEffectSlot(context.get(), effectslots[i])};
392 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[i]);
395 if(ReadRef(slot->ref) != 0) UNLIKELY
397 context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
403 /* Remove any duplicates. */
404 auto slots_end = slots.end();
405 for(auto start=slots.begin()+1;start != slots_end;++start)
407 slots_end = std::remove(start, slots_end, *(start-1));
408 if(start == slots_end) break;
410 slots.erase(slots_end, slots.end());
412 /* All effectslots are valid, remove and delete them */
413 RemoveActiveEffectSlots(slots, context.get());
414 for(ALeffectslot *slot : slots)
415 FreeEffectSlot(context.get(), slot);
420 AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
423 ContextRef context{GetContextRef()};
426 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
427 if(LookupEffectSlot(context.get(), effectslot) != nullptr)
435 AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid)
438 ContextRef context{GetContextRef()};
439 if(!context) UNLIKELY return;
441 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
442 ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
445 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
448 if(slot->mState == SlotState::Playing)
451 slot->mPropsDirty = false;
452 slot->updateProps(context.get());
454 AddActiveEffectSlots({&slot, 1}, context.get());
455 slot->mState = SlotState::Playing;
459 AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids)
462 ContextRef context{GetContextRef()};
463 if(!context) UNLIKELY return;
466 context->setError(AL_INVALID_VALUE, "Playing %d effect slots", n);
467 if(n <= 0) UNLIKELY return;
469 auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
470 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
471 for(size_t i{0};i < slots.size();++i)
473 ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])};
476 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]);
480 if(slot->mState != SlotState::Playing)
482 slot->mPropsDirty = false;
483 slot->updateProps(context.get());
488 AddActiveEffectSlots(slots, context.get());
489 for(auto slot : slots)
490 slot->mState = SlotState::Playing;
494 AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid)
497 ContextRef context{GetContextRef()};
498 if(!context) UNLIKELY return;
500 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
501 ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
504 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
508 RemoveActiveEffectSlots({&slot, 1}, context.get());
509 slot->mState = SlotState::Stopped;
513 AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids)
516 ContextRef context{GetContextRef()};
517 if(!context) UNLIKELY return;
520 context->setError(AL_INVALID_VALUE, "Stopping %d effect slots", n);
521 if(n <= 0) UNLIKELY return;
523 auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
524 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
525 for(size_t i{0};i < slots.size();++i)
527 ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])};
530 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]);
537 RemoveActiveEffectSlots(slots, context.get());
538 for(auto slot : slots)
539 slot->mState = SlotState::Stopped;
544 AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value)
547 ContextRef context{GetContextRef()};
548 if(!context) UNLIKELY return;
550 std::lock_guard<std::mutex> _{context->mPropLock};
551 std::lock_guard<std::mutex> __{context->mEffectSlotLock};
552 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
554 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
556 ALeffectslot *target{};
561 case AL_EFFECTSLOT_EFFECT:
562 device = context->mALDevice.get();
565 std::lock_guard<std::mutex> ___{device->EffectLock};
566 ALeffect *effect{value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr};
568 err = slot->initEffect(effect->type, effect->Props, context.get());
572 return context->setError(AL_INVALID_VALUE, "Invalid effect ID %u", value);
573 err = slot->initEffect(AL_EFFECT_NULL, EffectProps{}, context.get());
576 if(err != AL_NO_ERROR) UNLIKELY
578 context->setError(err, "Effect initialization failed");
581 if(slot->mState == SlotState::Initial) UNLIKELY
583 slot->mPropsDirty = false;
584 slot->updateProps(context.get());
586 AddActiveEffectSlots({&slot, 1}, context.get());
587 slot->mState = SlotState::Playing;
592 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
593 if(!(value == AL_TRUE || value == AL_FALSE))
594 return context->setError(AL_INVALID_VALUE,
595 "Effect slot auxiliary send auto out of range");
596 if(slot->AuxSendAuto == !!value) UNLIKELY
598 slot->AuxSendAuto = !!value;
601 case AL_EFFECTSLOT_TARGET_SOFT:
602 target = LookupEffectSlot(context.get(), static_cast<ALuint>(value));
604 return context->setError(AL_INVALID_VALUE, "Invalid effect slot target ID");
605 if(slot->Target == target) UNLIKELY
609 ALeffectslot *checker{target};
610 while(checker && checker != slot)
611 checker = checker->Target;
613 return context->setError(AL_INVALID_OPERATION,
614 "Setting target of effect slot ID %u to %u creates circular chain", slot->id,
618 if(ALeffectslot *oldtarget{slot->Target})
620 /* We must force an update if there was an existing effect slot
621 * target, in case it's about to be deleted.
623 if(target) IncrementRef(target->ref);
624 DecrementRef(oldtarget->ref);
625 slot->Target = target;
626 slot->updateProps(context.get());
630 if(target) IncrementRef(target->ref);
631 slot->Target = target;
635 device = context->mALDevice.get();
637 if(slot->mState == SlotState::Playing)
638 return context->setError(AL_INVALID_OPERATION,
639 "Setting buffer on playing effect slot %u", slot->id);
641 if(ALbuffer *buffer{slot->Buffer})
643 if(buffer->id == static_cast<ALuint>(value)) UNLIKELY
646 else if(value == 0) UNLIKELY
650 std::lock_guard<std::mutex> ___{device->BufferLock};
654 buffer = LookupBuffer(device, static_cast<ALuint>(value));
655 if(!buffer) return context->setError(AL_INVALID_VALUE, "Invalid buffer ID");
656 if(buffer->mCallback)
657 return context->setError(AL_INVALID_OPERATION,
658 "Callback buffer not valid for effects");
660 IncrementRef(buffer->ref);
663 if(ALbuffer *oldbuffer{slot->Buffer})
664 DecrementRef(oldbuffer->ref);
665 slot->Buffer = buffer;
668 auto *state = slot->Effect.State.get();
669 state->deviceUpdate(device, buffer);
673 case AL_EFFECTSLOT_STATE_SOFT:
674 return context->setError(AL_INVALID_OPERATION, "AL_EFFECTSLOT_STATE_SOFT is read-only");
677 return context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x",
680 UpdateProps(slot, context.get());
684 AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
689 case AL_EFFECTSLOT_EFFECT:
690 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
691 case AL_EFFECTSLOT_TARGET_SOFT:
692 case AL_EFFECTSLOT_STATE_SOFT:
694 alAuxiliaryEffectSloti(effectslot, param, values[0]);
698 ContextRef context{GetContextRef()};
699 if(!context) UNLIKELY return;
701 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
702 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
704 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
709 return context->setError(AL_INVALID_ENUM,
710 "Invalid effect slot integer-vector property 0x%04x", param);
715 AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
718 ContextRef context{GetContextRef()};
719 if(!context) UNLIKELY return;
721 std::lock_guard<std::mutex> _{context->mPropLock};
722 std::lock_guard<std::mutex> __{context->mEffectSlotLock};
723 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
725 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
729 case AL_EFFECTSLOT_GAIN:
730 if(!(value >= 0.0f && value <= 1.0f))
731 return context->setError(AL_INVALID_VALUE, "Effect slot gain out of range");
732 if(slot->Gain == value) UNLIKELY
738 return context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x",
741 UpdateProps(slot, context.get());
745 AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
750 case AL_EFFECTSLOT_GAIN:
751 alAuxiliaryEffectSlotf(effectslot, param, values[0]);
755 ContextRef context{GetContextRef()};
756 if(!context) UNLIKELY return;
758 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
759 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
761 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
766 return context->setError(AL_INVALID_ENUM,
767 "Invalid effect slot float-vector property 0x%04x", param);
773 AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
776 ContextRef context{GetContextRef()};
777 if(!context) UNLIKELY return;
779 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
780 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
782 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
786 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
787 *value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE;
790 case AL_EFFECTSLOT_TARGET_SOFT:
791 if(auto *target = slot->Target)
792 *value = static_cast<ALint>(target->id);
797 case AL_EFFECTSLOT_STATE_SOFT:
798 *value = static_cast<int>(slot->mState);
802 if(auto *buffer = slot->Buffer)
803 *value = static_cast<ALint>(buffer->id);
809 context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param);
814 AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
819 case AL_EFFECTSLOT_EFFECT:
820 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
821 case AL_EFFECTSLOT_TARGET_SOFT:
822 case AL_EFFECTSLOT_STATE_SOFT:
824 alGetAuxiliaryEffectSloti(effectslot, param, values);
828 ContextRef context{GetContextRef()};
829 if(!context) UNLIKELY return;
831 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
832 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
834 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
839 context->setError(AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
845 AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
848 ContextRef context{GetContextRef()};
849 if(!context) UNLIKELY return;
851 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
852 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
854 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
858 case AL_EFFECTSLOT_GAIN:
863 context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param);
868 AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
873 case AL_EFFECTSLOT_GAIN:
874 alGetAuxiliaryEffectSlotf(effectslot, param, values);
878 ContextRef context{GetContextRef()};
879 if(!context) UNLIKELY return;
881 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
882 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
884 return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
889 context->setError(AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
896 ALeffectslot::ALeffectslot(ALCcontext *context)
898 EffectStateFactory *factory{getFactoryByType(EffectSlotType::None)};
899 if(!factory) throw std::runtime_error{"Failed to get null effect factory"};
901 al::intrusive_ptr<EffectState> state{factory->create()};
902 Effect.State = state;
904 mSlot = context->getEffectSlot();
906 mSlot->mEffectState = std::move(state);
909 ALeffectslot::~ALeffectslot()
912 DecrementRef(Target->ref);
915 DecrementRef(Buffer->ref);
918 if(EffectSlotProps *props{mSlot->Update.exchange(nullptr)})
920 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n",
921 decltype(std::declval<void*>()){props});
925 mSlot->mEffectState = nullptr;
926 mSlot->InUse = false;
929 ALenum ALeffectslot::initEffect(ALenum effectType, const EffectProps &effectProps,
932 EffectSlotType newtype{EffectSlotTypeFromEnum(effectType)};
933 if(newtype != Effect.Type)
935 EffectStateFactory *factory{getFactoryByType(newtype)};
938 ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype));
939 return AL_INVALID_ENUM;
941 al::intrusive_ptr<EffectState> state{factory->create()};
943 ALCdevice *device{context->mALDevice.get()};
944 std::unique_lock<std::mutex> statelock{device->StateLock};
945 state->mOutTarget = device->Dry.Buffer;
948 state->deviceUpdate(device, Buffer);
951 Effect.Type = newtype;
952 Effect.Props = effectProps;
954 Effect.State = std::move(state);
956 else if(newtype != EffectSlotType::None)
957 Effect.Props = effectProps;
959 /* Remove state references from old effect slot property updates. */
960 EffectSlotProps *props{context->mFreeEffectslotProps.load()};
963 props->State = nullptr;
964 props = props->next.load(std::memory_order_relaxed);
970 void ALeffectslot::updateProps(ALCcontext *context)
972 /* Get an unused property container, or allocate a new one as needed. */
973 EffectSlotProps *props{context->mFreeEffectslotProps.load(std::memory_order_relaxed)};
975 props = new EffectSlotProps{};
978 EffectSlotProps *next;
980 next = props->next.load(std::memory_order_relaxed);
981 } while(context->mFreeEffectslotProps.compare_exchange_weak(props, next,
982 std::memory_order_seq_cst, std::memory_order_acquire) == 0);
985 /* Copy in current property values. */
987 props->AuxSendAuto = AuxSendAuto;
988 props->Target = Target ? Target->mSlot : nullptr;
990 props->Type = Effect.Type;
991 props->Props = Effect.Props;
992 props->State = Effect.State;
994 /* Set the new container for updating internal parameters. */
995 props = mSlot->Update.exchange(props, std::memory_order_acq_rel);
998 /* If there was an unused update container, put it back in the
1001 props->State = nullptr;
1002 AtomicReplaceHead(context->mFreeEffectslotProps, props);
1006 void UpdateAllEffectSlotProps(ALCcontext *context)
1008 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
1009 for(auto &sublist : context->mEffectSlotList)
1011 uint64_t usemask{~sublist.FreeMask};
1014 const int idx{al::countr_zero(usemask)};
1015 usemask &= ~(1_u64 << idx);
1016 ALeffectslot *slot{sublist.EffectSlots + idx};
1018 if(slot->mState != SlotState::Stopped && std::exchange(slot->mPropsDirty, false))
1019 slot->updateProps(context);
1024 EffectSlotSubList::~EffectSlotSubList()
1026 uint64_t usemask{~FreeMask};
1029 const int idx{al::countr_zero(usemask)};
1030 al::destroy_at(EffectSlots+idx);
1031 usemask &= ~(1_u64 << idx);
1033 FreeMask = ~usemask;
1034 al_free(EffectSlots);
1035 EffectSlots = nullptr;
1039 void ALeffectslot::eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index)
1041 if(index >= EAX_MAX_FXSLOTS)
1042 eax_fail("Index out of range.");
1044 eax_al_context_ = &al_context;
1045 eax_fx_slot_index_ = index;
1046 eax_fx_slot_set_defaults();
1048 eax_effect_ = std::make_unique<EaxEffect>();
1049 if(index == 0) eax_effect_->init<EaxReverbCommitter>();
1050 else if(index == 1) eax_effect_->init<EaxChorusCommitter>();
1051 else eax_effect_->init<EaxNullCommitter>();
1054 void ALeffectslot::eax_commit()
1056 if(eax_df_ != EaxDirtyFlags{})
1058 auto df = EaxDirtyFlags{};
1059 switch(eax_version_)
1064 eax5_fx_slot_commit(eax123_, df);
1067 eax4_fx_slot_commit(df);
1070 eax5_fx_slot_commit(eax5_, df);
1073 eax_df_ = EaxDirtyFlags{};
1075 if((df & eax_volume_dirty_bit) != EaxDirtyFlags{})
1076 eax_fx_slot_set_volume();
1077 if((df & eax_flags_dirty_bit) != EaxDirtyFlags{})
1078 eax_fx_slot_set_flags();
1081 if(eax_effect_->commit(eax_version_))
1082 eax_set_efx_slot_effect(*eax_effect_);
1085 [[noreturn]] void ALeffectslot::eax_fail(const char* message)
1087 throw Exception{message};
1090 [[noreturn]] void ALeffectslot::eax_fail_unknown_effect_id()
1092 eax_fail("Unknown effect ID.");
1095 [[noreturn]] void ALeffectslot::eax_fail_unknown_property_id()
1097 eax_fail("Unknown property ID.");
1100 [[noreturn]] void ALeffectslot::eax_fail_unknown_version()
1102 eax_fail("Unknown version.");
1105 void ALeffectslot::eax4_fx_slot_ensure_unlocked() const
1107 if(eax4_fx_slot_is_legacy())
1108 eax_fail("Locked legacy slot.");
1111 ALenum ALeffectslot::eax_get_efx_effect_type(const GUID& guid)
1113 if(guid == EAX_NULL_GUID)
1114 return AL_EFFECT_NULL;
1115 if(guid == EAX_AUTOWAH_EFFECT)
1116 return AL_EFFECT_AUTOWAH;
1117 if(guid == EAX_CHORUS_EFFECT)
1118 return AL_EFFECT_CHORUS;
1119 if(guid == EAX_AGCCOMPRESSOR_EFFECT)
1120 return AL_EFFECT_COMPRESSOR;
1121 if(guid == EAX_DISTORTION_EFFECT)
1122 return AL_EFFECT_DISTORTION;
1123 if(guid == EAX_REVERB_EFFECT)
1124 return AL_EFFECT_EAXREVERB;
1125 if(guid == EAX_ECHO_EFFECT)
1126 return AL_EFFECT_ECHO;
1127 if(guid == EAX_EQUALIZER_EFFECT)
1128 return AL_EFFECT_EQUALIZER;
1129 if(guid == EAX_FLANGER_EFFECT)
1130 return AL_EFFECT_FLANGER;
1131 if(guid == EAX_FREQUENCYSHIFTER_EFFECT)
1132 return AL_EFFECT_FREQUENCY_SHIFTER;
1133 if(guid == EAX_PITCHSHIFTER_EFFECT)
1134 return AL_EFFECT_PITCH_SHIFTER;
1135 if(guid == EAX_RINGMODULATOR_EFFECT)
1136 return AL_EFFECT_RING_MODULATOR;
1137 if(guid == EAX_VOCALMORPHER_EFFECT)
1138 return AL_EFFECT_VOCAL_MORPHER;
1140 eax_fail_unknown_effect_id();
1143 const GUID& ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
1145 switch(eax_fx_slot_index_)
1147 case 0: return EAX_REVERB_EFFECT;
1148 case 1: return EAX_CHORUS_EFFECT;
1149 default: return EAX_NULL_GUID;
1153 long ALeffectslot::eax_get_eax_default_lock() const noexcept
1155 return eax4_fx_slot_is_legacy() ? EAXFXSLOT_LOCKED : EAXFXSLOT_UNLOCKED;
1158 void ALeffectslot::eax4_fx_slot_set_defaults(Eax4Props& props) noexcept
1160 props.guidLoadEffect = eax_get_eax_default_effect_guid();
1161 props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
1162 props.lLock = eax_get_eax_default_lock();
1163 props.ulFlags = EAX40FXSLOT_DEFAULTFLAGS;
1166 void ALeffectslot::eax5_fx_slot_set_defaults(Eax5Props& props) noexcept
1168 props.guidLoadEffect = eax_get_eax_default_effect_guid();
1169 props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
1170 props.lLock = EAXFXSLOT_UNLOCKED;
1171 props.ulFlags = EAX50FXSLOT_DEFAULTFLAGS;
1172 props.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
1173 props.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
1176 void ALeffectslot::eax_fx_slot_set_defaults()
1178 eax5_fx_slot_set_defaults(eax123_.i);
1179 eax4_fx_slot_set_defaults(eax4_.i);
1180 eax5_fx_slot_set_defaults(eax5_.i);
1182 eax_df_ = EaxDirtyFlags{};
1185 void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) const
1187 switch(call.get_property_id())
1189 case EAXFXSLOT_ALLPARAMETERS:
1190 call.set_value<Exception>(props);
1192 case EAXFXSLOT_LOADEFFECT:
1193 call.set_value<Exception>(props.guidLoadEffect);
1195 case EAXFXSLOT_VOLUME:
1196 call.set_value<Exception>(props.lVolume);
1198 case EAXFXSLOT_LOCK:
1199 call.set_value<Exception>(props.lLock);
1201 case EAXFXSLOT_FLAGS:
1202 call.set_value<Exception>(props.ulFlags);
1205 eax_fail_unknown_property_id();
1209 void ALeffectslot::eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) const
1211 switch(call.get_property_id())
1213 case EAXFXSLOT_ALLPARAMETERS:
1214 call.set_value<Exception>(props);
1216 case EAXFXSLOT_LOADEFFECT:
1217 call.set_value<Exception>(props.guidLoadEffect);
1219 case EAXFXSLOT_VOLUME:
1220 call.set_value<Exception>(props.lVolume);
1222 case EAXFXSLOT_LOCK:
1223 call.set_value<Exception>(props.lLock);
1225 case EAXFXSLOT_FLAGS:
1226 call.set_value<Exception>(props.ulFlags);
1228 case EAXFXSLOT_OCCLUSION:
1229 call.set_value<Exception>(props.lOcclusion);
1231 case EAXFXSLOT_OCCLUSIONLFRATIO:
1232 call.set_value<Exception>(props.flOcclusionLFRatio);
1235 eax_fail_unknown_property_id();
1239 void ALeffectslot::eax_fx_slot_get(const EaxCall& call) const
1241 switch(call.get_version())
1243 case 4: eax4_fx_slot_get(call, eax4_.i); break;
1244 case 5: eax5_fx_slot_get(call, eax5_.i); break;
1245 default: eax_fail_unknown_version();
1249 bool ALeffectslot::eax_get(const EaxCall& call)
1251 switch(call.get_property_set_id())
1253 case EaxCallPropertySetId::fx_slot:
1254 eax_fx_slot_get(call);
1256 case EaxCallPropertySetId::fx_slot_effect:
1257 eax_effect_->get(call);
1260 eax_fail_unknown_property_id();
1266 void ALeffectslot::eax_fx_slot_load_effect(int version, ALenum altype)
1268 if(!IsValidEffectType(altype))
1269 altype = AL_EFFECT_NULL;
1270 eax_effect_->set_defaults(version, altype);
1273 void ALeffectslot::eax_fx_slot_set_volume()
1275 const auto volume = clamp(eax_.lVolume, EAXFXSLOT_MINVOLUME, EAXFXSLOT_MAXVOLUME);
1276 const auto gain = level_mb_to_gain(static_cast<float>(volume));
1277 eax_set_efx_slot_gain(gain);
1280 void ALeffectslot::eax_fx_slot_set_environment_flag()
1282 eax_set_efx_slot_send_auto((eax_.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0u);
1285 void ALeffectslot::eax_fx_slot_set_flags()
1287 eax_fx_slot_set_environment_flag();
1290 void ALeffectslot::eax4_fx_slot_set_all(const EaxCall& call)
1292 eax4_fx_slot_ensure_unlocked();
1293 const auto& src = call.get_value<Exception, const EAX40FXSLOTPROPERTIES>();
1294 Eax4AllValidator{}(src);
1295 auto& dst = eax4_.i;
1296 eax_df_ |= eax_load_effect_dirty_bit; // Always reset the effect.
1297 eax_df_ |= (dst.lVolume != src.lVolume ? eax_volume_dirty_bit : EaxDirtyFlags{});
1298 eax_df_ |= (dst.lLock != src.lLock ? eax_lock_dirty_bit : EaxDirtyFlags{});
1299 eax_df_ |= (dst.ulFlags != src.ulFlags ? eax_flags_dirty_bit : EaxDirtyFlags{});
1303 void ALeffectslot::eax5_fx_slot_set_all(const EaxCall& call)
1305 const auto& src = call.get_value<Exception, const EAX50FXSLOTPROPERTIES>();
1306 Eax5AllValidator{}(src);
1307 auto& dst = eax5_.i;
1308 eax_df_ |= eax_load_effect_dirty_bit; // Always reset the effect.
1309 eax_df_ |= (dst.lVolume != src.lVolume ? eax_volume_dirty_bit : EaxDirtyFlags{});
1310 eax_df_ |= (dst.lLock != src.lLock ? eax_lock_dirty_bit : EaxDirtyFlags{});
1311 eax_df_ |= (dst.ulFlags != src.ulFlags ? eax_flags_dirty_bit : EaxDirtyFlags{});
1312 eax_df_ |= (dst.lOcclusion != src.lOcclusion ? eax_flags_dirty_bit : EaxDirtyFlags{});
1313 eax_df_ |= (dst.flOcclusionLFRatio != src.flOcclusionLFRatio ? eax_flags_dirty_bit : EaxDirtyFlags{});
1317 bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept
1319 const auto dirty_bits =
1320 eax_occlusion_dirty_bit |
1321 eax_occlusion_lf_ratio_dirty_bit |
1322 eax_flags_dirty_bit;
1324 if((eax_df_ & dirty_bits) != EaxDirtyFlags{})
1330 // Returns `true` if all sources should be updated, or `false` otherwise.
1331 bool ALeffectslot::eax4_fx_slot_set(const EaxCall& call)
1333 auto& dst = eax4_.i;
1335 switch(call.get_property_id())
1337 case EAXFXSLOT_NONE:
1339 case EAXFXSLOT_ALLPARAMETERS:
1340 eax4_fx_slot_set_all(call);
1341 if((eax_df_ & eax_load_effect_dirty_bit))
1342 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
1344 case EAXFXSLOT_LOADEFFECT:
1345 eax4_fx_slot_ensure_unlocked();
1346 eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call, dst.guidLoadEffect, eax_df_);
1347 if((eax_df_ & eax_load_effect_dirty_bit))
1348 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
1350 case EAXFXSLOT_VOLUME:
1351 eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, eax_df_);
1353 case EAXFXSLOT_LOCK:
1354 eax4_fx_slot_ensure_unlocked();
1355 eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, eax_df_);
1357 case EAXFXSLOT_FLAGS:
1358 eax_fx_slot_set<Eax4FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, eax_df_);
1361 eax_fail_unknown_property_id();
1364 return eax_fx_slot_should_update_sources();
1367 // Returns `true` if all sources should be updated, or `false` otherwise.
1368 bool ALeffectslot::eax5_fx_slot_set(const EaxCall& call)
1370 auto& dst = eax5_.i;
1372 switch(call.get_property_id())
1374 case EAXFXSLOT_NONE:
1376 case EAXFXSLOT_ALLPARAMETERS:
1377 eax5_fx_slot_set_all(call);
1378 if((eax_df_ & eax_load_effect_dirty_bit))
1379 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
1381 case EAXFXSLOT_LOADEFFECT:
1382 eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call, dst.guidLoadEffect, eax_df_);
1383 if((eax_df_ & eax_load_effect_dirty_bit))
1384 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
1386 case EAXFXSLOT_VOLUME:
1387 eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, eax_df_);
1389 case EAXFXSLOT_LOCK:
1390 eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, eax_df_);
1392 case EAXFXSLOT_FLAGS:
1393 eax_fx_slot_set<Eax5FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, eax_df_);
1395 case EAXFXSLOT_OCCLUSION:
1396 eax_fx_slot_set<Eax5OcclusionValidator, eax_occlusion_dirty_bit>(call, dst.lOcclusion, eax_df_);
1398 case EAXFXSLOT_OCCLUSIONLFRATIO:
1399 eax_fx_slot_set<Eax5OcclusionLfRatioValidator, eax_occlusion_lf_ratio_dirty_bit>(call, dst.flOcclusionLFRatio, eax_df_);
1402 eax_fail_unknown_property_id();
1405 return eax_fx_slot_should_update_sources();
1408 // Returns `true` if all sources should be updated, or `false` otherwise.
1409 bool ALeffectslot::eax_fx_slot_set(const EaxCall& call)
1411 switch (call.get_version())
1413 case 4: return eax4_fx_slot_set(call);
1414 case 5: return eax5_fx_slot_set(call);
1415 default: eax_fail_unknown_version();
1419 // Returns `true` if all sources should be updated, or `false` otherwise.
1420 bool ALeffectslot::eax_set(const EaxCall& call)
1424 switch(call.get_property_set_id())
1426 case EaxCallPropertySetId::fx_slot: ret = eax_fx_slot_set(call); break;
1427 case EaxCallPropertySetId::fx_slot_effect: eax_effect_->set(call); break;
1428 default: eax_fail_unknown_property_id();
1431 const auto version = call.get_version();
1432 if(eax_version_ != version)
1433 eax_df_ = ~EaxDirtyFlags{};
1434 eax_version_ = version;
1439 void ALeffectslot::eax4_fx_slot_commit(EaxDirtyFlags& dst_df)
1441 eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::guidLoadEffect);
1442 eax_fx_slot_commit_property<eax_volume_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::lVolume);
1443 eax_fx_slot_commit_property<eax_lock_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::lLock);
1444 eax_fx_slot_commit_property<eax_flags_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::ulFlags);
1448 if(dst_i.lOcclusion != EAXFXSLOT_DEFAULTOCCLUSION) {
1449 dst_df |= eax_occlusion_dirty_bit;
1450 dst_i.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
1453 if(dst_i.flOcclusionLFRatio != EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO) {
1454 dst_df |= eax_occlusion_lf_ratio_dirty_bit;
1455 dst_i.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
1459 void ALeffectslot::eax5_fx_slot_commit(Eax5State& state, EaxDirtyFlags& dst_df)
1461 eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::guidLoadEffect);
1462 eax_fx_slot_commit_property<eax_volume_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lVolume);
1463 eax_fx_slot_commit_property<eax_lock_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lLock);
1464 eax_fx_slot_commit_property<eax_flags_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::ulFlags);
1465 eax_fx_slot_commit_property<eax_occlusion_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lOcclusion);
1466 eax_fx_slot_commit_property<eax_occlusion_lf_ratio_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::flOcclusionLFRatio);
1469 void ALeffectslot::eax_set_efx_slot_effect(EaxEffect &effect)
1471 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
1473 const auto error = initEffect(effect.al_effect_type_, effect.al_effect_props_, eax_al_context_);
1475 if(error != AL_NO_ERROR) {
1476 ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect.");
1480 if(mState == SlotState::Initial) {
1481 mPropsDirty = false;
1482 updateProps(eax_al_context_);
1483 auto effect_slot_ptr = this;
1484 AddActiveEffectSlots({&effect_slot_ptr, 1}, eax_al_context_);
1485 mState = SlotState::Playing;
1494 void ALeffectslot::eax_set_efx_slot_send_auto(bool is_send_auto)
1496 if(AuxSendAuto == is_send_auto)
1499 AuxSendAuto = is_send_auto;
1503 void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain)
1505 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
1509 if(gain < 0.0f || gain > 1.0f)
1510 ERR(EAX_PREFIX "Gain out of range (%f)\n", gain);
1512 Gain = clampf(gain, 0.0f, 1.0f);
1518 void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot)
1520 assert(effect_slot);
1521 eax_delete_al_effect_slot(*effect_slot->eax_al_context_, *effect_slot);
1524 EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context)
1526 #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
1528 std::unique_lock<std::mutex> effect_slot_lock{context.mEffectSlotLock};
1529 auto& device = *context.mALDevice;
1531 if(context.mNumEffectSlots == device.AuxiliaryEffectSlotMax) {
1532 ERR(EAX_PREFIX "%s\n", "Out of memory.");
1536 if(!EnsureEffectSlots(&context, 1)) {
1537 ERR(EAX_PREFIX "%s\n", "Failed to ensure.");
1541 return EaxAlEffectSlotUPtr{AllocEffectSlot(&context)};
1546 void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot)
1548 #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
1550 std::lock_guard<std::mutex> effect_slot_lock{context.mEffectSlotLock};
1552 if(ReadRef(effect_slot.ref) != 0) {
1553 ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id);
1557 auto effect_slot_ptr = &effect_slot;
1558 RemoveActiveEffectSlots({&effect_slot_ptr, 1}, &context);
1559 FreeEffectSlot(&context, &effect_slot);
1563 #endif // ALSOFT_EAX