Cast the ringbuffer size to the correct type
[openal-soft.git] / al / auxeffectslot.cpp
blob71d43adca20431703b962fba459ddfa5fbbf5259
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 <cstdint>
27 #include <iterator>
28 #include <memory>
29 #include <mutex>
30 #include <numeric>
31 #include <thread>
33 #include "AL/al.h"
34 #include "AL/alc.h"
35 #include "AL/efx.h"
37 #include "alcmain.h"
38 #include "alcontext.h"
39 #include "alexcpt.h"
40 #include "almalloc.h"
41 #include "alnumeric.h"
42 #include "alspan.h"
43 #include "alu.h"
44 #include "buffer.h"
45 #include "effect.h"
46 #include "fpu_ctrl.h"
47 #include "inprogext.h"
48 #include "logging.h"
49 #include "opthelpers.h"
52 namespace {
54 inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
56 const size_t lidx{(id-1) >> 6};
57 const ALuint slidx{(id-1) & 0x3f};
59 if UNLIKELY(lidx >= context->mEffectSlotList.size())
60 return nullptr;
61 EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
62 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
63 return nullptr;
64 return sublist.EffectSlots + slidx;
67 inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept
69 const size_t lidx{(id-1) >> 6};
70 const ALuint slidx{(id-1) & 0x3f};
72 if UNLIKELY(lidx >= device->EffectList.size())
73 return nullptr;
74 EffectSubList &sublist = device->EffectList[lidx];
75 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
76 return nullptr;
77 return sublist.Effects + slidx;
80 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
82 const size_t lidx{(id-1) >> 6};
83 const ALuint slidx{(id-1) & 0x3f};
85 if UNLIKELY(lidx >= device->BufferList.size())
86 return nullptr;
87 BufferSubList &sublist = device->BufferList[lidx];
88 if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
89 return nullptr;
90 return sublist.Buffers + slidx;
94 void AddActiveEffectSlots(const ALuint *slotids, size_t count, ALCcontext *context)
96 if(count < 1) return;
97 ALeffectslotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
98 size_t newcount{curarray->size() + count};
100 /* Insert the new effect slots into the head of the array, followed by the
101 * existing ones.
103 ALeffectslotArray *newarray = ALeffectslot::CreatePtrArray(newcount);
104 auto slotiter = std::transform(slotids, slotids+count, newarray->begin(),
105 [context](ALuint id) noexcept -> ALeffectslot*
106 { return LookupEffectSlot(context, id); }
108 std::copy(curarray->begin(), curarray->end(), slotiter);
110 /* Remove any duplicates (first instance of each will be kept). */
111 auto last = newarray->end();
112 for(auto start=newarray->begin()+1;;)
114 last = std::remove(start, last, *(start-1));
115 if(start == last) break;
116 ++start;
118 newcount = static_cast<size_t>(std::distance(newarray->begin(), last));
120 /* Reallocate newarray if the new size ended up smaller from duplicate
121 * removal.
123 if UNLIKELY(newcount < newarray->size())
125 curarray = newarray;
126 newarray = ALeffectslot::CreatePtrArray(newcount);
127 std::copy_n(curarray->begin(), newcount, newarray->begin());
128 delete curarray;
129 curarray = nullptr;
131 std::uninitialized_fill_n(newarray->end(), newcount, nullptr);
133 curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
134 context->mDevice->waitForMix();
136 al::destroy_n(curarray->end(), curarray->size());
137 delete curarray;
140 void RemoveActiveEffectSlots(const ALuint *slotids, size_t count, ALCcontext *context)
142 if(count < 1) return;
143 ALeffectslotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
145 /* Don't shrink the allocated array size since we don't know how many (if
146 * any) of the effect slots to remove are in the array.
148 ALeffectslotArray *newarray = ALeffectslot::CreatePtrArray(curarray->size());
150 /* Copy each element in curarray to newarray whose ID is not in slotids. */
151 const ALuint *slotids_end{slotids + count};
152 auto slotiter = std::copy_if(curarray->begin(), curarray->end(), newarray->begin(),
153 [slotids, slotids_end](const ALeffectslot *slot) -> bool
154 { return std::find(slotids, slotids_end, slot->id) == slotids_end; }
157 /* Reallocate with the new size. */
158 auto newsize = static_cast<size_t>(std::distance(newarray->begin(), slotiter));
159 if LIKELY(newsize != newarray->size())
161 curarray = newarray;
162 newarray = ALeffectslot::CreatePtrArray(newsize);
163 std::copy_n(curarray->begin(), newsize, newarray->begin());
165 delete curarray;
166 curarray = nullptr;
168 std::uninitialized_fill_n(newarray->end(), newsize, nullptr);
170 curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
171 context->mDevice->waitForMix();
173 al::destroy_n(curarray->end(), curarray->size());
174 delete curarray;
178 bool EnsureEffectSlots(ALCcontext *context, size_t needed)
180 size_t count{std::accumulate(context->mEffectSlotList.cbegin(),
181 context->mEffectSlotList.cend(), size_t{0},
182 [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
183 { return cur + static_cast<ALuint>(POPCNT64(sublist.FreeMask)); }
186 while(needed > count)
188 if UNLIKELY(context->mEffectSlotList.size() >= 1<<25)
189 return false;
191 context->mEffectSlotList.emplace_back();
192 auto sublist = context->mEffectSlotList.end() - 1;
193 sublist->FreeMask = ~0_u64;
194 sublist->EffectSlots = static_cast<ALeffectslot*>(
195 al_calloc(alignof(ALeffectslot), sizeof(ALeffectslot)*64));
196 if UNLIKELY(!sublist->EffectSlots)
198 context->mEffectSlotList.pop_back();
199 return false;
201 count += 64;
203 return true;
206 ALeffectslot *AllocEffectSlot(ALCcontext *context)
208 auto sublist = std::find_if(context->mEffectSlotList.begin(), context->mEffectSlotList.end(),
209 [](const EffectSlotSubList &entry) noexcept -> bool
210 { return entry.FreeMask != 0; }
212 auto lidx = static_cast<ALuint>(std::distance(context->mEffectSlotList.begin(), sublist));
213 auto slidx = static_cast<ALuint>(CTZ64(sublist->FreeMask));
215 ALeffectslot *slot{::new (sublist->EffectSlots + slidx) ALeffectslot{}};
216 if(ALenum err{slot->init()})
218 al::destroy_at(slot);
219 context->setError(err, "Effect slot object initialization failed");
220 return nullptr;
222 aluInitEffectPanning(slot, context->mDevice.get());
224 /* Add 1 to avoid source ID 0. */
225 slot->id = ((lidx<<6) | slidx) + 1;
227 context->mNumEffectSlots += 1;
228 sublist->FreeMask &= ~(1_u64 << slidx);
230 return slot;
233 void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
235 const ALuint id{slot->id - 1};
236 const size_t lidx{id >> 6};
237 const ALuint slidx{id & 0x3f};
239 al::destroy_at(slot);
241 context->mEffectSlotList[lidx].FreeMask |= 1_u64 << slidx;
242 context->mNumEffectSlots--;
246 #define DO_UPDATEPROPS() do { \
247 if(!context->mDeferUpdates.load(std::memory_order_acquire) \
248 && slot->mState == SlotState::Playing) \
249 slot->updateProps(context.get()); \
250 else \
251 slot->PropsClean.clear(std::memory_order_release); \
252 } while(0)
254 } // namespace
256 ALeffectslotArray *ALeffectslot::CreatePtrArray(size_t count) noexcept
258 /* Allocate space for twice as many pointers, so the mixer has scratch
259 * space to store a sorted list during mixing.
261 void *ptr{al_calloc(alignof(ALeffectslotArray), ALeffectslotArray::Sizeof(count*2))};
262 return new (ptr) ALeffectslotArray{count};
266 AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
267 START_API_FUNC
269 ContextRef context{GetContextRef()};
270 if UNLIKELY(!context) return;
272 if UNLIKELY(n < 0)
273 context->setError(AL_INVALID_VALUE, "Generating %d effect slots", n);
274 if UNLIKELY(n <= 0) return;
276 std::unique_lock<std::mutex> slotlock{context->mEffectSlotLock};
277 ALCdevice *device{context->mDevice.get()};
278 if(static_cast<ALuint>(n) > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
280 context->setError(AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)",
281 device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n);
282 return;
284 if(!EnsureEffectSlots(context.get(), static_cast<ALuint>(n)))
286 context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n,
287 (n==1) ? "" : "s");
288 return;
291 if(n == 1)
293 ALeffectslot *slot{AllocEffectSlot(context.get())};
294 if(!slot) return;
295 effectslots[0] = slot->id;
297 else
299 al::vector<ALuint> ids;
300 ALsizei count{n};
301 ids.reserve(static_cast<ALuint>(count));
302 do {
303 ALeffectslot *slot{AllocEffectSlot(context.get())};
304 if(!slot)
306 slotlock.unlock();
307 alDeleteAuxiliaryEffectSlots(static_cast<ALsizei>(ids.size()), ids.data());
308 return;
310 ids.emplace_back(slot->id);
311 } while(--count);
312 std::copy(ids.cbegin(), ids.cend(), effectslots);
315 END_API_FUNC
317 AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots)
318 START_API_FUNC
320 ContextRef context{GetContextRef()};
321 if UNLIKELY(!context) return;
323 if UNLIKELY(n < 0)
324 context->setError(AL_INVALID_VALUE, "Deleting %d effect slots", n);
325 if UNLIKELY(n <= 0) return;
327 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
328 auto validate_slot = [&context](const ALuint id) -> bool
330 ALeffectslot *slot{LookupEffectSlot(context.get(), id)};
331 if UNLIKELY(!slot)
333 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", id);
334 return false;
336 if UNLIKELY(ReadRef(slot->ref) != 0)
338 context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u", id);
339 return false;
341 return true;
343 auto effectslots_end = effectslots + n;
344 auto bad_slot = std::find_if_not(effectslots, effectslots_end, validate_slot);
345 if UNLIKELY(bad_slot != effectslots_end) return;
347 // All effectslots are valid, remove and delete them
348 RemoveActiveEffectSlots(effectslots, static_cast<ALuint>(n), context.get());
349 auto delete_slot = [&context](const ALuint sid) -> void
351 ALeffectslot *slot{LookupEffectSlot(context.get(), sid)};
352 if(slot) FreeEffectSlot(context.get(), slot);
354 std::for_each(effectslots, effectslots_end, delete_slot);
356 END_API_FUNC
358 AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
359 START_API_FUNC
361 ContextRef context{GetContextRef()};
362 if LIKELY(context)
364 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
365 if(LookupEffectSlot(context.get(), effectslot) != nullptr)
366 return AL_TRUE;
368 return AL_FALSE;
370 END_API_FUNC
373 AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid)
374 START_API_FUNC
376 ContextRef context{GetContextRef()};
377 if UNLIKELY(!context) return;
379 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
380 ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
381 if UNLIKELY(!slot)
383 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
384 return;
386 if(slot->mState == SlotState::Playing)
387 return;
389 slot->PropsClean.test_and_set(std::memory_order_acq_rel);
390 slot->updateProps(context.get());
392 AddActiveEffectSlots(&slotid, 1, context.get());
393 slot->mState = SlotState::Playing;
395 END_API_FUNC
397 AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids)
398 START_API_FUNC
400 ContextRef context{GetContextRef()};
401 if UNLIKELY(!context) return;
403 if UNLIKELY(n < 0)
404 context->setError(AL_INVALID_VALUE, "Playing %d effect slots", n);
405 if UNLIKELY(n <= 0) return;
407 auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
408 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
409 for(size_t i{0};i < slots.size();++i)
411 ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])};
412 if UNLIKELY(!slot)
414 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]);
415 return;
418 if(slot->mState != SlotState::Playing)
420 slot->PropsClean.test_and_set(std::memory_order_acq_rel);
421 slot->updateProps(context.get());
423 slots[i] = slot;
426 AddActiveEffectSlots(slotids, static_cast<ALuint>(n), context.get());
427 for(auto slot : slots)
428 slot->mState = SlotState::Playing;
430 END_API_FUNC
432 AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid)
433 START_API_FUNC
435 ContextRef context{GetContextRef()};
436 if UNLIKELY(!context) return;
438 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
439 ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)};
440 if UNLIKELY(!slot)
442 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid);
443 return;
446 RemoveActiveEffectSlots(&slotid, 1, context.get());
447 slot->mState = SlotState::Stopped;
449 END_API_FUNC
451 AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids)
452 START_API_FUNC
454 ContextRef context{GetContextRef()};
455 if UNLIKELY(!context) return;
457 if UNLIKELY(n < 0)
458 context->setError(AL_INVALID_VALUE, "Stopping %d effect slots", n);
459 if UNLIKELY(n <= 0) return;
461 auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
462 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
463 for(size_t i{0};i < slots.size();++i)
465 ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])};
466 if UNLIKELY(!slot)
468 context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]);
469 return;
472 slots[i] = slot;
475 RemoveActiveEffectSlots(slotids, static_cast<ALuint>(n), context.get());
476 for(auto slot : slots)
477 slot->mState = SlotState::Stopped;
479 END_API_FUNC
482 AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value)
483 START_API_FUNC
485 ContextRef context{GetContextRef()};
486 if UNLIKELY(!context) return;
488 std::lock_guard<std::mutex> _{context->mPropLock};
489 std::lock_guard<std::mutex> __{context->mEffectSlotLock};
490 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
491 if UNLIKELY(!slot)
492 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
494 ALeffectslot *target{};
495 ALCdevice *device{};
496 ALenum err{};
497 switch(param)
499 case AL_EFFECTSLOT_EFFECT:
500 device = context->mDevice.get();
503 std::lock_guard<std::mutex> ___{device->EffectLock};
504 ALeffect *effect{value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr};
505 if(!(value == 0 || effect != nullptr))
506 SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid effect ID %u", value);
507 err = slot->initEffect(effect, context.get());
509 if UNLIKELY(err != AL_NO_ERROR)
511 context->setError(err, "Effect initialization failed");
512 return;
514 if UNLIKELY(slot->mState == SlotState::Initial)
516 AddActiveEffectSlots(&slot->id, 1, context.get());
517 slot->mState = SlotState::Playing;
519 break;
521 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
522 if(!(value == AL_TRUE || value == AL_FALSE))
523 SETERR_RETURN(context, AL_INVALID_VALUE,,
524 "Effect slot auxiliary send auto out of range");
525 slot->AuxSendAuto = !!value;
526 break;
528 case AL_EFFECTSLOT_TARGET_SOFT:
529 target = LookupEffectSlot(context.get(), static_cast<ALuint>(value));
530 if(value && !target)
531 SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid effect slot target ID");
532 if(target)
534 ALeffectslot *checker{target};
535 while(checker && checker != slot)
536 checker = checker->Target;
537 if(checker)
538 SETERR_RETURN(context, AL_INVALID_OPERATION,,
539 "Setting target of effect slot ID %u to %u creates circular chain", slot->id,
540 target->id);
543 if(ALeffectslot *oldtarget{slot->Target})
545 /* We must force an update if there was an existing effect slot
546 * target, in case it's about to be deleted.
548 if(target) IncrementRef(target->ref);
549 DecrementRef(oldtarget->ref);
550 slot->Target = target;
551 slot->updateProps(context.get());
552 return;
555 if(target) IncrementRef(target->ref);
556 slot->Target = target;
557 break;
559 case AL_BUFFER:
560 device = context->mDevice.get();
562 if(slot->mState == SlotState::Playing)
563 SETERR_RETURN(context, AL_INVALID_OPERATION,,
564 "Setting buffer on playing effect slot %u", slot->id);
567 std::lock_guard<std::mutex> ___{device->BufferLock};
568 ALbuffer *buffer{};
569 if(value)
571 buffer = LookupBuffer(device, static_cast<ALuint>(value));
572 if(!buffer) SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid buffer ID");
573 if(buffer->mBuffer.mCallback)
574 SETERR_RETURN(context, AL_INVALID_OPERATION,,
575 "Callback buffer not valid for effects");
577 IncrementRef(buffer->ref);
580 if(ALbuffer *oldbuffer{slot->Buffer})
581 DecrementRef(oldbuffer->ref);
582 slot->Buffer = buffer;
584 FPUCtl mixer_mode{};
585 auto *state = slot->Effect.State.get();
586 state->setBuffer(device, buffer ? &buffer->mBuffer : nullptr);
588 break;
590 case AL_EFFECTSLOT_STATE_SOFT:
591 SETERR_RETURN(context, AL_INVALID_OPERATION,, "AL_EFFECTSLOT_STATE_SOFT is read-only");
593 default:
594 SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid effect slot integer property 0x%04x",
595 param);
597 DO_UPDATEPROPS();
599 END_API_FUNC
601 AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
602 START_API_FUNC
604 switch(param)
606 case AL_EFFECTSLOT_EFFECT:
607 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
608 case AL_EFFECTSLOT_TARGET_SOFT:
609 case AL_EFFECTSLOT_STATE_SOFT:
610 case AL_BUFFER:
611 alAuxiliaryEffectSloti(effectslot, param, values[0]);
612 return;
615 ContextRef context{GetContextRef()};
616 if UNLIKELY(!context) return;
618 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
619 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
620 if UNLIKELY(!slot)
621 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
623 switch(param)
625 default:
626 SETERR_RETURN(context, AL_INVALID_ENUM,,
627 "Invalid effect slot integer-vector property 0x%04x", param);
630 END_API_FUNC
632 AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
633 START_API_FUNC
635 ContextRef context{GetContextRef()};
636 if UNLIKELY(!context) return;
638 std::lock_guard<std::mutex> _{context->mPropLock};
639 std::lock_guard<std::mutex> __{context->mEffectSlotLock};
640 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
641 if UNLIKELY(!slot)
642 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
644 switch(param)
646 case AL_EFFECTSLOT_GAIN:
647 if(!(value >= 0.0f && value <= 1.0f))
648 SETERR_RETURN(context, AL_INVALID_VALUE,, "Effect slot gain out of range");
649 slot->Gain = value;
650 break;
652 default:
653 SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid effect slot float property 0x%04x",
654 param);
656 DO_UPDATEPROPS();
658 END_API_FUNC
660 AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
661 START_API_FUNC
663 switch(param)
665 case AL_EFFECTSLOT_GAIN:
666 alAuxiliaryEffectSlotf(effectslot, param, values[0]);
667 return;
670 ContextRef context{GetContextRef()};
671 if UNLIKELY(!context) return;
673 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
674 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
675 if UNLIKELY(!slot)
676 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
678 switch(param)
680 default:
681 SETERR_RETURN(context, AL_INVALID_ENUM,,
682 "Invalid effect slot float-vector property 0x%04x", param);
685 END_API_FUNC
688 AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
689 START_API_FUNC
691 ContextRef context{GetContextRef()};
692 if UNLIKELY(!context) return;
694 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
695 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
696 if UNLIKELY(!slot)
697 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
699 switch(param)
701 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
702 *value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE;
703 break;
705 case AL_EFFECTSLOT_TARGET_SOFT:
706 if(auto *target = slot->Target)
707 *value = static_cast<ALint>(target->id);
708 else
709 *value = 0;
710 break;
712 case AL_EFFECTSLOT_STATE_SOFT:
713 *value = static_cast<int>(slot->mState);
714 break;
716 case AL_BUFFER:
717 if(auto *buffer = slot->Buffer)
718 *value = static_cast<ALint>(buffer->id);
719 else
720 *value = 0;
721 break;
723 default:
724 context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param);
727 END_API_FUNC
729 AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
730 START_API_FUNC
732 switch(param)
734 case AL_EFFECTSLOT_EFFECT:
735 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
736 case AL_EFFECTSLOT_TARGET_SOFT:
737 case AL_EFFECTSLOT_STATE_SOFT:
738 case AL_BUFFER:
739 alGetAuxiliaryEffectSloti(effectslot, param, values);
740 return;
743 ContextRef context{GetContextRef()};
744 if UNLIKELY(!context) return;
746 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
747 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
748 if UNLIKELY(!slot)
749 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
751 switch(param)
753 default:
754 context->setError(AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
755 param);
758 END_API_FUNC
760 AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
761 START_API_FUNC
763 ContextRef context{GetContextRef()};
764 if UNLIKELY(!context) return;
766 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
767 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
768 if UNLIKELY(!slot)
769 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
771 switch(param)
773 case AL_EFFECTSLOT_GAIN:
774 *value = slot->Gain;
775 break;
777 default:
778 context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param);
781 END_API_FUNC
783 AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
784 START_API_FUNC
786 switch(param)
788 case AL_EFFECTSLOT_GAIN:
789 alGetAuxiliaryEffectSlotf(effectslot, param, values);
790 return;
793 ContextRef context{GetContextRef()};
794 if UNLIKELY(!context) return;
796 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
797 ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
798 if UNLIKELY(!slot)
799 SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
801 switch(param)
803 default:
804 context->setError(AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
805 param);
808 END_API_FUNC
811 ALeffectslot::~ALeffectslot()
813 if(Target)
814 DecrementRef(Target->ref);
815 Target = nullptr;
816 if(Buffer)
817 DecrementRef(Buffer->ref);
818 Buffer = nullptr;
820 ALeffectslotProps *props{Params.Update.load()};
821 if(props)
823 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n",
824 decltype(std::declval<void*>()){props});
825 delete props;
828 if(Params.mEffectState)
829 Params.mEffectState->release();
832 ALenum ALeffectslot::init()
834 EffectStateFactory *factory{getFactoryByType(Effect.Type)};
835 if(!factory) return AL_INVALID_VALUE;
837 Effect.State.reset(factory->create());
838 if(!Effect.State) return AL_OUT_OF_MEMORY;
840 Effect.State->add_ref();
841 Params.mEffectState = Effect.State.get();
842 return AL_NO_ERROR;
845 ALenum ALeffectslot::initEffect(ALeffect *effect, ALCcontext *context)
847 ALenum newtype{effect ? effect->type : AL_EFFECT_NULL};
848 if(newtype != Effect.Type)
850 EffectStateFactory *factory{getFactoryByType(newtype)};
851 if(!factory)
853 ERR("Failed to find factory for effect type 0x%04x\n", newtype);
854 return AL_INVALID_ENUM;
856 al::intrusive_ptr<EffectState> State{factory->create()};
857 if(!State) return AL_OUT_OF_MEMORY;
859 ALCdevice *Device{context->mDevice.get()};
860 std::unique_lock<std::mutex> statelock{Device->StateLock};
861 State->mOutTarget = Device->Dry.Buffer;
863 FPUCtl mixer_mode{};
864 State->deviceUpdate(Device);
865 if(Buffer)
866 State->setBuffer(Device, &Buffer->mBuffer);
869 if(!effect)
871 Effect.Type = AL_EFFECT_NULL;
872 Effect.Props = EffectProps{};
874 else
876 Effect.Type = effect->type;
877 Effect.Props = effect->Props;
880 Effect.State = std::move(State);
882 else if(effect)
883 Effect.Props = effect->Props;
885 /* Remove state references from old effect slot property updates. */
886 ALeffectslotProps *props{context->mFreeEffectslotProps.load()};
887 while(props)
889 props->State = nullptr;
890 props = props->next.load(std::memory_order_relaxed);
893 return AL_NO_ERROR;
896 void ALeffectslot::updateProps(ALCcontext *context)
898 /* Get an unused property container, or allocate a new one as needed. */
899 ALeffectslotProps *props{context->mFreeEffectslotProps.load(std::memory_order_relaxed)};
900 if(!props)
901 props = new ALeffectslotProps{};
902 else
904 ALeffectslotProps *next;
905 do {
906 next = props->next.load(std::memory_order_relaxed);
907 } while(context->mFreeEffectslotProps.compare_exchange_weak(props, next,
908 std::memory_order_seq_cst, std::memory_order_acquire) == 0);
911 /* Copy in current property values. */
912 props->Gain = Gain;
913 props->AuxSendAuto = AuxSendAuto;
914 props->Target = Target;
916 props->Type = Effect.Type;
917 props->Props = Effect.Props;
918 props->State = Effect.State;
920 /* Set the new container for updating internal parameters. */
921 props = Params.Update.exchange(props, std::memory_order_acq_rel);
922 if(props)
924 /* If there was an unused update container, put it back in the
925 * freelist.
927 props->State = nullptr;
928 AtomicReplaceHead(context->mFreeEffectslotProps, props);
932 void UpdateAllEffectSlotProps(ALCcontext *context)
934 std::lock_guard<std::mutex> _{context->mEffectSlotLock};
935 ALeffectslotArray *auxslots{context->mActiveAuxSlots.load(std::memory_order_acquire)};
936 for(ALeffectslot *slot : *auxslots)
938 if(!slot->PropsClean.test_and_set(std::memory_order_acq_rel))
939 slot->updateProps(context);
943 EffectSlotSubList::~EffectSlotSubList()
945 uint64_t usemask{~FreeMask};
946 while(usemask)
948 ALsizei idx{CTZ64(usemask)};
949 al::destroy_at(EffectSlots+idx);
950 usemask &= ~(1_u64 << idx);
952 FreeMask = ~usemask;
953 al_free(EffectSlots);
954 EffectSlots = nullptr;