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"
37 #include <unordered_map>
47 #include "alc/context.h"
48 #include "alc/device.h"
49 #include "alc/effects/base.h"
50 #include "alc/inprogext.h"
52 #include "alnumeric.h"
56 #include "core/buffer_storage.h"
57 #include "core/device.h"
58 #include "core/fpu_ctrl.h"
59 #include "core/logging.h"
60 #include "direct_defs.h"
61 #include "effects/effects.h"
63 #include "flexarray.h"
64 #include "opthelpers.h"
69 #include "eax/effect.h"
70 #include "eax/fx_slot_index.h"
71 #include "eax/utils.h"
76 using SubListAllocator
= al::allocator
<std::array
<ALeffectslot
,64>>;
78 EffectStateFactory
*getFactoryByType(EffectSlotType type
)
82 case EffectSlotType::None
: return NullStateFactory_getFactory();
83 case EffectSlotType::EAXReverb
: return ReverbStateFactory_getFactory();
84 case EffectSlotType::Reverb
: return StdReverbStateFactory_getFactory();
85 case EffectSlotType::Autowah
: return AutowahStateFactory_getFactory();
86 case EffectSlotType::Chorus
: return ChorusStateFactory_getFactory();
87 case EffectSlotType::Compressor
: return CompressorStateFactory_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 FlangerStateFactory_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();
96 case EffectSlotType::DedicatedDialog
: return DedicatedDialogStateFactory_getFactory();
97 case EffectSlotType::DedicatedLFE
: return DedicatedLfeStateFactory_getFactory();
98 case EffectSlotType::Convolution
: return ConvolutionStateFactory_getFactory();
104 inline ALeffectslot
*LookupEffectSlot(ALCcontext
*context
, ALuint id
) noexcept
106 const size_t lidx
{(id
-1) >> 6};
107 const ALuint slidx
{(id
-1) & 0x3f};
109 if(lidx
>= context
->mEffectSlotList
.size()) UNLIKELY
111 EffectSlotSubList
&sublist
{context
->mEffectSlotList
[lidx
]};
112 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
114 return al::to_address(sublist
.EffectSlots
->begin() + slidx
);
117 inline ALeffect
*LookupEffect(ALCdevice
*device
, ALuint id
) noexcept
119 const size_t lidx
{(id
-1) >> 6};
120 const ALuint slidx
{(id
-1) & 0x3f};
122 if(lidx
>= device
->EffectList
.size()) UNLIKELY
124 EffectSubList
&sublist
= device
->EffectList
[lidx
];
125 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
127 return al::to_address(sublist
.Effects
->begin() + slidx
);
130 inline ALbuffer
*LookupBuffer(ALCdevice
*device
, ALuint id
) noexcept
132 const size_t lidx
{(id
-1) >> 6};
133 const ALuint slidx
{(id
-1) & 0x3f};
135 if(lidx
>= device
->BufferList
.size()) UNLIKELY
137 BufferSubList
&sublist
= device
->BufferList
[lidx
];
138 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
140 return al::to_address(sublist
.Buffers
->begin() + slidx
);
144 void AddActiveEffectSlots(const al::span
<ALeffectslot
*> auxslots
, ALCcontext
*context
)
146 if(auxslots
.empty()) return;
147 EffectSlotArray
*curarray
{context
->mActiveAuxSlots
.load(std::memory_order_acquire
)};
148 size_t newcount
{curarray
->size() + auxslots
.size()};
150 /* Insert the new effect slots into the head of the array, followed by the
153 auto newarray
= EffectSlot::CreatePtrArray(newcount
);
154 auto slotiter
= std::transform(auxslots
.begin(), auxslots
.end(), newarray
->begin(),
155 std::mem_fn(&ALeffectslot::mSlot
));
156 std::copy(curarray
->begin(), curarray
->end(), slotiter
);
158 /* Remove any duplicates (first instance of each will be kept). */
159 auto last
= newarray
->end();
160 for(auto start
=newarray
->begin()+1;;)
162 last
= std::remove(start
, last
, *(start
-1));
163 if(start
== last
) break;
166 newcount
= static_cast<size_t>(std::distance(newarray
->begin(), last
));
168 /* Reallocate newarray if the new size ended up smaller from duplicate
171 if(newcount
< newarray
->size()) UNLIKELY
173 auto oldarray
= std::move(newarray
);
174 newarray
= EffectSlot::CreatePtrArray(newcount
);
175 std::copy_n(oldarray
->begin(), newcount
, newarray
->begin());
177 std::uninitialized_fill_n(newarray
->end(), newcount
, nullptr);
179 auto oldarray
= context
->mActiveAuxSlots
.exchange(std::move(newarray
),
180 std::memory_order_acq_rel
);
181 std::ignore
= context
->mDevice
->waitForMix();
183 std::destroy_n(oldarray
->end(), oldarray
->size());
186 void RemoveActiveEffectSlots(const al::span
<ALeffectslot
*> auxslots
, ALCcontext
*context
)
188 if(auxslots
.empty()) return;
189 EffectSlotArray
*curarray
{context
->mActiveAuxSlots
.load(std::memory_order_acquire
)};
191 /* Don't shrink the allocated array size since we don't know how many (if
192 * any) of the effect slots to remove are in the array.
194 auto newarray
= EffectSlot::CreatePtrArray(curarray
->size());
196 auto new_end
= std::copy(curarray
->begin(), curarray
->end(), newarray
->begin());
197 /* Remove elements from newarray that match any ID in slotids. */
198 for(const ALeffectslot
*auxslot
: auxslots
)
200 auto slot_match
= [auxslot
](EffectSlot
*slot
) noexcept
-> bool
201 { return (slot
== auxslot
->mSlot
); };
202 new_end
= std::remove_if(newarray
->begin(), new_end
, slot_match
);
205 /* Reallocate with the new size. */
206 auto newsize
= static_cast<size_t>(std::distance(newarray
->begin(), new_end
));
207 if(newsize
!= newarray
->size()) LIKELY
209 auto oldarray
= std::move(newarray
);
210 newarray
= EffectSlot::CreatePtrArray(newsize
);
211 std::copy_n(oldarray
->begin(), newsize
, newarray
->begin());
213 std::uninitialized_fill_n(newarray
->end(), newsize
, nullptr);
215 auto oldarray
= context
->mActiveAuxSlots
.exchange(std::move(newarray
),
216 std::memory_order_acq_rel
);
217 std::ignore
= context
->mDevice
->waitForMix();
219 std::destroy_n(curarray
->end(), curarray
->size());
223 EffectSlotType
EffectSlotTypeFromEnum(ALenum type
)
227 case AL_EFFECT_NULL
: return EffectSlotType::None
;
228 case AL_EFFECT_REVERB
: return EffectSlotType::Reverb
;
229 case AL_EFFECT_CHORUS
: return EffectSlotType::Chorus
;
230 case AL_EFFECT_DISTORTION
: return EffectSlotType::Distortion
;
231 case AL_EFFECT_ECHO
: return EffectSlotType::Echo
;
232 case AL_EFFECT_FLANGER
: return EffectSlotType::Flanger
;
233 case AL_EFFECT_FREQUENCY_SHIFTER
: return EffectSlotType::FrequencyShifter
;
234 case AL_EFFECT_VOCAL_MORPHER
: return EffectSlotType::VocalMorpher
;
235 case AL_EFFECT_PITCH_SHIFTER
: return EffectSlotType::PitchShifter
;
236 case AL_EFFECT_RING_MODULATOR
: return EffectSlotType::RingModulator
;
237 case AL_EFFECT_AUTOWAH
: return EffectSlotType::Autowah
;
238 case AL_EFFECT_COMPRESSOR
: return EffectSlotType::Compressor
;
239 case AL_EFFECT_EQUALIZER
: return EffectSlotType::Equalizer
;
240 case AL_EFFECT_EAXREVERB
: return EffectSlotType::EAXReverb
;
241 case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT
: return EffectSlotType::DedicatedLFE
;
242 case AL_EFFECT_DEDICATED_DIALOGUE
: return EffectSlotType::DedicatedDialog
;
243 case AL_EFFECT_CONVOLUTION_SOFT
: return EffectSlotType::Convolution
;
245 ERR("Unhandled effect enum: 0x%04x\n", type
);
246 return EffectSlotType::None
;
249 bool EnsureEffectSlots(ALCcontext
*context
, size_t needed
)
251 size_t count
{std::accumulate(context
->mEffectSlotList
.cbegin(),
252 context
->mEffectSlotList
.cend(), 0_uz
,
253 [](size_t cur
, const EffectSlotSubList
&sublist
) noexcept
-> size_t
254 { return cur
+ static_cast<ALuint
>(al::popcount(sublist
.FreeMask
)); })};
257 while(needed
> count
)
259 if(context
->mEffectSlotList
.size() >= 1<<25) UNLIKELY
262 EffectSlotSubList sublist
{};
263 sublist
.FreeMask
= ~0_u64
;
264 sublist
.EffectSlots
= SubListAllocator
{}.allocate(1);
265 context
->mEffectSlotList
.emplace_back(std::move(sublist
));
266 count
+= std::tuple_size_v
<SubListAllocator::value_type
>;
275 ALeffectslot
*AllocEffectSlot(ALCcontext
*context
)
277 auto sublist
= std::find_if(context
->mEffectSlotList
.begin(), context
->mEffectSlotList
.end(),
278 [](const EffectSlotSubList
&entry
) noexcept
-> bool
279 { return entry
.FreeMask
!= 0; });
280 auto lidx
= static_cast<ALuint
>(std::distance(context
->mEffectSlotList
.begin(), sublist
));
281 auto slidx
= static_cast<ALuint
>(al::countr_zero(sublist
->FreeMask
));
284 ALeffectslot
*slot
{al::construct_at(al::to_address(sublist
->EffectSlots
->begin() + slidx
),
286 aluInitEffectPanning(slot
->mSlot
, context
);
288 /* Add 1 to avoid ID 0. */
289 slot
->id
= ((lidx
<<6) | slidx
) + 1;
291 context
->mNumEffectSlots
+= 1;
292 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
297 void FreeEffectSlot(ALCcontext
*context
, ALeffectslot
*slot
)
299 context
->mEffectSlotNames
.erase(slot
->id
);
301 const ALuint id
{slot
->id
- 1};
302 const size_t lidx
{id
>> 6};
303 const ALuint slidx
{id
& 0x3f};
305 std::destroy_at(slot
);
307 context
->mEffectSlotList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
308 context
->mNumEffectSlots
--;
312 inline void UpdateProps(ALeffectslot
*slot
, ALCcontext
*context
)
314 if(!context
->mDeferUpdates
&& slot
->mState
== SlotState::Playing
)
316 slot
->updateProps(context
);
319 slot
->mPropsDirty
= true;
325 AL_API
DECL_FUNC2(void, alGenAuxiliaryEffectSlots
, ALsizei
,n
, ALuint
*,effectslots
)
326 FORCE_ALIGN
void AL_APIENTRY
alGenAuxiliaryEffectSlotsDirect(ALCcontext
*context
, ALsizei n
,
327 ALuint
*effectslots
) noexcept
330 context
->setError(AL_INVALID_VALUE
, "Generating %d effect slots", n
);
331 if(n
<= 0) UNLIKELY
return;
333 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
334 ALCdevice
*device
{context
->mALDevice
.get()};
335 if(static_cast<ALuint
>(n
) > device
->AuxiliaryEffectSlotMax
-context
->mNumEffectSlots
)
337 context
->setError(AL_OUT_OF_MEMORY
, "Exceeding %u effect slot limit (%u + %d)",
338 device
->AuxiliaryEffectSlotMax
, context
->mNumEffectSlots
, n
);
341 if(!EnsureEffectSlots(context
, static_cast<ALuint
>(n
)))
343 context
->setError(AL_OUT_OF_MEMORY
, "Failed to allocate %d effectslot%s", n
,
348 std::vector
<ALeffectslot
*> slots
;
352 /* Special handling for the easy and normal case. */
353 *effectslots
= AllocEffectSlot(context
)->id
;
357 slots
.reserve(static_cast<ALuint
>(n
));
358 std::generate_n(std::back_inserter(slots
), static_cast<ALuint
>(n
),
359 [context
]{ return AllocEffectSlot(context
); });
361 const al::span eids
{effectslots
, static_cast<ALuint
>(n
)};
362 std::transform(slots
.cbegin(), slots
.cend(), eids
.begin(),
363 [](ALeffectslot
*slot
) -> ALuint
{ return slot
->id
; });
366 catch(std::exception
& e
) {
367 ERR("Exception allocating effectslot %zu of %d: %s\n", slots
.size()+1, n
, e
.what());
368 auto delete_effectslot
= [context
](ALeffectslot
*slot
) -> void
369 { FreeEffectSlot(context
, slot
); };
370 std::for_each(slots
.begin(), slots
.end(), delete_effectslot
);
371 context
->setError(AL_INVALID_OPERATION
, "Exception allocating %d effectslots: %s", n
,
376 AL_API
DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots
, ALsizei
,n
, const ALuint
*,effectslots
)
377 FORCE_ALIGN
void AL_APIENTRY
alDeleteAuxiliaryEffectSlotsDirect(ALCcontext
*context
, ALsizei n
,
378 const ALuint
*effectslots
) noexcept
381 context
->setError(AL_INVALID_VALUE
, "Deleting %d effect slots", n
);
382 if(n
<= 0) UNLIKELY
return;
384 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
387 ALeffectslot
*slot
{LookupEffectSlot(context
, *effectslots
)};
390 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", *effectslots
);
393 if(slot
->ref
.load(std::memory_order_relaxed
) != 0) UNLIKELY
395 context
->setError(AL_INVALID_OPERATION
, "Deleting in-use effect slot %u",
399 RemoveActiveEffectSlots({&slot
, 1u}, context
);
400 FreeEffectSlot(context
, slot
);
404 const al::span eids
{effectslots
, static_cast<ALuint
>(n
)};
405 auto slots
= std::vector
<ALeffectslot
*>(static_cast<ALuint
>(n
));
406 for(size_t i
{0};i
< slots
.size();++i
)
408 ALeffectslot
*slot
{LookupEffectSlot(context
, eids
[i
])};
411 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", eids
[i
]);
414 if(slot
->ref
.load(std::memory_order_relaxed
) != 0) UNLIKELY
416 context
->setError(AL_INVALID_OPERATION
, "Deleting in-use effect slot %u", eids
[i
]);
421 /* All effectslots are valid, remove and delete them */
422 RemoveActiveEffectSlots(slots
, context
);
424 auto delete_effectslot
= [context
](const ALuint eid
) -> void
426 if(ALeffectslot
*slot
{LookupEffectSlot(context
, eid
)})
427 FreeEffectSlot(context
, slot
);
429 std::for_each(eids
.begin(), eids
.end(), delete_effectslot
);
433 AL_API
DECL_FUNC1(ALboolean
, alIsAuxiliaryEffectSlot
, ALuint
,effectslot
)
434 FORCE_ALIGN ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlotDirect(ALCcontext
*context
,
435 ALuint effectslot
) noexcept
437 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
438 if(LookupEffectSlot(context
, effectslot
) != nullptr)
444 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlaySOFT(ALuint slotid
) noexcept
446 ContextRef context
{GetContextRef()};
447 if(!context
) UNLIKELY
return;
449 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
450 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotid
)};
453 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotid
);
456 if(slot
->mState
== SlotState::Playing
)
459 slot
->mPropsDirty
= false;
460 slot
->updateProps(context
.get());
462 AddActiveEffectSlots({&slot
, 1}, context
.get());
463 slot
->mState
= SlotState::Playing
;
466 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlayvSOFT(ALsizei n
, const ALuint
*slotids
) noexcept
468 ContextRef context
{GetContextRef()};
469 if(!context
) UNLIKELY
return;
472 context
->setError(AL_INVALID_VALUE
, "Playing %d effect slots", n
);
473 if(n
<= 0) UNLIKELY
return;
475 auto ids
= al::span
{slotids
, static_cast<ALuint
>(n
)};
476 auto slots
= std::vector
<ALeffectslot
*>(ids
.size());
477 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
479 auto lookupslot
= [&context
](const ALuint id
) -> ALeffectslot
*
481 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), id
)};
483 throw effect_exception
{AL_INVALID_NAME
, "Invalid effect slot ID %u", id
};
485 if(slot
->mState
!= SlotState::Playing
)
487 slot
->mPropsDirty
= false;
488 slot
->updateProps(context
.get());
492 std::transform(ids
.cbegin(), ids
.cend(), slots
.begin(), lookupslot
);
494 catch(effect_exception
& e
) {
495 context
->setError(e
.errorCode(), "%s", e
.what());
499 AddActiveEffectSlots(slots
, context
.get());
500 for(auto slot
: slots
)
501 slot
->mState
= SlotState::Playing
;
504 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopSOFT(ALuint slotid
) noexcept
506 ContextRef context
{GetContextRef()};
507 if(!context
) UNLIKELY
return;
509 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
510 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotid
)};
513 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotid
);
517 RemoveActiveEffectSlots({&slot
, 1}, context
.get());
518 slot
->mState
= SlotState::Stopped
;
521 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopvSOFT(ALsizei n
, const ALuint
*slotids
) noexcept
523 ContextRef context
{GetContextRef()};
524 if(!context
) UNLIKELY
return;
527 context
->setError(AL_INVALID_VALUE
, "Stopping %d effect slots", n
);
528 if(n
<= 0) UNLIKELY
return;
530 auto ids
= al::span
{slotids
, static_cast<ALuint
>(n
)};
531 auto slots
= std::vector
<ALeffectslot
*>(ids
.size());
532 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
534 auto lookupslot
= [&context
](const ALuint id
) -> ALeffectslot
*
536 if(ALeffectslot
*slot
{LookupEffectSlot(context
.get(), id
)})
538 throw effect_exception
{AL_INVALID_NAME
, "Invalid effect slot ID %u", id
};
540 std::transform(ids
.cbegin(), ids
.cend(), slots
.begin(), lookupslot
);
542 catch(effect_exception
& e
) {
543 context
->setError(e
.errorCode(), "%s", e
.what());
547 RemoveActiveEffectSlots(slots
, context
.get());
548 for(auto slot
: slots
)
549 slot
->mState
= SlotState::Stopped
;
553 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSloti
, ALuint
,effectslot
, ALenum
,param
, ALint
,value
)
554 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotiDirect(ALCcontext
*context
, ALuint effectslot
,
555 ALenum param
, ALint value
) noexcept
557 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
558 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
559 ALeffectslot
*slot
= LookupEffectSlot(context
, effectslot
);
561 return context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
);
563 ALeffectslot
*target
{};
568 case AL_EFFECTSLOT_EFFECT
:
569 device
= context
->mALDevice
.get();
572 std::lock_guard
<std::mutex
> effectlock
{device
->EffectLock
};
573 ALeffect
*effect
{value
? LookupEffect(device
, static_cast<ALuint
>(value
)) : nullptr};
575 err
= slot
->initEffect(effect
->id
, effect
->type
, effect
->Props
, context
);
579 return context
->setError(AL_INVALID_VALUE
, "Invalid effect ID %u", value
);
580 err
= slot
->initEffect(0, AL_EFFECT_NULL
, EffectProps
{}, context
);
583 if(err
!= AL_NO_ERROR
) UNLIKELY
585 context
->setError(err
, "Effect initialization failed");
588 if(slot
->mState
== SlotState::Initial
) UNLIKELY
590 slot
->mPropsDirty
= false;
591 slot
->updateProps(context
);
593 AddActiveEffectSlots({&slot
, 1}, context
);
594 slot
->mState
= SlotState::Playing
;
599 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
600 if(!(value
== AL_TRUE
|| value
== AL_FALSE
))
601 return context
->setError(AL_INVALID_VALUE
,
602 "Effect slot auxiliary send auto out of range");
603 if(slot
->AuxSendAuto
== !!value
) UNLIKELY
605 slot
->AuxSendAuto
= !!value
;
608 case AL_EFFECTSLOT_TARGET_SOFT
:
609 target
= LookupEffectSlot(context
, static_cast<ALuint
>(value
));
611 return context
->setError(AL_INVALID_VALUE
, "Invalid effect slot target ID");
612 if(slot
->Target
== target
) UNLIKELY
616 ALeffectslot
*checker
{target
};
617 while(checker
&& checker
!= slot
)
618 checker
= checker
->Target
;
620 return context
->setError(AL_INVALID_OPERATION
,
621 "Setting target of effect slot ID %u to %u creates circular chain", slot
->id
,
625 if(ALeffectslot
*oldtarget
{slot
->Target
})
627 /* We must force an update if there was an existing effect slot
628 * target, in case it's about to be deleted.
630 if(target
) IncrementRef(target
->ref
);
631 DecrementRef(oldtarget
->ref
);
632 slot
->Target
= target
;
633 slot
->updateProps(context
);
637 if(target
) IncrementRef(target
->ref
);
638 slot
->Target
= target
;
642 device
= context
->mALDevice
.get();
644 if(slot
->mState
== SlotState::Playing
)
645 return context
->setError(AL_INVALID_OPERATION
,
646 "Setting buffer on playing effect slot %u", slot
->id
);
648 if(ALbuffer
*buffer
{slot
->Buffer
})
650 if(buffer
->id
== static_cast<ALuint
>(value
)) UNLIKELY
653 else if(value
== 0) UNLIKELY
657 std::lock_guard
<std::mutex
> bufferlock
{device
->BufferLock
};
661 buffer
= LookupBuffer(device
, static_cast<ALuint
>(value
));
662 if(!buffer
) return context
->setError(AL_INVALID_VALUE
, "Invalid buffer ID");
663 if(buffer
->mCallback
)
664 return context
->setError(AL_INVALID_OPERATION
,
665 "Callback buffer not valid for effects");
667 IncrementRef(buffer
->ref
);
670 if(ALbuffer
*oldbuffer
{slot
->Buffer
})
671 DecrementRef(oldbuffer
->ref
);
672 slot
->Buffer
= buffer
;
675 auto *state
= slot
->Effect
.State
.get();
676 state
->deviceUpdate(device
, buffer
);
680 case AL_EFFECTSLOT_STATE_SOFT
:
681 return context
->setError(AL_INVALID_OPERATION
, "AL_EFFECTSLOT_STATE_SOFT is read-only");
684 return context
->setError(AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x",
687 UpdateProps(slot
, context
);
690 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSlotiv
, ALuint
,effectslot
, ALenum
,param
, const ALint
*,values
)
691 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotivDirect(ALCcontext
*context
, ALuint effectslot
,
692 ALenum param
, const ALint
*values
) noexcept
696 case AL_EFFECTSLOT_EFFECT
:
697 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
698 case AL_EFFECTSLOT_TARGET_SOFT
:
699 case AL_EFFECTSLOT_STATE_SOFT
:
701 alAuxiliaryEffectSlotiDirect(context
, effectslot
, param
, *values
);
705 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
706 ALeffectslot
*slot
= LookupEffectSlot(context
, effectslot
);
708 return context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
);
713 return context
->setError(AL_INVALID_ENUM
,
714 "Invalid effect slot integer-vector property 0x%04x", param
);
718 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSlotf
, ALuint
,effectslot
, ALenum
,param
, ALfloat
,value
)
719 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotfDirect(ALCcontext
*context
, ALuint effectslot
,
720 ALenum param
, ALfloat value
) noexcept
722 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
723 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
724 ALeffectslot
*slot
= LookupEffectSlot(context
, effectslot
);
726 return context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
);
730 case AL_EFFECTSLOT_GAIN
:
731 if(!(value
>= 0.0f
&& value
<= 1.0f
))
732 return context
->setError(AL_INVALID_VALUE
, "Effect slot gain out of range");
733 if(slot
->Gain
== value
) UNLIKELY
739 return context
->setError(AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x",
742 UpdateProps(slot
, context
);
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 return context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
);
764 return context
->setError(AL_INVALID_ENUM
,
765 "Invalid effect slot float-vector property 0x%04x", param
);
770 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSloti
, ALuint
,effectslot
, ALenum
,param
, ALint
*,value
)
771 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotiDirect(ALCcontext
*context
,
772 ALuint effectslot
, ALenum param
, ALint
*value
) noexcept
774 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
775 ALeffectslot
*slot
= LookupEffectSlot(context
, effectslot
);
777 return context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
);
781 case AL_EFFECTSLOT_EFFECT
:
782 *value
= static_cast<ALint
>(slot
->EffectId
);
785 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
786 *value
= slot
->AuxSendAuto
? AL_TRUE
: AL_FALSE
;
789 case AL_EFFECTSLOT_TARGET_SOFT
:
790 if(auto *target
= slot
->Target
)
791 *value
= static_cast<ALint
>(target
->id
);
796 case AL_EFFECTSLOT_STATE_SOFT
:
797 *value
= static_cast<int>(slot
->mState
);
801 if(auto *buffer
= slot
->Buffer
)
802 *value
= static_cast<ALint
>(buffer
->id
);
808 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x", param
);
812 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv
, ALuint
,effectslot
, ALenum
,param
, ALint
*,values
)
813 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotivDirect(ALCcontext
*context
,
814 ALuint effectslot
, ALenum param
, ALint
*values
) noexcept
818 case AL_EFFECTSLOT_EFFECT
:
819 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
820 case AL_EFFECTSLOT_TARGET_SOFT
:
821 case AL_EFFECTSLOT_STATE_SOFT
:
823 alGetAuxiliaryEffectSlotiDirect(context
, effectslot
, param
, values
);
827 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
828 ALeffectslot
*slot
= LookupEffectSlot(context
, effectslot
);
830 return context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
);
835 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
840 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotf
, ALuint
,effectslot
, ALenum
,param
, ALfloat
*,value
)
841 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotfDirect(ALCcontext
*context
,
842 ALuint effectslot
, ALenum param
, ALfloat
*value
) noexcept
844 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
845 ALeffectslot
*slot
= LookupEffectSlot(context
, effectslot
);
847 return context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
);
851 case AL_EFFECTSLOT_GAIN
:
856 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x", param
);
860 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv
, ALuint
,effectslot
, ALenum
,param
, ALfloat
*,values
)
861 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotfvDirect(ALCcontext
*context
,
862 ALuint effectslot
, ALenum param
, ALfloat
*values
) noexcept
866 case AL_EFFECTSLOT_GAIN
:
867 alGetAuxiliaryEffectSlotfDirect(context
, effectslot
, param
, values
);
871 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
872 ALeffectslot
*slot
= LookupEffectSlot(context
, effectslot
);
874 return context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
);
879 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
885 ALeffectslot::ALeffectslot(ALCcontext
*context
)
887 EffectStateFactory
*factory
{getFactoryByType(EffectSlotType::None
)};
888 if(!factory
) throw std::runtime_error
{"Failed to get null effect factory"};
890 al::intrusive_ptr
<EffectState
> state
{factory
->create()};
891 Effect
.State
= state
;
893 mSlot
= context
->getEffectSlot();
895 mSlot
->mEffectState
= std::move(state
);
898 ALeffectslot::~ALeffectslot()
901 DecrementRef(Target
->ref
);
904 DecrementRef(Buffer
->ref
);
907 if(auto *slot
= mSlot
->Update
.exchange(nullptr, std::memory_order_relaxed
))
908 slot
->State
= nullptr;
910 mSlot
->mEffectState
= nullptr;
911 mSlot
->InUse
= false;
914 ALenum
ALeffectslot::initEffect(ALuint effectId
, ALenum effectType
, const EffectProps
&effectProps
,
917 EffectSlotType newtype
{EffectSlotTypeFromEnum(effectType
)};
918 if(newtype
!= Effect
.Type
)
920 EffectStateFactory
*factory
{getFactoryByType(newtype
)};
923 ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype
));
924 return AL_INVALID_ENUM
;
926 al::intrusive_ptr
<EffectState
> state
{factory
->create()};
928 ALCdevice
*device
{context
->mALDevice
.get()};
929 std::unique_lock
<std::mutex
> statelock
{device
->StateLock
};
930 state
->mOutTarget
= device
->Dry
.Buffer
;
933 state
->deviceUpdate(device
, Buffer
);
936 Effect
.Type
= newtype
;
937 Effect
.Props
= effectProps
;
939 Effect
.State
= std::move(state
);
941 else if(newtype
!= EffectSlotType::None
)
942 Effect
.Props
= effectProps
;
945 /* Remove state references from old effect slot property updates. */
946 EffectSlotProps
*props
{context
->mFreeEffectSlotProps
.load()};
949 props
->State
= nullptr;
950 props
= props
->next
.load(std::memory_order_relaxed
);
956 void ALeffectslot::updateProps(ALCcontext
*context
) const
958 /* Get an unused property container, or allocate a new one as needed. */
959 EffectSlotProps
*props
{context
->mFreeEffectSlotProps
.load(std::memory_order_acquire
)};
962 context
->allocEffectSlotProps();
963 props
= context
->mFreeEffectSlotProps
.load(std::memory_order_acquire
);
965 EffectSlotProps
*next
;
967 next
= props
->next
.load(std::memory_order_relaxed
);
968 } while(!context
->mFreeEffectSlotProps
.compare_exchange_weak(props
, next
,
969 std::memory_order_acq_rel
, std::memory_order_acquire
));
971 /* Copy in current property values. */
973 props
->AuxSendAuto
= AuxSendAuto
;
974 props
->Target
= Target
? Target
->mSlot
: nullptr;
976 props
->Type
= Effect
.Type
;
977 props
->Props
= Effect
.Props
;
978 props
->State
= Effect
.State
;
980 /* Set the new container for updating internal parameters. */
981 props
= mSlot
->Update
.exchange(props
, std::memory_order_acq_rel
);
984 /* If there was an unused update container, put it back in the
987 props
->State
= nullptr;
988 AtomicReplaceHead(context
->mFreeEffectSlotProps
, props
);
992 void ALeffectslot::SetName(ALCcontext
* context
, ALuint id
, std::string_view name
)
994 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
996 auto slot
= LookupEffectSlot(context
, id
);
998 return context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", id
);
1000 context
->mEffectSlotNames
.insert_or_assign(id
, name
);
1003 void UpdateAllEffectSlotProps(ALCcontext
*context
)
1005 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
1006 for(auto &sublist
: context
->mEffectSlotList
)
1008 uint64_t usemask
{~sublist
.FreeMask
};
1011 const auto idx
= static_cast<uint
>(al::countr_zero(usemask
));
1012 usemask
&= ~(1_u64
<< idx
);
1013 auto &slot
= (*sublist
.EffectSlots
)[idx
];
1015 if(slot
.mState
!= SlotState::Stopped
&& std::exchange(slot
.mPropsDirty
, false))
1016 slot
.updateProps(context
);
1021 EffectSlotSubList::~EffectSlotSubList()
1026 uint64_t usemask
{~FreeMask
};
1029 const int idx
{al::countr_zero(usemask
)};
1030 std::destroy_at(al::to_address(EffectSlots
->begin() + idx
));
1031 usemask
&= ~(1_u64
<< idx
);
1033 FreeMask
= ~usemask
;
1034 SubListAllocator
{}.deallocate(EffectSlots
, 1);
1035 EffectSlots
= nullptr;
1039 void ALeffectslot::eax_initialize(ALCcontext
& al_context
, EaxFxSlotIndexValue index
)
1041 if(index
>= EAX_MAX_FXSLOTS
)
1042 eax_fail("Index out of range.");
1044 eax_al_context_
= &al_context
;
1045 eax_fx_slot_index_
= index
;
1046 eax_fx_slot_set_defaults();
1048 eax_effect_
= std::make_unique
<EaxEffect
>();
1049 if(index
== 0) eax_effect_
->init
<EaxReverbCommitter
>();
1050 else if(index
== 1) eax_effect_
->init
<EaxChorusCommitter
>();
1051 else eax_effect_
->init
<EaxNullCommitter
>();
1054 void ALeffectslot::eax_commit()
1056 if(eax_df_
!= EaxDirtyFlags
{})
1058 auto df
= EaxDirtyFlags
{};
1059 switch(eax_version_
)
1064 eax5_fx_slot_commit(eax123_
, df
);
1067 eax4_fx_slot_commit(df
);
1070 eax5_fx_slot_commit(eax5_
, df
);
1073 eax_df_
= EaxDirtyFlags
{};
1075 if((df
& eax_volume_dirty_bit
) != EaxDirtyFlags
{})
1076 eax_fx_slot_set_volume();
1077 if((df
& eax_flags_dirty_bit
) != EaxDirtyFlags
{})
1078 eax_fx_slot_set_flags();
1081 if(eax_effect_
->commit(eax_version_
))
1082 eax_set_efx_slot_effect(*eax_effect_
);
1085 [[noreturn
]] void ALeffectslot::eax_fail(const char* message
)
1087 throw Exception
{message
};
1090 [[noreturn
]] void ALeffectslot::eax_fail_unknown_effect_id()
1092 eax_fail("Unknown effect ID.");
1095 [[noreturn
]] void ALeffectslot::eax_fail_unknown_property_id()
1097 eax_fail("Unknown property ID.");
1100 [[noreturn
]] void ALeffectslot::eax_fail_unknown_version()
1102 eax_fail("Unknown version.");
1105 void ALeffectslot::eax4_fx_slot_ensure_unlocked() const
1107 if(eax4_fx_slot_is_legacy())
1108 eax_fail("Locked legacy slot.");
1111 ALenum
ALeffectslot::eax_get_efx_effect_type(const GUID
& guid
)
1113 if(guid
== EAX_NULL_GUID
)
1114 return AL_EFFECT_NULL
;
1115 if(guid
== EAX_AUTOWAH_EFFECT
)
1116 return AL_EFFECT_AUTOWAH
;
1117 if(guid
== EAX_CHORUS_EFFECT
)
1118 return AL_EFFECT_CHORUS
;
1119 if(guid
== EAX_AGCCOMPRESSOR_EFFECT
)
1120 return AL_EFFECT_COMPRESSOR
;
1121 if(guid
== EAX_DISTORTION_EFFECT
)
1122 return AL_EFFECT_DISTORTION
;
1123 if(guid
== EAX_REVERB_EFFECT
)
1124 return AL_EFFECT_EAXREVERB
;
1125 if(guid
== EAX_ECHO_EFFECT
)
1126 return AL_EFFECT_ECHO
;
1127 if(guid
== EAX_EQUALIZER_EFFECT
)
1128 return AL_EFFECT_EQUALIZER
;
1129 if(guid
== EAX_FLANGER_EFFECT
)
1130 return AL_EFFECT_FLANGER
;
1131 if(guid
== EAX_FREQUENCYSHIFTER_EFFECT
)
1132 return AL_EFFECT_FREQUENCY_SHIFTER
;
1133 if(guid
== EAX_PITCHSHIFTER_EFFECT
)
1134 return AL_EFFECT_PITCH_SHIFTER
;
1135 if(guid
== EAX_RINGMODULATOR_EFFECT
)
1136 return AL_EFFECT_RING_MODULATOR
;
1137 if(guid
== EAX_VOCALMORPHER_EFFECT
)
1138 return AL_EFFECT_VOCAL_MORPHER
;
1140 eax_fail_unknown_effect_id();
1143 const GUID
& ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
1145 switch(eax_fx_slot_index_
)
1147 case 0: return EAX_REVERB_EFFECT
;
1148 case 1: return EAX_CHORUS_EFFECT
;
1149 default: return EAX_NULL_GUID
;
1153 long ALeffectslot::eax_get_eax_default_lock() const noexcept
1155 return eax4_fx_slot_is_legacy() ? EAXFXSLOT_LOCKED
: EAXFXSLOT_UNLOCKED
;
1158 void ALeffectslot::eax4_fx_slot_set_defaults(Eax4Props
& props
) noexcept
1160 props
.guidLoadEffect
= eax_get_eax_default_effect_guid();
1161 props
.lVolume
= EAXFXSLOT_DEFAULTVOLUME
;
1162 props
.lLock
= eax_get_eax_default_lock();
1163 props
.ulFlags
= EAX40FXSLOT_DEFAULTFLAGS
;
1166 void ALeffectslot::eax5_fx_slot_set_defaults(Eax5Props
& props
) noexcept
1168 props
.guidLoadEffect
= eax_get_eax_default_effect_guid();
1169 props
.lVolume
= EAXFXSLOT_DEFAULTVOLUME
;
1170 props
.lLock
= EAXFXSLOT_UNLOCKED
;
1171 props
.ulFlags
= EAX50FXSLOT_DEFAULTFLAGS
;
1172 props
.lOcclusion
= EAXFXSLOT_DEFAULTOCCLUSION
;
1173 props
.flOcclusionLFRatio
= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
;
1176 void ALeffectslot::eax_fx_slot_set_defaults()
1178 eax5_fx_slot_set_defaults(eax123_
.i
);
1179 eax4_fx_slot_set_defaults(eax4_
.i
);
1180 eax5_fx_slot_set_defaults(eax5_
.i
);
1182 eax_df_
= EaxDirtyFlags
{};
1185 void ALeffectslot::eax4_fx_slot_get(const EaxCall
& call
, const Eax4Props
& props
)
1187 switch(call
.get_property_id())
1189 case EAXFXSLOT_ALLPARAMETERS
:
1190 call
.set_value
<Exception
>(props
);
1192 case EAXFXSLOT_LOADEFFECT
:
1193 call
.set_value
<Exception
>(props
.guidLoadEffect
);
1195 case EAXFXSLOT_VOLUME
:
1196 call
.set_value
<Exception
>(props
.lVolume
);
1198 case EAXFXSLOT_LOCK
:
1199 call
.set_value
<Exception
>(props
.lLock
);
1201 case EAXFXSLOT_FLAGS
:
1202 call
.set_value
<Exception
>(props
.ulFlags
);
1205 eax_fail_unknown_property_id();
1209 void ALeffectslot::eax5_fx_slot_get(const EaxCall
& call
, const Eax5Props
& props
)
1211 switch(call
.get_property_id())
1213 case EAXFXSLOT_ALLPARAMETERS
:
1214 call
.set_value
<Exception
>(props
);
1216 case EAXFXSLOT_LOADEFFECT
:
1217 call
.set_value
<Exception
>(props
.guidLoadEffect
);
1219 case EAXFXSLOT_VOLUME
:
1220 call
.set_value
<Exception
>(props
.lVolume
);
1222 case EAXFXSLOT_LOCK
:
1223 call
.set_value
<Exception
>(props
.lLock
);
1225 case EAXFXSLOT_FLAGS
:
1226 call
.set_value
<Exception
>(props
.ulFlags
);
1228 case EAXFXSLOT_OCCLUSION
:
1229 call
.set_value
<Exception
>(props
.lOcclusion
);
1231 case EAXFXSLOT_OCCLUSIONLFRATIO
:
1232 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
1235 eax_fail_unknown_property_id();
1239 void ALeffectslot::eax_fx_slot_get(const EaxCall
& call
) const
1241 switch(call
.get_version())
1243 case 4: eax4_fx_slot_get(call
, eax4_
.i
); break;
1244 case 5: eax5_fx_slot_get(call
, eax5_
.i
); break;
1245 default: eax_fail_unknown_version();
1249 bool ALeffectslot::eax_get(const EaxCall
& call
)
1251 switch(call
.get_property_set_id())
1253 case EaxCallPropertySetId::fx_slot
:
1254 eax_fx_slot_get(call
);
1256 case EaxCallPropertySetId::fx_slot_effect
:
1257 eax_effect_
->get(call
);
1260 eax_fail_unknown_property_id();
1266 void ALeffectslot::eax_fx_slot_load_effect(int version
, ALenum altype
)
1268 if(!IsValidEffectType(altype
))
1269 altype
= AL_EFFECT_NULL
;
1270 eax_effect_
->set_defaults(version
, altype
);
1273 void ALeffectslot::eax_fx_slot_set_volume()
1275 const auto volume
= std::clamp(eax_
.lVolume
, EAXFXSLOT_MINVOLUME
, EAXFXSLOT_MAXVOLUME
);
1276 const auto gain
= level_mb_to_gain(static_cast<float>(volume
));
1277 eax_set_efx_slot_gain(gain
);
1280 void ALeffectslot::eax_fx_slot_set_environment_flag()
1282 eax_set_efx_slot_send_auto((eax_
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0u);
1285 void ALeffectslot::eax_fx_slot_set_flags()
1287 eax_fx_slot_set_environment_flag();
1290 void ALeffectslot::eax4_fx_slot_set_all(const EaxCall
& call
)
1292 eax4_fx_slot_ensure_unlocked();
1293 const auto& src
= call
.get_value
<Exception
, const EAX40FXSLOTPROPERTIES
>();
1294 Eax4AllValidator
{}(src
);
1295 auto& dst
= eax4_
.i
;
1296 eax_df_
|= eax_load_effect_dirty_bit
; // Always reset the effect.
1297 eax_df_
|= (dst
.lVolume
!= src
.lVolume
? eax_volume_dirty_bit
: EaxDirtyFlags
{});
1298 eax_df_
|= (dst
.lLock
!= src
.lLock
? eax_lock_dirty_bit
: EaxDirtyFlags
{});
1299 eax_df_
|= (dst
.ulFlags
!= src
.ulFlags
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1303 void ALeffectslot::eax5_fx_slot_set_all(const EaxCall
& call
)
1305 const auto& src
= call
.get_value
<Exception
, const EAX50FXSLOTPROPERTIES
>();
1306 Eax5AllValidator
{}(src
);
1307 auto& dst
= eax5_
.i
;
1308 eax_df_
|= eax_load_effect_dirty_bit
; // Always reset the effect.
1309 eax_df_
|= (dst
.lVolume
!= src
.lVolume
? eax_volume_dirty_bit
: EaxDirtyFlags
{});
1310 eax_df_
|= (dst
.lLock
!= src
.lLock
? eax_lock_dirty_bit
: EaxDirtyFlags
{});
1311 eax_df_
|= (dst
.ulFlags
!= src
.ulFlags
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1312 eax_df_
|= (dst
.lOcclusion
!= src
.lOcclusion
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1313 eax_df_
|= (dst
.flOcclusionLFRatio
!= src
.flOcclusionLFRatio
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1317 bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept
1319 static constexpr auto dirty_bits
=
1320 eax_occlusion_dirty_bit
|
1321 eax_occlusion_lf_ratio_dirty_bit
|
1322 eax_flags_dirty_bit
;
1324 return (eax_df_
& dirty_bits
) != EaxDirtyFlags
{};
1327 // Returns `true` if all sources should be updated, or `false` otherwise.
1328 bool ALeffectslot::eax4_fx_slot_set(const EaxCall
& call
)
1330 auto& dst
= eax4_
.i
;
1332 switch(call
.get_property_id())
1334 case EAXFXSLOT_NONE
:
1336 case EAXFXSLOT_ALLPARAMETERS
:
1337 eax4_fx_slot_set_all(call
);
1338 if((eax_df_
& eax_load_effect_dirty_bit
))
1339 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1341 case EAXFXSLOT_LOADEFFECT
:
1342 eax4_fx_slot_ensure_unlocked();
1343 eax_fx_slot_set_dirty
<Eax4GuidLoadEffectValidator
, eax_load_effect_dirty_bit
>(call
, dst
.guidLoadEffect
, eax_df_
);
1344 if((eax_df_
& eax_load_effect_dirty_bit
))
1345 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1347 case EAXFXSLOT_VOLUME
:
1348 eax_fx_slot_set
<Eax4VolumeValidator
, eax_volume_dirty_bit
>(call
, dst
.lVolume
, eax_df_
);
1350 case EAXFXSLOT_LOCK
:
1351 eax4_fx_slot_ensure_unlocked();
1352 eax_fx_slot_set
<Eax4LockValidator
, eax_lock_dirty_bit
>(call
, dst
.lLock
, eax_df_
);
1354 case EAXFXSLOT_FLAGS
:
1355 eax_fx_slot_set
<Eax4FlagsValidator
, eax_flags_dirty_bit
>(call
, dst
.ulFlags
, eax_df_
);
1358 eax_fail_unknown_property_id();
1361 return eax_fx_slot_should_update_sources();
1364 // Returns `true` if all sources should be updated, or `false` otherwise.
1365 bool ALeffectslot::eax5_fx_slot_set(const EaxCall
& call
)
1367 auto& dst
= eax5_
.i
;
1369 switch(call
.get_property_id())
1371 case EAXFXSLOT_NONE
:
1373 case EAXFXSLOT_ALLPARAMETERS
:
1374 eax5_fx_slot_set_all(call
);
1375 if((eax_df_
& eax_load_effect_dirty_bit
))
1376 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1378 case EAXFXSLOT_LOADEFFECT
:
1379 eax_fx_slot_set_dirty
<Eax4GuidLoadEffectValidator
, eax_load_effect_dirty_bit
>(call
, dst
.guidLoadEffect
, eax_df_
);
1380 if((eax_df_
& eax_load_effect_dirty_bit
))
1381 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1383 case EAXFXSLOT_VOLUME
:
1384 eax_fx_slot_set
<Eax4VolumeValidator
, eax_volume_dirty_bit
>(call
, dst
.lVolume
, eax_df_
);
1386 case EAXFXSLOT_LOCK
:
1387 eax_fx_slot_set
<Eax4LockValidator
, eax_lock_dirty_bit
>(call
, dst
.lLock
, eax_df_
);
1389 case EAXFXSLOT_FLAGS
:
1390 eax_fx_slot_set
<Eax5FlagsValidator
, eax_flags_dirty_bit
>(call
, dst
.ulFlags
, eax_df_
);
1392 case EAXFXSLOT_OCCLUSION
:
1393 eax_fx_slot_set
<Eax5OcclusionValidator
, eax_occlusion_dirty_bit
>(call
, dst
.lOcclusion
, eax_df_
);
1395 case EAXFXSLOT_OCCLUSIONLFRATIO
:
1396 eax_fx_slot_set
<Eax5OcclusionLfRatioValidator
, eax_occlusion_lf_ratio_dirty_bit
>(call
, dst
.flOcclusionLFRatio
, eax_df_
);
1399 eax_fail_unknown_property_id();
1402 return eax_fx_slot_should_update_sources();
1405 // Returns `true` if all sources should be updated, or `false` otherwise.
1406 bool ALeffectslot::eax_fx_slot_set(const EaxCall
& call
)
1408 switch (call
.get_version())
1410 case 4: return eax4_fx_slot_set(call
);
1411 case 5: return eax5_fx_slot_set(call
);
1412 default: eax_fail_unknown_version();
1416 // Returns `true` if all sources should be updated, or `false` otherwise.
1417 bool ALeffectslot::eax_set(const EaxCall
& call
)
1421 switch(call
.get_property_set_id())
1423 case EaxCallPropertySetId::fx_slot
: ret
= eax_fx_slot_set(call
); break;
1424 case EaxCallPropertySetId::fx_slot_effect
: eax_effect_
->set(call
); break;
1425 default: eax_fail_unknown_property_id();
1428 const auto version
= call
.get_version();
1429 if(eax_version_
!= version
)
1430 eax_df_
= ~EaxDirtyFlags
{};
1431 eax_version_
= version
;
1436 void ALeffectslot::eax4_fx_slot_commit(EaxDirtyFlags
& dst_df
)
1438 eax_fx_slot_commit_property
<eax_load_effect_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::guidLoadEffect
);
1439 eax_fx_slot_commit_property
<eax_volume_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::lVolume
);
1440 eax_fx_slot_commit_property
<eax_lock_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::lLock
);
1441 eax_fx_slot_commit_property
<eax_flags_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::ulFlags
);
1445 if(dst_i
.lOcclusion
!= EAXFXSLOT_DEFAULTOCCLUSION
) {
1446 dst_df
|= eax_occlusion_dirty_bit
;
1447 dst_i
.lOcclusion
= EAXFXSLOT_DEFAULTOCCLUSION
;
1450 if(dst_i
.flOcclusionLFRatio
!= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
) {
1451 dst_df
|= eax_occlusion_lf_ratio_dirty_bit
;
1452 dst_i
.flOcclusionLFRatio
= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
;
1456 void ALeffectslot::eax5_fx_slot_commit(Eax5State
& state
, EaxDirtyFlags
& dst_df
)
1458 eax_fx_slot_commit_property
<eax_load_effect_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::guidLoadEffect
);
1459 eax_fx_slot_commit_property
<eax_volume_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lVolume
);
1460 eax_fx_slot_commit_property
<eax_lock_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lLock
);
1461 eax_fx_slot_commit_property
<eax_flags_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::ulFlags
);
1462 eax_fx_slot_commit_property
<eax_occlusion_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lOcclusion
);
1463 eax_fx_slot_commit_property
<eax_occlusion_lf_ratio_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::flOcclusionLFRatio
);
1466 void ALeffectslot::eax_set_efx_slot_effect(EaxEffect
&effect
)
1468 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
1470 const auto error
= initEffect(0, effect
.al_effect_type_
, effect
.al_effect_props_
,
1473 if(error
!= AL_NO_ERROR
) {
1474 ERR(EAX_PREFIX
"%s\n", "Failed to initialize an effect.");
1478 if(mState
== SlotState::Initial
) {
1479 mPropsDirty
= false;
1480 updateProps(eax_al_context_
);
1481 auto effect_slot_ptr
= this;
1482 AddActiveEffectSlots({&effect_slot_ptr
, 1}, eax_al_context_
);
1483 mState
= SlotState::Playing
;
1492 void ALeffectslot::eax_set_efx_slot_send_auto(bool is_send_auto
)
1494 if(AuxSendAuto
== is_send_auto
)
1497 AuxSendAuto
= is_send_auto
;
1501 void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain
)
1503 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
1507 if(gain
< 0.0f
|| gain
> 1.0f
)
1508 ERR(EAX_PREFIX
"Gain out of range (%f)\n", gain
);
1510 Gain
= std::clamp(gain
, 0.0f
, 1.0f
);
1516 void ALeffectslot::EaxDeleter::operator()(ALeffectslot
* effect_slot
)
1518 assert(effect_slot
);
1519 eax_delete_al_effect_slot(*effect_slot
->eax_al_context_
, *effect_slot
);
1522 EaxAlEffectSlotUPtr
eax_create_al_effect_slot(ALCcontext
& context
)
1524 #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
1526 std::lock_guard
<std::mutex
> slotlock
{context
.mEffectSlotLock
};
1527 auto& device
= *context
.mALDevice
;
1529 if(context
.mNumEffectSlots
== device
.AuxiliaryEffectSlotMax
) {
1530 ERR(EAX_PREFIX
"%s\n", "Out of memory.");
1534 if(!EnsureEffectSlots(&context
, 1)) {
1535 ERR(EAX_PREFIX
"%s\n", "Failed to ensure.");
1539 return EaxAlEffectSlotUPtr
{AllocEffectSlot(&context
)};
1544 void eax_delete_al_effect_slot(ALCcontext
& context
, ALeffectslot
& effect_slot
)
1546 #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
1548 std::lock_guard
<std::mutex
> slotlock
{context
.mEffectSlotLock
};
1550 if(effect_slot
.ref
.load(std::memory_order_relaxed
) != 0)
1552 ERR(EAX_PREFIX
"Deleting in-use effect slot %u.\n", effect_slot
.id
);
1556 auto effect_slot_ptr
= &effect_slot
;
1557 RemoveActiveEffectSlots({&effect_slot_ptr
, 1}, &context
);
1558 FreeEffectSlot(&context
, &effect_slot
);
1562 #endif // ALSOFT_EAX