diff options
author | Chris Robinson <[email protected]> | 2021-04-27 08:26:42 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2021-04-27 08:26:42 -0700 |
commit | ff380298e4086490584707b8ffde44c5ad64830f (patch) | |
tree | 313288fbfdc2ea7824508e85d264966db6078368 /alc | |
parent | 99157f149f180cfcc2e4be6a3d2a54843411e87a (diff) |
Move BufferStorage and Voice to core
Diffstat (limited to 'alc')
-rw-r--r-- | alc/alc.cpp | 2 | ||||
-rw-r--r-- | alc/alu.cpp | 4 | ||||
-rw-r--r-- | alc/buffer_storage.cpp | 41 | ||||
-rw-r--r-- | alc/buffer_storage.h | 75 | ||||
-rw-r--r-- | alc/effects/convolution.cpp | 2 | ||||
-rw-r--r-- | alc/voice.cpp | 869 | ||||
-rw-r--r-- | alc/voice.h | 271 |
7 files changed, 4 insertions, 1260 deletions
diff --git a/alc/alc.cpp b/alc/alc.cpp index 858fe278..1f7b999d 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -1131,7 +1131,7 @@ void alc_initconfig(void) AllowRTTimeLimit = *limopt; aluInit(); - aluInitMixer(ConfigValueStr(nullptr, nullptr, "resampler")); + Voice::InitMixer(ConfigValueStr(nullptr, nullptr, "resampler")); auto traperr = al::getenv("ALSOFT_TRAP_ERROR"); if(traperr && (al::strcasecmp(traperr->c_str(), "true") == 0 diff --git a/alc/alu.cpp b/alc/alu.cpp index 250d4ee4..d370aba7 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -44,7 +44,6 @@ #include "alspan.h" #include "alstring.h" #include "atomic.h" -#include "buffer_storage.h" #include "core/ambidefs.h" #include "core/async_event.h" #include "core/bformatdec.h" @@ -52,6 +51,7 @@ #include "core/bsinc_defs.h" #include "core/bsinc_tables.h" #include "core/bufferline.h" +#include "core/buffer_storage.h" #include "core/context.h" #include "core/cpu_caps.h" #include "core/devformat.h" @@ -66,6 +66,7 @@ #include "core/mixer/hrtfdefs.h" #include "core/resampler_limits.h" #include "core/uhjfilter.h" +#include "core/voice.h" #include "core/voice_change.h" #include "effects/base.h" #include "effectslot.h" @@ -77,7 +78,6 @@ #include "threads.h" #include "vecmat.h" #include "vector.h" -#include "voice.h" struct CTag; #ifdef HAVE_SSE diff --git a/alc/buffer_storage.cpp b/alc/buffer_storage.cpp deleted file mode 100644 index 3eb1e28a..00000000 --- a/alc/buffer_storage.cpp +++ /dev/null @@ -1,41 +0,0 @@ - -#include "config.h" - -#include "buffer_storage.h" - -#include <cstdint> - - -uint BytesFromFmt(FmtType type) noexcept -{ - switch(type) - { - case FmtUByte: return sizeof(uint8_t); - case FmtShort: return sizeof(int16_t); - case FmtFloat: return sizeof(float); - case FmtDouble: return sizeof(double); - case FmtMulaw: return sizeof(uint8_t); - case FmtAlaw: return sizeof(uint8_t); - } - return 0; -} - -uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept -{ - switch(chans) - { - case FmtMono: return 1; - case FmtStereo: return 2; - case FmtRear: return 2; - case FmtQuad: return 4; - case FmtX51: return 6; - case FmtX61: return 7; - case FmtX71: return 8; - case FmtBFormat2D: return (ambiorder*2) + 1; - case FmtBFormat3D: return (ambiorder+1) * (ambiorder+1); - case FmtUHJ2: return 2; - case FmtUHJ3: return 3; - case FmtUHJ4: return 4; - } - return 0; -} diff --git a/alc/buffer_storage.h b/alc/buffer_storage.h deleted file mode 100644 index 733a62e3..00000000 --- a/alc/buffer_storage.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef ALC_BUFFER_STORAGE_H -#define ALC_BUFFER_STORAGE_H - -#include <atomic> - -#include "albyte.h" - - -using uint = unsigned int; - -/* Storable formats */ -enum FmtType : unsigned char { - FmtUByte, - FmtShort, - FmtFloat, - FmtDouble, - FmtMulaw, - FmtAlaw, -}; -enum FmtChannels : unsigned char { - FmtMono, - FmtStereo, - FmtRear, - FmtQuad, - FmtX51, /* (WFX order) */ - FmtX61, /* (WFX order) */ - FmtX71, /* (WFX order) */ - FmtBFormat2D, - FmtBFormat3D, - FmtUHJ2, /* 2-channel UHJ, aka "BHJ", stereo-compatible */ - FmtUHJ3, /* 3-channel UHJ, aka "THJ", first-two channels are stereo-compatible */ - FmtUHJ4, /* 4-channel UHJ, aka "PHJ", first-two channels are stereo-compatible */ -}; - -enum class AmbiLayout : unsigned char { - FuMa, - ACN, -}; -enum class AmbiScaling : unsigned char { - FuMa, - SN3D, - N3D, -}; - -uint BytesFromFmt(FmtType type) noexcept; -uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept; -inline uint FrameSizeFromFmt(FmtChannels chans, FmtType type, uint ambiorder) noexcept -{ return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } - - -using CallbackType = int(*)(void*, void*, int); - -struct BufferStorage { - CallbackType mCallback{nullptr}; - void *mUserData{nullptr}; - - uint mSampleRate{0u}; - FmtChannels mChannels{FmtMono}; - FmtType mType{FmtShort}; - uint mSampleLen{0u}; - - AmbiLayout mAmbiLayout{AmbiLayout::FuMa}; - AmbiScaling mAmbiScaling{AmbiScaling::FuMa}; - uint mAmbiOrder{0u}; - - inline uint bytesFromFmt() const noexcept { return BytesFromFmt(mType); } - inline uint channelsFromFmt() const noexcept - { return ChannelsFromFmt(mChannels, mAmbiOrder); } - inline uint frameSizeFromFmt() const noexcept { return channelsFromFmt() * bytesFromFmt(); } - - inline bool isBFormat() const noexcept - { return mChannels == FmtBFormat2D || mChannels == FmtBFormat3D; } -}; - -#endif /* ALC_BUFFER_STORAGE_H */ diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 90907a40..b460904b 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -23,10 +23,10 @@ #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" -#include "buffer_storage.h" #include "config.h" #include "core/ambidefs.h" #include "core/bufferline.h" +#include "core/buffer_storage.h" #include "core/devformat.h" #include "core/device.h" #include "core/filters/splitter.h" diff --git a/alc/voice.cpp b/alc/voice.cpp deleted file mode 100644 index 686f4c9b..00000000 --- a/alc/voice.cpp +++ /dev/null @@ -1,869 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 by authors. - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * Or go to http://www.gnu.org/copyleft/lgpl.html - */ - -#include "config.h" - -#include "voice.h" - -#include <algorithm> -#include <array> -#include <atomic> -#include <cassert> -#include <cstdint> -#include <iterator> -#include <memory> -#include <new> -#include <stdlib.h> -#include <utility> -#include <vector> - -#include "albyte.h" -#include "alnumeric.h" -#include "aloptional.h" -#include "alspan.h" -#include "alstring.h" -#include "buffer_storage.h" -#include "core/ambidefs.h" -#include "core/async_event.h" -#include "core/context.h" -#include "core/cpu_caps.h" -#include "core/devformat.h" -#include "core/device.h" -#include "core/filters/biquad.h" -#include "core/filters/nfc.h" -#include "core/filters/splitter.h" -#include "core/fmt_traits.h" -#include "core/logging.h" -#include "core/mixer.h" -#include "core/mixer/defs.h" -#include "core/mixer/hrtfdefs.h" -#include "core/resampler_limits.h" -#include "core/voice_change.h" -#include "opthelpers.h" -#include "ringbuffer.h" -#include "vector.h" - -struct CTag; -#ifdef HAVE_SSE -struct SSETag; -#endif -#ifdef HAVE_NEON -struct NEONTag; -#endif -struct CopyTag; - - -static_assert(!(sizeof(Voice::BufferLine)&15), "Voice::BufferLine must be a multiple of 16 bytes"); - -Resampler ResamplerDefault{Resampler::Linear}; - -namespace { - -using uint = unsigned int; - -using HrtfMixerFunc = void(*)(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize); -using HrtfMixerBlendFunc = void(*)(const float *InSamples, float2 *AccumSamples, - const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, - const size_t BufferSize); - -HrtfMixerFunc MixHrtfSamples{MixHrtf_<CTag>}; -HrtfMixerBlendFunc MixHrtfBlendSamples{MixHrtfBlend_<CTag>}; - -inline MixerFunc SelectMixer() -{ -#ifdef HAVE_NEON - if((CPUCapFlags&CPU_CAP_NEON)) - return Mix_<NEONTag>; -#endif -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return Mix_<SSETag>; -#endif - return Mix_<CTag>; -} - -inline HrtfMixerFunc SelectHrtfMixer() -{ -#ifdef HAVE_NEON - if((CPUCapFlags&CPU_CAP_NEON)) - return MixHrtf_<NEONTag>; -#endif -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return MixHrtf_<SSETag>; -#endif - return MixHrtf_<CTag>; -} - -inline HrtfMixerBlendFunc SelectHrtfBlendMixer() -{ -#ifdef HAVE_NEON - if((CPUCapFlags&CPU_CAP_NEON)) - return MixHrtfBlend_<NEONTag>; -#endif -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return MixHrtfBlend_<SSETag>; -#endif - return MixHrtfBlend_<CTag>; -} - -} // namespace - - -void aluInitMixer(al::optional<std::string> resampler) -{ - if(resampler) - { - struct ResamplerEntry { - const char name[16]; - 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 }, - }; - - const char *str{resampler->c_str()}; - if(al::strcasecmp(str, "bsinc") == 0) - { - WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str); - str = "bsinc12"; - } - else if(al::strcasecmp(str, "sinc4") == 0 || al::strcasecmp(str, "sinc8") == 0) - { - WARN("Resampler option \"%s\" is deprecated, using cubic\n", str); - str = "cubic"; - } - - auto iter = std::find_if(std::begin(ResamplerList), std::end(ResamplerList), - [str](const ResamplerEntry &entry) -> bool - { return al::strcasecmp(str, entry.name) == 0; }); - if(iter == std::end(ResamplerList)) - ERR("Invalid resampler: %s\n", str); - else - ResamplerDefault = iter->resampler; - } - - MixSamples = SelectMixer(); - MixHrtfBlendSamples = SelectHrtfBlendMixer(); - MixHrtfSamples = SelectHrtfMixer(); -} - - -namespace { - -void SendSourceStoppedEvent(ContextBase *context, uint id) -{ - RingBuffer *ring{context->mAsyncEvents.get()}; - auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len < 1) return; - - AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}}; - evt->u.srcstate.id = id; - evt->u.srcstate.state = AsyncEvent::SrcState::Stop; - - ring->writeAdvance(1); -} - - -const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *dst, - const al::span<const float> src, int type) -{ - switch(type) - { - case AF_None: - lpfilter.clear(); - hpfilter.clear(); - break; - - case AF_LowPass: - lpfilter.process(src, dst); - hpfilter.clear(); - return dst; - case AF_HighPass: - lpfilter.clear(); - hpfilter.process(src, dst); - return dst; - - case AF_BandPass: - DualBiquad{lpfilter, hpfilter}.process(src, dst); - return dst; - } - return src.data(); -} - - -void LoadSamples(const al::span<Voice::BufferLine> dstSamples, const size_t dstOffset, - const al::byte *src, const size_t srcOffset, const FmtType srctype, const FmtChannels srcchans, - const size_t samples) noexcept -{ -#define HANDLE_FMT(T) case T: \ - { \ - constexpr size_t sampleSize{sizeof(al::FmtTypeTraits<T>::Type)}; \ - if(srcchans == FmtUHJ2) \ - { \ - constexpr size_t srcstep{2u}; \ - src += srcOffset*srcstep*sampleSize; \ - al::LoadSampleArray<T>(dstSamples[0].data() + dstOffset, src, \ - srcstep, samples); \ - al::LoadSampleArray<T>(dstSamples[1].data() + dstOffset, \ - src + sampleSize, srcstep, samples); \ - std::fill_n(dstSamples[2].data() + dstOffset, samples, 0.0f); \ - } \ - else \ - { \ - const size_t srcstep{dstSamples.size()}; \ - src += srcOffset*srcstep*sampleSize; \ - for(auto &dst : dstSamples) \ - { \ - al::LoadSampleArray<T>(dst.data() + dstOffset, src, srcstep, \ - samples); \ - src += sampleSize; \ - } \ - } \ - } \ - break - - switch(srctype) - { - HANDLE_FMT(FmtUByte); - HANDLE_FMT(FmtShort); - HANDLE_FMT(FmtFloat); - HANDLE_FMT(FmtDouble); - HANDLE_FMT(FmtMulaw); - HANDLE_FMT(FmtAlaw); - } -#undef HANDLE_FMT -} - -void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, - const size_t dataPosInt, const FmtType sampleType, const FmtChannels sampleChannels, - const size_t samplesToLoad, const al::span<Voice::BufferLine> voiceSamples) -{ - const uint loopStart{buffer->mLoopStart}; - const uint loopEnd{buffer->mLoopEnd}; - ASSUME(loopEnd > loopStart); - - /* If current pos is beyond the loop range, do not loop */ - if(!bufferLoopItem || dataPosInt >= loopEnd) - { - /* Load what's left to play from the buffer */ - const size_t remaining{minz(samplesToLoad, buffer->mSampleLen-dataPosInt)}; - LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, dataPosInt, sampleType, - sampleChannels, remaining); - - if(const size_t toFill{samplesToLoad - remaining}) - { - for(auto &chanbuffer : voiceSamples) - { - auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + remaining; - std::fill_n(srcsamples + 1, toFill, *srcsamples); - } - } - } - else - { - /* Load what's left of this loop iteration */ - const size_t remaining{minz(samplesToLoad, loopEnd-dataPosInt)}; - LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, dataPosInt, sampleType, - sampleChannels, remaining); - - /* Load repeats of the loop to fill the buffer. */ - const auto loopSize = static_cast<size_t>(loopEnd - loopStart); - size_t samplesLoaded{remaining}; - while(const size_t toFill{minz(samplesToLoad - samplesLoaded, loopSize)}) - { - LoadSamples(voiceSamples, MaxResamplerEdge + samplesLoaded, buffer->mSamples, - loopStart, sampleType, sampleChannels, toFill); - samplesLoaded += toFill; - } - } -} - -void LoadBufferCallback(VoiceBufferItem *buffer, const size_t numCallbackSamples, - const FmtType sampleType, const FmtChannels sampleChannels, const size_t samplesToLoad, - const al::span<Voice::BufferLine> voiceSamples) -{ - /* Load what's left to play from the buffer */ - const size_t remaining{minz(samplesToLoad, numCallbackSamples)}; - LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, 0, sampleType, sampleChannels, - remaining); - - if(const size_t toFill{samplesToLoad - remaining}) - { - for(auto &chanbuffer : voiceSamples) - { - auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + remaining; - std::fill_n(srcsamples + 1, toFill, *srcsamples); - } - } -} - -void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, - size_t dataPosInt, const FmtType sampleType, const FmtChannels sampleChannels, - const size_t samplesToLoad, const al::span<Voice::BufferLine> voiceSamples) -{ - /* Crawl the buffer queue to fill in the temp buffer */ - size_t samplesLoaded{0}; - while(buffer && samplesLoaded != samplesToLoad) - { - if(dataPosInt >= buffer->mSampleLen) - { - dataPosInt -= buffer->mSampleLen; - buffer = buffer->mNext.load(std::memory_order_acquire); - if(!buffer) buffer = bufferLoopItem; - continue; - } - - const size_t remaining{minz(samplesToLoad-samplesLoaded, buffer->mSampleLen-dataPosInt)}; - LoadSamples(voiceSamples, MaxResamplerEdge+samplesLoaded, buffer->mSamples, dataPosInt, - sampleType, sampleChannels, remaining); - - samplesLoaded += remaining; - if(samplesLoaded == samplesToLoad) - break; - - dataPosInt = 0; - buffer = buffer->mNext.load(std::memory_order_acquire); - if(!buffer) buffer = bufferLoopItem; - } - if(const size_t toFill{samplesToLoad - samplesLoaded}) - { - size_t chanidx{0}; - for(auto &chanbuffer : voiceSamples) - { - auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + samplesLoaded; - std::fill_n(srcsamples + 1, toFill, *srcsamples); - ++chanidx; - } - } -} - - -void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &parms, - const float TargetGain, const uint Counter, uint OutPos, DeviceBase *Device) -{ - const uint IrSize{Device->mIrSize}; - auto &HrtfSamples = Device->HrtfSourceData; - /* Source HRTF mixing needs to include the direct delay so it remains - * aligned with the direct mix's HRTF filtering. - */ - float2 *AccumSamples{Device->HrtfAccumData + HrtfDirectDelay}; - - /* Copy the HRTF history and new input samples into a temp buffer. */ - auto src_iter = std::copy(parms.Hrtf.History.begin(), parms.Hrtf.History.end(), - std::begin(HrtfSamples)); - std::copy_n(samples, DstBufferSize, src_iter); - /* Copy the last used samples back into the history buffer for later. */ - std::copy_n(std::begin(HrtfSamples) + DstBufferSize, parms.Hrtf.History.size(), - parms.Hrtf.History.begin()); - - /* If fading and this is the first mixing pass, fade between the IRs. */ - uint fademix{0u}; - if(Counter && OutPos == 0) - { - fademix = minu(DstBufferSize, Counter); - - float gain{TargetGain}; - - /* The new coefficients need to fade in completely since they're - * replacing the old ones. To keep the gain fading consistent, - * interpolate between the old and new target gains given how much of - * the fade time this mix handles. - */ - if(Counter > fademix) - { - const float a{static_cast<float>(fademix) / static_cast<float>(Counter)}; - gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a); - } - - MixHrtfFilter hrtfparams{ - 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); - - /* Update the old parameters with the result. */ - parms.Hrtf.Old = parms.Hrtf.Target; - parms.Hrtf.Old.Gain = gain; - OutPos += fademix; - } - - if(fademix < DstBufferSize) - { - const uint todo{DstBufferSize - fademix}; - float gain{TargetGain}; - - /* Interpolate the target gain if the gain fading lasts longer than - * this mix. - */ - if(Counter > DstBufferSize) - { - const float a{static_cast<float>(todo) / static_cast<float>(Counter-fademix)}; - gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a); - } - - MixHrtfFilter hrtfparams{ - parms.Hrtf.Target.Coeffs, - 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); - - /* Store the now-current gain for next time. */ - parms.Hrtf.Old.Gain = gain; - } -} - -void DoNfcMix(const al::span<const float> samples, FloatBufferLine *OutBuffer, DirectParams &parms, - 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}; - - float *CurrentGains{parms.Gains.Current.data()}; - MixSamples(samples, {OutBuffer, 1u}, CurrentGains, TargetGains, Counter, OutPos); - ++OutBuffer; - ++CurrentGains; - ++TargetGains; - - const al::span<float> nfcsamples{Device->NfcSampleData, samples.size()}; - size_t order{1}; - while(const size_t chancount{Device->NumChannelsPerOrder[order]}) - { - (parms.NFCtrlFilter.*NfcProcess[order])(samples, nfcsamples.data()); - MixSamples(nfcsamples, {OutBuffer, chancount}, CurrentGains, TargetGains, Counter, OutPos); - OutBuffer += chancount; - CurrentGains += chancount; - TargetGains += chancount; - if(++order == MaxAmbiOrder+1) - break; - } -} - -} // namespace - -void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo) -{ - static constexpr std::array<float,MAX_OUTPUT_CHANNELS> SilentTarget{}; - - ASSUME(SamplesToDo > 0); - - /* Get voice info */ - uint DataPosInt{mPosition.load(std::memory_order_relaxed)}; - uint DataPosFrac{mPositionFrac.load(std::memory_order_relaxed)}; - VoiceBufferItem *BufferListItem{mCurrentBuffer.load(std::memory_order_relaxed)}; - VoiceBufferItem *BufferLoopItem{mLoopBuffer.load(std::memory_order_relaxed)}; - const uint increment{mStep}; - if UNLIKELY(increment < 1) - { - /* If the voice is supposed to be stopping but can't be mixed, just - * stop it before bailing. - */ - if(vstate == Stopping) - mPlayState.store(Stopped, std::memory_order_release); - return; - } - - DeviceBase *Device{Context->mDevice}; - const uint NumSends{Device->NumAuxSends}; - - ResamplerFunc Resample{(increment == MixerFracOne && DataPosFrac == 0) ? - Resample_<CopyTag,CTag> : mResampler}; - - uint Counter{(mFlags&VoiceIsFading) ? SamplesToDo : 0}; - if(!Counter) - { - /* No fading, just overwrite the old/current params. */ - for(auto &chandata : mChans) - { - { - DirectParams &parms = chandata.mDryParams; - if(!(mFlags&VoiceHasHrtf)) - parms.Gains.Current = parms.Gains.Target; - else - parms.Hrtf.Old = parms.Hrtf.Target; - } - for(uint send{0};send < NumSends;++send) - { - if(mSend[send].Buffer.empty()) - continue; - - SendParams &parms = chandata.mWetParams[send]; - parms.Gains.Current = parms.Gains.Target; - } - } - } - else if UNLIKELY(!BufferListItem) - Counter = std::min(Counter, 64u); - - const uint PostPadding{MaxResamplerEdge + - ((mFmtChannels==FmtUHJ2 || mFmtChannels==FmtUHJ3 || mFmtChannels==FmtUHJ4) - ? uint{UhjDecoder::sFilterDelay} : 0u)}; - uint buffers_done{0u}; - uint OutPos{0u}; - do { - /* Figure out how many buffer samples will be needed */ - uint DstBufferSize{SamplesToDo - OutPos}; - uint SrcBufferSize; - - if(increment <= MixerFracOne) - { - /* Calculate the last written dst sample pos. */ - uint64_t DataSize64{DstBufferSize - 1}; - /* Calculate the last read src sample pos. */ - DataSize64 = (DataSize64*increment + DataPosFrac) >> MixerFracBits; - /* +1 to get the src sample count, include padding. */ - DataSize64 += 1 + PostPadding; - - /* Result is guaranteed to be <= BufferLineSize+ResamplerPrePadding - * since we won't use more src samples than dst samples+padding. - */ - SrcBufferSize = static_cast<uint>(DataSize64); - } - else - { - uint64_t DataSize64{DstBufferSize}; - /* Calculate the end src sample pos, include padding. */ - DataSize64 = (DataSize64*increment + DataPosFrac) >> MixerFracBits; - DataSize64 += PostPadding; - - if(DataSize64 <= LineSize - MaxResamplerEdge) - SrcBufferSize = static_cast<uint>(DataSize64); - else - { - /* If the source size got saturated, we can't fill the desired - * dst size. Figure out how many samples we can actually mix. - */ - SrcBufferSize = LineSize - MaxResamplerEdge; - - DataSize64 = SrcBufferSize - PostPadding; - DataSize64 = ((DataSize64<<MixerFracBits) - DataPosFrac) / increment; - if(DataSize64 < DstBufferSize) - { - /* Some mixers require being 16-byte aligned, so also limit - * to a multiple of 4 samples to maintain alignment. - */ - DstBufferSize = static_cast<uint>(DataSize64) & ~3u; - } - ASSUME(DstBufferSize > 0); - } - } - - if((mFlags&(VoiceIsCallback|VoiceCallbackStopped)) == VoiceIsCallback && BufferListItem) - { - if(SrcBufferSize > mNumCallbackSamples) - { - const size_t byteOffset{mNumCallbackSamples*mFrameSize}; - const size_t needBytes{SrcBufferSize*mFrameSize - byteOffset}; - - const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData, - &BufferListItem->mSamples[byteOffset], static_cast<int>(needBytes))}; - if(gotBytes < 0) - mFlags |= VoiceCallbackStopped; - else if(static_cast<uint>(gotBytes) < needBytes) - { - mFlags |= VoiceCallbackStopped; - mNumCallbackSamples += static_cast<uint>(static_cast<uint>(gotBytes) / - mFrameSize); - } - else - mNumCallbackSamples = SrcBufferSize; - } - } - - if UNLIKELY(!BufferListItem) - { - for(auto &chanbuffer : mVoiceSamples) - { - auto srciter = chanbuffer.data() + MaxResamplerEdge; - auto srcend = chanbuffer.data() + MaxResamplerPadding; - - /* When loading from a voice that ended prematurely, only take - * the samples that get closest to 0 amplitude. This helps - * certain sounds fade out better. - */ - auto abs_lt = [](const float lhs, const float rhs) noexcept -> bool - { return std::abs(lhs) < std::abs(rhs); }; - srciter = std::min_element(srciter, srcend, abs_lt); - - SrcBufferSize = SrcBufferSize - PostPadding + MaxResamplerPadding; - std::fill(srciter+1, chanbuffer.data() + SrcBufferSize, *srciter); - } - } - else - { - if((mFlags&VoiceIsStatic)) - LoadBufferStatic(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, mFmtChannels, - SrcBufferSize, mVoiceSamples); - else if((mFlags&VoiceIsCallback)) - LoadBufferCallback(BufferListItem, mNumCallbackSamples, mFmtType, mFmtChannels, - SrcBufferSize, mVoiceSamples); - else - LoadBufferQueue(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, mFmtChannels, - SrcBufferSize, mVoiceSamples); - - if(mDecoder) - { - const size_t srcOffset{(increment*DstBufferSize + DataPosFrac)>>MixerFracBits}; - SrcBufferSize = SrcBufferSize - PostPadding + MaxResamplerEdge; - mDecoder->decode(mVoiceSamples, MaxResamplerEdge, SrcBufferSize, srcOffset); - } - } - - auto voiceSamples = mVoiceSamples.begin(); - for(auto &chandata : mChans) - { - /* Resample, then apply ambisonic upsampling as needed. */ - float *ResampledData{Resample(&mResampleState, - voiceSamples->data() + MaxResamplerEdge, DataPosFrac, increment, - {Device->ResampledData, DstBufferSize})}; - if((mFlags&VoiceIsAmbisonic)) - chandata.mAmbiSplitter.processHfScale({ResampledData, DstBufferSize}, - chandata.mAmbiScale); - - /* Now filter and mix to the appropriate outputs. */ - const al::span<float,BufferLineSize> FilterBuf{Device->FilteredData}; - { - DirectParams &parms = chandata.mDryParams; - const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf.data(), - {ResampledData, DstBufferSize}, mDirect.FilterType)}; - - if((mFlags&VoiceHasHrtf)) - { - const float TargetGain{UNLIKELY(vstate == Stopping) ? 0.0f : - parms.Hrtf.Target.Gain}; - DoHrtfMix(samples, DstBufferSize, parms, TargetGain, Counter, OutPos, Device); - } - else if((mFlags&VoiceHasNfc)) - { - const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data() - : parms.Gains.Target.data()}; - DoNfcMix({samples, DstBufferSize}, mDirect.Buffer.data(), parms, TargetGains, - Counter, OutPos, Device); - } - else - { - const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data() - : parms.Gains.Target.data()}; - MixSamples({samples, DstBufferSize}, mDirect.Buffer, - parms.Gains.Current.data(), TargetGains, Counter, OutPos); - } - } - - for(uint send{0};send < NumSends;++send) - { - if(mSend[send].Buffer.empty()) - continue; - - SendParams &parms = chandata.mWetParams[send]; - const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf.data(), - {ResampledData, DstBufferSize}, mSend[send].FilterType)}; - - const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data() - : parms.Gains.Target.data()}; - MixSamples({samples, DstBufferSize}, mSend[send].Buffer, - parms.Gains.Current.data(), TargetGains, Counter, OutPos); - } - - /* Store the last source samples used for next time. */ - const size_t srcOffset{(increment*DstBufferSize + DataPosFrac)>>MixerFracBits}; - std::copy_n(voiceSamples->data()+srcOffset, MaxResamplerPadding, voiceSamples->data()); - ++voiceSamples; - } - /* Update positions */ - DataPosFrac += increment*DstBufferSize; - const uint SrcSamplesDone{DataPosFrac>>MixerFracBits}; - DataPosInt += SrcSamplesDone; - DataPosFrac &= MixerFracMask; - - OutPos += DstBufferSize; - Counter = maxu(DstBufferSize, Counter) - DstBufferSize; - - if UNLIKELY(!BufferListItem) - { - /* Do nothing extra when there's no buffers. */ - } - else if((mFlags&VoiceIsStatic)) - { - if(BufferLoopItem) - { - /* Handle looping static source */ - const uint LoopStart{BufferListItem->mLoopStart}; - const uint LoopEnd{BufferListItem->mLoopEnd}; - if(DataPosInt >= LoopEnd) - { - assert(LoopEnd > LoopStart); - DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart; - } - } - else - { - /* Handle non-looping static source */ - if(DataPosInt >= BufferListItem->mSampleLen) - { - BufferListItem = nullptr; - break; - } - } - } - else if((mFlags&VoiceIsCallback)) - { - if(SrcSamplesDone < mNumCallbackSamples) - { - const size_t byteOffset{SrcSamplesDone*mFrameSize}; - const size_t byteEnd{mNumCallbackSamples*mFrameSize}; - al::byte *data{BufferListItem->mSamples}; - std::copy(data+byteOffset, data+byteEnd, data); - mNumCallbackSamples -= SrcSamplesDone; - } - else - { - BufferListItem = nullptr; - mNumCallbackSamples = 0; - } - } - else - { - /* Handle streaming source */ - do { - if(BufferListItem->mSampleLen > DataPosInt) - break; - - DataPosInt -= BufferListItem->mSampleLen; - - ++buffers_done; - BufferListItem = BufferListItem->mNext.load(std::memory_order_relaxed); - if(!BufferListItem) BufferListItem = BufferLoopItem; - } while(BufferListItem); - } - } while(OutPos < SamplesToDo); - - mFlags |= VoiceIsFading; - - /* Don't update positions and buffers if we were stopping. */ - if UNLIKELY(vstate == Stopping) - { - mPlayState.store(Stopped, std::memory_order_release); - return; - } - - /* Capture the source ID in case it's reset for stopping. */ - const uint SourceID{mSourceID.load(std::memory_order_relaxed)}; - - /* Update voice info */ - mPosition.store(DataPosInt, std::memory_order_relaxed); - mPositionFrac.store(DataPosFrac, std::memory_order_relaxed); - mCurrentBuffer.store(BufferListItem, std::memory_order_relaxed); - if(!BufferListItem) - { - mLoopBuffer.store(nullptr, std::memory_order_relaxed); - mSourceID.store(0u, std::memory_order_relaxed); - } - std::atomic_thread_fence(std::memory_order_release); - - /* Send any events now, after the position/buffer info was updated. */ - const uint enabledevt{Context->mEnabledEvts.load(std::memory_order_acquire)}; - if(buffers_done > 0 && (enabledevt&EventType_BufferCompleted)) - { - RingBuffer *ring{Context->mAsyncEvents.get()}; - auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len > 0) - { - AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_BufferCompleted}}; - evt->u.bufcomp.id = SourceID; - evt->u.bufcomp.count = buffers_done; - ring->writeAdvance(1); - } - } - - if(!BufferListItem) - { - /* If the voice just ended, set it to Stopping so the next render - * ensures any residual noise fades to 0 amplitude. - */ - mPlayState.store(Stopping, std::memory_order_release); - if((enabledevt&EventType_SourceStateChange)) - SendSourceStoppedEvent(Context, SourceID); - } -} - -void Voice::prepare(DeviceBase *device) -{ - if((mFmtChannels == FmtUHJ2 || mFmtChannels == FmtUHJ3 || mFmtChannels==FmtUHJ4) && !mDecoder) - mDecoder = std::make_unique<UhjDecoder>(); - else if(mFmtChannels != FmtUHJ2 && mFmtChannels != FmtUHJ3 && mFmtChannels != FmtUHJ4) - mDecoder = nullptr; - - /* Clear the stepping value explicitly so the mixer knows not to mix this - * until the update gets applied. - */ - mStep = 0; - - /* Make sure the sample history is cleared. */ - std::fill(mVoiceSamples.begin(), mVoiceSamples.end(), BufferLine{}); - - /* Don't need to set the VoiceIsAmbisonic flag if the device is not higher - * order than the voice. No HF scaling is necessary to mix it. - */ - if(mAmbiOrder && device->mAmbiOrder > mAmbiOrder) - { - const uint8_t *OrderFromChan{(mFmtChannels == FmtBFormat2D) ? - AmbiIndex::OrderFrom2DChannel().data() : AmbiIndex::OrderFromChannel().data()}; - const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder); - - const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)}; - for(auto &chandata : mChans) - { - chandata.mAmbiScale = scales[*(OrderFromChan++)]; - chandata.mAmbiSplitter = splitter; - chandata.mDryParams = DirectParams{}; - std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{}); - } - mFlags |= VoiceIsAmbisonic; - } - else - { - for(auto &chandata : mChans) - { - chandata.mDryParams = DirectParams{}; - std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{}); - } - mFlags &= ~VoiceIsAmbisonic; - } - - if(device->AvgSpeakerDist > 0.0f) - { - const float w1{SpeedOfSoundMetersPerSec / - (device->AvgSpeakerDist * static_cast<float>(device->Frequency))}; - for(auto &chandata : mChans) - chandata.mDryParams.NFCtrlFilter.init(w1); - } -} diff --git a/alc/voice.h b/alc/voice.h deleted file mode 100644 index 937294e3..00000000 --- a/alc/voice.h +++ /dev/null @@ -1,271 +0,0 @@ -#ifndef VOICE_H -#define VOICE_H - -#include <stddef.h> - -#include <array> -#include <atomic> -#include <memory> -#include <string> - -#include "albyte.h" -#include "almalloc.h" -#include "aloptional.h" -#include "alspan.h" -#include "buffer_storage.h" -#include "core/bufferline.h" -#include "core/devformat.h" -#include "core/filters/biquad.h" -#include "core/filters/nfc.h" -#include "core/filters/splitter.h" -#include "core/mixer/defs.h" -#include "core/mixer/hrtfdefs.h" -#include "core/resampler_limits.h" -#include "core/uhjfilter.h" -#include "vector.h" - -struct ContextBase; -struct DeviceBase; -struct EffectSlot; -enum class DistanceModel : unsigned char; - -using uint = unsigned int; - - -#define MAX_SENDS 6 - - -enum class SpatializeMode : unsigned char { - Off, - On, - Auto -}; - -enum class DirectMode : unsigned char { - Off, - DropMismatch, - RemixMismatch -}; - - -/* Maximum number of extra source samples that may need to be loaded, for - * resampling or conversion purposes. - */ -constexpr uint MaxPostVoiceLoad{MaxResamplerEdge + UhjDecoder::sFilterDelay}; - - -enum { - AF_None = 0, - AF_LowPass = 1, - AF_HighPass = 2, - AF_BandPass = AF_LowPass | AF_HighPass -}; - - -struct DirectParams { - BiquadFilter LowPass; - BiquadFilter HighPass; - - NfcFilter NFCtrlFilter; - - struct { - 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; - } Gains; -}; - -struct SendParams { - BiquadFilter LowPass; - BiquadFilter HighPass; - - struct { - std::array<float,MAX_OUTPUT_CHANNELS> Current; - std::array<float,MAX_OUTPUT_CHANNELS> Target; - } Gains; -}; - - -struct VoiceBufferItem { - std::atomic<VoiceBufferItem*> mNext{nullptr}; - - CallbackType mCallback{nullptr}; - void *mUserData{nullptr}; - - uint mSampleLen{0u}; - uint mLoopStart{0u}; - uint mLoopEnd{0u}; - - al::byte *mSamples{nullptr}; -}; - - -struct VoiceProps { - float Pitch; - float Gain; - float OuterGain; - float MinGain; - float MaxGain; - float InnerAngle; - float OuterAngle; - float RefDistance; - float MaxDistance; - float RolloffFactor; - std::array<float,3> Position; - std::array<float,3> Velocity; - std::array<float,3> Direction; - std::array<float,3> OrientAt; - std::array<float,3> OrientUp; - bool HeadRelative; - DistanceModel mDistanceModel; - Resampler mResampler; - DirectMode DirectChannels; - SpatializeMode mSpatializeMode; - - bool DryGainHFAuto; - bool WetGainAuto; - bool WetGainHFAuto; - float OuterGainHF; - - float AirAbsorptionFactor; - float RoomRolloffFactor; - float DopplerFactor; - - std::array<float,2> StereoPan; - - float Radius; - - /** Direct filter and auxiliary send info. */ - struct { - float Gain; - float GainHF; - float HFReference; - float GainLF; - float LFReference; - } Direct; - struct SendData { - EffectSlot *Slot; - float Gain; - float GainHF; - float HFReference; - float GainLF; - float LFReference; - } Send[MAX_SENDS]; -}; - -struct VoicePropsItem : public VoiceProps { - std::atomic<VoicePropsItem*> next{nullptr}; - - DEF_NEWDEL(VoicePropsItem) -}; - -constexpr uint VoiceIsStatic{ 1u<<0}; -constexpr uint VoiceIsCallback{ 1u<<1}; -constexpr uint VoiceIsAmbisonic{ 1u<<2}; /* Needs HF scaling for ambisonic upsampling. */ -constexpr uint VoiceCallbackStopped{1u<<3}; -constexpr uint VoiceIsFading{ 1u<<4}; /* Use gain stepping for smooth transitions. */ -constexpr uint VoiceHasHrtf{ 1u<<5}; -constexpr uint VoiceHasNfc{ 1u<<6}; - -struct Voice { - enum State { - Stopped, - Playing, - Stopping, - Pending - }; - - std::atomic<VoicePropsItem*> mUpdate{nullptr}; - - VoiceProps mProps; - - std::atomic<uint> mSourceID{0u}; - std::atomic<State> mPlayState{Stopped}; - std::atomic<bool> mPendingChange{false}; - - /** - * Source offset in samples, relative to the currently playing buffer, NOT - * the whole queue. - */ - std::atomic<uint> mPosition; - /** Fractional (fixed-point) offset to the next sample. */ - std::atomic<uint> mPositionFrac; - - /* Current buffer queue item being played. */ - 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; - - /* Properties for the attached buffer(s). */ - FmtChannels mFmtChannels; - FmtType mFmtType; - uint mFrequency; - uint mFrameSize; - AmbiLayout mAmbiLayout; - AmbiScaling mAmbiScaling; - uint mAmbiOrder; - - std::unique_ptr<UhjDecoder> mDecoder; - - /** Current target parameters used for mixing. */ - uint mStep{0}; - - ResamplerFunc mResampler; - - InterpState mResampleState; - - uint mFlags{}; - uint mNumCallbackSamples{0}; - - struct TargetData { - int FilterType; - al::span<FloatBufferLine> Buffer; - }; - TargetData mDirect; - std::array<TargetData,MAX_SENDS> mSend; - - /* The first MaxResamplerPadding/2 elements are the sample history from the - * previous mix, with an additional MaxResamplerPadding/2 elements that are - * now current (which may be overwritten if the buffer data is still - * available). - */ - static constexpr size_t LineSize{BufferLineSize + MaxResamplerPadding + - UhjDecoder::sFilterDelay}; - using BufferLine = std::array<float,LineSize>; - al::vector<BufferLine,16> mVoiceSamples{2}; - - struct ChannelData { - float mAmbiScale; - BandSplitter mAmbiSplitter; - - DirectParams mDryParams; - std::array<SendParams,MAX_SENDS> mWetParams; - }; - al::vector<ChannelData> mChans{2}; - - Voice() = default; - ~Voice() { delete mUpdate.exchange(nullptr, std::memory_order_acq_rel); } - - Voice(const Voice&) = delete; - Voice& operator=(const Voice&) = delete; - - void mix(const State vstate, ContextBase *Context, const uint SamplesToDo); - - void prepare(DeviceBase *device); - - DEF_NEWDEL(Voice) -}; - -extern Resampler ResamplerDefault; - -void aluInitMixer(al::optional<std::string> resampler); - -#endif /* VOICE_H */ |