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::EAXReverb
: return ReverbStateFactory_getFactory();
83 case EffectSlotType::Reverb
: return StdReverbStateFactory_getFactory();
84 case EffectSlotType::Autowah
: return AutowahStateFactory_getFactory();
85 case EffectSlotType::Chorus
: return ChorusStateFactory_getFactory();
86 case EffectSlotType::Compressor
: return CompressorStateFactory_getFactory();
87 case EffectSlotType::Distortion
: return DistortionStateFactory_getFactory();
88 case EffectSlotType::Echo
: return EchoStateFactory_getFactory();
89 case EffectSlotType::Equalizer
: return EqualizerStateFactory_getFactory();
90 case EffectSlotType::Flanger
: return ChorusStateFactory_getFactory();
91 case EffectSlotType::FrequencyShifter
: return FshifterStateFactory_getFactory();
92 case EffectSlotType::RingModulator
: return ModulatorStateFactory_getFactory();
93 case EffectSlotType::PitchShifter
: return PshifterStateFactory_getFactory();
94 case EffectSlotType::VocalMorpher
: return VmorpherStateFactory_getFactory();
95 case EffectSlotType::DedicatedDialog
: return DedicatedStateFactory_getFactory();
96 case EffectSlotType::DedicatedLFE
: return DedicatedStateFactory_getFactory();
97 case EffectSlotType::Convolution
: return ConvolutionStateFactory_getFactory();
103 auto LookupEffectSlot(ALCcontext
*context
, ALuint id
) noexcept
-> ALeffectslot
*
105 const size_t lidx
{(id
-1) >> 6};
106 const ALuint slidx
{(id
-1) & 0x3f};
108 if(lidx
>= context
->mEffectSlotList
.size()) UNLIKELY
110 EffectSlotSubList
&sublist
{context
->mEffectSlotList
[lidx
]};
111 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
113 return al::to_address(sublist
.EffectSlots
->begin() + slidx
);
116 inline auto LookupEffect(ALCdevice
*device
, ALuint id
) noexcept
-> ALeffect
*
118 const size_t lidx
{(id
-1) >> 6};
119 const ALuint slidx
{(id
-1) & 0x3f};
121 if(lidx
>= device
->EffectList
.size()) UNLIKELY
123 EffectSubList
&sublist
= device
->EffectList
[lidx
];
124 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
126 return al::to_address(sublist
.Effects
->begin() + slidx
);
129 inline auto LookupBuffer(ALCdevice
*device
, ALuint id
) noexcept
-> ALbuffer
*
131 const size_t lidx
{(id
-1) >> 6};
132 const ALuint slidx
{(id
-1) & 0x3f};
134 if(lidx
>= device
->BufferList
.size()) UNLIKELY
136 BufferSubList
&sublist
= device
->BufferList
[lidx
];
137 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
139 return al::to_address(sublist
.Buffers
->begin() + slidx
);
143 void AddActiveEffectSlots(const al::span
<ALeffectslot
*> auxslots
, ALCcontext
*context
)
145 if(auxslots
.empty()) return;
146 EffectSlotArray
*curarray
{context
->mActiveAuxSlots
.load(std::memory_order_acquire
)};
147 if((curarray
->size()>>1) > std::numeric_limits
<size_t>::max()-auxslots
.size())
148 throw std::runtime_error
{"Too many active effect slots"};
150 size_t newcount
{(curarray
->size()>>1) + auxslots
.size()};
151 if(newcount
> std::numeric_limits
<size_t>::max()>>1)
152 throw std::runtime_error
{"Too many active effect slots"};
154 /* Insert the new effect slots into the head of the new array, followed by
157 auto newarray
= EffectSlot::CreatePtrArray(newcount
<<1);
158 auto new_end
= std::transform(auxslots
.begin(), auxslots
.end(), newarray
->begin(),
159 std::mem_fn(&ALeffectslot::mSlot
));
160 new_end
= std::copy_n(curarray
->begin(), curarray
->size()>>1, new_end
);
162 /* Remove any duplicates (first instance of each will be kept). */
163 for(auto start
=newarray
->begin()+1;;)
165 new_end
= std::remove(start
, new_end
, *(start
-1));
166 if(start
== new_end
) break;
169 newcount
= static_cast<size_t>(std::distance(newarray
->begin(), new_end
));
171 /* Reallocate newarray if the new size ended up smaller from duplicate
174 if(newcount
< newarray
->size()>>1) UNLIKELY
176 auto oldarray
= std::move(newarray
);
177 newarray
= EffectSlot::CreatePtrArray(newcount
<<1);
178 new_end
= std::copy_n(oldarray
->begin(), newcount
, newarray
->begin());
180 std::fill(new_end
, newarray
->end(), nullptr);
182 auto oldarray
= context
->mActiveAuxSlots
.exchange(std::move(newarray
),
183 std::memory_order_acq_rel
);
184 std::ignore
= context
->mDevice
->waitForMix();
187 void RemoveActiveEffectSlots(const al::span
<ALeffectslot
*> auxslots
, ALCcontext
*context
)
189 if(auxslots
.empty()) return;
190 EffectSlotArray
*curarray
{context
->mActiveAuxSlots
.load(std::memory_order_acquire
)};
192 /* Don't shrink the allocated array size since we don't know how many (if
193 * any) of the effect slots to remove are in the array.
195 auto newarray
= EffectSlot::CreatePtrArray(curarray
->size());
197 auto new_end
= std::copy_n(curarray
->begin(), curarray
->size()>>1, newarray
->begin());
198 /* Remove elements from newarray that match any ID in slotids. */
199 for(const ALeffectslot
*auxslot
: auxslots
)
201 auto slot_match
= [auxslot
](EffectSlot
*slot
) noexcept
-> bool
202 { return (slot
== auxslot
->mSlot
); };
203 new_end
= std::remove_if(newarray
->begin(), new_end
, slot_match
);
206 /* Reallocate with the new size. */
207 auto newsize
= static_cast<size_t>(std::distance(newarray
->begin(), new_end
));
208 if(newsize
< newarray
->size()>>1) LIKELY
210 auto oldarray
= std::move(newarray
);
211 newarray
= EffectSlot::CreatePtrArray(newsize
<<1);
212 new_end
= std::copy_n(oldarray
->begin(), newsize
, newarray
->begin());
214 std::fill(new_end
, newarray
->end(), nullptr);
216 auto oldarray
= context
->mActiveAuxSlots
.exchange(std::move(newarray
),
217 std::memory_order_acq_rel
);
218 std::ignore
= context
->mDevice
->waitForMix();
222 constexpr auto EffectSlotTypeFromEnum(ALenum type
) noexcept
-> EffectSlotType
226 case AL_EFFECT_NULL
: return EffectSlotType::None
;
227 case AL_EFFECT_REVERB
: return EffectSlotType::Reverb
;
228 case AL_EFFECT_CHORUS
: return EffectSlotType::Chorus
;
229 case AL_EFFECT_DISTORTION
: return EffectSlotType::Distortion
;
230 case AL_EFFECT_ECHO
: return EffectSlotType::Echo
;
231 case AL_EFFECT_FLANGER
: return EffectSlotType::Flanger
;
232 case AL_EFFECT_FREQUENCY_SHIFTER
: return EffectSlotType::FrequencyShifter
;
233 case AL_EFFECT_VOCAL_MORPHER
: return EffectSlotType::VocalMorpher
;
234 case AL_EFFECT_PITCH_SHIFTER
: return EffectSlotType::PitchShifter
;
235 case AL_EFFECT_RING_MODULATOR
: return EffectSlotType::RingModulator
;
236 case AL_EFFECT_AUTOWAH
: return EffectSlotType::Autowah
;
237 case AL_EFFECT_COMPRESSOR
: return EffectSlotType::Compressor
;
238 case AL_EFFECT_EQUALIZER
: return EffectSlotType::Equalizer
;
239 case AL_EFFECT_EAXREVERB
: return EffectSlotType::EAXReverb
;
240 case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT
: return EffectSlotType::DedicatedLFE
;
241 case AL_EFFECT_DEDICATED_DIALOGUE
: return EffectSlotType::DedicatedDialog
;
242 case AL_EFFECT_CONVOLUTION_SOFT
: return EffectSlotType::Convolution
;
244 ERR("Unhandled effect enum: 0x%04x\n", type
);
245 return EffectSlotType::None
;
248 auto EnsureEffectSlots(ALCcontext
*context
, size_t needed
) noexcept
-> bool
250 size_t count
{std::accumulate(context
->mEffectSlotList
.cbegin(),
251 context
->mEffectSlotList
.cend(), 0_uz
,
252 [](size_t cur
, const EffectSlotSubList
&sublist
) noexcept
-> size_t
253 { return cur
+ static_cast<ALuint
>(al::popcount(sublist
.FreeMask
)); })};
255 while(needed
> count
)
257 if(context
->mEffectSlotList
.size() >= 1<<25) UNLIKELY
260 EffectSlotSubList sublist
{};
261 sublist
.FreeMask
= ~0_u64
;
262 sublist
.EffectSlots
= SubListAllocator
{}.allocate(1);
263 context
->mEffectSlotList
.emplace_back(std::move(sublist
));
264 count
+= std::tuple_size_v
<SubListAllocator::value_type
>;
272 ALeffectslot
*AllocEffectSlot(ALCcontext
*context
)
274 auto sublist
= std::find_if(context
->mEffectSlotList
.begin(), context
->mEffectSlotList
.end(),
275 [](const EffectSlotSubList
&entry
) noexcept
-> bool
276 { return entry
.FreeMask
!= 0; });
277 auto lidx
= static_cast<ALuint
>(std::distance(context
->mEffectSlotList
.begin(), sublist
));
278 auto slidx
= static_cast<ALuint
>(al::countr_zero(sublist
->FreeMask
));
281 ALeffectslot
*slot
{al::construct_at(al::to_address(sublist
->EffectSlots
->begin() + slidx
),
283 aluInitEffectPanning(slot
->mSlot
, context
);
285 /* Add 1 to avoid ID 0. */
286 slot
->id
= ((lidx
<<6) | slidx
) + 1;
288 context
->mNumEffectSlots
+= 1;
289 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
294 void FreeEffectSlot(ALCcontext
*context
, ALeffectslot
*slot
)
296 context
->mEffectSlotNames
.erase(slot
->id
);
298 const ALuint id
{slot
->id
- 1};
299 const size_t lidx
{id
>> 6};
300 const ALuint slidx
{id
& 0x3f};
302 std::destroy_at(slot
);
304 context
->mEffectSlotList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
305 context
->mNumEffectSlots
--;
309 inline void UpdateProps(ALeffectslot
*slot
, ALCcontext
*context
)
311 if(!context
->mDeferUpdates
&& slot
->mState
== SlotState::Playing
)
313 slot
->updateProps(context
);
316 slot
->mPropsDirty
= true;
322 AL_API
DECL_FUNC2(void, alGenAuxiliaryEffectSlots
, ALsizei
,n
, ALuint
*,effectslots
)
323 FORCE_ALIGN
void AL_APIENTRY
alGenAuxiliaryEffectSlotsDirect(ALCcontext
*context
, ALsizei n
,
324 ALuint
*effectslots
) noexcept
327 throw al::context_error
{AL_INVALID_VALUE
, "Generating %d effect slots", n
};
328 if(n
<= 0) UNLIKELY
return;
330 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
331 ALCdevice
*device
{context
->mALDevice
.get()};
333 const al::span eids
{effectslots
, static_cast<ALuint
>(n
)};
334 if(eids
.size() > device
->AuxiliaryEffectSlotMax
-context
->mNumEffectSlots
)
335 throw al::context_error
{AL_OUT_OF_MEMORY
, "Exceeding %u effect slot limit (%u + %d)",
336 device
->AuxiliaryEffectSlotMax
, context
->mNumEffectSlots
, n
};
338 if(!EnsureEffectSlots(context
, eids
.size()))
339 throw al::context_error
{AL_OUT_OF_MEMORY
, "Failed to allocate %d effectslot%s", n
,
340 (n
== 1) ? "" : "s"};
342 std::vector
<ALeffectslot
*> slots
;
346 /* Special handling for the easy and normal case. */
347 eids
[0] = AllocEffectSlot(context
)->id
;
351 slots
.reserve(eids
.size());
352 std::generate_n(std::back_inserter(slots
), eids
.size(),
353 [context
]{ return AllocEffectSlot(context
); });
355 std::transform(slots
.cbegin(), slots
.cend(), eids
.begin(),
356 [](ALeffectslot
*slot
) -> ALuint
{ return slot
->id
; });
359 catch(std::exception
& e
) {
360 ERR("Exception allocating effectslot %zu of %d: %s\n", slots
.size()+1, n
, e
.what());
361 auto delete_effectslot
= [context
](ALeffectslot
*slot
) -> void
362 { FreeEffectSlot(context
, slot
); };
363 std::for_each(slots
.begin(), slots
.end(), delete_effectslot
);
364 throw al::context_error
{AL_INVALID_OPERATION
, "Exception allocating %d effectslots: %s", n
,
368 catch(al::context_error
& e
) {
369 context
->setError(e
.errorCode(), "%s", e
.what());
372 AL_API
DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots
, ALsizei
,n
, const ALuint
*,effectslots
)
373 FORCE_ALIGN
void AL_APIENTRY
alDeleteAuxiliaryEffectSlotsDirect(ALCcontext
*context
, ALsizei n
,
374 const ALuint
*effectslots
) noexcept
377 throw al::context_error
{AL_INVALID_VALUE
, "Deleting %d effect slots", n
};
378 if(n
<= 0) UNLIKELY
return;
380 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
383 ALeffectslot
*slot
{LookupEffectSlot(context
, *effectslots
)};
385 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", *effectslots
};
386 if(slot
->ref
.load(std::memory_order_relaxed
) != 0)
387 throw al::context_error
{AL_INVALID_OPERATION
, "Deleting in-use effect slot %u",
390 RemoveActiveEffectSlots({&slot
, 1u}, context
);
391 FreeEffectSlot(context
, slot
);
395 const al::span eids
{effectslots
, static_cast<ALuint
>(n
)};
396 std::vector
<ALeffectslot
*> slots
;
397 slots
.reserve(eids
.size());
399 auto lookupslot
= [context
](const ALuint eid
) -> ALeffectslot
*
401 ALeffectslot
*slot
{LookupEffectSlot(context
, eid
)};
403 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", eid
};
404 if(slot
->ref
.load(std::memory_order_relaxed
) != 0)
405 throw al::context_error
{AL_INVALID_OPERATION
, "Deleting in-use effect slot %u",
409 std::transform(eids
.cbegin(), eids
.cend(), std::back_inserter(slots
), lookupslot
);
411 /* All effectslots are valid, remove and delete them */
412 RemoveActiveEffectSlots(slots
, context
);
414 auto delete_effectslot
= [context
](const ALuint eid
) -> void
416 if(ALeffectslot
*slot
{LookupEffectSlot(context
, eid
)})
417 FreeEffectSlot(context
, slot
);
419 std::for_each(eids
.begin(), eids
.end(), delete_effectslot
);
422 catch(al::context_error
& e
) {
423 context
->setError(e
.errorCode(), "%s", e
.what());
426 AL_API
DECL_FUNC1(ALboolean
, alIsAuxiliaryEffectSlot
, ALuint
,effectslot
)
427 FORCE_ALIGN ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlotDirect(ALCcontext
*context
,
428 ALuint effectslot
) noexcept
430 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
431 if(LookupEffectSlot(context
, effectslot
) != nullptr)
437 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlaySOFT(ALuint slotid
) noexcept
439 ContextRef context
{GetContextRef()};
440 if(!context
) UNLIKELY
return;
442 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
443 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotid
)};
446 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotid
);
449 if(slot
->mState
== SlotState::Playing
)
452 slot
->mPropsDirty
= false;
453 slot
->updateProps(context
.get());
455 AddActiveEffectSlots({&slot
, 1}, context
.get());
456 slot
->mState
= SlotState::Playing
;
459 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlayvSOFT(ALsizei n
, const ALuint
*slotids
) noexcept
461 ContextRef context
{GetContextRef()};
462 if(!context
) UNLIKELY
return;
466 throw al::context_error
{AL_INVALID_VALUE
, "Playing %d effect slots", n
};
467 if(n
<= 0) UNLIKELY
return;
469 auto ids
= al::span
{slotids
, static_cast<ALuint
>(n
)};
470 auto slots
= std::vector
<ALeffectslot
*>(ids
.size());
471 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
473 auto lookupslot
= [&context
](const ALuint id
) -> ALeffectslot
*
475 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), id
)};
477 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", id
};
479 if(slot
->mState
!= SlotState::Playing
)
481 slot
->mPropsDirty
= false;
482 slot
->updateProps(context
.get());
486 std::transform(ids
.cbegin(), ids
.cend(), slots
.begin(), lookupslot
);
488 AddActiveEffectSlots(slots
, context
.get());
489 for(auto slot
: slots
)
490 slot
->mState
= SlotState::Playing
;
492 catch(al::context_error
& e
) {
493 context
->setError(e
.errorCode(), "%s", e
.what());
498 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopSOFT(ALuint slotid
) noexcept
500 ContextRef context
{GetContextRef()};
501 if(!context
) UNLIKELY
return;
503 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
504 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotid
)};
507 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotid
);
511 RemoveActiveEffectSlots({&slot
, 1}, context
.get());
512 slot
->mState
= SlotState::Stopped
;
515 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopvSOFT(ALsizei n
, const ALuint
*slotids
) noexcept
517 ContextRef context
{GetContextRef()};
518 if(!context
) UNLIKELY
return;
522 throw al::context_error
{AL_INVALID_VALUE
, "Stopping %d effect slots", n
};
523 if(n
<= 0) UNLIKELY
return;
525 auto ids
= al::span
{slotids
, static_cast<ALuint
>(n
)};
526 auto slots
= std::vector
<ALeffectslot
*>(ids
.size());
527 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
529 auto lookupslot
= [&context
](const ALuint id
) -> ALeffectslot
*
531 if(ALeffectslot
*slot
{LookupEffectSlot(context
.get(), id
)})
533 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", id
};
535 std::transform(ids
.cbegin(), ids
.cend(), slots
.begin(), lookupslot
);
537 RemoveActiveEffectSlots(slots
, context
.get());
538 for(auto slot
: slots
)
539 slot
->mState
= SlotState::Stopped
;
541 catch(al::context_error
& e
) {
542 context
->setError(e
.errorCode(), "%s", e
.what());
548 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSloti
, ALuint
,effectslot
, ALenum
,param
, ALint
,value
)
549 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotiDirect(ALCcontext
*context
, ALuint effectslot
,
550 ALenum param
, ALint value
) noexcept
552 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
553 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
555 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
557 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
559 ALeffectslot
*target
{};
563 case AL_EFFECTSLOT_EFFECT
:
565 ALCdevice
*device
{context
->mALDevice
.get()};
566 std::lock_guard
<std::mutex
> effectlock
{device
->EffectLock
};
567 ALeffect
*effect
{value
? LookupEffect(device
, static_cast<ALuint
>(value
)) : nullptr};
569 err
= slot
->initEffect(effect
->id
, effect
->type
, effect
->Props
, context
);
573 throw al::context_error
{AL_INVALID_VALUE
, "Invalid effect ID %u", value
};
574 err
= slot
->initEffect(0, AL_EFFECT_NULL
, EffectProps
{}, context
);
577 if(err
!= AL_NO_ERROR
)
578 throw al::context_error
{err
, "Effect initialization failed"};
580 if(slot
->mState
== SlotState::Initial
) UNLIKELY
582 slot
->mPropsDirty
= false;
583 slot
->updateProps(context
);
585 AddActiveEffectSlots({&slot
, 1}, context
);
586 slot
->mState
= SlotState::Playing
;
589 UpdateProps(slot
, context
);
592 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
593 if(!(value
== AL_TRUE
|| value
== AL_FALSE
))
594 throw al::context_error
{AL_INVALID_VALUE
,
595 "Effect slot auxiliary send auto out of range"};
596 if(!(slot
->AuxSendAuto
== !!value
)) LIKELY
598 slot
->AuxSendAuto
= !!value
;
599 UpdateProps(slot
, context
);
603 case AL_EFFECTSLOT_TARGET_SOFT
:
604 target
= LookupEffectSlot(context
, static_cast<ALuint
>(value
));
606 throw al::context_error
{AL_INVALID_VALUE
, "Invalid effect slot target ID"};
607 if(slot
->Target
== target
) UNLIKELY
611 ALeffectslot
*checker
{target
};
612 while(checker
&& checker
!= slot
)
613 checker
= checker
->Target
;
615 throw al::context_error
{AL_INVALID_OPERATION
,
616 "Setting target of effect slot ID %u to %u creates circular chain", slot
->id
,
620 if(ALeffectslot
*oldtarget
{slot
->Target
})
622 /* We must force an update if there was an existing effect slot
623 * target, in case it's about to be deleted.
625 if(target
) IncrementRef(target
->ref
);
626 DecrementRef(oldtarget
->ref
);
627 slot
->Target
= target
;
628 slot
->updateProps(context
);
632 if(target
) IncrementRef(target
->ref
);
633 slot
->Target
= target
;
634 UpdateProps(slot
, context
);
638 if(slot
->mState
== SlotState::Playing
)
639 throw al::context_error
{AL_INVALID_OPERATION
,
640 "Setting buffer on playing effect slot %u", slot
->id
};
642 if(ALbuffer
*buffer
{slot
->Buffer
})
644 if(buffer
->id
== static_cast<ALuint
>(value
)) UNLIKELY
647 else if(value
== 0) UNLIKELY
651 ALCdevice
*device
{context
->mALDevice
.get()};
652 std::lock_guard
<std::mutex
> bufferlock
{device
->BufferLock
};
656 buffer
= LookupBuffer(device
, static_cast<ALuint
>(value
));
658 throw al::context_error
{AL_INVALID_VALUE
, "Invalid buffer ID %u", value
};
659 if(buffer
->mCallback
)
660 throw al::context_error
{AL_INVALID_OPERATION
,
661 "Callback buffer not valid for effects"};
663 IncrementRef(buffer
->ref
);
666 if(ALbuffer
*oldbuffer
{slot
->Buffer
})
667 DecrementRef(oldbuffer
->ref
);
668 slot
->Buffer
= buffer
;
671 auto *state
= slot
->Effect
.State
.get();
672 state
->deviceUpdate(device
, buffer
);
674 UpdateProps(slot
, context
);
677 case AL_EFFECTSLOT_STATE_SOFT
:
678 throw al::context_error
{AL_INVALID_OPERATION
, "AL_EFFECTSLOT_STATE_SOFT is read-only"};
681 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x", param
};
683 catch(al::context_error
& e
) {
684 context
->setError(e
.errorCode(), "%s", e
.what());
687 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSlotiv
, ALuint
,effectslot
, ALenum
,param
, const ALint
*,values
)
688 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotivDirect(ALCcontext
*context
, ALuint effectslot
,
689 ALenum param
, const ALint
*values
) noexcept
693 case AL_EFFECTSLOT_EFFECT
:
694 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
695 case AL_EFFECTSLOT_TARGET_SOFT
:
696 case AL_EFFECTSLOT_STATE_SOFT
:
698 alAuxiliaryEffectSlotiDirect(context
, effectslot
, param
, *values
);
702 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
703 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
705 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
710 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
713 catch(al::context_error
& e
) {
714 context
->setError(e
.errorCode(), "%s", e
.what());
717 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSlotf
, ALuint
,effectslot
, ALenum
,param
, ALfloat
,value
)
718 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotfDirect(ALCcontext
*context
, ALuint effectslot
,
719 ALenum param
, ALfloat value
) noexcept
721 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
722 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
724 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
726 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
730 case AL_EFFECTSLOT_GAIN
:
731 if(!(value
>= 0.0f
&& value
<= 1.0f
))
732 throw al::context_error
{AL_INVALID_VALUE
, "Effect slot gain out of range"};
733 if(!(slot
->Gain
== value
)) LIKELY
736 UpdateProps(slot
, context
);
741 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x", param
};
743 catch(al::context_error
& e
) {
744 context
->setError(e
.errorCode(), "%s", e
.what());
747 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSlotfv
, ALuint
,effectslot
, ALenum
,param
, const ALfloat
*,values
)
748 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotfvDirect(ALCcontext
*context
, ALuint effectslot
,
749 ALenum param
, const ALfloat
*values
) noexcept
753 case AL_EFFECTSLOT_GAIN
:
754 alAuxiliaryEffectSlotfDirect(context
, effectslot
, param
, *values
);
758 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
759 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
761 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
766 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
769 catch(al::context_error
& e
) {
770 context
->setError(e
.errorCode(), "%s", e
.what());
774 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSloti
, ALuint
,effectslot
, ALenum
,param
, ALint
*,value
)
775 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotiDirect(ALCcontext
*context
,
776 ALuint effectslot
, ALenum param
, ALint
*value
) noexcept
778 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
779 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
781 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
785 case AL_EFFECTSLOT_EFFECT
:
786 *value
= static_cast<ALint
>(slot
->EffectId
);
789 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
790 *value
= slot
->AuxSendAuto
? AL_TRUE
: AL_FALSE
;
793 case AL_EFFECTSLOT_TARGET_SOFT
:
794 if(auto *target
= slot
->Target
)
795 *value
= static_cast<ALint
>(target
->id
);
800 case AL_EFFECTSLOT_STATE_SOFT
:
801 *value
= static_cast<int>(slot
->mState
);
805 if(auto *buffer
= slot
->Buffer
)
806 *value
= static_cast<ALint
>(buffer
->id
);
812 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x", param
};
814 catch(al::context_error
& e
) {
815 context
->setError(e
.errorCode(), "%s", e
.what());
818 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv
, ALuint
,effectslot
, ALenum
,param
, ALint
*,values
)
819 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotivDirect(ALCcontext
*context
,
820 ALuint effectslot
, ALenum param
, ALint
*values
) noexcept
824 case AL_EFFECTSLOT_EFFECT
:
825 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
826 case AL_EFFECTSLOT_TARGET_SOFT
:
827 case AL_EFFECTSLOT_STATE_SOFT
:
829 alGetAuxiliaryEffectSlotiDirect(context
, effectslot
, param
, values
);
833 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
834 ALeffectslot
*slot
= LookupEffectSlot(context
, effectslot
);
836 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
841 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
844 catch(al::context_error
& e
) {
845 context
->setError(e
.errorCode(), "%s", e
.what());
848 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotf
, ALuint
,effectslot
, ALenum
,param
, ALfloat
*,value
)
849 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotfDirect(ALCcontext
*context
,
850 ALuint effectslot
, ALenum param
, ALfloat
*value
) noexcept
852 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
853 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
855 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
859 case AL_EFFECTSLOT_GAIN
:
864 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x", param
};
866 catch(al::context_error
& e
) {
867 context
->setError(e
.errorCode(), "%s", e
.what());
870 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv
, ALuint
,effectslot
, ALenum
,param
, ALfloat
*,values
)
871 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotfvDirect(ALCcontext
*context
,
872 ALuint effectslot
, ALenum param
, ALfloat
*values
) noexcept
876 case AL_EFFECTSLOT_GAIN
:
877 alGetAuxiliaryEffectSlotfDirect(context
, effectslot
, param
, values
);
881 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
882 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
884 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
889 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
892 catch(al::context_error
& e
) {
893 context
->setError(e
.errorCode(), "%s", e
.what());
897 ALeffectslot::ALeffectslot(ALCcontext
*context
)
899 EffectStateFactory
*factory
{getFactoryByType(EffectSlotType::None
)};
900 if(!factory
) throw std::runtime_error
{"Failed to get null effect factory"};
902 al::intrusive_ptr
<EffectState
> state
{factory
->create()};
903 Effect
.State
= state
;
905 mSlot
= context
->getEffectSlot();
907 mSlot
->mEffectState
= std::move(state
);
910 ALeffectslot::~ALeffectslot()
913 DecrementRef(Target
->ref
);
916 DecrementRef(Buffer
->ref
);
919 if(auto *slot
= mSlot
->Update
.exchange(nullptr, std::memory_order_relaxed
))
920 slot
->State
= nullptr;
922 mSlot
->mEffectState
= nullptr;
923 mSlot
->InUse
= false;
926 ALenum
ALeffectslot::initEffect(ALuint effectId
, ALenum effectType
, const EffectProps
&effectProps
,
929 EffectSlotType newtype
{EffectSlotTypeFromEnum(effectType
)};
930 if(newtype
!= Effect
.Type
)
932 EffectStateFactory
*factory
{getFactoryByType(newtype
)};
935 ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype
));
936 return AL_INVALID_ENUM
;
938 al::intrusive_ptr
<EffectState
> state
{factory
->create()};
940 ALCdevice
*device
{context
->mALDevice
.get()};
941 std::unique_lock
<std::mutex
> statelock
{device
->StateLock
};
942 state
->mOutTarget
= device
->Dry
.Buffer
;
945 state
->deviceUpdate(device
, Buffer
);
948 Effect
.Type
= newtype
;
949 Effect
.Props
= effectProps
;
951 Effect
.State
= std::move(state
);
953 else if(newtype
!= EffectSlotType::None
)
954 Effect
.Props
= effectProps
;
957 /* Remove state references from old effect slot property updates. */
958 EffectSlotProps
*props
{context
->mFreeEffectSlotProps
.load()};
961 props
->State
= nullptr;
962 props
= props
->next
.load(std::memory_order_relaxed
);
968 void ALeffectslot::updateProps(ALCcontext
*context
) const
970 /* Get an unused property container, or allocate a new one as needed. */
971 EffectSlotProps
*props
{context
->mFreeEffectSlotProps
.load(std::memory_order_acquire
)};
974 context
->allocEffectSlotProps();
975 props
= context
->mFreeEffectSlotProps
.load(std::memory_order_acquire
);
977 EffectSlotProps
*next
;
979 next
= props
->next
.load(std::memory_order_relaxed
);
980 } while(!context
->mFreeEffectSlotProps
.compare_exchange_weak(props
, next
,
981 std::memory_order_acq_rel
, std::memory_order_acquire
));
983 /* Copy in current property values. */
985 props
->AuxSendAuto
= AuxSendAuto
;
986 props
->Target
= Target
? Target
->mSlot
: nullptr;
988 props
->Type
= Effect
.Type
;
989 props
->Props
= Effect
.Props
;
990 props
->State
= Effect
.State
;
992 /* Set the new container for updating internal parameters. */
993 props
= mSlot
->Update
.exchange(props
, std::memory_order_acq_rel
);
996 /* If there was an unused update container, put it back in the
999 props
->State
= nullptr;
1000 AtomicReplaceHead(context
->mFreeEffectSlotProps
, props
);
1004 void ALeffectslot::SetName(ALCcontext
* context
, ALuint id
, std::string_view name
)
1006 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
1008 auto slot
= LookupEffectSlot(context
, id
);
1010 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", id
};
1012 context
->mEffectSlotNames
.insert_or_assign(id
, name
);
1015 void UpdateAllEffectSlotProps(ALCcontext
*context
)
1017 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
1018 for(auto &sublist
: context
->mEffectSlotList
)
1020 uint64_t usemask
{~sublist
.FreeMask
};
1023 const auto idx
= static_cast<uint
>(al::countr_zero(usemask
));
1024 usemask
&= ~(1_u64
<< idx
);
1025 auto &slot
= (*sublist
.EffectSlots
)[idx
];
1027 if(slot
.mState
!= SlotState::Stopped
&& std::exchange(slot
.mPropsDirty
, false))
1028 slot
.updateProps(context
);
1033 EffectSlotSubList::~EffectSlotSubList()
1038 uint64_t usemask
{~FreeMask
};
1041 const int idx
{al::countr_zero(usemask
)};
1042 std::destroy_at(al::to_address(EffectSlots
->begin() + idx
));
1043 usemask
&= ~(1_u64
<< idx
);
1045 FreeMask
= ~usemask
;
1046 SubListAllocator
{}.deallocate(EffectSlots
, 1);
1047 EffectSlots
= nullptr;
1051 void ALeffectslot::eax_initialize(ALCcontext
& al_context
, EaxFxSlotIndexValue index
)
1053 if(index
>= EAX_MAX_FXSLOTS
)
1054 eax_fail("Index out of range.");
1056 eax_al_context_
= &al_context
;
1057 eax_fx_slot_index_
= index
;
1058 eax_fx_slot_set_defaults();
1060 eax_effect_
= std::make_unique
<EaxEffect
>();
1061 if(index
== 0) eax_effect_
->init
<EaxReverbCommitter
>();
1062 else if(index
== 1) eax_effect_
->init
<EaxChorusCommitter
>();
1063 else eax_effect_
->init
<EaxNullCommitter
>();
1066 void ALeffectslot::eax_commit()
1068 if(eax_df_
!= EaxDirtyFlags
{})
1070 auto df
= EaxDirtyFlags
{};
1071 switch(eax_version_
)
1076 eax5_fx_slot_commit(eax123_
, df
);
1079 eax4_fx_slot_commit(df
);
1082 eax5_fx_slot_commit(eax5_
, df
);
1085 eax_df_
= EaxDirtyFlags
{};
1087 if((df
& eax_volume_dirty_bit
) != EaxDirtyFlags
{})
1088 eax_fx_slot_set_volume();
1089 if((df
& eax_flags_dirty_bit
) != EaxDirtyFlags
{})
1090 eax_fx_slot_set_flags();
1093 if(eax_effect_
->commit(eax_version_
))
1094 eax_set_efx_slot_effect(*eax_effect_
);
1097 [[noreturn
]] void ALeffectslot::eax_fail(const char* message
)
1099 throw Exception
{message
};
1102 [[noreturn
]] void ALeffectslot::eax_fail_unknown_effect_id()
1104 eax_fail("Unknown effect ID.");
1107 [[noreturn
]] void ALeffectslot::eax_fail_unknown_property_id()
1109 eax_fail("Unknown property ID.");
1112 [[noreturn
]] void ALeffectslot::eax_fail_unknown_version()
1114 eax_fail("Unknown version.");
1117 void ALeffectslot::eax4_fx_slot_ensure_unlocked() const
1119 if(eax4_fx_slot_is_legacy())
1120 eax_fail("Locked legacy slot.");
1123 ALenum
ALeffectslot::eax_get_efx_effect_type(const GUID
& guid
)
1125 if(guid
== EAX_NULL_GUID
)
1126 return AL_EFFECT_NULL
;
1127 if(guid
== EAX_AUTOWAH_EFFECT
)
1128 return AL_EFFECT_AUTOWAH
;
1129 if(guid
== EAX_CHORUS_EFFECT
)
1130 return AL_EFFECT_CHORUS
;
1131 if(guid
== EAX_AGCCOMPRESSOR_EFFECT
)
1132 return AL_EFFECT_COMPRESSOR
;
1133 if(guid
== EAX_DISTORTION_EFFECT
)
1134 return AL_EFFECT_DISTORTION
;
1135 if(guid
== EAX_REVERB_EFFECT
)
1136 return AL_EFFECT_EAXREVERB
;
1137 if(guid
== EAX_ECHO_EFFECT
)
1138 return AL_EFFECT_ECHO
;
1139 if(guid
== EAX_EQUALIZER_EFFECT
)
1140 return AL_EFFECT_EQUALIZER
;
1141 if(guid
== EAX_FLANGER_EFFECT
)
1142 return AL_EFFECT_FLANGER
;
1143 if(guid
== EAX_FREQUENCYSHIFTER_EFFECT
)
1144 return AL_EFFECT_FREQUENCY_SHIFTER
;
1145 if(guid
== EAX_PITCHSHIFTER_EFFECT
)
1146 return AL_EFFECT_PITCH_SHIFTER
;
1147 if(guid
== EAX_RINGMODULATOR_EFFECT
)
1148 return AL_EFFECT_RING_MODULATOR
;
1149 if(guid
== EAX_VOCALMORPHER_EFFECT
)
1150 return AL_EFFECT_VOCAL_MORPHER
;
1152 eax_fail_unknown_effect_id();
1155 const GUID
& ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
1157 switch(eax_fx_slot_index_
)
1159 case 0: return EAX_REVERB_EFFECT
;
1160 case 1: return EAX_CHORUS_EFFECT
;
1161 default: return EAX_NULL_GUID
;
1165 long ALeffectslot::eax_get_eax_default_lock() const noexcept
1167 return eax4_fx_slot_is_legacy() ? EAXFXSLOT_LOCKED
: EAXFXSLOT_UNLOCKED
;
1170 void ALeffectslot::eax4_fx_slot_set_defaults(Eax4Props
& props
) noexcept
1172 props
.guidLoadEffect
= eax_get_eax_default_effect_guid();
1173 props
.lVolume
= EAXFXSLOT_DEFAULTVOLUME
;
1174 props
.lLock
= eax_get_eax_default_lock();
1175 props
.ulFlags
= EAX40FXSLOT_DEFAULTFLAGS
;
1178 void ALeffectslot::eax5_fx_slot_set_defaults(Eax5Props
& props
) noexcept
1180 props
.guidLoadEffect
= eax_get_eax_default_effect_guid();
1181 props
.lVolume
= EAXFXSLOT_DEFAULTVOLUME
;
1182 props
.lLock
= EAXFXSLOT_UNLOCKED
;
1183 props
.ulFlags
= EAX50FXSLOT_DEFAULTFLAGS
;
1184 props
.lOcclusion
= EAXFXSLOT_DEFAULTOCCLUSION
;
1185 props
.flOcclusionLFRatio
= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
;
1188 void ALeffectslot::eax_fx_slot_set_defaults()
1190 eax5_fx_slot_set_defaults(eax123_
.i
);
1191 eax4_fx_slot_set_defaults(eax4_
.i
);
1192 eax5_fx_slot_set_defaults(eax5_
.i
);
1194 eax_df_
= EaxDirtyFlags
{};
1197 void ALeffectslot::eax4_fx_slot_get(const EaxCall
& call
, const Eax4Props
& props
)
1199 switch(call
.get_property_id())
1201 case EAXFXSLOT_ALLPARAMETERS
:
1202 call
.set_value
<Exception
>(props
);
1204 case EAXFXSLOT_LOADEFFECT
:
1205 call
.set_value
<Exception
>(props
.guidLoadEffect
);
1207 case EAXFXSLOT_VOLUME
:
1208 call
.set_value
<Exception
>(props
.lVolume
);
1210 case EAXFXSLOT_LOCK
:
1211 call
.set_value
<Exception
>(props
.lLock
);
1213 case EAXFXSLOT_FLAGS
:
1214 call
.set_value
<Exception
>(props
.ulFlags
);
1217 eax_fail_unknown_property_id();
1221 void ALeffectslot::eax5_fx_slot_get(const EaxCall
& call
, const Eax5Props
& props
)
1223 switch(call
.get_property_id())
1225 case EAXFXSLOT_ALLPARAMETERS
:
1226 call
.set_value
<Exception
>(props
);
1228 case EAXFXSLOT_LOADEFFECT
:
1229 call
.set_value
<Exception
>(props
.guidLoadEffect
);
1231 case EAXFXSLOT_VOLUME
:
1232 call
.set_value
<Exception
>(props
.lVolume
);
1234 case EAXFXSLOT_LOCK
:
1235 call
.set_value
<Exception
>(props
.lLock
);
1237 case EAXFXSLOT_FLAGS
:
1238 call
.set_value
<Exception
>(props
.ulFlags
);
1240 case EAXFXSLOT_OCCLUSION
:
1241 call
.set_value
<Exception
>(props
.lOcclusion
);
1243 case EAXFXSLOT_OCCLUSIONLFRATIO
:
1244 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
1247 eax_fail_unknown_property_id();
1251 void ALeffectslot::eax_fx_slot_get(const EaxCall
& call
) const
1253 switch(call
.get_version())
1255 case 4: eax4_fx_slot_get(call
, eax4_
.i
); break;
1256 case 5: eax5_fx_slot_get(call
, eax5_
.i
); break;
1257 default: eax_fail_unknown_version();
1261 bool ALeffectslot::eax_get(const EaxCall
& call
)
1263 switch(call
.get_property_set_id())
1265 case EaxCallPropertySetId::fx_slot
:
1266 eax_fx_slot_get(call
);
1268 case EaxCallPropertySetId::fx_slot_effect
:
1269 eax_effect_
->get(call
);
1272 eax_fail_unknown_property_id();
1278 void ALeffectslot::eax_fx_slot_load_effect(int version
, ALenum altype
)
1280 if(!IsValidEffectType(altype
))
1281 altype
= AL_EFFECT_NULL
;
1282 eax_effect_
->set_defaults(version
, altype
);
1285 void ALeffectslot::eax_fx_slot_set_volume()
1287 const auto volume
= std::clamp(eax_
.lVolume
, EAXFXSLOT_MINVOLUME
, EAXFXSLOT_MAXVOLUME
);
1288 const auto gain
= level_mb_to_gain(static_cast<float>(volume
));
1289 eax_set_efx_slot_gain(gain
);
1292 void ALeffectslot::eax_fx_slot_set_environment_flag()
1294 eax_set_efx_slot_send_auto((eax_
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0u);
1297 void ALeffectslot::eax_fx_slot_set_flags()
1299 eax_fx_slot_set_environment_flag();
1302 void ALeffectslot::eax4_fx_slot_set_all(const EaxCall
& call
)
1304 eax4_fx_slot_ensure_unlocked();
1305 const auto& src
= call
.get_value
<Exception
, const EAX40FXSLOTPROPERTIES
>();
1306 Eax4AllValidator
{}(src
);
1307 auto& dst
= eax4_
.i
;
1308 eax_df_
|= eax_load_effect_dirty_bit
; // Always reset the effect.
1309 eax_df_
|= (dst
.lVolume
!= src
.lVolume
? eax_volume_dirty_bit
: EaxDirtyFlags
{});
1310 eax_df_
|= (dst
.lLock
!= src
.lLock
? eax_lock_dirty_bit
: EaxDirtyFlags
{});
1311 eax_df_
|= (dst
.ulFlags
!= src
.ulFlags
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1315 void ALeffectslot::eax5_fx_slot_set_all(const EaxCall
& call
)
1317 const auto& src
= call
.get_value
<Exception
, const EAX50FXSLOTPROPERTIES
>();
1318 Eax5AllValidator
{}(src
);
1319 auto& dst
= eax5_
.i
;
1320 eax_df_
|= eax_load_effect_dirty_bit
; // Always reset the effect.
1321 eax_df_
|= (dst
.lVolume
!= src
.lVolume
? eax_volume_dirty_bit
: EaxDirtyFlags
{});
1322 eax_df_
|= (dst
.lLock
!= src
.lLock
? eax_lock_dirty_bit
: EaxDirtyFlags
{});
1323 eax_df_
|= (dst
.ulFlags
!= src
.ulFlags
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1324 eax_df_
|= (dst
.lOcclusion
!= src
.lOcclusion
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1325 eax_df_
|= (dst
.flOcclusionLFRatio
!= src
.flOcclusionLFRatio
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1329 bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept
1331 static constexpr auto dirty_bits
=
1332 eax_occlusion_dirty_bit
|
1333 eax_occlusion_lf_ratio_dirty_bit
|
1334 eax_flags_dirty_bit
;
1336 return (eax_df_
& dirty_bits
) != EaxDirtyFlags
{};
1339 // Returns `true` if all sources should be updated, or `false` otherwise.
1340 bool ALeffectslot::eax4_fx_slot_set(const EaxCall
& call
)
1342 auto& dst
= eax4_
.i
;
1344 switch(call
.get_property_id())
1346 case EAXFXSLOT_NONE
:
1348 case EAXFXSLOT_ALLPARAMETERS
:
1349 eax4_fx_slot_set_all(call
);
1350 if((eax_df_
& eax_load_effect_dirty_bit
))
1351 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1353 case EAXFXSLOT_LOADEFFECT
:
1354 eax4_fx_slot_ensure_unlocked();
1355 eax_fx_slot_set_dirty
<Eax4GuidLoadEffectValidator
, eax_load_effect_dirty_bit
>(call
, dst
.guidLoadEffect
, eax_df_
);
1356 if((eax_df_
& eax_load_effect_dirty_bit
))
1357 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1359 case EAXFXSLOT_VOLUME
:
1360 eax_fx_slot_set
<Eax4VolumeValidator
, eax_volume_dirty_bit
>(call
, dst
.lVolume
, eax_df_
);
1362 case EAXFXSLOT_LOCK
:
1363 eax4_fx_slot_ensure_unlocked();
1364 eax_fx_slot_set
<Eax4LockValidator
, eax_lock_dirty_bit
>(call
, dst
.lLock
, eax_df_
);
1366 case EAXFXSLOT_FLAGS
:
1367 eax_fx_slot_set
<Eax4FlagsValidator
, eax_flags_dirty_bit
>(call
, dst
.ulFlags
, eax_df_
);
1370 eax_fail_unknown_property_id();
1373 return eax_fx_slot_should_update_sources();
1376 // Returns `true` if all sources should be updated, or `false` otherwise.
1377 bool ALeffectslot::eax5_fx_slot_set(const EaxCall
& call
)
1379 auto& dst
= eax5_
.i
;
1381 switch(call
.get_property_id())
1383 case EAXFXSLOT_NONE
:
1385 case EAXFXSLOT_ALLPARAMETERS
:
1386 eax5_fx_slot_set_all(call
);
1387 if((eax_df_
& eax_load_effect_dirty_bit
))
1388 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1390 case EAXFXSLOT_LOADEFFECT
:
1391 eax_fx_slot_set_dirty
<Eax4GuidLoadEffectValidator
, eax_load_effect_dirty_bit
>(call
, dst
.guidLoadEffect
, eax_df_
);
1392 if((eax_df_
& eax_load_effect_dirty_bit
))
1393 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1395 case EAXFXSLOT_VOLUME
:
1396 eax_fx_slot_set
<Eax4VolumeValidator
, eax_volume_dirty_bit
>(call
, dst
.lVolume
, eax_df_
);
1398 case EAXFXSLOT_LOCK
:
1399 eax_fx_slot_set
<Eax4LockValidator
, eax_lock_dirty_bit
>(call
, dst
.lLock
, eax_df_
);
1401 case EAXFXSLOT_FLAGS
:
1402 eax_fx_slot_set
<Eax5FlagsValidator
, eax_flags_dirty_bit
>(call
, dst
.ulFlags
, eax_df_
);
1404 case EAXFXSLOT_OCCLUSION
:
1405 eax_fx_slot_set
<Eax5OcclusionValidator
, eax_occlusion_dirty_bit
>(call
, dst
.lOcclusion
, eax_df_
);
1407 case EAXFXSLOT_OCCLUSIONLFRATIO
:
1408 eax_fx_slot_set
<Eax5OcclusionLfRatioValidator
, eax_occlusion_lf_ratio_dirty_bit
>(call
, dst
.flOcclusionLFRatio
, eax_df_
);
1411 eax_fail_unknown_property_id();
1414 return eax_fx_slot_should_update_sources();
1417 // Returns `true` if all sources should be updated, or `false` otherwise.
1418 bool ALeffectslot::eax_fx_slot_set(const EaxCall
& call
)
1420 switch (call
.get_version())
1422 case 4: return eax4_fx_slot_set(call
);
1423 case 5: return eax5_fx_slot_set(call
);
1424 default: eax_fail_unknown_version();
1428 // Returns `true` if all sources should be updated, or `false` otherwise.
1429 bool ALeffectslot::eax_set(const EaxCall
& call
)
1433 switch(call
.get_property_set_id())
1435 case EaxCallPropertySetId::fx_slot
: ret
= eax_fx_slot_set(call
); break;
1436 case EaxCallPropertySetId::fx_slot_effect
: eax_effect_
->set(call
); break;
1437 default: eax_fail_unknown_property_id();
1440 const auto version
= call
.get_version();
1441 if(eax_version_
!= version
)
1442 eax_df_
= ~EaxDirtyFlags
{};
1443 eax_version_
= version
;
1448 void ALeffectslot::eax4_fx_slot_commit(EaxDirtyFlags
& dst_df
)
1450 eax_fx_slot_commit_property
<eax_load_effect_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::guidLoadEffect
);
1451 eax_fx_slot_commit_property
<eax_volume_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::lVolume
);
1452 eax_fx_slot_commit_property
<eax_lock_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::lLock
);
1453 eax_fx_slot_commit_property
<eax_flags_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::ulFlags
);
1457 if(dst_i
.lOcclusion
!= EAXFXSLOT_DEFAULTOCCLUSION
) {
1458 dst_df
|= eax_occlusion_dirty_bit
;
1459 dst_i
.lOcclusion
= EAXFXSLOT_DEFAULTOCCLUSION
;
1462 if(dst_i
.flOcclusionLFRatio
!= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
) {
1463 dst_df
|= eax_occlusion_lf_ratio_dirty_bit
;
1464 dst_i
.flOcclusionLFRatio
= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
;
1468 void ALeffectslot::eax5_fx_slot_commit(Eax5State
& state
, EaxDirtyFlags
& dst_df
)
1470 eax_fx_slot_commit_property
<eax_load_effect_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::guidLoadEffect
);
1471 eax_fx_slot_commit_property
<eax_volume_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lVolume
);
1472 eax_fx_slot_commit_property
<eax_lock_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lLock
);
1473 eax_fx_slot_commit_property
<eax_flags_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::ulFlags
);
1474 eax_fx_slot_commit_property
<eax_occlusion_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lOcclusion
);
1475 eax_fx_slot_commit_property
<eax_occlusion_lf_ratio_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::flOcclusionLFRatio
);
1478 void ALeffectslot::eax_set_efx_slot_effect(EaxEffect
&effect
)
1480 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
1482 const auto error
= initEffect(0, effect
.al_effect_type_
, effect
.al_effect_props_
,
1485 if(error
!= AL_NO_ERROR
) {
1486 ERR(EAX_PREFIX
"%s\n", "Failed to initialize an effect.");
1490 if(mState
== SlotState::Initial
) {
1491 mPropsDirty
= false;
1492 updateProps(eax_al_context_
);
1493 auto effect_slot_ptr
= this;
1494 AddActiveEffectSlots({&effect_slot_ptr
, 1}, eax_al_context_
);
1495 mState
= SlotState::Playing
;
1504 void ALeffectslot::eax_set_efx_slot_send_auto(bool is_send_auto
)
1506 if(AuxSendAuto
== is_send_auto
)
1509 AuxSendAuto
= is_send_auto
;
1513 void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain
)
1515 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
1519 if(gain
< 0.0f
|| gain
> 1.0f
)
1520 ERR(EAX_PREFIX
"Gain out of range (%f)\n", gain
);
1522 Gain
= std::clamp(gain
, 0.0f
, 1.0f
);
1528 void ALeffectslot::EaxDeleter::operator()(ALeffectslot
* effect_slot
)
1530 eax_delete_al_effect_slot(*effect_slot
->eax_al_context_
, *effect_slot
);
1533 EaxAlEffectSlotUPtr
eax_create_al_effect_slot(ALCcontext
& context
)
1535 #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
1537 std::lock_guard
<std::mutex
> slotlock
{context
.mEffectSlotLock
};
1538 auto& device
= *context
.mALDevice
;
1540 if(context
.mNumEffectSlots
== device
.AuxiliaryEffectSlotMax
) {
1541 ERR(EAX_PREFIX
"%s\n", "Out of memory.");
1545 if(!EnsureEffectSlots(&context
, 1)) {
1546 ERR(EAX_PREFIX
"%s\n", "Failed to ensure.");
1550 return EaxAlEffectSlotUPtr
{AllocEffectSlot(&context
)};
1555 void eax_delete_al_effect_slot(ALCcontext
& context
, ALeffectslot
& effect_slot
)
1557 #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
1559 std::lock_guard
<std::mutex
> slotlock
{context
.mEffectSlotLock
};
1561 if(effect_slot
.ref
.load(std::memory_order_relaxed
) != 0)
1563 ERR(EAX_PREFIX
"Deleting in-use effect slot %u.\n", effect_slot
.id
);
1567 auto effect_slot_ptr
= &effect_slot
;
1568 RemoveActiveEffectSlots({&effect_slot_ptr
, 1}, &context
);
1569 FreeEffectSlot(&context
, &effect_slot
);
1573 #endif // ALSOFT_EAX