diff options
191 files changed, 6400 insertions, 6468 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d572908..2a3900ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -329,7 +329,7 @@ else() option(ALSOFT_STATIC_STDCXX "Static link libstdc++" OFF) if(ALSOFT_STATIC_STDCXX) set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state") + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-static-libstdc++") check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBSTDCXX_SWITCH) set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES}) unset(OLD_REQUIRED_LIBRARIES) @@ -337,7 +337,7 @@ else() if(NOT HAVE_STATIC_LIBSTDCXX_SWITCH) message(FATAL_ERROR "Cannot static link libstdc++") endif() - set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state") + set(LINKER_FLAGS ${LINKER_FLAGS} "-static-libstdc++") endif() if(WIN32) @@ -565,8 +565,6 @@ if(HAVE_INTRIN_H) }" HAVE_CPUID_INTRINSIC) endif() -check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) -check_symbol_exists(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) check_symbol_exists(proc_pidpath libproc.h HAVE_PROC_PIDPATH) if(NOT WIN32) @@ -622,7 +620,6 @@ set(COMMON_OBJS common/alcomplex.h common/alfstream.cpp common/alfstream.h - common/almalloc.cpp common/almalloc.h common/alnumbers.h common/alnumeric.h @@ -638,6 +635,7 @@ set(COMMON_OBJS common/comptr.h common/dynload.cpp common/dynload.h + common/flexarray.h common/intrusive_ptr.h common/opthelpers.h common/pffft.cpp @@ -1351,7 +1349,8 @@ configure_file( add_library(alcommon STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS}) -target_include_directories(alcommon PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include) +target_include_directories(alcommon PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include + PUBLIC ${OpenAL_SOURCE_DIR}/common) target_compile_definitions(alcommon PRIVATE ${CPP_DEFS}) target_compile_options(alcommon PRIVATE ${C_FLAGS}) set_target_properties(alcommon PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE) diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index fb646389..673ca4c9 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -53,33 +53,35 @@ namespace { +using SubListAllocator = typename al::allocator<std::array<ALeffectslot,64>>; + struct FactoryItem { EffectSlotType Type; - EffectStateFactory* (&GetFactory)(void); + EffectStateFactory* (&GetFactory)(); }; -constexpr FactoryItem FactoryList[] = { - { EffectSlotType::None, NullStateFactory_getFactory }, - { EffectSlotType::EAXReverb, ReverbStateFactory_getFactory }, - { EffectSlotType::Reverb, StdReverbStateFactory_getFactory }, - { EffectSlotType::Autowah, AutowahStateFactory_getFactory }, - { EffectSlotType::Chorus, ChorusStateFactory_getFactory }, - { EffectSlotType::Compressor, CompressorStateFactory_getFactory }, - { EffectSlotType::Distortion, DistortionStateFactory_getFactory }, - { EffectSlotType::Echo, EchoStateFactory_getFactory }, - { EffectSlotType::Equalizer, EqualizerStateFactory_getFactory }, - { EffectSlotType::Flanger, FlangerStateFactory_getFactory }, - { EffectSlotType::FrequencyShifter, FshifterStateFactory_getFactory }, - { EffectSlotType::RingModulator, ModulatorStateFactory_getFactory }, - { EffectSlotType::PitchShifter, PshifterStateFactory_getFactory }, - { EffectSlotType::VocalMorpher, VmorpherStateFactory_getFactory }, - { EffectSlotType::DedicatedDialog, DedicatedStateFactory_getFactory }, - { EffectSlotType::DedicatedLFE, DedicatedStateFactory_getFactory }, - { EffectSlotType::Convolution, ConvolutionStateFactory_getFactory }, +constexpr std::array FactoryList{ + FactoryItem{EffectSlotType::None, NullStateFactory_getFactory}, + FactoryItem{EffectSlotType::EAXReverb, ReverbStateFactory_getFactory}, + FactoryItem{EffectSlotType::Reverb, StdReverbStateFactory_getFactory}, + FactoryItem{EffectSlotType::Autowah, AutowahStateFactory_getFactory}, + FactoryItem{EffectSlotType::Chorus, ChorusStateFactory_getFactory}, + FactoryItem{EffectSlotType::Compressor, CompressorStateFactory_getFactory}, + FactoryItem{EffectSlotType::Distortion, DistortionStateFactory_getFactory}, + FactoryItem{EffectSlotType::Echo, EchoStateFactory_getFactory}, + FactoryItem{EffectSlotType::Equalizer, EqualizerStateFactory_getFactory}, + FactoryItem{EffectSlotType::Flanger, FlangerStateFactory_getFactory}, + FactoryItem{EffectSlotType::FrequencyShifter, FshifterStateFactory_getFactory}, + FactoryItem{EffectSlotType::RingModulator, ModulatorStateFactory_getFactory}, + FactoryItem{EffectSlotType::PitchShifter, PshifterStateFactory_getFactory}, + FactoryItem{EffectSlotType::VocalMorpher, VmorpherStateFactory_getFactory}, + FactoryItem{EffectSlotType::DedicatedDialog, DedicatedDialogStateFactory_getFactory}, + FactoryItem{EffectSlotType::DedicatedLFE, DedicatedLfeStateFactory_getFactory}, + FactoryItem{EffectSlotType::Convolution, ConvolutionStateFactory_getFactory}, }; EffectStateFactory *getFactoryByType(EffectSlotType type) { - auto iter = std::find_if(std::begin(FactoryList), std::end(FactoryList), + auto iter = std::find_if(FactoryList.begin(), FactoryList.end(), [type](const FactoryItem &item) noexcept -> bool { return item.Type == type; }); return (iter != std::end(FactoryList)) ? iter->GetFactory() : nullptr; @@ -96,7 +98,7 @@ inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept EffectSlotSubList &sublist{context->mEffectSlotList[lidx]}; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.EffectSlots + slidx; + return al::to_address(sublist.EffectSlots->begin() + slidx); } inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept @@ -109,7 +111,7 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept EffectSubList &sublist = device->EffectList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Effects + slidx; + return al::to_address(sublist.Effects->begin() + slidx); } inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept @@ -122,7 +124,7 @@ inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept BufferSubList &sublist = device->BufferList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Buffers + slidx; + return al::to_address(sublist.Buffers->begin() + slidx); } @@ -155,19 +157,17 @@ void AddActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *co */ if(newcount < newarray->size()) UNLIKELY { - curarray = newarray; + std::unique_ptr<EffectSlotArray> oldarray{newarray}; newarray = EffectSlot::CreatePtrArray(newcount); - std::copy_n(curarray->begin(), newcount, newarray->begin()); - delete curarray; - curarray = nullptr; + std::copy_n(oldarray->begin(), newcount, newarray->begin()); } std::uninitialized_fill_n(newarray->end(), newcount, nullptr); - curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel); - context->mDevice->waitForMix(); + std::unique_ptr<EffectSlotArray> oldarray{context->mActiveAuxSlots.exchange(newarray, + std::memory_order_acq_rel)}; + std::ignore = context->mDevice->waitForMix(); - std::destroy_n(curarray->end(), curarray->size()); - delete curarray; + std::destroy_n(oldarray->end(), oldarray->size()); } void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *context) @@ -193,20 +193,17 @@ void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext auto newsize = static_cast<size_t>(std::distance(newarray->begin(), new_end)); if(newsize != newarray->size()) LIKELY { - curarray = newarray; + std::unique_ptr<EffectSlotArray> oldarray{newarray}; newarray = EffectSlot::CreatePtrArray(newsize); - std::copy_n(curarray->begin(), newsize, newarray->begin()); - - delete curarray; - curarray = nullptr; + std::copy_n(oldarray->begin(), newsize, newarray->begin()); } std::uninitialized_fill_n(newarray->end(), newsize, nullptr); - curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel); - context->mDevice->waitForMix(); + std::unique_ptr<EffectSlotArray> oldarray{context->mActiveAuxSlots.exchange(newarray, + std::memory_order_acq_rel)}; + std::ignore = context->mDevice->waitForMix(); std::destroy_n(curarray->end(), curarray->size()); - delete curarray; } @@ -243,22 +240,21 @@ bool EnsureEffectSlots(ALCcontext *context, size_t needed) [](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; - - context->mEffectSlotList.emplace_back(); - auto sublist = context->mEffectSlotList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->EffectSlots = static_cast<ALeffectslot*>( - al_calloc(alignof(ALeffectslot), sizeof(ALeffectslot)*64)); - if(!sublist->EffectSlots) UNLIKELY + try { + while(needed > count) { - context->mEffectSlotList.pop_back(); - return false; + 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 += 64; } - count += 64; + } + catch(...) { + return false; } return true; } @@ -272,7 +268,8 @@ ALeffectslot *AllocEffectSlot(ALCcontext *context) auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALeffectslot *slot{al::construct_at(sublist->EffectSlots + slidx, context)}; + ALeffectslot *slot{al::construct_at(al::to_address(sublist->EffectSlots->begin() + slidx), + context)}; aluInitEffectPanning(slot->mSlot, context); /* Add 1 to avoid ID 0. */ @@ -370,7 +367,7 @@ FORCE_ALIGN void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *cont context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[0]); return; } - if(ReadRef(slot->ref) != 0) UNLIKELY + if(slot->ref.load(std::memory_order_relaxed) != 0) UNLIKELY { context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u", effectslots[0]); @@ -390,7 +387,7 @@ FORCE_ALIGN void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *cont context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[i]); return; } - if(ReadRef(slot->ref) != 0) UNLIKELY + if(slot->ref.load(std::memory_order_relaxed) != 0) UNLIKELY { context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u", effectslots[i]); @@ -880,12 +877,8 @@ ALeffectslot::~ALeffectslot() DecrementRef(Buffer->ref); Buffer = nullptr; - if(EffectSlotProps *props{mSlot->Update.exchange(nullptr)}) - { - TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", - decltype(std::declval<void*>()){props}); - delete props; - } + if(auto *slot = mSlot->Update.exchange(nullptr, std::memory_order_relaxed)) + slot->State = nullptr; mSlot->mEffectState = nullptr; mSlot->InUse = false; @@ -923,7 +916,7 @@ ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const Effect EffectId = effectId; /* Remove state references from old effect slot property updates. */ - EffectSlotProps *props{context->mFreeEffectslotProps.load()}; + EffectSlotProps *props{context->mFreeEffectSlotProps.load()}; while(props) { props->State = nullptr; @@ -936,17 +929,17 @@ ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const Effect void ALeffectslot::updateProps(ALCcontext *context) { /* Get an unused property container, or allocate a new one as needed. */ - EffectSlotProps *props{context->mFreeEffectslotProps.load(std::memory_order_relaxed)}; + EffectSlotProps *props{context->mFreeEffectSlotProps.load(std::memory_order_acquire)}; if(!props) - props = new EffectSlotProps{}; - else { - EffectSlotProps *next; - do { - next = props->next.load(std::memory_order_relaxed); - } while(context->mFreeEffectslotProps.compare_exchange_weak(props, next, - std::memory_order_seq_cst, std::memory_order_acquire) == 0); + 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) == false); /* Copy in current property values. */ props->Gain = Gain; @@ -965,7 +958,7 @@ void ALeffectslot::updateProps(ALCcontext *context) * freelist. */ props->State = nullptr; - AtomicReplaceHead(context->mFreeEffectslotProps, props); + AtomicReplaceHead(context->mFreeEffectSlotProps, props); } } @@ -988,12 +981,12 @@ void UpdateAllEffectSlotProps(ALCcontext *context) uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; + const auto idx = static_cast<uint>(al::countr_zero(usemask)); usemask &= ~(1_u64 << idx); - ALeffectslot *slot{sublist.EffectSlots + idx}; + auto &slot = (*sublist.EffectSlots)[idx]; - if(slot->mState != SlotState::Stopped && std::exchange(slot->mPropsDirty, false)) - slot->updateProps(context); + if(slot.mState != SlotState::Stopped && std::exchange(slot.mPropsDirty, false)) + slot.updateProps(context); } } } @@ -1007,11 +1000,11 @@ EffectSlotSubList::~EffectSlotSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(EffectSlots+idx); + std::destroy_at(al::to_address(EffectSlots->begin() + idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(EffectSlots); + SubListAllocator{}.deallocate(EffectSlots, 1); EffectSlots = nullptr; } @@ -1530,7 +1523,8 @@ void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot) std::lock_guard<std::mutex> effect_slot_lock{context.mEffectSlotLock}; - if(ReadRef(effect_slot.ref) != 0) { + if(effect_slot.ref.load(std::memory_order_relaxed) != 0) + { ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id); return; } diff --git a/al/auxeffectslot.h b/al/auxeffectslot.h index 1ad0ffc4..6946591e 100644 --- a/al/auxeffectslot.h +++ b/al/auxeffectslot.h @@ -3,6 +3,7 @@ #include <atomic> #include <cstddef> +#include <cstdint> #include <string_view> #include "AL/al.h" @@ -12,6 +13,7 @@ #include "alc/device.h" #include "alc/effects/base.h" #include "almalloc.h" +#include "alnumeric.h" #include "atomic.h" #include "core/effectslot.h" #include "intrusive_ptr.h" @@ -63,7 +65,7 @@ struct ALeffectslot { SlotState mState{SlotState::Initial}; - RefCount ref{0u}; + std::atomic<ALuint> ref{0u}; EffectSlot *mSlot{nullptr}; @@ -81,20 +83,17 @@ struct ALeffectslot { static void SetName(ALCcontext *context, ALuint id, std::string_view name); - /* This can be new'd for the context's default effect slot. */ - DEF_NEWDEL(ALeffectslot) - #ifdef ALSOFT_EAX public: void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index); - EaxFxSlotIndexValue eax_get_index() const noexcept { return eax_fx_slot_index_; } - const EAX50FXSLOTPROPERTIES& eax_get_eax_fx_slot() const noexcept + [[nodiscard]] auto eax_get_index() const noexcept -> EaxFxSlotIndexValue { return eax_fx_slot_index_; } + [[nodiscard]] auto eax_get_eax_fx_slot() const noexcept -> const EAX50FXSLOTPROPERTIES& { return eax_; } // Returns `true` if all sources should be updated, or `false` otherwise. - bool eax_dispatch(const EaxCall& call) + [[nodiscard]] auto eax_dispatch(const EaxCall& call) -> bool { return call.is_get() ? eax_get(call) : eax_set(call); } void eax_commit(); @@ -282,14 +281,14 @@ private: dst = src; } - constexpr bool eax4_fx_slot_is_legacy() const noexcept + [[nodiscard]] constexpr auto eax4_fx_slot_is_legacy() const noexcept -> bool { return eax_fx_slot_index_ < 2; } void eax4_fx_slot_ensure_unlocked() const; - static ALenum eax_get_efx_effect_type(const GUID& guid); - const GUID& eax_get_eax_default_effect_guid() const noexcept; - long eax_get_eax_default_lock() const noexcept; + [[nodiscard]] static auto eax_get_efx_effect_type(const GUID& guid) -> ALenum; + [[nodiscard]] auto eax_get_eax_default_effect_guid() const noexcept -> const GUID&; + [[nodiscard]] auto eax_get_eax_default_lock() const noexcept -> long; void eax4_fx_slot_set_defaults(Eax4Props& props) noexcept; void eax5_fx_slot_set_defaults(Eax5Props& props) noexcept; @@ -312,7 +311,7 @@ private: void eax4_fx_slot_set_all(const EaxCall& call); void eax5_fx_slot_set_all(const EaxCall& call); - bool eax_fx_slot_should_update_sources() const noexcept; + [[nodiscard]] auto eax_fx_slot_should_update_sources() const noexcept -> bool; // Returns `true` if all sources should be updated, or `false` otherwise. bool eax4_fx_slot_set(const EaxCall& call); @@ -370,4 +369,20 @@ EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context); void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot); #endif // ALSOFT_EAX +struct EffectSlotSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner<std::array<ALeffectslot,64>*> EffectSlots{nullptr}; + + EffectSlotSubList() noexcept = default; + EffectSlotSubList(const EffectSlotSubList&) = delete; + EffectSlotSubList(EffectSlotSubList&& rhs) noexcept + : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots} + { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; } + ~EffectSlotSubList(); + + EffectSlotSubList& operator=(const EffectSlotSubList&) = delete; + EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; } +}; + #endif diff --git a/al/buffer.cpp b/al/buffer.cpp index 7d043036..b7ed5b32 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -68,6 +68,8 @@ namespace { +using SubListAllocator = typename al::allocator<std::array<ALbuffer,64>>; + std::optional<AmbiLayout> AmbiLayoutFromEnum(ALenum layout) { switch(layout) @@ -178,21 +180,21 @@ bool EnsureBuffers(ALCdevice *device, size_t needed) [](size_t cur, const BufferSubList &sublist) noexcept -> size_t { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })}; - while(needed > count) - { - if(device->BufferList.size() >= 1<<25) UNLIKELY - return false; - - device->BufferList.emplace_back(); - auto sublist = device->BufferList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Buffers = static_cast<ALbuffer*>(al_calloc(alignof(ALbuffer), sizeof(ALbuffer)*64)); - if(!sublist->Buffers) UNLIKELY + try { + while(needed > count) { - device->BufferList.pop_back(); - return false; + if(device->BufferList.size() >= 1<<25) UNLIKELY + return false; + + BufferSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Buffers = SubListAllocator{}.allocate(1); + device->BufferList.emplace_back(std::move(sublist)); + count += 64; } - count += 64; + } + catch(...) { + return false; } return true; } @@ -206,7 +208,7 @@ ALbuffer *AllocBuffer(ALCdevice *device) auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALbuffer *buffer{al::construct_at(sublist->Buffers + slidx)}; + ALbuffer *buffer{al::construct_at(al::to_address(sublist->Buffers->begin() + slidx))}; /* Add 1 to avoid buffer ID 0. */ buffer->id = ((lidx<<6) | slidx) + 1; @@ -243,7 +245,7 @@ inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) BufferSubList &sublist = device->BufferList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Buffers + slidx; + return al::to_address(sublist.Buffers->begin() + slidx); } @@ -286,7 +288,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, const FmtChannels DstChannels, const FmtType DstType, const std::byte *SrcData, ALbitfieldSOFT access) { - if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY + if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) UNLIKELY return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u", ALBuf->id); @@ -393,7 +395,7 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, const FmtChannels DstChannels, const FmtType DstType, ALBUFFERCALLBACKTYPESOFT callback, void *userptr) { - if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY + if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) UNLIKELY return context->setError(AL_INVALID_OPERATION, "Modifying callback for in-use buffer %u", ALBuf->id); @@ -402,6 +404,10 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, const ALuint unpackalign{ALBuf->UnpackAlign}; const ALuint align{SanitizeAlignment(DstType, unpackalign)}; + if(align < 1) UNLIKELY + return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples", + unpackalign, NameFromFormat(DstType)); + const ALuint BlockSize{ChannelsFromFmt(DstChannels, ambiorder) * ((DstType == FmtIMA4) ? (align-1)/2 + 4 : (DstType == FmtMSADPCM) ? (align-2)/2 + 7 : @@ -445,7 +451,7 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, const FmtChannels DstChannels, const FmtType DstType, std::byte *sdata, const ALuint sdatalen) { - if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY + if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) UNLIKELY return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u", ALBuf->id); @@ -711,7 +717,7 @@ FORCE_ALIGN void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", bid); return false; } - if(ReadRef(ALBuf->ref) != 0) UNLIKELY + if(ALBuf->ref.load(std::memory_order_relaxed) != 0) UNLIKELY { context->setError(AL_INVALID_OPERATION, "Deleting in-use buffer %u", bid); return false; @@ -826,7 +832,8 @@ FORCE_ALIGN void* AL_APIENTRY alMapBufferDirectSOFT(ALCcontext *context, ALuint else { ALbitfieldSOFT unavailable = (albuf->Access^access) & access; - if(ReadRef(albuf->ref) != 0 && !(access&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY + if(albuf->ref.load(std::memory_order_relaxed) != 0 + && !(access&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY context->setError(AL_INVALID_OPERATION, "Mapping in-use buffer %u without persistent mapping", buffer); else if(albuf->MappedAccess != 0) UNLIKELY @@ -1042,7 +1049,7 @@ FORCE_ALIGN void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, break; case AL_AMBISONIC_LAYOUT_SOFT: - if(ReadRef(albuf->ref) != 0) UNLIKELY + if(albuf->ref.load(std::memory_order_relaxed) != 0) UNLIKELY context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic layout", buffer); else if(const auto layout = AmbiLayoutFromEnum(value)) @@ -1052,7 +1059,7 @@ FORCE_ALIGN void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, break; case AL_AMBISONIC_SCALING_SOFT: - if(ReadRef(albuf->ref) != 0) UNLIKELY + if(albuf->ref.load(std::memory_order_relaxed) != 0) UNLIKELY context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic scaling", buffer); else if(const auto scaling = AmbiScalingFromEnum(value)) @@ -1116,7 +1123,7 @@ FORCE_ALIGN void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer else switch(param) { case AL_LOOP_POINTS_SOFT: - if(ReadRef(albuf->ref) != 0) UNLIKELY + if(albuf->ref.load(std::memory_order_relaxed) != 0) UNLIKELY context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's loop points", buffer); else if(values[0] < 0 || values[0] >= values[1] @@ -1366,7 +1373,7 @@ FORCE_ALIGN void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuin else switch(param) { case AL_BUFFER_CALLBACK_FUNCTION_SOFT: - *value = al::bit_cast<void*>(albuf->mCallback); + *value = reinterpret_cast<void*>(albuf->mCallback); break; case AL_BUFFER_CALLBACK_USER_PARAM_SOFT: *value = albuf->mUserData; @@ -1480,11 +1487,11 @@ BufferSubList::~BufferSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(Buffers+idx); + std::destroy_at(al::to_address(Buffers->begin() + idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(Buffers); + SubListAllocator{}.deallocate(Buffers, 1); Buffers = nullptr; } diff --git a/al/buffer.h b/al/buffer.h index f936cf98..b9e59d68 100644 --- a/al/buffer.h +++ b/al/buffer.h @@ -3,12 +3,14 @@ #include <atomic> #include <cstddef> +#include <cstdint> #include <string_view> #include "AL/al.h" #include "alc/inprogext.h" #include "almalloc.h" +#include "alnumeric.h" #include "atomic.h" #include "core/buffer_storage.h" #include "vector.h" @@ -43,14 +45,14 @@ struct ALbuffer : public BufferStorage { ALuint mLoopEnd{0u}; /* Number of times buffer was attached to a source (deletion can only occur when 0) */ - RefCount ref{0u}; + std::atomic<ALuint> ref{0u}; /* Self ID */ ALuint id{0}; static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC #ifdef ALSOFT_EAX EaxStorage eax_x_ram_mode{EaxStorage::Automatic}; @@ -58,4 +60,19 @@ struct ALbuffer : public BufferStorage { #endif // ALSOFT_EAX }; +struct BufferSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner<std::array<ALbuffer,64>*> Buffers{nullptr}; + + BufferSubList() noexcept = default; + BufferSubList(const BufferSubList&) = delete; + BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers} + { rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; } + ~BufferSubList(); + + BufferSubList& operator=(const BufferSubList&) = delete; + BufferSubList& operator=(BufferSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; } +}; + #endif diff --git a/al/debug.cpp b/al/debug.cpp index b76ec9af..cd79c148 100644 --- a/al/debug.cpp +++ b/al/debug.cpp @@ -4,10 +4,10 @@ #include <algorithm> #include <array> +#include <cstddef> #include <cstring> #include <mutex> #include <optional> -#include <stddef.h> #include <stdexcept> #include <string> #include <utility> diff --git a/al/direct_defs.h b/al/direct_defs.h index 7526b611..a1999668 100644 --- a/al/direct_defs.h +++ b/al/direct_defs.h @@ -12,7 +12,7 @@ constexpr void DefaultVal() noexcept { } } // namespace detail_ #define DECL_FUNC(R, Name) \ -R AL_APIENTRY Name(void) noexcept \ +auto AL_APIENTRY Name() noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -20,7 +20,7 @@ R AL_APIENTRY Name(void) noexcept \ } #define DECL_FUNC1(R, Name, T1) \ -R AL_APIENTRY Name(T1 a) noexcept \ +auto AL_APIENTRY Name(T1 a) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -28,7 +28,7 @@ R AL_APIENTRY Name(T1 a) noexcept \ } #define DECL_FUNC2(R, Name, T1, T2) \ -R AL_APIENTRY Name(T1 a, T2 b) noexcept \ +auto AL_APIENTRY Name(T1 a, T2 b) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -36,7 +36,7 @@ R AL_APIENTRY Name(T1 a, T2 b) noexcept \ } #define DECL_FUNC3(R, Name, T1, T2, T3) \ -R AL_APIENTRY Name(T1 a, T2 b, T3 c) noexcept \ +auto AL_APIENTRY Name(T1 a, T2 b, T3 c) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -44,7 +44,7 @@ R AL_APIENTRY Name(T1 a, T2 b, T3 c) noexcept \ } #define DECL_FUNC4(R, Name, T1, T2, T3, T4) \ -R AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d) noexcept \ +auto AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -52,7 +52,7 @@ R AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d) noexcept \ } #define DECL_FUNC5(R, Name, T1, T2, T3, T4, T5) \ -R AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \ +auto AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -61,7 +61,7 @@ R AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \ #define DECL_FUNCEXT(R, Name,Ext) \ -R AL_APIENTRY Name##Ext(void) noexcept \ +auto AL_APIENTRY Name##Ext() noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -69,7 +69,7 @@ R AL_APIENTRY Name##Ext(void) noexcept \ } #define DECL_FUNCEXT1(R, Name,Ext, T1) \ -R AL_APIENTRY Name##Ext(T1 a) noexcept \ +auto AL_APIENTRY Name##Ext(T1 a) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -77,7 +77,7 @@ R AL_APIENTRY Name##Ext(T1 a) noexcept \ } #define DECL_FUNCEXT2(R, Name,Ext, T1, T2) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b) noexcept \ +auto AL_APIENTRY Name##Ext(T1 a, T2 b) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -85,7 +85,7 @@ R AL_APIENTRY Name##Ext(T1 a, T2 b) noexcept \ } #define DECL_FUNCEXT3(R, Name,Ext, T1, T2, T3) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c) noexcept \ +auto AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -93,7 +93,7 @@ R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c) noexcept \ } #define DECL_FUNCEXT4(R, Name,Ext, T1, T2, T3, T4) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d) noexcept \ +auto AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -101,7 +101,7 @@ R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d) noexcept \ } #define DECL_FUNCEXT5(R, Name,Ext, T1, T2, T3, T4, T5) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \ +auto AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -109,7 +109,7 @@ R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \ } #define DECL_FUNCEXT6(R, Name,Ext, T1, T2, T3, T4, T5, T6) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) noexcept \ +auto AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ @@ -117,7 +117,7 @@ R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) noexcept \ } #define DECL_FUNCEXT8(R, Name,Ext, T1, T2, T3, T4, T5, T6, T7, T8) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h) noexcept \ +auto AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ diff --git a/al/eax/api.h b/al/eax/api.h index 18d93ef8..0b019f11 100644 --- a/al/eax/api.h +++ b/al/eax/api.h @@ -22,12 +22,12 @@ #ifndef _WIN32 -typedef struct _GUID { +using GUID = struct _GUID { std::uint32_t Data1; std::uint16_t Data2; std::uint16_t Data3; - std::uint8_t Data4[8]; -} GUID; + std::array<std::uint8_t,8> Data4; +}; inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept { return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; } @@ -362,6 +362,7 @@ constexpr auto EAXCONTEXT_MINMACROFXFACTOR = 0.0F; constexpr auto EAXCONTEXT_MAXMACROFXFACTOR = 1.0F; constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F; +constexpr auto EAXCONTEXT_DEFAULTLASTERROR = EAX_OK; extern const GUID EAXPROPERTYID_EAX40_FXSlot0; extern const GUID EAXPROPERTYID_EAX50_FXSlot0; @@ -654,11 +655,11 @@ struct EAXSPEAKERLEVELPROPERTIES { }; // EAXSPEAKERLEVELPROPERTIES struct EAX40ACTIVEFXSLOTS { - GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS]; + std::array<GUID,EAX40_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots; }; // EAX40ACTIVEFXSLOTS struct EAX50ACTIVEFXSLOTS { - GUID guidActiveFXSlots[EAX50_MAX_ACTIVE_FXSLOTS]; + std::array<GUID,EAX50_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots; }; // EAX50ACTIVEFXSLOTS // Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property. diff --git a/al/eax/call.cpp b/al/eax/call.cpp index 689d5cf1..013a3992 100644 --- a/al/eax/call.cpp +++ b/al/eax/call.cpp @@ -22,8 +22,7 @@ EaxCall::EaxCall( ALuint property_source_id, ALvoid* property_buffer, ALuint property_size) - : mCallType{type}, mVersion{0}, mPropertySetId{EaxCallPropertySetId::none} - , mIsDeferred{(property_id & deferred_flag) != 0} + : mCallType{type}, mIsDeferred{(property_id & deferred_flag) != 0} , mPropertyId{property_id & ~deferred_flag}, mPropertySourceId{property_source_id} , mPropertyBuffer{property_buffer}, mPropertyBufferSize{property_size} { diff --git a/al/eax/call.h b/al/eax/call.h index 45ff328c..e7f2329f 100644 --- a/al/eax/call.h +++ b/al/eax/call.h @@ -31,16 +31,16 @@ public: ALvoid* property_buffer, ALuint property_size); - bool is_get() const noexcept { return mCallType == EaxCallType::get; } - bool is_deferred() const noexcept { return mIsDeferred; } - int get_version() const noexcept { return mVersion; } - EaxCallPropertySetId get_property_set_id() const noexcept { return mPropertySetId; } - ALuint get_property_id() const noexcept { return mPropertyId; } - ALuint get_property_al_name() const noexcept { return mPropertySourceId; } - EaxFxSlotIndex get_fx_slot_index() const noexcept { return mFxSlotIndex; } + [[nodiscard]] auto is_get() const noexcept -> bool { return mCallType == EaxCallType::get; } + [[nodiscard]] auto is_deferred() const noexcept -> bool { return mIsDeferred; } + [[nodiscard]] auto get_version() const noexcept -> int { return mVersion; } + [[nodiscard]] auto get_property_set_id() const noexcept -> EaxCallPropertySetId { return mPropertySetId; } + [[nodiscard]] auto get_property_id() const noexcept -> ALuint { return mPropertyId; } + [[nodiscard]] auto get_property_al_name() const noexcept -> ALuint { return mPropertySourceId; } + [[nodiscard]] auto get_fx_slot_index() const noexcept -> EaxFxSlotIndex { return mFxSlotIndex; } template<typename TException, typename TValue> - TValue& get_value() const + [[nodiscard]] auto get_value() const -> TValue& { if(mPropertyBufferSize < sizeof(TValue)) fail_too_small(); @@ -49,7 +49,7 @@ public: } template<typename TValue> - al::span<TValue> get_values(size_t max_count) const + [[nodiscard]] auto get_values(size_t max_count) const -> al::span<TValue> { if(max_count == 0 || mPropertyBufferSize < sizeof(TValue)) fail_too_small(); @@ -59,22 +59,22 @@ public: } template<typename TValue> - al::span<TValue> get_values() const + [[nodiscard]] auto get_values() const -> al::span<TValue> { return get_values<TValue>(~0_uz); } template<typename TException, typename TValue> - void set_value(const TValue& value) const + auto set_value(const TValue& value) const -> void { get_value<TException, TValue>() = value; } private: const EaxCallType mCallType; - int mVersion; - EaxFxSlotIndex mFxSlotIndex; - EaxCallPropertySetId mPropertySetId; + int mVersion{}; + EaxFxSlotIndex mFxSlotIndex{}; + EaxCallPropertySetId mPropertySetId{EaxCallPropertySetId::none}; bool mIsDeferred; const ALuint mPropertyId; diff --git a/al/eax/effect.h b/al/eax/effect.h index afe4d94d..a735fe6c 100644 --- a/al/eax/effect.h +++ b/al/eax/effect.h @@ -100,7 +100,6 @@ struct EaxReverbCommitter { bool commit(const EAX_REVERBPROPERTIES &props); bool commit(const EAX20LISTENERPROPERTIES &props); bool commit(const EAXREVERBPROPERTIES &props); - bool commit(const EaxEffectProps &props); static void SetDefaults(EAX_REVERBPROPERTIES &props); static void SetDefaults(EAX20LISTENERPROPERTIES &props); @@ -110,16 +109,13 @@ struct EaxReverbCommitter { static void Get(const EaxCall &call, const EAX_REVERBPROPERTIES &props); static void Get(const EaxCall &call, const EAX20LISTENERPROPERTIES &props); static void Get(const EaxCall &call, const EAXREVERBPROPERTIES &props); - static void Get(const EaxCall &call, const EaxEffectProps &props); static void Set(const EaxCall &call, EAX_REVERBPROPERTIES &props); static void Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props); static void Set(const EaxCall &call, EAXREVERBPROPERTIES &props); - static void Set(const EaxCall &call, EaxEffectProps &props); - static void translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept; - static void translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept; - static void translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept; + static void translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept; + static void translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept; }; template<typename T> @@ -144,51 +140,137 @@ struct EaxCommitter { [[noreturn]] static void fail(const char *message); [[noreturn]] static void fail_unknown_property_id() { fail(EaxEffectErrorMessages::unknown_property_id()); } - - bool commit(const EaxEffectProps &props); - - static void SetDefaults(EaxEffectProps &props); - static void Get(const EaxCall &call, const EaxEffectProps &props); - static void Set(const EaxCall &call, EaxEffectProps &props); }; struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> { using EaxCommitter<EaxAutowahCommitter>::EaxCommitter; + + bool commit(const EAXAUTOWAHPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props); + static void Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props); }; struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> { using EaxCommitter<EaxChorusCommitter>::EaxCommitter; + + bool commit(const EAXCHORUSPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props); + static void Set(const EaxCall &call, EAXCHORUSPROPERTIES &props); }; struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> { using EaxCommitter<EaxCompressorCommitter>::EaxCommitter; + + bool commit(const EAXAGCCOMPRESSORPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props); + static void Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props); }; struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> { using EaxCommitter<EaxDistortionCommitter>::EaxCommitter; + + bool commit(const EAXDISTORTIONPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props); + static void Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props); }; struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> { using EaxCommitter<EaxEchoCommitter>::EaxCommitter; + + bool commit(const EAXECHOPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXECHOPROPERTIES &props); + static void Set(const EaxCall &call, EAXECHOPROPERTIES &props); }; struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> { using EaxCommitter<EaxEqualizerCommitter>::EaxCommitter; + + bool commit(const EAXEQUALIZERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props); + static void Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props); }; struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> { using EaxCommitter<EaxFlangerCommitter>::EaxCommitter; + + bool commit(const EAXFLANGERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props); + static void Set(const EaxCall &call, EAXFLANGERPROPERTIES &props); }; struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCommitter> { using EaxCommitter<EaxFrequencyShifterCommitter>::EaxCommitter; + + bool commit(const EAXFREQUENCYSHIFTERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props); + static void Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props); }; struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> { using EaxCommitter<EaxModulatorCommitter>::EaxCommitter; + + bool commit(const EAXRINGMODULATORPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props); + static void Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props); }; struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter> { using EaxCommitter<EaxPitchShifterCommitter>::EaxCommitter; + + bool commit(const EAXPITCHSHIFTERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props); + static void Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props); }; struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter> { using EaxCommitter<EaxVocalMorpherCommitter>::EaxCommitter; + + bool commit(const EAXVOCALMORPHERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props); + static void Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props); }; struct EaxNullCommitter : public EaxCommitter<EaxNullCommitter> { using EaxCommitter<EaxNullCommitter>::EaxCommitter; + + bool commit(const std::monostate &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const std::monostate &props); + static void Set(const EaxCall &call, std::monostate &props); }; +template<typename T> +struct CommitterFromProps { }; + +template<> struct CommitterFromProps<std::monostate> { using type = EaxNullCommitter; }; +template<> struct CommitterFromProps<EAXREVERBPROPERTIES> { using type = EaxReverbCommitter; }; +template<> struct CommitterFromProps<EAXCHORUSPROPERTIES> { using type = EaxChorusCommitter; }; +template<> struct CommitterFromProps<EAXAGCCOMPRESSORPROPERTIES> { using type = EaxCompressorCommitter; }; +template<> struct CommitterFromProps<EAXAUTOWAHPROPERTIES> { using type = EaxAutowahCommitter; }; +template<> struct CommitterFromProps<EAXDISTORTIONPROPERTIES> { using type = EaxDistortionCommitter; }; +template<> struct CommitterFromProps<EAXECHOPROPERTIES> { using type = EaxEchoCommitter; }; +template<> struct CommitterFromProps<EAXEQUALIZERPROPERTIES> { using type = EaxEqualizerCommitter; }; +template<> struct CommitterFromProps<EAXFLANGERPROPERTIES> { using type = EaxFlangerCommitter; }; +template<> struct CommitterFromProps<EAXFREQUENCYSHIFTERPROPERTIES> { using type = EaxFrequencyShifterCommitter; }; +template<> struct CommitterFromProps<EAXRINGMODULATORPROPERTIES> { using type = EaxModulatorCommitter; }; +template<> struct CommitterFromProps<EAXPITCHSHIFTERPROPERTIES> { using type = EaxPitchShifterCommitter; }; +template<> struct CommitterFromProps<EAXVOCALMORPHERPROPERTIES> { using type = EaxVocalMorpherCommitter; }; + +template<typename T> +using CommitterFor = typename CommitterFromProps<std::remove_cv_t<std::remove_reference_t<T>>>::type; + class EaxEffect { public: @@ -233,51 +315,39 @@ public: State4 state5_{}; - template<typename T, typename ...Args> - void call_set_defaults(Args&& ...args) - { return T::SetDefaults(std::forward<Args>(args)...); } - void call_set_defaults(const ALenum altype, EaxEffectProps &props) { - if(altype == AL_EFFECT_EAXREVERB) - return call_set_defaults<EaxReverbCommitter>(props); - if(altype == AL_EFFECT_CHORUS) - return call_set_defaults<EaxChorusCommitter>(props); - if(altype == AL_EFFECT_AUTOWAH) - return call_set_defaults<EaxAutowahCommitter>(props); - if(altype == AL_EFFECT_COMPRESSOR) - return call_set_defaults<EaxCompressorCommitter>(props); - if(altype == AL_EFFECT_DISTORTION) - return call_set_defaults<EaxDistortionCommitter>(props); - if(altype == AL_EFFECT_ECHO) - return call_set_defaults<EaxEchoCommitter>(props); - if(altype == AL_EFFECT_EQUALIZER) - return call_set_defaults<EaxEqualizerCommitter>(props); - if(altype == AL_EFFECT_FLANGER) - return call_set_defaults<EaxFlangerCommitter>(props); - if(altype == AL_EFFECT_FREQUENCY_SHIFTER) - return call_set_defaults<EaxFrequencyShifterCommitter>(props); - if(altype == AL_EFFECT_RING_MODULATOR) - return call_set_defaults<EaxModulatorCommitter>(props); - if(altype == AL_EFFECT_PITCH_SHIFTER) - return call_set_defaults<EaxPitchShifterCommitter>(props); - if(altype == AL_EFFECT_VOCAL_MORPHER) - return call_set_defaults<EaxVocalMorpherCommitter>(props); - return call_set_defaults<EaxNullCommitter>(props); + switch(altype) + { + case AL_EFFECT_EAXREVERB: return EaxReverbCommitter::SetDefaults(props); + case AL_EFFECT_CHORUS: return EaxChorusCommitter::SetDefaults(props); + case AL_EFFECT_AUTOWAH: return EaxAutowahCommitter::SetDefaults(props); + case AL_EFFECT_COMPRESSOR: return EaxCompressorCommitter::SetDefaults(props); + case AL_EFFECT_DISTORTION: return EaxDistortionCommitter::SetDefaults(props); + case AL_EFFECT_ECHO: return EaxEchoCommitter::SetDefaults(props); + case AL_EFFECT_EQUALIZER: return EaxEqualizerCommitter::SetDefaults(props); + case AL_EFFECT_FLANGER: return EaxFlangerCommitter::SetDefaults(props); + case AL_EFFECT_FREQUENCY_SHIFTER: return EaxFrequencyShifterCommitter::SetDefaults(props); + case AL_EFFECT_RING_MODULATOR: return EaxModulatorCommitter::SetDefaults(props); + case AL_EFFECT_PITCH_SHIFTER: return EaxPitchShifterCommitter::SetDefaults(props); + case AL_EFFECT_VOCAL_MORPHER: return EaxVocalMorpherCommitter::SetDefaults(props); + case AL_EFFECT_NULL: break; + } + return EaxNullCommitter::SetDefaults(props); } template<typename T> void init() { - call_set_defaults<EaxReverbCommitter>(state1_.d); + EaxReverbCommitter::SetDefaults(state1_.d); state1_.i = state1_.d; - call_set_defaults<EaxReverbCommitter>(state2_.d); + EaxReverbCommitter::SetDefaults(state2_.d); state2_.i = state2_.d; - call_set_defaults<EaxReverbCommitter>(state3_.d); + EaxReverbCommitter::SetDefaults(state3_.d); state3_.i = state3_.d; - call_set_defaults<T>(state4_.d); + T::SetDefaults(state4_.d); state4_.i = state4_.d; - call_set_defaults<T>(state5_.d); + T::SetDefaults(state5_.d); state5_.i = state5_.d; } @@ -285,9 +355,9 @@ public: { switch(eax_version) { - case 1: call_set_defaults<EaxReverbCommitter>(state1_.d); break; - case 2: call_set_defaults<EaxReverbCommitter>(state2_.d); break; - case 3: call_set_defaults<EaxReverbCommitter>(state3_.d); break; + case 1: EaxReverbCommitter::SetDefaults(state1_.d); break; + case 2: EaxReverbCommitter::SetDefaults(state2_.d); break; + case 3: EaxReverbCommitter::SetDefaults(state3_.d); break; case 4: call_set_defaults(altype, state4_.d); break; case 5: call_set_defaults(altype, state5_.d); break; } @@ -295,47 +365,20 @@ public: } -#define EAXCALL(Props, Callable, ...) \ - if(std::holds_alternative<EAXREVERBPROPERTIES>(Props)) \ - return Callable<EaxReverbCommitter>(__VA_ARGS__); \ - if(std::holds_alternative<EAXCHORUSPROPERTIES>(Props)) \ - return Callable<EaxChorusCommitter>(__VA_ARGS__); \ - if(std::holds_alternative<EAXAUTOWAHPROPERTIES>(Props)) \ - return Callable<EaxAutowahCommitter>(__VA_ARGS__); \ - if(std::holds_alternative<EAXAGCCOMPRESSORPROPERTIES>(Props)) \ - return Callable<EaxCompressorCommitter>(__VA_ARGS__); \ - if(std::holds_alternative<EAXDISTORTIONPROPERTIES>(Props)) \ - return Callable<EaxDistortionCommitter>(__VA_ARGS__); \ - if(std::holds_alternative<EAXECHOPROPERTIES>(Props)) \ - return Callable<EaxEchoCommitter>(__VA_ARGS__); \ - if(std::holds_alternative<EAXEQUALIZERPROPERTIES>(Props)) \ - return Callable<EaxEqualizerCommitter>(__VA_ARGS__); \ - if(std::holds_alternative<EAXFLANGERPROPERTIES>(Props)) \ - return Callable<EaxFlangerCommitter>(__VA_ARGS__); \ - if(std::holds_alternative<EAXFREQUENCYSHIFTERPROPERTIES>(Props)) \ - return Callable<EaxFrequencyShifterCommitter>(__VA_ARGS__); \ - if(std::holds_alternative<EAXRINGMODULATORPROPERTIES>(Props)) \ - return Callable<EaxModulatorCommitter>(__VA_ARGS__); \ - if(std::holds_alternative<EAXPITCHSHIFTERPROPERTIES>(Props)) \ - return Callable<EaxPitchShifterCommitter>(__VA_ARGS__); \ - if(std::holds_alternative<EAXVOCALMORPHERPROPERTIES>(Props)) \ - return Callable<EaxVocalMorpherCommitter>(__VA_ARGS__); \ - return Callable<EaxNullCommitter>(__VA_ARGS__) - - template<typename T, typename ...Args> - static void call_set(Args&& ...args) - { return T::Set(std::forward<Args>(args)...); } - static void call_set(const EaxCall &call, EaxEffectProps &props) - { EAXCALL(props, call_set, call, props); } + { + return std::visit([&](auto &arg) + { return CommitterFor<decltype(arg)>::Set(call, arg); }, + props); + } void set(const EaxCall &call) { switch(call.get_version()) { - case 1: call_set<EaxReverbCommitter>(call, state1_.d); break; - case 2: call_set<EaxReverbCommitter>(call, state2_.d); break; - case 3: call_set<EaxReverbCommitter>(call, state3_.d); break; + case 1: EaxReverbCommitter::Set(call, state1_.d); break; + case 2: EaxReverbCommitter::Set(call, state2_.d); break; + case 3: EaxReverbCommitter::Set(call, state3_.d); break; case 4: call_set(call, state4_.d); break; case 5: call_set(call, state5_.d); break; } @@ -343,32 +386,32 @@ public: } - template<typename T, typename ...Args> - static void call_get(Args&& ...args) - { return T::Get(std::forward<Args>(args)...); } - static void call_get(const EaxCall &call, const EaxEffectProps &props) - { EAXCALL(props, call_get, call, props); } + { + return std::visit([&](auto &arg) + { return CommitterFor<decltype(arg)>::Get(call, arg); }, + props); + } void get(const EaxCall &call) { switch(call.get_version()) { - case 1: call_get<EaxReverbCommitter>(call, state1_.d); break; - case 2: call_get<EaxReverbCommitter>(call, state2_.d); break; - case 3: call_get<EaxReverbCommitter>(call, state3_.d); break; + case 1: EaxReverbCommitter::Get(call, state1_.d); break; + case 2: EaxReverbCommitter::Get(call, state2_.d); break; + case 3: EaxReverbCommitter::Get(call, state3_.d); break; case 4: call_get(call, state4_.d); break; case 5: call_get(call, state5_.d); break; } } - template<typename T, typename ...Args> - bool call_commit(Args&& ...args) - { return T{props_, al_effect_props_}.commit(std::forward<Args>(args)...); } - bool call_commit(const EaxEffectProps &props) - { EAXCALL(props, call_commit, props); } + { + return std::visit([&](auto &arg) + { return CommitterFor<decltype(arg)>{props_, al_effect_props_}.commit(arg); }, + props); + } bool commit(int eax_version) { @@ -383,15 +426,15 @@ public: { case 1: state1_.i = state1_.d; - ret |= call_commit<EaxReverbCommitter>(state1_.d); + ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state1_.d); break; case 2: state2_.i = state2_.d; - ret |= call_commit<EaxReverbCommitter>(state2_.d); + ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state2_.d); break; case 3: state3_.i = state3_.d; - ret |= call_commit<EaxReverbCommitter>(state3_.d); + ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state3_.d); break; case 4: state4_.i = state4_.d; diff --git a/al/eax/fx_slots.h b/al/eax/fx_slots.h index 18b2d3ad..b7ed1031 100644 --- a/al/eax/fx_slots.h +++ b/al/eax/fx_slots.h @@ -25,11 +25,9 @@ public: } - const ALeffectslot& get( - EaxFxSlotIndex index) const; + [[nodiscard]] auto get(EaxFxSlotIndex index) const -> const ALeffectslot&; - ALeffectslot& get( - EaxFxSlotIndex index); + [[nodiscard]] auto get(EaxFxSlotIndex index) -> ALeffectslot&; private: using Items = std::array<EaxAlEffectSlotUPtr, EAX_MAX_FXSLOTS>; @@ -39,8 +37,7 @@ private: [[noreturn]] - static void fail( - const char* message); + static void fail(const char* message); void initialize_fx_slots(ALCcontext& al_context); }; // EaxFxSlots diff --git a/al/eax/globals.h b/al/eax/globals.h index ff05d009..4d501ff9 100644 --- a/al/eax/globals.h +++ b/al/eax/globals.h @@ -3,6 +3,7 @@ inline bool eax_g_is_enabled{true}; +/* NOLINTBEGIN(*-avoid-c-arrays) */ inline constexpr char eax1_ext_name[]{"EAX"}; inline constexpr char eax2_ext_name[]{"EAX2.0"}; inline constexpr char eax3_ext_name[]{"EAX3.0"}; @@ -16,5 +17,6 @@ inline constexpr char eax_eax_get_func_name[]{"EAXGet"}; inline constexpr char eax_eax_set_buffer_mode_func_name[]{"EAXSetBufferMode"}; inline constexpr char eax_eax_get_buffer_mode_func_name[]{"EAXGetBufferMode"}; +/* NOLINTEND(*-avoid-c-arrays) */ #endif // !EAX_GLOBALS_INCLUDED diff --git a/al/effect.cpp b/al/effect.cpp index 3e48e91b..071b32c6 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -58,7 +58,7 @@ #include "eax/exception.h" #endif // ALSOFT_EAX -const EffectList gEffectList[16]{ +const std::array<EffectList,16> gEffectList{{ { "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB }, { "reverb", REVERB_EFFECT, AL_EFFECT_REVERB }, { "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH }, @@ -75,9 +75,7 @@ const EffectList gEffectList[16]{ { "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT }, { "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE }, { "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_SOFT }, -}; - -bool DisabledEffects[MAX_EFFECTS]; +}}; effect_exception::effect_exception(ALenum code, const char *msg, ...) : mErrorCode{code} @@ -91,72 +89,36 @@ effect_exception::~effect_exception() = default; namespace { -struct EffectPropsItem { - ALenum Type; - const EffectProps &DefaultProps; - const EffectVtable &Vtable; -}; -constexpr EffectPropsItem EffectPropsList[] = { - { AL_EFFECT_NULL, NullEffectProps, NullEffectVtable }, - { AL_EFFECT_EAXREVERB, ReverbEffectProps, ReverbEffectVtable }, - { AL_EFFECT_REVERB, StdReverbEffectProps, StdReverbEffectVtable }, - { AL_EFFECT_AUTOWAH, AutowahEffectProps, AutowahEffectVtable }, - { AL_EFFECT_CHORUS, ChorusEffectProps, ChorusEffectVtable }, - { AL_EFFECT_COMPRESSOR, CompressorEffectProps, CompressorEffectVtable }, - { AL_EFFECT_DISTORTION, DistortionEffectProps, DistortionEffectVtable }, - { AL_EFFECT_ECHO, EchoEffectProps, EchoEffectVtable }, - { AL_EFFECT_EQUALIZER, EqualizerEffectProps, EqualizerEffectVtable }, - { AL_EFFECT_FLANGER, FlangerEffectProps, FlangerEffectVtable }, - { AL_EFFECT_FREQUENCY_SHIFTER, FshifterEffectProps, FshifterEffectVtable }, - { AL_EFFECT_RING_MODULATOR, ModulatorEffectProps, ModulatorEffectVtable }, - { AL_EFFECT_PITCH_SHIFTER, PshifterEffectProps, PshifterEffectVtable }, - { AL_EFFECT_VOCAL_MORPHER, VmorpherEffectProps, VmorpherEffectVtable }, - { AL_EFFECT_DEDICATED_DIALOGUE, DedicatedEffectProps, DedicatedEffectVtable }, - { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedEffectProps, DedicatedEffectVtable }, - { AL_EFFECT_CONVOLUTION_SOFT, ConvolutionEffectProps, ConvolutionEffectVtable }, -}; - - -void ALeffect_setParami(ALeffect *effect, ALenum param, int value) -{ effect->vtab->setParami(&effect->Props, param, value); } -void ALeffect_setParamiv(ALeffect *effect, ALenum param, const int *values) -{ effect->vtab->setParamiv(&effect->Props, param, values); } -void ALeffect_setParamf(ALeffect *effect, ALenum param, float value) -{ effect->vtab->setParamf(&effect->Props, param, value); } -void ALeffect_setParamfv(ALeffect *effect, ALenum param, const float *values) -{ effect->vtab->setParamfv(&effect->Props, param, values); } +using SubListAllocator = typename al::allocator<std::array<ALeffect,64>>; -void ALeffect_getParami(const ALeffect *effect, ALenum param, int *value) -{ effect->vtab->getParami(&effect->Props, param, value); } -void ALeffect_getParamiv(const ALeffect *effect, ALenum param, int *values) -{ effect->vtab->getParamiv(&effect->Props, param, values); } -void ALeffect_getParamf(const ALeffect *effect, ALenum param, float *value) -{ effect->vtab->getParamf(&effect->Props, param, value); } -void ALeffect_getParamfv(const ALeffect *effect, ALenum param, float *values) -{ effect->vtab->getParamfv(&effect->Props, param, values); } - - -const EffectPropsItem *getEffectPropsItemByType(ALenum type) +auto GetDefaultProps(ALenum type) -> const EffectProps& { - auto iter = std::find_if(std::begin(EffectPropsList), std::end(EffectPropsList), - [type](const EffectPropsItem &item) noexcept -> bool - { return item.Type == type; }); - return (iter != std::end(EffectPropsList)) ? al::to_address(iter) : nullptr; + switch(type) + { + case AL_EFFECT_NULL: return NullEffectProps; + case AL_EFFECT_EAXREVERB: return ReverbEffectProps; + case AL_EFFECT_REVERB: return StdReverbEffectProps; + case AL_EFFECT_AUTOWAH: return AutowahEffectProps; + case AL_EFFECT_CHORUS: return ChorusEffectProps; + case AL_EFFECT_COMPRESSOR: return CompressorEffectProps; + case AL_EFFECT_DISTORTION: return DistortionEffectProps; + case AL_EFFECT_ECHO: return EchoEffectProps; + case AL_EFFECT_EQUALIZER: return EqualizerEffectProps; + case AL_EFFECT_FLANGER: return FlangerEffectProps; + case AL_EFFECT_FREQUENCY_SHIFTER: return FshifterEffectProps; + case AL_EFFECT_RING_MODULATOR: return ModulatorEffectProps; + case AL_EFFECT_PITCH_SHIFTER: return PshifterEffectProps; + case AL_EFFECT_VOCAL_MORPHER: return VmorpherEffectProps; + case AL_EFFECT_DEDICATED_DIALOGUE: return DedicatedDialogEffectProps; + case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return DedicatedLfeEffectProps; + case AL_EFFECT_CONVOLUTION_SOFT: return ConvolutionEffectProps; + } + return NullEffectProps; } void InitEffectParams(ALeffect *effect, ALenum type) { - const EffectPropsItem *item{getEffectPropsItemByType(type)}; - if(item) - { - effect->Props = item->DefaultProps; - effect->vtab = &item->Vtable; - } - else - { - effect->Props = EffectProps{}; - effect->vtab = &NullEffectVtable; - } + effect->Props = GetDefaultProps(type); effect->type = type; } @@ -166,21 +128,21 @@ bool EnsureEffects(ALCdevice *device, size_t needed) [](size_t cur, const EffectSubList &sublist) noexcept -> size_t { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })}; - while(needed > count) - { - if(device->EffectList.size() >= 1<<25) UNLIKELY - return false; - - device->EffectList.emplace_back(); - auto sublist = device->EffectList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Effects = static_cast<ALeffect*>(al_calloc(alignof(ALeffect), sizeof(ALeffect)*64)); - if(!sublist->Effects) UNLIKELY + try { + while(needed > count) { - device->EffectList.pop_back(); - return false; + if(device->EffectList.size() >= 1<<25) UNLIKELY + return false; + + EffectSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Effects = SubListAllocator{}.allocate(1); + device->EffectList.emplace_back(std::move(sublist)); + count += 64; } - count += 64; + } + catch(...) { + return false; } return true; } @@ -194,7 +156,7 @@ ALeffect *AllocEffect(ALCdevice *device) auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALeffect *effect{al::construct_at(sublist->Effects + slidx)}; + ALeffect *effect{al::construct_at(al::to_address(sublist->Effects->begin() + slidx))}; InitEffectParams(effect, AL_EFFECT_NULL); /* Add 1 to avoid effect ID 0. */ @@ -228,7 +190,7 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) EffectSubList &sublist = device->EffectList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Effects + slidx; + return al::to_address(sublist.Effects->begin() + slidx); } } // namespace @@ -328,7 +290,7 @@ FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, { for(const EffectList &effectitem : gEffectList) { - if(value == effectitem.val && !DisabledEffects[effectitem.type]) + if(value == effectitem.val && !DisabledEffects.test(effectitem.type)) { isOk = true; break; @@ -344,7 +306,16 @@ FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, else try { /* Call the appropriate handler */ - ALeffect_setParami(aleffect, param, value); + std::visit([aleffect,param,value](auto &arg) + { + using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>; + if constexpr(std::is_same_v<Type,ReverbProps>) + { + if(aleffect->type == AL_EFFECT_REVERB) + return EffectHandler::StdReverbSetParami(arg, param, value); + } + return EffectHandler::SetParami(arg, param, value); + }, aleffect->Props); } catch(effect_exception &e) { context->setError(e.errorCode(), "%s", e.what()); @@ -371,7 +342,16 @@ FORCE_ALIGN void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect else try { /* Call the appropriate handler */ - ALeffect_setParamiv(aleffect, param, values); + std::visit([aleffect,param,values](auto &arg) + { + using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>; + if constexpr(std::is_same_v<Type,ReverbProps>) + { + if(aleffect->type == AL_EFFECT_REVERB) + return EffectHandler::StdReverbSetParamiv(arg, param, values); + } + return EffectHandler::SetParamiv(arg, param, values); + }, aleffect->Props); } catch(effect_exception &e) { context->setError(e.errorCode(), "%s", e.what()); @@ -391,7 +371,16 @@ FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, else try { /* Call the appropriate handler */ - ALeffect_setParamf(aleffect, param, value); + std::visit([aleffect,param,value](auto &arg) + { + using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>; + if constexpr(std::is_same_v<Type,ReverbProps>) + { + if(aleffect->type == AL_EFFECT_REVERB) + return EffectHandler::StdReverbSetParamf(arg, param, value); + } + return EffectHandler::SetParamf(arg, param, value); + }, aleffect->Props); } catch(effect_exception &e) { context->setError(e.errorCode(), "%s", e.what()); @@ -411,7 +400,16 @@ FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect else try { /* Call the appropriate handler */ - ALeffect_setParamfv(aleffect, param, values); + std::visit([aleffect,param,values](auto &arg) + { + using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>; + if constexpr(std::is_same_v<Type,ReverbProps>) + { + if(aleffect->type == AL_EFFECT_REVERB) + return EffectHandler::StdReverbSetParamfv(arg, param, values); + } + return EffectHandler::SetParamfv(arg, param, values); + }, aleffect->Props); } catch(effect_exception &e) { context->setError(e.errorCode(), "%s", e.what()); @@ -433,7 +431,16 @@ FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effe else try { /* Call the appropriate handler */ - ALeffect_getParami(aleffect, param, value); + std::visit([aleffect,param,value](auto &arg) + { + using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>; + if constexpr(std::is_same_v<Type,ReverbProps>) + { + if(aleffect->type == AL_EFFECT_REVERB) + return EffectHandler::StdReverbGetParami(arg, param, value); + } + return EffectHandler::GetParami(arg, param, value); + }, aleffect->Props); } catch(effect_exception &e) { context->setError(e.errorCode(), "%s", e.what()); @@ -460,7 +467,16 @@ FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint eff else try { /* Call the appropriate handler */ - ALeffect_getParamiv(aleffect, param, values); + std::visit([aleffect,param,values](auto &arg) + { + using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>; + if constexpr(std::is_same_v<Type,ReverbProps>) + { + if(aleffect->type == AL_EFFECT_REVERB) + return EffectHandler::StdReverbGetParamiv(arg, param, values); + } + return EffectHandler::GetParamiv(arg, param, values); + }, aleffect->Props); } catch(effect_exception &e) { context->setError(e.errorCode(), "%s", e.what()); @@ -480,7 +496,16 @@ FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effe else try { /* Call the appropriate handler */ - ALeffect_getParamf(aleffect, param, value); + std::visit([aleffect,param,value](auto &arg) + { + using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>; + if constexpr(std::is_same_v<Type,ReverbProps>) + { + if(aleffect->type == AL_EFFECT_REVERB) + return EffectHandler::StdReverbGetParamf(arg, param, value); + } + return EffectHandler::GetParamf(arg, param, value); + }, aleffect->Props); } catch(effect_exception &e) { context->setError(e.errorCode(), "%s", e.what()); @@ -500,7 +525,16 @@ FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint eff else try { /* Call the appropriate handler */ - ALeffect_getParamfv(aleffect, param, values); + std::visit([aleffect,param,values](auto &arg) + { + using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>; + if constexpr(std::is_same_v<Type,ReverbProps>) + { + if(aleffect->type == AL_EFFECT_REVERB) + return EffectHandler::StdReverbGetParamfv(arg, param, values); + } + return EffectHandler::GetParamfv(arg, param, values); + }, aleffect->Props); } catch(effect_exception &e) { context->setError(e.errorCode(), "%s", e.what()); @@ -535,20 +569,21 @@ EffectSubList::~EffectSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(Effects+idx); + std::destroy_at(al::to_address(Effects->begin()+idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(Effects); + SubListAllocator{}.deallocate(Effects, 1); Effects = nullptr; } -#define DECL(x) { #x, EFX_REVERB_PRESET_##x } -static const struct { - const char name[32]; +struct EffectPreset { + const char name[32]; /* NOLINT(*-avoid-c-arrays) */ EFXEAXREVERBPROPERTIES props; -} reverblist[] = { +}; +#define DECL(x) EffectPreset{#x, EFX_REVERB_PRESET_##x} +static constexpr std::array reverblist{ DECL(GENERIC), DECL(PADDEDCELL), DECL(ROOM), @@ -687,48 +722,47 @@ void LoadReverbPreset(const char *name, ALeffect *effect) return; } - if(!DisabledEffects[EAXREVERB_EFFECT]) + if(!DisabledEffects.test(EAXREVERB_EFFECT)) InitEffectParams(effect, AL_EFFECT_EAXREVERB); - else if(!DisabledEffects[REVERB_EFFECT]) + else if(!DisabledEffects.test(REVERB_EFFECT)) InitEffectParams(effect, AL_EFFECT_REVERB); else InitEffectParams(effect, AL_EFFECT_NULL); for(const auto &reverbitem : reverblist) { - const EFXEAXREVERBPROPERTIES *props; - if(al::strcasecmp(name, reverbitem.name) != 0) continue; TRACE("Loading reverb '%s'\n", reverbitem.name); - props = &reverbitem.props; - effect->Props.Reverb.Density = props->flDensity; - effect->Props.Reverb.Diffusion = props->flDiffusion; - effect->Props.Reverb.Gain = props->flGain; - effect->Props.Reverb.GainHF = props->flGainHF; - effect->Props.Reverb.GainLF = props->flGainLF; - effect->Props.Reverb.DecayTime = props->flDecayTime; - effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio; - effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio; - effect->Props.Reverb.ReflectionsGain = props->flReflectionsGain; - effect->Props.Reverb.ReflectionsDelay = props->flReflectionsDelay; - effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0]; - effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1]; - effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2]; - effect->Props.Reverb.LateReverbGain = props->flLateReverbGain; - effect->Props.Reverb.LateReverbDelay = props->flLateReverbDelay; - effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0]; - effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1]; - effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2]; - effect->Props.Reverb.EchoTime = props->flEchoTime; - effect->Props.Reverb.EchoDepth = props->flEchoDepth; - effect->Props.Reverb.ModulationTime = props->flModulationTime; - effect->Props.Reverb.ModulationDepth = props->flModulationDepth; - effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF; - effect->Props.Reverb.HFReference = props->flHFReference; - effect->Props.Reverb.LFReference = props->flLFReference; - effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor; - effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit ? AL_TRUE : AL_FALSE; + const auto &props = reverbitem.props; + auto &dst = std::get<ReverbProps>(effect->Props); + dst.Density = props.flDensity; + dst.Diffusion = props.flDiffusion; + dst.Gain = props.flGain; + dst.GainHF = props.flGainHF; + dst.GainLF = props.flGainLF; + dst.DecayTime = props.flDecayTime; + dst.DecayHFRatio = props.flDecayHFRatio; + dst.DecayLFRatio = props.flDecayLFRatio; + dst.ReflectionsGain = props.flReflectionsGain; + dst.ReflectionsDelay = props.flReflectionsDelay; + dst.ReflectionsPan[0] = props.flReflectionsPan[0]; + dst.ReflectionsPan[1] = props.flReflectionsPan[1]; + dst.ReflectionsPan[2] = props.flReflectionsPan[2]; + dst.LateReverbGain = props.flLateReverbGain; + dst.LateReverbDelay = props.flLateReverbDelay; + dst.LateReverbPan[0] = props.flLateReverbPan[0]; + dst.LateReverbPan[1] = props.flLateReverbPan[1]; + dst.LateReverbPan[2] = props.flLateReverbPan[2]; + dst.EchoTime = props.flEchoTime; + dst.EchoDepth = props.flEchoDepth; + dst.ModulationTime = props.flModulationTime; + dst.ModulationDepth = props.flModulationDepth; + dst.AirAbsorptionGainHF = props.flAirAbsorptionGainHF; + dst.HFReference = props.flHFReference; + dst.LFReference = props.flLFReference; + dst.RoomRolloffFactor = props.flRoomRolloffFactor; + dst.DecayHFLimit = props.iDecayHFLimit ? AL_TRUE : AL_FALSE; return; } @@ -742,7 +776,7 @@ bool IsValidEffectType(ALenum type) noexcept for(const auto &effect_item : gEffectList) { - if(type == effect_item.val && !DisabledEffects[effect_item.type]) + if(type == effect_item.val && !DisabledEffects.test(effect_item.type)) return true; } return false; diff --git a/al/effect.h b/al/effect.h index 27e9dd72..8f069bee 100644 --- a/al/effect.h +++ b/al/effect.h @@ -1,6 +1,9 @@ #ifndef AL_EFFECT_H #define AL_EFFECT_H +#include <array> +#include <bitset> +#include <cstdint> #include <string_view> #include "AL/al.h" @@ -8,6 +11,8 @@ #include "al/effects/effects.h" #include "alc/effects/base.h" +#include "almalloc.h" +#include "alnumeric.h" enum { @@ -29,16 +34,14 @@ enum { MAX_EFFECTS }; -extern bool DisabledEffects[MAX_EFFECTS]; - -extern float ReverbBoost; +inline std::bitset<MAX_EFFECTS> DisabledEffects; struct EffectList { - const char name[16]; - int type; + const char name[16]; /* NOLINT(*-avoid-c-arrays) */ + ALuint type; ALenum val; }; -extern const EffectList gEffectList[16]; +extern const std::array<EffectList,16> gEffectList; struct ALeffect { @@ -47,14 +50,12 @@ struct ALeffect { EffectProps Props{}; - const EffectVtable *vtab{nullptr}; - /* Self ID */ ALuint id{0u}; static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC }; void InitEffect(ALeffect *effect); @@ -63,4 +64,19 @@ void LoadReverbPreset(const char *name, ALeffect *effect); bool IsValidEffectType(ALenum type) noexcept; +struct EffectSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner<std::array<ALeffect,64>*> Effects{nullptr}; /* 64 */ + + EffectSubList() noexcept = default; + EffectSubList(const EffectSubList&) = delete; + EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects} + { rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; } + ~EffectSubList(); + + EffectSubList& operator=(const EffectSubList&) = delete; + EffectSubList& operator=(EffectSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; } +}; + #endif diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp index 1a8b43fc..68704c11 100644 --- a/al/effects/autowah.cpp +++ b/al/effects/autowah.cpp @@ -20,100 +20,87 @@ namespace { -void Autowah_setParamf(EffectProps *props, ALenum param, float val) +EffectProps genDefaultProps() noexcept +{ + AutowahProps props{}; + props.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME; + props.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME; + props.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE; + props.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN; + return props; +} + +} // namespace + +const EffectProps AutowahEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(AutowahProps&, ALenum param, int) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } +void EffectHandler::SetParamiv(AutowahProps&, ALenum param, const int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", + param}; +} + +void EffectHandler::SetParamf(AutowahProps &props, ALenum param, float val) { switch(param) { case AL_AUTOWAH_ATTACK_TIME: if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME)) throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"}; - props->Autowah.AttackTime = val; + props.AttackTime = val; break; case AL_AUTOWAH_RELEASE_TIME: if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME)) throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"}; - props->Autowah.ReleaseTime = val; + props.ReleaseTime = val; break; case AL_AUTOWAH_RESONANCE: if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE)) throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"}; - props->Autowah.Resonance = val; + props.Resonance = val; break; case AL_AUTOWAH_PEAK_GAIN: if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"}; - props->Autowah.PeakGain = val; + props.PeakGain = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param}; } } -void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Autowah_setParamf(props, param, vals[0]); } +void EffectHandler::SetParamfv(AutowahProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, vals[0]); } -void Autowah_setParami(EffectProps*, ALenum param, int) +void EffectHandler::GetParami(const AutowahProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } -void Autowah_setParamiv(EffectProps*, ALenum param, const int*) +void EffectHandler::GetParamiv(const AutowahProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param}; } -void Autowah_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::GetParamf(const AutowahProps &props, ALenum param, float *val) { switch(param) { - case AL_AUTOWAH_ATTACK_TIME: - *val = props->Autowah.AttackTime; - break; - - case AL_AUTOWAH_RELEASE_TIME: - *val = props->Autowah.ReleaseTime; - break; - - case AL_AUTOWAH_RESONANCE: - *val = props->Autowah.Resonance; - break; - - case AL_AUTOWAH_PEAK_GAIN: - *val = props->Autowah.PeakGain; - break; + case AL_AUTOWAH_ATTACK_TIME: *val = props.AttackTime; break; + case AL_AUTOWAH_RELEASE_TIME: *val = props.ReleaseTime; break; + case AL_AUTOWAH_RESONANCE: *val = props.Resonance; break; + case AL_AUTOWAH_PEAK_GAIN: *val = props.PeakGain; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param}; } } -void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Autowah_getParamf(props, param, vals); } - -void Autowah_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } -void Autowah_getParamiv(const EffectProps*, ALenum param, int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", - param}; -} - -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME; - props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME; - props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE; - props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Autowah); - -const EffectProps AutowahEffectProps{genDefaultProps()}; +void EffectHandler::GetParamfv(const AutowahProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } #ifdef ALSOFT_EAX namespace { @@ -189,25 +176,25 @@ template<> throw Exception{message}; } -template<> -bool AutowahCommitter::commit(const EaxEffectProps &props) +bool EaxAutowahCommitter::commit(const EAXAUTOWAHPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if<EAXAUTOWAHPROPERTIES>(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get<EAXAUTOWAHPROPERTIES>(props); - mAlProps.Autowah.AttackTime = eaxprops.flAttackTime; - mAlProps.Autowah.ReleaseTime = eaxprops.flReleaseTime; - mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast<float>(eaxprops.lResonance)); - mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast<float>(eaxprops.lPeakLevel)); + mAlProps = [&]{ + AutowahProps ret{}; + ret.AttackTime = props.flAttackTime; + ret.ReleaseTime = props.flReleaseTime; + ret.Resonance = level_mb_to_gain(static_cast<float>(props.lResonance)); + ret.PeakGain = level_mb_to_gain(static_cast<float>(props.lPeakLevel)); + return ret; + }(); return true; } -template<> -void AutowahCommitter::SetDefaults(EaxEffectProps &props) +void EaxAutowahCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXAUTOWAHPROPERTIES defprops{[] { @@ -221,10 +208,8 @@ void AutowahCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxAutowahCommitter::Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props) { - auto &props = std::get<EAXAUTOWAHPROPERTIES>(props_); switch(call.get_property_id()) { case EAXAUTOWAH_NONE: break; @@ -237,10 +222,8 @@ void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxAutowahCommitter::Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props) { - auto &props = std::get<EAXAUTOWAHPROPERTIES>(props_); switch(call.get_property_id()) { case EAXAUTOWAH_NONE: break; diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp index 90b38e4d..913d1215 100644 --- a/al/effects/chorus.cpp +++ b/al/effects/chorus.cpp @@ -46,13 +46,41 @@ inline ALenum EnumFromWaveform(ChorusWaveform type) throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast<int>(type))}; } -void Chorus_setParami(EffectProps *props, ALenum param, int val) +EffectProps genDefaultChorusProps() noexcept +{ + ChorusProps props{}; + props.Waveform = WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM).value(); + props.Phase = AL_CHORUS_DEFAULT_PHASE; + props.Rate = AL_CHORUS_DEFAULT_RATE; + props.Depth = AL_CHORUS_DEFAULT_DEPTH; + props.Feedback = AL_CHORUS_DEFAULT_FEEDBACK; + props.Delay = AL_CHORUS_DEFAULT_DELAY; + return props; +} + +EffectProps genDefaultFlangerProps() noexcept +{ + FlangerProps props{}; + props.Waveform = WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM).value(); + props.Phase = AL_FLANGER_DEFAULT_PHASE; + props.Rate = AL_FLANGER_DEFAULT_RATE; + props.Depth = AL_FLANGER_DEFAULT_DEPTH; + props.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; + props.Delay = AL_FLANGER_DEFAULT_DELAY; + return props; +} + +} // namespace + +const EffectProps ChorusEffectProps{genDefaultChorusProps()}; + +void EffectHandler::SetParami(ChorusProps &props, ALenum param, int val) { switch(param) { case AL_CHORUS_WAVEFORM: if(auto formopt = WaveformFromEnum(val)) - props->Chorus.Waveform = *formopt; + props.Waveform = *formopt; else throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val}; break; @@ -60,115 +88,89 @@ void Chorus_setParami(EffectProps *props, ALenum param, int val) case AL_CHORUS_PHASE: if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE)) throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range: %d", val}; - props->Chorus.Phase = val; + props.Phase = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param}; } } -void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Chorus_setParami(props, param, vals[0]); } -void Chorus_setParamf(EffectProps *props, ALenum param, float val) +void EffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals) +{ SetParami(props, param, vals[0]); } +void EffectHandler::SetParamf(ChorusProps &props, ALenum param, float val) { switch(param) { case AL_CHORUS_RATE: if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE)) throw effect_exception{AL_INVALID_VALUE, "Chorus rate out of range: %f", val}; - props->Chorus.Rate = val; + props.Rate = val; break; case AL_CHORUS_DEPTH: if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH)) throw effect_exception{AL_INVALID_VALUE, "Chorus depth out of range: %f", val}; - props->Chorus.Depth = val; + props.Depth = val; break; case AL_CHORUS_FEEDBACK: if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK)) throw effect_exception{AL_INVALID_VALUE, "Chorus feedback out of range: %f", val}; - props->Chorus.Feedback = val; + props.Feedback = val; break; case AL_CHORUS_DELAY: if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY)) throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range: %f", val}; - props->Chorus.Delay = val; + props.Delay = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param}; } } -void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Chorus_setParamf(props, param, vals[0]); } +void EffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, vals[0]); } -void Chorus_getParami(const EffectProps *props, ALenum param, int *val) +void EffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val) { switch(param) { - case AL_CHORUS_WAVEFORM: - *val = EnumFromWaveform(props->Chorus.Waveform); - break; - - case AL_CHORUS_PHASE: - *val = props->Chorus.Phase; - break; + case AL_CHORUS_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break; + case AL_CHORUS_PHASE: *val = props.Phase; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param}; } } -void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Chorus_getParami(props, param, vals); } -void Chorus_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } +void EffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val) { switch(param) { - case AL_CHORUS_RATE: - *val = props->Chorus.Rate; - break; - - case AL_CHORUS_DEPTH: - *val = props->Chorus.Depth; - break; - - case AL_CHORUS_FEEDBACK: - *val = props->Chorus.Feedback; - break; - - case AL_CHORUS_DELAY: - *val = props->Chorus.Delay; - break; + case AL_CHORUS_RATE: *val = props.Rate; break; + case AL_CHORUS_DEPTH: *val = props.Depth; break; + case AL_CHORUS_FEEDBACK: *val = props.Feedback; break; + case AL_CHORUS_DELAY: *val = props.Delay; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param}; } } -void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Chorus_getParamf(props, param, vals); } +void EffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -EffectProps genDefaultChorusProps() noexcept -{ - EffectProps props{}; - props.Chorus.Waveform = *WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM); - props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE; - props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE; - props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH; - props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK; - props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY; - return props; -} +const EffectProps FlangerEffectProps{genDefaultFlangerProps()}; -void Flanger_setParami(EffectProps *props, ALenum param, int val) +void EffectHandler::SetParami(FlangerProps &props, ALenum param, int val) { switch(param) { case AL_FLANGER_WAVEFORM: if(auto formopt = WaveformFromEnum(val)) - props->Chorus.Waveform = *formopt; + props.Waveform = *formopt; else throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val}; break; @@ -176,124 +178,87 @@ void Flanger_setParami(EffectProps *props, ALenum param, int val) case AL_FLANGER_PHASE: if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE)) throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range: %d", val}; - props->Chorus.Phase = val; + props.Phase = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param}; } } -void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Flanger_setParami(props, param, vals[0]); } -void Flanger_setParamf(EffectProps *props, ALenum param, float val) +void EffectHandler::SetParamiv(FlangerProps &props, ALenum param, const int *vals) +{ SetParami(props, param, vals[0]); } +void EffectHandler::SetParamf(FlangerProps &props, ALenum param, float val) { switch(param) { case AL_FLANGER_RATE: if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE)) throw effect_exception{AL_INVALID_VALUE, "Flanger rate out of range: %f", val}; - props->Chorus.Rate = val; + props.Rate = val; break; case AL_FLANGER_DEPTH: if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH)) throw effect_exception{AL_INVALID_VALUE, "Flanger depth out of range: %f", val}; - props->Chorus.Depth = val; + props.Depth = val; break; case AL_FLANGER_FEEDBACK: if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK)) throw effect_exception{AL_INVALID_VALUE, "Flanger feedback out of range: %f", val}; - props->Chorus.Feedback = val; + props.Feedback = val; break; case AL_FLANGER_DELAY: if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY)) throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range: %f", val}; - props->Chorus.Delay = val; + props.Delay = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param}; } } -void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Flanger_setParamf(props, param, vals[0]); } +void EffectHandler::SetParamfv(FlangerProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, vals[0]); } -void Flanger_getParami(const EffectProps *props, ALenum param, int *val) +void EffectHandler::GetParami(const FlangerProps &props, ALenum param, int *val) { switch(param) { - case AL_FLANGER_WAVEFORM: - *val = EnumFromWaveform(props->Chorus.Waveform); - break; - - case AL_FLANGER_PHASE: - *val = props->Chorus.Phase; - break; + case AL_FLANGER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break; + case AL_FLANGER_PHASE: *val = props.Phase; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param}; } } -void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Flanger_getParami(props, param, vals); } -void Flanger_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::GetParamiv(const FlangerProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } +void EffectHandler::GetParamf(const FlangerProps &props, ALenum param, float *val) { switch(param) { - case AL_FLANGER_RATE: - *val = props->Chorus.Rate; - break; - - case AL_FLANGER_DEPTH: - *val = props->Chorus.Depth; - break; - - case AL_FLANGER_FEEDBACK: - *val = props->Chorus.Feedback; - break; - - case AL_FLANGER_DELAY: - *val = props->Chorus.Delay; - break; + case AL_FLANGER_RATE: *val = props.Rate; break; + case AL_FLANGER_DEPTH: *val = props.Depth; break; + case AL_FLANGER_FEEDBACK: *val = props.Feedback; break; + case AL_FLANGER_DELAY: *val = props.Delay; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param}; } } -void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Flanger_getParamf(props, param, vals); } - -EffectProps genDefaultFlangerProps() noexcept -{ - EffectProps props{}; - props.Chorus.Waveform = *WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM); - props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE; - props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE; - props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH; - props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; - props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Chorus); - -const EffectProps ChorusEffectProps{genDefaultChorusProps()}; - -DEFINE_ALEFFECT_VTABLE(Flanger); - -const EffectProps FlangerEffectProps{genDefaultFlangerProps()}; +void EffectHandler::GetParamfv(const FlangerProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } #ifdef ALSOFT_EAX namespace { struct EaxChorusTraits { - using Props = EAXCHORUSPROPERTIES; + using EaxProps = EAXCHORUSPROPERTIES; using Committer = EaxChorusCommitter; + using AlProps = ChorusProps; static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; } @@ -357,8 +322,9 @@ struct EaxChorusTraits { }; // EaxChorusTraits struct EaxFlangerTraits { - using Props = EAXFLANGERPROPERTIES; + using EaxProps = EAXFLANGERPROPERTIES; using Committer = EaxFlangerCommitter; + using AlProps = FlangerProps; static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; } @@ -424,7 +390,9 @@ struct EaxFlangerTraits { template<typename TTraits> struct ChorusFlangerEffect { using Traits = TTraits; + using EaxProps = typename Traits::EaxProps; using Committer = typename Traits::Committer; + using AlProps = typename Traits::AlProps; using Exception = typename Committer::Exception; struct WaveformValidator { @@ -494,7 +462,7 @@ struct ChorusFlangerEffect { }; // DelayValidator struct AllValidator { - void operator()(const typename Traits::Props& all) const + void operator()(const EaxProps& all) const { WaveformValidator{}(all.ulWaveform); PhaseValidator{}(all.lPhase); @@ -508,7 +476,7 @@ struct ChorusFlangerEffect { public: static void SetDefaults(EaxEffectProps &props) { - auto&& all = props.emplace<typename Traits::Props>(); + auto&& all = props.emplace<EaxProps>(); all.ulWaveform = Traits::eax_default_waveform(); all.lPhase = Traits::eax_default_phase(); all.flRate = Traits::eax_default_rate(); @@ -518,102 +486,83 @@ public: } - static void Get(const EaxCall &call, const EaxEffectProps &props) + static void Get(const EaxCall &call, const EaxProps &all) { - auto&& all = std::get<typename Traits::Props>(props); switch(call.get_property_id()) { case Traits::eax_none_param_id(): break; - case Traits::eax_allparameters_param_id(): call.template set_value<Exception>(all); break; - case Traits::eax_waveform_param_id(): call.template set_value<Exception>(all.ulWaveform); break; - case Traits::eax_phase_param_id(): call.template set_value<Exception>(all.lPhase); break; - case Traits::eax_rate_param_id(): call.template set_value<Exception>(all.flRate); break; - case Traits::eax_depth_param_id(): call.template set_value<Exception>(all.flDepth); break; - case Traits::eax_feedback_param_id(): call.template set_value<Exception>(all.flFeedback); break; - case Traits::eax_delay_param_id(): call.template set_value<Exception>(all.flDelay); break; - default: Committer::fail_unknown_property_id(); } } - static void Set(const EaxCall &call, EaxEffectProps &props) + static void Set(const EaxCall &call, EaxProps &all) { - auto&& all = std::get<typename Traits::Props>(props); switch(call.get_property_id()) { case Traits::eax_none_param_id(): break; - case Traits::eax_allparameters_param_id(): Committer::template defer<AllValidator>(call, all); break; - case Traits::eax_waveform_param_id(): Committer::template defer<WaveformValidator>(call, all.ulWaveform); break; - case Traits::eax_phase_param_id(): Committer::template defer<PhaseValidator>(call, all.lPhase); break; - case Traits::eax_rate_param_id(): Committer::template defer<RateValidator>(call, all.flRate); break; - case Traits::eax_depth_param_id(): Committer::template defer<DepthValidator>(call, all.flDepth); break; - case Traits::eax_feedback_param_id(): Committer::template defer<FeedbackValidator>(call, all.flFeedback); break; - case Traits::eax_delay_param_id(): Committer::template defer<DelayValidator>(call, all.flDelay); break; - default: Committer::fail_unknown_property_id(); } } - static bool Commit(const EaxEffectProps &props, EaxEffectProps &props_, EffectProps &al_props_) + static bool Commit(const EaxProps &props, EaxEffectProps &props_, AlProps &al_props_) { - if(props == props_) + if(auto *cur = std::get_if<EaxProps>(&props_); cur && *cur == props) return false; props_ = props; - auto&& dst = std::get<typename Traits::Props>(props); - al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform); - al_props_.Chorus.Phase = static_cast<int>(dst.lPhase); - al_props_.Chorus.Rate = dst.flRate; - al_props_.Chorus.Depth = dst.flDepth; - al_props_.Chorus.Feedback = dst.flFeedback; - al_props_.Chorus.Delay = dst.flDelay; + al_props_.Waveform = Traits::eax_waveform(props.ulWaveform); + al_props_.Phase = static_cast<int>(props.lPhase); + al_props_.Rate = props.flRate; + al_props_.Depth = props.flDepth; + al_props_.Feedback = props.flFeedback; + al_props_.Delay = props.flDelay; return true; } @@ -638,29 +587,25 @@ template<> throw Exception{message}; } -template<> -bool ChorusCommitter::commit(const EaxEffectProps &props) +bool EaxChorusCommitter::commit(const EAXCHORUSPROPERTIES &props) { using Committer = ChorusFlangerEffect<EaxChorusTraits>; - return Committer::Commit(props, mEaxProps, mAlProps); + return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>()); } -template<> -void ChorusCommitter::SetDefaults(EaxEffectProps &props) +void EaxChorusCommitter::SetDefaults(EaxEffectProps &props) { using Committer = ChorusFlangerEffect<EaxChorusTraits>; Committer::SetDefaults(props); } -template<> -void ChorusCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void EaxChorusCommitter::Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props) { using Committer = ChorusFlangerEffect<EaxChorusTraits>; Committer::Get(call, props); } -template<> -void ChorusCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void EaxChorusCommitter::Set(const EaxCall &call, EAXCHORUSPROPERTIES &props) { using Committer = ChorusFlangerEffect<EaxChorusTraits>; Committer::Set(call, props); @@ -679,29 +624,25 @@ template<> throw Exception{message}; } -template<> -bool FlangerCommitter::commit(const EaxEffectProps &props) +bool EaxFlangerCommitter::commit(const EAXFLANGERPROPERTIES &props) { using Committer = ChorusFlangerEffect<EaxFlangerTraits>; - return Committer::Commit(props, mEaxProps, mAlProps); + return Committer::Commit(props, mEaxProps, mAlProps.emplace<FlangerProps>()); } -template<> -void FlangerCommitter::SetDefaults(EaxEffectProps &props) +void EaxFlangerCommitter::SetDefaults(EaxEffectProps &props) { using Committer = ChorusFlangerEffect<EaxFlangerTraits>; Committer::SetDefaults(props); } -template<> -void FlangerCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void EaxFlangerCommitter::Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props) { using Committer = ChorusFlangerEffect<EaxFlangerTraits>; Committer::Get(call, props); } -template<> -void FlangerCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void EaxFlangerCommitter::Set(const EaxCall &call, EAXFLANGERPROPERTIES &props) { using Committer = ChorusFlangerEffect<EaxFlangerTraits>; Committer::Set(call, props); diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp index ca8af84f..9fcc8c61 100644 --- a/al/effects/compressor.cpp +++ b/al/effects/compressor.cpp @@ -16,14 +16,25 @@ namespace { -void Compressor_setParami(EffectProps *props, ALenum param, int val) +EffectProps genDefaultProps() noexcept +{ + CompressorProps props{}; + props.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF; + return props; +} + +} // namespace + +const EffectProps CompressorEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(CompressorProps &props, ALenum param, int val) { switch(param) { case AL_COMPRESSOR_ONOFF: if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF)) throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"}; - props->Compressor.OnOff = (val != AL_FALSE); + props.OnOff = (val != AL_FALSE); break; default: @@ -31,22 +42,22 @@ void Compressor_setParami(EffectProps *props, ALenum param, int val) param}; } } -void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Compressor_setParami(props, param, vals[0]); } -void Compressor_setParamf(EffectProps*, ALenum param, float) +void EffectHandler::SetParamiv(CompressorProps &props, ALenum param, const int *vals) +{ SetParami(props, param, vals[0]); } +void EffectHandler::SetParamf(CompressorProps&, ALenum param, float) { throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; } -void Compressor_setParamfv(EffectProps*, ALenum param, const float*) +void EffectHandler::SetParamfv(CompressorProps&, ALenum param, const float*) { throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param}; } -void Compressor_getParami(const EffectProps *props, ALenum param, int *val) +void EffectHandler::GetParami(const CompressorProps &props, ALenum param, int *val) { switch(param) { case AL_COMPRESSOR_ONOFF: - *val = props->Compressor.OnOff; + *val = props.OnOff; break; default: @@ -54,28 +65,16 @@ void Compressor_getParami(const EffectProps *props, ALenum param, int *val) param}; } } -void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Compressor_getParami(props, param, vals); } -void Compressor_getParamf(const EffectProps*, ALenum param, float*) +void EffectHandler::GetParamiv(const CompressorProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } +void EffectHandler::GetParamf(const CompressorProps&, ALenum param, float*) { throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; } -void Compressor_getParamfv(const EffectProps*, ALenum param, float*) +void EffectHandler::GetParamfv(const CompressorProps&, ALenum param, float*) { throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param}; } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Compressor); - -const EffectProps CompressorEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { @@ -115,28 +114,24 @@ template<> throw Exception{message}; } -template<> -bool CompressorCommitter::commit(const EaxEffectProps &props) +bool EaxCompressorCommitter::commit(const EAXAGCCOMPRESSORPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if<EAXAGCCOMPRESSORPROPERTIES>(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; + mAlProps = CompressorProps{props.ulOnOff != 0}; - mAlProps.Compressor.OnOff = (std::get<EAXAGCCOMPRESSORPROPERTIES>(props).ulOnOff != 0); return true; } -template<> -void CompressorCommitter::SetDefaults(EaxEffectProps &props) +void EaxCompressorCommitter::SetDefaults(EaxEffectProps &props) { props = EAXAGCCOMPRESSORPROPERTIES{EAXAGCCOMPRESSOR_DEFAULTONOFF}; } -template<> -void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxCompressorCommitter::Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props) { - auto &props = std::get<EAXAGCCOMPRESSORPROPERTIES>(props_); switch(call.get_property_id()) { case EAXAGCCOMPRESSOR_NONE: break; @@ -146,10 +141,8 @@ void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxCompressorCommitter::Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props) { - auto &props = std::get<EAXAGCCOMPRESSORPROPERTIES>(props_); switch(call.get_property_id()) { case EAXAGCCOMPRESSOR_NONE: break; diff --git a/al/effects/convolution.cpp b/al/effects/convolution.cpp index 9c091e53..1f8d1111 100644 --- a/al/effects/convolution.cpp +++ b/al/effects/convolution.cpp @@ -12,33 +12,45 @@ namespace { -void Convolution_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) +EffectProps genDefaultProps() noexcept +{ + ConvolutionProps props{}; + props.OrientAt = {0.0f, 0.0f, -1.0f}; + props.OrientUp = {0.0f, 1.0f, 0.0f}; + return props; +} + +} // namespace + +const EffectProps ConvolutionEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(ConvolutionProps& /*props*/, ALenum param, int /*val*/) { switch(param) { default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", + throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x", param}; } } -void Convolution_setParamiv(EffectProps *props, ALenum param, const int *vals) +void EffectHandler::SetParamiv(ConvolutionProps &props, ALenum param, const int *vals) { switch(param) { default: - Convolution_setParami(props, param, vals[0]); + SetParami(props, param, vals[0]); } } -void Convolution_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) +void EffectHandler::SetParamf(ConvolutionProps& /*props*/, ALenum param, float /*val*/) { switch(param) { default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", + throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x", param}; } } -void Convolution_setParamfv(EffectProps *props, ALenum param, const float *values) +void EffectHandler::SetParamfv(ConvolutionProps &props, ALenum param, const float *values) { switch(param) { @@ -47,73 +59,59 @@ void Convolution_setParamfv(EffectProps *props, ALenum param, const float *value && std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5]))) throw effect_exception{AL_INVALID_VALUE, "Property 0x%04x value out of range", param}; - props->Convolution.OrientAt[0] = values[0]; - props->Convolution.OrientAt[1] = values[1]; - props->Convolution.OrientAt[2] = values[2]; - props->Convolution.OrientUp[0] = values[3]; - props->Convolution.OrientUp[1] = values[4]; - props->Convolution.OrientUp[2] = values[5]; + props.OrientAt[0] = values[0]; + props.OrientAt[1] = values[1]; + props.OrientAt[2] = values[2]; + props.OrientUp[0] = values[3]; + props.OrientUp[1] = values[4]; + props.OrientUp[2] = values[5]; break; default: - Convolution_setParamf(props, param, values[0]); + SetParamf(props, param, values[0]); } } -void Convolution_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) +void EffectHandler::GetParami(const ConvolutionProps& /*props*/, ALenum param, int* /*val*/) { switch(param) { default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", + throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x", param}; } } -void Convolution_getParamiv(const EffectProps *props, ALenum param, int *vals) +void EffectHandler::GetParamiv(const ConvolutionProps &props, ALenum param, int *vals) { switch(param) { default: - Convolution_getParami(props, param, vals); + GetParami(props, param, vals); } } -void Convolution_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) +void EffectHandler::GetParamf(const ConvolutionProps& /*props*/, ALenum param, float* /*val*/) { switch(param) { default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", + throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x", param}; } } -void Convolution_getParamfv(const EffectProps *props, ALenum param, float *values) +void EffectHandler::GetParamfv(const ConvolutionProps &props, ALenum param, float *values) { switch(param) { case AL_CONVOLUTION_ORIENTATION_SOFT: - values[0] = props->Convolution.OrientAt[0]; - values[1] = props->Convolution.OrientAt[1]; - values[2] = props->Convolution.OrientAt[2]; - values[3] = props->Convolution.OrientUp[0]; - values[4] = props->Convolution.OrientUp[1]; - values[5] = props->Convolution.OrientUp[2]; + values[0] = props.OrientAt[0]; + values[1] = props.OrientAt[1]; + values[2] = props.OrientAt[2]; + values[3] = props.OrientUp[0]; + values[4] = props.OrientUp[1]; + values[5] = props.OrientUp[2]; break; default: - Convolution_getParamf(props, param, values); + GetParamf(props, param, values); } } - -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Convolution.OrientAt = {0.0f, 0.0f, -1.0f}; - props.Convolution.OrientUp = {0.0f, 1.0f, 0.0f}; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Convolution); - -const EffectProps ConvolutionEffectProps{genDefaultProps()}; diff --git a/al/effects/dedicated.cpp b/al/effects/dedicated.cpp index db57003c..518c224c 100644 --- a/al/effects/dedicated.cpp +++ b/al/effects/dedicated.cpp @@ -12,61 +12,115 @@ namespace { -void Dedicated_setParami(EffectProps*, ALenum param, int) +EffectProps genDefaultDialogProps() noexcept +{ + DedicatedDialogProps props{}; + props.Gain = 1.0f; + return props; +} + +EffectProps genDefaultLfeProps() noexcept +{ + DedicatedLfeProps props{}; + props.Gain = 1.0f; + return props; +} + +} // namespace + +const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()}; + +void EffectHandler::SetParami(DedicatedDialogProps&, ALenum param, int) { throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } -void Dedicated_setParamiv(EffectProps*, ALenum param, const int*) +void EffectHandler::SetParamiv(DedicatedDialogProps&, ALenum param, const int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param}; } -void Dedicated_setParamf(EffectProps *props, ALenum param, float val) +void EffectHandler::SetParamf(DedicatedDialogProps &props, ALenum param, float val) { switch(param) { case AL_DEDICATED_GAIN: if(!(val >= 0.0f && std::isfinite(val))) throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"}; - props->Dedicated.Gain = val; + props.Gain = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; } } -void Dedicated_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Dedicated_setParamf(props, param, vals[0]); } +void EffectHandler::SetParamfv(DedicatedDialogProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, vals[0]); } -void Dedicated_getParami(const EffectProps*, ALenum param, int*) +void EffectHandler::GetParami(const DedicatedDialogProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } -void Dedicated_getParamiv(const EffectProps*, ALenum param, int*) +void EffectHandler::GetParamiv(const DedicatedDialogProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param}; } -void Dedicated_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::GetParamf(const DedicatedDialogProps &props, ALenum param, float *val) { switch(param) { case AL_DEDICATED_GAIN: - *val = props->Dedicated.Gain; + *val = props.Gain; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; } } -void Dedicated_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Dedicated_getParamf(props, param, vals); } +void EffectHandler::GetParamfv(const DedicatedDialogProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept + +const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()}; + +void EffectHandler::SetParami(DedicatedLfeProps&, ALenum param, int) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } +void EffectHandler::SetParamiv(DedicatedLfeProps&, ALenum param, const int*) { - EffectProps props{}; - props.Dedicated.Gain = 1.0f; - return props; + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", + param}; } +void EffectHandler::SetParamf(DedicatedLfeProps &props, ALenum param, float val) +{ + switch(param) + { + case AL_DEDICATED_GAIN: + if(!(val >= 0.0f && std::isfinite(val))) + throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"}; + props.Gain = val; + break; -} // namespace + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; + } +} +void EffectHandler::SetParamfv(DedicatedLfeProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, vals[0]); } -DEFINE_ALEFFECT_VTABLE(Dedicated); +void EffectHandler::GetParami(const DedicatedLfeProps&, ALenum param, int*) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } +void EffectHandler::GetParamiv(const DedicatedLfeProps&, ALenum param, int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", + param}; +} +void EffectHandler::GetParamf(const DedicatedLfeProps &props, ALenum param, float *val) +{ + switch(param) + { + case AL_DEDICATED_GAIN: + *val = props.Gain; + break; -const EffectProps DedicatedEffectProps{genDefaultProps()}; + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; + } +} +void EffectHandler::GetParamfv(const DedicatedLfeProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp index e046d8e7..534b6c24 100644 --- a/al/effects/distortion.cpp +++ b/al/effects/distortion.cpp @@ -16,108 +16,93 @@ namespace { -void Distortion_setParami(EffectProps*, ALenum param, int) +EffectProps genDefaultProps() noexcept +{ + DistortionProps props{}; + props.Edge = AL_DISTORTION_DEFAULT_EDGE; + props.Gain = AL_DISTORTION_DEFAULT_GAIN; + props.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF; + props.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER; + props.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH; + return props; +} + +} // namespace + +const EffectProps DistortionEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(DistortionProps&, ALenum param, int) { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; } -void Distortion_setParamiv(EffectProps*, ALenum param, const int*) +void EffectHandler::SetParamiv(DistortionProps&, ALenum param, const int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param}; } -void Distortion_setParamf(EffectProps *props, ALenum param, float val) +void EffectHandler::SetParamf(DistortionProps &props, ALenum param, float val) { switch(param) { case AL_DISTORTION_EDGE: if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE)) throw effect_exception{AL_INVALID_VALUE, "Distortion edge out of range"}; - props->Distortion.Edge = val; + props.Edge = val; break; case AL_DISTORTION_GAIN: if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Distortion gain out of range"}; - props->Distortion.Gain = val; + props.Gain = val; break; case AL_DISTORTION_LOWPASS_CUTOFF: if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF)) throw effect_exception{AL_INVALID_VALUE, "Distortion low-pass cutoff out of range"}; - props->Distortion.LowpassCutoff = val; + props.LowpassCutoff = val; break; case AL_DISTORTION_EQCENTER: if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER)) throw effect_exception{AL_INVALID_VALUE, "Distortion EQ center out of range"}; - props->Distortion.EQCenter = val; + props.EQCenter = val; break; case AL_DISTORTION_EQBANDWIDTH: if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH)) throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"}; - props->Distortion.EQBandwidth = val; + props.EQBandwidth = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param}; } } -void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Distortion_setParamf(props, param, vals[0]); } +void EffectHandler::SetParamfv(DistortionProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, vals[0]); } -void Distortion_getParami(const EffectProps*, ALenum param, int*) +void EffectHandler::GetParami(const DistortionProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; } -void Distortion_getParamiv(const EffectProps*, ALenum param, int*) +void EffectHandler::GetParamiv(const DistortionProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param}; } -void Distortion_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::GetParamf(const DistortionProps &props, ALenum param, float *val) { switch(param) { - case AL_DISTORTION_EDGE: - *val = props->Distortion.Edge; - break; - - case AL_DISTORTION_GAIN: - *val = props->Distortion.Gain; - break; - - case AL_DISTORTION_LOWPASS_CUTOFF: - *val = props->Distortion.LowpassCutoff; - break; - - case AL_DISTORTION_EQCENTER: - *val = props->Distortion.EQCenter; - break; - - case AL_DISTORTION_EQBANDWIDTH: - *val = props->Distortion.EQBandwidth; - break; + case AL_DISTORTION_EDGE: *val = props.Edge; break; + case AL_DISTORTION_GAIN: *val = props.Gain; break; + case AL_DISTORTION_LOWPASS_CUTOFF: *val = props.LowpassCutoff; break; + case AL_DISTORTION_EQCENTER: *val = props.EQCenter; break; + case AL_DISTORTION_EQBANDWIDTH: *val = props.EQBandwidth; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param}; } } -void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Distortion_getParamf(props, param, vals); } - -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE; - props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN; - props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF; - props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER; - props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH; - return props; -} +void EffectHandler::GetParamfv(const DistortionProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Distortion); - -const EffectProps DistortionEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { @@ -204,26 +189,26 @@ template<> throw Exception{message}; } -template<> -bool DistortionCommitter::commit(const EaxEffectProps &props) +bool EaxDistortionCommitter::commit(const EAXDISTORTIONPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if<EAXDISTORTIONPROPERTIES>(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get<EAXDISTORTIONPROPERTIES>(props); - mAlProps.Distortion.Edge = eaxprops.flEdge; - mAlProps.Distortion.Gain = level_mb_to_gain(static_cast<float>(eaxprops.lGain)); - mAlProps.Distortion.LowpassCutoff = eaxprops.flLowPassCutOff; - mAlProps.Distortion.EQCenter = eaxprops.flEQCenter; - mAlProps.Distortion.EQBandwidth = eaxprops.flEdge; + mAlProps = [&]{ + DistortionProps ret{}; + ret.Edge = props.flEdge; + ret.Gain = level_mb_to_gain(static_cast<float>(props.lGain)); + ret.LowpassCutoff = props.flLowPassCutOff; + ret.EQCenter = props.flEQCenter; + ret.EQBandwidth = props.flEdge; + return ret; + }(); return true; } -template<> -void DistortionCommitter::SetDefaults(EaxEffectProps &props) +void EaxDistortionCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXDISTORTIONPROPERTIES defprops{[] { @@ -238,10 +223,8 @@ void DistortionCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxDistortionCommitter::Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props) { - auto &props = std::get<EAXDISTORTIONPROPERTIES>(props_); switch(call.get_property_id()) { case EAXDISTORTION_NONE: break; @@ -255,10 +238,8 @@ void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxDistortionCommitter::Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props) { - auto &props = std::get<EAXDISTORTIONPROPERTIES>(props_); switch(call.get_property_id()) { case EAXDISTORTION_NONE: break; diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp index 48aacef3..9412039b 100644 --- a/al/effects/echo.cpp +++ b/al/effects/echo.cpp @@ -19,102 +19,87 @@ namespace { static_assert(EchoMaxDelay >= AL_ECHO_MAX_DELAY, "Echo max delay too short"); static_assert(EchoMaxLRDelay >= AL_ECHO_MAX_LRDELAY, "Echo max left-right delay too short"); -void Echo_setParami(EffectProps*, ALenum param, int) +EffectProps genDefaultProps() noexcept +{ + EchoProps props{}; + props.Delay = AL_ECHO_DEFAULT_DELAY; + props.LRDelay = AL_ECHO_DEFAULT_LRDELAY; + props.Damping = AL_ECHO_DEFAULT_DAMPING; + props.Feedback = AL_ECHO_DEFAULT_FEEDBACK; + props.Spread = AL_ECHO_DEFAULT_SPREAD; + return props; +} + +} // namespace + +const EffectProps EchoEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(EchoProps&, ALenum param, int) { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; } -void Echo_setParamiv(EffectProps*, ALenum param, const int*) +void EffectHandler::SetParamiv(EchoProps&, ALenum param, const int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; } -void Echo_setParamf(EffectProps *props, ALenum param, float val) +void EffectHandler::SetParamf(EchoProps &props, ALenum param, float val) { switch(param) { case AL_ECHO_DELAY: if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY)) throw effect_exception{AL_INVALID_VALUE, "Echo delay out of range"}; - props->Echo.Delay = val; + props.Delay = val; break; case AL_ECHO_LRDELAY: if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY)) throw effect_exception{AL_INVALID_VALUE, "Echo LR delay out of range"}; - props->Echo.LRDelay = val; + props.LRDelay = val; break; case AL_ECHO_DAMPING: if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING)) throw effect_exception{AL_INVALID_VALUE, "Echo damping out of range"}; - props->Echo.Damping = val; + props.Damping = val; break; case AL_ECHO_FEEDBACK: if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK)) throw effect_exception{AL_INVALID_VALUE, "Echo feedback out of range"}; - props->Echo.Feedback = val; + props.Feedback = val; break; case AL_ECHO_SPREAD: if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD)) throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"}; - props->Echo.Spread = val; + props.Spread = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param}; } } -void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Echo_setParamf(props, param, vals[0]); } +void EffectHandler::SetParamfv(EchoProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, vals[0]); } -void Echo_getParami(const EffectProps*, ALenum param, int*) +void EffectHandler::GetParami(const EchoProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; } -void Echo_getParamiv(const EffectProps*, ALenum param, int*) +void EffectHandler::GetParamiv(const EchoProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; } -void Echo_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::GetParamf(const EchoProps &props, ALenum param, float *val) { switch(param) { - case AL_ECHO_DELAY: - *val = props->Echo.Delay; - break; - - case AL_ECHO_LRDELAY: - *val = props->Echo.LRDelay; - break; - - case AL_ECHO_DAMPING: - *val = props->Echo.Damping; - break; - - case AL_ECHO_FEEDBACK: - *val = props->Echo.Feedback; - break; - - case AL_ECHO_SPREAD: - *val = props->Echo.Spread; - break; + case AL_ECHO_DELAY: *val = props.Delay; break; + case AL_ECHO_LRDELAY: *val = props.LRDelay; break; + case AL_ECHO_DAMPING: *val = props.Damping; break; + case AL_ECHO_FEEDBACK: *val = props.Feedback; break; + case AL_ECHO_SPREAD: *val = props.Spread; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param}; } } -void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Echo_getParamf(props, param, vals); } - -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Echo.Delay = AL_ECHO_DEFAULT_DELAY; - props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY; - props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING; - props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK; - props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD; - return props; -} +void EffectHandler::GetParamfv(const EchoProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Echo); - -const EffectProps EchoEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { @@ -201,26 +186,26 @@ template<> throw Exception{message}; } -template<> -bool EchoCommitter::commit(const EaxEffectProps &props) +bool EaxEchoCommitter::commit(const EAXECHOPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if<EAXECHOPROPERTIES>(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get<EAXECHOPROPERTIES>(props); - mAlProps.Echo.Delay = eaxprops.flDelay; - mAlProps.Echo.LRDelay = eaxprops.flLRDelay; - mAlProps.Echo.Damping = eaxprops.flDamping; - mAlProps.Echo.Feedback = eaxprops.flFeedback; - mAlProps.Echo.Spread = eaxprops.flSpread; + mAlProps = [&]{ + EchoProps ret{}; + ret.Delay = props.flDelay; + ret.LRDelay = props.flLRDelay; + ret.Damping = props.flDamping; + ret.Feedback = props.flFeedback; + ret.Spread = props.flSpread; + return ret; + }(); return true; } -template<> -void EchoCommitter::SetDefaults(EaxEffectProps &props) +void EaxEchoCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXECHOPROPERTIES defprops{[] { @@ -235,10 +220,8 @@ void EchoCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxEchoCommitter::Get(const EaxCall &call, const EAXECHOPROPERTIES &props) { - auto &props = std::get<EAXECHOPROPERTIES>(props_); switch(call.get_property_id()) { case EAXECHO_NONE: break; @@ -252,10 +235,8 @@ void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxEchoCommitter::Set(const EaxCall &call, EAXECHOPROPERTIES &props) { - auto &props = std::get<EAXECHOPROPERTIES>(props_); switch(call.get_property_id()) { case EAXECHO_NONE: break; diff --git a/al/effects/effects.h b/al/effects/effects.h index 9d57dd82..38d47f86 100644 --- a/al/effects/effects.h +++ b/al/effects/effects.h @@ -3,52 +3,69 @@ #include "AL/al.h" +#include "core/effects/base.h" #include "core/except.h" #ifdef ALSOFT_EAX #include "al/eax/effect.h" #endif // ALSOFT_EAX -union EffectProps; +struct EffectHandler { +#define DECL_HANDLER(T) \ + static void SetParami(T &props, ALenum param, int val); \ + static void SetParamiv(T &props, ALenum param, const int *vals); \ + static void SetParamf(T &props, ALenum param, float val); \ + static void SetParamfv(T &props, ALenum param, const float *vals); \ + static void GetParami(const T &props, ALenum param, int *val); \ + static void GetParamiv(const T &props, ALenum param, int *vals); \ + static void GetParamf(const T &props, ALenum param, float *val); \ + static void GetParamfv(const T &props, ALenum param, float *vals); + + DECL_HANDLER(std::monostate) + DECL_HANDLER(ReverbProps) + DECL_HANDLER(ChorusProps) + DECL_HANDLER(AutowahProps) + DECL_HANDLER(CompressorProps) + DECL_HANDLER(ConvolutionProps) + DECL_HANDLER(DedicatedDialogProps) + DECL_HANDLER(DedicatedLfeProps) + DECL_HANDLER(DistortionProps) + DECL_HANDLER(EchoProps) + DECL_HANDLER(EqualizerProps) + DECL_HANDLER(FlangerProps) + DECL_HANDLER(FshifterProps) + DECL_HANDLER(ModulatorProps) + DECL_HANDLER(PshifterProps) + DECL_HANDLER(VmorpherProps) +#undef DECL_HANDLER + + static void StdReverbSetParami(ReverbProps &props, ALenum param, int val); + static void StdReverbSetParamiv(ReverbProps &props, ALenum param, const int *vals); + static void StdReverbSetParamf(ReverbProps &props, ALenum param, float val); + static void StdReverbSetParamfv(ReverbProps &props, ALenum param, const float *vals); + static void StdReverbGetParami(const ReverbProps &props, ALenum param, int *val); + static void StdReverbGetParamiv(const ReverbProps &props, ALenum param, int *vals); + static void StdReverbGetParamf(const ReverbProps &props, ALenum param, float *val); + static void StdReverbGetParamfv(const ReverbProps &props, ALenum param, float *vals); +}; class effect_exception final : public al::base_exception { ALenum mErrorCode; public: -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] +#ifdef __MINGW32__ + [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]] #else [[gnu::format(printf, 3, 4)]] #endif effect_exception(ALenum code, const char *msg, ...); ~effect_exception() override; - ALenum errorCode() const noexcept { return mErrorCode; } + [[nodiscard]] auto errorCode() const noexcept -> ALenum { return mErrorCode; } }; -struct EffectVtable { - void (*const setParami)(EffectProps *props, ALenum param, int val); - void (*const setParamiv)(EffectProps *props, ALenum param, const int *vals); - void (*const setParamf)(EffectProps *props, ALenum param, float val); - void (*const setParamfv)(EffectProps *props, ALenum param, const float *vals); - - void (*const getParami)(const EffectProps *props, ALenum param, int *val); - void (*const getParamiv)(const EffectProps *props, ALenum param, int *vals); - void (*const getParamf)(const EffectProps *props, ALenum param, float *val); - void (*const getParamfv)(const EffectProps *props, ALenum param, float *vals); -}; - -#define DEFINE_ALEFFECT_VTABLE(T) \ -const EffectVtable T##EffectVtable = { \ - T##_setParami, T##_setParamiv, \ - T##_setParamf, T##_setParamfv, \ - T##_getParami, T##_getParamiv, \ - T##_getParamf, T##_getParamfv, \ -} - - /* Default properties for the given effect types. */ extern const EffectProps NullEffectProps; extern const EffectProps ReverbEffectProps; @@ -64,25 +81,8 @@ extern const EffectProps FshifterEffectProps; extern const EffectProps ModulatorEffectProps; extern const EffectProps PshifterEffectProps; extern const EffectProps VmorpherEffectProps; -extern const EffectProps DedicatedEffectProps; +extern const EffectProps DedicatedDialogEffectProps; +extern const EffectProps DedicatedLfeEffectProps; extern const EffectProps ConvolutionEffectProps; -/* Vtables to get/set properties for the given effect types. */ -extern const EffectVtable NullEffectVtable; -extern const EffectVtable ReverbEffectVtable; -extern const EffectVtable StdReverbEffectVtable; -extern const EffectVtable AutowahEffectVtable; -extern const EffectVtable ChorusEffectVtable; -extern const EffectVtable CompressorEffectVtable; -extern const EffectVtable DistortionEffectVtable; -extern const EffectVtable EchoEffectVtable; -extern const EffectVtable EqualizerEffectVtable; -extern const EffectVtable FlangerEffectVtable; -extern const EffectVtable FshifterEffectVtable; -extern const EffectVtable ModulatorEffectVtable; -extern const EffectVtable PshifterEffectVtable; -extern const EffectVtable VmorpherEffectVtable; -extern const EffectVtable DedicatedEffectVtable; -extern const EffectVtable ConvolutionEffectVtable; - #endif /* AL_EFFECTS_EFFECTS_H */ diff --git a/al/effects/equalizer.cpp b/al/effects/equalizer.cpp index 76d5bdef..74fc43fc 100644 --- a/al/effects/equalizer.cpp +++ b/al/effects/equalizer.cpp @@ -16,163 +16,133 @@ namespace { -void Equalizer_setParami(EffectProps*, ALenum param, int) +EffectProps genDefaultProps() noexcept +{ + EqualizerProps props{}; + props.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF; + props.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN; + props.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER; + props.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN; + props.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH; + props.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER; + props.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN; + props.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH; + props.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF; + props.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN; + return props; +} + +} // namespace + +const EffectProps EqualizerEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(EqualizerProps&, ALenum param, int) { throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; } -void Equalizer_setParamiv(EffectProps*, ALenum param, const int*) +void EffectHandler::SetParamiv(EqualizerProps&, ALenum param, const int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param}; } -void Equalizer_setParamf(EffectProps *props, ALenum param, float val) +void EffectHandler::SetParamf(EqualizerProps &props, ALenum param, float val) { switch(param) { case AL_EQUALIZER_LOW_GAIN: if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band gain out of range"}; - props->Equalizer.LowGain = val; + props.LowGain = val; break; case AL_EQUALIZER_LOW_CUTOFF: if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF)) throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band cutoff out of range"}; - props->Equalizer.LowCutoff = val; + props.LowCutoff = val; break; case AL_EQUALIZER_MID1_GAIN: if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band gain out of range"}; - props->Equalizer.Mid1Gain = val; + props.Mid1Gain = val; break; case AL_EQUALIZER_MID1_CENTER: if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band center out of range"}; - props->Equalizer.Mid1Center = val; + props.Mid1Center = val; break; case AL_EQUALIZER_MID1_WIDTH: if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band width out of range"}; - props->Equalizer.Mid1Width = val; + props.Mid1Width = val; break; case AL_EQUALIZER_MID2_GAIN: if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band gain out of range"}; - props->Equalizer.Mid2Gain = val; + props.Mid2Gain = val; break; case AL_EQUALIZER_MID2_CENTER: if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band center out of range"}; - props->Equalizer.Mid2Center = val; + props.Mid2Center = val; break; case AL_EQUALIZER_MID2_WIDTH: if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band width out of range"}; - props->Equalizer.Mid2Width = val; + props.Mid2Width = val; break; case AL_EQUALIZER_HIGH_GAIN: if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band gain out of range"}; - props->Equalizer.HighGain = val; + props.HighGain = val; break; case AL_EQUALIZER_HIGH_CUTOFF: if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF)) throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"}; - props->Equalizer.HighCutoff = val; + props.HighCutoff = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param}; } } -void Equalizer_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Equalizer_setParamf(props, param, vals[0]); } +void EffectHandler::SetParamfv(EqualizerProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, vals[0]); } -void Equalizer_getParami(const EffectProps*, ALenum param, int*) +void EffectHandler::GetParami(const EqualizerProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; } -void Equalizer_getParamiv(const EffectProps*, ALenum param, int*) +void EffectHandler::GetParamiv(const EqualizerProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param}; } -void Equalizer_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::GetParamf(const EqualizerProps &props, ALenum param, float *val) { switch(param) { - case AL_EQUALIZER_LOW_GAIN: - *val = props->Equalizer.LowGain; - break; - - case AL_EQUALIZER_LOW_CUTOFF: - *val = props->Equalizer.LowCutoff; - break; - - case AL_EQUALIZER_MID1_GAIN: - *val = props->Equalizer.Mid1Gain; - break; - - case AL_EQUALIZER_MID1_CENTER: - *val = props->Equalizer.Mid1Center; - break; - - case AL_EQUALIZER_MID1_WIDTH: - *val = props->Equalizer.Mid1Width; - break; - - case AL_EQUALIZER_MID2_GAIN: - *val = props->Equalizer.Mid2Gain; - break; - - case AL_EQUALIZER_MID2_CENTER: - *val = props->Equalizer.Mid2Center; - break; - - case AL_EQUALIZER_MID2_WIDTH: - *val = props->Equalizer.Mid2Width; - break; - - case AL_EQUALIZER_HIGH_GAIN: - *val = props->Equalizer.HighGain; - break; - - case AL_EQUALIZER_HIGH_CUTOFF: - *val = props->Equalizer.HighCutoff; - break; + case AL_EQUALIZER_LOW_GAIN: *val = props.LowGain; break; + case AL_EQUALIZER_LOW_CUTOFF: *val = props.LowCutoff; break; + case AL_EQUALIZER_MID1_GAIN: *val = props.Mid1Gain; break; + case AL_EQUALIZER_MID1_CENTER: *val = props.Mid1Center; break; + case AL_EQUALIZER_MID1_WIDTH: *val = props.Mid1Width; break; + case AL_EQUALIZER_MID2_GAIN: *val = props.Mid2Gain; break; + case AL_EQUALIZER_MID2_CENTER: *val = props.Mid2Center; break; + case AL_EQUALIZER_MID2_WIDTH: *val = props.Mid2Width; break; + case AL_EQUALIZER_HIGH_GAIN: *val = props.HighGain; break; + case AL_EQUALIZER_HIGH_CUTOFF: *val = props.HighCutoff; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param}; } } -void Equalizer_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Equalizer_getParamf(props, param, vals); } - -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF; - props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN; - props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER; - props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN; - props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH; - props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER; - props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN; - props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH; - props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF; - props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN; - return props; -} +void EffectHandler::GetParamfv(const EqualizerProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Equalizer); - -const EffectProps EqualizerEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { @@ -319,31 +289,31 @@ template<> throw Exception{message}; } -template<> -bool EqualizerCommitter::commit(const EaxEffectProps &props) +bool EaxEqualizerCommitter::commit(const EAXEQUALIZERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if<EAXEQUALIZERPROPERTIES>(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get<EAXEQUALIZERPROPERTIES>(props); - mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast<float>(eaxprops.lLowGain)); - mAlProps.Equalizer.LowCutoff = eaxprops.flLowCutOff; - mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast<float>(eaxprops.lMid1Gain)); - mAlProps.Equalizer.Mid1Center = eaxprops.flMid1Center; - mAlProps.Equalizer.Mid1Width = eaxprops.flMid1Width; - mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast<float>(eaxprops.lMid2Gain)); - mAlProps.Equalizer.Mid2Center = eaxprops.flMid2Center; - mAlProps.Equalizer.Mid2Width = eaxprops.flMid2Width; - mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast<float>(eaxprops.lHighGain)); - mAlProps.Equalizer.HighCutoff = eaxprops.flHighCutOff; + mAlProps = [&]{ + EqualizerProps ret{}; + ret.LowGain = level_mb_to_gain(static_cast<float>(props.lLowGain)); + ret.LowCutoff = props.flLowCutOff; + ret.Mid1Gain = level_mb_to_gain(static_cast<float>(props.lMid1Gain)); + ret.Mid1Center = props.flMid1Center; + ret.Mid1Width = props.flMid1Width; + ret.Mid2Gain = level_mb_to_gain(static_cast<float>(props.lMid2Gain)); + ret.Mid2Center = props.flMid2Center; + ret.Mid2Width = props.flMid2Width; + ret.HighGain = level_mb_to_gain(static_cast<float>(props.lHighGain)); + ret.HighCutoff = props.flHighCutOff; + return ret; + }(); return true; } -template<> -void EqualizerCommitter::SetDefaults(EaxEffectProps &props) +void EaxEqualizerCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXEQUALIZERPROPERTIES defprops{[] { @@ -363,10 +333,8 @@ void EqualizerCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxEqualizerCommitter::Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props) { - auto &props = std::get<EAXEQUALIZERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXEQUALIZER_NONE: break; @@ -385,10 +353,8 @@ void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxEqualizerCommitter::Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props) { - auto &props = std::get<EAXEQUALIZERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXEQUALIZER_NONE: break; diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp index 37c372c3..556244ac 100644 --- a/al/effects/fshifter.cpp +++ b/al/effects/fshifter.cpp @@ -41,31 +41,26 @@ ALenum EnumFromDirection(FShifterDirection dir) throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast<int>(dir))}; } -void Fshifter_setParamf(EffectProps *props, ALenum param, float val) +EffectProps genDefaultProps() noexcept { - switch(param) - { - case AL_FREQUENCY_SHIFTER_FREQUENCY: - if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY)) - throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"}; - props->Fshifter.Frequency = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", - param}; - } + FshifterProps props{}; + props.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY; + props.LeftDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION).value(); + props.RightDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION).value(); + return props; } -void Fshifter_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Fshifter_setParamf(props, param, vals[0]); } -void Fshifter_setParami(EffectProps *props, ALenum param, int val) +} // namespace + +const EffectProps FshifterEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(FshifterProps &props, ALenum param, int val) { switch(param) { case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION: if(auto diropt = DirectionFromEmum(val)) - props->Fshifter.LeftDirection = *diropt; + props.LeftDirection = *diropt; else throw effect_exception{AL_INVALID_VALUE, "Unsupported frequency shifter left direction: 0x%04x", val}; @@ -73,7 +68,7 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val) case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION: if(auto diropt = DirectionFromEmum(val)) - props->Fshifter.RightDirection = *diropt; + props.RightDirection = *diropt; else throw effect_exception{AL_INVALID_VALUE, "Unsupported frequency shifter right direction: 0x%04x", val}; @@ -84,33 +79,52 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val) "Invalid frequency shifter integer property 0x%04x", param}; } } -void Fshifter_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Fshifter_setParami(props, param, vals[0]); } +void EffectHandler::SetParamiv(FshifterProps &props, ALenum param, const int *vals) +{ SetParami(props, param, vals[0]); } + +void EffectHandler::SetParamf(FshifterProps &props, ALenum param, float val) +{ + switch(param) + { + case AL_FREQUENCY_SHIFTER_FREQUENCY: + if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY)) + throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"}; + props.Frequency = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", + param}; + } +} +void EffectHandler::SetParamfv(FshifterProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, vals[0]); } -void Fshifter_getParami(const EffectProps *props, ALenum param, int *val) +void EffectHandler::GetParami(const FshifterProps &props, ALenum param, int *val) { switch(param) { case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION: - *val = EnumFromDirection(props->Fshifter.LeftDirection); + *val = EnumFromDirection(props.LeftDirection); break; case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION: - *val = EnumFromDirection(props->Fshifter.RightDirection); + *val = EnumFromDirection(props.RightDirection); break; + default: throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param}; } } -void Fshifter_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Fshifter_getParami(props, param, vals); } +void EffectHandler::GetParamiv(const FshifterProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } -void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::GetParamf(const FshifterProps &props, ALenum param, float *val) { switch(param) { case AL_FREQUENCY_SHIFTER_FREQUENCY: - *val = props->Fshifter.Frequency; + *val = props.Frequency; break; default: @@ -118,23 +132,9 @@ void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val) param}; } } -void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Fshifter_getParamf(props, param, vals); } +void EffectHandler::GetParamfv(const FshifterProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY; - props.Fshifter.LeftDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION); - props.Fshifter.RightDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION); - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Fshifter); - -const EffectProps FshifterEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { @@ -197,10 +197,9 @@ template<> throw Exception{message}; } -template<> -bool FrequencyShifterCommitter::commit(const EaxEffectProps &props) +bool EaxFrequencyShifterCommitter::commit(const EAXFREQUENCYSHIFTERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if<EAXFREQUENCYSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; @@ -214,16 +213,18 @@ bool FrequencyShifterCommitter::commit(const EaxEffectProps &props) return FShifterDirection::Off; }; - auto &eaxprops = std::get<EAXFREQUENCYSHIFTERPROPERTIES>(props); - mAlProps.Fshifter.Frequency = eaxprops.flFrequency; - mAlProps.Fshifter.LeftDirection = get_direction(eaxprops.ulLeftDirection); - mAlProps.Fshifter.RightDirection = get_direction(eaxprops.ulRightDirection); + mAlProps = [&]{ + FshifterProps ret{}; + ret.Frequency = props.flFrequency; + ret.LeftDirection = get_direction(props.ulLeftDirection); + ret.RightDirection = get_direction(props.ulRightDirection); + return ret; + }(); return true; } -template<> -void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props) +void EaxFrequencyShifterCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXFREQUENCYSHIFTERPROPERTIES defprops{[] { @@ -236,10 +237,8 @@ void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxFrequencyShifterCommitter::Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props) { - auto &props = std::get<EAXFREQUENCYSHIFTERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXFREQUENCYSHIFTER_NONE: break; @@ -251,10 +250,8 @@ void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &p } } -template<> -void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxFrequencyShifterCommitter::Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props) { - auto &props = std::get<EAXFREQUENCYSHIFTERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXFREQUENCYSHIFTER_NONE: break; diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp index 366e7ef7..7e9424c0 100644 --- a/al/effects/modulator.cpp +++ b/al/effects/modulator.cpp @@ -42,40 +42,31 @@ ALenum EnumFromWaveform(ModulatorWaveform type) std::to_string(static_cast<int>(type))}; } -void Modulator_setParamf(EffectProps *props, ALenum param, float val) +EffectProps genDefaultProps() noexcept { - switch(param) - { - case AL_RING_MODULATOR_FREQUENCY: - if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY)) - throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val}; - props->Modulator.Frequency = val; - break; + ModulatorProps props{}; + props.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY; + props.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF; + props.Waveform = WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM).value(); + return props; +} - case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF)) - throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val}; - props->Modulator.HighPassCutoff = val; - break; +} // namespace - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; - } -} -void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Modulator_setParamf(props, param, vals[0]); } -void Modulator_setParami(EffectProps *props, ALenum param, int val) +const EffectProps ModulatorEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(ModulatorProps &props, ALenum param, int val) { switch(param) { case AL_RING_MODULATOR_FREQUENCY: case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - Modulator_setParamf(props, param, static_cast<float>(val)); + SetParamf(props, param, static_cast<float>(val)); break; case AL_RING_MODULATOR_WAVEFORM: if(auto formopt = WaveformFromEmum(val)) - props->Modulator.Waveform = *formopt; + props.Waveform = *formopt; else throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val}; break; @@ -85,62 +76,61 @@ void Modulator_setParami(EffectProps *props, ALenum param, int val) param}; } } -void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Modulator_setParami(props, param, vals[0]); } +void EffectHandler::SetParamiv(ModulatorProps &props, ALenum param, const int *vals) +{ SetParami(props, param, vals[0]); } -void Modulator_getParami(const EffectProps *props, ALenum param, int *val) +void EffectHandler::SetParamf(ModulatorProps &props, ALenum param, float val) { switch(param) { case AL_RING_MODULATOR_FREQUENCY: - *val = static_cast<int>(props->Modulator.Frequency); + if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY)) + throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val}; + props.Frequency = val; break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - *val = static_cast<int>(props->Modulator.HighPassCutoff); - break; - case AL_RING_MODULATOR_WAVEFORM: - *val = EnumFromWaveform(props->Modulator.Waveform); + if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF)) + throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val}; + props.HighPassCutoff = val; break; default: + throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; + } +} +void EffectHandler::SetParamfv(ModulatorProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, vals[0]); } + +void EffectHandler::GetParami(const ModulatorProps &props, ALenum param, int *val) +{ + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: *val = static_cast<int>(props.Frequency); break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast<int>(props.HighPassCutoff); break; + case AL_RING_MODULATOR_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break; + + default: throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param}; } } -void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Modulator_getParami(props, param, vals); } -void Modulator_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::GetParamiv(const ModulatorProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } +void EffectHandler::GetParamf(const ModulatorProps &props, ALenum param, float *val) { switch(param) { - case AL_RING_MODULATOR_FREQUENCY: - *val = props->Modulator.Frequency; - break; - case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - *val = props->Modulator.HighPassCutoff; - break; + case AL_RING_MODULATOR_FREQUENCY: *val = props.Frequency; break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = props.HighPassCutoff; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; } } -void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Modulator_getParamf(props, param, vals); } +void EffectHandler::GetParamfv(const ModulatorProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY; - props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF; - props.Modulator.Waveform = *WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM); - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Modulator); - -const EffectProps ModulatorEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { @@ -203,10 +193,9 @@ template<> throw Exception{message}; } -template<> -bool ModulatorCommitter::commit(const EaxEffectProps &props) +bool EaxModulatorCommitter::commit(const EAXRINGMODULATORPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if<EAXRINGMODULATORPROPERTIES>(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; @@ -222,16 +211,18 @@ bool ModulatorCommitter::commit(const EaxEffectProps &props) return ModulatorWaveform::Sinusoid; }; - auto &eaxprops = std::get<EAXRINGMODULATORPROPERTIES>(props); - mAlProps.Modulator.Frequency = eaxprops.flFrequency; - mAlProps.Modulator.HighPassCutoff = eaxprops.flHighPassCutOff; - mAlProps.Modulator.Waveform = get_waveform(eaxprops.ulWaveform); + mAlProps = [&]{ + ModulatorProps ret{}; + ret.Frequency = props.flFrequency; + ret.HighPassCutoff = props.flHighPassCutOff; + ret.Waveform = get_waveform(props.ulWaveform); + return ret; + }(); return true; } -template<> -void ModulatorCommitter::SetDefaults(EaxEffectProps &props) +void EaxModulatorCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXRINGMODULATORPROPERTIES defprops{[] { @@ -244,10 +235,8 @@ void ModulatorCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxModulatorCommitter::Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props) { - auto &props = std::get<EAXRINGMODULATORPROPERTIES>(props_); switch(call.get_property_id()) { case EAXRINGMODULATOR_NONE: break; @@ -259,11 +248,9 @@ void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxModulatorCommitter::Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props) { - auto &props = std::get<EAXRINGMODULATORPROPERTIES>(props_); - switch (call.get_property_id()) + switch(call.get_property_id()) { case EAXRINGMODULATOR_NONE: break; case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props); break; diff --git a/al/effects/null.cpp b/al/effects/null.cpp index 1e8787e7..4b00ef4f 100644 --- a/al/effects/null.cpp +++ b/al/effects/null.cpp @@ -14,7 +14,17 @@ namespace { -void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + return props; +} + +} // namespace + +const EffectProps NullEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(std::monostate& /*props*/, ALenum param, int /*val*/) { switch(param) { @@ -23,15 +33,15 @@ void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) param}; } } -void Null_setParamiv(EffectProps *props, ALenum param, const int *vals) +void EffectHandler::SetParamiv(std::monostate &props, ALenum param, const int *vals) { switch(param) { default: - Null_setParami(props, param, vals[0]); + SetParami(props, param, vals[0]); } } -void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) +void EffectHandler::SetParamf(std::monostate& /*props*/, ALenum param, float /*val*/) { switch(param) { @@ -40,16 +50,16 @@ void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) param}; } } -void Null_setParamfv(EffectProps *props, ALenum param, const float *vals) +void EffectHandler::SetParamfv(std::monostate &props, ALenum param, const float *vals) { switch(param) { default: - Null_setParamf(props, param, vals[0]); + SetParamf(props, param, vals[0]); } } -void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) +void EffectHandler::GetParami(const std::monostate& /*props*/, ALenum param, int* /*val*/) { switch(param) { @@ -58,15 +68,15 @@ void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) param}; } } -void Null_getParamiv(const EffectProps *props, ALenum param, int *vals) +void EffectHandler::GetParamiv(const std::monostate &props, ALenum param, int *vals) { switch(param) { default: - Null_getParami(props, param, vals); + GetParami(props, param, vals); } } -void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) +void EffectHandler::GetParamf(const std::monostate& /*props*/, ALenum param, float* /*val*/) { switch(param) { @@ -75,27 +85,15 @@ void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) param}; } } -void Null_getParamfv(const EffectProps *props, ALenum param, float *vals) +void EffectHandler::GetParamfv(const std::monostate &props, ALenum param, float *vals) { switch(param) { default: - Null_getParamf(props, param, vals); + GetParamf(props, param, vals); } } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Null); - -const EffectProps NullEffectProps{genDefaultProps()}; - #ifdef ALSOFT_EAX namespace { @@ -117,29 +115,26 @@ template<> throw Exception{message}; } -template<> -bool NullCommitter::commit(const EaxEffectProps &props) +bool EaxNullCommitter::commit(const std::monostate &props) { - const bool ret{props != mEaxProps}; + const bool ret{std::holds_alternative<std::monostate>(mEaxProps)}; mEaxProps = props; + mAlProps = std::monostate{}; return ret; } -template<> -void NullCommitter::SetDefaults(EaxEffectProps &props) +void EaxNullCommitter::SetDefaults(EaxEffectProps &props) { - props.emplace<std::monostate>(); + props = std::monostate{}; } -template<> -void NullCommitter::Get(const EaxCall &call, const EaxEffectProps&) +void EaxNullCommitter::Get(const EaxCall &call, const std::monostate&) { if(call.get_property_id() != 0) fail_unknown_property_id(); } -template<> -void NullCommitter::Set(const EaxCall &call, EaxEffectProps&) +void EaxNullCommitter::Set(const EaxCall &call, std::monostate&) { if(call.get_property_id() != 0) fail_unknown_property_id(); diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp index 10824016..b36fe034 100644 --- a/al/effects/pshifter.cpp +++ b/al/effects/pshifter.cpp @@ -16,28 +16,32 @@ namespace { -void Pshifter_setParamf(EffectProps*, ALenum param, float) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; } -void Pshifter_setParamfv(EffectProps*, ALenum param, const float*) +EffectProps genDefaultProps() noexcept { - throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", - param}; + PshifterProps props{}; + props.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE; + props.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE; + return props; } -void Pshifter_setParami(EffectProps *props, ALenum param, int val) +} // namespace + +const EffectProps PshifterEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(PshifterProps &props, ALenum param, int val) { switch(param) { case AL_PITCH_SHIFTER_COARSE_TUNE: if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE)) throw effect_exception{AL_INVALID_VALUE, "Pitch shifter coarse tune out of range"}; - props->Pshifter.CoarseTune = val; + props.CoarseTune = val; break; case AL_PITCH_SHIFTER_FINE_TUNE: if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE)) throw effect_exception{AL_INVALID_VALUE, "Pitch shifter fine tune out of range"}; - props->Pshifter.FineTune = val; + props.FineTune = val; break; default: @@ -45,49 +49,40 @@ void Pshifter_setParami(EffectProps *props, ALenum param, int val) param}; } } -void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Pshifter_setParami(props, param, vals[0]); } +void EffectHandler::SetParamiv(PshifterProps &props, ALenum param, const int *vals) +{ SetParami(props, param, vals[0]); } -void Pshifter_getParami(const EffectProps *props, ALenum param, int *val) +void EffectHandler::SetParamf(PshifterProps&, ALenum param, float) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; } +void EffectHandler::SetParamfv(PshifterProps&, ALenum param, const float*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", + param}; +} + +void EffectHandler::GetParami(const PshifterProps &props, ALenum param, int *val) { switch(param) { - case AL_PITCH_SHIFTER_COARSE_TUNE: - *val = props->Pshifter.CoarseTune; - break; - case AL_PITCH_SHIFTER_FINE_TUNE: - *val = props->Pshifter.FineTune; - break; + case AL_PITCH_SHIFTER_COARSE_TUNE: *val = props.CoarseTune; break; + case AL_PITCH_SHIFTER_FINE_TUNE: *val = props.FineTune; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param}; } } -void Pshifter_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Pshifter_getParami(props, param, vals); } +void EffectHandler::GetParamiv(const PshifterProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } -void Pshifter_getParamf(const EffectProps*, ALenum param, float*) +void EffectHandler::GetParamf(const PshifterProps&, ALenum param, float*) { throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; } -void Pshifter_getParamfv(const EffectProps*, ALenum param, float*) +void EffectHandler::GetParamfv(const PshifterProps&, ALenum param, float*) { throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", param}; } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE; - props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Pshifter); - -const EffectProps PshifterEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { @@ -138,32 +133,30 @@ template<> throw Exception{message}; } -template<> -bool PitchShifterCommitter::commit(const EaxEffectProps &props) +bool EaxPitchShifterCommitter::commit(const EAXPITCHSHIFTERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if<EAXPITCHSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get<EAXPITCHSHIFTERPROPERTIES>(props); - mAlProps.Pshifter.CoarseTune = static_cast<int>(eaxprops.lCoarseTune); - mAlProps.Pshifter.FineTune = static_cast<int>(eaxprops.lFineTune); + mAlProps = [&]{ + PshifterProps ret{}; + ret.CoarseTune = static_cast<int>(props.lCoarseTune); + ret.FineTune = static_cast<int>(props.lFineTune); + return ret; + }(); return true; } -template<> -void PitchShifterCommitter::SetDefaults(EaxEffectProps &props) +void EaxPitchShifterCommitter::SetDefaults(EaxEffectProps &props) { props = EAXPITCHSHIFTERPROPERTIES{EAXPITCHSHIFTER_DEFAULTCOARSETUNE, EAXPITCHSHIFTER_DEFAULTFINETUNE}; } -template<> -void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxPitchShifterCommitter::Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props) { - auto &props = std::get<EAXPITCHSHIFTERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXPITCHSHIFTER_NONE: break; @@ -174,10 +167,8 @@ void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props } } -template<> -void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxPitchShifterCommitter::Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props) { - auto &props = std::get<EAXPITCHSHIFTERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXPITCHSHIFTER_NONE: break; diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp index b037443f..f0df51b2 100644 --- a/al/effects/reverb.cpp +++ b/al/effects/reverb.cpp @@ -20,14 +20,80 @@ namespace { -void Reverb_setParami(EffectProps *props, ALenum param, int val) +EffectProps genDefaultProps() noexcept +{ + ReverbProps props{}; + props.Density = AL_EAXREVERB_DEFAULT_DENSITY; + props.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION; + props.Gain = AL_EAXREVERB_DEFAULT_GAIN; + props.GainHF = AL_EAXREVERB_DEFAULT_GAINHF; + props.GainLF = AL_EAXREVERB_DEFAULT_GAINLF; + props.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME; + props.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO; + props.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO; + props.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN; + props.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY; + props.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN; + props.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY; + props.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME; + props.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH; + props.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME; + props.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH; + props.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF; + props.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE; + props.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE; + props.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; + props.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT; + return props; +} + +EffectProps genDefaultStdProps() noexcept +{ + ReverbProps props{}; + props.Density = AL_REVERB_DEFAULT_DENSITY; + props.Diffusion = AL_REVERB_DEFAULT_DIFFUSION; + props.Gain = AL_REVERB_DEFAULT_GAIN; + props.GainHF = AL_REVERB_DEFAULT_GAINHF; + props.GainLF = 1.0f; + props.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME; + props.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO; + props.DecayLFRatio = 1.0f; + props.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN; + props.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY; + props.ReflectionsPan = {0.0f, 0.0f, 0.0f}; + props.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN; + props.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY; + props.LateReverbPan = {0.0f, 0.0f, 0.0f}; + props.EchoTime = 0.25f; + props.EchoDepth = 0.0f; + props.ModulationTime = 0.25f; + props.ModulationDepth = 0.0f; + props.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF; + props.HFReference = 5000.0f; + props.LFReference = 250.0f; + props.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; + props.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; + return props; +} + +} // namespace + +const EffectProps ReverbEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(ReverbProps &props, ALenum param, int val) { switch(param) { case AL_EAXREVERB_DECAY_HFLIMIT: if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"}; - props->Reverb.DecayHFLimit = val != AL_FALSE; + props.DecayHFLimit = val != AL_FALSE; break; default: @@ -35,167 +101,167 @@ void Reverb_setParami(EffectProps *props, ALenum param, int val) param}; } } -void Reverb_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Reverb_setParami(props, param, vals[0]); } -void Reverb_setParamf(EffectProps *props, ALenum param, float val) +void EffectHandler::SetParamiv(ReverbProps &props, ALenum param, const int *vals) +{ SetParami(props, param, vals[0]); } +void EffectHandler::SetParamf(ReverbProps &props, ALenum param, float val) { switch(param) { case AL_EAXREVERB_DENSITY: if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb density out of range"}; - props->Reverb.Density = val; + props.Density = val; break; case AL_EAXREVERB_DIFFUSION: if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb diffusion out of range"}; - props->Reverb.Diffusion = val; + props.Diffusion = val; break; case AL_EAXREVERB_GAIN: if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gain out of range"}; - props->Reverb.Gain = val; + props.Gain = val; break; case AL_EAXREVERB_GAINHF: if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainhf out of range"}; - props->Reverb.GainHF = val; + props.GainHF = val; break; case AL_EAXREVERB_GAINLF: if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainlf out of range"}; - props->Reverb.GainLF = val; + props.GainLF = val; break; case AL_EAXREVERB_DECAY_TIME: if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay time out of range"}; - props->Reverb.DecayTime = val; + props.DecayTime = val; break; case AL_EAXREVERB_DECAY_HFRATIO: if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"}; - props->Reverb.DecayHFRatio = val; + props.DecayHFRatio = val; break; case AL_EAXREVERB_DECAY_LFRATIO: if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay lfratio out of range"}; - props->Reverb.DecayLFRatio = val; + props.DecayLFRatio = val; break; case AL_EAXREVERB_REFLECTIONS_GAIN: if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"}; - props->Reverb.ReflectionsGain = val; + props.ReflectionsGain = val; break; case AL_EAXREVERB_REFLECTIONS_DELAY: if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"}; - props->Reverb.ReflectionsDelay = val; + props.ReflectionsDelay = val; break; case AL_EAXREVERB_LATE_REVERB_GAIN: if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"}; - props->Reverb.LateReverbGain = val; + props.LateReverbGain = val; break; case AL_EAXREVERB_LATE_REVERB_DELAY: if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"}; - props->Reverb.LateReverbDelay = val; + props.LateReverbDelay = val; break; case AL_EAXREVERB_ECHO_TIME: if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo time out of range"}; - props->Reverb.EchoTime = val; + props.EchoTime = val; break; case AL_EAXREVERB_ECHO_DEPTH: if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo depth out of range"}; - props->Reverb.EchoDepth = val; + props.EchoDepth = val; break; case AL_EAXREVERB_MODULATION_TIME: if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation time out of range"}; - props->Reverb.ModulationTime = val; + props.ModulationTime = val; break; case AL_EAXREVERB_MODULATION_DEPTH: if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation depth out of range"}; - props->Reverb.ModulationDepth = val; + props.ModulationDepth = val; break; case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"}; - props->Reverb.AirAbsorptionGainHF = val; + props.AirAbsorptionGainHF = val; break; case AL_EAXREVERB_HFREFERENCE: if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb hfreference out of range"}; - props->Reverb.HFReference = val; + props.HFReference = val; break; case AL_EAXREVERB_LFREFERENCE: if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb lfreference out of range"}; - props->Reverb.LFReference = val; + props.LFReference = val; break; case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"}; - props->Reverb.RoomRolloffFactor = val; + props.RoomRolloffFactor = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; } } -void Reverb_setParamfv(EffectProps *props, ALenum param, const float *vals) +void EffectHandler::SetParamfv(ReverbProps &props, ALenum param, const float *vals) { switch(param) { case AL_EAXREVERB_REFLECTIONS_PAN: if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2]))) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections pan out of range"}; - props->Reverb.ReflectionsPan[0] = vals[0]; - props->Reverb.ReflectionsPan[1] = vals[1]; - props->Reverb.ReflectionsPan[2] = vals[2]; + props.ReflectionsPan[0] = vals[0]; + props.ReflectionsPan[1] = vals[1]; + props.ReflectionsPan[2] = vals[2]; break; case AL_EAXREVERB_LATE_REVERB_PAN: if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2]))) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb pan out of range"}; - props->Reverb.LateReverbPan[0] = vals[0]; - props->Reverb.LateReverbPan[1] = vals[1]; - props->Reverb.LateReverbPan[2] = vals[2]; + props.LateReverbPan[0] = vals[0]; + props.LateReverbPan[1] = vals[1]; + props.LateReverbPan[2] = vals[2]; break; default: - Reverb_setParamf(props, param, vals[0]); + SetParamf(props, param, vals[0]); break; } } -void Reverb_getParami(const EffectProps *props, ALenum param, int *val) +void EffectHandler::GetParami(const ReverbProps &props, ALenum param, int *val) { switch(param) { case AL_EAXREVERB_DECAY_HFLIMIT: - *val = props->Reverb.DecayHFLimit; + *val = props.DecayHFLimit; break; default: @@ -203,365 +269,214 @@ void Reverb_getParami(const EffectProps *props, ALenum param, int *val) param}; } } -void Reverb_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Reverb_getParami(props, param, vals); } -void Reverb_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::GetParamiv(const ReverbProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } +void EffectHandler::GetParamf(const ReverbProps &props, ALenum param, float *val) { switch(param) { - case AL_EAXREVERB_DENSITY: - *val = props->Reverb.Density; - break; - - case AL_EAXREVERB_DIFFUSION: - *val = props->Reverb.Diffusion; - break; - - case AL_EAXREVERB_GAIN: - *val = props->Reverb.Gain; - break; - - case AL_EAXREVERB_GAINHF: - *val = props->Reverb.GainHF; - break; - - case AL_EAXREVERB_GAINLF: - *val = props->Reverb.GainLF; - break; - - case AL_EAXREVERB_DECAY_TIME: - *val = props->Reverb.DecayTime; - break; - - case AL_EAXREVERB_DECAY_HFRATIO: - *val = props->Reverb.DecayHFRatio; - break; - - case AL_EAXREVERB_DECAY_LFRATIO: - *val = props->Reverb.DecayLFRatio; - break; - - case AL_EAXREVERB_REFLECTIONS_GAIN: - *val = props->Reverb.ReflectionsGain; - break; - - case AL_EAXREVERB_REFLECTIONS_DELAY: - *val = props->Reverb.ReflectionsDelay; - break; - - case AL_EAXREVERB_LATE_REVERB_GAIN: - *val = props->Reverb.LateReverbGain; - break; - - case AL_EAXREVERB_LATE_REVERB_DELAY: - *val = props->Reverb.LateReverbDelay; - break; - - case AL_EAXREVERB_ECHO_TIME: - *val = props->Reverb.EchoTime; - break; - - case AL_EAXREVERB_ECHO_DEPTH: - *val = props->Reverb.EchoDepth; - break; - - case AL_EAXREVERB_MODULATION_TIME: - *val = props->Reverb.ModulationTime; - break; - - case AL_EAXREVERB_MODULATION_DEPTH: - *val = props->Reverb.ModulationDepth; - break; - - case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: - *val = props->Reverb.AirAbsorptionGainHF; - break; - - case AL_EAXREVERB_HFREFERENCE: - *val = props->Reverb.HFReference; - break; - - case AL_EAXREVERB_LFREFERENCE: - *val = props->Reverb.LFReference; - break; - - case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: - *val = props->Reverb.RoomRolloffFactor; - break; + case AL_EAXREVERB_DENSITY: *val = props.Density; break; + case AL_EAXREVERB_DIFFUSION: *val = props.Diffusion; break; + case AL_EAXREVERB_GAIN: *val = props.Gain; break; + case AL_EAXREVERB_GAINHF: *val = props.GainHF; break; + case AL_EAXREVERB_GAINLF: *val = props.GainLF; break; + case AL_EAXREVERB_DECAY_TIME: *val = props.DecayTime; break; + case AL_EAXREVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; break; + case AL_EAXREVERB_DECAY_LFRATIO: *val = props.DecayLFRatio; break; + case AL_EAXREVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; break; + case AL_EAXREVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; break; + case AL_EAXREVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; break; + case AL_EAXREVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; break; + case AL_EAXREVERB_ECHO_TIME: *val = props.EchoTime; break; + case AL_EAXREVERB_ECHO_DEPTH: *val = props.EchoDepth; break; + case AL_EAXREVERB_MODULATION_TIME: *val = props.ModulationTime; break; + case AL_EAXREVERB_MODULATION_DEPTH: *val = props.ModulationDepth; break; + case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; break; + case AL_EAXREVERB_HFREFERENCE: *val = props.HFReference; break; + case AL_EAXREVERB_LFREFERENCE: *val = props.LFReference; break; + case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; } } -void Reverb_getParamfv(const EffectProps *props, ALenum param, float *vals) +void EffectHandler::GetParamfv(const ReverbProps &props, ALenum param, float *vals) { switch(param) { case AL_EAXREVERB_REFLECTIONS_PAN: - vals[0] = props->Reverb.ReflectionsPan[0]; - vals[1] = props->Reverb.ReflectionsPan[1]; - vals[2] = props->Reverb.ReflectionsPan[2]; + vals[0] = props.ReflectionsPan[0]; + vals[1] = props.ReflectionsPan[1]; + vals[2] = props.ReflectionsPan[2]; break; case AL_EAXREVERB_LATE_REVERB_PAN: - vals[0] = props->Reverb.LateReverbPan[0]; - vals[1] = props->Reverb.LateReverbPan[1]; - vals[2] = props->Reverb.LateReverbPan[2]; + vals[0] = props.LateReverbPan[0]; + vals[1] = props.LateReverbPan[1]; + vals[2] = props.LateReverbPan[2]; break; default: - Reverb_getParamf(props, param, vals); + GetParamf(props, param, vals); break; } } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Reverb.Density = AL_EAXREVERB_DEFAULT_DENSITY; - props.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION; - props.Reverb.Gain = AL_EAXREVERB_DEFAULT_GAIN; - props.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF; - props.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF; - props.Reverb.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME; - props.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO; - props.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO; - props.Reverb.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN; - props.Reverb.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY; - props.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN; - props.Reverb.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY; - props.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME; - props.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH; - props.Reverb.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME; - props.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH; - props.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF; - props.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE; - props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE; - props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; - props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT; - return props; -} +const EffectProps StdReverbEffectProps{genDefaultStdProps()}; -void StdReverb_setParami(EffectProps *props, ALenum param, int val) +void EffectHandler::StdReverbSetParami(ReverbProps &props, ALenum param, int val) { switch(param) { case AL_REVERB_DECAY_HFLIMIT: if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay hflimit out of range"}; - props->Reverb.DecayHFLimit = val != AL_FALSE; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"}; + props.DecayHFLimit = val != AL_FALSE; break; default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param}; + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", + param}; } } -void StdReverb_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ StdReverb_setParami(props, param, vals[0]); } -void StdReverb_setParamf(EffectProps *props, ALenum param, float val) +void EffectHandler::StdReverbSetParamiv(ReverbProps &props, ALenum param, const int *vals) +{ StdReverbSetParami(props, param, vals[0]); } +void EffectHandler::StdReverbSetParamf(ReverbProps &props, ALenum param, float val) { switch(param) { case AL_REVERB_DENSITY: if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb density out of range"}; - props->Reverb.Density = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb density out of range"}; + props.Density = val; break; case AL_REVERB_DIFFUSION: if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION)) - throw effect_exception{AL_INVALID_VALUE, "Reverb diffusion out of range"}; - props->Reverb.Diffusion = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb diffusion out of range"}; + props.Diffusion = val; break; case AL_REVERB_GAIN: if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb gain out of range"}; - props->Reverb.Gain = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gain out of range"}; + props.Gain = val; break; case AL_REVERB_GAINHF: if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "Reverb gainhf out of range"}; - props->Reverb.GainHF = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainhf out of range"}; + props.GainHF = val; break; case AL_REVERB_DECAY_TIME: if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay time out of range"}; - props->Reverb.DecayTime = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay time out of range"}; + props.DecayTime = val; break; case AL_REVERB_DECAY_HFRATIO: if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay hfratio out of range"}; - props->Reverb.DecayHFRatio = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"}; + props.DecayHFRatio = val; break; case AL_REVERB_REFLECTIONS_GAIN: if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb reflections gain out of range"}; - props->Reverb.ReflectionsGain = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"}; + props.ReflectionsGain = val; break; case AL_REVERB_REFLECTIONS_DELAY: if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb reflections delay out of range"}; - props->Reverb.ReflectionsDelay = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"}; + props.ReflectionsDelay = val; break; case AL_REVERB_LATE_REVERB_GAIN: if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb gain out of range"}; - props->Reverb.LateReverbGain = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"}; + props.LateReverbGain = val; break; case AL_REVERB_LATE_REVERB_DELAY: if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb delay out of range"}; - props->Reverb.LateReverbDelay = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"}; + props.LateReverbDelay = val; break; case AL_REVERB_AIR_ABSORPTION_GAINHF: if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "Reverb air absorption gainhf out of range"}; - props->Reverb.AirAbsorptionGainHF = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"}; + props.AirAbsorptionGainHF = val; break; case AL_REVERB_ROOM_ROLLOFF_FACTOR: if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR)) - throw effect_exception{AL_INVALID_VALUE, "Reverb room rolloff factor out of range"}; - props->Reverb.RoomRolloffFactor = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"}; + props.RoomRolloffFactor = val; break; default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param}; + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; + } +} +void EffectHandler::StdReverbSetParamfv(ReverbProps &props, ALenum param, const float *vals) +{ + switch(param) + { + default: + StdReverbSetParamf(props, param, vals[0]); + break; } } -void StdReverb_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ StdReverb_setParamf(props, param, vals[0]); } -void StdReverb_getParami(const EffectProps *props, ALenum param, int *val) +void EffectHandler::StdReverbGetParami(const ReverbProps &props, ALenum param, int *val) { switch(param) { case AL_REVERB_DECAY_HFLIMIT: - *val = props->Reverb.DecayHFLimit; + *val = props.DecayHFLimit; break; default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param}; + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", + param}; } } -void StdReverb_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ StdReverb_getParami(props, param, vals); } -void StdReverb_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::StdReverbGetParamiv(const ReverbProps &props, ALenum param, int *vals) +{ StdReverbGetParami(props, param, vals); } +void EffectHandler::StdReverbGetParamf(const ReverbProps &props, ALenum param, float *val) { switch(param) { - case AL_REVERB_DENSITY: - *val = props->Reverb.Density; - break; - - case AL_REVERB_DIFFUSION: - *val = props->Reverb.Diffusion; - break; - - case AL_REVERB_GAIN: - *val = props->Reverb.Gain; - break; - - case AL_REVERB_GAINHF: - *val = props->Reverb.GainHF; - break; - - case AL_REVERB_DECAY_TIME: - *val = props->Reverb.DecayTime; - break; - - case AL_REVERB_DECAY_HFRATIO: - *val = props->Reverb.DecayHFRatio; - break; - - case AL_REVERB_REFLECTIONS_GAIN: - *val = props->Reverb.ReflectionsGain; - break; - - case AL_REVERB_REFLECTIONS_DELAY: - *val = props->Reverb.ReflectionsDelay; - break; - - case AL_REVERB_LATE_REVERB_GAIN: - *val = props->Reverb.LateReverbGain; - break; - - case AL_REVERB_LATE_REVERB_DELAY: - *val = props->Reverb.LateReverbDelay; - break; - - case AL_REVERB_AIR_ABSORPTION_GAINHF: - *val = props->Reverb.AirAbsorptionGainHF; - break; - - case AL_REVERB_ROOM_ROLLOFF_FACTOR: - *val = props->Reverb.RoomRolloffFactor; - break; + case AL_REVERB_DENSITY: *val = props.Density; break; + case AL_REVERB_DIFFUSION: *val = props.Diffusion; break; + case AL_REVERB_GAIN: *val = props.Gain; break; + case AL_REVERB_GAINHF: *val = props.GainHF; break; + case AL_REVERB_DECAY_TIME: *val = props.DecayTime; break; + case AL_REVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; break; + case AL_REVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; break; + case AL_REVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; break; + case AL_REVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; break; + case AL_REVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; break; + case AL_REVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; break; + case AL_REVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; break; default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param}; + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; } } -void StdReverb_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ StdReverb_getParamf(props, param, vals); } - -EffectProps genDefaultStdProps() noexcept +void EffectHandler::StdReverbGetParamfv(const ReverbProps &props, ALenum param, float *vals) { - EffectProps props{}; - props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY; - props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION; - props.Reverb.Gain = AL_REVERB_DEFAULT_GAIN; - props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF; - props.Reverb.GainLF = 1.0f; - props.Reverb.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME; - props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO; - props.Reverb.DecayLFRatio = 1.0f; - props.Reverb.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN; - props.Reverb.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY; - props.Reverb.ReflectionsPan[0] = 0.0f; - props.Reverb.ReflectionsPan[1] = 0.0f; - props.Reverb.ReflectionsPan[2] = 0.0f; - props.Reverb.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN; - props.Reverb.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY; - props.Reverb.LateReverbPan[0] = 0.0f; - props.Reverb.LateReverbPan[1] = 0.0f; - props.Reverb.LateReverbPan[2] = 0.0f; - props.Reverb.EchoTime = 0.25f; - props.Reverb.EchoDepth = 0.0f; - props.Reverb.ModulationTime = 0.25f; - props.Reverb.ModulationDepth = 0.0f; - props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF; - props.Reverb.HFReference = 5000.0f; - props.Reverb.LFReference = 250.0f; - props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; - props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; - return props; + switch(param) + { + default: + StdReverbGetParamf(props, param, vals); + break; + } } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Reverb); - -const EffectProps ReverbEffectProps{genDefaultProps()}; - -DEFINE_ALEFFECT_VTABLE(StdReverb); - -const EffectProps StdReverbEffectProps{genDefaultStdProps()}; #ifdef ALSOFT_EAX namespace { @@ -1086,98 +1001,87 @@ struct EaxReverbCommitter::Exception : public EaxReverbEffectException throw Exception{message}; } -void EaxReverbCommitter::translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept +void EaxReverbCommitter::translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept { assert(src.environment <= EAX1REVERB_MAXENVIRONMENT); - auto&& eaxprops = dst.emplace<EAXREVERBPROPERTIES>(EAXREVERB_PRESETS[src.environment]); - eaxprops.flDecayTime = src.fDecayTime_sec; - eaxprops.flDecayHFRatio = src.fDamping; - eaxprops.lReverb = mini(static_cast<int>(gain_to_level_mb(src.fVolume)), 0); + dst = EAXREVERB_PRESETS[src.environment]; + dst.flDecayTime = src.fDecayTime_sec; + dst.flDecayHFRatio = src.fDamping; + dst.lReverb = mini(static_cast<int>(gain_to_level_mb(src.fVolume)), 0); } -void EaxReverbCommitter::translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept +void EaxReverbCommitter::translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept { assert(src.dwEnvironment <= EAX1REVERB_MAXENVIRONMENT); - auto&& eaxprops = dst.emplace<EAXREVERBPROPERTIES>(EAXREVERB_PRESETS[src.dwEnvironment]); - eaxprops.ulEnvironment = src.dwEnvironment; - eaxprops.flEnvironmentSize = src.flEnvironmentSize; - eaxprops.flEnvironmentDiffusion = src.flEnvironmentDiffusion; - eaxprops.lRoom = src.lRoom; - eaxprops.lRoomHF = src.lRoomHF; - eaxprops.flDecayTime = src.flDecayTime; - eaxprops.flDecayHFRatio = src.flDecayHFRatio; - eaxprops.lReflections = src.lReflections; - eaxprops.flReflectionsDelay = src.flReflectionsDelay; - eaxprops.lReverb = src.lReverb; - eaxprops.flReverbDelay = src.flReverbDelay; - eaxprops.flAirAbsorptionHF = src.flAirAbsorptionHF; - eaxprops.flRoomRolloffFactor = src.flRoomRolloffFactor; - eaxprops.ulFlags = src.dwFlags; -} - -void EaxReverbCommitter::translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept -{ - dst = src; + dst = EAXREVERB_PRESETS[src.dwEnvironment]; + dst.ulEnvironment = src.dwEnvironment; + dst.flEnvironmentSize = src.flEnvironmentSize; + dst.flEnvironmentDiffusion = src.flEnvironmentDiffusion; + dst.lRoom = src.lRoom; + dst.lRoomHF = src.lRoomHF; + dst.flDecayTime = src.flDecayTime; + dst.flDecayHFRatio = src.flDecayHFRatio; + dst.lReflections = src.lReflections; + dst.flReflectionsDelay = src.flReflectionsDelay; + dst.lReverb = src.lReverb; + dst.flReverbDelay = src.flReverbDelay; + dst.flAirAbsorptionHF = src.flAirAbsorptionHF; + dst.flRoomRolloffFactor = src.flRoomRolloffFactor; + dst.ulFlags = src.dwFlags; } bool EaxReverbCommitter::commit(const EAX_REVERBPROPERTIES &props) { - EaxEffectProps dst{}; + EAXREVERBPROPERTIES dst{}; translate(props, dst); return commit(dst); } bool EaxReverbCommitter::commit(const EAX20LISTENERPROPERTIES &props) { - EaxEffectProps dst{}; + EAXREVERBPROPERTIES dst{}; translate(props, dst); return commit(dst); } bool EaxReverbCommitter::commit(const EAXREVERBPROPERTIES &props) { - EaxEffectProps dst{}; - translate(props, dst); - return commit(dst); -} - -bool EaxReverbCommitter::commit(const EaxEffectProps &props) -{ - if(props == mEaxProps) + if(auto *cur = std::get_if<EAXREVERBPROPERTIES>(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - auto &eaxprops = std::get<EAXREVERBPROPERTIES>(props); - const auto size = eaxprops.flEnvironmentSize; - const auto density = (size * size * size) / 16.0F; - mAlProps.Reverb.Density = std::min(density, AL_EAXREVERB_MAX_DENSITY); - mAlProps.Reverb.Diffusion = eaxprops.flEnvironmentDiffusion; - mAlProps.Reverb.Gain = level_mb_to_gain(static_cast<float>(eaxprops.lRoom)); - mAlProps.Reverb.GainHF = level_mb_to_gain(static_cast<float>(eaxprops.lRoomHF)); - mAlProps.Reverb.GainLF = level_mb_to_gain(static_cast<float>(eaxprops.lRoomLF)); - mAlProps.Reverb.DecayTime = eaxprops.flDecayTime; - mAlProps.Reverb.DecayHFRatio = eaxprops.flDecayHFRatio; - mAlProps.Reverb.DecayLFRatio = eaxprops.flDecayLFRatio; - mAlProps.Reverb.ReflectionsGain = level_mb_to_gain(static_cast<float>(eaxprops.lReflections)); - mAlProps.Reverb.ReflectionsDelay = eaxprops.flReflectionsDelay; - mAlProps.Reverb.ReflectionsPan[0] = eaxprops.vReflectionsPan.x; - mAlProps.Reverb.ReflectionsPan[1] = eaxprops.vReflectionsPan.y; - mAlProps.Reverb.ReflectionsPan[2] = eaxprops.vReflectionsPan.z; - mAlProps.Reverb.LateReverbGain = level_mb_to_gain(static_cast<float>(eaxprops.lReverb)); - mAlProps.Reverb.LateReverbDelay = eaxprops.flReverbDelay; - mAlProps.Reverb.LateReverbPan[0] = eaxprops.vReverbPan.x; - mAlProps.Reverb.LateReverbPan[1] = eaxprops.vReverbPan.y; - mAlProps.Reverb.LateReverbPan[2] = eaxprops.vReverbPan.z; - mAlProps.Reverb.EchoTime = eaxprops.flEchoTime; - mAlProps.Reverb.EchoDepth = eaxprops.flEchoDepth; - mAlProps.Reverb.ModulationTime = eaxprops.flModulationTime; - mAlProps.Reverb.ModulationDepth = eaxprops.flModulationDepth; - mAlProps.Reverb.AirAbsorptionGainHF = level_mb_to_gain(eaxprops.flAirAbsorptionHF); - mAlProps.Reverb.HFReference = eaxprops.flHFReference; - mAlProps.Reverb.LFReference = eaxprops.flLFReference; - mAlProps.Reverb.RoomRolloffFactor = eaxprops.flRoomRolloffFactor; - mAlProps.Reverb.DecayHFLimit = ((eaxprops.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0); + const auto size = props.flEnvironmentSize; + const auto density = (size * size * size) / 16.0f; + mAlProps = [&]{ + ReverbProps ret{}; + ret.Density = std::min(density, AL_EAXREVERB_MAX_DENSITY); + ret.Diffusion = props.flEnvironmentDiffusion; + ret.Gain = level_mb_to_gain(static_cast<float>(props.lRoom)); + ret.GainHF = level_mb_to_gain(static_cast<float>(props.lRoomHF)); + ret.GainLF = level_mb_to_gain(static_cast<float>(props.lRoomLF)); + ret.DecayTime = props.flDecayTime; + ret.DecayHFRatio = props.flDecayHFRatio; + ret.DecayLFRatio = props.flDecayLFRatio; + ret.ReflectionsGain = level_mb_to_gain(static_cast<float>(props.lReflections)); + ret.ReflectionsDelay = props.flReflectionsDelay; + ret.ReflectionsPan = {props.vReflectionsPan.x, props.vReflectionsPan.y, + props.vReflectionsPan.z}; + ret.LateReverbGain = level_mb_to_gain(static_cast<float>(props.lReverb)); + ret.LateReverbDelay = props.flReverbDelay; + ret.LateReverbPan = {props.vReverbPan.x, props.vReverbPan.y, props.vReverbPan.z}; + ret.EchoTime = props.flEchoTime; + ret.EchoDepth = props.flEchoDepth; + ret.ModulationTime = props.flModulationTime; + ret.ModulationDepth = props.flModulationDepth; + ret.AirAbsorptionGainHF = level_mb_to_gain(props.flAirAbsorptionHF); + ret.HFReference = props.flHFReference; + ret.LFReference = props.flLFReference; + ret.RoomRolloffFactor = props.flRoomRolloffFactor; + ret.DecayHFLimit = ((props.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0); + return ret; + }(); + return true; } @@ -1274,11 +1178,6 @@ void EaxReverbCommitter::Get(const EaxCall &call, const EAXREVERBPROPERTIES &pro } } -void EaxReverbCommitter::Get(const EaxCall &call, const EaxEffectProps &props) -{ - Get(call, std::get<EAXREVERBPROPERTIES>(props)); -} - void EaxReverbCommitter::Set(const EaxCall &call, EAX_REVERBPROPERTIES &props) { @@ -1297,71 +1196,23 @@ void EaxReverbCommitter::Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props { switch(call.get_property_id()) { - case DSPROPERTY_EAX20LISTENER_NONE: - break; - - case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: - defer<AllValidator2>(call, props); - break; - - case DSPROPERTY_EAX20LISTENER_ROOM: - defer<RoomValidator>(call, props.lRoom); - break; - - case DSPROPERTY_EAX20LISTENER_ROOMHF: - defer<RoomHFValidator>(call, props.lRoomHF); - break; - - case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: - defer<RoomRolloffFactorValidator>(call, props.flRoomRolloffFactor); - break; - - case DSPROPERTY_EAX20LISTENER_DECAYTIME: - defer<DecayTimeValidator>(call, props.flDecayTime); - break; - - case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: - defer<DecayHFRatioValidator>(call, props.flDecayHFRatio); - break; - - case DSPROPERTY_EAX20LISTENER_REFLECTIONS: - defer<ReflectionsValidator>(call, props.lReflections); - break; - - case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: - defer<ReflectionsDelayValidator>(call, props.flReverbDelay); - break; - - case DSPROPERTY_EAX20LISTENER_REVERB: - defer<ReverbValidator>(call, props.lReverb); - break; - - case DSPROPERTY_EAX20LISTENER_REVERBDELAY: - defer<ReverbDelayValidator>(call, props.flReverbDelay); - break; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: - defer<EnvironmentValidator1, EnvironmentDeferrer2>(call, props, props.dwEnvironment); - break; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: - defer<EnvironmentSizeValidator, EnvironmentSizeDeferrer2>(call, props, props.flEnvironmentSize); - break; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: - defer<EnvironmentDiffusionValidator>(call, props.flEnvironmentDiffusion); - break; - - case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: - defer<AirAbsorptionHFValidator>(call, props.flAirAbsorptionHF); - break; - - case DSPROPERTY_EAX20LISTENER_FLAGS: - defer<FlagsValidator2>(call, props.dwFlags); - break; - - default: - fail_unknown_property_id(); + case DSPROPERTY_EAX20LISTENER_NONE: break; + case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: defer<AllValidator2>(call, props); break; + case DSPROPERTY_EAX20LISTENER_ROOM: defer<RoomValidator>(call, props.lRoom); break; + case DSPROPERTY_EAX20LISTENER_ROOMHF: defer<RoomHFValidator>(call, props.lRoomHF); break; + case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: defer<RoomRolloffFactorValidator>(call, props.flRoomRolloffFactor); break; + case DSPROPERTY_EAX20LISTENER_DECAYTIME: defer<DecayTimeValidator>(call, props.flDecayTime); break; + case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: defer<DecayHFRatioValidator>(call, props.flDecayHFRatio); break; + case DSPROPERTY_EAX20LISTENER_REFLECTIONS: defer<ReflectionsValidator>(call, props.lReflections); break; + case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: defer<ReflectionsDelayValidator>(call, props.flReverbDelay); break; + case DSPROPERTY_EAX20LISTENER_REVERB: defer<ReverbValidator>(call, props.lReverb); break; + case DSPROPERTY_EAX20LISTENER_REVERBDELAY: defer<ReverbDelayValidator>(call, props.flReverbDelay); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: defer<EnvironmentValidator1, EnvironmentDeferrer2>(call, props, props.dwEnvironment); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: defer<EnvironmentSizeValidator, EnvironmentSizeDeferrer2>(call, props, props.flEnvironmentSize); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: defer<EnvironmentDiffusionValidator>(call, props.flEnvironmentDiffusion); break; + case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: defer<AirAbsorptionHFValidator>(call, props.flAirAbsorptionHF); break; + case DSPROPERTY_EAX20LISTENER_FLAGS: defer<FlagsValidator2>(call, props.dwFlags); break; + default: fail_unknown_property_id(); } } @@ -1369,117 +1220,34 @@ void EaxReverbCommitter::Set(const EaxCall &call, EAXREVERBPROPERTIES &props) { switch(call.get_property_id()) { - case EAXREVERB_NONE: - break; - - case EAXREVERB_ALLPARAMETERS: - defer<AllValidator3>(call, props); - break; - - case EAXREVERB_ENVIRONMENT: - defer<EnvironmentValidator3, EnvironmentDeferrer3>(call, props, props.ulEnvironment); - break; - - case EAXREVERB_ENVIRONMENTSIZE: - defer<EnvironmentSizeValidator, EnvironmentSizeDeferrer3>(call, props, props.flEnvironmentSize); - break; - - case EAXREVERB_ENVIRONMENTDIFFUSION: - defer3<EnvironmentDiffusionValidator>(call, props, props.flEnvironmentDiffusion); - break; - - case EAXREVERB_ROOM: - defer3<RoomValidator>(call, props, props.lRoom); - break; - - case EAXREVERB_ROOMHF: - defer3<RoomHFValidator>(call, props, props.lRoomHF); - break; - - case EAXREVERB_ROOMLF: - defer3<RoomLFValidator>(call, props, props.lRoomLF); - break; - - case EAXREVERB_DECAYTIME: - defer3<DecayTimeValidator>(call, props, props.flDecayTime); - break; - - case EAXREVERB_DECAYHFRATIO: - defer3<DecayHFRatioValidator>(call, props, props.flDecayHFRatio); - break; - - case EAXREVERB_DECAYLFRATIO: - defer3<DecayLFRatioValidator>(call, props, props.flDecayLFRatio); - break; - - case EAXREVERB_REFLECTIONS: - defer3<ReflectionsValidator>(call, props, props.lReflections); - break; - - case EAXREVERB_REFLECTIONSDELAY: - defer3<ReflectionsDelayValidator>(call, props, props.flReflectionsDelay); - break; - - case EAXREVERB_REFLECTIONSPAN: - defer3<VectorValidator>(call, props, props.vReflectionsPan); - break; - - case EAXREVERB_REVERB: - defer3<ReverbValidator>(call, props, props.lReverb); - break; - - case EAXREVERB_REVERBDELAY: - defer3<ReverbDelayValidator>(call, props, props.flReverbDelay); - break; - - case EAXREVERB_REVERBPAN: - defer3<VectorValidator>(call, props, props.vReverbPan); - break; - - case EAXREVERB_ECHOTIME: - defer3<EchoTimeValidator>(call, props, props.flEchoTime); - break; - - case EAXREVERB_ECHODEPTH: - defer3<EchoDepthValidator>(call, props, props.flEchoDepth); - break; - - case EAXREVERB_MODULATIONTIME: - defer3<ModulationTimeValidator>(call, props, props.flModulationTime); - break; - - case EAXREVERB_MODULATIONDEPTH: - defer3<ModulationDepthValidator>(call, props, props.flModulationDepth); - break; - - case EAXREVERB_AIRABSORPTIONHF: - defer3<AirAbsorptionHFValidator>(call, props, props.flAirAbsorptionHF); - break; - - case EAXREVERB_HFREFERENCE: - defer3<HFReferenceValidator>(call, props, props.flHFReference); - break; - - case EAXREVERB_LFREFERENCE: - defer3<LFReferenceValidator>(call, props, props.flLFReference); - break; - - case EAXREVERB_ROOMROLLOFFFACTOR: - defer3<RoomRolloffFactorValidator>(call, props, props.flRoomRolloffFactor); - break; - - case EAXREVERB_FLAGS: - defer3<FlagsValidator3>(call, props, props.ulFlags); - break; - - default: - fail_unknown_property_id(); + case EAXREVERB_NONE: break; + case EAXREVERB_ALLPARAMETERS: defer<AllValidator3>(call, props); break; + case EAXREVERB_ENVIRONMENT: defer<EnvironmentValidator3, EnvironmentDeferrer3>(call, props, props.ulEnvironment); break; + case EAXREVERB_ENVIRONMENTSIZE: defer<EnvironmentSizeValidator, EnvironmentSizeDeferrer3>(call, props, props.flEnvironmentSize); break; + case EAXREVERB_ENVIRONMENTDIFFUSION: defer3<EnvironmentDiffusionValidator>(call, props, props.flEnvironmentDiffusion); break; + case EAXREVERB_ROOM: defer3<RoomValidator>(call, props, props.lRoom); break; + case EAXREVERB_ROOMHF: defer3<RoomHFValidator>(call, props, props.lRoomHF); break; + case EAXREVERB_ROOMLF: defer3<RoomLFValidator>(call, props, props.lRoomLF); break; + case EAXREVERB_DECAYTIME: defer3<DecayTimeValidator>(call, props, props.flDecayTime); break; + case EAXREVERB_DECAYHFRATIO: defer3<DecayHFRatioValidator>(call, props, props.flDecayHFRatio); break; + case EAXREVERB_DECAYLFRATIO: defer3<DecayLFRatioValidator>(call, props, props.flDecayLFRatio); break; + case EAXREVERB_REFLECTIONS: defer3<ReflectionsValidator>(call, props, props.lReflections); break; + case EAXREVERB_REFLECTIONSDELAY: defer3<ReflectionsDelayValidator>(call, props, props.flReflectionsDelay); break; + case EAXREVERB_REFLECTIONSPAN: defer3<VectorValidator>(call, props, props.vReflectionsPan); break; + case EAXREVERB_REVERB: defer3<ReverbValidator>(call, props, props.lReverb); break; + case EAXREVERB_REVERBDELAY: defer3<ReverbDelayValidator>(call, props, props.flReverbDelay); break; + case EAXREVERB_REVERBPAN: defer3<VectorValidator>(call, props, props.vReverbPan); break; + case EAXREVERB_ECHOTIME: defer3<EchoTimeValidator>(call, props, props.flEchoTime); break; + case EAXREVERB_ECHODEPTH: defer3<EchoDepthValidator>(call, props, props.flEchoDepth); break; + case EAXREVERB_MODULATIONTIME: defer3<ModulationTimeValidator>(call, props, props.flModulationTime); break; + case EAXREVERB_MODULATIONDEPTH: defer3<ModulationDepthValidator>(call, props, props.flModulationDepth); break; + case EAXREVERB_AIRABSORPTIONHF: defer3<AirAbsorptionHFValidator>(call, props, props.flAirAbsorptionHF); break; + case EAXREVERB_HFREFERENCE: defer3<HFReferenceValidator>(call, props, props.flHFReference); break; + case EAXREVERB_LFREFERENCE: defer3<LFReferenceValidator>(call, props, props.flLFReference); break; + case EAXREVERB_ROOMROLLOFFFACTOR: defer3<RoomRolloffFactorValidator>(call, props, props.flRoomRolloffFactor); break; + case EAXREVERB_FLAGS: defer3<FlagsValidator3>(call, props, props.ulFlags); break; + default: fail_unknown_property_id(); } } -void EaxReverbCommitter::Set(const EaxCall &call, EaxEffectProps &props) -{ - Set(call, std::get<EAXREVERBPROPERTIES>(props)); -} - #endif // ALSOFT_EAX diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp index f2551229..35a64d32 100644 --- a/al/effects/vmorpher.cpp +++ b/al/effects/vmorpher.cpp @@ -122,13 +122,29 @@ ALenum EnumFromWaveform(VMorpherWaveform type) std::to_string(static_cast<int>(type))}; } -void Vmorpher_setParami(EffectProps *props, ALenum param, int val) +EffectProps genDefaultProps() noexcept +{ + VmorpherProps props{}; + props.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE; + props.PhonemeA = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA).value(); + props.PhonemeB = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB).value(); + props.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING; + props.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING; + props.Waveform = WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM).value(); + return props; +} + +} // namespace + +const EffectProps VmorpherEffectProps{genDefaultProps()}; + +void EffectHandler::SetParami(VmorpherProps &props, ALenum param, int val) { switch(param) { case AL_VOCAL_MORPHER_PHONEMEA: if(auto phenomeopt = PhenomeFromEnum(val)) - props->Vmorpher.PhonemeA = *phenomeopt; + props.PhonemeA = *phenomeopt; else throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val}; break; @@ -136,12 +152,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val) case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING)) throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a coarse tuning out of range"}; - props->Vmorpher.PhonemeACoarseTuning = val; + props.PhonemeACoarseTuning = val; break; case AL_VOCAL_MORPHER_PHONEMEB: if(auto phenomeopt = PhenomeFromEnum(val)) - props->Vmorpher.PhonemeB = *phenomeopt; + props.PhonemeB = *phenomeopt; else throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val}; break; @@ -149,12 +165,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val) case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING)) throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b coarse tuning out of range"}; - props->Vmorpher.PhonemeBCoarseTuning = val; + props.PhonemeBCoarseTuning = val; break; case AL_VOCAL_MORPHER_WAVEFORM: if(auto formopt = WaveformFromEmum(val)) - props->Vmorpher.Waveform = *formopt; + props.Waveform = *formopt; else throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val}; break; @@ -164,19 +180,19 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val) param}; } } -void Vmorpher_setParamiv(EffectProps*, ALenum param, const int*) +void EffectHandler::SetParamiv(VmorpherProps&, ALenum param, const int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", param}; } -void Vmorpher_setParamf(EffectProps *props, ALenum param, float val) +void EffectHandler::SetParamf(VmorpherProps &props, ALenum param, float val) { switch(param) { case AL_VOCAL_MORPHER_RATE: if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE)) throw effect_exception{AL_INVALID_VALUE, "Vocal morpher rate out of range"}; - props->Vmorpher.Rate = val; + props.Rate = val; break; default: @@ -184,49 +200,35 @@ void Vmorpher_setParamf(EffectProps *props, ALenum param, float val) param}; } } -void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Vmorpher_setParamf(props, param, vals[0]); } +void EffectHandler::SetParamfv(VmorpherProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, vals[0]); } -void Vmorpher_getParami(const EffectProps *props, ALenum param, int* val) +void EffectHandler::GetParami(const VmorpherProps &props, ALenum param, int* val) { switch(param) { - case AL_VOCAL_MORPHER_PHONEMEA: - *val = EnumFromPhenome(props->Vmorpher.PhonemeA); - break; - - case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: - *val = props->Vmorpher.PhonemeACoarseTuning; - break; - - case AL_VOCAL_MORPHER_PHONEMEB: - *val = EnumFromPhenome(props->Vmorpher.PhonemeB); - break; - - case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: - *val = props->Vmorpher.PhonemeBCoarseTuning; - break; - - case AL_VOCAL_MORPHER_WAVEFORM: - *val = EnumFromWaveform(props->Vmorpher.Waveform); - break; + case AL_VOCAL_MORPHER_PHONEMEA: *val = EnumFromPhenome(props.PhonemeA); break; + case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: *val = props.PhonemeACoarseTuning; break; + case AL_VOCAL_MORPHER_PHONEMEB: *val = EnumFromPhenome(props.PhonemeB); break; + case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: *val = props.PhonemeBCoarseTuning; break; + case AL_VOCAL_MORPHER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", param}; } } -void Vmorpher_getParamiv(const EffectProps*, ALenum param, int*) +void EffectHandler::GetParamiv(const VmorpherProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", param}; } -void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val) +void EffectHandler::GetParamf(const VmorpherProps &props, ALenum param, float *val) { switch(param) { case AL_VOCAL_MORPHER_RATE: - *val = props->Vmorpher.Rate; + *val = props.Rate; break; default: @@ -234,26 +236,9 @@ void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val) param}; } } -void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Vmorpher_getParamf(props, param, vals); } - -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE; - props.Vmorpher.PhonemeA = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA); - props.Vmorpher.PhonemeB = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB); - props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING; - props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING; - props.Vmorpher.Waveform = *WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM); - return props; -} - -} // namespace +void EffectHandler::GetParamfv(const VmorpherProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -DEFINE_ALEFFECT_VTABLE(Vmorpher); - -const EffectProps VmorpherEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { @@ -352,10 +337,9 @@ template<> throw Exception{message}; } -template<> -bool VocalMorpherCommitter::commit(const EaxEffectProps &props) +bool EaxVocalMorpherCommitter::commit(const EAXVOCALMORPHERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if<EAXVOCALMORPHERPROPERTIES>(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; @@ -407,19 +391,21 @@ bool VocalMorpherCommitter::commit(const EaxEffectProps &props) return VMorpherWaveform::Sinusoid; }; - auto &eaxprops = std::get<EAXVOCALMORPHERPROPERTIES>(props); - mAlProps.Vmorpher.PhonemeA = get_phoneme(eaxprops.ulPhonemeA); - mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast<int>(eaxprops.lPhonemeACoarseTuning); - mAlProps.Vmorpher.PhonemeB = get_phoneme(eaxprops.ulPhonemeB); - mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast<int>(eaxprops.lPhonemeBCoarseTuning); - mAlProps.Vmorpher.Waveform = get_waveform(eaxprops.ulWaveform); - mAlProps.Vmorpher.Rate = eaxprops.flRate; + mAlProps = [&]{ + VmorpherProps ret{}; + ret.PhonemeA = get_phoneme(props.ulPhonemeA); + ret.PhonemeACoarseTuning = static_cast<int>(props.lPhonemeACoarseTuning); + ret.PhonemeB = get_phoneme(props.ulPhonemeB); + ret.PhonemeBCoarseTuning = static_cast<int>(props.lPhonemeBCoarseTuning); + ret.Waveform = get_waveform(props.ulWaveform); + ret.Rate = props.flRate; + return ret; + }(); return true; } -template<> -void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props) +void EaxVocalMorpherCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXVOCALMORPHERPROPERTIES defprops{[] { @@ -435,87 +421,35 @@ void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxVocalMorpherCommitter::Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props) { - auto &props = std::get<EAXVOCALMORPHERPROPERTIES>(props_); switch(call.get_property_id()) { - case EAXVOCALMORPHER_NONE: - break; - - case EAXVOCALMORPHER_ALLPARAMETERS: - call.set_value<Exception>(props); - break; - - case EAXVOCALMORPHER_PHONEMEA: - call.set_value<Exception>(props.ulPhonemeA); - break; - - case EAXVOCALMORPHER_PHONEMEACOARSETUNING: - call.set_value<Exception>(props.lPhonemeACoarseTuning); - break; - - case EAXVOCALMORPHER_PHONEMEB: - call.set_value<Exception>(props.ulPhonemeB); - break; - - case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: - call.set_value<Exception>(props.lPhonemeBCoarseTuning); - break; - - case EAXVOCALMORPHER_WAVEFORM: - call.set_value<Exception>(props.ulWaveform); - break; - - case EAXVOCALMORPHER_RATE: - call.set_value<Exception>(props.flRate); - break; - - default: - fail_unknown_property_id(); + case EAXVOCALMORPHER_NONE: break; + case EAXVOCALMORPHER_ALLPARAMETERS: call.set_value<Exception>(props); break; + case EAXVOCALMORPHER_PHONEMEA: call.set_value<Exception>(props.ulPhonemeA); break; + case EAXVOCALMORPHER_PHONEMEACOARSETUNING: call.set_value<Exception>(props.lPhonemeACoarseTuning); break; + case EAXVOCALMORPHER_PHONEMEB: call.set_value<Exception>(props.ulPhonemeB); break; + case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: call.set_value<Exception>(props.lPhonemeBCoarseTuning); break; + case EAXVOCALMORPHER_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break; + case EAXVOCALMORPHER_RATE: call.set_value<Exception>(props.flRate); break; + default: fail_unknown_property_id(); } } -template<> -void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxVocalMorpherCommitter::Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props) { - auto &props = std::get<EAXVOCALMORPHERPROPERTIES>(props_); switch(call.get_property_id()) { - case EAXVOCALMORPHER_NONE: - break; - - case EAXVOCALMORPHER_ALLPARAMETERS: - defer<AllValidator>(call, props); - break; - - case EAXVOCALMORPHER_PHONEMEA: - defer<PhonemeAValidator>(call, props.ulPhonemeA); - break; - - case EAXVOCALMORPHER_PHONEMEACOARSETUNING: - defer<PhonemeACoarseTuningValidator>(call, props.lPhonemeACoarseTuning); - break; - - case EAXVOCALMORPHER_PHONEMEB: - defer<PhonemeBValidator>(call, props.ulPhonemeB); - break; - - case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: - defer<PhonemeBCoarseTuningValidator>(call, props.lPhonemeBCoarseTuning); - break; - - case EAXVOCALMORPHER_WAVEFORM: - defer<WaveformValidator>(call, props.ulWaveform); - break; - - case EAXVOCALMORPHER_RATE: - defer<RateValidator>(call, props.flRate); - break; - - default: - fail_unknown_property_id(); + case EAXVOCALMORPHER_NONE: break; + case EAXVOCALMORPHER_ALLPARAMETERS: defer<AllValidator>(call, props); break; + case EAXVOCALMORPHER_PHONEMEA: defer<PhonemeAValidator>(call, props.ulPhonemeA); break; + case EAXVOCALMORPHER_PHONEMEACOARSETUNING: defer<PhonemeACoarseTuningValidator>(call, props.lPhonemeACoarseTuning); break; + case EAXVOCALMORPHER_PHONEMEB: defer<PhonemeBValidator>(call, props.ulPhonemeB); break; + case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: defer<PhonemeBCoarseTuningValidator>(call, props.lPhonemeBCoarseTuning); break; + case EAXVOCALMORPHER_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break; + case EAXVOCALMORPHER_RATE: defer<RateValidator>(call, props.flRate); break; + default: fail_unknown_property_id(); } } diff --git a/al/error.cpp b/al/error.cpp index c2359477..26dc522f 100644 --- a/al/error.cpp +++ b/al/error.cpp @@ -99,41 +99,39 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...) /* Special-case alGetError since it (potentially) raises a debug signal and * returns a non-default value for a null context. */ -AL_API ALenum AL_APIENTRY alGetError(void) noexcept +AL_API auto AL_APIENTRY alGetError() noexcept -> ALenum { - auto context = GetContextRef(); - if(!context) UNLIKELY + if(auto context = GetContextRef()) LIKELY + return alGetErrorDirect(context.get()); + + static const ALenum deferror{[](const char *envname, const char *optname) -> ALenum { - static const ALenum deferror{[](const char *envname, const char *optname) -> ALenum - { - auto optstr = al::getenv(envname); - if(!optstr) - optstr = ConfigValueStr(nullptr, "game_compat", optname); - - if(optstr) - { - char *end{}; - auto value = std::strtoul(optstr->c_str(), &end, 0); - if(end && *end == '\0' && value <= std::numeric_limits<ALenum>::max()) - return static_cast<ALenum>(value); - ERR("Invalid default error value: \"%s\"", optstr->c_str()); - } - return AL_INVALID_OPERATION; - }("__ALSOFT_DEFAULT_ERROR", "default-error")}; - - WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror); - if(TrapALError) + auto optstr = al::getenv(envname); + if(!optstr) + optstr = ConfigValueStr({}, "game_compat", optname); + + if(optstr) { + char *end{}; + auto value = std::strtoul(optstr->c_str(), &end, 0); + if(end && *end == '\0' && value <= std::numeric_limits<ALenum>::max()) + return static_cast<ALenum>(value); + ERR("Invalid default error value: \"%s\"", optstr->c_str()); + } + return AL_INVALID_OPERATION; + }("__ALSOFT_DEFAULT_ERROR", "default-error")}; + + WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror); + if(TrapALError) + { #ifdef _WIN32 - if(IsDebuggerPresent()) - DebugBreak(); + if(IsDebuggerPresent()) + DebugBreak(); #elif defined(SIGTRAP) - raise(SIGTRAP); + raise(SIGTRAP); #endif - } - return deferror; } - return alGetErrorDirect(context.get()); + return deferror; } FORCE_ALIGN ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) noexcept diff --git a/al/event.cpp b/al/event.cpp index 8b76ceff..95b07dc2 100644 --- a/al/event.cpp +++ b/al/event.cpp @@ -118,7 +118,7 @@ int EventThread(ALCcontext *context) }; auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt) { - const std::string_view message{evt.msg}; + const std::string_view message{evt.msg.data()}; context->debugMessage(DebugSource::System, DebugType::Error, 0, DebugSeverity::High, message); diff --git a/al/filter.cpp b/al/filter.cpp index f0a078b7..528d6059 100644 --- a/al/filter.cpp +++ b/al/filter.cpp @@ -49,19 +49,21 @@ namespace { +using SubListAllocator = typename al::allocator<std::array<ALfilter,64>>; + class filter_exception final : public al::base_exception { ALenum mErrorCode; public: -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] +#ifdef __MINGW32__ + [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]] #else [[gnu::format(printf, 3, 4)]] #endif filter_exception(ALenum code, const char *msg, ...); ~filter_exception() override; - ALenum errorCode() const noexcept { return mErrorCode; } + [[nodiscard]] auto errorCode() const noexcept -> ALenum { return mErrorCode; } }; filter_exception::filter_exception(ALenum code, const char* msg, ...) : mErrorCode{code} @@ -80,36 +82,36 @@ void InitFilterParams(ALfilter *filter, ALenum type) { filter->Gain = AL_LOWPASS_DEFAULT_GAIN; filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = 1.0f; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace<LowpassFilterTable>(); } else if(type == AL_FILTER_HIGHPASS) { filter->Gain = AL_HIGHPASS_DEFAULT_GAIN; filter->GainHF = 1.0f; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace<HighpassFilterTable>(); } else if(type == AL_FILTER_BANDPASS) { filter->Gain = AL_BANDPASS_DEFAULT_GAIN; filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace<BandpassFilterTable>(); } else { filter->Gain = 1.0f; filter->GainHF = 1.0f; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = 1.0f; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace<NullFilterTable>(); } filter->type = type; @@ -121,21 +123,21 @@ bool EnsureFilters(ALCdevice *device, size_t needed) [](size_t cur, const FilterSubList &sublist) noexcept -> size_t { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })}; - while(needed > count) - { - if(device->FilterList.size() >= 1<<25) UNLIKELY - return false; - - device->FilterList.emplace_back(); - auto sublist = device->FilterList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Filters = static_cast<ALfilter*>(al_calloc(alignof(ALfilter), sizeof(ALfilter)*64)); - if(!sublist->Filters) UNLIKELY + try { + while(needed > count) { - device->FilterList.pop_back(); - return false; + if(device->FilterList.size() >= 1<<25) UNLIKELY + return false; + + FilterSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Filters = SubListAllocator{}.allocate(1); + device->FilterList.emplace_back(std::move(sublist)); + count += 64; } - count += 64; + } + catch(...) { + return false; } return true; } @@ -150,7 +152,7 @@ ALfilter *AllocFilter(ALCdevice *device) auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALfilter *filter{al::construct_at(sublist->Filters + slidx)}; + ALfilter *filter{al::construct_at(al::to_address(sublist->Filters->begin() + slidx))}; InitFilterParams(filter, AL_FILTER_NULL); /* Add 1 to avoid filter ID 0. */ @@ -185,7 +187,7 @@ inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) FilterSubList &sublist = device->FilterList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Filters + slidx; + return al::to_address(sublist.Filters->begin() + slidx); } } // namespace @@ -695,10 +697,10 @@ FilterSubList::~FilterSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(Filters+idx); + std::destroy_at(al::to_address(Filters->begin() + idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(Filters); + SubListAllocator{}.deallocate(Filters, 1); Filters = nullptr; } diff --git a/al/filter.h b/al/filter.h index 505900d4..dffe8c6e 100644 --- a/al/filter.h +++ b/al/filter.h @@ -1,6 +1,8 @@ #ifndef AL_FILTER_H #define AL_FILTER_H +#include <array> +#include <cstdint> #include <string_view> #include <variant> @@ -9,10 +11,11 @@ #include "AL/alext.h" #include "almalloc.h" +#include "alnumeric.h" -#define LOWPASSFREQREF 5000.0f -#define HIGHPASSFREQREF 250.0f +inline constexpr float LowPassFreqRef{5000.0f}; +inline constexpr float HighPassFreqRef{250.0f}; template<typename T> struct FilterTable { @@ -38,9 +41,9 @@ struct ALfilter { float Gain{1.0f}; float GainHF{1.0f}; - float HFReference{LOWPASSFREQREF}; + float HFReference{LowPassFreqRef}; float GainLF{1.0f}; - float LFReference{HIGHPASSFREQREF}; + float LFReference{HighPassFreqRef}; using TableTypes = std::variant<NullFilterTable,LowpassFilterTable,HighpassFilterTable, BandpassFilterTable>; @@ -51,7 +54,22 @@ struct ALfilter { static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC +}; + +struct FilterSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner<std::array<ALfilter,64>*> Filters{nullptr}; + + FilterSubList() noexcept = default; + FilterSubList(const FilterSubList&) = delete; + FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters} + { rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; } + ~FilterSubList(); + + FilterSubList& operator=(const FilterSubList&) = delete; + FilterSubList& operator=(FilterSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; } }; #endif diff --git a/al/listener.h b/al/listener.h index 81532877..a50f118a 100644 --- a/al/listener.h +++ b/al/listener.h @@ -18,7 +18,7 @@ struct ALlistener { float Gain{1.0f}; float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT}; - DISABLE_ALLOC() + DISABLE_ALLOC }; #endif diff --git a/al/source.cpp b/al/source.cpp index fe5bba40..b3af9428 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -80,7 +80,7 @@ namespace { -using namespace std::placeholders; +using SubListAllocator = typename al::allocator<std::array<ALsource,64>>; using std::chrono::nanoseconds; using seconds_d = std::chrono::duration<double>; @@ -171,7 +171,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context ret.LFReference = srcsend.LFReference; return ret; }; - std::transform(source->Send.cbegin(), source->Send.cend(), props->Send, copy_send); + std::transform(source->Send.cbegin(), source->Send.cend(), props->Send.begin(), copy_send); if(!props->Send[0].Slot && context->mDefaultSlot) props->Send[0].Slot = context->mDefaultSlot->mSlot; @@ -202,7 +202,7 @@ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds do { refcount = device->waitForMix(); - *clocktime = GetDeviceClockTime(device); + *clocktime = device->getClockTime(); voice = GetSourceVoice(Source, context); if(voice) { @@ -212,7 +212,7 @@ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds readPos += voice->mPositionFrac.load(std::memory_order_relaxed); } std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != device->MixCount.load(std::memory_order_relaxed)); + } while(refcount != device->mMixCount.load(std::memory_order_relaxed)); if(!voice) return 0; @@ -242,7 +242,7 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl do { refcount = device->waitForMix(); - *clocktime = GetDeviceClockTime(device); + *clocktime = device->getClockTime(); voice = GetSourceVoice(Source, context); if(voice) { @@ -252,7 +252,7 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl readPos += voice->mPositionFrac.load(std::memory_order_relaxed); } std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != device->MixCount.load(std::memory_order_relaxed)); + } while(refcount != device->mMixCount.load(std::memory_order_relaxed)); if(!voice) return 0.0f; @@ -302,7 +302,7 @@ NOINLINE T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) readPosFrac = voice->mPositionFrac.load(std::memory_order_relaxed); } std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != device->MixCount.load(std::memory_order_relaxed)); + } while(refcount != device->mMixCount.load(std::memory_order_relaxed)); if(!voice) return T{0}; @@ -575,7 +575,7 @@ void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail) oldhead->mNext.store(tail, std::memory_order_release); const bool connected{device->Connected.load(std::memory_order_acquire)}; - device->waitForMix(); + std::ignore = device->waitForMix(); if(!connected) UNLIKELY { if(ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire)) @@ -681,7 +681,7 @@ bool SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALC return true; /* Otherwise, wait for any current mix to finish and check one last time. */ - device->waitForMix(); + std::ignore = device->waitForMix(); if(newvoice->mPlayState.load(std::memory_order_acquire) != Voice::Pending) return true; /* The change-over failed because the old voice stopped before the new @@ -721,21 +721,21 @@ bool EnsureSources(ALCcontext *context, size_t needed) [](size_t cur, const SourceSubList &sublist) noexcept -> size_t { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })}; - while(needed > count) - { - if(context->mSourceList.size() >= 1<<25) UNLIKELY - return false; - - context->mSourceList.emplace_back(); - auto sublist = context->mSourceList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Sources = static_cast<ALsource*>(al_calloc(alignof(ALsource), sizeof(ALsource)*64)); - if(!sublist->Sources) UNLIKELY + try { + while(needed > count) { - context->mSourceList.pop_back(); - return false; + if(context->mSourceList.size() >= 1<<25) UNLIKELY + return false; + + SourceSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Sources = SubListAllocator{}.allocate(1); + context->mSourceList.emplace_back(std::move(sublist)); + count += 64; } - count += 64; + } + catch(...) { + return false; } return true; } @@ -749,7 +749,7 @@ ALsource *AllocSource(ALCcontext *context) auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALsource *source{al::construct_at(sublist->Sources + slidx)}; + ALsource *source{al::construct_at(al::to_address(sublist->Sources->begin() + slidx))}; /* Add 1 to avoid source ID 0. */ source->id = ((lidx<<6) | slidx) + 1; @@ -797,7 +797,7 @@ inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept SourceSubList &sublist{context->mSourceList[lidx]}; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Sources + slidx; + return al::to_address(sublist.Sources->begin() + slidx); } auto LookupBuffer = [](ALCdevice *device, auto id) noexcept -> ALbuffer* @@ -810,7 +810,7 @@ auto LookupBuffer = [](ALCdevice *device, auto id) noexcept -> ALbuffer* BufferSubList &sublist = device->BufferList[static_cast<size_t>(lidx)]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Buffers + static_cast<size_t>(slidx); + return al::to_address(sublist.Buffers->begin() + static_cast<size_t>(slidx)); }; auto LookupFilter = [](ALCdevice *device, auto id) noexcept -> ALfilter* @@ -823,7 +823,7 @@ auto LookupFilter = [](ALCdevice *device, auto id) noexcept -> ALfilter* FilterSubList &sublist = device->FilterList[static_cast<size_t>(lidx)]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Filters + static_cast<size_t>(slidx); + return al::to_address(sublist.Filters->begin() + static_cast<size_t>(slidx)); }; auto LookupEffectSlot = [](ALCcontext *context, auto id) noexcept -> ALeffectslot* @@ -836,7 +836,7 @@ auto LookupEffectSlot = [](ALCcontext *context, auto id) noexcept -> ALeffectslo EffectSlotSubList &sublist{context->mEffectSlotList[static_cast<size_t>(lidx)]}; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.EffectSlots + static_cast<size_t>(slidx); + return al::to_address(sublist.EffectSlots->begin() + static_cast<size_t>(slidx)); }; @@ -1316,11 +1316,11 @@ constexpr ALuint DoubleValsByProp(ALenum prop) struct check_exception : std::exception { }; struct check_size_exception final : check_exception { - const char *what() const noexcept override + [[nodiscard]] auto what() const noexcept -> const char* override { return "check_size_exception"; } }; struct check_value_exception final : check_exception { - const char *what() const noexcept override + [[nodiscard]] auto what() const noexcept -> const char* override { return "check_value_exception"; } }; @@ -1371,21 +1371,22 @@ struct PropType<ALfloat> { static const char *Name() { return "float"; } }; template<> struct PropType<ALdouble> { static const char *Name() { return "double"; } }; -template<typename T> struct HexPrinter { - char mStr[sizeof(T)*2 + 3]{}; + std::array<char,32> mStr{}; + + template<typename T> HexPrinter(T value) { using ST = std::make_signed_t<std::remove_cv_t<T>>; if constexpr(std::is_same_v<ST,int>) - std::snprintf(mStr, std::size(mStr), "0x%x", value); + std::snprintf(mStr.data(), mStr.size(), "0x%x", value); else if constexpr(std::is_same_v<ST,long>) - std::snprintf(mStr, std::size(mStr), "0x%lx", value); + std::snprintf(mStr.data(), mStr.size(), "0x%lx", value); else if constexpr(std::is_same_v<ST,long long>) - std::snprintf(mStr, std::size(mStr), "0x%llx", value); + std::snprintf(mStr.data(), mStr.size(), "0x%llx", value); } - const char *c_str() const noexcept { return mStr; } + [[nodiscard]] auto c_str() const noexcept -> const char* { return mStr.data(); } }; @@ -1579,7 +1580,7 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con * to ensure it isn't currently looping back or reaching the * end. */ - device->waitForMix(); + std::ignore = device->waitForMix(); } return; } @@ -1604,10 +1605,11 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if(!buffer) UNLIKELY return Context->setError(AL_INVALID_VALUE, "Invalid buffer ID %s", std::to_string(values[0]).c_str()); - if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY + if(buffer->MappedAccess + && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY return Context->setError(AL_INVALID_OPERATION, "Setting non-persistently mapped buffer %u", buffer->id); - if(buffer->mCallback && ReadRef(buffer->ref) != 0) UNLIKELY + if(buffer->mCallback && buffer->ref.load(std::memory_order_relaxed) != 0) UNLIKELY return Context->setError(AL_INVALID_OPERATION, "Setting already-set callback buffer %u", buffer->id); @@ -1793,9 +1795,9 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con { Source->Direct.Gain = 1.0f; Source->Direct.GainHF = 1.0f; - Source->Direct.HFReference = LOWPASSFREQREF; + Source->Direct.HFReference = LowPassFreqRef; Source->Direct.GainLF = 1.0f; - Source->Direct.LFReference = HIGHPASSFREQREF; + Source->Direct.LFReference = HighPassFreqRef; } return UpdateSourceProps(Source, Context); } @@ -1921,7 +1923,8 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con ALeffectslot *slot{}; if(values[0]) { - if((slot=LookupEffectSlot(Context, slotid)) == nullptr) UNLIKELY + slot = LookupEffectSlot(Context, slotid); + if(!slot) UNLIKELY return Context->setError(AL_INVALID_VALUE, "Invalid effect ID %s", std::to_string(slotid).c_str()); } @@ -1950,9 +1953,9 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con /* Disable filter */ send.Gain = 1.0f; send.GainHF = 1.0f; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; } /* We must force an update if the current auxiliary slot is valid @@ -3404,90 +3407,94 @@ FORCE_ALIGN void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALu std::unique_lock<std::mutex> buflock{device->BufferLock}; const size_t NewListStart{source->mQueue.size()}; - ALbufferQueueItem *BufferList{nullptr}; - for(ALsizei i{0};i < nb;i++) - { - bool fmt_mismatch{false}; - ALbuffer *buffer{nullptr}; - if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr) + try { + ALbufferQueueItem *BufferList{nullptr}; + for(ALsizei i{0};i < nb;i++) { - context->setError(AL_INVALID_NAME, "Queueing invalid buffer ID %u", buffers[i]); - goto buffer_error; - } - if(buffer) - { - if(buffer->mSampleRate < 1) + bool fmt_mismatch{false}; + ALbuffer *buffer{buffers[i] ? LookupBuffer(device, buffers[i]) : nullptr}; + if(buffers[i] && !buffer) { - context->setError(AL_INVALID_OPERATION, "Queueing buffer %u with no format", - buffer->id); - goto buffer_error; + context->setError(AL_INVALID_NAME, "Queueing invalid buffer ID %u", buffers[i]); + throw std::exception{}; } - if(buffer->mCallback) + if(buffer) { - context->setError(AL_INVALID_OPERATION, "Queueing callback buffer %u", buffer->id); - goto buffer_error; + if(buffer->mSampleRate < 1) + { + context->setError(AL_INVALID_OPERATION, "Queueing buffer %u with no format", + buffer->id); + throw std::exception{}; + } + if(buffer->mCallback) + { + context->setError(AL_INVALID_OPERATION, "Queueing callback buffer %u", + buffer->id); + throw std::exception{}; + } + if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) + { + context->setError(AL_INVALID_OPERATION, + "Queueing non-persistently mapped buffer %u", buffer->id); + throw std::exception{}; + } } - if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) + + source->mQueue.emplace_back(); + if(!BufferList) + BufferList = &source->mQueue.back(); + else { - context->setError(AL_INVALID_OPERATION, - "Queueing non-persistently mapped buffer %u", buffer->id); - goto buffer_error; + auto &item = source->mQueue.back(); + BufferList->mNext.store(&item, std::memory_order_relaxed); + BufferList = &item; } - } - - source->mQueue.emplace_back(); - if(!BufferList) - BufferList = &source->mQueue.back(); - else - { - auto &item = source->mQueue.back(); - BufferList->mNext.store(&item, std::memory_order_relaxed); - BufferList = &item; - } - if(!buffer) continue; - BufferList->mBlockAlign = buffer->mBlockAlign; - BufferList->mSampleLen = buffer->mSampleLen; - BufferList->mLoopEnd = buffer->mSampleLen; - BufferList->mSamples = buffer->mData.data(); - BufferList->mBuffer = buffer; - IncrementRef(buffer->ref); - - if(BufferFmt == nullptr) - BufferFmt = buffer; - else - { - fmt_mismatch |= BufferFmt->mSampleRate != buffer->mSampleRate; - fmt_mismatch |= BufferFmt->mChannels != buffer->mChannels; - fmt_mismatch |= BufferFmt->mType != buffer->mType; - if(BufferFmt->isBFormat()) + if(!buffer) continue; + BufferList->mBlockAlign = buffer->mBlockAlign; + BufferList->mSampleLen = buffer->mSampleLen; + BufferList->mLoopEnd = buffer->mSampleLen; + BufferList->mSamples = buffer->mData.data(); + BufferList->mBuffer = buffer; + IncrementRef(buffer->ref); + + if(BufferFmt == nullptr) + BufferFmt = buffer; + else { - fmt_mismatch |= BufferFmt->mAmbiLayout != buffer->mAmbiLayout; - fmt_mismatch |= BufferFmt->mAmbiScaling != buffer->mAmbiScaling; + fmt_mismatch |= BufferFmt->mSampleRate != buffer->mSampleRate; + fmt_mismatch |= BufferFmt->mChannels != buffer->mChannels; + fmt_mismatch |= BufferFmt->mType != buffer->mType; + if(BufferFmt->isBFormat()) + { + fmt_mismatch |= BufferFmt->mAmbiLayout != buffer->mAmbiLayout; + fmt_mismatch |= BufferFmt->mAmbiScaling != buffer->mAmbiScaling; + } + fmt_mismatch |= BufferFmt->mAmbiOrder != buffer->mAmbiOrder; } - fmt_mismatch |= BufferFmt->mAmbiOrder != buffer->mAmbiOrder; - } - if(fmt_mismatch) UNLIKELY - { - context->setError(AL_INVALID_OPERATION, "Queueing buffer with mismatched format\n" - " Expected: %uhz, %s, %s ; Got: %uhz, %s, %s\n", BufferFmt->mSampleRate, - NameFromFormat(BufferFmt->mType), NameFromFormat(BufferFmt->mChannels), - buffer->mSampleRate, NameFromFormat(buffer->mType), - NameFromFormat(buffer->mChannels)); - - buffer_error: - /* A buffer failed (invalid ID or format), so unlock and release - * each buffer we had. - */ - auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart); - for(;iter != source->mQueue.end();++iter) + if(fmt_mismatch) UNLIKELY { - if(ALbuffer *buf{iter->mBuffer}) - DecrementRef(buf->ref); + context->setError(AL_INVALID_OPERATION, "Queueing buffer with mismatched format\n" + " Expected: %uhz, %s, %s ; Got: %uhz, %s, %s\n", BufferFmt->mSampleRate, + NameFromFormat(BufferFmt->mType), NameFromFormat(BufferFmt->mChannels), + buffer->mSampleRate, NameFromFormat(buffer->mType), + NameFromFormat(buffer->mChannels)); + throw std::exception{}; } - source->mQueue.resize(NewListStart); - return; } } + catch(...) { + /* A buffer failed (invalid ID or format), or there was some other + * unexpected error, so unlock and release each buffer we had. + */ + auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart); + for(;iter != source->mQueue.end();++iter) + { + if(ALbuffer *buf{iter->mBuffer}) + DecrementRef(buf->ref); + } + source->mQueue.resize(NewListStart); + return; + } /* All buffers good. */ buflock.unlock(); @@ -3565,17 +3572,17 @@ ALsource::ALsource() { Direct.Gain = 1.0f; Direct.GainHF = 1.0f; - Direct.HFReference = LOWPASSFREQREF; + Direct.HFReference = LowPassFreqRef; Direct.GainLF = 1.0f; - Direct.LFReference = HIGHPASSFREQREF; + Direct.LFReference = HighPassFreqRef; for(auto &send : Send) { send.Slot = nullptr; send.Gain = 1.0f; send.GainHF = 1.0f; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; } } @@ -3632,18 +3639,15 @@ SourceSubList::~SourceSubList() { const int idx{al::countr_zero(usemask)}; usemask &= ~(1_u64 << idx); - std::destroy_at(Sources+idx); + std::destroy_at(al::to_address(Sources->begin() + idx)); } FreeMask = ~usemask; - al_free(Sources); + SubListAllocator{}.deallocate(Sources, 1); Sources = nullptr; } #ifdef ALSOFT_EAX -constexpr const ALsource::EaxFxSlotIds ALsource::eax4_fx_slot_ids; -constexpr const ALsource::EaxFxSlotIds ALsource::eax5_fx_slot_ids; - void ALsource::eaxInitialize(ALCcontext *context) noexcept { assert(context != nullptr); @@ -3916,27 +3920,30 @@ void ALsource::eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept // Active FX slots. // - for (auto i = 0; i < EAX50_MAX_ACTIVE_FXSLOTS; ++i) { + for(size_t i{0};i < EAX50_MAX_ACTIVE_FXSLOTS;++i) + { auto& dst_id = dst.active_fx_slots.guidActiveFXSlots[i]; - if (i < EAX40_MAX_ACTIVE_FXSLOTS) { + if(i < EAX40_MAX_ACTIVE_FXSLOTS) + { const auto& src_id = src.active_fx_slots.guidActiveFXSlots[i]; - if (src_id == EAX_NULL_GUID) + if(src_id == EAX_NULL_GUID) dst_id = EAX_NULL_GUID; - else if (src_id == EAX_PrimaryFXSlotID) + else if(src_id == EAX_PrimaryFXSlotID) dst_id = EAX_PrimaryFXSlotID; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot0) + else if(src_id == EAXPROPERTYID_EAX40_FXSlot0) dst_id = EAXPROPERTYID_EAX50_FXSlot0; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot1) + else if(src_id == EAXPROPERTYID_EAX40_FXSlot1) dst_id = EAXPROPERTYID_EAX50_FXSlot1; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot2) + else if(src_id == EAXPROPERTYID_EAX40_FXSlot2) dst_id = EAXPROPERTYID_EAX50_FXSlot2; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot3) + else if(src_id == EAXPROPERTYID_EAX40_FXSlot3) dst_id = EAXPROPERTYID_EAX50_FXSlot3; else assert(false && "Unknown active FX slot ID."); - } else + } + else dst_id = EAX_NULL_GUID; } @@ -4067,9 +4074,9 @@ void ALsource::eax_update_direct_filter() const auto& direct_param = eax_create_direct_filter_param(); Direct.Gain = direct_param.gain; Direct.GainHF = direct_param.gain_hf; - Direct.HFReference = LOWPASSFREQREF; + Direct.HFReference = LowPassFreqRef; Direct.GainLF = 1.0f; - Direct.LFReference = HIGHPASSFREQREF; + Direct.LFReference = HighPassFreqRef; mPropsDirty = true; } @@ -4358,7 +4365,7 @@ void ALsource::eax4_set(const EaxCall& call, Eax4Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax4_defer_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); + eax4_defer_active_fx_slot_id(call, al::span{props.active_fx_slots.guidActiveFXSlots}); break; default: @@ -4439,7 +4446,7 @@ void ALsource::eax5_set(const EaxCall& call, Eax5Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax5_defer_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); + eax5_defer_active_fx_slot_id(call, al::span{props.active_fx_slots.guidActiveFXSlots}); break; case EAXSOURCE_MACROFXFACTOR: @@ -4729,7 +4736,8 @@ void ALsource::eax4_get(const EaxCall& call, const Eax4Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots, EAX40_MAX_ACTIVE_FXSLOTS); + eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots.data(), + EAX40_MAX_ACTIVE_FXSLOTS); break; default: @@ -4801,7 +4809,8 @@ void ALsource::eax5_get(const EaxCall& call, const Eax5Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots, EAX50_MAX_ACTIVE_FXSLOTS); + eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots.data(), + EAX50_MAX_ACTIVE_FXSLOTS); break; case EAXSOURCE_MACROFXFACTOR: @@ -4841,9 +4850,9 @@ void ALsource::eax_set_al_source_send(ALeffectslot *slot, size_t sendidx, const auto &send = Send[sendidx]; send.Gain = filter.gain; send.GainHF = filter.gain_hf; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; if(slot != nullptr) IncrementRef(slot->ref); diff --git a/al/source.h b/al/source.h index c7694f83..84fd7664 100644 --- a/al/source.h +++ b/al/source.h @@ -4,6 +4,7 @@ #include <array> #include <atomic> #include <cstddef> +#include <cstdint> #include <deque> #include <iterator> #include <limits> @@ -37,7 +38,7 @@ enum class SourceStereo : bool { Enhanced = AL_SUPER_STEREO_SOFT }; -#define DEFAULT_SENDS 2 +inline constexpr size_t DefaultSendCount{2}; inline constexpr ALuint InvalidVoiceIndex{std::numeric_limits<ALuint>::max()}; @@ -46,7 +47,7 @@ inline bool sBufferSubDataCompat{false}; struct ALbufferQueueItem : public VoiceBufferItem { ALbuffer *mBuffer{nullptr}; - DISABLE_ALLOC() + DISABLE_ALLOC }; @@ -108,21 +109,21 @@ struct ALsource { /** Direct filter and auxiliary send info. */ struct { - float Gain; - float GainHF; - float HFReference; - float GainLF; - float LFReference; + float Gain{}; + float GainHF{}; + float HFReference{}; + float GainLF{}; + float LFReference{}; } Direct; struct SendData { - ALeffectslot *Slot; - float Gain; - float GainHF; - float HFReference; - float GainLF; - float LFReference; + ALeffectslot *Slot{}; + float Gain{}; + float GainHF{}; + float HFReference{}; + float GainLF{}; + float LFReference{}; }; - std::array<SendData,MAX_SENDS> Send; + std::array<SendData,MaxSendCount> Send; /** * Last user-specified offset, and the offset type (bytes, samples, or @@ -159,7 +160,7 @@ struct ALsource { static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC #ifdef ALSOFT_EAX public: @@ -173,18 +174,18 @@ public: private: using Exception = EaxSourceException; - static constexpr auto eax_max_speakers = 9; + static constexpr auto eax_max_speakers{9u}; - using EaxFxSlotIds = const GUID* [EAX_MAX_FXSLOTS]; + using EaxFxSlotIds = std::array<const GUID*,EAX_MAX_FXSLOTS>; - static constexpr const EaxFxSlotIds eax4_fx_slot_ids = { + static constexpr const EaxFxSlotIds eax4_fx_slot_ids{ &EAXPROPERTYID_EAX40_FXSlot0, &EAXPROPERTYID_EAX40_FXSlot1, &EAXPROPERTYID_EAX40_FXSlot2, &EAXPROPERTYID_EAX40_FXSlot3, }; - static constexpr const EaxFxSlotIds eax5_fx_slot_ids = { + static constexpr const EaxFxSlotIds eax5_fx_slot_ids{ &EAXPROPERTYID_EAX50_FXSlot0, &EAXPROPERTYID_EAX50_FXSlot1, &EAXPROPERTYID_EAX50_FXSlot2, @@ -839,11 +840,10 @@ private: float path_ratio, float lf_ratio) noexcept; - EaxAlLowPassParam eax_create_direct_filter_param() const noexcept; + [[nodiscard]] auto eax_create_direct_filter_param() const noexcept -> EaxAlLowPassParam; - EaxAlLowPassParam eax_create_room_filter_param( - const ALeffectslot& fx_slot, - const EAXSOURCEALLSENDPROPERTIES& send) const noexcept; + [[nodiscard]] auto eax_create_room_filter_param(const ALeffectslot& fx_slot, + const EAXSOURCEALLSENDPROPERTIES& send) const noexcept -> EaxAlLowPassParam; void eax_update_direct_filter(); void eax_update_room_filters(); @@ -978,21 +978,21 @@ private: } template<typename TValidator, size_t TIdCount> - void eax_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + void eax_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids) { const auto src_ids = call.get_values<const GUID>(TIdCount); std::for_each(src_ids.cbegin(), src_ids.cend(), TValidator{}); - std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids); + std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids.begin()); } template<size_t TIdCount> - void eax4_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + void eax4_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids) { eax_defer_active_fx_slot_id<Eax4ActiveFxSlotIdValidator>(call, dst_ids); } template<size_t TIdCount> - void eax5_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + void eax5_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids) { eax_defer_active_fx_slot_id<Eax5ActiveFxSlotIdValidator>(call, dst_ids); } @@ -1043,4 +1043,19 @@ private: void UpdateAllSourceProps(ALCcontext *context); +struct SourceSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner<std::array<ALsource,64>*> Sources{nullptr}; + + SourceSubList() noexcept = default; + SourceSubList(const SourceSubList&) = delete; + SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources} + { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; } + ~SourceSubList(); + + SourceSubList& operator=(const SourceSubList&) = delete; + SourceSubList& operator=(SourceSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; } +}; + #endif diff --git a/al/state.cpp b/al/state.cpp index 5131edd9..3ef87fbb 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -60,6 +60,7 @@ namespace { +/* NOLINTBEGIN(*-avoid-c-arrays) */ constexpr ALchar alVendor[] = "OpenAL Community"; constexpr ALchar alVersion[] = "1.1 ALSOFT " ALSOFT_VERSION; constexpr ALchar alRenderer[] = "OpenAL Soft"; @@ -73,6 +74,7 @@ constexpr ALchar alErrInvalidOp[] = "Invalid Operation"; constexpr ALchar alErrOutOfMemory[] = "Out of Memory"; constexpr ALchar alStackOverflow[] = "Stack Overflow"; constexpr ALchar alStackUnderflow[] = "Stack Underflow"; +/* NOLINTEND(*-avoid-c-arrays) */ /* Resampler strings */ template<Resampler rtype> struct ResamplerName { }; @@ -299,7 +301,7 @@ inline void UpdateProps(ALCcontext *context) /* WARNING: Non-standard export! Not part of any extension, or exposed in the * alcFunctions list. */ -AL_API const ALchar* AL_APIENTRY alsoft_get_version(void) noexcept +AL_API auto AL_APIENTRY alsoft_get_version() noexcept -> const ALchar* { static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION"); if(spoof) return spoof->c_str(); @@ -386,7 +388,7 @@ FORCE_ALIGN ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum } #define DECL_GETFUNC(R, Name, Ext) \ -AL_API R AL_APIENTRY Name##Ext(ALenum pname) noexcept \ +AL_API auto AL_APIENTRY Name##Ext(ALenum pname) noexcept -> R \ { \ R value{}; \ auto context = GetContextRef(); \ @@ -394,7 +396,7 @@ AL_API R AL_APIENTRY Name##Ext(ALenum pname) noexcept \ Name##vDirect##Ext(GetContextRef().get(), pname, &value); \ return value; \ } \ -FORCE_ALIGN R AL_APIENTRY Name##Direct##Ext(ALCcontext *context, ALenum pname) noexcept \ +FORCE_ALIGN auto AL_APIENTRY Name##Direct##Ext(ALCcontext *context, ALenum pname) noexcept -> R \ { \ R value{}; \ Name##vDirect##Ext(context, pname, &value); \ @@ -461,7 +463,7 @@ FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum switch(pname) { case AL_EVENT_CALLBACK_FUNCTION_SOFT: - *values = al::bit_cast<void*>(context->mEventCb); + *values = reinterpret_cast<void*>(context->mEventCb); break; case AL_EVENT_CALLBACK_USER_PARAM_SOFT: @@ -469,7 +471,7 @@ FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum break; case AL_DEBUG_CALLBACK_FUNCTION_EXT: - *values = al::bit_cast<void*>(context->mDebugCb); + *values = reinterpret_cast<void*>(context->mDebugCb); break; case AL_DEBUG_CALLBACK_USER_PARAM_EXT: @@ -641,18 +643,18 @@ AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) noexcept void UpdateContextProps(ALCcontext *context) { - /* Get an unused proprty container, or allocate a new one as needed. */ + /* Get an unused property container, or allocate a new one as needed. */ ContextProps *props{context->mFreeContextProps.load(std::memory_order_acquire)}; if(!props) - props = new ContextProps{}; - else { - ContextProps *next; - do { - next = props->next.load(std::memory_order_relaxed); - } while(context->mFreeContextProps.compare_exchange_weak(props, next, - std::memory_order_seq_cst, std::memory_order_acquire) == 0); + context->allocContextProps(); + props = context->mFreeContextProps.load(std::memory_order_acquire); } + ContextProps *next; + do { + next = props->next.load(std::memory_order_relaxed); + } while(context->mFreeContextProps.compare_exchange_weak(props, next, + std::memory_order_acq_rel, std::memory_order_acquire) == false); /* Copy in current property values. */ ALlistener &listener = context->mListener; diff --git a/alc/alc.cpp b/alc/alc.cpp index 6017e743..b629a3fc 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -50,7 +50,6 @@ #include <mutex> #include <new> #include <optional> -#include <stddef.h> #include <stdexcept> #include <string> #include <type_traits> @@ -100,6 +99,7 @@ #include "device.h" #include "effects/base.h" #include "export_list.h" +#include "flexarray.h" #include "inprogext.h" #include "intrusive_ptr.h" #include "opthelpers.h" @@ -174,7 +174,7 @@ BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/) case DLL_PROCESS_ATTACH: /* Pin the DLL so we won't get unloaded until the process terminates */ GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - al::bit_cast<WCHAR*>(module), &module); + reinterpret_cast<WCHAR*>(module), &module); break; } return TRUE; @@ -196,66 +196,66 @@ using float2 = std::array<float,2>; ************************************************/ struct BackendInfo { const char *name; - BackendFactory& (*getFactory)(void); + BackendFactory& (*getFactory)(); }; -BackendInfo BackendList[] = { +std::array BackendList{ #ifdef HAVE_PIPEWIRE - { "pipewire", PipeWireBackendFactory::getFactory }, + BackendInfo{"pipewire", PipeWireBackendFactory::getFactory}, #endif #ifdef HAVE_PULSEAUDIO - { "pulse", PulseBackendFactory::getFactory }, + BackendInfo{"pulse", PulseBackendFactory::getFactory}, #endif #ifdef HAVE_WASAPI - { "wasapi", WasapiBackendFactory::getFactory }, + BackendInfo{"wasapi", WasapiBackendFactory::getFactory}, #endif #ifdef HAVE_COREAUDIO - { "core", CoreAudioBackendFactory::getFactory }, + BackendInfo{"core", CoreAudioBackendFactory::getFactory}, #endif #ifdef HAVE_OBOE - { "oboe", OboeBackendFactory::getFactory }, + BackendInfo{"oboe", OboeBackendFactory::getFactory}, #endif #ifdef HAVE_OPENSL - { "opensl", OSLBackendFactory::getFactory }, + BackendInfo{"opensl", OSLBackendFactory::getFactory}, #endif #ifdef HAVE_ALSA - { "alsa", AlsaBackendFactory::getFactory }, + BackendInfo{"alsa", AlsaBackendFactory::getFactory}, #endif #ifdef HAVE_SOLARIS - { "solaris", SolarisBackendFactory::getFactory }, + BackendInfo{"solaris", SolarisBackendFactory::getFactory}, #endif #ifdef HAVE_SNDIO - { "sndio", SndIOBackendFactory::getFactory }, + BackendInfo{"sndio", SndIOBackendFactory::getFactory}, #endif #ifdef HAVE_OSS - { "oss", OSSBackendFactory::getFactory }, + BackendInfo{"oss", OSSBackendFactory::getFactory}, #endif #ifdef HAVE_JACK - { "jack", JackBackendFactory::getFactory }, + BackendInfo{"jack", JackBackendFactory::getFactory}, #endif #ifdef HAVE_DSOUND - { "dsound", DSoundBackendFactory::getFactory }, + BackendInfo{"dsound", DSoundBackendFactory::getFactory}, #endif #ifdef HAVE_WINMM - { "winmm", WinMMBackendFactory::getFactory }, + BackendInfo{"winmm", WinMMBackendFactory::getFactory}, #endif #ifdef HAVE_PORTAUDIO - { "port", PortBackendFactory::getFactory }, + BackendInfo{"port", PortBackendFactory::getFactory}, #endif #ifdef HAVE_SDL2 - { "sdl2", SDL2BackendFactory::getFactory }, + BackendInfo{"sdl2", SDL2BackendFactory::getFactory}, #endif - { "null", NullBackendFactory::getFactory }, + BackendInfo{"null", NullBackendFactory::getFactory}, #ifdef HAVE_WAVE - { "wave", WaveBackendFactory::getFactory }, + BackendInfo{"wave", WaveBackendFactory::getFactory}, #endif }; BackendFactory *PlaybackFactory{}; BackendFactory *CaptureFactory{}; - +/* NOLINTBEGIN(*-avoid-c-arrays) */ constexpr ALCchar alcNoError[] = "No Error"; constexpr ALCchar alcErrInvalidDevice[] = "Invalid Device"; constexpr ALCchar alcErrInvalidContext[] = "Invalid Context"; @@ -270,6 +270,7 @@ constexpr ALCchar alcErrOutOfMemory[] = "Out of Memory"; /* Enumerated device names */ constexpr ALCchar alcDefaultName[] = "OpenAL Soft\0"; +/* NOLINTEND(*-avoid-c-arrays) */ std::string alcAllDevicesList; std::string alcCaptureDeviceList; @@ -298,6 +299,7 @@ constexpr uint DitherRNGSeed{22222u}; /************************************************ * ALC information ************************************************/ +/* NOLINTBEGIN(*-avoid-c-arrays) */ constexpr ALCchar alcNoDeviceExtList[] = "ALC_ENUMERATE_ALL_EXT " "ALC_ENUMERATION_EXT " @@ -328,6 +330,7 @@ constexpr ALCchar alcExtensionList[] = "ALC_SOFT_pause_device " "ALC_SOFT_reopen_device " "ALC_SOFT_system_events"; +/* NOLINTEND(*-avoid-c-arrays) */ constexpr int alcMajorVersion{1}; constexpr int alcMinorVersion{1}; @@ -347,7 +350,7 @@ std::vector<ALCcontext*> ContextList; std::recursive_mutex ListLock; -void alc_initconfig(void) +void alc_initconfig() { if(auto loglevel = al::getenv("ALSOFT_LOGLEVEL")) { @@ -424,7 +427,7 @@ void alc_initconfig(void) #ifdef HAVE_NEON capfilter |= CPU_CAP_NEON; #endif - if(auto cpuopt = ConfigValueStr(nullptr, nullptr, "disable-cpu-exts")) + if(auto cpuopt = ConfigValueStr({}, {}, "disable-cpu-exts")) { const char *str{cpuopt->c_str()}; if(al::strcasecmp(str, "all") == 0) @@ -477,9 +480,9 @@ void alc_initconfig(void) CPUCapFlags = caps & capfilter; } - if(auto priopt = ConfigValueInt(nullptr, nullptr, "rt-prio")) + if(auto priopt = ConfigValueInt({}, {}, "rt-prio")) RTPrioLevel = *priopt; - if(auto limopt = ConfigValueBool(nullptr, nullptr, "rt-time-limit")) + if(auto limopt = ConfigValueBool({}, {}, "rt-time-limit")) AllowRTTimeLimit = *limopt; { @@ -493,18 +496,18 @@ void alc_initconfig(void) return true; return false; } - return GetConfigValueBool(nullptr, "game_compat", optname, false); + return GetConfigValueBool({}, "game_compat", optname, false); }; sBufferSubDataCompat = checkflag("__ALSOFT_ENABLE_SUB_DATA_EXT", "enable-sub-data-ext"); compatflags.set(CompatFlags::ReverseX, checkflag("__ALSOFT_REVERSE_X", "reverse-x")); compatflags.set(CompatFlags::ReverseY, checkflag("__ALSOFT_REVERSE_Y", "reverse-y")); compatflags.set(CompatFlags::ReverseZ, checkflag("__ALSOFT_REVERSE_Z", "reverse-z")); - aluInit(compatflags, ConfigValueFloat(nullptr, "game_compat", "nfc-scale").value_or(1.0f)); + aluInit(compatflags, ConfigValueFloat({}, "game_compat", "nfc-scale").value_or(1.0f)); } - Voice::InitMixer(ConfigValueStr(nullptr, nullptr, "resampler")); + Voice::InitMixer(ConfigValueStr({}, {}, "resampler")); - if(auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "decode-filter")) + if(auto uhjfiltopt = ConfigValueStr({}, "uhj", "decode-filter")) { if(al::strcasecmp(uhjfiltopt->c_str(), "fir256") == 0) UhjDecodeQuality = UhjQualityType::FIR256; @@ -515,7 +518,7 @@ void alc_initconfig(void) else WARN("Unsupported uhj/decode-filter: %s\n", uhjfiltopt->c_str()); } - if(auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "encode-filter")) + if(auto uhjfiltopt = ConfigValueStr({}, "uhj", "encode-filter")) { if(al::strcasecmp(uhjfiltopt->c_str(), "fir256") == 0) UhjEncodeQuality = UhjQualityType::FIR256; @@ -541,17 +544,17 @@ void alc_initconfig(void) TrapALError = al::strcasecmp(traperr->c_str(), "true") == 0 || strtol(traperr->c_str(), nullptr, 0) == 1; else - TrapALError = !!GetConfigValueBool(nullptr, nullptr, "trap-al-error", false); + TrapALError = GetConfigValueBool({}, {}, "trap-al-error", false); traperr = al::getenv("ALSOFT_TRAP_ALC_ERROR"); if(traperr) TrapALCError = al::strcasecmp(traperr->c_str(), "true") == 0 || strtol(traperr->c_str(), nullptr, 0) == 1; else - TrapALCError = !!GetConfigValueBool(nullptr, nullptr, "trap-alc-error", false); + TrapALCError = GetConfigValueBool({}, {}, "trap-alc-error", false); } - if(auto boostopt = ConfigValueFloat(nullptr, "reverb", "boost")) + if(auto boostopt = ConfigValueFloat({}, "reverb", "boost")) { const float valf{std::isfinite(*boostopt) ? clampf(*boostopt, -24.0f, 24.0f) : 0.0f}; ReverbBoost *= std::pow(10.0f, valf / 20.0f); @@ -559,7 +562,8 @@ void alc_initconfig(void) auto BackendListEnd = std::end(BackendList); auto devopt = al::getenv("ALSOFT_DRIVERS"); - if(devopt || (devopt=ConfigValueStr(nullptr, nullptr, "drivers"))) + if(!devopt) devopt = ConfigValueStr({}, {}, "drivers"); + if(devopt) { auto backendlist_cur = std::begin(BackendList); @@ -645,7 +649,7 @@ void alc_initconfig(void) if(!CaptureFactory) WARN("No capture backend available!\n"); - if(auto exclopt = ConfigValueStr(nullptr, nullptr, "excludefx")) + if(auto exclopt = ConfigValueStr({}, {}, "excludefx")) { const char *next{exclopt->c_str()}; do { @@ -660,21 +664,19 @@ void alc_initconfig(void) { if(len == strlen(effectitem.name) && strncmp(effectitem.name, str, len) == 0) - DisabledEffects[effectitem.type] = true; + DisabledEffects.set(effectitem.type); } } while(next++); } InitEffect(&ALCcontext::sDefaultEffect); auto defrevopt = al::getenv("ALSOFT_DEFAULT_REVERB"); - if(defrevopt || (defrevopt=ConfigValueStr(nullptr, nullptr, "default-reverb"))) - LoadReverbPreset(defrevopt->c_str(), &ALCcontext::sDefaultEffect); + if(!defrevopt) defrevopt = ConfigValueStr({}, {}, "default-reverb"); + if(defrevopt) LoadReverbPreset(defrevopt->c_str(), &ALCcontext::sDefaultEffect); #ifdef ALSOFT_EAX { - static constexpr char eax_block_name[] = "eax"; - - if(const auto eax_enable_opt = ConfigValueBool(nullptr, eax_block_name, "enable")) + if(const auto eax_enable_opt = ConfigValueBool({}, "eax", "enable")) { eax_g_is_enabled = *eax_enable_opt; if(!eax_g_is_enabled) @@ -683,15 +685,15 @@ void alc_initconfig(void) else eax_g_is_enabled = true; - if((DisabledEffects[EAXREVERB_EFFECT] || DisabledEffects[CHORUS_EFFECT]) + if((DisabledEffects.test(EAXREVERB_EFFECT) || DisabledEffects.test(CHORUS_EFFECT)) && eax_g_is_enabled) { eax_g_is_enabled = false; TRACE("EAX disabled because %s disabled.\n", - (DisabledEffects[EAXREVERB_EFFECT] && DisabledEffects[CHORUS_EFFECT]) + (DisabledEffects.test(EAXREVERB_EFFECT) && DisabledEffects.test(CHORUS_EFFECT)) ? "EAXReverb and Chorus are" : - DisabledEffects[EAXREVERB_EFFECT] ? "EAXReverb is" : - DisabledEffects[CHORUS_EFFECT] ? "Chorus is" : ""); + DisabledEffects.test(EAXREVERB_EFFECT) ? "EAXReverb is" : + DisabledEffects.test(CHORUS_EFFECT) ? "Chorus is" : ""); } } #endif // ALSOFT_EAX @@ -736,44 +738,45 @@ void ProbeCaptureDeviceList() struct DevFmtPair { DevFmtChannels chans; DevFmtType type; }; std::optional<DevFmtPair> DecomposeDevFormat(ALenum format) { - static const struct { + struct FormatType { ALenum format; DevFmtChannels channels; DevFmtType type; - } list[] = { - { AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte }, - { AL_FORMAT_MONO16, DevFmtMono, DevFmtShort }, - { AL_FORMAT_MONO_I32, DevFmtMono, DevFmtInt }, - { AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat }, - - { AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte }, - { AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort }, - { AL_FORMAT_STEREO_I32, DevFmtStereo, DevFmtInt }, - { AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat }, - - { AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte }, - { AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort }, - { AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat }, - { AL_FORMAT_QUAD_I32, DevFmtQuad, DevFmtInt }, - { AL_FORMAT_QUAD_FLOAT32, DevFmtQuad, DevFmtFloat }, - - { AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte }, - { AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort }, - { AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat }, - { AL_FORMAT_51CHN_I32, DevFmtX51, DevFmtInt }, - { AL_FORMAT_51CHN_FLOAT32, DevFmtX51, DevFmtFloat }, - - { AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte }, - { AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort }, - { AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat }, - { AL_FORMAT_61CHN_I32, DevFmtX61, DevFmtInt }, - { AL_FORMAT_61CHN_FLOAT32, DevFmtX61, DevFmtFloat }, - - { AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte }, - { AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort }, - { AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat }, - { AL_FORMAT_71CHN_I32, DevFmtX71, DevFmtInt }, - { AL_FORMAT_71CHN_FLOAT32, DevFmtX71, DevFmtFloat }, + }; + static constexpr std::array list{ + FormatType{AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte}, + FormatType{AL_FORMAT_MONO16, DevFmtMono, DevFmtShort}, + FormatType{AL_FORMAT_MONO_I32, DevFmtMono, DevFmtInt}, + FormatType{AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat}, + + FormatType{AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte}, + FormatType{AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort}, + FormatType{AL_FORMAT_STEREO_I32, DevFmtStereo, DevFmtInt}, + FormatType{AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat}, + + FormatType{AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte}, + FormatType{AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort}, + FormatType{AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat}, + FormatType{AL_FORMAT_QUAD_I32, DevFmtQuad, DevFmtInt}, + FormatType{AL_FORMAT_QUAD_FLOAT32, DevFmtQuad, DevFmtFloat}, + + FormatType{AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte}, + FormatType{AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort}, + FormatType{AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat}, + FormatType{AL_FORMAT_51CHN_I32, DevFmtX51, DevFmtInt}, + FormatType{AL_FORMAT_51CHN_FLOAT32, DevFmtX51, DevFmtFloat}, + + FormatType{AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte}, + FormatType{AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort}, + FormatType{AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat}, + FormatType{AL_FORMAT_61CHN_I32, DevFmtX61, DevFmtInt}, + FormatType{AL_FORMAT_61CHN_FLOAT32, DevFmtX61, DevFmtFloat}, + + FormatType{AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte}, + FormatType{AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort}, + FormatType{AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat}, + FormatType{AL_FORMAT_71CHN_I32, DevFmtX71, DevFmtInt}, + FormatType{AL_FORMAT_71CHN_FLOAT32, DevFmtX71, DevFmtFloat}, }; for(const auto &item : list) @@ -976,10 +979,14 @@ std::unique_ptr<Compressor> CreateDeviceLimiter(const ALCdevice *device, const f */ inline void UpdateClockBase(ALCdevice *device) { - IncrementRef(device->MixCount); - device->ClockBase += nanoseconds{seconds{device->SamplesDone}} / device->Frequency; - device->SamplesDone = 0; - IncrementRef(device->MixCount); + const auto mixLock = device->getWriteMixLock(); + + auto samplesDone = device->mSamplesDone.load(std::memory_order_relaxed); + auto clockBase = device->mClockBase.load(std::memory_order_relaxed); + + clockBase += nanoseconds{seconds{samplesDone}} / device->Frequency; + device->mClockBase.store(clockBase, std::memory_order_relaxed); + device->mSamplesDone.store(0, std::memory_order_relaxed); } /** @@ -1004,8 +1011,8 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) std::optional<DevFmtType> opttype; std::optional<DevAmbiLayout> optlayout; std::optional<DevAmbiScaling> optscale; - uint period_size{DEFAULT_UPDATE_SIZE}; - uint buffer_size{DEFAULT_UPDATE_SIZE * DEFAULT_NUM_UPDATES}; + uint period_size{DefaultUpdateSize}; + uint buffer_size{DefaultUpdateSize * DefaultNumUpdates}; int hrtf_id{-1}; uint aorder{0u}; @@ -1013,71 +1020,73 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) { /* Get default settings from the user configuration */ - if(auto freqopt = device->configValue<uint>(nullptr, "frequency")) + if(auto freqopt = device->configValue<uint>({}, "frequency")) { - optsrate = clampu(*freqopt, MIN_OUTPUT_RATE, MAX_OUTPUT_RATE); + optsrate = clampu(*freqopt, MinOutputRate, MaxOutputRate); - const double scale{static_cast<double>(*optsrate) / DEFAULT_OUTPUT_RATE}; - period_size = static_cast<uint>(period_size*scale + 0.5); + const double scale{static_cast<double>(*optsrate) / double{DefaultOutputRate}}; + period_size = static_cast<uint>(std::lround(period_size * scale)); } - if(auto persizeopt = device->configValue<uint>(nullptr, "period_size")) + if(auto persizeopt = device->configValue<uint>({}, "period_size")) period_size = clampu(*persizeopt, 64, 8192); - if(auto numperopt = device->configValue<uint>(nullptr, "periods")) + if(auto numperopt = device->configValue<uint>({}, "periods")) buffer_size = clampu(*numperopt, 2, 16) * period_size; else - buffer_size = period_size * DEFAULT_NUM_UPDATES; + buffer_size = period_size * uint{DefaultNumUpdates}; - if(auto typeopt = device->configValue<std::string>(nullptr, "sample-type")) + if(auto typeopt = device->configValue<std::string>({}, "sample-type")) { - static constexpr struct TypeMap { - const char name[8]; + struct TypeMap { + const char name[8]; /* NOLINT(*-avoid-c-arrays) */ DevFmtType type; - } typelist[] = { - { "int8", DevFmtByte }, - { "uint8", DevFmtUByte }, - { "int16", DevFmtShort }, - { "uint16", DevFmtUShort }, - { "int32", DevFmtInt }, - { "uint32", DevFmtUInt }, - { "float32", DevFmtFloat }, + }; + static constexpr std::array typelist{ + TypeMap{"int8", DevFmtByte }, + TypeMap{"uint8", DevFmtUByte }, + TypeMap{"int16", DevFmtShort }, + TypeMap{"uint16", DevFmtUShort}, + TypeMap{"int32", DevFmtInt }, + TypeMap{"uint32", DevFmtUInt }, + TypeMap{"float32", DevFmtFloat }, }; const ALCchar *fmt{typeopt->c_str()}; - auto iter = std::find_if(std::begin(typelist), std::end(typelist), + auto iter = std::find_if(typelist.begin(), typelist.end(), [fmt](const TypeMap &entry) -> bool { return al::strcasecmp(entry.name, fmt) == 0; }); - if(iter == std::end(typelist)) + if(iter == typelist.end()) ERR("Unsupported sample-type: %s\n", fmt); else opttype = iter->type; } - if(auto chanopt = device->configValue<std::string>(nullptr, "channels")) + if(auto chanopt = device->configValue<std::string>({}, "channels")) { - static constexpr struct ChannelMap { - const char name[16]; + struct ChannelMap { + const char name[16]; /* NOLINT(*-avoid-c-arrays) */ DevFmtChannels chans; uint8_t order; - } chanlist[] = { - { "mono", DevFmtMono, 0 }, - { "stereo", DevFmtStereo, 0 }, - { "quad", DevFmtQuad, 0 }, - { "surround51", DevFmtX51, 0 }, - { "surround61", DevFmtX61, 0 }, - { "surround71", DevFmtX71, 0 }, - { "surround714", DevFmtX714, 0 }, - { "surround3d71", DevFmtX3D71, 0 }, - { "surround51rear", DevFmtX51, 0 }, - { "ambi1", DevFmtAmbi3D, 1 }, - { "ambi2", DevFmtAmbi3D, 2 }, - { "ambi3", DevFmtAmbi3D, 3 }, + }; + static constexpr std::array chanlist{ + ChannelMap{"mono", DevFmtMono, 0}, + ChannelMap{"stereo", DevFmtStereo, 0}, + ChannelMap{"quad", DevFmtQuad, 0}, + ChannelMap{"surround51", DevFmtX51, 0}, + ChannelMap{"surround61", DevFmtX61, 0}, + ChannelMap{"surround71", DevFmtX71, 0}, + ChannelMap{"surround714", DevFmtX714, 0}, + ChannelMap{"surround3d71", DevFmtX3D71, 0}, + ChannelMap{"surround51rear", DevFmtX51, 0}, + ChannelMap{"ambi1", DevFmtAmbi3D, 1}, + ChannelMap{"ambi2", DevFmtAmbi3D, 2}, + ChannelMap{"ambi3", DevFmtAmbi3D, 3}, }; const ALCchar *fmt{chanopt->c_str()}; - auto iter = std::find_if(std::begin(chanlist), std::end(chanlist), + auto iter = std::find_if(chanlist.begin(), chanlist.end(), [fmt](const ChannelMap &entry) -> bool { return al::strcasecmp(entry.name, fmt) == 0; }); - if(iter == std::end(chanlist)) + if(iter == chanlist.end()) ERR("Unsupported channels: %s\n", fmt); else { @@ -1085,7 +1094,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) aorder = iter->order; } } - if(auto ambiopt = device->configValue<std::string>(nullptr, "ambi-format")) + if(auto ambiopt = device->configValue<std::string>({}, "ambi-format")) { const ALCchar *fmt{ambiopt->c_str()}; if(al::strcasecmp(fmt, "fuma") == 0) @@ -1112,7 +1121,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) ERR("Unsupported ambi-format: %s\n", fmt); } - if(auto hrtfopt = device->configValue<std::string>(nullptr, "hrtf")) + if(auto hrtfopt = device->configValue<std::string>({}, "hrtf")) { WARN("general/hrtf is deprecated, please use stereo-encoding instead\n"); @@ -1129,7 +1138,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) } } - if(auto encopt = device->configValue<std::string>(nullptr, "stereo-encoding")) + if(auto encopt = device->configValue<std::string>({}, "stereo-encoding")) { const char *mode{encopt->c_str()}; if(al::strcasecmp(mode, "basic") == 0 || al::strcasecmp(mode, "panpot") == 0) @@ -1197,7 +1206,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) case ATTRIBUTE(ALC_MAX_AUXILIARY_SENDS) numSends = static_cast<uint>(attrList[attrIdx + 1]); if(numSends > INT_MAX) numSends = 0; - else numSends = minu(numSends, MAX_SENDS); + else numSends = minu(numSends, MaxSendCount); break; case ATTRIBUTE(ALC_HRTF_SOFT) @@ -1240,7 +1249,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) { if(!optchans || !opttype) return ALC_INVALID_VALUE; - if(freqAttr < MIN_OUTPUT_RATE || freqAttr > MAX_OUTPUT_RATE) + if(freqAttr < int{MinOutputRate} || freqAttr > int{MaxOutputRate}) return ALC_INVALID_VALUE; if(*optchans == DevFmtAmbi3D) { @@ -1317,12 +1326,12 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(freqAttr) { - uint oldrate = optsrate.value_or(DEFAULT_OUTPUT_RATE); - freqAttr = clampi(freqAttr, MIN_OUTPUT_RATE, MAX_OUTPUT_RATE); + uint oldrate = optsrate.value_or(DefaultOutputRate); + freqAttr = clampi(freqAttr, MinOutputRate, MaxOutputRate); const double scale{static_cast<double>(freqAttr) / oldrate}; - period_size = static_cast<uint>(period_size*scale + 0.5); - buffer_size = static_cast<uint>(buffer_size*scale + 0.5); + period_size = static_cast<uint>(std::lround(period_size * scale)); + buffer_size = static_cast<uint>(std::lround(buffer_size * scale)); optsrate = static_cast<uint>(freqAttr); } } @@ -1330,16 +1339,19 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) /* If a context is already running on the device, stop playback so the * device attributes can be updated. */ - if(device->Flags.test(DeviceRunning)) + if(device->mDeviceState == DeviceState::Playing) + { device->Backend->stop(); - device->Flags.reset(DeviceRunning); + device->mDeviceState = DeviceState::Unprepared; + } UpdateClockBase(device); } - if(device->Flags.test(DeviceRunning)) + if(device->mDeviceState == DeviceState::Playing) return ALC_NO_ERROR; + device->mDeviceState = DeviceState::Unprepared; device->AvgSpeakerDist = 0.0f; device->mNFCtrlFilter = NfcFilter{}; device->mUhjEncoder = nullptr; @@ -1393,7 +1405,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) device->mAmbiOrder = 0; device->BufferSize = buffer_size; device->UpdateSize = period_size; - device->Frequency = optsrate.value_or(DEFAULT_OUTPUT_RATE); + device->Frequency = optsrate.value_or(DefaultOutputRate); device->Flags.set(FrequencyRequest, optsrate.has_value()) .set(ChannelsRequest, optchans.has_value()) .set(SampleTypeRequest, opttype.has_value()); @@ -1462,7 +1474,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(device->Type != DeviceType::Loopback) { - if(auto modeopt = device->configValue<std::string>(nullptr, "stereo-mode")) + if(auto modeopt = device->configValue<std::string>({}, "stereo-mode")) { const char *mode{modeopt->c_str()}; if(al::strcasecmp(mode, "headphones") == 0) @@ -1479,7 +1491,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) /* Calculate the max number of sources, and split them between the mono and * stereo count given the requested number of stereo sources. */ - if(auto srcsopt = device->configValue<uint>(nullptr, "sources")) + if(auto srcsopt = device->configValue<uint>({}, "sources")) { if(*srcsopt <= 0) numMono = 256; else numMono = maxu(*srcsopt, 16); @@ -1495,8 +1507,8 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) device->NumMonoSources = numMono; device->NumStereoSources = numStereo; - if(auto sendsopt = device->configValue<int>(nullptr, "sends")) - numSends = minu(numSends, static_cast<uint>(clampi(*sendsopt, 0, MAX_SENDS))); + if(auto sendsopt = device->configValue<int>({}, "sends")) + numSends = minu(numSends, static_cast<uint>(clampi(*sendsopt, 0, MaxSendCount))); device->NumAuxSends = numSends; TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n", @@ -1519,13 +1531,13 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) case DevFmtAmbi3D: break; } - nanoseconds::rep sample_delay{0}; + size_t sample_delay{0}; if(auto *encoder{device->mUhjEncoder.get()}) sample_delay += encoder->getDelay(); - if(device->getConfigValueBool(nullptr, "dither", true)) + if(device->getConfigValueBool({}, "dither", true)) { - int depth{device->configValue<int>(nullptr, "dither-depth").value_or(0)}; + int depth{device->configValue<int>({}, "dither-depth").value_or(0)}; if(depth <= 0) { switch(device->FmtType) @@ -1558,7 +1570,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) device->DitherDepth); if(!optlimit) - optlimit = device->configValue<bool>(nullptr, "output-limiter"); + optlimit = device->configValue<bool>({}, "output-limiter"); /* If the gain limiter is unset, use the limiter for integer-based output * (where samples must be clamped), and don't for floating-point (which can @@ -1612,6 +1624,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) } /* Convert the sample delay from samples to nanosamples to nanoseconds. */ + sample_delay = std::min<size_t>(sample_delay, std::numeric_limits<int>::max()); device->FixedLatency += nanoseconds{seconds{sample_delay}} / device->Frequency; TRACE("Fixed device latency: %" PRId64 "ns\n", int64_t{device->FixedLatency.count()}); @@ -1624,9 +1637,10 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) std::unique_lock<std::mutex> slotlock{context->mEffectSlotLock}; /* Clear out unused effect slot clusters. */ - auto slot_cluster_not_in_use = [](ContextBase::EffectSlotCluster &cluster) + auto slot_cluster_not_in_use = [](ContextBase::EffectSlotCluster &clusterptr) { - for(size_t i{0};i < ContextBase::EffectSlotClusterSize;++i) + const auto cluster = al::span{*clusterptr}; + for(size_t i{0};i < cluster.size();++i) { if(cluster[i].InUse) return false; @@ -1640,24 +1654,29 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) /* Free all wet buffers. Any in use will be reallocated with an updated * configuration in aluInitEffectPanning. */ - for(auto&& slots : context->mEffectSlotClusters) + for(auto& clusterptr : context->mEffectSlotClusters) { - for(size_t i{0};i < ContextBase::EffectSlotClusterSize;++i) + const auto cluster = al::span{*clusterptr}; + for(size_t i{0};i < cluster.size();++i) { - slots[i].mWetBuffer.clear(); - slots[i].mWetBuffer.shrink_to_fit(); - slots[i].Wet.Buffer = {}; + cluster[i].mWetBuffer.clear(); + cluster[i].mWetBuffer.shrink_to_fit(); + cluster[i].Wet.Buffer = {}; } } if(ALeffectslot *slot{context->mDefaultSlot.get()}) { - aluInitEffectPanning(slot->mSlot, context); + auto *slotbase = slot->mSlot; + aluInitEffectPanning(slotbase, context); + + if(auto *props = slotbase->Update.exchange(nullptr, std::memory_order_relaxed)) + AtomicReplaceHead(context->mFreeEffectSlotProps, props); EffectState *state{slot->Effect.State.get()}; state->mOutTarget = device->Dry.Buffer; state->deviceUpdate(device, slot->Buffer); - slot->updateProps(context); + slot->mPropsDirty = true; } if(EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_relaxed)}) @@ -1667,18 +1686,25 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; - ALeffectslot *slot{sublist.EffectSlots + idx}; + const auto idx = static_cast<uint>(al::countr_zero(usemask)); + auto &slot = (*sublist.EffectSlots)[idx]; usemask &= ~(1_u64 << idx); - aluInitEffectPanning(slot->mSlot, context); + auto *slotbase = slot.mSlot; + aluInitEffectPanning(slotbase, context); - EffectState *state{slot->Effect.State.get()}; + if(auto *props = slotbase->Update.exchange(nullptr, std::memory_order_relaxed)) + AtomicReplaceHead(context->mFreeEffectSlotProps, props); + + EffectState *state{slot.Effect.State.get()}; state->mOutTarget = device->Dry.Buffer; - state->deviceUpdate(device, slot->Buffer); - slot->updateProps(context); + state->deviceUpdate(device, slot.Buffer); + slot.mPropsDirty = true; } } + /* Clear all effect slot props to let them get allocated again. */ + context->mEffectSlotPropClusters.clear(); + context->mFreeEffectSlotProps.store(nullptr, std::memory_order_relaxed); slotlock.unlock(); const uint num_sends{device->NumAuxSends}; @@ -1688,8 +1714,8 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; - ALsource *source{sublist.Sources + idx}; + const auto idx = static_cast<uint>(al::countr_zero(usemask)); + auto &source = (*sublist.Sources)[idx]; usemask &= ~(1_u64 << idx); auto clear_send = [](ALsource::SendData &send) -> void @@ -1699,19 +1725,18 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) send.Slot = nullptr; send.Gain = 1.0f; send.GainHF = 1.0f; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; }; - auto send_begin = source->Send.begin() + static_cast<ptrdiff_t>(num_sends); - std::for_each(send_begin, source->Send.end(), clear_send); + auto send_begin = source.Send.begin() + static_cast<ptrdiff_t>(num_sends); + std::for_each(send_begin, source.Send.end(), clear_send); - source->mPropsDirty = true; + source.mPropsDirty = true; } } - auto voicelist = context->getVoicesSpan(); - for(Voice *voice : voicelist) + for(Voice *voice : context->getVoicesSpan()) { /* Clear extraneous property set sends. */ std::fill(std::begin(voice->mProps.Send)+num_sends, std::end(voice->mProps.Send), @@ -1743,16 +1768,18 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) context->mPropsDirty = false; UpdateContextProps(context); + UpdateAllEffectSlotProps(context); UpdateAllSourceProps(context); } mixer_mode.leave(); + device->mDeviceState = DeviceState::Configured; if(!device->Flags.test(DevicePaused)) { try { auto backend = device->Backend.get(); backend->start(); - device->Flags.set(DeviceRunning); + device->mDeviceState = DeviceState::Playing; } catch(al::backend_exception& e) { ERR("%s\n", e.what()); @@ -1777,7 +1804,7 @@ bool ResetDeviceParams(ALCdevice *device, const int *attrList) if(!device->Connected.load(std::memory_order_relaxed)) UNLIKELY { /* Make sure disconnection is finished before continuing on. */ - device->waitForMix(); + std::ignore = device->waitForMix(); for(ContextBase *ctxbase : *device->mContexts.load(std::memory_order_acquire)) { @@ -1850,7 +1877,7 @@ FORCE_ALIGN void ALC_APIENTRY alsoft_set_log_callback(LPALSOFTLOGCALLBACK callba } /** Returns a new reference to the currently active context for this thread. */ -ContextRef GetContextRef(void) +ContextRef GetContextRef() { ALCcontext *context{ALCcontext::getThreadContext()}; if(context) @@ -2030,7 +2057,7 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para ProbeAllDevicesList(); /* Copy first entry as default. */ - alcDefaultAllDevicesSpecifier = alcAllDevicesList.c_str(); + alcDefaultAllDevicesSpecifier = alcAllDevicesList.substr(0, alcAllDevicesList.find('\0')); value = alcDefaultAllDevicesSpecifier.c_str(); break; @@ -2039,7 +2066,8 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para ProbeCaptureDeviceList(); /* Copy first entry as default. */ - alcCaptureDefaultDeviceSpecifier = alcCaptureDeviceList.c_str(); + alcCaptureDefaultDeviceSpecifier = alcCaptureDeviceList.substr(0, + alcCaptureDeviceList.find('\0')); value = alcCaptureDefaultDeviceSpecifier.c_str(); break; @@ -2097,7 +2125,7 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span<int> values[0] = alcEFXMinorVersion; return 1; case ALC_MAX_AUXILIARY_SENDS: - values[0] = MAX_SENDS; + values[0] = MaxSendCount; return 1; case ALC_ATTRIBUTES_SIZE: @@ -2504,9 +2532,10 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, nanoseconds basecount; do { refcount = dev->waitForMix(); - basecount = dev->ClockBase; - samplecount = dev->SamplesDone; - } while(refcount != ReadRef(dev->MixCount)); + basecount = dev->mClockBase.load(std::memory_order_relaxed); + samplecount = dev->mSamplesDone.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + } while(refcount != dev->mMixCount.load(std::memory_order_relaxed)); basecount += nanoseconds{seconds{samplecount}} / dev->Frequency; *values = basecount.count(); } @@ -2540,23 +2569,25 @@ ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const A { DeviceRef dev{VerifyDevice(device)}; if(!extName) + { alcSetError(dev.get(), ALC_INVALID_VALUE); - else + return ALC_FALSE; + } + + const std::string_view tofind{extName}; + auto extlist = dev ? std::string_view{alcExtensionList} : std::string_view{alcNoDeviceExtList}; + while(!extlist.empty()) { - size_t len = strlen(extName); - const char *ptr = (dev ? alcExtensionList : alcNoDeviceExtList); - while(ptr && *ptr) - { - if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len]))) - return ALC_TRUE; + auto nextpos = extlist.find(' '); + auto tocheck = extlist.substr(0, nextpos); + if(tocheck.size() == tofind.size() + && al::strncasecmp(tofind.data(), tocheck.data(), tofind.size()) == 0) + return ALC_TRUE; - if((ptr=strchr(ptr, ' ')) != nullptr) - { - do { - ++ptr; - } while(isspace(*ptr)); - } - } + if(nextpos == std::string_view::npos) + break; + + extlist.remove_prefix(nextpos+1); } return ALC_FALSE; } @@ -2678,7 +2709,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin } context->init(); - if(auto volopt = dev->configValue<float>(nullptr, "volume-adjust")) + if(auto volopt = dev->configValue<float>({}, "volume-adjust")) { const float valf{*volopt}; if(!std::isfinite(valf)) @@ -2700,8 +2731,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin * old array. */ auto *oldarray = device->mContexts.load(); - const size_t newcount{oldarray->size()+1}; - std::unique_ptr<ContextArray> newarray{ContextArray::Create(newcount)}; + auto newarray = ContextArray::Create(oldarray->size() + 1); /* Copy the current/old context handles to the new array, appending the * new context. @@ -2712,12 +2742,8 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin /* Store the new context array in the device. Wait for any current mix * to finish before deleting the old array. */ - dev->mContexts.store(newarray.release()); - if(oldarray != &DeviceBase::sEmptyContextArray) - { - dev->waitForMix(); - delete oldarray; - } + auto prevarray = dev->mContexts.exchange(std::move(newarray)); + std::ignore = dev->waitForMix(); } statelock.unlock(); @@ -2761,15 +2787,11 @@ ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept ALCdevice *Device{ctx->mALDevice.get()}; std::lock_guard<std::mutex> _{Device->StateLock}; - if(!ctx->deinit() && Device->Flags.test(DeviceRunning)) - { - Device->Backend->stop(); - Device->Flags.reset(DeviceRunning); - } + ctx->deinit(); } -ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) noexcept +ALC_API auto ALC_APIENTRY alcGetCurrentContext() noexcept -> ALCcontext* { ALCcontext *Context{ALCcontext::getThreadContext()}; if(!Context) Context = ALCcontext::sGlobalContext.load(); @@ -2777,7 +2799,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) noexcept } /** Returns the currently active thread-local context. */ -ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) noexcept +ALC_API auto ALC_APIENTRY alcGetThreadContext() noexcept -> ALCcontext* { return ALCcontext::getThreadContext(); } ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) noexcept @@ -2802,7 +2824,7 @@ ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) noexc * the current context as its refcount is decremented. */ } - ContextRef{ALCcontext::sGlobalContext.exchange(ctx.release())}; + ctx = ContextRef{ALCcontext::sGlobalContext.exchange(ctx.release())}; ALCcontext::sGlobalContextLock.store(false, std::memory_order_release); /* Take ownership of the thread-local context reference (if any), clearing @@ -2887,17 +2909,23 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) noexcep #ifdef ALSOFT_EAX eax_g_is_enabled ? uint{EAX_MAX_FXSLOTS} : #endif // ALSOFT_EAX - DEFAULT_SENDS + uint{DefaultSendCount} }; - DeviceRef device{new ALCdevice{DeviceType::Playback}}; + DeviceRef device{new(std::nothrow) ALCdevice{DeviceType::Playback}}; + if(!device) + { + WARN("Failed to create playback device handle\n"); + alcSetError(nullptr, ALC_OUT_OF_MEMORY); + return nullptr; + } /* Set output format */ device->FmtChans = DevFmtChannelsDefault; device->FmtType = DevFmtTypeDefault; - device->Frequency = DEFAULT_OUTPUT_RATE; - device->UpdateSize = DEFAULT_UPDATE_SIZE; - device->BufferSize = DEFAULT_UPDATE_SIZE * DEFAULT_NUM_UPDATES; + device->Frequency = DefaultOutputRate; + device->UpdateSize = DefaultUpdateSize; + device->BufferSize = DefaultUpdateSize * DefaultNumUpdates; device->SourcesMax = 256; device->NumStereoSources = 1; @@ -2973,7 +3001,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept auto ctxiter = std::lower_bound(ContextList.begin(), ContextList.end(), ctx); if(ctxiter != ContextList.end() && *ctxiter == ctx) { - orphanctxs.emplace_back(ContextRef{*ctxiter}); + orphanctxs.emplace_back(*ctxiter); ContextList.erase(ctxiter); } } @@ -2986,9 +3014,11 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept } orphanctxs.clear(); - if(dev->Flags.test(DeviceRunning)) + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } return ALC_TRUE; } @@ -3023,7 +3053,13 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, else TRACE("Opening default capture device\n"); - DeviceRef device{new ALCdevice{DeviceType::Capture}}; + DeviceRef device{new(std::nothrow) ALCdevice{DeviceType::Capture}}; + if(!device) + { + WARN("Failed to create capture device handle\n"); + alcSetError(nullptr, ALC_OUT_OF_MEMORY); + return nullptr; + } auto decompfmt = DecomposeDevFormat(format); if(!decompfmt) @@ -3070,6 +3106,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get()); DeviceList.emplace(iter, device.get()); } + device->mDeviceState = DeviceState::Configured; TRACE("Created capture device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str()); return device.release(); @@ -3095,9 +3132,11 @@ ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) noexcep listlock.unlock(); std::lock_guard<std::mutex> _{dev->StateLock}; - if(dev->Flags.test(DeviceRunning)) + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } return ALC_TRUE; } @@ -3112,14 +3151,15 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) noexcept } std::lock_guard<std::mutex> _{dev->StateLock}; - if(!dev->Connected.load(std::memory_order_acquire)) + if(!dev->Connected.load(std::memory_order_acquire) + || dev->mDeviceState < DeviceState::Configured) alcSetError(dev.get(), ALC_INVALID_DEVICE); - else if(!dev->Flags.test(DeviceRunning)) + else if(dev->mDeviceState != DeviceState::Playing) { try { auto backend = dev->Backend.get(); backend->start(); - dev->Flags.set(DeviceRunning); + dev->mDeviceState = DeviceState::Playing; } catch(al::backend_exception& e) { ERR("%s\n", e.what()); @@ -3137,9 +3177,11 @@ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) noexcept else { std::lock_guard<std::mutex> _{dev->StateLock}; - if(dev->Flags.test(DeviceRunning)) + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } } } @@ -3194,10 +3236,16 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN #ifdef ALSOFT_EAX eax_g_is_enabled ? uint{EAX_MAX_FXSLOTS} : #endif // ALSOFT_EAX - DEFAULT_SENDS + uint{DefaultSendCount} }; - DeviceRef device{new ALCdevice{DeviceType::Loopback}}; + DeviceRef device{new(std::nothrow) ALCdevice{DeviceType::Loopback}}; + if(!device) + { + WARN("Failed to create loopback device handle\n"); + alcSetError(nullptr, ALC_OUT_OF_MEMORY); + return nullptr; + } device->SourcesMax = 256; device->AuxiliaryEffectSlotMax = 64; @@ -3207,7 +3255,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN device->BufferSize = 0; device->UpdateSize = 0; - device->Frequency = DEFAULT_OUTPUT_RATE; + device->Frequency = DefaultOutputRate; device->FmtChans = DevFmtChannelsDefault; device->FmtType = DevFmtTypeDefault; @@ -3250,7 +3298,7 @@ ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device else { if(DevFmtTypeFromEnum(type).has_value() && DevFmtChannelsFromEnum(channels).has_value() - && freq >= MIN_OUTPUT_RATE && freq <= MAX_OUTPUT_RATE) + && freq >= int{MinOutputRate} && freq <= int{MaxOutputRate}) return ALC_TRUE; } @@ -3291,9 +3339,11 @@ ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) noexcept else { std::lock_guard<std::mutex> _{dev->StateLock}; - if(dev->Flags.test(DeviceRunning)) + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } dev->Flags.set(DevicePaused); } } @@ -3311,6 +3361,18 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept std::lock_guard<std::mutex> _{dev->StateLock}; if(!dev->Flags.test(DevicePaused)) return; + if(dev->mDeviceState < DeviceState::Configured) + { + WARN("Cannot resume unconfigured device\n"); + alcSetError(dev.get(), ALC_INVALID_DEVICE); + return; + } + if(!dev->Connected.load()) + { + WARN("Cannot resume a disconnected device\n"); + alcSetError(dev.get(), ALC_INVALID_DEVICE); + return; + } dev->Flags.reset(DevicePaused); if(dev->mContexts.load()->empty()) return; @@ -3318,7 +3380,7 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept try { auto backend = dev->Backend.get(); backend->start(); - dev->Flags.set(DeviceRunning); + dev->mDeviceState = DeviceState::Playing; } catch(al::backend_exception& e) { ERR("%s\n", e.what()); @@ -3327,8 +3389,8 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept return; } TRACE("Post-resume: %s, %s, %uhz, %u / %u buffer\n", - DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), - device->Frequency, device->UpdateSize, device->BufferSize); + DevFmtChannelsString(dev->FmtChans), DevFmtTypeString(dev->FmtType), + dev->Frequency, dev->UpdateSize, dev->BufferSize); } @@ -3375,9 +3437,11 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi /* Force the backend to stop mixing first since we're resetting. Also reset * the connected state so lost devices can attempt recover. */ - if(dev->Flags.test(DeviceRunning)) + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } return ResetDeviceParams(dev.get(), attribs) ? ALC_TRUE : ALC_FALSE; } @@ -3407,12 +3471,12 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, } std::lock_guard<std::mutex> _{dev->StateLock}; - /* Force the backend to stop mixing first since we're reopening. */ - if(dev->Flags.test(DeviceRunning)) + /* Force the backend device to stop first since we're opening another one. */ + const bool wasPlaying{dev->mDeviceState == DeviceState::Playing}; + if(wasPlaying) { - auto backend = dev->Backend.get(); - backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->Backend->stop(); + dev->mDeviceState = DeviceState::Configured; } BackendPtr newbackend; @@ -3434,16 +3498,12 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, alcSetError(dev.get(), (e.errorCode() == al::backend_error::OutOfMemory) ? ALC_OUT_OF_MEMORY : ALC_INVALID_VALUE); - /* If the device is connected, not paused, and has contexts, ensure it - * continues playing. - */ - if(dev->Connected.load(std::memory_order_relaxed) && !dev->Flags.test(DevicePaused) - && !dev->mContexts.load(std::memory_order_relaxed)->empty()) + if(dev->Connected.load(std::memory_order_relaxed) && wasPlaying) { try { auto backend = dev->Backend.get(); backend->start(); - dev->Flags.set(DeviceRunning); + dev->mDeviceState = DeviceState::Playing; } catch(al::backend_exception &be) { ERR("%s\n", be.what()); @@ -3454,6 +3514,7 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, } listlock.unlock(); dev->Backend = std::move(newbackend); + dev->mDeviceState = DeviceState::Unprepared; TRACE("Reopened device %p, \"%s\"\n", voidp{dev.get()}, dev->DeviceName.c_str()); /* Always return true even if resetting fails. It shouldn't fail, but this diff --git a/alc/alconfig.cpp b/alc/alconfig.cpp index cfa178fb..d9e97e09 100644 --- a/alc/alconfig.cpp +++ b/alc/alconfig.cpp @@ -22,9 +22,6 @@ #include "alconfig.h" -#include <cstdlib> -#include <cctype> -#include <cstring> #ifdef _WIN32 #include <windows.h> #include <shlobj.h> @@ -34,16 +31,20 @@ #endif #include <algorithm> -#include <cstdio> +#include <cctype> +#include <cstdlib> +#include <istream> +#include <limits> #include <string> +#include <string_view> #include <utility> +#include <vector> #include "alfstream.h" #include "alstring.h" #include "core/helpers.h" #include "core/logging.h" #include "strutils.h" -#include "vector.h" #if defined(ALSOFT_UWP) #include <winrt/Windows.Media.Core.h> // !!This is important!! @@ -79,57 +80,43 @@ bool readline(std::istream &f, std::string &output) return std::getline(f, output) && !output.empty(); } -std::string expdup(const char *str) +std::string expdup(std::string_view str) { std::string output; - std::string envval; - while(*str != '\0') + while(!str.empty()) { - const char *addstr; - size_t addstrlen; - - if(str[0] != '$') + if(auto nextpos = str.find('$')) { - const char *next = std::strchr(str, '$'); - addstr = str; - addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str); + output += str.substr(0, nextpos); + if(nextpos == std::string_view::npos) + break; - str += addstrlen; + str.remove_prefix(nextpos); } - else - { - str++; - if(*str == '$') - { - const char *next = std::strchr(str+1, '$'); - addstr = str; - addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str); - str += addstrlen; - } - else - { - const bool hasbraces{(*str == '{')}; + str.remove_prefix(1); + if(str.front() == '$') + { + output += '$'; + str.remove_prefix(1); + continue; + } - if(hasbraces) str++; - const char *envstart = str; - while(std::isalnum(*str) || *str == '_') - ++str; - if(hasbraces && *str != '}') - continue; - const std::string envname{envstart, str}; - if(hasbraces) str++; + const bool hasbraces{str.front() == '{'}; + if(hasbraces) str.remove_prefix(1); - envval = al::getenv(envname.c_str()).value_or(std::string{}); - addstr = envval.data(); - addstrlen = envval.length(); - } - } - if(addstrlen == 0) + size_t envend{0}; + while(std::isalnum(str[envend]) || str[envend] == '_') + ++envend; + if(hasbraces && str[envend] != '}') continue; + const std::string envname{str.substr(0, envend)}; + if(hasbraces) ++envend; + str.remove_prefix(envend); - output.append(addstr, addstrlen); + if(auto envval = al::getenv(envname.c_str())) + output += *envval; } return output; @@ -147,42 +134,42 @@ void LoadConfigFromFile(std::istream &f) if(buffer[0] == '[') { - auto line = const_cast<char*>(buffer.data()); - char *section = line+1; - char *endsection; - - endsection = std::strchr(section, ']'); - if(!endsection || section == endsection) + auto endpos = buffer.find(']', 1); + if(endpos == 1 || endpos == std::string::npos) { - ERR(" config parse error: bad line \"%s\"\n", line); + ERR(" config parse error: bad line \"%s\"\n", buffer.c_str()); continue; } - if(endsection[1] != 0) + if(buffer[endpos+1] != '\0') { - char *end = endsection+1; - while(std::isspace(*end)) - ++end; - if(*end != 0 && *end != '#') + size_t last{endpos+1}; + while(last < buffer.size() && std::isspace(buffer[last])) + ++last; + + if(last < buffer.size() && buffer[last] != '#') { - ERR(" config parse error: bad line \"%s\"\n", line); + ERR(" config parse error: bad line \"%s\"\n", buffer.c_str()); continue; } } - *endsection = 0; + + auto section = std::string_view{buffer}.substr(1, endpos-1); + auto generalName = std::string_view{"general"}; curSection.clear(); - if(al::strcasecmp(section, "general") != 0) + if(section.size() != generalName.size() + || al::strncasecmp(section.data(), generalName.data(), section.size()) != 0) { do { - char *nextp = std::strchr(section, '%'); - if(!nextp) + auto nextp = section.find('%'); + if(nextp == std::string_view::npos) { curSection += section; break; } - curSection.append(section, nextp); - section = nextp; + curSection += section.substr(0, nextp); + section.remove_prefix(nextp); if(((section[1] >= '0' && section[1] <= '9') || (section[1] >= 'a' && section[1] <= 'f') || @@ -205,19 +192,19 @@ void LoadConfigFromFile(std::istream &f) else if(section[2] >= 'A' && section[2] <= 'F') b |= (section[2]-'A'+0x0a); curSection += static_cast<char>(b); - section += 3; + section.remove_prefix(3); } else if(section[1] == '%') { curSection += '%'; - section += 2; + section.remove_prefix(2); } else { curSection += '%'; - section += 1; + section.remove_prefix(1); } - } while(*section != 0); + } while(!section.empty()); } continue; @@ -235,16 +222,17 @@ void LoadConfigFromFile(std::istream &f) ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str()); continue; } - auto keyend = sep++; - while(keyend > 0 && std::isspace(buffer[keyend-1])) - --keyend; - if(!keyend) + auto keypart = std::string_view{buffer}.substr(0, sep++); + while(!keypart.empty() && std::isspace(keypart.back())) + keypart.remove_suffix(1); + if(keypart.empty()) { ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str()); continue; } - while(sep < buffer.size() && std::isspace(buffer[sep])) - sep++; + auto valpart = std::string_view{buffer}.substr(sep); + while(!valpart.empty() && std::isspace(valpart.front())) + valpart.remove_prefix(1); std::string fullKey; if(!curSection.empty()) @@ -252,20 +240,25 @@ void LoadConfigFromFile(std::istream &f) fullKey += curSection; fullKey += '/'; } - fullKey += buffer.substr(0u, keyend); + fullKey += keypart; - std::string value{(sep < buffer.size()) ? buffer.substr(sep) : std::string{}}; - if(value.size() > 1) + if(valpart.size() > std::numeric_limits<int>::max()) + { + ERR(" config parse error: value too long in line \"%s\"\n", buffer.c_str()); + continue; + } + if(valpart.size() > 1) { - if((value.front() == '"' && value.back() == '"') - || (value.front() == '\'' && value.back() == '\'')) + if((valpart.front() == '"' && valpart.back() == '"') + || (valpart.front() == '\'' && valpart.back() == '\'')) { - value.pop_back(); - value.erase(value.begin()); + valpart.remove_prefix(1); + valpart.remove_suffix(1); } } - TRACE(" setting '%s' = '%s'\n", fullKey.c_str(), value.c_str()); + TRACE(" setting '%s' = '%.*s'\n", fullKey.c_str(), static_cast<int>(valpart.size()), + valpart.data()); /* Check if we already have this option set */ auto find_key = [&fullKey](const ConfigEntry &entry) -> bool @@ -273,27 +266,30 @@ void LoadConfigFromFile(std::istream &f) auto ent = std::find_if(ConfOpts.begin(), ConfOpts.end(), find_key); if(ent != ConfOpts.end()) { - if(!value.empty()) - ent->value = expdup(value.c_str()); + if(!valpart.empty()) + ent->value = expdup(valpart); else ConfOpts.erase(ent); } - else if(!value.empty()) - ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(value.c_str())}); + else if(!valpart.empty()) + ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(valpart)}); } ConfOpts.shrink_to_fit(); } -const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName) +const char *GetConfigValue(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) { - if(!keyName) + if(keyName.empty()) return nullptr; + auto generalName = std::string_view{"general"}; std::string key; - if(blockName && al::strcasecmp(blockName, "general") != 0) + if(!blockName.empty() && (blockName.size() != generalName.size() || + al::strncasecmp(blockName.data(), generalName.data(), blockName.size()) != 0)) { key = blockName; - if(devName) + if(!devName.empty()) { key += '/'; key += devName; @@ -303,7 +299,7 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha } else { - if(devName) + if(!devName.empty()) { key = devName; key += '/'; @@ -322,9 +318,9 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha return nullptr; } - if(!devName) + if(devName.empty()) return nullptr; - return GetConfigValue(nullptr, blockName, keyName); + return GetConfigValue({}, blockName, keyName); } } // namespace @@ -496,35 +492,40 @@ void ReadALConfig() } #endif -std::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName) +std::optional<std::string> ConfigValueStr(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName) { if(const char *val{GetConfigValue(devName, blockName, keyName)}) return val; return std::nullopt; } -std::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName) +std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) { if(const char *val{GetConfigValue(devName, blockName, keyName)}) return static_cast<int>(std::strtol(val, nullptr, 0)); return std::nullopt; } -std::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName) +std::optional<unsigned int> ConfigValueUInt(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName) { if(const char *val{GetConfigValue(devName, blockName, keyName)}) return static_cast<unsigned int>(std::strtoul(val, nullptr, 0)); return std::nullopt; } -std::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName) +std::optional<float> ConfigValueFloat(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName) { if(const char *val{GetConfigValue(devName, blockName, keyName)}) return std::strtof(val, nullptr); return std::nullopt; } -std::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName) +std::optional<bool> ConfigValueBool(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName) { if(const char *val{GetConfigValue(devName, blockName, keyName)}) return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0 @@ -532,7 +533,8 @@ std::optional<bool> ConfigValueBool(const char *devName, const char *blockName, return std::nullopt; } -bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def) +bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName, bool def) { if(const char *val{GetConfigValue(devName, blockName, keyName)}) return (al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0 diff --git a/alc/alconfig.h b/alc/alconfig.h index 1eb44405..e7daac28 100644 --- a/alc/alconfig.h +++ b/alc/alconfig.h @@ -3,16 +3,23 @@ #include <optional> #include <string> +#include <string_view> void ReadALConfig(); -bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def); +bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName, bool def); -std::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName); -std::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName); -std::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName); -std::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName); -std::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName); +std::optional<std::string> ConfigValueStr(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); +std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName); +std::optional<unsigned int> ConfigValueUInt(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); +std::optional<float> ConfigValueFloat(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); +std::optional<bool> ConfigValueBool(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); #endif /* ALCONFIG_H */ diff --git a/alc/alu.cpp b/alc/alu.cpp index e0858b18..b50eaa41 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -29,6 +29,7 @@ #include <chrono> #include <climits> #include <cstdarg> +#include <cstdint> #include <cstdio> #include <cstdlib> #include <functional> @@ -37,7 +38,6 @@ #include <memory> #include <new> #include <optional> -#include <stdint.h> #include <utility> #include "almalloc.h" @@ -141,7 +141,7 @@ using HrtfDirectMixerFunc = void(*)(const FloatBufferSpan LeftOut, const FloatBu HrtfDirectMixerFunc MixDirectHrtf{MixDirectHrtf_<CTag>}; -inline HrtfDirectMixerFunc SelectHrtfMixer(void) +inline HrtfDirectMixerFunc SelectHrtfMixer() { #ifdef HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) @@ -261,15 +261,15 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState case Resampler::Linear: break; case Resampler::Cubic: - state->cubic.filter = gCubicSpline.Tab.data(); + state->emplace<CubicState>().filter = gCubicSpline.Tab.data(); break; case Resampler::FastBSinc12: case Resampler::BSinc12: - BsincPrepare(increment, &state->bsinc, &gBSinc12); + BsincPrepare(increment, &state->emplace<BsincState>(), &gBSinc12); break; case Resampler::FastBSinc24: case Resampler::BSinc24: - BsincPrepare(increment, &state->bsinc, &gBSinc24); + BsincPrepare(increment, &state->emplace<BsincState>(), &gBSinc24); break; } return SelectResampler(resampler, increment); @@ -282,7 +282,7 @@ void DeviceBase::ProcessHrtf(const size_t SamplesToDo) const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; const size_t ridx{RealOut.ChannelIndex[FrontRight]}; - MixDirectHrtf(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer, HrtfAccumData, + MixDirectHrtf(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer, HrtfAccumData.data(), mHrtfState->mTemp.data(), mHrtfState->mChannels.data(), mHrtfState->mIrSize, SamplesToDo); } @@ -323,8 +323,7 @@ void DeviceBase::ProcessBs2b(const size_t SamplesToDo) const size_t ridx{RealOut.ChannelIndex[FrontRight]}; /* Now apply the BS2B binaural/crossfeed filter. */ - bs2b_cross_feed(Bs2b.get(), RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(), - SamplesToDo); + Bs2b->cross_feed(RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(), SamplesToDo); } @@ -451,14 +450,14 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa slot->Target = props->Target; slot->EffectType = props->Type; slot->mEffectProps = props->Props; - if(props->Type == EffectSlotType::Reverb || props->Type == EffectSlotType::EAXReverb) + if(auto *reverbprops = std::get_if<ReverbProps>(&props->Props)) { - slot->RoomRolloff = props->Props.Reverb.RoomRolloffFactor; - slot->DecayTime = props->Props.Reverb.DecayTime; - slot->DecayLFRatio = props->Props.Reverb.DecayLFRatio; - slot->DecayHFRatio = props->Props.Reverb.DecayHFRatio; - slot->DecayHFLimit = props->Props.Reverb.DecayHFLimit; - slot->AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF; + slot->RoomRolloff = reverbprops->RoomRolloffFactor; + slot->DecayTime = reverbprops->DecayTime; + slot->DecayLFRatio = reverbprops->DecayLFRatio; + slot->DecayHFRatio = reverbprops->DecayHFRatio; + slot->DecayHFLimit = reverbprops->DecayHFLimit; + slot->AirAbsorptionGainHF = reverbprops->AirAbsorptionGainHF; } else { @@ -499,7 +498,7 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa } } - AtomicReplaceHead(context->mFreeEffectslotProps, props); + AtomicReplaceHead(context->mFreeEffectSlotProps, props); EffectTarget output; if(EffectSlot *target{slot->Target}) @@ -679,16 +678,16 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order) auto P = [](const int i, const int l, const int a, const int n, const size_t last_band, const AmbiRotateMatrix &R) { - const float ri1{ R[ 1+2][static_cast<size_t>(i+2)]}; - const float rim1{R[-1+2][static_cast<size_t>(i+2)]}; - const float ri0{ R[ 0+2][static_cast<size_t>(i+2)]}; + const float ri1{ R[ 1+2][static_cast<size_t>(i+2_z)]}; + const float rim1{R[-1+2][static_cast<size_t>(i+2_z)]}; + const float ri0{ R[ 0+2][static_cast<size_t>(i+2_z)]}; const size_t y{last_band + static_cast<size_t>(a+l-1)}; if(n == -l) - return ri1*R[last_band][y] + rim1*R[last_band + static_cast<size_t>(l-1)*2][y]; + return ri1*R[last_band][y] + rim1*R[last_band + static_cast<size_t>(l-1_z)*2][y]; if(n == l) - return ri1*R[last_band + static_cast<size_t>(l-1)*2][y] - rim1*R[last_band][y]; - return ri0*R[last_band + static_cast<size_t>(n+l-1)][y]; + return ri1*R[last_band + static_cast<size_t>(l-1_z)*2][y] - rim1*R[last_band][y]; + return ri0*R[last_band + static_cast<size_t>(l-1_z+n)][y]; }; auto U = [P](const int l, const int m, const int n, const size_t last_band, @@ -776,48 +775,54 @@ struct GainTriplet { float Base, HF, LF; }; void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, const float zpos, const float Distance, const float Spread, const GainTriplet &DryGain, - const al::span<const GainTriplet,MAX_SENDS> WetGain, EffectSlot *(&SendSlots)[MAX_SENDS], - const VoiceProps *props, const ContextParams &Context, DeviceBase *Device) + const al::span<const GainTriplet,MaxSendCount> WetGain, + const al::span<EffectSlot*,MaxSendCount> SendSlots, const VoiceProps *props, + const ContextParams &Context, DeviceBase *Device) { - static constexpr ChanPosMap MonoMap[1]{ - { FrontCenter, std::array{0.0f, 0.0f, -1.0f} } - }, RearMap[2]{ - { BackLeft, std::array{-sin30, 0.0f, cos30} }, - { BackRight, std::array{ sin30, 0.0f, cos30} }, - }, QuadMap[4]{ - { FrontLeft, std::array{-sin45, 0.0f, -cos45} }, - { FrontRight, std::array{ sin45, 0.0f, -cos45} }, - { BackLeft, std::array{-sin45, 0.0f, cos45} }, - { BackRight, std::array{ sin45, 0.0f, cos45} }, - }, X51Map[6]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { SideLeft, std::array{-sin110, 0.0f, -cos110} }, - { SideRight, std::array{ sin110, 0.0f, -cos110} }, - }, X61Map[7]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { BackCenter, std::array{ 0.0f, 0.0f, 1.0f} }, - { SideLeft, std::array{-1.0f, 0.0f, 0.0f} }, - { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, - }, X71Map[8]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { BackLeft, std::array{-sin30, 0.0f, cos30} }, - { BackRight, std::array{ sin30, 0.0f, cos30} }, - { SideLeft, std::array{ -1.0f, 0.0f, 0.0f} }, - { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, + static constexpr std::array MonoMap{ + ChanPosMap{FrontCenter, std::array{0.0f, 0.0f, -1.0f}} + }; + static constexpr std::array RearMap{ + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + }; + static constexpr std::array QuadMap{ + ChanPosMap{FrontLeft, std::array{-sin45, 0.0f, -cos45}}, + ChanPosMap{FrontRight, std::array{ sin45, 0.0f, -cos45}}, + ChanPosMap{BackLeft, std::array{-sin45, 0.0f, cos45}}, + ChanPosMap{BackRight, std::array{ sin45, 0.0f, cos45}}, + }; + static constexpr std::array X51Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{SideLeft, std::array{-sin110, 0.0f, -cos110}}, + ChanPosMap{SideRight, std::array{ sin110, 0.0f, -cos110}}, + }; + static constexpr std::array X61Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackCenter, std::array{ 0.0f, 0.0f, 1.0f}}, + ChanPosMap{SideLeft, std::array{-1.0f, 0.0f, 0.0f}}, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f}}, + }; + static constexpr std::array X71Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + ChanPosMap{SideLeft, std::array{ -1.0f, 0.0f, 0.0f}}, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f}}, }; - ChanPosMap StereoMap[2]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + std::array StereoMap{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, }; const auto Frequency = static_cast<float>(Device->Frequency); @@ -834,45 +839,45 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con [](SendParams ¶ms) -> void { params.Gains.Target.fill(0.0f); }); } - DirectMode DirectChannels{props->DirectChannels}; - const ChanPosMap *chans{nullptr}; - switch(voice->mFmtChannels) + const auto getChans = [props,&StereoMap](FmtChannels chanfmt) noexcept + -> std::pair<DirectMode,al::span<const ChanPosMap>> { - case FmtMono: - chans = MonoMap; - /* Mono buffers are never played direct. */ - DirectChannels = DirectMode::Off; - break; - - case FmtStereo: - if(DirectChannels == DirectMode::Off) + switch(chanfmt) { - for(size_t i{0};i < 2;++i) + case FmtMono: + /* Mono buffers are never played direct. */ + return {DirectMode::Off, al::span{MonoMap}}; + + case FmtStereo: + if(props->DirectChannels == DirectMode::Off) { - /* StereoPan is counter-clockwise in radians. */ - const float a{props->StereoPan[i]}; - StereoMap[i].pos[0] = -std::sin(a); - StereoMap[i].pos[2] = -std::cos(a); + for(size_t i{0};i < 2;++i) + { + /* StereoPan is counter-clockwise in radians. */ + const float a{props->StereoPan[i]}; + StereoMap[i].pos[0] = -std::sin(a); + StereoMap[i].pos[2] = -std::cos(a); + } } + return {props->DirectChannels, al::span{StereoMap}}; + + case FmtRear: return {props->DirectChannels, al::span{RearMap}}; + case FmtQuad: return {props->DirectChannels, al::span{QuadMap}}; + case FmtX51: return {props->DirectChannels, al::span{X51Map}}; + case FmtX61: return {props->DirectChannels, al::span{X61Map}}; + case FmtX71: return {props->DirectChannels, al::span{X71Map}}; + + case FmtBFormat2D: + case FmtBFormat3D: + case FmtUHJ2: + case FmtUHJ3: + case FmtUHJ4: + case FmtSuperStereo: + return {DirectMode::Off, {}}; } - chans = StereoMap; - break; - - case FmtRear: chans = RearMap; break; - case FmtQuad: chans = QuadMap; break; - case FmtX51: chans = X51Map; break; - case FmtX61: chans = X61Map; break; - case FmtX71: chans = X71Map; break; - - case FmtBFormat2D: - case FmtBFormat3D: - case FmtUHJ2: - case FmtUHJ3: - case FmtUHJ4: - case FmtSuperStereo: - DirectChannels = DirectMode::Off; - break; - } + return {props->DirectChannels, {}}; + }; + const auto [DirectChannels,chans] = getChans(voice->mFmtChannels); voice->mFlags.reset(VoiceHasHrtf).reset(VoiceHasNfc); if(auto *decoder{voice->mDecoder.get()}) @@ -1065,8 +1070,8 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base; else if(DirectChannels == DirectMode::RemixMismatch) { - auto match_channel = [chans,c](const InputRemixMap &map) noexcept -> bool - { return chans[c].channel == map.channel; }; + auto match_channel = [channel=chans[c].channel](const InputRemixMap &map) noexcept + { return channel == map.channel; }; auto remap = std::find_if(Device->RealOut.RemixMap.cbegin(), Device->RealOut.RemixMap.cend(), match_channel); if(remap != Device->RealOut.RemixMap.cend()) @@ -1176,7 +1181,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * where it can be 0 or full (non-mono sources are always full * spread here). */ - const float spread{Spread * (voice->mFmtChannels == FmtMono)}; + const float spread{Spread * float(voice->mFmtChannels == FmtMono)}; /* Local sources on HRTF play with each channel panned to its * relative location around the listener, providing "virtual @@ -1324,7 +1329,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * where it can be 0 or full (non-mono sources are always full * spread here). */ - const float spread{Spread * (voice->mFmtChannels == FmtMono)}; + const float spread{Spread * float(voice->mFmtChannels == FmtMono)}; for(size_t c{0};c < num_channels;c++) { /* Special-case LFE */ @@ -1396,7 +1401,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBase *context) { DeviceBase *Device{context->mDevice}; - EffectSlot *SendSlots[MAX_SENDS]; + std::array<EffectSlot*,MaxSendCount> SendSlots; voice->mDirect.Buffer = Device->Dry.Buffer; for(uint i{0};i < Device->NumAuxSends;i++) @@ -1426,7 +1431,8 @@ void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const Contex context->mParams.Gain, GainMixMax); DryGain.HF = props->Direct.GainHF; DryGain.LF = props->Direct.GainLF; - GainTriplet WetGain[MAX_SENDS]; + + std::array<GainTriplet,MaxSendCount> WetGain; for(uint i{0};i < Device->NumAuxSends;i++) { WetGain[i].Base = minf(clampf(props->Gain, props->MinGain, props->MaxGain) * @@ -1446,20 +1452,30 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa /* Set mixing buffers and get send parameters. */ voice->mDirect.Buffer = Device->Dry.Buffer; - EffectSlot *SendSlots[MAX_SENDS]; - uint UseDryAttnForRoom{0}; + std::array<EffectSlot*,MaxSendCount> SendSlots{}; + std::array<float,MaxSendCount> RoomRolloff{}; + std::bitset<MaxSendCount> UseDryAttnForRoom{0}; for(uint i{0};i < NumSends;i++) { SendSlots[i] = props->Send[i].Slot; if(!SendSlots[i] || SendSlots[i]->EffectType == EffectSlotType::None) SendSlots[i] = nullptr; - else if(!SendSlots[i]->AuxSendAuto) + else if(SendSlots[i]->AuxSendAuto) { - /* If the slot's auxiliary send auto is off, the data sent to the - * effect slot is the same as the dry path, sans filter effects. + /* NOTE: Contrary to the EFX docs, the effect's room rolloff factor + * applies to the selected distance model along with the source's + * room rolloff factor, not necessarily the inverse distance model. + * + * Generic Software also applies these rolloff factors regardless + * of any setting. It doesn't seem to use the effect slot's send + * auto for anything, though as far as I understand, it's supposed + * to control whether the send gets the same gain/gainhf as the + * direct path (excluding the filter). */ - UseDryAttnForRoom |= 1u<<i; + RoomRolloff[i] = props->RoomRolloffFactor + SendSlots[i]->RoomRolloff; } + else + UseDryAttnForRoom.set(i); if(!SendSlots[i]) voice->mSend[i].Buffer = {}; @@ -1491,62 +1507,77 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa /* Calculate distance attenuation */ float ClampedDist{Distance}; float DryGainBase{props->Gain}; - float WetGainBase{props->Gain}; + std::array<float,MaxSendCount> WetGainBase{}; + WetGainBase.fill(props->Gain); + float DryAttnBase{1.0f}; switch(context->mParams.SourceDistanceModel ? props->mDistanceModel : context->mParams.mDistanceModel) { - case DistanceModel::InverseClamped: - if(props->MaxDistance < props->RefDistance) break; - ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); - /*fall-through*/ - case DistanceModel::Inverse: - if(props->RefDistance > 0.0f) + case DistanceModel::InverseClamped: + if(props->MaxDistance < props->RefDistance) break; + ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); + /*fall-through*/ + case DistanceModel::Inverse: + if(props->RefDistance > 0.0f) + { + float dist{lerpf(props->RefDistance, ClampedDist, props->RolloffFactor)}; + if(dist > 0.0f) { - float dist{lerpf(props->RefDistance, ClampedDist, props->RolloffFactor)}; - if(dist > 0.0f) DryGainBase *= props->RefDistance / dist; - - dist = lerpf(props->RefDistance, ClampedDist, props->RoomRolloffFactor); - if(dist > 0.0f) WetGainBase *= props->RefDistance / dist; + DryAttnBase = props->RefDistance / dist; + DryGainBase *= DryAttnBase; } - break; - - case DistanceModel::LinearClamped: - if(props->MaxDistance < props->RefDistance) break; - ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); - /*fall-through*/ - case DistanceModel::Linear: - if(props->MaxDistance != props->RefDistance) - { - float attn{(ClampedDist-props->RefDistance) / - (props->MaxDistance-props->RefDistance) * props->RolloffFactor}; - DryGainBase *= maxf(1.0f - attn, 0.0f); - attn = (ClampedDist-props->RefDistance) / - (props->MaxDistance-props->RefDistance) * props->RoomRolloffFactor; - WetGainBase *= maxf(1.0f - attn, 0.0f); + for(size_t i{0};i < NumSends;++i) + { + dist = lerpf(props->RefDistance, ClampedDist, RoomRolloff[i]); + if(dist > 0.0f) WetGainBase[i] *= props->RefDistance / dist; } - break; - - case DistanceModel::ExponentClamped: - if(props->MaxDistance < props->RefDistance) break; - ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); - /*fall-through*/ - case DistanceModel::Exponent: - if(ClampedDist > 0.0f && props->RefDistance > 0.0f) + } + break; + + case DistanceModel::LinearClamped: + if(props->MaxDistance < props->RefDistance) break; + ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); + /*fall-through*/ + case DistanceModel::Linear: + if(props->MaxDistance != props->RefDistance) + { + float attn{(ClampedDist-props->RefDistance) / + (props->MaxDistance-props->RefDistance) * props->RolloffFactor}; + DryAttnBase = maxf(1.0f - attn, 0.0f); + DryGainBase *= DryAttnBase; + + for(size_t i{0};i < NumSends;++i) { - const float dist_ratio{ClampedDist/props->RefDistance}; - DryGainBase *= std::pow(dist_ratio, -props->RolloffFactor); - WetGainBase *= std::pow(dist_ratio, -props->RoomRolloffFactor); + attn = (ClampedDist-props->RefDistance) / + (props->MaxDistance-props->RefDistance) * RoomRolloff[i]; + WetGainBase[i] *= maxf(1.0f - attn, 0.0f); } - break; + } + break; + + case DistanceModel::ExponentClamped: + if(props->MaxDistance < props->RefDistance) break; + ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); + /*fall-through*/ + case DistanceModel::Exponent: + if(ClampedDist > 0.0f && props->RefDistance > 0.0f) + { + const float dist_ratio{ClampedDist/props->RefDistance}; + DryAttnBase = std::pow(dist_ratio, -props->RolloffFactor); + DryGainBase *= DryAttnBase; + for(size_t i{0};i < NumSends;++i) + WetGainBase[i] *= std::pow(dist_ratio, -RoomRolloff[i]); + } + break; - case DistanceModel::Disable: - break; + case DistanceModel::Disable: + break; } /* Calculate directional soundcones */ - float ConeHF{1.0f}, WetConeHF{1.0f}; + float ConeHF{1.0f}, WetCone{1.0f}, WetConeHF{1.0f}; if(directional && props->InnerAngle < 360.0f) { static constexpr float Rad2Deg{static_cast<float>(180.0 / al::numbers::pi)}; @@ -1556,39 +1587,43 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa if(Angle >= props->OuterAngle) { ConeGain = props->OuterGain; - ConeHF = lerpf(1.0f, props->OuterGainHF, props->DryGainHFAuto); + if(props->DryGainHFAuto) + ConeHF = props->OuterGainHF; } else if(Angle >= props->InnerAngle) { const float scale{(Angle-props->InnerAngle) / (props->OuterAngle-props->InnerAngle)}; ConeGain = lerpf(1.0f, props->OuterGain, scale); - ConeHF = lerpf(1.0f, props->OuterGainHF, scale * props->DryGainHFAuto); + if(props->DryGainHFAuto) + ConeHF = lerpf(1.0f, props->OuterGainHF, scale); } DryGainBase *= ConeGain; - WetGainBase *= lerpf(1.0f, ConeGain, props->WetGainAuto); - - WetConeHF = lerpf(1.0f, ConeHF, props->WetGainHFAuto); + if(props->WetGainAuto) + WetCone = ConeGain; + if(props->WetGainHFAuto) + WetConeHF = ConeHF; } /* Apply gain and frequency filters */ - DryGainBase = clampf(DryGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain; - WetGainBase = clampf(WetGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain; - GainTriplet DryGain{}; + DryGainBase = clampf(DryGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain; DryGain.Base = minf(DryGainBase * props->Direct.Gain, GainMixMax); DryGain.HF = ConeHF * props->Direct.GainHF; DryGain.LF = props->Direct.GainLF; - GainTriplet WetGain[MAX_SENDS]{}; + + std::array<GainTriplet,MaxSendCount> WetGain{}; for(uint i{0};i < NumSends;i++) { + WetGainBase[i] = clampf(WetGainBase[i]*WetCone, props->MinGain, props->MaxGain) * + context->mParams.Gain; /* If this effect slot's Auxiliary Send Auto is off, then use the dry * path distance and cone attenuation, otherwise use the wet (room) * path distance and cone attenuation. The send filter is used instead * of the direct filter, regardless. */ - const bool use_room{!(UseDryAttnForRoom&(1u<<i))}; - const float gain{use_room ? WetGainBase : DryGainBase}; + const bool use_room{!UseDryAttnForRoom.test(i)}; + const float gain{use_room ? WetGainBase[i] : DryGainBase}; WetGain[i].Base = minf(gain * props->Send[i].Gain, GainMixMax); WetGain[i].HF = (use_room ? WetConeHF : ConeHF) * props->Send[i].GainHF; WetGain[i].LF = props->Send[i].GainLF; @@ -1611,25 +1646,15 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa if(!SendSlots[i] || !(SendSlots[i]->DecayTime > 0.0f)) continue; - auto calc_attenuation = [](float distance, float refdist, float rolloff) noexcept - { - const float dist{lerpf(refdist, distance, rolloff)}; - if(dist > refdist) return refdist / dist; - return 1.0f; - }; - - /* The reverb effect's room rolloff factor always applies to an - * inverse distance rolloff model. - */ - WetGain[i].Base *= calc_attenuation(Distance, props->RefDistance, - SendSlots[i]->RoomRolloff); - if(distance_meters > std::numeric_limits<float>::epsilon()) WetGain[i].HF *= std::pow(SendSlots[i]->AirAbsorptionGainHF, distance_meters); /* If this effect slot's Auxiliary Send Auto is off, don't apply - * the automatic initial reverb decay (should the reverb's room - * rolloff still apply?). + * the automatic initial reverb decay. + * + * NOTE: Generic Software applies the initial decay regardless of + * this setting. It doesn't seem to use it for anything, only the + * source's send filter gain auto flag affects this. */ if(!SendSlots[i]->AuxSendAuto) continue; @@ -1640,7 +1665,7 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa */ DecayDistance.Base = SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec; DecayDistance.LF = DecayDistance.Base * SendSlots[i]->DecayLFRatio; - DecayDistance.HF = DecayDistance.Base * SendSlots[i]->DecayHFRatio; + DecayDistance.HF = SendSlots[i]->DecayHFRatio; if(SendSlots[i]->DecayHFLimit) { const float airAbsorption{SendSlots[i]->AirAbsorptionGainHF}; @@ -1656,14 +1681,18 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa DecayDistance.HF = minf(absorb_dist, DecayDistance.HF); } } - - const float baseAttn = calc_attenuation(Distance, props->RefDistance, - props->RolloffFactor); + DecayDistance.HF *= DecayDistance.Base; /* Apply a decay-time transformation to the wet path, based on the * source distance. The initial decay of the reverb effect is * calculated and applied to the wet path. + * + * FIXME: This is very likely not correct. It more likely should + * work by calculating a rolloff dynamically based on the reverb + * parameters (and source distance?) and add it to the room rolloff + * with the reverb and source rolloff parameters. */ + const float baseAttn{DryAttnBase}; const float fact{distance_base / DecayDistance.Base}; const float gain{std::pow(ReverbDecayGain, fact)*(1.0f-baseAttn) + baseAttn}; WetGain[i].Base *= gain; @@ -1743,7 +1772,7 @@ void CalcSourceParams(Voice *voice, ContextBase *context, bool force) if(props) { - voice->mProps = *props; + voice->mProps = static_cast<VoiceProps&>(*props); AtomicReplaceHead(context->mFreeVoiceProps, props); } @@ -1883,7 +1912,7 @@ void ProcessVoiceChanges(ContextBase *ctx) ctx->mCurrentVoiceChange.store(cur, std::memory_order_release); } -void ProcessParamUpdates(ContextBase *ctx, const EffectSlotArray &slots, +void ProcessParamUpdates(ContextBase *ctx, const al::span<EffectSlot*> slots, const al::span<Voice*> voices) { ProcessVoiceChanges(ctx); @@ -1892,7 +1921,7 @@ void ProcessParamUpdates(ContextBase *ctx, const EffectSlotArray &slots, if(!ctx->mHoldUpdates.load(std::memory_order_acquire)) LIKELY { bool force{CalcContextParams(ctx)}; - auto sorted_slots = const_cast<EffectSlot**>(slots.data() + slots.size()); + auto sorted_slots = al::to_address(slots.end()); for(EffectSlot *slot : slots) force |= CalcEffectSlotParams(slot, sorted_slots, ctx); @@ -1910,12 +1939,13 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo) { ASSUME(SamplesToDo > 0); - const nanoseconds curtime{device->ClockBase + - nanoseconds{seconds{device->SamplesDone}}/device->Frequency}; + const nanoseconds curtime{device->mClockBase.load(std::memory_order_relaxed) + + nanoseconds{seconds{device->mSamplesDone.load(std::memory_order_relaxed)}}/ + device->Frequency}; for(ContextBase *ctx : *device->mContexts.load(std::memory_order_acquire)) { - const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire); + auto auxslots = al::span{*ctx->mActiveAuxSlots.load(std::memory_order_acquire)}; const al::span<Voice*> voices{ctx->getVoicesSpanAcquired()}; /* Process pending property updates for objects on the context. */ @@ -1937,16 +1967,12 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo) } /* Process effects. */ - if(const size_t num_slots{auxslots.size()}) + if(!auxslots.empty()) { - auto slots = auxslots.data(); - auto slots_end = slots + num_slots; - /* Sort the slots into extra storage, so that effect slots come * before their effect slot target (or their targets' target). */ - const al::span<EffectSlot*> sorted_slots{const_cast<EffectSlot**>(slots_end), - num_slots}; + const al::span sorted_slots{al::to_address(auxslots.end()), auxslots.size()}; /* Skip sorting if it has already been done. */ if(!sorted_slots[0]) { @@ -1954,7 +1980,7 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo) * sorted list so that all slots without a target slot go to * the end. */ - std::copy(slots, slots_end, sorted_slots.begin()); + std::copy(auxslots.begin(), auxslots.end(), sorted_slots.begin()); auto split_point = std::partition(sorted_slots.begin(), sorted_slots.end(), [](const EffectSlot *slot) noexcept -> bool { return slot->Target != nullptr; }); @@ -2041,11 +2067,12 @@ void ApplyDistanceComp(const al::span<FloatBufferLine> Samples, const size_t Sam void ApplyDither(const al::span<FloatBufferLine> Samples, uint *dither_seed, const float quant_scale, const size_t SamplesToDo) { + static constexpr double invRNGRange{1.0 / std::numeric_limits<uint>::max()}; ASSUME(SamplesToDo > 0); /* Dithering. Generate whitenoise (uniform distribution of random values * between -1 and +1) and add it to the sample values, after scaling up to - * the desired quantization depth amd before rounding. + * the desired quantization depth and before rounding. */ const float invscale{1.0f / quant_scale}; uint seed{*dither_seed}; @@ -2054,7 +2081,7 @@ void ApplyDither(const al::span<FloatBufferLine> Samples, uint *dither_seed, float val{sample * quant_scale}; uint rng0{dither_rng(&seed)}; uint rng1{dither_rng(&seed)}; - val += static_cast<float>(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX)); + val += static_cast<float>(rng0*invRNGRange - rng1*invRNGRange); return fast_roundf(val) * invscale; }; for(FloatBufferLine &inout : Samples) @@ -2134,22 +2161,22 @@ uint DeviceBase::renderSamples(const uint numSamples) for(FloatBufferLine &buffer : MixBuffer) buffer.fill(0.0f); - /* Increment the mix count at the start (lsb should now be 1). */ - IncrementRef(MixCount); - - /* Process and mix each context's sources and effects. */ - ProcessContexts(this, samplesToDo); + { + const auto mixLock = getWriteMixLock(); - /* Increment the clock time. Every second's worth of samples is converted - * and added to clock base so that large sample counts don't overflow - * during conversion. This also guarantees a stable conversion. - */ - SamplesDone += samplesToDo; - ClockBase += std::chrono::seconds{SamplesDone / Frequency}; - SamplesDone %= Frequency; + /* Process and mix each context's sources and effects. */ + ProcessContexts(this, samplesToDo); - /* Increment the mix count at the end (lsb should now be 0). */ - IncrementRef(MixCount); + /* Every second's worth of samples is converted and added to clock base + * so that large sample counts don't overflow during conversion. This + * also guarantees a stable conversion. + */ + auto samplesDone = mSamplesDone.load(std::memory_order_relaxed) + samplesToDo; + auto clockBase = mClockBase.load(std::memory_order_relaxed) + + std::chrono::seconds{samplesDone/Frequency}; + mSamplesDone.store(samplesDone%Frequency, std::memory_order_relaxed); + mClockBase.store(clockBase, std::memory_order_relaxed); + } /* Apply any needed post-process for finalizing the Dry mix to the RealOut * (Ambisonic decode, UHJ encode, etc). @@ -2225,7 +2252,8 @@ void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const siz void DeviceBase::handleDisconnect(const char *msg, ...) { - IncrementRef(MixCount); + const auto mixLock = getWriteMixLock(); + if(Connected.exchange(false, std::memory_order_acq_rel)) { AsyncEvent evt{std::in_place_type<AsyncDisconnectEvent>}; @@ -2233,10 +2261,10 @@ void DeviceBase::handleDisconnect(const char *msg, ...) va_list args; va_start(args, msg); - int msglen{vsnprintf(disconnect.msg, sizeof(disconnect.msg), msg, args)}; + int msglen{vsnprintf(disconnect.msg.data(), disconnect.msg.size(), msg, args)}; va_end(args); - if(msglen < 0 || static_cast<size_t>(msglen) >= sizeof(disconnect.msg)) + if(msglen < 0 || static_cast<size_t>(msglen) >= disconnect.msg.size()) disconnect.msg[sizeof(disconnect.msg)-1] = 0; for(ContextBase *ctx : *mContexts.load()) @@ -2267,5 +2295,4 @@ void DeviceBase::handleDisconnect(const char *msg, ...) std::for_each(voicelist.begin(), voicelist.end(), stop_voice); } } - IncrementRef(MixCount); } diff --git a/alc/backends/alsa.cpp b/alc/backends/alsa.cpp index 0d9ff30d..4bda5b02 100644 --- a/alc/backends/alsa.cpp +++ b/alc/backends/alsa.cpp @@ -53,6 +53,7 @@ namespace { +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr char alsaDevice[] = "ALSA Default"; @@ -252,10 +253,11 @@ std::vector<DevMap> PlaybackDevices; std::vector<DevMap> CaptureDevices; -const char *prefix_name(snd_pcm_stream_t stream) +const std::string_view prefix_name(snd_pcm_stream_t stream) { - assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE); - return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix"; + if(stream == SND_PCM_STREAM_PLAYBACK) + return "device-prefix"; + return "capture-prefix"; } std::vector<DevMap> probe_devices(snd_pcm_stream_t stream) @@ -267,11 +269,11 @@ std::vector<DevMap> probe_devices(snd_pcm_stream_t stream) snd_pcm_info_t *pcminfo; snd_pcm_info_malloc(&pcminfo); - auto defname = ConfigValueStr(nullptr, "alsa", + auto defname = ConfigValueStr({}, "alsa", (stream == SND_PCM_STREAM_PLAYBACK) ? "device" : "capture"); devlist.emplace_back(alsaDevice, defname ? defname->c_str() : "default"); - if(auto customdevs = ConfigValueStr(nullptr, "alsa", + if(auto customdevs = ConfigValueStr({}, "alsa", (stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices" : "custom-captures")) { size_t nextpos{customdevs->find_first_not_of(';')}; @@ -299,8 +301,8 @@ std::vector<DevMap> probe_devices(snd_pcm_stream_t stream) } } - const std::string main_prefix{ - ConfigValueStr(nullptr, "alsa", prefix_name(stream)).value_or("plughw:")}; + const std::string main_prefix{ConfigValueStr({}, "alsa", prefix_name(stream)) + .value_or("plughw:")}; int card{-1}; int err{snd_card_next(&card)}; @@ -309,12 +311,14 @@ std::vector<DevMap> probe_devices(snd_pcm_stream_t stream) std::string name{"hw:" + std::to_string(card)}; snd_ctl_t *handle; - if((err=snd_ctl_open(&handle, name.c_str(), 0)) < 0) + err = snd_ctl_open(&handle, name.c_str(), 0); + if(err < 0) { ERR("control open (hw:%d): %s\n", card, snd_strerror(err)); continue; } - if((err=snd_ctl_card_info(handle, info)) < 0) + err = snd_ctl_card_info(handle, info); + if(err < 0) { ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err)); snd_ctl_close(handle); @@ -326,8 +330,7 @@ std::vector<DevMap> probe_devices(snd_pcm_stream_t stream) name = prefix_name(stream); name += '-'; name += cardid; - const std::string card_prefix{ - ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(main_prefix)}; + const std::string card_prefix{ConfigValueStr({}, "alsa", name).value_or(main_prefix)}; int dev{-1}; while(true) @@ -339,7 +342,8 @@ std::vector<DevMap> probe_devices(snd_pcm_stream_t stream) snd_pcm_info_set_device(pcminfo, static_cast<uint>(dev)); snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_stream(pcminfo, stream); - if((err=snd_ctl_pcm_info(handle, pcminfo)) < 0) + err = snd_ctl_pcm_info(handle, pcminfo); + if(err < 0) { if(err != -ENOENT) ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err)); @@ -352,8 +356,7 @@ std::vector<DevMap> probe_devices(snd_pcm_stream_t stream) name += cardid; name += '-'; name += std::to_string(dev); - const std::string device_prefix{ - ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(card_prefix)}; + const std::string device_prefix{ConfigValueStr({},"alsa", name).value_or(card_prefix)}; /* "CardName, PcmName (CARD=cardid,DEV=dev)" */ name = cardname; @@ -405,13 +408,14 @@ int verify_state(snd_pcm_t *handle) break; case SND_PCM_STATE_XRUN: - if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0) - return err; + err=snd_pcm_recover(handle, -EPIPE, 1); + if(err < 0) return err; break; case SND_PCM_STATE_SUSPENDED: - if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0) - return err; + err = snd_pcm_recover(handle, -ESTRPIPE, 1); + if(err < 0) return err; break; + case SND_PCM_STATE_DISCONNECTED: return -ENODEV; } @@ -443,8 +447,6 @@ struct AlsaPlayback final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(AlsaPlayback) }; AlsaPlayback::~AlsaPlayback() @@ -644,7 +646,7 @@ void AlsaPlayback::open(std::string_view name) else { name = alsaDevice; - if(auto driveropt = ConfigValueStr(nullptr, "alsa", "device")) + if(auto driveropt = ConfigValueStr({}, "alsa", "device")) driver = std::move(driveropt).value(); } TRACE("Opening device \"%s\"\n", driver.c_str()); @@ -692,7 +694,7 @@ bool AlsaPlayback::reset() break; } - bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", true)}; + bool allowmmap{GetConfigValueBool(mDevice->DeviceName, "alsa", "mmap", true)}; uint periodLen{static_cast<uint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)}; uint bufferLen{static_cast<uint>(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)}; uint rate{mDevice->Frequency}; @@ -700,7 +702,8 @@ bool AlsaPlayback::reset() int err{}; HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ - if((err=(x)) < 0) \ + err = (x); \ + if(err < 0) \ throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ snd_strerror(err)}; \ } while(0) @@ -715,17 +718,18 @@ bool AlsaPlayback::reset() /* test and set format (implicitly sets sample bits) */ if(snd_pcm_hw_params_test_format(mPcmHandle, hp.get(), format) < 0) { - static const struct { + struct FormatMap { snd_pcm_format_t format; DevFmtType fmttype; - } formatlist[] = { - { SND_PCM_FORMAT_FLOAT, DevFmtFloat }, - { SND_PCM_FORMAT_S32, DevFmtInt }, - { SND_PCM_FORMAT_U32, DevFmtUInt }, - { SND_PCM_FORMAT_S16, DevFmtShort }, - { SND_PCM_FORMAT_U16, DevFmtUShort }, - { SND_PCM_FORMAT_S8, DevFmtByte }, - { SND_PCM_FORMAT_U8, DevFmtUByte }, + }; + static constexpr std::array formatlist{ + FormatMap{SND_PCM_FORMAT_FLOAT, DevFmtFloat }, + FormatMap{SND_PCM_FORMAT_S32, DevFmtInt }, + FormatMap{SND_PCM_FORMAT_U32, DevFmtUInt }, + FormatMap{SND_PCM_FORMAT_S16, DevFmtShort }, + FormatMap{SND_PCM_FORMAT_U16, DevFmtUShort}, + FormatMap{SND_PCM_FORMAT_S8, DevFmtByte }, + FormatMap{SND_PCM_FORMAT_U8, DevFmtUByte }, }; for(const auto &fmt : formatlist) @@ -750,7 +754,7 @@ bool AlsaPlayback::reset() else mDevice->FmtChans = DevFmtStereo; } /* set rate (implicitly constrains period/buffer parameters) */ - if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", false) + if(!GetConfigValueBool(mDevice->DeviceName, "alsa", "allow-resampler", false) || !mDevice->Flags.test(FrequencyRequest)) { if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 0) < 0) @@ -760,11 +764,11 @@ bool AlsaPlayback::reset() WARN("Failed to enable ALSA resampler\n"); CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp.get(), &rate, nullptr)); /* set period time (implicitly constrains period/buffer parameters) */ - if((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)) < 0) - ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err)); + err = snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr); + if(err < 0) ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err)); /* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */ - if((err=snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)) < 0) - ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err)); + err = snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr); + if(err < 0) ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err)); /* install and prepare hardware configuration */ CHECK(snd_pcm_hw_params(mPcmHandle, hp.get())); @@ -802,7 +806,8 @@ void AlsaPlayback::start() snd_pcm_access_t access{}; HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ - if((err=(x)) < 0) \ + err = (x); \ + if(err < 0) \ throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ snd_strerror(err)}; \ } while(0) @@ -852,7 +857,7 @@ ClockLatency AlsaPlayback::getClockLatency() ClockLatency ret; std::lock_guard<std::mutex> _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); snd_pcm_sframes_t delay{}; int err{snd_pcm_delay(mPcmHandle, &delay)}; if(err < 0) @@ -886,8 +891,6 @@ struct AlsaCapture final : public BackendBase { RingBufferPtr mRing{nullptr}; snd_pcm_sframes_t mLastAvail{0}; - - DEF_NEWDEL(AlsaCapture) }; AlsaCapture::~AlsaCapture() @@ -916,7 +919,7 @@ void AlsaCapture::open(std::string_view name) else { name = alsaDevice; - if(auto driveropt = ConfigValueStr(nullptr, "alsa", "capture")) + if(auto driveropt = ConfigValueStr({}, "alsa", "capture")) driver = std::move(driveropt).value(); } @@ -961,7 +964,8 @@ void AlsaCapture::open(std::string_view name) bool needring{false}; HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ - if((err=(x)) < 0) \ + err = (x); \ + if(err < 0) \ throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ snd_strerror(err)}; \ } while(0) @@ -1039,7 +1043,7 @@ void AlsaCapture::captureSamples(std::byte *buffer, uint samples) { if(mRing) { - mRing->read(buffer, samples); + std::ignore = mRing->read(buffer, samples); return; } @@ -1068,7 +1072,8 @@ void AlsaCapture::captureSamples(std::byte *buffer, uint samples) if(amt == -EAGAIN) continue; - if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0) + amt = snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1); + if(amt >= 0) { amt = snd_pcm_start(mPcmHandle); if(amt >= 0) @@ -1105,7 +1110,8 @@ uint AlsaCapture::availableSamples() { ERR("avail update failed: %s\n", snd_strerror(static_cast<int>(avail))); - if((avail=snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1)) >= 0) + avail = snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1); + if(avail >= 0) { if(mDoCapture) avail = snd_pcm_start(mPcmHandle); @@ -1141,7 +1147,8 @@ uint AlsaCapture::availableSamples() if(amt == -EAGAIN) continue; - if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0) + amt = snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1); + if(amt >= 0) { if(mDoCapture) amt = snd_pcm_start(mPcmHandle); @@ -1170,7 +1177,7 @@ ClockLatency AlsaCapture::getClockLatency() { ClockLatency ret; - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); snd_pcm_sframes_t delay{}; int err{snd_pcm_delay(mPcmHandle, &delay)}; if(err < 0) @@ -1189,13 +1196,9 @@ ClockLatency AlsaCapture::getClockLatency() bool AlsaBackendFactory::init() { - bool error{false}; - #ifdef HAVE_DYNLOAD if(!alsa_handle) { - std::string missing_funcs; - alsa_handle = LoadLib("libasound.so.2"); if(!alsa_handle) { @@ -1203,27 +1206,25 @@ bool AlsaBackendFactory::init() return false; } - error = false; + std::string missing_funcs; #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \ - if(p##f == nullptr) { \ - error = true; \ - missing_funcs += "\n" #f; \ - } \ + p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \ + if(p##f == nullptr) missing_funcs += "\n" #f; \ } while(0) ALSA_FUNCS(LOAD_FUNC); #undef LOAD_FUNC - if(error) + if(!missing_funcs.empty()) { WARN("Missing expected functions:%s\n", missing_funcs.c_str()); CloseLib(alsa_handle); alsa_handle = nullptr; + return false; } } #endif - return !error; + return true; } bool AlsaBackendFactory::querySupport(BackendType type) diff --git a/alc/backends/base.cpp b/alc/backends/base.cpp index ab3ad028..b287b6d9 100644 --- a/alc/backends/base.cpp +++ b/alc/backends/base.cpp @@ -45,21 +45,20 @@ uint BackendBase::availableSamples() ClockLatency BackendBase::getClockLatency() { - ClockLatency ret; + ClockLatency ret{}; uint refcount; do { refcount = mDevice->waitForMix(); - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != ReadRef(mDevice->MixCount)); + } while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed)); /* NOTE: The device will generally have about all but one periods filled at * any given time during playback. Without a more accurate measurement from * the output, this is an okay approximation. */ - ret.Latency = std::max(std::chrono::seconds{mDevice->BufferSize-mDevice->UpdateSize}, - std::chrono::seconds::zero()); + ret.Latency = std::chrono::seconds{mDevice->BufferSize - mDevice->UpdateSize}; ret.Latency /= mDevice->Frequency; return ret; diff --git a/alc/backends/base.h b/alc/backends/base.h index eea0d238..6726cd9a 100644 --- a/alc/backends/base.h +++ b/alc/backends/base.h @@ -52,18 +52,6 @@ enum class BackendType { }; -/* Helper to get the current clock time from the device's ClockBase, and - * SamplesDone converted from the sample rate. - */ -inline std::chrono::nanoseconds GetDeviceClockTime(DeviceBase *device) -{ - using std::chrono::seconds; - using std::chrono::nanoseconds; - - auto ns = nanoseconds{seconds{device->SamplesDone}} / device->Frequency; - return device->ClockBase + ns; -} - /* Helper to get the device latency from the backend, including any fixed * latency from post-processing. */ @@ -76,6 +64,8 @@ inline ClockLatency GetClockLatency(DeviceBase *device, BackendBase *backend) struct BackendFactory { + virtual ~BackendFactory() = default; + virtual bool init() = 0; virtual bool querySupport(BackendType type) = 0; @@ -86,9 +76,6 @@ struct BackendFactory { virtual std::string probe(BackendType type) = 0; virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0; - -protected: - virtual ~BackendFactory() = default; }; namespace al { @@ -103,15 +90,15 @@ class backend_exception final : public base_exception { backend_error mErrorCode; public: -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] +#ifdef __MINGW32__ + [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]] #else [[gnu::format(printf, 3, 4)]] #endif backend_exception(backend_error code, const char *msg, ...); ~backend_exception() override; - backend_error errorCode() const noexcept { return mErrorCode; } + [[nodiscard]] auto errorCode() const noexcept -> backend_error { return mErrorCode; } }; } // namespace al diff --git a/alc/backends/coreaudio.cpp b/alc/backends/coreaudio.cpp index 16b0781e..86c4b89b 100644 --- a/alc/backends/coreaudio.cpp +++ b/alc/backends/coreaudio.cpp @@ -337,8 +337,6 @@ struct CoreAudioPlayback final : public BackendBase { uint mFrameSize{0u}; AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD - - DEF_NEWDEL(CoreAudioPlayback) }; CoreAudioPlayback::~CoreAudioPlayback() @@ -623,8 +621,6 @@ struct CoreAudioCapture final : public BackendBase { std::vector<char> mCaptureData; RingBufferPtr mRing{nullptr}; - - DEF_NEWDEL(CoreAudioCapture) }; CoreAudioCapture::~CoreAudioCapture() @@ -657,7 +653,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags, return err; } - mRing->write(mCaptureData.data(), inNumberFrames); + std::ignore = mRing->write(mCaptureData.data(), inNumberFrames); return noErr; } @@ -924,7 +920,7 @@ void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples) { if(!mConverter) { - mRing->read(buffer, samples); + std::ignore = mRing->read(buffer, samples); return; } diff --git a/alc/backends/dsound.cpp b/alc/backends/dsound.cpp index 58aa69b2..e51c7ab5 100644 --- a/alc/backends/dsound.cpp +++ b/alc/backends/dsound.cpp @@ -35,11 +35,11 @@ #include <algorithm> #include <atomic> #include <cassert> +#include <cstdio> +#include <cstdlib> #include <functional> #include <memory.h> #include <mutex> -#include <stdlib.h> -#include <stdio.h> #include <string> #include <thread> #include <vector> @@ -191,8 +191,6 @@ struct DSoundPlayback final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(DSoundPlayback) }; DSoundPlayback::~DSoundPlayback() @@ -560,8 +558,6 @@ struct DSoundCapture final : public BackendBase { DWORD mCursor{0u}; RingBufferPtr mRing; - - DEF_NEWDEL(DSoundCapture) }; DSoundCapture::~DSoundCapture() @@ -717,7 +713,7 @@ void DSoundCapture::stop() } void DSoundCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint DSoundCapture::availableSamples() { @@ -740,9 +736,9 @@ uint DSoundCapture::availableSamples() } if(SUCCEEDED(hr)) { - mRing->write(ReadPtr1, ReadCnt1/FrameSize); + std::ignore = mRing->write(ReadPtr1, ReadCnt1/FrameSize); if(ReadPtr2 != nullptr && ReadCnt2 > 0) - mRing->write(ReadPtr2, ReadCnt2/FrameSize); + std::ignore = mRing->write(ReadPtr2, ReadCnt2/FrameSize); hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2); mCursor = ReadCursor; } @@ -778,7 +774,7 @@ bool DSoundBackendFactory::init() } #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \ + p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \ if(!p##f) \ { \ CloseLib(ds_handle); \ diff --git a/alc/backends/jack.cpp b/alc/backends/jack.cpp index a0a5c440..529bb9fe 100644 --- a/alc/backends/jack.cpp +++ b/alc/backends/jack.cpp @@ -102,19 +102,16 @@ decltype(jack_error_callback) * pjack_error_callback; #endif +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr char JackDefaultAudioType[] = JACK_DEFAULT_AUDIO_TYPE; jack_options_t ClientOptions = JackNullOption; bool jack_load() { - bool error{false}; - #ifdef HAVE_DYNLOAD if(!jack_handle) { - std::string missing_funcs; - #ifdef _WIN32 #define JACKLIB "libjack.dll" #else @@ -127,38 +124,36 @@ bool jack_load() return false; } - error = false; + std::string missing_funcs; #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \ - if(p##f == nullptr) { \ - error = true; \ - missing_funcs += "\n" #f; \ - } \ + p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \ + if(p##f == nullptr) missing_funcs += "\n" #f; \ } while(0) JACK_FUNCS(LOAD_FUNC); #undef LOAD_FUNC /* Optional symbols. These don't exist in all versions of JACK. */ -#define LOAD_SYM(f) p##f = al::bit_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)) +#define LOAD_SYM(f) p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)) LOAD_SYM(jack_error_callback); #undef LOAD_SYM - if(error) + if(!missing_funcs.empty()) { WARN("Missing expected functions:%s\n", missing_funcs.c_str()); CloseLib(jack_handle); jack_handle = nullptr; + return false; } } #endif - return !error; + return true; } struct JackDeleter { void operator()(void *ptr) { jack_free(ptr); } }; -using JackPortsPtr = std::unique_ptr<const char*[],JackDeleter>; +using JackPortsPtr = std::unique_ptr<const char*[],JackDeleter>; /* NOLINT(*-avoid-c-arrays) */ struct DeviceEntry { std::string mName; @@ -209,7 +204,7 @@ void EnumerateDevices(jack_client_t *client, std::vector<DeviceEntry> &list) } } - if(auto listopt = ConfigValueStr(nullptr, "jack", "custom-devices")) + if(auto listopt = ConfigValueStr({}, "jack", "custom-devices")) { for(size_t strpos{0};strpos < listopt->size();) { @@ -307,7 +302,7 @@ struct JackPlayback final : public BackendBase { std::string mPortPattern; jack_client_t *mClient{nullptr}; - std::array<jack_port_t*,MAX_OUTPUT_CHANNELS> mPort{}; + std::array<jack_port_t*,MaxOutputChannels> mPort{}; std::mutex mMutex; @@ -318,8 +313,6 @@ struct JackPlayback final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(JackPlayback) }; JackPlayback::~JackPlayback() @@ -339,7 +332,7 @@ JackPlayback::~JackPlayback() int JackPlayback::processRt(jack_nframes_t numframes) noexcept { - std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out; + std::array<jack_default_audio_sample_t*,MaxOutputChannels> out; size_t numchans{0}; for(auto port : mPort) { @@ -363,7 +356,7 @@ int JackPlayback::processRt(jack_nframes_t numframes) noexcept int JackPlayback::process(jack_nframes_t numframes) noexcept { - std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out; + std::array<jack_default_audio_sample_t*,MaxOutputChannels> out; size_t numchans{0}; for(auto port : mPort) { @@ -378,7 +371,7 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept jack_nframes_t todo{minu(numframes, static_cast<uint>(data.first.len))}; auto write_first = [&data,numchans,todo](float *outbuf) -> float* { - const float *RESTRICT in = reinterpret_cast<float*>(data.first.buf); + const auto *RESTRICT in = reinterpret_cast<const float*>(data.first.buf); auto deinterlace_input = [&in,numchans]() noexcept -> float { float ret{*in}; @@ -397,7 +390,7 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept { auto write_second = [&data,numchans,todo](float *outbuf) -> float* { - const float *RESTRICT in = reinterpret_cast<float*>(data.second.buf); + const auto *RESTRICT in = reinterpret_cast<const float*>(data.second.buf); auto deinterlace_input = [&in,numchans]() noexcept -> float { float ret{*in}; @@ -510,7 +503,7 @@ bool JackPlayback::reset() std::for_each(mPort.begin(), mPort.end(), unregister_port); mPort.fill(nullptr); - mRTMixing = GetConfigValueBool(mDevice->DeviceName.c_str(), "jack", "rt-mix", true); + mRTMixing = GetConfigValueBool(mDevice->DeviceName, "jack", "rt-mix", true); jack_set_process_callback(mClient, mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this); @@ -528,7 +521,7 @@ bool JackPlayback::reset() } else { - const char *devname{mDevice->DeviceName.c_str()}; + const std::string_view devname{mDevice->DeviceName}; uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)}; bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize); mDevice->BufferSize = bufsize + mDevice->UpdateSize; @@ -578,7 +571,7 @@ void JackPlayback::start() if(jack_activate(mClient)) throw al::backend_exception{al::backend_error::DeviceError, "Failed to activate client"}; - const char *devname{mDevice->DeviceName.c_str()}; + const std::string_view devname{mDevice->DeviceName}; if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true)) { JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JackDefaultAudioType, @@ -657,7 +650,7 @@ ClockLatency JackPlayback::getClockLatency() ClockLatency ret; std::lock_guard<std::mutex> _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); ret.Latency = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->UpdateSize}; ret.Latency /= mDevice->Frequency; @@ -677,7 +670,7 @@ bool JackBackendFactory::init() if(!jack_load()) return false; - if(!GetConfigValueBool(nullptr, "jack", "spawn-server", false)) + if(!GetConfigValueBool({}, "jack", "spawn-server", false)) ClientOptions = static_cast<jack_options_t>(ClientOptions | JackNoStartServer); const PathNamePair &binname = GetProcBinary(); diff --git a/alc/backends/loopback.cpp b/alc/backends/loopback.cpp index 2972fc01..e42e35b0 100644 --- a/alc/backends/loopback.cpp +++ b/alc/backends/loopback.cpp @@ -34,8 +34,6 @@ struct LoopbackBackend final : public BackendBase { bool reset() override; void start() override; void stop() override; - - DEF_NEWDEL(LoopbackBackend) }; diff --git a/alc/backends/null.cpp b/alc/backends/null.cpp index 3c68e4ce..f28eaa47 100644 --- a/alc/backends/null.cpp +++ b/alc/backends/null.cpp @@ -42,6 +42,7 @@ using std::chrono::seconds; using std::chrono::milliseconds; using std::chrono::nanoseconds; +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr char nullDevice[] = "No Output"; @@ -57,8 +58,6 @@ struct NullBackend final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(NullBackend) }; int NullBackend::mixerProc() diff --git a/alc/backends/oboe.cpp b/alc/backends/oboe.cpp index b7bab19a..d55a7066 100644 --- a/alc/backends/oboe.cpp +++ b/alc/backends/oboe.cpp @@ -4,8 +4,8 @@ #include "oboe.h" #include <cassert> +#include <cstdint> #include <cstring> -#include <stdint.h> #include "alnumeric.h" #include "core/device.h" @@ -17,6 +17,7 @@ namespace { +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr char device_name[] = "Oboe Default"; @@ -48,11 +49,10 @@ oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStrea return oboe::DataCallbackResult::Continue; } -void OboePlayback::onErrorAfterClose(oboe::AudioStream* audioStream, oboe::Result error) +void OboePlayback::onErrorAfterClose(oboe::AudioStream*, oboe::Result error) { - if (error == oboe::Result::ErrorDisconnected) { + if(error == oboe::Result::ErrorDisconnected) mDevice->handleDisconnect("Oboe AudioStream was disconnected: %s", oboe::convertToText(error)); - } TRACE("Error was %s", oboe::convertToText(error)); } @@ -81,6 +81,7 @@ bool OboePlayback::reset() oboe::AudioStreamBuilder builder; builder.setDirection(oboe::Direction::Output); builder.setPerformanceMode(oboe::PerformanceMode::LowLatency); + builder.setUsage(oboe::Usage::Game); /* Don't let Oboe convert. We should be able to handle anything it gives * back. */ @@ -230,7 +231,7 @@ struct OboeCapture final : public BackendBase, public oboe::AudioStreamCallback oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *audioData, int32_t numFrames) { - mRing->write(audioData, static_cast<uint32_t>(numFrames)); + std::ignore = mRing->write(audioData, static_cast<uint32_t>(numFrames)); return oboe::DataCallbackResult::Continue; } @@ -330,7 +331,7 @@ uint OboeCapture::availableSamples() { return static_cast<uint>(mRing->readSpace()); } void OboeCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } } // namespace diff --git a/alc/backends/opensl.cpp b/alc/backends/opensl.cpp index 61e3c9a7..6b2de909 100644 --- a/alc/backends/opensl.cpp +++ b/alc/backends/opensl.cpp @@ -23,10 +23,10 @@ #include "opensl.h" -#include <stdlib.h> #include <jni.h> #include <array> +#include <cstdlib> #include <cstring> #include <mutex> #include <new> @@ -56,6 +56,7 @@ namespace { #define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr char opensl_device[] = "OpenSL"; @@ -189,8 +190,6 @@ struct OpenSLPlayback final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(OpenSLPlayback) }; OpenSLPlayback::~OpenSLPlayback() @@ -375,74 +374,6 @@ bool OpenSLPlayback::reset() mRing = nullptr; -#if 0 - if(!mDevice->Flags.get<FrequencyRequest>()) - { - /* FIXME: Disabled until I figure out how to get the Context needed for - * the getSystemService call. - */ - JNIEnv *env = Android_GetJNIEnv(); - jobject jctx = Android_GetContext(); - - /* Get necessary stuff for using java.lang.Integer, - * android.content.Context, and android.media.AudioManager. - */ - jclass int_cls = JCALL(env,FindClass)("java/lang/Integer"); - jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls, - "parseInt", "(Ljava/lang/String;)I" - ); - TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint); - - jclass ctx_cls = JCALL(env,FindClass)("android/content/Context"); - jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls, - "AUDIO_SERVICE", "Ljava/lang/String;" - ); - jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls, - "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;" - ); - TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n", - ctx_cls, ctx_audsvc, ctx_getSysSvc); - - jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager"); - jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls, - "PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;" - ); - jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls, - "getProperty", "(Ljava/lang/String;)Ljava/lang/String;" - ); - TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n", - audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty); - - const char *strchars; - jstring strobj; - - /* Now make the calls. */ - //AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE); - strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc); - jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj); - strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr); - TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr); - JCALL(env,ReleaseStringUTFChars)(strobj, strchars); - - //String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); - strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate); - jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj); - strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr); - TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr); - JCALL(env,ReleaseStringUTFChars)(strobj, strchars); - - //int sampleRate = Integer.parseInt(srateStr); - sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr); - - strchars = JCALL(env,GetStringUTFChars)(srateStr, nullptr); - TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars); - JCALL(env,ReleaseStringUTFChars)(srateStr, strchars); - - if(!sampleRate) sampleRate = device->Frequency; - else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE); - } -#endif - mDevice->FmtChans = DevFmtStereo; mDevice->FmtType = DevFmtShort; @@ -631,7 +562,7 @@ ClockLatency OpenSLPlayback::getClockLatency() ClockLatency ret; std::lock_guard<std::mutex> _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize}; ret.Latency /= mDevice->Frequency; @@ -662,8 +593,6 @@ struct OpenSLCapture final : public BackendBase { uint mSplOffset{0u}; uint mFrameSize{0}; - - DEF_NEWDEL(OpenSLCapture) }; OpenSLCapture::~OpenSLCapture() diff --git a/alc/backends/oss.cpp b/alc/backends/oss.cpp index 87d3ba35..d541b534 100644 --- a/alc/backends/oss.cpp +++ b/alc/backends/oss.cpp @@ -79,7 +79,9 @@ namespace { +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr char DefaultName[] = "OSS Default"; + std::string DefaultPlayback{"/dev/dsp"}; std::string DefaultCapture{"/dev/dsp"}; @@ -238,8 +240,6 @@ struct OSSPlayback final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(OSSPlayback) }; OSSPlayback::~OSSPlayback() @@ -367,11 +367,9 @@ bool OSSPlayback::reset() uint numFragmentsLogSize{(periods << 16) | log2FragmentSize}; audio_buf_info info{}; - const char *err; -#define CHECKERR(func) if((func) < 0) { \ - err = #func; \ - goto err; \ -} +#define CHECKERR(func) if((func) < 0) \ + throw al::backend_exception{al::backend_error::DeviceError, "%s failed: %s\n", #func, strerror(errno)}; + /* Don't fail if SETFRAGMENT fails. We can handle just about anything * that's reported back via GETOSPACE */ ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize); @@ -379,12 +377,6 @@ bool OSSPlayback::reset() CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels)); CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed)); CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info)); - if(0) - { - err: - ERR("%s failed: %s\n", err, strerror(errno)); - return false; - } #undef CHECKERR if(mDevice->channelsFromFmt() != numChannels) @@ -409,7 +401,7 @@ bool OSSPlayback::reset() setDefaultChannelOrder(); - mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt()); + mMixData.resize(size_t{mDevice->UpdateSize} * mDevice->frameSizeFromFmt()); return true; } @@ -455,8 +447,6 @@ struct OSScapture final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(OSScapture) }; OSScapture::~OSScapture() @@ -617,7 +607,7 @@ void OSScapture::stop() } void OSScapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint OSScapture::availableSamples() { return static_cast<uint>(mRing->readSpace()); } @@ -633,9 +623,9 @@ BackendFactory &OSSBackendFactory::getFactory() bool OSSBackendFactory::init() { - if(auto devopt = ConfigValueStr(nullptr, "oss", "device")) + if(auto devopt = ConfigValueStr({}, "oss", "device")) DefaultPlayback = std::move(*devopt); - if(auto capopt = ConfigValueStr(nullptr, "oss", "capture")) + if(auto capopt = ConfigValueStr({}, "oss", "capture")) DefaultCapture = std::move(*capopt); return true; diff --git a/alc/backends/pipewire.cpp b/alc/backends/pipewire.cpp index 6a001d7a..55bcf6f4 100644 --- a/alc/backends/pipewire.cpp +++ b/alc/backends/pipewire.cpp @@ -28,12 +28,12 @@ #include <cstring> #include <cerrno> #include <chrono> +#include <cstdint> #include <ctime> #include <list> #include <memory> #include <mutex> #include <optional> -#include <stdint.h> #include <thread> #include <type_traits> #include <utility> @@ -76,6 +76,10 @@ _Pragma("GCC diagnostic ignored \"-Weverything\"") #include "spa/pod/builder.h" #include "spa/utils/json.h" +/* NOLINTBEGIN : All kinds of unsafe C stuff here from PipeWire headers + * (function-like macros, C style casts in macros, etc), which we can't do + * anything about except wrap into inline functions. + */ namespace { /* Wrap some nasty macros here too... */ template<typename ...Args> @@ -117,6 +121,7 @@ constexpr auto make_pod_builder(void *data, uint32_t size) noexcept constexpr auto PwIdAny = PW_ID_ANY; } // namespace +/* NOLINTEND */ _Pragma("GCC diagnostic pop") namespace { @@ -169,8 +174,10 @@ using std::chrono::milliseconds; using std::chrono::nanoseconds; using uint = unsigned int; +/* NOLINTBEGIN(*-avoid-c-arrays) */ constexpr char pwireDevice[] = "PipeWire Output"; constexpr char pwireInput[] = "PipeWire Input"; +/* NOLINTEND(*-avoid-c-arrays) */ bool check_version(const char *version) @@ -238,7 +245,7 @@ bool pwire_load() if(pwire_handle) return true; - static constexpr char pwire_library[] = "libpipewire-0.3.so.0"; + const char *pwire_library{"libpipewire-0.3.so.0"}; std::string missing_funcs; pwire_handle = LoadLib(pwire_library); @@ -249,7 +256,7 @@ bool pwire_load() } #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast<decltype(p##f)>(GetSymbol(pwire_handle, #f)); \ + p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pwire_handle, #f)); \ if(p##f == nullptr) missing_funcs += "\n" #f; \ } while(0); PWIRE_FUNCS(LOAD_FUNC) @@ -367,11 +374,11 @@ To as(From) noexcept = delete; * - pw_metadata */ template<> -pw_proxy* as(pw_registry *reg) noexcept { return al::bit_cast<pw_proxy*>(reg); } +pw_proxy* as(pw_registry *reg) noexcept { return reinterpret_cast<pw_proxy*>(reg); } template<> -pw_proxy* as(pw_node *node) noexcept { return al::bit_cast<pw_proxy*>(node); } +pw_proxy* as(pw_node *node) noexcept { return reinterpret_cast<pw_proxy*>(node); } template<> -pw_proxy* as(pw_metadata *mdata) noexcept { return al::bit_cast<pw_proxy*>(mdata); } +pw_proxy* as(pw_metadata *mdata) noexcept { return reinterpret_cast<pw_proxy*>(mdata); } struct PwContextDeleter { @@ -434,9 +441,11 @@ public: explicit operator bool() const noexcept { return mLoop != nullptr; } + [[nodiscard]] auto start() const { return pw_thread_loop_start(mLoop); } auto stop() const { return pw_thread_loop_stop(mLoop); } + [[nodiscard]] auto getLoop() const { return pw_thread_loop_get_loop(mLoop); } auto lock() const { return pw_thread_loop_lock(mLoop); } @@ -501,8 +510,8 @@ struct NodeProxy { /* Track changes to the enumerable and current formats (indicates the * default and active format, which is what we're interested in). */ - uint32_t fmtids[]{SPA_PARAM_EnumFormat, SPA_PARAM_Format}; - ppw_node_subscribe_params(mNode.get(), std::data(fmtids), std::size(fmtids)); + std::array<uint32_t,2> fmtids{{SPA_PARAM_EnumFormat, SPA_PARAM_Format}}; + ppw_node_subscribe_params(mNode.get(), fmtids.data(), fmtids.size()); } ~NodeProxy() { spa_hook_remove(&mListener); } @@ -765,25 +774,32 @@ void DeviceNode::Remove(uint32_t id) } -const spa_audio_channel MonoMap[]{ +constexpr std::array MonoMap{ SPA_AUDIO_CHANNEL_MONO -}, StereoMap[] { +}; +constexpr std::array StereoMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR -}, QuadMap[]{ +}; +constexpr std::array QuadMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR -}, X51Map[]{ +}; +constexpr std::array X51Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X51RearMap[]{ +}; +constexpr std::array X51RearMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR -}, X61Map[]{ +}; +constexpr std::array X61Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RC, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X71Map[]{ +}; +constexpr std::array X71Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X714Map[]{ +}; +constexpr std::array X714Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, SPA_AUDIO_CHANNEL_TFL, SPA_AUDIO_CHANNEL_TFR, SPA_AUDIO_CHANNEL_TRL, SPA_AUDIO_CHANNEL_TRR @@ -793,10 +809,10 @@ const spa_audio_channel MonoMap[]{ * Checks if every channel in 'map1' exists in 'map0' (that is, map0 is equal * to or a superset of map1). */ -template<size_t N> -bool MatchChannelMap(const al::span<const uint32_t> map0, const spa_audio_channel (&map1)[N]) +bool MatchChannelMap(const al::span<const uint32_t> map0, + const al::span<const spa_audio_channel> map1) { - if(map0.size() < N) + if(map0.size() < map1.size()) return false; for(const spa_audio_channel chid : map1) { @@ -831,7 +847,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce /* [0] is the default, [1] is the min, and [2] is the max. */ TRACE(" sample rate: %d (range: %d -> %d)\n", srates[0], srates[1], srates[2]); if(!mSampleRate || force_update) - mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); + mSampleRate = static_cast<uint>(clampi(srates[0], MinOutputRate, MaxOutputRate)); return; } @@ -857,7 +873,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce */ for(const auto &rate : srates) { - if(rate >= MIN_OUTPUT_RATE && rate <= MAX_OUTPUT_RATE) + if(rate >= int{MinOutputRate} && rate <= int{MaxOutputRate}) { if(!mSampleRate || force_update) mSampleRate = static_cast<uint>(rate); @@ -878,7 +894,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce TRACE(" sample rate: %d\n", srates[0]); if(!mSampleRate || force_update) - mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); + mSampleRate = static_cast<uint>(clampi(srates[0], MinOutputRate, MaxOutputRate)); return; } @@ -956,6 +972,7 @@ void DeviceNode::parseChannelCount(const spa_pod *value, bool force_update) noex } +/* NOLINTBEGIN(*-avoid-c-arrays) */ constexpr char MonitorPrefix[]{"Monitor of "}; constexpr auto MonitorPrefixLen = std::size(MonitorPrefix) - 1; constexpr char AudioSinkClass[]{"Audio/Sink"}; @@ -963,6 +980,7 @@ constexpr char AudioSourceClass[]{"Audio/Source"}; constexpr char AudioSourceVirtualClass[]{"Audio/Source/Virtual"}; constexpr char AudioDuplexClass[]{"Audio/Duplex"}; constexpr char StreamClass[]{"Stream/"}; +/* NOLINTEND(*-avoid-c-arrays) */ void NodeProxy::infoCallback(const pw_node_info *info) noexcept { @@ -1070,8 +1088,11 @@ void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_po if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_position)}) node->parsePositions(&prop->value, force_update); - else if((prop=spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels)) != nullptr) - node->parseChannelCount(&prop->value, force_update); + else + { + prop = spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels); + if(prop) node->parseChannelCount(&prop->value, force_update); + } } } @@ -1103,8 +1124,8 @@ int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *ty return 0; } - spa_json it[2]{}; - spa_json_init(&it[0], value, strlen(value)); + std::array<spa_json,2> it{}; + spa_json_init(it.data(), value, strlen(value)); if(spa_json_enter_object(&it[0], &it[1]) <= 0) return 0; @@ -1407,8 +1428,7 @@ class PipeWirePlayback final : public BackendBase { PwStreamPtr mStream; spa_hook mStreamListener{}; spa_io_rate_match *mRateMatch{}; - std::unique_ptr<float*[]> mChannelPtrs; - uint mNumChannels{}; + std::vector<float*> mChannelPtrs; static constexpr pw_stream_events CreateEvents() { @@ -1425,13 +1445,11 @@ class PipeWirePlayback final : public BackendBase { public: PipeWirePlayback(DeviceBase *device) noexcept : BackendBase{device} { } - ~PipeWirePlayback() + ~PipeWirePlayback() final { /* Stop the mainloop so the stream can be properly destroyed. */ if(mLoop) mLoop.stop(); } - - DEF_NEWDEL(PipeWirePlayback) }; @@ -1455,7 +1473,7 @@ void PipeWirePlayback::outputCallback() noexcept if(!pw_buf) UNLIKELY return; const al::span<spa_data> datas{pw_buf->buffer->datas, - minu(mNumChannels, pw_buf->buffer->n_datas)}; + minz(mChannelPtrs.size(), pw_buf->buffer->n_datas)}; #if PW_CHECK_VERSION(0,3,49) /* In 0.3.49, pw_buffer::requested specifies the number of samples needed * by the resampler/graph for this audio update. @@ -1475,7 +1493,7 @@ void PipeWirePlayback::outputCallback() noexcept * buffer length in any one channel is smaller than we wanted (shouldn't * be, but just in case). */ - float **chanptr_end{mChannelPtrs.get()}; + auto chanptr_end = mChannelPtrs.begin(); for(const auto &data : datas) { length = minu(length, data.maxsize/sizeof(float)); @@ -1487,7 +1505,7 @@ void PipeWirePlayback::outputCallback() noexcept data.chunk->size = length * sizeof(float); } - mDevice->renderSamples({mChannelPtrs.get(), chanptr_end}, length); + mDevice->renderSamples(mChannelPtrs, length); pw_buf->size = length; pw_stream_queue_buffer(mStream.get(), pw_buf); @@ -1590,7 +1608,7 @@ bool PipeWirePlayback::reset() } mStreamListener = {}; mRateMatch = nullptr; - mTimeBase = GetDeviceClockTime(mDevice); + mTimeBase = mDevice->getClockTime(); /* If connecting to a specific device, update various device parameters to * match its format. @@ -1611,11 +1629,12 @@ bool PipeWirePlayback::reset() { /* Scale the update size if the sample rate changes. */ const double scale{static_cast<double>(match->mSampleRate) / mDevice->Frequency}; - const double numbufs{static_cast<double>(mDevice->BufferSize)/mDevice->UpdateSize}; + const double updatesize{std::round(mDevice->UpdateSize * scale)}; + const double buffersize{std::round(mDevice->BufferSize * scale)}; + mDevice->Frequency = match->mSampleRate; - mDevice->UpdateSize = static_cast<uint>(clampd(mDevice->UpdateSize*scale + 0.5, - 64.0, 8192.0)); - mDevice->BufferSize = static_cast<uint>(numbufs*mDevice->UpdateSize + 0.5); + mDevice->UpdateSize = static_cast<uint>(clampd(updatesize, 64.0, 8192.0)); + mDevice->BufferSize = static_cast<uint>(maxd(buffersize, 128.0)); } if(!mDevice->Flags.test(ChannelsRequest) && match->mChannels != InvalidChannelConfig) mDevice->FmtChans = match->mChannels; @@ -1673,7 +1692,7 @@ bool PipeWirePlayback::reset() pw_stream_flags flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE | PW_STREAM_FLAG_MAP_BUFFERS}; - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pipewire", "rt-mix", true)) + if(GetConfigValueBool(mDevice->DeviceName, "pipewire", "rt-mix", false)) flags |= PW_STREAM_FLAG_RT_PROCESS; if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_OUTPUT, PwIdAny, flags, ¶ms, 1)}) throw al::backend_exception{al::backend_error::DeviceError, @@ -1698,8 +1717,7 @@ bool PipeWirePlayback::reset() */ plock.unlock(); - mNumChannels = mDevice->channelsFromFmt(); - mChannelPtrs = std::make_unique<float*[]>(mNumChannels); + mChannelPtrs.resize(mDevice->channelsFromFmt()); setDefaultWFXChannelOrder(); @@ -1757,7 +1775,7 @@ void PipeWirePlayback::start() mDevice->UpdateSize = updatesize; mDevice->BufferSize = static_cast<uint>(ptime.buffered + delay + - totalbuffers*updatesize); + uint64_t{totalbuffers}*updatesize); break; } #else @@ -1791,8 +1809,7 @@ void PipeWirePlayback::stop() { MainloopUniqueLock plock{mLoop}; if(int res{pw_stream_set_active(mStream.get(), false)}) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to stop PipeWire stream (res: %d)", res}; + ERR("Failed to stop PipeWire stream (res: %d)\n", res); /* Wait for the stream to stop playing. */ plock.wait([stream=mStream.get()]() @@ -1824,10 +1841,10 @@ ClockLatency PipeWirePlayback::getClockLatency() uint refcount; do { refcount = mDevice->waitForMix(); - mixtime = GetDeviceClockTime(mDevice); + mixtime = mDevice->getClockTime(); clock_gettime(CLOCK_MONOTONIC, &tspec); std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != ReadRef(mDevice->MixCount)); + } while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed)); /* Convert the monotonic clock, stream ticks, and stream delay to * nanoseconds. @@ -1916,9 +1933,7 @@ class PipeWireCapture final : public BackendBase { public: PipeWireCapture(DeviceBase *device) noexcept : BackendBase{device} { } - ~PipeWireCapture() { if(mLoop) mLoop.stop(); } - - DEF_NEWDEL(PipeWireCapture) + ~PipeWireCapture() final { if(mLoop) mLoop.stop(); } }; @@ -1934,7 +1949,8 @@ void PipeWireCapture::inputCallback() noexcept const uint offset{minu(bufdata->chunk->offset, bufdata->maxsize)}; const uint size{minu(bufdata->chunk->size, bufdata->maxsize - offset)}; - mRing->write(static_cast<char*>(bufdata->data) + offset, size / mRing->getElemSize()); + std::ignore = mRing->write(static_cast<char*>(bufdata->data) + offset, + size / mRing->getElemSize()); pw_stream_queue_buffer(mStream.get(), pw_buf); } @@ -2057,7 +2073,8 @@ void PipeWireCapture::open(std::string_view name) static constexpr uint32_t pod_buffer_size{1024}; PodDynamicBuilder b(pod_buffer_size); - const spa_pod *params[]{spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info)}; + std::array params{static_cast<const spa_pod*>(spa_format_audio_raw_build(b.get(), + SPA_PARAM_EnumFormat, &info))}; if(!params[0]) throw al::backend_exception{al::backend_error::DeviceError, "Failed to set PipeWire audio format parameters"}; @@ -2099,7 +2116,7 @@ void PipeWireCapture::open(std::string_view name) constexpr pw_stream_flags Flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS}; - if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, params, 1)}) + if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, params.data(), 1)}) throw al::backend_exception{al::backend_error::DeviceError, "Error connecting PipeWire stream (res: %d)", res}; @@ -2145,8 +2162,7 @@ void PipeWireCapture::stop() { MainloopUniqueLock plock{mLoop}; if(int res{pw_stream_set_active(mStream.get(), false)}) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to stop PipeWire stream (res: %d)", res}; + ERR("Failed to stop PipeWire stream (res: %d)\n", res); plock.wait([stream=mStream.get()]() { return pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING; }); @@ -2156,7 +2172,7 @@ uint PipeWireCapture::availableSamples() { return static_cast<uint>(mRing->readSpace()); } void PipeWireCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } } // namespace @@ -2175,11 +2191,11 @@ bool PipeWireBackendFactory::init() } TRACE("Found PipeWire version \"%s\" (%s or newer)\n", version, pw_get_headers_version()); - pw_init(0, nullptr); + pw_init(nullptr, nullptr); if(!gEventHandler.init()) return false; - if(!GetConfigValueBool(nullptr, "pipewire", "assume-audio", false) + if(!GetConfigValueBool({}, "pipewire", "assume-audio", false) && !gEventHandler.waitForAudio()) { gEventHandler.kill(); diff --git a/alc/backends/portaudio.cpp b/alc/backends/portaudio.cpp index 979a54d6..7c61e134 100644 --- a/alc/backends/portaudio.cpp +++ b/alc/backends/portaudio.cpp @@ -39,7 +39,8 @@ namespace { -constexpr char pa_device[] = "PortAudio Default"; +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ +constexpr char pa_device[]{"PortAudio Default"}; #ifdef HAVE_DYNLOAD @@ -78,13 +79,6 @@ struct PortPlayback final : public BackendBase { int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept; - static int writeCallbackC(const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, - const PaStreamCallbackFlags statusFlags, void *userData) noexcept - { - return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer, - framesPerBuffer, timeInfo, statusFlags); - } void open(std::string_view name) override; bool reset() override; @@ -94,8 +88,6 @@ struct PortPlayback final : public BackendBase { PaStream *mStream{nullptr}; PaStreamParameters mParams{}; uint mUpdateSize{0u}; - - DEF_NEWDEL(PortPlayback) }; PortPlayback::~PortPlayback() @@ -125,7 +117,7 @@ void PortPlayback::open(std::string_view name) static_cast<int>(name.length()), name.data()}; PaStreamParameters params{}; - auto devidopt = ConfigValueInt(nullptr, "port", "device"); + auto devidopt = ConfigValueInt({}, "port", "device"); if(devidopt && *devidopt >= 0) params.device = *devidopt; else params.device = Pa_GetDefaultOutputDevice(); params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency); @@ -156,19 +148,21 @@ void PortPlayback::open(std::string_view name) break; } -retry_open: + static constexpr auto writeCallback = [](const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags statusFlags, void *userData) noexcept + { + return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer, + framesPerBuffer, timeInfo, statusFlags); + }; PaStream *stream{}; - PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency, mDevice->UpdateSize, - paNoFlag, &PortPlayback::writeCallbackC, this)}; - if(err != paNoError) + while(PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency, + mDevice->UpdateSize, paNoFlag, writeCallback, this)}) { - if(params.sampleFormat == paFloat32) - { - params.sampleFormat = paInt16; - goto retry_open; - } - throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s", - Pa_GetErrorText(err)}; + if(params.sampleFormat != paFloat32) + throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s", + Pa_GetErrorText(err)}; + params.sampleFormat = paInt16; } Pa_CloseStream(mStream); @@ -237,13 +231,6 @@ struct PortCapture final : public BackendBase { int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept; - static int readCallbackC(const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, - const PaStreamCallbackFlags statusFlags, void *userData) noexcept - { - return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer, - framesPerBuffer, timeInfo, statusFlags); - } void open(std::string_view name) override; void start() override; @@ -252,11 +239,9 @@ struct PortCapture final : public BackendBase { uint availableSamples() override; PaStream *mStream{nullptr}; - PaStreamParameters mParams; + PaStreamParameters mParams{}; RingBufferPtr mRing{nullptr}; - - DEF_NEWDEL(PortCapture) }; PortCapture::~PortCapture() @@ -271,7 +256,7 @@ PortCapture::~PortCapture() int PortCapture::readCallback(const void *inputBuffer, void*, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo*, const PaStreamCallbackFlags) noexcept { - mRing->write(inputBuffer, framesPerBuffer); + std::ignore = mRing->write(inputBuffer, framesPerBuffer); return 0; } @@ -290,7 +275,7 @@ void PortCapture::open(std::string_view name) mRing = RingBuffer::Create(samples, frame_size, false); - auto devidopt = ConfigValueInt(nullptr, "port", "capture"); + auto devidopt = ConfigValueInt({}, "port", "capture"); if(devidopt && *devidopt >= 0) mParams.device = *devidopt; else mParams.device = Pa_GetDefaultOutputDevice(); mParams.suggestedLatency = 0.0f; @@ -320,8 +305,15 @@ void PortCapture::open(std::string_view name) } mParams.channelCount = static_cast<int>(mDevice->channelsFromFmt()); + static constexpr auto readCallback = [](const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags statusFlags, void *userData) noexcept + { + return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer, + framesPerBuffer, timeInfo, statusFlags); + }; PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency, - paFramesPerBufferUnspecified, paNoFlag, &PortCapture::readCallbackC, this)}; + paFramesPerBufferUnspecified, paNoFlag, readCallback, this)}; if(err != paNoError) throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s", Pa_GetErrorText(err)}; @@ -350,15 +342,13 @@ uint PortCapture::availableSamples() { return static_cast<uint>(mRing->readSpace()); } void PortCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } } // namespace bool PortBackendFactory::init() { - PaError err; - #ifdef HAVE_DYNLOAD if(!pa_handle) { @@ -377,7 +367,7 @@ bool PortBackendFactory::init() return false; #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast<decltype(p##f)>(GetSymbol(pa_handle, #f)); \ + p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pa_handle, #f)); \ if(p##f == nullptr) \ { \ CloseLib(pa_handle); \ @@ -397,7 +387,8 @@ bool PortBackendFactory::init() LOAD_FUNC(Pa_GetStreamInfo); #undef LOAD_FUNC - if((err=Pa_Initialize()) != paNoError) + const PaError err{Pa_Initialize()}; + if(err != paNoError) { ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err)); CloseLib(pa_handle); @@ -406,7 +397,8 @@ bool PortBackendFactory::init() } } #else - if((err=Pa_Initialize()) != paNoError) + const PaError err{Pa_Initialize()}; + if(err != paNoError) { ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err)); return false; diff --git a/alc/backends/pulseaudio.cpp b/alc/backends/pulseaudio.cpp index bebc182d..e976fc27 100644 --- a/alc/backends/pulseaudio.cpp +++ b/alc/backends/pulseaudio.cpp @@ -28,12 +28,12 @@ #include <atomic> #include <bitset> #include <chrono> +#include <cstdint> +#include <cstdlib> #include <cstring> #include <limits> #include <mutex> #include <optional> -#include <stdint.h> -#include <stdlib.h> #include <string> #include <sys/types.h> #include <utility> @@ -320,11 +320,12 @@ public: explicit operator bool() const noexcept { return mLoop != nullptr; } + [[nodiscard]] auto start() const { return pa_threaded_mainloop_start(mLoop); } auto stop() const { return pa_threaded_mainloop_stop(mLoop); } - auto getApi() const { return pa_threaded_mainloop_get_api(mLoop); } - auto getContext() const noexcept { return mContext; } + [[nodiscard]] auto getApi() const { return pa_threaded_mainloop_get_api(mLoop); } + [[nodiscard]] auto getContext() const noexcept { return mContext; } auto lock() const { return pa_threaded_mainloop_lock(mLoop); } auto unlock() const { return pa_threaded_mainloop_unlock(mLoop); } @@ -509,8 +510,8 @@ void MainloopUniqueLock::connectContext() pa_context_set_state_callback(mutex()->mContext, [](pa_context *ctx, void *pdata) noexcept { return static_cast<MainloopUniqueLock*>(pdata)->contextStateCallback(ctx); }, this); - int err; - if((err=pa_context_connect(mutex()->mContext, nullptr, pulse_ctx_flags, nullptr)) >= 0) + int err{pa_context_connect(mutex()->mContext, nullptr, pulse_ctx_flags, nullptr)}; + if(err >= 0) { pa_context_state_t state; while((state=pa_context_get_state(mutex()->mContext)) != PA_CONTEXT_READY) @@ -657,14 +658,12 @@ struct PulsePlayback final : public BackendBase { std::optional<std::string> mDeviceName{std::nullopt}; bool mIs51Rear{false}; - pa_buffer_attr mAttr; - pa_sample_spec mSpec; + pa_buffer_attr mAttr{}; + pa_sample_spec mSpec{}; pa_stream *mStream{nullptr}; uint mFrameSize{0u}; - - DEF_NEWDEL(PulsePlayback) }; PulsePlayback::~PulsePlayback() @@ -753,9 +752,9 @@ void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int else { mIs51Rear = false; - char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX]{}; - pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map); - WARN("Failed to find format for channel map:\n %s\n", chanmap_str); + std::array<char,PA_CHANNEL_MAP_SNPRINT_MAX> chanmap_str{}; + pa_channel_map_snprint(chanmap_str.data(), chanmap_str.size(), &info->channel_map); + WARN("Failed to find format for channel map:\n %s\n", chanmap_str.data()); } if(info->active_port) @@ -784,7 +783,9 @@ void PulsePlayback::streamMovedCallback(pa_stream *stream) noexcept void PulsePlayback::open(std::string_view name) { mMainloop = PulseMainloop::Create(); - mMainloop.start(); + if(mMainloop.start() != 0) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to start device mainloop"}; const char *pulse_name{nullptr}; const char *dev_name{nullptr}; @@ -807,7 +808,7 @@ void PulsePlayback::open(std::string_view name) pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS}; - if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true)) + if(!GetConfigValueBool({}, "pulse", "allow-moves", true)) flags |= PA_STREAM_DONT_MOVE; pa_sample_spec spec{}; @@ -866,9 +867,9 @@ bool PulsePlayback::reset() pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS}; - if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true)) + if(!GetConfigValueBool({}, "pulse", "allow-moves", true)) flags |= PA_STREAM_DONT_MOVE; - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "adjust-latency", false)) + if(GetConfigValueBool(mDevice->DeviceName, "pulse", "adjust-latency", false)) { /* ADJUST_LATENCY can't be specified with EARLY_REQUESTS, for some * reason. So if the user wants to adjust the overall device latency, @@ -877,7 +878,7 @@ bool PulsePlayback::reset() flags &= ~PA_STREAM_EARLY_REQUESTS; flags |= PA_STREAM_ADJUST_LATENCY; } - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "fix-rate", false) + if(GetConfigValueBool(mDevice->DeviceName, "pulse", "fix-rate", false) || !mDevice->Flags.test(FrequencyRequest)) flags |= PA_STREAM_FIX_RATE; @@ -967,8 +968,9 @@ bool PulsePlayback::reset() const auto scale = static_cast<double>(mSpec.rate) / mDevice->Frequency; const auto perlen = static_cast<uint>(clampd(scale*mDevice->UpdateSize + 0.5, 64.0, 8192.0)); - const auto buflen = static_cast<uint>(clampd(scale*mDevice->BufferSize + 0.5, perlen*2, - std::numeric_limits<int>::max()/mFrameSize)); + const auto bufmax = uint{std::numeric_limits<int>::max() / mFrameSize}; + const auto buflen = static_cast<uint>(clampd(scale*mDevice->BufferSize + 0.5, perlen*2.0, + bufmax)); mAttr.maxlength = ~0u; mAttr.tlength = buflen * mFrameSize; @@ -1034,7 +1036,7 @@ ClockLatency PulsePlayback::getClockLatency() { MainloopUniqueLock plock{mMainloop}; - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); err = pa_stream_get_latency(mStream, &latency, &neg); } @@ -1087,8 +1089,6 @@ struct PulseCapture final : public BackendBase { pa_sample_spec mSpec{}; pa_stream *mStream{nullptr}; - - DEF_NEWDEL(PulseCapture) }; PulseCapture::~PulseCapture() @@ -1127,7 +1127,9 @@ void PulseCapture::open(std::string_view name) if(!mMainloop) { mMainloop = PulseMainloop::Create(); - mMainloop.start(); + if(mMainloop.start() != 0) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to start device mainloop"}; } const char *pulse_name{nullptr}; @@ -1214,7 +1216,7 @@ void PulseCapture::open(std::string_view name) mAttr.fragsize = minu(samples, 50*mDevice->Frequency/1000) * frame_size; pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY}; - if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true)) + if(!GetConfigValueBool({}, "pulse", "allow-moves", true)) flags |= PA_STREAM_DONT_MOVE; TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); @@ -1362,7 +1364,7 @@ ClockLatency PulseCapture::getClockLatency() { MainloopUniqueLock plock{mMainloop}; - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); err = pa_stream_get_latency(mStream, &latency, &neg); } @@ -1387,9 +1389,6 @@ bool PulseBackendFactory::init() #ifdef HAVE_DYNLOAD if(!pulse_handle) { - bool ret{true}; - std::string missing_funcs; - #ifdef _WIN32 #define PALIB "libpulse-0.dll" #elif defined(__APPLE__) && defined(__MACH__) @@ -1404,17 +1403,15 @@ bool PulseBackendFactory::init() return false; } + std::string missing_funcs; #define LOAD_FUNC(x) do { \ - p##x = al::bit_cast<decltype(p##x)>(GetSymbol(pulse_handle, #x)); \ - if(!(p##x)) { \ - ret = false; \ - missing_funcs += "\n" #x; \ - } \ + p##x = reinterpret_cast<decltype(p##x)>(GetSymbol(pulse_handle, #x)); \ + if(!(p##x)) missing_funcs += "\n" #x; \ } while(0) PULSE_FUNCS(LOAD_FUNC) #undef LOAD_FUNC - if(!ret) + if(!missing_funcs.empty()) { WARN("Missing expected functions:%s\n", missing_funcs.c_str()); CloseLib(pulse_handle); @@ -1425,14 +1422,18 @@ bool PulseBackendFactory::init() #endif /* HAVE_DYNLOAD */ pulse_ctx_flags = PA_CONTEXT_NOFLAGS; - if(!GetConfigValueBool(nullptr, "pulse", "spawn-server", false)) + if(!GetConfigValueBool({}, "pulse", "spawn-server", false)) pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN; try { if(!gGlobalMainloop) { gGlobalMainloop = PulseMainloop::Create(); - gGlobalMainloop.start(); + if(gGlobalMainloop.start() != 0) + { + gGlobalMainloop = nullptr; + return false; + } } MainloopUniqueLock plock{gGlobalMainloop}; diff --git a/alc/backends/sdl2.cpp b/alc/backends/sdl2.cpp index f5ed4316..b69f17fd 100644 --- a/alc/backends/sdl2.cpp +++ b/alc/backends/sdl2.cpp @@ -26,6 +26,7 @@ #include <cstdlib> #include <cstring> #include <string> +#include <string_view> #include "almalloc.h" #include "alnumeric.h" @@ -46,7 +47,9 @@ namespace { #define DEVNAME_PREFIX "" #endif -constexpr char defaultDeviceName[] = DEVNAME_PREFIX "Default Device"; +constexpr auto getDevicePrefix() noexcept -> std::string_view { return DEVNAME_PREFIX; } +constexpr auto getDefaultDeviceName() noexcept -> std::string_view +{ return DEVNAME_PREFIX "Default Device"; } struct Sdl2Backend final : public BackendBase { Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { } @@ -66,8 +69,6 @@ struct Sdl2Backend final : public BackendBase { DevFmtChannels mFmtChans{}; DevFmtType mFmtType{}; uint mUpdateSize{0u}; - - DEF_NEWDEL(Sdl2Backend) }; Sdl2Backend::~Sdl2Backend() @@ -108,6 +109,7 @@ void Sdl2Backend::open(std::string_view name) /* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't * necessarily the first in the list. */ + const auto defaultDeviceName = getDefaultDeviceName(); SDL_AudioDeviceID devid; if(name.empty() || name == defaultDeviceName) { @@ -116,13 +118,13 @@ void Sdl2Backend::open(std::string_view name) } else { - const size_t prefix_len = strlen(DEVNAME_PREFIX); - if(name.length() >= prefix_len && strncmp(name.data(), DEVNAME_PREFIX, prefix_len) == 0) + const auto namePrefix = getDevicePrefix(); + if(name.size() >= namePrefix.size() && name.substr(0, namePrefix.size()) == namePrefix) { /* Copy the string_view to a string to ensure it's null terminated * for this call. */ - const std::string devname{name.substr(prefix_len)}; + const std::string devname{name.substr(namePrefix.size())}; devid = SDL_OpenAudioDevice(devname.c_str(), SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE); } @@ -217,13 +219,16 @@ std::string SDL2BackendFactory::probe(BackendType type) int num_devices{SDL_GetNumAudioDevices(SDL_FALSE)}; /* Includes null char. */ - outnames.append(defaultDeviceName, sizeof(defaultDeviceName)); + outnames += getDefaultDeviceName(); + outnames += '\0'; for(int i{0};i < num_devices;++i) { - std::string name{DEVNAME_PREFIX}; - name += SDL_GetAudioDeviceName(i, SDL_FALSE); - if(!name.empty()) - outnames.append(name.c_str(), name.length()+1); + outnames += getDevicePrefix(); + if(const char *name = SDL_GetAudioDeviceName(i, SDL_FALSE)) + outnames += name; + else + outnames += "Unknown Device Name #"+std::to_string(i); + outnames += '\0'; } return outnames; } diff --git a/alc/backends/sndio.cpp b/alc/backends/sndio.cpp index d54c337b..ce3de366 100644 --- a/alc/backends/sndio.cpp +++ b/alc/backends/sndio.cpp @@ -23,11 +23,11 @@ #include "sndio.h" #include <cinttypes> +#include <cstdio> +#include <cstdlib> +#include <cstring> #include <functional> #include <poll.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> #include <thread> #include <vector> @@ -43,10 +43,11 @@ namespace { -static const char sndio_device[] = "SndIO Default"; +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ +constexpr char sndio_device[] = "SndIO Default"; struct SioPar : public sio_par { - SioPar() { sio_initpar(this); } + SioPar() : sio_par{} { sio_initpar(this); } void clear() { sio_initpar(this); } }; @@ -69,8 +70,6 @@ struct SndioPlayback final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(SndioPlayback) }; SndioPlayback::~SndioPlayback() @@ -136,72 +135,75 @@ bool SndioPlayback::reset() SioPar par; auto tryfmt = mDevice->FmtType; -retry_params: - switch(tryfmt) + while(true) { - case DevFmtByte: - par.bits = 8; - par.sig = 1; - break; - case DevFmtUByte: - par.bits = 8; - par.sig = 0; - break; - case DevFmtShort: - par.bits = 16; - par.sig = 1; - break; - case DevFmtUShort: - par.bits = 16; - par.sig = 0; - break; - case DevFmtFloat: - case DevFmtInt: - par.bits = 32; - par.sig = 1; - break; - case DevFmtUInt: - par.bits = 32; - par.sig = 0; - break; - } - par.bps = SIO_BPS(par.bits); - par.le = SIO_LE_NATIVE; - par.msb = 1; - - par.rate = mDevice->Frequency; - par.pchan = mDevice->channelsFromFmt(); - - par.round = mDevice->UpdateSize; - par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize; - if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize; + switch(tryfmt) + { + case DevFmtByte: + par.bits = 8; + par.sig = 1; + break; + case DevFmtUByte: + par.bits = 8; + par.sig = 0; + break; + case DevFmtShort: + par.bits = 16; + par.sig = 1; + break; + case DevFmtUShort: + par.bits = 16; + par.sig = 0; + break; + case DevFmtFloat: + case DevFmtInt: + par.bits = 32; + par.sig = 1; + break; + case DevFmtUInt: + par.bits = 32; + par.sig = 0; + break; + } + par.bps = SIO_BPS(par.bits); + par.le = SIO_LE_NATIVE; + par.msb = 1; + + par.rate = mDevice->Frequency; + par.pchan = mDevice->channelsFromFmt(); + + par.round = mDevice->UpdateSize; + par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize; + if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize; + + try { + if(!sio_setpar(mSndHandle, &par)) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to set device parameters"}; + + par.clear(); + if(!sio_getpar(mSndHandle, &par)) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to get device parameters"}; + + if(par.bps > 1 && par.le != SIO_LE_NATIVE) + throw al::backend_exception{al::backend_error::DeviceError, + "%s-endian samples not supported", par.le ? "Little" : "Big"}; + if(par.bits < par.bps*8 && !par.msb) + throw al::backend_exception{al::backend_error::DeviceError, + "MSB-padded samples not supported (%u of %u bits)", par.bits, par.bps*8}; + if(par.pchan < 1) + throw al::backend_exception{al::backend_error::DeviceError, + "No playback channels on device"}; - try { - if(!sio_setpar(mSndHandle, &par)) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to set device parameters"}; - - par.clear(); - if(!sio_getpar(mSndHandle, &par)) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to get device parameters"}; - - if(par.bps > 1 && par.le != SIO_LE_NATIVE) - throw al::backend_exception{al::backend_error::DeviceError, - "%s-endian samples not supported", par.le ? "Little" : "Big"}; - if(par.bits < par.bps*8 && !par.msb) - throw al::backend_exception{al::backend_error::DeviceError, - "MSB-padded samples not supported (%u of %u bits)", par.bits, par.bps*8}; - if(par.pchan < 1) - throw al::backend_exception{al::backend_error::DeviceError, - "No playback channels on device"}; - } - catch(al::backend_exception &e) { - if(tryfmt == DevFmtShort) - throw; - par.clear(); - tryfmt = DevFmtShort; - goto retry_params; + break; + } + catch(al::backend_exception &e) { + if(tryfmt == DevFmtShort) + throw; + par.clear(); + tryfmt = DevFmtShort; + } } if(par.bps == 1) @@ -229,7 +231,7 @@ retry_params: mDevice->UpdateSize = par.round; mDevice->BufferSize = par.bufsz + par.round; - mBuffer.resize(mDevice->UpdateSize * par.pchan*par.bps); + mBuffer.resize(size_t{mDevice->UpdateSize} * par.pchan*par.bps); if(par.sig == 1) std::fill(mBuffer.begin(), mBuffer.end(), std::byte{}); else if(par.bits == 8) @@ -292,8 +294,6 @@ struct SndioCapture final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(SndioCapture) }; SndioCapture::~SndioCapture() @@ -317,19 +317,19 @@ int SndioCapture::recordProc() return 1; } - auto fds = std::make_unique<pollfd[]>(static_cast<uint>(nfds_pre)); + auto fds = std::vector<pollfd>(static_cast<uint>(nfds_pre)); while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) { /* Wait until there's some samples to read. */ - const int nfds{sio_pollfd(mSndHandle, fds.get(), POLLIN)}; + const int nfds{sio_pollfd(mSndHandle, fds.data(), POLLIN)}; if(nfds <= 0) { mDevice->handleDisconnect("Failed to get polling fds: %d", nfds); break; } - int pollres{::poll(fds.get(), static_cast<uint>(nfds), 2000)}; + int pollres{::poll(fds.data(), fds.size(), 2000)}; if(pollres < 0) { if(errno == EINTR) continue; @@ -339,7 +339,7 @@ int SndioCapture::recordProc() if(pollres == 0) continue; - const int revents{sio_revents(mSndHandle, fds.get())}; + const int revents{sio_revents(mSndHandle, fds.data())}; if((revents&POLLHUP)) { mDevice->handleDisconnect("Got POLLHUP from poll events"); @@ -373,8 +373,8 @@ int SndioCapture::recordProc() if(buffer.empty()) { /* Got samples to read, but no place to store it. Drop it. */ - static char junk[4096]; - sio_read(mSndHandle, junk, sizeof(junk) - (sizeof(junk)%frameSize)); + static std::array<char,4096> junk; + sio_read(mSndHandle, junk.data(), junk.size() - (junk.size()%frameSize)); } } @@ -461,7 +461,7 @@ void SndioCapture::open(std::string_view name) DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans), mDevice->Frequency, par.sig?'s':'u', par.bps*8, par.rchan, par.rate}; - mRing = RingBuffer::Create(mDevice->BufferSize, par.bps*par.rchan, false); + mRing = RingBuffer::Create(mDevice->BufferSize, size_t{par.bps}*par.rchan, false); mDevice->BufferSize = static_cast<uint>(mRing->writeSpace()); mDevice->UpdateSize = par.round; @@ -497,7 +497,7 @@ void SndioCapture::stop() } void SndioCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint SndioCapture::availableSamples() { return static_cast<uint>(mRing->readSpace()); } diff --git a/alc/backends/solaris.cpp b/alc/backends/solaris.cpp index 38f9db19..c7387284 100644 --- a/alc/backends/solaris.cpp +++ b/alc/backends/solaris.cpp @@ -51,6 +51,7 @@ namespace { +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr char solaris_device[] = "Solaris Default"; std::string solaris_driver{"/dev/audio"}; @@ -74,8 +75,6 @@ struct SolarisBackend final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(SolarisBackend) }; SolarisBackend::~SolarisBackend() @@ -91,7 +90,7 @@ int SolarisBackend::mixerProc() althrd_setname(MIXER_THREAD_NAME); const size_t frame_step{mDevice->channelsFromFmt()}; - const uint frame_size{mDevice->frameSizeFromFmt()}; + const size_t frame_size{mDevice->frameSizeFromFmt()}; while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) @@ -115,12 +114,12 @@ int SolarisBackend::mixerProc() continue; } - std::byte *write_ptr{mBuffer.data()}; - size_t to_write{mBuffer.size()}; - mDevice->renderSamples(write_ptr, static_cast<uint>(to_write/frame_size), frame_step); - while(to_write > 0 && !mKillNow.load(std::memory_order_acquire)) + al::span<std::byte> buffer{mBuffer}; + mDevice->renderSamples(buffer.data(), static_cast<uint>(buffer.size()/frame_size), + frame_step); + while(!buffer.empty() && !mKillNow.load(std::memory_order_acquire)) { - ssize_t wrote{write(mFd, write_ptr, to_write)}; + ssize_t wrote{write(mFd, buffer.data(), buffer.size())}; if(wrote < 0) { if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) @@ -130,8 +129,7 @@ int SolarisBackend::mixerProc() break; } - to_write -= static_cast<size_t>(wrote); - write_ptr += wrote; + buffer = buffer.subspan(static_cast<size_t>(wrote)); } } @@ -267,7 +265,7 @@ BackendFactory &SolarisBackendFactory::getFactory() bool SolarisBackendFactory::init() { - if(auto devopt = ConfigValueStr(nullptr, "solaris", "device")) + if(auto devopt = ConfigValueStr({}, "solaris", "device")) solaris_driver = std::move(*devopt); return true; } diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp index 3ee98457..4fcae59c 100644 --- a/alc/backends/wasapi.cpp +++ b/alc/backends/wasapi.cpp @@ -25,8 +25,8 @@ #define WIN32_LEAN_AND_MEAN #include <windows.h> -#include <stdlib.h> -#include <stdio.h> +#include <cstdio> +#include <cstdlib> #include <memory.h> #include <wtypes.h> @@ -171,7 +171,7 @@ constexpr AudioObjectType ChannelMask_X714{AudioObjectType_FrontLeft | AudioObje | AudioObjectType_TopFrontLeft | AudioObjectType_TopFrontRight | AudioObjectType_TopBackLeft | AudioObjectType_TopBackRight}; - +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr char DevNameHead[] = "OpenAL Soft on "; constexpr size_t DevNameHeadLen{std::size(DevNameHead) - 1}; @@ -201,16 +201,16 @@ constexpr uint RefTime2Samples(const ReferenceTime &val, T srate) noexcept class GuidPrinter { - char mMsg[64]; + std::array<char,64> mMsg; public: GuidPrinter(const GUID &guid) { - std::snprintf(mMsg, std::size(mMsg), "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + std::snprintf(mMsg.data(), mMsg.size(), "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", DWORD{guid.Data1}, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); } - const char *c_str() const { return mMsg; } + [[nodiscard]] auto c_str() const -> const char* { return mMsg.data(); } }; struct PropVariant { @@ -270,13 +270,13 @@ private: struct DeviceListLock : public std::unique_lock<DeviceList> { using std::unique_lock<DeviceList>::unique_lock; - auto& getPlaybackList() const noexcept { return mutex()->mPlayback; } - auto& getCaptureList() const noexcept { return mutex()->mCapture; } + [[nodiscard]] auto& getPlaybackList() const noexcept { return mutex()->mPlayback; } + [[nodiscard]] auto& getCaptureList() const noexcept { return mutex()->mCapture; } void setPlaybackDefaultId(std::wstring_view devid) const { mutex()->mPlaybackDefaultId = devid; } - std::wstring_view getPlaybackDefaultId() const noexcept { return mutex()->mPlaybackDefaultId; } + [[nodiscard]] auto getPlaybackDefaultId() const noexcept -> std::wstring_view { return mutex()->mPlaybackDefaultId; } void setCaptureDefaultId(std::wstring_view devid) const { mutex()->mCaptureDefaultId = devid; } - std::wstring_view getCaptureDefaultId() const noexcept { return mutex()->mCaptureDefaultId; } + [[nodiscard]] auto getCaptureDefaultId() const noexcept -> std::wstring_view { return mutex()->mCaptureDefaultId; } }; DeviceList gDeviceList; @@ -302,8 +302,10 @@ using DeviceHandle = ComPtr<IMMDevice>; using NameGUIDPair = std::pair<std::string,std::string>; static NameGUIDPair GetDeviceNameAndGuid(const DeviceHandle &device) { + /* NOLINTBEGIN(*-avoid-c-arrays) */ static constexpr char UnknownName[]{"Unknown Device Name"}; static constexpr char UnknownGuid[]{"Unknown Device GUID"}; + /* NOLINTEND(*-avoid-c-arrays) */ #if !defined(ALSOFT_UWP) std::string name, guid; @@ -384,9 +386,9 @@ struct DeviceHelper final : public IActivateAudioInterfaceCompletionHandler struct DeviceHelper final : private IMMNotificationClient #endif { +#if defined(ALSOFT_UWP) DeviceHelper() { -#if defined(ALSOFT_UWP) /* TODO: UWP also needs to watch for device added/removed events and * dynamically add/remove devices from the lists. */ @@ -411,8 +413,10 @@ struct DeviceHelper final : private IMMNotificationClient msg); } }); -#endif } +#else + DeviceHelper() = default; +#endif ~DeviceHelper() { #if defined(ALSOFT_UWP) @@ -1071,7 +1075,7 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { HANDLE mNotifyEvent{nullptr}; UINT32 mOrigBufferSize{}, mOrigUpdateSize{}; - std::unique_ptr<char[]> mResampleBuffer{}; + std::vector<char> mResampleBuffer{}; uint mBufferFilled{0}; SampleConverterPtr mResampler; @@ -1082,8 +1086,6 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WasapiPlayback) }; WasapiPlayback::~WasapiPlayback() @@ -1151,9 +1153,9 @@ FORCE_ALIGN int WasapiPlayback::mixerProc() { if(mBufferFilled == 0) { - mDevice->renderSamples(mResampleBuffer.get(), mDevice->UpdateSize, + mDevice->renderSamples(mResampleBuffer.data(), mDevice->UpdateSize, mFormat.Format.nChannels); - resbufferptr = mResampleBuffer.get(); + resbufferptr = mResampleBuffer.data(); mBufferFilled = mDevice->UpdateSize; } @@ -1249,7 +1251,7 @@ FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() tmpbuffers.resize(buffers.size()); resbuffers.resize(buffers.size()); for(size_t i{0};i < tmpbuffers.size();++i) - resbuffers[i] = reinterpret_cast<float*>(mResampleBuffer.get()) + + resbuffers[i] = reinterpret_cast<float*>(mResampleBuffer.data()) + mDevice->UpdateSize*i; } } @@ -1496,7 +1498,7 @@ void WasapiPlayback::prepareFormat(WAVEFORMATEXTENSIBLE &OutputType) void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType) { - if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true)) + if(!GetConfigValueBool(mDevice->DeviceName, "wasapi", "allow-resampler", true)) mDevice->Frequency = OutputType.Format.nSamplesPerSec; else mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec); @@ -1610,7 +1612,7 @@ bool WasapiPlayback::reset() HRESULT WasapiPlayback::resetProxy() { - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "spatial-api", false)) + if(GetConfigValueBool(mDevice->DeviceName, "wasapi", "spatial-api", false)) { auto &audio = mAudio.emplace<SpatialDevice>(); HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(ISpatialAudioClient), @@ -1775,7 +1777,7 @@ HRESULT WasapiPlayback::resetProxy() mDevice->Flags.reset(DirectEar).set(Virtualization); if(streamParams.StaticObjectTypeMask == ChannelMask_Stereo) mDevice->FmtChans = DevFmtStereo; - if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true)) + if(!GetConfigValueBool(mDevice->DeviceName, "wasapi", "allow-resampler", true)) mDevice->Frequency = OutputType.Format.nSamplesPerSec; else mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec); @@ -1819,7 +1821,8 @@ HRESULT WasapiPlayback::resetProxy() mDevice->BufferSize = mDevice->UpdateSize*2; mResampler = nullptr; - mResampleBuffer = nullptr; + mResampleBuffer.clear(); + mResampleBuffer.shrink_to_fit(); mBufferFilled = 0; if(mDevice->Frequency != mFormat.Format.nSamplesPerSec) { @@ -1828,7 +1831,7 @@ HRESULT WasapiPlayback::resetProxy() mResampler = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType, channelCount, mDevice->Frequency, mFormat.Format.nSamplesPerSec, Resampler::FastBSinc24); - mResampleBuffer = std::make_unique<char[]>(size_t{mDevice->UpdateSize} * channelCount * + mResampleBuffer.resize(size_t{mDevice->UpdateSize} * channelCount * mFormat.Format.wBitsPerSample / 8); TRACE("Created converter for %s/%s format, dst: %luhz (%u), src: %uhz (%u)\n", @@ -1950,15 +1953,16 @@ no_spatial: mDevice->BufferSize/2); mResampler = nullptr; - mResampleBuffer = nullptr; + mResampleBuffer.clear(); + mResampleBuffer.shrink_to_fit(); mBufferFilled = 0; if(mDevice->Frequency != mFormat.Format.nSamplesPerSec) { mResampler = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType, mFormat.Format.nChannels, mDevice->Frequency, mFormat.Format.nSamplesPerSec, Resampler::FastBSinc24); - mResampleBuffer = std::make_unique<char[]>(size_t{mDevice->UpdateSize} * - mFormat.Format.nChannels * mFormat.Format.wBitsPerSample / 8); + mResampleBuffer.resize(size_t{mDevice->UpdateSize} * mFormat.Format.nChannels * + mFormat.Format.wBitsPerSample / 8); TRACE("Created converter for %s/%s format, dst: %luhz (%u), src: %uhz (%u)\n", DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType), @@ -2072,7 +2076,7 @@ ClockLatency WasapiPlayback::getClockLatency() ClockLatency ret; std::lock_guard<std::mutex> _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); ret.Latency = seconds{mPadding.load(std::memory_order_relaxed)}; ret.Latency /= mFormat.Format.nSamplesPerSec; if(mResampler) @@ -2117,8 +2121,6 @@ struct WasapiCapture final : public BackendBase, WasapiProxy { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WasapiCapture) }; WasapiCapture::~WasapiCapture() @@ -2651,7 +2653,7 @@ void WasapiCapture::stopProxy() void WasapiCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint WasapiCapture::availableSamples() { return static_cast<uint>(mRing->readSpace()); } diff --git a/alc/backends/wave.cpp b/alc/backends/wave.cpp index 794d5cb8..985bb539 100644 --- a/alc/backends/wave.cpp +++ b/alc/backends/wave.cpp @@ -55,38 +55,44 @@ using std::chrono::nanoseconds; using ubyte = unsigned char; using ushort = unsigned short; +struct FileDeleter { + void operator()(gsl::owner<FILE*> f) { fclose(f); } +}; +using FilePtr = std::unique_ptr<FILE,FileDeleter>; + +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr char waveDevice[] = "Wave File Writer"; -constexpr ubyte SUBTYPE_PCM[]{ +constexpr std::array<ubyte,16> SUBTYPE_PCM{{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 -}; -constexpr ubyte SUBTYPE_FLOAT[]{ +}}; +constexpr std::array<ubyte,16> SUBTYPE_FLOAT{{ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 -}; +}}; -constexpr ubyte SUBTYPE_BFORMAT_PCM[]{ +constexpr std::array<ubyte,16> SUBTYPE_BFORMAT_PCM{{ 0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00 -}; +}}; -constexpr ubyte SUBTYPE_BFORMAT_FLOAT[]{ +constexpr std::array<ubyte,16> SUBTYPE_BFORMAT_FLOAT{{ 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00 -}; +}}; void fwrite16le(ushort val, FILE *f) { - ubyte data[2]{ static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff) }; - fwrite(data, 1, 2, f); + std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff)}; + fwrite(data.data(), 1, data.size(), f); } void fwrite32le(uint val, FILE *f) { - ubyte data[4]{ static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff), - static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff) }; - fwrite(data, 1, 4, f); + std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff), + static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff)}; + fwrite(data.data(), 1, data.size(), f); } @@ -101,23 +107,16 @@ struct WaveBackend final : public BackendBase { void start() override; void stop() override; - FILE *mFile{nullptr}; + FilePtr mFile{nullptr}; long mDataStart{-1}; std::vector<std::byte> mBuffer; std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WaveBackend) }; -WaveBackend::~WaveBackend() -{ - if(mFile) - fclose(mFile); - mFile = nullptr; -} +WaveBackend::~WaveBackend() = default; int WaveBackend::mixerProc() { @@ -169,8 +168,8 @@ int WaveBackend::mixerProc() } } - const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile)}; - if(fs < mDevice->UpdateSize || ferror(mFile)) + const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile.get())}; + if(fs < mDevice->UpdateSize || ferror(mFile.get())) { ERR("Error writing to file\n"); mDevice->handleDisconnect("Failed to write playback samples"); @@ -196,7 +195,7 @@ int WaveBackend::mixerProc() void WaveBackend::open(std::string_view name) { - auto fname = ConfigValueStr(nullptr, "wave", "file"); + auto fname = ConfigValueStr({}, "wave", "file"); if(!fname) throw al::backend_exception{al::backend_error::NoDevice, "No wave output filename"}; @@ -212,10 +211,10 @@ void WaveBackend::open(std::string_view name) #ifdef _WIN32 { std::wstring wname{utf8_to_wstr(fname.value())}; - mFile = _wfopen(wname.c_str(), L"wb"); + mFile = FilePtr{_wfopen(wname.c_str(), L"wb")}; } #else - mFile = fopen(fname->c_str(), "wb"); + mFile = FilePtr{fopen(fname->c_str(), "wb")}; #endif if(!mFile) throw al::backend_exception{al::backend_error::DeviceError, "Could not open file '%s': %s", @@ -228,12 +227,11 @@ bool WaveBackend::reset() { uint channels{0}, bytes{0}, chanmask{0}; bool isbformat{false}; - size_t val; - fseek(mFile, 0, SEEK_SET); - clearerr(mFile); + fseek(mFile.get(), 0, SEEK_SET); + clearerr(mFile.get()); - if(GetConfigValueBool(nullptr, "wave", "bformat", false)) + if(GetConfigValueBool({}, "wave", "bformat", false)) { mDevice->FmtChans = DevFmtAmbi3D; mDevice->mAmbiOrder = 1; @@ -282,49 +280,48 @@ bool WaveBackend::reset() bytes = mDevice->bytesFromFmt(); channels = mDevice->channelsFromFmt(); - rewind(mFile); + rewind(mFile.get()); - fputs("RIFF", mFile); - fwrite32le(0xFFFFFFFF, mFile); // 'RIFF' header len; filled in at close + fputs("RIFF", mFile.get()); + fwrite32le(0xFFFFFFFF, mFile.get()); // 'RIFF' header len; filled in at close - fputs("WAVE", mFile); + fputs("WAVE", mFile.get()); - fputs("fmt ", mFile); - fwrite32le(40, mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE + fputs("fmt ", mFile.get()); + fwrite32le(40, mFile.get()); // 'fmt ' header len; 40 bytes for EXTENSIBLE // 16-bit val, format type id (extensible: 0xFFFE) - fwrite16le(0xFFFE, mFile); + fwrite16le(0xFFFE, mFile.get()); // 16-bit val, channel count - fwrite16le(static_cast<ushort>(channels), mFile); + fwrite16le(static_cast<ushort>(channels), mFile.get()); // 32-bit val, frequency - fwrite32le(mDevice->Frequency, mFile); + fwrite32le(mDevice->Frequency, mFile.get()); // 32-bit val, bytes per second - fwrite32le(mDevice->Frequency * channels * bytes, mFile); + fwrite32le(mDevice->Frequency * channels * bytes, mFile.get()); // 16-bit val, frame size - fwrite16le(static_cast<ushort>(channels * bytes), mFile); + fwrite16le(static_cast<ushort>(channels * bytes), mFile.get()); // 16-bit val, bits per sample - fwrite16le(static_cast<ushort>(bytes * 8), mFile); + fwrite16le(static_cast<ushort>(bytes * 8), mFile.get()); // 16-bit val, extra byte count - fwrite16le(22, mFile); + fwrite16le(22, mFile.get()); // 16-bit val, valid bits per sample - fwrite16le(static_cast<ushort>(bytes * 8), mFile); + fwrite16le(static_cast<ushort>(bytes * 8), mFile.get()); // 32-bit val, channel mask - fwrite32le(chanmask, mFile); + fwrite32le(chanmask, mFile.get()); // 16 byte GUID, sub-type format - val = fwrite((mDevice->FmtType == DevFmtFloat) ? - (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) : - (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, mFile); - (void)val; + std::ignore = fwrite((mDevice->FmtType == DevFmtFloat) ? + (isbformat ? SUBTYPE_BFORMAT_FLOAT.data() : SUBTYPE_FLOAT.data()) : + (isbformat ? SUBTYPE_BFORMAT_PCM.data() : SUBTYPE_PCM.data()), 1, 16, mFile.get()); - fputs("data", mFile); - fwrite32le(0xFFFFFFFF, mFile); // 'data' header len; filled in at close + fputs("data", mFile.get()); + fwrite32le(0xFFFFFFFF, mFile.get()); // 'data' header len; filled in at close - if(ferror(mFile)) + if(ferror(mFile.get())) { ERR("Error writing header: %s\n", strerror(errno)); return false; } - mDataStart = ftell(mFile); + mDataStart = ftell(mFile.get()); setDefaultWFXChannelOrder(); @@ -336,7 +333,7 @@ bool WaveBackend::reset() void WaveBackend::start() { - if(mDataStart > 0 && fseek(mFile, 0, SEEK_END) != 0) + if(mDataStart > 0 && fseek(mFile.get(), 0, SEEK_END) != 0) WARN("Failed to seek on output file\n"); try { mKillNow.store(false, std::memory_order_release); @@ -356,14 +353,14 @@ void WaveBackend::stop() if(mDataStart > 0) { - long size{ftell(mFile)}; + long size{ftell(mFile.get())}; if(size > 0) { long dataLen{size - mDataStart}; - if(fseek(mFile, 4, SEEK_SET) == 0) - fwrite32le(static_cast<uint>(size-8), mFile); // 'WAVE' header len - if(fseek(mFile, mDataStart-4, SEEK_SET) == 0) - fwrite32le(static_cast<uint>(dataLen), mFile); // 'data' header len + if(fseek(mFile.get(), 4, SEEK_SET) == 0) + fwrite32le(static_cast<uint>(size-8), mFile.get()); // 'WAVE' header len + if(fseek(mFile.get(), mDataStart-4, SEEK_SET) == 0) + fwrite32le(static_cast<uint>(dataLen), mFile.get()); // 'data' header len } } } diff --git a/alc/backends/winmm.cpp b/alc/backends/winmm.cpp index f0fb0a1c..e593defb 100644 --- a/alc/backends/winmm.cpp +++ b/alc/backends/winmm.cpp @@ -22,8 +22,8 @@ #include "winmm.h" -#include <stdlib.h> -#include <stdio.h> +#include <cstdlib> +#include <cstdio> #include <memory.h> #include <windows.h> @@ -46,6 +46,7 @@ #include "core/logging.h" #include "ringbuffer.h" #include "strutils.h" +#include "vector.h" #ifndef WAVE_FORMAT_IEEE_FLOAT #define WAVE_FORMAT_IEEE_FLOAT 0x0003 @@ -62,7 +63,7 @@ std::vector<std::string> CaptureDevices; bool checkName(const std::vector<std::string> &list, const std::string &name) { return std::find(list.cbegin(), list.cend(), name) != list.cend(); } -void ProbePlaybackDevices(void) +void ProbePlaybackDevices() { PlaybackDevices.clear(); @@ -93,7 +94,7 @@ void ProbePlaybackDevices(void) } } -void ProbeCaptureDevices(void) +void ProbeCaptureDevices() { CaptureDevices.clear(); @@ -144,6 +145,7 @@ struct WinMMPlayback final : public BackendBase { al::semaphore mSem; uint mIdx{0u}; std::array<WAVEHDR,4> mWaveBuffer{}; + al::vector<char,16> mBuffer; HWAVEOUT mOutHdl{nullptr}; @@ -151,8 +153,6 @@ struct WinMMPlayback final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WinMMPlayback) }; WinMMPlayback::~WinMMPlayback() @@ -160,9 +160,6 @@ WinMMPlayback::~WinMMPlayback() if(mOutHdl) waveOutClose(mOutHdl); mOutHdl = nullptr; - - al_free(mWaveBuffer[0].lpData); - std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{}); } /* WinMMPlayback::waveOutProc @@ -313,11 +310,11 @@ bool WinMMPlayback::reset() } setDefaultWFXChannelOrder(); - uint BufferSize{mDevice->UpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()}; + const uint BufferSize{mDevice->UpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()}; - al_free(mWaveBuffer[0].lpData); + decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer); mWaveBuffer[0] = WAVEHDR{}; - mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize * mWaveBuffer.size())); + mWaveBuffer[0].lpData = mBuffer.data(); mWaveBuffer[0].dwBufferLength = BufferSize; for(size_t i{1};i < mWaveBuffer.size();i++) { @@ -380,6 +377,7 @@ struct WinMMCapture final : public BackendBase { al::semaphore mSem; uint mIdx{0}; std::array<WAVEHDR,4> mWaveBuffer{}; + al::vector<char,16> mBuffer; HWAVEIN mInHdl{nullptr}; @@ -389,8 +387,6 @@ struct WinMMCapture final : public BackendBase { std::atomic<bool> mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WinMMCapture) }; WinMMCapture::~WinMMCapture() @@ -399,9 +395,6 @@ WinMMCapture::~WinMMCapture() if(mInHdl) waveInClose(mInHdl); mInHdl = nullptr; - - al_free(mWaveBuffer[0].lpData); - std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{}); } /* WinMMCapture::waveInProc @@ -435,7 +428,8 @@ int WinMMCapture::captureProc() WAVEHDR &waveHdr = mWaveBuffer[widx]; widx = (widx+1) % mWaveBuffer.size(); - mRing->write(waveHdr.lpData, waveHdr.dwBytesRecorded / mFormat.nBlockAlign); + std::ignore = mRing->write(waveHdr.lpData, + waveHdr.dwBytesRecorded / mFormat.nBlockAlign); mReadable.fetch_sub(1, std::memory_order_acq_rel); waveInAddBuffer(mInHdl, &waveHdr, sizeof(WAVEHDR)); } while(--todo); @@ -519,9 +513,9 @@ void WinMMCapture::open(std::string_view name) mRing = RingBuffer::Create(CapturedDataSize, mFormat.nBlockAlign, false); - al_free(mWaveBuffer[0].lpData); + decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer); mWaveBuffer[0] = WAVEHDR{}; - mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize * mWaveBuffer.size())); + mWaveBuffer[0].lpData = mBuffer.data(); mWaveBuffer[0].dwBufferLength = BufferSize; for(size_t i{1};i < mWaveBuffer.size();++i) { @@ -573,7 +567,7 @@ void WinMMCapture::stop() } void WinMMCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint WinMMCapture::availableSamples() { return static_cast<uint>(mRing->readSpace()); } diff --git a/alc/context.cpp b/alc/context.cpp index ffc2743e..67f08aee 100644 --- a/alc/context.cpp +++ b/alc/context.cpp @@ -5,11 +5,11 @@ #include <algorithm> #include <array> +#include <cstddef> #include <cstring> #include <functional> #include <limits> #include <numeric> -#include <stddef.h> #include <stdexcept> #include <string_view> #include <utility> @@ -24,6 +24,7 @@ #include "al/listener.h" #include "albit.h" #include "alc/alu.h" +#include "alc/backends/base.h" #include "alspan.h" #include "core/async_event.h" #include "core/device.h" @@ -32,6 +33,7 @@ #include "core/voice.h" #include "core/voice_change.h" #include "device.h" +#include "flexarray.h" #include "ringbuffer.h" #include "vecmat.h" @@ -163,9 +165,13 @@ void ALCcontext::init() else { auxslots = EffectSlot::CreatePtrArray(1); - (*auxslots)[0] = mDefaultSlot->mSlot; - mDefaultSlot->mState = SlotState::Playing; + if(auxslots) + { + (*auxslots)[0] = mDefaultSlot->mSlot; + mDefaultSlot->mState = SlotState::Playing; + } } + if(!auxslots) throw std::bad_alloc{}; mActiveAuxSlots.store(auxslots, std::memory_order_relaxed); allocVoiceChanges(); @@ -231,7 +237,7 @@ void ALCcontext::init() mActiveVoiceCount.store(64, std::memory_order_relaxed); } -bool ALCcontext::deinit() +void ALCcontext::deinit() { if(sLocalContext == this) { @@ -251,18 +257,14 @@ bool ALCcontext::deinit() dec_ref(); } - bool ret{}; + bool stopPlayback{}; /* First make sure this context exists in the device's list. */ auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire); if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this))) { using ContextArray = al::FlexArray<ContextBase*>; - auto alloc_ctx_array = [](const size_t count) -> ContextArray* - { - if(count == 0) return &DeviceBase::sEmptyContextArray; - return ContextArray::Create(count).release(); - }; - auto *newarray = alloc_ctx_array(oldarray->size() - toremove); + const size_t newsize{oldarray->size() - toremove}; + auto newarray = ContextArray::Create(newsize); /* Copy the current/old context handles to the new array, excluding the * given context. @@ -273,21 +275,21 @@ bool ALCcontext::deinit() /* Store the new context array in the device. Wait for any current mix * to finish before deleting the old array. */ - mDevice->mContexts.store(newarray); - if(oldarray != &DeviceBase::sEmptyContextArray) - { - mDevice->waitForMix(); - delete oldarray; - } + auto prevarray = mDevice->mContexts.exchange(std::move(newarray)); + std::ignore = mDevice->waitForMix(); - ret = !newarray->empty(); + stopPlayback = (newsize == 0); } else - ret = !oldarray->empty(); + stopPlayback = oldarray->empty(); StopEventThrd(this); - return ret; + if(stopPlayback && mALDevice->mDeviceState == DeviceState::Playing) + { + mALDevice->Backend->stop(); + mALDevice->mDeviceState = DeviceState::Configured; + } } void ALCcontext::applyAllUpdates() @@ -328,10 +330,10 @@ void ForEachSource(ALCcontext *context, F func) uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; + const auto idx = static_cast<uint>(al::countr_zero(usemask)); usemask &= ~(1_u64 << idx); - func(sublist.Sources[idx]); + func((*sublist.Sources)[idx]); } } } @@ -576,7 +578,7 @@ void ALCcontext::eax_update_speaker_configuration() void ALCcontext::eax_set_last_error_defaults() noexcept { - mEaxLastError = EAX_OK; + mEaxLastError = EAXCONTEXT_DEFAULTLASTERROR; } void ALCcontext::eax_session_set_defaults() noexcept @@ -665,6 +667,7 @@ void ALCcontext::eax_get_misc(const EaxCall& call) break; case EAXCONTEXT_LASTERROR: call.set_value<ContextException>(mEaxLastError); + mEaxLastError = EAX_OK; break; case EAXCONTEXT_SPEAKERCONFIG: call.set_value<ContextException>(mEaxSpeakerConfig); @@ -1035,6 +1038,7 @@ try } catch(...) { + context->eaxSetLastError(); eax_log_exception(__func__); return AL_INVALID_OPERATION; } @@ -1062,6 +1066,7 @@ try } catch(...) { + context->eaxSetLastError(); eax_log_exception(__func__); return AL_INVALID_OPERATION; } diff --git a/alc/context.h b/alc/context.h index 201c8873..e27d10d3 100644 --- a/alc/context.h +++ b/alc/context.h @@ -1,6 +1,7 @@ #ifndef ALC_CONTEXT_H #define ALC_CONTEXT_H +#include <array> #include <atomic> #include <deque> #include <memory> @@ -36,6 +37,8 @@ struct ALeffect; struct ALeffectslot; struct ALsource; struct DebugGroup; +struct EffectSlotSubList; +struct SourceSubList; enum class DebugSource : uint8_t; enum class DebugType : uint8_t; @@ -68,37 +71,6 @@ struct DebugLogEntry { }; -struct SourceSubList { - uint64_t FreeMask{~0_u64}; - ALsource *Sources{nullptr}; /* 64 */ - - SourceSubList() noexcept = default; - SourceSubList(const SourceSubList&) = delete; - SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources} - { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; } - ~SourceSubList(); - - SourceSubList& operator=(const SourceSubList&) = delete; - SourceSubList& operator=(SourceSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; } -}; - -struct EffectSlotSubList { - uint64_t FreeMask{~0_u64}; - ALeffectslot *EffectSlots{nullptr}; /* 64 */ - - EffectSlotSubList() noexcept = default; - EffectSlotSubList(const EffectSlotSubList&) = delete; - EffectSlotSubList(EffectSlotSubList&& rhs) noexcept - : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots} - { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; } - ~EffectSlotSubList(); - - EffectSlotSubList& operator=(const EffectSlotSubList&) = delete; - EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; } -}; - struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase { const al::intrusive_ptr<ALCdevice> mALDevice; @@ -158,10 +130,10 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase { void init(); /** * Removes the context from its device and removes it from being current on - * the running thread or globally. Returns true if other contexts still - * exist on the device. + * the running thread or globally. Stops device playback if this was the + * last context on its device. */ - bool deinit(); + void deinit(); /** * Defers/suspends updates for the given context's listener and sources. @@ -186,8 +158,8 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase { */ void applyAllUpdates(); -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] +#ifdef __MINGW32__ + [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]] #else [[gnu::format(printf, 3, 4)]] #endif @@ -230,10 +202,7 @@ public: /* Default effect that applies to sources that don't have an effect on send 0. */ static ALeffect sDefaultEffect; - DEF_NEWDEL(ALCcontext) - #ifdef ALSOFT_EAX -public: bool hasEax() const noexcept { return mEaxIsInitialized; } bool eaxIsCapable() const noexcept; @@ -560,7 +529,7 @@ private: using ContextRef = al::intrusive_ptr<ALCcontext>; -ContextRef GetContextRef(void); +ContextRef GetContextRef(); void UpdateContextProps(ALCcontext *context); diff --git a/alc/device.cpp b/alc/device.cpp index 27aa6f36..fc145579 100644 --- a/alc/device.cpp +++ b/alc/device.cpp @@ -3,9 +3,12 @@ #include "device.h" +#include <cstddef> #include <numeric> -#include <stddef.h> +#include "al/buffer.h" +#include "al/effect.h" +#include "al/filter.h" #include "albit.h" #include "alconfig.h" #include "backends/base.h" @@ -55,8 +58,8 @@ ALCdevice::~ALCdevice() void ALCdevice::enumerateHrtfs() { - mHrtfList = EnumerateHrtf(configValue<std::string>(nullptr, "hrtf-paths")); - if(auto defhrtfopt = configValue<std::string>(nullptr, "default-hrtf")) + mHrtfList = EnumerateHrtf(configValue<std::string>({}, "hrtf-paths")); + if(auto defhrtfopt = configValue<std::string>({}, "default-hrtf")) { auto iter = std::find(mHrtfList.begin(), mHrtfList.end(), *defhrtfopt); if(iter == mHrtfList.end()) diff --git a/alc/device.h b/alc/device.h index 66f37a7e..dd9335dc 100644 --- a/alc/device.h +++ b/alc/device.h @@ -1,6 +1,7 @@ #ifndef ALC_DEVICE_H #define ALC_DEVICE_H +#include <array> #include <atomic> #include <memory> #include <mutex> @@ -29,56 +30,13 @@ struct ALbuffer; struct ALeffect; struct ALfilter; struct BackendBase; +struct BufferSubList; +struct EffectSubList; +struct FilterSubList; using uint = unsigned int; -struct BufferSubList { - uint64_t FreeMask{~0_u64}; - ALbuffer *Buffers{nullptr}; /* 64 */ - - BufferSubList() noexcept = default; - BufferSubList(const BufferSubList&) = delete; - BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers} - { rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; } - ~BufferSubList(); - - BufferSubList& operator=(const BufferSubList&) = delete; - BufferSubList& operator=(BufferSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; } -}; - -struct EffectSubList { - uint64_t FreeMask{~0_u64}; - ALeffect *Effects{nullptr}; /* 64 */ - - EffectSubList() noexcept = default; - EffectSubList(const EffectSubList&) = delete; - EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects} - { rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; } - ~EffectSubList(); - - EffectSubList& operator=(const EffectSubList&) = delete; - EffectSubList& operator=(EffectSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; } -}; - -struct FilterSubList { - uint64_t FreeMask{~0_u64}; - ALfilter *Filters{nullptr}; /* 64 */ - - FilterSubList() noexcept = default; - FilterSubList(const FilterSubList&) = delete; - FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters} - { rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; } - ~FilterSubList(); - - FilterSubList& operator=(const FilterSubList&) = delete; - FilterSubList& operator=(FilterSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; } -}; - - struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase { /* This lock protects the device state (format, update size, etc) from * being from being changed in multiple threads, or being accessed while @@ -143,30 +101,28 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase { void enumerateHrtfs(); - bool getConfigValueBool(const char *block, const char *key, bool def) - { return GetConfigValueBool(DeviceName.c_str(), block, key, def); } + bool getConfigValueBool(const std::string_view block, const std::string_view key, bool def) + { return GetConfigValueBool(DeviceName, block, key, def); } template<typename T> - inline std::optional<T> configValue(const char *block, const char *key) = delete; - - DEF_NEWDEL(ALCdevice) + inline std::optional<T> configValue(const std::string_view block, const std::string_view key) = delete; }; template<> -inline std::optional<std::string> ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueStr(DeviceName.c_str(), block, key); } +inline std::optional<std::string> ALCdevice::configValue(const std::string_view block, const std::string_view key) +{ return ConfigValueStr(DeviceName, block, key); } template<> -inline std::optional<int> ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueInt(DeviceName.c_str(), block, key); } +inline std::optional<int> ALCdevice::configValue(const std::string_view block, const std::string_view key) +{ return ConfigValueInt(DeviceName, block, key); } template<> -inline std::optional<uint> ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueUInt(DeviceName.c_str(), block, key); } +inline std::optional<uint> ALCdevice::configValue(const std::string_view block, const std::string_view key) +{ return ConfigValueUInt(DeviceName, block, key); } template<> -inline std::optional<float> ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueFloat(DeviceName.c_str(), block, key); } +inline std::optional<float> ALCdevice::configValue(const std::string_view block, const std::string_view key) +{ return ConfigValueFloat(DeviceName, block, key); } template<> -inline std::optional<bool> ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueBool(DeviceName.c_str(), block, key); } +inline std::optional<bool> ALCdevice::configValue(const std::string_view block, const std::string_view key) +{ return ConfigValueBool(DeviceName, block, key); } /** Stores the latest ALC device error. */ void alcSetError(ALCdevice *device, ALCenum errorCode); diff --git a/alc/effects/autowah.cpp b/alc/effects/autowah.cpp index 4f874ef2..424230e8 100644 --- a/alc/effects/autowah.cpp +++ b/alc/effects/autowah.cpp @@ -50,35 +50,37 @@ constexpr float QFactor{5.0f}; struct AutowahState final : public EffectState { /* Effect parameters */ - float mAttackRate; - float mReleaseRate; - float mResonanceGain; - float mPeakGain; - float mFreqMinNorm; - float mBandwidthNorm; - float mEnvDelay; + float mAttackRate{}; + float mReleaseRate{}; + float mResonanceGain{}; + float mPeakGain{}; + float mFreqMinNorm{}; + float mBandwidthNorm{}; + float mEnvDelay{}; /* Filter components derived from the envelope. */ - struct { - float cos_w0; - float alpha; - } mEnv[BufferLineSize]; + struct FilterParam { + float cos_w0{}; + float alpha{}; + }; + std::array<FilterParam,BufferLineSize> mEnv; - struct { + struct ChannelData { uint mTargetChannel{InvalidChannelIndex}; /* Effect filters' history. */ struct { - float z1, z2; + float z1{}, z2{}; } mFilter; /* Effect gains for each output channel */ - float mCurrentGain; - float mTargetGain; - } mChans[MaxAmbiChannels]; + float mCurrentGain{}; + float mTargetGain{}; + }; + std::array<ChannelData,MaxAmbiChannels> mChans; /* Effects buffers */ - alignas(16) float mBufferOut[BufferLineSize]; + alignas(16) FloatBufferLine mBufferOut{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -86,8 +88,6 @@ struct AutowahState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - - DEF_NEWDEL(AutowahState) }; void AutowahState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -118,18 +118,19 @@ void AutowahState::deviceUpdate(const DeviceBase*, const BufferStorage*) } void AutowahState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get<AutowahProps>(*props_); const DeviceBase *device{context->mDevice}; const auto frequency = static_cast<float>(device->Frequency); - const float ReleaseTime{clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f)}; + const float ReleaseTime{clampf(props.ReleaseTime, 0.001f, 1.0f)}; - mAttackRate = std::exp(-1.0f / (props->Autowah.AttackTime*frequency)); + mAttackRate = std::exp(-1.0f / (props.AttackTime*frequency)); mReleaseRate = std::exp(-1.0f / (ReleaseTime*frequency)); /* 0-20dB Resonance Peak gain */ - mResonanceGain = std::sqrt(std::log10(props->Autowah.Resonance)*10.0f / 3.0f); - mPeakGain = 1.0f - std::log10(props->Autowah.PeakGain / GainScale); + mResonanceGain = std::sqrt(std::log10(props.Resonance)*10.0f / 3.0f); + mPeakGain = 1.0f - std::log10(props.PeakGain / GainScale); mFreqMinNorm = MinFreq / frequency; mBandwidthNorm = (MaxFreq-MinFreq) / frequency; @@ -155,17 +156,16 @@ void AutowahState::process(const size_t samplesToDo, float env_delay{mEnvDelay}; for(size_t i{0u};i < samplesToDo;i++) { - float w0, sample, a; - /* Envelope follower described on the book: Audio Effects, Theory, * Implementation and Application. */ - sample = peak_gain * std::fabs(samplesIn[0][i]); - a = (sample > env_delay) ? attack_rate : release_rate; + const float sample{peak_gain * std::fabs(samplesIn[0][i])}; + const float a{(sample > env_delay) ? attack_rate : release_rate}; env_delay = lerpf(sample, env_delay, a); /* Calculate the cos and alpha components for this sample's filter. */ - w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * (al::numbers::pi_v<float>*2.0f); + const float w0{minf((bandwidth*env_delay + freq_min), 0.46f) * + (al::numbers::pi_v<float>*2.0f)}; mEnv[i].cos_w0 = std::cos(w0); mEnv[i].alpha = std::sin(w0)/(2.0f * QFactor); } @@ -194,18 +194,18 @@ void AutowahState::process(const size_t samplesToDo, { const float alpha{mEnv[i].alpha}; const float cos_w0{mEnv[i].cos_w0}; - float input, output; - float a[3], b[3]; - - b[0] = 1.0f + alpha*res_gain; - b[1] = -2.0f * cos_w0; - b[2] = 1.0f - alpha*res_gain; - a[0] = 1.0f + alpha/res_gain; - a[1] = -2.0f * cos_w0; - a[2] = 1.0f - alpha/res_gain; - - input = insamples[i]; - output = input*(b[0]/a[0]) + z1; + + const std::array b{ + 1.0f + alpha*res_gain, + -2.0f * cos_w0, + 1.0f - alpha*res_gain}; + const std::array a{ + 1.0f + alpha/res_gain, + -2.0f * cos_w0, + 1.0f - alpha/res_gain}; + + const float input{insamples[i]}; + const float output{input*(b[0]/a[0]) + z1}; z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2; z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]); mBufferOut[i] = output; @@ -214,8 +214,8 @@ void AutowahState::process(const size_t samplesToDo, chandata->mFilter.z2 = z2; /* Now, mix the processed sound data to the output. */ - MixSamples({mBufferOut, samplesToDo}, samplesOut[outidx].data(), chandata->mCurrentGain, - chandata->mTargetGain, samplesToDo); + MixSamples({mBufferOut.data(), samplesToDo}, samplesOut[outidx].data(), + chandata->mCurrentGain, chandata->mTargetGain, samplesToDo); ++chandata; } } diff --git a/alc/effects/base.h b/alc/effects/base.h index 95695857..9bbbfc71 100644 --- a/alc/effects/base.h +++ b/alc/effects/base.h @@ -4,23 +4,30 @@ #include "core/effects/base.h" -EffectStateFactory *NullStateFactory_getFactory(void); -EffectStateFactory *ReverbStateFactory_getFactory(void); -EffectStateFactory *StdReverbStateFactory_getFactory(void); -EffectStateFactory *AutowahStateFactory_getFactory(void); -EffectStateFactory *ChorusStateFactory_getFactory(void); -EffectStateFactory *CompressorStateFactory_getFactory(void); -EffectStateFactory *DistortionStateFactory_getFactory(void); -EffectStateFactory *EchoStateFactory_getFactory(void); -EffectStateFactory *EqualizerStateFactory_getFactory(void); -EffectStateFactory *FlangerStateFactory_getFactory(void); -EffectStateFactory *FshifterStateFactory_getFactory(void); -EffectStateFactory *ModulatorStateFactory_getFactory(void); -EffectStateFactory *PshifterStateFactory_getFactory(void); -EffectStateFactory* VmorpherStateFactory_getFactory(void); - -EffectStateFactory *DedicatedStateFactory_getFactory(void); - -EffectStateFactory *ConvolutionStateFactory_getFactory(void); +/* This is a user config option for modifying the overall output of the reverb + * effect. + */ +inline float ReverbBoost{1.0f}; + + +EffectStateFactory *NullStateFactory_getFactory(); +EffectStateFactory *ReverbStateFactory_getFactory(); +EffectStateFactory *StdReverbStateFactory_getFactory(); +EffectStateFactory *AutowahStateFactory_getFactory(); +EffectStateFactory *ChorusStateFactory_getFactory(); +EffectStateFactory *CompressorStateFactory_getFactory(); +EffectStateFactory *DistortionStateFactory_getFactory(); +EffectStateFactory *EchoStateFactory_getFactory(); +EffectStateFactory *EqualizerStateFactory_getFactory(); +EffectStateFactory *FlangerStateFactory_getFactory(); +EffectStateFactory *FshifterStateFactory_getFactory(); +EffectStateFactory *ModulatorStateFactory_getFactory(); +EffectStateFactory *PshifterStateFactory_getFactory(); +EffectStateFactory* VmorpherStateFactory_getFactory(); + +EffectStateFactory *DedicatedDialogStateFactory_getFactory(); +EffectStateFactory *DedicatedLfeStateFactory_getFactory(); + +EffectStateFactory *ConvolutionStateFactory_getFactory(); #endif /* EFFECTS_BASE_H */ diff --git a/alc/effects/chorus.cpp b/alc/effects/chorus.cpp index 9cbc922f..bc6ddaf0 100644 --- a/alc/effects/chorus.cpp +++ b/alc/effects/chorus.cpp @@ -48,7 +48,14 @@ namespace { using uint = unsigned int; -struct ChorusState final : public EffectState { +constexpr auto inv_sqrt2 = static_cast<float>(1.0 / al::numbers::sqrt2); +constexpr auto lcoeffs_pw = CalcDirectionCoeffs(std::array{-1.0f, 0.0f, 0.0f}); +constexpr auto rcoeffs_pw = CalcDirectionCoeffs(std::array{ 1.0f, 0.0f, 0.0f}); +constexpr auto lcoeffs_nrml = CalcDirectionCoeffs(std::array{-inv_sqrt2, 0.0f, inv_sqrt2}); +constexpr auto rcoeffs_nrml = CalcDirectionCoeffs(std::array{ inv_sqrt2, 0.0f, inv_sqrt2}); + + +struct ChorusState : public EffectState { std::vector<float> mDelayBuffer; uint mOffset{0}; @@ -58,16 +65,17 @@ struct ChorusState final : public EffectState { uint mLfoDisp{0}; /* Calculated delays to apply to the left and right outputs. */ - uint mModDelays[2][BufferLineSize]; + std::array<std::array<uint,BufferLineSize>,2> mModDelays{}; /* Temp storage for the modulated left and right outputs. */ - alignas(16) float mBuffer[2][BufferLineSize]; + alignas(16) std::array<FloatBufferLine,2> mBuffer{}; /* Gains for left and right outputs. */ - struct { - float Current[MaxAmbiChannels]{}; - float Target[MaxAmbiChannels]{}; - } mGains[2]; + struct OutGains { + std::array<float,MaxAmbiChannels> Current{}; + std::array<float,MaxAmbiChannels> Target{}; + }; + std::array<OutGains,2> mGains; /* effect parameters */ ChorusWaveform mWaveform{}; @@ -78,65 +86,81 @@ struct ChorusState final : public EffectState { void calcTriangleDelays(const size_t todo); void calcSinusoidDelays(const size_t todo); - void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; - void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, - const EffectTarget target) override; + void deviceUpdate(const DeviceBase *device, const float MaxDelay); + void update(const ContextBase *context, const EffectSlot *slot, const ChorusWaveform waveform, + const float delay, const float depth, const float feedback, const float rate, + int phase, const EffectTarget target); + + void deviceUpdate(const DeviceBase *device, const BufferStorage*) override + { deviceUpdate(device, ChorusMaxDelay); } + void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props_, + const EffectTarget target) override + { + auto &props = std::get<ChorusProps>(*props_); + update(context, slot, props.Waveform, props.Delay, props.Depth, props.Feedback, props.Rate, + props.Phase, target); + } void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, - const al::span<FloatBufferLine> samplesOut) override; + const al::span<FloatBufferLine> samplesOut) final; +}; - DEF_NEWDEL(ChorusState) +struct FlangerState final : public ChorusState { + void deviceUpdate(const DeviceBase *device, const BufferStorage*) final + { ChorusState::deviceUpdate(device, FlangerMaxDelay); } + void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props_, + const EffectTarget target) final + { + auto &props = std::get<FlangerProps>(*props_); + ChorusState::update(context, slot, props.Waveform, props.Delay, props.Depth, + props.Feedback, props.Rate, props.Phase, target); + } }; -void ChorusState::deviceUpdate(const DeviceBase *Device, const BufferStorage*) -{ - constexpr float max_delay{maxf(ChorusMaxDelay, FlangerMaxDelay)}; +void ChorusState::deviceUpdate(const DeviceBase *Device, const float MaxDelay) +{ const auto frequency = static_cast<float>(Device->Frequency); - const size_t maxlen{NextPowerOf2(float2uint(max_delay*2.0f*frequency) + 1u)}; + const size_t maxlen{NextPowerOf2(float2uint(MaxDelay*2.0f*frequency) + 1u)}; if(maxlen != mDelayBuffer.size()) decltype(mDelayBuffer)(maxlen).swap(mDelayBuffer); std::fill(mDelayBuffer.begin(), mDelayBuffer.end(), 0.0f); for(auto &e : mGains) { - std::fill(std::begin(e.Current), std::end(e.Current), 0.0f); - std::fill(std::begin(e.Target), std::end(e.Target), 0.0f); + e.Current.fill(0.0f); + e.Target.fill(0.0f); } } -void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, - const EffectProps *props, const EffectTarget target) +void ChorusState::update(const ContextBase *context, const EffectSlot *slot, + const ChorusWaveform waveform, const float delay, const float depth, const float feedback, + const float rate, int phase, const EffectTarget target) { - constexpr int mindelay{(MaxResamplerPadding>>1) << MixerFracBits}; + static constexpr int mindelay{(MaxResamplerPadding>>1) << MixerFracBits}; /* The LFO depth is scaled to be relative to the sample delay. Clamp the * delay and depth to allow enough padding for resampling. */ - const DeviceBase *device{Context->mDevice}; + const DeviceBase *device{context->mDevice}; const auto frequency = static_cast<float>(device->Frequency); - mWaveform = props->Chorus.Waveform; + mWaveform = waveform; - mDelay = maxi(float2int(props->Chorus.Delay*frequency*MixerFracOne + 0.5f), mindelay); - mDepth = minf(props->Chorus.Depth * static_cast<float>(mDelay), + mDelay = maxi(float2int(delay*frequency*MixerFracOne + 0.5f), mindelay); + mDepth = minf(depth * static_cast<float>(mDelay), static_cast<float>(mDelay - mindelay)); - mFeedback = props->Chorus.Feedback; + mFeedback = feedback; /* Gains for left and right sides */ - static constexpr auto inv_sqrt2 = static_cast<float>(1.0 / al::numbers::sqrt2); - static constexpr auto lcoeffs_pw = CalcDirectionCoeffs(std::array{-1.0f, 0.0f, 0.0f}); - static constexpr auto rcoeffs_pw = CalcDirectionCoeffs(std::array{ 1.0f, 0.0f, 0.0f}); - static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs(std::array{-inv_sqrt2, 0.0f, inv_sqrt2}); - static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs(std::array{ inv_sqrt2, 0.0f, inv_sqrt2}); - auto &lcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? lcoeffs_nrml : lcoeffs_pw; - auto &rcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? rcoeffs_nrml : rcoeffs_pw; + const bool ispairwise{device->mRenderMode == RenderMode::Pairwise}; + const auto lcoeffs = (!ispairwise) ? al::span{lcoeffs_nrml} : al::span{lcoeffs_pw}; + const auto rcoeffs = (!ispairwise) ? al::span{rcoeffs_nrml} : al::span{rcoeffs_pw}; mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, lcoeffs, Slot->Gain, mGains[0].Target); - ComputePanGains(target.Main, rcoeffs, Slot->Gain, mGains[1].Target); + ComputePanGains(target.Main, lcoeffs, slot->Gain, mGains[0].Target); + ComputePanGains(target.Main, rcoeffs, slot->Gain, mGains[1].Target); - float rate{props->Chorus.Rate}; if(!(rate > 0.0f)) { mLfoOffset = 0; @@ -149,7 +173,7 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, /* Calculate LFO coefficient (number of samples per cycle). Limit the * max range to avoid overflow when calculating the displacement. */ - uint lfo_range{float2uint(minf(frequency/rate + 0.5f, float{INT_MAX/360 - 180}))}; + const uint lfo_range{float2uint(minf(frequency/rate + 0.5f, float{INT_MAX/360 - 180}))}; mLfoOffset = mLfoOffset * lfo_range / mLfoRange; mLfoRange = lfo_range; @@ -164,7 +188,6 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, } /* Calculate lfo phase displacement */ - int phase{props->Chorus.Phase}; if(phase < 0) phase = 360 + phase; mLfoDisp = (mLfoRange*static_cast<uint>(phase) + 180) / 360; } @@ -266,10 +289,10 @@ void ChorusState::process(const size_t samplesToDo, const al::span<const FloatBu else /*if(mWaveform == ChorusWaveform::Triangle)*/ calcTriangleDelays(samplesToDo); - const uint *RESTRICT ldelays{mModDelays[0]}; - const uint *RESTRICT rdelays{mModDelays[1]}; - float *RESTRICT lbuffer{al::assume_aligned<16>(mBuffer[0])}; - float *RESTRICT rbuffer{al::assume_aligned<16>(mBuffer[1])}; + const uint *RESTRICT ldelays{mModDelays[0].data()}; + const uint *RESTRICT rdelays{mModDelays[1].data()}; + float *RESTRICT lbuffer{al::assume_aligned<16>(mBuffer[0].data())}; + float *RESTRICT rbuffer{al::assume_aligned<16>(mBuffer[1].data())}; for(size_t i{0u};i < samplesToDo;++i) { // Feed the buffer's input first (necessary for delays < 1). @@ -292,10 +315,10 @@ void ChorusState::process(const size_t samplesToDo, const al::span<const FloatBu ++offset; } - MixSamples({lbuffer, samplesToDo}, samplesOut, mGains[0].Current, mGains[0].Target, - samplesToDo, 0); - MixSamples({rbuffer, samplesToDo}, samplesOut, mGains[1].Current, mGains[1].Target, - samplesToDo, 0); + MixSamples({lbuffer, samplesToDo}, samplesOut, mGains[0].Current.data(), + mGains[0].Target.data(), samplesToDo, 0); + MixSamples({rbuffer, samplesToDo}, samplesOut, mGains[1].Current.data(), + mGains[1].Target.data(), samplesToDo, 0); mOffset = offset; } @@ -312,7 +335,7 @@ struct ChorusStateFactory final : public EffectStateFactory { */ struct FlangerStateFactory final : public EffectStateFactory { al::intrusive_ptr<EffectState> create() override - { return al::intrusive_ptr<EffectState>{new ChorusState{}}; } + { return al::intrusive_ptr<EffectState>{new FlangerState{}}; } }; } // namespace diff --git a/alc/effects/compressor.cpp b/alc/effects/compressor.cpp index 0a7ed67a..717b6dd2 100644 --- a/alc/effects/compressor.cpp +++ b/alc/effects/compressor.cpp @@ -64,10 +64,11 @@ namespace { struct CompressorState final : public EffectState { /* Effect gains for each channel */ - struct { + struct TargetGain { uint mTarget{InvalidChannelIndex}; float mGain{1.0f}; - } mChans[MaxAmbiChannels]; + }; + std::array<TargetGain,MaxAmbiChannels> mChans; /* Effect parameters */ bool mEnabled{true}; @@ -81,8 +82,6 @@ struct CompressorState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - - DEF_NEWDEL(CompressorState) }; void CompressorState::deviceUpdate(const DeviceBase *device, const BufferStorage*) @@ -103,7 +102,7 @@ void CompressorState::deviceUpdate(const DeviceBase *device, const BufferStorage void CompressorState::update(const ContextBase*, const EffectSlot *slot, const EffectProps *props, const EffectTarget target) { - mEnabled = props->Compressor.OnOff; + mEnabled = std::get<CompressorProps>(*props).OnOff; mOutTarget = target.Main->Buffer; auto set_channel = [this](size_t idx, uint outchan, float outgain) @@ -119,8 +118,8 @@ void CompressorState::process(const size_t samplesToDo, { for(size_t base{0u};base < samplesToDo;) { - float gains[256]; - const size_t td{minz(256, samplesToDo-base)}; + std::array<float,256> gains; + const size_t td{minz(gains.size(), samplesToDo-base)}; /* Generate the per-sample gains from the signal envelope. */ float env{mEnvFollower}; diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 517e6b08..3f3e157c 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -5,11 +5,12 @@ #include <array> #include <complex> #include <cstddef> +#include <cstdint> #include <functional> #include <iterator> #include <memory> -#include <stdint.h> #include <utility> +#include <vector> #ifdef HAVE_SSE_INTRINSICS #include <xmmintrin.h> @@ -190,12 +191,6 @@ void apply_fir(al::span<float> dst, const float *RESTRICT src, const float *REST } -struct PFFFTSetupDeleter { - void operator()(PFFFT_Setup *ptr) { pffft_destroy_setup(ptr); } -}; -using PFFFTSetupPtr = std::unique_ptr<PFFFT_Setup,PFFFTSetupDeleter>; - - struct ConvolutionState final : public EffectState { FmtChannels mChannels{}; AmbiLayout mAmbiLayout{}; @@ -207,7 +202,7 @@ struct ConvolutionState final : public EffectState { al::vector<std::array<float,ConvolveUpdateSamples>,16> mFilter; al::vector<std::array<float,ConvolveUpdateSamples*2>,16> mOutput; - PFFFTSetupPtr mFft{}; + PFFFTSetup mFft{}; alignas(16) std::array<float,ConvolveUpdateSize> mFftBuffer{}; alignas(16) std::array<float,ConvolveUpdateSize> mFftWorkBuffer{}; @@ -218,8 +213,8 @@ struct ConvolutionState final : public EffectState { alignas(16) FloatBufferLine mBuffer{}; float mHfScale{}, mLfScale{}; BandSplitter mFilter{}; - float Current[MAX_OUTPUT_CHANNELS]{}; - float Target[MAX_OUTPUT_CHANNELS]{}; + std::array<float,MaxOutputChannels> Current{}; + std::array<float,MaxOutputChannels> Target{}; }; std::vector<ChannelData> mChans; al::vector<float,16> mComplexData; @@ -238,16 +233,14 @@ struct ConvolutionState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - - DEF_NEWDEL(ConvolutionState) }; void ConvolutionState::NormalMix(const al::span<FloatBufferLine> samplesOut, const size_t samplesToDo) { for(auto &chan : mChans) - MixSamples({chan.mBuffer.data(), samplesToDo}, samplesOut, chan.Current, chan.Target, - samplesToDo, 0); + MixSamples({chan.mBuffer.data(), samplesToDo}, samplesOut, chan.Current.data(), + chan.Target.data(), samplesToDo, 0); } void ConvolutionState::UpsampleMix(const al::span<FloatBufferLine> samplesOut, @@ -257,7 +250,7 @@ void ConvolutionState::UpsampleMix(const al::span<FloatBufferLine> samplesOut, { const al::span<float> src{chan.mBuffer.data(), samplesToDo}; chan.mFilter.processScale(src, chan.mHfScale, chan.mLfScale); - MixSamples(src, samplesOut, chan.Current, chan.Target, samplesToDo, 0); + MixSamples(src, samplesOut, chan.Current.data(), chan.Target.data(), samplesToDo, 0); } } @@ -270,7 +263,7 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag static constexpr uint MaxConvolveAmbiOrder{1u}; if(!mFft) - mFft = PFFFTSetupPtr{pffft_new_setup(ConvolveUpdateSize, PFFFT_REAL)}; + mFft = PFFFTSetup{ConvolveUpdateSize, PFFFT_REAL}; mFifoPos = 0; mInput.fill(0.0f); @@ -331,10 +324,10 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag /* Load the samples from the buffer. */ const size_t srclinelength{RoundUp(buffer->mSampleLen+DecoderPadding, 16)}; - auto srcsamples = std::make_unique<float[]>(srclinelength * numChannels); - std::fill_n(srcsamples.get(), srclinelength * numChannels, 0.0f); + auto srcsamples = std::vector<float>(srclinelength * numChannels); + std::fill(srcsamples.begin(), srcsamples.end(), 0.0f); for(size_t c{0};c < numChannels && c < realChannels;++c) - LoadSamples(srcsamples.get() + srclinelength*c, buffer->mData.data() + bytesPerSample*c, + LoadSamples(srcsamples.data() + srclinelength*c, buffer->mData.data() + bytesPerSample*c, realChannels, buffer->mType, buffer->mSampleLen); if(IsUHJ(mChannels)) @@ -342,12 +335,11 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag auto decoder = std::make_unique<UhjDecoderType>(); std::array<float*,4> samples{}; for(size_t c{0};c < numChannels;++c) - samples[c] = srcsamples.get() + srclinelength*c; + samples[c] = srcsamples.data() + srclinelength*c; decoder->decode({samples.data(), numChannels}, buffer->mSampleLen, buffer->mSampleLen); } - auto ressamples = std::make_unique<double[]>(buffer->mSampleLen + - (resampler ? resampledCount : 0)); + auto ressamples = std::vector<double>(buffer->mSampleLen + (resampler ? resampledCount : 0)); auto ffttmp = al::vector<float,16>(ConvolveUpdateSize); auto fftbuffer = std::vector<std::complex<double>>(ConvolveUpdateSize); @@ -357,19 +349,20 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag /* Resample to match the device. */ if(resampler) { - std::copy_n(srcsamples.get() + srclinelength*c, buffer->mSampleLen, - ressamples.get() + resampledCount); - resampler.process(buffer->mSampleLen, ressamples.get()+resampledCount, - resampledCount, ressamples.get()); + std::copy_n(srcsamples.data() + srclinelength*c, buffer->mSampleLen, + ressamples.data() + resampledCount); + resampler.process(buffer->mSampleLen, ressamples.data()+resampledCount, + resampledCount, ressamples.data()); } else - std::copy_n(srcsamples.get() + srclinelength*c, buffer->mSampleLen, ressamples.get()); + std::copy_n(srcsamples.data() + srclinelength*c, buffer->mSampleLen, + ressamples.data()); /* Store the first segment's samples in reverse in the time-domain, to * apply as a FIR filter. */ const size_t first_size{minz(resampledCount, ConvolveUpdateSamples)}; - std::transform(ressamples.get(), ressamples.get()+first_size, mFilter[c].rbegin(), + std::transform(ressamples.data(), ressamples.data()+first_size, mFilter[c].rbegin(), [](const double d) noexcept -> float { return static_cast<float>(d); }); size_t done{first_size}; @@ -400,7 +393,7 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag /* Reorder backward to make it suitable for pffft_zconvolve and the * subsequent pffft_transform(..., PFFFT_BACKWARD). */ - pffft_zreorder(mFft.get(), ffttmp.data(), al::to_address(filteriter), PFFFT_BACKWARD); + mFft.zreorder(ffttmp.data(), al::to_address(filteriter), PFFFT_BACKWARD); filteriter += ConvolveUpdateSize; } } @@ -408,54 +401,61 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { /* TODO: LFE is not mixed to output. This will require each buffer channel * to have its own output target since the main mixing buffer won't have an * LFE channel (due to being B-Format). */ - static constexpr ChanPosMap MonoMap[1]{ - { FrontCenter, std::array{0.0f, 0.0f, -1.0f} } - }, StereoMap[2]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - }, RearMap[2]{ - { BackLeft, std::array{-sin30, 0.0f, cos30} }, - { BackRight, std::array{ sin30, 0.0f, cos30} }, - }, QuadMap[4]{ - { FrontLeft, std::array{-sin45, 0.0f, -cos45} }, - { FrontRight, std::array{ sin45, 0.0f, -cos45} }, - { BackLeft, std::array{-sin45, 0.0f, cos45} }, - { BackRight, std::array{ sin45, 0.0f, cos45} }, - }, X51Map[6]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { SideLeft, std::array{-sin110, 0.0f, -cos110} }, - { SideRight, std::array{ sin110, 0.0f, -cos110} }, - }, X61Map[7]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { BackCenter, std::array{ 0.0f, 0.0f, 1.0f} }, - { SideLeft, std::array{-1.0f, 0.0f, 0.0f} }, - { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, - }, X71Map[8]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { BackLeft, std::array{-sin30, 0.0f, cos30} }, - { BackRight, std::array{ sin30, 0.0f, cos30} }, - { SideLeft, std::array{ -1.0f, 0.0f, 0.0f} }, - { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, + static constexpr std::array MonoMap{ + ChanPosMap{FrontCenter, std::array{0.0f, 0.0f, -1.0f}} + }; + static constexpr std::array StereoMap{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + }; + static constexpr std::array RearMap{ + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + }; + static constexpr std::array QuadMap{ + ChanPosMap{FrontLeft, std::array{-sin45, 0.0f, -cos45}}, + ChanPosMap{FrontRight, std::array{ sin45, 0.0f, -cos45}}, + ChanPosMap{BackLeft, std::array{-sin45, 0.0f, cos45}}, + ChanPosMap{BackRight, std::array{ sin45, 0.0f, cos45}}, + }; + static constexpr std::array X51Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{SideLeft, std::array{-sin110, 0.0f, -cos110}}, + ChanPosMap{SideRight, std::array{ sin110, 0.0f, -cos110}}, + }; + static constexpr std::array X61Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackCenter, std::array{ 0.0f, 0.0f, 1.0f} }, + ChanPosMap{SideLeft, std::array{-1.0f, 0.0f, 0.0f} }, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, + }; + static constexpr std::array X71Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + ChanPosMap{SideLeft, std::array{ -1.0f, 0.0f, 0.0f}}, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f}}, }; if(mNumConvolveSegs < 1) UNLIKELY return; + auto &props = std::get<ConvolutionProps>(*props_); mMix = &ConvolutionState::NormalMix; for(auto &chan : mChans) @@ -489,21 +489,19 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot } mOutTarget = target.Main->Buffer; - alu::Vector N{props->Convolution.OrientAt[0], props->Convolution.OrientAt[1], - props->Convolution.OrientAt[2], 0.0f}; + alu::Vector N{props.OrientAt[0], props.OrientAt[1], props.OrientAt[2], 0.0f}; N.normalize(); - alu::Vector V{props->Convolution.OrientUp[0], props->Convolution.OrientUp[1], - props->Convolution.OrientUp[2], 0.0f}; + alu::Vector V{props.OrientUp[0], props.OrientUp[1], props.OrientUp[2], 0.0f}; V.normalize(); /* Build and normalize right-vector */ alu::Vector U{N.cross_product(V)}; U.normalize(); - const float mixmatrix[4][4]{ - {1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, U[0], -U[1], U[2]}, - {0.0f, -V[0], V[1], -V[2]}, - {0.0f, -N[0], N[1], -N[2]}, + const std::array mixmatrix{ + std::array{1.0f, 0.0f, 0.0f, 0.0f}, + std::array{0.0f, U[0], -U[1], U[2]}, + std::array{0.0f, -V[0], V[1], -V[2]}, + std::array{0.0f, -N[0], N[1], -N[2]}, }; const auto scales = GetAmbiScales(mAmbiScaling); @@ -642,7 +640,7 @@ void ConvolutionState::process(const size_t samplesToDo, /* Calculate the frequency-domain response and add the relevant * frequency bins to the FFT history. */ - pffft_transform(mFft.get(), mInput.data(), mComplexData.data() + curseg*ConvolveUpdateSize, + mFft.transform(mInput.data(), mComplexData.data() + curseg*ConvolveUpdateSize, mFftWorkBuffer.data(), PFFFT_FORWARD); const float *filter{mComplexData.data() + mNumConvolveSegs*ConvolveUpdateSize}; @@ -655,14 +653,14 @@ void ConvolutionState::process(const size_t samplesToDo, const float *input{&mComplexData[curseg*ConvolveUpdateSize]}; for(size_t s{curseg};s < mNumConvolveSegs;++s) { - pffft_zconvolve_accumulate(mFft.get(), input, filter, mFftBuffer.data()); + mFft.zconvolve_accumulate(input, filter, mFftBuffer.data()); input += ConvolveUpdateSize; filter += ConvolveUpdateSize; } input = mComplexData.data(); for(size_t s{0};s < curseg;++s) { - pffft_zconvolve_accumulate(mFft.get(), input, filter, mFftBuffer.data()); + mFft.zconvolve_accumulate(input, filter, mFftBuffer.data()); input += ConvolveUpdateSize; filter += ConvolveUpdateSize; } @@ -672,8 +670,8 @@ void ConvolutionState::process(const size_t samplesToDo, * second-half samples (and this output's second half is * subsequently saved for next time). */ - pffft_transform(mFft.get(), mFftBuffer.data(), mFftBuffer.data(), - mFftWorkBuffer.data(), PFFFT_BACKWARD); + mFft.transform(mFftBuffer.data(), mFftBuffer.data(), mFftWorkBuffer.data(), + PFFFT_BACKWARD); /* The filter was attenuated, so the response is already scaled. */ for(size_t i{0};i < ConvolveUpdateSamples;++i) diff --git a/alc/effects/dedicated.cpp b/alc/effects/dedicated.cpp index a9131bfa..23ac4d1a 100644 --- a/alc/effects/dedicated.cpp +++ b/alc/effects/dedicated.cpp @@ -42,82 +42,100 @@ namespace { using uint = unsigned int; -struct DedicatedState final : public EffectState { +struct DedicatedState : public EffectState { /* The "dedicated" effect can output to the real output, so should have * gains for all possible output channels and not just the main ambisonic * buffer. */ - float mCurrentGains[MAX_OUTPUT_CHANNELS]; - float mTargetGains[MAX_OUTPUT_CHANNELS]; + std::array<float,MaxOutputChannels> mCurrentGains{}; + std::array<float,MaxOutputChannels> mTargetGains{}; - void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; + void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) final; void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, - const al::span<FloatBufferLine> samplesOut) override; + const al::span<FloatBufferLine> samplesOut) final; +}; - DEF_NEWDEL(DedicatedState) +struct DedicatedLfeState final : public DedicatedState { + void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) final; }; void DedicatedState::deviceUpdate(const DeviceBase*, const BufferStorage*) { - std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f); + std::fill(mCurrentGains.begin(), mCurrentGains.end(), 0.0f); } void DedicatedState::update(const ContextBase*, const EffectSlot *slot, const EffectProps *props, const EffectTarget target) { - std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f); + std::fill(mTargetGains.begin(), mTargetGains.end(), 0.0f); - const float Gain{slot->Gain * props->Dedicated.Gain}; + const float Gain{slot->Gain * std::get<DedicatedDialogProps>(*props).Gain}; - if(slot->EffectType == EffectSlotType::DedicatedLFE) + /* Dialog goes to the front-center speaker if it exists, otherwise it plays + * from the front-center location. + */ + const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[FrontCenter] + : InvalidChannelIndex}; + if(idx != InvalidChannelIndex) { - const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[LFE] : InvalidChannelIndex}; - if(idx != InvalidChannelIndex) - { - mOutTarget = target.RealOut->Buffer; - mTargetGains[idx] = Gain; - } + mOutTarget = target.RealOut->Buffer; + mTargetGains[idx] = Gain; } - else if(slot->EffectType == EffectSlotType::DedicatedDialog) + else { - /* Dialog goes to the front-center speaker if it exists, otherwise it - * plays from the front-center location. */ - const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[FrontCenter] - : InvalidChannelIndex}; - if(idx != InvalidChannelIndex) - { - mOutTarget = target.RealOut->Buffer; - mTargetGains[idx] = Gain; - } - else - { - static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f}); - - mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs, Gain, mTargetGains); - } + static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f}); + + mOutTarget = target.Main->Buffer; + ComputePanGains(target.Main, coeffs, Gain, mTargetGains); + } +} + +void DedicatedLfeState::update(const ContextBase*, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) +{ + std::fill(mTargetGains.begin(), mTargetGains.end(), 0.0f); + + const float Gain{slot->Gain * std::get<DedicatedLfeProps>(*props).Gain}; + + const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[LFE] : InvalidChannelIndex}; + if(idx != InvalidChannelIndex) + { + mOutTarget = target.RealOut->Buffer; + mTargetGains[idx] = Gain; } } void DedicatedState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) { - MixSamples({samplesIn[0].data(), samplesToDo}, samplesOut, mCurrentGains, mTargetGains, - samplesToDo, 0); + MixSamples({samplesIn[0].data(), samplesToDo}, samplesOut, mCurrentGains.data(), + mTargetGains.data(), samplesToDo, 0); } -struct DedicatedStateFactory final : public EffectStateFactory { +struct DedicatedDialogStateFactory final : public EffectStateFactory { al::intrusive_ptr<EffectState> create() override { return al::intrusive_ptr<EffectState>{new DedicatedState{}}; } }; +struct DedicatedLfeStateFactory final : public EffectStateFactory { + al::intrusive_ptr<EffectState> create() override + { return al::intrusive_ptr<EffectState>{new DedicatedLfeState{}}; } +}; + } // namespace -EffectStateFactory *DedicatedStateFactory_getFactory() +EffectStateFactory *DedicatedDialogStateFactory_getFactory() +{ + static DedicatedDialogStateFactory DedicatedFactory{}; + return &DedicatedFactory; +} + +EffectStateFactory *DedicatedLfeStateFactory_getFactory() { - static DedicatedStateFactory DedicatedFactory{}; + static DedicatedLfeStateFactory DedicatedFactory{}; return &DedicatedFactory; } diff --git a/alc/effects/distortion.cpp b/alc/effects/distortion.cpp index 3d77ff35..d0946971 100644 --- a/alc/effects/distortion.cpp +++ b/alc/effects/distortion.cpp @@ -45,7 +45,7 @@ namespace { struct DistortionState final : public EffectState { /* Effect gains for each channel */ - float mGain[MaxAmbiChannels]{}; + std::array<float,MaxAmbiChannels> mGain{}; /* Effect parameters */ BiquadFilter mLowpass; @@ -53,7 +53,7 @@ struct DistortionState final : public EffectState { float mAttenuation{}; float mEdgeCoeff{}; - alignas(16) float mBuffer[2][BufferLineSize]{}; + alignas(16) std::array<FloatBufferLine,2> mBuffer{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -61,8 +61,6 @@ struct DistortionState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - - DEF_NEWDEL(DistortionState) }; void DistortionState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -72,16 +70,17 @@ void DistortionState::deviceUpdate(const DeviceBase*, const BufferStorage*) } void DistortionState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get<DistortionProps>(*props_); const DeviceBase *device{context->mDevice}; /* Store waveshaper edge settings. */ - const float edge{minf(std::sin(al::numbers::pi_v<float>*0.5f * props->Distortion.Edge), + const float edge{minf(std::sin(al::numbers::pi_v<float>*0.5f * props.Edge), 0.99f)}; mEdgeCoeff = 2.0f * edge / (1.0f-edge); - float cutoff{props->Distortion.LowpassCutoff}; + float cutoff{props.LowpassCutoff}; /* Bandwidth value is constant in octaves. */ float bandwidth{(cutoff / 2.0f) / (cutoff * 0.67f)}; /* Divide normalized frequency by the amount of oversampling done during @@ -90,15 +89,15 @@ void DistortionState::update(const ContextBase *context, const EffectSlot *slot, auto frequency = static_cast<float>(device->Frequency); mLowpass.setParamsFromBandwidth(BiquadType::LowPass, cutoff/frequency/4.0f, 1.0f, bandwidth); - cutoff = props->Distortion.EQCenter; + cutoff = props.EQCenter; /* Convert bandwidth in Hz to octaves. */ - bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f); + bandwidth = props.EQBandwidth / (cutoff * 0.67f); mBandpass.setParamsFromBandwidth(BiquadType::BandPass, cutoff/frequency/4.0f, 1.0f, bandwidth); static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f}); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs, slot->Gain*props->Distortion.Gain, mGain); + ComputePanGains(target.Main, coeffs, slot->Gain*props.Gain, mGain); } void DistortionState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) @@ -124,7 +123,7 @@ void DistortionState::process(const size_t samplesToDo, const al::span<const Flo * (which is fortunately first step of distortion). So combine three * operations into the one. */ - mLowpass.process({mBuffer[0], todo}, mBuffer[1]); + mLowpass.process({mBuffer[0].data(), todo}, mBuffer[1].data()); /* Second step, do distortion using waveshaper function to emulate * signal processing during tube overdriving. Three steps of @@ -138,15 +137,15 @@ void DistortionState::process(const size_t samplesToDo, const al::span<const Flo smp = (1.0f + fc) * smp/(1.0f + fc*std::abs(smp)); return smp; }; - std::transform(std::begin(mBuffer[1]), std::begin(mBuffer[1])+todo, std::begin(mBuffer[0]), + std::transform(mBuffer[1].begin(), mBuffer[1].begin()+todo, mBuffer[0].begin(), proc_sample); /* Third step, do bandpass filtering of distorted signal. */ - mBandpass.process({mBuffer[0], todo}, mBuffer[1]); + mBandpass.process({mBuffer[0].data(), todo}, mBuffer[1].data()); todo >>= 2; - const float *outgains{mGain}; - for(FloatBufferLine &output : samplesOut) + const float *outgains{mGain.data()}; + for(FloatBufferLine &RESTRICT output : samplesOut) { /* Fourth step, final, do attenuation and perform decimation, * storing only one sample out of four. diff --git a/alc/effects/echo.cpp b/alc/effects/echo.cpp index 714649c9..a5bfa6a5 100644 --- a/alc/effects/echo.cpp +++ b/alc/effects/echo.cpp @@ -53,29 +53,26 @@ struct EchoState final : public EffectState { // The echo is two tap. The delay is the number of samples from before the // current offset - struct { - size_t delay{0u}; - } mTap[2]; + std::array<size_t,2> mDelayTap{}; size_t mOffset{0u}; /* The panning gains for the two taps */ - struct { - float Current[MaxAmbiChannels]{}; - float Target[MaxAmbiChannels]{}; - } mGains[2]; + struct OutGains { + std::array<float,MaxAmbiChannels> Current{}; + std::array<float,MaxAmbiChannels> Target{}; + }; + std::array<OutGains,2> mGains; BiquadFilter mFilter; float mFeedGain{0.0f}; - alignas(16) float mTempBuffer[2][BufferLineSize]; + alignas(16) std::array<FloatBufferLine,2> mTempBuffer{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - - DEF_NEWDEL(EchoState) }; void EchoState::deviceUpdate(const DeviceBase *Device, const BufferStorage*) @@ -92,27 +89,28 @@ void EchoState::deviceUpdate(const DeviceBase *Device, const BufferStorage*) std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f); for(auto &e : mGains) { - std::fill(std::begin(e.Current), std::end(e.Current), 0.0f); - std::fill(std::begin(e.Target), std::end(e.Target), 0.0f); + std::fill(e.Current.begin(), e.Current.end(), 0.0f); + std::fill(e.Target.begin(), e.Target.end(), 0.0f); } } void EchoState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get<EchoProps>(*props_); const DeviceBase *device{context->mDevice}; const auto frequency = static_cast<float>(device->Frequency); - mTap[0].delay = maxu(float2uint(props->Echo.Delay*frequency + 0.5f), 1); - mTap[1].delay = float2uint(props->Echo.LRDelay*frequency + 0.5f) + mTap[0].delay; + mDelayTap[0] = maxu(float2uint(props.Delay*frequency + 0.5f), 1); + mDelayTap[1] = float2uint(props.LRDelay*frequency + 0.5f) + mDelayTap[0]; - const float gainhf{maxf(1.0f - props->Echo.Damping, 0.0625f)}; /* Limit -24dB */ + const float gainhf{maxf(1.0f - props.Damping, 0.0625f)}; /* Limit -24dB */ mFilter.setParamsFromSlope(BiquadType::HighShelf, LowpassFreqRef/frequency, gainhf, 1.0f); - mFeedGain = props->Echo.Feedback; + mFeedGain = props.Feedback; /* Convert echo spread (where 0 = center, +/-1 = sides) to angle. */ - const float angle{std::asin(props->Echo.Spread)}; + const float angle{std::asin(props.Spread)}; const auto coeffs0 = CalcAngleCoeffs(-angle, 0.0f, 0.0f); const auto coeffs1 = CalcAngleCoeffs( angle, 0.0f, 0.0f); @@ -127,14 +125,13 @@ void EchoState::process(const size_t samplesToDo, const al::span<const FloatBuff const size_t mask{mSampleBuffer.size()-1}; float *RESTRICT delaybuf{mSampleBuffer.data()}; size_t offset{mOffset}; - size_t tap1{offset - mTap[0].delay}; - size_t tap2{offset - mTap[1].delay}; - float z1, z2; + size_t tap1{offset - mDelayTap[0]}; + size_t tap2{offset - mDelayTap[1]}; ASSUME(samplesToDo > 0); const BiquadFilter filter{mFilter}; - std::tie(z1, z2) = mFilter.getComponents(); + auto [z1, z2] = mFilter.getComponents(); for(size_t i{0u};i < samplesToDo;) { offset &= mask; @@ -161,8 +158,8 @@ void EchoState::process(const size_t samplesToDo, const al::span<const FloatBuff mOffset = offset; for(size_t c{0};c < 2;c++) - MixSamples({mTempBuffer[c], samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target, - samplesToDo, 0); + MixSamples({mTempBuffer[c].data(), samplesToDo}, samplesOut, mGains[c].Current.data(), + mGains[c].Target.data(), samplesToDo, 0); } diff --git a/alc/effects/equalizer.cpp b/alc/effects/equalizer.cpp index 50bec4ad..165d00f2 100644 --- a/alc/effects/equalizer.cpp +++ b/alc/effects/equalizer.cpp @@ -86,16 +86,17 @@ namespace { struct EqualizerState final : public EffectState { - struct { + struct OutParams { uint mTargetChannel{InvalidChannelIndex}; /* Effect parameters */ - BiquadFilter mFilter[4]; + std::array<BiquadFilter,4> mFilter; /* Effect gains for each channel */ float mCurrentGain{}; float mTargetGain{}; - } mChans[MaxAmbiChannels]; + }; + std::array<OutParams,MaxAmbiChannels> mChans; alignas(16) FloatBufferLine mSampleBuffer{}; @@ -105,8 +106,6 @@ struct EqualizerState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - - DEF_NEWDEL(EqualizerState) }; void EqualizerState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -114,18 +113,17 @@ void EqualizerState::deviceUpdate(const DeviceBase*, const BufferStorage*) for(auto &e : mChans) { e.mTargetChannel = InvalidChannelIndex; - std::for_each(std::begin(e.mFilter), std::end(e.mFilter), - std::mem_fn(&BiquadFilter::clear)); + std::for_each(e.mFilter.begin(), e.mFilter.end(), std::mem_fn(&BiquadFilter::clear)); e.mCurrentGain = 0.0f; } } void EqualizerState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get<EqualizerProps>(*props_); const DeviceBase *device{context->mDevice}; auto frequency = static_cast<float>(device->Frequency); - float gain, f0norm; /* Calculate coefficients for the each type of filter. Note that the shelf * and peaking filters' gain is for the centerpoint of the transition band, @@ -133,22 +131,22 @@ void EqualizerState::update(const ContextBase *context, const EffectSlot *slot, * property gains need their dB halved (sqrt of linear gain) for the * shelf/peak to reach the provided gain. */ - gain = std::sqrt(props->Equalizer.LowGain); - f0norm = props->Equalizer.LowCutoff / frequency; + float gain{std::sqrt(props.LowGain)}; + float f0norm{props.LowCutoff / frequency}; mChans[0].mFilter[0].setParamsFromSlope(BiquadType::LowShelf, f0norm, gain, 0.75f); - gain = std::sqrt(props->Equalizer.Mid1Gain); - f0norm = props->Equalizer.Mid1Center / frequency; + gain = std::sqrt(props.Mid1Gain); + f0norm = props.Mid1Center / frequency; mChans[0].mFilter[1].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain, - props->Equalizer.Mid1Width); + props.Mid1Width); - gain = std::sqrt(props->Equalizer.Mid2Gain); - f0norm = props->Equalizer.Mid2Center / frequency; + gain = std::sqrt(props.Mid2Gain); + f0norm = props.Mid2Center / frequency; mChans[0].mFilter[2].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain, - props->Equalizer.Mid2Width); + props.Mid2Width); - gain = std::sqrt(props->Equalizer.HighGain); - f0norm = props->Equalizer.HighCutoff / frequency; + gain = std::sqrt(props.HighGain); + f0norm = props.HighCutoff / frequency; mChans[0].mFilter[3].setParamsFromSlope(BiquadType::HighShelf, f0norm, gain, 0.75f); /* Copy the filter coefficients for the other input channels. */ diff --git a/alc/effects/fshifter.cpp b/alc/effects/fshifter.cpp index d3989e84..076661cf 100644 --- a/alc/effects/fshifter.cpp +++ b/alc/effects/fshifter.cpp @@ -57,7 +57,7 @@ constexpr size_t HilStep{HilSize / OversampleFactor}; /* Define a Hann window, used to filter the HIL input and output. */ struct Windower { - alignas(16) std::array<double,HilSize> mData; + alignas(16) std::array<double,HilSize> mData{}; Windower() { @@ -91,10 +91,11 @@ struct FshifterState final : public EffectState { alignas(16) FloatBufferLine mBufferOut{}; /* Effect gains for each output channel */ - struct { - float Current[MaxAmbiChannels]{}; - float Target[MaxAmbiChannels]{}; - } mGains[2]; + struct OutGains { + std::array<float,MaxAmbiChannels> Current{}; + std::array<float,MaxAmbiChannels> Target{}; + }; + std::array<OutGains,2> mGains; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -102,8 +103,6 @@ struct FshifterState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - - DEF_NEWDEL(FshifterState) }; void FshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -122,20 +121,21 @@ void FshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) for(auto &gain : mGains) { - std::fill(std::begin(gain.Current), std::end(gain.Current), 0.0f); - std::fill(std::begin(gain.Target), std::end(gain.Target), 0.0f); + gain.Current.fill(0.0f); + gain.Target.fill(0.0f); } } void FshifterState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get<FshifterProps>(*props_); const DeviceBase *device{context->mDevice}; - const float step{props->Fshifter.Frequency / static_cast<float>(device->Frequency)}; + const float step{props.Frequency / static_cast<float>(device->Frequency)}; mPhaseStep[0] = mPhaseStep[1] = fastf2u(minf(step, 1.0f) * MixerFracOne); - switch(props->Fshifter.LeftDirection) + switch(props.LeftDirection) { case FShifterDirection::Down: mSign[0] = -1.0; @@ -149,7 +149,7 @@ void FshifterState::update(const ContextBase *context, const EffectSlot *slot, break; } - switch(props->Fshifter.RightDirection) + switch(props.RightDirection) { case FShifterDirection::Down: mSign[1] = -1.0; @@ -235,8 +235,8 @@ void FshifterState::process(const size_t samplesToDo, const al::span<const Float mPhase[c] = phase_idx; /* Now, mix the processed sound data to the output. */ - MixSamples({BufferOut, samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target, - maxz(samplesToDo, 512), 0); + MixSamples({BufferOut, samplesToDo}, samplesOut, mGains[c].Current.data(), + mGains[c].Target.data(), maxz(samplesToDo, 512), 0); } } diff --git a/alc/effects/modulator.cpp b/alc/effects/modulator.cpp index f99ba19c..7350ca5a 100644 --- a/alc/effects/modulator.cpp +++ b/alc/effects/modulator.cpp @@ -52,7 +52,7 @@ inline float Saw(uint index, float scale) { return static_cast<float>(index)*scale - 1.0f; } inline float Square(uint index, float scale) -{ return (static_cast<float>(index)*scale < 0.5f)*2.0f - 1.0f; } +{ return float(static_cast<float>(index)*scale < 0.5f)*2.0f - 1.0f; } inline float One(uint, float) { return 1.0f; } @@ -89,14 +89,15 @@ struct ModulatorState final : public EffectState { alignas(16) FloatBufferLine mModSamples{}; alignas(16) FloatBufferLine mBuffer{}; - struct { + struct OutParams { uint mTargetChannel{InvalidChannelIndex}; BiquadFilter mFilter; float mCurrentGain{}; float mTargetGain{}; - } mChans[MaxAmbiChannels]; + }; + std::array<OutParams,MaxAmbiChannels> mChans; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -104,8 +105,6 @@ struct ModulatorState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - - DEF_NEWDEL(ModulatorState) }; template<> @@ -126,8 +125,9 @@ void ModulatorState::deviceUpdate(const DeviceBase*, const BufferStorage*) } void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get<ModulatorProps>(*props_); const DeviceBase *device{context->mDevice}; /* The effective frequency will be adjusted to have a whole number of @@ -137,8 +137,8 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, * but that may need a more efficient sin function since it needs to do * many iterations per sample. */ - const float samplesPerCycle{props->Modulator.Frequency > 0.0f - ? static_cast<float>(device->Frequency)/props->Modulator.Frequency + 0.5f + const float samplesPerCycle{props.Frequency > 0.0f + ? static_cast<float>(device->Frequency)/props.Frequency + 0.5f : 1.0f}; const uint range{static_cast<uint>(clampf(samplesPerCycle, 1.0f, static_cast<float>(device->Frequency)))}; @@ -150,17 +150,17 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, mIndexScale = 0.0f; mGenModSamples = &ModulatorState::Modulate<One>; } - else if(props->Modulator.Waveform == ModulatorWaveform::Sinusoid) + else if(props.Waveform == ModulatorWaveform::Sinusoid) { mIndexScale = al::numbers::pi_v<float>*2.0f / static_cast<float>(mRange); mGenModSamples = &ModulatorState::Modulate<Sin>; } - else if(props->Modulator.Waveform == ModulatorWaveform::Sawtooth) + else if(props.Waveform == ModulatorWaveform::Sawtooth) { mIndexScale = 2.0f / static_cast<float>(mRange-1); mGenModSamples = &ModulatorState::Modulate<Saw>; } - else /*if(props->Modulator.Waveform == ModulatorWaveform::Square)*/ + else /*if(props.Waveform == ModulatorWaveform::Square)*/ { /* For square wave, the range should be even (there should be an equal * number of high and low samples). An odd number of samples per cycle @@ -171,7 +171,7 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, mGenModSamples = &ModulatorState::Modulate<Square>; } - float f0norm{props->Modulator.HighPassCutoff / static_cast<float>(device->Frequency)}; + float f0norm{props.HighPassCutoff / static_cast<float>(device->Frequency)}; f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f); /* Bandwidth value is constant in octaves. */ mChans[0].mFilter.setParamsFromBandwidth(BiquadType::HighPass, f0norm, 1.0f, 0.75f); diff --git a/alc/effects/null.cpp b/alc/effects/null.cpp index 1f9ae67b..964afe47 100644 --- a/alc/effects/null.cpp +++ b/alc/effects/null.cpp @@ -1,7 +1,7 @@ #include "config.h" -#include <stddef.h> +#include <cstddef> #include "almalloc.h" #include "alspan.h" @@ -25,8 +25,6 @@ struct NullState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - - DEF_NEWDEL(NullState) }; /* This constructs the effect state. It's called when the object is first diff --git a/alc/effects/pshifter.cpp b/alc/effects/pshifter.cpp index 0c27be30..1cc1a18c 100644 --- a/alc/effects/pshifter.cpp +++ b/alc/effects/pshifter.cpp @@ -58,7 +58,7 @@ constexpr size_t StftStep{StftSize / OversampleFactor}; /* Define a Hann window, used to filter the STFT input and output. */ struct Windower { - alignas(16) std::array<float,StftSize> mData; + alignas(16) std::array<float,StftSize> mData{}; Windower() { @@ -74,12 +74,6 @@ struct Windower { const Windower gWindow{}; -struct PFFFTSetupDeleter { - void operator()(PFFFT_Setup *ptr) { pffft_destroy_setup(ptr); } -}; -using PFFFTSetupPtr = std::unique_ptr<PFFFT_Setup,PFFFTSetupDeleter>; - - struct FrequencyBin { float Magnitude; float FreqBin; @@ -88,29 +82,29 @@ struct FrequencyBin { struct PshifterState final : public EffectState { /* Effect parameters */ - size_t mCount; - size_t mPos; - uint mPitchShiftI; - float mPitchShift; + size_t mCount{}; + size_t mPos{}; + uint mPitchShiftI{}; + float mPitchShift{}; /* Effects buffers */ - std::array<float,StftSize> mFIFO; - std::array<float,StftHalfSize+1> mLastPhase; - std::array<float,StftHalfSize+1> mSumPhase; - std::array<float,StftSize> mOutputAccum; + std::array<float,StftSize> mFIFO{}; + std::array<float,StftHalfSize+1> mLastPhase{}; + std::array<float,StftHalfSize+1> mSumPhase{}; + std::array<float,StftSize> mOutputAccum{}; - PFFFTSetupPtr mFft; - alignas(16) std::array<float,StftSize> mFftBuffer; - alignas(16) std::array<float,StftSize> mFftWorkBuffer; + PFFFTSetup mFft; + alignas(16) std::array<float,StftSize> mFftBuffer{}; + alignas(16) std::array<float,StftSize> mFftWorkBuffer{}; - std::array<FrequencyBin,StftHalfSize+1> mAnalysisBuffer; - std::array<FrequencyBin,StftHalfSize+1> mSynthesisBuffer; + std::array<FrequencyBin,StftHalfSize+1> mAnalysisBuffer{}; + std::array<FrequencyBin,StftHalfSize+1> mSynthesisBuffer{}; - alignas(16) FloatBufferLine mBufferOut; + alignas(16) FloatBufferLine mBufferOut{}; /* Effect gains for each output channel */ - float mCurrentGains[MaxAmbiChannels]; - float mTargetGains[MaxAmbiChannels]; + std::array<float,MaxAmbiChannels> mCurrentGains{}; + std::array<float,MaxAmbiChannels> mTargetGains{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -118,8 +112,6 @@ struct PshifterState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - - DEF_NEWDEL(PshifterState) }; void PshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -138,17 +130,18 @@ void PshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) mAnalysisBuffer.fill(FrequencyBin{}); mSynthesisBuffer.fill(FrequencyBin{}); - std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f); - std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f); + mCurrentGains.fill(0.0f); + mTargetGains.fill(0.0f); if(!mFft) - mFft = PFFFTSetupPtr{pffft_new_setup(StftSize, PFFFT_REAL)}; + mFft = PFFFTSetup{StftSize, PFFFT_REAL}; } void PshifterState::update(const ContextBase*, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { - const int tune{props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune}; + auto &props = std::get<PshifterProps>(*props_); + const int tune{props.CoarseTune*100 + props.FineTune}; const float pitch{std::pow(2.0f, static_cast<float>(tune) / 1200.0f)}; mPitchShiftI = clampu(fastf2u(pitch*MixerFracOne), MixerFracHalf, MixerFracOne*2); mPitchShift = static_cast<float>(mPitchShiftI) * float{1.0f/MixerFracOne}; @@ -197,8 +190,8 @@ void PshifterState::process(const size_t samplesToDo, mFftBuffer[k] = mFIFO[src] * gWindow.mData[k]; for(size_t src{0u}, k{StftSize-mPos};src < mPos;++src,++k) mFftBuffer[k] = mFIFO[src] * gWindow.mData[k]; - pffft_transform_ordered(mFft.get(), mFftBuffer.data(), mFftBuffer.data(), - mFftWorkBuffer.data(), PFFFT_FORWARD); + mFft.transform_ordered(mFftBuffer.data(), mFftBuffer.data(), mFftWorkBuffer.data(), + PFFFT_FORWARD); /* Analyze the obtained data. Since the real FFT is symmetric, only * StftHalfSize+1 samples are needed. @@ -296,8 +289,8 @@ void PshifterState::process(const size_t samplesToDo, /* Apply an inverse FFT to get the time-domain signal, and accumulate * for the output with windowing. */ - pffft_transform_ordered(mFft.get(), mFftBuffer.data(), mFftBuffer.data(), - mFftWorkBuffer.data(), PFFFT_BACKWARD); + mFft.transform_ordered(mFftBuffer.data(), mFftBuffer.data(), mFftWorkBuffer.data(), + PFFFT_BACKWARD); static constexpr float scale{3.0f / OversampleFactor / StftSize}; for(size_t dst{mPos}, k{0u};dst < StftSize;++dst,++k) @@ -311,8 +304,8 @@ void PshifterState::process(const size_t samplesToDo, } /* Now, mix the processed sound data to the output. */ - MixSamples({mBufferOut.data(), samplesToDo}, samplesOut, mCurrentGains, mTargetGains, - maxz(samplesToDo, 512), 0); + MixSamples({mBufferOut.data(), samplesToDo}, samplesOut, mCurrentGains.data(), + mTargetGains.data(), maxz(samplesToDo, 512), 0); } diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp index 0f1fcca1..45bfaf0f 100644 --- a/alc/effects/reverb.cpp +++ b/alc/effects/reverb.cpp @@ -22,11 +22,12 @@ #include <algorithm> #include <array> +#include <cassert> +#include <cstdint> #include <cstdio> #include <functional> #include <iterator> #include <numeric> -#include <stdint.h> #include "alc/effects/base.h" #include "almalloc.h" @@ -48,11 +49,6 @@ #include "vecmat.h" #include "vector.h" -/* This is a user config option for modifying the overall output of the reverb - * effect. - */ -float ReverbBoost = 1.0f; - namespace { using uint = unsigned int; @@ -70,7 +66,7 @@ struct CubicFilter { static constexpr size_t sTableSteps{1 << sTableBits}; static constexpr size_t sTableMask{sTableSteps - 1}; - float mFilter[sTableSteps*2 + 1]{}; + std::array<float,sTableSteps*2 + 1> mFilter{}; constexpr CubicFilter() { @@ -90,10 +86,14 @@ struct CubicFilter { } } - constexpr float getCoeff0(size_t i) const noexcept { return mFilter[sTableSteps+i]; } - constexpr float getCoeff1(size_t i) const noexcept { return mFilter[i]; } - constexpr float getCoeff2(size_t i) const noexcept { return mFilter[sTableSteps-i]; } - constexpr float getCoeff3(size_t i) const noexcept { return mFilter[sTableSteps*2-i]; } + [[nodiscard]] constexpr auto getCoeff0(size_t i) const noexcept -> float + { return mFilter[sTableSteps+i]; } + [[nodiscard]] constexpr auto getCoeff1(size_t i) const noexcept -> float + { return mFilter[i]; } + [[nodiscard]] constexpr auto getCoeff2(size_t i) const noexcept -> float + { return mFilter[sTableSteps-i]; } + [[nodiscard]] constexpr auto getCoeff3(size_t i) const noexcept -> float + { return mFilter[sTableSteps*2-i]; } }; constexpr CubicFilter gCubicTable; @@ -124,12 +124,12 @@ constexpr float MODULATION_DEPTH_COEFF{0.05f}; * tetrahedral array of discrete signals (boosted by a factor of sqrt(3), to * reduce the error introduced in the conversion). */ -alignas(16) constexpr float B2A[NUM_LINES][NUM_LINES]{ - { 0.5f, 0.5f, 0.5f, 0.5f }, - { 0.5f, -0.5f, -0.5f, 0.5f }, - { 0.5f, 0.5f, -0.5f, -0.5f }, - { 0.5f, -0.5f, 0.5f, -0.5f } -}; +alignas(16) constexpr std::array<std::array<float,NUM_LINES>,NUM_LINES> B2A{{ + {{ 0.5f, 0.5f, 0.5f, 0.5f }}, + {{ 0.5f, -0.5f, -0.5f, 0.5f }}, + {{ 0.5f, 0.5f, -0.5f, -0.5f }}, + {{ 0.5f, -0.5f, 0.5f, -0.5f }} +}}; /* Converts (W-normalized) A-Format to B-Format for early reflections (scaled * by 1/sqrt(3) to compensate for the boost in the B2A matrix). @@ -252,7 +252,7 @@ constexpr std::array<float,NUM_LINES> EARLY_ALLPASS_LENGTHS{{ * Using an average dimension of 1m, we get: */ constexpr std::array<float,NUM_LINES> EARLY_LINE_LENGTHS{{ - 5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f + 0.0000000e+0f, 4.9281100e-4f, 9.3916180e-4f, 1.3434322e-3f }}; /* The late all-pass filter lengths are based on the late line lengths: @@ -290,20 +290,16 @@ struct DelayLineI { * of 2 to allow the use of bit-masking instead of a modulus for wrapping. */ size_t Mask{0u}; - union { - uintptr_t LineOffset{0u}; - std::array<float,NUM_LINES> *Line; - }; + std::array<float,NUM_LINES> *Line; /* Given the allocated sample buffer, this function updates each delay line * offset. */ void realizeLineOffset(std::array<float,NUM_LINES> *sampleBuffer) noexcept - { Line = sampleBuffer + LineOffset; } + { Line = sampleBuffer; } /* Calculate the length of a delay line and store its mask and offset. */ - uint calcLineLength(const float length, const uintptr_t offset, const float frequency, - const uint extra) + size_t calcLineLength(const float length, const float frequency, const uint extra) { /* All line lengths are powers of 2, calculated from their lengths in * seconds, rounded up. @@ -313,7 +309,6 @@ struct DelayLineI { /* All lines share a single sample buffer. */ Mask = samples - 1; - LineOffset = offset; /* Return the sample count for accumulation. */ return samples; @@ -369,7 +364,7 @@ struct DelayLineI { struct VecAllpass { DelayLineI Delay; float Coeff{0.0f}; - size_t Offset[NUM_LINES]{}; + std::array<size_t,NUM_LINES> Offset{}; void process(const al::span<ReverbUpdateLine,NUM_LINES> samples, size_t offset, const float xCoeff, const float yCoeff, const size_t todo); @@ -402,12 +397,12 @@ struct EarlyReflections { * reflections. */ DelayLineI Delay; - size_t Offset[NUM_LINES]{}; - float Coeff[NUM_LINES]{}; + std::array<size_t,NUM_LINES> Offset{}; + std::array<float,NUM_LINES> Coeff{}; /* The gain for each output channel based on 3D panning. */ - float CurrentGains[NUM_LINES][MaxAmbiChannels]{}; - float TargetGains[NUM_LINES][MaxAmbiChannels]{}; + std::array<std::array<float,MaxAmbiChannels>,NUM_LINES> CurrentGains{}; + std::array<std::array<float,MaxAmbiChannels>,NUM_LINES> TargetGains{}; void updateLines(const float density_mult, const float diffusion, const float decayTime, const float frequency); @@ -418,12 +413,12 @@ struct Modulation { /* The vibrato time is tracked with an index over a (MOD_FRACONE) * normalized range. */ - uint Index, Step; + uint Index{}, Step{}; /* The depth of frequency change, in samples. */ - float Depth; + float Depth{}; - float ModDelays[MAX_UPDATE_SAMPLES]; + std::array<float,MAX_UPDATE_SAMPLES> ModDelays{}; void updateModulator(float modTime, float modDepth, float frequency); @@ -433,7 +428,7 @@ struct Modulation { struct LateReverb { /* A recursive delay line is used fill in the reverb tail. */ DelayLineI Delay; - size_t Offset[NUM_LINES]{}; + std::array<size_t,NUM_LINES> Offset{}; /* Attenuation to compensate for the modal density and decay rate of the * late lines. @@ -441,7 +436,7 @@ struct LateReverb { float DensityGain{0.0f}; /* T60 decay filters are used to simulate absorption. */ - T60Filter T60[NUM_LINES]; + std::array<T60Filter,NUM_LINES> T60; Modulation Mod; @@ -449,8 +444,8 @@ struct LateReverb { VecAllpass VecAp; /* The gain for each output channel based on 3D panning. */ - float CurrentGains[NUM_LINES][MaxAmbiChannels]{}; - float TargetGains[NUM_LINES][MaxAmbiChannels]{}; + std::array<std::array<float,MaxAmbiChannels>,NUM_LINES> CurrentGains{}; + std::array<std::array<float,MaxAmbiChannels>,NUM_LINES> TargetGains{}; void updateLines(const float density_mult, const float diffusion, const float lfDecayTime, const float mfDecayTime, const float hfDecayTime, const float lf0norm, @@ -465,21 +460,22 @@ struct LateReverb { struct ReverbPipeline { /* Master effect filters */ - struct { + struct FilterPair { BiquadFilter Lp; BiquadFilter Hp; - } mFilter[NUM_LINES]; + }; + std::array<FilterPair,NUM_LINES> mFilter; /* Core delay line (early reflections and late reverb tap from this). */ DelayLineI mEarlyDelayIn; DelayLineI mLateDelayIn; /* Tap points for early reflection delay. */ - size_t mEarlyDelayTap[NUM_LINES][2]{}; - float mEarlyDelayCoeff[NUM_LINES]{}; + std::array<std::array<size_t,2>,NUM_LINES> mEarlyDelayTap{}; + std::array<float,NUM_LINES> mEarlyDelayCoeff{}; /* Tap points for late reverb feed and delay. */ - size_t mLateDelayTap[NUM_LINES][2]{}; + std::array<std::array<size_t,2>,NUM_LINES> mLateDelayTap{}; /* Coefficients for the all-pass and line scattering matrices. */ float mMixX{0.0f}; @@ -551,9 +547,9 @@ struct ReverbState final : public EffectState { Normal, }; PipelineState mPipelineState{DeviceClear}; - uint8_t mCurrentPipeline{0}; + bool mCurrentPipeline{false}; - ReverbPipeline mPipelines[2]; + std::array<ReverbPipeline,2> mPipelines; /* The current write offset for all delay lines. */ size_t mOffset{}; @@ -582,14 +578,14 @@ struct ReverbState final : public EffectState { for(size_t c{0u};c < NUM_LINES;c++) { const al::span<float> tmpspan{mEarlySamples[c].data(), todo}; - MixSamples(tmpspan, samplesOut, pipeline.mEarly.CurrentGains[c], - pipeline.mEarly.TargetGains[c], todo, 0); + MixSamples(tmpspan, samplesOut, pipeline.mEarly.CurrentGains[c].data(), + pipeline.mEarly.TargetGains[c].data(), todo, 0); } for(size_t c{0u};c < NUM_LINES;c++) { const al::span<float> tmpspan{mLateSamples[c].data(), todo}; - MixSamples(tmpspan, samplesOut, pipeline.mLate.CurrentGains[c], - pipeline.mLate.TargetGains[c], todo, 0); + MixSamples(tmpspan, samplesOut, pipeline.mLate.CurrentGains[c].data(), + pipeline.mLate.TargetGains[c].data(), todo, 0); } } @@ -632,8 +628,8 @@ struct ReverbState final : public EffectState { const float hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]}; pipeline.mAmbiSplitter[0][c].processHfScale(tmpspan, hfscale); - MixSamples(tmpspan, samplesOut, pipeline.mEarly.CurrentGains[c], - pipeline.mEarly.TargetGains[c], todo, 0); + MixSamples(tmpspan, samplesOut, pipeline.mEarly.CurrentGains[c].data(), + pipeline.mEarly.TargetGains[c].data(), todo, 0); } for(size_t c{0u};c < NUM_LINES;c++) { @@ -642,8 +638,8 @@ struct ReverbState final : public EffectState { const float hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]}; pipeline.mAmbiSplitter[1][c].processHfScale(tmpspan, hfscale); - MixSamples(tmpspan, samplesOut, pipeline.mLate.CurrentGains[c], - pipeline.mLate.TargetGains[c], todo, 0); + MixSamples(tmpspan, samplesOut, pipeline.mLate.CurrentGains[c].data(), + pipeline.mLate.TargetGains[c].data(), todo, 0); } } @@ -662,8 +658,6 @@ struct ReverbState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - - DEF_NEWDEL(ReverbState) }; /************************************** @@ -678,11 +672,6 @@ inline float CalcDelayLengthMult(float density) */ void ReverbState::allocLines(const float frequency) { - /* All delay line lengths are calculated to accommodate the full range of - * lengths given their respective parameters. - */ - size_t totalSamples{0u}; - /* Multiplier for the maximum density value, i.e. density=1, which is * actually the least density... */ @@ -692,8 +681,12 @@ void ReverbState::allocLines(const float frequency) * time and depth coefficient, and halfed for the low-to-high frequency * swing. */ - constexpr float max_mod_delay{MaxModulationTime*MODULATION_DEPTH_COEFF / 2.0f}; + static constexpr float max_mod_delay{MaxModulationTime*MODULATION_DEPTH_COEFF / 2.0f}; + + std::array<size_t,12> lineoffsets{}; + size_t oidx{0}; + size_t totalSamples{0u}; for(auto &pipeline : mPipelines) { /* The main delay length includes the maximum early reflection delay, @@ -702,37 +695,45 @@ void ReverbState::allocLines(const float frequency) * update size (BufferLineSize) for block processing. */ float length{ReverbMaxReflectionsDelay + EARLY_TAP_LENGTHS.back()*multiplier}; - totalSamples += pipeline.mEarlyDelayIn.calcLineLength(length, totalSamples, frequency, - BufferLineSize); + size_t count{pipeline.mEarlyDelayIn.calcLineLength(length, frequency, BufferLineSize)}; + lineoffsets[oidx++] = totalSamples; + totalSamples += count; - constexpr float LateLineDiffAvg{(LATE_LINE_LENGTHS.back()-LATE_LINE_LENGTHS.front()) / + static constexpr float LateDiffAvg{(LATE_LINE_LENGTHS.back()-LATE_LINE_LENGTHS.front()) / float{NUM_LINES}}; - length = ReverbMaxLateReverbDelay + LateLineDiffAvg*multiplier; - totalSamples += pipeline.mLateDelayIn.calcLineLength(length, totalSamples, frequency, - BufferLineSize); + length = ReverbMaxLateReverbDelay + LateDiffAvg*multiplier; + count = pipeline.mLateDelayIn.calcLineLength(length, frequency, BufferLineSize); + lineoffsets[oidx++] = totalSamples; + totalSamples += count; /* The early vector all-pass line. */ length = EARLY_ALLPASS_LENGTHS.back() * multiplier; - totalSamples += pipeline.mEarly.VecAp.Delay.calcLineLength(length, totalSamples, frequency, - 0); + count = pipeline.mEarly.VecAp.Delay.calcLineLength(length, frequency, 0); + lineoffsets[oidx++] = totalSamples; + totalSamples += count; /* The early reflection line. */ length = EARLY_LINE_LENGTHS.back() * multiplier; - totalSamples += pipeline.mEarly.Delay.calcLineLength(length, totalSamples, frequency, - MAX_UPDATE_SAMPLES); + count = pipeline.mEarly.Delay.calcLineLength(length, frequency, MAX_UPDATE_SAMPLES); + lineoffsets[oidx++] = totalSamples; + totalSamples += count; /* The late vector all-pass line. */ length = LATE_ALLPASS_LENGTHS.back() * multiplier; - totalSamples += pipeline.mLate.VecAp.Delay.calcLineLength(length, totalSamples, frequency, - 0); + count = pipeline.mLate.VecAp.Delay.calcLineLength(length, frequency, 0); + lineoffsets[oidx++] = totalSamples; + totalSamples += count; /* The late delay lines are calculated from the largest maximum density * line length, and the maximum modulation delay. Four additional * samples are needed for resampling the modulator delay. */ length = LATE_LINE_LENGTHS.back()*multiplier + max_mod_delay; - totalSamples += pipeline.mLate.Delay.calcLineLength(length, totalSamples, frequency, 4); + count = pipeline.mLate.Delay.calcLineLength(length, frequency, 4); + lineoffsets[oidx++] = totalSamples; + totalSamples += count; } + assert(oidx == lineoffsets.size()); if(totalSamples != mSampleBuffer.size()) decltype(mSampleBuffer)(totalSamples).swap(mSampleBuffer); @@ -741,14 +742,15 @@ void ReverbState::allocLines(const float frequency) std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), decltype(mSampleBuffer)::value_type{}); /* Update all delays to reflect the new sample buffer. */ + oidx = 0; for(auto &pipeline : mPipelines) { - pipeline.mEarlyDelayIn.realizeLineOffset(mSampleBuffer.data()); - pipeline.mLateDelayIn.realizeLineOffset(mSampleBuffer.data()); - pipeline.mEarly.VecAp.Delay.realizeLineOffset(mSampleBuffer.data()); - pipeline.mEarly.Delay.realizeLineOffset(mSampleBuffer.data()); - pipeline.mLate.VecAp.Delay.realizeLineOffset(mSampleBuffer.data()); - pipeline.mLate.Delay.realizeLineOffset(mSampleBuffer.data()); + pipeline.mEarlyDelayIn.realizeLineOffset(mSampleBuffer.data() + lineoffsets[oidx++]); + pipeline.mLateDelayIn.realizeLineOffset(mSampleBuffer.data() + lineoffsets[oidx++]); + pipeline.mEarly.VecAp.Delay.realizeLineOffset(mSampleBuffer.data() + lineoffsets[oidx++]); + pipeline.mEarly.Delay.realizeLineOffset(mSampleBuffer.data() + lineoffsets[oidx++]); + pipeline.mLate.VecAp.Delay.realizeLineOffset(mSampleBuffer.data() + lineoffsets[oidx++]); + pipeline.mLate.Delay.realizeLineOffset(mSampleBuffer.data() + lineoffsets[oidx++]); } } @@ -761,17 +763,16 @@ void ReverbState::deviceUpdate(const DeviceBase *device, const BufferStorage*) for(auto &pipeline : mPipelines) { - /* Clear filters and gain coefficients since the delay lines were all just - * cleared (if not reallocated). - */ + /* Clear filters and gain coefficients since the delay lines were all + * just cleared (if not reallocated). + */ for(auto &filter : pipeline.mFilter) { filter.Lp.clear(); filter.Hp.clear(); } - std::fill(std::begin(pipeline.mEarlyDelayCoeff),std::end(pipeline.mEarlyDelayCoeff), 0.0f); - std::fill(std::begin(pipeline.mEarlyDelayCoeff),std::end(pipeline.mEarlyDelayCoeff), 0.0f); + pipeline.mEarlyDelayCoeff.fill(0.0f); pipeline.mLate.DensityGain = 0.0f; for(auto &t60 : pipeline.mLate.T60) @@ -786,13 +787,13 @@ void ReverbState::deviceUpdate(const DeviceBase *device, const BufferStorage*) pipeline.mLate.Mod.Depth = 0.0f; for(auto &gains : pipeline.mEarly.CurrentGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); + gains.fill(0.0f); for(auto &gains : pipeline.mEarly.TargetGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); + gains.fill(0.0f); for(auto &gains : pipeline.mLate.CurrentGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); + gains.fill(0.0f); for(auto &gains : pipeline.mLate.TargetGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); + gains.fill(0.0f); } mPipelineState = DeviceClear; @@ -1057,7 +1058,7 @@ void ReverbPipeline::updateDelayLine(const float earlyDelay, const float lateDel * output. */ length = (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS.front())/float{NUM_LINES}*density_mult + - std::max(lateDelay - EARLY_LINE_LENGTHS[0]*density_mult, 0.0f); + lateDelay; mLateDelayTap[i][1] = float2uint(length * frequency); } } @@ -1076,7 +1077,7 @@ std::array<std::array<float,4>,4> GetTransformFromVector(const al::span<const fl * rest of OpenAL which use right-handed. This is fixed by negating Z, * which cancels out with the B-Format Z negation. */ - float norm[3]; + std::array<float,3> norm; float mag{std::sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2])}; if(mag > 1.0f) { @@ -1185,75 +1186,73 @@ void ReverbPipeline::update3DPanning(const al::span<const float,3> ReflectionsPa } void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get<ReverbProps>(*props_); const DeviceBase *Device{Context->mDevice}; const auto frequency = static_cast<float>(Device->Frequency); /* If the HF limit parameter is flagged, calculate an appropriate limit * based on the air absorption parameter. */ - float hfRatio{props->Reverb.DecayHFRatio}; - if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f) - hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF, - props->Reverb.DecayTime); + float hfRatio{props.DecayHFRatio}; + if(props.DecayHFLimit && props.AirAbsorptionGainHF < 1.0f) + hfRatio = CalcLimitedHfRatio(hfRatio, props.AirAbsorptionGainHF, props.DecayTime); /* Calculate the LF/HF decay times. */ constexpr float MinDecayTime{0.1f}, MaxDecayTime{20.0f}; - const float lfDecayTime{clampf(props->Reverb.DecayTime*props->Reverb.DecayLFRatio, - MinDecayTime, MaxDecayTime)}; - const float hfDecayTime{clampf(props->Reverb.DecayTime*hfRatio, MinDecayTime, MaxDecayTime)}; + const float lfDecayTime{clampf(props.DecayTime*props.DecayLFRatio, MinDecayTime,MaxDecayTime)}; + const float hfDecayTime{clampf(props.DecayTime*hfRatio, MinDecayTime, MaxDecayTime)}; /* Determine if a full update is required. */ const bool fullUpdate{mPipelineState == DeviceClear || /* Density is essentially a master control for the feedback delays, so * changes the offsets of many delay lines. */ - mParams.Density != props->Reverb.Density || + mParams.Density != props.Density || /* Diffusion and decay times influences the decay rate (gain) of the * late reverb T60 filter. */ - mParams.Diffusion != props->Reverb.Diffusion || - mParams.DecayTime != props->Reverb.DecayTime || + mParams.Diffusion != props.Diffusion || + mParams.DecayTime != props.DecayTime || mParams.HFDecayTime != hfDecayTime || mParams.LFDecayTime != lfDecayTime || /* Modulation time and depth both require fading the modulation delay. */ - mParams.ModulationTime != props->Reverb.ModulationTime || - mParams.ModulationDepth != props->Reverb.ModulationDepth || + mParams.ModulationTime != props.ModulationTime || + mParams.ModulationDepth != props.ModulationDepth || /* HF/LF References control the weighting used to calculate the density * gain. */ - mParams.HFReference != props->Reverb.HFReference || - mParams.LFReference != props->Reverb.LFReference}; + mParams.HFReference != props.HFReference || + mParams.LFReference != props.LFReference}; if(fullUpdate) { - mParams.Density = props->Reverb.Density; - mParams.Diffusion = props->Reverb.Diffusion; - mParams.DecayTime = props->Reverb.DecayTime; + mParams.Density = props.Density; + mParams.Diffusion = props.Diffusion; + mParams.DecayTime = props.DecayTime; mParams.HFDecayTime = hfDecayTime; mParams.LFDecayTime = lfDecayTime; - mParams.ModulationTime = props->Reverb.ModulationTime; - mParams.ModulationDepth = props->Reverb.ModulationDepth; - mParams.HFReference = props->Reverb.HFReference; - mParams.LFReference = props->Reverb.LFReference; + mParams.ModulationTime = props.ModulationTime; + mParams.ModulationDepth = props.ModulationDepth; + mParams.HFReference = props.HFReference; + mParams.LFReference = props.LFReference; mPipelineState = (mPipelineState != DeviceClear) ? StartFade : Normal; - mCurrentPipeline ^= 1; + mCurrentPipeline = !mCurrentPipeline; } auto &pipeline = mPipelines[mCurrentPipeline]; /* Update early and late 3D panning. */ mOutTarget = target.Main->Buffer; - const float gain{props->Reverb.Gain * Slot->Gain * ReverbBoost}; - pipeline.update3DPanning(props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan, - props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain, mUpmixOutput, - target.Main); + const float gain{props.Gain * Slot->Gain * ReverbBoost}; + pipeline.update3DPanning(props.ReflectionsPan, props.LateReverbPan, props.ReflectionsGain*gain, + props.LateReverbGain*gain, mUpmixOutput, target.Main); /* Calculate the master filters */ - float hf0norm{minf(props->Reverb.HFReference/frequency, 0.49f)}; - pipeline.mFilter[0].Lp.setParamsFromSlope(BiquadType::HighShelf, hf0norm, props->Reverb.GainHF, 1.0f); - float lf0norm{minf(props->Reverb.LFReference/frequency, 0.49f)}; - pipeline.mFilter[0].Hp.setParamsFromSlope(BiquadType::LowShelf, lf0norm, props->Reverb.GainLF, 1.0f); + float hf0norm{minf(props.HFReference/frequency, 0.49f)}; + pipeline.mFilter[0].Lp.setParamsFromSlope(BiquadType::HighShelf, hf0norm, props.GainHF, 1.0f); + float lf0norm{minf(props.LFReference/frequency, 0.49f)}; + pipeline.mFilter[0].Hp.setParamsFromSlope(BiquadType::LowShelf, lf0norm, props.GainLF, 1.0f); for(size_t i{1u};i < NUM_LINES;i++) { pipeline.mFilter[i].Lp.copyParamsFrom(pipeline.mFilter[0].Lp); @@ -1261,34 +1260,32 @@ void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot, } /* The density-based room size (delay length) multiplier. */ - const float density_mult{CalcDelayLengthMult(props->Reverb.Density)}; + const float density_mult{CalcDelayLengthMult(props.Density)}; /* Update the main effect delay and associated taps. */ - pipeline.updateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay, - density_mult, props->Reverb.DecayTime, frequency); + pipeline.updateDelayLine(props.ReflectionsDelay, props.LateReverbDelay, density_mult, + props.DecayTime, frequency); if(fullUpdate) { /* Update the early lines. */ - pipeline.mEarly.updateLines(density_mult, props->Reverb.Diffusion, props->Reverb.DecayTime, - frequency); + pipeline.mEarly.updateLines(density_mult, props.Diffusion, props.DecayTime, frequency); /* Get the mixing matrix coefficients. */ - CalcMatrixCoeffs(props->Reverb.Diffusion, &pipeline.mMixX, &pipeline.mMixY); + CalcMatrixCoeffs(props.Diffusion, &pipeline.mMixX, &pipeline.mMixY); /* Update the modulator rate and depth. */ - pipeline.mLate.Mod.updateModulator(props->Reverb.ModulationTime, - props->Reverb.ModulationDepth, frequency); + pipeline.mLate.Mod.updateModulator(props.ModulationTime, props.ModulationDepth, frequency); /* Update the late lines. */ - pipeline.mLate.updateLines(density_mult, props->Reverb.Diffusion, lfDecayTime, - props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm, frequency); + pipeline.mLate.updateLines(density_mult, props.Diffusion, lfDecayTime, props.DecayTime, + hfDecayTime, lf0norm, hf0norm, frequency); } /* Calculate the gain at the start of the late reverb stage, and the gain * difference from the decay target (0.001, or -60dB). */ - const float decayBase{props->Reverb.ReflectionsGain * props->Reverb.LateReverbGain}; + const float decayBase{props.ReflectionsGain * props.LateReverbGain}; const float decayDiff{ReverbDecayGain / decayBase}; if(decayDiff < 1.0f) @@ -1297,10 +1294,10 @@ void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot, * by -60dB), calculate the time to decay to -60dB from the start of * the late reverb. */ - const float diffTime{std::log10(decayDiff)*(20.0f / -60.0f) * props->Reverb.DecayTime}; + const float diffTime{std::log10(decayDiff)*(20.0f / -60.0f) * props.DecayTime}; - const float decaySamples{(props->Reverb.ReflectionsDelay + props->Reverb.LateReverbDelay - + diffTime) * frequency}; + const float decaySamples{(props.ReflectionsDelay+props.LateReverbDelay+diffTime) + * frequency}; /* Limit to 100,000 samples (a touch over 2 seconds at 48khz) to * avoid excessive double-processing. */ @@ -1311,8 +1308,7 @@ void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot, /* Otherwise, if the late reverb already starts at -60dB or less, only * include the time to get to the late reverb. */ - const float decaySamples{(props->Reverb.ReflectionsDelay + props->Reverb.LateReverbDelay) - * frequency}; + const float decaySamples{(props.ReflectionsDelay+props.LateReverbDelay) * frequency}; pipeline.mFadeSampleCount = static_cast<size_t>(minf(decaySamples, 100'000.0f)); } } @@ -1413,7 +1409,7 @@ void VecAllpass::process(const al::span<ReverbUpdateLine,NUM_LINES> samples, siz ASSUME(todo > 0); - size_t vap_offset[NUM_LINES]; + std::array<size_t,NUM_LINES> vap_offset; for(size_t j{0u};j < NUM_LINES;j++) vap_offset[j] = offset - Offset[j]; for(size_t i{0u};i < todo;) @@ -1504,10 +1500,11 @@ void ReverbPipeline::processEarly(size_t offset, const size_t samplesToDo, mEarlyDelayTap[j][0] = mEarlyDelayTap[j][1]; } - /* Apply a vector all-pass, to help color the initial reflections based - * on the diffusion strength. + /* Apply a vector all-pass, to help color the initial reflections. + * Don't apply diffusion-based scattering since these are still the + * first reflections. */ - mEarly.VecAp.process(tempSamples, offset, mixX, mixY, todo); + mEarly.VecAp.process(tempSamples, offset, 1.0f, 0.0f, todo); /* Apply a delay and bounce to generate secondary reflections, combine * with the primary reflections and write out the result for mixing. @@ -1594,17 +1591,52 @@ void ReverbPipeline::processLate(size_t offset, const size_t samplesToDo, /* First, calculate the modulated delays for the late feedback. */ mLate.Mod.calcDelays(todo); - /* Next, load decorrelated samples from the main and feedback delay - * lines. Filter the signal to apply its frequency-dependent decay. + /* Now load samples from the feedback delay lines. Filter the signal to + * apply its frequency-dependent decay. */ + for(size_t j{0u};j < NUM_LINES;++j) + { + size_t late_feedb_tap{offset - mLate.Offset[j]}; + const float midGain{mLate.T60[j].MidGain}; + + for(size_t i{0u};i < todo;++i) + { + /* Calculate the read offset and offset between it and the next + * sample. + */ + const float fdelay{mLate.Mod.ModDelays[i]}; + const size_t idelay{float2uint(fdelay * float{gCubicTable.sTableSteps})}; + const size_t delay{late_feedb_tap - (idelay>>gCubicTable.sTableBits)}; + const size_t delayoffset{idelay & gCubicTable.sTableMask}; + ++late_feedb_tap; + + /* Get the samples around by the delayed offset. */ + const float out0{late_delay.Line[(delay ) & late_delay.Mask][j]}; + const float out1{late_delay.Line[(delay-1) & late_delay.Mask][j]}; + const float out2{late_delay.Line[(delay-2) & late_delay.Mask][j]}; + const float out3{late_delay.Line[(delay-3) & late_delay.Mask][j]}; + + /* The output is obtained by interpolating the four samples + * that were acquired above, and combined with the main delay + * tap. + */ + const float out{out0*gCubicTable.getCoeff0(delayoffset) + + out1*gCubicTable.getCoeff1(delayoffset) + + out2*gCubicTable.getCoeff2(delayoffset) + + out3*gCubicTable.getCoeff3(delayoffset)}; + tempSamples[j][i] = out * midGain; + } + + mLate.T60[j].process({tempSamples[j].data(), todo}); + } + + /* Next load decorrelated samples from the main delay lines. */ const float fadeStep{1.0f / static_cast<float>(todo)}; - for(size_t j{0u};j < NUM_LINES;j++) + for(size_t j{0u};j < NUM_LINES;++j) { size_t late_delay_tap0{offset - mLateDelayTap[j][0]}; size_t late_delay_tap1{offset - mLateDelayTap[j][1]}; - size_t late_feedb_tap{offset - mLate.Offset[j]}; - const float midGain{mLate.T60[j].MidGain}; - const float densityGain{mLate.DensityGain * midGain}; + const float densityGain{mLate.DensityGain}; const float densityStep{late_delay_tap0 != late_delay_tap1 ? densityGain*fadeStep : 0.0f}; float fadeCount{0.0f}; @@ -1615,48 +1647,22 @@ void ReverbPipeline::processLate(size_t offset, const size_t samplesToDo, late_delay_tap1 &= in_delay.Mask; size_t td{minz(todo-i, in_delay.Mask+1 - maxz(late_delay_tap0, late_delay_tap1))}; do { - /* Calculate the read offset and offset between it and the - * next sample. - */ - const float fdelay{mLate.Mod.ModDelays[i]}; - const size_t idelay{float2uint(fdelay * float{gCubicTable.sTableSteps})}; - const size_t delay{late_feedb_tap - (idelay>>gCubicTable.sTableBits)}; - const size_t delayoffset{idelay & gCubicTable.sTableMask}; - ++late_feedb_tap; - - /* Get the samples around by the delayed offset. */ - const float out0{late_delay.Line[(delay ) & late_delay.Mask][j]}; - const float out1{late_delay.Line[(delay-1) & late_delay.Mask][j]}; - const float out2{late_delay.Line[(delay-2) & late_delay.Mask][j]}; - const float out3{late_delay.Line[(delay-3) & late_delay.Mask][j]}; - - /* The output is obtained by interpolating the four samples - * that were acquired above, and combined with the main - * delay tap. - */ - const float out{out0*gCubicTable.getCoeff0(delayoffset) - + out1*gCubicTable.getCoeff1(delayoffset) - + out2*gCubicTable.getCoeff2(delayoffset) - + out3*gCubicTable.getCoeff3(delayoffset)}; const float fade0{densityGain - densityStep*fadeCount}; const float fade1{densityStep*fadeCount}; fadeCount += 1.0f; - tempSamples[j][i] = out*midGain + - in_delay.Line[late_delay_tap0++][j]*fade0 + + tempSamples[j][i] += in_delay.Line[late_delay_tap0++][j]*fade0 + in_delay.Line[late_delay_tap1++][j]*fade1; ++i; } while(--td); } mLateDelayTap[j][0] = mLateDelayTap[j][1]; - - mLate.T60[j].process({tempSamples[j].data(), todo}); } /* Apply a vector all-pass to improve micro-surface diffusion, and * write out the results for mixing. */ mLate.VecAp.process(tempSamples, offset, mixX, mixY, todo); - for(size_t j{0u};j < NUM_LINES;j++) + for(size_t j{0u};j < NUM_LINES;++j) std::copy_n(tempSamples[j].begin(), todo, outSamples[j].begin()+base); /* Finally, scatter and bounce the results to refeed the feedback buffer. */ @@ -1673,7 +1679,7 @@ void ReverbState::process(const size_t samplesToDo, const al::span<const FloatBu ASSUME(samplesToDo > 0); - auto &oldpipeline = mPipelines[mCurrentPipeline^1]; + auto &oldpipeline = mPipelines[!mCurrentPipeline]; auto &pipeline = mPipelines[mCurrentPipeline]; if(mPipelineState >= Fading) @@ -1681,7 +1687,7 @@ void ReverbState::process(const size_t samplesToDo, const al::span<const FloatBu /* Convert B-Format to A-Format for processing. */ const size_t numInput{minz(samplesIn.size(), NUM_LINES)}; const al::span<float> tmpspan{al::assume_aligned<16>(mTempLine.data()), samplesToDo}; - for(size_t c{0u};c < NUM_LINES;c++) + for(size_t c{0u};c < NUM_LINES;++c) { std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); for(size_t i{0};i < numInput;++i) @@ -1722,7 +1728,7 @@ void ReverbState::process(const size_t samplesToDo, const al::span<const FloatBu const al::span<float> tmpspan{al::assume_aligned<16>(mTempLine.data()), samplesToDo}; const float fadeStep{1.0f / static_cast<float>(samplesToDo)}; - for(size_t c{0u};c < NUM_LINES;c++) + for(size_t c{0u};c < NUM_LINES;++c) { std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); for(size_t i{0};i < numInput;++i) @@ -1746,7 +1752,7 @@ void ReverbState::process(const size_t samplesToDo, const al::span<const FloatBu filter.process(tmpspan, tmpspan.data()); pipeline.mEarlyDelayIn.write(offset, c, tmpspan.cbegin(), samplesToDo); } - for(size_t c{0u};c < NUM_LINES;c++) + for(size_t c{0u};c < NUM_LINES;++c) { std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); for(size_t i{0};i < numInput;++i) @@ -1783,7 +1789,7 @@ void ReverbState::process(const size_t samplesToDo, const al::span<const FloatBu if(mPipelineState == Cleanup) { size_t numSamples{mSampleBuffer.size()/2}; - size_t pipelineOffset{numSamples * (mCurrentPipeline^1)}; + size_t pipelineOffset{numSamples * (!mCurrentPipeline)}; std::fill_n(mSampleBuffer.data()+pipelineOffset, numSamples, decltype(mSampleBuffer)::value_type{}); diff --git a/alc/effects/vmorpher.cpp b/alc/effects/vmorpher.cpp index 872c7add..eaf30d07 100644 --- a/alc/effects/vmorpher.cpp +++ b/alc/effects/vmorpher.cpp @@ -57,29 +57,30 @@ namespace { using uint = unsigned int; -#define MAX_UPDATE_SAMPLES 256 -#define NUM_FORMANTS 4 -#define NUM_FILTERS 2 -#define Q_FACTOR 5.0f - -#define VOWEL_A_INDEX 0 -#define VOWEL_B_INDEX 1 +constexpr size_t MaxUpdateSamples{256}; +constexpr size_t NumFormants{4}; +constexpr float QFactor{5.0f}; +enum : size_t { + VowelAIndex, + VowelBIndex, + NumFilters +}; -#define WAVEFORM_FRACBITS 24 -#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS) -#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1) +constexpr size_t WaveformFracBits{24}; +constexpr size_t WaveformFracOne{1<<WaveformFracBits}; +constexpr size_t WaveformFracMask{WaveformFracOne-1}; inline float Sin(uint index) { - constexpr float scale{al::numbers::pi_v<float>*2.0f / WAVEFORM_FRACONE}; + constexpr float scale{al::numbers::pi_v<float>*2.0f / float{WaveformFracOne}}; return std::sin(static_cast<float>(index) * scale)*0.5f + 0.5f; } inline float Saw(uint index) -{ return static_cast<float>(index) / float{WAVEFORM_FRACONE}; } +{ return static_cast<float>(index) / float{WaveformFracOne}; } inline float Triangle(uint index) -{ return std::fabs(static_cast<float>(index)*(2.0f/WAVEFORM_FRACONE) - 1.0f); } +{ return std::fabs(static_cast<float>(index)*(2.0f/WaveformFracOne) - 1.0f); } inline float Half(uint) { return 0.5f; } @@ -89,13 +90,12 @@ void Oscillate(float *RESTRICT dst, uint index, const uint step, size_t todo) for(size_t i{0u};i < todo;i++) { index += step; - index &= WAVEFORM_FRACMASK; + index &= WaveformFracMask; dst[i] = func(index); } } -struct FormantFilter -{ +struct FormantFilter { float mCoeff{0.0f}; float mGain{1.0f}; float mS1{0.0f}; @@ -106,20 +106,21 @@ struct FormantFilter : mCoeff{std::tan(al::numbers::pi_v<float> * f0norm)}, mGain{gain} { } - inline void process(const float *samplesIn, float *samplesOut, const size_t numInput) + void process(const float *samplesIn, float *samplesOut, const size_t numInput) noexcept { /* A state variable filter from a topology-preserving transform. * Based on a talk given by Ivan Cohen: https://www.youtube.com/watch?v=esjHXGPyrhg */ const float g{mCoeff}; const float gain{mGain}; - const float h{1.0f / (1.0f + (g/Q_FACTOR) + (g*g))}; + const float h{1.0f / (1.0f + (g/QFactor) + (g*g))}; + const float coeff{1.0f/QFactor + g}; float s1{mS1}; float s2{mS2}; for(size_t i{0u};i < numInput;i++) { - const float H{(samplesIn[i] - (1.0f/Q_FACTOR + g)*s1 - s2)*h}; + const float H{(samplesIn[i] - coeff*s1 - s2)*h}; const float B{g*H + s1}; const float L{g*B + s2}; @@ -133,7 +134,7 @@ struct FormantFilter mS2 = s2; } - inline void clear() + void clear() noexcept { mS1 = 0.0f; mS2 = 0.0f; @@ -142,16 +143,17 @@ struct FormantFilter struct VmorpherState final : public EffectState { - struct { + struct OutParams { uint mTargetChannel{InvalidChannelIndex}; /* Effect parameters */ - FormantFilter mFormants[NUM_FILTERS][NUM_FORMANTS]; + std::array<std::array<FormantFilter,NumFormants>,NumFilters> mFormants; /* Effect gains for each channel */ float mCurrentGain{}; float mTargetGain{}; - } mChans[MaxAmbiChannels]; + }; + std::array<OutParams,MaxAmbiChannels> mChans; void (*mGetSamples)(float*RESTRICT, uint, const uint, size_t){}; @@ -159,9 +161,9 @@ struct VmorpherState final : public EffectState { uint mStep{1}; /* Effects buffers */ - alignas(16) float mSampleBufferA[MAX_UPDATE_SAMPLES]{}; - alignas(16) float mSampleBufferB[MAX_UPDATE_SAMPLES]{}; - alignas(16) float mLfo[MAX_UPDATE_SAMPLES]{}; + alignas(16) std::array<float,MaxUpdateSamples> mSampleBufferA{}; + alignas(16) std::array<float,MaxUpdateSamples> mSampleBufferB{}; + alignas(16) std::array<float,MaxUpdateSamples> mLfo{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, @@ -169,14 +171,12 @@ struct VmorpherState final : public EffectState { void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override; - static std::array<FormantFilter,4> getFiltersByPhoneme(VMorpherPhenome phoneme, - float frequency, float pitch); - - DEF_NEWDEL(VmorpherState) + static std::array<FormantFilter,NumFormants> getFiltersByPhoneme(VMorpherPhenome phoneme, + float frequency, float pitch) noexcept; }; -std::array<FormantFilter,4> VmorpherState::getFiltersByPhoneme(VMorpherPhenome phoneme, - float frequency, float pitch) +std::array<FormantFilter,NumFormants> VmorpherState::getFiltersByPhoneme(VMorpherPhenome phoneme, + float frequency, float pitch) noexcept { /* Using soprano formant set of values to * better match mid-range frequency space. @@ -232,44 +232,43 @@ void VmorpherState::deviceUpdate(const DeviceBase*, const BufferStorage*) for(auto &e : mChans) { e.mTargetChannel = InvalidChannelIndex; - std::for_each(std::begin(e.mFormants[VOWEL_A_INDEX]), std::end(e.mFormants[VOWEL_A_INDEX]), + std::for_each(e.mFormants[VowelAIndex].begin(), e.mFormants[VowelAIndex].end(), std::mem_fn(&FormantFilter::clear)); - std::for_each(std::begin(e.mFormants[VOWEL_B_INDEX]), std::end(e.mFormants[VOWEL_B_INDEX]), + std::for_each(e.mFormants[VowelBIndex].begin(), e.mFormants[VowelBIndex].end(), std::mem_fn(&FormantFilter::clear)); e.mCurrentGain = 0.0f; } } void VmorpherState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get<VmorpherProps>(*props_); const DeviceBase *device{context->mDevice}; const float frequency{static_cast<float>(device->Frequency)}; - const float step{props->Vmorpher.Rate / frequency}; - mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1})); + const float step{props.Rate / frequency}; + mStep = fastf2u(clampf(step*WaveformFracOne, 0.0f, float{WaveformFracOne}-1.0f)); if(mStep == 0) mGetSamples = Oscillate<Half>; - else if(props->Vmorpher.Waveform == VMorpherWaveform::Sinusoid) + else if(props.Waveform == VMorpherWaveform::Sinusoid) mGetSamples = Oscillate<Sin>; - else if(props->Vmorpher.Waveform == VMorpherWaveform::Triangle) + else if(props.Waveform == VMorpherWaveform::Triangle) mGetSamples = Oscillate<Triangle>; - else /*if(props->Vmorpher.Waveform == VMorpherWaveform::Sawtooth)*/ + else /*if(props.Waveform == VMorpherWaveform::Sawtooth)*/ mGetSamples = Oscillate<Saw>; - const float pitchA{std::pow(2.0f, - static_cast<float>(props->Vmorpher.PhonemeACoarseTuning) / 12.0f)}; - const float pitchB{std::pow(2.0f, - static_cast<float>(props->Vmorpher.PhonemeBCoarseTuning) / 12.0f)}; + const float pitchA{std::pow(2.0f, static_cast<float>(props.PhonemeACoarseTuning) / 12.0f)}; + const float pitchB{std::pow(2.0f, static_cast<float>(props.PhonemeBCoarseTuning) / 12.0f)}; - auto vowelA = getFiltersByPhoneme(props->Vmorpher.PhonemeA, frequency, pitchA); - auto vowelB = getFiltersByPhoneme(props->Vmorpher.PhonemeB, frequency, pitchB); + auto vowelA = getFiltersByPhoneme(props.PhonemeA, frequency, pitchA); + auto vowelB = getFiltersByPhoneme(props.PhonemeB, frequency, pitchB); /* Copy the filter coefficients to the input channels. */ for(size_t i{0u};i < slot->Wet.Buffer.size();++i) { - std::copy(vowelA.begin(), vowelA.end(), std::begin(mChans[i].mFormants[VOWEL_A_INDEX])); - std::copy(vowelB.begin(), vowelB.end(), std::begin(mChans[i].mFormants[VOWEL_B_INDEX])); + std::copy(vowelA.begin(), vowelA.end(), mChans[i].mFormants[VowelAIndex].begin()); + std::copy(vowelB.begin(), vowelB.end(), mChans[i].mFormants[VowelBIndex].begin()); } mOutTarget = target.Main->Buffer; @@ -288,11 +287,11 @@ void VmorpherState::process(const size_t samplesToDo, const al::span<const Float */ for(size_t base{0u};base < samplesToDo;) { - const size_t td{minz(MAX_UPDATE_SAMPLES, samplesToDo-base)}; + const size_t td{minz(MaxUpdateSamples, samplesToDo-base)}; - mGetSamples(mLfo, mIndex, mStep, td); + mGetSamples(mLfo.data(), mIndex, mStep, td); mIndex += static_cast<uint>(mStep * td); - mIndex &= WAVEFORM_FRACMASK; + mIndex &= WaveformFracMask; auto chandata = std::begin(mChans); for(const auto &input : samplesIn) @@ -304,30 +303,30 @@ void VmorpherState::process(const size_t samplesToDo, const al::span<const Float continue; } - auto& vowelA = chandata->mFormants[VOWEL_A_INDEX]; - auto& vowelB = chandata->mFormants[VOWEL_B_INDEX]; + const auto vowelA = al::span{chandata->mFormants[VowelAIndex]}; + const auto vowelB = al::span{chandata->mFormants[VowelBIndex]}; /* Process first vowel. */ std::fill_n(std::begin(mSampleBufferA), td, 0.0f); - vowelA[0].process(&input[base], mSampleBufferA, td); - vowelA[1].process(&input[base], mSampleBufferA, td); - vowelA[2].process(&input[base], mSampleBufferA, td); - vowelA[3].process(&input[base], mSampleBufferA, td); + vowelA[0].process(&input[base], mSampleBufferA.data(), td); + vowelA[1].process(&input[base], mSampleBufferA.data(), td); + vowelA[2].process(&input[base], mSampleBufferA.data(), td); + vowelA[3].process(&input[base], mSampleBufferA.data(), td); /* Process second vowel. */ std::fill_n(std::begin(mSampleBufferB), td, 0.0f); - vowelB[0].process(&input[base], mSampleBufferB, td); - vowelB[1].process(&input[base], mSampleBufferB, td); - vowelB[2].process(&input[base], mSampleBufferB, td); - vowelB[3].process(&input[base], mSampleBufferB, td); + vowelB[0].process(&input[base], mSampleBufferB.data(), td); + vowelB[1].process(&input[base], mSampleBufferB.data(), td); + vowelB[2].process(&input[base], mSampleBufferB.data(), td); + vowelB[3].process(&input[base], mSampleBufferB.data(), td); - alignas(16) float blended[MAX_UPDATE_SAMPLES]; + alignas(16) std::array<float,MaxUpdateSamples> blended; for(size_t i{0u};i < td;i++) blended[i] = lerpf(mSampleBufferA[i], mSampleBufferB[i], mLfo[i]); /* Now, mix the processed sound data to the output. */ - MixSamples({blended, td}, samplesOut[outidx].data()+base, chandata->mCurrentGain, - chandata->mTargetGain, samplesToDo-base); + MixSamples({blended.data(), td}, samplesOut[outidx].data()+base, + chandata->mCurrentGain, chandata->mTargetGain, samplesToDo-base); ++chandata; } diff --git a/alc/export_list.h b/alc/export_list.h index c5af1ab0..7856bdc8 100644 --- a/alc/export_list.h +++ b/alc/export_list.h @@ -16,7 +16,8 @@ struct FuncExport { const char *funcName; void *address; }; -#define DECL(x) { #x, reinterpret_cast<void*>(x) } +#define DECL(x) FuncExport{#x, reinterpret_cast<void*>(x)} +/* NOLINTNEXTLINE(*-avoid-c-arrays) Too large for std::array auto-deduction :( */ inline const FuncExport alcFunctions[]{ DECL(alcCreateContext), DECL(alcMakeContextCurrent), @@ -376,8 +377,9 @@ inline const FuncExport alcFunctions[]{ /* Extra functions */ DECL(alsoft_set_log_callback), +}; #ifdef ALSOFT_EAX -}, eaxFunctions[]{ +inline const std::array eaxFunctions{ DECL(EAXGet), DECL(EAXSet), DECL(EAXGetBufferMode), @@ -387,15 +389,16 @@ inline const FuncExport alcFunctions[]{ DECL(EAXSetDirect), DECL(EAXGetBufferModeDirect), DECL(EAXSetBufferModeDirect), -#endif }; +#endif #undef DECL struct EnumExport { const char *enumName; int value; }; -#define DECL(x) { #x, (x) } +#define DECL(x) EnumExport{#x, (x)} +/* NOLINTNEXTLINE(*-avoid-c-arrays) Too large for std::array auto-deduction :( */ inline const EnumExport alcEnumerations[]{ DECL(ALC_INVALID), DECL(ALC_FALSE), @@ -901,15 +904,16 @@ inline const EnumExport alcEnumerations[]{ DECL(AL_AUXILIARY_EFFECT_SLOT_EXT), DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT), +}; #ifdef ALSOFT_EAX -}, eaxEnumerations[]{ +inline const std::array eaxEnumerations{ DECL(AL_EAX_RAM_SIZE), DECL(AL_EAX_RAM_FREE), DECL(AL_STORAGE_AUTOMATIC), DECL(AL_STORAGE_HARDWARE), DECL(AL_STORAGE_ACCESSIBLE), -#endif // ALSOFT_EAX }; +#endif // ALSOFT_EAX #undef DECL #endif /* ALC_EXPORT_LIST_H */ diff --git a/alc/inprogext.h b/alc/inprogext.h index c5b09f13..a150af86 100644 --- a/alc/inprogext.h +++ b/alc/inprogext.h @@ -1,6 +1,7 @@ #ifndef INPROGEXT_H #define INPROGEXT_H +/* NOLINTBEGIN */ #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" @@ -398,24 +399,24 @@ ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer, AL #endif #endif -#ifndef AL_EXT_int32 -#define AL_EXT_int32 +#ifndef AL_EXT_32bit_formats +#define AL_EXT_32bit_formats #define AL_FORMAT_MONO_I32 0x1202 /* Same as AL_FORMAT_MONO32 */ #define AL_FORMAT_STEREO_I32 0x1203 /* Same as AL_FORMAT_STEREO32 */ #define AL_FORMAT_REAR_I32 0x19DB -#define AL_FORMAT_QUAD_I32 0x19DC -#define AL_FORMAT_51CHN_I32 0x19DD -#define AL_FORMAT_61CHN_I32 0x19DE -#define AL_FORMAT_71CHN_I32 0x19DF -#define AL_FORMAT_UHJ2CHN_I32 0x19E0 -#define AL_FORMAT_UHJ3CHN_I32 0x19E1 -#define AL_FORMAT_UHJ4CHN_I32 0x19E2 - -#define AL_FORMAT_REAR_FLOAT32 0x19E3 -#define AL_FORMAT_QUAD_FLOAT32 0x19E4 -#define AL_FORMAT_51CHN_FLOAT32 0x19E5 -#define AL_FORMAT_61CHN_FLOAT32 0x19E6 -#define AL_FORMAT_71CHN_FLOAT32 0x19E7 +#define AL_FORMAT_REAR_FLOAT32 0x19DC +#define AL_FORMAT_QUAD_I32 0x19DD +#define AL_FORMAT_QUAD_FLOAT32 0x19DE +#define AL_FORMAT_51CHN_I32 0x19DF +#define AL_FORMAT_51CHN_FLOAT32 0x19E0 +#define AL_FORMAT_61CHN_I32 0x19E1 +#define AL_FORMAT_61CHN_FLOAT32 0x19E2 +#define AL_FORMAT_71CHN_I32 0x19E3 +#define AL_FORMAT_71CHN_FLOAT32 0x19E4 + +#define AL_FORMAT_UHJ2CHN_I32 0x19E5 +#define AL_FORMAT_UHJ3CHN_I32 0x19E6 +#define AL_FORMAT_UHJ4CHN_I32 0x19E7 #endif /* Non-standard exports. Not part of any extension. */ @@ -436,5 +437,6 @@ void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALenum pname, AL #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* INPROGEXT_H */ diff --git a/alc/panning.cpp b/alc/panning.cpp index b512a42a..3b40687e 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -227,10 +227,10 @@ struct DecoderConfig<DualBand, 0> { using DecoderView = DecoderConfig<DualBand, 0>; -void InitNearFieldCtrl(ALCdevice *device, float ctrl_dist, uint order, bool is3d) +void InitNearFieldCtrl(ALCdevice *device, const float ctrl_dist, const uint order, const bool is3d) { - static const uint chans_per_order2d[MaxAmbiOrder+1]{ 1, 2, 2, 2 }; - static const uint chans_per_order3d[MaxAmbiOrder+1]{ 1, 3, 5, 7 }; + static const std::array<uint,MaxAmbiOrder+1> chans_per_order2d{{1, 2, 2, 2}}; + static const std::array<uint,MaxAmbiOrder+1> chans_per_order3d{{1, 3, 5, 7}}; /* NFC is only used when AvgSpeakerDist is greater than 0. */ if(!device->getConfigValueBool("decoder", "nfc", false) || !(ctrl_dist > 0.0f)) @@ -243,13 +243,13 @@ void InitNearFieldCtrl(ALCdevice *device, float ctrl_dist, uint order, bool is3d (device->AvgSpeakerDist * static_cast<float>(device->Frequency))}; device->mNFCtrlFilter.init(w1); - auto iter = std::copy_n(is3d ? chans_per_order3d : chans_per_order2d, order+1u, + auto iter = std::copy_n(is3d ? chans_per_order3d.begin() : chans_per_order2d.begin(), order+1u, std::begin(device->NumChannelsPerOrder)); std::fill(iter, std::end(device->NumChannelsPerOrder), 0u); } void InitDistanceComp(ALCdevice *device, const al::span<const Channel> channels, - const al::span<const float,MAX_OUTPUT_CHANNELS> dists) + const al::span<const float,MaxOutputChannels> dists) { const float maxdist{std::accumulate(std::begin(dists), std::end(dists), 0.0f, maxf)}; @@ -329,7 +329,7 @@ constexpr auto GetAmbiLayout(DevAmbiLayout layouttype) noexcept DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf, - DecoderConfig<DualBand, MAX_OUTPUT_CHANNELS> &decoder) + DecoderConfig<DualBand,MaxOutputChannels> &decoder) { DecoderView ret{}; @@ -361,8 +361,7 @@ DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf, const auto lfmatrix = conf->LFMatrix; uint chan_count{0}; - using const_speaker_span = al::span<const AmbDecConf::SpeakerConf>; - for(auto &speaker : const_speaker_span{conf->Speakers.get(), conf->NumSpeakers}) + for(auto &speaker : al::span<const AmbDecConf::SpeakerConf>{conf->Speakers}) { /* NOTE: AmbDec does not define any standard speaker names, however * for this to work we have to by able to find the output channel @@ -708,120 +707,126 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= void InitHrtfPanning(ALCdevice *device) { - constexpr float Deg180{al::numbers::pi_v<float>}; - constexpr float Deg_90{Deg180 / 2.0f /* 90 degrees*/}; - constexpr float Deg_45{Deg_90 / 2.0f /* 45 degrees*/}; - constexpr float Deg135{Deg_45 * 3.0f /*135 degrees*/}; - constexpr float Deg_21{3.648638281e-01f /* 20~ 21 degrees*/}; - constexpr float Deg_32{5.535743589e-01f /* 31~ 32 degrees*/}; - constexpr float Deg_35{6.154797087e-01f /* 35~ 36 degrees*/}; - constexpr float Deg_58{1.017221968e+00f /* 58~ 59 degrees*/}; - constexpr float Deg_69{1.205932499e+00f /* 69~ 70 degrees*/}; - constexpr float Deg111{1.935660155e+00f /*110~111 degrees*/}; - constexpr float Deg122{2.124370686e+00f /*121~122 degrees*/}; - static const AngularPoint AmbiPoints1O[]{ - { EvRadians{ Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{-Deg135} }, - { EvRadians{ Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{ Deg135} }, - { EvRadians{-Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{-Deg135} }, - { EvRadians{-Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{ Deg135} }, - }, AmbiPoints2O[]{ - { EvRadians{-Deg_32}, AzRadians{ 0.0f} }, - { EvRadians{ 0.0f}, AzRadians{ Deg_58} }, - { EvRadians{ Deg_58}, AzRadians{ Deg_90} }, - { EvRadians{ Deg_32}, AzRadians{ 0.0f} }, - { EvRadians{ 0.0f}, AzRadians{ Deg122} }, - { EvRadians{-Deg_58}, AzRadians{-Deg_90} }, - { EvRadians{-Deg_32}, AzRadians{ Deg180} }, - { EvRadians{ 0.0f}, AzRadians{-Deg122} }, - { EvRadians{ Deg_58}, AzRadians{-Deg_90} }, - { EvRadians{ Deg_32}, AzRadians{ Deg180} }, - { EvRadians{ 0.0f}, AzRadians{-Deg_58} }, - { EvRadians{-Deg_58}, AzRadians{ Deg_90} }, - }, AmbiPoints3O[]{ - { EvRadians{ Deg_69}, AzRadians{-Deg_90} }, - { EvRadians{ Deg_69}, AzRadians{ Deg_90} }, - { EvRadians{-Deg_69}, AzRadians{-Deg_90} }, - { EvRadians{-Deg_69}, AzRadians{ Deg_90} }, - { EvRadians{ 0.0f}, AzRadians{-Deg_69} }, - { EvRadians{ 0.0f}, AzRadians{-Deg111} }, - { EvRadians{ 0.0f}, AzRadians{ Deg_69} }, - { EvRadians{ 0.0f}, AzRadians{ Deg111} }, - { EvRadians{ Deg_21}, AzRadians{ 0.0f} }, - { EvRadians{ Deg_21}, AzRadians{ Deg180} }, - { EvRadians{-Deg_21}, AzRadians{ 0.0f} }, - { EvRadians{-Deg_21}, AzRadians{ Deg180} }, - { EvRadians{ Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{-Deg135} }, - { EvRadians{ Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{ Deg135} }, - { EvRadians{-Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{-Deg135} }, - { EvRadians{-Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{ Deg135} }, + static constexpr float Deg180{al::numbers::pi_v<float>}; + static constexpr float Deg_90{Deg180 / 2.0f /* 90 degrees*/}; + static constexpr float Deg_45{Deg_90 / 2.0f /* 45 degrees*/}; + static constexpr float Deg135{Deg_45 * 3.0f /*135 degrees*/}; + static constexpr float Deg_21{3.648638281e-01f /* 20~ 21 degrees*/}; + static constexpr float Deg_32{5.535743589e-01f /* 31~ 32 degrees*/}; + static constexpr float Deg_35{6.154797087e-01f /* 35~ 36 degrees*/}; + static constexpr float Deg_58{1.017221968e+00f /* 58~ 59 degrees*/}; + static constexpr float Deg_69{1.205932499e+00f /* 69~ 70 degrees*/}; + static constexpr float Deg111{1.935660155e+00f /*110~111 degrees*/}; + static constexpr float Deg122{2.124370686e+00f /*121~122 degrees*/}; + static constexpr std::array AmbiPoints1O{ + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg135}}, }; - static const float AmbiMatrix1O[][MaxAmbiChannels]{ - { 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f }, - { 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f }, - { 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f }, - { 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f }, - { 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f }, - { 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f }, - { 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f }, - { 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f }, - }, AmbiMatrix2O[][MaxAmbiChannels]{ - { 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }, - { 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }, - { 8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }, - { 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }, - { 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }, - { 8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }, - { 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }, - { 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }, - { 8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }, - { 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }, - { 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }, - { 8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }, - }, AmbiMatrix3O[][MaxAmbiChannels]{ - { 5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }, - { 5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }, - { 5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }, - { 5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }, - { 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }, - { 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }, - { 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }, - { 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }, - { 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f, }, - { 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f, }, - { 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f, }, - { 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f, }, - { 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }, - { 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }, - { 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }, - { 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }, - { 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }, - { 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }, - { 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }, - { 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }, + static constexpr std::array AmbiPoints2O{ + AngularPoint{EvRadians{-Deg_32}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg_58}}, + AngularPoint{EvRadians{ Deg_58}, AzRadians{ Deg_90}}, + AngularPoint{EvRadians{ Deg_32}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg122}}, + AngularPoint{EvRadians{-Deg_58}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{-Deg_32}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg122}}, + AngularPoint{EvRadians{ Deg_58}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{ Deg_32}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg_58}}, + AngularPoint{EvRadians{-Deg_58}, AzRadians{ Deg_90}}, }; - static const float AmbiOrderHFGain1O[MaxAmbiOrder+1]{ + static constexpr std::array AmbiPoints3O{ + AngularPoint{EvRadians{ Deg_69}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{ Deg_69}, AzRadians{ Deg_90}}, + AngularPoint{EvRadians{-Deg_69}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{-Deg_69}, AzRadians{ Deg_90}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg_69}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg111}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg_69}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg111}}, + AngularPoint{EvRadians{ Deg_21}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{ Deg_21}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{-Deg_21}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{-Deg_21}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg135}}, + }; + static constexpr std::array AmbiMatrix1O{ + std::array<float,MaxAmbiChannels>{{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f}}, + std::array<float,MaxAmbiChannels>{{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f}}, + std::array<float,MaxAmbiChannels>{{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f}}, + std::array<float,MaxAmbiChannels>{{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f}}, + std::array<float,MaxAmbiChannels>{{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f}}, + std::array<float,MaxAmbiChannels>{{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f}}, + std::array<float,MaxAmbiChannels>{{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f}}, + std::array<float,MaxAmbiChannels>{{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f}}, + }; + static constexpr std::array AmbiMatrix2O{ + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f}}, + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}}, + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}}, + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f}}, + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}}, + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}}, + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f}}, + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}}, + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}}, + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f}}, + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}}, + std::array<float,MaxAmbiChannels>{{8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}}, + }; + static constexpr std::array AmbiMatrix3O{ + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f}}, + std::array<float,MaxAmbiChannels>{{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f}}, + }; + static constexpr std::array<float,MaxAmbiOrder+1> AmbiOrderHFGain1O{ /*ENRGY*/ 2.000000000e+00f, 1.154700538e+00f - }, AmbiOrderHFGain2O[MaxAmbiOrder+1]{ + }; + static constexpr std::array<float,MaxAmbiOrder+1> AmbiOrderHFGain2O{ /*ENRGY*/ 1.825741858e+00f, 1.414213562e+00f, 7.302967433e-01f /*AMP 1.000000000e+00f, 7.745966692e-01f, 4.000000000e-01f*/ /*RMS 9.128709292e-01f, 7.071067812e-01f, 3.651483717e-01f*/ - }, AmbiOrderHFGain3O[MaxAmbiOrder+1]{ + }; + static constexpr std::array<float,MaxAmbiOrder+1> AmbiOrderHFGain3O{ /*ENRGY 1.865086714e+00f, 1.606093894e+00f, 1.142055301e+00f, 5.683795528e-01f*/ /*AMP*/ 1.000000000e+00f, 8.611363116e-01f, 6.123336207e-01f, 3.047469850e-01f /*RMS 8.340921354e-01f, 7.182670250e-01f, 5.107426573e-01f, 2.541870634e-01f*/ }; - static_assert(std::size(AmbiPoints1O) == std::size(AmbiMatrix1O), "First-Order Ambisonic HRTF mismatch"); - static_assert(std::size(AmbiPoints2O) == std::size(AmbiMatrix2O), "Second-Order Ambisonic HRTF mismatch"); - static_assert(std::size(AmbiPoints3O) == std::size(AmbiMatrix3O), "Third-Order Ambisonic HRTF mismatch"); + static_assert(AmbiPoints1O.size() == AmbiMatrix1O.size(), "First-Order Ambisonic HRTF mismatch"); + static_assert(AmbiPoints2O.size() == AmbiMatrix2O.size(), "Second-Order Ambisonic HRTF mismatch"); + static_assert(AmbiPoints3O.size() == AmbiMatrix3O.size(), "Third-Order Ambisonic HRTF mismatch"); /* A 700hz crossover frequency provides tighter sound imaging at the sweet * spot with ambisonic decoding, as the distance between the ears is closer @@ -841,18 +846,18 @@ void InitHrtfPanning(ALCdevice *device) */ device->mRenderMode = RenderMode::Hrtf; uint ambi_order{1}; - if(auto modeopt = device->configValue<std::string>(nullptr, "hrtf-mode")) + if(auto modeopt = device->configValue<std::string>({}, "hrtf-mode")) { struct HrtfModeEntry { - char name[8]; + char name[7]; /* NOLINT(*-avoid-c-arrays) */ RenderMode mode; uint order; }; - static const HrtfModeEntry hrtf_modes[]{ - { "full", RenderMode::Hrtf, 1 }, - { "ambi1", RenderMode::Normal, 1 }, - { "ambi2", RenderMode::Normal, 2 }, - { "ambi3", RenderMode::Normal, 3 }, + static constexpr std::array hrtf_modes{ + HrtfModeEntry{"full", RenderMode::Hrtf, 1}, + HrtfModeEntry{"ambi1", RenderMode::Normal, 1}, + HrtfModeEntry{"ambi2", RenderMode::Normal, 2}, + HrtfModeEntry{"ambi3", RenderMode::Normal, 3}, }; const char *mode{modeopt->c_str()}; @@ -882,9 +887,9 @@ void InitHrtfPanning(ALCdevice *device) device->mHrtfName.c_str()); bool perHrirMin{false}; - al::span<const AngularPoint> AmbiPoints{AmbiPoints1O}; - const float (*AmbiMatrix)[MaxAmbiChannels]{AmbiMatrix1O}; - al::span<const float,MaxAmbiOrder+1> AmbiOrderHFGain{AmbiOrderHFGain1O}; + auto AmbiPoints = al::span{AmbiPoints1O}.subspan(0); + auto AmbiMatrix = al::span{AmbiMatrix1O}.subspan(0); + auto AmbiOrderHFGain = al::span{AmbiOrderHFGain1O}; if(ambi_order >= 3) { perHrirMin = true; @@ -903,7 +908,7 @@ void InitHrtfPanning(ALCdevice *device) const size_t count{AmbiChannelsFromOrder(ambi_order)}; std::transform(AmbiIndex::FromACN.begin(), AmbiIndex::FromACN.begin()+count, - std::begin(device->Dry.AmbiMap), + device->Dry.AmbiMap.begin(), [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; } ); AllocChannels(device, count, device->channelsFromFmt()); @@ -969,9 +974,9 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin break; } - std::unique_ptr<DecoderConfig<DualBand,MAX_OUTPUT_CHANNELS>> decoder_store; + std::unique_ptr<DecoderConfig<DualBand,MaxOutputChannels>> decoder_store; DecoderView decoder{}; - float speakerdists[MAX_OUTPUT_CHANNELS]{}; + std::array<float,MaxOutputChannels> speakerdists{}; auto load_config = [device,&decoder_store,&decoder,&speakerdists](const char *config) { AmbDecConf conf{}; @@ -981,10 +986,10 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin ERR(" %s\n", err->c_str()); return false; } - else if(conf.NumSpeakers > MAX_OUTPUT_CHANNELS) + else if(conf.Speakers.size() > MaxOutputChannels) { - ERR("Unsupported decoder speaker count %zu (max %d)\n", conf.NumSpeakers, - MAX_OUTPUT_CHANNELS); + ERR("Unsupported decoder speaker count %zu (max %zu)\n", conf.Speakers.size(), + MaxOutputChannels); return false; } else if(conf.ChanMask > Ambi3OrderMask) @@ -998,7 +1003,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin conf.Description.c_str()); device->mXOverFreq = clampf(conf.XOverFreq, 100.0f, 1000.0f); - decoder_store = std::make_unique<DecoderConfig<DualBand,MAX_OUTPUT_CHANNELS>>(); + decoder_store = std::make_unique<DecoderConfig<DualBand,MaxOutputChannels>>(); decoder = MakeDecoderView(device, &conf, *decoder_store); for(size_t i{0};i < decoder.mChannels.size();++i) speakerdists[i] = conf.Speakers[i].Distance; @@ -1019,7 +1024,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin const bool stablize{device->RealOut.ChannelIndex[FrontCenter] != InvalidChannelIndex && device->RealOut.ChannelIndex[FrontLeft] != InvalidChannelIndex && device->RealOut.ChannelIndex[FrontRight] != InvalidChannelIndex - && device->getConfigValueBool(nullptr, "front-stablizer", false) != 0}; + && device->getConfigValueBool({}, "front-stablizer", false) != 0}; const bool hqdec{device->getConfigValueBool("decoder", "hq-mode", true) != 0}; InitPanning(device, hqdec, stablize, decoder); if(decoder) @@ -1088,7 +1093,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin HrtfStore *hrtf{device->mHrtf.get()}; device->mIrSize = hrtf->mIrSize; - if(auto hrtfsizeopt = device->configValue<uint>(nullptr, "hrtf-size")) + if(auto hrtfsizeopt = device->configValue<uint>({}, "hrtf-size")) { if(*hrtfsizeopt > 0 && *hrtfsizeopt < device->mIrSize) device->mIrSize = maxu(*hrtfsizeopt, MinIrLength); @@ -1127,13 +1132,13 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin device->mRenderMode = RenderMode::Pairwise; if(device->Type != DeviceType::Loopback) { - if(auto cflevopt = device->configValue<int>(nullptr, "cf_level")) + if(auto cflevopt = device->configValue<int>({}, "cf_level")) { if(*cflevopt > 0 && *cflevopt <= 6) { - device->Bs2b = std::make_unique<bs2b>(); - bs2b_set_params(device->Bs2b.get(), *cflevopt, - static_cast<int>(device->Frequency)); + auto bs2b = std::make_unique<Bs2b::bs2b>(); + bs2b->set_params(*cflevopt, static_cast<int>(device->Frequency)); + device->Bs2b = std::move(bs2b); TRACE("BS2B enabled\n"); InitPanning(device); device->PostProcess = &ALCdevice::ProcessBs2b; diff --git a/alsoftrc.sample b/alsoftrc.sample index 42802184..278d78e5 100644 --- a/alsoftrc.sample +++ b/alsoftrc.sample @@ -406,7 +406,7 @@ # Renders samples directly in the real-time processing callback. This allows # for lower latency and less overall CPU utilization, but can increase the # risk of underruns when increasing the amount of work the mixer needs to do. -#rt-mix = true +#rt-mix = false ## ## PulseAudio backend stuff diff --git a/common/albit.h b/common/albit.h index 82a4a00d..d54a189c 100644 --- a/common/albit.h +++ b/common/albit.h @@ -1,6 +1,7 @@ #ifndef AL_BIT_H #define AL_BIT_H +#include <array> #include <cstdint> #include <cstring> #include <limits> @@ -17,9 +18,9 @@ std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From && std::is_trivially_copyable_v<To>, To> bit_cast(const From &src) noexcept { - alignas(To) char dst[sizeof(To)]; - std::memcpy(&dst[0], &src, sizeof(To)); - return *std::launder(reinterpret_cast<To*>(&dst[0])); + alignas(To) std::array<char,sizeof(To)> dst; + std::memcpy(dst.data(), &src, sizeof(To)); + return *std::launder(reinterpret_cast<To*>(dst.data())); } #ifdef __BYTE_ORDER__ diff --git a/common/alcomplex.cpp b/common/alcomplex.cpp index 82a0c43c..f2de5f51 100644 --- a/common/alcomplex.cpp +++ b/common/alcomplex.cpp @@ -155,6 +155,8 @@ void complex_fft(const al::span<std::complex<double>> buffer, const double sign) } else { + assert(log2_size < 32); + for(size_t idx{1u};idx < fftsize-1;++idx) { size_t revidx{idx}; diff --git a/common/almalloc.cpp b/common/almalloc.cpp deleted file mode 100644 index ad1dc6be..00000000 --- a/common/almalloc.cpp +++ /dev/null @@ -1,61 +0,0 @@ - -#include "config.h" - -#include "almalloc.h" - -#include <cassert> -#include <cstddef> -#include <cstdlib> -#include <cstring> -#include <memory> -#ifdef HAVE_MALLOC_H -#include <malloc.h> -#endif - - -void *al_malloc(size_t alignment, size_t size) -{ - assert((alignment & (alignment-1)) == 0); - alignment = std::max(alignment, alignof(std::max_align_t)); - -#if defined(HAVE_POSIX_MEMALIGN) - void *ret{}; - if(posix_memalign(&ret, alignment, size) == 0) - return ret; - return nullptr; -#elif defined(HAVE__ALIGNED_MALLOC) - return _aligned_malloc(size, alignment); -#else - size_t total_size{size + alignment-1 + sizeof(void*)}; - void *base{std::malloc(total_size)}; - if(base != nullptr) - { - void *aligned_ptr{static_cast<char*>(base) + sizeof(void*)}; - total_size -= sizeof(void*); - - std::align(alignment, size, aligned_ptr, total_size); - *(static_cast<void**>(aligned_ptr)-1) = base; - base = aligned_ptr; - } - return base; -#endif -} - -void *al_calloc(size_t alignment, size_t size) -{ - void *ret{al_malloc(alignment, size)}; - if(ret) std::memset(ret, 0, size); - return ret; -} - -void al_free(void *ptr) noexcept -{ -#if defined(HAVE_POSIX_MEMALIGN) - std::free(ptr); -#elif defined(HAVE__ALIGNED_MALLOC) - _aligned_free(ptr); -#else - if(ptr != nullptr) - std::free(*(static_cast<void**>(ptr) - 1)); -#endif -} diff --git a/common/almalloc.h b/common/almalloc.h index 873473ca..3b9965e6 100644 --- a/common/almalloc.h +++ b/common/almalloc.h @@ -13,39 +13,17 @@ #include "pragmadefs.h" -void al_free(void *ptr) noexcept; -[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]] -void *al_malloc(size_t alignment, size_t size); -[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]] -void *al_calloc(size_t alignment, size_t size); +namespace gsl { +template<typename T> using owner = T; +}; -#define DISABLE_ALLOC() \ +#define DISABLE_ALLOC \ void *operator new(size_t) = delete; \ void *operator new[](size_t) = delete; \ void operator delete(void*) noexcept = delete; \ void operator delete[](void*) noexcept = delete; -#define DEF_NEWDEL(T) \ - void *operator new(size_t size) \ - { \ - static_assert(&operator new == &T::operator new, \ - "Incorrect container type specified"); \ - if(void *ret{al_malloc(alignof(T), size)}) \ - return ret; \ - throw std::bad_alloc(); \ - } \ - void *operator new[](size_t size) { return operator new(size); } \ - void operator delete(void *block) noexcept { al_free(block); } \ - void operator delete[](void *block) noexcept { operator delete(block); } - -#define DEF_PLACE_NEWDEL() \ - void *operator new(size_t /*size*/, void *ptr) noexcept { return ptr; } \ - void *operator new[](size_t /*size*/, void *ptr) noexcept { return ptr; } \ - void operator delete(void *block, void*) noexcept { al_free(block); } \ - void operator delete(void *block) noexcept { al_free(block); } \ - void operator delete[](void *block, void*) noexcept { al_free(block); } \ - void operator delete[](void *block) noexcept { al_free(block); } enum FamCount : size_t { }; @@ -58,54 +36,59 @@ enum FamCount : size_t { }; sizeof(T)); \ } \ \ - void *operator new(size_t /*size*/, FamCount count) \ + gsl::owner<void*> operator new(size_t /*size*/, FamCount count) \ { \ - if(void *ret{al_malloc(alignof(T), T::Sizeof(count))}) \ - return ret; \ - throw std::bad_alloc(); \ + const auto alignment = std::align_val_t{alignof(T)}; \ + return ::operator new[](T::Sizeof(count), alignment); \ } \ + void operator delete(gsl::owner<void*> block, FamCount) noexcept \ + { ::operator delete[](block, std::align_val_t{alignof(T)}); } \ + void operator delete(gsl::owner<void*> block) noexcept \ + { ::operator delete[](block, std::align_val_t{alignof(T)}); } \ void *operator new[](size_t /*size*/) = delete; \ - void operator delete(void *block, FamCount) { al_free(block); } \ - void operator delete(void *block) noexcept { al_free(block); } \ void operator delete[](void* /*block*/) = delete; namespace al { -template<typename T, std::size_t Align=alignof(T)> +template<typename T, std::size_t AlignV=alignof(T)> struct allocator { - static constexpr std::size_t alignment{std::max(Align, alignof(T))}; - - using value_type = T; - using reference = T&; - using const_reference = const T&; - using pointer = T*; - using const_pointer = const T*; + static constexpr auto Alignment = std::max(AlignV, alignof(T)); + static constexpr auto AlignVal = std::align_val_t{Alignment}; + + using value_type = std::remove_cv_t<std::remove_reference_t<T>>; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using is_always_equal = std::true_type; - template<typename U> + template<typename U, std::enable_if_t<alignof(U) <= Alignment,bool> = true> struct rebind { - using other = allocator<U, Align>; + using other = allocator<U,Alignment>; }; constexpr explicit allocator() noexcept = default; template<typename U, std::size_t N> - constexpr explicit allocator(const allocator<U,N>&) noexcept { } + constexpr explicit allocator(const allocator<U,N>&) noexcept + { static_assert(Alignment == allocator<U,N>::Alignment); } - T *allocate(std::size_t n) + gsl::owner<T*> allocate(std::size_t n) { if(n > std::numeric_limits<std::size_t>::max()/sizeof(T)) throw std::bad_alloc(); - if(auto p = al_malloc(alignment, n*sizeof(T))) return static_cast<T*>(p); - throw std::bad_alloc(); + return static_cast<gsl::owner<T*>>(::operator new[](n*sizeof(T), AlignVal)); } - void deallocate(T *p, std::size_t) noexcept { al_free(p); } + void deallocate(gsl::owner<T*> p, std::size_t) noexcept + { ::operator delete[](gsl::owner<void*>{p}, AlignVal); } }; template<typename T, std::size_t N, typename U, std::size_t M> -constexpr bool operator==(const allocator<T,N>&, const allocator<U,M>&) noexcept { return true; } +constexpr bool operator==(const allocator<T,N>&, const allocator<U,M>&) noexcept +{ return allocator<T,N>::Alignment == allocator<U,M>::Alignment; } template<typename T, std::size_t N, typename U, std::size_t M> -constexpr bool operator!=(const allocator<T,N>&, const allocator<U,M>&) noexcept { return false; } +constexpr bool operator!=(const allocator<T,N>&, const allocator<U,M>&) noexcept +{ return allocator<T,N>::Alignment != allocator<U,M>::Alignment; } template<typename T> @@ -124,124 +107,15 @@ constexpr auto to_address(const T &p) noexcept template<typename T, typename ...Args> constexpr T* construct_at(T *ptr, Args&& ...args) - noexcept(std::is_nothrow_constructible<T, Args...>::value) -{ return ::new(static_cast<void*>(ptr)) T{std::forward<Args>(args)...}; } - - -/* Storage for flexible array data. This is trivially destructible if type T is - * trivially destructible. - */ -template<typename T, size_t alignment, bool = std::is_trivially_destructible<T>::value> -struct FlexArrayStorage { - const size_t mSize; - union { - char mDummy; - alignas(alignment) T mArray[1]; - }; - - static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept - { - const size_t len{sizeof(T)*count}; - return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base; - } - - FlexArrayStorage(size_t size) : mSize{size} - { std::uninitialized_default_construct_n(mArray, mSize); } - ~FlexArrayStorage() = default; - - FlexArrayStorage(const FlexArrayStorage&) = delete; - FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; -}; - -template<typename T, size_t alignment> -struct FlexArrayStorage<T,alignment,false> { - const size_t mSize; - union { - char mDummy; - alignas(alignment) T mArray[1]; - }; - - static constexpr size_t Sizeof(size_t count, size_t base) noexcept - { - const size_t len{sizeof(T)*count}; - return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base; - } - - FlexArrayStorage(size_t size) : mSize{size} - { std::uninitialized_default_construct_n(mArray, mSize); } - ~FlexArrayStorage() { std::destroy_n(mArray, mSize); } - - FlexArrayStorage(const FlexArrayStorage&) = delete; - FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; -}; - -/* A flexible array type. Used either standalone or at the end of a parent - * struct, with placement new, to have a run-time-sized array that's embedded - * with its size. - */ -template<typename T, size_t alignment=alignof(T)> -struct FlexArray { - using element_type = T; - using value_type = std::remove_cv_t<T>; - using index_type = size_t; - using difference_type = ptrdiff_t; - - using pointer = T*; - using const_pointer = const T*; - using reference = T&; - using const_reference = const T&; - - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator<iterator>; - using const_reverse_iterator = std::reverse_iterator<const_iterator>; - - using Storage_t_ = FlexArrayStorage<element_type,alignment>; - - Storage_t_ mStore; - - static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept - { return Storage_t_::Sizeof(count, base); } - static std::unique_ptr<FlexArray> Create(index_type count) - { - void *ptr{al_calloc(alignof(FlexArray), Sizeof(count))}; - return std::unique_ptr<FlexArray>{al::construct_at(static_cast<FlexArray*>(ptr), count)}; - } - - FlexArray(index_type size) : mStore{size} { } - ~FlexArray() = default; - - index_type size() const noexcept { return mStore.mSize; } - bool empty() const noexcept { return mStore.mSize == 0; } - - pointer data() noexcept { return mStore.mArray; } - const_pointer data() const noexcept { return mStore.mArray; } - - reference operator[](index_type i) noexcept { return mStore.mArray[i]; } - const_reference operator[](index_type i) const noexcept { return mStore.mArray[i]; } - - reference front() noexcept { return mStore.mArray[0]; } - const_reference front() const noexcept { return mStore.mArray[0]; } - - reference back() noexcept { return mStore.mArray[mStore.mSize-1]; } - const_reference back() const noexcept { return mStore.mArray[mStore.mSize-1]; } - - iterator begin() noexcept { return mStore.mArray; } - const_iterator begin() const noexcept { return mStore.mArray; } - const_iterator cbegin() const noexcept { return mStore.mArray; } - iterator end() noexcept { return mStore.mArray + mStore.mSize; } - const_iterator end() const noexcept { return mStore.mArray + mStore.mSize; } - const_iterator cend() const noexcept { return mStore.mArray + mStore.mSize; } - - reverse_iterator rbegin() noexcept { return end(); } - const_reverse_iterator rbegin() const noexcept { return end(); } - const_reverse_iterator crbegin() const noexcept { return cend(); } - reverse_iterator rend() noexcept { return begin(); } - const_reverse_iterator rend() const noexcept { return begin(); } - const_reverse_iterator crend() const noexcept { return cbegin(); } - - DEF_PLACE_NEWDEL() -}; + noexcept(std::is_nothrow_constructible_v<T, Args...>) +{ + /* NOLINTBEGIN(cppcoreguidelines-owning-memory) construct_at doesn't + * necessarily handle the address from an owner, while placement new + * expects to. + */ + return ::new(static_cast<void*>(ptr)) T{std::forward<Args>(args)...}; + /* NOLINTEND(cppcoreguidelines-owning-memory) */ +} } // namespace al diff --git a/common/alnumbers.h b/common/alnumbers.h index e92d7b87..7abe6b32 100644 --- a/common/alnumbers.h +++ b/common/alnumbers.h @@ -3,9 +3,7 @@ #include <utility> -namespace al { - -namespace numbers { +namespace al::numbers { namespace detail_ { template<typename T> @@ -29,8 +27,6 @@ inline constexpr auto inv_pi = inv_pi_v<double>; inline constexpr auto sqrt2 = sqrt2_v<double>; inline constexpr auto sqrt3 = sqrt3_v<double>; -} // namespace numbers - -} // namespace al +} // namespace al::numbers #endif /* COMMON_ALNUMBERS_H */ diff --git a/common/alnumeric.h b/common/alnumeric.h index 6281b012..cb8704b2 100644 --- a/common/alnumeric.h +++ b/common/alnumeric.h @@ -245,7 +245,7 @@ inline float fast_roundf(float f) noexcept /* Integral limit, where sub-integral precision is not available for * floats. */ - static constexpr float ilim[2]{ + static constexpr std::array ilim{ 8388608.0f /* 0x1.0p+23 */, -8388608.0f /* -0x1.0p+23 */ }; diff --git a/common/alsem.h b/common/alsem.h index 9f72d1c6..90b39319 100644 --- a/common/alsem.h +++ b/common/alsem.h @@ -24,7 +24,7 @@ class semaphore { #else using native_type = sem_t; #endif - native_type mSem; + native_type mSem{}; public: semaphore(unsigned int initial=0); diff --git a/common/alspan.h b/common/alspan.h index 341ce7c8..822915da 100644 --- a/common/alspan.h +++ b/common/alspan.h @@ -5,6 +5,7 @@ #include <cstddef> #include <initializer_list> #include <iterator> +#include <stdexcept> #include <type_traits> #include "almalloc.h" @@ -12,7 +13,7 @@ namespace al { -constexpr size_t dynamic_extent{static_cast<size_t>(-1)}; +inline constexpr size_t dynamic_extent{static_cast<size_t>(-1)}; template<typename T, size_t E=dynamic_extent> class span; @@ -23,31 +24,31 @@ namespace detail_ { template<typename T, size_t E> struct is_span_<span<T,E>> : std::true_type { }; template<typename T> - constexpr bool is_span_v = is_span_<std::remove_cv_t<T>>::value; + inline constexpr bool is_span_v = is_span_<std::remove_cv_t<T>>::value; template<typename T> struct is_std_array_ : std::false_type { }; template<typename T, size_t N> struct is_std_array_<std::array<T,N>> : std::true_type { }; template<typename T> - constexpr bool is_std_array_v = is_std_array_<std::remove_cv_t<T>>::value; + inline constexpr bool is_std_array_v = is_std_array_<std::remove_cv_t<T>>::value; template<typename T, typename = void> - constexpr bool has_size_and_data = false; + inline constexpr bool has_size_and_data = false; template<typename T> - constexpr bool has_size_and_data<T, + inline constexpr bool has_size_and_data<T, std::void_t<decltype(std::size(std::declval<T>())),decltype(std::data(std::declval<T>()))>> = true; template<typename C> - constexpr bool is_valid_container_type = !is_span_v<C> && !is_std_array_v<C> + inline constexpr bool is_valid_container_type = !is_span_v<C> && !is_std_array_v<C> && !std::is_array<C>::value && has_size_and_data<C>; template<typename T, typename U> - constexpr bool is_array_compatible = std::is_convertible<T(*)[],U(*)[]>::value; + inline constexpr bool is_array_compatible = std::is_convertible<T(*)[],U(*)[]>::value; /* NOLINT(*-avoid-c-arrays) */ template<typename C, typename T> - constexpr bool is_valid_container = is_valid_container_type<C> + inline constexpr bool is_valid_container = is_valid_container_type<C> && is_array_compatible<std::remove_pointer_t<decltype(std::data(std::declval<C&>()))>,T>; } // namespace detail_ @@ -79,9 +80,9 @@ public: constexpr explicit span(U iter, index_type) : mData{::al::to_address(iter)} { } template<typename U, typename V, REQUIRES(!std::is_convertible<V,size_t>::value)> constexpr explicit span(U first, V) : mData{::al::to_address(first)} - {} + { } - constexpr span(type_identity_t<element_type> (&arr)[E]) noexcept + constexpr span(type_identity_t<element_type> (&arr)[E]) noexcept /* NOLINT(*-avoid-c-arrays) */ : span{std::data(arr), std::size(arr)} { } constexpr span(std::array<value_type,E> &arr) noexcept @@ -107,43 +108,43 @@ public: constexpr span& operator=(const span &rhs) noexcept = default; - constexpr reference front() const { return *mData; } - constexpr reference back() const { return *(mData+E-1); } - constexpr reference operator[](index_type idx) const { return mData[idx]; } - constexpr pointer data() const noexcept { return mData; } + [[nodiscard]] constexpr auto front() const -> reference { return mData[0]; } + [[nodiscard]] constexpr auto back() const -> reference { return mData[E-1]; } + [[nodiscard]] constexpr auto operator[](index_type idx) const -> reference { return mData[idx]; } + [[nodiscard]] constexpr auto data() const noexcept -> pointer { return mData; } - constexpr index_type size() const noexcept { return E; } - constexpr index_type size_bytes() const noexcept { return E * sizeof(value_type); } - constexpr bool empty() const noexcept { return E == 0; } + [[nodiscard]] constexpr auto size() const noexcept -> index_type { return E; } + [[nodiscard]] constexpr auto size_bytes() const noexcept -> index_type { return E * sizeof(value_type); } + [[nodiscard]] constexpr auto empty() const noexcept -> bool { return E == 0; } - constexpr iterator begin() const noexcept { return mData; } - constexpr iterator end() const noexcept { return mData+E; } - constexpr const_iterator cbegin() const noexcept { return mData; } - constexpr const_iterator cend() const noexcept { return mData+E; } + [[nodiscard]] constexpr auto begin() const noexcept -> iterator { return mData; } + [[nodiscard]] constexpr auto end() const noexcept -> iterator { return mData+E; } + [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator { return mData; } + [[nodiscard]] constexpr auto cend() const noexcept -> const_iterator { return mData+E; } - constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - constexpr const_reverse_iterator crbegin() const noexcept + [[nodiscard]] constexpr auto rbegin() const noexcept -> reverse_iterator { return reverse_iterator{end()}; } + [[nodiscard]] constexpr auto rend() const noexcept -> reverse_iterator { return reverse_iterator{begin()}; } + [[nodiscard]] constexpr auto crbegin() const noexcept -> const_reverse_iterator { return const_reverse_iterator{cend()}; } - constexpr const_reverse_iterator crend() const noexcept + [[nodiscard]] constexpr auto crend() const noexcept -> const_reverse_iterator { return const_reverse_iterator{cbegin()}; } template<size_t C> - constexpr span<element_type,C> first() const + [[nodiscard]] constexpr auto first() const -> span<element_type,C> { static_assert(E >= C, "New size exceeds original capacity"); return span<element_type,C>{mData, C}; } template<size_t C> - constexpr span<element_type,C> last() const + [[nodiscard]] constexpr auto last() const -> span<element_type,C> { static_assert(E >= C, "New size exceeds original capacity"); return span<element_type,C>{mData+(E-C), C}; } template<size_t O, size_t C> - constexpr auto subspan() const -> std::enable_if_t<C!=dynamic_extent,span<element_type,C>> + [[nodiscard]] constexpr auto subspan() const -> std::enable_if_t<C!=dynamic_extent,span<element_type,C>> { static_assert(E >= O, "Offset exceeds extent"); static_assert(E-O >= C, "New size exceeds original capacity"); @@ -151,7 +152,7 @@ public: } template<size_t O, size_t C=dynamic_extent> - constexpr auto subspan() const -> std::enable_if_t<C==dynamic_extent,span<element_type,E-O>> + [[nodiscard]] constexpr auto subspan() const -> std::enable_if_t<C==dynamic_extent,span<element_type,E-O>> { static_assert(E >= O, "Offset exceeds extent"); return span<element_type,E-O>{mData+O, E-O}; @@ -161,10 +162,10 @@ public: * defining the specialization. As a result, these methods need to be * defined later. */ - constexpr span<element_type,dynamic_extent> first(size_t count) const; - constexpr span<element_type,dynamic_extent> last(size_t count) const; - constexpr span<element_type,dynamic_extent> subspan(size_t offset, - size_t count=dynamic_extent) const; + [[nodiscard]] constexpr auto first(size_t count) const -> span<element_type,dynamic_extent>; + [[nodiscard]] constexpr auto last(size_t count) const -> span<element_type,dynamic_extent>; + [[nodiscard]] constexpr auto subspan(size_t offset, + size_t count=dynamic_extent) const -> span<element_type,dynamic_extent>; private: pointer mData{nullptr}; @@ -192,14 +193,14 @@ public: constexpr span() noexcept = default; template<typename U> - constexpr span(U iter, index_type count) : mData{::al::to_address(iter)}, mDataEnd{::al::to_address(iter) + count} + constexpr span(U iter, index_type count) : mData{::al::to_address(iter)}, mDataLength{count} { } template<typename U, typename V, REQUIRES(!std::is_convertible<V,size_t>::value)> constexpr span(U first, V last) : span{::al::to_address(first), static_cast<size_t>(last - first)} { } template<size_t N> - constexpr span(type_identity_t<element_type> (&arr)[N]) noexcept + constexpr span(type_identity_t<element_type> (&arr)[N]) noexcept /* NOLINT(*-avoid-c-arrays) */ : span{std::data(arr), std::size(arr)} { } template<size_t N> @@ -221,83 +222,122 @@ public: constexpr span& operator=(const span &rhs) noexcept = default; - constexpr reference front() const { return *mData; } - constexpr reference back() const { return *(mDataEnd-1); } - constexpr reference operator[](index_type idx) const { return mData[idx]; } - constexpr pointer data() const noexcept { return mData; } + [[nodiscard]] constexpr auto front() const -> reference { return mData[0]; } + [[nodiscard]] constexpr auto back() const -> reference { return mData[mDataLength-1]; } + [[nodiscard]] constexpr auto operator[](index_type idx) const -> reference { return mData[idx]; } + [[nodiscard]] constexpr auto data() const noexcept -> pointer { return mData; } - constexpr index_type size() const noexcept { return static_cast<index_type>(mDataEnd-mData); } - constexpr index_type size_bytes() const noexcept - { return static_cast<index_type>(mDataEnd-mData) * sizeof(value_type); } - constexpr bool empty() const noexcept { return mData == mDataEnd; } + [[nodiscard]] constexpr auto size() const noexcept -> index_type { return mDataLength; } + [[nodiscard]] constexpr auto size_bytes() const noexcept -> index_type { return mDataLength * sizeof(value_type); } + [[nodiscard]] constexpr auto empty() const noexcept -> bool { return mDataLength == 0; } - constexpr iterator begin() const noexcept { return mData; } - constexpr iterator end() const noexcept { return mDataEnd; } - constexpr const_iterator cbegin() const noexcept { return mData; } - constexpr const_iterator cend() const noexcept { return mDataEnd; } + [[nodiscard]] constexpr auto begin() const noexcept -> iterator { return mData; } + [[nodiscard]] constexpr auto end() const noexcept -> iterator { return mData+mDataLength; } + [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator { return mData; } + [[nodiscard]] constexpr auto cend() const noexcept -> const_iterator { return mData+mDataLength; } - constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - constexpr const_reverse_iterator crbegin() const noexcept + [[nodiscard]] constexpr auto rbegin() const noexcept -> reverse_iterator { return reverse_iterator{end()}; } + [[nodiscard]] constexpr auto rend() const noexcept -> reverse_iterator { return reverse_iterator{begin()}; } + [[nodiscard]] constexpr auto crbegin() const noexcept -> const_reverse_iterator { return const_reverse_iterator{cend()}; } - constexpr const_reverse_iterator crend() const noexcept + [[nodiscard]] constexpr auto crend() const noexcept -> const_reverse_iterator { return const_reverse_iterator{cbegin()}; } template<size_t C> - constexpr span<element_type,C> first() const - { return span<element_type,C>{mData, C}; } + [[nodiscard]] constexpr auto first() const -> span<element_type,C> + { + if(C > mDataLength) + throw std::out_of_range{"Subspan count out of range"}; + return span<element_type,C>{mData, C}; + } - constexpr span first(size_t count) const - { return (count >= size()) ? *this : span{mData, mData+count}; } + [[nodiscard]] constexpr auto first(size_t count) const -> span + { + if(count > mDataLength) + throw std::out_of_range{"Subspan count out of range"}; + return span{mData, count}; + } template<size_t C> - constexpr span<element_type,C> last() const - { return span<element_type,C>{mDataEnd-C, C}; } + [[nodiscard]] constexpr auto last() const -> span<element_type,C> + { + if(C > mDataLength) + throw std::out_of_range{"Subspan count out of range"}; + return span<element_type,C>{mData+mDataLength-C, C}; + } - constexpr span last(size_t count) const - { return (count >= size()) ? *this : span{mDataEnd-count, mDataEnd}; } + [[nodiscard]] constexpr auto last(size_t count) const -> span + { + if(count > mDataLength) + throw std::out_of_range{"Subspan count out of range"}; + return span{mData+mDataLength-count, count}; + } template<size_t O, size_t C> - constexpr auto subspan() const -> std::enable_if_t<C!=dynamic_extent,span<element_type,C>> - { return span<element_type,C>{mData+O, C}; } + [[nodiscard]] constexpr auto subspan() const -> std::enable_if_t<C!=dynamic_extent,span<element_type,C>> + { + if(O > mDataLength) + throw std::out_of_range{"Subspan offset out of range"}; + if(C > mDataLength-O) + throw std::out_of_range{"Subspan length out of range"}; + return span<element_type,C>{mData+O, C}; + } template<size_t O, size_t C=dynamic_extent> - constexpr auto subspan() const -> std::enable_if_t<C==dynamic_extent,span<element_type,C>> - { return span<element_type,C>{mData+O, mDataEnd}; } + [[nodiscard]] constexpr auto subspan() const -> std::enable_if_t<C==dynamic_extent,span<element_type,C>> + { + if(O > mDataLength) + throw std::out_of_range{"Subspan offset out of range"}; + return span<element_type,C>{mData+O, mDataLength-O}; + } - constexpr span subspan(size_t offset, size_t count=dynamic_extent) const + [[nodiscard]] constexpr auto subspan(size_t offset, size_t count=dynamic_extent) const -> span { - return (offset > size()) ? span{} : - (count >= size()-offset) ? span{mData+offset, mDataEnd} : - span{mData+offset, mData+offset+count}; + if(offset > mDataLength) + throw std::out_of_range{"Subspan offset out of range"}; + if(count != dynamic_extent) + { + if(count > mDataLength-offset) + throw std::out_of_range{"Subspan length out of range"}; + return span{mData+offset, count}; + } + return span{mData+offset, mDataLength-offset}; } private: pointer mData{nullptr}; - pointer mDataEnd{nullptr}; + index_type mDataLength{0}; }; template<typename T, size_t E> -constexpr inline auto span<T,E>::first(size_t count) const -> span<element_type,dynamic_extent> +[[nodiscard]] constexpr inline auto span<T,E>::first(size_t count) const -> span<element_type,dynamic_extent> { - return (count >= size()) ? span<element_type>{mData, extent} : - span<element_type>{mData, count}; + if(count > size()) + throw std::out_of_range{"Subspan count out of range"}; + return span<element_type>{mData, count}; } template<typename T, size_t E> -constexpr inline auto span<T,E>::last(size_t count) const -> span<element_type,dynamic_extent> +[[nodiscard]] constexpr inline auto span<T,E>::last(size_t count) const -> span<element_type,dynamic_extent> { - return (count >= size()) ? span<element_type>{mData, extent} : - span<element_type>{mData+extent-count, count}; + if(count > size()) + throw std::out_of_range{"Subspan count out of range"}; + return span<element_type>{mData+size()-count, count}; } template<typename T, size_t E> -constexpr inline auto span<T,E>::subspan(size_t offset, size_t count) const +[[nodiscard]] constexpr inline auto span<T,E>::subspan(size_t offset, size_t count) const -> span<element_type,dynamic_extent> { - return (offset > size()) ? span<element_type>{} : - (count >= size()-offset) ? span<element_type>{mData+offset, mData+extent} : - span<element_type>{mData+offset, mData+offset+count}; + if(offset > size()) + throw std::out_of_range{"Subspan offset out of range"}; + if(count != dynamic_extent) + { + if(count > size()-offset) + throw std::out_of_range{"Subspan length out of range"}; + return span{mData+offset, count}; + } + return span{mData+offset, size()-offset}; } @@ -305,7 +345,7 @@ template<typename T, typename EndOrSize> span(T, EndOrSize) -> span<std::remove_reference_t<decltype(*std::declval<T&>())>>; template<typename T, std::size_t N> -span(T (&)[N]) -> span<T, N>; +span(T (&)[N]) -> span<T, N>; /* NOLINT(*-avoid-c-arrays) */ template<typename T, std::size_t N> span(std::array<T, N>&) -> span<T, N>; diff --git a/common/althrd_setname.cpp b/common/althrd_setname.cpp index 22d33092..21197ba0 100644 --- a/common/althrd_setname.cpp +++ b/common/althrd_setname.cpp @@ -60,7 +60,7 @@ using setname_t4 = int(*)(pthread_t, const char*, void*); { func(pthread_self(), name); } [[maybe_unused]] void setname_caller(setname_t4 func, const char *name) -{ func(pthread_self(), "%s", static_cast<void*>(const_cast<char*>(name))); } +{ func(pthread_self(), "%s", const_cast<char*>(name)); /* NOLINT(*-const-cast) */ } } // namespace diff --git a/common/atomic.h b/common/atomic.h index 5e9b04c6..e85c4f76 100644 --- a/common/atomic.h +++ b/common/atomic.h @@ -2,17 +2,16 @@ #define AL_ATOMIC_H #include <atomic> +#include <memory> +#include "almalloc.h" -using RefCount = std::atomic<unsigned int>; - -inline void InitRef(RefCount &ref, unsigned int value) -{ ref.store(value, std::memory_order_relaxed); } -inline unsigned int ReadRef(RefCount &ref) -{ return ref.load(std::memory_order_acquire); } -inline unsigned int IncrementRef(RefCount &ref) +template<typename T> +auto IncrementRef(std::atomic<T> &ref) noexcept { return ref.fetch_add(1u, std::memory_order_acq_rel)+1u; } -inline unsigned int DecrementRef(RefCount &ref) + +template<typename T> +auto DecrementRef(std::atomic<T> &ref) noexcept { return ref.fetch_sub(1u, std::memory_order_acq_rel)-1u; } @@ -30,4 +29,75 @@ inline void AtomicReplaceHead(std::atomic<T> &head, T newhead) std::memory_order_acq_rel, std::memory_order_acquire)); } +namespace al { + +template<typename T, typename D=std::default_delete<T>> +class atomic_unique_ptr { + std::atomic<gsl::owner<T*>> mPointer{}; + + using unique_ptr_t = std::unique_ptr<T,D>; + +public: + atomic_unique_ptr() = default; + atomic_unique_ptr(const atomic_unique_ptr&) = delete; + explicit atomic_unique_ptr(std::nullptr_t) noexcept { } + explicit atomic_unique_ptr(gsl::owner<T*> ptr) noexcept : mPointer{ptr} { } + explicit atomic_unique_ptr(unique_ptr_t&& rhs) noexcept : mPointer{rhs.release()} { } + ~atomic_unique_ptr() + { + if(auto ptr = mPointer.exchange(nullptr, std::memory_order_relaxed)) + D{}(ptr); + } + + auto operator=(const atomic_unique_ptr&) -> atomic_unique_ptr& = delete; + auto operator=(std::nullptr_t) noexcept -> atomic_unique_ptr& + { + if(auto ptr = mPointer.exchange(nullptr)) + D{}(ptr); + return *this; + } + auto operator=(unique_ptr_t&& rhs) noexcept -> atomic_unique_ptr& + { + if(auto ptr = mPointer.exchange(rhs.release())) + D{}(ptr); + return *this; + } + + [[nodiscard]] + auto load(std::memory_order m=std::memory_order_seq_cst) const noexcept -> T* + { return mPointer.load(m); } + void store(std::nullptr_t, std::memory_order m=std::memory_order_seq_cst) noexcept + { + if(auto oldptr = mPointer.exchange(nullptr, m)) + D{}(oldptr); + } + void store(gsl::owner<T*> ptr, std::memory_order m=std::memory_order_seq_cst) noexcept + { + if(auto oldptr = mPointer.exchange(ptr, m)) + D{}(oldptr); + } + void store(unique_ptr_t&& ptr, std::memory_order m=std::memory_order_seq_cst) noexcept + { + if(auto oldptr = mPointer.exchange(ptr.release(), m)) + D{}(oldptr); + } + + [[nodiscard]] + auto exchange(std::nullptr_t, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t + { return unique_ptr_t{mPointer.exchange(nullptr, m)}; } + [[nodiscard]] + auto exchange(gsl::owner<T*> ptr, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t + { return unique_ptr_t{mPointer.exchange(ptr, m)}; } + [[nodiscard]] + auto exchange(std::unique_ptr<T>&& ptr, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t + { return unique_ptr_t{mPointer.exchange(ptr.release(), m)}; } + + [[nodiscard]] + auto is_lock_free() const noexcept -> bool { return mPointer.is_lock_free(); } + + static constexpr auto is_always_lock_free = std::atomic<gsl::owner<T*>>::is_always_lock_free; +}; + +} // namespace al + #endif /* AL_ATOMIC_H */ diff --git a/common/dynload.cpp b/common/dynload.cpp index 86c36e00..333a9435 100644 --- a/common/dynload.cpp +++ b/common/dynload.cpp @@ -3,13 +3,12 @@ #include "dynload.h" -#include "albit.h" -#include "strutils.h" - #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include <windows.h> +#include "strutils.h" + void *LoadLib(const char *name) { std::wstring wname{utf8_to_wstr(name)}; @@ -18,7 +17,7 @@ void *LoadLib(const char *name) void CloseLib(void *handle) { FreeLibrary(static_cast<HMODULE>(handle)); } void *GetSymbol(void *handle, const char *name) -{ return al::bit_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name)); } +{ return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name)); } #elif defined(HAVE_DLFCN_H) diff --git a/common/flexarray.h b/common/flexarray.h new file mode 100644 index 00000000..b8077988 --- /dev/null +++ b/common/flexarray.h @@ -0,0 +1,125 @@ +#ifndef AL_FLEXARRAY_H +#define AL_FLEXARRAY_H + +#include <algorithm> +#include <cstddef> +#include <stdexcept> +#include <type_traits> + +#include "almalloc.h" +#include "alspan.h" + +namespace al { + +/* Storage for flexible array data. This is trivially destructible if type T is + * trivially destructible. + */ +template<typename T, size_t alignment, bool = std::is_trivially_destructible<T>::value> +struct alignas(std::max(alignment, alignof(al::span<T>))) FlexArrayStorage : al::span<T> { + static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept + { return sizeof(FlexArrayStorage) + sizeof(T)*count + base; } + + FlexArrayStorage(size_t size) noexcept(std::is_nothrow_constructible_v<T>) + : al::span<T>{::new(static_cast<void*>(this+1)) T[size], size} + { } + ~FlexArrayStorage() = default; + + FlexArrayStorage(const FlexArrayStorage&) = delete; + FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; +}; + +template<typename T, size_t alignment> +struct alignas(std::max(alignment, alignof(al::span<T>))) FlexArrayStorage<T,alignment,false> : al::span<T> { + static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept + { return sizeof(FlexArrayStorage) + sizeof(T)*count + base; } + + FlexArrayStorage(size_t size) noexcept(std::is_nothrow_constructible_v<T>) + : al::span<T>{::new(static_cast<void*>(this+1)) T[size], size} + { } + ~FlexArrayStorage() { std::destroy(this->begin(), this->end()); } + + FlexArrayStorage(const FlexArrayStorage&) = delete; + FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; +}; + +/* A flexible array type. Used either standalone or at the end of a parent + * struct, to have a run-time-sized array that's embedded with its size. Should + * be used delicately, ensuring there's no additional data after the FlexArray + * member. + */ +template<typename T, size_t Align=alignof(T)> +struct FlexArray { + using element_type = T; + using value_type = std::remove_cv_t<T>; + using index_type = size_t; + using difference_type = ptrdiff_t; + + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + static constexpr size_t alignment{std::max(alignof(T), Align)}; + using Storage_t_ = FlexArrayStorage<element_type,alignment>; + + const Storage_t_ mStore; + + static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept + { return Storage_t_::Sizeof(count, base); } + static std::unique_ptr<FlexArray> Create(index_type count) + { return std::unique_ptr<FlexArray>{new(FamCount{count}) FlexArray{count}}; } + + FlexArray(index_type size) noexcept(std::is_nothrow_constructible_v<Storage_t_,index_type>) + : mStore{size} + { } + ~FlexArray() = default; + + [[nodiscard]] auto size() const noexcept -> index_type { return mStore.size(); } + [[nodiscard]] auto empty() const noexcept -> bool { return mStore.empty(); } + + [[nodiscard]] auto data() noexcept -> pointer { return mStore.data(); } + [[nodiscard]] auto data() const noexcept -> const_pointer { return mStore.data(); } + + [[nodiscard]] auto operator[](index_type i) noexcept -> reference { return mStore[i]; } + [[nodiscard]] auto operator[](index_type i) const noexcept -> const_reference { return mStore[i]; } + + [[nodiscard]] auto front() noexcept -> reference { return mStore.front(); } + [[nodiscard]] auto front() const noexcept -> const_reference { return mStore.front(); } + + [[nodiscard]] auto back() noexcept -> reference { return mStore.back(); } + [[nodiscard]] auto back() const noexcept -> const_reference { return mStore.back(); } + + [[nodiscard]] auto begin() noexcept -> iterator { return mStore.begin(); } + [[nodiscard]] auto begin() const noexcept -> const_iterator { return mStore.cbegin(); } + [[nodiscard]] auto cbegin() const noexcept -> const_iterator { return mStore.cbegin(); } + [[nodiscard]] auto end() noexcept -> iterator { return mStore.end(); } + [[nodiscard]] auto end() const noexcept -> const_iterator { return mStore.cend(); } + [[nodiscard]] auto cend() const noexcept -> const_iterator { return mStore.cend(); } + + [[nodiscard]] auto rbegin() noexcept -> reverse_iterator { return end(); } + [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator { return cend(); } + [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator { return cend(); } + [[nodiscard]] auto rend() noexcept -> reverse_iterator { return begin(); } + [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator { return cbegin(); } + [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator { return cbegin(); } + + gsl::owner<void*> operator new(size_t, FamCount count) + { return ::operator new[](Sizeof(count), std::align_val_t{alignof(FlexArray)}); } + void operator delete(gsl::owner<void*> block, FamCount) noexcept + { ::operator delete[](block, std::align_val_t{alignof(FlexArray)}); } + void operator delete(gsl::owner<void*> block) noexcept + { ::operator delete[](block, std::align_val_t{alignof(FlexArray)}); } + + void *operator new(size_t size) = delete; + void *operator new[](size_t size) = delete; + void operator delete[](void *block) = delete; +}; + +} // namespace al + +#endif /* AL_FLEXARRAY_H */ diff --git a/common/intrusive_ptr.h b/common/intrusive_ptr.h index 27075347..0152b92a 100644 --- a/common/intrusive_ptr.h +++ b/common/intrusive_ptr.h @@ -11,7 +11,7 @@ namespace al { template<typename T> class intrusive_ref { - RefCount mRef{1u}; + std::atomic<unsigned int> mRef{1u}; public: unsigned int add_ref() noexcept { return IncrementRef(mRef); } @@ -81,9 +81,9 @@ public: explicit operator bool() const noexcept { return mPtr != nullptr; } - T& operator*() const noexcept { return *mPtr; } - T* operator->() const noexcept { return mPtr; } - T* get() const noexcept { return mPtr; } + [[nodiscard]] auto operator*() const noexcept -> T& { return *mPtr; } + [[nodiscard]] auto operator->() const noexcept -> T* { return mPtr; } + [[nodiscard]] auto get() const noexcept -> T* { return mPtr; } void reset(T *ptr=nullptr) noexcept { diff --git a/common/opthelpers.h b/common/opthelpers.h index dc43ccdb..ae2611da 100644 --- a/common/opthelpers.h +++ b/common/opthelpers.h @@ -42,7 +42,7 @@ #elif HAS_BUILTIN(__builtin_unreachable) #define ASSUME(x) do { if(x) break; __builtin_unreachable(); } while(0) #else -#define ASSUME(x) ((void)0) +#define ASSUME(x) (static_cast<void>(0)) #endif /* This shouldn't be needed since unknown attributes are ignored, but older diff --git a/common/pffft.cpp b/common/pffft.cpp index 71f71fa6..46d97918 100644 --- a/common/pffft.cpp +++ b/common/pffft.cpp @@ -58,16 +58,17 @@ #include "pffft.h" #include <array> -#include <assert.h> +#include <cassert> #include <cmath> +#include <cstdio> +#include <cstdlib> #include <cstring> -#include <stdio.h> -#include <stdlib.h> #include <vector> #include "albit.h" #include "almalloc.h" #include "alnumbers.h" +#include "alnumeric.h" #include "alspan.h" #include "opthelpers.h" @@ -90,8 +91,8 @@ using uint = unsigned int; * Altivec support macros */ #if defined(__ppc__) || defined(__ppc64__) || defined(__powerpc__) || defined(__powerpc64__) -typedef vector float v4sf; -#define SIMD_SZ 4 +using v4sf = vector float; +constexpr uint SimdSize{4}; #define VZERO() ((vector float) vec_splat_u8(0)) #define VMUL(a,b) vec_madd(a,b, VZERO()) #define VADD vec_add @@ -142,19 +143,27 @@ force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept (defined(_M_IX86_FP) && _M_IX86_FP >= 1) #include <xmmintrin.h> -typedef __m128 v4sf; -#define SIMD_SZ 4 // 4 floats by simd vector -- this is pretty much hardcoded in the preprocess/finalize functions anyway so you will have to work if you want to enable AVX with its 256-bit vectors. +using v4sf = __m128; +/* 4 floats by simd vector -- this is pretty much hardcoded in the preprocess/ + * finalize functions anyway so you will have to work if you want to enable AVX + * with its 256-bit vectors. + */ +constexpr uint SimdSize{4}; #define VZERO _mm_setzero_ps #define VMUL _mm_mul_ps #define VADD _mm_add_ps -#define VMADD(a,b,c) _mm_add_ps(_mm_mul_ps(a,b), c) +force_inline v4sf vmadd(const v4sf a, const v4sf b, const v4sf c) noexcept +{ return _mm_add_ps(_mm_mul_ps(a,b), c); } +#define VMADD vmadd #define VSUB _mm_sub_ps #define LD_PS1 _mm_set1_ps #define VSET4 _mm_setr_ps -#define VINSERT0(v, a) _mm_move_ss((v), _mm_set_ss(a)) +force_inline v4sf vinsert0(const v4sf v, const float a) noexcept +{ return _mm_move_ss(v, _mm_set_ss(a)); } +#define VINSERT0 vinsert0 #define VEXTRACT0 _mm_cvtss_f32 -force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +force_inline void interleave2(const v4sf in1, const v4sf in2, v4sf &out1, v4sf &out2) noexcept { v4sf tmp{_mm_unpacklo_ps(in1, in2)}; out2 = _mm_unpackhi_ps(in1, in2); @@ -170,7 +179,7 @@ force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noex force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept { _MM_TRANSPOSE4_PS(x0, x1, x2, x3); } -#define VSWAPHL(a,b) _mm_shuffle_ps(b, a, _MM_SHUFFLE(3,2,1,0)) +#define VSWAPHL(a,b) _mm_shuffle_ps((b), (a), _MM_SHUFFLE(3,2,1,0)) /* * ARM NEON support macros @@ -178,8 +187,8 @@ force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept #elif defined(__ARM_NEON) || defined(__aarch64__) || defined(__arm64) #include <arm_neon.h> -typedef float32x4_t v4sf; -#define SIMD_SZ 4 +using v4sf = float32x4_t; +constexpr uint SimdSize{4}; #define VZERO() vdupq_n_f32(0) #define VMUL vmulq_f32 #define VADD vaddq_f32 @@ -238,7 +247,7 @@ force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept #elif defined(__GNUC__) using v4sf [[gnu::vector_size(16), gnu::aligned(16)]] = float; -#define SIMD_SZ 4 +constexpr uint SimdSize{4}; #define VZERO() v4sf{0,0,0,0} #define VMUL(a,b) ((a) * (b)) #define VADD(a,b) ((a) + (b)) @@ -297,8 +306,8 @@ force_inline v4sf vswaphl(v4sf a, v4sf b) noexcept // fallback mode for situations where SIMD is not available, use scalar mode instead #ifdef PFFFT_SIMD_DISABLE -typedef float v4sf; -#define SIMD_SZ 1 +using v4sf = float; +constexpr uint SimdSize{1}; #define VZERO() 0.f #define VMUL(a,b) ((a)*(b)) #define VADD(a,b) ((a)+(b)) @@ -309,7 +318,7 @@ typedef float v4sf; inline bool valigned(const float *ptr) noexcept { - static constexpr uintptr_t alignmask{SIMD_SZ*4 - 1}; + static constexpr uintptr_t alignmask{SimdSize*4 - 1}; return (reinterpret_cast<uintptr_t>(ptr) & alignmask) == 0; } @@ -335,14 +344,14 @@ force_inline void vcplxmulconj(v4sf &ar, v4sf &ai, v4sf br, v4sf bi) noexcept [[maybe_unused]] void validate_pffft_simd() { using float4 = std::array<float,4>; - static constexpr float f[16]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + static constexpr std::array<float,16> f{{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}}; float4 a0_f, a1_f, a2_f, a3_f, t_f, u_f; v4sf a0_v, a1_v, a2_v, a3_v, t_v, u_v; - std::memcpy(&a0_v, f, 4*sizeof(float)); - std::memcpy(&a1_v, f+4, 4*sizeof(float)); - std::memcpy(&a2_v, f+8, 4*sizeof(float)); - std::memcpy(&a3_v, f+12, 4*sizeof(float)); + std::memcpy(&a0_v, f.data(), 4*sizeof(float)); + std::memcpy(&a1_v, f.data()+4, 4*sizeof(float)); + std::memcpy(&a2_v, f.data()+8, 4*sizeof(float)); + std::memcpy(&a3_v, f.data()+12, 4*sizeof(float)); t_v = VZERO(); t_f = al::bit_cast<float4>(t_v); printf("VZERO=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); assertv4(t, 0, 0, 0, 0); @@ -379,7 +388,9 @@ force_inline void vcplxmulconj(v4sf &ar, v4sf &ai, v4sf br, v4sf bi) noexcept #endif //!PFFFT_SIMD_DISABLE /* SSE and co like 16-bytes aligned pointers */ -#define MALLOC_V4SF_ALIGNMENT 64 // with a 64-byte alignment, we are even aligned on L2 cache lines... +/* with a 64-byte alignment, we are even aligned on L2 cache lines... */ +constexpr auto V4sfAlignment = size_t(64); +constexpr auto V4sfAlignVal = std::align_val_t(V4sfAlignment); /* passf2 and passb2 has been merged here, fsign = -1 for passf2, +1 for passb2 @@ -538,8 +549,8 @@ NOINLINE void passf5_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf const v4sf ti11{LD_PS1(0.951056516295154f*fsign)}; const v4sf ti12{LD_PS1(0.587785252292473f*fsign)}; -#define cc_ref(a_1,a_2) cc[(a_2-1)*ido + (a_1) + 1] -#define ch_ref(a_1,a_3) ch[(a_3-1)*l1*ido + (a_1) + 1] +#define cc_ref(a_1,a_2) cc[((a_2)-1)*ido + (a_1) + 1] +#define ch_ref(a_1,a_3) ch[((a_3)-1)*l1*ido + (a_1) + 1] assert(ido > 2); for(size_t k{0};k < l1;++k, cc += 5*ido, ch += ido) @@ -958,8 +969,8 @@ void radf5_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf * const v4sf tr12{LD_PS1(-0.809016994374947f)}; const v4sf ti12{LD_PS1(0.587785252292473f)}; -#define cc_ref(a_1,a_2,a_3) cc[((a_3)*l1 + (a_2))*ido + a_1] -#define ch_ref(a_1,a_2,a_3) ch[((a_3)*5 + (a_2))*ido + a_1] +#define cc_ref(a_1,a_2,a_3) cc[((a_3)*l1 + (a_2))*ido + (a_1)] +#define ch_ref(a_1,a_2,a_3) ch[((a_3)*5 + (a_2))*ido + (a_1)] /* Parameter adjustments */ ch -= 1 + ido * 6; @@ -1040,8 +1051,8 @@ void radb5_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf * const v4sf tr12{LD_PS1(-0.809016994374947f)}; const v4sf ti12{LD_PS1(0.587785252292473f)}; -#define cc_ref(a_1,a_2,a_3) cc[((a_3)*5 + (a_2))*ido + a_1] -#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1] +#define cc_ref(a_1,a_2,a_3) cc[((a_3)*5 + (a_2))*ido + (a_1)] +#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + (a_1)] /* Parameter adjustments */ ch -= 1 + ido*(1 + l1); @@ -1331,7 +1342,7 @@ uint decompose(const uint n, const al::span<uint,15> ifac, const al::span<const void rffti1_ps(const uint n, float *wa, const al::span<uint,15> ifac) { - static constexpr uint ntryh[]{4,2,3,5}; + static constexpr std::array ntryh{4u,2u,3u,5u}; const uint nf{decompose(n, ifac, ntryh)}; const double argh{2.0*al::numbers::pi / n}; @@ -1365,7 +1376,7 @@ void rffti1_ps(const uint n, float *wa, const al::span<uint,15> ifac) void cffti1_ps(const uint n, float *wa, const al::span<uint,15> ifac) { - static constexpr uint ntryh[]{5,3,4,2}; + static constexpr std::array ntryh{5u,3u,4u,2u}; const uint nf{decompose(n, ifac, ntryh)}; const double argh{2.0*al::numbers::pi / n}; @@ -1405,24 +1416,20 @@ void cffti1_ps(const uint n, float *wa, const al::span<uint,15> ifac) } // namespace -void *pffft_aligned_malloc(size_t nb_bytes) -{ return al_malloc(MALLOC_V4SF_ALIGNMENT, nb_bytes); } - -void pffft_aligned_free(void *p) { al_free(p); } - -int pffft_simd_size() { return SIMD_SZ; } - +/* NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding) */ struct PFFFT_Setup { - uint N; - uint Ncvec; // nb of complex simd vectors (N/4 if PFFFT_COMPLEX, N/8 if PFFFT_REAL) - std::array<uint,15> ifac; - pffft_transform_t transform; + uint N{}; + uint Ncvec{}; /* nb of complex simd vectors (N/4 if PFFFT_COMPLEX, N/8 if PFFFT_REAL) */ + std::array<uint,15> ifac{}; + pffft_transform_t transform{}; - float *twiddle; // N/4 elements - alignas(MALLOC_V4SF_ALIGNMENT) v4sf e[1]; // N/4*3 elements + float *twiddle{}; /* N/4 elements */ + al::span<v4sf> e; /* N/4*3 elements */ + + alignas(V4sfAlignment) std::byte end; }; -PFFFT_Setup *pffft_new_setup(unsigned int N, pffft_transform_t transform) +gsl::owner<PFFFT_Setup*> pffft_new_setup(unsigned int N, pffft_transform_t transform) { assert(transform == PFFFT_REAL || transform == PFFFT_COMPLEX); assert(N > 0); @@ -1431,50 +1438,53 @@ PFFFT_Setup *pffft_new_setup(unsigned int N, pffft_transform_t transform) * handle other cases (or maybe just switch to a scalar fft, I don't know..) */ if(transform == PFFFT_REAL) - assert((N%(2*SIMD_SZ*SIMD_SZ)) == 0); + assert((N%(2*SimdSize*SimdSize)) == 0); else - assert((N%(SIMD_SZ*SIMD_SZ)) == 0); + assert((N%(SimdSize*SimdSize)) == 0); - const uint Ncvec = (transform == PFFFT_REAL ? N/2 : N)/SIMD_SZ; - const size_t storelen{offsetof(PFFFT_Setup, e[0]) + (2u*Ncvec * sizeof(v4sf))}; + const uint Ncvec{(transform == PFFFT_REAL ? N/2 : N) / SimdSize}; - void *store{al_calloc(MALLOC_V4SF_ALIGNMENT, storelen)}; - if(!store) return nullptr; + const size_t storelen{std::max(offsetof(PFFFT_Setup, end) + 2_zu*Ncvec*sizeof(v4sf), + sizeof(PFFFT_Setup))}; + auto storage = static_cast<gsl::owner<std::byte*>>(::operator new[](storelen, V4sfAlignVal)); + al::span extrastore{&storage[offsetof(PFFFT_Setup, end)], 2_zu*Ncvec*sizeof(v4sf)}; - PFFFT_Setup *s{::new(store) PFFFT_Setup{}}; + gsl::owner<PFFFT_Setup*> s{::new(storage) PFFFT_Setup{}}; s->N = N; s->transform = transform; - /* nb of complex simd vectors */ s->Ncvec = Ncvec; - s->twiddle = reinterpret_cast<float*>(&s->e[2u*Ncvec*(SIMD_SZ-1)/SIMD_SZ]); - if constexpr(SIMD_SZ > 1) + const size_t ecount{2_zu*Ncvec*(SimdSize-1)/SimdSize}; + s->e = {std::launder(reinterpret_cast<v4sf*>(extrastore.data())), ecount}; + s->twiddle = std::launder(reinterpret_cast<float*>(&extrastore[ecount*sizeof(v4sf)])); + + if constexpr(SimdSize > 1) { - auto e = std::vector<float>(2u*Ncvec*(SIMD_SZ-1), 0.0f); + auto e = std::vector<float>(s->e.size()*SimdSize, 0.0f); for(size_t k{0};k < s->Ncvec;++k) { - const size_t i{k / SIMD_SZ}; - const size_t j{k % SIMD_SZ}; - for(size_t m{0};m < SIMD_SZ-1;++m) + const size_t i{k / SimdSize}; + const size_t j{k % SimdSize}; + for(size_t m{0};m < SimdSize-1;++m) { const double A{-2.0*al::numbers::pi*static_cast<double>((m+1)*k) / N}; - e[((i*3 + m)*2 + 0)*SIMD_SZ + j] = static_cast<float>(std::cos(A)); - e[((i*3 + m)*2 + 1)*SIMD_SZ + j] = static_cast<float>(std::sin(A)); + e[((i*3 + m)*2 + 0)*SimdSize + j] = static_cast<float>(std::cos(A)); + e[((i*3 + m)*2 + 1)*SimdSize + j] = static_cast<float>(std::sin(A)); } } - std::memcpy(s->e, e.data(), e.size()*sizeof(float)); + std::memcpy(s->e.data(), e.data(), e.size()*sizeof(float)); } if(transform == PFFFT_REAL) - rffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac); + rffti1_ps(N/SimdSize, s->twiddle, s->ifac); else - cffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac); + cffti1_ps(N/SimdSize, s->twiddle, s->ifac); /* check that N is decomposable with allowed prime factors */ size_t m{1}; for(size_t k{0};k < s->ifac[1];++k) m *= s->ifac[2+k]; - if(m != N/SIMD_SZ) + if(m != N/SimdSize) { pffft_destroy_setup(s); s = nullptr; @@ -1484,10 +1494,10 @@ PFFFT_Setup *pffft_new_setup(unsigned int N, pffft_transform_t transform) } -void pffft_destroy_setup(PFFFT_Setup *s) +void pffft_destroy_setup(gsl::owner<PFFFT_Setup*> s) noexcept { std::destroy_at(s); - al_free(s); + ::operator delete[](gsl::owner<void*>{s}, V4sfAlignVal); } #if !defined(PFFFT_SIMD_DISABLE) @@ -1537,7 +1547,7 @@ void pffft_cplx_finalize(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out, { assert(in != out); - const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks + const size_t dk{Ncvec/SimdSize}; // number of 4x4 matrix blocks for(size_t k{0};k < dk;++k) { v4sf r0{in[8*k+0]}, i0{in[8*k+1]}; @@ -1581,7 +1591,7 @@ void pffft_cplx_preprocess(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT ou { assert(in != out); - const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks + const size_t dk{Ncvec/SimdSize}; // number of 4x4 matrix blocks for(size_t k{0};k < dk;++k) { v4sf r0{in[8*k+0]}, i0{in[8*k+1]}; @@ -1674,12 +1684,12 @@ NOINLINE void pffft_real_finalize(const size_t Ncvec, const v4sf *in, v4sf *REST static constexpr float s{al::numbers::sqrt2_v<float>/2.0f}; assert(in != out); - const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks + const size_t dk{Ncvec/SimdSize}; // number of 4x4 matrix blocks /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */ const v4sf zero{VZERO()}; - const auto cr = al::bit_cast<std::array<float,SIMD_SZ>>(in[0]); - const auto ci = al::bit_cast<std::array<float,SIMD_SZ>>(in[Ncvec*2-1]); + const auto cr = al::bit_cast<std::array<float,SimdSize>>(in[0]); + const auto ci = al::bit_cast<std::array<float,SimdSize>>(in[Ncvec*2-1]); pffft_real_finalize_4x4(&zero, &zero, in+1, e, out); /* [cr0 cr1 cr2 cr3 ci0 ci1 ci2 ci3] @@ -1765,11 +1775,11 @@ NOINLINE void pffft_real_preprocess(const size_t Ncvec, const v4sf *in, v4sf *RE static constexpr float sqrt2{al::numbers::sqrt2_v<float>}; assert(in != out); - const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks + const size_t dk{Ncvec/SimdSize}; // number of 4x4 matrix blocks /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */ - std::array<float,SIMD_SZ> Xr, Xi; - for(size_t k{0};k < SIMD_SZ;++k) + std::array<float,SimdSize> Xr, Xi; + for(size_t k{0};k < SimdSize;++k) { Xr[k] = VEXTRACT0(in[2*k]); Xi[k] = VEXTRACT0(in[2*k + 1]); @@ -1813,7 +1823,7 @@ void pffft_transform_internal(const PFFFT_Setup *setup, const v4sf *vinput, v4sf const size_t Ncvec{setup->Ncvec}; const bool nf_odd{(setup->ifac[1]&1) != 0}; - v4sf *buff[2]{voutput, scratch}; + std::array buff{voutput, scratch}; bool ib{nf_odd != ordered}; if(direction == PFFFT_FORWARD) { @@ -1824,7 +1834,7 @@ void pffft_transform_internal(const PFFFT_Setup *setup, const v4sf *vinput, v4sf if(setup->transform == PFFFT_REAL) { ib = (rfftf1_ps(Ncvec*2, vinput, buff[ib], buff[!ib], setup->twiddle, setup->ifac) == buff[1]); - pffft_real_finalize(Ncvec, buff[ib], buff[!ib], setup->e); + pffft_real_finalize(Ncvec, buff[ib], buff[!ib], setup->e.data()); } else { @@ -1833,7 +1843,7 @@ void pffft_transform_internal(const PFFFT_Setup *setup, const v4sf *vinput, v4sf uninterleave2(vinput[k*2], vinput[k*2+1], tmp[k*2], tmp[k*2+1]); ib = (cfftf1_ps(Ncvec, buff[ib], buff[!ib], buff[ib], setup->twiddle, setup->ifac, -1.0f) == buff[1]); - pffft_cplx_finalize(Ncvec, buff[ib], buff[!ib], setup->e); + pffft_cplx_finalize(Ncvec, buff[ib], buff[!ib], setup->e.data()); } if(ordered) pffft_zreorder(setup, reinterpret_cast<float*>(buff[!ib]), @@ -1855,12 +1865,12 @@ void pffft_transform_internal(const PFFFT_Setup *setup, const v4sf *vinput, v4sf } if(setup->transform == PFFFT_REAL) { - pffft_real_preprocess(Ncvec, vinput, buff[ib], setup->e); + pffft_real_preprocess(Ncvec, vinput, buff[ib], setup->e.data()); ib = (rfftb1_ps(Ncvec*2, buff[ib], buff[0], buff[1], setup->twiddle, setup->ifac) == buff[1]); } else { - pffft_cplx_preprocess(Ncvec, vinput, buff[ib], setup->e); + pffft_cplx_preprocess(Ncvec, vinput, buff[ib], setup->e.data()); ib = (cfftf1_ps(Ncvec, buff[ib], buff[0], buff[1], setup->twiddle, setup->ifac, +1.0f) == buff[1]); for(size_t k{0};k < Ncvec;++k) interleave2(buff[ib][k*2], buff[ib][k*2+1], buff[ib][k*2], buff[ib][k*2+1]); @@ -1899,8 +1909,8 @@ void pffft_zreorder(const PFFFT_Setup *setup, const float *in, float *out, interleave2(vin[k*8 + 0], vin[k*8 + 1], vout[2*(0*dk + k) + 0], vout[2*(0*dk + k) + 1]); interleave2(vin[k*8 + 4], vin[k*8 + 5], vout[2*(2*dk + k) + 0], vout[2*(2*dk + k) + 1]); } - reversed_copy(dk, vin+2, 8, vout + N/SIMD_SZ/2); - reversed_copy(dk, vin+6, 8, vout + N/SIMD_SZ); + reversed_copy(dk, vin+2, 8, vout + N/SimdSize/2); + reversed_copy(dk, vin+6, 8, vout + N/SimdSize); } else { @@ -1909,8 +1919,8 @@ void pffft_zreorder(const PFFFT_Setup *setup, const float *in, float *out, uninterleave2(vin[2*(0*dk + k) + 0], vin[2*(0*dk + k) + 1], vout[k*8 + 0], vout[k*8 + 1]); uninterleave2(vin[2*(2*dk + k) + 0], vin[2*(2*dk + k) + 1], vout[k*8 + 4], vout[k*8 + 5]); } - unreversed_copy(dk, vin + N/SIMD_SZ/4, vout + N/SIMD_SZ - 6, -8); - unreversed_copy(dk, vin + 3*N/SIMD_SZ/4, vout + N/SIMD_SZ - 2, -8); + unreversed_copy(dk, vin + N/SimdSize/4, vout + N/SimdSize - 6, -8); + unreversed_copy(dk, vin + 3_uz*N/SimdSize/4, vout + N/SimdSize - 2, -8); } } else diff --git a/common/pffft.h b/common/pffft.h index 9cff9e54..cf356524 100644 --- a/common/pffft.h +++ b/common/pffft.h @@ -79,36 +79,32 @@ #ifndef PFFFT_H #define PFFFT_H -#include <stddef.h> // for size_t -#include <stdint.h> +#include <cstddef> +#include <cstdint> +#include <utility> + +#include "almalloc.h" -#ifdef __cplusplus -extern "C" { -#endif /* opaque struct holding internal stuff (precomputed twiddle factors) this * struct can be shared by many threads as it contains only read-only data. */ -typedef struct PFFFT_Setup PFFFT_Setup; - -#ifndef PFFFT_COMMON_ENUMS -#define PFFFT_COMMON_ENUMS +struct PFFFT_Setup; /* direction of the transform */ -typedef enum { PFFFT_FORWARD, PFFFT_BACKWARD } pffft_direction_t; +enum pffft_direction_t { PFFFT_FORWARD, PFFFT_BACKWARD }; /* type of transform */ -typedef enum { PFFFT_REAL, PFFFT_COMPLEX } pffft_transform_t; - -#endif +enum pffft_transform_t { PFFFT_REAL, PFFFT_COMPLEX }; /** * Prepare for performing transforms of size N -- the returned PFFFT_Setup * structure is read-only so it can safely be shared by multiple concurrent * threads. */ -PFFFT_Setup *pffft_new_setup(unsigned int N, pffft_transform_t transform); -void pffft_destroy_setup(PFFFT_Setup *setup); +[[gnu::malloc]] +gsl::owner<PFFFT_Setup*> pffft_new_setup(unsigned int N, pffft_transform_t transform); +void pffft_destroy_setup(gsl::owner<PFFFT_Setup*> setup) noexcept; /** * Perform a Fourier transform. The z-domain data is stored in the most @@ -174,19 +170,47 @@ void pffft_zconvolve_scale_accumulate(const PFFFT_Setup *setup, const float *dft */ void pffft_zconvolve_accumulate(const PFFFT_Setup *setup, const float *dft_a, const float *dft_b, float *dft_ab); -/** - * The float buffers must have the correct alignment (16-byte boundary on intel - * and powerpc). This function may be used to obtain such correctly aligned - * buffers. - */ -void *pffft_aligned_malloc(size_t nb_bytes); -void pffft_aligned_free(void *ptr); -/* Return 4 or 1 depending if vectorization was enable when building pffft.cpp. */ -int pffft_simd_size(); +struct PFFFTSetup { + gsl::owner<PFFFT_Setup*> mSetup{}; + + PFFFTSetup() = default; + PFFFTSetup(const PFFFTSetup&) = delete; + PFFFTSetup(PFFFTSetup&& rhs) noexcept : mSetup{rhs.mSetup} { rhs.mSetup = nullptr; } + explicit PFFFTSetup(std::nullptr_t) noexcept { } + explicit PFFFTSetup(unsigned int n, pffft_transform_t transform) + : mSetup{pffft_new_setup(n, transform)} + { } + ~PFFFTSetup() { if(mSetup) pffft_destroy_setup(mSetup); } + + PFFFTSetup& operator=(const PFFFTSetup&) = delete; + PFFFTSetup& operator=(PFFFTSetup&& rhs) noexcept + { + if(mSetup) + pffft_destroy_setup(mSetup); + mSetup = rhs.mSetup; + rhs.mSetup = nullptr; + return *this; + } + + void transform(const float *input, float *output, float *work, pffft_direction_t direction) const + { pffft_transform(mSetup, input, output, work, direction); } + + void transform_ordered(const float *input, float *output, float *work, + pffft_direction_t direction) const + { pffft_transform_ordered(mSetup, input, output, work, direction); } + + void zreorder(const float *input, float *output, pffft_direction_t direction) const + { pffft_zreorder(mSetup, input, output, direction); } + + void zconvolve_scale_accumulate(const float *dft_a, const float *dft_b, float *dft_ab, + float scaling) const + { pffft_zconvolve_scale_accumulate(mSetup, dft_a, dft_b, dft_ab, scaling); } + + void zconvolve_accumulate(const float *dft_a, const float *dft_b, float *dft_ab) const + { pffft_zconvolve_accumulate(mSetup, dft_a, dft_b, dft_ab); } -#ifdef __cplusplus -} -#endif + [[nodiscard]] operator bool() const noexcept { return mSetup != nullptr; } +}; #endif // PFFFT_H diff --git a/common/phase_shifter.h b/common/phase_shifter.h index e1a83dab..1b3463de 100644 --- a/common/phase_shifter.h +++ b/common/phase_shifter.h @@ -10,6 +10,7 @@ #include <array> #include <stddef.h> #include <type_traits> +#include <vector> #include "alcomplex.h" #include "alspan.h" @@ -52,20 +53,19 @@ struct PhaseShifterT { constexpr size_t fft_size{FilterSize}; constexpr size_t half_size{fft_size / 2}; - auto fftBuffer = std::make_unique<complex_d[]>(fft_size); - std::fill_n(fftBuffer.get(), fft_size, complex_d{}); + auto fftBuffer = std::vector<complex_d>(fft_size, complex_d{}); fftBuffer[half_size] = 1.0; - forward_fft(al::span{fftBuffer.get(), fft_size}); + forward_fft(al::span{fftBuffer}); fftBuffer[0] *= std::numeric_limits<double>::epsilon(); for(size_t i{1};i < half_size;++i) fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; fftBuffer[half_size] *= std::numeric_limits<double>::epsilon(); for(size_t i{half_size+1};i < fft_size;++i) fftBuffer[i] = std::conj(fftBuffer[fft_size - i]); - inverse_fft(al::span{fftBuffer.get(), fft_size}); + inverse_fft(al::span{fftBuffer}); - auto fftiter = fftBuffer.get() + fft_size - 1; + auto fftiter = fftBuffer.data() + fft_size - 1; for(float &coeff : mCoeffs) { coeff = static_cast<float>(fftiter->real() / double{fft_size}); diff --git a/common/polyphase_resampler.h b/common/polyphase_resampler.h index 557485bb..764111c9 100644 --- a/common/polyphase_resampler.h +++ b/common/polyphase_resampler.h @@ -40,7 +40,7 @@ struct PPhaseResampler { explicit operator bool() const noexcept { return !mF.empty(); } private: - uint mP, mQ, mM, mL; + uint mP{}, mQ{}, mM{}, mL{}; std::vector<double> mF; }; diff --git a/common/ringbuffer.cpp b/common/ringbuffer.cpp index af1f3669..2636bfb4 100644 --- a/common/ringbuffer.cpp +++ b/common/ringbuffer.cpp @@ -24,6 +24,8 @@ #include <algorithm> #include <climits> +#include <cstdint> +#include <limits> #include <stdexcept> #include "almalloc.h" @@ -40,7 +42,7 @@ RingBufferPtr RingBuffer::Create(std::size_t sz, std::size_t elem_sz, int limit_ power_of_two |= power_of_two>>4; power_of_two |= power_of_two>>8; power_of_two |= power_of_two>>16; - if constexpr(SIZE_MAX > UINT_MAX) + if constexpr(sizeof(size_t) > sizeof(uint32_t)) power_of_two |= power_of_two>>32; } ++power_of_two; @@ -159,7 +161,7 @@ std::size_t RingBuffer::write(const void *src, std::size_t cnt) noexcept } -auto RingBuffer::getReadVector() const noexcept -> DataPair +auto RingBuffer::getReadVector() noexcept -> DataPair { DataPair ret; @@ -174,15 +176,15 @@ auto RingBuffer::getReadVector() const noexcept -> DataPair { /* Two part vector: the rest of the buffer after the current read ptr, * plus some from the start of the buffer. */ - ret.first.buf = const_cast<std::byte*>(mBuffer.data() + r*mElemSize); + ret.first.buf = mBuffer.data() + r*mElemSize; ret.first.len = mSizeMask+1 - r; - ret.second.buf = const_cast<std::byte*>(mBuffer.data()); + ret.second.buf = mBuffer.data(); ret.second.len = cnt2 & mSizeMask; } else { /* Single part vector: just the rest of the buffer */ - ret.first.buf = const_cast<std::byte*>(mBuffer.data() + r*mElemSize); + ret.first.buf = mBuffer.data() + r*mElemSize; ret.first.len = free_cnt; ret.second.buf = nullptr; ret.second.len = 0; @@ -191,7 +193,7 @@ auto RingBuffer::getReadVector() const noexcept -> DataPair return ret; } -auto RingBuffer::getWriteVector() const noexcept -> DataPair +auto RingBuffer::getWriteVector() noexcept -> DataPair { DataPair ret; @@ -206,14 +208,14 @@ auto RingBuffer::getWriteVector() const noexcept -> DataPair { /* Two part vector: the rest of the buffer after the current write ptr, * plus some from the start of the buffer. */ - ret.first.buf = const_cast<std::byte*>(mBuffer.data() + w*mElemSize); + ret.first.buf = mBuffer.data() + w*mElemSize; ret.first.len = mSizeMask+1 - w; - ret.second.buf = const_cast<std::byte*>(mBuffer.data()); + ret.second.buf = mBuffer.data(); ret.second.len = cnt2 & mSizeMask; } else { - ret.first.buf = const_cast<std::byte*>(mBuffer.data() + w*mElemSize); + ret.first.buf = mBuffer.data() + w*mElemSize; ret.first.len = free_cnt; ret.second.buf = nullptr; ret.second.len = 0; diff --git a/common/ringbuffer.h b/common/ringbuffer.h index 8c65c3af..ee59205a 100644 --- a/common/ringbuffer.h +++ b/common/ringbuffer.h @@ -7,6 +7,7 @@ #include <utility> #include "almalloc.h" +#include "flexarray.h" /* NOTE: This lockless ringbuffer implementation is copied from JACK, extended @@ -32,30 +33,29 @@ public: }; using DataPair = std::pair<Data,Data>; - RingBuffer(const std::size_t count) : mBuffer{count} { } /** Reset the read and write pointers to zero. This is not thread safe. */ - void reset() noexcept; + auto reset() noexcept -> void; /** * The non-copying data reader. Returns two ringbuffer data pointers that * hold the current readable data. If the readable data is in one segment * the second segment has zero length. */ - DataPair getReadVector() const noexcept; + [[nodiscard]] auto getReadVector() noexcept -> DataPair; /** * The non-copying data writer. Returns two ringbuffer data pointers that * hold the current writeable data. If the writeable data is in one segment * the second segment has zero length. */ - DataPair getWriteVector() const noexcept; + [[nodiscard]] auto getWriteVector() noexcept -> DataPair; /** * Return the number of elements available for reading. This is the number * of elements in front of the read pointer and behind the write pointer. */ - std::size_t readSpace() const noexcept + [[nodiscard]] auto readSpace() const noexcept -> size_t { const size_t w{mWritePtr.load(std::memory_order_acquire)}; const size_t r{mReadPtr.load(std::memory_order_acquire)}; @@ -66,14 +66,14 @@ public: * The copying data reader. Copy at most `cnt' elements into `dest'. * Returns the actual number of elements copied. */ - std::size_t read(void *dest, std::size_t cnt) noexcept; + [[nodiscard]] auto read(void *dest, size_t cnt) noexcept -> size_t; /** * The copying data reader w/o read pointer advance. Copy at most `cnt' * elements into `dest'. Returns the actual number of elements copied. */ - std::size_t peek(void *dest, std::size_t cnt) const noexcept; + [[nodiscard]] auto peek(void *dest, size_t cnt) const noexcept -> size_t; /** Advance the read pointer `cnt' places. */ - void readAdvance(std::size_t cnt) noexcept + auto readAdvance(size_t cnt) noexcept -> void { mReadPtr.fetch_add(cnt, std::memory_order_acq_rel); } @@ -81,7 +81,7 @@ public: * Return the number of elements available for writing. This is the number * of elements in front of the write pointer and behind the read pointer. */ - std::size_t writeSpace() const noexcept + [[nodiscard]] auto writeSpace() const noexcept -> size_t { const size_t w{mWritePtr.load(std::memory_order_acquire)}; const size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask}; @@ -92,12 +92,12 @@ public: * The copying data writer. Copy at most `cnt' elements from `src'. Returns * the actual number of elements copied. */ - std::size_t write(const void *src, std::size_t cnt) noexcept; + [[nodiscard]] auto write(const void *src, size_t cnt) noexcept -> size_t; /** Advance the write pointer `cnt' places. */ - void writeAdvance(std::size_t cnt) noexcept + auto writeAdvance(size_t cnt) noexcept -> void { mWritePtr.fetch_add(cnt, std::memory_order_acq_rel); } - std::size_t getElemSize() const noexcept { return mElemSize; } + [[nodiscard]] auto getElemSize() const noexcept -> size_t { return mElemSize; } /** * Create a new ringbuffer to hold at least `sz' elements of `elem_sz' @@ -105,7 +105,8 @@ public: * (even if it is already a power of two, to ensure the requested amount * can be written). */ - static std::unique_ptr<RingBuffer> Create(std::size_t sz, std::size_t elem_sz, int limit_writes); + [[nodiscard]] + static auto Create(size_t sz, size_t elem_sz, int limit_writes) -> std::unique_ptr<RingBuffer>; DEF_FAM_NEWDEL(RingBuffer, mBuffer) }; diff --git a/common/vecmat.h b/common/vecmat.h index a45f262f..0cdb82eb 100644 --- a/common/vecmat.h +++ b/common/vecmat.h @@ -14,7 +14,7 @@ namespace alu { template<typename T> class VectorR { static_assert(std::is_floating_point<T>::value, "Must use floating-point types"); - alignas(16) T mVals[4]; + alignas(16) std::array<T,4> mVals; public: constexpr VectorR() noexcept = default; @@ -58,7 +58,7 @@ public: return T{0}; } - constexpr VectorR cross_product(const alu::VectorR<T> &rhs) const noexcept + [[nodiscard]] constexpr auto cross_product(const alu::VectorR<T> &rhs) const noexcept -> VectorR { return VectorR{ mVals[1]*rhs.mVals[2] - mVals[2]*rhs.mVals[1], @@ -67,7 +67,7 @@ public: T{0}}; } - constexpr T dot_product(const alu::VectorR<T> &rhs) const noexcept + [[nodiscard]] constexpr auto dot_product(const alu::VectorR<T> &rhs) const noexcept -> T { return mVals[0]*rhs.mVals[0] + mVals[1]*rhs.mVals[1] + mVals[2]*rhs.mVals[2]; } }; using Vector = VectorR<float>; @@ -75,7 +75,7 @@ using Vector = VectorR<float>; template<typename T> class MatrixR { static_assert(std::is_floating_point<T>::value, "Must use floating-point types"); - alignas(16) T mVals[16]; + alignas(16) std::array<T,16> mVals; public: constexpr MatrixR() noexcept = default; diff --git a/config.h.in b/config.h.in index 20df5b46..19cb79e0 100644 --- a/config.h.in +++ b/config.h.in @@ -7,12 +7,6 @@ /* Define if HRTF data is embedded in the library */ #cmakedefine ALSOFT_EMBED_HRTF_DATA -/* Define if we have the posix_memalign function */ -#cmakedefine HAVE_POSIX_MEMALIGN - -/* Define if we have the _aligned_malloc function */ -#cmakedefine HAVE__ALIGNED_MALLOC - /* Define if we have the proc_pidpath function */ #cmakedefine HAVE_PROC_PIDPATH diff --git a/core/ambdec.cpp b/core/ambdec.cpp index f98e1098..f3fe7566 100644 --- a/core/ambdec.cpp +++ b/core/ambdec.cpp @@ -42,8 +42,8 @@ enum class ReaderScope { HFMatrix, }; -#ifdef __USE_MINGW_ANSI_STDIO -[[gnu::format(gnu_printf,2,3)]] +#ifdef __MINGW32__ +[[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]] #else [[gnu::format(printf,2,3)]] #endif @@ -53,7 +53,7 @@ std::optional<std::string> make_error(size_t linenum, const char *fmt, ...) auto &str = ret.emplace(); str.resize(256); - int printed{std::snprintf(const_cast<char*>(str.data()), str.length(), "Line %zu: ", linenum)}; + int printed{std::snprintf(str.data(), str.length(), "Line %zu: ", linenum)}; if(printed < 0) printed = 0; auto plen = std::min(static_cast<size_t>(printed), str.length()); @@ -111,7 +111,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept { if(command == "add_spkr") { - if(speaker_pos == NumSpeakers) + if(speaker_pos == Speakers.size()) return make_error(linenum, "Too many speakers specified"); AmbDecConf::SpeakerConf &spkr = Speakers[speaker_pos++]; @@ -145,7 +145,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept } else if(command == "add_row") { - if(pos == NumSpeakers) + if(pos == Speakers.size()) return make_error(linenum, "Too many matrix rows specified"); unsigned int mask{ChanMask}; @@ -205,12 +205,13 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept } else if(command == "/dec/speakers") { - if(NumSpeakers) + if(!Speakers.empty()) return make_error(linenum, "Duplicate speakers"); - istr >> NumSpeakers; - if(!NumSpeakers) - return make_error(linenum, "Invalid speakers: %zu", NumSpeakers); - Speakers = std::make_unique<SpeakerConf[]>(NumSpeakers); + size_t numspeakers{}; + istr >> numspeakers; + if(!numspeakers) + return make_error(linenum, "Invalid speakers: %zu", numspeakers); + Speakers.resize(numspeakers); } else if(command == "/dec/coeff_scale") { @@ -243,22 +244,22 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept } else if(command == "/speakers/{") { - if(!NumSpeakers) + if(Speakers.empty()) return make_error(linenum, "Speakers defined without a count"); scope = ReaderScope::Speakers; } else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{") { - if(!NumSpeakers) + if(Speakers.empty()) return make_error(linenum, "Matrix defined without a speaker count"); if(!ChanMask) return make_error(linenum, "Matrix defined without a channel mask"); - if(!Matrix) + if(Matrix.empty()) { - Matrix = std::make_unique<CoeffArray[]>(NumSpeakers * FreqBands); - LFMatrix = Matrix.get(); - HFMatrix = LFMatrix + NumSpeakers*(FreqBands-1); + Matrix.resize(Speakers.size() * FreqBands); + LFMatrix = Matrix.data(); + HFMatrix = LFMatrix + Speakers.size()*(FreqBands-1); } if(FreqBands == 1) @@ -285,8 +286,8 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept if(!is_at_end(buffer, endpos)) return make_error(linenum, "Extra junk on end: %s", buffer.substr(endpos).c_str()); - if(speaker_pos < NumSpeakers || hfmatrix_pos < NumSpeakers - || (FreqBands == 2 && lfmatrix_pos < NumSpeakers)) + if(speaker_pos < Speakers.size() || hfmatrix_pos < Speakers.size() + || (FreqBands == 2 && lfmatrix_pos < Speakers.size())) return make_error(linenum, "Incomplete decoder definition"); if(CoeffScale == AmbDecScale::Unset) return make_error(linenum, "No coefficient scaling defined"); diff --git a/core/ambdec.h b/core/ambdec.h index 19f68697..4305070f 100644 --- a/core/ambdec.h +++ b/core/ambdec.h @@ -5,6 +5,7 @@ #include <memory> #include <optional> #include <string> +#include <vector> #include "core/ambidefs.h" @@ -34,17 +35,16 @@ struct AmbDecConf { float Elevation{0.0f}; std::string Connection; }; - size_t NumSpeakers{0}; - std::unique_ptr<SpeakerConf[]> Speakers; + std::vector<SpeakerConf> Speakers; using CoeffArray = std::array<float,MaxAmbiChannels>; - std::unique_ptr<CoeffArray[]> Matrix; + std::vector<CoeffArray> Matrix; /* Unused when FreqBands == 1 */ - float LFOrderGain[MaxAmbiOrder+1]{}; + std::array<float,MaxAmbiOrder+1> LFOrderGain{}; CoeffArray *LFMatrix; - float HFOrderGain[MaxAmbiOrder+1]{}; + std::array<float,MaxAmbiOrder+1> HFOrderGain{}; CoeffArray *HFMatrix; ~AmbDecConf(); diff --git a/core/async_event.h b/core/async_event.h index f1ca0c7b..20857c9c 100644 --- a/core/async_event.h +++ b/core/async_event.h @@ -1,6 +1,7 @@ #ifndef CORE_EVENT_H #define CORE_EVENT_H +#include <array> #include <stdint.h> #include <variant> @@ -39,7 +40,7 @@ struct AsyncBufferCompleteEvent { }; struct AsyncDisconnectEvent { - char msg[244]; + std::array<char,244> msg; }; struct AsyncEffectReleaseEvent { diff --git a/core/bformatdec.cpp b/core/bformatdec.cpp index a308e185..d6a44799 100644 --- a/core/bformatdec.cpp +++ b/core/bformatdec.cpp @@ -36,7 +36,7 @@ BFormatDec::BFormatDec(const size_t inchans, const al::span<const ChannelDec> co auto &decoder = mChannelDec.emplace<std::vector<ChannelDecoderSingle>>(inchans); for(size_t j{0};j < decoder.size();++j) { - float *outcoeffs{decoder[j].mGains}; + float *outcoeffs{decoder[j].mGains.data()}; for(const ChannelDec &incoeffs : coeffs) *(outcoeffs++) = incoeffs[j]; } @@ -50,11 +50,11 @@ BFormatDec::BFormatDec(const size_t inchans, const al::span<const ChannelDec> co for(size_t j{0};j < decoder.size();++j) { - float *outcoeffs{decoder[j].mGains[sHFBand]}; + float *outcoeffs{decoder[j].mGains[sHFBand].data()}; for(const ChannelDec &incoeffs : coeffs) *(outcoeffs++) = incoeffs[j]; - outcoeffs = decoder[j].mGains[sLFBand]; + outcoeffs = decoder[j].mGains[sLFBand].data(); for(const ChannelDec &incoeffs : coeffslf) *(outcoeffs++) = incoeffs[j]; } @@ -76,8 +76,10 @@ void BFormatDec::process(const al::span<FloatBufferLine> OutBuffer, { chandec.mXOver.process({input->data(), SamplesToDo}, hfSamples.data(), lfSamples.data()); - MixSamples(hfSamples, OutBuffer, chandec.mGains[sHFBand], chandec.mGains[sHFBand],0,0); - MixSamples(lfSamples, OutBuffer, chandec.mGains[sLFBand], chandec.mGains[sLFBand],0,0); + MixSamples(hfSamples, OutBuffer, chandec.mGains[sHFBand].data(), + chandec.mGains[sHFBand].data(), 0, 0); + MixSamples(lfSamples, OutBuffer, chandec.mGains[sLFBand].data(), + chandec.mGains[sLFBand].data(), 0, 0); ++input; } }; @@ -86,8 +88,8 @@ void BFormatDec::process(const al::span<FloatBufferLine> OutBuffer, auto *input = InSamples; for(auto &chandec : decoder) { - MixSamples({input->data(), SamplesToDo}, OutBuffer, chandec.mGains, chandec.mGains, - 0, 0); + MixSamples({input->data(), SamplesToDo}, OutBuffer, chandec.mGains.data(), + chandec.mGains.data(), 0, 0); ++input; } }; diff --git a/core/bformatdec.h b/core/bformatdec.h index 3bb7f544..8513db03 100644 --- a/core/bformatdec.h +++ b/core/bformatdec.h @@ -25,15 +25,15 @@ class BFormatDec { static constexpr size_t sNumBands{2}; struct ChannelDecoderSingle { - float mGains[MAX_OUTPUT_CHANNELS]; + std::array<float,MaxOutputChannels> mGains{}; }; struct ChannelDecoderDual { BandSplitter mXOver; - float mGains[sNumBands][MAX_OUTPUT_CHANNELS]; + std::array<std::array<float,MaxOutputChannels>,sNumBands> mGains{}; }; - alignas(16) std::array<FloatBufferLine,2> mSamples; + alignas(16) std::array<FloatBufferLine,2> mSamples{}; const std::unique_ptr<FrontStablizer> mStablizer; @@ -44,7 +44,7 @@ public: const al::span<const ChannelDec> coeffslf, const float xover_f0norm, std::unique_ptr<FrontStablizer> stablizer); - bool hasStablizer() const noexcept { return mStablizer != nullptr; } + [[nodiscard]] auto hasStablizer() const noexcept -> bool { return mStablizer != nullptr; } /* Decodes the ambisonic input to the given output channels. */ void process(const al::span<FloatBufferLine> OutBuffer, const FloatBufferLine *InSamples, @@ -58,8 +58,6 @@ public: static std::unique_ptr<BFormatDec> Create(const size_t inchans, const al::span<const ChannelDec> coeffs, const al::span<const ChannelDec> coeffslf, const float xover_f0norm, std::unique_ptr<FrontStablizer> stablizer); - - DEF_NEWDEL(BFormatDec) }; #endif /* CORE_BFORMATDEC_H */ diff --git a/core/bs2b.cpp b/core/bs2b.cpp index 303bf9bd..9157c4d7 100644 --- a/core/bs2b.cpp +++ b/core/bs2b.cpp @@ -26,72 +26,74 @@ #include <algorithm> #include <cmath> #include <iterator> +#include <stdexcept> #include "alnumbers.h" #include "bs2b.h" +namespace { /* Set up all data. */ -static void init(struct bs2b *bs2b) +void init(Bs2b::bs2b *bs2b) { float Fc_lo, Fc_hi; float G_lo, G_hi; - float x, g; switch(bs2b->level) { - case BS2B_LOW_CLEVEL: /* Low crossfeed level */ + case Bs2b::LowCLevel: /* Low crossfeed level */ Fc_lo = 360.0f; Fc_hi = 501.0f; G_lo = 0.398107170553497f; G_hi = 0.205671765275719f; break; - case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */ + case Bs2b::MiddleCLevel: /* Middle crossfeed level */ Fc_lo = 500.0f; Fc_hi = 711.0f; G_lo = 0.459726988530872f; G_hi = 0.228208484414988f; break; - case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */ + case Bs2b::HighCLevel: /* High crossfeed level (virtual speakers are closer to itself) */ Fc_lo = 700.0f; Fc_hi = 1021.0f; G_lo = 0.530884444230988f; G_hi = 0.250105790667544f; break; - case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */ + case Bs2b::LowECLevel: /* Low easy crossfeed level */ Fc_lo = 360.0f; Fc_hi = 494.0f; G_lo = 0.316227766016838f; G_hi = 0.168236228897329f; break; - case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */ + case Bs2b::MiddleECLevel: /* Middle easy crossfeed level */ Fc_lo = 500.0f; Fc_hi = 689.0f; G_lo = 0.354813389233575f; G_hi = 0.187169483835901f; break; - default: /* High easy crossfeed level */ - bs2b->level = BS2B_HIGH_ECLEVEL; + case Bs2b::HighECLevel: /* High easy crossfeed level */ + default: + bs2b->level = Bs2b::HighECLevel; Fc_lo = 700.0f; Fc_hi = 975.0f; G_lo = 0.398107170553497f; G_hi = 0.205671765275719f; break; - } /* switch */ + } - g = 1.0f / (1.0f - G_hi + G_lo); + float g{1.0f / (1.0f - G_hi + G_lo)}; /* $fc = $Fc / $s; * $d = 1 / 2 / pi / $fc; * $x = exp(-1 / $d); */ - x = std::exp(-al::numbers::pi_v<float>*2.0f*Fc_lo/static_cast<float>(bs2b->srate)); + float x{ std::exp(-al::numbers::pi_v<float>*2.0f*Fc_lo/static_cast<float>(bs2b->srate))}; bs2b->b1_lo = x; bs2b->a0_lo = G_lo * (1.0f - x) * g; @@ -99,85 +101,84 @@ static void init(struct bs2b *bs2b) bs2b->b1_hi = x; bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g; bs2b->a1_hi = -x * g; -} /* init */ +} +} // namespace /* Exported functions. * See descriptions in "bs2b.h" */ +namespace Bs2b { -void bs2b_set_params(struct bs2b *bs2b, int level, int srate) -{ - if(srate <= 0) srate = 1; - - bs2b->level = level; - bs2b->srate = srate; - init(bs2b); -} /* bs2b_set_params */ - -int bs2b_get_level(struct bs2b *bs2b) +void bs2b::set_params(int level_, int srate_) { - return bs2b->level; -} /* bs2b_get_level */ + if(srate_ < 1) + throw std::runtime_error{"BS2B srate < 1"}; -int bs2b_get_srate(struct bs2b *bs2b) -{ - return bs2b->srate; -} /* bs2b_get_srate */ + level = level_; + srate = srate_; + init(this); +} -void bs2b_clear(struct bs2b *bs2b) +void bs2b::clear() { - std::fill(std::begin(bs2b->history), std::end(bs2b->history), bs2b::t_last_sample{}); -} /* bs2b_clear */ + history.fill(bs2b::t_last_sample{}); +} -void bs2b_cross_feed(struct bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo) +void bs2b::cross_feed(float *RESTRICT Left, float *RESTRICT Right, size_t SamplesToDo) { - const float a0_lo{bs2b->a0_lo}; - const float b1_lo{bs2b->b1_lo}; - const float a0_hi{bs2b->a0_hi}; - const float a1_hi{bs2b->a1_hi}; - const float b1_hi{bs2b->b1_hi}; - float lsamples[128][2]; - float rsamples[128][2]; + const float a0lo{a0_lo}; + const float b1lo{b1_lo}; + const float a0hi{a0_hi}; + const float a1hi{a1_hi}; + const float b1hi{b1_hi}; + std::array<std::array<float,2>,128> samples; for(size_t base{0};base < SamplesToDo;) { - const size_t todo{std::min<size_t>(128, SamplesToDo-base)}; + const size_t todo{std::min(samples.size(), SamplesToDo-base)}; /* Process left input */ - float z_lo{bs2b->history[0].lo}; - float z_hi{bs2b->history[0].hi}; + float z_lo{history[0].lo}; + float z_hi{history[0].hi}; for(size_t i{0};i < todo;i++) { - lsamples[i][0] = a0_lo*Left[i] + z_lo; - z_lo = b1_lo*lsamples[i][0]; - - lsamples[i][1] = a0_hi*Left[i] + z_hi; - z_hi = a1_hi*Left[i] + b1_hi*lsamples[i][1]; + const float x{Left[i]}; + float y{a0hi*x + z_hi}; + z_hi = a1hi*x + b1hi*y; + samples[i][0] = y; + + y = a0lo*x + z_lo; + z_lo = b1lo*y; + samples[i][1] = y; } - bs2b->history[0].lo = z_lo; - bs2b->history[0].hi = z_hi; + history[0].lo = z_lo; + history[0].hi = z_hi; /* Process right input */ - z_lo = bs2b->history[1].lo; - z_hi = bs2b->history[1].hi; + z_lo = history[1].lo; + z_hi = history[1].hi; for(size_t i{0};i < todo;i++) { - rsamples[i][0] = a0_lo*Right[i] + z_lo; - z_lo = b1_lo*rsamples[i][0]; - - rsamples[i][1] = a0_hi*Right[i] + z_hi; - z_hi = a1_hi*Right[i] + b1_hi*rsamples[i][1]; + const float x{Right[i]}; + float y{a0lo*x + z_lo}; + z_lo = b1lo*y; + samples[i][0] += y; + + y = a0hi*x + z_hi; + z_hi = a1hi*x + b1hi*y; + samples[i][1] += y; } - bs2b->history[1].lo = z_lo; - bs2b->history[1].hi = z_hi; + history[1].lo = z_lo; + history[1].hi = z_hi; - /* Crossfeed */ for(size_t i{0};i < todo;i++) - *(Left++) = lsamples[i][1] + rsamples[i][0]; + *(Left++) = samples[i][0]; for(size_t i{0};i < todo;i++) - *(Right++) = rsamples[i][1] + lsamples[i][0]; + *(Right++) = samples[i][1]; base += todo; } -} /* bs2b_cross_feed */ +} + +} // namespace Bs2b diff --git a/core/bs2b.h b/core/bs2b.h index 4d0b9dd8..6fb54c0c 100644 --- a/core/bs2b.h +++ b/core/bs2b.h @@ -24,66 +24,65 @@ #ifndef CORE_BS2B_H #define CORE_BS2B_H -#include "almalloc.h" +#include <array> -/* Number of crossfeed levels */ -#define BS2B_CLEVELS 3 +namespace Bs2b { -/* Normal crossfeed levels */ -#define BS2B_HIGH_CLEVEL 3 -#define BS2B_MIDDLE_CLEVEL 2 -#define BS2B_LOW_CLEVEL 1 +enum { + /* Normal crossfeed levels */ + LowCLevel = 1, + MiddleCLevel = 2, + HighCLevel = 3, -/* Easy crossfeed levels */ -#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS -#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS -#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS + /* Easy crossfeed levels */ + LowECLevel = 4, + MiddleECLevel = 5, + HighECLevel = 6, -/* Default crossfeed levels */ -#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL -/* Default sample rate (Hz) */ -#define BS2B_DEFAULT_SRATE 44100 + DefaultCLevel = HighECLevel +}; struct bs2b { - int level; /* Crossfeed level */ - int srate; /* Sample rate (Hz) */ + int level{}; /* Crossfeed level */ + int srate{}; /* Sample rate (Hz) */ /* Lowpass IIR filter coefficients */ - float a0_lo; - float b1_lo; + float a0_lo{}; + float b1_lo{}; /* Highboost IIR filter coefficients */ - float a0_hi; - float a1_hi; - float b1_hi; + float a0_hi{}; + float a1_hi{}; + float b1_hi{}; /* Buffer of filter history * [0] - first channel, [1] - second channel */ struct t_last_sample { - float lo; - float hi; - } history[2]; - - DEF_NEWDEL(bs2b) -}; + float lo{}; + float hi{}; + }; + std::array<t_last_sample,2> history{}; + + /* Clear buffers and set new coefficients with new crossfeed level and + * sample rate values. + * level - crossfeed level of *Level enum values. + * srate - sample rate by Hz. + */ + void set_params(int level, int srate); -/* Clear buffers and set new coefficients with new crossfeed level and sample - * rate values. - * level - crossfeed level of *LEVEL values. - * srate - sample rate by Hz. - */ -void bs2b_set_params(bs2b *bs2b, int level, int srate); + /* Return current crossfeed level value */ + [[nodiscard]] auto get_level() const noexcept -> int { return level; } -/* Return current crossfeed level value */ -int bs2b_get_level(bs2b *bs2b); + /* Return current sample rate value */ + [[nodiscard]] auto get_srate() const noexcept -> int { return srate; } -/* Return current sample rate value */ -int bs2b_get_srate(bs2b *bs2b); + /* Clear buffer */ + void clear(); -/* Clear buffer */ -void bs2b_clear(bs2b *bs2b); + void cross_feed(float *Left, float *Right, size_t SamplesToDo); +}; -void bs2b_cross_feed(bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo); +} // namespace Bs2b #endif /* CORE_BS2B_H */ diff --git a/core/bsinc_tables.cpp b/core/bsinc_tables.cpp index 41102e9a..6e3ee338 100644 --- a/core/bsinc_tables.cpp +++ b/core/bsinc_tables.cpp @@ -5,9 +5,9 @@ #include <array> #include <cassert> #include <cmath> +#include <cstddef> #include <limits> #include <memory> -#include <stddef.h> #include <stdexcept> #include "alnumbers.h" @@ -123,15 +123,13 @@ struct BSincHeader { double beta{}; double scaleBase{}; - uint a[BSincScaleCount]{}; + std::array<uint,BSincScaleCount> a{}; uint total_size{}; constexpr BSincHeader(uint Rejection, uint Order) noexcept + : width{CalcKaiserWidth(Rejection, Order)}, beta{CalcKaiserBeta(Rejection)} + , scaleBase{width / 2.0} { - width = CalcKaiserWidth(Rejection, Order); - beta = CalcKaiserBeta(Rejection); - scaleBase = width / 2.0; - uint num_points{Order+1}; for(uint si{0};si < BSincScaleCount;++si) { @@ -162,8 +160,9 @@ struct BSincFilterArray { constexpr uint BSincPointsMax{(hdr.a[0]*2 + 3) & ~3u}; static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small"); - using filter_type = double[BSincPhaseCount+1][BSincPointsMax]; - auto filter = std::make_unique<filter_type[]>(BSincScaleCount); + using filter_type = std::array<std::array<double,BSincPointsMax>,BSincPhaseCount+1>; + auto filterptr = std::make_unique<std::array<filter_type,BSincScaleCount>>(); + const auto filter = filterptr->begin(); const double besseli_0_beta{cyl_bessel_i(0, hdr.beta)}; @@ -254,8 +253,8 @@ struct BSincFilterArray { assert(idx == hdr.total_size); } - constexpr const BSincHeader &getHeader() const noexcept { return hdr; } - constexpr const float *getTable() const noexcept { return &mTable.front(); } + [[nodiscard]] constexpr auto getHeader() const noexcept -> const BSincHeader& { return hdr; } + [[nodiscard]] constexpr auto getTable() const noexcept -> const float* { return mTable.data(); } }; const BSincFilterArray<bsinc12_hdr> bsinc12_filter{}; diff --git a/core/bsinc_tables.h b/core/bsinc_tables.h index aca4b274..6c33bd56 100644 --- a/core/bsinc_tables.h +++ b/core/bsinc_tables.h @@ -1,13 +1,15 @@ #ifndef CORE_BSINC_TABLES_H #define CORE_BSINC_TABLES_H +#include <array> + #include "bsinc_defs.h" struct BSincTable { float scaleBase, scaleRange; - unsigned int m[BSincScaleCount]; - unsigned int filterOffset[BSincScaleCount]; + std::array<unsigned int,BSincScaleCount> m; + std::array<unsigned int,BSincScaleCount> filterOffset; const float *Tab; }; diff --git a/core/buffer_storage.cpp b/core/buffer_storage.cpp index 6ffab124..a343b946 100644 --- a/core/buffer_storage.cpp +++ b/core/buffer_storage.cpp @@ -3,7 +3,7 @@ #include "buffer_storage.h" -#include <stdint.h> +#include <cstdint> const char *NameFromFormat(FmtType type) noexcept diff --git a/core/buffer_storage.h b/core/buffer_storage.h index 3b581b5e..dec774bf 100644 --- a/core/buffer_storage.h +++ b/core/buffer_storage.h @@ -98,19 +98,19 @@ struct BufferStorage { AmbiScaling mAmbiScaling{AmbiScaling::FuMa}; uint mAmbiOrder{0u}; - inline uint bytesFromFmt() const noexcept { return BytesFromFmt(mType); } - inline uint channelsFromFmt() const noexcept + [[nodiscard]] auto bytesFromFmt() const noexcept -> uint { return BytesFromFmt(mType); } + [[nodiscard]] auto channelsFromFmt() const noexcept -> uint { return ChannelsFromFmt(mChannels, mAmbiOrder); } - inline uint frameSizeFromFmt() const noexcept { return channelsFromFmt() * bytesFromFmt(); } + [[nodiscard]] auto frameSizeFromFmt() const noexcept -> uint { return channelsFromFmt() * bytesFromFmt(); } - inline uint blockSizeFromFmt() const noexcept + [[nodiscard]] auto blockSizeFromFmt() const noexcept -> uint { if(mType == FmtIMA4) return ((mBlockAlign-1)/2 + 4) * channelsFromFmt(); if(mType == FmtMSADPCM) return ((mBlockAlign-2)/2 + 7) * channelsFromFmt(); return frameSizeFromFmt(); }; - inline bool isBFormat() const noexcept { return IsBFormat(mChannels); } + [[nodiscard]] auto isBFormat() const noexcept -> bool { return IsBFormat(mChannels); } }; #endif /* CORE_BUFFER_STORAGE_H */ diff --git a/core/context.cpp b/core/context.cpp index 2ebbc7b1..0fe50d25 100644 --- a/core/context.cpp +++ b/core/context.cpp @@ -2,6 +2,7 @@ #include "config.h" #include <cassert> +#include <functional> #include <limits> #include <memory> #include <stdexcept> @@ -26,43 +27,14 @@ ContextBase::ContextBase(DeviceBase *device) : mDevice{device} ContextBase::~ContextBase() { - size_t count{0}; - ContextProps *cprops{mParams.ContextUpdate.exchange(nullptr, std::memory_order_relaxed)}; - if(cprops) - { - ++count; - delete cprops; - } - cprops = mFreeContextProps.exchange(nullptr, std::memory_order_acquire); - while(cprops) - { - std::unique_ptr<ContextProps> old{cprops}; - cprops = old->next.load(std::memory_order_relaxed); - ++count; - } - TRACE("Freed %zu context property object%s\n", count, (count==1)?"":"s"); - - count = 0; - EffectSlotProps *eprops{mFreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)}; - while(eprops) - { - std::unique_ptr<EffectSlotProps> old{eprops}; - eprops = old->next.load(std::memory_order_relaxed); - ++count; - } - TRACE("Freed %zu AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s"); - - if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)}) - { + if(std::unique_ptr<EffectSlotArray> curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)}) std::destroy_n(curarray->end(), curarray->size()); - delete curarray; - } - delete mVoices.exchange(nullptr, std::memory_order_relaxed); + mVoices.store(nullptr, std::memory_order_relaxed); if(mAsyncEvents) { - count = 0; + size_t count{0}; auto evt_vec = mAsyncEvents->getReadVector(); if(evt_vec.first.len > 0) { @@ -85,85 +57,131 @@ ContextBase::~ContextBase() void ContextBase::allocVoiceChanges() { - constexpr size_t clustersize{128}; + static constexpr size_t clustersize{std::tuple_size_v<VoiceChangeCluster::element_type>}; + + VoiceChangeCluster clusterptr{std::make_unique<VoiceChangeCluster::element_type>()}; + const auto cluster = al::span{*clusterptr}; - VoiceChangeCluster cluster{std::make_unique<VoiceChange[]>(clustersize)}; for(size_t i{1};i < clustersize;++i) cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed); cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed); - mVoiceChangeClusters.emplace_back(std::move(cluster)); - mVoiceChangeTail = mVoiceChangeClusters.back().get(); + mVoiceChangeClusters.emplace_back(std::move(clusterptr)); + mVoiceChangeTail = mVoiceChangeClusters.back()->data(); } void ContextBase::allocVoiceProps() { - constexpr size_t clustersize{32}; + static constexpr size_t clustersize{std::tuple_size_v<VoicePropsCluster::element_type>}; TRACE("Increasing allocated voice properties to %zu\n", (mVoicePropClusters.size()+1) * clustersize); - VoicePropsCluster cluster{std::make_unique<VoicePropsItem[]>(clustersize)}; + auto clusterptr = std::make_unique<VoicePropsCluster::element_type>(); + auto cluster = al::span{*clusterptr}; for(size_t i{1};i < clustersize;++i) cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed); - mVoicePropClusters.emplace_back(std::move(cluster)); + mVoicePropClusters.emplace_back(std::move(clusterptr)); VoicePropsItem *oldhead{mFreeVoiceProps.load(std::memory_order_acquire)}; do { - mVoicePropClusters.back()[clustersize-1].next.store(oldhead, std::memory_order_relaxed); - } while(mFreeVoiceProps.compare_exchange_weak(oldhead, mVoicePropClusters.back().get(), + mVoicePropClusters.back()->back().next.store(oldhead, std::memory_order_relaxed); + } while(mFreeVoiceProps.compare_exchange_weak(oldhead, mVoicePropClusters.back()->data(), std::memory_order_acq_rel, std::memory_order_acquire) == false); } void ContextBase::allocVoices(size_t addcount) { - constexpr size_t clustersize{32}; + static constexpr size_t clustersize{std::tuple_size_v<VoiceCluster::element_type>}; /* Convert element count to cluster count. */ addcount = (addcount+(clustersize-1)) / clustersize; + if(!addcount) + { + if(!mVoiceClusters.empty()) + return; + ++addcount; + } + if(addcount >= std::numeric_limits<int>::max()/clustersize - mVoiceClusters.size()) throw std::runtime_error{"Allocating too many voices"}; const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize}; TRACE("Increasing allocated voices to %zu\n", totalcount); - auto newarray = VoiceArray::Create(totalcount); while(addcount) { - mVoiceClusters.emplace_back(std::make_unique<Voice[]>(clustersize)); + mVoiceClusters.emplace_back(std::make_unique<VoiceCluster::element_type>()); --addcount; } + auto newarray = VoiceArray::Create(totalcount); auto voice_iter = newarray->begin(); for(VoiceCluster &cluster : mVoiceClusters) - { - for(size_t i{0};i < clustersize;++i) - *(voice_iter++) = &cluster[i]; - } + voice_iter = std::transform(cluster->begin(), cluster->end(), voice_iter, + [](Voice &voice) noexcept -> Voice* { return &voice; }); - if(auto *oldvoices = mVoices.exchange(newarray.release(), std::memory_order_acq_rel)) - { - mDevice->waitForMix(); - delete oldvoices; - } + if(auto oldvoices = mVoices.exchange(std::move(newarray), std::memory_order_acq_rel)) + std::ignore = mDevice->waitForMix(); } +void ContextBase::allocEffectSlotProps() +{ + static constexpr size_t clustersize{std::tuple_size_v<EffectSlotPropsCluster::element_type>}; + + TRACE("Increasing allocated effect slot properties to %zu\n", + (mEffectSlotPropClusters.size()+1) * clustersize); + + auto clusterptr = std::make_unique<EffectSlotPropsCluster::element_type>(); + auto cluster = al::span{*clusterptr}; + for(size_t i{1};i < clustersize;++i) + cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed); + auto *newcluster = mEffectSlotPropClusters.emplace_back(std::move(clusterptr)).get(); + + EffectSlotProps *oldhead{mFreeEffectSlotProps.load(std::memory_order_acquire)}; + do { + newcluster->back().next.store(oldhead, std::memory_order_relaxed); + } while(mFreeEffectSlotProps.compare_exchange_weak(oldhead, newcluster->data(), + std::memory_order_acq_rel, std::memory_order_acquire) == false); +} + EffectSlot *ContextBase::getEffectSlot() { - for(auto& cluster : mEffectSlotClusters) + for(auto& clusterptr : mEffectSlotClusters) { - for(size_t i{0};i < EffectSlotClusterSize;++i) - { - if(!cluster[i].InUse) - return &cluster[i]; - } + const auto cluster = al::span{*clusterptr}; + auto iter = std::find_if_not(cluster.begin(), cluster.end(), + std::mem_fn(&EffectSlot::InUse)); + if(iter != cluster.end()) return al::to_address(iter); } - if(1 >= std::numeric_limits<int>::max()/EffectSlotClusterSize - mEffectSlotClusters.size()) + auto clusterptr = std::make_unique<EffectSlotCluster::element_type>(); + if(1 >= std::numeric_limits<int>::max()/clusterptr->size() - mEffectSlotClusters.size()) throw std::runtime_error{"Allocating too many effect slots"}; - const size_t totalcount{(mEffectSlotClusters.size()+1) * EffectSlotClusterSize}; + const size_t totalcount{(mEffectSlotClusters.size()+1) * clusterptr->size()}; TRACE("Increasing allocated effect slots to %zu\n", totalcount); - mEffectSlotClusters.emplace_back(std::make_unique<EffectSlot[]>(EffectSlotClusterSize)); - return getEffectSlot(); + mEffectSlotClusters.emplace_back(std::move(clusterptr)); + return mEffectSlotClusters.back()->data(); +} + + +void ContextBase::allocContextProps() +{ + static constexpr size_t clustersize{std::tuple_size_v<ContextPropsCluster::element_type>}; + + TRACE("Increasing allocated context properties to %zu\n", + (mContextPropClusters.size()+1) * clustersize); + + auto clusterptr = std::make_unique<ContextPropsCluster::element_type>(); + auto cluster = al::span{*clusterptr}; + for(size_t i{1};i < clustersize;++i) + cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed); + auto *newcluster = mContextPropClusters.emplace_back(std::move(clusterptr)).get(); + + ContextProps *oldhead{mFreeContextProps.load(std::memory_order_acquire)}; + do { + newcluster->back().next.store(oldhead, std::memory_order_relaxed); + } while(mFreeContextProps.compare_exchange_weak(oldhead, newcluster->data(), + std::memory_order_acq_rel, std::memory_order_acquire) == false); } diff --git a/core/context.h b/core/context.h index ccb7dd3b..980514b3 100644 --- a/core/context.h +++ b/core/context.h @@ -14,6 +14,7 @@ #include "alspan.h" #include "async_event.h" #include "atomic.h" +#include "flexarray.h" #include "opthelpers.h" #include "vecmat.h" @@ -26,9 +27,9 @@ struct VoiceChange; struct VoicePropsItem; -constexpr float SpeedOfSoundMetersPerSec{343.3f}; +inline constexpr float SpeedOfSoundMetersPerSec{343.3f}; -constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */ +inline constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */ enum class DistanceModel : unsigned char { Disable, @@ -56,8 +57,6 @@ struct ContextProps { DistanceModel mDistanceModel; std::atomic<ContextProps*> next; - - DEF_NEWDEL(ContextProps) }; struct ContextParams { @@ -85,7 +84,7 @@ struct ContextBase { /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit * indicates if updates are currently happening). */ - RefCount mUpdateCount{0u}; + std::atomic<unsigned int> mUpdateCount{0u}; std::atomic<bool> mHoldUpdates{false}; std::atomic<bool> mStopVoicesOnDisconnect{true}; @@ -96,7 +95,7 @@ struct ContextBase { */ std::atomic<ContextProps*> mFreeContextProps{nullptr}; std::atomic<VoicePropsItem*> mFreeVoiceProps{nullptr}; - std::atomic<EffectSlotProps*> mFreeEffectslotProps{nullptr}; + std::atomic<EffectSlotProps*> mFreeEffectSlotProps{nullptr}; /* The voice change tail is the beginning of the "free" elements, up to and * *excluding* the current. If tail==current, there's no free elements and @@ -108,21 +107,22 @@ struct ContextBase { void allocVoiceChanges(); void allocVoiceProps(); - + void allocEffectSlotProps(); + void allocContextProps(); ContextParams mParams; using VoiceArray = al::FlexArray<Voice*>; - std::atomic<VoiceArray*> mVoices{}; + al::atomic_unique_ptr<VoiceArray> mVoices{}; std::atomic<size_t> mActiveVoiceCount{}; void allocVoices(size_t addcount); - al::span<Voice*> getVoicesSpan() const noexcept + [[nodiscard]] auto getVoicesSpan() const noexcept -> al::span<Voice*> { return {mVoices.load(std::memory_order_relaxed)->data(), mActiveVoiceCount.load(std::memory_order_relaxed)}; } - al::span<Voice*> getVoicesSpanAcquired() const noexcept + [[nodiscard]] auto getVoicesSpanAcquired() const noexcept -> al::span<Voice*> { return {mVoices.load(std::memory_order_acquire)->data(), mActiveVoiceCount.load(std::memory_order_acquire)}; @@ -143,22 +143,30 @@ struct ContextBase { * However, to avoid allocating each object individually, they're allocated * in clusters that are stored in a vector for easy automatic cleanup. */ - using VoiceChangeCluster = std::unique_ptr<VoiceChange[]>; + using VoiceChangeCluster = std::unique_ptr<std::array<VoiceChange,128>>; std::vector<VoiceChangeCluster> mVoiceChangeClusters; - using VoiceCluster = std::unique_ptr<Voice[]>; + using VoiceCluster = std::unique_ptr<std::array<Voice,32>>; std::vector<VoiceCluster> mVoiceClusters; - using VoicePropsCluster = std::unique_ptr<VoicePropsItem[]>; + using VoicePropsCluster = std::unique_ptr<std::array<VoicePropsItem,32>>; std::vector<VoicePropsCluster> mVoicePropClusters; - static constexpr size_t EffectSlotClusterSize{4}; EffectSlot *getEffectSlot(); - using EffectSlotCluster = std::unique_ptr<EffectSlot[]>; + using EffectSlotCluster = std::unique_ptr<std::array<EffectSlot,4>>; std::vector<EffectSlotCluster> mEffectSlotClusters; + using EffectSlotPropsCluster = std::unique_ptr<std::array<EffectSlotProps,4>>; + std::vector<EffectSlotPropsCluster> mEffectSlotPropClusters; + + /* This could be greater than 2, but there should be no way there can be + * more than two context property updates in use simultaneously. + */ + using ContextPropsCluster = std::unique_ptr<std::array<ContextProps,2>>; + std::vector<ContextPropsCluster> mContextPropClusters; + ContextBase(DeviceBase *device); ContextBase(const ContextBase&) = delete; diff --git a/core/converter.cpp b/core/converter.cpp index 5b2f3e15..805b8548 100644 --- a/core/converter.cpp +++ b/core/converter.cpp @@ -9,7 +9,7 @@ #include <cstddef> #include <cstdint> #include <iterator> -#include <limits.h> +#include <climits> #include "albit.h" #include "alnumeric.h" @@ -24,26 +24,23 @@ static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for Buffer static_assert((INT_MAX>>MixerFracBits)/MaxPitch > BufferLineSize, "MaxPitch and/or BufferLineSize are too large for MixerFracBits!"); -/* Base template left undefined. Should be marked =delete, but Clang 3.8.1 - * chokes on that given the inline specializations. - */ template<DevFmtType T> -inline float LoadSample(DevFmtType_t<T> val) noexcept; +constexpr float LoadSample(DevFmtType_t<T> val) noexcept = delete; -template<> inline float LoadSample<DevFmtByte>(DevFmtType_t<DevFmtByte> val) noexcept -{ return val * (1.0f/128.0f); } -template<> inline float LoadSample<DevFmtShort>(DevFmtType_t<DevFmtShort> val) noexcept -{ return val * (1.0f/32768.0f); } -template<> inline float LoadSample<DevFmtInt>(DevFmtType_t<DevFmtInt> val) noexcept +template<> constexpr float LoadSample<DevFmtByte>(DevFmtType_t<DevFmtByte> val) noexcept +{ return float(val) * (1.0f/128.0f); } +template<> constexpr float LoadSample<DevFmtShort>(DevFmtType_t<DevFmtShort> val) noexcept +{ return float(val) * (1.0f/32768.0f); } +template<> constexpr float LoadSample<DevFmtInt>(DevFmtType_t<DevFmtInt> val) noexcept { return static_cast<float>(val) * (1.0f/2147483648.0f); } -template<> inline float LoadSample<DevFmtFloat>(DevFmtType_t<DevFmtFloat> val) noexcept +template<> constexpr float LoadSample<DevFmtFloat>(DevFmtType_t<DevFmtFloat> val) noexcept { return val; } -template<> inline float LoadSample<DevFmtUByte>(DevFmtType_t<DevFmtUByte> val) noexcept +template<> constexpr float LoadSample<DevFmtUByte>(DevFmtType_t<DevFmtUByte> val) noexcept { return LoadSample<DevFmtByte>(static_cast<int8_t>(val - 128)); } -template<> inline float LoadSample<DevFmtUShort>(DevFmtType_t<DevFmtUShort> val) noexcept +template<> constexpr float LoadSample<DevFmtUShort>(DevFmtType_t<DevFmtUShort> val) noexcept { return LoadSample<DevFmtShort>(static_cast<int16_t>(val - 32768)); } -template<> inline float LoadSample<DevFmtUInt>(DevFmtType_t<DevFmtUInt> val) noexcept +template<> constexpr float LoadSample<DevFmtUInt>(DevFmtType_t<DevFmtUInt> val) noexcept { return LoadSample<DevFmtInt>(static_cast<int32_t>(val - 2147483648u)); } @@ -51,7 +48,7 @@ template<DevFmtType T> inline void LoadSampleArray(float *RESTRICT dst, const void *src, const size_t srcstep, const size_t samples) noexcept { - const DevFmtType_t<T> *ssrc = static_cast<const DevFmtType_t<T>*>(src); + auto *ssrc = static_cast<const DevFmtType_t<T>*>(src); for(size_t i{0u};i < samples;i++) dst[i] = LoadSample<T>(ssrc[i*srcstep]); } @@ -99,7 +96,7 @@ template<DevFmtType T> inline void StoreSampleArray(void *dst, const float *RESTRICT src, const size_t dststep, const size_t samples) noexcept { - DevFmtType_t<T> *sdst = static_cast<DevFmtType_t<T>*>(dst); + auto *sdst = static_cast<DevFmtType_t<T>*>(dst); for(size_t i{0u};i < samples;i++) sdst[i*dststep] = StoreSample<T>(src[i]); } @@ -127,7 +124,7 @@ void StoreSamples(void *dst, const float *src, const size_t dststep, const DevFm template<DevFmtType T> void Mono2Stereo(float *RESTRICT dst, const void *src, const size_t frames) noexcept { - const DevFmtType_t<T> *ssrc = static_cast<const DevFmtType_t<T>*>(src); + auto *ssrc = static_cast<const DevFmtType_t<T>*>(src); for(size_t i{0u};i < frames;i++) dst[i*2 + 1] = dst[i*2 + 0] = LoadSample<T>(ssrc[i]) * 0.707106781187f; } @@ -136,7 +133,7 @@ template<DevFmtType T> void Multi2Mono(uint chanmask, const size_t step, const float scale, float *RESTRICT dst, const void *src, const size_t frames) noexcept { - const DevFmtType_t<T> *ssrc = static_cast<const DevFmtType_t<T>*>(src); + auto *ssrc = static_cast<const DevFmtType_t<T>*>(src); std::fill_n(dst, frames, 0.0f); for(size_t c{0};chanmask;++c) { @@ -216,8 +213,8 @@ uint SampleConverter::availableOut(uint srcframes) const uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint dstframes) { - const uint SrcFrameSize{static_cast<uint>(mChan.size()) * mSrcTypeSize}; - const uint DstFrameSize{static_cast<uint>(mChan.size()) * mDstTypeSize}; + const size_t SrcFrameSize{mChan.size() * mSrcTypeSize}; + const size_t DstFrameSize{mChan.size() * mDstTypeSize}; const uint increment{mIncrement}; auto SamplesIn = static_cast<const std::byte*>(*src); uint NumSrcSamples{*srcframes}; @@ -243,8 +240,8 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint break; } - float *RESTRICT SrcData{mSrcSamples}; - float *RESTRICT DstData{mDstSamples}; + float *RESTRICT SrcData{mSrcSamples.data()}; + float *RESTRICT DstData{mDstSamples.data()}; uint DataPosFrac{mFracOffset}; uint64_t DataSize64{prepcount}; DataSize64 += readable; @@ -271,13 +268,13 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint /* Load the previous samples into the source data first, then the * new samples from the input buffer. */ - std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData); + std::copy_n(mChan[chan].PrevSamples.cbegin(), prepcount, SrcData); LoadSamples(SrcData + prepcount, SrcSamples, mChan.size(), mSrcType, readable); /* Store as many prep samples for next time as possible, given the * number of output samples being generated. */ - std::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples); + std::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples.begin()); std::fill(std::begin(mChan[chan].PrevSamples)+nextprep, std::end(mChan[chan].PrevSamples), 0.0f); @@ -328,9 +325,9 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *con */ for(size_t chan{0u};chan < mChan.size();chan++) { - LoadSamples(&mChan[chan].PrevSamples[prepcount], - static_cast<const std::byte*>(src[chan]), 1, mSrcType, readable); - src[chan] = static_cast<const std::byte*>(src[chan]) + mSrcTypeSize*readable; + auto *samples = static_cast<const std::byte*>(src[chan]); + LoadSamples(&mChan[chan].PrevSamples[prepcount], samples, 1, mSrcType, readable); + src[chan] = samples + size_t{mSrcTypeSize}*readable; } mSrcPrepCount = prepcount + readable; @@ -338,8 +335,8 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *con break; } - float *RESTRICT SrcData{mSrcSamples}; - float *RESTRICT DstData{mDstSamples}; + float *RESTRICT SrcData{mSrcSamples.data()}; + float *RESTRICT DstData{mDstSamples.data()}; uint DataPosFrac{mFracOffset}; uint64_t DataSize64{prepcount}; DataSize64 += readable; @@ -363,13 +360,13 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *con /* Load the previous samples into the source data first, then the * new samples from the input buffer. */ - std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData); + std::copy_n(mChan[chan].PrevSamples.cbegin(), prepcount, SrcData); LoadSamples(SrcData + prepcount, src[chan], 1, mSrcType, readable); /* Store as many prep samples for next time as possible, given the * number of output samples being generated. */ - std::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples); + std::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples.begin()); std::fill(std::begin(mChan[chan].PrevSamples)+nextprep, std::end(mChan[chan].PrevSamples), 0.0f); @@ -377,7 +374,7 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *con mResample(&mState, SrcData+MaxResamplerEdge, DataPosFrac, increment, {DstData, DstSize}); - std::byte *DstSamples = static_cast<std::byte*>(dst[chan]) + pos*mDstTypeSize; + auto *DstSamples = static_cast<std::byte*>(dst[chan]) + pos*size_t{mDstTypeSize}; StoreSamples(DstSamples, DstData, 1, mDstType, DstSize); } @@ -390,7 +387,7 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *con /* Update the src and dst pointers in case there's still more to do. */ const uint srcread{minu(NumSrcSamples, SrcDataEnd + mSrcPrepCount - prepcount)}; for(size_t chan{0u};chan < mChan.size();chan++) - src[chan] = static_cast<const std::byte*>(src[chan]) + mSrcTypeSize*srcread; + src[chan] = static_cast<const std::byte*>(src[chan]) + size_t{mSrcTypeSize}*srcread; NumSrcSamples -= srcread; pos += DstSize; diff --git a/core/converter.h b/core/converter.h index 49ca124d..3dc2babb 100644 --- a/core/converter.h +++ b/core/converter.h @@ -7,6 +7,7 @@ #include "almalloc.h" #include "devformat.h" +#include "flexarray.h" #include "mixer/defs.h" using uint = unsigned int; @@ -25,22 +26,22 @@ struct SampleConverter { InterpState mState{}; ResamplerFunc mResample{}; - alignas(16) float mSrcSamples[BufferLineSize]{}; - alignas(16) float mDstSamples[BufferLineSize]{}; + alignas(16) FloatBufferLine mSrcSamples{}; + alignas(16) FloatBufferLine mDstSamples{}; struct ChanSamples { - alignas(16) float PrevSamples[MaxResamplerPadding]; + alignas(16) std::array<float,MaxResamplerPadding> PrevSamples; }; al::FlexArray<ChanSamples> mChan; SampleConverter(size_t numchans) : mChan{numchans} { } - uint convert(const void **src, uint *srcframes, void *dst, uint dstframes); - uint convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes); - uint availableOut(uint srcframes) const; + [[nodiscard]] auto convert(const void **src, uint *srcframes, void *dst, uint dstframes) -> uint; + [[nodiscard]] auto convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes) -> uint; + [[nodiscard]] auto availableOut(uint srcframes) const -> uint; using SampleOffset = std::chrono::duration<int64_t, std::ratio<1,MixerFracOne>>; - SampleOffset currentInputDelay() const noexcept + [[nodiscard]] auto currentInputDelay() const noexcept -> SampleOffset { const int64_t prep{int64_t{mSrcPrepCount} - MaxResamplerEdge}; return SampleOffset{(prep<<MixerFracBits) + mFracOffset}; @@ -59,7 +60,7 @@ struct ChannelConverter { uint mChanMask{}; DevFmtChannels mDstChans{}; - bool is_active() const noexcept { return mChanMask != 0; } + [[nodiscard]] auto is_active() const noexcept -> bool { return mChanMask != 0; } void convert(const void *src, float *dst, uint frames) const; }; diff --git a/core/cubic_defs.h b/core/cubic_defs.h index 33751c97..f3ded415 100644 --- a/core/cubic_defs.h +++ b/core/cubic_defs.h @@ -1,13 +1,15 @@ #ifndef CORE_CUBIC_DEFS_H #define CORE_CUBIC_DEFS_H +#include <array> + /* The number of distinct phase intervals within the cubic filter tables. */ constexpr unsigned int CubicPhaseBits{5}; constexpr unsigned int CubicPhaseCount{1 << CubicPhaseBits}; struct CubicCoefficients { - float mCoeffs[4]; - float mDeltas[4]; + std::array<float,4> mCoeffs; + std::array<float,4> mDeltas; }; #endif /* CORE_CUBIC_DEFS_H */ diff --git a/core/cubic_tables.cpp b/core/cubic_tables.cpp index 5e7aafad..84462893 100644 --- a/core/cubic_tables.cpp +++ b/core/cubic_tables.cpp @@ -2,7 +2,7 @@ #include "cubic_tables.h" #include <array> -#include <stddef.h> +#include <cstddef> #include "cubic_defs.h" @@ -41,7 +41,7 @@ struct SplineFilterArray { mTable[pi].mDeltas[3] = -mTable[pi].mCoeffs[3]; } - constexpr auto& getTable() const noexcept { return mTable; } + [[nodiscard]] constexpr auto& getTable() const noexcept { return mTable; } }; constexpr SplineFilterArray SplineFilter{}; diff --git a/core/dbus_wrap.cpp b/core/dbus_wrap.cpp index 48419566..05d9fc06 100644 --- a/core/dbus_wrap.cpp +++ b/core/dbus_wrap.cpp @@ -8,16 +8,22 @@ #include <mutex> #include <type_traits> -#include "albit.h" #include "logging.h" void PrepareDBus() { - static constexpr char libname[] = "libdbus-1.so.3"; + const char *libname{"libdbus-1.so.3"}; + + dbus_handle = LoadLib(libname); + if(!dbus_handle) + { + WARN("Failed to load %s\n", libname); + return; + } auto load_func = [](auto &f, const char *name) -> void - { f = al::bit_cast<std::remove_reference_t<decltype(f)>>(GetSymbol(dbus_handle, name)); }; + { f = reinterpret_cast<std::remove_reference_t<decltype(f)>>(GetSymbol(dbus_handle, name)); }; #define LOAD_FUNC(x) do { \ load_func(p##x, #x); \ if(!p##x) \ @@ -29,14 +35,8 @@ void PrepareDBus() } \ } while(0); - dbus_handle = LoadLib(libname); - if(!dbus_handle) - { - WARN("Failed to load %s\n", libname); - return; - } + DBUS_FUNCTIONS(LOAD_FUNC) -DBUS_FUNCTIONS(LOAD_FUNC) #undef LOAD_FUNC } #endif diff --git a/core/devformat.h b/core/devformat.h index 485826a3..d918e531 100644 --- a/core/devformat.h +++ b/core/devformat.h @@ -2,6 +2,7 @@ #define CORE_DEVFORMAT_H #include <cstdint> +#include <cstddef> using uint = unsigned int; @@ -71,7 +72,7 @@ enum DevFmtChannels : unsigned char { DevFmtChannelsDefault = DevFmtStereo }; -#define MAX_OUTPUT_CHANNELS 16 +inline constexpr size_t MaxOutputChannels{16}; /* DevFmtType traits, providing the type, etc given a DevFmtType. */ template<DevFmtType T> diff --git a/core/device.cpp b/core/device.cpp index 2766c5e4..795a9601 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -9,15 +9,12 @@ #include "mastering.h" -al::FlexArray<ContextBase*> DeviceBase::sEmptyContextArray{0u}; +static_assert(std::atomic<std::chrono::nanoseconds>::is_always_lock_free); -DeviceBase::DeviceBase(DeviceType type) : Type{type}, mContexts{&sEmptyContextArray} +DeviceBase::DeviceBase(DeviceType type) + : Type{type}, mContexts{al::FlexArray<ContextBase*>::Create(0)} { } -DeviceBase::~DeviceBase() -{ - auto *oldarray = mContexts.exchange(nullptr, std::memory_order_relaxed); - if(oldarray != &sEmptyContextArray) delete oldarray; -} +DeviceBase::~DeviceBase() = default; diff --git a/core/device.h b/core/device.h index b1ffc9ce..1da08727 100644 --- a/core/device.h +++ b/core/device.h @@ -17,6 +17,7 @@ #include "bufferline.h" #include "devformat.h" #include "filters/nfc.h" +#include "flexarray.h" #include "intrusive_ptr.h" #include "mixer/hrtfdefs.h" #include "opthelpers.h" @@ -25,8 +26,10 @@ #include "vector.h" class BFormatDec; +namespace Bs2b { struct bs2b; -struct Compressor; +} // namespace Bs2b +class Compressor; struct ContextBase; struct DirectHrtfState; struct HrtfStore; @@ -34,12 +37,12 @@ struct HrtfStore; using uint = unsigned int; -#define MIN_OUTPUT_RATE 8000 -#define MAX_OUTPUT_RATE 192000 -#define DEFAULT_OUTPUT_RATE 48000 +inline constexpr size_t MinOutputRate{8000}; +inline constexpr size_t MaxOutputRate{192000}; +inline constexpr size_t DefaultOutputRate{48000}; -#define DEFAULT_UPDATE_SIZE 960 /* 20ms */ -#define DEFAULT_NUM_UPDATES 3 +inline constexpr size_t DefaultUpdateSize{960}; /* 20ms */ +inline constexpr size_t DefaultNumUpdates{3}; enum class DeviceType : uint8_t { @@ -82,7 +85,7 @@ struct DistanceComp { float *Buffer{nullptr}; }; - std::array<ChanData,MAX_OUTPUT_CHANNELS> mChannels; + std::array<ChanData,MaxOutputChannels> mChannels; al::FlexArray<float,16> mSamples; DistanceComp(size_t count) : mSamples{count} { } @@ -158,8 +161,6 @@ enum { // Specifies if the DSP is paused at user request DevicePaused, - // Specifies if the device is currently running - DeviceRunning, // Specifies if the output plays directly on/in ears (headphones, headset, // ear buds, etc). @@ -173,12 +174,13 @@ enum { DeviceFlagsCount }; -struct DeviceBase { - /* To avoid extraneous allocations, a 0-sized FlexArray<ContextBase*> is - * defined globally as a sharable object. - */ - static al::FlexArray<ContextBase*> sEmptyContextArray; +enum class DeviceState : uint8_t { + Unprepared, + Configured, + Playing +}; +struct DeviceBase { std::atomic<bool> Connected{true}; const DeviceType Type{}; @@ -202,6 +204,7 @@ struct DeviceBase { // Device flags std::bitset<DeviceFlagsCount> Flags{}; + DeviceState mDeviceState{DeviceState::Unprepared}; uint NumAuxSends{}; @@ -218,8 +221,8 @@ struct DeviceBase { */ NfcFilter mNFCtrlFilter{}; - uint SamplesDone{0u}; - std::chrono::nanoseconds ClockBase{0}; + std::atomic<uint> mSamplesDone{0u}; + std::atomic<std::chrono::nanoseconds> mClockBase{std::chrono::nanoseconds{}}; std::chrono::nanoseconds FixedLatency{0}; AmbiRotateMatrix mAmbiRotateMatrix{}; @@ -229,24 +232,24 @@ struct DeviceBase { static constexpr size_t MixerLineSize{BufferLineSize + DecoderBase::sMaxPadding}; static constexpr size_t MixerChannelsMax{16}; using MixerBufferLine = std::array<float,MixerLineSize>; - alignas(16) std::array<MixerBufferLine,MixerChannelsMax> mSampleData; - alignas(16) std::array<float,MixerLineSize+MaxResamplerPadding> mResampleData; + alignas(16) std::array<MixerBufferLine,MixerChannelsMax> mSampleData{}; + alignas(16) std::array<float,MixerLineSize+MaxResamplerPadding> mResampleData{}; - alignas(16) float FilteredData[BufferLineSize]; + alignas(16) std::array<float,BufferLineSize> FilteredData{}; union { - alignas(16) float HrtfSourceData[BufferLineSize + HrtfHistoryLength]; - alignas(16) float NfcSampleData[BufferLineSize]; + alignas(16) std::array<float,BufferLineSize+HrtfHistoryLength> HrtfSourceData{}; + alignas(16) std::array<float,BufferLineSize> NfcSampleData; }; /* Persistent storage for HRTF mixing. */ - alignas(16) float2 HrtfAccumData[BufferLineSize + HrirLength]; + alignas(16) std::array<float2,BufferLineSize+HrirLength> HrtfAccumData{}; /* Mixing buffer used by the Dry mix and Real output. */ al::vector<FloatBufferLine, 16> MixBuffer; /* The "dry" path corresponds to the main output. */ MixParams Dry; - uint NumChannelsPerOrder[MaxAmbiOrder+1]{}; + std::array<uint,MaxAmbiOrder+1> NumChannelsPerOrder{}; /* "Real" output, which will be written to the device buffer. May alias the * dry buffer. @@ -265,7 +268,7 @@ struct DeviceBase { std::unique_ptr<BFormatDec> AmbiDecoder; /* Stereo-to-binaural filter */ - std::unique_ptr<bs2b> Bs2b; + std::unique_ptr<Bs2b::bs2b> Bs2b; using PostProc = void(DeviceBase::*)(const size_t SamplesToDo); PostProc PostProcess{nullptr}; @@ -284,10 +287,10 @@ struct DeviceBase { * the end, so the bottom bit indicates if the device is currently mixing * and the upper bits indicates how many mixes have been done. */ - RefCount MixCount{0u}; + std::atomic<uint> mMixCount{0u}; // Contexts created on this device - std::atomic<al::FlexArray<ContextBase*>*> mContexts{nullptr}; + al::atomic_unique_ptr<al::FlexArray<ContextBase*>> mContexts; DeviceBase(DeviceType type); @@ -295,18 +298,56 @@ struct DeviceBase { DeviceBase& operator=(const DeviceBase&) = delete; ~DeviceBase(); - uint bytesFromFmt() const noexcept { return BytesFromDevFmt(FmtType); } - uint channelsFromFmt() const noexcept { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); } - uint frameSizeFromFmt() const noexcept { return bytesFromFmt() * channelsFromFmt(); } + [[nodiscard]] auto bytesFromFmt() const noexcept -> uint { return BytesFromDevFmt(FmtType); } + [[nodiscard]] auto channelsFromFmt() const noexcept -> uint { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); } + [[nodiscard]] auto frameSizeFromFmt() const noexcept -> uint { return bytesFromFmt() * channelsFromFmt(); } + + struct MixLock { + std::atomic<uint> &mCount; + const uint mLastVal; + + MixLock(std::atomic<uint> &count, const uint last_val) noexcept + : mCount{count}, mLastVal{last_val} + { } + /* Increment the mix count when the lock goes out of scope to "release" + * it (lsb should be 0). + */ + ~MixLock() { mCount.store(mLastVal+2, std::memory_order_release); } + }; + auto getWriteMixLock() noexcept + { + /* Increment the mix count at the start of mixing and writing clock + * info (lsb should be 1). + */ + const auto mixCount = mMixCount.load(std::memory_order_relaxed); + mMixCount.store(mixCount+1, std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_release); + return MixLock{mMixCount, mixCount}; + } - uint waitForMix() const noexcept + /** Waits for the mixer to not be mixing or updating the clock. */ + [[nodiscard]] auto waitForMix() const noexcept -> uint { uint refcount; - while((refcount=MixCount.load(std::memory_order_acquire))&1) { + while((refcount=mMixCount.load(std::memory_order_acquire))&1) { } return refcount; } + /** + * Helper to get the current clock time from the device's ClockBase, and + * SamplesDone converted from the sample rate. Should only be called while + * watching the MixCount. + */ + [[nodiscard]] auto getClockTime() const noexcept -> std::chrono::nanoseconds + { + using std::chrono::seconds; + using std::chrono::nanoseconds; + + auto ns = nanoseconds{seconds{mSamplesDone.load(std::memory_order_relaxed)}} / Frequency; + return mClockBase.load(std::memory_order_relaxed) + ns; + } + void ProcessHrtf(const size_t SamplesToDo); void ProcessAmbiDec(const size_t SamplesToDo); void ProcessAmbiDecStablized(const size_t SamplesToDo); @@ -320,8 +361,8 @@ struct DeviceBase { void renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep); /* Caller must lock the device state, and the mixer must not be running. */ -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf,2,3)]] +#ifdef __MINGW32__ + [[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]] #else [[gnu::format(printf,2,3)]] #endif @@ -331,11 +372,9 @@ struct DeviceBase { * Returns the index for the given channel name (e.g. FrontCenter), or * InvalidChannelIndex if it doesn't exist. */ - uint8_t channelIdxByName(Channel chan) const noexcept + [[nodiscard]] auto channelIdxByName(Channel chan) const noexcept -> uint8_t { return RealOut.ChannelIndex[chan]; } - DISABLE_ALLOC() - private: uint renderSamples(const uint numSamples); }; diff --git a/core/effects/base.h b/core/effects/base.h index 83df7cf0..df4eb129 100644 --- a/core/effects/base.h +++ b/core/effects/base.h @@ -2,7 +2,8 @@ #define CORE_EFFECTS_BASE_H #include <array> -#include <stddef.h> +#include <cstddef> +#include <variant> #include "almalloc.h" #include "alspan.h" @@ -19,21 +20,21 @@ struct RealMixParams; /** Target gain for the reverb decay feedback reaching the decay time. */ -constexpr float ReverbDecayGain{0.001f}; /* -60 dB */ +inline constexpr float ReverbDecayGain{0.001f}; /* -60 dB */ -constexpr float ReverbMaxReflectionsDelay{0.3f}; -constexpr float ReverbMaxLateReverbDelay{0.1f}; +inline constexpr float ReverbMaxReflectionsDelay{0.3f}; +inline constexpr float ReverbMaxLateReverbDelay{0.1f}; enum class ChorusWaveform { Sinusoid, Triangle }; -constexpr float ChorusMaxDelay{0.016f}; -constexpr float FlangerMaxDelay{0.004f}; +inline constexpr float ChorusMaxDelay{0.016f}; +inline constexpr float FlangerMaxDelay{0.004f}; -constexpr float EchoMaxDelay{0.207f}; -constexpr float EchoMaxLRDelay{0.404f}; +inline constexpr float EchoMaxDelay{0.207f}; +inline constexpr float EchoMaxLRDelay{0.404f}; enum class FShifterDirection { Down, @@ -59,120 +60,148 @@ enum class VMorpherWaveform { Sawtooth }; -union EffectProps { - struct { - float Density; - float Diffusion; - float Gain; - float GainHF; - float GainLF; - float DecayTime; - float DecayHFRatio; - float DecayLFRatio; - float ReflectionsGain; - float ReflectionsDelay; - float ReflectionsPan[3]; - float LateReverbGain; - float LateReverbDelay; - float LateReverbPan[3]; - float EchoTime; - float EchoDepth; - float ModulationTime; - float ModulationDepth; - float AirAbsorptionGainHF; - float HFReference; - float LFReference; - float RoomRolloffFactor; - bool DecayHFLimit; - } Reverb; - - struct { - float AttackTime; - float ReleaseTime; - float Resonance; - float PeakGain; - } Autowah; - - struct { - ChorusWaveform Waveform; - int Phase; - float Rate; - float Depth; - float Feedback; - float Delay; - } Chorus; /* Also Flanger */ - - struct { - bool OnOff; - } Compressor; - - struct { - float Edge; - float Gain; - float LowpassCutoff; - float EQCenter; - float EQBandwidth; - } Distortion; - - struct { - float Delay; - float LRDelay; - - float Damping; - float Feedback; - - float Spread; - } Echo; - - struct { - float LowCutoff; - float LowGain; - float Mid1Center; - float Mid1Gain; - float Mid1Width; - float Mid2Center; - float Mid2Gain; - float Mid2Width; - float HighCutoff; - float HighGain; - } Equalizer; - - struct { - float Frequency; - FShifterDirection LeftDirection; - FShifterDirection RightDirection; - } Fshifter; - - struct { - float Frequency; - float HighPassCutoff; - ModulatorWaveform Waveform; - } Modulator; - - struct { - int CoarseTune; - int FineTune; - } Pshifter; - - struct { - float Rate; - VMorpherPhenome PhonemeA; - VMorpherPhenome PhonemeB; - int PhonemeACoarseTuning; - int PhonemeBCoarseTuning; - VMorpherWaveform Waveform; - } Vmorpher; - - struct { - float Gain; - } Dedicated; - - struct { - std::array<float,3> OrientAt; - std::array<float,3> OrientUp; - } Convolution; +struct ReverbProps { + float Density; + float Diffusion; + float Gain; + float GainHF; + float GainLF; + float DecayTime; + float DecayHFRatio; + float DecayLFRatio; + float ReflectionsGain; + float ReflectionsDelay; + std::array<float,3> ReflectionsPan; + float LateReverbGain; + float LateReverbDelay; + std::array<float,3> LateReverbPan; + float EchoTime; + float EchoDepth; + float ModulationTime; + float ModulationDepth; + float AirAbsorptionGainHF; + float HFReference; + float LFReference; + float RoomRolloffFactor; + bool DecayHFLimit; }; +struct AutowahProps { + float AttackTime; + float ReleaseTime; + float Resonance; + float PeakGain; +}; + +struct ChorusProps { + ChorusWaveform Waveform; + int Phase; + float Rate; + float Depth; + float Feedback; + float Delay; +}; + +struct FlangerProps { + ChorusWaveform Waveform; + int Phase; + float Rate; + float Depth; + float Feedback; + float Delay; +}; + +struct CompressorProps { + bool OnOff; +}; + +struct DistortionProps { + float Edge; + float Gain; + float LowpassCutoff; + float EQCenter; + float EQBandwidth; +}; + +struct EchoProps { + float Delay; + float LRDelay; + + float Damping; + float Feedback; + + float Spread; +}; + +struct EqualizerProps { + float LowCutoff; + float LowGain; + float Mid1Center; + float Mid1Gain; + float Mid1Width; + float Mid2Center; + float Mid2Gain; + float Mid2Width; + float HighCutoff; + float HighGain; +}; + +struct FshifterProps { + float Frequency; + FShifterDirection LeftDirection; + FShifterDirection RightDirection; +}; + +struct ModulatorProps { + float Frequency; + float HighPassCutoff; + ModulatorWaveform Waveform; +}; + +struct PshifterProps { + int CoarseTune; + int FineTune; +}; + +struct VmorpherProps { + float Rate; + VMorpherPhenome PhonemeA; + VMorpherPhenome PhonemeB; + int PhonemeACoarseTuning; + int PhonemeBCoarseTuning; + VMorpherWaveform Waveform; +}; + +struct DedicatedDialogProps { + float Gain; +}; + +struct DedicatedLfeProps { + float Gain; +}; + +struct ConvolutionProps { + std::array<float,3> OrientAt; + std::array<float,3> OrientUp; +}; + +using EffectProps = std::variant<std::monostate, + ReverbProps, + AutowahProps, + ChorusProps, + FlangerProps, + CompressorProps, + DistortionProps, + EchoProps, + EqualizerProps, + FshifterProps, + ModulatorProps, + PshifterProps, + VmorpherProps, + DedicatedDialogProps, + DedicatedLfeProps, + ConvolutionProps>; + struct EffectTarget { MixParams *Main; diff --git a/core/effectslot.cpp b/core/effectslot.cpp index db8aa078..6c80317e 100644 --- a/core/effectslot.cpp +++ b/core/effectslot.cpp @@ -3,7 +3,7 @@ #include "effectslot.h" -#include <stddef.h> +#include <cstddef> #include "almalloc.h" #include "context.h" @@ -14,6 +14,8 @@ EffectSlotArray *EffectSlot::CreatePtrArray(size_t count) noexcept /* Allocate space for twice as many pointers, so the mixer has scratch * space to store a sorted list during mixing. */ - void *ptr{al_calloc(alignof(EffectSlotArray), EffectSlotArray::Sizeof(count*2))}; - return al::construct_at(static_cast<EffectSlotArray*>(ptr), count); + static constexpr auto AlignVal = std::align_val_t{alignof(EffectSlotArray)}; + if(gsl::owner<void*> ptr{::operator new[](EffectSlotArray::Sizeof(count*2), AlignVal)}) + return al::construct_at(static_cast<EffectSlotArray*>(ptr), count); + return nullptr; } diff --git a/core/effectslot.h b/core/effectslot.h index 2624ae5f..cf8503ff 100644 --- a/core/effectslot.h +++ b/core/effectslot.h @@ -6,6 +6,7 @@ #include "almalloc.h" #include "device.h" #include "effects/base.h" +#include "flexarray.h" #include "intrusive_ptr.h" struct EffectSlot; @@ -45,8 +46,6 @@ struct EffectSlotProps { al::intrusive_ptr<EffectState> State; std::atomic<EffectSlotProps*> next; - - DEF_NEWDEL(EffectSlotProps) }; @@ -82,8 +81,6 @@ struct EffectSlot { static EffectSlotArray *CreatePtrArray(size_t count) noexcept; - - DEF_NEWDEL(EffectSlot) }; #endif /* CORE_EFFECTSLOT_H */ diff --git a/core/except.cpp b/core/except.cpp index 45fd4eb5..86c7ccca 100644 --- a/core/except.cpp +++ b/core/except.cpp @@ -21,7 +21,7 @@ void base_exception::setMessage(const char* msg, std::va_list args) if(msglen > 0) LIKELY { mMessage.resize(static_cast<size_t>(msglen)+1); - std::vsnprintf(const_cast<char*>(mMessage.data()), mMessage.length(), msg, args2); + std::vsnprintf(mMessage.data(), mMessage.length(), msg, args2); mMessage.pop_back(); } va_end(args2); diff --git a/core/except.h b/core/except.h index 0e28e9df..90e3346e 100644 --- a/core/except.h +++ b/core/except.h @@ -14,12 +14,13 @@ class base_exception : public std::exception { protected: base_exception() = default; - virtual ~base_exception(); - void setMessage(const char *msg, std::va_list args); + auto setMessage(const char *msg, std::va_list args) -> void; public: - const char *what() const noexcept override { return mMessage.c_str(); } + ~base_exception() override; + + [[nodiscard]] auto what() const noexcept -> const char* override { return mMessage.c_str(); } }; } // namespace al diff --git a/core/filters/biquad.cpp b/core/filters/biquad.cpp index a0a62eb8..6671f60f 100644 --- a/core/filters/biquad.cpp +++ b/core/filters/biquad.cpp @@ -27,8 +27,8 @@ void BiquadFilterR<Real>::setParams(BiquadType type, Real f0norm, Real gain, Rea const Real alpha{sin_w0/2.0f * rcpQ}; Real sqrtgain_alpha_2; - Real a[3]{ 1.0f, 0.0f, 0.0f }; - Real b[3]{ 1.0f, 0.0f, 0.0f }; + std::array<Real,3> a{{1.0f, 0.0f, 0.0f}}; + std::array<Real,3> b{{1.0f, 0.0f, 0.0f}}; /* Calculate filter coefficients depending on filter type */ switch(type) diff --git a/core/filters/biquad.h b/core/filters/biquad.h index 75a4009b..e176caae 100644 --- a/core/filters/biquad.h +++ b/core/filters/biquad.h @@ -119,9 +119,9 @@ public: void dualProcess(BiquadFilterR &other, const al::span<const Real> src, Real *dst); /* Rather hacky. It's just here to support "manual" processing. */ - std::pair<Real,Real> getComponents() const noexcept { return {mZ1, mZ2}; } + [[nodiscard]] auto getComponents() const noexcept -> std::pair<Real,Real> { return {mZ1, mZ2}; } void setComponents(Real z1, Real z2) noexcept { mZ1 = z1; mZ2 = z2; } - Real processOne(const Real in, Real &z1, Real &z2) const noexcept + [[nodiscard]] auto processOne(const Real in, Real &z1, Real &z2) const noexcept -> Real { const Real out{in*mB0 + z1}; z1 = in*mB1 - out*mA1 + z2; diff --git a/core/filters/nfc.cpp b/core/filters/nfc.cpp index aa64c613..95b84e2c 100644 --- a/core/filters/nfc.cpp +++ b/core/filters/nfc.cpp @@ -48,12 +48,12 @@ namespace { -constexpr float B[5][4] = { - { 0.0f }, - { 1.0f }, - { 3.0f, 3.0f }, - { 3.6778f, 6.4595f, 2.3222f }, - { 4.2076f, 11.4877f, 5.7924f, 9.1401f } +constexpr std::array B{ + std::array{ 0.0f, 0.0f, 0.0f, 0.0f}, + std::array{ 1.0f, 0.0f, 0.0f, 0.0f}, + std::array{ 3.0f, 3.0f, 0.0f, 0.0f}, + std::array{3.6778f, 6.4595f, 2.3222f, 0.0f}, + std::array{4.2076f, 11.4877f, 5.7924f, 9.1401f} }; NfcFilter1 NfcFilterCreate1(const float w0, const float w1) noexcept diff --git a/core/filters/nfc.h b/core/filters/nfc.h index 4b8e68b5..9c58f863 100644 --- a/core/filters/nfc.h +++ b/core/filters/nfc.h @@ -1,30 +1,31 @@ #ifndef CORE_FILTERS_NFC_H #define CORE_FILTERS_NFC_H +#include <array> #include <cstddef> #include "alspan.h" struct NfcFilter1 { - float base_gain, gain; - float b1, a1; - float z[1]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, a1{}; + std::array<float,1> z{}; }; struct NfcFilter2 { - float base_gain, gain; - float b1, b2, a1, a2; - float z[2]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, b2{}, a1{}, a2{}; + std::array<float,2> z{}; }; struct NfcFilter3 { - float base_gain, gain; - float b1, b2, b3, a1, a2, a3; - float z[3]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, b2{}, b3{}, a1{}, a2{}, a3{}; + std::array<float,3> z{}; }; struct NfcFilter4 { - float base_gain, gain; - float b1, b2, b3, b4, a1, a2, a3, a4; - float z[4]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, b2{}, b3{}, b4{}, a1{}, a2{}, a3{}, a4{}; + std::array<float,4> z{}; }; class NfcFilter { diff --git a/core/fmt_traits.cpp b/core/fmt_traits.cpp index 054d8766..9d79287d 100644 --- a/core/fmt_traits.cpp +++ b/core/fmt_traits.cpp @@ -6,7 +6,7 @@ namespace al { -const int16_t muLawDecompressionTable[256] = { +const std::array<int16_t,256> muLawDecompressionTable{{ -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, @@ -39,9 +39,9 @@ const int16_t muLawDecompressionTable[256] = { 244, 228, 212, 196, 180, 164, 148, 132, 120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0 -}; +}}; -const int16_t aLawDecompressionTable[256] = { +const std::array<int16_t,256> aLawDecompressionTable{{ -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, @@ -74,6 +74,6 @@ const int16_t aLawDecompressionTable[256] = { 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, 688, 656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976, 816, 784, 880, 848 -}; +}}; } // namespace al diff --git a/core/fmt_traits.h b/core/fmt_traits.h index 02473014..101e20b6 100644 --- a/core/fmt_traits.h +++ b/core/fmt_traits.h @@ -1,6 +1,7 @@ #ifndef CORE_FMT_TRAITS_H #define CORE_FMT_TRAITS_H +#include <array> #include <cstddef> #include <stdint.h> @@ -9,8 +10,8 @@ namespace al { -extern const int16_t muLawDecompressionTable[256]; -extern const int16_t aLawDecompressionTable[256]; +extern const std::array<int16_t,256> muLawDecompressionTable; +extern const std::array<int16_t,256> aLawDecompressionTable; template<FmtType T> diff --git a/core/fpu_ctrl.cpp b/core/fpu_ctrl.cpp index 435855ad..28e60c04 100644 --- a/core/fpu_ctrl.cpp +++ b/core/fpu_ctrl.cpp @@ -64,29 +64,24 @@ void reset_fpu(unsigned int state [[maybe_unused]]) } // namespace -void FPUCtl::enter() noexcept +unsigned int FPUCtl::Set() noexcept { - if(this->in_mode) return; - + unsigned int state{}; #if defined(HAVE_SSE_INTRINSICS) - disable_denormals(&this->sse_state); + disable_denormals(&state); #elif defined(HAVE_SSE) if((CPUCapFlags&CPU_CAP_SSE)) - disable_denormals(&this->sse_state); + disable_denormals(&state); #endif - - this->in_mode = true; + return state; } -void FPUCtl::leave() noexcept +void FPUCtl::Reset(unsigned int state [[maybe_unused]]) noexcept { - if(!this->in_mode) return; - #if defined(HAVE_SSE_INTRINSICS) - reset_fpu(this->sse_state); + reset_fpu(state); #elif defined(HAVE_SSE) if((CPUCapFlags&CPU_CAP_SSE)) - reset_fpu(this->sse_state); + reset_fpu(state); #endif - this->in_mode = false; } diff --git a/core/fpu_ctrl.h b/core/fpu_ctrl.h index 9554313a..d4f75ec3 100644 --- a/core/fpu_ctrl.h +++ b/core/fpu_ctrl.h @@ -2,20 +2,31 @@ #define CORE_FPU_CTRL_H class FPUCtl { -#if defined(HAVE_SSE_INTRINSICS) || (defined(__GNUC__) && defined(HAVE_SSE)) unsigned int sse_state{}; -#endif bool in_mode{}; + static unsigned int Set() noexcept; + static void Reset(unsigned int state) noexcept; + public: - FPUCtl() noexcept { enter(); in_mode = true; } - ~FPUCtl() { if(in_mode) leave(); } + FPUCtl() noexcept : sse_state{Set()}, in_mode{true} { } + ~FPUCtl() { if(in_mode) Reset(sse_state); } FPUCtl(const FPUCtl&) = delete; FPUCtl& operator=(const FPUCtl&) = delete; - void enter() noexcept; - void leave() noexcept; + void enter() noexcept + { + if(!in_mode) + sse_state = Set(); + in_mode = true; + } + void leave() noexcept + { + if(in_mode) + Reset(sse_state); + in_mode = false; + } }; #endif /* CORE_FPU_CTRL_H */ diff --git a/core/front_stablizer.h b/core/front_stablizer.h index 6825111a..8eeb6d74 100644 --- a/core/front_stablizer.h +++ b/core/front_stablizer.h @@ -7,6 +7,7 @@ #include "almalloc.h" #include "bufferline.h" #include "filters/splitter.h" +#include "flexarray.h" struct FrontStablizer { diff --git a/core/helpers.cpp b/core/helpers.cpp index 5a996eee..b669b8d6 100644 --- a/core/helpers.cpp +++ b/core/helpers.cpp @@ -177,7 +177,7 @@ std::vector<std::string> SearchDataFiles(const char *ext, const char *subdir) return results; } -void SetRTPriority(void) +void SetRTPriority() { #if !defined(ALSOFT_UWP) if(RTPrioLevel > 0) @@ -256,7 +256,7 @@ const PathNamePair &GetProcBinary() #ifndef __SWITCH__ if(pathname.empty()) { - const char *SelfLinkNames[]{ + std::array SelfLinkNames{ "/proc/self/exe", "/proc/self/file", "/proc/curproc/exe", diff --git a/core/helpers.h b/core/helpers.h index df51c116..64fc67fb 100644 --- a/core/helpers.h +++ b/core/helpers.h @@ -15,11 +15,11 @@ struct PathNamePair { : path{std::forward<T>(path_)}, fname{std::forward<U>(fname_)} { } }; -const PathNamePair &GetProcBinary(void); +const PathNamePair &GetProcBinary(); extern int RTPrioLevel; extern bool AllowRTTimeLimit; -void SetRTPriority(void); +void SetRTPriority(); std::vector<std::string> SearchDataFiles(const char *match, const char *subdir); diff --git a/core/hrtf.cpp b/core/hrtf.cpp index 9a13a004..eef68bf9 100644 --- a/core/hrtf.cpp +++ b/core/hrtf.cpp @@ -18,6 +18,7 @@ #include <mutex> #include <numeric> #include <optional> +#include <tuple> #include <type_traits> #include <utility> #include <vector> @@ -88,10 +89,12 @@ constexpr uint HrirDelayFracHalf{HrirDelayFracOne >> 1}; static_assert(MaxHrirDelay*HrirDelayFracOne < 256, "MAX_HRIR_DELAY or DELAY_FRAC too large"); +/* NOLINTBEGIN(*-avoid-c-arrays) */ constexpr char magicMarker00[8]{'M','i','n','P','H','R','0','0'}; constexpr char magicMarker01[8]{'M','i','n','P','H','R','0','1'}; constexpr char magicMarker02[8]{'M','i','n','P','H','R','0','2'}; constexpr char magicMarker03[8]{'M','i','n','P','H','R','0','3'}; +/* NOLINTEND(*-avoid-c-arrays) */ /* First value for pass-through coefficients (remaining are 0), used for omni- * directional sounds. */ @@ -231,29 +234,29 @@ void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float const auto az1 = CalcAzIndex(mElev[ebase + elev1_idx].azCount, azimuth); /* Calculate the HRIR indices to blend. */ - const size_t idx[4]{ + const std::array<size_t,4> idx{{ ir0offset + az0.idx, ir0offset + ((az0.idx+1) % mElev[ebase + elev0.idx].azCount), ir1offset + az1.idx, ir1offset + ((az1.idx+1) % mElev[ebase + elev1_idx].azCount) - }; + }}; /* Calculate bilinear blending weights, attenuated according to the * directional panning factor. */ - const float blend[4]{ + const std::array<float,4> blend{{ (1.0f-elev0.blend) * (1.0f-az0.blend) * dirfact, (1.0f-elev0.blend) * ( az0.blend) * dirfact, ( elev0.blend) * (1.0f-az1.blend) * dirfact, ( elev0.blend) * ( az1.blend) * dirfact - }; + }}; /* Calculate the blended HRIR delays. */ - float d{mDelays[idx[0]][0]*blend[0] + mDelays[idx[1]][0]*blend[1] + mDelays[idx[2]][0]*blend[2] - + mDelays[idx[3]][0]*blend[3]}; + float d{float(mDelays[idx[0]][0])*blend[0] + float(mDelays[idx[1]][0])*blend[1] + + float(mDelays[idx[2]][0])*blend[2] + float(mDelays[idx[3]][0])*blend[3]}; delays[0] = fastf2u(d * float{1.0f/HrirDelayFracOne}); - d = mDelays[idx[0]][1]*blend[0] + mDelays[idx[1]][1]*blend[1] + mDelays[idx[2]][1]*blend[2] - + mDelays[idx[3]][1]*blend[3]; + d = float(mDelays[idx[0]][1])*blend[0] + float(mDelays[idx[1]][1])*blend[1] + + float(mDelays[idx[2]][1])*blend[2] + float(mDelays[idx[3]][1])*blend[3]; delays[1] = fastf2u(d * float{1.0f/HrirDelayFracOne}); /* Calculate the blended HRIR coefficients. */ @@ -267,7 +270,7 @@ void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float const float mult{blend[c]}; auto blend_coeffs = [mult](const float src, const float coeff) noexcept -> float { return src*mult + coeff; }; - std::transform(srccoeffs, srccoeffs + HrirLength*2, coeffout, coeffout, blend_coeffs); + std::transform(srccoeffs, srccoeffs + HrirLength*2_uz, coeffout, coeffout, blend_coeffs); } } @@ -276,7 +279,8 @@ std::unique_ptr<DirectHrtfState> DirectHrtfState::Create(size_t num_chans) { return std::unique_ptr<DirectHrtfState>{new(FamCount(num_chans)) DirectHrtfState{num_chans}}; } void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool perHrirMin, - const al::span<const AngularPoint> AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels], + const al::span<const AngularPoint> AmbiPoints, + const al::span<const std::array<float,MaxAmbiChannels>> AmbiMatrix, const float XOverFreq, const al::span<const float,MaxAmbiOrder+1> AmbiOrderHFGain) { using double2 = std::array<double,2>; @@ -287,7 +291,8 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool const double xover_norm{double{XOverFreq} / Hrtf->mSampleRate}; mChannels[0].mSplitter.init(static_cast<float>(xover_norm)); - for(size_t i{0};i < mChannels.size();++i) + mChannels[0].mHfScale = AmbiOrderHFGain[0]; + for(size_t i{1};i < mChannels.size();++i) { const size_t order{AmbiIndex::OrderFromChannel[i]}; mChannels[i].mSplitter = mChannels[0].mSplitter; @@ -307,7 +312,7 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool const auto az0 = CalcAzIndex(Hrtf->mElev[elev0.idx].azCount, pt.Azim.value); const auto az1 = CalcAzIndex(Hrtf->mElev[elev1_idx].azCount, pt.Azim.value); - const size_t idx[4]{ + const std::array<size_t,4> idx{ ir0offset + az0.idx, ir0offset + ((az0.idx+1) % Hrtf->mElev[elev0.idx].azCount), ir1offset + az1.idx, @@ -333,34 +338,38 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool auto tmpres = std::vector<std::array<double2,HrirLength>>(mChannels.size()); max_delay = 0; - for(size_t c{0u};c < AmbiPoints.size();++c) + auto matrixline = AmbiMatrix.cbegin(); + for(auto &impulse : impres) { - const ConstHrirSpan hrir{impres[c].hrir}; - const uint base_delay{perHrirMin ? minu(impres[c].ldelay, impres[c].rdelay) : min_delay}; - const uint ldelay{hrir_delay_round(impres[c].ldelay - base_delay)}; - const uint rdelay{hrir_delay_round(impres[c].rdelay - base_delay)}; - max_delay = maxu(max_delay, maxu(impres[c].ldelay, impres[c].rdelay) - base_delay); - - for(size_t i{0u};i < mChannels.size();++i) + const ConstHrirSpan hrir{impulse.hrir}; + const uint base_delay{perHrirMin ? std::min(impulse.ldelay, impulse.rdelay) : min_delay}; + const uint ldelay{hrir_delay_round(impulse.ldelay - base_delay)}; + const uint rdelay{hrir_delay_round(impulse.rdelay - base_delay)}; + max_delay = std::max(max_delay, std::max(impulse.ldelay, impulse.rdelay) - base_delay); + + auto gains = matrixline->cbegin(); + ++matrixline; + for(auto &result : tmpres) { - const double mult{AmbiMatrix[c][i]}; - const size_t numirs{HrirLength - maxz(ldelay, rdelay)}; + const double mult{*(gains++)}; + const size_t numirs{HrirLength - std::max(ldelay, rdelay)}; size_t lidx{ldelay}, ridx{rdelay}; for(size_t j{0};j < numirs;++j) { - tmpres[i][lidx++][0] += hrir[j][0] * mult; - tmpres[i][ridx++][1] += hrir[j][1] * mult; + result[lidx++][0] += hrir[j][0] * mult; + result[ridx++][1] += hrir[j][1] * mult; } } } impres.clear(); - for(size_t i{0u};i < mChannels.size();++i) + auto output = mChannels.begin(); + for(auto &result : tmpres) { - auto copy_arr = [](const double2 &in) noexcept -> float2 + auto cast_array2 = [](const double2 &in) noexcept -> float2 { return float2{{static_cast<float>(in[0]), static_cast<float>(in[1])}}; }; - std::transform(tmpres[i].cbegin(), tmpres[i].cend(), mChannels[i].mCoeffs.begin(), - copy_arr); + std::transform(result.cbegin(), result.cend(), output->mCoeffs.begin(), cast_array2); + ++output; } tmpres.clear(); @@ -378,6 +387,10 @@ std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize, const al::span<const HrtfStore::Elevation> elevs, const HrirArray *coeffs, const ubyte2 *delays, const char *filename) { + static_assert(alignof(HrtfStore::Field) <= alignof(HrtfStore)); + static_assert(alignof(HrtfStore::Elevation) <= alignof(HrtfStore)); + static_assert(16 <= alignof(HrtfStore)); + const size_t irCount{size_t{elevs.back().azCount} + elevs.back().irOffset}; size_t total{sizeof(HrtfStore)}; total = RoundUp(total, alignof(HrtfStore::Field)); /* Align for field infos */ @@ -388,11 +401,12 @@ std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize, total += sizeof(std::declval<HrtfStore&>().mCoeffs[0])*irCount; total += sizeof(std::declval<HrtfStore&>().mDelays[0])*irCount; + static constexpr auto AlignVal = std::align_val_t{alignof(HrtfStore)}; std::unique_ptr<HrtfStore> Hrtf{}; - if(void *ptr{al_calloc(16, total)}) + if(gsl::owner<void*> ptr{::operator new[](total, AlignVal, std::nothrow)}) { - Hrtf.reset(al::construct_at(static_cast<HrtfStore*>(ptr))); - InitRef(Hrtf->mRef, 1u); + Hrtf = decltype(Hrtf){::new(ptr) HrtfStore{}}; + Hrtf->mRef.store(1u, std::memory_order_relaxed); Hrtf->mSampleRate = rate & 0xff'ff'ff; Hrtf->mIrSize = irSize; @@ -492,10 +506,10 @@ T> readle(std::istream &data) static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type"); T ret{}; - std::byte b[sizeof(T)]{}; - if(!data.read(reinterpret_cast<char*>(b), num_bits/8)) + std::array<std::byte,sizeof(T)> b{}; + if(!data.read(reinterpret_cast<char*>(b.data()), num_bits/8)) return static_cast<T>(EOF); - std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast<std::byte*>(&ret)); + std::reverse_copy(b.begin(), b.end(), reinterpret_cast<std::byte*>(&ret)); return fixsign<num_bits>(ret); } @@ -576,7 +590,7 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data, const char *filename) for(auto &hrir : coeffs) { for(auto &val : al::span<float2>{hrir.data(), irSize}) - val[0] = readle<int16_t>(data) / 32768.0f; + val[0] = float(readle<int16_t>(data)) / 32768.0f; } for(auto &val : delays) val[0] = readle<uint8_t>(data); @@ -598,9 +612,9 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data, const char *filename) /* Mirror the left ear responses to the right ear. */ MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); - const HrtfStore::Field field[1]{{0.0f, evCount}}; - return CreateHrtfStore(rate, static_cast<uint8_t>(irSize), field, {elevs.data(), elevs.size()}, - coeffs.data(), delays.data(), filename); + const std::array field{HrtfStore::Field{0.0f, evCount}}; + return CreateHrtfStore(rate, static_cast<uint8_t>(irSize), field, elevs, coeffs.data(), + delays.data(), filename); } std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename) @@ -654,7 +668,7 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename) for(auto &hrir : coeffs) { for(auto &val : al::span<float2>{hrir.data(), irSize}) - val[0] = readle<int16_t>(data) / 32768.0f; + val[0] = float(readle<int16_t>(data)) / 32768.0f; } for(auto &val : delays) val[0] = readle<uint8_t>(data); @@ -676,9 +690,8 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename) /* Mirror the left ear responses to the right ear. */ MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); - const HrtfStore::Field field[1]{{0.0f, evCount}}; - return CreateHrtfStore(rate, irSize, field, {elevs.data(), elevs.size()}, coeffs.data(), - delays.data(), filename); + const std::array field{HrtfStore::Field{0.0f, evCount}}; + return CreateHrtfStore(rate, irSize, field, elevs, coeffs.data(), delays.data(), filename); } std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename) @@ -747,7 +760,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename) return nullptr; } - fields[f].distance = distance / 1000.0f; + fields[f].distance = float(distance) / 1000.0f; fields[f].evCount = evCount; if(f > 0 && fields[f].distance <= fields[f-1].distance) { @@ -796,7 +809,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename) for(auto &hrir : coeffs) { for(auto &val : al::span<float2>{hrir.data(), irSize}) - val[0] = readle<int16_t>(data) / 32768.0f; + val[0] = float(readle<int16_t>(data)) / 32768.0f; } } else if(sampleType == SampleType_S24) @@ -835,8 +848,8 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename) { for(auto &val : al::span<float2>{hrir.data(), irSize}) { - val[0] = readle<int16_t>(data) / 32768.0f; - val[1] = readle<int16_t>(data) / 32768.0f; + val[0] = float(readle<int16_t>(data)) / 32768.0f; + val[1] = float(readle<int16_t>(data)) / 32768.0f; } } } @@ -901,7 +914,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename) elevs__end = std::copy_backward(elevs_src, elevs_src+field.evCount, elevs__end); return ebase + field.evCount; }; - (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs); + std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs); assert(elevs_.begin() == elevs__end); /* Reestablish the IR offset for each elevation index, given the new @@ -936,7 +949,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename) return ebase + field.evCount; }; - (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs); + std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs); assert(coeffs_.begin() == coeffs_end); assert(delays_.begin() == delays_end); @@ -946,8 +959,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename) delays = std::move(delays_); } - return CreateHrtfStore(rate, irSize, {fields.data(), fields.size()}, - {elevs.data(), elevs.size()}, coeffs.data(), delays.data(), filename); + return CreateHrtfStore(rate, irSize, fields, elevs, coeffs.data(), delays.data(), filename); } std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename) @@ -1008,7 +1020,7 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename) return nullptr; } - fields[f].distance = distance / 1000.0f; + fields[f].distance = float(distance) / 1000.0f; fields[f].evCount = evCount; if(f > 0 && fields[f].distance > fields[f-1].distance) { @@ -1115,8 +1127,7 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename) } } - return CreateHrtfStore(rate, irSize, {fields.data(), fields.size()}, - {elevs.data(), elevs.size()}, coeffs.data(), delays.data(), filename); + return CreateHrtfStore(rate, irSize, fields, elevs, coeffs.data(), delays.data(), filename); } @@ -1206,6 +1217,7 @@ al::span<const char> GetResource(int /*name*/) #else +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr unsigned char hrtf_default[]{ #include "default_hrtf.txt" }; @@ -1329,32 +1341,32 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) } std::unique_ptr<HrtfStore> hrtf; - char magic[sizeof(magicMarker03)]; - stream->read(magic, sizeof(magic)); + std::array<char,sizeof(magicMarker03)> magic{}; + stream->read(magic.data(), magic.size()); if(stream->gcount() < static_cast<std::streamsize>(sizeof(magicMarker03))) ERR("%s data is too short (%zu bytes)\n", name.c_str(), stream->gcount()); - else if(memcmp(magic, magicMarker03, sizeof(magicMarker03)) == 0) + else if(memcmp(magic.data(), magicMarker03, sizeof(magicMarker03)) == 0) { TRACE("Detected data set format v3\n"); hrtf = LoadHrtf03(*stream, name.c_str()); } - else if(memcmp(magic, magicMarker02, sizeof(magicMarker02)) == 0) + else if(memcmp(magic.data(), magicMarker02, sizeof(magicMarker02)) == 0) { TRACE("Detected data set format v2\n"); hrtf = LoadHrtf02(*stream, name.c_str()); } - else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0) + else if(memcmp(magic.data(), magicMarker01, sizeof(magicMarker01)) == 0) { TRACE("Detected data set format v1\n"); hrtf = LoadHrtf01(*stream, name.c_str()); } - else if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0) + else if(memcmp(magic.data(), magicMarker00, sizeof(magicMarker00)) == 0) { TRACE("Detected data set format v0\n"); hrtf = LoadHrtf00(*stream, name.c_str()); } else - ERR("Invalid header in %s: \"%.8s\"\n", name.c_str(), magic); + ERR("Invalid header in %s: \"%.8s\"\n", name.c_str(), magic.data()); stream.reset(); if(!hrtf) @@ -1380,7 +1392,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) rs.init(hrtf->mSampleRate, devrate); for(size_t i{0};i < irCount;++i) { - HrirArray &coeffs = const_cast<HrirArray&>(hrtf->mCoeffs[i]); + auto &coeffs = const_cast<HrirArray&>(hrtf->mCoeffs[i]); for(size_t j{0};j < 2;++j) { std::transform(coeffs.cbegin(), coeffs.cend(), inout[0].begin(), @@ -1400,7 +1412,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) { for(size_t j{0};j < 2;++j) { - const float new_delay{std::round(hrtf->mDelays[i][j] * rate_scale) / + const float new_delay{std::round(float(hrtf->mDelays[i][j]) * rate_scale) / float{HrirDelayFracOne}}; max_delay = maxf(max_delay, new_delay); new_delays[i][j] = new_delay; @@ -1420,7 +1432,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) for(size_t i{0};i < irCount;++i) { - ubyte2 &delays = const_cast<ubyte2&>(hrtf->mDelays[i]); + auto &delays = const_cast<ubyte2&>(hrtf->mDelays[i]); for(size_t j{0};j < 2;++j) delays[j] = static_cast<ubyte>(float2int(new_delays[i][j]*delay_scale + 0.5f)); } @@ -1459,9 +1471,9 @@ void HrtfStore::dec_ref() auto remove_unused = [](LoadedHrtf &hrtf) -> bool { HrtfStore *entry{hrtf.mEntry.get()}; - if(entry && ReadRef(entry->mRef) == 0) + if(entry && entry->mRef.load() == 0) { - TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.data()); + TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.c_str()); hrtf.mEntry = nullptr; return true; } diff --git a/core/hrtf.h b/core/hrtf.h index 5e6e09a8..882724b8 100644 --- a/core/hrtf.h +++ b/core/hrtf.h @@ -13,12 +13,13 @@ #include "atomic.h" #include "ambidefs.h" #include "bufferline.h" -#include "mixer/hrtfdefs.h" +#include "flexarray.h" #include "intrusive_ptr.h" +#include "mixer/hrtfdefs.h" -struct HrtfStore { - RefCount mRef; +struct alignas(16) HrtfStore { + std::atomic<uint> mRef; uint mSampleRate : 24; uint mIrSize : 8; @@ -46,7 +47,14 @@ struct HrtfStore { void add_ref(); void dec_ref(); - DEF_PLACE_NEWDEL() + void *operator new(size_t) = delete; + void *operator new[](size_t) = delete; + void operator delete[](void*) noexcept = delete; + + void operator delete(gsl::owner<void*> block, void*) noexcept + { ::operator delete[](block, std::align_val_t{alignof(HrtfStore)}); } + void operator delete(gsl::owner<void*> block) noexcept + { ::operator delete[](block, std::align_val_t{alignof(HrtfStore)}); } }; using HrtfStorePtr = al::intrusive_ptr<HrtfStore>; @@ -60,7 +68,7 @@ struct AngularPoint { struct DirectHrtfState { - std::array<float,BufferLineSize> mTemp; + std::array<float,BufferLineSize> mTemp{}; /* HRTF filter state for dry buffer content */ uint mIrSize{0}; @@ -74,7 +82,8 @@ struct DirectHrtfState { * are ordered and scaled according to the matrix input. */ void build(const HrtfStore *Hrtf, const uint irSize, const bool perHrirMin, - const al::span<const AngularPoint> AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels], + const al::span<const AngularPoint> AmbiPoints, + const al::span<const std::array<float,MaxAmbiChannels>> AmbiMatrix, const float XOverFreq, const al::span<const float,MaxAmbiOrder+1> AmbiOrderHFGain); static std::unique_ptr<DirectHrtfState> Create(size_t num_chans); diff --git a/core/logging.h b/core/logging.h index 06b7cdde..a8d9b731 100644 --- a/core/logging.h +++ b/core/logging.h @@ -22,8 +22,8 @@ using LogCallbackFunc = void(*)(void *userptr, char level, const char *message, void al_set_log_callback(LogCallbackFunc callback, void *userptr); -#ifdef __USE_MINGW_ANSI_STDIO -[[gnu::format(gnu_printf,2,3)]] +#ifdef __MINGW32__ +[[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]] #else [[gnu::format(printf,2,3)]] #endif diff --git a/core/mastering.cpp b/core/mastering.cpp index 4445719b..9eaabc35 100644 --- a/core/mastering.cpp +++ b/core/mastering.cpp @@ -21,8 +21,8 @@ static_assert((BufferLineSize & (BufferLineSize-1)) == 0, "BufferLineSize is not a power of 2"); struct SlidingHold { - alignas(16) float mValues[BufferLineSize]; - uint mExpiries[BufferLineSize]; + alignas(16) FloatBufferLine mValues; + std::array<uint,BufferLineSize> mExpiries; uint mLowerIndex; uint mUpperIndex; uint mLength; @@ -44,8 +44,8 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in) { static constexpr uint mask{BufferLineSize - 1}; const uint length{Hold->mLength}; - float (&values)[BufferLineSize] = Hold->mValues; - uint (&expiries)[BufferLineSize] = Hold->mExpiries; + const al::span values{Hold->mValues}; + const al::span expiries{Hold->mExpiries}; uint lowerIndex{Hold->mLowerIndex}; uint upperIndex{Hold->mUpperIndex}; @@ -60,14 +60,16 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in) } else { - do { + auto findLowerIndex = [&lowerIndex,in,values]() noexcept -> bool + { do { if(!(in >= values[lowerIndex])) - goto found_place; + return true; } while(lowerIndex--); + return false; + }; + while(!findLowerIndex()) lowerIndex = mask; - } while(true); - found_place: lowerIndex = (lowerIndex + 1) & mask; values[lowerIndex] = in; @@ -82,38 +84,38 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in) void ShiftSlidingHold(SlidingHold *Hold, const uint n) { - auto exp_begin = std::begin(Hold->mExpiries) + Hold->mUpperIndex; - auto exp_last = std::begin(Hold->mExpiries) + Hold->mLowerIndex; - if(exp_last-exp_begin < 0) + auto exp_upper = Hold->mExpiries.begin() + Hold->mUpperIndex; + if(Hold->mLowerIndex < Hold->mUpperIndex) { - std::transform(exp_begin, std::end(Hold->mExpiries), exp_begin, - [n](uint e){ return e - n; }); - exp_begin = std::begin(Hold->mExpiries); + std::transform(exp_upper, Hold->mExpiries.end(), exp_upper, + [n](const uint e) noexcept { return e - n; }); + exp_upper = Hold->mExpiries.begin(); } - std::transform(exp_begin, exp_last+1, exp_begin, [n](uint e){ return e - n; }); + const auto exp_lower = Hold->mExpiries.begin() + Hold->mLowerIndex; + std::transform(exp_upper, exp_lower+1, exp_upper, + [n](const uint e) noexcept { return e - n; }); } +} // namespace /* Multichannel compression is linked via the absolute maximum of all * channels. */ -void LinkChannels(Compressor *Comp, const uint SamplesToDo, const FloatBufferLine *OutBuffer) +void Compressor::linkChannels(const uint SamplesToDo, const FloatBufferLine *OutBuffer) { - const size_t numChans{Comp->mNumChans}; - ASSUME(SamplesToDo > 0); - ASSUME(numChans > 0); - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; + const auto side_begin = mSideChain.begin() + mLookAhead; std::fill(side_begin, side_begin+SamplesToDo, 0.0f); auto fill_max = [SamplesToDo,side_begin](const FloatBufferLine &input) -> void { const float *RESTRICT buffer{al::assume_aligned<16>(input.data())}; - auto max_abs = std::bind(maxf, _1, std::bind(static_cast<float(&)(float)>(std::fabs), _2)); + auto max_abs = [](const float s0, const float s1) noexcept -> float + { return std::max(s0, std::fabs(s1)); }; std::transform(side_begin, side_begin+SamplesToDo, buffer, side_begin, max_abs); }; - std::for_each(OutBuffer, OutBuffer+numChans, fill_max); + std::for_each(OutBuffer, OutBuffer+mNumChans, fill_max); } /* This calculates the squared crest factor of the control signal for the @@ -121,59 +123,59 @@ void LinkChannels(Compressor *Comp, const uint SamplesToDo, const FloatBufferLin * it uses an instantaneous squared peak detector and a squared RMS detector * both with 200ms release times. */ -void CrestDetector(Compressor *Comp, const uint SamplesToDo) +void Compressor::crestDetector(const uint SamplesToDo) { - const float a_crest{Comp->mCrestCoeff}; - float y2_peak{Comp->mLastPeakSq}; - float y2_rms{Comp->mLastRmsSq}; + const float a_crest{mCrestCoeff}; + float y2_peak{mLastPeakSq}; + float y2_rms{mLastRmsSq}; ASSUME(SamplesToDo > 0); auto calc_crest = [&y2_rms,&y2_peak,a_crest](const float x_abs) noexcept -> float { - const float x2{clampf(x_abs * x_abs, 0.000001f, 1000000.0f)}; + const float x2{clampf(x_abs*x_abs, 0.000001f, 1000000.0f)}; - y2_peak = maxf(x2, lerpf(x2, y2_peak, a_crest)); + y2_peak = std::max(x2, lerpf(x2, y2_peak, a_crest)); y2_rms = lerpf(x2, y2_rms, a_crest); return y2_peak / y2_rms; }; - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::transform(side_begin, side_begin+SamplesToDo, std::begin(Comp->mCrestFactor), calc_crest); + const auto side_begin = mSideChain.begin() + mLookAhead; + std::transform(side_begin, side_begin+SamplesToDo, mCrestFactor.begin(), calc_crest); - Comp->mLastPeakSq = y2_peak; - Comp->mLastRmsSq = y2_rms; + mLastPeakSq = y2_peak; + mLastRmsSq = y2_rms; } /* The side-chain starts with a simple peak detector (based on the absolute * value of the incoming signal) and performs most of its operations in the * log domain. */ -void PeakDetector(Compressor *Comp, const uint SamplesToDo) +void Compressor::peakDetector(const uint SamplesToDo) { ASSUME(SamplesToDo > 0); - /* Clamp the minimum amplitude to near-zero and convert to logarithm. */ - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; + /* Clamp the minimum amplitude to near-zero and convert to logarithmic. */ + const auto side_begin = mSideChain.begin() + mLookAhead; std::transform(side_begin, side_begin+SamplesToDo, side_begin, - [](float s) { return std::log(maxf(0.000001f, s)); }); + [](float s) { return std::log(std::max(0.000001f, s)); }); } /* An optional hold can be used to extend the peak detector so it can more * solidly detect fast transients. This is best used when operating as a * limiter. */ -void PeakHoldDetector(Compressor *Comp, const uint SamplesToDo) +void Compressor::peakHoldDetector(const uint SamplesToDo) { ASSUME(SamplesToDo > 0); - SlidingHold *hold{Comp->mHold}; + SlidingHold *hold{mHold.get()}; uint i{0}; auto detect_peak = [&i,hold](const float x_abs) -> float { - const float x_G{std::log(maxf(0.000001f, x_abs))}; + const float x_G{std::log(std::max(0.000001f, x_abs))}; return UpdateSlidingHold(hold, i++, x_G); }; - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; + auto side_begin = mSideChain.begin() + mLookAhead; std::transform(side_begin, side_begin+SamplesToDo, side_begin, detect_peak); ShiftSlidingHold(hold, SamplesToDo); @@ -184,46 +186,46 @@ void PeakHoldDetector(Compressor *Comp, const uint SamplesToDo) * to knee width, attack/release times, make-up/post gain, and clipping * reduction. */ -void GainCompressor(Compressor *Comp, const uint SamplesToDo) +void Compressor::gainCompressor(const uint SamplesToDo) { - const bool autoKnee{Comp->mAuto.Knee}; - const bool autoAttack{Comp->mAuto.Attack}; - const bool autoRelease{Comp->mAuto.Release}; - const bool autoPostGain{Comp->mAuto.PostGain}; - const bool autoDeclip{Comp->mAuto.Declip}; - const uint lookAhead{Comp->mLookAhead}; - const float threshold{Comp->mThreshold}; - const float slope{Comp->mSlope}; - const float attack{Comp->mAttack}; - const float release{Comp->mRelease}; - const float c_est{Comp->mGainEstimate}; - const float a_adp{Comp->mAdaptCoeff}; - const float *crestFactor{Comp->mCrestFactor}; - float postGain{Comp->mPostGain}; - float knee{Comp->mKnee}; + const bool autoKnee{mAuto.Knee}; + const bool autoAttack{mAuto.Attack}; + const bool autoRelease{mAuto.Release}; + const bool autoPostGain{mAuto.PostGain}; + const bool autoDeclip{mAuto.Declip}; + const float threshold{mThreshold}; + const float slope{mSlope}; + const float attack{mAttack}; + const float release{mRelease}; + const float c_est{mGainEstimate}; + const float a_adp{mAdaptCoeff}; + auto lookAhead = mSideChain.cbegin() + mLookAhead; + auto crestFactor = mCrestFactor.cbegin(); + float postGain{mPostGain}; + float knee{mKnee}; float t_att{attack}; float t_rel{release - attack}; float a_att{std::exp(-1.0f / t_att)}; float a_rel{std::exp(-1.0f / t_rel)}; - float y_1{Comp->mLastRelease}; - float y_L{Comp->mLastAttack}; - float c_dev{Comp->mLastGainDev}; + float y_1{mLastRelease}; + float y_L{mLastAttack}; + float c_dev{mLastGainDev}; ASSUME(SamplesToDo > 0); - for(float &sideChain : al::span<float>{Comp->mSideChain, SamplesToDo}) + auto process = [&](const float input) -> float { if(autoKnee) - knee = maxf(0.0f, 2.5f * (c_dev + c_est)); + knee = std::max(0.0f, 2.5f * (c_dev + c_est)); const float knee_h{0.5f * knee}; /* This is the gain computer. It applies a static compression curve * to the control signal. */ - const float x_over{std::addressof(sideChain)[lookAhead] - threshold}; + const float x_over{*(lookAhead++) - threshold}; const float y_G{ (x_over <= -knee_h) ? 0.0f : - (std::fabs(x_over) < knee_h) ? (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee) : + (std::fabs(x_over) < knee_h) ? (x_over+knee_h) * (x_over+knee_h) / (2.0f * knee) : x_over}; const float y2_crest{*(crestFactor++)}; @@ -243,7 +245,7 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo) * above to compensate for the chained operating mode. */ const float x_L{-slope * y_G}; - y_1 = maxf(x_L, lerpf(x_L, y_1, a_rel)); + y_1 = std::max(x_L, lerpf(x_L, y_1, a_rel)); y_L = lerpf(y_1, y_L, a_att); /* Knee width and make-up gain automation make use of a smoothed @@ -262,17 +264,19 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo) * same output level. */ if(autoDeclip) - c_dev = maxf(c_dev, sideChain - y_L - threshold - c_est); + c_dev = std::max(c_dev, input - y_L - threshold - c_est); postGain = -(c_dev + c_est); } - sideChain = std::exp(postGain - y_L); - } + return std::exp(postGain - y_L); + }; + auto sideChain = al::span{mSideChain}.first(SamplesToDo); + std::transform(sideChain.begin(), sideChain.end(), sideChain.begin(), process); - Comp->mLastRelease = y_1; - Comp->mLastAttack = y_L; - Comp->mLastGainDev = c_dev; + mLastRelease = y_1; + mLastAttack = y_L; + mLastGainDev = c_dev; } /* Combined with the hold time, a look-ahead delay can improve handling of @@ -280,10 +284,10 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo) * reaching the offending impulse. This is best used when operating as a * limiter. */ -void SignalDelay(Compressor *Comp, const uint SamplesToDo, FloatBufferLine *OutBuffer) +void Compressor::signalDelay(const uint SamplesToDo, FloatBufferLine *OutBuffer) { - const size_t numChans{Comp->mNumChans}; - const uint lookAhead{Comp->mLookAhead}; + const size_t numChans{mNumChans}; + const uint lookAhead{mLookAhead}; ASSUME(SamplesToDo > 0); ASSUME(numChans > 0); @@ -292,7 +296,7 @@ void SignalDelay(Compressor *Comp, const uint SamplesToDo, FloatBufferLine *OutB for(size_t c{0};c < numChans;c++) { float *inout{al::assume_aligned<16>(OutBuffer[c].data())}; - float *delaybuf{al::assume_aligned<16>(Comp->mDelay[c].data())}; + float *delaybuf{al::assume_aligned<16>(mDelay[c].data())}; auto inout_end = inout + SamplesToDo; if(SamplesToDo >= lookAhead) LIKELY @@ -308,8 +312,6 @@ void SignalDelay(Compressor *Comp, const uint SamplesToDo, FloatBufferLine *OutB } } -} // namespace - std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const float SampleRate, const bool AutoKnee, const bool AutoAttack, const bool AutoRelease, const bool AutoPostGain, @@ -317,24 +319,12 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa const float PostGainDb, const float ThresholdDb, const float Ratio, const float KneeDb, const float AttackTime, const float ReleaseTime) { - const auto lookAhead = static_cast<uint>( - clampf(std::round(LookAheadTime*SampleRate), 0.0f, BufferLineSize-1)); - const auto hold = static_cast<uint>( - clampf(std::round(HoldTime*SampleRate), 0.0f, BufferLineSize-1)); + const auto lookAhead = static_cast<uint>(clampf(std::round(LookAheadTime*SampleRate), 0.0f, + BufferLineSize-1)); + const auto hold = static_cast<uint>(clampf(std::round(HoldTime*SampleRate), 0.0f, + BufferLineSize-1)); - size_t size{sizeof(Compressor)}; - if(lookAhead > 0) - { - size += sizeof(*Compressor::mDelay) * NumChans; - /* The sliding hold implementation doesn't handle a length of 1. A 1- - * sample hold is useless anyway, it would only ever give back what was - * just given to it. - */ - if(hold > 1) - size += sizeof(*Compressor::mHold); - } - - auto Comp = CompressorPtr{al::construct_at(static_cast<Compressor*>(al_calloc(16, size)))}; + auto Comp = CompressorPtr{new Compressor{}}; Comp->mNumChans = NumChans; Comp->mAuto.Knee = AutoKnee; Comp->mAuto.Attack = AutoAttack; @@ -343,12 +333,12 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa Comp->mAuto.Declip = AutoPostGain && AutoDeclip; Comp->mLookAhead = lookAhead; Comp->mPreGain = std::pow(10.0f, PreGainDb / 20.0f); - Comp->mPostGain = PostGainDb * std::log(10.0f) / 20.0f; - Comp->mThreshold = ThresholdDb * std::log(10.0f) / 20.0f; - Comp->mSlope = 1.0f / maxf(1.0f, Ratio) - 1.0f; - Comp->mKnee = maxf(0.0f, KneeDb * std::log(10.0f) / 20.0f); - Comp->mAttack = maxf(1.0f, AttackTime * SampleRate); - Comp->mRelease = maxf(1.0f, ReleaseTime * SampleRate); + Comp->mPostGain = std::log(10.0f)/20.0f * PostGainDb; + Comp->mThreshold = std::log(10.0f)/20.0f * ThresholdDb; + Comp->mSlope = 1.0f / std::max(1.0f, Ratio) - 1.0f; + Comp->mKnee = std::max(0.0f, std::log(10.0f)/20.0f * KneeDb); + Comp->mAttack = std::max(1.0f, AttackTime * SampleRate); + Comp->mRelease = std::max(1.0f, ReleaseTime * SampleRate); /* Knee width automation actually treats the compressor as a limiter. By * varying the knee width, it can effectively be seen as applying @@ -359,17 +349,18 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa if(lookAhead > 0) { + /* The sliding hold implementation doesn't handle a length of 1. A 1- + * sample hold is useless anyway, it would only ever give back what was + * just given to it. + */ if(hold > 1) { - Comp->mHold = al::construct_at(reinterpret_cast<SlidingHold*>(Comp.get() + 1)); + Comp->mHold = std::make_unique<SlidingHold>(); Comp->mHold->mValues[0] = -std::numeric_limits<float>::infinity(); Comp->mHold->mExpiries[0] = hold; Comp->mHold->mLength = hold; - Comp->mDelay = reinterpret_cast<FloatBufferLine*>(Comp->mHold + 1); } - else - Comp->mDelay = reinterpret_cast<FloatBufferLine*>(Comp.get() + 1); - std::uninitialized_fill_n(Comp->mDelay, NumChans, FloatBufferLine{}); + Comp->mDelay.resize(NumChans, FloatBufferLine{}); } Comp->mCrestCoeff = std::exp(-1.0f / (0.200f * SampleRate)); // 200ms @@ -379,15 +370,7 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa return Comp; } -Compressor::~Compressor() -{ - if(mHold) - std::destroy_at(mHold); - mHold = nullptr; - if(mDelay) - std::destroy_n(mDelay, mNumChans); - mDelay = nullptr; -} +Compressor::~Compressor() = default; void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer) @@ -404,36 +387,36 @@ void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer) { float *buffer{al::assume_aligned<16>(input.data())}; std::transform(buffer, buffer+SamplesToDo, buffer, - [preGain](float s) { return s * preGain; }); + [preGain](const float s) noexcept { return s * preGain; }); }; std::for_each(OutBuffer, OutBuffer+numChans, apply_gain); } - LinkChannels(this, SamplesToDo, OutBuffer); + linkChannels(SamplesToDo, OutBuffer); if(mAuto.Attack || mAuto.Release) - CrestDetector(this, SamplesToDo); + crestDetector(SamplesToDo); if(mHold) - PeakHoldDetector(this, SamplesToDo); + peakHoldDetector(SamplesToDo); else - PeakDetector(this, SamplesToDo); + peakDetector(SamplesToDo); - GainCompressor(this, SamplesToDo); + gainCompressor(SamplesToDo); - if(mDelay) - SignalDelay(this, SamplesToDo, OutBuffer); + if(!mDelay.empty()) + signalDelay(SamplesToDo, OutBuffer); - const float (&sideChain)[BufferLineSize*2] = mSideChain; - auto apply_comp = [SamplesToDo,&sideChain](FloatBufferLine &input) noexcept -> void + const auto sideChain = al::span{mSideChain}; + auto apply_comp = [SamplesToDo,sideChain](FloatBufferLine &input) noexcept -> void { float *buffer{al::assume_aligned<16>(input.data())}; - const float *gains{al::assume_aligned<16>(&sideChain[0])}; + const float *gains{al::assume_aligned<16>(sideChain.data())}; std::transform(gains, gains+SamplesToDo, buffer, buffer, - [](float g, float s) { return g * s; }); + [](const float g, const float s) noexcept { return g * s; }); }; std::for_each(OutBuffer, OutBuffer+numChans, apply_comp); - auto side_begin = std::begin(mSideChain) + SamplesToDo; - std::copy(side_begin, side_begin+mLookAhead, std::begin(mSideChain)); + auto side_begin = mSideChain.begin() + SamplesToDo; + std::copy(side_begin, side_begin+mLookAhead, mSideChain.begin()); } diff --git a/core/mastering.h b/core/mastering.h index 1a36937c..ca7266a3 100644 --- a/core/mastering.h +++ b/core/mastering.h @@ -1,10 +1,14 @@ #ifndef CORE_MASTERING_H #define CORE_MASTERING_H +#include <array> #include <memory> #include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" #include "bufferline.h" +#include "vector.h" struct SlidingHold; @@ -21,7 +25,7 @@ using uint = unsigned int; * * http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/ */ -struct Compressor { +class Compressor { size_t mNumChans{0u}; struct { @@ -44,11 +48,11 @@ struct Compressor { float mAttack{0.0f}; float mRelease{0.0f}; - alignas(16) float mSideChain[2*BufferLineSize]{}; - alignas(16) float mCrestFactor[BufferLineSize]{}; + alignas(16) std::array<float,BufferLineSize*2_uz> mSideChain{}; + alignas(16) std::array<float,BufferLineSize> mCrestFactor{}; - SlidingHold *mHold{nullptr}; - FloatBufferLine *mDelay{nullptr}; + std::unique_ptr<SlidingHold> mHold; + al::vector<FloatBufferLine,16> mDelay; float mCrestCoeff{0.0f}; float mGainEstimate{0.0f}; @@ -60,12 +64,19 @@ struct Compressor { float mLastAttack{0.0f}; float mLastGainDev{0.0f}; + Compressor() = default; + void linkChannels(const uint SamplesToDo, const FloatBufferLine *OutBuffer); + void crestDetector(const uint SamplesToDo); + void peakDetector(const uint SamplesToDo); + void peakHoldDetector(const uint SamplesToDo); + void gainCompressor(const uint SamplesToDo); + void signalDelay(const uint SamplesToDo, FloatBufferLine *OutBuffer); + +public: ~Compressor(); void process(const uint SamplesToDo, FloatBufferLine *OutBuffer); - int getLookAhead() const noexcept { return static_cast<int>(mLookAhead); } - - DEF_PLACE_NEWDEL() + [[nodiscard]] auto getLookAhead() const noexcept -> uint { return mLookAhead; } /** * The compressor is initialized with the following settings: diff --git a/core/mixer/defs.h b/core/mixer/defs.h index 48daca9b..4d0d19bf 100644 --- a/core/mixer/defs.h +++ b/core/mixer/defs.h @@ -2,7 +2,8 @@ #define CORE_MIXER_DEFS_H #include <array> -#include <stdlib.h> +#include <cstdlib> +#include <variant> #include "alspan.h" #include "core/bufferline.h" @@ -17,12 +18,12 @@ using uint = unsigned int; using float2 = std::array<float,2>; -constexpr int MixerFracBits{16}; -constexpr int MixerFracOne{1 << MixerFracBits}; -constexpr int MixerFracMask{MixerFracOne - 1}; -constexpr int MixerFracHalf{MixerFracOne >> 1}; +inline constexpr int MixerFracBits{16}; +inline constexpr int MixerFracOne{1 << MixerFracBits}; +inline constexpr int MixerFracMask{MixerFracOne - 1}; +inline constexpr int MixerFracHalf{MixerFracOne >> 1}; -constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */ +inline constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */ enum class Resampler : uint8_t { @@ -59,10 +60,7 @@ struct CubicState { const CubicCoefficients *filter; }; -union InterpState { - CubicState cubic; - BsincState bsinc; -}; +using InterpState = std::variant<CubicState,BsincState>; using ResamplerFunc = void(*)(const InterpState *state, const float *RESTRICT src, uint frac, const uint increment, const al::span<float> dst); @@ -94,7 +92,8 @@ void MixDirectHrtf_(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOu /* Vectorized resampler helpers */ template<size_t N> -inline void InitPosArrays(uint frac, uint increment, uint (&frac_arr)[N], uint (&pos_arr)[N]) +inline void InitPosArrays(uint frac, uint increment, const al::span<uint,N> frac_arr, + const al::span<uint,N> pos_arr) { pos_arr[0] = 0; frac_arr[0] = frac; diff --git a/core/mixer/mixer_c.cpp b/core/mixer/mixer_c.cpp index 28a92ef7..93306bba 100644 --- a/core/mixer/mixer_c.cpp +++ b/core/mixer/mixer_c.cpp @@ -28,53 +28,53 @@ constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits}; constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits}; constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; -inline float do_point(const InterpState&, const float *RESTRICT vals, const uint) +inline float do_point(const float *RESTRICT vals, const uint) { return vals[0]; } -inline float do_lerp(const InterpState&, const float *RESTRICT vals, const uint frac) +inline float do_lerp(const float *RESTRICT vals, const uint frac) { return lerpf(vals[0], vals[1], static_cast<float>(frac)*(1.0f/MixerFracOne)); } -inline float do_cubic(const InterpState &istate, const float *RESTRICT vals, const uint frac) +inline float do_cubic(const CubicState &istate, const float *RESTRICT vals, const uint frac) { /* Calculate the phase index and factor. */ const uint pi{frac >> CubicPhaseDiffBits}; const float pf{static_cast<float>(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; - const float *RESTRICT fil{al::assume_aligned<16>(istate.cubic.filter[pi].mCoeffs)}; - const float *RESTRICT phd{al::assume_aligned<16>(istate.cubic.filter[pi].mDeltas)}; + const float *RESTRICT fil{al::assume_aligned<16>(istate.filter[pi].mCoeffs.data())}; + const float *RESTRICT phd{al::assume_aligned<16>(istate.filter[pi].mDeltas.data())}; /* Apply the phase interpolated filter. */ return (fil[0] + pf*phd[0])*vals[0] + (fil[1] + pf*phd[1])*vals[1] + (fil[2] + pf*phd[2])*vals[2] + (fil[3] + pf*phd[3])*vals[3]; } -inline float do_bsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac) +inline float do_bsinc(const BsincState &istate, const float *RESTRICT vals, const uint frac) { - const size_t m{istate.bsinc.m}; + const size_t m{istate.m}; ASSUME(m > 0); /* Calculate the phase index and factor. */ const uint pi{frac >> BsincPhaseDiffBits}; const float pf{static_cast<float>(frac&BsincPhaseDiffMask) * (1.0f/BsincPhaseDiffOne)}; - const float *RESTRICT fil{istate.bsinc.filter + m*pi*2}; + const float *RESTRICT fil{istate.filter + m*pi*2_uz}; const float *RESTRICT phd{fil + m}; - const float *RESTRICT scd{fil + BSincPhaseCount*2*m}; + const float *RESTRICT scd{fil + BSincPhaseCount*2_uz*m}; const float *RESTRICT spd{scd + m}; /* Apply the scale and phase interpolated filter. */ float r{0.0f}; for(size_t j_f{0};j_f < m;j_f++) - r += (fil[j_f] + istate.bsinc.sf*scd[j_f] + pf*(phd[j_f] + istate.bsinc.sf*spd[j_f])) * vals[j_f]; + r += (fil[j_f] + istate.sf*scd[j_f] + pf*(phd[j_f] + istate.sf*spd[j_f])) * vals[j_f]; return r; } -inline float do_fastbsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac) +inline float do_fastbsinc(const BsincState &istate, const float *RESTRICT vals, const uint frac) { - const size_t m{istate.bsinc.m}; + const size_t m{istate.m}; ASSUME(m > 0); /* Calculate the phase index and factor. */ const uint pi{frac >> BsincPhaseDiffBits}; const float pf{static_cast<float>(frac&BsincPhaseDiffMask) * (1.0f/BsincPhaseDiffOne)}; - const float *RESTRICT fil{istate.bsinc.filter + m*pi*2}; + const float *RESTRICT fil{istate.filter + m*pi*2_uz}; const float *RESTRICT phd{fil + m}; /* Apply the phase interpolated filter. */ @@ -84,16 +84,30 @@ inline float do_fastbsinc(const InterpState &istate, const float *RESTRICT vals, return r; } -using SamplerT = float(&)(const InterpState&, const float*RESTRICT, const uint); +using SamplerT = float(&)(const float*RESTRICT, const uint); template<SamplerT Sampler> -void DoResample(const InterpState *state, const float *RESTRICT src, uint frac, +void DoResample(const float *RESTRICT src, uint frac, const uint increment, + const al::span<float> dst) +{ + ASSUME(frac < MixerFracOne); + for(float &out : dst) + { + out = Sampler(src, frac); + + frac += increment; + src += frac>>MixerFracBits; + frac &= MixerFracMask; + } +} + +template<typename T, typename U> +void DoResample(T sampler, const U istate, const float *RESTRICT src, uint frac, const uint increment, const al::span<float> dst) { - const InterpState istate{*state}; ASSUME(frac < MixerFracOne); for(float &out : dst) { - out = Sampler(istate, src, frac); + out = sampler(istate, src, frac); frac += increment; src += frac>>MixerFracBits; @@ -146,29 +160,35 @@ force_inline void MixLine(const al::span<const float> InSamples, float *RESTRICT } // namespace template<> -void Resample_<PointTag,CTag>(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_<PointTag,CTag>(const InterpState*, const float *RESTRICT src, uint frac, const uint increment, const al::span<float> dst) -{ DoResample<do_point>(state, src, frac, increment, dst); } +{ DoResample<do_point>(src, frac, increment, dst); } template<> -void Resample_<LerpTag,CTag>(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_<LerpTag,CTag>(const InterpState*, const float *RESTRICT src, uint frac, const uint increment, const al::span<float> dst) -{ DoResample<do_lerp>(state, src, frac, increment, dst); } +{ DoResample<do_lerp>(src, frac, increment, dst); } template<> void Resample_<CubicTag,CTag>(const InterpState *state, const float *RESTRICT src, uint frac, const uint increment, const al::span<float> dst) -{ DoResample<do_cubic>(state, src-1, frac, increment, dst); } +{ DoResample(do_cubic, std::get<CubicState>(*state), src-1, frac, increment, dst); } template<> void Resample_<BSincTag,CTag>(const InterpState *state, const float *RESTRICT src, uint frac, const uint increment, const al::span<float> dst) -{ DoResample<do_bsinc>(state, src-state->bsinc.l, frac, increment, dst); } +{ + const auto istate = std::get<BsincState>(*state); + DoResample(do_bsinc, istate, src-istate.l, frac, increment, dst); +} template<> void Resample_<FastBSincTag,CTag>(const InterpState *state, const float *RESTRICT src, uint frac, const uint increment, const al::span<float> dst) -{ DoResample<do_fastbsinc>(state, src-state->bsinc.l, frac, increment, dst); } +{ + const auto istate = std::get<BsincState>(*state); + DoResample(do_fastbsinc, istate, src-istate.l, frac, increment, dst); +} template<> diff --git a/core/mixer/mixer_neon.cpp b/core/mixer/mixer_neon.cpp index ead775af..9fa2425f 100644 --- a/core/mixer/mixer_neon.cpp +++ b/core/mixer/mixer_neon.cpp @@ -146,12 +146,11 @@ void Resample_<LerpTag,NEONTag>(const InterpState*, const float *RESTRICT src, u const int32x4_t increment4 = vdupq_n_s32(static_cast<int>(increment*4)); const float32x4_t fracOne4 = vdupq_n_f32(1.0f/MixerFracOne); const int32x4_t fracMask4 = vdupq_n_s32(MixerFracMask); - alignas(16) uint pos_[4], frac_[4]; - int32x4_t pos4, frac4; - InitPosArrays(frac, increment, frac_, pos_); - frac4 = vld1q_s32(reinterpret_cast<int*>(frac_)); - pos4 = vld1q_s32(reinterpret_cast<int*>(pos_)); + alignas(16) std::array<uint,4> pos_, frac_; + InitPosArrays(frac, increment, al::span{frac_}, al::span{pos_}); + int32x4_t frac4 = vld1q_s32(reinterpret_cast<int*>(frac_.data())); + int32x4_t pos4 = vld1q_s32(reinterpret_cast<int*>(pos_.data())); auto dst_iter = dst.begin(); for(size_t todo{dst.size()>>2};todo;--todo) @@ -197,7 +196,7 @@ void Resample_<CubicTag,NEONTag>(const InterpState *state, const float *RESTRICT { ASSUME(frac < MixerFracOne); - const CubicCoefficients *RESTRICT filter = al::assume_aligned<16>(state->cubic.filter); + const auto *RESTRICT filter = al::assume_aligned<16>(std::get<CubicState>(*state).filter); src -= 1; for(float &out_sample : dst) @@ -209,8 +208,8 @@ void Resample_<CubicTag,NEONTag>(const InterpState *state, const float *RESTRICT /* Apply the phase interpolated filter. */ /* f = fil + pf*phd */ - const float32x4_t f4 = vmlaq_f32(vld1q_f32(filter[pi].mCoeffs), pf4, - vld1q_f32(filter[pi].mDeltas)); + const float32x4_t f4 = vmlaq_f32(vld1q_f32(filter[pi].mCoeffs.data()), pf4, + vld1q_f32(filter[pi].mDeltas.data())); /* r = f*src */ float32x4_t r4{vmulq_f32(f4, vld1q_f32(src))}; @@ -227,13 +226,14 @@ template<> void Resample_<BSincTag,NEONTag>(const InterpState *state, const float *RESTRICT src, uint frac, const uint increment, const al::span<float> dst) { - const float *const filter{state->bsinc.filter}; - const float32x4_t sf4{vdupq_n_f32(state->bsinc.sf)}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get<BsincState>(*state); + const float *const filter{bsinc.filter}; + const float32x4_t sf4{vdupq_n_f32(bsinc.sf)}; + const size_t m{bsinc.m}; ASSUME(m > 0); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; + src -= bsinc.l; for(float &out_sample : dst) { // Calculate the phase index and factor. @@ -244,9 +244,9 @@ void Resample_<BSincTag,NEONTag>(const InterpState *state, const float *RESTRICT float32x4_t r4{vdupq_n_f32(0.0f)}; { const float32x4_t pf4{vdupq_n_f32(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; + const float *RESTRICT fil{filter + m*pi*2_uz}; const float *RESTRICT phd{fil + m}; - const float *RESTRICT scd{fil + BSincPhaseCount*2*m}; + const float *RESTRICT scd{fil + BSincPhaseCount*2_uz*m}; const float *RESTRICT spd{scd + m}; size_t td{m >> 2}; size_t j{0u}; @@ -271,15 +271,16 @@ void Resample_<BSincTag,NEONTag>(const InterpState *state, const float *RESTRICT } template<> -void Resample_<FastBSincTag,NEONTag>(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span<float> dst) +void Resample_<FastBSincTag,NEONTag>(const InterpState *state, const float *RESTRICT src, + uint frac, const uint increment, const al::span<float> dst) { - const float *const filter{state->bsinc.filter}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get<BsincState>(*state); + const float *const filter{bsinc.filter}; + const size_t m{bsinc.m}; ASSUME(m > 0); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; + src -= bsinc.l; for(float &out_sample : dst) { // Calculate the phase index and factor. @@ -290,7 +291,7 @@ void Resample_<FastBSincTag,NEONTag>(const InterpState *state, const float *REST float32x4_t r4{vdupq_n_f32(0.0f)}; { const float32x4_t pf4{vdupq_n_f32(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; + const float *RESTRICT fil{filter + m*pi*2_uz}; const float *RESTRICT phd{fil + m}; size_t td{m >> 2}; size_t j{0u}; diff --git a/core/mixer/mixer_sse.cpp b/core/mixer/mixer_sse.cpp index 70f77c14..809d585d 100644 --- a/core/mixer/mixer_sse.cpp +++ b/core/mixer/mixer_sse.cpp @@ -159,7 +159,7 @@ void Resample_<CubicTag,SSETag>(const InterpState *state, const float *RESTRICT { ASSUME(frac < MixerFracOne); - const CubicCoefficients *RESTRICT filter = al::assume_aligned<16>(state->cubic.filter); + const auto *RESTRICT filter = al::assume_aligned<16>(std::get<CubicState>(*state).filter); src -= 1; for(float &out_sample : dst) @@ -171,8 +171,8 @@ void Resample_<CubicTag,SSETag>(const InterpState *state, const float *RESTRICT /* Apply the phase interpolated filter. */ /* f = fil + pf*phd */ - const __m128 f4 = MLA4(_mm_load_ps(filter[pi].mCoeffs), pf4, - _mm_load_ps(filter[pi].mDeltas)); + const __m128 f4 = MLA4(_mm_load_ps(filter[pi].mCoeffs.data()), pf4, + _mm_load_ps(filter[pi].mDeltas.data())); /* r = f*src */ __m128 r4{_mm_mul_ps(f4, _mm_loadu_ps(src))}; @@ -190,13 +190,14 @@ template<> void Resample_<BSincTag,SSETag>(const InterpState *state, const float *RESTRICT src, uint frac, const uint increment, const al::span<float> dst) { - const float *const filter{state->bsinc.filter}; - const __m128 sf4{_mm_set1_ps(state->bsinc.sf)}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get<BsincState>(*state); + const float *const filter{bsinc.filter}; + const __m128 sf4{_mm_set1_ps(bsinc.sf)}; + const size_t m{bsinc.m}; ASSUME(m > 0); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; + src -= bsinc.l; for(float &out_sample : dst) { // Calculate the phase index and factor. @@ -207,9 +208,9 @@ void Resample_<BSincTag,SSETag>(const InterpState *state, const float *RESTRICT __m128 r4{_mm_setzero_ps()}; { const __m128 pf4{_mm_set1_ps(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; + const float *RESTRICT fil{filter + m*pi*2_uz}; const float *RESTRICT phd{fil + m}; - const float *RESTRICT scd{fil + BSincPhaseCount*2*m}; + const float *RESTRICT scd{fil + BSincPhaseCount*2_uz*m}; const float *RESTRICT spd{scd + m}; size_t td{m >> 2}; size_t j{0u}; @@ -238,12 +239,13 @@ template<> void Resample_<FastBSincTag,SSETag>(const InterpState *state, const float *RESTRICT src, uint frac, const uint increment, const al::span<float> dst) { - const float *const filter{state->bsinc.filter}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get<BsincState>(*state); + const float *const filter{bsinc.filter}; + const size_t m{bsinc.m}; ASSUME(m > 0); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; + src -= bsinc.l; for(float &out_sample : dst) { // Calculate the phase index and factor. @@ -254,7 +256,7 @@ void Resample_<FastBSincTag,SSETag>(const InterpState *state, const float *RESTR __m128 r4{_mm_setzero_ps()}; { const __m128 pf4{_mm_set1_ps(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; + const float *RESTRICT fil{filter + m*pi*2_uz}; const float *RESTRICT phd{fil + m}; size_t td{m >> 2}; size_t j{0u}; diff --git a/core/mixer/mixer_sse2.cpp b/core/mixer/mixer_sse2.cpp index edaaf7a1..aa08b7ed 100644 --- a/core/mixer/mixer_sse2.cpp +++ b/core/mixer/mixer_sse2.cpp @@ -44,8 +44,8 @@ void Resample_<LerpTag,SSE2Tag>(const InterpState*, const float *RESTRICT src, u const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)}; const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; - alignas(16) uint pos_[4], frac_[4]; - InitPosArrays(frac, increment, frac_, pos_); + alignas(16) std::array<uint,4> pos_, frac_; + InitPosArrays(frac, increment, al::span{frac_}, al::span{pos_}); __m128i frac4{_mm_setr_epi32(static_cast<int>(frac_[0]), static_cast<int>(frac_[1]), static_cast<int>(frac_[2]), static_cast<int>(frac_[3]))}; __m128i pos4{_mm_setr_epi32(static_cast<int>(pos_[0]), static_cast<int>(pos_[1]), diff --git a/core/mixer/mixer_sse41.cpp b/core/mixer/mixer_sse41.cpp index 8ccd9fd3..d66f9ce5 100644 --- a/core/mixer/mixer_sse41.cpp +++ b/core/mixer/mixer_sse41.cpp @@ -45,8 +45,8 @@ void Resample_<LerpTag,SSE4Tag>(const InterpState*, const float *RESTRICT src, u const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)}; const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; - alignas(16) uint pos_[4], frac_[4]; - InitPosArrays(frac, increment, frac_, pos_); + alignas(16) std::array<uint,4> pos_, frac_; + InitPosArrays(frac, increment, al::span{frac_}, al::span{pos_}); __m128i frac4{_mm_setr_epi32(static_cast<int>(frac_[0]), static_cast<int>(frac_[1]), static_cast<int>(frac_[2]), static_cast<int>(frac_[3]))}; __m128i pos4{_mm_setr_epi32(static_cast<int>(pos_[0]), static_cast<int>(pos_[1]), diff --git a/core/rtkit.cpp b/core/rtkit.cpp index ff944ebf..73ea132f 100644 --- a/core/rtkit.cpp +++ b/core/rtkit.cpp @@ -30,14 +30,14 @@ #include "rtkit.h" -#include <errno.h> +#include <cerrno> #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <memory> -#include <string.h> +#include <cstring> #include <unistd.h> #include <sys/types.h> #ifdef __linux__ diff --git a/core/uhjfilter.cpp b/core/uhjfilter.cpp index 28999e09..681b0abc 100644 --- a/core/uhjfilter.cpp +++ b/core/uhjfilter.cpp @@ -5,6 +5,7 @@ #include <algorithm> #include <iterator> +#include <vector> #include "alcomplex.h" #include "alnumeric.h" @@ -20,11 +21,6 @@ UhjQualityType UhjEncodeQuality{UhjQualityType::Default}; namespace { -struct PFFFTSetupDeleter { - void operator()(PFFFT_Setup *ptr) { pffft_destroy_setup(ptr); } -}; -using PFFFTSetupPtr = std::unique_ptr<PFFFT_Setup,PFFFTSetupDeleter>; - /* Convolution is implemented using a segmented overlap-add method. The filter * response is broken up into multiple segments of 128 samples, and each * segment has an FFT applied with a 256-sample buffer (the latter half left @@ -57,13 +53,11 @@ struct SegmentedFilter { static_assert(N >= sFftLength); static_assert((N % sSampleLength) == 0); - PFFFTSetupPtr mFft; + PFFFTSetup mFft; alignas(16) std::array<float,sFftLength*sNumSegments> mFilterData; - SegmentedFilter() + SegmentedFilter() : mFft{sFftLength, PFFFT_REAL} { - mFft = PFFFTSetupPtr{pffft_new_setup(sFftLength, PFFFT_REAL)}; - using complex_d = std::complex<double>; constexpr size_t fft_size{N}; constexpr size_t half_size{fft_size / 2}; @@ -71,8 +65,7 @@ struct SegmentedFilter { /* To set up the filter, we need to generate the desired response. * Start with a pure delay that passes all frequencies through. */ - auto fftBuffer = std::make_unique<complex_d[]>(fft_size); - std::fill_n(fftBuffer.get(), fft_size, complex_d{}); + auto fftBuffer = std::vector<complex_d>(fft_size, complex_d{}); fftBuffer[half_size] = 1.0; /* Convert to the frequency domain, shift the phase of each bin by +90 @@ -82,27 +75,27 @@ struct SegmentedFilter { * To maintain that and their phase (0 or pi), they're heavily * attenuated instead of shifted like the others. */ - forward_fft(al::span{fftBuffer.get(), fft_size}); + forward_fft(al::span{fftBuffer}); fftBuffer[0] *= std::numeric_limits<double>::epsilon(); for(size_t i{1};i < half_size;++i) fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; fftBuffer[half_size] *= std::numeric_limits<double>::epsilon(); for(size_t i{half_size+1};i < fft_size;++i) fftBuffer[i] = std::conj(fftBuffer[fft_size - i]); - inverse_fft(al::span{fftBuffer.get(), fft_size}); + inverse_fft(al::span{fftBuffer}); /* The segments of the filter are converted back to the frequency * domain, each on their own (0 stuffed). */ - auto fftBuffer2 = std::make_unique<complex_d[]>(sFftLength); + auto fftBuffer2 = std::vector<complex_d>(sFftLength); auto fftTmp = al::vector<float,16>(sFftLength); float *filter{mFilterData.data()}; for(size_t s{0};s < sNumSegments;++s) { for(size_t i{0};i < sSampleLength;++i) fftBuffer2[i] = fftBuffer[sSampleLength*s + i].real() / double{fft_size}; - std::fill_n(fftBuffer2.get()+sSampleLength, sSampleLength, complex_d{}); - forward_fft(al::span{fftBuffer2.get(), sFftLength}); + std::fill_n(fftBuffer2.data()+sSampleLength, sSampleLength, complex_d{}); + forward_fft(al::span{fftBuffer2}); /* Convert to zdomain data for PFFFT, scaled by the FFT length so * the iFFT result will be normalized. @@ -113,7 +106,7 @@ struct SegmentedFilter { fftTmp[i*2 + 1] = static_cast<float>((i == 0) ? fftBuffer2[sSampleLength].real() : fftBuffer2[i].imag()) / float{sFftLength}; } - pffft_zreorder(mFft.get(), fftTmp.data(), filter, PFFFT_BACKWARD); + mFft.zreorder(fftTmp.data(), filter, PFFFT_BACKWARD); filter += sFftLength; } } @@ -246,7 +239,7 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut, std::copy_n(mWXInOut.begin(), sSegmentSize, input); std::fill_n(input+sSegmentSize, sSegmentSize, 0.0f); - pffft_transform(Filter.mFft.get(), input, input, mWorkData.data(), PFFFT_FORWARD); + Filter.mFft.transform(input, input, mWorkData.data(), PFFFT_FORWARD); /* Convolve each input segment with its IR filter counterpart (aligned * in time, from newest to oldest). @@ -255,14 +248,14 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut, const float *filter{Filter.mFilterData.data()}; for(size_t s{curseg};s < sNumSegments;++s) { - pffft_zconvolve_accumulate(Filter.mFft.get(), input, filter, mFftBuffer.data()); + Filter.mFft.zconvolve_accumulate(input, filter, mFftBuffer.data()); input += sFftLength; filter += sFftLength; } input = mWXHistory.data(); for(size_t s{0};s < curseg;++s) { - pffft_zconvolve_accumulate(Filter.mFft.get(), input, filter, mFftBuffer.data()); + Filter.mFft.zconvolve_accumulate(input, filter, mFftBuffer.data()); input += sFftLength; filter += sFftLength; } @@ -270,8 +263,8 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut, /* Convert back to samples, writing to the output and storing the extra * for next time. */ - pffft_transform(Filter.mFft.get(), mFftBuffer.data(), mFftBuffer.data(), - mWorkData.data(), PFFFT_BACKWARD); + Filter.mFft.transform(mFftBuffer.data(), mFftBuffer.data(), mWorkData.data(), + PFFFT_BACKWARD); for(size_t i{0};i < sSegmentSize;++i) mWXInOut[i] = mFftBuffer[i] + mWXInOut[sSegmentSize+i]; diff --git a/core/uhjfilter.h b/core/uhjfilter.h index 348dc7e1..74ff2167 100644 --- a/core/uhjfilter.h +++ b/core/uhjfilter.h @@ -25,7 +25,7 @@ extern UhjQualityType UhjEncodeQuality; struct UhjAllPassFilter { struct AllPassState { /* Last two delayed components for direct form II. */ - float z[2]; + std::array<float,2> z{}; }; std::array<AllPassState,4> mState; @@ -110,8 +110,6 @@ struct UhjEncoderIIR final : public UhjEncoderBase { */ void encode(float *LeftOut, float *RightOut, const al::span<const float*const,3> InSamples, const size_t SamplesToDo) override; - - DEF_NEWDEL(UhjEncoderIIR) }; @@ -158,8 +156,6 @@ struct UhjDecoder final : public DecoderBase { */ void decode(const al::span<float*> samples, const size_t samplesToDo, const bool updateState) override; - - DEF_NEWDEL(UhjDecoder) }; struct UhjDecoderIIR final : public DecoderBase { @@ -184,8 +180,6 @@ struct UhjDecoderIIR final : public DecoderBase { void decode(const al::span<float*> samples, const size_t samplesToDo, const bool updateState) override; - - DEF_NEWDEL(UhjDecoderIIR) }; template<size_t N> @@ -210,8 +204,6 @@ struct UhjStereoDecoder final : public DecoderBase { */ void decode(const al::span<float*> samples, const size_t samplesToDo, const bool updateState) override; - - DEF_NEWDEL(UhjStereoDecoder) }; struct UhjStereoDecoderIIR final : public DecoderBase { @@ -231,8 +223,6 @@ struct UhjStereoDecoderIIR final : public DecoderBase { void decode(const al::span<float*> samples, const size_t samplesToDo, const bool updateState) override; - - DEF_NEWDEL(UhjStereoDecoderIIR) }; #endif /* CORE_UHJFILTER_H */ diff --git a/core/voice.cpp b/core/voice.cpp index 3889c42d..4a30ee83 100644 --- a/core/voice.cpp +++ b/core/voice.cpp @@ -9,11 +9,11 @@ #include <cassert> #include <climits> #include <cstdint> +#include <cstdlib> #include <iterator> #include <memory> #include <new> #include <optional> -#include <stdlib.h> #include <utility> #include <vector> @@ -133,18 +133,18 @@ void Voice::InitMixer(std::optional<std::string> resampler) if(resampler) { struct ResamplerEntry { - const char name[16]; + const char *name; const Resampler resampler; }; - constexpr ResamplerEntry ResamplerList[]{ - { "none", Resampler::Point }, - { "point", Resampler::Point }, - { "linear", Resampler::Linear }, - { "cubic", Resampler::Cubic }, - { "bsinc12", Resampler::BSinc12 }, - { "fast_bsinc12", Resampler::FastBSinc12 }, - { "bsinc24", Resampler::BSinc24 }, - { "fast_bsinc24", Resampler::FastBSinc24 }, + constexpr std::array ResamplerList{ + ResamplerEntry{"none", Resampler::Point}, + ResamplerEntry{"point", Resampler::Point}, + ResamplerEntry{"linear", Resampler::Linear}, + ResamplerEntry{"cubic", Resampler::Cubic}, + ResamplerEntry{"bsinc12", Resampler::BSinc12}, + ResamplerEntry{"fast_bsinc12", Resampler::FastBSinc12}, + ResamplerEntry{"bsinc24", Resampler::BSinc24}, + ResamplerEntry{"fast_bsinc24", Resampler::FastBSinc24}, }; const char *str{resampler->c_str()}; @@ -159,10 +159,10 @@ void Voice::InitMixer(std::optional<std::string> resampler) str = "cubic"; } - auto iter = std::find_if(std::begin(ResamplerList), std::end(ResamplerList), + auto iter = std::find_if(ResamplerList.begin(), ResamplerList.end(), [str](const ResamplerEntry &entry) -> bool { return al::strcasecmp(str, entry.name) == 0; }); - if(iter == std::end(ResamplerList)) + if(iter == ResamplerList.end()) ERR("Invalid resampler: %s\n", str); else ResamplerDefault = iter->resampler; @@ -178,7 +178,7 @@ void Voice::InitMixer(std::optional<std::string> resampler) namespace { /* IMA ADPCM Stepsize table */ -constexpr int IMAStep_size[89] = { +constexpr std::array<int,89> IMAStep_size{{ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, @@ -188,35 +188,35 @@ constexpr int IMAStep_size[89] = { 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,10442, 11487,12635,13899,15289,16818,18500,20350,22358,24633,27086,29794, 32767 -}; +}}; /* IMA4 ADPCM Codeword decode table */ -constexpr int IMA4Codeword[16] = { +constexpr std::array<int,16> IMA4Codeword{{ 1, 3, 5, 7, 9, 11, 13, 15, -1,-3,-5,-7,-9,-11,-13,-15, -}; +}}; /* IMA4 ADPCM Step index adjust decode table */ -constexpr int IMA4Index_adjust[16] = { +constexpr std::array<int,16>IMA4Index_adjust{{ -1,-1,-1,-1, 2, 4, 6, 8, -1,-1,-1,-1, 2, 4, 6, 8 -}; +}}; /* MSADPCM Adaption table */ -constexpr int MSADPCMAdaption[16] = { +constexpr std::array<int,16> MSADPCMAdaption{{ 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 -}; +}}; /* MSADPCM Adaption Coefficient tables */ -constexpr int MSADPCMAdaptionCoeff[7][2] = { - { 256, 0 }, - { 512, -256 }, - { 0, 0 }, - { 192, 64 }, - { 240, 0 }, - { 460, -208 }, - { 392, -232 } +constexpr std::array MSADPCMAdaptionCoeff{ + std::array{256, 0}, + std::array{512, -256}, + std::array{ 0, 0}, + std::array{192, 64}, + std::array{240, 0}, + std::array{460, -208}, + std::array{392, -232} }; @@ -307,7 +307,7 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const std::byte *sr auto decode_sample = [&sample,&index](const uint nibble) { - sample += IMA4Codeword[nibble] * IMAStep_size[index] / 8; + sample += IMA4Codeword[nibble] * IMAStep_size[static_cast<uint>(index)] / 8; sample = clampi(sample, -32768, 32767); index += IMA4Index_adjust[nibble]; @@ -382,7 +382,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const std::byte int delta{int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1]) << 8)}; input += srcStep*2; - int sampleHistory[2]{}; + std::array<int,2> sampleHistory{}; sampleHistory[0] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8); input += srcStep*2; sampleHistory[1] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8); @@ -421,7 +421,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const std::byte sampleHistory[1] = sampleHistory[0]; sampleHistory[0] = pred; - delta = (MSADPCMAdaption[nibble] * delta) / 256; + delta = (MSADPCMAdaption[static_cast<uint>(nibble)] * delta) / 256; delta = maxi(16, delta); return pred; @@ -630,8 +630,8 @@ void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &par parms.Hrtf.Target.Coeffs, parms.Hrtf.Target.Delay, 0.0f, gain / static_cast<float>(fademix)}; - MixHrtfBlendSamples(HrtfSamples, AccumSamples+OutPos, IrSize, &parms.Hrtf.Old, &hrtfparams, - fademix); + MixHrtfBlendSamples(HrtfSamples.data(), AccumSamples.data()+OutPos, IrSize, + &parms.Hrtf.Old, &hrtfparams, fademix); /* Update the old parameters with the result. */ parms.Hrtf.Old = parms.Hrtf.Target; @@ -658,7 +658,8 @@ void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &par parms.Hrtf.Target.Delay, parms.Hrtf.Old.Gain, (gain - parms.Hrtf.Old.Gain) / static_cast<float>(todo)}; - MixHrtfSamples(HrtfSamples+fademix, AccumSamples+OutPos, IrSize, &hrtfparams, todo); + MixHrtfSamples(HrtfSamples.data()+fademix, AccumSamples.data()+OutPos, IrSize, &hrtfparams, + todo); /* Store the now-current gain for next time. */ parms.Hrtf.Old.Gain = gain; @@ -669,8 +670,8 @@ void DoNfcMix(const al::span<const float> samples, FloatBufferLine *OutBuffer, D const float *TargetGains, const uint Counter, const uint OutPos, DeviceBase *Device) { using FilterProc = void (NfcFilter::*)(const al::span<const float>, float*); - static constexpr FilterProc NfcProcess[MaxAmbiOrder+1]{ - nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3}; + static constexpr std::array<FilterProc,MaxAmbiOrder+1> NfcProcess{{ + nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3}}; float *CurrentGains{parms.Gains.Current.data()}; MixSamples(samples, {OutBuffer, 1u}, CurrentGains, TargetGains, Counter, OutPos); @@ -678,7 +679,7 @@ void DoNfcMix(const al::span<const float> samples, FloatBufferLine *OutBuffer, D ++CurrentGains; ++TargetGains; - const al::span<float> nfcsamples{Device->NfcSampleData, samples.size()}; + const al::span<float> nfcsamples{Device->NfcSampleData.data(), samples.size()}; size_t order{1}; while(const size_t chancount{Device->NumChannelsPerOrder[order]}) { @@ -697,7 +698,7 @@ void DoNfcMix(const al::span<const float> samples, FloatBufferLine *OutBuffer, D void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds deviceTime, const uint SamplesToDo) { - static constexpr std::array<float,MAX_OUTPUT_CHANNELS> SilentTarget{}; + static constexpr std::array<float,MaxOutputChannels> SilentTarget{}; ASSUME(SamplesToDo > 0); @@ -903,8 +904,8 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi const size_t needBlocks{(needSamples + mSamplesPerBlock-1) / mSamplesPerBlock}; if(!mFlags.test(VoiceCallbackStopped) && needBlocks > mNumCallbackBlocks) { - const size_t byteOffset{mNumCallbackBlocks*mBytesPerBlock}; - const size_t needBytes{(needBlocks-mNumCallbackBlocks)*mBytesPerBlock}; + const size_t byteOffset{mNumCallbackBlocks*size_t{mBytesPerBlock}}; + const size_t needBytes{(needBlocks-mNumCallbackBlocks)*size_t{mBytesPerBlock}}; const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData, &BufferListItem->mSamples[byteOffset], static_cast<int>(needBytes))}; @@ -918,7 +919,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi else mNumCallbackBlocks = static_cast<uint>(needBlocks); } - const size_t numSamples{uint{mNumCallbackBlocks} * mSamplesPerBlock}; + const size_t numSamples{size_t{mNumCallbackBlocks} * mSamplesPerBlock}; LoadBufferCallback(BufferListItem, bufferOffset, numSamples, mFmtType, chan, mFrameStep, srcSampleDelay, srcBufferSize, al::to_address(resampleBuffer)); } @@ -953,7 +954,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi fracPos += dstBufferSize*increment; const uint srcOffset{fracPos >> MixerFracBits}; fracPos &= MixerFracMask; - intPos += srcOffset; + intPos += static_cast<int>(srcOffset); /* If more samples need to be loaded, copy the back of the * resampleBuffer to the front to reuse it. prevSamples isn't @@ -1017,7 +1018,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi if(mFlags.test(VoiceHasHrtf)) { - const float TargetGain{parms.Hrtf.Target.Gain * (vstate == Playing)}; + const float TargetGain{parms.Hrtf.Target.Gain * float(vstate == Playing)}; DoHrtfMix(samples, samplesToMix, parms, TargetGain, Counter, OutPos, (vstate == Playing), Device); } @@ -1063,8 +1064,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi /* Update voice positions and buffers as needed. */ DataPosFrac += increment*samplesToMix; - const uint SrcSamplesDone{DataPosFrac>>MixerFracBits}; - DataPosInt += SrcSamplesDone; + DataPosInt += static_cast<int>(DataPosFrac>>MixerFracBits); DataPosFrac &= MixerFracMask; uint buffers_done{0u}; @@ -1099,8 +1099,8 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi const uint blocksDone{currentBlock - mCallbackBlockBase}; if(blocksDone < mNumCallbackBlocks) { - const size_t byteOffset{blocksDone*mBytesPerBlock}; - const size_t byteEnd{mNumCallbackBlocks*mBytesPerBlock}; + const size_t byteOffset{blocksDone*size_t{mBytesPerBlock}}; + const size_t byteEnd{mNumCallbackBlocks*size_t{mBytesPerBlock}}; std::byte *data{BufferListItem->mSamples}; std::copy(data+byteOffset, data+byteEnd, data); mNumCallbackBlocks -= blocksDone; @@ -1120,7 +1120,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi if(BufferListItem->mSampleLen > static_cast<uint>(DataPosInt)) break; - DataPosInt -= BufferListItem->mSampleLen; + DataPosInt -= static_cast<int>(BufferListItem->mSampleLen); ++buffers_done; BufferListItem = BufferListItem->mNext.load(std::memory_order_relaxed); diff --git a/core/voice.h b/core/voice.h index a599eda8..f212ebe5 100644 --- a/core/voice.h +++ b/core/voice.h @@ -32,7 +32,7 @@ enum class DistanceModel : unsigned char; using uint = unsigned int; -#define MAX_SENDS 6 +inline constexpr size_t MaxSendCount{6}; enum class SpatializeMode : unsigned char { @@ -48,7 +48,7 @@ enum class DirectMode : unsigned char { }; -constexpr uint MaxPitch{10}; +inline constexpr uint MaxPitch{10}; enum { @@ -66,14 +66,14 @@ struct DirectParams { NfcFilter NFCtrlFilter; struct { - HrtfFilter Old; - HrtfFilter Target; - alignas(16) std::array<float,HrtfHistoryLength> History; + HrtfFilter Old{}; + HrtfFilter Target{}; + alignas(16) std::array<float,HrtfHistoryLength> History{}; } Hrtf; struct { - std::array<float,MAX_OUTPUT_CHANNELS> Current; - std::array<float,MAX_OUTPUT_CHANNELS> Target; + std::array<float,MaxOutputChannels> Current{}; + std::array<float,MaxOutputChannels> Target{}; } Gains; }; @@ -82,8 +82,8 @@ struct SendParams { BiquadFilter HighPass; struct { - std::array<float,MaxAmbiChannels> Current; - std::array<float,MaxAmbiChannels> Target; + std::array<float,MaxAmbiChannels> Current{}; + std::array<float,MaxAmbiChannels> Target{}; } Gains; }; @@ -154,13 +154,12 @@ struct VoiceProps { float HFReference; float GainLF; float LFReference; - } Send[MAX_SENDS]; + }; + std::array<SendData,MaxSendCount> Send; }; struct VoicePropsItem : public VoiceProps { std::atomic<VoicePropsItem*> next{nullptr}; - - DEF_NEWDEL(VoicePropsItem) }; enum : uint { @@ -185,7 +184,7 @@ struct Voice { std::atomic<VoicePropsItem*> mUpdate{nullptr}; - VoiceProps mProps; + VoiceProps mProps{}; std::atomic<uint> mSourceID{0u}; std::atomic<State> mPlayState{Stopped}; @@ -195,30 +194,30 @@ struct Voice { * Source offset in samples, relative to the currently playing buffer, NOT * the whole queue. */ - std::atomic<int> mPosition; + std::atomic<int> mPosition{}; /** Fractional (fixed-point) offset to the next sample. */ - std::atomic<uint> mPositionFrac; + std::atomic<uint> mPositionFrac{}; /* Current buffer queue item being played. */ - std::atomic<VoiceBufferItem*> mCurrentBuffer; + std::atomic<VoiceBufferItem*> mCurrentBuffer{}; /* Buffer queue item to loop to at end of queue (will be NULL for non- * looping voices). */ - std::atomic<VoiceBufferItem*> mLoopBuffer; + std::atomic<VoiceBufferItem*> mLoopBuffer{}; std::chrono::nanoseconds mStartTime{}; /* Properties for the attached buffer(s). */ - FmtChannels mFmtChannels; - FmtType mFmtType; - uint mFrequency; - uint mFrameStep; /**< In steps of the sample type size. */ - uint mBytesPerBlock; /**< Or for PCM formats, BytesPerFrame. */ - uint mSamplesPerBlock; /**< Always 1 for PCM formats. */ - AmbiLayout mAmbiLayout; - AmbiScaling mAmbiScaling; - uint mAmbiOrder; + FmtChannels mFmtChannels{}; + FmtType mFmtType{}; + uint mFrequency{}; + uint mFrameStep{}; /**< In steps of the sample type size. */ + uint mBytesPerBlock{}; /**< Or for PCM formats, BytesPerFrame. */ + uint mSamplesPerBlock{}; /**< Always 1 for PCM formats. */ + AmbiLayout mAmbiLayout{}; + AmbiScaling mAmbiScaling{}; + uint mAmbiOrder{}; std::unique_ptr<DecoderBase> mDecoder; uint mDecoderPadding{}; @@ -226,20 +225,20 @@ struct Voice { /** Current target parameters used for mixing. */ uint mStep{0}; - ResamplerFunc mResampler; + ResamplerFunc mResampler{}; - InterpState mResampleState; + InterpState mResampleState{}; std::bitset<VoiceFlagCount> mFlags{}; uint mNumCallbackBlocks{0}; uint mCallbackBlockBase{0}; struct TargetData { - int FilterType; + int FilterType{}; al::span<FloatBufferLine> Buffer; }; TargetData mDirect; - std::array<TargetData,MAX_SENDS> mSend; + std::array<TargetData,MaxSendCount> mSend; /* The first MaxResamplerPadding/2 elements are the sample history from the * previous mix, with an additional MaxResamplerPadding/2 elements that are @@ -250,11 +249,11 @@ struct Voice { al::vector<HistoryLine,16> mPrevSamples{2}; struct ChannelData { - float mAmbiHFScale, mAmbiLFScale; + float mAmbiHFScale{}, mAmbiLFScale{}; BandSplitter mAmbiSplitter; DirectParams mDryParams; - std::array<SendParams,MAX_SENDS> mWetParams; + std::array<SendParams,MaxSendCount> mWetParams; }; al::vector<ChannelData> mChans{2}; @@ -270,8 +269,6 @@ struct Voice { void prepare(DeviceBase *device); static void InitMixer(std::optional<std::string> resampler); - - DEF_NEWDEL(Voice) }; extern Resampler ResamplerDefault; diff --git a/core/voice_change.h b/core/voice_change.h index ddc6186f..e97c48f3 100644 --- a/core/voice_change.h +++ b/core/voice_change.h @@ -3,8 +3,6 @@ #include <atomic> -#include "almalloc.h" - struct Voice; using uint = unsigned int; @@ -24,8 +22,6 @@ struct VoiceChange { VChangeState mState{}; std::atomic<VoiceChange*> mNext{nullptr}; - - DEF_NEWDEL(VoiceChange) }; #endif /* VOICE_CHANGE_H */ diff --git a/examples/alconvolve.c b/examples/alconvolve.c index 8580d443..597d6ea2 100644 --- a/examples/alconvolve.c +++ b/examples/alconvolve.c @@ -38,6 +38,8 @@ #include "common/alhelpers.h" +#include "win_main_utf8.h" + #ifndef AL_SOFT_convolution_effect #define AL_SOFT_convolution_effect @@ -88,11 +90,11 @@ static LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; /* This stuff defines a simple streaming player object, the same as alstream.c. * Comments are removed for brevity, see alstream.c for more details. */ -#define NUM_BUFFERS 4 -#define BUFFER_SAMPLES 8192 +enum { NumBuffers = 4 }; +enum { BufferSamples = 8192 }; typedef struct StreamPlayer { - ALuint buffers[NUM_BUFFERS]; + ALuint buffers[NumBuffers]; ALuint source; SNDFILE *sndfile; @@ -109,7 +111,7 @@ static StreamPlayer *NewPlayer(void) player = calloc(1, sizeof(*player)); assert(player != NULL); - alGenBuffers(NUM_BUFFERS, player->buffers); + alGenBuffers(NumBuffers, player->buffers); assert(alGetError() == AL_NO_ERROR && "Could not create buffers"); alGenSources(1, &player->source); @@ -138,11 +140,11 @@ static void DeletePlayer(StreamPlayer *player) ClosePlayerFile(player); alDeleteSources(1, &player->source); - alDeleteBuffers(NUM_BUFFERS, player->buffers); + alDeleteBuffers(NumBuffers, player->buffers); if(alGetError() != AL_NO_ERROR) fprintf(stderr, "Failed to delete object IDs\n"); - memset(player, 0, sizeof(*player)); + memset(player, 0, sizeof(*player)); /* NOLINT(clang-analyzer-security.insecureAPI.*) */ free(player); } @@ -184,7 +186,7 @@ static int OpenPlayerFile(StreamPlayer *player, const char *filename) return 0; } - frame_size = (size_t)(BUFFER_SAMPLES * player->sfinfo.channels) * sizeof(float); + frame_size = (size_t)(BufferSamples * player->sfinfo.channels) * sizeof(float); player->membuf = malloc(frame_size); return 1; @@ -197,9 +199,9 @@ static int StartPlayer(StreamPlayer *player) alSourceRewind(player->source); alSourcei(player->source, AL_BUFFER, 0); - for(i = 0;i < NUM_BUFFERS;i++) + for(i = 0;i < NumBuffers;i++) { - sf_count_t slen = sf_readf_float(player->sndfile, player->membuf, BUFFER_SAMPLES); + sf_count_t slen = sf_readf_float(player->sndfile, player->membuf, BufferSamples); if(slen < 1) break; slen *= player->sfinfo.channels * (sf_count_t)sizeof(float); @@ -243,7 +245,7 @@ static int UpdatePlayer(StreamPlayer *player) alSourceUnqueueBuffers(player->source, 1, &bufid); processed--; - slen = sf_readf_float(player->sndfile, player->membuf, BUFFER_SAMPLES); + slen = sf_readf_float(player->sndfile, player->membuf, BufferSamples); if(slen > 0) { slen *= player->sfinfo.channels * (sf_count_t)sizeof(float); @@ -359,10 +361,10 @@ static ALuint LoadSound(const char *filename) } namepart = strrchr(filename, '/'); - if(namepart || (namepart=strrchr(filename, '\\'))) - namepart++; - else - namepart = filename; + if(!namepart) namepart = strrchr(filename, '\\'); + if(!namepart) namepart = filename; + else namepart++; + printf("Loading: %s (%s, %dhz, %" PRId64 " samples / %.2f seconds)\n", namepart, FormatName(format), sfinfo.samplerate, sfinfo.frames, (double)sfinfo.frames / sfinfo.samplerate); @@ -555,10 +557,9 @@ int main(int argc, char **argv) continue; namepart = strrchr(argv[i], '/'); - if(namepart || (namepart=strrchr(argv[i], '\\'))) - namepart++; - else - namepart = argv[i]; + if(!namepart) namepart = strrchr(argv[i], '\\'); + if(!namepart) namepart = argv[i]; + else namepart++; printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->format), player->sfinfo.samplerate); diff --git a/examples/alffplay.cpp b/examples/alffplay.cpp index 1f02ef70..cfbe5564 100644 --- a/examples/alffplay.cpp +++ b/examples/alffplay.cpp @@ -285,7 +285,7 @@ struct AudioState { AVStream *mStream{nullptr}; AVCodecCtxPtr mCodecCtx; - DataQueue<2*1024*1024> mQueue; + DataQueue<size_t{2}*1024*1024> mQueue; /* Used for clock difference average computation */ seconds_d64 mClockDiffAvg{0}; @@ -310,8 +310,9 @@ struct AudioState { int mSamplesPos{0}; int mSamplesMax{0}; - std::unique_ptr<uint8_t[]> mBufferData; - size_t mBufferDataSize{0}; + std::vector<uint8_t> mBufferData_; + //std::unique_ptr<uint8_t[]> mBufferData; + //size_t mBufferDataSize{0}; std::atomic<size_t> mReadPos{0}; std::atomic<size_t> mWritePos{0}; @@ -321,7 +322,7 @@ struct AudioState { std::mutex mSrcMutex; std::condition_variable mSrcCond; - std::atomic_flag mConnected; + std::atomic_flag mConnected{}; ALuint mSource{0}; std::array<ALuint,AudioBufferCount> mBuffers{}; ALuint mBufferIdx{0}; @@ -371,7 +372,7 @@ struct VideoState { AVStream *mStream{nullptr}; AVCodecCtxPtr mCodecCtx; - DataQueue<14*1024*1024> mQueue; + DataQueue<size_t{14}*1024*1024> mQueue; /* The pts of the currently displayed frame, and the time (av_gettime) it * was last updated - used to have running video pts @@ -474,8 +475,8 @@ nanoseconds AudioState::getClockNoLock() // Get the current device clock time and latency. auto device = alcGetContextsDevice(alcGetCurrentContext()); - ALCint64SOFT devtimes[2]{0,0}; - alcGetInteger64vSOFT(device, ALC_DEVICE_CLOCK_LATENCY_SOFT, 2, devtimes); + std::array<ALCint64SOFT,2> devtimes{}; + alcGetInteger64vSOFT(device, ALC_DEVICE_CLOCK_LATENCY_SOFT, 2, devtimes.data()); auto latency = nanoseconds{devtimes[1]}; auto device_time = nanoseconds{devtimes[0]}; @@ -485,7 +486,7 @@ nanoseconds AudioState::getClockNoLock() return device_time - mDeviceStartTime - latency; } - if(mBufferDataSize > 0) + if(!mBufferData_.empty()) { if(mDeviceStartTime == nanoseconds::min()) return nanoseconds::zero(); @@ -494,15 +495,14 @@ nanoseconds AudioState::getClockNoLock() * actually the timestamp of the first sample frame played. The audio * clock, then, is that plus the current source offset. */ - ALint64SOFT offset[2]; + std::array<ALint64SOFT,2> offset{}; if(alGetSourcei64vSOFT) - alGetSourcei64vSOFT(mSource, AL_SAMPLE_OFFSET_LATENCY_SOFT, offset); + alGetSourcei64vSOFT(mSource, AL_SAMPLE_OFFSET_LATENCY_SOFT, offset.data()); else { ALint ioffset; alGetSourcei(mSource, AL_SAMPLE_OFFSET, &ioffset); offset[0] = ALint64SOFT{ioffset} << 32; - offset[1] = 0; } /* NOTE: The source state must be checked last, in case an underrun * occurs and the source stops between getting the state and retrieving @@ -523,7 +523,7 @@ nanoseconds AudioState::getClockNoLock() */ const size_t woffset{mWritePos.load(std::memory_order_acquire)}; const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; - const size_t readable{((woffset >= roffset) ? woffset : (mBufferDataSize+woffset)) - + const size_t readable{((woffset>=roffset) ? woffset : (mBufferData_.size()+woffset)) - roffset}; pts = mCurrentPts - nanoseconds{seconds{readable/mFrameSize}}/mCodecCtx->sample_rate; @@ -550,15 +550,14 @@ nanoseconds AudioState::getClockNoLock() nanoseconds pts{mCurrentPts}; if(mSource) { - ALint64SOFT offset[2]; + std::array<ALint64SOFT,2> offset{}; if(alGetSourcei64vSOFT) - alGetSourcei64vSOFT(mSource, AL_SAMPLE_OFFSET_LATENCY_SOFT, offset); + alGetSourcei64vSOFT(mSource, AL_SAMPLE_OFFSET_LATENCY_SOFT, offset.data()); else { ALint ioffset; alGetSourcei(mSource, AL_SAMPLE_OFFSET, &ioffset); offset[0] = ALint64SOFT{ioffset} << 32; - offset[1] = 0; } ALint queued, status; alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); @@ -586,10 +585,10 @@ bool AudioState::startPlayback() { const size_t woffset{mWritePos.load(std::memory_order_acquire)}; const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; - const size_t readable{((woffset >= roffset) ? woffset : (mBufferDataSize+woffset)) - + const size_t readable{((woffset >= roffset) ? woffset : (mBufferData_.size()+woffset)) - roffset}; - if(mBufferDataSize > 0) + if(!mBufferData_.empty()) { if(readable == 0) return false; @@ -610,8 +609,8 @@ bool AudioState::startPlayback() /* Subtract the total buffer queue time from the current pts to get the * pts of the start of the queue. */ - int64_t srctimes[2]{0,0}; - alGetSourcei64vSOFT(mSource, AL_SAMPLE_OFFSET_CLOCK_SOFT, srctimes); + std::array<int64_t,2> srctimes{}; + alGetSourcei64vSOFT(mSource, AL_SAMPLE_OFFSET_CLOCK_SOFT, srctimes.data()); auto device_time = nanoseconds{srctimes[1]}; auto src_offset = duration_cast<nanoseconds>(fixed32{srctimes[0]}) / mCodecCtx->sample_rate; @@ -622,7 +621,7 @@ bool AudioState::startPlayback() * the device time the stream would have started at to reach where it * is now. */ - if(mBufferDataSize > 0) + if(!mBufferData_.empty()) { nanoseconds startpts{mCurrentPts - nanoseconds{seconds{readable/mFrameSize}}/mCodecCtx->sample_rate}; @@ -739,8 +738,8 @@ bool AudioState::readAudio(uint8_t *samples, unsigned int length, int &sample_sk { const auto len = static_cast<unsigned int>(mSamplesLen - mSamplesPos); if(rem > len) rem = len; - std::copy_n(mSamples + static_cast<unsigned int>(mSamplesPos)*mFrameSize, - rem*mFrameSize, samples); + std::copy_n(mSamples + static_cast<unsigned int>(mSamplesPos)*size_t{mFrameSize}, + rem*size_t{mFrameSize}, samples); } else { @@ -750,9 +749,9 @@ bool AudioState::readAudio(uint8_t *samples, unsigned int length, int &sample_sk sample_dup(samples, mSamples, rem, mFrameSize); } - mSamplesPos += rem; + mSamplesPos += static_cast<int>(rem); mCurrentPts += nanoseconds{seconds{rem}} / mCodecCtx->sample_rate; - samples += rem*mFrameSize; + samples += rem*size_t{mFrameSize}; audio_size += rem; while(mSamplesPos >= mSamplesLen) @@ -791,17 +790,17 @@ bool AudioState::readAudio(int sample_skip) while(mSamplesLen > 0) { const size_t nsamples{((roffset > woffset) ? roffset-woffset-1 - : (roffset == 0) ? (mBufferDataSize-woffset-1) - : (mBufferDataSize-woffset)) / mFrameSize}; + : (roffset == 0) ? (mBufferData_.size()-woffset-1) + : (mBufferData_.size()-woffset)) / mFrameSize}; if(!nsamples) break; if(mSamplesPos < 0) { const size_t rem{std::min<size_t>(nsamples, static_cast<ALuint>(-mSamplesPos))}; - sample_dup(&mBufferData[woffset], mSamples, rem, mFrameSize); + sample_dup(&mBufferData_[woffset], mSamples, rem, mFrameSize); woffset += rem * mFrameSize; - if(woffset == mBufferDataSize) woffset = 0; + if(woffset == mBufferData_.size()) woffset = 0; mWritePos.store(woffset, std::memory_order_release); mCurrentPts += nanoseconds{seconds{rem}} / mCodecCtx->sample_rate; @@ -813,9 +812,9 @@ bool AudioState::readAudio(int sample_skip) const size_t boffset{static_cast<ALuint>(mSamplesPos) * size_t{mFrameSize}}; const size_t nbytes{rem * mFrameSize}; - memcpy(&mBufferData[woffset], mSamples + boffset, nbytes); + memcpy(&mBufferData_[woffset], mSamples + boffset, nbytes); woffset += nbytes; - if(woffset == mBufferDataSize) woffset = 0; + if(woffset == mBufferData_.size()) woffset = 0; mWritePos.store(woffset, std::memory_order_release); mCurrentPts += nanoseconds{seconds{rem}} / mCodecCtx->sample_rate; @@ -888,15 +887,15 @@ ALsizei AudioState::bufferCallback(void *data, ALsizei size) noexcept const size_t woffset{mWritePos.load(std::memory_order_relaxed)}; if(woffset == roffset) break; - size_t todo{((woffset < roffset) ? mBufferDataSize : woffset) - roffset}; + size_t todo{((woffset < roffset) ? mBufferData_.size() : woffset) - roffset}; todo = std::min<size_t>(todo, static_cast<ALuint>(size-got)); - memcpy(data, &mBufferData[roffset], todo); + memcpy(data, &mBufferData_[roffset], todo); data = static_cast<ALbyte*>(data) + todo; got += static_cast<ALsizei>(todo); roffset += todo; - if(roffset == mBufferDataSize) + if(roffset == mBufferData_.size()) roffset = 0; } mReadPos.store(roffset, std::memory_order_release); @@ -936,7 +935,7 @@ int AudioState::handler() }; EventControlManager event_controller{sleep_time}; - std::unique_ptr<uint8_t[]> samples; + std::vector<uint8_t> samples; ALsizei buffer_len{0}; /* Find a suitable format for OpenAL. */ @@ -1151,9 +1150,9 @@ int AudioState::handler() mSwresCtx.reset(ps); if(err != 0) { - char errstr[AV_ERROR_MAX_STRING_SIZE]{}; + std::array<char,AV_ERROR_MAX_STRING_SIZE> errstr{}; std::cerr<< "Failed to allocate SwrContext: " - <<av_make_error_string(errstr, AV_ERROR_MAX_STRING_SIZE, err) <<std::endl; + <<av_make_error_string(errstr.data(), AV_ERROR_MAX_STRING_SIZE, err) <<std::endl; return 0; } @@ -1166,7 +1165,7 @@ int AudioState::handler() * ordering and normalization, so a custom matrix is needed to * scale and reorder the source from AmbiX. */ - std::vector<double> mtx(64*64, 0.0); + std::vector<double> mtx(size_t{64}*64, 0.0); mtx[0 + 0*64] = std::sqrt(0.5); mtx[3 + 1*64] = 1.0; mtx[1 + 2*64] = 1.0; @@ -1185,17 +1184,17 @@ int AudioState::handler() mSwresCtx.reset(ps); if(err != 0) { - char errstr[AV_ERROR_MAX_STRING_SIZE]{}; + std::array<char,AV_ERROR_MAX_STRING_SIZE> errstr{}; std::cerr<< "Failed to allocate SwrContext: " - <<av_make_error_string(errstr, AV_ERROR_MAX_STRING_SIZE, err) <<std::endl; + <<av_make_error_string(errstr.data(), AV_ERROR_MAX_STRING_SIZE, err) <<std::endl; return 0; } } if(int err{swr_init(mSwresCtx.get())}) { - char errstr[AV_ERROR_MAX_STRING_SIZE]{}; + std::array<char,AV_ERROR_MAX_STRING_SIZE> errstr{}; std::cerr<< "Failed to initialize audio converter: " - <<av_make_error_string(errstr, AV_ERROR_MAX_STRING_SIZE, err) <<std::endl; + <<av_make_error_string(errstr.data(), AV_ERROR_MAX_STRING_SIZE, err) <<std::endl; return 0; } @@ -1206,8 +1205,8 @@ int AudioState::handler() alSourcei(mSource, AL_DIRECT_CHANNELS_SOFT, DirectOutMode); if(EnableWideStereo) { - const float angles[2]{static_cast<float>(M_PI / 3.0), static_cast<float>(-M_PI / 3.0)}; - alSourcefv(mSource, AL_STEREO_ANGLES, angles); + const std::array angles{static_cast<float>(M_PI / 3.0), static_cast<float>(-M_PI / 3.0)}; + alSourcefv(mSource, AL_STEREO_ANGLES, angles.data()); } if(has_bfmt_ex) { @@ -1237,13 +1236,12 @@ int AudioState::handler() } else { - mBufferDataSize = static_cast<size_t>(duration_cast<seconds>(mCodecCtx->sample_rate * - AudioBufferTotalTime).count()) * mFrameSize; - mBufferData = std::make_unique<uint8_t[]>(mBufferDataSize); - std::fill_n(mBufferData.get(), mBufferDataSize, uint8_t{}); + mBufferData_.resize(static_cast<size_t>(duration_cast<seconds>(mCodecCtx->sample_rate * + AudioBufferTotalTime).count()) * mFrameSize); + std::fill(mBufferData_.begin(), mBufferData_.end(), uint8_t{}); mReadPos.store(0, std::memory_order_relaxed); - mWritePos.store(mBufferDataSize/mFrameSize/2*mFrameSize, std::memory_order_relaxed); + mWritePos.store(mBufferData_.size()/mFrameSize/2*mFrameSize, std::memory_order_relaxed); ALCint refresh{}; alcGetIntegerv(alcGetContextsDevice(alcGetCurrentContext()), ALC_REFRESH, 1, &refresh); @@ -1255,12 +1253,12 @@ int AudioState::handler() buffer_len = static_cast<int>(duration_cast<seconds>(mCodecCtx->sample_rate * AudioBufferTime).count() * mFrameSize); if(buffer_len > 0) - samples = std::make_unique<uint8_t[]>(static_cast<ALuint>(buffer_len)); + samples.resize(static_cast<ALuint>(buffer_len)); /* Prefill the codec buffer. */ auto packet_sender = [this]() { - while(1) + while(true) { const int ret{mQueue.sendPacket(mCodecCtx.get())}; if(ret == AVErrorEOF) break; @@ -1287,7 +1285,7 @@ int AudioState::handler() mCurrentPts += skip; } - while(1) + while(true) { if(mMovie.mQuit.load(std::memory_order_relaxed)) { @@ -1299,11 +1297,11 @@ int AudioState::handler() mSamplesLen = decodeFrame(); mSamplesPos = mSamplesLen; } while(mSamplesLen > 0); - goto finish; + break; } ALenum state; - if(mBufferDataSize > 0) + if(!mBufferData_.empty()) { alGetSourcei(mSource, AL_SOURCE_STATE, &state); @@ -1333,13 +1331,13 @@ int AudioState::handler() /* Read the next chunk of data, filling the buffer, and queue * it on the source. */ - if(!readAudio(samples.get(), static_cast<ALuint>(buffer_len), sync_skip)) + if(!readAudio(samples.data(), static_cast<ALuint>(buffer_len), sync_skip)) break; const ALuint bufid{mBuffers[mBufferIdx]}; mBufferIdx = static_cast<ALuint>((mBufferIdx+1) % mBuffers.size()); - alBufferData(bufid, mFormat, samples.get(), buffer_len, mCodecCtx->sample_rate); + alBufferData(bufid, mFormat, samples.data(), buffer_len, mCodecCtx->sample_rate); alSourceQueueBuffers(mSource, 1, &bufid); ++queued; } @@ -1380,7 +1378,6 @@ int AudioState::handler() mSrcCond.wait_for(srclock, sleep_time); } -finish: alSourceRewind(mSource); alSourcei(mSource, AL_BUFFER, 0); @@ -1451,7 +1448,7 @@ void VideoState::updateVideo(SDL_Window *screen, SDL_Renderer *renderer, bool re auto clocktime = mMovie.getMasterClock(); bool updated{false}; - while(1) + while(true) { size_t next_idx{(read_idx+1)%mPictQ.size()}; if(next_idx == mPictQWrite.load(std::memory_order_acquire)) @@ -1511,9 +1508,9 @@ void VideoState::updateVideo(SDL_Window *screen, SDL_Renderer *renderer, bool re { double aspect_ratio = av_q2d(frame->sample_aspect_ratio); if(aspect_ratio >= 1.0) - frame_width = static_cast<int>(frame_width*aspect_ratio + 0.5); + frame_width = static_cast<int>(std::lround(frame_width * aspect_ratio)); else if(aspect_ratio > 0.0) - frame_height = static_cast<int>(frame_height/aspect_ratio + 0.5); + frame_height = static_cast<int>(std::lround(frame_height / aspect_ratio)); } SDL_SetWindowSize(screen, frame_width, frame_height); } @@ -1546,18 +1543,15 @@ void VideoState::updateVideo(SDL_Window *screen, SDL_Renderer *renderer, bool re } /* point pict at the queue */ - uint8_t *pict_data[3]; + std::array<uint8_t*,3> pict_data; pict_data[0] = static_cast<uint8_t*>(pixels); - pict_data[1] = pict_data[0] + w*h; - pict_data[2] = pict_data[1] + w*h/4; + pict_data[1] = pict_data[0] + ptrdiff_t{w}*h; + pict_data[2] = pict_data[1] + ptrdiff_t{w}*h/4; - int pict_linesize[3]; - pict_linesize[0] = pitch; - pict_linesize[1] = pitch / 2; - pict_linesize[2] = pitch / 2; + std::array pict_linesize{pitch, pitch/2, pitch/2}; - sws_scale(mSwscaleCtx.get(), reinterpret_cast<uint8_t**>(frame->data), frame->linesize, - 0, h, pict_data, pict_linesize); + sws_scale(mSwscaleCtx.get(), reinterpret_cast<uint8_t**>(frame->data), + frame->linesize, 0, h, pict_data.data(), pict_linesize.data()); SDL_UnlockTexture(mImage); } @@ -1599,7 +1593,7 @@ int VideoState::handler() /* Prefill the codec buffer. */ auto packet_sender = [this]() { - while(1) + while(true) { const int ret{mQueue.sendPacket(mCodecCtx.get())}; if(ret == AVErrorEOF) break; @@ -1613,7 +1607,7 @@ int VideoState::handler() } auto current_pts = nanoseconds::zero(); - while(1) + while(true) { size_t write_idx{mPictQWrite.load(std::memory_order_relaxed)}; Picture *vp{&mPictQ[write_idx]}; @@ -2062,7 +2056,7 @@ int main(int argc, char *argv[]) while(fileidx < argc && !movState) { - movState = std::unique_ptr<MovieState>{new MovieState{argv[fileidx++]}}; + movState = std::make_unique<MovieState>(argv[fileidx++]); if(!movState->prepare()) movState = nullptr; } if(!movState) @@ -2077,7 +2071,7 @@ int main(int argc, char *argv[]) Next, Quit } eom_action{EomAction::Next}; seconds last_time{seconds::min()}; - while(1) + while(true) { /* SDL_WaitEventTimeout is broken, just force a 10ms sleep. */ std::this_thread::sleep_for(milliseconds{10}); @@ -2145,7 +2139,7 @@ int main(int argc, char *argv[]) movState = nullptr; while(fileidx < argc && !movState) { - movState = std::unique_ptr<MovieState>{new MovieState{argv[fileidx++]}}; + movState = std::make_unique<MovieState>(argv[fileidx++]); if(!movState->prepare()) movState = nullptr; } if(movState) diff --git a/examples/alhrtf.c b/examples/alhrtf.c index 7ea1b99e..a2f1824a 100644 --- a/examples/alhrtf.c +++ b/examples/alhrtf.c @@ -40,6 +40,8 @@ #include "common/alhelpers.h" +#include "win_main_utf8.h" + #ifndef M_PI #define M_PI (3.14159265358979323846) diff --git a/examples/allatency.c b/examples/allatency.c index 01f4eb69..9a535442 100644 --- a/examples/allatency.c +++ b/examples/allatency.c @@ -37,6 +37,8 @@ #include "common/alhelpers.h" +#include "win_main_utf8.h" + static LPALSOURCEDSOFT alSourcedSOFT; static LPALSOURCE3DSOFT alSource3dSOFT; diff --git a/examples/almultireverb.c b/examples/almultireverb.c index dcb76c87..f9a46c4f 100644 --- a/examples/almultireverb.c +++ b/examples/almultireverb.c @@ -47,6 +47,8 @@ #include "common/alhelpers.h" +#include "win_main_utf8.h" + #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -106,7 +108,8 @@ static int LoadEffect(ALuint effect, const EFXEAXREVERBPROPERTIES *reverb) * the needed panning vectors). */ alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); - if((err=alGetError()) != AL_NO_ERROR) + err = alGetError(); + if(err != AL_NO_ERROR) { fprintf(stderr, "Failed to set EAX Reverb: %s (0x%04x)\n", alGetString(err), err); return 0; @@ -138,7 +141,8 @@ static int LoadEffect(ALuint effect, const EFXEAXREVERBPROPERTIES *reverb) alEffecti(effect, AL_EAXREVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit); /* Check if an error occurred, and return failure if so. */ - if((err=alGetError()) != AL_NO_ERROR) + err = alGetError(); + if(err != AL_NO_ERROR) { fprintf(stderr, "Error setting up reverb: %s\n", alGetString(err)); return 0; diff --git a/examples/alplay.c b/examples/alplay.c index 1eabcccd..9af7ca40 100644 --- a/examples/alplay.c +++ b/examples/alplay.c @@ -37,6 +37,8 @@ #include "common/alhelpers.h" +#include "win_main_utf8.h" + enum FormatType { Int16, diff --git a/examples/alreverb.c b/examples/alreverb.c index ff49db25..15b92235 100644 --- a/examples/alreverb.c +++ b/examples/alreverb.c @@ -40,6 +40,8 @@ #include "common/alhelpers.h" +#include "win_main_utf8.h" + /* Effect object functions */ static LPALGENEFFECTS alGenEffects; diff --git a/examples/alstream.c b/examples/alstream.c index a61680d2..028290f5 100644 --- a/examples/alstream.c +++ b/examples/alstream.c @@ -37,13 +37,15 @@ #include "common/alhelpers.h" +#include "win_main_utf8.h" + /* Define the number of buffers and buffer size (in milliseconds) to use. 4 * buffers at 200ms each gives a nice per-chunk size, and lets the queue last * for almost one second. */ -#define NUM_BUFFERS 4 -#define BUFFER_MILLISEC 200 +enum { NumBuffers = 4 }; +enum { BufferMillisec = 200 }; typedef enum SampleType { Int16, Float, IMA4, MSADPCM @@ -51,7 +53,7 @@ typedef enum SampleType { typedef struct StreamPlayer { /* These are the buffers and source to play out through OpenAL with. */ - ALuint buffers[NUM_BUFFERS]; + ALuint buffers[NumBuffers]; ALuint source; /* Handle for the audio file */ @@ -88,7 +90,7 @@ static StreamPlayer *NewPlayer(void) assert(player != NULL); /* Generate the buffers and source */ - alGenBuffers(NUM_BUFFERS, player->buffers); + alGenBuffers(NumBuffers, player->buffers); assert(alGetError() == AL_NO_ERROR && "Could not create buffers"); alGenSources(1, &player->source); @@ -111,11 +113,11 @@ static void DeletePlayer(StreamPlayer *player) ClosePlayerFile(player); alDeleteSources(1, &player->source); - alDeleteBuffers(NUM_BUFFERS, player->buffers); + alDeleteBuffers(NumBuffers, player->buffers); if(alGetError() != AL_NO_ERROR) fprintf(stderr, "Failed to delete object IDs\n"); - memset(player, 0, sizeof(*player)); + memset(player, 0, sizeof(*player)); /* NOLINT(clang-analyzer-security.insecureAPI.*) */ free(player); } @@ -291,8 +293,8 @@ static int OpenPlayerFile(StreamPlayer *player, const char *filename) } player->block_count = player->sfinfo.samplerate / player->sampleblockalign; - player->block_count = player->block_count * BUFFER_MILLISEC / 1000; - player->membuf = malloc((size_t)(player->block_count * player->byteblockalign)); + player->block_count = player->block_count * BufferMillisec / 1000; + player->membuf = malloc((size_t)player->block_count * (size_t)player->byteblockalign); return 1; } @@ -310,7 +312,7 @@ static void ClosePlayerFile(StreamPlayer *player) if(player->sampleblockalign > 1) { ALsizei i; - for(i = 0;i < NUM_BUFFERS;i++) + for(i = 0;i < NumBuffers;i++) alBufferi(player->buffers[i], AL_UNPACK_BLOCK_ALIGNMENT_SOFT, 0); player->sampleblockalign = 0; player->byteblockalign = 0; @@ -328,7 +330,7 @@ static int StartPlayer(StreamPlayer *player) alSourcei(player->source, AL_BUFFER, 0); /* Fill the buffer queue */ - for(i = 0;i < NUM_BUFFERS;i++) + for(i = 0;i < NumBuffers;i++) { sf_count_t slen; @@ -336,21 +338,21 @@ static int StartPlayer(StreamPlayer *player) if(player->sample_type == Int16) { slen = sf_readf_short(player->sndfile, player->membuf, - player->block_count * player->sampleblockalign); + (sf_count_t)player->block_count * player->sampleblockalign); if(slen < 1) break; slen *= player->byteblockalign; } else if(player->sample_type == Float) { slen = sf_readf_float(player->sndfile, player->membuf, - player->block_count * player->sampleblockalign); + (sf_count_t)player->block_count * player->sampleblockalign); if(slen < 1) break; slen *= player->byteblockalign; } else { slen = sf_read_raw(player->sndfile, player->membuf, - player->block_count * player->byteblockalign); + (sf_count_t)player->block_count * player->byteblockalign); if(slen > 0) slen -= slen%player->byteblockalign; if(slen < 1) break; } @@ -407,19 +409,19 @@ static int UpdatePlayer(StreamPlayer *player) if(player->sample_type == Int16) { slen = sf_readf_short(player->sndfile, player->membuf, - player->block_count * player->sampleblockalign); + (sf_count_t)player->block_count * player->sampleblockalign); if(slen > 0) slen *= player->byteblockalign; } else if(player->sample_type == Float) { slen = sf_readf_float(player->sndfile, player->membuf, - player->block_count * player->sampleblockalign); + (sf_count_t)player->block_count * player->sampleblockalign); if(slen > 0) slen *= player->byteblockalign; } else { slen = sf_read_raw(player->sndfile, player->membuf, - player->block_count * player->byteblockalign); + (sf_count_t)player->block_count * player->byteblockalign); if(slen > 0) slen -= slen%player->byteblockalign; } @@ -486,10 +488,9 @@ int main(int argc, char **argv) /* Get the name portion, without the path, for display. */ namepart = strrchr(argv[i], '/'); - if(namepart || (namepart=strrchr(argv[i], '\\'))) - namepart++; - else - namepart = argv[i]; + if(!namepart) namepart = strrchr(argv[i], '\\'); + if(!namepart) namepart = argv[i]; + else namepart++; printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->format), player->sfinfo.samplerate); diff --git a/examples/alstreamcb.cpp b/examples/alstreamcb.cpp index 6cc24034..ebf3d3b0 100644 --- a/examples/alstreamcb.cpp +++ b/examples/alstreamcb.cpp @@ -24,12 +24,12 @@ /* This file contains a streaming audio player using a callback buffer. */ -#include <string.h> -#include <stdlib.h> -#include <stdio.h> #include <atomic> #include <chrono> +#include <cstring> +#include <cstdlib> +#include <cstdio> #include <memory> #include <stdexcept> #include <string> @@ -44,6 +44,8 @@ #include "common/alhelpers.h" +#include "win_main_utf8.h" + namespace { @@ -56,8 +58,7 @@ struct StreamPlayer { /* A lockless ring-buffer (supports single-provider, single-consumer * operation). */ - std::unique_ptr<ALbyte[]> mBufferData; - size_t mBufferDataSize{0}; + std::vector<ALbyte> mBufferData; std::atomic<size_t> mReadPos{0}; std::atomic<size_t> mWritePos{0}; size_t mSamplesPerBlock{1}; @@ -78,7 +79,7 @@ struct StreamPlayer { size_t mDecoderOffset{0}; /* The format of the callback samples. */ - ALenum mFormat; + ALenum mFormat{}; StreamPlayer() { @@ -166,8 +167,8 @@ struct StreamPlayer { mSampleFormat = SampleType::Int16; else { - auto fmtbuf = std::make_unique<ALubyte[]>(inf.datalen); - inf.data = fmtbuf.get(); + auto fmtbuf = std::vector<ALubyte>(inf.datalen); + inf.data = fmtbuf.data(); if(sf_get_chunk_data(iter, &inf) != SF_ERR_NO_ERROR) mSampleFormat = SampleType::Int16; else @@ -194,12 +195,12 @@ struct StreamPlayer { if(mSampleFormat == SampleType::Int16) { mSamplesPerBlock = 1; - mBytesPerBlock = static_cast<size_t>(mSfInfo.channels * 2); + mBytesPerBlock = static_cast<size_t>(mSfInfo.channels) * 2; } else if(mSampleFormat == SampleType::Float) { mSamplesPerBlock = 1; - mBytesPerBlock = static_cast<size_t>(mSfInfo.channels * 4); + mBytesPerBlock = static_cast<size_t>(mSfInfo.channels) * 4; } else { @@ -232,7 +233,7 @@ struct StreamPlayer { } else if(mSfInfo.channels == 3) { - if(sf_command(mSndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT) + if(sf_command(mSndfile, SFC_WAVEX_GET_AMBISONIC, nullptr, 0) == SF_AMBISONIC_B_FORMAT) { if(mSampleFormat == SampleType::Int16) mFormat = AL_FORMAT_BFORMAT2D_16; @@ -242,7 +243,7 @@ struct StreamPlayer { } else if(mSfInfo.channels == 4) { - if(sf_command(mSndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT) + if(sf_command(mSndfile, SFC_WAVEX_GET_AMBISONIC, nullptr, 0) == SF_AMBISONIC_B_FORMAT) { if(mSampleFormat == SampleType::Int16) mFormat = AL_FORMAT_BFORMAT3D_16; @@ -262,8 +263,7 @@ struct StreamPlayer { /* Set a 1s ring buffer size. */ size_t numblocks{(static_cast<ALuint>(mSfInfo.samplerate) + mSamplesPerBlock-1) / mSamplesPerBlock}; - mBufferDataSize = static_cast<ALuint>(numblocks * mBytesPerBlock); - mBufferData.reset(new ALbyte[mBufferDataSize]); + mBufferData.resize(static_cast<ALuint>(numblocks * mBytesPerBlock)); mReadPos.store(0, std::memory_order_relaxed); mWritePos.store(0, std::memory_order_relaxed); mDecoderOffset = 0; @@ -303,7 +303,7 @@ struct StreamPlayer { * that case, otherwise read up to the write offset. Also limit the * amount to copy given how much is remaining to write. */ - size_t todo{((woffset < roffset) ? mBufferDataSize : woffset) - roffset}; + size_t todo{((woffset < roffset) ? mBufferData.size() : woffset) - roffset}; todo = std::min<size_t>(todo, static_cast<ALuint>(size-got)); /* Copy from the ring buffer to the provided output buffer. Wrap @@ -315,7 +315,7 @@ struct StreamPlayer { got += static_cast<ALsizei>(todo); roffset += todo; - if(roffset == mBufferDataSize) + if(roffset == mBufferData.size()) roffset = 0; } /* Finally, store the updated read offset, and return how many bytes @@ -351,7 +351,7 @@ struct StreamPlayer { if(state != AL_INITIAL) { const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; - const size_t readable{((woffset >= roffset) ? woffset : (mBufferDataSize+woffset)) - + const size_t readable{((woffset >= roffset) ? woffset : (mBufferData.size()+woffset)) - roffset}; /* For a stopped (underrun) source, the current playback offset is * the current decoder offset excluding the readable buffered data. @@ -362,7 +362,7 @@ struct StreamPlayer { ? (mDecoderOffset-readable) / mBytesPerBlock * mSamplesPerBlock : (static_cast<ALuint>(pos) + mStartOffset/mBytesPerBlock*mSamplesPerBlock)) / static_cast<ALuint>(mSfInfo.samplerate)}; - printf("\r%3zus (%3zu%% full)", curtime, readable * 100 / mBufferDataSize); + printf("\r%3zus (%3zu%% full)", curtime, readable * 100 / mBufferData.size()); } else fputs("Starting...", stdout); @@ -415,8 +415,8 @@ struct StreamPlayer { * data can fit, and calculate how much can go in front before * wrapping. */ - const size_t writable{(!roffset ? mBufferDataSize-woffset-1 : - (mBufferDataSize-woffset)) / mBytesPerBlock}; + const size_t writable{(!roffset ? mBufferData.size()-woffset-1 : + (mBufferData.size()-woffset)) / mBytesPerBlock}; if(!writable) break; if(mSampleFormat == SampleType::Int16) @@ -444,7 +444,7 @@ struct StreamPlayer { } woffset += read_bytes; - if(woffset == mBufferDataSize) + if(woffset == mBufferData.size()) woffset = 0; } mWritePos.store(woffset, std::memory_order_release); @@ -459,7 +459,7 @@ struct StreamPlayer { * what's available. */ const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; - const size_t readable{((woffset >= roffset) ? woffset : (mBufferDataSize+woffset)) - + const size_t readable{((woffset >= roffset) ? woffset : (mBufferData.size()+woffset)) - roffset}; if(readable == 0) return false; @@ -522,7 +522,8 @@ int main(int argc, char **argv) /* Get the name portion, without the path, for display. */ const char *namepart{strrchr(argv[i], '/')}; - if(namepart || (namepart=strrchr(argv[i], '\\'))) + if(!namepart) namepart = strrchr(argv[i], '\\'); + if(namepart) ++namepart; else namepart = argv[i]; diff --git a/examples/altonegen.c b/examples/altonegen.c index ec7f77bb..2fab783d 100644 --- a/examples/altonegen.c +++ b/examples/altonegen.c @@ -321,7 +321,6 @@ int main(int argc, char *argv[]) if(pos > last_pos) { - last_pos = 0; printf("%d...\n", seconds - pos); fflush(stdout); } diff --git a/include/AL/al.h b/include/AL/al.h index 87274184..e9f8f3b1 100644 --- a/include/AL/al.h +++ b/include/AL/al.h @@ -1,6 +1,7 @@ #ifndef AL_AL_H #define AL_AL_H +/* NOLINTBEGIN */ #ifdef __cplusplus extern "C" { @@ -689,5 +690,6 @@ typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel) AL_ #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_AL_H */ diff --git a/include/AL/alc.h b/include/AL/alc.h index 73dcf08f..3311b57f 100644 --- a/include/AL/alc.h +++ b/include/AL/alc.h @@ -1,6 +1,7 @@ #ifndef AL_ALC_H #define AL_ALC_H +/* NOLINTBEGIN */ #ifdef __cplusplus extern "C" { @@ -289,5 +290,6 @@ typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, AL #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_ALC_H */ diff --git a/include/AL/alext.h b/include/AL/alext.h index c75e0770..3f373704 100644 --- a/include/AL/alext.h +++ b/include/AL/alext.h @@ -1,6 +1,7 @@ #ifndef AL_ALEXT_H #define AL_ALEXT_H +/* NOLINTBEGIN */ #include <stddef.h> /* Define int64 and uint64 types */ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ @@ -737,5 +738,6 @@ void ALC_APIENTRY alcEventCallbackSOFT(ALCEVENTPROCTYPESOFT callback, void *user #ifdef __cplusplus } #endif +/* NOLINTEND */ #endif diff --git a/include/AL/efx-presets.h b/include/AL/efx-presets.h index 8539fd51..acd5bf39 100644 --- a/include/AL/efx-presets.h +++ b/include/AL/efx-presets.h @@ -2,6 +2,7 @@ #ifndef EFX_PRESETS_H #define EFX_PRESETS_H +/* NOLINTBEGIN */ #ifndef EFXEAXREVERBPROPERTIES_DEFINED #define EFXEAXREVERBPROPERTIES_DEFINED @@ -399,4 +400,5 @@ typedef struct { #define EFX_REVERB_PRESET_SMALLWATERROOM \ { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } +/* NOLINTEND */ #endif /* EFX_PRESETS_H */ diff --git a/include/AL/efx.h b/include/AL/efx.h index f24222c3..1e93bf22 100644 --- a/include/AL/efx.h +++ b/include/AL/efx.h @@ -1,6 +1,7 @@ #ifndef AL_EFX_H #define AL_EFX_H +/* NOLINTBEGIN */ #include <float.h> #include "alc.h" @@ -758,5 +759,6 @@ AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum par #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_EFX_H */ diff --git a/router/al.cpp b/router/al.cpp index 6ed8a626..57c8d179 100644 --- a/router/al.cpp +++ b/router/al.cpp @@ -1,37 +1,42 @@ #include "config.h" -#include <stddef.h> +#include <cstddef> #include "AL/al.h" #include "router.h" -#define DECL_THUNK1(R,n,T1) AL_API R AL_APIENTRY n(T1 a) noexcept \ +#define DECL_THUNK1(R,n,T1) \ +AL_API auto AL_APIENTRY n(T1 a) noexcept -> R \ { \ DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a); \ } -#define DECL_THUNK2(R,n,T1,T2) AL_API R AL_APIENTRY n(T1 a, T2 b) noexcept \ +#define DECL_THUNK2(R,n,T1,T2) \ +AL_API auto AL_APIENTRY n(T1 a, T2 b) noexcept -> R \ { \ DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a, b); \ } -#define DECL_THUNK3(R,n,T1,T2,T3) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c) noexcept \ +#define DECL_THUNK3(R,n,T1,T2,T3) \ +AL_API auto AL_APIENTRY n(T1 a, T2 b, T3 c) noexcept -> R \ { \ DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a, b, c); \ } -#define DECL_THUNK4(R,n,T1,T2,T3,T4) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d) noexcept \ +#define DECL_THUNK4(R,n,T1,T2,T3,T4) \ +AL_API auto AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d) noexcept -> R \ { \ DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a, b, c, d); \ } -#define DECL_THUNK5(R,n,T1,T2,T3,T4,T5) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \ +#define DECL_THUNK5(R,n,T1,T2,T3,T4,T5) \ +AL_API auto AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept -> R \ { \ DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ @@ -42,7 +47,7 @@ /* Ugly hack for some apps calling alGetError without a current context, and * expecting it to be AL_NO_ERROR. */ -AL_API ALenum AL_APIENTRY alGetError(void) noexcept +AL_API auto AL_APIENTRY alGetError() noexcept -> ALenum { DriverIface *iface = GetThreadDriver(); if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); diff --git a/router/alc.cpp b/router/alc.cpp index fe9faa45..71deaf0b 100644 --- a/router/alc.cpp +++ b/router/alc.cpp @@ -6,9 +6,10 @@ #include <string.h> #include <stdio.h> -#include <mutex> #include <algorithm> #include <array> +#include <mutex> +#include <tuple> #include "AL/alc.h" #include "alstring.h" @@ -413,12 +414,12 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename) noexcep { std::lock_guard<std::recursive_mutex> _{EnumerationLock}; if(DevicesList.Names.empty()) - (void)alcGetString(nullptr, ALC_DEVICE_SPECIFIER); + std::ignore = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); idx = GetDriverIndexForName(&DevicesList, devicename); if(idx < 0) { if(AllDevicesList.Names.empty()) - (void)alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); + std::ignore = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); idx = GetDriverIndexForName(&AllDevicesList, devicename); } } @@ -578,7 +579,7 @@ ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept ContextIfaceMap.removeByKey(context); } -ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) noexcept +ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext() noexcept { DriverIface *iface = GetThreadDriver(); if(!iface) iface = CurrentCtxDriver.load(); @@ -884,7 +885,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, { std::lock_guard<std::recursive_mutex> _{EnumerationLock}; if(CaptureDevicesList.Names.empty()) - (void)alcGetString(nullptr, ALC_CAPTURE_DEVICE_SPECIFIER); + std::ignore = alcGetString(nullptr, ALC_CAPTURE_DEVICE_SPECIFIER); idx = GetDriverIndexForName(&CaptureDevicesList, devicename); } @@ -1009,7 +1010,7 @@ ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) noexcep return ALC_FALSE; } -ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) noexcept +ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext() noexcept { DriverIface *iface = GetThreadDriver(); if(iface) return iface->alcGetThreadContext(); diff --git a/router/router.cpp b/router/router.cpp index e2ba91d1..1fdf45ac 100644 --- a/router/router.cpp +++ b/router/router.cpp @@ -20,7 +20,7 @@ enum LogLevel LogLevel = LogLevel_Error; FILE *LogFile; -static void LoadDriverList(void); +static void LoadDriverList(); BOOL APIENTRY DllMain(HINSTANCE, DWORD reason, void*) @@ -313,7 +313,7 @@ static int GetLoadedModuleDirectory(const WCHAR *name, WCHAR *moddir, DWORD leng return 1; } -void LoadDriverList(void) +void LoadDriverList() { WCHAR dll_path[MAX_PATH+1] = L""; WCHAR cwd_path[MAX_PATH+1] = L""; @@ -361,7 +361,7 @@ void LoadDriverList(void) PtrIntMap::~PtrIntMap() { std::lock_guard<std::mutex> maplock{mLock}; - al_free(mKeys); + free(mKeys); mKeys = nullptr; mValues = nullptr; mSize = 0; @@ -382,8 +382,7 @@ ALenum PtrIntMap::insert(void *key, int value) ALsizei newcap{mCapacity ? (mCapacity<<1) : 4}; if(newcap > mCapacity) newkeys = static_cast<void**>( - al_calloc(16, (sizeof(mKeys[0])+sizeof(mValues[0]))*newcap) - ); + calloc(newcap, sizeof(mKeys[0])+sizeof(mValues[0]))); if(!newkeys) return AL_OUT_OF_MEMORY; auto newvalues = reinterpret_cast<int*>(&newkeys[newcap]); @@ -393,7 +392,7 @@ ALenum PtrIntMap::insert(void *key, int value) std::copy_n(mKeys, mSize, newkeys); std::copy_n(mValues, mSize, newvalues); } - al_free(mKeys); + free(mKeys); mKeys = newkeys; mValues = newvalues; mCapacity = newcap; diff --git a/utils/alsoft-config/CMakeLists.txt b/utils/alsoft-config/CMakeLists.txt index c6a46075..d9e6ca9a 100644 --- a/utils/alsoft-config/CMakeLists.txt +++ b/utils/alsoft-config/CMakeLists.txt @@ -12,7 +12,7 @@ if(Qt5Widgets_FOUND) verstr.cpp verstr.h ${UIS} ${RSCS} ${TRS} ${MOCS}) - target_link_libraries(alsoft-config Qt5::Widgets) + target_link_libraries(alsoft-config PUBLIC Qt5::Widgets PRIVATE alcommon) target_include_directories(alsoft-config PRIVATE "${alsoft-config_BINARY_DIR}" "${OpenAL_BINARY_DIR}") set_target_properties(alsoft-config PROPERTIES ${DEFAULT_TARGET_PROPS} diff --git a/utils/alsoft-config/mainwindow.cpp b/utils/alsoft-config/mainwindow.cpp index bee7022f..9a65f79c 100644 --- a/utils/alsoft-config/mainwindow.cpp +++ b/utils/alsoft-config/mainwindow.cpp @@ -3,8 +3,9 @@ #include "mainwindow.h" -#include <iostream> +#include <array> #include <cmath> +#include <iostream> #include <QFileDialog> #include <QMessageBox> @@ -19,137 +20,134 @@ #include <shlobj.h> #endif +#include "almalloc.h" +#include "alspan.h" + namespace { -const struct { +struct BackendNamePair { + /* NOLINTBEGIN(*-avoid-c-arrays) */ char backend_name[16]; char full_string[32]; -} backendList[] = { -#ifdef HAVE_JACK - { "jack", "JACK" }, -#endif + /* NOLINTEND(*-avoid-c-arrays) */ +}; +constexpr std::array backendList{ #ifdef HAVE_PIPEWIRE - { "pipewire", "PipeWire" }, + BackendNamePair{ "pipewire", "PipeWire" }, #endif #ifdef HAVE_PULSEAUDIO - { "pulse", "PulseAudio" }, + BackendNamePair{ "pulse", "PulseAudio" }, #endif #ifdef HAVE_ALSA - { "alsa", "ALSA" }, + BackendNamePair{ "alsa", "ALSA" }, +#endif +#ifdef HAVE_JACK + BackendNamePair{ "jack", "JACK" }, #endif #ifdef HAVE_COREAUDIO - { "core", "CoreAudio" }, + BackendNamePair{ "core", "CoreAudio" }, #endif #ifdef HAVE_OSS - { "oss", "OSS" }, + BackendNamePair{ "oss", "OSS" }, #endif #ifdef HAVE_SOLARIS - { "solaris", "Solaris" }, + BackendNamePair{ "solaris", "Solaris" }, #endif #ifdef HAVE_SNDIO - { "sndio", "SoundIO" }, -#endif -#ifdef HAVE_QSA - { "qsa", "QSA" }, + BackendNamePair{ "sndio", "SoundIO" }, #endif #ifdef HAVE_WASAPI - { "wasapi", "WASAPI" }, + BackendNamePair{ "wasapi", "WASAPI" }, #endif #ifdef HAVE_DSOUND - { "dsound", "DirectSound" }, + BackendNamePair{ "dsound", "DirectSound" }, #endif #ifdef HAVE_WINMM - { "winmm", "Windows Multimedia" }, + BackendNamePair{ "winmm", "Windows Multimedia" }, #endif #ifdef HAVE_PORTAUDIO - { "port", "PortAudio" }, + BackendNamePair{ "port", "PortAudio" }, #endif #ifdef HAVE_OPENSL - { "opensl", "OpenSL" }, + BackendNamePair{ "opensl", "OpenSL" }, #endif - { "null", "Null Output" }, + BackendNamePair{ "null", "Null Output" }, #ifdef HAVE_WAVE - { "wave", "Wave Writer" }, + BackendNamePair{ "wave", "Wave Writer" }, #endif - { "", "" } }; -const struct NameValuePair { +struct NameValuePair { + /* NOLINTBEGIN(*-avoid-c-arrays) */ const char name[64]; const char value[16]; -} speakerModeList[] = { - { "Autodetect", "" }, - { "Mono", "mono" }, - { "Stereo", "stereo" }, - { "Quadraphonic", "quad" }, - { "5.1 Surround", "surround51" }, - { "6.1 Surround", "surround61" }, - { "7.1 Surround", "surround71" }, - { "3D7.1 Surround", "surround3d71" }, - - { "Ambisonic, 1st Order", "ambi1" }, - { "Ambisonic, 2nd Order", "ambi2" }, - { "Ambisonic, 3rd Order", "ambi3" }, - - { "", "" } -}, sampleTypeList[] = { - { "Autodetect", "" }, - { "8-bit int", "int8" }, - { "8-bit uint", "uint8" }, - { "16-bit int", "int16" }, - { "16-bit uint", "uint16" }, - { "32-bit int", "int32" }, - { "32-bit uint", "uint32" }, - { "32-bit float", "float32" }, - - { "", "" } -}, resamplerList[] = { - { "Point", "point" }, - { "Linear", "linear" }, - { "Cubic Spline", "cubic" }, - { "Default (Cubic Spline)", "" }, - { "11th order Sinc (fast)", "fast_bsinc12" }, - { "11th order Sinc", "bsinc12" }, - { "23rd order Sinc (fast)", "fast_bsinc24" }, - { "23rd order Sinc", "bsinc24" }, - - { "", "" } -}, stereoModeList[] = { - { "Autodetect", "" }, - { "Speakers", "speakers" }, - { "Headphones", "headphones" }, - - { "", "" } -}, stereoEncList[] = { - { "Default", "" }, - { "Basic", "panpot" }, - { "UHJ", "uhj" }, - { "Binaural", "hrtf" }, - - { "", "" } -}, ambiFormatList[] = { - { "Default", "" }, - { "AmbiX (ACN, SN3D)", "ambix" }, - { "Furse-Malham", "fuma" }, - { "ACN, N3D", "acn+n3d" }, - { "ACN, FuMa", "acn+fuma" }, - - { "", "" } -}, hrtfModeList[] = { - { "1st Order Ambisonic", "ambi1" }, - { "2nd Order Ambisonic", "ambi2" }, - { "3rd Order Ambisonic", "ambi3" }, - { "Default (Full)", "" }, - { "Full", "full" }, - - { "", "" } + /* NOLINTEND(*-avoid-c-arrays) */ +}; +constexpr std::array speakerModeList{ + NameValuePair{ "Autodetect", "" }, + NameValuePair{ "Mono", "mono" }, + NameValuePair{ "Stereo", "stereo" }, + NameValuePair{ "Quadraphonic", "quad" }, + NameValuePair{ "5.1 Surround", "surround51" }, + NameValuePair{ "6.1 Surround", "surround61" }, + NameValuePair{ "7.1 Surround", "surround71" }, + NameValuePair{ "3D7.1 Surround", "surround3d71" }, + + NameValuePair{ "Ambisonic, 1st Order", "ambi1" }, + NameValuePair{ "Ambisonic, 2nd Order", "ambi2" }, + NameValuePair{ "Ambisonic, 3rd Order", "ambi3" }, +}; +constexpr std::array sampleTypeList{ + NameValuePair{ "Autodetect", "" }, + NameValuePair{ "8-bit int", "int8" }, + NameValuePair{ "8-bit uint", "uint8" }, + NameValuePair{ "16-bit int", "int16" }, + NameValuePair{ "16-bit uint", "uint16" }, + NameValuePair{ "32-bit int", "int32" }, + NameValuePair{ "32-bit uint", "uint32" }, + NameValuePair{ "32-bit float", "float32" }, +}; +constexpr std::array resamplerList{ + NameValuePair{ "Point", "point" }, + NameValuePair{ "Linear", "linear" }, + NameValuePair{ "Cubic Spline", "cubic" }, + NameValuePair{ "Default (Cubic Spline)", "" }, + NameValuePair{ "11th order Sinc (fast)", "fast_bsinc12" }, + NameValuePair{ "11th order Sinc", "bsinc12" }, + NameValuePair{ "23rd order Sinc (fast)", "fast_bsinc24" }, + NameValuePair{ "23rd order Sinc", "bsinc24" }, +}; +constexpr std::array stereoModeList{ + NameValuePair{ "Autodetect", "" }, + NameValuePair{ "Speakers", "speakers" }, + NameValuePair{ "Headphones", "headphones" }, +}; +constexpr std::array stereoEncList{ + NameValuePair{ "Default", "" }, + NameValuePair{ "Basic", "panpot" }, + NameValuePair{ "UHJ", "uhj" }, + NameValuePair{ "Binaural", "hrtf" }, +}; +constexpr std::array ambiFormatList{ + NameValuePair{ "Default", "" }, + NameValuePair{ "AmbiX (ACN, SN3D)", "ambix" }, + NameValuePair{ "Furse-Malham", "fuma" }, + NameValuePair{ "ACN, N3D", "acn+n3d" }, + NameValuePair{ "ACN, FuMa", "acn+fuma" }, +}; +constexpr std::array hrtfModeList{ + NameValuePair{ "1st Order Ambisonic", "ambi1" }, + NameValuePair{ "2nd Order Ambisonic", "ambi2" }, + NameValuePair{ "3rd Order Ambisonic", "ambi3" }, + NameValuePair{ "Default (Full)", "" }, + NameValuePair{ "Full", "full" }, }; QString getDefaultConfigName() { #ifdef Q_OS_WIN32 - static const char fname[] = "alsoft.ini"; + static constexpr char fname[] = "alsoft.ini"; /* NOLINT(*-avoid-c-arrays) */ auto get_appdata_path = []() noexcept -> QString { WCHAR buffer[MAX_PATH]; @@ -159,7 +157,7 @@ QString getDefaultConfigName() }; QString base = get_appdata_path(); #else - static const char fname[] = "alsoft.conf"; + static constexpr char fname[] = "alsoft.conf"; /* NOLINT(*-avoid-c-arrays) */ QByteArray base = qgetenv("XDG_CONFIG_HOME"); if(base.isEmpty()) { @@ -226,10 +224,9 @@ QStringList getAllDataPaths(const QString &append) return list; } -template<size_t N> -QString getValueFromName(const NameValuePair (&list)[N], const QString &str) +QString getValueFromName(const al::span<const NameValuePair> list, const QString &str) { - for(size_t i = 0;i < N-1;i++) + for(size_t i{0};i < list.size();++i) { if(str == list[i].name) return list[i].value; @@ -237,10 +234,9 @@ QString getValueFromName(const NameValuePair (&list)[N], const QString &str) return QString{}; } -template<size_t N> -QString getNameFromValue(const NameValuePair (&list)[N], const QString &str) +QString getNameFromValue(const al::span<const NameValuePair> list, const QString &str) { - for(size_t i = 0;i < N-1;i++) + for(size_t i{0};i < list.size();++i) { if(str == list[i].value) return list[i].name; @@ -270,44 +266,29 @@ QString getCheckValue(const QCheckBox *checkbox) } -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow), - mPeriodSizeValidator(nullptr), - mPeriodCountValidator(nullptr), - mSourceCountValidator(nullptr), - mEffectSlotValidator(nullptr), - mSourceSendValidator(nullptr), - mSampleRateValidator(nullptr), - mJackBufferValidator(nullptr), - mNeedsSave(false) +MainWindow::MainWindow(QWidget *parent) : QMainWindow{parent} + , ui{std::make_unique<Ui::MainWindow>()} { ui->setupUi(this); - for(int i = 0;speakerModeList[i].name[0];i++) - ui->channelConfigCombo->addItem(speakerModeList[i].name); + for(auto &item : speakerModeList) + ui->channelConfigCombo->addItem(item.name); ui->channelConfigCombo->adjustSize(); - for(int i = 0;sampleTypeList[i].name[0];i++) - ui->sampleFormatCombo->addItem(sampleTypeList[i].name); + for(auto &item : sampleTypeList) + ui->sampleFormatCombo->addItem(item.name); ui->sampleFormatCombo->adjustSize(); - for(int i = 0;stereoModeList[i].name[0];i++) - ui->stereoModeCombo->addItem(stereoModeList[i].name); + for(auto &item : stereoModeList) + ui->stereoModeCombo->addItem(item.name); ui->stereoModeCombo->adjustSize(); - for(int i = 0;stereoEncList[i].name[0];i++) - ui->stereoEncodingComboBox->addItem(stereoEncList[i].name); + for(auto &item : stereoEncList) + ui->stereoEncodingComboBox->addItem(item.name); ui->stereoEncodingComboBox->adjustSize(); - for(int i = 0;ambiFormatList[i].name[0];i++) - ui->ambiFormatComboBox->addItem(ambiFormatList[i].name); + for(auto &item : ambiFormatList) + ui->ambiFormatComboBox->addItem(item.name); ui->ambiFormatComboBox->adjustSize(); - int count; - for(count = 0;resamplerList[count].name[0];count++) { - } - ui->resamplerSlider->setRange(0, count-1); - - for(count = 0;hrtfModeList[count].name[0];count++) { - } - ui->hrtfmodeSlider->setRange(0, count-1); + ui->resamplerSlider->setRange(0, resamplerList.size()-1); + ui->hrtfmodeSlider->setRange(0, hrtfModeList.size()-1); #if !defined(HAVE_NEON) && !defined(HAVE_SSE) ui->cpuExtDisabledLabel->move(ui->cpuExtDisabledLabel->x(), ui->cpuExtDisabledLabel->y() - 60); @@ -355,22 +336,22 @@ MainWindow::MainWindow(QWidget *parent) : ui->enableEaxCheck->setVisible(false); #endif - mPeriodSizeValidator = new QIntValidator{64, 8192, this}; - ui->periodSizeEdit->setValidator(mPeriodSizeValidator); - mPeriodCountValidator = new QIntValidator{2, 16, this}; - ui->periodCountEdit->setValidator(mPeriodCountValidator); + mPeriodSizeValidator = std::make_unique<QIntValidator>(64, 8192, this); + ui->periodSizeEdit->setValidator(mPeriodSizeValidator.get()); + mPeriodCountValidator = std::make_unique<QIntValidator>(2, 16, this); + ui->periodCountEdit->setValidator(mPeriodCountValidator.get()); - mSourceCountValidator = new QIntValidator{0, 4096, this}; - ui->srcCountLineEdit->setValidator(mSourceCountValidator); - mEffectSlotValidator = new QIntValidator{0, 64, this}; - ui->effectSlotLineEdit->setValidator(mEffectSlotValidator); - mSourceSendValidator = new QIntValidator{0, 16, this}; - ui->srcSendLineEdit->setValidator(mSourceSendValidator); - mSampleRateValidator = new QIntValidator{8000, 192000, this}; - ui->sampleRateCombo->lineEdit()->setValidator(mSampleRateValidator); + mSourceCountValidator = std::make_unique<QIntValidator>(0, 4096, this); + ui->srcCountLineEdit->setValidator(mSourceCountValidator.get()); + mEffectSlotValidator = std::make_unique<QIntValidator>(0, 64, this); + ui->effectSlotLineEdit->setValidator(mEffectSlotValidator.get()); + mSourceSendValidator = std::make_unique<QIntValidator>(0, 16, this); + ui->srcSendLineEdit->setValidator(mSourceSendValidator.get()); + mSampleRateValidator = std::make_unique<QIntValidator>(8000, 192000, this); + ui->sampleRateCombo->lineEdit()->setValidator(mSampleRateValidator.get()); - mJackBufferValidator = new QIntValidator{0, 8192, this}; - ui->jackBufferSizeLine->setValidator(mJackBufferValidator); + mJackBufferValidator = std::make_unique<QIntValidator>(0, 8192, this); + ui->jackBufferSizeLine->setValidator(mJackBufferValidator.get()); connect(ui->actionLoad, &QAction::triggered, this, &MainWindow::loadConfigFromFile); connect(ui->actionSave_As, &QAction::triggered, this, &MainWindow::saveConfigAsFile); @@ -495,7 +476,7 @@ MainWindow::MainWindow(QWidget *parent) : for(int i = 1;i < ui->backendListWidget->count();i++) ui->backendListWidget->setRowHidden(i, true); - for(int i = 0;backendList[i].backend_name[0];i++) + for(size_t i{0};i < backendList.size();++i) { QList<QListWidgetItem*> items = ui->backendListWidget->findItems( backendList[i].full_string, Qt::MatchFixedString); @@ -506,17 +487,7 @@ MainWindow::MainWindow(QWidget *parent) : loadConfig(getDefaultConfigName()); } -MainWindow::~MainWindow() -{ - delete ui; - delete mPeriodSizeValidator; - delete mPeriodCountValidator; - delete mSourceCountValidator; - delete mEffectSlotValidator; - delete mSourceSendValidator; - delete mSampleRateValidator; - delete mJackBufferValidator; -} +MainWindow::~MainWindow() = default; void MainWindow::closeEvent(QCloseEvent *event) { @@ -583,7 +554,7 @@ QStringList MainWindow::collectHrtfs() break; } ++i; - } while(1); + } while(true); } } } @@ -618,7 +589,7 @@ QStringList MainWindow::collectHrtfs() break; } ++i; - } while(1); + } while(true); } } } @@ -789,11 +760,11 @@ void MainWindow::loadConfig(const QString &fname) /* The "basic" mode name is no longer supported. Use "ambi2" instead. */ if(hrtfmode == "basic") hrtfmode = "ambi2"; - for(int i = 0;hrtfModeList[i].name[0];i++) + for(size_t i{0};i < hrtfModeList.size();++i) { if(hrtfmode == hrtfModeList[i].value) { - ui->hrtfmodeSlider->setValue(i); + ui->hrtfmodeSlider->setValue(static_cast<int>(i)); ui->hrtfmodeLabel->setText(hrtfModeList[i].name); break; } @@ -869,7 +840,8 @@ void MainWindow::loadConfig(const QString &fname) if(lastWasEmpty) continue; if(!backend.startsWith(QChar('-'))) - for(int j = 0;backendList[j].backend_name[0];j++) + { + for(size_t j{0};j < backendList.size();++j) { if(backend == backendList[j].backend_name) { @@ -877,10 +849,11 @@ void MainWindow::loadConfig(const QString &fname) break; } } + } else if(backend.size() > 1) { QStringRef backendref{backend.rightRef(backend.size()-1)}; - for(int j = 0;backendList[j].backend_name[0];j++) + for(size_t j{0};j < backendList.size();++j) { if(backendref == backendList[j].backend_name) { @@ -1070,7 +1043,7 @@ void MainWindow::saveConfig(const QString &fname) const for(int i = 0;i < ui->enabledBackendList->count();i++) { QString label{ui->enabledBackendList->item(i)->text()}; - for(int j = 0;backendList[j].backend_name[0];j++) + for(size_t j{0};j < backendList.size();++j) { if(label == backendList[j].full_string) { @@ -1082,7 +1055,7 @@ void MainWindow::saveConfig(const QString &fname) const for(int i = 0;i < ui->disabledBackendList->count();i++) { QString label{ui->disabledBackendList->item(i)->text()}; - for(int j = 0;backendList[j].backend_name[0];j++) + for(size_t j{0};j < backendList.size();++j) { if(label == backendList[j].full_string) { @@ -1294,7 +1267,7 @@ void MainWindow::updateJackBufferSizeSlider() void MainWindow::updateHrtfModeLabel(int num) { - ui->hrtfmodeLabel->setText(hrtfModeList[num].name); + ui->hrtfmodeLabel->setText(hrtfModeList[static_cast<uint>(num)].name); enableApplyButton(); } @@ -1311,11 +1284,10 @@ void MainWindow::addHrtfFile() void MainWindow::removeHrtfFile() { - QList<QListWidgetItem*> selected{ui->hrtfFileList->selectedItems()}; + QList<gsl::owner<QListWidgetItem*>> selected{ui->hrtfFileList->selectedItems()}; if(!selected.isEmpty()) { - foreach(QListWidgetItem *item, selected) - delete item; + std::for_each(selected.begin(), selected.end(), std::default_delete<QListWidgetItem>{}); enableApplyButton(); } } @@ -1336,7 +1308,7 @@ void MainWindow::showEnabledBackendMenu(QPoint pt) if(ui->enabledBackendList->selectedItems().empty()) removeAction->setEnabled(false); ctxmenu.addSeparator(); - for(size_t i = 0;backendList[i].backend_name[0];i++) + for(size_t i{0};i < backendList.size();++i) { QString backend{backendList[i].full_string}; QAction *action{ctxmenu.addAction(QString("Add ")+backend)}; @@ -1349,9 +1321,8 @@ void MainWindow::showEnabledBackendMenu(QPoint pt) QAction *gotAction{ctxmenu.exec(pt)}; if(gotAction == removeAction) { - QList<QListWidgetItem*> selected{ui->enabledBackendList->selectedItems()}; - foreach(QListWidgetItem *item, selected) - delete item; + QList<gsl::owner<QListWidgetItem*>> selected{ui->enabledBackendList->selectedItems()}; + std::for_each(selected.begin(), selected.end(), std::default_delete<QListWidgetItem>{}); enableApplyButton(); } else if(gotAction != nullptr) @@ -1374,7 +1345,7 @@ void MainWindow::showDisabledBackendMenu(QPoint pt) if(ui->disabledBackendList->selectedItems().empty()) removeAction->setEnabled(false); ctxmenu.addSeparator(); - for(size_t i = 0;backendList[i].backend_name[0];i++) + for(size_t i{0};i < backendList.size();++i) { QString backend{backendList[i].full_string}; QAction *action{ctxmenu.addAction(QString("Add ")+backend)}; @@ -1387,9 +1358,8 @@ void MainWindow::showDisabledBackendMenu(QPoint pt) QAction *gotAction{ctxmenu.exec(pt)}; if(gotAction == removeAction) { - QList<QListWidgetItem*> selected{ui->disabledBackendList->selectedItems()}; - foreach(QListWidgetItem *item, selected) - delete item; + QList<gsl::owner<QListWidgetItem*>> selected{ui->disabledBackendList->selectedItems()}; + std::for_each(selected.begin(), selected.end(), std::default_delete<QListWidgetItem>{}); enableApplyButton(); } else if(gotAction != nullptr) diff --git a/utils/alsoft-config/mainwindow.h b/utils/alsoft-config/mainwindow.h index f7af8eac..96151ca2 100644 --- a/utils/alsoft-config/mainwindow.h +++ b/utils/alsoft-config/mainwindow.h @@ -1,6 +1,8 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H +#include <memory> + #include <QMainWindow> #include <QListWidget> @@ -8,13 +10,12 @@ namespace Ui { class MainWindow; } -class MainWindow : public QMainWindow -{ +class MainWindow : public QMainWindow { Q_OBJECT public: - explicit MainWindow(QWidget *parent = 0); - ~MainWindow(); + explicit MainWindow(QWidget *parent=nullptr); + ~MainWindow() override; private slots: void cancelCloseAction(); @@ -61,19 +62,19 @@ private slots: void selectWaveOutput(); private: - Ui::MainWindow *ui; + std::unique_ptr<QValidator> mPeriodSizeValidator; + std::unique_ptr<QValidator> mPeriodCountValidator; + std::unique_ptr<QValidator> mSourceCountValidator; + std::unique_ptr<QValidator> mEffectSlotValidator; + std::unique_ptr<QValidator> mSourceSendValidator; + std::unique_ptr<QValidator> mSampleRateValidator; + std::unique_ptr<QValidator> mJackBufferValidator; - QValidator *mPeriodSizeValidator; - QValidator *mPeriodCountValidator; - QValidator *mSourceCountValidator; - QValidator *mEffectSlotValidator; - QValidator *mSourceSendValidator; - QValidator *mSampleRateValidator; - QValidator *mJackBufferValidator; + std::unique_ptr<Ui::MainWindow> ui; - bool mNeedsSave; + bool mNeedsSave{}; - void closeEvent(QCloseEvent *event); + void closeEvent(QCloseEvent *event) override; void selectDecoderFile(QLineEdit *line, const char *name); diff --git a/utils/makemhr/loaddef.cpp b/utils/makemhr/loaddef.cpp index c8a98511..f01e93fc 100644 --- a/utils/makemhr/loaddef.cpp +++ b/utils/makemhr/loaddef.cpp @@ -34,9 +34,13 @@ #include <limits> #include <memory> #include <optional> +#include <string> +#include <string_view> #include <vector> +#include "albit.h" #include "alfstream.h" +#include "alnumeric.h" #include "alspan.h" #include "alstring.h" #include "makemhr.h" @@ -45,12 +49,12 @@ #include "mysofa.h" // Constants for accessing the token reader's ring buffer. -#define TR_RING_BITS (16) -#define TR_RING_SIZE (1 << TR_RING_BITS) -#define TR_RING_MASK (TR_RING_SIZE - 1) +constexpr uint TRRingBits{16}; +constexpr uint TRRingSize{1 << TRRingBits}; +constexpr uint TRRingMask{TRRingSize - 1}; // The token reader's load interval in bytes. -#define TR_LOAD_SIZE (TR_RING_SIZE >> 2) +constexpr uint TRLoadSize{TRRingSize >> 2}; // Token reader state for parsing the data set definition. struct TokenReaderT { @@ -58,7 +62,7 @@ struct TokenReaderT { const char *mName{}; uint mLine{}; uint mColumn{}; - char mRing[TR_RING_SIZE]{}; + std::array<char,TRRingSize> mRing{}; std::streamsize mIn{}; std::streamsize mOut{}; @@ -69,44 +73,48 @@ struct TokenReaderT { // The maximum identifier length used when processing the data set // definition. -#define MAX_IDENT_LEN (16) +constexpr uint MaxIdentLen{16}; // The limits for the listener's head 'radius' in the data set definition. -#define MIN_RADIUS (0.05) -#define MAX_RADIUS (0.15) +constexpr double MinRadius{0.05}; +constexpr double MaxRadius{0.15}; // The maximum number of channels that can be addressed for a WAVE file // source listed in the data set definition. -#define MAX_WAVE_CHANNELS (65535) +constexpr uint MaxWaveChannels{65535}; // The limits to the byte size for a binary source listed in the definition // file. -#define MIN_BIN_SIZE (2) -#define MAX_BIN_SIZE (4) - -// The minimum number of significant bits for binary sources listed in the -// data set definition. The maximum is calculated from the byte size. -#define MIN_BIN_BITS (16) +enum : uint { + MinBinSize = 2, + MaxBinSize = 4 +}; // The limits to the number of significant bits for an ASCII source listed in // the data set definition. -#define MIN_ASCII_BITS (16) -#define MAX_ASCII_BITS (32) +enum : uint { + MinASCIIBits = 16, + MaxASCIIBits = 32 +}; // The four-character-codes for RIFF/RIFX WAVE file chunks. -#define FOURCC_RIFF (0x46464952) // 'RIFF' -#define FOURCC_RIFX (0x58464952) // 'RIFX' -#define FOURCC_WAVE (0x45564157) // 'WAVE' -#define FOURCC_FMT (0x20746D66) // 'fmt ' -#define FOURCC_DATA (0x61746164) // 'data' -#define FOURCC_LIST (0x5453494C) // 'LIST' -#define FOURCC_WAVL (0x6C766177) // 'wavl' -#define FOURCC_SLNT (0x746E6C73) // 'slnt' +enum : uint { + FOURCC_RIFF = 0x46464952, // 'RIFF' + FOURCC_RIFX = 0x58464952, // 'RIFX' + FOURCC_WAVE = 0x45564157, // 'WAVE' + FOURCC_FMT = 0x20746D66, // 'fmt ' + FOURCC_DATA = 0x61746164, // 'data' + FOURCC_LIST = 0x5453494C, // 'LIST' + FOURCC_WAVL = 0x6C766177, // 'wavl' + FOURCC_SLNT = 0x746E6C73, // 'slnt' +}; // The supported wave formats. -#define WAVE_FORMAT_PCM (0x0001) -#define WAVE_FORMAT_IEEE_FLOAT (0x0003) -#define WAVE_FORMAT_EXTENSIBLE (0xFFFE) +enum : uint { + WAVE_FORMAT_PCM = 0x0001, + WAVE_FORMAT_IEEE_FLOAT = 0x0003, + WAVE_FORMAT_EXTENSIBLE = 0xFFFE, +}; enum ByteOrderT { @@ -144,7 +152,7 @@ struct SourceRefT { double mRadius; uint mSkip; uint mOffset; - char mPath[MAX_PATH_LEN+1]; + std::array<char,MAX_PATH_LEN+1> mPath; }; @@ -196,13 +204,14 @@ static int TrLoad(TokenReaderT *tr) { std::istream &istream = tr->mIStream; - std::streamsize toLoad{TR_RING_SIZE - static_cast<std::streamsize>(tr->mIn - tr->mOut)}; - if(toLoad >= TR_LOAD_SIZE && istream.good()) + std::streamsize toLoad{TRRingSize - static_cast<std::streamsize>(tr->mIn - tr->mOut)}; + if(toLoad >= TRLoadSize && istream.good()) { - // Load TR_LOAD_SIZE (or less if at the end of the file) per read. - toLoad = TR_LOAD_SIZE; - std::streamsize in{tr->mIn&TR_RING_MASK}; - std::streamsize count{TR_RING_SIZE - in}; + // Load TRLoadSize (or less if at the end of the file) per read. + toLoad = TRLoadSize; + + const auto in = static_cast<uint>(tr->mIn&TRRingMask); + std::streamsize count{TRRingSize - in}; if(count < toLoad) { istream.read(&tr->mRing[in], count); @@ -216,10 +225,10 @@ static int TrLoad(TokenReaderT *tr) tr->mIn += istream.gcount(); } - if(tr->mOut >= TR_RING_SIZE) + if(tr->mOut >= TRRingSize) { - tr->mOut -= TR_RING_SIZE; - tr->mIn -= TR_RING_SIZE; + tr->mOut -= TRRingSize; + tr->mIn -= TRRingSize; } } if(tr->mIn > tr->mOut) @@ -263,7 +272,7 @@ static void TrSkipLine(TokenReaderT *tr) while(TrLoad(tr)) { - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + ch = tr->mRing[tr->mOut&TRRingMask]; tr->mOut++; if(ch == '\n') { @@ -280,7 +289,7 @@ static int TrSkipWhitespace(TokenReaderT *tr) { while(TrLoad(tr)) { - char ch{tr->mRing[tr->mOut&TR_RING_MASK]}; + char ch{tr->mRing[tr->mOut&TRRingMask]}; if(isspace(ch)) { tr->mOut++; @@ -314,7 +323,7 @@ static int TrIsIdent(TokenReaderT *tr) { if(!TrSkipWhitespace(tr)) return 0; - char ch{tr->mRing[tr->mOut&TR_RING_MASK]}; + char ch{tr->mRing[tr->mOut&TRRingMask]}; return ch == '_' || isalpha(ch); } @@ -333,7 +342,7 @@ static int TrIsOperator(TokenReaderT *tr, const char *op) len = 0; while(op[len] != '\0' && out < tr->mIn) { - ch = tr->mRing[out&TR_RING_MASK]; + ch = tr->mRing[out&TRRingMask]; if(ch != op[len]) break; len++; out++; @@ -358,7 +367,7 @@ static int TrReadIdent(TokenReaderT *tr, const uint maxLen, char *ident) if(TrSkipWhitespace(tr)) { col = tr->mColumn; - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + ch = tr->mRing[tr->mOut&TRRingMask]; if(ch == '_' || isalpha(ch)) { len = 0; @@ -369,7 +378,7 @@ static int TrReadIdent(TokenReaderT *tr, const uint maxLen, char *ident) tr->mOut++; if(!TrLoad(tr)) break; - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + ch = tr->mRing[tr->mOut&TRRingMask]; } while(ch == '_' || isdigit(ch) || isalpha(ch)); tr->mColumn += len; @@ -389,25 +398,23 @@ static int TrReadIdent(TokenReaderT *tr, const uint maxLen, char *ident) // Reads and validates (including bounds) an integer token. static int TrReadInt(TokenReaderT *tr, const int loBound, const int hiBound, int *value) { - uint col, digis, len; - char ch, temp[64+1]; - - col = tr->mColumn; + uint col{tr->mColumn}; if(TrSkipWhitespace(tr)) { col = tr->mColumn; - len = 0; - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + uint len{0}; + std::array<char,64+1> temp{}; + char ch{tr->mRing[tr->mOut&TRRingMask]}; if(ch == '+' || ch == '-') { temp[len] = ch; len++; tr->mOut++; } - digis = 0; + uint digis{0}; while(TrLoad(tr)) { - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + ch = tr->mRing[tr->mOut&TRRingMask]; if(!isdigit(ch)) break; if(len < 64) temp[len] = ch; @@ -424,7 +431,7 @@ static int TrReadInt(TokenReaderT *tr, const int loBound, const int hiBound, int return 0; } temp[len] = '\0'; - *value = static_cast<int>(strtol(temp, nullptr, 10)); + *value = static_cast<int>(strtol(temp.data(), nullptr, 10)); if(*value < loBound || *value > hiBound) { TrErrorAt(tr, tr->mLine, col, "Expected a value from %d to %d.\n", loBound, hiBound); @@ -440,15 +447,13 @@ static int TrReadInt(TokenReaderT *tr, const int loBound, const int hiBound, int // Reads and validates (including bounds) a float token. static int TrReadFloat(TokenReaderT *tr, const double loBound, const double hiBound, double *value) { - uint col, digis, len; - char ch, temp[64+1]; - - col = tr->mColumn; + uint col{tr->mColumn}; if(TrSkipWhitespace(tr)) { col = tr->mColumn; - len = 0; - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + std::array<char,64+1> temp{}; + uint len{0}; + char ch{tr->mRing[tr->mOut&TRRingMask]}; if(ch == '+' || ch == '-') { temp[len] = ch; @@ -456,10 +461,10 @@ static int TrReadFloat(TokenReaderT *tr, const double loBound, const double hiBo tr->mOut++; } - digis = 0; + uint digis{0}; while(TrLoad(tr)) { - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + ch = tr->mRing[tr->mOut&TRRingMask]; if(!isdigit(ch)) break; if(len < 64) temp[len] = ch; @@ -476,7 +481,7 @@ static int TrReadFloat(TokenReaderT *tr, const double loBound, const double hiBo } while(TrLoad(tr)) { - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + ch = tr->mRing[tr->mOut&TRRingMask]; if(!isdigit(ch)) break; if(len < 64) temp[len] = ch; @@ -502,7 +507,7 @@ static int TrReadFloat(TokenReaderT *tr, const double loBound, const double hiBo } while(TrLoad(tr)) { - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + ch = tr->mRing[tr->mOut&TRRingMask]; if(!isdigit(ch)) break; if(len < 64) temp[len] = ch; @@ -520,7 +525,7 @@ static int TrReadFloat(TokenReaderT *tr, const double loBound, const double hiBo return 0; } temp[len] = '\0'; - *value = strtod(temp, nullptr); + *value = strtod(temp.data(), nullptr); if(*value < loBound || *value > hiBound) { TrErrorAt(tr, tr->mLine, col, "Expected a value from %f to %f.\n", loBound, hiBound); @@ -546,14 +551,14 @@ static int TrReadString(TokenReaderT *tr, const uint maxLen, char *text) if(TrSkipWhitespace(tr)) { col = tr->mColumn; - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + ch = tr->mRing[tr->mOut&TRRingMask]; if(ch == '\"') { tr->mOut++; len = 0; while(TrLoad(tr)) { - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + ch = tr->mRing[tr->mOut&TRRingMask]; tr->mOut++; if(ch == '\"') break; @@ -599,7 +604,7 @@ static int TrReadOperator(TokenReaderT *tr, const char *op) len = 0; while(op[len] != '\0' && TrLoad(tr)) { - ch = tr->mRing[tr->mOut&TR_RING_MASK]; + ch = tr->mRing[tr->mOut&TRRingMask]; if(ch != op[len]) break; len++; tr->mOut++; @@ -621,8 +626,8 @@ static int TrReadOperator(TokenReaderT *tr, const char *op) // storing it as a 32-bit unsigned integer. static int ReadBin4(std::istream &istream, const char *filename, const ByteOrderT order, const uint bytes, uint32_t *out) { - uint8_t in[4]; - istream.read(reinterpret_cast<char*>(in), static_cast<int>(bytes)); + std::array<uint8_t,4> in{}; + istream.read(reinterpret_cast<char*>(in.data()), static_cast<int>(bytes)); if(istream.gcount() != bytes) { fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename); @@ -650,29 +655,27 @@ static int ReadBin4(std::istream &istream, const char *filename, const ByteOrder // a 64-bit unsigned integer. static int ReadBin8(std::istream &istream, const char *filename, const ByteOrderT order, uint64_t *out) { - uint8_t in[8]; - uint64_t accum; - uint i; - - istream.read(reinterpret_cast<char*>(in), 8); + std::array<uint8_t,8> in{}; + istream.read(reinterpret_cast<char*>(in.data()), 8); if(istream.gcount() != 8) { fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename); return 0; } - accum = 0; + + uint64_t accum{}; switch(order) { - case BO_LITTLE: - for(i = 0;i < 8;i++) - accum = (accum<<8) | in[8 - i - 1]; - break; - case BO_BIG: - for(i = 0;i < 8;i++) - accum = (accum<<8) | in[i]; - break; - default: - break; + case BO_LITTLE: + for(uint i{0};i < 8;++i) + accum = (accum<<8) | in[8 - i - 1]; + break; + case BO_BIG: + for(uint i{0};i < 8;++i) + accum = (accum<<8) | in[i]; + break; + default: + break; } *out = accum; return 1; @@ -687,40 +690,32 @@ static int ReadBin8(std::istream &istream, const char *filename, const ByteOrder static int ReadBinAsDouble(std::istream &istream, const char *filename, const ByteOrderT order, const ElementTypeT type, const uint bytes, const int bits, double *out) { - union { - uint32_t ui; - int32_t i; - float f; - } v4; - union { - uint64_t ui; - double f; - } v8; - *out = 0.0; if(bytes > 4) { - if(!ReadBin8(istream, filename, order, &v8.ui)) + uint64_t val{}; + if(!ReadBin8(istream, filename, order, &val)) return 0; if(type == ET_FP) - *out = v8.f; + *out = al::bit_cast<double>(val); } else { - if(!ReadBin4(istream, filename, order, bytes, &v4.ui)) + uint32_t val{}; + if(!ReadBin4(istream, filename, order, bytes, &val)) return 0; if(type == ET_FP) - *out = v4.f; + *out = al::bit_cast<float>(val); else { if(bits > 0) - v4.ui >>= (8*bytes) - (static_cast<uint>(bits)); + val >>= (8*bytes) - (static_cast<uint>(bits)); else - v4.ui &= (0xFFFFFFFF >> (32+bits)); + val &= (0xFFFFFFFF >> (32+bits)); - if(v4.ui&static_cast<uint>(1<<(std::abs(bits)-1))) - v4.ui |= (0xFFFFFFFF << std::abs(bits)); - *out = v4.i / static_cast<double>(1<<(std::abs(bits)-1)); + if(val&static_cast<uint>(1<<(std::abs(bits)-1))) + val |= (0xFFFFFFFF << std::abs(bits)); + *out = static_cast<int32_t>(val) / static_cast<double>(1<<(std::abs(bits)-1)); } } return 1; @@ -776,20 +771,20 @@ static int ReadWaveFormat(std::istream &istream, const ByteOrderT order, const u do { if(chunkSize > 0) istream.seekg(static_cast<int>(chunkSize), std::ios::cur); - if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC) - || !ReadBin4(istream, src->mPath, order, 4, &chunkSize)) + if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC) + || !ReadBin4(istream, src->mPath.data(), order, 4, &chunkSize)) return 0; } while(fourCC != FOURCC_FMT); - if(!ReadBin4(istream, src->mPath, order, 2, &format) - || !ReadBin4(istream, src->mPath, order, 2, &channels) - || !ReadBin4(istream, src->mPath, order, 4, &rate) - || !ReadBin4(istream, src->mPath, order, 4, &dummy) - || !ReadBin4(istream, src->mPath, order, 2, &block)) + if(!ReadBin4(istream, src->mPath.data(), order, 2, &format) + || !ReadBin4(istream, src->mPath.data(), order, 2, &channels) + || !ReadBin4(istream, src->mPath.data(), order, 4, &rate) + || !ReadBin4(istream, src->mPath.data(), order, 4, &dummy) + || !ReadBin4(istream, src->mPath.data(), order, 2, &block)) return 0; block /= channels; if(chunkSize > 14) { - if(!ReadBin4(istream, src->mPath, order, 2, &size)) + if(!ReadBin4(istream, src->mPath.data(), order, 2, &size)) return 0; size /= 8; if(block > size) @@ -800,12 +795,12 @@ static int ReadWaveFormat(std::istream &istream, const ByteOrderT order, const u if(format == WAVE_FORMAT_EXTENSIBLE) { istream.seekg(2, std::ios::cur); - if(!ReadBin4(istream, src->mPath, order, 2, &bits)) + if(!ReadBin4(istream, src->mPath.data(), order, 2, &bits)) return 0; if(bits == 0) bits = 8 * size; istream.seekg(4, std::ios::cur); - if(!ReadBin4(istream, src->mPath, order, 2, &format)) + if(!ReadBin4(istream, src->mPath.data(), order, 2, &format)) return 0; istream.seekg(static_cast<int>(chunkSize - 26), std::ios::cur); } @@ -819,29 +814,32 @@ static int ReadWaveFormat(std::istream &istream, const ByteOrderT order, const u } if(format != WAVE_FORMAT_PCM && format != WAVE_FORMAT_IEEE_FLOAT) { - fprintf(stderr, "\nError: Unsupported WAVE format in file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Unsupported WAVE format in file '%s'.\n", src->mPath.data()); return 0; } if(src->mChannel >= channels) { - fprintf(stderr, "\nError: Missing source channel in WAVE file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Missing source channel in WAVE file '%s'.\n", src->mPath.data()); return 0; } if(rate != hrirRate) { - fprintf(stderr, "\nError: Mismatched source sample rate in WAVE file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Mismatched source sample rate in WAVE file '%s'.\n", + src->mPath.data()); return 0; } if(format == WAVE_FORMAT_PCM) { if(size < 2 || size > 4) { - fprintf(stderr, "\nError: Unsupported sample size in WAVE file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Unsupported sample size in WAVE file '%s'.\n", + src->mPath.data()); return 0; } if(bits < 16 || bits > (8*size)) { - fprintf(stderr, "\nError: Bad significant bits in WAVE file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Bad significant bits in WAVE file '%s'.\n", + src->mPath.data()); return 0; } src->mType = ET_INT; @@ -850,7 +848,8 @@ static int ReadWaveFormat(std::istream &istream, const ByteOrderT order, const u { if(size != 4 && size != 8) { - fprintf(stderr, "\nError: Unsupported sample size in WAVE file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Unsupported sample size in WAVE file '%s'.\n", + src->mPath.data()); return 0; } src->mType = ET_FP; @@ -876,7 +875,8 @@ static int ReadWaveData(std::istream &istream, const SourceRefT *src, const Byte skip += pre; if(skip > 0) istream.seekg(skip, std::ios::cur); - if(!ReadBinAsDouble(istream, src->mPath, order, src->mType, src->mSize, src->mBits, &hrir[i])) + if(!ReadBinAsDouble(istream, src->mPath.data(), order, src->mType, src->mSize, src->mBits, + &hrir[i])) return 0; skip = post; } @@ -896,8 +896,8 @@ static int ReadWaveList(std::istream &istream, const SourceRefT *src, const Byte for(;;) { - if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC) - || !ReadBin4(istream, src->mPath, order, 4, &chunkSize)) + if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC) + || !ReadBin4(istream, src->mPath.data(), order, 4, &chunkSize)) return 0; if(fourCC == FOURCC_DATA) @@ -906,17 +906,18 @@ static int ReadWaveList(std::istream &istream, const SourceRefT *src, const Byte count = chunkSize / block; if(count < (src->mOffset + n)) { - fprintf(stderr, "\nError: Bad read from file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Bad read from file '%s'.\n", src->mPath.data()); return 0; } - istream.seekg(static_cast<long>(src->mOffset * block), std::ios::cur); + using off_type = std::istream::off_type; + istream.seekg(off_type(src->mOffset) * off_type(block), std::ios::cur); if(!ReadWaveData(istream, src, order, n, &hrir[0])) return 0; return 1; } else if(fourCC == FOURCC_LIST) { - if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC)) + if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC)) return 0; chunkSize -= 4; if(fourCC == FOURCC_WAVL) @@ -932,8 +933,8 @@ static int ReadWaveList(std::istream &istream, const SourceRefT *src, const Byte lastSample = 0.0; while(offset < n && listSize > 8) { - if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC) - || !ReadBin4(istream, src->mPath, order, 4, &chunkSize)) + if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC) + || !ReadBin4(istream, src->mPath.data(), order, 4, &chunkSize)) return 0; listSize -= 8 + chunkSize; if(fourCC == FOURCC_DATA) @@ -941,7 +942,8 @@ static int ReadWaveList(std::istream &istream, const SourceRefT *src, const Byte count = chunkSize / block; if(count > skip) { - istream.seekg(static_cast<long>(skip * block), std::ios::cur); + using off_type = std::istream::off_type; + istream.seekg(off_type(skip) * off_type(block), std::ios::cur); chunkSize -= skip * block; count -= skip; skip = 0; @@ -961,7 +963,7 @@ static int ReadWaveList(std::istream &istream, const SourceRefT *src, const Byte } else if(fourCC == FOURCC_SLNT) { - if(!ReadBin4(istream, src->mPath, order, 4, &count)) + if(!ReadBin4(istream, src->mPath.data(), order, 4, &count)) return 0; chunkSize -= 4; if(count > skip) @@ -985,7 +987,7 @@ static int ReadWaveList(std::istream &istream, const SourceRefT *src, const Byte } if(offset < n) { - fprintf(stderr, "\nError: Bad read from file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Bad read from file '%s'.\n", src->mPath.data()); return 0; } return 1; @@ -997,22 +999,25 @@ static int LoadAsciiSource(std::istream &istream, const SourceRefT *src, const uint n, double *hrir) { TokenReaderT tr{istream}; - uint i, j; - double dummy; TrSetup(nullptr, 0, nullptr, &tr); - for(i = 0;i < src->mOffset;i++) + for(uint i{0};i < src->mOffset;++i) { - if(!ReadAsciiAsDouble(&tr, src->mPath, src->mType, static_cast<uint>(src->mBits), &dummy)) + double dummy{}; + if(!ReadAsciiAsDouble(&tr, src->mPath.data(), src->mType, static_cast<uint>(src->mBits), + &dummy)) return 0; } - for(i = 0;i < n;i++) + for(uint i{0};i < n;++i) { - if(!ReadAsciiAsDouble(&tr, src->mPath, src->mType, static_cast<uint>(src->mBits), &hrir[i])) + if(!ReadAsciiAsDouble(&tr, src->mPath.data(), src->mType, static_cast<uint>(src->mBits), + &hrir[i])) return 0; - for(j = 0;j < src->mSkip;j++) + for(uint j{0};j < src->mSkip;++j) { - if(!ReadAsciiAsDouble(&tr, src->mPath, src->mType, static_cast<uint>(src->mBits), &dummy)) + double dummy{}; + if(!ReadAsciiAsDouble(&tr, src->mPath.data(), src->mType, + static_cast<uint>(src->mBits), &dummy)) return 0; } } @@ -1026,7 +1031,8 @@ static int LoadBinarySource(std::istream &istream, const SourceRefT *src, const istream.seekg(static_cast<long>(src->mOffset), std::ios::beg); for(uint i{0};i < n;i++) { - if(!ReadBinAsDouble(istream, src->mPath, order, src->mType, src->mSize, src->mBits, &hrir[i])) + if(!ReadBinAsDouble(istream, src->mPath.data(), order, src->mType, src->mSize, src->mBits, + &hrir[i])) return 0; if(src->mSkip > 0) istream.seekg(static_cast<long>(src->mSkip), std::ios::cur); @@ -1041,8 +1047,8 @@ static int LoadWaveSource(std::istream &istream, SourceRefT *src, const uint hri uint32_t fourCC, dummy; ByteOrderT order; - if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC) - || !ReadBin4(istream, src->mPath, BO_LITTLE, 4, &dummy)) + if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC) + || !ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &dummy)) return 0; if(fourCC == FOURCC_RIFF) order = BO_LITTLE; @@ -1050,15 +1056,15 @@ static int LoadWaveSource(std::istream &istream, SourceRefT *src, const uint hri order = BO_BIG; else { - fprintf(stderr, "\nError: No RIFF/RIFX chunk in file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: No RIFF/RIFX chunk in file '%s'.\n", src->mPath.data()); return 0; } - if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC)) + if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC)) return 0; if(fourCC != FOURCC_WAVE) { - fprintf(stderr, "\nError: Not a RIFF/RIFX WAVE file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Not a RIFF/RIFX WAVE file '%s'.\n", src->mPath.data()); return 0; } if(!ReadWaveFormat(istream, order, hrirRate, src)) @@ -1069,15 +1075,39 @@ static int LoadWaveSource(std::istream &istream, SourceRefT *src, const uint hri } +namespace { + +struct SofaEasyDeleter { + void operator()(gsl::owner<MYSOFA_EASY*> sofa) + { + if(sofa->neighborhood) mysofa_neighborhood_free(sofa->neighborhood); + if(sofa->lookup) mysofa_lookup_free(sofa->lookup); + if(sofa->hrtf) mysofa_free(sofa->hrtf); + delete sofa; + } +}; +using SofaEasyPtr = std::unique_ptr<MYSOFA_EASY,SofaEasyDeleter>; + +struct SofaCacheEntry { + std::string mName; + uint mSampleRate{}; + SofaEasyPtr mSofa; +}; +std::vector<SofaCacheEntry> gSofaCache; + +} // namespace // Load a Spatially Oriented Format for Accoustics (SOFA) file. static MYSOFA_EASY* LoadSofaFile(SourceRefT *src, const uint hrirRate, const uint n) { - struct MYSOFA_EASY *sofa{mysofa_cache_lookup(src->mPath, static_cast<float>(hrirRate))}; - if(sofa) return sofa; - - sofa = static_cast<MYSOFA_EASY*>(calloc(1, sizeof(*sofa))); - if(sofa == nullptr) + const std::string_view srcname{src->mPath.data()}; + auto iter = std::find_if(gSofaCache.begin(), gSofaCache.end(), + [srcname,hrirRate](SofaCacheEntry &entry) -> bool + { return entry.mName == srcname && entry.mSampleRate == hrirRate; }); + if(iter != gSofaCache.end()) return iter->mSofa.get(); + + SofaEasyPtr sofa{new(std::nothrow) MYSOFA_EASY{}}; + if(!sofa) { fprintf(stderr, "\nError: Out of memory.\n"); return nullptr; @@ -1086,38 +1116,37 @@ static MYSOFA_EASY* LoadSofaFile(SourceRefT *src, const uint hrirRate, const uin sofa->neighborhood = nullptr; int err; - sofa->hrtf = mysofa_load(src->mPath, &err); + sofa->hrtf = mysofa_load(src->mPath.data(), &err); if(!sofa->hrtf) { - mysofa_close(sofa); - fprintf(stderr, "\nError: Could not load source file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Could not load source file '%s' (error: %d).\n", + src->mPath.data(), err); return nullptr; } /* NOTE: Some valid SOFA files are failing this check. */ err = mysofa_check(sofa->hrtf); if(err != MYSOFA_OK) - fprintf(stderr, "\nWarning: Supposedly malformed source file '%s'.\n", src->mPath); + fprintf(stderr, "\nWarning: Supposedly malformed source file '%s' (error: %d).\n", + src->mPath.data(), err); if((src->mOffset + n) > sofa->hrtf->N) { - mysofa_close(sofa); - fprintf(stderr, "\nError: Not enough samples in SOFA file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Not enough samples in SOFA file '%s'.\n", src->mPath.data()); return nullptr; } if(src->mChannel >= sofa->hrtf->R) { - mysofa_close(sofa); - fprintf(stderr, "\nError: Missing source receiver in SOFA file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Missing source receiver in SOFA file '%s'.\n",src->mPath.data()); return nullptr; } mysofa_tocartesian(sofa->hrtf); sofa->lookup = mysofa_lookup_init(sofa->hrtf); if(sofa->lookup == nullptr) { - mysofa_close(sofa); fprintf(stderr, "\nError: Out of memory.\n"); return nullptr; } - return mysofa_cache_store(sofa, src->mPath, static_cast<float>(hrirRate)); + gSofaCache.emplace_back(SofaCacheEntry{std::string{srcname}, hrirRate, std::move(sofa)}); + return gSofaCache.back().mSofa.get(); } // Copies the HRIR data from a particular SOFA measurement. @@ -1131,40 +1160,39 @@ static void ExtractSofaHrir(const MYSOFA_EASY *sofa, const uint index, const uin // file. static int LoadSofaSource(SourceRefT *src, const uint hrirRate, const uint n, double *hrir) { - struct MYSOFA_EASY *sofa; - float target[3]; - int nearest; - float *coords; + MYSOFA_EASY *sofa{LoadSofaFile(src, hrirRate, n)}; + if(sofa == nullptr) return 0; - sofa = LoadSofaFile(src, hrirRate, n); - if(sofa == nullptr) - return 0; - - /* NOTE: At some point it may be benficial or necessary to consider the + /* NOTE: At some point it may be beneficial or necessary to consider the various coordinate systems, listener/source orientations, and - direciontal vectors defined in the SOFA file. + directional vectors defined in the SOFA file. */ - target[0] = static_cast<float>(src->mAzimuth); - target[1] = static_cast<float>(src->mElevation); - target[2] = static_cast<float>(src->mRadius); - mysofa_s2c(target); - - nearest = mysofa_lookup(sofa->lookup, target); + std::array target{ + static_cast<float>(src->mAzimuth), + static_cast<float>(src->mElevation), + static_cast<float>(src->mRadius) + }; + mysofa_s2c(target.data()); + + int nearest{mysofa_lookup(sofa->lookup, target.data())}; if(nearest < 0) { - fprintf(stderr, "\nError: Lookup failed in source file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Lookup failed in source file '%s'.\n", src->mPath.data()); return 0; } - coords = &sofa->hrtf->SourcePosition.values[3 * nearest]; - if(std::abs(coords[0] - target[0]) > 0.001 || std::abs(coords[1] - target[1]) > 0.001 || std::abs(coords[2] - target[2]) > 0.001) + al::span<float,3> coords{&sofa->hrtf->SourcePosition.values[3_z * nearest], 3}; + if(std::abs(coords[0] - target[0]) > 0.001 || std::abs(coords[1] - target[1]) > 0.001 + || std::abs(coords[2] - target[2]) > 0.001) { - fprintf(stderr, "\nError: No impulse response at coordinates (%.3fr, %.1fev, %.1faz) in file '%s'.\n", src->mRadius, src->mElevation, src->mAzimuth, src->mPath); + fprintf(stderr, "\nError: No impulse response at coordinates (%.3fr, %.1fev, %.1faz) in file '%s'.\n", + src->mRadius, src->mElevation, src->mAzimuth, src->mPath.data()); target[0] = coords[0]; target[1] = coords[1]; target[2] = coords[2]; - mysofa_c2s(target); - fprintf(stderr, " Nearest candidate at (%.3fr, %.1fev, %.1faz).\n", target[2], target[1], target[0]); + mysofa_c2s(target.data()); + fprintf(stderr, " Nearest candidate at (%.3fr, %.1fev, %.1faz).\n", target[2], + target[1], target[0]); return 0; } @@ -1180,12 +1208,12 @@ static int LoadSource(SourceRefT *src, const uint hrirRate, const uint n, double if(src->mFormat != SF_SOFA) { if(src->mFormat == SF_ASCII) - istream.reset(new al::ifstream{src->mPath}); + istream = std::make_unique<al::ifstream>(src->mPath.data()); else - istream.reset(new al::ifstream{src->mPath, std::ios::binary}); + istream = std::make_unique<al::ifstream>(src->mPath.data(), std::ios::binary); if(!istream->good()) { - fprintf(stderr, "\nError: Could not open source file '%s'.\n", src->mPath); + fprintf(stderr, "\nError: Could not open source file '%s'.\n", src->mPath.data()); return 0; } } @@ -1230,14 +1258,14 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc { int hasRate = 0, hasType = 0, hasPoints = 0, hasRadius = 0; int hasDistance = 0, hasAzimuths = 0; - char ident[MAX_IDENT_LEN+1]; + std::array<char,MaxIdentLen+1> ident; uint line, col; double fpVal; uint points; int intVal; - double distances[MAX_FD_COUNT]; + std::array<double,MAX_FD_COUNT> distances; uint fdCount = 0; - uint evCounts[MAX_FD_COUNT]; + std::array<uint,MAX_FD_COUNT> evCounts; auto azCounts = std::vector<std::array<uint,MAX_EV_COUNT>>(MAX_FD_COUNT); for(auto &azs : azCounts) azs.fill(0u); @@ -1245,9 +1273,9 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc while(TrIsIdent(tr)) { TrIndication(tr, &line, &col); - if(!TrReadIdent(tr, MAX_IDENT_LEN, ident)) + if(!TrReadIdent(tr, MaxIdentLen, ident.data())) return 0; - if(al::strcasecmp(ident, "rate") == 0) + if(al::strcasecmp(ident.data(), "rate") == 0) { if(hasRate) { @@ -1261,9 +1289,9 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc hData->mIrRate = static_cast<uint>(intVal); hasRate = 1; } - else if(al::strcasecmp(ident, "type") == 0) + else if(al::strcasecmp(ident.data(), "type") == 0) { - char type[MAX_IDENT_LEN+1]; + std::array<char,MaxIdentLen+1> type; if(hasType) { @@ -1273,9 +1301,9 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc if(!TrReadOperator(tr, "=")) return 0; - if(!TrReadIdent(tr, MAX_IDENT_LEN, type)) + if(!TrReadIdent(tr, MaxIdentLen, type.data())) return 0; - hData->mChannelType = MatchChannelType(type); + hData->mChannelType = MatchChannelType(type.data()); if(hData->mChannelType == CT_NONE) { TrErrorAt(tr, line, col, "Expected a channel type.\n"); @@ -1288,7 +1316,7 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc } hasType = 1; } - else if(al::strcasecmp(ident, "points") == 0) + else if(al::strcasecmp(ident.data(), "points") == 0) { if(hasPoints) { @@ -1318,7 +1346,7 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc hData->mIrSize = points; hasPoints = 1; } - else if(al::strcasecmp(ident, "radius") == 0) + else if(al::strcasecmp(ident.data(), "radius") == 0) { if(hasRadius) { @@ -1327,12 +1355,12 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc } if(!TrReadOperator(tr, "=")) return 0; - if(!TrReadFloat(tr, MIN_RADIUS, MAX_RADIUS, &fpVal)) + if(!TrReadFloat(tr, MinRadius, MaxRadius, &fpVal)) return 0; hData->mRadius = fpVal; hasRadius = 1; } - else if(al::strcasecmp(ident, "distance") == 0) + else if(al::strcasecmp(ident.data(), "distance") == 0) { uint count = 0; @@ -1371,7 +1399,7 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc fdCount = count; hasDistance = 1; } - else if(al::strcasecmp(ident, "azimuths") == 0) + else if(al::strcasecmp(ident.data(), "azimuths") == 0) { uint count = 0; @@ -1451,7 +1479,7 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc if(hData->mChannelType == CT_NONE) hData->mChannelType = CT_MONO; const auto azs = al::span{azCounts}.first<MAX_FD_COUNT>(); - if(!PrepareHrirData({distances, fdCount}, evCounts, azs, hData)) + if(!PrepareHrirData(al::span{distances}.first(fdCount), evCounts, azs, hData)) { fprintf(stderr, "Error: Out of memory.\n"); exit(-1); @@ -1516,15 +1544,15 @@ static ElementTypeT MatchElementType(const char *ident) // Parse and validate a source reference from the data set definition. static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src) { - char ident[MAX_IDENT_LEN+1]; + std::array<char,MaxIdentLen+1> ident; uint line, col; double fpVal; int intVal; TrIndication(tr, &line, &col); - if(!TrReadIdent(tr, MAX_IDENT_LEN, ident)) + if(!TrReadIdent(tr, MaxIdentLen, ident.data())) return 0; - src->mFormat = MatchSourceFormat(ident); + src->mFormat = MatchSourceFormat(ident.data()); if(src->mFormat == SF_NONE) { TrErrorAt(tr, line, col, "Expected a source format.\n"); @@ -1549,7 +1577,7 @@ static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src) src->mAzimuth = fpVal; if(!TrReadOperator(tr, ":")) return 0; - if(!TrReadInt(tr, 0, MAX_WAVE_CHANNELS, &intVal)) + if(!TrReadInt(tr, 0, MaxWaveChannels, &intVal)) return 0; src->mType = ET_NONE; src->mSize = 0; @@ -1559,7 +1587,7 @@ static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src) } else if(src->mFormat == SF_WAVE) { - if(!TrReadInt(tr, 0, MAX_WAVE_CHANNELS, &intVal)) + if(!TrReadInt(tr, 0, MaxWaveChannels, &intVal)) return 0; src->mType = ET_NONE; src->mSize = 0; @@ -1570,9 +1598,9 @@ static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src) else { TrIndication(tr, &line, &col); - if(!TrReadIdent(tr, MAX_IDENT_LEN, ident)) + if(!TrReadIdent(tr, MaxIdentLen, ident.data())) return 0; - src->mType = MatchElementType(ident); + src->mType = MatchElementType(ident.data()); if(src->mType == ET_NONE) { TrErrorAt(tr, line, col, "Expected a source element type.\n"); @@ -1584,7 +1612,7 @@ static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src) return 0; if(src->mType == ET_INT) { - if(!TrReadInt(tr, MIN_BIN_SIZE, MAX_BIN_SIZE, &intVal)) + if(!TrReadInt(tr, MinBinSize, MaxBinSize, &intVal)) return 0; src->mSize = static_cast<uint>(intVal); if(!TrIsOperator(tr, ",")) @@ -1595,9 +1623,9 @@ static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src) TrIndication(tr, &line, &col); if(!TrReadInt(tr, -2147483647-1, 2147483647, &intVal)) return 0; - if(std::abs(intVal) < MIN_BIN_BITS || static_cast<uint>(std::abs(intVal)) > (8*src->mSize)) + if(std::abs(intVal) < int{MinBinSize}*8 || static_cast<uint>(std::abs(intVal)) > (8*src->mSize)) { - TrErrorAt(tr, line, col, "Expected a value of (+/-) %d to %d.\n", MIN_BIN_BITS, 8*src->mSize); + TrErrorAt(tr, line, col, "Expected a value of (+/-) %d to %d.\n", MinBinSize*8, 8*src->mSize); return 0; } src->mBits = intVal; @@ -1621,7 +1649,7 @@ static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src) { if(!TrReadOperator(tr, ",")) return 0; - if(!TrReadInt(tr, MIN_ASCII_BITS, MAX_ASCII_BITS, &intVal)) + if(!TrReadInt(tr, MinASCIIBits, MaxASCIIBits, &intVal)) return 0; src->mSize = 0; src->mBits = intVal; @@ -1655,7 +1683,7 @@ static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src) src->mOffset = 0; if(!TrReadOperator(tr, ":")) return 0; - if(!TrReadString(tr, MAX_PATH_LEN, src->mPath)) + if(!TrReadString(tr, MAX_PATH_LEN, src->mPath.data())) return 0; return 1; } @@ -1663,14 +1691,14 @@ static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src) // Parse and validate a SOFA source reference from the data set definition. static int ReadSofaRef(TokenReaderT *tr, SourceRefT *src) { - char ident[MAX_IDENT_LEN+1]; + std::array<char,MaxIdentLen+1> ident; uint line, col; int intVal; TrIndication(tr, &line, &col); - if(!TrReadIdent(tr, MAX_IDENT_LEN, ident)) + if(!TrReadIdent(tr, MaxIdentLen, ident.data())) return 0; - src->mFormat = MatchSourceFormat(ident); + src->mFormat = MatchSourceFormat(ident.data()); if(src->mFormat != SF_SOFA) { TrErrorAt(tr, line, col, "Expected the SOFA source format.\n"); @@ -1694,7 +1722,7 @@ static int ReadSofaRef(TokenReaderT *tr, SourceRefT *src) src->mOffset = 0; if(!TrReadOperator(tr, ":")) return 0; - if(!TrReadString(tr, MAX_PATH_LEN, src->mPath)) + if(!TrReadString(tr, MAX_PATH_LEN, src->mPath.data())) return 0; return 1; } @@ -1745,12 +1773,12 @@ static void AverageHrirMagnitude(const uint points, const uint n, const double * static int ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate) { const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u}; - hData->mHrirsBase.resize(channels * hData->mIrCount * hData->mIrSize); + hData->mHrirsBase.resize(size_t{channels} * hData->mIrCount * hData->mIrSize); double *hrirs = hData->mHrirsBase.data(); - auto hrir = std::make_unique<double[]>(hData->mIrSize); + auto hrir = std::vector<double>(hData->mIrSize); uint line, col, fi, ei, ai; - std::vector<double> onsetSamples(OnsetRateMultiple * hData->mIrPoints); + std::vector<double> onsetSamples(size_t{OnsetRateMultiple} * hData->mIrPoints); PPhaseResampler onsetResampler; onsetResampler.init(hData->mIrRate, OnsetRateMultiple*hData->mIrRate); @@ -1767,57 +1795,50 @@ static int ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate int count{0}; while(TrIsOperator(tr, "[")) { - double factor[2]{ 1.0, 1.0 }; + std::array factor{1.0, 1.0}; TrIndication(tr, &line, &col); TrReadOperator(tr, "["); if(TrIsOperator(tr, "*")) { - SourceRefT src; - struct MYSOFA_EASY *sofa; - uint si; - TrReadOperator(tr, "*"); if(!TrReadOperator(tr, "]") || !TrReadOperator(tr, "=")) return 0; TrIndication(tr, &line, &col); + SourceRefT src{}; if(!ReadSofaRef(tr, &src)) return 0; if(hData->mChannelType == CT_STEREO) { - char type[MAX_IDENT_LEN+1]; - ChannelTypeT channelType; + std::array<char,MaxIdentLen+1> type{}; - if(!TrReadIdent(tr, MAX_IDENT_LEN, type)) + if(!TrReadIdent(tr, MaxIdentLen, type.data())) return 0; - channelType = MatchChannelType(type); - + const ChannelTypeT channelType{MatchChannelType(type.data())}; switch(channelType) { - case CT_NONE: - TrErrorAt(tr, line, col, "Expected a channel type.\n"); - return 0; - case CT_MONO: - src.mChannel = 0; - break; - case CT_STEREO: - src.mChannel = 1; - break; + case CT_NONE: + TrErrorAt(tr, line, col, "Expected a channel type.\n"); + return 0; + case CT_MONO: + src.mChannel = 0; + break; + case CT_STEREO: + src.mChannel = 1; + break; } } else { - char type[MAX_IDENT_LEN+1]; - ChannelTypeT channelType; - - if(!TrReadIdent(tr, MAX_IDENT_LEN, type)) + std::array<char,MaxIdentLen+1> type{}; + if(!TrReadIdent(tr, MaxIdentLen, type.data())) return 0; - channelType = MatchChannelType(type); + ChannelTypeT channelType{MatchChannelType(type.data())}; if(channelType != CT_MONO) { TrErrorAt(tr, line, col, "Expected a mono channel type.\n"); @@ -1826,20 +1847,20 @@ static int ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate src.mChannel = 0; } - sofa = LoadSofaFile(&src, hData->mIrRate, hData->mIrPoints); + MYSOFA_EASY *sofa{LoadSofaFile(&src, hData->mIrRate, hData->mIrPoints)}; if(!sofa) return 0; - for(si = 0;si < sofa->hrtf->M;si++) + for(uint si{0};si < sofa->hrtf->M;++si) { printf("\rLoading sources... %d of %d", si+1, sofa->hrtf->M); fflush(stdout); - float aer[3] = { - sofa->hrtf->SourcePosition.values[3*si], - sofa->hrtf->SourcePosition.values[3*si + 1], - sofa->hrtf->SourcePosition.values[3*si + 2] + std::array aer{ + sofa->hrtf->SourcePosition.values[3_uz*si], + sofa->hrtf->SourcePosition.values[3_uz*si + 1], + sofa->hrtf->SourcePosition.values[3_uz*si + 2] }; - mysofa_c2s(aer); + mysofa_c2s(aer.data()); if(std::fabs(aer[1]) >= 89.999f) aer[0] = 0.0f; @@ -1875,24 +1896,25 @@ static int ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate return 0; } - ExtractSofaHrir(sofa, si, 0, src.mOffset, hData->mIrPoints, hrir.get()); - azd->mIrs[0] = &hrirs[hData->mIrSize * azd->mIndex]; + ExtractSofaHrir(sofa, si, 0, src.mOffset, hData->mIrPoints, hrir.data()); + azd->mIrs[0] = &hrirs[size_t{hData->mIrSize} * azd->mIndex]; azd->mDelays[0] = AverageHrirOnset(onsetResampler, onsetSamples, hData->mIrRate, - hData->mIrPoints, hrir.get(), 1.0, azd->mDelays[0]); + hData->mIrPoints, hrir.data(), 1.0, azd->mDelays[0]); if(resampler) - resampler->process(hData->mIrPoints, hrir.get(), hData->mIrSize, hrir.get()); - AverageHrirMagnitude(irPoints, hData->mFftSize, hrir.get(), 1.0, azd->mIrs[0]); + resampler->process(hData->mIrPoints, hrir.data(), hData->mIrSize, hrir.data()); + AverageHrirMagnitude(irPoints, hData->mFftSize, hrir.data(), 1.0, azd->mIrs[0]); if(src.mChannel == 1) { - ExtractSofaHrir(sofa, si, 1, src.mOffset, hData->mIrPoints, hrir.get()); - azd->mIrs[1] = &hrirs[hData->mIrSize * (hData->mIrCount + azd->mIndex)]; + ExtractSofaHrir(sofa, si, 1, src.mOffset, hData->mIrPoints, hrir.data()); + azd->mIrs[1] = &hrirs[hData->mIrSize * (size_t{hData->mIrCount}+azd->mIndex)]; azd->mDelays[1] = AverageHrirOnset(onsetResampler, onsetSamples, - hData->mIrRate, hData->mIrPoints, hrir.get(), 1.0, azd->mDelays[1]); + hData->mIrRate, hData->mIrPoints, hrir.data(), 1.0, azd->mDelays[1]); if(resampler) - resampler->process(hData->mIrPoints, hrir.get(), hData->mIrSize, - hrir.get()); - AverageHrirMagnitude(irPoints, hData->mFftSize, hrir.get(), 1.0, azd->mIrs[1]); + resampler->process(hData->mIrPoints, hrir.data(), hData->mIrSize, + hrir.data()); + AverageHrirMagnitude(irPoints, hData->mFftSize, hrir.data(), 1.0, + azd->mIrs[1]); } // TODO: Since some SOFA files contain minimum phase HRIRs, @@ -1917,10 +1939,9 @@ static int ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate if(!TrReadOperator(tr, "=")) return 0; - for(;;) + while(true) { - SourceRefT src; - + SourceRefT src{}; if(!ReadSourceRef(tr, &src)) return 0; @@ -1931,29 +1952,28 @@ static int ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate printf("\rLoading sources... %d file%s", count, (count==1)?"":"s"); fflush(stdout); - if(!LoadSource(&src, hData->mIrRate, hData->mIrPoints, hrir.get())) + if(!LoadSource(&src, hData->mIrRate, hData->mIrPoints, hrir.data())) return 0; uint ti{0}; if(hData->mChannelType == CT_STEREO) { - char ident[MAX_IDENT_LEN+1]; - - if(!TrReadIdent(tr, MAX_IDENT_LEN, ident)) + std::array<char,MaxIdentLen+1> ident{}; + if(!TrReadIdent(tr, MaxIdentLen, ident.data())) return 0; - ti = static_cast<uint>(MatchTargetEar(ident)); + ti = static_cast<uint>(MatchTargetEar(ident.data())); if(static_cast<int>(ti) < 0) { TrErrorAt(tr, line, col, "Expected a target ear.\n"); return 0; } } - azd->mIrs[ti] = &hrirs[hData->mIrSize * (ti * hData->mIrCount + azd->mIndex)]; + azd->mIrs[ti] = &hrirs[hData->mIrSize * (ti*size_t{hData->mIrCount} + azd->mIndex)]; azd->mDelays[ti] = AverageHrirOnset(onsetResampler, onsetSamples, hData->mIrRate, - hData->mIrPoints, hrir.get(), 1.0 / factor[ti], azd->mDelays[ti]); + hData->mIrPoints, hrir.data(), 1.0 / factor[ti], azd->mDelays[ti]); if(resampler) - resampler->process(hData->mIrPoints, hrir.get(), hData->mIrSize, hrir.get()); - AverageHrirMagnitude(irPoints, hData->mFftSize, hrir.get(), 1.0 / factor[ti], + resampler->process(hData->mIrPoints, hrir.data(), hData->mIrSize, hrir.data()); + AverageHrirMagnitude(irPoints, hData->mFftSize, hrir.data(), 1.0 / factor[ti], azd->mIrs[ti]); factor[ti] += 1.0; if(!TrIsOperator(tr, "+")) @@ -1975,7 +1995,7 @@ static int ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate } } printf("\n"); - hrir = nullptr; + hrir.clear(); if(resampler) { hData->mIrRate = outRate; @@ -2025,19 +2045,19 @@ static int ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate { HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai]; - azd->mIrs[ti] = &hrirs[hData->mIrSize * (ti * hData->mIrCount + azd->mIndex)]; + azd->mIrs[ti] = &hrirs[hData->mIrSize * (ti*size_t{hData->mIrCount} + azd->mIndex)]; } } } } if(!TrLoad(tr)) { - mysofa_cache_release_all(); + gSofaCache.clear(); return 1; } TrError(tr, "Errant data at end of source list.\n"); - mysofa_cache_release_all(); + gSofaCache.clear(); return 0; } diff --git a/utils/makemhr/loadsofa.cpp b/utils/makemhr/loadsofa.cpp index 9bcfc38d..b2038c77 100644 --- a/utils/makemhr/loadsofa.cpp +++ b/utils/makemhr/loadsofa.cpp @@ -39,6 +39,7 @@ #include <vector> #include "alspan.h" +#include "alnumeric.h" #include "makemhr.h" #include "polyphase_resampler.h" #include "sofa-support.h" @@ -65,8 +66,8 @@ static bool PrepareLayout(const uint m, const float *xyzs, HrirDataT *hData) return false; } - double distances[MAX_FD_COUNT]{}; - uint evCounts[MAX_FD_COUNT]{}; + std::array<double,MAX_FD_COUNT> distances{}; + std::array<uint,MAX_FD_COUNT> evCounts{}; auto azCounts = std::vector<std::array<uint,MAX_EV_COUNT>>(MAX_FD_COUNT); for(auto &azs : azCounts) azs.fill(0u); @@ -88,7 +89,7 @@ static bool PrepareLayout(const uint m, const float *xyzs, HrirDataT *hData) } fprintf(stdout, "Using %u of %u IRs.\n", ir_total, m); const auto azs = al::span{azCounts}.first<MAX_FD_COUNT>(); - return PrepareHrirData({distances, fi}, evCounts, azs, hData); + return PrepareHrirData(al::span{distances}.first(fi), evCounts, azs, hData); } @@ -144,7 +145,7 @@ float GetSampleRate(MYSOFA_HRTF *sofaHrtf) return 0.0f; } /* I dimensions guarantees 1 element, so just extract it. */ - if(srate_array->values[0] < MIN_RATE || srate_array->values[0] > MAX_RATE) + if(srate_array->values[0] < float{MIN_RATE} || srate_array->values[0] > float{MAX_RATE}) { fprintf(stderr, "Sample rate out of range: %f (expected %u to %u)", srate_array->values[0], MIN_RATE, MAX_RATE); @@ -261,27 +262,27 @@ static bool LoadResponses(MYSOFA_HRTF *sofaHrtf, HrirDataT *hData, const DelayTy auto load_proc = [sofaHrtf,hData,delayType,outRate,&loaded_count]() -> bool { const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u}; - hData->mHrirsBase.resize(channels * hData->mIrCount * hData->mIrSize, 0.0); + hData->mHrirsBase.resize(channels * size_t{hData->mIrCount} * hData->mIrSize, 0.0); double *hrirs = hData->mHrirsBase.data(); - std::unique_ptr<double[]> restmp; + std::vector<double> restmp; std::optional<PPhaseResampler> resampler; if(outRate && outRate != hData->mIrRate) { resampler.emplace().init(hData->mIrRate, outRate); - restmp = std::make_unique<double[]>(sofaHrtf->N); + restmp.resize(sofaHrtf->N); } for(uint si{0u};si < sofaHrtf->M;++si) { loaded_count.fetch_add(1u); - float aer[3]{ - sofaHrtf->SourcePosition.values[3*si], - sofaHrtf->SourcePosition.values[3*si + 1], - sofaHrtf->SourcePosition.values[3*si + 2] + std::array aer{ + sofaHrtf->SourcePosition.values[3_uz*si], + sofaHrtf->SourcePosition.values[3_uz*si + 1], + sofaHrtf->SourcePosition.values[3_uz*si + 2] }; - mysofa_c2s(aer); + mysofa_c2s(aer.data()); if(std::abs(aer[1]) >= 89.999f) aer[0] = 0.0f; @@ -317,15 +318,15 @@ static bool LoadResponses(MYSOFA_HRTF *sofaHrtf, HrirDataT *hData, const DelayTy for(uint ti{0u};ti < channels;++ti) { - azd->mIrs[ti] = &hrirs[hData->mIrSize * (hData->mIrCount*ti + azd->mIndex)]; + azd->mIrs[ti] = &hrirs[(size_t{hData->mIrCount}*ti + azd->mIndex)*hData->mIrSize]; if(!resampler) - std::copy_n(&sofaHrtf->DataIR.values[(si*sofaHrtf->R + ti)*sofaHrtf->N], + std::copy_n(&sofaHrtf->DataIR.values[(size_t{si}*sofaHrtf->R + ti)*sofaHrtf->N], sofaHrtf->N, azd->mIrs[ti]); else { - std::copy_n(&sofaHrtf->DataIR.values[(si*sofaHrtf->R + ti)*sofaHrtf->N], - sofaHrtf->N, restmp.get()); - resampler->process(sofaHrtf->N, restmp.get(), hData->mIrSize, azd->mIrs[ti]); + std::copy_n(&sofaHrtf->DataIR.values[(size_t{si}*sofaHrtf->R + ti)*sofaHrtf->N], + sofaHrtf->N, restmp.data()); + resampler->process(sofaHrtf->N, restmp.data(), hData->mIrSize, azd->mIrs[ti]); } } @@ -382,7 +383,7 @@ struct MagCalculator { { auto htemp = std::vector<complex_d>(mFftSize); - while(1) + while(true) { /* Load the current index to process. */ size_t idx{mCurrent.load()}; @@ -459,7 +460,7 @@ bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSiz /* Assume a default head radius of 9cm. */ hData->mRadius = 0.09; - hData->mIrRate = static_cast<uint>(GetSampleRate(sofaHrtf.get()) + 0.5f); + hData->mIrRate = static_cast<uint>(std::lround(GetSampleRate(sofaHrtf.get()))); if(!hData->mIrRate) return false; @@ -520,7 +521,7 @@ bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSiz for(uint ai{0u};ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++) { HrirAzT &azd = hData->mFds[fi].mEvs[ei].mAzs[ai]; - for(uint ti{0u};ti < channels;ti++) + for(size_t ti{0u};ti < channels;ti++) azd.mIrs[ti] = &hrirs[hData->mIrSize * (hData->mIrCount*ti + azd.mIndex)]; } } @@ -533,7 +534,7 @@ bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSiz auto onset_proc = [hData,channels,&hrir_done]() -> bool { /* Temporary buffer used to calculate the IR's onset. */ - auto upsampled = std::vector<double>(OnsetRateMultiple * hData->mIrPoints); + auto upsampled = std::vector<double>(size_t{OnsetRateMultiple} * hData->mIrPoints); /* This resampler is used to help detect the response onset. */ PPhaseResampler rs; rs.init(hData->mIrRate, OnsetRateMultiple*hData->mIrRate); diff --git a/utils/makemhr/makemhr.cpp b/utils/makemhr/makemhr.cpp index 98e1b73f..0a9b71e7 100644 --- a/utils/makemhr/makemhr.cpp +++ b/utils/makemhr/makemhr.cpp @@ -90,6 +90,8 @@ #include "alcomplex.h" #include "alfstream.h" +#include "alnumbers.h" +#include "alnumeric.h" #include "alspan.h" #include "alstring.h" #include "loaddef.h" @@ -98,61 +100,61 @@ #include "win_main_utf8.h" -namespace { - -using namespace std::placeholders; - -} // namespace - -#ifndef M_PI -#define M_PI (3.14159265358979323846) -#endif - - HrirDataT::~HrirDataT() = default; -// Head model used for calculating the impulse delays. -enum HeadModelT { - HM_NONE, - HM_DATASET, // Measure the onset from the dataset. - HM_SPHERE // Calculate the onset using a spherical head model. +namespace { + +struct FileDeleter { + void operator()(gsl::owner<FILE*> f) { fclose(f); } }; +using FilePtr = std::unique_ptr<FILE,FileDeleter>; +using namespace std::placeholders; // The epsilon used to maintain signal stability. -#define EPSILON (1e-9) +constexpr double Epsilon{1e-9}; // The limits to the FFT window size override on the command line. -#define MIN_FFTSIZE (65536) -#define MAX_FFTSIZE (131072) +constexpr uint MinFftSize{65536}; +constexpr uint MaxFftSize{131072}; // The limits to the equalization range limit on the command line. -#define MIN_LIMIT (2.0) -#define MAX_LIMIT (120.0) +constexpr double MinLimit{2.0}; +constexpr double MaxLimit{120.0}; // The limits to the truncation window size on the command line. -#define MIN_TRUNCSIZE (16) -#define MAX_TRUNCSIZE (128) +constexpr uint MinTruncSize{16}; +constexpr uint MaxTruncSize{128}; // The limits to the custom head radius on the command line. -#define MIN_CUSTOM_RADIUS (0.05) -#define MAX_CUSTOM_RADIUS (0.15) - -// The defaults for the command line options. -#define DEFAULT_FFTSIZE (65536) -#define DEFAULT_EQUALIZE (1) -#define DEFAULT_SURFACE (1) -#define DEFAULT_LIMIT (24.0) -#define DEFAULT_TRUNCSIZE (64) -#define DEFAULT_HEAD_MODEL (HM_DATASET) -#define DEFAULT_CUSTOM_RADIUS (0.0) +constexpr double MinCustomRadius{0.05}; +constexpr double MaxCustomRadius{0.15}; // The maximum propagation delay value supported by OpenAL Soft. -#define MAX_HRTD (63.0) +constexpr double MaxHrtd{63.0}; // The OpenAL Soft HRTF format marker. It stands for minimum-phase head // response protocol 03. -#define MHR_FORMAT ("MinPHR03") +constexpr char MHRFormat[] = "MinPHR03"; // NOLINT(*-avoid-c-arrays) + + +// Head model used for calculating the impulse delays. +enum HeadModelT { + HM_NONE, + HM_DATASET, // Measure the onset from the dataset. + HM_SPHERE, // Calculate the onset using a spherical head model. + + DEFAULT_HEAD_MODEL = HM_DATASET +}; + + +// The defaults for the command line options. +constexpr uint DefaultFftSize{65536}; +constexpr bool DefaultEqualize{true}; +constexpr bool DefaultSurface{true}; +constexpr double DefaultLimit{24.0}; +constexpr uint DefaultTruncSize{64}; +constexpr double DefaultCustomRadius{0.0}; /* Channel index enums. Mono uses LeftChannel only. */ enum ChannelIndex : uint { @@ -165,7 +167,7 @@ enum ChannelIndex : uint { * pattern string are replaced with the replacement string. The result is * truncated if necessary. */ -static std::string StrSubst(al::span<const char> in, const al::span<const char> pat, +std::string StrSubst(al::span<const char> in, const al::span<const char> pat, const al::span<const char> rep) { std::string ret; @@ -198,12 +200,12 @@ static std::string StrSubst(al::span<const char> in, const al::span<const char> *********************/ // Simple clamp routine. -static double Clamp(const double val, const double lower, const double upper) +double Clamp(const double val, const double lower, const double upper) { return std::min(std::max(val, lower), upper); } -static inline uint dither_rng(uint *seed) +inline uint dither_rng(uint *seed) { *seed = *seed * 96314165 + 907633515; return *seed; @@ -211,8 +213,8 @@ static inline uint dither_rng(uint *seed) // Performs a triangular probability density function dither. The input samples // should be normalized (-1 to +1). -static void TpdfDither(double *RESTRICT out, const double *RESTRICT in, const double scale, - const uint count, const uint step, uint *seed) +void TpdfDither(double *RESTRICT out, const double *RESTRICT in, const double scale, + const uint count, const uint step, uint *seed) { static constexpr double PRNG_SCALE = 1.0 / std::numeric_limits<uint>::max(); @@ -231,9 +233,11 @@ static void TpdfDither(double *RESTRICT out, const double *RESTRICT in, const do * of a signal's magnitude response, the imaginary components can be used as * the angles for minimum-phase reconstruction. */ -inline static void Hilbert(const uint n, complex_d *inout) +inline void Hilbert(const uint n, complex_d *inout) { complex_hilbert({inout, n}); } +} // namespace + /* Calculate the magnitude response of the given input. This is used in * place of phase decomposition, since the phase residuals are discarded for * minimum phase reconstruction. The mirrored half of the response is also @@ -244,7 +248,7 @@ void MagnitudeResponse(const uint n, const complex_d *in, double *out) const uint m = 1 + (n / 2); uint i; for(i = 0;i < m;i++) - out[i] = std::max(std::abs(in[i]), EPSILON); + out[i] = std::max(std::abs(in[i]), Epsilon); } /* Apply a range limit (in dB) to the given magnitude response. This is used @@ -295,7 +299,7 @@ static void MinimumPhase(const uint n, double *mags, complex_d *out) } Hilbert(n, out); // Remove any DC offset the filter has. - mags[0] = EPSILON; + mags[0] = Epsilon; for(i = 0;i < n;i++) out[i] = std::polar(mags[i], out[i].imag()); } @@ -313,7 +317,6 @@ static int WriteAscii(const char *out, FILE *fp, const char *filename) len = strlen(out); if(fwrite(out, 1, len, fp) != len) { - fclose(fp); fprintf(stderr, "\nError: Bad write to file '%s'.\n", filename); return 0; } @@ -324,13 +327,11 @@ static int WriteAscii(const char *out, FILE *fp, const char *filename) // loading it from a 32-bit unsigned integer. static int WriteBin4(const uint bytes, const uint32_t in, FILE *fp, const char *filename) { - uint8_t out[4]; - uint i; - - for(i = 0;i < bytes;i++) + std::array<uint8_t,4> out{}; + for(uint i{0};i < bytes;i++) out[i] = (in>>(i*8)) & 0x000000FF; - if(fwrite(out, 1, bytes, fp) != bytes) + if(fwrite(out.data(), 1, bytes, fp) != bytes) { fprintf(stderr, "\nError: Bad write to file '%s'.\n", filename); return 0; @@ -345,34 +346,34 @@ static int StoreMhr(const HrirDataT *hData, const char *filename) const uint n{hData->mIrPoints}; uint dither_seed{22222}; uint fi, ei, ai, i; - FILE *fp; - if((fp=fopen(filename, "wb")) == nullptr) + FilePtr fp{fopen(filename, "wb")}; + if(!fp) { fprintf(stderr, "\nError: Could not open MHR file '%s'.\n", filename); return 0; } - if(!WriteAscii(MHR_FORMAT, fp, filename)) + if(!WriteAscii(MHRFormat, fp.get(), filename)) return 0; - if(!WriteBin4(4, hData->mIrRate, fp, filename)) + if(!WriteBin4(4, hData->mIrRate, fp.get(), filename)) return 0; - if(!WriteBin4(1, static_cast<uint32_t>(hData->mChannelType), fp, filename)) + if(!WriteBin4(1, static_cast<uint32_t>(hData->mChannelType), fp.get(), filename)) return 0; - if(!WriteBin4(1, hData->mIrPoints, fp, filename)) + if(!WriteBin4(1, hData->mIrPoints, fp.get(), filename)) return 0; - if(!WriteBin4(1, static_cast<uint>(hData->mFds.size()), fp, filename)) + if(!WriteBin4(1, static_cast<uint>(hData->mFds.size()), fp.get(), filename)) return 0; for(fi = static_cast<uint>(hData->mFds.size()-1);fi < hData->mFds.size();fi--) { auto fdist = static_cast<uint32_t>(std::round(1000.0 * hData->mFds[fi].mDistance)); - if(!WriteBin4(2, fdist, fp, filename)) + if(!WriteBin4(2, fdist, fp.get(), filename)) return 0; - if(!WriteBin4(1, static_cast<uint32_t>(hData->mFds[fi].mEvs.size()), fp, filename)) + if(!WriteBin4(1, static_cast<uint32_t>(hData->mFds[fi].mEvs.size()), fp.get(), filename)) return 0; for(ei = 0;ei < hData->mFds[fi].mEvs.size();ei++) { const auto &elev = hData->mFds[fi].mEvs[ei]; - if(!WriteBin4(1, static_cast<uint32_t>(elev.mAzs.size()), fp, filename)) + if(!WriteBin4(1, static_cast<uint32_t>(elev.mAzs.size()), fp.get(), filename)) return 0; } } @@ -387,15 +388,15 @@ static int StoreMhr(const HrirDataT *hData, const char *filename) for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++) { HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai]; - double out[2 * MAX_TRUNCSIZE]; + std::array<double,MaxTruncSize*2_uz> out{}; - TpdfDither(out, azd->mIrs[0], scale, n, channels, &dither_seed); + TpdfDither(out.data(), azd->mIrs[0], scale, n, channels, &dither_seed); if(hData->mChannelType == CT_STEREO) - TpdfDither(out+1, azd->mIrs[1], scale, n, channels, &dither_seed); + TpdfDither(out.data()+1, azd->mIrs[1], scale, n, channels, &dither_seed); for(i = 0;i < (channels * n);i++) { const auto v = static_cast<int>(Clamp(out[i], -scale-1.0, scale)); - if(!WriteBin4(bps, static_cast<uint32_t>(v), fp, filename)) + if(!WriteBin4(bps, static_cast<uint32_t>(v), fp.get(), filename)) return 0; } } @@ -410,16 +411,15 @@ static int StoreMhr(const HrirDataT *hData, const char *filename) for(const auto &azd : hData->mFds[fi].mEvs[ei].mAzs) { auto v = static_cast<uint>(std::round(azd.mDelays[0]*DelayPrecScale)); - if(!WriteBin4(1, v, fp, filename)) return 0; + if(!WriteBin4(1, v, fp.get(), filename)) return 0; if(hData->mChannelType == CT_STEREO) { v = static_cast<uint>(std::round(azd.mDelays[1]*DelayPrecScale)); - if(!WriteBin4(1, v, fp, filename)) return 0; + if(!WriteBin4(1, v, fp.get(), filename)) return 0; } } } } - fclose(fp); return 1; } @@ -434,21 +434,18 @@ static int StoreMhr(const HrirDataT *hData, const char *filename) */ static void BalanceFieldMagnitudes(const HrirDataT *hData, const uint channels, const uint m) { - double maxMags[MAX_FD_COUNT]; - uint fi, ei, ti, i; - + std::array<double,MAX_FD_COUNT> maxMags{}; double maxMag{0.0}; - for(fi = 0;fi < hData->mFds.size();fi++) - { - maxMags[fi] = 0.0; - for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++) + for(size_t fi{0};fi < hData->mFds.size();++fi) + { + for(size_t ei{hData->mFds[fi].mEvStart};ei < hData->mFds[fi].mEvs.size();++ei) { for(const auto &azd : hData->mFds[fi].mEvs[ei].mAzs) { - for(ti = 0;ti < channels;ti++) + for(size_t ti{0};ti < channels;++ti) { - for(i = 0;i < m;i++) + for(size_t i{0};i < m;++i) maxMags[fi] = std::max(azd.mIrs[ti][i], maxMags[fi]); } } @@ -457,17 +454,17 @@ static void BalanceFieldMagnitudes(const HrirDataT *hData, const uint channels, maxMag = std::max(maxMags[fi], maxMag); } - for(fi = 0;fi < hData->mFds.size();fi++) + for(size_t fi{0};fi < hData->mFds.size();++fi) { const double magFactor{maxMag / maxMags[fi]}; - for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++) + for(size_t ei{hData->mFds[fi].mEvStart};ei < hData->mFds[fi].mEvs.size();++ei) { for(const auto &azd : hData->mFds[fi].mEvs[ei].mAzs) { - for(ti = 0;ti < channels;ti++) + for(size_t ti{0};ti < channels;++ti) { - for(i = 0;i < m;i++) + for(size_t i{0};i < m;++i) azd.mIrs[ti][i] *= magFactor; } } @@ -499,17 +496,17 @@ static void CalculateDfWeights(const HrirDataT *hData, double *weights) outerRa = 10.0f; const double raPowDiff{std::pow(outerRa, 3.0) - std::pow(innerRa, 3.0)}; - evs = M_PI / 2.0 / static_cast<double>(hData->mFds[fi].mEvs.size() - 1); + evs = al::numbers::pi / 2.0 / static_cast<double>(hData->mFds[fi].mEvs.size() - 1); for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++) { const auto &elev = hData->mFds[fi].mEvs[ei]; // For each elevation, calculate the upper and lower limits of // the patch band. ev = elev.mElevation; - lowerEv = std::max(-M_PI / 2.0, ev - evs); - upperEv = std::min(M_PI / 2.0, ev + evs); + lowerEv = std::max(-al::numbers::pi / 2.0, ev - evs); + upperEv = std::min(al::numbers::pi / 2.0, ev + evs); // Calculate the surface area of the patch band. - solidAngle = 2.0 * M_PI * (std::sin(upperEv) - std::sin(lowerEv)); + solidAngle = 2.0 * al::numbers::pi * (std::sin(upperEv) - std::sin(lowerEv)); // Then the volume of the extruded patch band. solidVolume = solidAngle * raPowDiff / 3.0; // Each weight is the volume of one extruded patch. @@ -539,7 +536,7 @@ static void CalculateDiffuseFieldAverage(const HrirDataT *hData, const uint chan const int weighted, const double limit, double *dfa) { std::vector<double> weights(hData->mFds.size() * MAX_EV_COUNT); - uint count, ti, fi, ei, i, ai; + uint count; if(weighted) { @@ -553,42 +550,42 @@ static void CalculateDiffuseFieldAverage(const HrirDataT *hData, const uint chan // If coverage weighting is not used, the weights still need to be // averaged by the number of existing HRIRs. count = hData->mIrCount; - for(fi = 0;fi < hData->mFds.size();fi++) + for(size_t fi{0};fi < hData->mFds.size();++fi) { - for(ei = 0;ei < hData->mFds[fi].mEvStart;ei++) + for(size_t ei{0};ei < hData->mFds[fi].mEvStart;++ei) count -= static_cast<uint>(hData->mFds[fi].mEvs[ei].mAzs.size()); } weight = 1.0 / count; - for(fi = 0;fi < hData->mFds.size();fi++) + for(size_t fi{0};fi < hData->mFds.size();++fi) { - for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++) + for(size_t ei{hData->mFds[fi].mEvStart};ei < hData->mFds[fi].mEvs.size();++ei) weights[(fi * MAX_EV_COUNT) + ei] = weight; } } - for(ti = 0;ti < channels;ti++) + for(size_t ti{0};ti < channels;++ti) { - for(i = 0;i < m;i++) + for(size_t i{0};i < m;++i) dfa[(ti * m) + i] = 0.0; - for(fi = 0;fi < hData->mFds.size();fi++) + for(size_t fi{0};fi < hData->mFds.size();++fi) { - for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++) + for(size_t ei{hData->mFds[fi].mEvStart};ei < hData->mFds[fi].mEvs.size();++ei) { - for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++) + for(size_t ai{0};ai < hData->mFds[fi].mEvs[ei].mAzs.size();++ai) { HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai]; // Get the weight for this HRIR's contribution. double weight = weights[(fi * MAX_EV_COUNT) + ei]; // Add this HRIR's weighted power average to the total. - for(i = 0;i < m;i++) + for(size_t i{0};i < m;++i) dfa[(ti * m) + i] += weight * azd->mIrs[ti][i] * azd->mIrs[ti][i]; } } } // Finish the average calculation and keep it from being too small. - for(i = 0;i < m;i++) - dfa[(ti * m) + i] = std::max(sqrt(dfa[(ti * m) + i]), EPSILON); + for(size_t i{0};i < m;++i) + dfa[(ti * m) + i] = std::max(sqrt(dfa[(ti * m) + i]), Epsilon); // Apply a limit to the magnitude range of the diffuse-field average // if desired. if(limit > 0.0) @@ -600,17 +597,15 @@ static void CalculateDiffuseFieldAverage(const HrirDataT *hData, const uint chan // set using the given average response. static void DiffuseFieldEqualize(const uint channels, const uint m, const double *dfa, const HrirDataT *hData) { - uint ti, fi, ei, i; - - for(fi = 0;fi < hData->mFds.size();fi++) + for(size_t fi{0};fi < hData->mFds.size();++fi) { - for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++) + for(size_t ei{hData->mFds[fi].mEvStart};ei < hData->mFds[fi].mEvs.size();++ei) { for(auto &azd : hData->mFds[fi].mEvs[ei].mAzs) { - for(ti = 0;ti < channels;ti++) + for(size_t ti{0};ti < channels;++ti) { - for(i = 0;i < m;i++) + for(size_t i{0};i < m;++i) azd.mIrs[ti][i] /= dfa[(ti * m) + i]; } } @@ -624,7 +619,8 @@ static void DiffuseFieldEqualize(const uint channels, const uint m, const double */ static void CalcAzIndices(const HrirFdT &field, const uint ei, const double az, uint *a0, uint *a1, double *af) { - double f{(2.0*M_PI + az) * static_cast<double>(field.mEvs[ei].mAzs.size()) / (2.0*M_PI)}; + double f{(2.0*al::numbers::pi + az) * static_cast<double>(field.mEvs[ei].mAzs.size()) / + (2.0*al::numbers::pi)}; const uint i{static_cast<uint>(f) % static_cast<uint>(field.mEvs[ei].mAzs.size())}; f -= std::floor(f); @@ -674,7 +670,7 @@ static void SynthesizeOnsets(HrirDataT *hData) * the mirrored elevation to find the indices for the polar * opposite position (may need blending). */ - const double az{field.mEvs[ei].mAzs[ai].mAzimuth + M_PI}; + const double az{field.mEvs[ei].mAzs[ai].mAzimuth + al::numbers::pi}; CalcAzIndices(field, topElev, az, &a0, &a1, &af); /* Blend the delays, and again, swap the ears. */ @@ -706,8 +702,8 @@ static void SynthesizeOnsets(HrirDataT *hData) * measurement). */ double az{field.mEvs[ei].mAzs[ai].mAzimuth}; - if(az <= M_PI) az = M_PI - az; - else az = (M_PI*2.0)-az + M_PI; + if(az <= al::numbers::pi) az = al::numbers::pi - az; + else az = (al::numbers::pi*2.0)-az + al::numbers::pi; CalcAzIndices(field, topElev, az, &a0, &a1, &af); field.mEvs[ei].mAzs[ai].mDelays[0] = Lerp( @@ -735,12 +731,12 @@ static void SynthesizeOnsets(HrirDataT *hData) double az{field.mEvs[ei].mAzs[ai].mAzimuth}; CalcAzIndices(field, upperElevReal, az, &a0, &a1, &af0); CalcAzIndices(field, lowerElevFake, az, &a2, &a3, &af1); - double blend[4]{ + std::array<double,4> blend{{ (1.0-ef) * (1.0-af0), (1.0-ef) * ( af0), ( ef) * (1.0-af1), ( ef) * ( af1) - }; + }}; for(uint ti{0u};ti < channels;ti++) { @@ -785,7 +781,7 @@ static void SynthesizeHrirs(HrirDataT *hData) * and vice-versa, this produces a decent phantom-center response * underneath the head. */ - CalcAzIndices(field, oi, ((ti==0) ? -M_PI : M_PI) / 2.0, &a0, &a1, &af); + CalcAzIndices(field, oi, al::numbers::pi / ((ti==0) ? -2.0 : 2.0), &a0, &a1, &af); for(uint i{0u};i < m;i++) { field.mEvs[0].mAzs[0].mIrs[ti][i] = Lerp(field.mEvs[oi].mAzs[a0].mIrs[ti][i], @@ -797,7 +793,7 @@ static void SynthesizeHrirs(HrirDataT *hData) { const double of{static_cast<double>(ei) / field.mEvStart}; const double b{(1.0 - of) * beta}; - double lp[4]{}; + std::array<double,4> lp{}; /* Calculate a low-pass filter to simulate body occlusion. */ lp[0] = Lerp(1.0, lp[0], b); @@ -842,7 +838,7 @@ static void SynthesizeHrirs(HrirDataT *hData) } } const double b{beta}; - double lp[4]{}; + std::array<double,4> lp{}; lp[0] = Lerp(1.0, lp[0], b); lp[1] = Lerp(lp[0], lp[1], b); lp[2] = Lerp(lp[1], lp[2], b); @@ -877,10 +873,10 @@ static void SynthesizeHrirs(HrirDataT *hData) */ struct HrirReconstructor { std::vector<double*> mIrs; - std::atomic<size_t> mCurrent; - std::atomic<size_t> mDone; - uint mFftSize; - uint mIrPoints; + std::atomic<size_t> mCurrent{}; + std::atomic<size_t> mDone{}; + uint mFftSize{}; + uint mIrPoints{}; void Worker() { @@ -888,7 +884,7 @@ struct HrirReconstructor { auto mags = std::vector<double>(mFftSize); size_t m{(mFftSize/2) + 1}; - while(1) + while(true) { /* Load the current index to process. */ size_t idx{mCurrent.load()}; @@ -907,7 +903,7 @@ struct HrirReconstructor { * time-domain response. */ for(size_t i{0};i < m;++i) - mags[i] = std::max(mIrs[idx][i], EPSILON); + mags[i] = std::max(mIrs[idx][i], Epsilon); MinimumPhase(mFftSize, mags.data(), h.data()); FftInverse(mFftSize, h.data()); for(uint i{0u};i < mIrPoints;++i) @@ -991,7 +987,7 @@ static void NormalizeHrirs(HrirDataT *hData) return LevelPair{std::max(current.amp, levels.amp), std::max(current.rms, levels.rms)}; }; auto measure_azi = [channels,mesasure_channel](const LevelPair levels, const HrirAzT &azi) - { return std::accumulate(azi.mIrs, azi.mIrs+channels, levels, mesasure_channel); }; + { return std::accumulate(azi.mIrs.begin(), azi.mIrs.begin()+channels, levels, mesasure_channel); }; auto measure_elev = [measure_azi](const LevelPair levels, const HrirEvT &elev) { return std::accumulate(elev.mAzs.cbegin(), elev.mAzs.cend(), levels, measure_azi); }; auto measure_field = [measure_elev](const LevelPair levels, const HrirFdT &field) @@ -1018,7 +1014,7 @@ static void NormalizeHrirs(HrirDataT *hData) auto proc_channel = [irSize,factor](double *ir) { std::transform(ir, ir+irSize, ir, [factor](double s){ return s * factor; }); }; auto proc_azi = [channels,proc_channel](HrirAzT &azi) - { std::for_each(azi.mIrs, azi.mIrs+channels, proc_channel); }; + { std::for_each(azi.mIrs.begin(), azi.mIrs.begin()+channels, proc_channel); }; auto proc_elev = [proc_azi](HrirEvT &elev) { std::for_each(elev.mAzs.begin(), elev.mAzs.end(), proc_azi); }; auto proc1_field = [proc_elev](HrirFdT &field) @@ -1035,7 +1031,7 @@ static double CalcLTD(const double ev, const double az, const double rad, const azp = std::asin(std::cos(ev) * std::sin(az)); dlp = std::sqrt((dist*dist) + (rad*rad) + (2.0*dist*rad*sin(azp))); l = std::sqrt((dist*dist) - (rad*rad)); - al = (0.5 * M_PI) + azp; + al = (0.5 * al::numbers::pi) + azp; if(dlp > l) dlp = l + (rad * (al - std::acos(rad / dist))); return dlp / 343.3; @@ -1103,10 +1099,10 @@ static void CalculateHrtds(const HeadModelT model, const double radius, HrirData } } } - if(maxHrtd > MAX_HRTD) + if(maxHrtd > MaxHrtd) { - fprintf(stdout, " Scaling for max delay of %f samples to %f\n...\n", maxHrtd, MAX_HRTD); - const double scale{MAX_HRTD / maxHrtd}; + fprintf(stdout, " Scaling for max delay of %f samples to %f\n...\n", maxHrtd, MaxHrtd); + const double scale{MaxHrtd / maxHrtd}; for(auto &field : hData->mFds) { for(auto &elev : field.mEvs) @@ -1153,11 +1149,12 @@ bool PrepareHrirData(const al::span<const double> distances, { uint azCount = azCounts[fi][ei]; - hData->mFds[fi].mEvs[ei].mElevation = -M_PI / 2.0 + M_PI * ei / (evCounts[fi] - 1); + hData->mFds[fi].mEvs[ei].mElevation = -al::numbers::pi / 2.0 + al::numbers::pi * ei / + (evCounts[fi] - 1); hData->mFds[fi].mEvs[ei].mAzs = {&hData->mAzsBase[azTotal], azCount}; for(uint ai{0};ai < azCount;ai++) { - hData->mFds[fi].mEvs[ei].mAzs[ai].mAzimuth = 2.0 * M_PI * ai / azCount; + hData->mFds[fi].mEvs[ei].mAzs[ai].mAzimuth = 2.0 * al::numbers::pi * ai / azCount; hData->mFds[fi].mEvs[ei].mAzs[ai].mIndex = azTotal + ai; hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[0] = 0.0; hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[1] = 0.0; @@ -1199,10 +1196,10 @@ static int ProcessDefinition(const char *inName, const uint outRate, const Chann return 0; } - char startbytes[4]{}; - input->read(startbytes, sizeof(startbytes)); + std::array<char,4> startbytes{}; + input->read(startbytes.data(), startbytes.size()); std::streamsize startbytecount{input->gcount()}; - if(startbytecount != sizeof(startbytes) || !input->good()) + if(startbytecount != startbytes.size() || !input->good()) { fprintf(stderr, "Error: Could not read input file '%s'\n", inName); return 0; @@ -1219,7 +1216,8 @@ static int ProcessDefinition(const char *inName, const uint outRate, const Chann else { fprintf(stdout, "Reading HRIR definition from %s...\n", inName); - if(!LoadDefInput(*input, startbytes, startbytecount, inName, fftSize, truncSize, outRate, chanMode, &hData)) + if(!LoadDefInput(*input, startbytes.data(), startbytecount, inName, fftSize, truncSize, + outRate, chanMode, &hData)) return 0; } } @@ -1228,7 +1226,7 @@ static int ProcessDefinition(const char *inName, const uint outRate, const Chann { uint c{(hData.mChannelType == CT_STEREO) ? 2u : 1u}; uint m{hData.mFftSize/2u + 1u}; - auto dfa = std::vector<double>(c * m); + auto dfa = std::vector<double>(size_t{c} * m); if(hData.mFds.size() > 1) { @@ -1264,7 +1262,7 @@ static int ProcessDefinition(const char *inName, const uint outRate, const Chann fprintf(stdout, "Normalizing final HRIRs...\n"); NormalizeHrirs(&hData); fprintf(stdout, "Calculating impulse delays...\n"); - CalculateHrtds(model, (radius > DEFAULT_CUSTOM_RADIUS) ? radius : hData.mRadius, &hData); + CalculateHrtds(model, (radius > DefaultCustomRadius) ? radius : hData.mRadius, &hData); const auto rateStr = std::to_string(hData.mIrRate); const auto expName = StrSubst({outName, strlen(outName)}, {"%r", 2}, @@ -1283,13 +1281,13 @@ static void PrintHelp(const char *argv0, FILE *ofile) fprintf(ofile, " right ear.\n"); fprintf(ofile, " -a Change the data set to single field, using the farthest field.\n"); fprintf(ofile, " -j <threads> Number of threads used to process HRIRs (default: 2).\n"); - fprintf(ofile, " -f <points> Override the FFT window size (default: %u).\n", DEFAULT_FFTSIZE); - fprintf(ofile, " -e {on|off} Toggle diffuse-field equalization (default: %s).\n", (DEFAULT_EQUALIZE ? "on" : "off")); - fprintf(ofile, " -s {on|off} Toggle surface-weighted diffuse-field average (default: %s).\n", (DEFAULT_SURFACE ? "on" : "off")); + fprintf(ofile, " -f <points> Override the FFT window size (default: %u).\n", DefaultFftSize); + fprintf(ofile, " -e {on|off} Toggle diffuse-field equalization (default: %s).\n", (DefaultEqualize ? "on" : "off")); + fprintf(ofile, " -s {on|off} Toggle surface-weighted diffuse-field average (default: %s).\n", (DefaultSurface ? "on" : "off")); fprintf(ofile, " -l {<dB>|none} Specify a limit to the magnitude range of the diffuse-field\n"); - fprintf(ofile, " average (default: %.2f).\n", DEFAULT_LIMIT); + fprintf(ofile, " average (default: %.2f).\n", DefaultLimit); fprintf(ofile, " -w <points> Specify the size of the truncation window that's applied\n"); - fprintf(ofile, " after minimum-phase reconstruction (default: %u).\n", DEFAULT_TRUNCSIZE); + fprintf(ofile, " after minimum-phase reconstruction (default: %u).\n", DefaultTruncSize); fprintf(ofile, " -d {dataset| Specify the model used for calculating the head-delay timing\n"); fprintf(ofile, " sphere} values (default: %s).\n", ((DEFAULT_HEAD_MODEL == HM_DATASET) ? "dataset" : "sphere")); fprintf(ofile, " -c <radius> Use a customized head radius measured to-ear in meters.\n"); @@ -1324,14 +1322,14 @@ int main(int argc, char *argv[]) outName = "./oalsoft_hrtf_%r.mhr"; outRate = 0; chanMode = CM_AllowStereo; - fftSize = DEFAULT_FFTSIZE; - equalize = DEFAULT_EQUALIZE; - surface = DEFAULT_SURFACE; - limit = DEFAULT_LIMIT; + fftSize = DefaultFftSize; + equalize = DefaultEqualize; + surface = DefaultSurface; + limit = DefaultLimit; numThreads = 2; - truncSize = DEFAULT_TRUNCSIZE; + truncSize = DefaultTruncSize; model = DEFAULT_HEAD_MODEL; - radius = DEFAULT_CUSTOM_RADIUS; + radius = DefaultCustomRadius; farfield = false; while((opt=getopt(argc, argv, "r:maj:f:e:s:l:w:d:c:e:i:o:h")) != -1) @@ -1368,9 +1366,9 @@ int main(int argc, char *argv[]) case 'f': fftSize = static_cast<uint>(strtoul(optarg, &end, 10)); - if(end[0] != '\0' || (fftSize&(fftSize-1)) || fftSize < MIN_FFTSIZE || fftSize > MAX_FFTSIZE) + if(end[0] != '\0' || (fftSize&(fftSize-1)) || fftSize < MinFftSize || fftSize > MaxFftSize) { - fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected a power-of-two between %u to %u.\n", optarg, opt, MIN_FFTSIZE, MAX_FFTSIZE); + fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected a power-of-two between %u to %u.\n", optarg, opt, MinFftSize, MaxFftSize); exit(EXIT_FAILURE); } break; @@ -1405,9 +1403,9 @@ int main(int argc, char *argv[]) else { limit = strtod(optarg, &end); - if(end[0] != '\0' || limit < MIN_LIMIT || limit > MAX_LIMIT) + if(end[0] != '\0' || limit < MinLimit || limit > MaxLimit) { - fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %.0f to %.0f.\n", optarg, opt, MIN_LIMIT, MAX_LIMIT); + fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %.0f to %.0f.\n", optarg, opt, MinLimit, MaxLimit); exit(EXIT_FAILURE); } } @@ -1415,9 +1413,9 @@ int main(int argc, char *argv[]) case 'w': truncSize = static_cast<uint>(strtoul(optarg, &end, 10)); - if(end[0] != '\0' || truncSize < MIN_TRUNCSIZE || truncSize > MAX_TRUNCSIZE) + if(end[0] != '\0' || truncSize < MinTruncSize || truncSize > MaxTruncSize) { - fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %u to %u.\n", optarg, opt, MIN_TRUNCSIZE, MAX_TRUNCSIZE); + fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %u to %u.\n", optarg, opt, MinTruncSize, MaxTruncSize); exit(EXIT_FAILURE); } break; @@ -1436,9 +1434,9 @@ int main(int argc, char *argv[]) case 'c': radius = strtod(optarg, &end); - if(end[0] != '\0' || radius < MIN_CUSTOM_RADIUS || radius > MAX_CUSTOM_RADIUS) + if(end[0] != '\0' || radius < MinCustomRadius || radius > MaxCustomRadius) { - fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %.2f to %.2f.\n", optarg, opt, MIN_CUSTOM_RADIUS, MAX_CUSTOM_RADIUS); + fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %.2f to %.2f.\n", optarg, opt, MinCustomRadius, MaxCustomRadius); exit(EXIT_FAILURE); } break; diff --git a/utils/makemhr/makemhr.h b/utils/makemhr/makemhr.h index aa18134d..71c2e55b 100644 --- a/utils/makemhr/makemhr.h +++ b/utils/makemhr/makemhr.h @@ -9,35 +9,43 @@ // The maximum path length used when processing filenames. -#define MAX_PATH_LEN (256) +enum { MAX_PATH_LEN = 256u }; // The limit to the number of 'distances' listed in the data set definition. // Must be less than 256 -#define MAX_FD_COUNT (16) +enum { MAX_FD_COUNT = 16u }; // The limits to the number of 'elevations' listed in the data set definition. // Must be less than 256. -#define MIN_EV_COUNT (5) -#define MAX_EV_COUNT (181) +enum { + MIN_EV_COUNT = 5u, + MAX_EV_COUNT = 181u +}; // The limits for each of the 'azimuths' listed in the data set definition. // Must be less than 256. -#define MIN_AZ_COUNT (1) -#define MAX_AZ_COUNT (255) +enum { + MIN_AZ_COUNT = 1u, + MAX_AZ_COUNT = 255u +}; // The limits for the 'distance' from source to listener for each field in // the definition file. -#define MIN_DISTANCE (0.05) -#define MAX_DISTANCE (2.50) +inline constexpr double MIN_DISTANCE{0.05}; +inline constexpr double MAX_DISTANCE{2.50}; // The limits for the sample 'rate' metric in the data set definition and for // resampling. -#define MIN_RATE (32000) -#define MAX_RATE (96000) +enum { + MIN_RATE = 32000u, + MAX_RATE = 96000u +}; // The limits for the HRIR 'points' metric in the data set definition. -#define MIN_POINTS (16) -#define MAX_POINTS (8192) +enum { + MIN_POINTS = 16u, + MAX_POINTS = 8192u +}; using uint = unsigned int; @@ -68,8 +76,8 @@ enum ChannelTypeT { struct HrirAzT { double mAzimuth{0.0}; uint mIndex{0u}; - double mDelays[2]{0.0, 0.0}; - double *mIrs[2]{nullptr, nullptr}; + std::array<double,2> mDelays{}; + std::array<double*,2> mIrs{}; }; struct HrirEvT { diff --git a/utils/openal-info.c b/utils/openal-info.c index 8ef6ebde..669727a6 100644 --- a/utils/openal-info.c +++ b/utils/openal-info.c @@ -45,11 +45,11 @@ #define FUNCTION_CAST(T, ptr) (T)(ptr) #endif -#define MAX_WIDTH 80 +enum { MaxWidth = 80 }; static void printList(const char *list, char separator) { - size_t col = MAX_WIDTH, len; + size_t col = MaxWidth, len; const char *indent = " "; const char *next; @@ -71,7 +71,7 @@ static void printList(const char *list, char separator) else len = strlen(list); - if(len + col + 2 >= MAX_WIDTH) + if(len + col + 2 >= MaxWidth) { fprintf(stdout, "\n%s", indent); col = strlen(indent); @@ -181,6 +181,14 @@ static void printHRTFInfo(ALCdevice *device) checkALCErrors(device); } +static void printALCIntegerValue(ALCdevice *device, ALCenum enumValue, char* enumName) +{ + ALCint value; + alcGetIntegerv(device, enumValue, 1, &value); + if (checkALCErrors(device) == ALC_NO_ERROR) + printf("%s: %d\n", enumName, value); +} + static void printModeInfo(ALCdevice *device) { ALCint srate = 0; @@ -228,6 +236,9 @@ static void printModeInfo(ALCdevice *device) } printf("Device HRTF profile: %s\n", hrtfname ? hrtfname : "<null>"); } + + printALCIntegerValue(device, ALC_MONO_SOURCES, "Device number of mono sources"); + printALCIntegerValue(device, ALC_STEREO_SOURCES, "Device number of stereo sources"); } static void printALCSOFTSystemEventIsSupportedResult(LPALCEVENTISSUPPORTEDSOFT alcEventIsSupportedSOFT, ALCenum eventType, ALCenum deviceType) @@ -382,7 +393,7 @@ static void printEFXInfo(ALCdevice *device) palFilteri(object, AL_FILTER_TYPE, filters[i]); if(alGetError() != AL_NO_ERROR) - memmove(current, next+1, strlen(next)); + memmove(current, next+1, strlen(next)); /* NOLINT(clang-analyzer-security.insecureAPI.*) */ else current = next+1; } @@ -401,7 +412,7 @@ static void printEFXInfo(ALCdevice *device) palEffecti(object, AL_EFFECT_TYPE, effects[i]); if(alGetError() != AL_NO_ERROR) - memmove(current, next+1, strlen(next)); + memmove(current, next+1, strlen(next)); /* NOLINT(clang-analyzer-security.insecureAPI.*) */ else current = next+1; } @@ -414,7 +425,7 @@ static void printEFXInfo(ALCdevice *device) palEffecti(object, AL_EFFECT_TYPE, dedeffects[i]); if(alGetError() != AL_NO_ERROR) - memmove(current, next+1, strlen(next)); + memmove(current, next+1, strlen(next)); /* NOLINT(clang-analyzer-security.insecureAPI.*) */ else current = next+1; } @@ -425,7 +436,7 @@ static void printEFXInfo(ALCdevice *device) { char *next = strchr(current, ','); assert(next != NULL); - memmove(current, next+1, strlen(next)); + memmove(current, next+1, strlen(next)); /* NOLINT(clang-analyzer-security.insecureAPI.*) */ } } printf("Supported effects:"); diff --git a/utils/sofa-info.cpp b/utils/sofa-info.cpp index 6dffef44..7775b8e3 100644 --- a/utils/sofa-info.cpp +++ b/utils/sofa-info.cpp @@ -21,8 +21,7 @@ * Or visit: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ -#include <stdio.h> - +#include <cstdio> #include <memory> #include <vector> diff --git a/utils/sofa-support.cpp b/utils/sofa-support.cpp index e37789d5..ceb3067a 100644 --- a/utils/sofa-support.cpp +++ b/utils/sofa-support.cpp @@ -24,11 +24,11 @@ #include "sofa-support.h" -#include <stdio.h> #include <algorithm> #include <array> #include <cmath> +#include <cstdio> #include <utility> #include <vector> @@ -47,7 +47,7 @@ using double3 = std::array<double,3>; * equality of unique elements. */ std::vector<double> GetUniquelySortedElems(const std::vector<double3> &aers, const uint axis, - const double *const (&filters)[3], const double (&epsilons)[3]) + const std::array<const double*,3> filters, const std::array<double,3> epsilons) { std::vector<double> elems; for(const double3 &aer : aers) @@ -183,8 +183,8 @@ std::vector<SofaField> GetCompatibleLayout(const size_t m, const float *xyzs) auto aers = std::vector<double3>(m, double3{}); for(size_t i{0u};i < m;++i) { - float vals[3]{xyzs[i*3], xyzs[i*3 + 1], xyzs[i*3 + 2]}; - mysofa_c2s(&vals[0]); + std::array vals{xyzs[i*3], xyzs[i*3 + 1], xyzs[i*3 + 2]}; + mysofa_c2s(vals.data()); aers[i] = {vals[0], vals[1], vals[2]}; } diff --git a/utils/uhjdecoder.cpp b/utils/uhjdecoder.cpp index c7efa376..970d9f82 100644 --- a/utils/uhjdecoder.cpp +++ b/utils/uhjdecoder.cpp @@ -48,7 +48,7 @@ struct FileDeleter { - void operator()(FILE *file) { fclose(file); } + void operator()(gsl::owner<FILE*> file) { fclose(file); } }; using FilePtr = std::unique_ptr<FILE,FileDeleter>; @@ -66,22 +66,22 @@ using complex_d = std::complex<double>; using byte4 = std::array<std::byte,4>; -constexpr ubyte SUBTYPE_BFORMAT_FLOAT[]{ +constexpr std::array<ubyte,16> SUBTYPE_BFORMAT_FLOAT{ 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00 }; void fwrite16le(ushort val, FILE *f) { - ubyte data[2]{ static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff) }; - fwrite(data, 1, 2, f); + std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff)}; + fwrite(data.data(), 1, data.size(), f); } void fwrite32le(uint val, FILE *f) { - ubyte data[4]{ static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff), - static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff) }; - fwrite(data, 1, 4, f); + std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff), + static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff)}; + fwrite(data.data(), 1, data.size(), f); } template<al::endian = al::endian::native> @@ -129,8 +129,6 @@ struct UhjDecoder { const al::span<FloatBufferLine> OutSamples, const std::size_t SamplesToDo); void decode2(const float *RESTRICT InSamples, const al::span<FloatBufferLine> OutSamples, const std::size_t SamplesToDo); - - DEF_NEWDEL(UhjDecoder) }; const PhaseShifterT<UhjDecoder::sFilterDelay*2> PShift{}; @@ -389,7 +387,7 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to open %s\n", argv[fidx]); continue; } - if(sf_command(infile.get(), SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT) + if(sf_command(infile.get(), SFC_WAVEX_GET_AMBISONIC, nullptr, 0) == SF_AMBISONIC_B_FORMAT) { fprintf(stderr, "%s is already B-Format\n", argv[fidx]); continue; @@ -438,7 +436,7 @@ int main(int argc, char **argv) // 32-bit val, frequency fwrite32le(static_cast<uint>(ininfo.samplerate), outfile.get()); // 32-bit val, bytes per second - fwrite32le(static_cast<uint>(ininfo.samplerate)*sizeof(float)*outchans, outfile.get()); + fwrite32le(static_cast<uint>(ininfo.samplerate)*outchans*uint{sizeof(float)}, outfile.get()); // 16-bit val, frame size fwrite16le(static_cast<ushort>(sizeof(float)*outchans), outfile.get()); // 16-bit val, bits per sample @@ -450,7 +448,7 @@ int main(int argc, char **argv) // 32-bit val, channel mask fwrite32le(0, outfile.get()); // 16 byte GUID, sub-type format - fwrite(SUBTYPE_BFORMAT_FLOAT, 1, 16, outfile.get()); + fwrite(SUBTYPE_BFORMAT_FLOAT.data(), 1, SUBTYPE_BFORMAT_FLOAT.size(), outfile.get()); fputs("data", outfile.get()); fwrite32le(0xFFFFFFFF, outfile.get()); // 'data' header len; filled in at close @@ -463,9 +461,9 @@ int main(int argc, char **argv) auto DataStart = ftell(outfile.get()); auto decoder = std::make_unique<UhjDecoder>(); - auto inmem = std::make_unique<float[]>(BufferLineSize*static_cast<uint>(ininfo.channels)); + auto inmem = std::vector<float>(size_t{BufferLineSize}*static_cast<uint>(ininfo.channels)); auto decmem = al::vector<std::array<float,BufferLineSize>, 16>(outchans); - auto outmem = std::make_unique<byte4[]>(BufferLineSize*outchans); + auto outmem = std::vector<byte4>(size_t{BufferLineSize}*outchans); /* A number of initial samples need to be skipped to cut the lead-in * from the all-pass filter delay. The same number of samples need to @@ -476,21 +474,21 @@ int main(int argc, char **argv) sf_count_t LeadOut{UhjDecoder::sFilterDelay}; while(LeadOut > 0) { - sf_count_t sgot{sf_readf_float(infile.get(), inmem.get(), BufferLineSize)}; + sf_count_t sgot{sf_readf_float(infile.get(), inmem.data(), BufferLineSize)}; sgot = std::max<sf_count_t>(sgot, 0); if(sgot < BufferLineSize) { const sf_count_t remaining{std::min(BufferLineSize - sgot, LeadOut)}; - std::fill_n(inmem.get() + sgot*ininfo.channels, remaining*ininfo.channels, 0.0f); + std::fill_n(inmem.data() + sgot*ininfo.channels, remaining*ininfo.channels, 0.0f); sgot += remaining; LeadOut -= remaining; } auto got = static_cast<std::size_t>(sgot); if(ininfo.channels > 2 || use_general) - decoder->decode(inmem.get(), static_cast<uint>(ininfo.channels), decmem, got); + decoder->decode(inmem.data(), static_cast<uint>(ininfo.channels), decmem, got); else - decoder->decode2(inmem.get(), decmem, got); + decoder->decode2(inmem.data(), decmem, got); if(LeadIn >= got) { LeadIn -= got; @@ -507,7 +505,7 @@ int main(int argc, char **argv) } LeadIn = 0; - std::size_t wrote{fwrite(outmem.get(), sizeof(byte4)*outchans, got, outfile.get())}; + std::size_t wrote{fwrite(outmem.data(), sizeof(byte4)*outchans, got, outfile.get())}; if(wrote < got) { fprintf(stderr, "Error writing wave data: %s (%d)\n", strerror(errno), errno); diff --git a/utils/uhjencoder.cpp b/utils/uhjencoder.cpp index 154a1155..02836181 100644 --- a/utils/uhjencoder.cpp +++ b/utils/uhjencoder.cpp @@ -26,14 +26,13 @@ #include <array> #include <cinttypes> +#include <cstddef> #include <cstring> #include <memory> -#include <stddef.h> #include <string> #include <utility> #include <vector> -#include "almalloc.h" #include "alnumbers.h" #include "alspan.h" #include "opthelpers.h" @@ -82,8 +81,6 @@ struct UhjEncoder { void encode(const al::span<FloatBufferLine> OutSamples, const al::span<FloatBufferLine,4> InSamples, const size_t SamplesToDo); - - DEF_NEWDEL(UhjEncoder) }; const PhaseShifterT<UhjEncoder::sFilterDelay*2> PShift{}; @@ -179,50 +176,55 @@ struct SpeakerPos { }; /* Azimuth is counter-clockwise. */ -constexpr SpeakerPos StereoMap[2]{ - { SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f }, - { SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f }, -}, QuadMap[4]{ - { SF_CHANNEL_MAP_LEFT, 45.0f, 0.0f }, - { SF_CHANNEL_MAP_RIGHT, -45.0f, 0.0f }, - { SF_CHANNEL_MAP_REAR_LEFT, 135.0f, 0.0f }, - { SF_CHANNEL_MAP_REAR_RIGHT, -135.0f, 0.0f }, -}, X51Map[6]{ - { SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f }, - { SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f }, - { SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_SIDE_LEFT, 110.0f, 0.0f }, - { SF_CHANNEL_MAP_SIDE_RIGHT, -110.0f, 0.0f }, -}, X51RearMap[6]{ - { SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f }, - { SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f }, - { SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_REAR_LEFT, 110.0f, 0.0f }, - { SF_CHANNEL_MAP_REAR_RIGHT, -110.0f, 0.0f }, -}, X71Map[8]{ - { SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f }, - { SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f }, - { SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_REAR_LEFT, 150.0f, 0.0f }, - { SF_CHANNEL_MAP_REAR_RIGHT, -150.0f, 0.0f }, - { SF_CHANNEL_MAP_SIDE_LEFT, 90.0f, 0.0f }, - { SF_CHANNEL_MAP_SIDE_RIGHT, -90.0f, 0.0f }, -}, X714Map[12]{ - { SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f }, - { SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f }, - { SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_REAR_LEFT, 150.0f, 0.0f }, - { SF_CHANNEL_MAP_REAR_RIGHT, -150.0f, 0.0f }, - { SF_CHANNEL_MAP_SIDE_LEFT, 90.0f, 0.0f }, - { SF_CHANNEL_MAP_SIDE_RIGHT, -90.0f, 0.0f }, - { SF_CHANNEL_MAP_TOP_FRONT_LEFT, 45.0f, 35.0f }, - { SF_CHANNEL_MAP_TOP_FRONT_RIGHT, -45.0f, 35.0f }, - { SF_CHANNEL_MAP_TOP_REAR_LEFT, 135.0f, 35.0f }, - { SF_CHANNEL_MAP_TOP_REAR_RIGHT, -135.0f, 35.0f }, +constexpr std::array StereoMap{ + SpeakerPos{SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f}, +}; +constexpr std::array QuadMap{ + SpeakerPos{SF_CHANNEL_MAP_LEFT, 45.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_RIGHT, -45.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_REAR_LEFT, 135.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_REAR_RIGHT, -135.0f, 0.0f}, +}; +constexpr std::array X51Map{ + SpeakerPos{SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_LFE, 0.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_SIDE_LEFT, 110.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_SIDE_RIGHT, -110.0f, 0.0f}, +}; +constexpr std::array X51RearMap{ + SpeakerPos{SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_LFE, 0.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_REAR_LEFT, 110.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_REAR_RIGHT, -110.0f, 0.0f}, +}; +constexpr std::array X71Map{ + SpeakerPos{SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_LFE, 0.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_REAR_LEFT, 150.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_REAR_RIGHT, -150.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_SIDE_LEFT, 90.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_SIDE_RIGHT, -90.0f, 0.0f}, +}; +constexpr std::array X714Map{ + SpeakerPos{SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_LFE, 0.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_REAR_LEFT, 150.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_REAR_RIGHT, -150.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_SIDE_LEFT, 90.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_SIDE_RIGHT, -90.0f, 0.0f}, + SpeakerPos{SF_CHANNEL_MAP_TOP_FRONT_LEFT, 45.0f, 35.0f}, + SpeakerPos{SF_CHANNEL_MAP_TOP_FRONT_RIGHT, -45.0f, 35.0f}, + SpeakerPos{SF_CHANNEL_MAP_TOP_REAR_LEFT, 135.0f, 35.0f}, + SpeakerPos{SF_CHANNEL_MAP_TOP_REAR_RIGHT, -135.0f, 35.0f}, }; constexpr auto GenCoeffs(double x /*+front*/, double y /*+left*/, double z /*+up*/) noexcept @@ -413,11 +415,11 @@ int main(int argc, char **argv) } auto encoder = std::make_unique<UhjEncoder>(); - auto splbuf = al::vector<FloatBufferLine, 16>(static_cast<uint>(9+ininfo.channels)+uhjchans); - auto ambmem = al::span<FloatBufferLine,4>{splbuf.data(), 4}; - auto encmem = al::span<FloatBufferLine,4>{&splbuf[4], 4}; - auto srcmem = al::span<float,BufferLineSize>{splbuf[8].data(), BufferLineSize}; - auto outmem = al::span<float>{splbuf[9].data(), BufferLineSize*uhjchans}; + auto splbuf = al::vector<FloatBufferLine, 16>(static_cast<uint>(ininfo.channels)+9+size_t{uhjchans}); + auto ambmem = al::span{splbuf}.subspan<0,4>(); + auto encmem = al::span{splbuf}.subspan<4,4>(); + auto srcmem = al::span{splbuf[8]}; + auto outmem = al::span<float>{splbuf[9].data(), size_t{BufferLineSize}*uhjchans}; /* A number of initial samples need to be skipped to cut the lead-in * from the all-pass filter delay. The same number of samples need to |