aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2022-11-03 02:17:54 -0700
committerChris Robinson <[email protected]>2022-11-03 02:17:54 -0700
commitd8361bdd6fa807a4200e18e8ef7ffd13ab849b74 (patch)
tree1df40b4006ade3e715c84701916fab23af65d612
parentb73e0ecbc55cb09788d056131aaec0ed27f6046a (diff)
Add the ability to start a voice at a particular time
-rw-r--r--al/source.cpp2
-rw-r--r--alc/alu.cpp6
-rw-r--r--core/voice.cpp44
-rw-r--r--core/voice.h6
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);