| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550 |
- /**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
- #include "config.h"
- #include "auxeffectslot.h"
- #include <algorithm>
- #include <cstddef>
- #include <cstdint>
- #include <functional>
- #include <iterator>
- #include <memory>
- #include <mutex>
- #include <numeric>
- #include <stdexcept>
- #include <tuple>
- #include <unordered_map>
- #include <vector>
- #include "AL/al.h"
- #include "AL/alc.h"
- #include "AL/alext.h"
- #include "AL/efx.h"
- #include "albit.h"
- #include "alc/alu.h"
- #include "alc/context.h"
- #include "alc/device.h"
- #include "alc/effects/base.h"
- #include "alc/inprogext.h"
- #include "almalloc.h"
- #include "alnumeric.h"
- #include "alspan.h"
- #include "atomic.h"
- #include "buffer.h"
- #include "core/buffer_storage.h"
- #include "core/device.h"
- #include "core/except.h"
- #include "core/fpu_ctrl.h"
- #include "core/logging.h"
- #include "direct_defs.h"
- #include "effect.h"
- #include "flexarray.h"
- #include "opthelpers.h"
- #if ALSOFT_EAX
- #include "eax/api.h"
- #include "eax/call.h"
- #include "eax/effect.h"
- #include "eax/fx_slot_index.h"
- #endif
- namespace {
- using SubListAllocator = al::allocator<std::array<ALeffectslot,64>>;
- [[nodiscard]]
- auto getFactoryByType(EffectSlotType type) -> EffectStateFactory*
- {
- switch(type)
- {
- case EffectSlotType::None: return NullStateFactory_getFactory();
- case EffectSlotType::Reverb: return ReverbStateFactory_getFactory();
- case EffectSlotType::Chorus: return ChorusStateFactory_getFactory();
- case EffectSlotType::Autowah: return AutowahStateFactory_getFactory();
- case EffectSlotType::Compressor: return CompressorStateFactory_getFactory();
- case EffectSlotType::Convolution: return ConvolutionStateFactory_getFactory();
- case EffectSlotType::Dedicated: return DedicatedStateFactory_getFactory();
- case EffectSlotType::Distortion: return DistortionStateFactory_getFactory();
- case EffectSlotType::Echo: return EchoStateFactory_getFactory();
- case EffectSlotType::Equalizer: return EqualizerStateFactory_getFactory();
- case EffectSlotType::Flanger: return ChorusStateFactory_getFactory();
- case EffectSlotType::FrequencyShifter: return FshifterStateFactory_getFactory();
- case EffectSlotType::RingModulator: return ModulatorStateFactory_getFactory();
- case EffectSlotType::PitchShifter: return PshifterStateFactory_getFactory();
- case EffectSlotType::VocalMorpher: return VmorpherStateFactory_getFactory();
- }
- return nullptr;
- }
- [[nodiscard]]
- auto LookupEffectSlot(ALCcontext *context, ALuint id) noexcept -> ALeffectslot*
- {
- const size_t lidx{(id-1) >> 6};
- const ALuint slidx{(id-1) & 0x3f};
- if(lidx >= context->mEffectSlotList.size()) UNLIKELY
- return nullptr;
- EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
- if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
- return nullptr;
- return al::to_address(sublist.EffectSlots->begin() + slidx);
- }
- [[nodiscard]]
- inline auto LookupEffect(al::Device *device, ALuint id) noexcept -> ALeffect*
- {
- const size_t lidx{(id-1) >> 6};
- const ALuint slidx{(id-1) & 0x3f};
- if(lidx >= device->EffectList.size()) UNLIKELY
- return nullptr;
- EffectSubList &sublist = device->EffectList[lidx];
- if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
- return nullptr;
- return al::to_address(sublist.Effects->begin() + slidx);
- }
- [[nodiscard]]
- inline auto LookupBuffer(al::Device *device, ALuint id) noexcept -> ALbuffer*
- {
- const size_t lidx{(id-1) >> 6};
- const ALuint slidx{(id-1) & 0x3f};
- if(lidx >= device->BufferList.size()) UNLIKELY
- return nullptr;
- BufferSubList &sublist = device->BufferList[lidx];
- if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
- return nullptr;
- return al::to_address(sublist.Buffers->begin() + slidx);
- }
- void AddActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
- {
- if(auxslots.empty()) return;
- EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
- if((curarray->size()>>1) > std::numeric_limits<size_t>::max()-auxslots.size())
- throw std::runtime_error{"Too many active effect slots"};
- size_t newcount{(curarray->size()>>1) + auxslots.size()};
- if(newcount > std::numeric_limits<size_t>::max()>>1)
- throw std::runtime_error{"Too many active effect slots"};
- /* Insert the new effect slots into the head of the new array, followed by
- * the existing ones.
- */
- auto newarray = EffectSlot::CreatePtrArray(newcount<<1);
- auto new_end = std::transform(auxslots.begin(), auxslots.end(), newarray->begin(),
- std::mem_fn(&ALeffectslot::mSlot));
- new_end = std::copy_n(curarray->begin(), curarray->size()>>1, new_end);
- /* Remove any duplicates (first instance of each will be kept). */
- for(auto start=newarray->begin()+1;;)
- {
- new_end = std::remove(start, new_end, *(start-1));
- if(start == new_end) break;
- ++start;
- }
- newcount = static_cast<size_t>(std::distance(newarray->begin(), new_end));
- /* Reallocate newarray if the new size ended up smaller from duplicate
- * removal.
- */
- if(newcount < newarray->size()>>1) UNLIKELY
- {
- auto oldarray = std::move(newarray);
- newarray = EffectSlot::CreatePtrArray(newcount<<1);
- new_end = std::copy_n(oldarray->begin(), newcount, newarray->begin());
- }
- std::fill(new_end, newarray->end(), nullptr);
- auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray),
- std::memory_order_acq_rel);
- std::ignore = context->mDevice->waitForMix();
- }
- void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context)
- {
- if(auxslots.empty()) return;
- EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)};
- /* Don't shrink the allocated array size since we don't know how many (if
- * any) of the effect slots to remove are in the array.
- */
- auto newarray = EffectSlot::CreatePtrArray(curarray->size());
- auto new_end = std::copy_n(curarray->begin(), curarray->size()>>1, newarray->begin());
- /* Remove elements from newarray that match any ID in slotids. */
- for(const ALeffectslot *auxslot : auxslots)
- {
- auto slot_match = [auxslot](EffectSlot *slot) noexcept -> bool
- { return (slot == auxslot->mSlot); };
- new_end = std::remove_if(newarray->begin(), new_end, slot_match);
- }
- /* Reallocate with the new size. */
- auto newsize = static_cast<size_t>(std::distance(newarray->begin(), new_end));
- if(newsize < newarray->size()>>1) LIKELY
- {
- auto oldarray = std::move(newarray);
- newarray = EffectSlot::CreatePtrArray(newsize<<1);
- new_end = std::copy_n(oldarray->begin(), newsize, newarray->begin());
- }
- std::fill(new_end, newarray->end(), nullptr);
- auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray),
- std::memory_order_acq_rel);
- std::ignore = context->mDevice->waitForMix();
- }
- [[nodiscard]]
- constexpr auto EffectSlotTypeFromEnum(ALenum type) noexcept -> EffectSlotType
- {
- switch(type)
- {
- case AL_EFFECT_NULL: return EffectSlotType::None;
- case AL_EFFECT_REVERB: return EffectSlotType::Reverb;
- case AL_EFFECT_CHORUS: return EffectSlotType::Chorus;
- case AL_EFFECT_DISTORTION: return EffectSlotType::Distortion;
- case AL_EFFECT_ECHO: return EffectSlotType::Echo;
- case AL_EFFECT_FLANGER: return EffectSlotType::Flanger;
- case AL_EFFECT_FREQUENCY_SHIFTER: return EffectSlotType::FrequencyShifter;
- case AL_EFFECT_VOCAL_MORPHER: return EffectSlotType::VocalMorpher;
- case AL_EFFECT_PITCH_SHIFTER: return EffectSlotType::PitchShifter;
- case AL_EFFECT_RING_MODULATOR: return EffectSlotType::RingModulator;
- case AL_EFFECT_AUTOWAH: return EffectSlotType::Autowah;
- case AL_EFFECT_COMPRESSOR: return EffectSlotType::Compressor;
- case AL_EFFECT_EQUALIZER: return EffectSlotType::Equalizer;
- case AL_EFFECT_EAXREVERB: return EffectSlotType::Reverb;
- case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::Dedicated;
- case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::Dedicated;
- case AL_EFFECT_CONVOLUTION_SOFT: return EffectSlotType::Convolution;
- }
- ERR("Unhandled effect enum: {:#04x}", as_unsigned(type));
- return EffectSlotType::None;
- }
- [[nodiscard]]
- auto EnsureEffectSlots(ALCcontext *context, size_t needed) noexcept -> bool
- try {
- size_t count{std::accumulate(context->mEffectSlotList.cbegin(),
- context->mEffectSlotList.cend(), 0_uz,
- [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
- { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
- while(needed > count)
- {
- if(context->mEffectSlotList.size() >= 1<<25) UNLIKELY
- return false;
- EffectSlotSubList sublist{};
- sublist.FreeMask = ~0_u64;
- sublist.EffectSlots = SubListAllocator{}.allocate(1);
- context->mEffectSlotList.emplace_back(std::move(sublist));
- count += std::tuple_size_v<SubListAllocator::value_type>;
- }
- return true;
- }
- catch(...) {
- return false;
- }
- [[nodiscard]]
- auto AllocEffectSlot(ALCcontext *context) -> ALeffectslot*
- {
- auto sublist = std::find_if(context->mEffectSlotList.begin(), context->mEffectSlotList.end(),
- [](const EffectSlotSubList &entry) noexcept -> bool
- { return entry.FreeMask != 0; });
- auto lidx = static_cast<ALuint>(std::distance(context->mEffectSlotList.begin(), sublist));
- auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
- ASSUME(slidx < 64);
- ALeffectslot *slot{al::construct_at(al::to_address(sublist->EffectSlots->begin() + slidx),
- context)};
- aluInitEffectPanning(slot->mSlot, context);
- /* Add 1 to avoid ID 0. */
- slot->id = ((lidx<<6) | slidx) + 1;
- context->mNumEffectSlots += 1;
- sublist->FreeMask &= ~(1_u64 << slidx);
- return slot;
- }
- void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
- {
- context->mEffectSlotNames.erase(slot->id);
- const ALuint id{slot->id - 1};
- const size_t lidx{id >> 6};
- const ALuint slidx{id & 0x3f};
- std::destroy_at(slot);
- context->mEffectSlotList[lidx].FreeMask |= 1_u64 << slidx;
- context->mNumEffectSlots--;
- }
- inline void UpdateProps(ALeffectslot *slot, ALCcontext *context)
- {
- if(!context->mDeferUpdates && slot->mState == SlotState::Playing)
- {
- slot->updateProps(context);
- return;
- }
- slot->mPropsDirty = true;
- }
- } // namespace
- AL_API DECL_FUNC2(void, alGenAuxiliaryEffectSlots, ALsizei,n, ALuint*,effectslots)
- FORCE_ALIGN void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n,
- ALuint *effectslots) noexcept
- try {
- if(n < 0)
- context->throw_error(AL_INVALID_VALUE, "Generating {} effect slots", n);
- if(n <= 0) UNLIKELY return;
- auto slotlock = std::lock_guard{context->mEffectSlotLock};
- auto *device = context->mALDevice.get();
- const al::span eids{effectslots, static_cast<ALuint>(n)};
- if(context->mNumEffectSlots > device->AuxiliaryEffectSlotMax
- || eids.size() > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
- context->throw_error(AL_OUT_OF_MEMORY, "Exceeding {} effect slot limit ({} + {})",
- device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n);
- if(!EnsureEffectSlots(context, eids.size()))
- context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} effectslot{}", n,
- (n==1) ? "" : "s");
- std::vector<ALeffectslot*> slots;
- try {
- if(eids.size() == 1)
- {
- /* Special handling for the easy and normal case. */
- eids[0] = AllocEffectSlot(context)->id;
- }
- else
- {
- slots.reserve(eids.size());
- std::generate_n(std::back_inserter(slots), eids.size(),
- [context]{ return AllocEffectSlot(context); });
- std::transform(slots.cbegin(), slots.cend(), eids.begin(),
- [](ALeffectslot *slot) -> ALuint { return slot->id; });
- }
- }
- catch(std::exception& e) {
- ERR("Exception allocating effectslot {} of {}: {}", slots.size()+1, n, e.what());
- auto delete_effectslot = [context](ALeffectslot *slot) -> void
- { FreeEffectSlot(context, slot); };
- std::for_each(slots.begin(), slots.end(), delete_effectslot);
- context->throw_error(AL_INVALID_OPERATION, "Exception allocating {} effectslots: {}", n,
- e.what());
- }
- }
- catch(al::base_exception&) {
- }
- catch(std::exception &e) {
- ERR("Caught exception: {}", e.what());
- }
- AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei,n, const ALuint*,effectslots)
- FORCE_ALIGN void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n,
- const ALuint *effectslots) noexcept
- try {
- if(n < 0) UNLIKELY
- context->throw_error(AL_INVALID_VALUE, "Deleting {} effect slots", n);
- if(n <= 0) UNLIKELY return;
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- if(n == 1)
- {
- ALeffectslot *slot{LookupEffectSlot(context, *effectslots)};
- if(!slot)
- context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", *effectslots);
- if(slot->ref.load(std::memory_order_relaxed) != 0)
- context->throw_error(AL_INVALID_OPERATION, "Deleting in-use effect slot {}",
- *effectslots);
- RemoveActiveEffectSlots({&slot, 1u}, context);
- FreeEffectSlot(context, slot);
- }
- else
- {
- const al::span eids{effectslots, static_cast<ALuint>(n)};
- std::vector<ALeffectslot*> slots;
- slots.reserve(eids.size());
- auto lookupslot = [context](const ALuint eid) -> ALeffectslot*
- {
- ALeffectslot *slot{LookupEffectSlot(context, eid)};
- if(!slot)
- context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", eid);
- if(slot->ref.load(std::memory_order_relaxed) != 0)
- context->throw_error(AL_INVALID_OPERATION, "Deleting in-use effect slot {}", eid);
- return slot;
- };
- std::transform(eids.cbegin(), eids.cend(), std::back_inserter(slots), lookupslot);
- /* All effectslots are valid, remove and delete them */
- RemoveActiveEffectSlots(slots, context);
- auto delete_effectslot = [context](const ALuint eid) -> void
- {
- if(ALeffectslot *slot{LookupEffectSlot(context, eid)})
- FreeEffectSlot(context, slot);
- };
- std::for_each(eids.begin(), eids.end(), delete_effectslot);
- }
- }
- catch(al::base_exception&) {
- }
- catch(std::exception &e) {
- ERR("Caught exception: {}", e.what());
- }
- AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint,effectslot)
- FORCE_ALIGN ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context,
- ALuint effectslot) noexcept
- {
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- if(LookupEffectSlot(context, effectslot) != nullptr)
- return AL_TRUE;
- return AL_FALSE;
- }
- AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint) noexcept
- {
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
- context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlaySOFT not supported");
- }
- AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei, const ALuint*) noexcept
- {
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
- context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlayvSOFT not supported");
- }
- AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint) noexcept
- {
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
- context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopSOFT not supported");
- }
- AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei, const ALuint*) noexcept
- {
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
- context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopvSOFT not supported");
- }
- AL_API DECL_FUNC3(void, alAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint,value)
- FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot,
- ALenum param, ALint value) noexcept
- try {
- std::lock_guard<std::mutex> proplock{context->mPropLock};
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
- if(!slot) UNLIKELY
- context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
- ALeffectslot *target{};
- ALenum err{};
- switch(param)
- {
- case AL_EFFECTSLOT_EFFECT:
- {
- auto *device = context->mALDevice.get();
- auto effectlock = std::lock_guard{device->EffectLock};
- auto *effect = value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr;
- if(effect)
- err = slot->initEffect(effect->id, effect->type, effect->Props, context);
- else
- {
- if(value != 0)
- context->throw_error(AL_INVALID_VALUE, "Invalid effect ID {}", value);
- err = slot->initEffect(0, AL_EFFECT_NULL, EffectProps{}, context);
- }
- }
- if(err != AL_NO_ERROR)
- context->throw_error(err, "Effect initialization failed");
- if(slot->mState == SlotState::Initial) UNLIKELY
- {
- slot->mPropsDirty = false;
- slot->updateProps(context);
- AddActiveEffectSlots({&slot, 1}, context);
- slot->mState = SlotState::Playing;
- return;
- }
- UpdateProps(slot, context);
- return;
- case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
- if(!(value == AL_TRUE || value == AL_FALSE))
- context->throw_error(AL_INVALID_VALUE, "Effect slot auxiliary send auto out of range");
- if(!(slot->AuxSendAuto == !!value)) LIKELY
- {
- slot->AuxSendAuto = !!value;
- UpdateProps(slot, context);
- }
- return;
- case AL_EFFECTSLOT_TARGET_SOFT:
- target = LookupEffectSlot(context, static_cast<ALuint>(value));
- if(value && !target)
- context->throw_error(AL_INVALID_VALUE, "Invalid effect slot target ID {}", value);
- if(slot->Target == target) UNLIKELY
- return;
- if(target)
- {
- ALeffectslot *checker{target};
- while(checker && checker != slot)
- checker = checker->Target;
- if(checker)
- context->throw_error(AL_INVALID_OPERATION,
- "Setting target of effect slot ID {} to {} creates circular chain", slot->id,
- target->id);
- }
- if(ALeffectslot *oldtarget{slot->Target})
- {
- /* We must force an update if there was an existing effect slot
- * target, in case it's about to be deleted.
- */
- if(target) IncrementRef(target->ref);
- DecrementRef(oldtarget->ref);
- slot->Target = target;
- slot->updateProps(context);
- return;
- }
- if(target) IncrementRef(target->ref);
- slot->Target = target;
- UpdateProps(slot, context);
- return;
- case AL_BUFFER:
- if(ALbuffer *buffer{slot->Buffer})
- {
- if(buffer->id == static_cast<ALuint>(value))
- return;
- }
- else if(value == 0)
- return;
- if(slot->mState == SlotState::Playing)
- {
- EffectStateFactory *factory{getFactoryByType(slot->Effect.Type)};
- assert(factory);
- al::intrusive_ptr<EffectState> state{factory->create()};
- auto *device = context->mALDevice.get();
- auto bufferlock = std::unique_lock{device->BufferLock};
- ALbuffer *buffer{};
- if(value)
- {
- buffer = LookupBuffer(device, static_cast<ALuint>(value));
- if(!buffer)
- context->throw_error(AL_INVALID_VALUE, "Invalid buffer ID {}", value);
- if(buffer->mCallback)
- context->throw_error(AL_INVALID_OPERATION,
- "Callback buffer not valid for effects");
- IncrementRef(buffer->ref);
- }
- /* Stop the effect slot from processing while we switch buffers. */
- RemoveActiveEffectSlots({&slot, 1}, context);
- if(ALbuffer *oldbuffer{slot->Buffer})
- DecrementRef(oldbuffer->ref);
- slot->Buffer = buffer;
- bufferlock.unlock();
- state->mOutTarget = device->Dry.Buffer;
- {
- FPUCtl mixer_mode{};
- state->deviceUpdate(device, buffer);
- }
- slot->Effect.State = std::move(state);
- slot->mPropsDirty = false;
- slot->updateProps(context);
- AddActiveEffectSlots({&slot, 1}, context);
- }
- else
- {
- auto *device = context->mALDevice.get();
- auto bufferlock = std::unique_lock{device->BufferLock};
- ALbuffer *buffer{};
- if(value)
- {
- buffer = LookupBuffer(device, static_cast<ALuint>(value));
- if(!buffer)
- context->throw_error(AL_INVALID_VALUE, "Invalid buffer ID {}", value);
- if(buffer->mCallback)
- context->throw_error(AL_INVALID_OPERATION,
- "Callback buffer not valid for effects");
- IncrementRef(buffer->ref);
- }
- if(ALbuffer *oldbuffer{slot->Buffer})
- DecrementRef(oldbuffer->ref);
- slot->Buffer = buffer;
- bufferlock.unlock();
- FPUCtl mixer_mode{};
- auto *state = slot->Effect.State.get();
- state->deviceUpdate(device, buffer);
- slot->mPropsDirty = true;
- }
- return;
- case AL_EFFECTSLOT_STATE_SOFT:
- context->throw_error(AL_INVALID_OPERATION, "AL_EFFECTSLOT_STATE_SOFT is read-only");
- }
- context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer property {:#04x}",
- as_unsigned(param));
- }
- catch(al::base_exception&) {
- }
- catch(std::exception &e) {
- ERR("Caught exception: {}", e.what());
- }
- AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, const ALint*,values)
- FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot,
- ALenum param, const ALint *values) noexcept
- try {
- switch(param)
- {
- case AL_EFFECTSLOT_EFFECT:
- case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
- case AL_EFFECTSLOT_TARGET_SOFT:
- case AL_EFFECTSLOT_STATE_SOFT:
- case AL_BUFFER:
- alAuxiliaryEffectSlotiDirect(context, effectslot, param, *values);
- return;
- }
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
- if(!slot)
- context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
- context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer-vector property {:#04x}",
- as_unsigned(param));
- }
- catch(al::base_exception&) {
- }
- catch(std::exception &e) {
- ERR("Caught exception: {}", e.what());
- }
- AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat,value)
- FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot,
- ALenum param, ALfloat value) noexcept
- try {
- std::lock_guard<std::mutex> proplock{context->mPropLock};
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
- if(!slot)
- context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
- switch(param)
- {
- case AL_EFFECTSLOT_GAIN:
- if(!(value >= 0.0f && value <= 1.0f))
- context->throw_error(AL_INVALID_VALUE, "Effect slot gain {} out of range", value);
- if(!(slot->Gain == value)) LIKELY
- {
- slot->Gain = value;
- UpdateProps(slot, context);
- }
- return;
- }
- context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float property {:#04x}",
- as_unsigned(param));
- }
- catch(al::base_exception&) {
- }
- catch(std::exception &e) {
- ERR("Caught exception: {}", e.what());
- }
- AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, const ALfloat*,values)
- FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot,
- ALenum param, const ALfloat *values) noexcept
- try {
- switch(param)
- {
- case AL_EFFECTSLOT_GAIN:
- alAuxiliaryEffectSlotfDirect(context, effectslot, param, *values);
- return;
- }
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
- if(!slot)
- context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
- context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float-vector property {:#04x}",
- as_unsigned(param));
- }
- catch(al::base_exception&) {
- }
- catch(std::exception &e) {
- ERR("Caught exception: {}", e.what());
- }
- AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint*,value)
- FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context,
- ALuint effectslot, ALenum param, ALint *value) noexcept
- try {
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
- if(!slot)
- context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
- switch(param)
- {
- case AL_EFFECTSLOT_EFFECT:
- *value = static_cast<ALint>(slot->EffectId);
- return;
- case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
- *value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE;
- return;
- case AL_EFFECTSLOT_TARGET_SOFT:
- if(auto *target = slot->Target)
- *value = static_cast<ALint>(target->id);
- else
- *value = 0;
- return;
- case AL_EFFECTSLOT_STATE_SOFT:
- *value = static_cast<int>(slot->mState);
- return;
- case AL_BUFFER:
- if(auto *buffer = slot->Buffer)
- *value = static_cast<ALint>(buffer->id);
- else
- *value = 0;
- return;
- }
- context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer property {:#04x}",
- as_unsigned(param));
- }
- catch(al::base_exception&) {
- }
- catch(std::exception &e) {
- ERR("Caught exception: {}", e.what());
- }
- AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, ALint*,values)
- FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context,
- ALuint effectslot, ALenum param, ALint *values) noexcept
- try {
- switch(param)
- {
- case AL_EFFECTSLOT_EFFECT:
- case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
- case AL_EFFECTSLOT_TARGET_SOFT:
- case AL_EFFECTSLOT_STATE_SOFT:
- case AL_BUFFER:
- alGetAuxiliaryEffectSlotiDirect(context, effectslot, param, values);
- return;
- }
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- ALeffectslot *slot = LookupEffectSlot(context, effectslot);
- if(!slot)
- context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
- context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer-vector property {:#04x}",
- as_unsigned(param));
- }
- catch(al::base_exception&) {
- }
- catch(std::exception &e) {
- ERR("Caught exception: {}", e.what());
- }
- AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat*,value)
- FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context,
- ALuint effectslot, ALenum param, ALfloat *value) noexcept
- try {
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
- if(!slot)
- context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
- switch(param)
- {
- case AL_EFFECTSLOT_GAIN: *value = slot->Gain; return;
- }
- context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float property {:#04x}",
- as_unsigned(param));
- }
- catch(al::base_exception&) {
- }
- catch(std::exception &e) {
- ERR("Caught exception: {}", e.what());
- }
- AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, ALfloat*,values)
- FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context,
- ALuint effectslot, ALenum param, ALfloat *values) noexcept
- try {
- switch(param)
- {
- case AL_EFFECTSLOT_GAIN:
- alGetAuxiliaryEffectSlotfDirect(context, effectslot, param, values);
- return;
- }
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
- if(!slot)
- context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
- context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float-vector property {:#04x}",
- as_unsigned(param));
- }
- catch(al::base_exception&) {
- }
- catch(std::exception &e) {
- ERR("Caught exception: {}", e.what());
- }
- ALeffectslot::ALeffectslot(ALCcontext *context)
- {
- EffectStateFactory *factory{getFactoryByType(EffectSlotType::None)};
- if(!factory) throw std::runtime_error{"Failed to get null effect factory"};
- al::intrusive_ptr<EffectState> state{factory->create()};
- Effect.State = state;
- mSlot = context->getEffectSlot();
- mSlot->InUse = true;
- mSlot->mEffectState = std::move(state);
- }
- ALeffectslot::~ALeffectslot()
- {
- if(Target)
- DecrementRef(Target->ref);
- Target = nullptr;
- if(Buffer)
- DecrementRef(Buffer->ref);
- Buffer = nullptr;
- if(auto *slot = mSlot->Update.exchange(nullptr, std::memory_order_relaxed))
- slot->State = nullptr;
- mSlot->mEffectState = nullptr;
- mSlot->InUse = false;
- }
- ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
- ALCcontext *context)
- {
- EffectSlotType newtype{EffectSlotTypeFromEnum(effectType)};
- if(newtype != Effect.Type)
- {
- EffectStateFactory *factory{getFactoryByType(newtype)};
- if(!factory)
- {
- ERR("Failed to find factory for effect slot type {}",
- int{al::to_underlying(newtype)});
- return AL_INVALID_ENUM;
- }
- al::intrusive_ptr<EffectState> state{factory->create()};
- auto *device = context->mALDevice.get();
- state->mOutTarget = device->Dry.Buffer;
- {
- FPUCtl mixer_mode{};
- state->deviceUpdate(device, Buffer);
- }
- Effect.Type = newtype;
- Effect.Props = effectProps;
- Effect.State = std::move(state);
- }
- else if(newtype != EffectSlotType::None)
- Effect.Props = effectProps;
- EffectId = effectId;
- /* Remove state references from old effect slot property updates. */
- EffectSlotProps *props{context->mFreeEffectSlotProps.load()};
- while(props)
- {
- props->State = nullptr;
- props = props->next.load(std::memory_order_relaxed);
- }
- return AL_NO_ERROR;
- }
- void ALeffectslot::updateProps(ALCcontext *context) const
- {
- /* Get an unused property container, or allocate a new one as needed. */
- EffectSlotProps *props{context->mFreeEffectSlotProps.load(std::memory_order_acquire)};
- if(!props)
- {
- context->allocEffectSlotProps();
- props = context->mFreeEffectSlotProps.load(std::memory_order_acquire);
- }
- EffectSlotProps *next;
- do {
- next = props->next.load(std::memory_order_relaxed);
- } while(!context->mFreeEffectSlotProps.compare_exchange_weak(props, next,
- std::memory_order_acq_rel, std::memory_order_acquire));
- /* Copy in current property values. */
- props->Gain = Gain;
- props->AuxSendAuto = AuxSendAuto;
- props->Target = Target ? Target->mSlot : nullptr;
- props->Type = Effect.Type;
- props->Props = Effect.Props;
- props->State = Effect.State;
- /* Set the new container for updating internal parameters. */
- props = mSlot->Update.exchange(props, std::memory_order_acq_rel);
- if(props)
- {
- /* If there was an unused update container, put it back in the
- * freelist.
- */
- props->State = nullptr;
- AtomicReplaceHead(context->mFreeEffectSlotProps, props);
- }
- }
- void ALeffectslot::SetName(ALCcontext* context, ALuint id, std::string_view name)
- {
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- auto slot = LookupEffectSlot(context, id);
- if(!slot)
- context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", id);
- context->mEffectSlotNames.insert_or_assign(id, name);
- }
- void UpdateAllEffectSlotProps(ALCcontext *context)
- {
- std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
- for(auto &sublist : context->mEffectSlotList)
- {
- uint64_t usemask{~sublist.FreeMask};
- while(usemask)
- {
- const auto idx = static_cast<uint>(al::countr_zero(usemask));
- usemask &= ~(1_u64 << idx);
- auto &slot = (*sublist.EffectSlots)[idx];
- if(std::exchange(slot.mPropsDirty, false))
- slot.updateProps(context);
- }
- }
- }
- EffectSlotSubList::~EffectSlotSubList()
- {
- if(!EffectSlots)
- return;
- uint64_t usemask{~FreeMask};
- while(usemask)
- {
- const int idx{al::countr_zero(usemask)};
- std::destroy_at(al::to_address(EffectSlots->begin() + idx));
- usemask &= ~(1_u64 << idx);
- }
- FreeMask = ~usemask;
- SubListAllocator{}.deallocate(EffectSlots, 1);
- EffectSlots = nullptr;
- }
- #if ALSOFT_EAX
- void ALeffectslot::eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index)
- {
- if(index >= EAX_MAX_FXSLOTS)
- eax_fail("Index out of range.");
- mEaxALContext = &al_context;
- mEaxFXSlotIndex = index;
- eax_fx_slot_set_defaults();
- mEaxEffect = std::make_unique<EaxEffect>();
- if(index == 0) mEaxEffect->init<EaxReverbCommitter>();
- else if(index == 1) mEaxEffect->init<EaxChorusCommitter>();
- else mEaxEffect->init<EaxNullCommitter>();
- }
- void ALeffectslot::eax_commit()
- {
- if(mEaxDf.any())
- {
- auto df = std::bitset<eax_dirty_bit_count>{};
- switch(mEaxVersion)
- {
- case 1:
- case 2:
- case 3:
- eax5_fx_slot_commit(mEax123, df);
- break;
- case 4:
- eax4_fx_slot_commit(df);
- break;
- case 5:
- eax5_fx_slot_commit(mEax5, df);
- break;
- }
- mEaxDf.reset();
- if(df.test(eax_volume_dirty_bit))
- eax_fx_slot_set_volume();
- if(df.test(eax_flags_dirty_bit))
- eax_fx_slot_set_flags();
- }
- if(mEaxEffect->commit(mEaxVersion))
- eax_set_efx_slot_effect(*mEaxEffect);
- }
- [[noreturn]] void ALeffectslot::eax_fail(const char* message)
- {
- throw Exception{message};
- }
- [[noreturn]] void ALeffectslot::eax_fail_unknown_effect_id()
- {
- eax_fail("Unknown effect ID.");
- }
- [[noreturn]] void ALeffectslot::eax_fail_unknown_property_id()
- {
- eax_fail("Unknown property ID.");
- }
- [[noreturn]] void ALeffectslot::eax_fail_unknown_version()
- {
- eax_fail("Unknown version.");
- }
- void ALeffectslot::eax4_fx_slot_ensure_unlocked() const
- {
- if(eax4_fx_slot_is_legacy())
- eax_fail("Locked legacy slot.");
- }
- ALenum ALeffectslot::eax_get_efx_effect_type(const GUID& guid)
- {
- if(guid == EAX_NULL_GUID)
- return AL_EFFECT_NULL;
- if(guid == EAX_AUTOWAH_EFFECT)
- return AL_EFFECT_AUTOWAH;
- if(guid == EAX_CHORUS_EFFECT)
- return AL_EFFECT_CHORUS;
- if(guid == EAX_AGCCOMPRESSOR_EFFECT)
- return AL_EFFECT_COMPRESSOR;
- if(guid == EAX_DISTORTION_EFFECT)
- return AL_EFFECT_DISTORTION;
- if(guid == EAX_REVERB_EFFECT)
- return AL_EFFECT_EAXREVERB;
- if(guid == EAX_ECHO_EFFECT)
- return AL_EFFECT_ECHO;
- if(guid == EAX_EQUALIZER_EFFECT)
- return AL_EFFECT_EQUALIZER;
- if(guid == EAX_FLANGER_EFFECT)
- return AL_EFFECT_FLANGER;
- if(guid == EAX_FREQUENCYSHIFTER_EFFECT)
- return AL_EFFECT_FREQUENCY_SHIFTER;
- if(guid == EAX_PITCHSHIFTER_EFFECT)
- return AL_EFFECT_PITCH_SHIFTER;
- if(guid == EAX_RINGMODULATOR_EFFECT)
- return AL_EFFECT_RING_MODULATOR;
- if(guid == EAX_VOCALMORPHER_EFFECT)
- return AL_EFFECT_VOCAL_MORPHER;
- eax_fail_unknown_effect_id();
- }
- const GUID& ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
- {
- switch(mEaxFXSlotIndex)
- {
- case 0: return EAX_REVERB_EFFECT;
- case 1: return EAX_CHORUS_EFFECT;
- default: return EAX_NULL_GUID;
- }
- }
- long ALeffectslot::eax_get_eax_default_lock() const noexcept
- {
- return eax4_fx_slot_is_legacy() ? EAXFXSLOT_LOCKED : EAXFXSLOT_UNLOCKED;
- }
- void ALeffectslot::eax4_fx_slot_set_defaults(EAX40FXSLOTPROPERTIES& props) noexcept
- {
- props.guidLoadEffect = eax_get_eax_default_effect_guid();
- props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
- props.lLock = eax_get_eax_default_lock();
- props.ulFlags = EAX40FXSLOT_DEFAULTFLAGS;
- }
- void ALeffectslot::eax5_fx_slot_set_defaults(EAX50FXSLOTPROPERTIES& props) noexcept
- {
- props.guidLoadEffect = eax_get_eax_default_effect_guid();
- props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
- props.lLock = EAXFXSLOT_UNLOCKED;
- props.ulFlags = EAX50FXSLOT_DEFAULTFLAGS;
- props.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
- props.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
- }
- void ALeffectslot::eax_fx_slot_set_defaults()
- {
- eax5_fx_slot_set_defaults(mEax123.i);
- eax4_fx_slot_set_defaults(mEax4.i);
- eax5_fx_slot_set_defaults(mEax5.i);
- mEax = mEax5.i;
- mEaxDf.reset();
- }
- void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const EAX40FXSLOTPROPERTIES& props)
- {
- switch(call.get_property_id())
- {
- case EAXFXSLOT_ALLPARAMETERS:
- call.set_value<Exception>(props);
- break;
- case EAXFXSLOT_LOADEFFECT:
- call.set_value<Exception>(props.guidLoadEffect);
- break;
- case EAXFXSLOT_VOLUME:
- call.set_value<Exception>(props.lVolume);
- break;
- case EAXFXSLOT_LOCK:
- call.set_value<Exception>(props.lLock);
- break;
- case EAXFXSLOT_FLAGS:
- call.set_value<Exception>(props.ulFlags);
- break;
- default:
- eax_fail_unknown_property_id();
- }
- }
- void ALeffectslot::eax5_fx_slot_get(const EaxCall& call, const EAX50FXSLOTPROPERTIES& props)
- {
- switch(call.get_property_id())
- {
- case EAXFXSLOT_ALLPARAMETERS:
- call.set_value<Exception>(props);
- break;
- case EAXFXSLOT_LOADEFFECT:
- call.set_value<Exception>(props.guidLoadEffect);
- break;
- case EAXFXSLOT_VOLUME:
- call.set_value<Exception>(props.lVolume);
- break;
- case EAXFXSLOT_LOCK:
- call.set_value<Exception>(props.lLock);
- break;
- case EAXFXSLOT_FLAGS:
- call.set_value<Exception>(props.ulFlags);
- break;
- case EAXFXSLOT_OCCLUSION:
- call.set_value<Exception>(props.lOcclusion);
- break;
- case EAXFXSLOT_OCCLUSIONLFRATIO:
- call.set_value<Exception>(props.flOcclusionLFRatio);
- break;
- default:
- eax_fail_unknown_property_id();
- }
- }
- void ALeffectslot::eax_fx_slot_get(const EaxCall& call) const
- {
- switch(call.get_version())
- {
- case 4: eax4_fx_slot_get(call, mEax4.i); break;
- case 5: eax5_fx_slot_get(call, mEax5.i); break;
- default: eax_fail_unknown_version();
- }
- }
- bool ALeffectslot::eax_get(const EaxCall& call)
- {
- switch(call.get_property_set_id())
- {
- case EaxCallPropertySetId::fx_slot:
- eax_fx_slot_get(call);
- break;
- case EaxCallPropertySetId::fx_slot_effect:
- mEaxEffect->get(call);
- break;
- default:
- eax_fail_unknown_property_id();
- }
- return false;
- }
- void ALeffectslot::eax_fx_slot_load_effect(int version, ALenum altype)
- {
- if(!IsValidEffectType(altype))
- altype = AL_EFFECT_NULL;
- mEaxEffect->set_defaults(version, altype);
- }
- void ALeffectslot::eax_fx_slot_set_volume()
- {
- const auto volume = std::clamp(mEax.lVolume, EAXFXSLOT_MINVOLUME, EAXFXSLOT_MAXVOLUME);
- const auto gain = level_mb_to_gain(static_cast<float>(volume));
- eax_set_efx_slot_gain(gain);
- }
- void ALeffectslot::eax_fx_slot_set_environment_flag()
- {
- eax_set_efx_slot_send_auto((mEax.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0u);
- }
- void ALeffectslot::eax_fx_slot_set_flags()
- {
- eax_fx_slot_set_environment_flag();
- }
- void ALeffectslot::eax4_fx_slot_set_all(const EaxCall& call)
- {
- eax4_fx_slot_ensure_unlocked();
- const auto& src = call.get_value<Exception, const EAX40FXSLOTPROPERTIES>();
- Eax4AllValidator{}(src);
- auto& dst = mEax4.i;
- mEaxDf.set(eax_load_effect_dirty_bit); // Always reset the effect.
- if(dst.lVolume != src.lVolume) mEaxDf.set(eax_volume_dirty_bit);
- if(dst.lLock != src.lLock) mEaxDf.set(eax_lock_dirty_bit);
- if(dst.ulFlags != src.ulFlags) mEaxDf.set(eax_flags_dirty_bit);
- dst = src;
- }
- void ALeffectslot::eax5_fx_slot_set_all(const EaxCall& call)
- {
- const auto& src = call.get_value<Exception, const EAX50FXSLOTPROPERTIES>();
- Eax5AllValidator{}(src);
- auto& dst = mEax5.i;
- mEaxDf.set(eax_load_effect_dirty_bit); // Always reset the effect.
- if(dst.lVolume != src.lVolume) mEaxDf.set(eax_volume_dirty_bit);
- if(dst.lLock != src.lLock) mEaxDf.set(eax_lock_dirty_bit);
- if(dst.ulFlags != src.ulFlags) mEaxDf.set(eax_flags_dirty_bit);
- if(dst.lOcclusion != src.lOcclusion) mEaxDf.set(eax_flags_dirty_bit);
- if(dst.flOcclusionLFRatio != src.flOcclusionLFRatio) mEaxDf.set(eax_flags_dirty_bit);
- dst = src;
- }
- bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept
- {
- static constexpr auto dirty_bits = std::bitset<eax_dirty_bit_count>{
- (1u << eax_occlusion_dirty_bit)
- | (1u << eax_occlusion_lf_ratio_dirty_bit)
- | (1u << eax_flags_dirty_bit)
- };
- return (mEaxDf & dirty_bits).any();
- }
- // Returns `true` if all sources should be updated, or `false` otherwise.
- bool ALeffectslot::eax4_fx_slot_set(const EaxCall& call)
- {
- auto& dst = mEax4.i;
- switch(call.get_property_id())
- {
- case EAXFXSLOT_NONE:
- break;
- case EAXFXSLOT_ALLPARAMETERS:
- eax4_fx_slot_set_all(call);
- if(mEaxDf.test(eax_load_effect_dirty_bit))
- eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
- break;
- case EAXFXSLOT_LOADEFFECT:
- eax4_fx_slot_ensure_unlocked();
- eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call,
- dst.guidLoadEffect, mEaxDf);
- if(mEaxDf.test(eax_load_effect_dirty_bit))
- eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
- break;
- case EAXFXSLOT_VOLUME:
- eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, mEaxDf);
- break;
- case EAXFXSLOT_LOCK:
- eax4_fx_slot_ensure_unlocked();
- eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, mEaxDf);
- break;
- case EAXFXSLOT_FLAGS:
- eax_fx_slot_set<Eax4FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, mEaxDf);
- break;
- default:
- eax_fail_unknown_property_id();
- }
- return eax_fx_slot_should_update_sources();
- }
- // Returns `true` if all sources should be updated, or `false` otherwise.
- bool ALeffectslot::eax5_fx_slot_set(const EaxCall& call)
- {
- auto& dst = mEax5.i;
- switch(call.get_property_id())
- {
- case EAXFXSLOT_NONE:
- break;
- case EAXFXSLOT_ALLPARAMETERS:
- eax5_fx_slot_set_all(call);
- if(mEaxDf.test(eax_load_effect_dirty_bit))
- eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
- break;
- case EAXFXSLOT_LOADEFFECT:
- eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call,
- dst.guidLoadEffect, mEaxDf);
- if(mEaxDf.test(eax_load_effect_dirty_bit))
- eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
- break;
- case EAXFXSLOT_VOLUME:
- eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, mEaxDf);
- break;
- case EAXFXSLOT_LOCK:
- eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, mEaxDf);
- break;
- case EAXFXSLOT_FLAGS:
- eax_fx_slot_set<Eax5FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, mEaxDf);
- break;
- case EAXFXSLOT_OCCLUSION:
- eax_fx_slot_set<Eax5OcclusionValidator, eax_occlusion_dirty_bit>(call, dst.lOcclusion,
- mEaxDf);
- break;
- case EAXFXSLOT_OCCLUSIONLFRATIO:
- eax_fx_slot_set<Eax5OcclusionLfRatioValidator, eax_occlusion_lf_ratio_dirty_bit>(call,
- dst.flOcclusionLFRatio, mEaxDf);
- break;
- default:
- eax_fail_unknown_property_id();
- }
- return eax_fx_slot_should_update_sources();
- }
- // Returns `true` if all sources should be updated, or `false` otherwise.
- bool ALeffectslot::eax_fx_slot_set(const EaxCall& call)
- {
- switch(call.get_version())
- {
- case 4: return eax4_fx_slot_set(call);
- case 5: return eax5_fx_slot_set(call);
- default: eax_fail_unknown_version();
- }
- }
- // Returns `true` if all sources should be updated, or `false` otherwise.
- bool ALeffectslot::eax_set(const EaxCall& call)
- {
- bool ret{false};
- switch(call.get_property_set_id())
- {
- case EaxCallPropertySetId::fx_slot: ret = eax_fx_slot_set(call); break;
- case EaxCallPropertySetId::fx_slot_effect: mEaxEffect->set(call); break;
- default: eax_fail_unknown_property_id();
- }
- const auto version = call.get_version();
- if(mEaxVersion != version)
- mEaxDf.set();
- mEaxVersion = version;
- return ret;
- }
- void ALeffectslot::eax4_fx_slot_commit(std::bitset<eax_dirty_bit_count>& dst_df)
- {
- eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(mEax4, dst_df, &EAX40FXSLOTPROPERTIES::guidLoadEffect);
- eax_fx_slot_commit_property<eax_volume_dirty_bit>(mEax4, dst_df, &EAX40FXSLOTPROPERTIES::lVolume);
- eax_fx_slot_commit_property<eax_lock_dirty_bit>(mEax4, dst_df, &EAX40FXSLOTPROPERTIES::lLock);
- eax_fx_slot_commit_property<eax_flags_dirty_bit>(mEax4, dst_df, &EAX40FXSLOTPROPERTIES::ulFlags);
- auto& dst_i = mEax;
- if(dst_i.lOcclusion != EAXFXSLOT_DEFAULTOCCLUSION)
- {
- dst_df.set(eax_occlusion_dirty_bit);
- dst_i.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
- }
- if(dst_i.flOcclusionLFRatio != EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO)
- {
- dst_df.set(eax_occlusion_lf_ratio_dirty_bit);
- dst_i.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
- }
- }
- void ALeffectslot::eax5_fx_slot_commit(Eax5State& state, std::bitset<eax_dirty_bit_count>& dst_df)
- {
- eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::guidLoadEffect);
- eax_fx_slot_commit_property<eax_volume_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lVolume);
- eax_fx_slot_commit_property<eax_lock_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lLock);
- eax_fx_slot_commit_property<eax_flags_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::ulFlags);
- eax_fx_slot_commit_property<eax_occlusion_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lOcclusion);
- eax_fx_slot_commit_property<eax_occlusion_lf_ratio_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::flOcclusionLFRatio);
- }
- void ALeffectslot::eax_set_efx_slot_effect(EaxEffect &effect)
- {
- #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
- const auto error = initEffect(0, effect.al_effect_type_, effect.al_effect_props_,
- mEaxALContext);
- if(error != AL_NO_ERROR)
- {
- ERR(EAX_PREFIX "Failed to initialize an effect.");
- return;
- }
- if(mState == SlotState::Initial)
- {
- mPropsDirty = false;
- updateProps(mEaxALContext);
- auto effect_slot_ptr = this;
- AddActiveEffectSlots({&effect_slot_ptr, 1}, mEaxALContext);
- mState = SlotState::Playing;
- return;
- }
- mPropsDirty = true;
- #undef EAX_PREFIX
- }
- void ALeffectslot::eax_set_efx_slot_send_auto(bool is_send_auto)
- {
- if(AuxSendAuto == is_send_auto)
- return;
- AuxSendAuto = is_send_auto;
- mPropsDirty = true;
- }
- void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain)
- {
- #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
- if(gain == Gain)
- return;
- if(gain < 0.0f || gain > 1.0f)
- ERR(EAX_PREFIX "Slot gain out of range ({:f})", gain);
- Gain = std::clamp(gain, 0.0f, 1.0f);
- mPropsDirty = true;
- #undef EAX_PREFIX
- }
- void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot)
- {
- eax_delete_al_effect_slot(*effect_slot->mEaxALContext, *effect_slot);
- }
- EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context)
- {
- #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
- std::lock_guard<std::mutex> slotlock{context.mEffectSlotLock};
- auto& device = *context.mALDevice;
- if(context.mNumEffectSlots == device.AuxiliaryEffectSlotMax)
- {
- ERR(EAX_PREFIX "Out of memory.");
- return nullptr;
- }
- if(!EnsureEffectSlots(&context, 1))
- {
- ERR(EAX_PREFIX "Failed to ensure.");
- return nullptr;
- }
- return EaxAlEffectSlotUPtr{AllocEffectSlot(&context)};
- #undef EAX_PREFIX
- }
- void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot)
- {
- #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
- std::lock_guard<std::mutex> slotlock{context.mEffectSlotLock};
- if(effect_slot.ref.load(std::memory_order_relaxed) != 0)
- {
- ERR(EAX_PREFIX "Deleting in-use effect slot {}.", effect_slot.id);
- return;
- }
- auto effect_slot_ptr = &effect_slot;
- RemoveActiveEffectSlots({&effect_slot_ptr, 1}, &context);
- FreeEffectSlot(&context, &effect_slot);
- #undef EAX_PREFIX
- }
- #endif // ALSOFT_EAX
|