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"
38 #include "alcontext.h"
41 #include "alnumeric.h"
47 #include "inprogext.h"
49 #include "opthelpers.h"
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())
61 EffectSlotSubList
&sublist
{context
->mEffectSlotList
[lidx
]};
62 if UNLIKELY(sublist
.FreeMask
& (1_u64
<< slidx
))
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())
74 EffectSubList
&sublist
= device
->EffectList
[lidx
];
75 if UNLIKELY(sublist
.FreeMask
& (1_u64
<< slidx
))
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())
87 BufferSubList
&sublist
= device
->BufferList
[lidx
];
88 if UNLIKELY(sublist
.FreeMask
& (1_u64
<< slidx
))
90 return sublist
.Buffers
+ slidx
;
94 void AddActiveEffectSlots(const ALuint
*slotids
, size_t count
, ALCcontext
*context
)
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
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;
118 newcount
= static_cast<size_t>(std::distance(newarray
->begin(), last
));
120 /* Reallocate newarray if the new size ended up smaller from duplicate
123 if UNLIKELY(newcount
< newarray
->size())
126 newarray
= ALeffectslot::CreatePtrArray(newcount
);
127 std::copy_n(curarray
->begin(), newcount
, newarray
->begin());
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());
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())
162 newarray
= ALeffectslot::CreatePtrArray(newsize
);
163 std::copy_n(curarray
->begin(), newsize
, newarray
->begin());
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());
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)
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();
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");
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
);
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()); \
251 slot->PropsClean.clear(std::memory_order_release); \
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
)
269 ContextRef context
{GetContextRef()};
270 if UNLIKELY(!context
) return;
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
);
284 if(!EnsureEffectSlots(context
.get(), static_cast<ALuint
>(n
)))
286 context
->setError(AL_OUT_OF_MEMORY
, "Failed to allocate %d effectslot%s", n
,
293 ALeffectslot
*slot
{AllocEffectSlot(context
.get())};
295 effectslots
[0] = slot
->id
;
299 al::vector
<ALuint
> ids
;
301 ids
.reserve(static_cast<ALuint
>(count
));
303 ALeffectslot
*slot
{AllocEffectSlot(context
.get())};
307 alDeleteAuxiliaryEffectSlots(static_cast<ALsizei
>(ids
.size()), ids
.data());
310 ids
.emplace_back(slot
->id
);
312 std::copy(ids
.cbegin(), ids
.cend(), effectslots
);
317 AL_API
void AL_APIENTRY
alDeleteAuxiliaryEffectSlots(ALsizei n
, const ALuint
*effectslots
)
320 ContextRef context
{GetContextRef()};
321 if UNLIKELY(!context
) return;
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
)};
333 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", id
);
336 if UNLIKELY(ReadRef(slot
->ref
) != 0)
338 context
->setError(AL_INVALID_OPERATION
, "Deleting in-use effect slot %u", id
);
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
);
358 AL_API ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlot(ALuint effectslot
)
361 ContextRef context
{GetContextRef()};
364 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
365 if(LookupEffectSlot(context
.get(), effectslot
) != nullptr)
373 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlaySOFT(ALuint slotid
)
376 ContextRef context
{GetContextRef()};
377 if UNLIKELY(!context
) return;
379 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
380 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotid
)};
383 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotid
);
386 if(slot
->mState
== SlotState::Playing
)
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
;
397 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlayvSOFT(ALsizei n
, const ALuint
*slotids
)
400 ContextRef context
{GetContextRef()};
401 if UNLIKELY(!context
) return;
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
])};
414 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotids
[i
]);
418 if(slot
->mState
!= SlotState::Playing
)
420 slot
->PropsClean
.test_and_set(std::memory_order_acq_rel
);
421 slot
->updateProps(context
.get());
426 AddActiveEffectSlots(slotids
, static_cast<ALuint
>(n
), context
.get());
427 for(auto slot
: slots
)
428 slot
->mState
= SlotState::Playing
;
432 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopSOFT(ALuint slotid
)
435 ContextRef context
{GetContextRef()};
436 if UNLIKELY(!context
) return;
438 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
439 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotid
)};
442 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotid
);
446 RemoveActiveEffectSlots(&slotid
, 1, context
.get());
447 slot
->mState
= SlotState::Stopped
;
451 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopvSOFT(ALsizei n
, const ALuint
*slotids
)
454 ContextRef context
{GetContextRef()};
455 if UNLIKELY(!context
) return;
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
])};
468 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotids
[i
]);
475 RemoveActiveEffectSlots(slotids
, static_cast<ALuint
>(n
), context
.get());
476 for(auto slot
: slots
)
477 slot
->mState
= SlotState::Stopped
;
482 AL_API
void AL_APIENTRY
alAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint value
)
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
);
492 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
494 ALeffectslot
*target
{};
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");
514 if UNLIKELY(slot
->mState
== SlotState::Initial
)
516 AddActiveEffectSlots(&slot
->id
, 1, context
.get());
517 slot
->mState
= SlotState::Playing
;
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
;
528 case AL_EFFECTSLOT_TARGET_SOFT
:
529 target
= LookupEffectSlot(context
.get(), static_cast<ALuint
>(value
));
531 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid effect slot target ID");
534 ALeffectslot
*checker
{target
};
535 while(checker
&& checker
!= slot
)
536 checker
= checker
->Target
;
538 SETERR_RETURN(context
, AL_INVALID_OPERATION
,,
539 "Setting target of effect slot ID %u to %u creates circular chain", slot
->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());
555 if(target
) IncrementRef(target
->ref
);
556 slot
->Target
= target
;
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
};
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
;
585 auto *state
= slot
->Effect
.State
.get();
586 state
->setBuffer(device
, buffer
? &buffer
->mBuffer
: nullptr);
590 case AL_EFFECTSLOT_STATE_SOFT
:
591 SETERR_RETURN(context
, AL_INVALID_OPERATION
,, "AL_EFFECTSLOT_STATE_SOFT is read-only");
594 SETERR_RETURN(context
, AL_INVALID_ENUM
,, "Invalid effect slot integer property 0x%04x",
601 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, const ALint
*values
)
606 case AL_EFFECTSLOT_EFFECT
:
607 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
608 case AL_EFFECTSLOT_TARGET_SOFT
:
609 case AL_EFFECTSLOT_STATE_SOFT
:
611 alAuxiliaryEffectSloti(effectslot
, param
, values
[0]);
615 ContextRef context
{GetContextRef()};
616 if UNLIKELY(!context
) return;
618 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
619 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
621 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
626 SETERR_RETURN(context
, AL_INVALID_ENUM
,,
627 "Invalid effect slot integer-vector property 0x%04x", param
);
632 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat value
)
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
);
642 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
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");
653 SETERR_RETURN(context
, AL_INVALID_ENUM
,, "Invalid effect slot float property 0x%04x",
660 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, const ALfloat
*values
)
665 case AL_EFFECTSLOT_GAIN
:
666 alAuxiliaryEffectSlotf(effectslot
, param
, values
[0]);
670 ContextRef context
{GetContextRef()};
671 if UNLIKELY(!context
) return;
673 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
674 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
676 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
681 SETERR_RETURN(context
, AL_INVALID_ENUM
,,
682 "Invalid effect slot float-vector property 0x%04x", param
);
688 AL_API
void AL_APIENTRY
alGetAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint
*value
)
691 ContextRef context
{GetContextRef()};
692 if UNLIKELY(!context
) return;
694 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
695 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
697 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
701 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
702 *value
= slot
->AuxSendAuto
? AL_TRUE
: AL_FALSE
;
705 case AL_EFFECTSLOT_TARGET_SOFT
:
706 if(auto *target
= slot
->Target
)
707 *value
= static_cast<ALint
>(target
->id
);
712 case AL_EFFECTSLOT_STATE_SOFT
:
713 *value
= static_cast<int>(slot
->mState
);
717 if(auto *buffer
= slot
->Buffer
)
718 *value
= static_cast<ALint
>(buffer
->id
);
724 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x", param
);
729 AL_API
void AL_APIENTRY
alGetAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, ALint
*values
)
734 case AL_EFFECTSLOT_EFFECT
:
735 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
736 case AL_EFFECTSLOT_TARGET_SOFT
:
737 case AL_EFFECTSLOT_STATE_SOFT
:
739 alGetAuxiliaryEffectSloti(effectslot
, param
, values
);
743 ContextRef context
{GetContextRef()};
744 if UNLIKELY(!context
) return;
746 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
747 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
749 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
754 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
760 AL_API
void AL_APIENTRY
alGetAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat
*value
)
763 ContextRef context
{GetContextRef()};
764 if UNLIKELY(!context
) return;
766 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
767 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
769 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
773 case AL_EFFECTSLOT_GAIN
:
778 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x", param
);
783 AL_API
void AL_APIENTRY
alGetAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, ALfloat
*values
)
788 case AL_EFFECTSLOT_GAIN
:
789 alGetAuxiliaryEffectSlotf(effectslot
, param
, values
);
793 ContextRef context
{GetContextRef()};
794 if UNLIKELY(!context
) return;
796 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
797 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
799 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
804 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
811 ALeffectslot::~ALeffectslot()
814 DecrementRef(Target
->ref
);
817 DecrementRef(Buffer
->ref
);
820 ALeffectslotProps
*props
{Params
.Update
.load()};
823 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n",
824 decltype(std::declval
<void*>()){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();
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
)};
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
;
864 State
->deviceUpdate(Device
);
866 State
->setBuffer(Device
, &Buffer
->mBuffer
);
871 Effect
.Type
= AL_EFFECT_NULL
;
872 Effect
.Props
= EffectProps
{};
876 Effect
.Type
= effect
->type
;
877 Effect
.Props
= effect
->Props
;
880 Effect
.State
= std::move(State
);
883 Effect
.Props
= effect
->Props
;
885 /* Remove state references from old effect slot property updates. */
886 ALeffectslotProps
*props
{context
->mFreeEffectslotProps
.load()};
889 props
->State
= nullptr;
890 props
= props
->next
.load(std::memory_order_relaxed
);
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
)};
901 props
= new ALeffectslotProps
{};
904 ALeffectslotProps
*next
;
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. */
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
);
924 /* If there was an unused update container, put it back in the
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
};
948 ALsizei idx
{CTZ64(usemask
)};
949 al::destroy_at(EffectSlots
+idx
);
950 usemask
&= ~(1_u64
<< idx
);
953 al_free(EffectSlots
);
954 EffectSlots
= nullptr;