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"
36 #include <unordered_map>
46 #include "alc/context.h"
47 #include "alc/device.h"
48 #include "alc/effects/base.h"
49 #include "alc/inprogext.h"
51 #include "alnumeric.h"
55 #include "core/buffer_storage.h"
56 #include "core/device.h"
57 #include "core/fpu_ctrl.h"
58 #include "core/logging.h"
59 #include "direct_defs.h"
62 #include "flexarray.h"
63 #include "opthelpers.h"
68 #include "eax/effect.h"
69 #include "eax/fx_slot_index.h"
70 #include "eax/utils.h"
75 using SubListAllocator
= al::allocator
<std::array
<ALeffectslot
,64>>;
77 EffectStateFactory
*getFactoryByType(EffectSlotType type
)
81 case EffectSlotType::None
: return NullStateFactory_getFactory();
82 case EffectSlotType::Reverb
: return ReverbStateFactory_getFactory();
83 case EffectSlotType::Chorus
: return ChorusStateFactory_getFactory();
84 case EffectSlotType::Autowah
: return AutowahStateFactory_getFactory();
85 case EffectSlotType::Compressor
: return CompressorStateFactory_getFactory();
86 case EffectSlotType::Convolution
: return ConvolutionStateFactory_getFactory();
87 case EffectSlotType::Dedicated
: return DedicatedStateFactory_getFactory();
88 case EffectSlotType::Distortion
: return DistortionStateFactory_getFactory();
89 case EffectSlotType::Echo
: return EchoStateFactory_getFactory();
90 case EffectSlotType::Equalizer
: return EqualizerStateFactory_getFactory();
91 case EffectSlotType::Flanger
: return ChorusStateFactory_getFactory();
92 case EffectSlotType::FrequencyShifter
: return FshifterStateFactory_getFactory();
93 case EffectSlotType::RingModulator
: return ModulatorStateFactory_getFactory();
94 case EffectSlotType::PitchShifter
: return PshifterStateFactory_getFactory();
95 case EffectSlotType::VocalMorpher
: return VmorpherStateFactory_getFactory();
101 auto LookupEffectSlot(ALCcontext
*context
, ALuint id
) noexcept
-> ALeffectslot
*
103 const size_t lidx
{(id
-1) >> 6};
104 const ALuint slidx
{(id
-1) & 0x3f};
106 if(lidx
>= context
->mEffectSlotList
.size()) UNLIKELY
108 EffectSlotSubList
&sublist
{context
->mEffectSlotList
[lidx
]};
109 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
111 return al::to_address(sublist
.EffectSlots
->begin() + slidx
);
114 inline auto LookupEffect(ALCdevice
*device
, ALuint id
) noexcept
-> ALeffect
*
116 const size_t lidx
{(id
-1) >> 6};
117 const ALuint slidx
{(id
-1) & 0x3f};
119 if(lidx
>= device
->EffectList
.size()) UNLIKELY
121 EffectSubList
&sublist
= device
->EffectList
[lidx
];
122 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
124 return al::to_address(sublist
.Effects
->begin() + slidx
);
127 inline auto LookupBuffer(ALCdevice
*device
, ALuint id
) noexcept
-> ALbuffer
*
129 const size_t lidx
{(id
-1) >> 6};
130 const ALuint slidx
{(id
-1) & 0x3f};
132 if(lidx
>= device
->BufferList
.size()) UNLIKELY
134 BufferSubList
&sublist
= device
->BufferList
[lidx
];
135 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
137 return al::to_address(sublist
.Buffers
->begin() + slidx
);
141 void AddActiveEffectSlots(const al::span
<ALeffectslot
*> auxslots
, ALCcontext
*context
)
143 if(auxslots
.empty()) return;
144 EffectSlotArray
*curarray
{context
->mActiveAuxSlots
.load(std::memory_order_acquire
)};
145 if((curarray
->size()>>1) > std::numeric_limits
<size_t>::max()-auxslots
.size())
146 throw std::runtime_error
{"Too many active effect slots"};
148 size_t newcount
{(curarray
->size()>>1) + auxslots
.size()};
149 if(newcount
> std::numeric_limits
<size_t>::max()>>1)
150 throw std::runtime_error
{"Too many active effect slots"};
152 /* Insert the new effect slots into the head of the new array, followed by
155 auto newarray
= EffectSlot::CreatePtrArray(newcount
<<1);
156 auto new_end
= std::transform(auxslots
.begin(), auxslots
.end(), newarray
->begin(),
157 std::mem_fn(&ALeffectslot::mSlot
));
158 new_end
= std::copy_n(curarray
->begin(), curarray
->size()>>1, new_end
);
160 /* Remove any duplicates (first instance of each will be kept). */
161 for(auto start
=newarray
->begin()+1;;)
163 new_end
= std::remove(start
, new_end
, *(start
-1));
164 if(start
== new_end
) break;
167 newcount
= static_cast<size_t>(std::distance(newarray
->begin(), new_end
));
169 /* Reallocate newarray if the new size ended up smaller from duplicate
172 if(newcount
< newarray
->size()>>1) UNLIKELY
174 auto oldarray
= std::move(newarray
);
175 newarray
= EffectSlot::CreatePtrArray(newcount
<<1);
176 new_end
= std::copy_n(oldarray
->begin(), newcount
, newarray
->begin());
178 std::fill(new_end
, newarray
->end(), nullptr);
180 auto oldarray
= context
->mActiveAuxSlots
.exchange(std::move(newarray
),
181 std::memory_order_acq_rel
);
182 std::ignore
= context
->mDevice
->waitForMix();
185 void RemoveActiveEffectSlots(const al::span
<ALeffectslot
*> auxslots
, ALCcontext
*context
)
187 if(auxslots
.empty()) return;
188 EffectSlotArray
*curarray
{context
->mActiveAuxSlots
.load(std::memory_order_acquire
)};
190 /* Don't shrink the allocated array size since we don't know how many (if
191 * any) of the effect slots to remove are in the array.
193 auto newarray
= EffectSlot::CreatePtrArray(curarray
->size());
195 auto new_end
= std::copy_n(curarray
->begin(), curarray
->size()>>1, newarray
->begin());
196 /* Remove elements from newarray that match any ID in slotids. */
197 for(const ALeffectslot
*auxslot
: auxslots
)
199 auto slot_match
= [auxslot
](EffectSlot
*slot
) noexcept
-> bool
200 { return (slot
== auxslot
->mSlot
); };
201 new_end
= std::remove_if(newarray
->begin(), new_end
, slot_match
);
204 /* Reallocate with the new size. */
205 auto newsize
= static_cast<size_t>(std::distance(newarray
->begin(), new_end
));
206 if(newsize
< newarray
->size()>>1) LIKELY
208 auto oldarray
= std::move(newarray
);
209 newarray
= EffectSlot::CreatePtrArray(newsize
<<1);
210 new_end
= std::copy_n(oldarray
->begin(), newsize
, newarray
->begin());
212 std::fill(new_end
, newarray
->end(), nullptr);
214 auto oldarray
= context
->mActiveAuxSlots
.exchange(std::move(newarray
),
215 std::memory_order_acq_rel
);
216 std::ignore
= context
->mDevice
->waitForMix();
220 constexpr auto EffectSlotTypeFromEnum(ALenum type
) noexcept
-> EffectSlotType
224 case AL_EFFECT_NULL
: return EffectSlotType::None
;
225 case AL_EFFECT_REVERB
: return EffectSlotType::Reverb
;
226 case AL_EFFECT_CHORUS
: return EffectSlotType::Chorus
;
227 case AL_EFFECT_DISTORTION
: return EffectSlotType::Distortion
;
228 case AL_EFFECT_ECHO
: return EffectSlotType::Echo
;
229 case AL_EFFECT_FLANGER
: return EffectSlotType::Flanger
;
230 case AL_EFFECT_FREQUENCY_SHIFTER
: return EffectSlotType::FrequencyShifter
;
231 case AL_EFFECT_VOCAL_MORPHER
: return EffectSlotType::VocalMorpher
;
232 case AL_EFFECT_PITCH_SHIFTER
: return EffectSlotType::PitchShifter
;
233 case AL_EFFECT_RING_MODULATOR
: return EffectSlotType::RingModulator
;
234 case AL_EFFECT_AUTOWAH
: return EffectSlotType::Autowah
;
235 case AL_EFFECT_COMPRESSOR
: return EffectSlotType::Compressor
;
236 case AL_EFFECT_EQUALIZER
: return EffectSlotType::Equalizer
;
237 case AL_EFFECT_EAXREVERB
: return EffectSlotType::Reverb
;
238 case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT
: return EffectSlotType::Dedicated
;
239 case AL_EFFECT_DEDICATED_DIALOGUE
: return EffectSlotType::Dedicated
;
240 case AL_EFFECT_CONVOLUTION_SOFT
: return EffectSlotType::Convolution
;
242 ERR("Unhandled effect enum: 0x%04x\n", type
);
243 return EffectSlotType::None
;
246 auto EnsureEffectSlots(ALCcontext
*context
, size_t needed
) noexcept
-> bool
248 size_t count
{std::accumulate(context
->mEffectSlotList
.cbegin(),
249 context
->mEffectSlotList
.cend(), 0_uz
,
250 [](size_t cur
, const EffectSlotSubList
&sublist
) noexcept
-> size_t
251 { return cur
+ static_cast<ALuint
>(al::popcount(sublist
.FreeMask
)); })};
253 while(needed
> count
)
255 if(context
->mEffectSlotList
.size() >= 1<<25) UNLIKELY
258 EffectSlotSubList sublist
{};
259 sublist
.FreeMask
= ~0_u64
;
260 sublist
.EffectSlots
= SubListAllocator
{}.allocate(1);
261 context
->mEffectSlotList
.emplace_back(std::move(sublist
));
262 count
+= std::tuple_size_v
<SubListAllocator::value_type
>;
270 ALeffectslot
*AllocEffectSlot(ALCcontext
*context
)
272 auto sublist
= std::find_if(context
->mEffectSlotList
.begin(), context
->mEffectSlotList
.end(),
273 [](const EffectSlotSubList
&entry
) noexcept
-> bool
274 { return entry
.FreeMask
!= 0; });
275 auto lidx
= static_cast<ALuint
>(std::distance(context
->mEffectSlotList
.begin(), sublist
));
276 auto slidx
= static_cast<ALuint
>(al::countr_zero(sublist
->FreeMask
));
279 ALeffectslot
*slot
{al::construct_at(al::to_address(sublist
->EffectSlots
->begin() + slidx
),
281 aluInitEffectPanning(slot
->mSlot
, context
);
283 /* Add 1 to avoid ID 0. */
284 slot
->id
= ((lidx
<<6) | slidx
) + 1;
286 context
->mNumEffectSlots
+= 1;
287 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
292 void FreeEffectSlot(ALCcontext
*context
, ALeffectslot
*slot
)
294 context
->mEffectSlotNames
.erase(slot
->id
);
296 const ALuint id
{slot
->id
- 1};
297 const size_t lidx
{id
>> 6};
298 const ALuint slidx
{id
& 0x3f};
300 std::destroy_at(slot
);
302 context
->mEffectSlotList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
303 context
->mNumEffectSlots
--;
307 inline void UpdateProps(ALeffectslot
*slot
, ALCcontext
*context
)
309 if(!context
->mDeferUpdates
&& slot
->mState
== SlotState::Playing
)
311 slot
->updateProps(context
);
314 slot
->mPropsDirty
= true;
320 AL_API
DECL_FUNC2(void, alGenAuxiliaryEffectSlots
, ALsizei
,n
, ALuint
*,effectslots
)
321 FORCE_ALIGN
void AL_APIENTRY
alGenAuxiliaryEffectSlotsDirect(ALCcontext
*context
, ALsizei n
,
322 ALuint
*effectslots
) noexcept
325 throw al::context_error
{AL_INVALID_VALUE
, "Generating %d effect slots", n
};
326 if(n
<= 0) UNLIKELY
return;
328 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
329 ALCdevice
*device
{context
->mALDevice
.get()};
331 const al::span eids
{effectslots
, static_cast<ALuint
>(n
)};
332 if(eids
.size() > device
->AuxiliaryEffectSlotMax
-context
->mNumEffectSlots
)
333 throw al::context_error
{AL_OUT_OF_MEMORY
, "Exceeding %u effect slot limit (%u + %d)",
334 device
->AuxiliaryEffectSlotMax
, context
->mNumEffectSlots
, n
};
336 if(!EnsureEffectSlots(context
, eids
.size()))
337 throw al::context_error
{AL_OUT_OF_MEMORY
, "Failed to allocate %d effectslot%s", n
,
338 (n
== 1) ? "" : "s"};
340 std::vector
<ALeffectslot
*> slots
;
344 /* Special handling for the easy and normal case. */
345 eids
[0] = AllocEffectSlot(context
)->id
;
349 slots
.reserve(eids
.size());
350 std::generate_n(std::back_inserter(slots
), eids
.size(),
351 [context
]{ return AllocEffectSlot(context
); });
353 std::transform(slots
.cbegin(), slots
.cend(), eids
.begin(),
354 [](ALeffectslot
*slot
) -> ALuint
{ return slot
->id
; });
357 catch(std::exception
& e
) {
358 ERR("Exception allocating effectslot %zu of %d: %s\n", slots
.size()+1, n
, e
.what());
359 auto delete_effectslot
= [context
](ALeffectslot
*slot
) -> void
360 { FreeEffectSlot(context
, slot
); };
361 std::for_each(slots
.begin(), slots
.end(), delete_effectslot
);
362 throw al::context_error
{AL_INVALID_OPERATION
, "Exception allocating %d effectslots: %s", n
,
366 catch(al::context_error
& e
) {
367 context
->setError(e
.errorCode(), "%s", e
.what());
370 AL_API
DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots
, ALsizei
,n
, const ALuint
*,effectslots
)
371 FORCE_ALIGN
void AL_APIENTRY
alDeleteAuxiliaryEffectSlotsDirect(ALCcontext
*context
, ALsizei n
,
372 const ALuint
*effectslots
) noexcept
375 throw al::context_error
{AL_INVALID_VALUE
, "Deleting %d effect slots", n
};
376 if(n
<= 0) UNLIKELY
return;
378 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
381 ALeffectslot
*slot
{LookupEffectSlot(context
, *effectslots
)};
383 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", *effectslots
};
384 if(slot
->ref
.load(std::memory_order_relaxed
) != 0)
385 throw al::context_error
{AL_INVALID_OPERATION
, "Deleting in-use effect slot %u",
388 RemoveActiveEffectSlots({&slot
, 1u}, context
);
389 FreeEffectSlot(context
, slot
);
393 const al::span eids
{effectslots
, static_cast<ALuint
>(n
)};
394 std::vector
<ALeffectslot
*> slots
;
395 slots
.reserve(eids
.size());
397 auto lookupslot
= [context
](const ALuint eid
) -> ALeffectslot
*
399 ALeffectslot
*slot
{LookupEffectSlot(context
, eid
)};
401 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", eid
};
402 if(slot
->ref
.load(std::memory_order_relaxed
) != 0)
403 throw al::context_error
{AL_INVALID_OPERATION
, "Deleting in-use effect slot %u",
407 std::transform(eids
.cbegin(), eids
.cend(), std::back_inserter(slots
), lookupslot
);
409 /* All effectslots are valid, remove and delete them */
410 RemoveActiveEffectSlots(slots
, context
);
412 auto delete_effectslot
= [context
](const ALuint eid
) -> void
414 if(ALeffectslot
*slot
{LookupEffectSlot(context
, eid
)})
415 FreeEffectSlot(context
, slot
);
417 std::for_each(eids
.begin(), eids
.end(), delete_effectslot
);
420 catch(al::context_error
& e
) {
421 context
->setError(e
.errorCode(), "%s", e
.what());
424 AL_API
DECL_FUNC1(ALboolean
, alIsAuxiliaryEffectSlot
, ALuint
,effectslot
)
425 FORCE_ALIGN ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlotDirect(ALCcontext
*context
,
426 ALuint effectslot
) noexcept
428 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
429 if(LookupEffectSlot(context
, effectslot
) != nullptr)
435 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlaySOFT(ALuint slotid
) noexcept
437 ContextRef context
{GetContextRef()};
438 if(!context
) UNLIKELY
return;
440 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
441 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotid
)};
444 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotid
);
447 if(slot
->mState
== SlotState::Playing
)
450 slot
->mPropsDirty
= false;
451 slot
->updateProps(context
.get());
453 AddActiveEffectSlots({&slot
, 1}, context
.get());
454 slot
->mState
= SlotState::Playing
;
457 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlayvSOFT(ALsizei n
, const ALuint
*slotids
) noexcept
459 ContextRef context
{GetContextRef()};
460 if(!context
) UNLIKELY
return;
464 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d effect slots", n
};
465 if(n
<= 0) UNLIKELY
return;
467 auto ids
= al::span
{slotids
, static_cast<ALuint
>(n
)};
468 auto slots
= std::vector
<ALeffectslot
*>(ids
.size());
469 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
471 auto lookupslot
= [&context
](const ALuint id
) -> ALeffectslot
*
473 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), id
)};
475 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", id
};
477 if(slot
->mState
!= SlotState::Playing
)
479 slot
->mPropsDirty
= false;
480 slot
->updateProps(context
.get());
484 std::transform(ids
.cbegin(), ids
.cend(), slots
.begin(), lookupslot
);
486 AddActiveEffectSlots(slots
, context
.get());
487 for(auto slot
: slots
)
488 slot
->mState
= SlotState::Playing
;
490 catch(al::context_error
& e
) {
491 context
->setError(e
.errorCode(), "%s", e
.what());
496 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopSOFT(ALuint slotid
) noexcept
498 ContextRef context
{GetContextRef()};
499 if(!context
) UNLIKELY
return;
501 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
502 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotid
)};
505 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotid
);
509 RemoveActiveEffectSlots({&slot
, 1}, context
.get());
510 slot
->mState
= SlotState::Stopped
;
513 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopvSOFT(ALsizei n
, const ALuint
*slotids
) noexcept
515 ContextRef context
{GetContextRef()};
516 if(!context
) UNLIKELY
return;
520 throw al::context_error
{AL_INVALID_VALUE
, "Stopping %d effect slots", n
};
521 if(n
<= 0) UNLIKELY
return;
523 auto ids
= al::span
{slotids
, static_cast<ALuint
>(n
)};
524 auto slots
= std::vector
<ALeffectslot
*>(ids
.size());
525 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
527 auto lookupslot
= [&context
](const ALuint id
) -> ALeffectslot
*
529 if(ALeffectslot
*slot
{LookupEffectSlot(context
.get(), id
)})
531 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", id
};
533 std::transform(ids
.cbegin(), ids
.cend(), slots
.begin(), lookupslot
);
535 RemoveActiveEffectSlots(slots
, context
.get());
536 for(auto slot
: slots
)
537 slot
->mState
= SlotState::Stopped
;
539 catch(al::context_error
& e
) {
540 context
->setError(e
.errorCode(), "%s", e
.what());
546 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSloti
, ALuint
,effectslot
, ALenum
,param
, ALint
,value
)
547 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotiDirect(ALCcontext
*context
, ALuint effectslot
,
548 ALenum param
, ALint value
) noexcept
550 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
551 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
553 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
555 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
557 ALeffectslot
*target
{};
561 case AL_EFFECTSLOT_EFFECT
:
563 ALCdevice
*device
{context
->mALDevice
.get()};
564 std::lock_guard
<std::mutex
> effectlock
{device
->EffectLock
};
565 ALeffect
*effect
{value
? LookupEffect(device
, static_cast<ALuint
>(value
)) : nullptr};
567 err
= slot
->initEffect(effect
->id
, effect
->type
, effect
->Props
, context
);
571 throw al::context_error
{AL_INVALID_VALUE
, "Invalid effect ID %u", value
};
572 err
= slot
->initEffect(0, AL_EFFECT_NULL
, EffectProps
{}, context
);
575 if(err
!= AL_NO_ERROR
)
576 throw al::context_error
{err
, "Effect initialization failed"};
578 if(slot
->mState
== SlotState::Initial
) UNLIKELY
580 slot
->mPropsDirty
= false;
581 slot
->updateProps(context
);
583 AddActiveEffectSlots({&slot
, 1}, context
);
584 slot
->mState
= SlotState::Playing
;
587 UpdateProps(slot
, context
);
590 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
591 if(!(value
== AL_TRUE
|| value
== AL_FALSE
))
592 throw al::context_error
{AL_INVALID_VALUE
,
593 "Effect slot auxiliary send auto out of range"};
594 if(!(slot
->AuxSendAuto
== !!value
)) LIKELY
596 slot
->AuxSendAuto
= !!value
;
597 UpdateProps(slot
, context
);
601 case AL_EFFECTSLOT_TARGET_SOFT
:
602 target
= LookupEffectSlot(context
, static_cast<ALuint
>(value
));
604 throw al::context_error
{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 throw al::context_error
{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
);
630 if(target
) IncrementRef(target
->ref
);
631 slot
->Target
= target
;
632 UpdateProps(slot
, context
);
636 if(slot
->mState
== SlotState::Playing
)
637 throw al::context_error
{AL_INVALID_OPERATION
,
638 "Setting buffer on playing effect slot %u", slot
->id
};
640 if(ALbuffer
*buffer
{slot
->Buffer
})
642 if(buffer
->id
== static_cast<ALuint
>(value
)) UNLIKELY
645 else if(value
== 0) UNLIKELY
649 ALCdevice
*device
{context
->mALDevice
.get()};
650 std::lock_guard
<std::mutex
> bufferlock
{device
->BufferLock
};
654 buffer
= LookupBuffer(device
, static_cast<ALuint
>(value
));
656 throw al::context_error
{AL_INVALID_VALUE
, "Invalid buffer ID %u", value
};
657 if(buffer
->mCallback
)
658 throw al::context_error
{AL_INVALID_OPERATION
,
659 "Callback buffer not valid for effects"};
661 IncrementRef(buffer
->ref
);
664 if(ALbuffer
*oldbuffer
{slot
->Buffer
})
665 DecrementRef(oldbuffer
->ref
);
666 slot
->Buffer
= buffer
;
669 auto *state
= slot
->Effect
.State
.get();
670 state
->deviceUpdate(device
, buffer
);
672 UpdateProps(slot
, context
);
675 case AL_EFFECTSLOT_STATE_SOFT
:
676 throw al::context_error
{AL_INVALID_OPERATION
, "AL_EFFECTSLOT_STATE_SOFT is read-only"};
679 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x", param
};
681 catch(al::context_error
& e
) {
682 context
->setError(e
.errorCode(), "%s", e
.what());
685 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSlotiv
, ALuint
,effectslot
, ALenum
,param
, const ALint
*,values
)
686 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotivDirect(ALCcontext
*context
, ALuint effectslot
,
687 ALenum param
, const ALint
*values
) noexcept
691 case AL_EFFECTSLOT_EFFECT
:
692 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
693 case AL_EFFECTSLOT_TARGET_SOFT
:
694 case AL_EFFECTSLOT_STATE_SOFT
:
696 alAuxiliaryEffectSlotiDirect(context
, effectslot
, param
, *values
);
700 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
701 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
703 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
708 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
711 catch(al::context_error
& e
) {
712 context
->setError(e
.errorCode(), "%s", e
.what());
715 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSlotf
, ALuint
,effectslot
, ALenum
,param
, ALfloat
,value
)
716 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotfDirect(ALCcontext
*context
, ALuint effectslot
,
717 ALenum param
, ALfloat value
) noexcept
719 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
720 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
722 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
724 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
728 case AL_EFFECTSLOT_GAIN
:
729 if(!(value
>= 0.0f
&& value
<= 1.0f
))
730 throw al::context_error
{AL_INVALID_VALUE
, "Effect slot gain out of range"};
731 if(!(slot
->Gain
== value
)) LIKELY
734 UpdateProps(slot
, context
);
739 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x", param
};
741 catch(al::context_error
& e
) {
742 context
->setError(e
.errorCode(), "%s", e
.what());
745 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSlotfv
, ALuint
,effectslot
, ALenum
,param
, const ALfloat
*,values
)
746 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotfvDirect(ALCcontext
*context
, ALuint effectslot
,
747 ALenum param
, const ALfloat
*values
) noexcept
751 case AL_EFFECTSLOT_GAIN
:
752 alAuxiliaryEffectSlotfDirect(context
, effectslot
, param
, *values
);
756 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
757 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
759 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
764 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
767 catch(al::context_error
& e
) {
768 context
->setError(e
.errorCode(), "%s", e
.what());
772 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSloti
, ALuint
,effectslot
, ALenum
,param
, ALint
*,value
)
773 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotiDirect(ALCcontext
*context
,
774 ALuint effectslot
, ALenum param
, ALint
*value
) noexcept
776 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
777 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
779 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
783 case AL_EFFECTSLOT_EFFECT
:
784 *value
= static_cast<ALint
>(slot
->EffectId
);
787 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
788 *value
= slot
->AuxSendAuto
? AL_TRUE
: AL_FALSE
;
791 case AL_EFFECTSLOT_TARGET_SOFT
:
792 if(auto *target
= slot
->Target
)
793 *value
= static_cast<ALint
>(target
->id
);
798 case AL_EFFECTSLOT_STATE_SOFT
:
799 *value
= static_cast<int>(slot
->mState
);
803 if(auto *buffer
= slot
->Buffer
)
804 *value
= static_cast<ALint
>(buffer
->id
);
810 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x", param
};
812 catch(al::context_error
& e
) {
813 context
->setError(e
.errorCode(), "%s", e
.what());
816 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv
, ALuint
,effectslot
, ALenum
,param
, ALint
*,values
)
817 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotivDirect(ALCcontext
*context
,
818 ALuint effectslot
, ALenum param
, ALint
*values
) noexcept
822 case AL_EFFECTSLOT_EFFECT
:
823 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
824 case AL_EFFECTSLOT_TARGET_SOFT
:
825 case AL_EFFECTSLOT_STATE_SOFT
:
827 alGetAuxiliaryEffectSlotiDirect(context
, effectslot
, param
, values
);
831 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
832 ALeffectslot
*slot
= LookupEffectSlot(context
, effectslot
);
834 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
839 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
842 catch(al::context_error
& e
) {
843 context
->setError(e
.errorCode(), "%s", e
.what());
846 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotf
, ALuint
,effectslot
, ALenum
,param
, ALfloat
*,value
)
847 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotfDirect(ALCcontext
*context
,
848 ALuint effectslot
, ALenum param
, ALfloat
*value
) noexcept
850 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
851 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
853 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
857 case AL_EFFECTSLOT_GAIN
:
862 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x", param
};
864 catch(al::context_error
& e
) {
865 context
->setError(e
.errorCode(), "%s", e
.what());
868 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv
, ALuint
,effectslot
, ALenum
,param
, ALfloat
*,values
)
869 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotfvDirect(ALCcontext
*context
,
870 ALuint effectslot
, ALenum param
, ALfloat
*values
) noexcept
874 case AL_EFFECTSLOT_GAIN
:
875 alGetAuxiliaryEffectSlotfDirect(context
, effectslot
, param
, values
);
879 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
880 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
882 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
887 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
890 catch(al::context_error
& e
) {
891 context
->setError(e
.errorCode(), "%s", e
.what());
895 ALeffectslot::ALeffectslot(ALCcontext
*context
)
897 EffectStateFactory
*factory
{getFactoryByType(EffectSlotType::None
)};
898 if(!factory
) throw std::runtime_error
{"Failed to get null effect factory"};
900 al::intrusive_ptr
<EffectState
> state
{factory
->create()};
901 Effect
.State
= state
;
903 mSlot
= context
->getEffectSlot();
905 mSlot
->mEffectState
= std::move(state
);
908 ALeffectslot::~ALeffectslot()
911 DecrementRef(Target
->ref
);
914 DecrementRef(Buffer
->ref
);
917 if(auto *slot
= mSlot
->Update
.exchange(nullptr, std::memory_order_relaxed
))
918 slot
->State
= nullptr;
920 mSlot
->mEffectState
= nullptr;
921 mSlot
->InUse
= false;
924 ALenum
ALeffectslot::initEffect(ALuint effectId
, ALenum effectType
, const EffectProps
&effectProps
,
927 EffectSlotType newtype
{EffectSlotTypeFromEnum(effectType
)};
928 if(newtype
!= Effect
.Type
)
930 EffectStateFactory
*factory
{getFactoryByType(newtype
)};
933 ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype
));
934 return AL_INVALID_ENUM
;
936 al::intrusive_ptr
<EffectState
> state
{factory
->create()};
938 ALCdevice
*device
{context
->mALDevice
.get()};
939 std::unique_lock
<std::mutex
> statelock
{device
->StateLock
};
940 state
->mOutTarget
= device
->Dry
.Buffer
;
943 state
->deviceUpdate(device
, Buffer
);
946 Effect
.Type
= newtype
;
947 Effect
.Props
= effectProps
;
949 Effect
.State
= std::move(state
);
951 else if(newtype
!= EffectSlotType::None
)
952 Effect
.Props
= effectProps
;
955 /* Remove state references from old effect slot property updates. */
956 EffectSlotProps
*props
{context
->mFreeEffectSlotProps
.load()};
959 props
->State
= nullptr;
960 props
= props
->next
.load(std::memory_order_relaxed
);
966 void ALeffectslot::updateProps(ALCcontext
*context
) const
968 /* Get an unused property container, or allocate a new one as needed. */
969 EffectSlotProps
*props
{context
->mFreeEffectSlotProps
.load(std::memory_order_acquire
)};
972 context
->allocEffectSlotProps();
973 props
= context
->mFreeEffectSlotProps
.load(std::memory_order_acquire
);
975 EffectSlotProps
*next
;
977 next
= props
->next
.load(std::memory_order_relaxed
);
978 } while(!context
->mFreeEffectSlotProps
.compare_exchange_weak(props
, next
,
979 std::memory_order_acq_rel
, std::memory_order_acquire
));
981 /* Copy in current property values. */
983 props
->AuxSendAuto
= AuxSendAuto
;
984 props
->Target
= Target
? Target
->mSlot
: nullptr;
986 props
->Type
= Effect
.Type
;
987 props
->Props
= Effect
.Props
;
988 props
->State
= Effect
.State
;
990 /* Set the new container for updating internal parameters. */
991 props
= mSlot
->Update
.exchange(props
, std::memory_order_acq_rel
);
994 /* If there was an unused update container, put it back in the
997 props
->State
= nullptr;
998 AtomicReplaceHead(context
->mFreeEffectSlotProps
, props
);
1002 void ALeffectslot::SetName(ALCcontext
* context
, ALuint id
, std::string_view name
)
1004 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
1006 auto slot
= LookupEffectSlot(context
, id
);
1008 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", id
};
1010 context
->mEffectSlotNames
.insert_or_assign(id
, name
);
1013 void UpdateAllEffectSlotProps(ALCcontext
*context
)
1015 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
1016 for(auto &sublist
: context
->mEffectSlotList
)
1018 uint64_t usemask
{~sublist
.FreeMask
};
1021 const auto idx
= static_cast<uint
>(al::countr_zero(usemask
));
1022 usemask
&= ~(1_u64
<< idx
);
1023 auto &slot
= (*sublist
.EffectSlots
)[idx
];
1025 if(slot
.mState
!= SlotState::Stopped
&& std::exchange(slot
.mPropsDirty
, false))
1026 slot
.updateProps(context
);
1031 EffectSlotSubList::~EffectSlotSubList()
1036 uint64_t usemask
{~FreeMask
};
1039 const int idx
{al::countr_zero(usemask
)};
1040 std::destroy_at(al::to_address(EffectSlots
->begin() + idx
));
1041 usemask
&= ~(1_u64
<< idx
);
1043 FreeMask
= ~usemask
;
1044 SubListAllocator
{}.deallocate(EffectSlots
, 1);
1045 EffectSlots
= nullptr;
1049 void ALeffectslot::eax_initialize(ALCcontext
& al_context
, EaxFxSlotIndexValue index
)
1051 if(index
>= EAX_MAX_FXSLOTS
)
1052 eax_fail("Index out of range.");
1054 eax_al_context_
= &al_context
;
1055 eax_fx_slot_index_
= index
;
1056 eax_fx_slot_set_defaults();
1058 eax_effect_
= std::make_unique
<EaxEffect
>();
1059 if(index
== 0) eax_effect_
->init
<EaxReverbCommitter
>();
1060 else if(index
== 1) eax_effect_
->init
<EaxChorusCommitter
>();
1061 else eax_effect_
->init
<EaxNullCommitter
>();
1064 void ALeffectslot::eax_commit()
1066 if(eax_df_
!= EaxDirtyFlags
{})
1068 auto df
= EaxDirtyFlags
{};
1069 switch(eax_version_
)
1074 eax5_fx_slot_commit(eax123_
, df
);
1077 eax4_fx_slot_commit(df
);
1080 eax5_fx_slot_commit(eax5_
, df
);
1083 eax_df_
= EaxDirtyFlags
{};
1085 if((df
& eax_volume_dirty_bit
) != EaxDirtyFlags
{})
1086 eax_fx_slot_set_volume();
1087 if((df
& eax_flags_dirty_bit
) != EaxDirtyFlags
{})
1088 eax_fx_slot_set_flags();
1091 if(eax_effect_
->commit(eax_version_
))
1092 eax_set_efx_slot_effect(*eax_effect_
);
1095 [[noreturn
]] void ALeffectslot::eax_fail(const char* message
)
1097 throw Exception
{message
};
1100 [[noreturn
]] void ALeffectslot::eax_fail_unknown_effect_id()
1102 eax_fail("Unknown effect ID.");
1105 [[noreturn
]] void ALeffectslot::eax_fail_unknown_property_id()
1107 eax_fail("Unknown property ID.");
1110 [[noreturn
]] void ALeffectslot::eax_fail_unknown_version()
1112 eax_fail("Unknown version.");
1115 void ALeffectslot::eax4_fx_slot_ensure_unlocked() const
1117 if(eax4_fx_slot_is_legacy())
1118 eax_fail("Locked legacy slot.");
1121 ALenum
ALeffectslot::eax_get_efx_effect_type(const GUID
& guid
)
1123 if(guid
== EAX_NULL_GUID
)
1124 return AL_EFFECT_NULL
;
1125 if(guid
== EAX_AUTOWAH_EFFECT
)
1126 return AL_EFFECT_AUTOWAH
;
1127 if(guid
== EAX_CHORUS_EFFECT
)
1128 return AL_EFFECT_CHORUS
;
1129 if(guid
== EAX_AGCCOMPRESSOR_EFFECT
)
1130 return AL_EFFECT_COMPRESSOR
;
1131 if(guid
== EAX_DISTORTION_EFFECT
)
1132 return AL_EFFECT_DISTORTION
;
1133 if(guid
== EAX_REVERB_EFFECT
)
1134 return AL_EFFECT_EAXREVERB
;
1135 if(guid
== EAX_ECHO_EFFECT
)
1136 return AL_EFFECT_ECHO
;
1137 if(guid
== EAX_EQUALIZER_EFFECT
)
1138 return AL_EFFECT_EQUALIZER
;
1139 if(guid
== EAX_FLANGER_EFFECT
)
1140 return AL_EFFECT_FLANGER
;
1141 if(guid
== EAX_FREQUENCYSHIFTER_EFFECT
)
1142 return AL_EFFECT_FREQUENCY_SHIFTER
;
1143 if(guid
== EAX_PITCHSHIFTER_EFFECT
)
1144 return AL_EFFECT_PITCH_SHIFTER
;
1145 if(guid
== EAX_RINGMODULATOR_EFFECT
)
1146 return AL_EFFECT_RING_MODULATOR
;
1147 if(guid
== EAX_VOCALMORPHER_EFFECT
)
1148 return AL_EFFECT_VOCAL_MORPHER
;
1150 eax_fail_unknown_effect_id();
1153 const GUID
& ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
1155 switch(eax_fx_slot_index_
)
1157 case 0: return EAX_REVERB_EFFECT
;
1158 case 1: return EAX_CHORUS_EFFECT
;
1159 default: return EAX_NULL_GUID
;
1163 long ALeffectslot::eax_get_eax_default_lock() const noexcept
1165 return eax4_fx_slot_is_legacy() ? EAXFXSLOT_LOCKED
: EAXFXSLOT_UNLOCKED
;
1168 void ALeffectslot::eax4_fx_slot_set_defaults(Eax4Props
& props
) noexcept
1170 props
.guidLoadEffect
= eax_get_eax_default_effect_guid();
1171 props
.lVolume
= EAXFXSLOT_DEFAULTVOLUME
;
1172 props
.lLock
= eax_get_eax_default_lock();
1173 props
.ulFlags
= EAX40FXSLOT_DEFAULTFLAGS
;
1176 void ALeffectslot::eax5_fx_slot_set_defaults(Eax5Props
& props
) noexcept
1178 props
.guidLoadEffect
= eax_get_eax_default_effect_guid();
1179 props
.lVolume
= EAXFXSLOT_DEFAULTVOLUME
;
1180 props
.lLock
= EAXFXSLOT_UNLOCKED
;
1181 props
.ulFlags
= EAX50FXSLOT_DEFAULTFLAGS
;
1182 props
.lOcclusion
= EAXFXSLOT_DEFAULTOCCLUSION
;
1183 props
.flOcclusionLFRatio
= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
;
1186 void ALeffectslot::eax_fx_slot_set_defaults()
1188 eax5_fx_slot_set_defaults(eax123_
.i
);
1189 eax4_fx_slot_set_defaults(eax4_
.i
);
1190 eax5_fx_slot_set_defaults(eax5_
.i
);
1192 eax_df_
= EaxDirtyFlags
{};
1195 void ALeffectslot::eax4_fx_slot_get(const EaxCall
& call
, const Eax4Props
& props
)
1197 switch(call
.get_property_id())
1199 case EAXFXSLOT_ALLPARAMETERS
:
1200 call
.set_value
<Exception
>(props
);
1202 case EAXFXSLOT_LOADEFFECT
:
1203 call
.set_value
<Exception
>(props
.guidLoadEffect
);
1205 case EAXFXSLOT_VOLUME
:
1206 call
.set_value
<Exception
>(props
.lVolume
);
1208 case EAXFXSLOT_LOCK
:
1209 call
.set_value
<Exception
>(props
.lLock
);
1211 case EAXFXSLOT_FLAGS
:
1212 call
.set_value
<Exception
>(props
.ulFlags
);
1215 eax_fail_unknown_property_id();
1219 void ALeffectslot::eax5_fx_slot_get(const EaxCall
& call
, const Eax5Props
& props
)
1221 switch(call
.get_property_id())
1223 case EAXFXSLOT_ALLPARAMETERS
:
1224 call
.set_value
<Exception
>(props
);
1226 case EAXFXSLOT_LOADEFFECT
:
1227 call
.set_value
<Exception
>(props
.guidLoadEffect
);
1229 case EAXFXSLOT_VOLUME
:
1230 call
.set_value
<Exception
>(props
.lVolume
);
1232 case EAXFXSLOT_LOCK
:
1233 call
.set_value
<Exception
>(props
.lLock
);
1235 case EAXFXSLOT_FLAGS
:
1236 call
.set_value
<Exception
>(props
.ulFlags
);
1238 case EAXFXSLOT_OCCLUSION
:
1239 call
.set_value
<Exception
>(props
.lOcclusion
);
1241 case EAXFXSLOT_OCCLUSIONLFRATIO
:
1242 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
1245 eax_fail_unknown_property_id();
1249 void ALeffectslot::eax_fx_slot_get(const EaxCall
& call
) const
1251 switch(call
.get_version())
1253 case 4: eax4_fx_slot_get(call
, eax4_
.i
); break;
1254 case 5: eax5_fx_slot_get(call
, eax5_
.i
); break;
1255 default: eax_fail_unknown_version();
1259 bool ALeffectslot::eax_get(const EaxCall
& call
)
1261 switch(call
.get_property_set_id())
1263 case EaxCallPropertySetId::fx_slot
:
1264 eax_fx_slot_get(call
);
1266 case EaxCallPropertySetId::fx_slot_effect
:
1267 eax_effect_
->get(call
);
1270 eax_fail_unknown_property_id();
1276 void ALeffectslot::eax_fx_slot_load_effect(int version
, ALenum altype
)
1278 if(!IsValidEffectType(altype
))
1279 altype
= AL_EFFECT_NULL
;
1280 eax_effect_
->set_defaults(version
, altype
);
1283 void ALeffectslot::eax_fx_slot_set_volume()
1285 const auto volume
= std::clamp(eax_
.lVolume
, EAXFXSLOT_MINVOLUME
, EAXFXSLOT_MAXVOLUME
);
1286 const auto gain
= level_mb_to_gain(static_cast<float>(volume
));
1287 eax_set_efx_slot_gain(gain
);
1290 void ALeffectslot::eax_fx_slot_set_environment_flag()
1292 eax_set_efx_slot_send_auto((eax_
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0u);
1295 void ALeffectslot::eax_fx_slot_set_flags()
1297 eax_fx_slot_set_environment_flag();
1300 void ALeffectslot::eax4_fx_slot_set_all(const EaxCall
& call
)
1302 eax4_fx_slot_ensure_unlocked();
1303 const auto& src
= call
.get_value
<Exception
, const EAX40FXSLOTPROPERTIES
>();
1304 Eax4AllValidator
{}(src
);
1305 auto& dst
= eax4_
.i
;
1306 eax_df_
|= eax_load_effect_dirty_bit
; // Always reset the effect.
1307 eax_df_
|= (dst
.lVolume
!= src
.lVolume
? eax_volume_dirty_bit
: EaxDirtyFlags
{});
1308 eax_df_
|= (dst
.lLock
!= src
.lLock
? eax_lock_dirty_bit
: EaxDirtyFlags
{});
1309 eax_df_
|= (dst
.ulFlags
!= src
.ulFlags
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1313 void ALeffectslot::eax5_fx_slot_set_all(const EaxCall
& call
)
1315 const auto& src
= call
.get_value
<Exception
, const EAX50FXSLOTPROPERTIES
>();
1316 Eax5AllValidator
{}(src
);
1317 auto& dst
= eax5_
.i
;
1318 eax_df_
|= eax_load_effect_dirty_bit
; // Always reset the effect.
1319 eax_df_
|= (dst
.lVolume
!= src
.lVolume
? eax_volume_dirty_bit
: EaxDirtyFlags
{});
1320 eax_df_
|= (dst
.lLock
!= src
.lLock
? eax_lock_dirty_bit
: EaxDirtyFlags
{});
1321 eax_df_
|= (dst
.ulFlags
!= src
.ulFlags
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1322 eax_df_
|= (dst
.lOcclusion
!= src
.lOcclusion
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1323 eax_df_
|= (dst
.flOcclusionLFRatio
!= src
.flOcclusionLFRatio
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1327 bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept
1329 static constexpr auto dirty_bits
=
1330 eax_occlusion_dirty_bit
|
1331 eax_occlusion_lf_ratio_dirty_bit
|
1332 eax_flags_dirty_bit
;
1334 return (eax_df_
& dirty_bits
) != EaxDirtyFlags
{};
1337 // Returns `true` if all sources should be updated, or `false` otherwise.
1338 bool ALeffectslot::eax4_fx_slot_set(const EaxCall
& call
)
1340 auto& dst
= eax4_
.i
;
1342 switch(call
.get_property_id())
1344 case EAXFXSLOT_NONE
:
1346 case EAXFXSLOT_ALLPARAMETERS
:
1347 eax4_fx_slot_set_all(call
);
1348 if((eax_df_
& eax_load_effect_dirty_bit
))
1349 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1351 case EAXFXSLOT_LOADEFFECT
:
1352 eax4_fx_slot_ensure_unlocked();
1353 eax_fx_slot_set_dirty
<Eax4GuidLoadEffectValidator
, eax_load_effect_dirty_bit
>(call
, dst
.guidLoadEffect
, eax_df_
);
1354 if((eax_df_
& eax_load_effect_dirty_bit
))
1355 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1357 case EAXFXSLOT_VOLUME
:
1358 eax_fx_slot_set
<Eax4VolumeValidator
, eax_volume_dirty_bit
>(call
, dst
.lVolume
, eax_df_
);
1360 case EAXFXSLOT_LOCK
:
1361 eax4_fx_slot_ensure_unlocked();
1362 eax_fx_slot_set
<Eax4LockValidator
, eax_lock_dirty_bit
>(call
, dst
.lLock
, eax_df_
);
1364 case EAXFXSLOT_FLAGS
:
1365 eax_fx_slot_set
<Eax4FlagsValidator
, eax_flags_dirty_bit
>(call
, dst
.ulFlags
, eax_df_
);
1368 eax_fail_unknown_property_id();
1371 return eax_fx_slot_should_update_sources();
1374 // Returns `true` if all sources should be updated, or `false` otherwise.
1375 bool ALeffectslot::eax5_fx_slot_set(const EaxCall
& call
)
1377 auto& dst
= eax5_
.i
;
1379 switch(call
.get_property_id())
1381 case EAXFXSLOT_NONE
:
1383 case EAXFXSLOT_ALLPARAMETERS
:
1384 eax5_fx_slot_set_all(call
);
1385 if((eax_df_
& eax_load_effect_dirty_bit
))
1386 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1388 case EAXFXSLOT_LOADEFFECT
:
1389 eax_fx_slot_set_dirty
<Eax4GuidLoadEffectValidator
, eax_load_effect_dirty_bit
>(call
, dst
.guidLoadEffect
, eax_df_
);
1390 if((eax_df_
& eax_load_effect_dirty_bit
))
1391 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1393 case EAXFXSLOT_VOLUME
:
1394 eax_fx_slot_set
<Eax4VolumeValidator
, eax_volume_dirty_bit
>(call
, dst
.lVolume
, eax_df_
);
1396 case EAXFXSLOT_LOCK
:
1397 eax_fx_slot_set
<Eax4LockValidator
, eax_lock_dirty_bit
>(call
, dst
.lLock
, eax_df_
);
1399 case EAXFXSLOT_FLAGS
:
1400 eax_fx_slot_set
<Eax5FlagsValidator
, eax_flags_dirty_bit
>(call
, dst
.ulFlags
, eax_df_
);
1402 case EAXFXSLOT_OCCLUSION
:
1403 eax_fx_slot_set
<Eax5OcclusionValidator
, eax_occlusion_dirty_bit
>(call
, dst
.lOcclusion
, eax_df_
);
1405 case EAXFXSLOT_OCCLUSIONLFRATIO
:
1406 eax_fx_slot_set
<Eax5OcclusionLfRatioValidator
, eax_occlusion_lf_ratio_dirty_bit
>(call
, dst
.flOcclusionLFRatio
, eax_df_
);
1409 eax_fail_unknown_property_id();
1412 return eax_fx_slot_should_update_sources();
1415 // Returns `true` if all sources should be updated, or `false` otherwise.
1416 bool ALeffectslot::eax_fx_slot_set(const EaxCall
& call
)
1418 switch (call
.get_version())
1420 case 4: return eax4_fx_slot_set(call
);
1421 case 5: return eax5_fx_slot_set(call
);
1422 default: eax_fail_unknown_version();
1426 // Returns `true` if all sources should be updated, or `false` otherwise.
1427 bool ALeffectslot::eax_set(const EaxCall
& call
)
1431 switch(call
.get_property_set_id())
1433 case EaxCallPropertySetId::fx_slot
: ret
= eax_fx_slot_set(call
); break;
1434 case EaxCallPropertySetId::fx_slot_effect
: eax_effect_
->set(call
); break;
1435 default: eax_fail_unknown_property_id();
1438 const auto version
= call
.get_version();
1439 if(eax_version_
!= version
)
1440 eax_df_
= ~EaxDirtyFlags
{};
1441 eax_version_
= version
;
1446 void ALeffectslot::eax4_fx_slot_commit(EaxDirtyFlags
& dst_df
)
1448 eax_fx_slot_commit_property
<eax_load_effect_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::guidLoadEffect
);
1449 eax_fx_slot_commit_property
<eax_volume_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::lVolume
);
1450 eax_fx_slot_commit_property
<eax_lock_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::lLock
);
1451 eax_fx_slot_commit_property
<eax_flags_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::ulFlags
);
1455 if(dst_i
.lOcclusion
!= EAXFXSLOT_DEFAULTOCCLUSION
) {
1456 dst_df
|= eax_occlusion_dirty_bit
;
1457 dst_i
.lOcclusion
= EAXFXSLOT_DEFAULTOCCLUSION
;
1460 if(dst_i
.flOcclusionLFRatio
!= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
) {
1461 dst_df
|= eax_occlusion_lf_ratio_dirty_bit
;
1462 dst_i
.flOcclusionLFRatio
= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
;
1466 void ALeffectslot::eax5_fx_slot_commit(Eax5State
& state
, EaxDirtyFlags
& dst_df
)
1468 eax_fx_slot_commit_property
<eax_load_effect_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::guidLoadEffect
);
1469 eax_fx_slot_commit_property
<eax_volume_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lVolume
);
1470 eax_fx_slot_commit_property
<eax_lock_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lLock
);
1471 eax_fx_slot_commit_property
<eax_flags_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::ulFlags
);
1472 eax_fx_slot_commit_property
<eax_occlusion_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lOcclusion
);
1473 eax_fx_slot_commit_property
<eax_occlusion_lf_ratio_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::flOcclusionLFRatio
);
1476 void ALeffectslot::eax_set_efx_slot_effect(EaxEffect
&effect
)
1478 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
1480 const auto error
= initEffect(0, effect
.al_effect_type_
, effect
.al_effect_props_
,
1483 if(error
!= AL_NO_ERROR
) {
1484 ERR(EAX_PREFIX
"%s\n", "Failed to initialize an effect.");
1488 if(mState
== SlotState::Initial
) {
1489 mPropsDirty
= false;
1490 updateProps(eax_al_context_
);
1491 auto effect_slot_ptr
= this;
1492 AddActiveEffectSlots({&effect_slot_ptr
, 1}, eax_al_context_
);
1493 mState
= SlotState::Playing
;
1502 void ALeffectslot::eax_set_efx_slot_send_auto(bool is_send_auto
)
1504 if(AuxSendAuto
== is_send_auto
)
1507 AuxSendAuto
= is_send_auto
;
1511 void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain
)
1513 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
1517 if(gain
< 0.0f
|| gain
> 1.0f
)
1518 ERR(EAX_PREFIX
"Gain out of range (%f)\n", gain
);
1520 Gain
= std::clamp(gain
, 0.0f
, 1.0f
);
1526 void ALeffectslot::EaxDeleter::operator()(ALeffectslot
* effect_slot
)
1528 eax_delete_al_effect_slot(*effect_slot
->eax_al_context_
, *effect_slot
);
1531 EaxAlEffectSlotUPtr
eax_create_al_effect_slot(ALCcontext
& context
)
1533 #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
1535 std::lock_guard
<std::mutex
> slotlock
{context
.mEffectSlotLock
};
1536 auto& device
= *context
.mALDevice
;
1538 if(context
.mNumEffectSlots
== device
.AuxiliaryEffectSlotMax
) {
1539 ERR(EAX_PREFIX
"%s\n", "Out of memory.");
1543 if(!EnsureEffectSlots(&context
, 1)) {
1544 ERR(EAX_PREFIX
"%s\n", "Failed to ensure.");
1548 return EaxAlEffectSlotUPtr
{AllocEffectSlot(&context
)};
1553 void eax_delete_al_effect_slot(ALCcontext
& context
, ALeffectslot
& effect_slot
)
1555 #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
1557 std::lock_guard
<std::mutex
> slotlock
{context
.mEffectSlotLock
};
1559 if(effect_slot
.ref
.load(std::memory_order_relaxed
) != 0)
1561 ERR(EAX_PREFIX
"Deleting in-use effect slot %u.\n", effect_slot
.id
);
1565 auto effect_slot_ptr
= &effect_slot
;
1566 RemoveActiveEffectSlots({&effect_slot_ptr
, 1}, &context
);
1567 FreeEffectSlot(&context
, &effect_slot
);
1571 #endif // ALSOFT_EAX