diff options
-rw-r--r-- | al/source.cpp | 14 | ||||
-rw-r--r-- | core/device.h | 7 | ||||
-rw-r--r-- | core/voice.cpp | 58 | ||||
-rw-r--r-- | core/voice.h | 6 |
4 files changed, 55 insertions, 30 deletions
diff --git a/al/source.cpp b/al/source.cpp index 1ed1281f..b8278fed 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -509,17 +509,23 @@ void InitVoice(Voice *voice, ALsource *source, ALbufferQueueItem *BufferList, AL /* Even if storing really high order ambisonics, we only mix channels for * orders up to MaxAmbiOrder. The rest are simply dropped. */ - const ALuint num_channels{(buffer->mChannels == FmtUHJ2) ? 3 : + ALuint num_channels{(buffer->mChannels == FmtUHJ2) ? 3 : ChannelsFromFmt(buffer->mChannels, minu(buffer->mAmbiOrder, MaxAmbiOrder))}; + if UNLIKELY(num_channels > device->mSampleData.size()) + { + ERR("Unexpected channel count: %u (limit: %zu, %d:%d)\n", num_channels, + device->mSampleData.size(), buffer->mChannels, buffer->mAmbiOrder); + num_channels = static_cast<ALuint>(device->mSampleData.size()); + } if(voice->mChans.capacity() > 2 && num_channels < voice->mChans.capacity()) { decltype(voice->mChans){}.swap(voice->mChans); - decltype(voice->mVoiceSamples){}.swap(voice->mVoiceSamples); + decltype(voice->mPrevSamples){}.swap(voice->mPrevSamples); } voice->mChans.reserve(maxu(2, num_channels)); voice->mChans.resize(num_channels); - voice->mVoiceSamples.reserve(maxu(2, num_channels)); - voice->mVoiceSamples.resize(num_channels); + voice->mPrevSamples.reserve(maxu(2, num_channels)); + voice->mPrevSamples.resize(num_channels); voice->prepare(device); diff --git a/core/device.h b/core/device.h index ce9f4ea4..0ffc05c4 100644 --- a/core/device.h +++ b/core/device.h @@ -20,6 +20,8 @@ #include "intrusive_ptr.h" #include "mixer/hrtfdefs.h" #include "opthelpers.h" +#include "resampler_limits.h" +#include "uhjfilter.h" #include "vector.h" struct BackendBase; @@ -166,6 +168,11 @@ struct DeviceBase { std::chrono::nanoseconds FixedLatency{0}; /* Temp storage used for mixer processing. */ + static constexpr size_t MixerLineSize{BufferLineSize + MaxResamplerPadding + + UhjDecoder::sFilterDelay}; + using MixerBufferLine = std::array<float,MixerLineSize>; + alignas(16) std::array<MixerBufferLine,16> mSampleData; + alignas(16) float ResampledData[BufferLineSize]; alignas(16) float FilteredData[BufferLineSize]; union { diff --git a/core/voice.cpp b/core/voice.cpp index 4aadf9fa..261e9a79 100644 --- a/core/voice.cpp +++ b/core/voice.cpp @@ -51,7 +51,8 @@ struct NEONTag; struct CopyTag; -static_assert(!(sizeof(Voice::BufferLine)&15), "Voice::BufferLine must be a multiple of 16 bytes"); +static_assert(!(sizeof(DeviceBase::MixerBufferLine)&15), + "DeviceBase::MixerBufferLine must be a multiple of 16 bytes"); Resampler ResamplerDefault{Resampler::Linear}; @@ -198,7 +199,7 @@ const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *ds } -void LoadSamples(const al::span<Voice::BufferLine> dstSamples, const size_t dstOffset, +void LoadSamples(const al::span<DeviceBase::MixerBufferLine> dstSamples, const size_t dstOffset, const al::byte *src, const size_t srcOffset, const FmtType srctype, const FmtChannels srcchans, const size_t srcstep, const size_t samples) noexcept { @@ -242,7 +243,7 @@ void LoadSamples(const al::span<Voice::BufferLine> dstSamples, const size_t dstO void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, const size_t dataPosInt, const FmtType sampleType, const FmtChannels sampleChannels, const size_t srcStep, const size_t samplesToLoad, - const al::span<Voice::BufferLine> voiceSamples) + const al::span<DeviceBase::MixerBufferLine> voiceSamples) { const uint loopStart{buffer->mLoopStart}; const uint loopEnd{buffer->mLoopEnd}; @@ -286,7 +287,7 @@ void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, void LoadBufferCallback(VoiceBufferItem *buffer, const size_t numCallbackSamples, const FmtType sampleType, const FmtChannels sampleChannels, const size_t srcStep, - const size_t samplesToLoad, const al::span<Voice::BufferLine> voiceSamples) + const size_t samplesToLoad, const al::span<DeviceBase::MixerBufferLine> voiceSamples) { /* Load what's left to play from the buffer */ const size_t remaining{minz(samplesToLoad, numCallbackSamples)}; @@ -306,7 +307,7 @@ void LoadBufferCallback(VoiceBufferItem *buffer, const size_t numCallbackSamples void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, size_t dataPosInt, const FmtType sampleType, const FmtChannels sampleChannels, const size_t srcStep, const size_t samplesToLoad, - const al::span<Voice::BufferLine> voiceSamples) + const al::span<DeviceBase::MixerBufferLine> voiceSamples) { /* Crawl the buffer queue to fill in the temp buffer */ size_t samplesLoaded{0}; @@ -504,6 +505,9 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo else if UNLIKELY(!BufferListItem) Counter = std::min(Counter, 64u); + al::span<DeviceBase::MixerBufferLine> MixingSamples{ + Device->mSampleData.data() + Device->mSampleData.size() - mChans.size(), + mChans.size()}; const uint PostPadding{MaxResamplerEdge + ((mFmtChannels==FmtUHJ2 || mFmtChannels==FmtUHJ3 || mFmtChannels==FmtUHJ4) ? uint{UhjDecoder::sFilterDelay} : 0u)}; @@ -535,14 +539,14 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo DataSize64 = (DataSize64*increment + DataPosFrac) >> MixerFracBits; DataSize64 += PostPadding; - if(DataSize64 <= LineSize - MaxResamplerEdge) + if(DataSize64 <= DeviceBase::MixerLineSize - 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; + SrcBufferSize = DeviceBase::MixerLineSize - MaxResamplerEdge; DataSize64 = SrcBufferSize - PostPadding; DataSize64 = ((DataSize64<<MixerFracBits) - DataPosFrac) / increment; @@ -559,11 +563,13 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo if UNLIKELY(!BufferListItem) { + auto prevSamples = mPrevSamples.data(); SrcBufferSize = SrcBufferSize - PostPadding + MaxResamplerPadding; - for(auto &chanbuffer : mVoiceSamples) + for(auto &chanbuffer : MixingSamples) { - auto srciter = chanbuffer.data() + MaxResamplerEdge; - auto srcend = chanbuffer.data() + MaxResamplerPadding; + auto srcend = std::copy_n(prevSamples->data(), MaxResamplerPadding, + chanbuffer.data()); + ++prevSamples; /* When loading from a voice that ended prematurely, only take * the samples that get closest to 0 amplitude. This helps @@ -571,16 +577,22 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo */ 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); + auto srciter = std::min_element(srcend - MaxResamplerEdge, srcend, abs_lt); std::fill(srciter+1, chanbuffer.data() + SrcBufferSize, *srciter); } } else { + auto prevSamples = mPrevSamples.data(); + for(auto &chanbuffer : MixingSamples) + { + std::copy_n(prevSamples->data(), MaxResamplerEdge, chanbuffer.data()); + ++prevSamples; + } if((mFlags&VoiceIsStatic)) LoadBufferStatic(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, mFmtChannels, - mNumChannels, SrcBufferSize, mVoiceSamples); + mNumChannels, SrcBufferSize, MixingSamples); else if((mFlags&VoiceIsCallback)) { if(!(mFlags&VoiceCallbackStopped)) @@ -605,27 +617,34 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo } } LoadBufferCallback(BufferListItem, mNumCallbackSamples, mFmtType, mFmtChannels, - mNumChannels, SrcBufferSize, mVoiceSamples); + mNumChannels, SrcBufferSize, MixingSamples); } else LoadBufferQueue(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, mFmtChannels, - mNumChannels, SrcBufferSize, mVoiceSamples); + mNumChannels, SrcBufferSize, MixingSamples); if(mDecoder) { const size_t srcOffset{(increment*DstBufferSize + DataPosFrac)>>MixerFracBits}; SrcBufferSize = SrcBufferSize - PostPadding + MaxResamplerEdge; - mDecoder->decode(mVoiceSamples, MaxResamplerEdge, SrcBufferSize, srcOffset); + mDecoder->decode(MixingSamples, MaxResamplerEdge, SrcBufferSize, srcOffset); } } - auto voiceSamples = mVoiceSamples.begin(); + auto prevSamples = mPrevSamples.data(); + auto voiceSamples = MixingSamples.begin(); + const size_t srcOffset{(increment*DstBufferSize + DataPosFrac)>>MixerFracBits}; for(auto &chandata : mChans) { + /* Store the last source samples used for next time. */ + std::copy_n(voiceSamples->data()+srcOffset, MaxResamplerPadding, prevSamples->data()); + ++prevSamples; + /* Resample, then apply ambisonic upsampling as needed. */ float *ResampledData{Resample(&mResampleState, voiceSamples->data() + MaxResamplerEdge, DataPosFrac, increment, {Device->ResampledData, DstBufferSize})}; + ++voiceSamples; if((mFlags&VoiceIsAmbisonic)) chandata.mAmbiSplitter.processHfScale({ResampledData, DstBufferSize}, chandata.mAmbiScale); @@ -673,11 +692,6 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo 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; @@ -809,7 +823,7 @@ void Voice::prepare(DeviceBase *device) mStep = 0; /* Make sure the sample history is cleared. */ - std::fill(mVoiceSamples.begin(), mVoiceSamples.end(), BufferLine{}); + std::fill(mPrevSamples.begin(), mPrevSamples.end(), HistoryLine{}); /* 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. diff --git a/core/voice.h b/core/voice.h index 3b19abbb..169517d5 100644 --- a/core/voice.h +++ b/core/voice.h @@ -237,10 +237,8 @@ struct Voice { * 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}; + using HistoryLine = std::array<float,MaxResamplerPadding>; + al::vector<HistoryLine,16> mPrevSamples{2}; struct ChannelData { float mAmbiScale; |