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"
35 #include <unordered_map>
45 #include "alc/context.h"
46 #include "alc/device.h"
47 #include "alc/effects/base.h"
48 #include "alc/inprogext.h"
50 #include "alnumeric.h"
54 #include "core/buffer_storage.h"
55 #include "core/device.h"
56 #include "core/fpu_ctrl.h"
57 #include "core/logging.h"
58 #include "direct_defs.h"
61 #include "flexarray.h"
62 #include "opthelpers.h"
67 #include "eax/effect.h"
68 #include "eax/fx_slot_index.h"
69 #include "eax/utils.h"
74 using SubListAllocator
= al::allocator
<std::array
<ALeffectslot
,64>>;
76 EffectStateFactory
*getFactoryByType(EffectSlotType type
)
80 case EffectSlotType::None
: return NullStateFactory_getFactory();
81 case EffectSlotType::Reverb
: return ReverbStateFactory_getFactory();
82 case EffectSlotType::Chorus
: return ChorusStateFactory_getFactory();
83 case EffectSlotType::Autowah
: return AutowahStateFactory_getFactory();
84 case EffectSlotType::Compressor
: return CompressorStateFactory_getFactory();
85 case EffectSlotType::Convolution
: return ConvolutionStateFactory_getFactory();
86 case EffectSlotType::Dedicated
: return DedicatedStateFactory_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();
100 auto LookupEffectSlot(ALCcontext
*context
, ALuint id
) noexcept
-> ALeffectslot
*
102 const size_t lidx
{(id
-1) >> 6};
103 const ALuint slidx
{(id
-1) & 0x3f};
105 if(lidx
>= context
->mEffectSlotList
.size()) UNLIKELY
107 EffectSlotSubList
&sublist
{context
->mEffectSlotList
[lidx
]};
108 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
110 return al::to_address(sublist
.EffectSlots
->begin() + slidx
);
113 inline auto LookupEffect(al::Device
*device
, ALuint id
) noexcept
-> ALeffect
*
115 const size_t lidx
{(id
-1) >> 6};
116 const ALuint slidx
{(id
-1) & 0x3f};
118 if(lidx
>= device
->EffectList
.size()) UNLIKELY
120 EffectSubList
&sublist
= device
->EffectList
[lidx
];
121 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
123 return al::to_address(sublist
.Effects
->begin() + slidx
);
126 inline auto LookupBuffer(al::Device
*device
, ALuint id
) noexcept
-> ALbuffer
*
128 const size_t lidx
{(id
-1) >> 6};
129 const ALuint slidx
{(id
-1) & 0x3f};
131 if(lidx
>= device
->BufferList
.size()) UNLIKELY
133 BufferSubList
&sublist
= device
->BufferList
[lidx
];
134 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
136 return al::to_address(sublist
.Buffers
->begin() + slidx
);
140 void AddActiveEffectSlots(const al::span
<ALeffectslot
*> auxslots
, ALCcontext
*context
)
142 if(auxslots
.empty()) return;
143 EffectSlotArray
*curarray
{context
->mActiveAuxSlots
.load(std::memory_order_acquire
)};
144 if((curarray
->size()>>1) > std::numeric_limits
<size_t>::max()-auxslots
.size())
145 throw std::runtime_error
{"Too many active effect slots"};
147 size_t newcount
{(curarray
->size()>>1) + auxslots
.size()};
148 if(newcount
> std::numeric_limits
<size_t>::max()>>1)
149 throw std::runtime_error
{"Too many active effect slots"};
151 /* Insert the new effect slots into the head of the new array, followed by
154 auto newarray
= EffectSlot::CreatePtrArray(newcount
<<1);
155 auto new_end
= std::transform(auxslots
.begin(), auxslots
.end(), newarray
->begin(),
156 std::mem_fn(&ALeffectslot::mSlot
));
157 new_end
= std::copy_n(curarray
->begin(), curarray
->size()>>1, new_end
);
159 /* Remove any duplicates (first instance of each will be kept). */
160 for(auto start
=newarray
->begin()+1;;)
162 new_end
= std::remove(start
, new_end
, *(start
-1));
163 if(start
== new_end
) break;
166 newcount
= static_cast<size_t>(std::distance(newarray
->begin(), new_end
));
168 /* Reallocate newarray if the new size ended up smaller from duplicate
171 if(newcount
< newarray
->size()>>1) UNLIKELY
173 auto oldarray
= std::move(newarray
);
174 newarray
= EffectSlot::CreatePtrArray(newcount
<<1);
175 new_end
= std::copy_n(oldarray
->begin(), newcount
, newarray
->begin());
177 std::fill(new_end
, newarray
->end(), nullptr);
179 auto oldarray
= context
->mActiveAuxSlots
.exchange(std::move(newarray
),
180 std::memory_order_acq_rel
);
181 std::ignore
= context
->mDevice
->waitForMix();
184 void RemoveActiveEffectSlots(const al::span
<ALeffectslot
*> auxslots
, ALCcontext
*context
)
186 if(auxslots
.empty()) return;
187 EffectSlotArray
*curarray
{context
->mActiveAuxSlots
.load(std::memory_order_acquire
)};
189 /* Don't shrink the allocated array size since we don't know how many (if
190 * any) of the effect slots to remove are in the array.
192 auto newarray
= EffectSlot::CreatePtrArray(curarray
->size());
194 auto new_end
= std::copy_n(curarray
->begin(), curarray
->size()>>1, newarray
->begin());
195 /* Remove elements from newarray that match any ID in slotids. */
196 for(const ALeffectslot
*auxslot
: auxslots
)
198 auto slot_match
= [auxslot
](EffectSlot
*slot
) noexcept
-> bool
199 { return (slot
== auxslot
->mSlot
); };
200 new_end
= std::remove_if(newarray
->begin(), new_end
, slot_match
);
203 /* Reallocate with the new size. */
204 auto newsize
= static_cast<size_t>(std::distance(newarray
->begin(), new_end
));
205 if(newsize
< newarray
->size()>>1) LIKELY
207 auto oldarray
= std::move(newarray
);
208 newarray
= EffectSlot::CreatePtrArray(newsize
<<1);
209 new_end
= std::copy_n(oldarray
->begin(), newsize
, newarray
->begin());
211 std::fill(new_end
, newarray
->end(), nullptr);
213 auto oldarray
= context
->mActiveAuxSlots
.exchange(std::move(newarray
),
214 std::memory_order_acq_rel
);
215 std::ignore
= context
->mDevice
->waitForMix();
219 constexpr auto EffectSlotTypeFromEnum(ALenum type
) noexcept
-> EffectSlotType
223 case AL_EFFECT_NULL
: return EffectSlotType::None
;
224 case AL_EFFECT_REVERB
: return EffectSlotType::Reverb
;
225 case AL_EFFECT_CHORUS
: return EffectSlotType::Chorus
;
226 case AL_EFFECT_DISTORTION
: return EffectSlotType::Distortion
;
227 case AL_EFFECT_ECHO
: return EffectSlotType::Echo
;
228 case AL_EFFECT_FLANGER
: return EffectSlotType::Flanger
;
229 case AL_EFFECT_FREQUENCY_SHIFTER
: return EffectSlotType::FrequencyShifter
;
230 case AL_EFFECT_VOCAL_MORPHER
: return EffectSlotType::VocalMorpher
;
231 case AL_EFFECT_PITCH_SHIFTER
: return EffectSlotType::PitchShifter
;
232 case AL_EFFECT_RING_MODULATOR
: return EffectSlotType::RingModulator
;
233 case AL_EFFECT_AUTOWAH
: return EffectSlotType::Autowah
;
234 case AL_EFFECT_COMPRESSOR
: return EffectSlotType::Compressor
;
235 case AL_EFFECT_EQUALIZER
: return EffectSlotType::Equalizer
;
236 case AL_EFFECT_EAXREVERB
: return EffectSlotType::Reverb
;
237 case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT
: return EffectSlotType::Dedicated
;
238 case AL_EFFECT_DEDICATED_DIALOGUE
: return EffectSlotType::Dedicated
;
239 case AL_EFFECT_CONVOLUTION_SOFT
: return EffectSlotType::Convolution
;
241 ERR("Unhandled effect enum: 0x%04x\n", type
);
242 return EffectSlotType::None
;
245 auto EnsureEffectSlots(ALCcontext
*context
, size_t needed
) noexcept
-> bool
247 size_t count
{std::accumulate(context
->mEffectSlotList
.cbegin(),
248 context
->mEffectSlotList
.cend(), 0_uz
,
249 [](size_t cur
, const EffectSlotSubList
&sublist
) noexcept
-> size_t
250 { return cur
+ static_cast<ALuint
>(al::popcount(sublist
.FreeMask
)); })};
252 while(needed
> count
)
254 if(context
->mEffectSlotList
.size() >= 1<<25) UNLIKELY
257 EffectSlotSubList sublist
{};
258 sublist
.FreeMask
= ~0_u64
;
259 sublist
.EffectSlots
= SubListAllocator
{}.allocate(1);
260 context
->mEffectSlotList
.emplace_back(std::move(sublist
));
261 count
+= std::tuple_size_v
<SubListAllocator::value_type
>;
269 ALeffectslot
*AllocEffectSlot(ALCcontext
*context
)
271 auto sublist
= std::find_if(context
->mEffectSlotList
.begin(), context
->mEffectSlotList
.end(),
272 [](const EffectSlotSubList
&entry
) noexcept
-> bool
273 { return entry
.FreeMask
!= 0; });
274 auto lidx
= static_cast<ALuint
>(std::distance(context
->mEffectSlotList
.begin(), sublist
));
275 auto slidx
= static_cast<ALuint
>(al::countr_zero(sublist
->FreeMask
));
278 ALeffectslot
*slot
{al::construct_at(al::to_address(sublist
->EffectSlots
->begin() + slidx
),
280 aluInitEffectPanning(slot
->mSlot
, context
);
282 /* Add 1 to avoid ID 0. */
283 slot
->id
= ((lidx
<<6) | slidx
) + 1;
285 context
->mNumEffectSlots
+= 1;
286 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
291 void FreeEffectSlot(ALCcontext
*context
, ALeffectslot
*slot
)
293 context
->mEffectSlotNames
.erase(slot
->id
);
295 const ALuint id
{slot
->id
- 1};
296 const size_t lidx
{id
>> 6};
297 const ALuint slidx
{id
& 0x3f};
299 std::destroy_at(slot
);
301 context
->mEffectSlotList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
302 context
->mNumEffectSlots
--;
306 inline void UpdateProps(ALeffectslot
*slot
, ALCcontext
*context
)
308 if(!context
->mDeferUpdates
&& slot
->mState
== SlotState::Playing
)
310 slot
->updateProps(context
);
313 slot
->mPropsDirty
= true;
319 AL_API
DECL_FUNC2(void, alGenAuxiliaryEffectSlots
, ALsizei
,n
, ALuint
*,effectslots
)
320 FORCE_ALIGN
void AL_APIENTRY
alGenAuxiliaryEffectSlotsDirect(ALCcontext
*context
, ALsizei n
,
321 ALuint
*effectslots
) noexcept
324 throw al::context_error
{AL_INVALID_VALUE
, "Generating %d effect slots", n
};
325 if(n
<= 0) UNLIKELY
return;
327 auto slotlock
= std::lock_guard
{context
->mEffectSlotLock
};
328 auto *device
= context
->mALDevice
.get();
330 const al::span eids
{effectslots
, static_cast<ALuint
>(n
)};
331 if(context
->mNumEffectSlots
> device
->AuxiliaryEffectSlotMax
332 || eids
.size() > device
->AuxiliaryEffectSlotMax
-context
->mNumEffectSlots
)
333 throw al::context_error
{AL_OUT_OF_MEMORY
, "Exceeding %u effect slot limit (%u + %d)",
334 device
->AuxiliaryEffectSlotMax
, context
->mNumEffectSlots
, n
};
336 if(!EnsureEffectSlots(context
, eids
.size()))
337 throw al::context_error
{AL_OUT_OF_MEMORY
, "Failed to allocate %d effectslot%s", n
,
338 (n
== 1) ? "" : "s"};
340 std::vector
<ALeffectslot
*> slots
;
344 /* Special handling for the easy and normal case. */
345 eids
[0] = AllocEffectSlot(context
)->id
;
349 slots
.reserve(eids
.size());
350 std::generate_n(std::back_inserter(slots
), eids
.size(),
351 [context
]{ return AllocEffectSlot(context
); });
353 std::transform(slots
.cbegin(), slots
.cend(), eids
.begin(),
354 [](ALeffectslot
*slot
) -> ALuint
{ return slot
->id
; });
357 catch(std::exception
& e
) {
358 ERR("Exception allocating effectslot %zu of %d: %s\n", slots
.size()+1, n
, e
.what());
359 auto delete_effectslot
= [context
](ALeffectslot
*slot
) -> void
360 { FreeEffectSlot(context
, slot
); };
361 std::for_each(slots
.begin(), slots
.end(), delete_effectslot
);
362 throw al::context_error
{AL_INVALID_OPERATION
, "Exception allocating %d effectslots: %s", n
,
366 catch(al::context_error
& e
) {
367 context
->setError(e
.errorCode(), "%s", e
.what());
370 AL_API
DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots
, ALsizei
,n
, const ALuint
*,effectslots
)
371 FORCE_ALIGN
void AL_APIENTRY
alDeleteAuxiliaryEffectSlotsDirect(ALCcontext
*context
, ALsizei n
,
372 const ALuint
*effectslots
) noexcept
375 throw al::context_error
{AL_INVALID_VALUE
, "Deleting %d effect slots", n
};
376 if(n
<= 0) UNLIKELY
return;
378 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
381 ALeffectslot
*slot
{LookupEffectSlot(context
, *effectslots
)};
383 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", *effectslots
};
384 if(slot
->ref
.load(std::memory_order_relaxed
) != 0)
385 throw al::context_error
{AL_INVALID_OPERATION
, "Deleting in-use effect slot %u",
388 RemoveActiveEffectSlots({&slot
, 1u}, context
);
389 FreeEffectSlot(context
, slot
);
393 const al::span eids
{effectslots
, static_cast<ALuint
>(n
)};
394 std::vector
<ALeffectslot
*> slots
;
395 slots
.reserve(eids
.size());
397 auto lookupslot
= [context
](const ALuint eid
) -> ALeffectslot
*
399 ALeffectslot
*slot
{LookupEffectSlot(context
, eid
)};
401 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", eid
};
402 if(slot
->ref
.load(std::memory_order_relaxed
) != 0)
403 throw al::context_error
{AL_INVALID_OPERATION
, "Deleting in-use effect slot %u",
407 std::transform(eids
.cbegin(), eids
.cend(), std::back_inserter(slots
), lookupslot
);
409 /* All effectslots are valid, remove and delete them */
410 RemoveActiveEffectSlots(slots
, context
);
412 auto delete_effectslot
= [context
](const ALuint eid
) -> void
414 if(ALeffectslot
*slot
{LookupEffectSlot(context
, eid
)})
415 FreeEffectSlot(context
, slot
);
417 std::for_each(eids
.begin(), eids
.end(), delete_effectslot
);
420 catch(al::context_error
& e
) {
421 context
->setError(e
.errorCode(), "%s", e
.what());
424 AL_API
DECL_FUNC1(ALboolean
, alIsAuxiliaryEffectSlot
, ALuint
,effectslot
)
425 FORCE_ALIGN ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlotDirect(ALCcontext
*context
,
426 ALuint effectslot
) noexcept
428 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
429 if(LookupEffectSlot(context
, effectslot
) != nullptr)
435 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlaySOFT(ALuint
) noexcept
437 ContextRef context
{GetContextRef()};
438 if(!context
) UNLIKELY
return;
440 context
->setError(AL_INVALID_OPERATION
, "alAuxiliaryEffectSlotPlaySOFT not supported");
443 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlayvSOFT(ALsizei
, const ALuint
*) noexcept
445 ContextRef context
{GetContextRef()};
446 if(!context
) UNLIKELY
return;
448 context
->setError(AL_INVALID_OPERATION
, "alAuxiliaryEffectSlotPlayvSOFT not supported");
451 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopSOFT(ALuint
) noexcept
453 ContextRef context
{GetContextRef()};
454 if(!context
) UNLIKELY
return;
456 context
->setError(AL_INVALID_OPERATION
, "alAuxiliaryEffectSlotStopSOFT not supported");
459 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopvSOFT(ALsizei
, const ALuint
*) noexcept
461 ContextRef context
{GetContextRef()};
462 if(!context
) UNLIKELY
return;
464 context
->setError(AL_INVALID_OPERATION
, "alAuxiliaryEffectSlotStopvSOFT not supported");
468 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSloti
, ALuint
,effectslot
, ALenum
,param
, ALint
,value
)
469 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotiDirect(ALCcontext
*context
, ALuint effectslot
,
470 ALenum param
, ALint value
) noexcept
472 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
473 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
475 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
477 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
479 ALeffectslot
*target
{};
483 case AL_EFFECTSLOT_EFFECT
:
485 auto *device
= context
->mALDevice
.get();
486 auto effectlock
= std::lock_guard
{device
->EffectLock
};
487 auto *effect
= value
? LookupEffect(device
, static_cast<ALuint
>(value
)) : nullptr;
489 err
= slot
->initEffect(effect
->id
, effect
->type
, effect
->Props
, context
);
493 throw al::context_error
{AL_INVALID_VALUE
, "Invalid effect ID %u", value
};
494 err
= slot
->initEffect(0, AL_EFFECT_NULL
, EffectProps
{}, context
);
497 if(err
!= AL_NO_ERROR
)
498 throw al::context_error
{err
, "Effect initialization failed"};
500 if(slot
->mState
== SlotState::Initial
) UNLIKELY
502 slot
->mPropsDirty
= false;
503 slot
->updateProps(context
);
505 AddActiveEffectSlots({&slot
, 1}, context
);
506 slot
->mState
= SlotState::Playing
;
509 UpdateProps(slot
, context
);
512 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
513 if(!(value
== AL_TRUE
|| value
== AL_FALSE
))
514 throw al::context_error
{AL_INVALID_VALUE
,
515 "Effect slot auxiliary send auto out of range"};
516 if(!(slot
->AuxSendAuto
== !!value
)) LIKELY
518 slot
->AuxSendAuto
= !!value
;
519 UpdateProps(slot
, context
);
523 case AL_EFFECTSLOT_TARGET_SOFT
:
524 target
= LookupEffectSlot(context
, static_cast<ALuint
>(value
));
526 throw al::context_error
{AL_INVALID_VALUE
, "Invalid effect slot target ID"};
527 if(slot
->Target
== target
) UNLIKELY
531 ALeffectslot
*checker
{target
};
532 while(checker
&& checker
!= slot
)
533 checker
= checker
->Target
;
535 throw al::context_error
{AL_INVALID_OPERATION
,
536 "Setting target of effect slot ID %u to %u creates circular chain", slot
->id
,
540 if(ALeffectslot
*oldtarget
{slot
->Target
})
542 /* We must force an update if there was an existing effect slot
543 * target, in case it's about to be deleted.
545 if(target
) IncrementRef(target
->ref
);
546 DecrementRef(oldtarget
->ref
);
547 slot
->Target
= target
;
548 slot
->updateProps(context
);
552 if(target
) IncrementRef(target
->ref
);
553 slot
->Target
= target
;
554 UpdateProps(slot
, context
);
558 if(ALbuffer
*buffer
{slot
->Buffer
})
560 if(buffer
->id
== static_cast<ALuint
>(value
))
566 if(slot
->mState
== SlotState::Playing
)
568 EffectStateFactory
*factory
{getFactoryByType(slot
->Effect
.Type
)};
570 al::intrusive_ptr
<EffectState
> state
{factory
->create()};
572 auto *device
= context
->mALDevice
.get();
573 auto bufferlock
= std::unique_lock
{device
->BufferLock
};
577 buffer
= LookupBuffer(device
, static_cast<ALuint
>(value
));
579 throw al::context_error
{AL_INVALID_VALUE
, "Invalid buffer ID %u", value
};
580 if(buffer
->mCallback
)
581 throw al::context_error
{AL_INVALID_OPERATION
,
582 "Callback buffer not valid for effects"};
584 IncrementRef(buffer
->ref
);
587 /* Stop the effect slot from processing while we switch buffers. */
588 RemoveActiveEffectSlots({&slot
, 1}, context
);
590 if(ALbuffer
*oldbuffer
{slot
->Buffer
})
591 DecrementRef(oldbuffer
->ref
);
592 slot
->Buffer
= buffer
;
595 state
->mOutTarget
= device
->Dry
.Buffer
;
598 state
->deviceUpdate(device
, buffer
);
600 slot
->Effect
.State
= std::move(state
);
602 slot
->mPropsDirty
= false;
603 slot
->updateProps(context
);
604 AddActiveEffectSlots({&slot
, 1}, context
);
608 auto *device
= context
->mALDevice
.get();
609 auto bufferlock
= std::unique_lock
{device
->BufferLock
};
613 buffer
= LookupBuffer(device
, static_cast<ALuint
>(value
));
615 throw al::context_error
{AL_INVALID_VALUE
, "Invalid buffer ID %u", value
};
616 if(buffer
->mCallback
)
617 throw al::context_error
{AL_INVALID_OPERATION
,
618 "Callback buffer not valid for effects"};
620 IncrementRef(buffer
->ref
);
623 if(ALbuffer
*oldbuffer
{slot
->Buffer
})
624 DecrementRef(oldbuffer
->ref
);
625 slot
->Buffer
= buffer
;
629 auto *state
= slot
->Effect
.State
.get();
630 state
->deviceUpdate(device
, buffer
);
631 slot
->mPropsDirty
= true;
635 case AL_EFFECTSLOT_STATE_SOFT
:
636 throw al::context_error
{AL_INVALID_OPERATION
, "AL_EFFECTSLOT_STATE_SOFT is read-only"};
639 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x", param
};
641 catch(al::context_error
& e
) {
642 context
->setError(e
.errorCode(), "%s", e
.what());
645 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSlotiv
, ALuint
,effectslot
, ALenum
,param
, const ALint
*,values
)
646 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotivDirect(ALCcontext
*context
, ALuint effectslot
,
647 ALenum param
, const ALint
*values
) noexcept
651 case AL_EFFECTSLOT_EFFECT
:
652 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
653 case AL_EFFECTSLOT_TARGET_SOFT
:
654 case AL_EFFECTSLOT_STATE_SOFT
:
656 alAuxiliaryEffectSlotiDirect(context
, effectslot
, param
, *values
);
660 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
661 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
663 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
668 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
671 catch(al::context_error
& e
) {
672 context
->setError(e
.errorCode(), "%s", e
.what());
675 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSlotf
, ALuint
,effectslot
, ALenum
,param
, ALfloat
,value
)
676 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotfDirect(ALCcontext
*context
, ALuint effectslot
,
677 ALenum param
, ALfloat value
) noexcept
679 std::lock_guard
<std::mutex
> proplock
{context
->mPropLock
};
680 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
682 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
684 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
688 case AL_EFFECTSLOT_GAIN
:
689 if(!(value
>= 0.0f
&& value
<= 1.0f
))
690 throw al::context_error
{AL_INVALID_VALUE
, "Effect slot gain out of range"};
691 if(!(slot
->Gain
== value
)) LIKELY
694 UpdateProps(slot
, context
);
699 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x", param
};
701 catch(al::context_error
& e
) {
702 context
->setError(e
.errorCode(), "%s", e
.what());
705 AL_API
DECL_FUNC3(void, alAuxiliaryEffectSlotfv
, ALuint
,effectslot
, ALenum
,param
, const ALfloat
*,values
)
706 FORCE_ALIGN
void AL_APIENTRY
alAuxiliaryEffectSlotfvDirect(ALCcontext
*context
, ALuint effectslot
,
707 ALenum param
, const ALfloat
*values
) noexcept
711 case AL_EFFECTSLOT_GAIN
:
712 alAuxiliaryEffectSlotfDirect(context
, effectslot
, param
, *values
);
716 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
717 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
719 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
724 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
727 catch(al::context_error
& e
) {
728 context
->setError(e
.errorCode(), "%s", e
.what());
732 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSloti
, ALuint
,effectslot
, ALenum
,param
, ALint
*,value
)
733 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotiDirect(ALCcontext
*context
,
734 ALuint effectslot
, ALenum param
, ALint
*value
) noexcept
736 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
737 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
739 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
743 case AL_EFFECTSLOT_EFFECT
:
744 *value
= static_cast<ALint
>(slot
->EffectId
);
747 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
748 *value
= slot
->AuxSendAuto
? AL_TRUE
: AL_FALSE
;
751 case AL_EFFECTSLOT_TARGET_SOFT
:
752 if(auto *target
= slot
->Target
)
753 *value
= static_cast<ALint
>(target
->id
);
758 case AL_EFFECTSLOT_STATE_SOFT
:
759 *value
= static_cast<int>(slot
->mState
);
763 if(auto *buffer
= slot
->Buffer
)
764 *value
= static_cast<ALint
>(buffer
->id
);
770 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x", param
};
772 catch(al::context_error
& e
) {
773 context
->setError(e
.errorCode(), "%s", e
.what());
776 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv
, ALuint
,effectslot
, ALenum
,param
, ALint
*,values
)
777 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotivDirect(ALCcontext
*context
,
778 ALuint effectslot
, ALenum param
, ALint
*values
) noexcept
782 case AL_EFFECTSLOT_EFFECT
:
783 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
784 case AL_EFFECTSLOT_TARGET_SOFT
:
785 case AL_EFFECTSLOT_STATE_SOFT
:
787 alGetAuxiliaryEffectSlotiDirect(context
, effectslot
, param
, values
);
791 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
792 ALeffectslot
*slot
= LookupEffectSlot(context
, effectslot
);
794 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
799 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
802 catch(al::context_error
& e
) {
803 context
->setError(e
.errorCode(), "%s", e
.what());
806 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotf
, ALuint
,effectslot
, ALenum
,param
, ALfloat
*,value
)
807 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotfDirect(ALCcontext
*context
,
808 ALuint effectslot
, ALenum param
, ALfloat
*value
) noexcept
810 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
811 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
813 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
817 case AL_EFFECTSLOT_GAIN
:
822 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x", param
};
824 catch(al::context_error
& e
) {
825 context
->setError(e
.errorCode(), "%s", e
.what());
828 AL_API
DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv
, ALuint
,effectslot
, ALenum
,param
, ALfloat
*,values
)
829 FORCE_ALIGN
void AL_APIENTRY
alGetAuxiliaryEffectSlotfvDirect(ALCcontext
*context
,
830 ALuint effectslot
, ALenum param
, ALfloat
*values
) noexcept
834 case AL_EFFECTSLOT_GAIN
:
835 alGetAuxiliaryEffectSlotfDirect(context
, effectslot
, param
, values
);
839 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
840 ALeffectslot
*slot
{LookupEffectSlot(context
, effectslot
)};
842 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslot
};
847 throw al::context_error
{AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
850 catch(al::context_error
& e
) {
851 context
->setError(e
.errorCode(), "%s", e
.what());
855 ALeffectslot::ALeffectslot(ALCcontext
*context
)
857 EffectStateFactory
*factory
{getFactoryByType(EffectSlotType::None
)};
858 if(!factory
) throw std::runtime_error
{"Failed to get null effect factory"};
860 al::intrusive_ptr
<EffectState
> state
{factory
->create()};
861 Effect
.State
= state
;
863 mSlot
= context
->getEffectSlot();
865 mSlot
->mEffectState
= std::move(state
);
868 ALeffectslot::~ALeffectslot()
871 DecrementRef(Target
->ref
);
874 DecrementRef(Buffer
->ref
);
877 if(auto *slot
= mSlot
->Update
.exchange(nullptr, std::memory_order_relaxed
))
878 slot
->State
= nullptr;
880 mSlot
->mEffectState
= nullptr;
881 mSlot
->InUse
= false;
884 ALenum
ALeffectslot::initEffect(ALuint effectId
, ALenum effectType
, const EffectProps
&effectProps
,
887 EffectSlotType newtype
{EffectSlotTypeFromEnum(effectType
)};
888 if(newtype
!= Effect
.Type
)
890 EffectStateFactory
*factory
{getFactoryByType(newtype
)};
893 ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype
));
894 return AL_INVALID_ENUM
;
896 al::intrusive_ptr
<EffectState
> state
{factory
->create()};
898 auto *device
= context
->mALDevice
.get();
899 state
->mOutTarget
= device
->Dry
.Buffer
;
902 state
->deviceUpdate(device
, Buffer
);
905 Effect
.Type
= newtype
;
906 Effect
.Props
= effectProps
;
908 Effect
.State
= std::move(state
);
910 else if(newtype
!= EffectSlotType::None
)
911 Effect
.Props
= effectProps
;
914 /* Remove state references from old effect slot property updates. */
915 EffectSlotProps
*props
{context
->mFreeEffectSlotProps
.load()};
918 props
->State
= nullptr;
919 props
= props
->next
.load(std::memory_order_relaxed
);
925 void ALeffectslot::updateProps(ALCcontext
*context
) const
927 /* Get an unused property container, or allocate a new one as needed. */
928 EffectSlotProps
*props
{context
->mFreeEffectSlotProps
.load(std::memory_order_acquire
)};
931 context
->allocEffectSlotProps();
932 props
= context
->mFreeEffectSlotProps
.load(std::memory_order_acquire
);
934 EffectSlotProps
*next
;
936 next
= props
->next
.load(std::memory_order_relaxed
);
937 } while(!context
->mFreeEffectSlotProps
.compare_exchange_weak(props
, next
,
938 std::memory_order_acq_rel
, std::memory_order_acquire
));
940 /* Copy in current property values. */
942 props
->AuxSendAuto
= AuxSendAuto
;
943 props
->Target
= Target
? Target
->mSlot
: nullptr;
945 props
->Type
= Effect
.Type
;
946 props
->Props
= Effect
.Props
;
947 props
->State
= Effect
.State
;
949 /* Set the new container for updating internal parameters. */
950 props
= mSlot
->Update
.exchange(props
, std::memory_order_acq_rel
);
953 /* If there was an unused update container, put it back in the
956 props
->State
= nullptr;
957 AtomicReplaceHead(context
->mFreeEffectSlotProps
, props
);
961 void ALeffectslot::SetName(ALCcontext
* context
, ALuint id
, std::string_view name
)
963 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
965 auto slot
= LookupEffectSlot(context
, id
);
967 throw al::context_error
{AL_INVALID_NAME
, "Invalid effect slot ID %u", id
};
969 context
->mEffectSlotNames
.insert_or_assign(id
, name
);
972 void UpdateAllEffectSlotProps(ALCcontext
*context
)
974 std::lock_guard
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
975 for(auto &sublist
: context
->mEffectSlotList
)
977 uint64_t usemask
{~sublist
.FreeMask
};
980 const auto idx
= static_cast<uint
>(al::countr_zero(usemask
));
981 usemask
&= ~(1_u64
<< idx
);
982 auto &slot
= (*sublist
.EffectSlots
)[idx
];
984 if(std::exchange(slot
.mPropsDirty
, false))
985 slot
.updateProps(context
);
990 EffectSlotSubList::~EffectSlotSubList()
995 uint64_t usemask
{~FreeMask
};
998 const int idx
{al::countr_zero(usemask
)};
999 std::destroy_at(al::to_address(EffectSlots
->begin() + idx
));
1000 usemask
&= ~(1_u64
<< idx
);
1002 FreeMask
= ~usemask
;
1003 SubListAllocator
{}.deallocate(EffectSlots
, 1);
1004 EffectSlots
= nullptr;
1008 void ALeffectslot::eax_initialize(ALCcontext
& al_context
, EaxFxSlotIndexValue index
)
1010 if(index
>= EAX_MAX_FXSLOTS
)
1011 eax_fail("Index out of range.");
1013 eax_al_context_
= &al_context
;
1014 eax_fx_slot_index_
= index
;
1015 eax_fx_slot_set_defaults();
1017 eax_effect_
= std::make_unique
<EaxEffect
>();
1018 if(index
== 0) eax_effect_
->init
<EaxReverbCommitter
>();
1019 else if(index
== 1) eax_effect_
->init
<EaxChorusCommitter
>();
1020 else eax_effect_
->init
<EaxNullCommitter
>();
1023 void ALeffectslot::eax_commit()
1025 if(eax_df_
!= EaxDirtyFlags
{})
1027 auto df
= EaxDirtyFlags
{};
1028 switch(eax_version_
)
1033 eax5_fx_slot_commit(eax123_
, df
);
1036 eax4_fx_slot_commit(df
);
1039 eax5_fx_slot_commit(eax5_
, df
);
1042 eax_df_
= EaxDirtyFlags
{};
1044 if((df
& eax_volume_dirty_bit
) != EaxDirtyFlags
{})
1045 eax_fx_slot_set_volume();
1046 if((df
& eax_flags_dirty_bit
) != EaxDirtyFlags
{})
1047 eax_fx_slot_set_flags();
1050 if(eax_effect_
->commit(eax_version_
))
1051 eax_set_efx_slot_effect(*eax_effect_
);
1054 [[noreturn
]] void ALeffectslot::eax_fail(const char* message
)
1056 throw Exception
{message
};
1059 [[noreturn
]] void ALeffectslot::eax_fail_unknown_effect_id()
1061 eax_fail("Unknown effect ID.");
1064 [[noreturn
]] void ALeffectslot::eax_fail_unknown_property_id()
1066 eax_fail("Unknown property ID.");
1069 [[noreturn
]] void ALeffectslot::eax_fail_unknown_version()
1071 eax_fail("Unknown version.");
1074 void ALeffectslot::eax4_fx_slot_ensure_unlocked() const
1076 if(eax4_fx_slot_is_legacy())
1077 eax_fail("Locked legacy slot.");
1080 ALenum
ALeffectslot::eax_get_efx_effect_type(const GUID
& guid
)
1082 if(guid
== EAX_NULL_GUID
)
1083 return AL_EFFECT_NULL
;
1084 if(guid
== EAX_AUTOWAH_EFFECT
)
1085 return AL_EFFECT_AUTOWAH
;
1086 if(guid
== EAX_CHORUS_EFFECT
)
1087 return AL_EFFECT_CHORUS
;
1088 if(guid
== EAX_AGCCOMPRESSOR_EFFECT
)
1089 return AL_EFFECT_COMPRESSOR
;
1090 if(guid
== EAX_DISTORTION_EFFECT
)
1091 return AL_EFFECT_DISTORTION
;
1092 if(guid
== EAX_REVERB_EFFECT
)
1093 return AL_EFFECT_EAXREVERB
;
1094 if(guid
== EAX_ECHO_EFFECT
)
1095 return AL_EFFECT_ECHO
;
1096 if(guid
== EAX_EQUALIZER_EFFECT
)
1097 return AL_EFFECT_EQUALIZER
;
1098 if(guid
== EAX_FLANGER_EFFECT
)
1099 return AL_EFFECT_FLANGER
;
1100 if(guid
== EAX_FREQUENCYSHIFTER_EFFECT
)
1101 return AL_EFFECT_FREQUENCY_SHIFTER
;
1102 if(guid
== EAX_PITCHSHIFTER_EFFECT
)
1103 return AL_EFFECT_PITCH_SHIFTER
;
1104 if(guid
== EAX_RINGMODULATOR_EFFECT
)
1105 return AL_EFFECT_RING_MODULATOR
;
1106 if(guid
== EAX_VOCALMORPHER_EFFECT
)
1107 return AL_EFFECT_VOCAL_MORPHER
;
1109 eax_fail_unknown_effect_id();
1112 const GUID
& ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
1114 switch(eax_fx_slot_index_
)
1116 case 0: return EAX_REVERB_EFFECT
;
1117 case 1: return EAX_CHORUS_EFFECT
;
1118 default: return EAX_NULL_GUID
;
1122 long ALeffectslot::eax_get_eax_default_lock() const noexcept
1124 return eax4_fx_slot_is_legacy() ? EAXFXSLOT_LOCKED
: EAXFXSLOT_UNLOCKED
;
1127 void ALeffectslot::eax4_fx_slot_set_defaults(Eax4Props
& props
) noexcept
1129 props
.guidLoadEffect
= eax_get_eax_default_effect_guid();
1130 props
.lVolume
= EAXFXSLOT_DEFAULTVOLUME
;
1131 props
.lLock
= eax_get_eax_default_lock();
1132 props
.ulFlags
= EAX40FXSLOT_DEFAULTFLAGS
;
1135 void ALeffectslot::eax5_fx_slot_set_defaults(Eax5Props
& props
) noexcept
1137 props
.guidLoadEffect
= eax_get_eax_default_effect_guid();
1138 props
.lVolume
= EAXFXSLOT_DEFAULTVOLUME
;
1139 props
.lLock
= EAXFXSLOT_UNLOCKED
;
1140 props
.ulFlags
= EAX50FXSLOT_DEFAULTFLAGS
;
1141 props
.lOcclusion
= EAXFXSLOT_DEFAULTOCCLUSION
;
1142 props
.flOcclusionLFRatio
= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
;
1145 void ALeffectslot::eax_fx_slot_set_defaults()
1147 eax5_fx_slot_set_defaults(eax123_
.i
);
1148 eax4_fx_slot_set_defaults(eax4_
.i
);
1149 eax5_fx_slot_set_defaults(eax5_
.i
);
1151 eax_df_
= EaxDirtyFlags
{};
1154 void ALeffectslot::eax4_fx_slot_get(const EaxCall
& call
, const Eax4Props
& props
)
1156 switch(call
.get_property_id())
1158 case EAXFXSLOT_ALLPARAMETERS
:
1159 call
.set_value
<Exception
>(props
);
1161 case EAXFXSLOT_LOADEFFECT
:
1162 call
.set_value
<Exception
>(props
.guidLoadEffect
);
1164 case EAXFXSLOT_VOLUME
:
1165 call
.set_value
<Exception
>(props
.lVolume
);
1167 case EAXFXSLOT_LOCK
:
1168 call
.set_value
<Exception
>(props
.lLock
);
1170 case EAXFXSLOT_FLAGS
:
1171 call
.set_value
<Exception
>(props
.ulFlags
);
1174 eax_fail_unknown_property_id();
1178 void ALeffectslot::eax5_fx_slot_get(const EaxCall
& call
, const Eax5Props
& props
)
1180 switch(call
.get_property_id())
1182 case EAXFXSLOT_ALLPARAMETERS
:
1183 call
.set_value
<Exception
>(props
);
1185 case EAXFXSLOT_LOADEFFECT
:
1186 call
.set_value
<Exception
>(props
.guidLoadEffect
);
1188 case EAXFXSLOT_VOLUME
:
1189 call
.set_value
<Exception
>(props
.lVolume
);
1191 case EAXFXSLOT_LOCK
:
1192 call
.set_value
<Exception
>(props
.lLock
);
1194 case EAXFXSLOT_FLAGS
:
1195 call
.set_value
<Exception
>(props
.ulFlags
);
1197 case EAXFXSLOT_OCCLUSION
:
1198 call
.set_value
<Exception
>(props
.lOcclusion
);
1200 case EAXFXSLOT_OCCLUSIONLFRATIO
:
1201 call
.set_value
<Exception
>(props
.flOcclusionLFRatio
);
1204 eax_fail_unknown_property_id();
1208 void ALeffectslot::eax_fx_slot_get(const EaxCall
& call
) const
1210 switch(call
.get_version())
1212 case 4: eax4_fx_slot_get(call
, eax4_
.i
); break;
1213 case 5: eax5_fx_slot_get(call
, eax5_
.i
); break;
1214 default: eax_fail_unknown_version();
1218 bool ALeffectslot::eax_get(const EaxCall
& call
)
1220 switch(call
.get_property_set_id())
1222 case EaxCallPropertySetId::fx_slot
:
1223 eax_fx_slot_get(call
);
1225 case EaxCallPropertySetId::fx_slot_effect
:
1226 eax_effect_
->get(call
);
1229 eax_fail_unknown_property_id();
1235 void ALeffectslot::eax_fx_slot_load_effect(int version
, ALenum altype
)
1237 if(!IsValidEffectType(altype
))
1238 altype
= AL_EFFECT_NULL
;
1239 eax_effect_
->set_defaults(version
, altype
);
1242 void ALeffectslot::eax_fx_slot_set_volume()
1244 const auto volume
= std::clamp(eax_
.lVolume
, EAXFXSLOT_MINVOLUME
, EAXFXSLOT_MAXVOLUME
);
1245 const auto gain
= level_mb_to_gain(static_cast<float>(volume
));
1246 eax_set_efx_slot_gain(gain
);
1249 void ALeffectslot::eax_fx_slot_set_environment_flag()
1251 eax_set_efx_slot_send_auto((eax_
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0u);
1254 void ALeffectslot::eax_fx_slot_set_flags()
1256 eax_fx_slot_set_environment_flag();
1259 void ALeffectslot::eax4_fx_slot_set_all(const EaxCall
& call
)
1261 eax4_fx_slot_ensure_unlocked();
1262 const auto& src
= call
.get_value
<Exception
, const EAX40FXSLOTPROPERTIES
>();
1263 Eax4AllValidator
{}(src
);
1264 auto& dst
= eax4_
.i
;
1265 eax_df_
|= eax_load_effect_dirty_bit
; // Always reset the effect.
1266 eax_df_
|= (dst
.lVolume
!= src
.lVolume
? eax_volume_dirty_bit
: EaxDirtyFlags
{});
1267 eax_df_
|= (dst
.lLock
!= src
.lLock
? eax_lock_dirty_bit
: EaxDirtyFlags
{});
1268 eax_df_
|= (dst
.ulFlags
!= src
.ulFlags
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1272 void ALeffectslot::eax5_fx_slot_set_all(const EaxCall
& call
)
1274 const auto& src
= call
.get_value
<Exception
, const EAX50FXSLOTPROPERTIES
>();
1275 Eax5AllValidator
{}(src
);
1276 auto& dst
= eax5_
.i
;
1277 eax_df_
|= eax_load_effect_dirty_bit
; // Always reset the effect.
1278 eax_df_
|= (dst
.lVolume
!= src
.lVolume
? eax_volume_dirty_bit
: EaxDirtyFlags
{});
1279 eax_df_
|= (dst
.lLock
!= src
.lLock
? eax_lock_dirty_bit
: EaxDirtyFlags
{});
1280 eax_df_
|= (dst
.ulFlags
!= src
.ulFlags
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1281 eax_df_
|= (dst
.lOcclusion
!= src
.lOcclusion
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1282 eax_df_
|= (dst
.flOcclusionLFRatio
!= src
.flOcclusionLFRatio
? eax_flags_dirty_bit
: EaxDirtyFlags
{});
1286 bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept
1288 static constexpr auto dirty_bits
=
1289 eax_occlusion_dirty_bit
|
1290 eax_occlusion_lf_ratio_dirty_bit
|
1291 eax_flags_dirty_bit
;
1293 return (eax_df_
& dirty_bits
) != EaxDirtyFlags
{};
1296 // Returns `true` if all sources should be updated, or `false` otherwise.
1297 bool ALeffectslot::eax4_fx_slot_set(const EaxCall
& call
)
1299 auto& dst
= eax4_
.i
;
1301 switch(call
.get_property_id())
1303 case EAXFXSLOT_NONE
:
1305 case EAXFXSLOT_ALLPARAMETERS
:
1306 eax4_fx_slot_set_all(call
);
1307 if((eax_df_
& eax_load_effect_dirty_bit
))
1308 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1310 case EAXFXSLOT_LOADEFFECT
:
1311 eax4_fx_slot_ensure_unlocked();
1312 eax_fx_slot_set_dirty
<Eax4GuidLoadEffectValidator
, eax_load_effect_dirty_bit
>(call
, dst
.guidLoadEffect
, eax_df_
);
1313 if((eax_df_
& eax_load_effect_dirty_bit
))
1314 eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1316 case EAXFXSLOT_VOLUME
:
1317 eax_fx_slot_set
<Eax4VolumeValidator
, eax_volume_dirty_bit
>(call
, dst
.lVolume
, eax_df_
);
1319 case EAXFXSLOT_LOCK
:
1320 eax4_fx_slot_ensure_unlocked();
1321 eax_fx_slot_set
<Eax4LockValidator
, eax_lock_dirty_bit
>(call
, dst
.lLock
, eax_df_
);
1323 case EAXFXSLOT_FLAGS
:
1324 eax_fx_slot_set
<Eax4FlagsValidator
, eax_flags_dirty_bit
>(call
, dst
.ulFlags
, eax_df_
);
1327 eax_fail_unknown_property_id();
1330 return eax_fx_slot_should_update_sources();
1333 // Returns `true` if all sources should be updated, or `false` otherwise.
1334 bool ALeffectslot::eax5_fx_slot_set(const EaxCall
& call
)
1336 auto& dst
= eax5_
.i
;
1338 switch(call
.get_property_id())
1340 case EAXFXSLOT_NONE
:
1342 case EAXFXSLOT_ALLPARAMETERS
:
1343 eax5_fx_slot_set_all(call
);
1344 if((eax_df_
& eax_load_effect_dirty_bit
))
1345 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1347 case EAXFXSLOT_LOADEFFECT
:
1348 eax_fx_slot_set_dirty
<Eax4GuidLoadEffectValidator
, eax_load_effect_dirty_bit
>(call
, dst
.guidLoadEffect
, eax_df_
);
1349 if((eax_df_
& eax_load_effect_dirty_bit
))
1350 eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst
.guidLoadEffect
));
1352 case EAXFXSLOT_VOLUME
:
1353 eax_fx_slot_set
<Eax4VolumeValidator
, eax_volume_dirty_bit
>(call
, dst
.lVolume
, eax_df_
);
1355 case EAXFXSLOT_LOCK
:
1356 eax_fx_slot_set
<Eax4LockValidator
, eax_lock_dirty_bit
>(call
, dst
.lLock
, eax_df_
);
1358 case EAXFXSLOT_FLAGS
:
1359 eax_fx_slot_set
<Eax5FlagsValidator
, eax_flags_dirty_bit
>(call
, dst
.ulFlags
, eax_df_
);
1361 case EAXFXSLOT_OCCLUSION
:
1362 eax_fx_slot_set
<Eax5OcclusionValidator
, eax_occlusion_dirty_bit
>(call
, dst
.lOcclusion
, eax_df_
);
1364 case EAXFXSLOT_OCCLUSIONLFRATIO
:
1365 eax_fx_slot_set
<Eax5OcclusionLfRatioValidator
, eax_occlusion_lf_ratio_dirty_bit
>(call
, dst
.flOcclusionLFRatio
, eax_df_
);
1368 eax_fail_unknown_property_id();
1371 return eax_fx_slot_should_update_sources();
1374 // Returns `true` if all sources should be updated, or `false` otherwise.
1375 bool ALeffectslot::eax_fx_slot_set(const EaxCall
& call
)
1377 switch (call
.get_version())
1379 case 4: return eax4_fx_slot_set(call
);
1380 case 5: return eax5_fx_slot_set(call
);
1381 default: eax_fail_unknown_version();
1385 // Returns `true` if all sources should be updated, or `false` otherwise.
1386 bool ALeffectslot::eax_set(const EaxCall
& call
)
1390 switch(call
.get_property_set_id())
1392 case EaxCallPropertySetId::fx_slot
: ret
= eax_fx_slot_set(call
); break;
1393 case EaxCallPropertySetId::fx_slot_effect
: eax_effect_
->set(call
); break;
1394 default: eax_fail_unknown_property_id();
1397 const auto version
= call
.get_version();
1398 if(eax_version_
!= version
)
1399 eax_df_
= ~EaxDirtyFlags
{};
1400 eax_version_
= version
;
1405 void ALeffectslot::eax4_fx_slot_commit(EaxDirtyFlags
& dst_df
)
1407 eax_fx_slot_commit_property
<eax_load_effect_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::guidLoadEffect
);
1408 eax_fx_slot_commit_property
<eax_volume_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::lVolume
);
1409 eax_fx_slot_commit_property
<eax_lock_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::lLock
);
1410 eax_fx_slot_commit_property
<eax_flags_dirty_bit
>(eax4_
, dst_df
, &EAX40FXSLOTPROPERTIES::ulFlags
);
1414 if(dst_i
.lOcclusion
!= EAXFXSLOT_DEFAULTOCCLUSION
) {
1415 dst_df
|= eax_occlusion_dirty_bit
;
1416 dst_i
.lOcclusion
= EAXFXSLOT_DEFAULTOCCLUSION
;
1419 if(dst_i
.flOcclusionLFRatio
!= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
) {
1420 dst_df
|= eax_occlusion_lf_ratio_dirty_bit
;
1421 dst_i
.flOcclusionLFRatio
= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
;
1425 void ALeffectslot::eax5_fx_slot_commit(Eax5State
& state
, EaxDirtyFlags
& dst_df
)
1427 eax_fx_slot_commit_property
<eax_load_effect_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::guidLoadEffect
);
1428 eax_fx_slot_commit_property
<eax_volume_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lVolume
);
1429 eax_fx_slot_commit_property
<eax_lock_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lLock
);
1430 eax_fx_slot_commit_property
<eax_flags_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::ulFlags
);
1431 eax_fx_slot_commit_property
<eax_occlusion_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::lOcclusion
);
1432 eax_fx_slot_commit_property
<eax_occlusion_lf_ratio_dirty_bit
>(state
, dst_df
, &EAX50FXSLOTPROPERTIES::flOcclusionLFRatio
);
1435 void ALeffectslot::eax_set_efx_slot_effect(EaxEffect
&effect
)
1437 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
1439 const auto error
= initEffect(0, effect
.al_effect_type_
, effect
.al_effect_props_
,
1442 if(error
!= AL_NO_ERROR
) {
1443 ERR(EAX_PREFIX
"%s\n", "Failed to initialize an effect.");
1447 if(mState
== SlotState::Initial
) {
1448 mPropsDirty
= false;
1449 updateProps(eax_al_context_
);
1450 auto effect_slot_ptr
= this;
1451 AddActiveEffectSlots({&effect_slot_ptr
, 1}, eax_al_context_
);
1452 mState
= SlotState::Playing
;
1461 void ALeffectslot::eax_set_efx_slot_send_auto(bool is_send_auto
)
1463 if(AuxSendAuto
== is_send_auto
)
1466 AuxSendAuto
= is_send_auto
;
1470 void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain
)
1472 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
1476 if(gain
< 0.0f
|| gain
> 1.0f
)
1477 ERR(EAX_PREFIX
"Gain out of range (%f)\n", gain
);
1479 Gain
= std::clamp(gain
, 0.0f
, 1.0f
);
1485 void ALeffectslot::EaxDeleter::operator()(ALeffectslot
* effect_slot
)
1487 eax_delete_al_effect_slot(*effect_slot
->eax_al_context_
, *effect_slot
);
1490 EaxAlEffectSlotUPtr
eax_create_al_effect_slot(ALCcontext
& context
)
1492 #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
1494 std::lock_guard
<std::mutex
> slotlock
{context
.mEffectSlotLock
};
1495 auto& device
= *context
.mALDevice
;
1497 if(context
.mNumEffectSlots
== device
.AuxiliaryEffectSlotMax
) {
1498 ERR(EAX_PREFIX
"%s\n", "Out of memory.");
1502 if(!EnsureEffectSlots(&context
, 1)) {
1503 ERR(EAX_PREFIX
"%s\n", "Failed to ensure.");
1507 return EaxAlEffectSlotUPtr
{AllocEffectSlot(&context
)};
1512 void eax_delete_al_effect_slot(ALCcontext
& context
, ALeffectslot
& effect_slot
)
1514 #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
1516 std::lock_guard
<std::mutex
> slotlock
{context
.mEffectSlotLock
};
1518 if(effect_slot
.ref
.load(std::memory_order_relaxed
) != 0)
1520 ERR(EAX_PREFIX
"Deleting in-use effect slot %u.\n", effect_slot
.id
);
1524 auto effect_slot_ptr
= &effect_slot
;
1525 RemoveActiveEffectSlots({&effect_slot_ptr
, 1}, &context
);
1526 FreeEffectSlot(&context
, &effect_slot
);
1530 #endif // ALSOFT_EAX