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"
40 #include "alc/context.h"
41 #include "alc/device.h"
42 #include "alc/inprogext.h"
44 #include "alnumeric.h"
47 #include "core/except.h"
48 #include "core/fpu_ctrl.h"
49 #include "core/logging.h"
51 #include "opthelpers.h"
54 #include "eax_exception.h"
55 #include "eax_utils.h"
62 EffectStateFactory
* (&GetFactory
)(void);
64 constexpr FactoryItem FactoryList
[] = {
65 { EffectSlotType::None
, NullStateFactory_getFactory
},
66 { EffectSlotType::EAXReverb
, ReverbStateFactory_getFactory
},
67 { EffectSlotType::Reverb
, StdReverbStateFactory_getFactory
},
68 { EffectSlotType::Autowah
, AutowahStateFactory_getFactory
},
69 { EffectSlotType::Chorus
, ChorusStateFactory_getFactory
},
70 { EffectSlotType::Compressor
, CompressorStateFactory_getFactory
},
71 { EffectSlotType::Distortion
, DistortionStateFactory_getFactory
},
72 { EffectSlotType::Echo
, EchoStateFactory_getFactory
},
73 { EffectSlotType::Equalizer
, EqualizerStateFactory_getFactory
},
74 { EffectSlotType::Flanger
, FlangerStateFactory_getFactory
},
75 { EffectSlotType::FrequencyShifter
, FshifterStateFactory_getFactory
},
76 { EffectSlotType::RingModulator
, ModulatorStateFactory_getFactory
},
77 { EffectSlotType::PitchShifter
, PshifterStateFactory_getFactory
},
78 { EffectSlotType::VocalMorpher
, VmorpherStateFactory_getFactory
},
79 { EffectSlotType::DedicatedDialog
, DedicatedStateFactory_getFactory
},
80 { EffectSlotType::DedicatedLFE
, DedicatedStateFactory_getFactory
},
81 { EffectSlotType::Convolution
, ConvolutionStateFactory_getFactory
},
84 EffectStateFactory
*getFactoryByType(EffectSlotType type
)
86 auto iter
= std::find_if(std::begin(FactoryList
), std::end(FactoryList
),
87 [type
](const FactoryItem
&item
) noexcept
-> bool
88 { return item
.Type
== type
; });
89 return (iter
!= std::end(FactoryList
)) ? iter
->GetFactory() : nullptr;
93 inline ALeffectslot
*LookupEffectSlot(ALCcontext
*context
, ALuint id
) noexcept
95 const size_t lidx
{(id
-1) >> 6};
96 const ALuint slidx
{(id
-1) & 0x3f};
98 if UNLIKELY(lidx
>= context
->mEffectSlotList
.size())
100 EffectSlotSubList
&sublist
{context
->mEffectSlotList
[lidx
]};
101 if UNLIKELY(sublist
.FreeMask
& (1_u64
<< slidx
))
103 return sublist
.EffectSlots
+ slidx
;
106 inline ALeffect
*LookupEffect(ALCdevice
*device
, ALuint id
) noexcept
108 const size_t lidx
{(id
-1) >> 6};
109 const ALuint slidx
{(id
-1) & 0x3f};
111 if UNLIKELY(lidx
>= device
->EffectList
.size())
113 EffectSubList
&sublist
= device
->EffectList
[lidx
];
114 if UNLIKELY(sublist
.FreeMask
& (1_u64
<< slidx
))
116 return sublist
.Effects
+ slidx
;
119 inline ALbuffer
*LookupBuffer(ALCdevice
*device
, ALuint id
) noexcept
121 const size_t lidx
{(id
-1) >> 6};
122 const ALuint slidx
{(id
-1) & 0x3f};
124 if UNLIKELY(lidx
>= device
->BufferList
.size())
126 BufferSubList
&sublist
= device
->BufferList
[lidx
];
127 if UNLIKELY(sublist
.FreeMask
& (1_u64
<< slidx
))
129 return sublist
.Buffers
+ slidx
;
133 inline auto GetEffectBuffer(ALbuffer
*buffer
) noexcept
-> EffectState::Buffer
135 if(!buffer
) return EffectState::Buffer
{};
136 return EffectState::Buffer
{buffer
, buffer
->mData
};
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 size_t newcount
{curarray
->size() + auxslots
.size()};
146 /* Insert the new effect slots into the head of the array, followed by the
149 EffectSlotArray
*newarray
= EffectSlot::CreatePtrArray(newcount
);
150 auto slotiter
= std::transform(auxslots
.begin(), auxslots
.end(), newarray
->begin(),
151 [](ALeffectslot
*auxslot
) noexcept
{ return &auxslot
->mSlot
; });
152 std::copy(curarray
->begin(), curarray
->end(), slotiter
);
154 /* Remove any duplicates (first instance of each will be kept). */
155 auto last
= newarray
->end();
156 for(auto start
=newarray
->begin()+1;;)
158 last
= std::remove(start
, last
, *(start
-1));
159 if(start
== last
) break;
162 newcount
= static_cast<size_t>(std::distance(newarray
->begin(), last
));
164 /* Reallocate newarray if the new size ended up smaller from duplicate
167 if UNLIKELY(newcount
< newarray
->size())
170 newarray
= EffectSlot::CreatePtrArray(newcount
);
171 std::copy_n(curarray
->begin(), newcount
, newarray
->begin());
175 std::uninitialized_fill_n(newarray
->end(), newcount
, nullptr);
177 curarray
= context
->mActiveAuxSlots
.exchange(newarray
, std::memory_order_acq_rel
);
178 context
->mDevice
->waitForMix();
180 al::destroy_n(curarray
->end(), curarray
->size());
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 EffectSlotArray
*newarray
= EffectSlot::CreatePtrArray(curarray
->size());
194 auto new_end
= std::copy(curarray
->begin(), curarray
->end(), 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 LIKELY(newsize
!= newarray
->size())
208 newarray
= EffectSlot::CreatePtrArray(newsize
);
209 std::copy_n(curarray
->begin(), newsize
, newarray
->begin());
214 std::uninitialized_fill_n(newarray
->end(), newsize
, nullptr);
216 curarray
= context
->mActiveAuxSlots
.exchange(newarray
, std::memory_order_acq_rel
);
217 context
->mDevice
->waitForMix();
219 al::destroy_n(curarray
->end(), curarray
->size());
224 EffectSlotType
EffectSlotTypeFromEnum(ALenum type
)
228 case AL_EFFECT_NULL
: return EffectSlotType::None
;
229 case AL_EFFECT_REVERB
: return EffectSlotType::Reverb
;
230 case AL_EFFECT_CHORUS
: return EffectSlotType::Chorus
;
231 case AL_EFFECT_DISTORTION
: return EffectSlotType::Distortion
;
232 case AL_EFFECT_ECHO
: return EffectSlotType::Echo
;
233 case AL_EFFECT_FLANGER
: return EffectSlotType::Flanger
;
234 case AL_EFFECT_FREQUENCY_SHIFTER
: return EffectSlotType::FrequencyShifter
;
235 case AL_EFFECT_VOCAL_MORPHER
: return EffectSlotType::VocalMorpher
;
236 case AL_EFFECT_PITCH_SHIFTER
: return EffectSlotType::PitchShifter
;
237 case AL_EFFECT_RING_MODULATOR
: return EffectSlotType::RingModulator
;
238 case AL_EFFECT_AUTOWAH
: return EffectSlotType::Autowah
;
239 case AL_EFFECT_COMPRESSOR
: return EffectSlotType::Compressor
;
240 case AL_EFFECT_EQUALIZER
: return EffectSlotType::Equalizer
;
241 case AL_EFFECT_EAXREVERB
: return EffectSlotType::EAXReverb
;
242 case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT
: return EffectSlotType::DedicatedLFE
;
243 case AL_EFFECT_DEDICATED_DIALOGUE
: return EffectSlotType::DedicatedDialog
;
244 case AL_EFFECT_CONVOLUTION_REVERB_SOFT
: return EffectSlotType::Convolution
;
246 ERR("Unhandled effect enum: 0x%04x\n", type
);
247 return EffectSlotType::None
;
250 bool EnsureEffectSlots(ALCcontext
*context
, size_t needed
)
252 size_t count
{std::accumulate(context
->mEffectSlotList
.cbegin(),
253 context
->mEffectSlotList
.cend(), size_t{0},
254 [](size_t cur
, const EffectSlotSubList
&sublist
) noexcept
-> size_t
255 { return cur
+ static_cast<ALuint
>(al::popcount(sublist
.FreeMask
)); })};
257 while(needed
> count
)
259 if UNLIKELY(context
->mEffectSlotList
.size() >= 1<<25)
262 context
->mEffectSlotList
.emplace_back();
263 auto sublist
= context
->mEffectSlotList
.end() - 1;
264 sublist
->FreeMask
= ~0_u64
;
265 sublist
->EffectSlots
= static_cast<ALeffectslot
*>(
266 al_calloc(alignof(ALeffectslot
), sizeof(ALeffectslot
)*64));
267 if UNLIKELY(!sublist
->EffectSlots
)
269 context
->mEffectSlotList
.pop_back();
277 ALeffectslot
*AllocEffectSlot(ALCcontext
*context
)
279 auto sublist
= std::find_if(context
->mEffectSlotList
.begin(), context
->mEffectSlotList
.end(),
280 [](const EffectSlotSubList
&entry
) noexcept
-> bool
281 { return entry
.FreeMask
!= 0; });
282 auto lidx
= static_cast<ALuint
>(std::distance(context
->mEffectSlotList
.begin(), sublist
));
283 auto slidx
= static_cast<ALuint
>(al::countr_zero(sublist
->FreeMask
));
286 ALeffectslot
*slot
{al::construct_at(sublist
->EffectSlots
+ slidx
)};
287 aluInitEffectPanning(&slot
->mSlot
, context
);
289 /* Add 1 to avoid source ID 0. */
290 slot
->id
= ((lidx
<<6) | slidx
) + 1;
292 context
->mNumEffectSlots
+= 1;
293 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
298 void FreeEffectSlot(ALCcontext
*context
, ALeffectslot
*slot
)
300 const ALuint id
{slot
->id
- 1};
301 const size_t lidx
{id
>> 6};
302 const ALuint slidx
{id
& 0x3f};
304 al::destroy_at(slot
);
306 context
->mEffectSlotList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
307 context
->mNumEffectSlots
--;
311 inline void UpdateProps(ALeffectslot
*slot
, ALCcontext
*context
)
313 if(!context
->mDeferUpdates
&& slot
->mState
== SlotState::Playing
)
315 slot
->updateProps(context
);
318 slot
->mPropsDirty
= true;
324 AL_API
void AL_APIENTRY
alGenAuxiliaryEffectSlots(ALsizei n
, ALuint
*effectslots
)
327 ContextRef context
{GetContextRef()};
328 if UNLIKELY(!context
) return;
331 context
->setError(AL_INVALID_VALUE
, "Generating %d effect slots", n
);
332 if UNLIKELY(n
<= 0) return;
334 std::unique_lock
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
335 ALCdevice
*device
{context
->mALDevice
.get()};
336 if(static_cast<ALuint
>(n
) > device
->AuxiliaryEffectSlotMax
-context
->mNumEffectSlots
)
338 context
->setError(AL_OUT_OF_MEMORY
, "Exceeding %u effect slot limit (%u + %d)",
339 device
->AuxiliaryEffectSlotMax
, context
->mNumEffectSlots
, n
);
342 if(!EnsureEffectSlots(context
.get(), static_cast<ALuint
>(n
)))
344 context
->setError(AL_OUT_OF_MEMORY
, "Failed to allocate %d effectslot%s", n
,
351 ALeffectslot
*slot
{AllocEffectSlot(context
.get())};
353 effectslots
[0] = slot
->id
;
357 al::vector
<ALuint
> ids
;
359 ids
.reserve(static_cast<ALuint
>(count
));
361 ALeffectslot
*slot
{AllocEffectSlot(context
.get())};
365 alDeleteAuxiliaryEffectSlots(static_cast<ALsizei
>(ids
.size()), ids
.data());
368 ids
.emplace_back(slot
->id
);
370 std::copy(ids
.cbegin(), ids
.cend(), effectslots
);
375 AL_API
void AL_APIENTRY
alDeleteAuxiliaryEffectSlots(ALsizei n
, const ALuint
*effectslots
)
378 ContextRef context
{GetContextRef()};
379 if UNLIKELY(!context
) return;
382 context
->setError(AL_INVALID_VALUE
, "Deleting %d effect slots", n
);
383 if UNLIKELY(n
<= 0) return;
385 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
388 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), effectslots
[0])};
391 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslots
[0]);
394 if UNLIKELY(ReadRef(slot
->ref
) != 0)
396 context
->setError(AL_INVALID_OPERATION
, "Deleting in-use effect slot %u",
400 RemoveActiveEffectSlots({&slot
, 1u}, context
.get());
401 FreeEffectSlot(context
.get(), slot
);
405 auto slots
= al::vector
<ALeffectslot
*>(static_cast<ALuint
>(n
));
406 for(size_t i
{0};i
< slots
.size();++i
)
408 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), effectslots
[i
])};
411 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", effectslots
[i
]);
414 if UNLIKELY(ReadRef(slot
->ref
) != 0)
416 context
->setError(AL_INVALID_OPERATION
, "Deleting in-use effect slot %u",
422 /* Remove any duplicates. */
423 auto slots_end
= slots
.end();
424 for(auto start
=slots
.begin()+1;start
!= slots_end
;++start
)
426 slots_end
= std::remove(start
, slots_end
, *(start
-1));
427 if(start
== slots_end
) break;
429 slots
.erase(slots_end
, slots
.end());
431 /* All effectslots are valid, remove and delete them */
432 RemoveActiveEffectSlots(slots
, context
.get());
433 for(ALeffectslot
*slot
: slots
)
434 FreeEffectSlot(context
.get(), slot
);
439 AL_API ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlot(ALuint effectslot
)
442 ContextRef context
{GetContextRef()};
445 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
446 if(LookupEffectSlot(context
.get(), effectslot
) != nullptr)
454 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlaySOFT(ALuint slotid
)
457 ContextRef context
{GetContextRef()};
458 if UNLIKELY(!context
) return;
460 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
461 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotid
)};
464 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotid
);
467 if(slot
->mState
== SlotState::Playing
)
470 slot
->mPropsDirty
= false;
471 slot
->updateProps(context
.get());
473 AddActiveEffectSlots({&slot
, 1}, context
.get());
474 slot
->mState
= SlotState::Playing
;
478 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotPlayvSOFT(ALsizei n
, const ALuint
*slotids
)
481 ContextRef context
{GetContextRef()};
482 if UNLIKELY(!context
) return;
485 context
->setError(AL_INVALID_VALUE
, "Playing %d effect slots", n
);
486 if UNLIKELY(n
<= 0) return;
488 auto slots
= al::vector
<ALeffectslot
*>(static_cast<ALuint
>(n
));
489 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
490 for(size_t i
{0};i
< slots
.size();++i
)
492 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotids
[i
])};
495 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotids
[i
]);
499 if(slot
->mState
!= SlotState::Playing
)
501 slot
->mPropsDirty
= false;
502 slot
->updateProps(context
.get());
507 AddActiveEffectSlots(slots
, context
.get());
508 for(auto slot
: slots
)
509 slot
->mState
= SlotState::Playing
;
513 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopSOFT(ALuint slotid
)
516 ContextRef context
{GetContextRef()};
517 if UNLIKELY(!context
) return;
519 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
520 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotid
)};
523 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotid
);
527 RemoveActiveEffectSlots({&slot
, 1}, context
.get());
528 slot
->mState
= SlotState::Stopped
;
532 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotStopvSOFT(ALsizei n
, const ALuint
*slotids
)
535 ContextRef context
{GetContextRef()};
536 if UNLIKELY(!context
) return;
539 context
->setError(AL_INVALID_VALUE
, "Stopping %d effect slots", n
);
540 if UNLIKELY(n
<= 0) return;
542 auto slots
= al::vector
<ALeffectslot
*>(static_cast<ALuint
>(n
));
543 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
544 for(size_t i
{0};i
< slots
.size();++i
)
546 ALeffectslot
*slot
{LookupEffectSlot(context
.get(), slotids
[i
])};
549 context
->setError(AL_INVALID_NAME
, "Invalid effect slot ID %u", slotids
[i
]);
556 RemoveActiveEffectSlots(slots
, context
.get());
557 for(auto slot
: slots
)
558 slot
->mState
= SlotState::Stopped
;
563 AL_API
void AL_APIENTRY
alAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint value
)
566 ContextRef context
{GetContextRef()};
567 if UNLIKELY(!context
) return;
569 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
570 std::lock_guard
<std::mutex
> __
{context
->mEffectSlotLock
};
571 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
573 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
575 ALeffectslot
*target
{};
580 case AL_EFFECTSLOT_EFFECT
:
581 device
= context
->mALDevice
.get();
584 std::lock_guard
<std::mutex
> ___
{device
->EffectLock
};
585 ALeffect
*effect
{value
? LookupEffect(device
, static_cast<ALuint
>(value
)) : nullptr};
587 err
= slot
->initEffect(effect
->type
, effect
->Props
, context
.get());
591 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid effect ID %u", value
);
592 err
= slot
->initEffect(AL_EFFECT_NULL
, EffectProps
{}, context
.get());
595 if UNLIKELY(err
!= AL_NO_ERROR
)
597 context
->setError(err
, "Effect initialization failed");
600 if UNLIKELY(slot
->mState
== SlotState::Initial
)
602 slot
->mPropsDirty
= false;
603 slot
->updateProps(context
.get());
605 AddActiveEffectSlots({&slot
, 1}, context
.get());
606 slot
->mState
= SlotState::Playing
;
611 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
612 if(!(value
== AL_TRUE
|| value
== AL_FALSE
))
613 SETERR_RETURN(context
, AL_INVALID_VALUE
,,
614 "Effect slot auxiliary send auto out of range");
615 if UNLIKELY(slot
->AuxSendAuto
== !!value
)
617 slot
->AuxSendAuto
= !!value
;
620 case AL_EFFECTSLOT_TARGET_SOFT
:
621 target
= LookupEffectSlot(context
.get(), static_cast<ALuint
>(value
));
623 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid effect slot target ID");
624 if UNLIKELY(slot
->Target
== target
)
628 ALeffectslot
*checker
{target
};
629 while(checker
&& checker
!= slot
)
630 checker
= checker
->Target
;
632 SETERR_RETURN(context
, AL_INVALID_OPERATION
,,
633 "Setting target of effect slot ID %u to %u creates circular chain", slot
->id
,
637 if(ALeffectslot
*oldtarget
{slot
->Target
})
639 /* We must force an update if there was an existing effect slot
640 * target, in case it's about to be deleted.
642 if(target
) IncrementRef(target
->ref
);
643 DecrementRef(oldtarget
->ref
);
644 slot
->Target
= target
;
645 slot
->updateProps(context
.get());
649 if(target
) IncrementRef(target
->ref
);
650 slot
->Target
= target
;
654 device
= context
->mALDevice
.get();
656 if(slot
->mState
== SlotState::Playing
)
657 SETERR_RETURN(context
, AL_INVALID_OPERATION
,,
658 "Setting buffer on playing effect slot %u", slot
->id
);
660 if(ALbuffer
*buffer
{slot
->Buffer
})
662 if UNLIKELY(buffer
->id
== static_cast<ALuint
>(value
))
665 else if UNLIKELY(value
== 0)
669 std::lock_guard
<std::mutex
> ___
{device
->BufferLock
};
673 buffer
= LookupBuffer(device
, static_cast<ALuint
>(value
));
674 if(!buffer
) SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid buffer ID");
675 if(buffer
->mCallback
)
676 SETERR_RETURN(context
, AL_INVALID_OPERATION
,,
677 "Callback buffer not valid for effects");
679 IncrementRef(buffer
->ref
);
682 if(ALbuffer
*oldbuffer
{slot
->Buffer
})
683 DecrementRef(oldbuffer
->ref
);
684 slot
->Buffer
= buffer
;
687 auto *state
= slot
->Effect
.State
.get();
688 state
->deviceUpdate(device
, GetEffectBuffer(buffer
));
692 case AL_EFFECTSLOT_STATE_SOFT
:
693 SETERR_RETURN(context
, AL_INVALID_OPERATION
,, "AL_EFFECTSLOT_STATE_SOFT is read-only");
696 SETERR_RETURN(context
, AL_INVALID_ENUM
,, "Invalid effect slot integer property 0x%04x",
699 UpdateProps(slot
, context
.get());
703 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, const ALint
*values
)
708 case AL_EFFECTSLOT_EFFECT
:
709 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
710 case AL_EFFECTSLOT_TARGET_SOFT
:
711 case AL_EFFECTSLOT_STATE_SOFT
:
713 alAuxiliaryEffectSloti(effectslot
, param
, values
[0]);
717 ContextRef context
{GetContextRef()};
718 if UNLIKELY(!context
) return;
720 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
721 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
723 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
728 SETERR_RETURN(context
, AL_INVALID_ENUM
,,
729 "Invalid effect slot integer-vector property 0x%04x", param
);
734 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat value
)
737 ContextRef context
{GetContextRef()};
738 if UNLIKELY(!context
) return;
740 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
741 std::lock_guard
<std::mutex
> __
{context
->mEffectSlotLock
};
742 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
744 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
748 case AL_EFFECTSLOT_GAIN
:
749 if(!(value
>= 0.0f
&& value
<= 1.0f
))
750 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Effect slot gain out of range");
751 if UNLIKELY(slot
->Gain
== value
)
757 SETERR_RETURN(context
, AL_INVALID_ENUM
,, "Invalid effect slot float property 0x%04x",
760 UpdateProps(slot
, context
.get());
764 AL_API
void AL_APIENTRY
alAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, const ALfloat
*values
)
769 case AL_EFFECTSLOT_GAIN
:
770 alAuxiliaryEffectSlotf(effectslot
, param
, values
[0]);
774 ContextRef context
{GetContextRef()};
775 if UNLIKELY(!context
) return;
777 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
778 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
780 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
785 SETERR_RETURN(context
, AL_INVALID_ENUM
,,
786 "Invalid effect slot float-vector property 0x%04x", param
);
792 AL_API
void AL_APIENTRY
alGetAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint
*value
)
795 ContextRef context
{GetContextRef()};
796 if UNLIKELY(!context
) return;
798 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
799 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
801 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
805 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
806 *value
= slot
->AuxSendAuto
? AL_TRUE
: AL_FALSE
;
809 case AL_EFFECTSLOT_TARGET_SOFT
:
810 if(auto *target
= slot
->Target
)
811 *value
= static_cast<ALint
>(target
->id
);
816 case AL_EFFECTSLOT_STATE_SOFT
:
817 *value
= static_cast<int>(slot
->mState
);
821 if(auto *buffer
= slot
->Buffer
)
822 *value
= static_cast<ALint
>(buffer
->id
);
828 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x", param
);
833 AL_API
void AL_APIENTRY
alGetAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, ALint
*values
)
838 case AL_EFFECTSLOT_EFFECT
:
839 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
840 case AL_EFFECTSLOT_TARGET_SOFT
:
841 case AL_EFFECTSLOT_STATE_SOFT
:
843 alGetAuxiliaryEffectSloti(effectslot
, param
, values
);
847 ContextRef context
{GetContextRef()};
848 if UNLIKELY(!context
) return;
850 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
851 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
853 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
858 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
864 AL_API
void AL_APIENTRY
alGetAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat
*value
)
867 ContextRef context
{GetContextRef()};
868 if UNLIKELY(!context
) return;
870 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
871 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
873 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
877 case AL_EFFECTSLOT_GAIN
:
882 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x", param
);
887 AL_API
void AL_APIENTRY
alGetAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, ALfloat
*values
)
892 case AL_EFFECTSLOT_GAIN
:
893 alGetAuxiliaryEffectSlotf(effectslot
, param
, values
);
897 ContextRef context
{GetContextRef()};
898 if UNLIKELY(!context
) return;
900 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
901 ALeffectslot
*slot
= LookupEffectSlot(context
.get(), effectslot
);
903 SETERR_RETURN(context
, AL_INVALID_NAME
,, "Invalid effect slot ID %u", effectslot
);
908 context
->setError(AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
915 ALeffectslot::ALeffectslot()
917 EffectStateFactory
*factory
{getFactoryByType(EffectSlotType::None
)};
918 assert(factory
!= nullptr);
920 al::intrusive_ptr
<EffectState
> state
{factory
->create()};
921 Effect
.State
= state
;
922 mSlot
.mEffectState
= state
.release();
925 ALeffectslot::~ALeffectslot()
928 DecrementRef(Target
->ref
);
931 DecrementRef(Buffer
->ref
);
934 EffectSlotProps
*props
{mSlot
.Update
.exchange(nullptr)};
937 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n",
938 decltype(std::declval
<void*>()){props
});
942 if(mSlot
.mEffectState
)
943 mSlot
.mEffectState
->release();
946 ALenum
ALeffectslot::initEffect(ALenum effectType
, const EffectProps
&effectProps
,
949 EffectSlotType newtype
{EffectSlotTypeFromEnum(effectType
)};
950 if(newtype
!= Effect
.Type
)
952 EffectStateFactory
*factory
{getFactoryByType(newtype
)};
955 ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype
));
956 return AL_INVALID_ENUM
;
958 al::intrusive_ptr
<EffectState
> state
{factory
->create()};
960 ALCdevice
*device
{context
->mALDevice
.get()};
961 std::unique_lock
<std::mutex
> statelock
{device
->StateLock
};
962 state
->mOutTarget
= device
->Dry
.Buffer
;
965 state
->deviceUpdate(device
, GetEffectBuffer(Buffer
));
968 Effect
.Type
= newtype
;
969 Effect
.Props
= effectProps
;
971 Effect
.State
= std::move(state
);
973 else if(newtype
!= EffectSlotType::None
)
974 Effect
.Props
= effectProps
;
976 /* Remove state references from old effect slot property updates. */
977 EffectSlotProps
*props
{context
->mFreeEffectslotProps
.load()};
980 props
->State
= nullptr;
981 props
= props
->next
.load(std::memory_order_relaxed
);
987 void ALeffectslot::updateProps(ALCcontext
*context
)
989 /* Get an unused property container, or allocate a new one as needed. */
990 EffectSlotProps
*props
{context
->mFreeEffectslotProps
.load(std::memory_order_relaxed
)};
992 props
= new EffectSlotProps
{};
995 EffectSlotProps
*next
;
997 next
= props
->next
.load(std::memory_order_relaxed
);
998 } while(context
->mFreeEffectslotProps
.compare_exchange_weak(props
, next
,
999 std::memory_order_seq_cst
, std::memory_order_acquire
) == 0);
1002 /* Copy in current property values. */
1004 props
->AuxSendAuto
= AuxSendAuto
;
1005 props
->Target
= Target
? &Target
->mSlot
: nullptr;
1007 props
->Type
= Effect
.Type
;
1008 props
->Props
= Effect
.Props
;
1009 props
->State
= Effect
.State
;
1011 /* Set the new container for updating internal parameters. */
1012 props
= mSlot
.Update
.exchange(props
, std::memory_order_acq_rel
);
1015 /* If there was an unused update container, put it back in the
1018 props
->State
= nullptr;
1019 AtomicReplaceHead(context
->mFreeEffectslotProps
, props
);
1023 void UpdateAllEffectSlotProps(ALCcontext
*context
)
1025 std::lock_guard
<std::mutex
> _
{context
->mEffectSlotLock
};
1027 if(context
->has_eax())
1028 context
->eax_commit_fx_slots();
1030 for(auto &sublist
: context
->mEffectSlotList
)
1032 uint64_t usemask
{~sublist
.FreeMask
};
1035 const int idx
{al::countr_zero(usemask
)};
1036 usemask
&= ~(1_u64
<< idx
);
1037 ALeffectslot
*slot
{sublist
.EffectSlots
+ idx
};
1039 if(slot
->mState
!= SlotState::Stopped
&& std::exchange(slot
->mPropsDirty
, false))
1040 slot
->updateProps(context
);
1045 EffectSlotSubList::~EffectSlotSubList()
1047 uint64_t usemask
{~FreeMask
};
1050 const int idx
{al::countr_zero(usemask
)};
1051 al::destroy_at(EffectSlots
+idx
);
1052 usemask
&= ~(1_u64
<< idx
);
1054 FreeMask
= ~usemask
;
1055 al_free(EffectSlots
);
1056 EffectSlots
= nullptr;
1062 class EaxFxSlotException
:
1066 explicit EaxFxSlotException(
1067 const char* message
)
1069 EaxException
{"EAX_FX_SLOT", message
}
1072 }; // EaxFxSlotException
1078 void ALeffectslot::eax_initialize(
1079 ALCcontext
& al_context
,
1080 EaxFxSlotIndexValue index
)
1082 eax_al_context_
= &al_context
;
1084 if (index
>= EAX_MAX_FXSLOTS
)
1086 eax_fail("Index out of range.");
1089 eax_fx_slot_index_
= index
;
1091 eax_initialize_eax();
1092 eax_initialize_lock();
1093 eax_initialize_effects();
1096 const EAX50FXSLOTPROPERTIES
& ALeffectslot::eax_get_eax_fx_slot() const noexcept
1098 return eax_eax_fx_slot_
;
1101 void ALeffectslot::eax_ensure_is_unlocked() const
1104 eax_fail("Locked.");
1107 void ALeffectslot::eax_validate_fx_slot_effect(
1108 const GUID
& eax_effect_id
)
1110 eax_ensure_is_unlocked();
1112 if (eax_effect_id
!= EAX_NULL_GUID
&&
1113 eax_effect_id
!= EAX_REVERB_EFFECT
&&
1114 eax_effect_id
!= EAX_AGCCOMPRESSOR_EFFECT
&&
1115 eax_effect_id
!= EAX_AUTOWAH_EFFECT
&&
1116 eax_effect_id
!= EAX_CHORUS_EFFECT
&&
1117 eax_effect_id
!= EAX_DISTORTION_EFFECT
&&
1118 eax_effect_id
!= EAX_ECHO_EFFECT
&&
1119 eax_effect_id
!= EAX_EQUALIZER_EFFECT
&&
1120 eax_effect_id
!= EAX_FLANGER_EFFECT
&&
1121 eax_effect_id
!= EAX_FREQUENCYSHIFTER_EFFECT
&&
1122 eax_effect_id
!= EAX_VOCALMORPHER_EFFECT
&&
1123 eax_effect_id
!= EAX_PITCHSHIFTER_EFFECT
&&
1124 eax_effect_id
!= EAX_RINGMODULATOR_EFFECT
)
1126 eax_fail("Unsupported EAX effect GUID.");
1130 void ALeffectslot::eax_validate_fx_slot_volume(
1133 eax_validate_range
<EaxFxSlotException
>(
1136 EAXFXSLOT_MINVOLUME
,
1137 EAXFXSLOT_MAXVOLUME
);
1140 void ALeffectslot::eax_validate_fx_slot_lock(
1143 eax_ensure_is_unlocked();
1145 eax_validate_range
<EaxFxSlotException
>(
1152 void ALeffectslot::eax_validate_fx_slot_flags(
1153 unsigned long eax_flags
,
1156 eax_validate_range
<EaxFxSlotException
>(
1160 ~(eax_version
== 4 ? EAX40FXSLOTFLAGS_RESERVED
: EAX50FXSLOTFLAGS_RESERVED
));
1163 void ALeffectslot::eax_validate_fx_slot_occlusion(
1166 eax_validate_range
<EaxFxSlotException
>(
1169 EAXFXSLOT_MINOCCLUSION
,
1170 EAXFXSLOT_MAXOCCLUSION
);
1173 void ALeffectslot::eax_validate_fx_slot_occlusion_lf_ratio(
1174 float eax_occlusion_lf_ratio
)
1176 eax_validate_range
<EaxFxSlotException
>(
1177 "Occlusion LF Ratio",
1178 eax_occlusion_lf_ratio
,
1179 EAXFXSLOT_MINOCCLUSIONLFRATIO
,
1180 EAXFXSLOT_MAXOCCLUSIONLFRATIO
);
1183 void ALeffectslot::eax_validate_fx_slot_all(
1184 const EAX40FXSLOTPROPERTIES
& fx_slot
,
1187 eax_validate_fx_slot_effect(fx_slot
.guidLoadEffect
);
1188 eax_validate_fx_slot_volume(fx_slot
.lVolume
);
1189 eax_validate_fx_slot_lock(fx_slot
.lLock
);
1190 eax_validate_fx_slot_flags(fx_slot
.ulFlags
, eax_version
);
1193 void ALeffectslot::eax_validate_fx_slot_all(
1194 const EAX50FXSLOTPROPERTIES
& fx_slot
,
1197 eax_validate_fx_slot_all(static_cast<const EAX40FXSLOTPROPERTIES
&>(fx_slot
), eax_version
);
1199 eax_validate_fx_slot_occlusion(fx_slot
.lOcclusion
);
1200 eax_validate_fx_slot_occlusion_lf_ratio(fx_slot
.flOcclusionLFRatio
);
1203 void ALeffectslot::eax_set_fx_slot_effect(
1204 const GUID
& eax_effect_id
)
1206 if (eax_eax_fx_slot_
.guidLoadEffect
== eax_effect_id
)
1211 eax_eax_fx_slot_
.guidLoadEffect
= eax_effect_id
;
1213 eax_set_fx_slot_effect();
1216 void ALeffectslot::eax_set_fx_slot_volume(
1219 if (eax_eax_fx_slot_
.lVolume
== eax_volume
)
1224 eax_eax_fx_slot_
.lVolume
= eax_volume
;
1226 eax_set_fx_slot_volume();
1229 void ALeffectslot::eax_set_fx_slot_lock(
1232 if (eax_eax_fx_slot_
.lLock
== eax_lock
)
1237 eax_eax_fx_slot_
.lLock
= eax_lock
;
1240 void ALeffectslot::eax_set_fx_slot_flags(
1241 unsigned long eax_flags
)
1243 if (eax_eax_fx_slot_
.ulFlags
== eax_flags
)
1248 eax_eax_fx_slot_
.ulFlags
= eax_flags
;
1250 eax_set_fx_slot_flags();
1254 bool ALeffectslot::eax_set_fx_slot_occlusion(
1257 if (eax_eax_fx_slot_
.lOcclusion
== eax_occlusion
)
1262 eax_eax_fx_slot_
.lOcclusion
= eax_occlusion
;
1268 bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio(
1269 float eax_occlusion_lf_ratio
)
1271 if (eax_eax_fx_slot_
.flOcclusionLFRatio
== eax_occlusion_lf_ratio
)
1276 eax_eax_fx_slot_
.flOcclusionLFRatio
= eax_occlusion_lf_ratio
;
1281 void ALeffectslot::eax_set_fx_slot_all(
1282 const EAX40FXSLOTPROPERTIES
& eax_fx_slot
)
1284 eax_set_fx_slot_effect(eax_fx_slot
.guidLoadEffect
);
1285 eax_set_fx_slot_volume(eax_fx_slot
.lVolume
);
1286 eax_set_fx_slot_lock(eax_fx_slot
.lLock
);
1287 eax_set_fx_slot_flags(eax_fx_slot
.ulFlags
);
1291 bool ALeffectslot::eax_set_fx_slot_all(
1292 const EAX50FXSLOTPROPERTIES
& eax_fx_slot
)
1294 eax_set_fx_slot_all(static_cast<const EAX40FXSLOTPROPERTIES
&>(eax_fx_slot
));
1296 const auto is_occlusion_modified
= eax_set_fx_slot_occlusion(eax_fx_slot
.lOcclusion
);
1297 const auto is_occlusion_lf_ratio_modified
= eax_set_fx_slot_occlusion_lf_ratio(eax_fx_slot
.flOcclusionLFRatio
);
1299 return is_occlusion_modified
|| is_occlusion_lf_ratio_modified
;
1302 void ALeffectslot::eax_unlock_legacy() noexcept
1304 assert(eax_fx_slot_index_
< 2);
1305 eax_is_locked_
= false;
1306 eax_eax_fx_slot_
.lLock
= EAXFXSLOT_UNLOCKED
;
1310 void ALeffectslot::eax_fail(
1311 const char* message
)
1313 throw EaxFxSlotException
{message
};
1316 GUID
ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
1318 switch (eax_fx_slot_index_
)
1320 case 0: return EAX_REVERB_EFFECT
;
1321 case 1: return EAX_CHORUS_EFFECT
;
1322 default: return EAX_NULL_GUID
;
1326 long ALeffectslot::eax_get_eax_default_lock() const noexcept
1328 return eax_fx_slot_index_
< 2 ? EAXFXSLOT_LOCKED
: EAXFXSLOT_UNLOCKED
;
1331 void ALeffectslot::eax_set_eax_fx_slot_defaults()
1333 eax_eax_fx_slot_
.guidLoadEffect
= eax_get_eax_default_effect_guid();
1334 eax_eax_fx_slot_
.lVolume
= EAXFXSLOT_DEFAULTVOLUME
;
1335 eax_eax_fx_slot_
.lLock
= eax_get_eax_default_lock();
1336 eax_eax_fx_slot_
.ulFlags
= EAX40FXSLOT_DEFAULTFLAGS
;
1337 eax_eax_fx_slot_
.lOcclusion
= EAXFXSLOT_DEFAULTOCCLUSION
;
1338 eax_eax_fx_slot_
.flOcclusionLFRatio
= EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO
;
1341 void ALeffectslot::eax_initialize_eax()
1343 eax_set_eax_fx_slot_defaults();
1346 void ALeffectslot::eax_initialize_lock()
1348 eax_is_locked_
= (eax_fx_slot_index_
< 2);
1351 void ALeffectslot::eax_initialize_effects()
1353 eax_set_fx_slot_effect();
1356 void ALeffectslot::eax_get_fx_slot_all(
1357 const EaxEaxCall
& eax_call
) const
1359 switch (eax_call
.get_version())
1362 eax_call
.set_value
<EaxFxSlotException
, EAX40FXSLOTPROPERTIES
>(eax_eax_fx_slot_
);
1366 eax_call
.set_value
<EaxFxSlotException
, EAX50FXSLOTPROPERTIES
>(eax_eax_fx_slot_
);
1370 eax_fail("Unsupported EAX version.");
1374 void ALeffectslot::eax_get_fx_slot(
1375 const EaxEaxCall
& eax_call
) const
1377 switch (eax_call
.get_property_id())
1379 case EAXFXSLOT_ALLPARAMETERS
:
1380 eax_get_fx_slot_all(eax_call
);
1383 case EAXFXSLOT_LOADEFFECT
:
1384 eax_call
.set_value
<EaxFxSlotException
>(eax_eax_fx_slot_
.guidLoadEffect
);
1387 case EAXFXSLOT_VOLUME
:
1388 eax_call
.set_value
<EaxFxSlotException
>(eax_eax_fx_slot_
.lVolume
);
1391 case EAXFXSLOT_LOCK
:
1392 eax_call
.set_value
<EaxFxSlotException
>(eax_eax_fx_slot_
.lLock
);
1395 case EAXFXSLOT_FLAGS
:
1396 eax_call
.set_value
<EaxFxSlotException
>(eax_eax_fx_slot_
.ulFlags
);
1399 case EAXFXSLOT_OCCLUSION
:
1400 eax_call
.set_value
<EaxFxSlotException
>(eax_eax_fx_slot_
.lOcclusion
);
1403 case EAXFXSLOT_OCCLUSIONLFRATIO
:
1404 eax_call
.set_value
<EaxFxSlotException
>(eax_eax_fx_slot_
.flOcclusionLFRatio
);
1408 eax_fail("Unsupported FX slot property id.");
1413 bool ALeffectslot::eax_get(
1414 const EaxEaxCall
& eax_call
)
1416 switch (eax_call
.get_property_set_id())
1418 case EaxEaxCallPropertySetId::fx_slot
:
1419 eax_get_fx_slot(eax_call
);
1422 case EaxEaxCallPropertySetId::fx_slot_effect
:
1423 eax_dispatch_effect(eax_call
);
1427 eax_fail("Unsupported property id.");
1433 void ALeffectslot::eax_set_fx_slot_effect(
1434 ALenum al_effect_type
)
1436 if(!IsValidEffectType(al_effect_type
))
1437 eax_fail("Unsupported effect.");
1439 eax_effect_
= nullptr;
1440 eax_effect_
= eax_create_eax_effect(al_effect_type
);
1442 eax_set_effect_slot_effect(*eax_effect_
);
1445 void ALeffectslot::eax_set_fx_slot_effect()
1447 auto al_effect_type
= ALenum
{};
1452 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_NULL_GUID
)
1454 al_effect_type
= AL_EFFECT_NULL
;
1456 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_AUTOWAH_EFFECT
)
1458 al_effect_type
= AL_EFFECT_AUTOWAH
;
1460 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_CHORUS_EFFECT
)
1462 al_effect_type
= AL_EFFECT_CHORUS
;
1464 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_AGCCOMPRESSOR_EFFECT
)
1466 al_effect_type
= AL_EFFECT_COMPRESSOR
;
1468 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_DISTORTION_EFFECT
)
1470 al_effect_type
= AL_EFFECT_DISTORTION
;
1472 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_REVERB_EFFECT
)
1474 al_effect_type
= AL_EFFECT_EAXREVERB
;
1476 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_ECHO_EFFECT
)
1478 al_effect_type
= AL_EFFECT_ECHO
;
1480 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_EQUALIZER_EFFECT
)
1482 al_effect_type
= AL_EFFECT_EQUALIZER
;
1484 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_FLANGER_EFFECT
)
1486 al_effect_type
= AL_EFFECT_FLANGER
;
1488 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_FREQUENCYSHIFTER_EFFECT
)
1490 al_effect_type
= AL_EFFECT_FREQUENCY_SHIFTER
;
1492 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_PITCHSHIFTER_EFFECT
)
1494 al_effect_type
= AL_EFFECT_PITCH_SHIFTER
;
1496 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_RINGMODULATOR_EFFECT
)
1498 al_effect_type
= AL_EFFECT_RING_MODULATOR
;
1500 else if (eax_eax_fx_slot_
.guidLoadEffect
== EAX_VOCALMORPHER_EFFECT
)
1502 al_effect_type
= AL_EFFECT_VOCAL_MORPHER
;
1506 eax_fail("Unsupported effect.");
1509 eax_set_fx_slot_effect(al_effect_type
);
1512 void ALeffectslot::eax_set_efx_effect_slot_gain()
1514 const auto gain
= level_mb_to_gain(
1515 static_cast<float>(clamp(
1516 eax_eax_fx_slot_
.lVolume
,
1517 EAXFXSLOT_MINVOLUME
,
1518 EAXFXSLOT_MAXVOLUME
)));
1520 eax_set_effect_slot_gain(gain
);
1523 void ALeffectslot::eax_set_fx_slot_volume()
1525 eax_set_efx_effect_slot_gain();
1528 void ALeffectslot::eax_set_effect_slot_send_auto()
1530 eax_set_effect_slot_send_auto((eax_eax_fx_slot_
.ulFlags
& EAXFXSLOTFLAGS_ENVIRONMENT
) != 0);
1533 void ALeffectslot::eax_set_fx_slot_flags()
1535 eax_set_effect_slot_send_auto();
1538 void ALeffectslot::eax_set_fx_slot_effect(
1539 const EaxEaxCall
& eax_call
)
1541 const auto& eax_effect_id
=
1542 eax_call
.get_value
<EaxFxSlotException
, const decltype(EAX40FXSLOTPROPERTIES::guidLoadEffect
)>();
1544 eax_validate_fx_slot_effect(eax_effect_id
);
1545 eax_set_fx_slot_effect(eax_effect_id
);
1548 void ALeffectslot::eax_set_fx_slot_volume(
1549 const EaxEaxCall
& eax_call
)
1551 const auto& eax_volume
=
1552 eax_call
.get_value
<EaxFxSlotException
, const decltype(EAX40FXSLOTPROPERTIES::lVolume
)>();
1554 eax_validate_fx_slot_volume(eax_volume
);
1555 eax_set_fx_slot_volume(eax_volume
);
1558 void ALeffectslot::eax_set_fx_slot_lock(
1559 const EaxEaxCall
& eax_call
)
1561 const auto& eax_lock
=
1562 eax_call
.get_value
<EaxFxSlotException
, const decltype(EAX40FXSLOTPROPERTIES::lLock
)>();
1564 eax_validate_fx_slot_lock(eax_lock
);
1565 eax_set_fx_slot_lock(eax_lock
);
1568 void ALeffectslot::eax_set_fx_slot_flags(
1569 const EaxEaxCall
& eax_call
)
1571 const auto& eax_flags
=
1572 eax_call
.get_value
<EaxFxSlotException
, const decltype(EAX40FXSLOTPROPERTIES::ulFlags
)>();
1574 eax_validate_fx_slot_flags(eax_flags
, eax_call
.get_version());
1575 eax_set_fx_slot_flags(eax_flags
);
1579 bool ALeffectslot::eax_set_fx_slot_occlusion(
1580 const EaxEaxCall
& eax_call
)
1582 const auto& eax_occlusion
=
1583 eax_call
.get_value
<EaxFxSlotException
, const decltype(EAX50FXSLOTPROPERTIES::lOcclusion
)>();
1585 eax_validate_fx_slot_occlusion(eax_occlusion
);
1587 return eax_set_fx_slot_occlusion(eax_occlusion
);
1591 bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio(
1592 const EaxEaxCall
& eax_call
)
1594 const auto& eax_occlusion_lf_ratio
=
1595 eax_call
.get_value
<EaxFxSlotException
, const decltype(EAX50FXSLOTPROPERTIES::flOcclusionLFRatio
)>();
1597 eax_validate_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio
);
1599 return eax_set_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio
);
1603 bool ALeffectslot::eax_set_fx_slot_all(
1604 const EaxEaxCall
& eax_call
)
1606 switch (eax_call
.get_version())
1610 const auto& eax_all
=
1611 eax_call
.get_value
<EaxFxSlotException
, const EAX40FXSLOTPROPERTIES
>();
1613 eax_validate_fx_slot_all(eax_all
, eax_call
.get_version());
1614 eax_set_fx_slot_all(eax_all
);
1621 const auto& eax_all
=
1622 eax_call
.get_value
<EaxFxSlotException
, const EAX50FXSLOTPROPERTIES
>();
1624 eax_validate_fx_slot_all(eax_all
, eax_call
.get_version());
1625 return eax_set_fx_slot_all(eax_all
);
1629 eax_fail("Unsupported EAX version.");
1633 bool ALeffectslot::eax_set_fx_slot(
1634 const EaxEaxCall
& eax_call
)
1636 switch (eax_call
.get_property_id())
1638 case EAXFXSLOT_NONE
:
1641 case EAXFXSLOT_ALLPARAMETERS
:
1642 return eax_set_fx_slot_all(eax_call
);
1644 case EAXFXSLOT_LOADEFFECT
:
1645 eax_set_fx_slot_effect(eax_call
);
1648 case EAXFXSLOT_VOLUME
:
1649 eax_set_fx_slot_volume(eax_call
);
1652 case EAXFXSLOT_LOCK
:
1653 eax_set_fx_slot_lock(eax_call
);
1656 case EAXFXSLOT_FLAGS
:
1657 eax_set_fx_slot_flags(eax_call
);
1660 case EAXFXSLOT_OCCLUSION
:
1661 return eax_set_fx_slot_occlusion(eax_call
);
1663 case EAXFXSLOT_OCCLUSIONLFRATIO
:
1664 return eax_set_fx_slot_occlusion_lf_ratio(eax_call
);
1668 eax_fail("Unsupported FX slot property id.");
1673 bool ALeffectslot::eax_set(const EaxEaxCall
& eax_call
)
1675 switch(eax_call
.get_property_set_id())
1677 case EaxEaxCallPropertySetId::fx_slot
:
1678 return eax_set_fx_slot(eax_call
);
1680 case EaxEaxCallPropertySetId::fx_slot_effect
:
1681 eax_dispatch_effect(eax_call
);
1685 eax_fail("Unsupported property id.");
1691 void ALeffectslot::eax_dispatch_effect(const EaxEaxCall
& eax_call
)
1692 { if(eax_effect_
) eax_effect_
->dispatch(eax_call
); }
1694 void ALeffectslot::eax_apply_deferred()
1696 /* The other FXSlot properties (volume, effect, etc) aren't deferred? */
1698 auto is_changed
= false;
1700 is_changed
= eax_effect_
->apply_deferred();
1702 eax_set_effect_slot_effect(*eax_effect_
);
1706 void ALeffectslot::eax_set_effect_slot_effect(EaxEffect
&effect
)
1708 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
1710 const auto error
= initEffect(effect
.al_effect_type_
, effect
.al_effect_props_
, eax_al_context_
);
1711 if (error
!= AL_NO_ERROR
)
1713 ERR(EAX_PREFIX
"%s\n", "Failed to initialize an effect.");
1717 if (mState
== SlotState::Initial
)
1719 mPropsDirty
= false;
1720 updateProps(eax_al_context_
);
1722 auto effect_slot_ptr
= this;
1724 AddActiveEffectSlots({&effect_slot_ptr
, 1}, eax_al_context_
);
1725 mState
= SlotState::Playing
;
1730 UpdateProps(this, eax_al_context_
);
1735 void ALeffectslot::eax_set_effect_slot_send_auto(
1738 if(AuxSendAuto
== is_send_auto
)
1741 AuxSendAuto
= is_send_auto
;
1742 UpdateProps(this, eax_al_context_
);
1745 void ALeffectslot::eax_set_effect_slot_gain(
1748 #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
1752 if(gain
< 0.0f
|| gain
> 1.0f
)
1753 ERR(EAX_PREFIX
"Gain out of range (%f)\n", gain
);
1755 Gain
= clampf(gain
, 0.0f
, 1.0f
);
1756 UpdateProps(this, eax_al_context_
);
1762 void ALeffectslot::EaxDeleter::operator()(ALeffectslot
* effect_slot
)
1764 assert(effect_slot
);
1765 eax_delete_al_effect_slot(*effect_slot
->eax_al_context_
, *effect_slot
);
1769 EaxAlEffectSlotUPtr
eax_create_al_effect_slot(
1770 ALCcontext
& context
)
1772 #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
1774 std::unique_lock
<std::mutex
> effect_slot_lock
{context
.mEffectSlotLock
};
1776 auto& device
= *context
.mALDevice
;
1778 if (context
.mNumEffectSlots
== device
.AuxiliaryEffectSlotMax
)
1780 ERR(EAX_PREFIX
"%s\n", "Out of memory.");
1784 if (!EnsureEffectSlots(&context
, 1))
1786 ERR(EAX_PREFIX
"%s\n", "Failed to ensure.");
1790 auto effect_slot
= EaxAlEffectSlotUPtr
{AllocEffectSlot(&context
)};
1793 ERR(EAX_PREFIX
"%s\n", "Failed to allocate.");
1802 void eax_delete_al_effect_slot(
1803 ALCcontext
& context
,
1804 ALeffectslot
& effect_slot
)
1806 #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
1808 std::lock_guard
<std::mutex
> effect_slot_lock
{context
.mEffectSlotLock
};
1810 if (ReadRef(effect_slot
.ref
) != 0)
1812 ERR(EAX_PREFIX
"Deleting in-use effect slot %u.\n", effect_slot
.id
);
1816 auto effect_slot_ptr
= &effect_slot
;
1818 RemoveActiveEffectSlots({&effect_slot_ptr
, 1}, &context
);
1819 FreeEffectSlot(&context
, &effect_slot
);
1823 #endif // ALSOFT_EAX