diff options
author | Chris Robinson <[email protected]> | 2022-11-03 02:17:54 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2022-11-03 02:17:54 -0700 |
commit | d8361bdd6fa807a4200e18e8ef7ffd13ab849b74 (patch) | |
tree | 1df40b4006ade3e715c84701916fab23af65d612 | |
parent | b73e0ecbc55cb09788d056131aaec0ed27f6046a (diff) |
Add the ability to start a voice at a particular time
-rw-r--r-- | al/source.cpp | 2 | ||||
-rw-r--r-- | alc/alu.cpp | 6 | ||||
-rw-r--r-- | core/voice.cpp | 44 | ||||
-rw-r--r-- | core/voice.h | 6 |
4 files changed, 49 insertions, 9 deletions
diff --git a/al/source.cpp b/al/source.cpp index 6daa3f4e..93f3285b 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -644,6 +644,7 @@ bool SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALC newvoice->mPosition.store(vpos.pos, std::memory_order_relaxed); newvoice->mPositionFrac.store(vpos.frac, std::memory_order_relaxed); newvoice->mCurrentBuffer.store(vpos.bufferitem, std::memory_order_relaxed); + newvoice->mStartTime = oldvoice->mStartTime; newvoice->mFlags.reset(); if(vpos.pos > 0 || vpos.frac > 0 || vpos.bufferitem != &source->mQueue.front()) newvoice->mFlags.set(VoiceIsFading); @@ -3198,6 +3199,7 @@ START_API_FUNC voice->mPosition.store(0, std::memory_order_relaxed); voice->mPositionFrac.store(0, std::memory_order_relaxed); voice->mCurrentBuffer.store(&source->mQueue.front(), std::memory_order_relaxed); + voice->mStartTime = std::chrono::nanoseconds::min(); voice->mFlags.reset(); /* A source that's not playing or paused has any offset applied when it * starts playing. diff --git a/alc/alu.cpp b/alc/alu.cpp index bc39b108..647b163b 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -105,6 +105,7 @@ static_assert(!(MaxResamplerPadding&1), "MaxResamplerPadding is not a multiple o namespace { using uint = unsigned int; +using namespace std::chrono; constexpr uint MaxPitch{10}; @@ -1841,6 +1842,9 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo) { ASSUME(SamplesToDo > 0); + const nanoseconds curtime{device->ClockBase + + nanoseconds{seconds{device->SamplesDone}}/device->Frequency}; + for(ContextBase *ctx : *device->mContexts.load(std::memory_order_acquire)) { const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire); @@ -1861,7 +1865,7 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo) { const Voice::State vstate{voice->mPlayState.load(std::memory_order_acquire)}; if(vstate != Voice::Stopped && vstate != Voice::Pending) - voice->mix(vstate, ctx, SamplesToDo); + voice->mix(vstate, ctx, curtime, SamplesToDo); } /* Process effects. */ diff --git a/core/voice.cpp b/core/voice.cpp index 349c3430..bf13d563 100644 --- a/core/voice.cpp +++ b/core/voice.cpp @@ -60,6 +60,7 @@ Resampler ResamplerDefault{Resampler::Linear}; namespace { using uint = unsigned int; +using namespace std::chrono; using HrtfMixerFunc = void(*)(const float *InSamples, float2 *AccumSamples, const uint IrSize, const MixHrtfFilter *hrtfparams, const size_t BufferSize); @@ -456,12 +457,16 @@ void DoNfcMix(const al::span<const float> samples, FloatBufferLine *OutBuffer, D } // namespace -void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo) +void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds deviceTime, + const uint SamplesToDo) { static constexpr std::array<float,MAX_OUTPUT_CHANNELS> SilentTarget{}; ASSUME(SamplesToDo > 0); + DeviceBase *Device{Context->mDevice}; + const uint NumSends{Device->NumAuxSends}; + /* Get voice info */ int DataPosInt{mPosition.load(std::memory_order_relaxed)}; uint DataPosFrac{mPositionFrac.load(std::memory_order_relaxed)}; @@ -478,13 +483,37 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo return; } - DeviceBase *Device{Context->mDevice}; - const uint NumSends{Device->NumAuxSends}; + uint Counter{mFlags.test(VoiceIsFading) ? SamplesToDo : 0}; + uint OutPos{0u}; - ResamplerFunc Resample{(increment == MixerFracOne && DataPosFrac == 0) ? - Resample_<CopyTag,CTag> : mResampler}; + /* Check if we're doing a delayed start, and we start in this update. */ + if(mStartTime > deviceTime) + { + /* If the start time is too far ahead, don't bother. */ + auto diff = mStartTime - deviceTime; + if(diff >= seconds{1}) + return; + + /* Get the number of samples ahead of the current time that output + * should start at. Skip this update if it's beyond the output sample + * count. + */ + seconds::rep sampleOffset{duration_cast<seconds>(diff * Device->Frequency).count()}; + if(sampleOffset >= SamplesToDo) + return; + + /* Round the start position down to a multiple of 4, which some mixers + * want. This makes the start time accurate to 4 samples. This could be + * made sample-accurate by forcing non-SIMD functions on the first run. + * + * Also ensure any fading completes within this update (though don't go + * less than 64 samples, or the fade could be too fast). + */ + OutPos = static_cast<uint>(sampleOffset) & ~3u; + if(Counter > 0) + Counter = maxu(Counter-OutPos, 64); + } - uint Counter{mFlags.test(VoiceIsFading) ? SamplesToDo : 0}; if(!Counter) { /* No fading, just overwrite the old/current params. */ @@ -517,9 +546,10 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo std::transform(Device->mSampleData.end() - mChans.size(), Device->mSampleData.end(), MixingSamples.begin(), offset_bufferline); + const ResamplerFunc Resample{(increment == MixerFracOne && DataPosFrac == 0) ? + Resample_<CopyTag,CTag> : mResampler}; const uint PostPadding{MaxResamplerEdge + mDecoderPadding}; uint buffers_done{0u}; - uint OutPos{0u}; do { /* Figure out how many buffer samples will be needed */ uint DstBufferSize{SamplesToDo - OutPos}; diff --git a/core/voice.h b/core/voice.h index cf558341..df0c8c9e 100644 --- a/core/voice.h +++ b/core/voice.h @@ -4,6 +4,7 @@ #include <array> #include <atomic> #include <bitset> +#include <chrono> #include <memory> #include <stddef.h> #include <string> @@ -209,6 +210,8 @@ struct Voice { */ std::atomic<VoiceBufferItem*> mLoopBuffer; + std::chrono::nanoseconds mStartTime{}; + /* Properties for the attached buffer(s). */ FmtChannels mFmtChannels; FmtType mFmtType; @@ -262,7 +265,8 @@ struct Voice { Voice(const Voice&) = delete; Voice& operator=(const Voice&) = delete; - void mix(const State vstate, ContextBase *Context, const uint SamplesToDo); + void mix(const State vstate, ContextBase *Context, const std::chrono::nanoseconds deviceTime, + const uint SamplesToDo); void prepare(DeviceBase *device); |