aboutsummaryrefslogtreecommitdiffstats
path: root/alc
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2021-04-27 08:26:42 -0700
committerChris Robinson <[email protected]>2021-04-27 08:26:42 -0700
commitff380298e4086490584707b8ffde44c5ad64830f (patch)
tree313288fbfdc2ea7824508e85d264966db6078368 /alc
parent99157f149f180cfcc2e4be6a3d2a54843411e87a (diff)
Move BufferStorage and Voice to core
Diffstat (limited to 'alc')
-rw-r--r--alc/alc.cpp2
-rw-r--r--alc/alu.cpp4
-rw-r--r--alc/buffer_storage.cpp41
-rw-r--r--alc/buffer_storage.h75
-rw-r--r--alc/effects/convolution.cpp2
-rw-r--r--alc/voice.cpp869
-rw-r--r--alc/voice.h271
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 */