From 85bae2e94eadb187ddbfcf5fe2005ac7bcd3bd5e Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 4 Aug 2024 16:46:43 +0300 Subject: [PATCH] HLE DSP: Handle cycle drifting --- include/audio/dsp_core.hpp | 2 +- include/audio/hle_core.hpp | 4 +++- include/audio/null_core.hpp | 2 +- include/audio/teakra_core.hpp | 2 +- src/core/audio/hle_core.cpp | 21 ++++++++++++++------- src/core/audio/null_core.cpp | 2 +- src/emulator.cpp | 2 +- 7 files changed, 22 insertions(+), 13 deletions(-) diff --git a/include/audio/dsp_core.hpp b/include/audio/dsp_core.hpp index a4fb1ab1..5addfd19 100644 --- a/include/audio/dsp_core.hpp +++ b/include/audio/dsp_core.hpp @@ -43,7 +43,7 @@ namespace Audio { virtual ~DSPCore() {} virtual void reset() = 0; - virtual void runAudioFrame() = 0; + virtual void runAudioFrame(u64 eventTimestamp) = 0; virtual u8* getDspMemory() = 0; virtual u16 recvData(u32 regId) = 0; diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp index 35c1c1b8..c0e0896f 100644 --- a/include/audio/hle_core.hpp +++ b/include/audio/hle_core.hpp @@ -42,6 +42,7 @@ namespace Audio { return this->bufferID > other.bufferID; } }; + // Buffer of decoded PCM16 samples. TODO: Are there better alternatives to use over deque? using SampleBuffer = std::deque>; @@ -53,6 +54,7 @@ namespace Audio { std::array gain0, gain1, gain2; u32 samplePosition; // Sample number into the current audio buffer + float rateMultiplier; u16 syncCount; u16 currentBufferID; u16 previousBufferID; @@ -185,7 +187,7 @@ namespace Audio { ~HLE_DSP() override {} void reset() override; - void runAudioFrame() override; + void runAudioFrame(u64 eventTimestamp) override; u8* getDspMemory() override { return dspRam.rawMemory.data(); } diff --git a/include/audio/null_core.hpp b/include/audio/null_core.hpp index 7d6f1c9e..bedec8d3 100644 --- a/include/audio/null_core.hpp +++ b/include/audio/null_core.hpp @@ -27,7 +27,7 @@ namespace Audio { ~NullDSP() override {} void reset() override; - void runAudioFrame() override; + void runAudioFrame(u64 eventTimestamp) override; u8* getDspMemory() override { return dspRam.data(); } diff --git a/include/audio/teakra_core.hpp b/include/audio/teakra_core.hpp index 6a011231..17104985 100644 --- a/include/audio/teakra_core.hpp +++ b/include/audio/teakra_core.hpp @@ -83,7 +83,7 @@ namespace Audio { void reset() override; // Run 1 slice of DSP instructions and schedule the next audio frame - void runAudioFrame() override { + void runAudioFrame(u64 eventTimestamp) override { runSlice(); scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2); } diff --git a/src/core/audio/hle_core.cpp b/src/core/audio/hle_core.cpp index ffab9301..d1297ad8 100644 --- a/src/core/audio/hle_core.cpp +++ b/src/core/audio/hle_core.cpp @@ -95,7 +95,7 @@ namespace Audio { scheduler.removeEvent(Scheduler::EventType::RunDSP); } - void HLE_DSP::runAudioFrame() { + void HLE_DSP::runAudioFrame(u64 eventTimestamp) { // Signal audio pipe when an audio frame is done if (dspState == DSPState::On) [[likely]] { dspService.triggerPipeEvent(DSPPipeType::Audio); @@ -103,7 +103,10 @@ namespace Audio { // TODO: Should this be called if dspState != DSPState::On? outputFrame(); - scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::cyclesPerFrame); + + // How many cycles we were late + const u64 cycleDrift = scheduler.currentTimestamp - eventTimestamp; + scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::cyclesPerFrame - cycleDrift); } u16 HLE_DSP::recvData(u32 regId) { @@ -237,10 +240,9 @@ namespace Audio { auto& status = write.sourceStatuses.status[i]; status.enabled = source.enabled; status.syncCount = source.syncCount; - status.currentBufferIDDirty = source.isBufferIDDirty ? 1 : 0; + status.currentBufferIDDirty = (source.isBufferIDDirty ? 1 : 0); status.currentBufferID = source.currentBufferID; status.previousBufferID = source.previousBufferID; - // TODO: Properly update sample position status.samplePosition = source.samplePosition; source.isBufferIDDirty = false; @@ -292,6 +294,10 @@ namespace Audio { source.sourceType = config.monoOrStereo; } + if (config.rateMultiplierDirty) { + source.rateMultiplier = (config.rateMultiplier > 0.f) ? config.rateMultiplier : 1.f; + } + if (config.embeddedBufferDirty) { // Annoyingly, and only for embedded buffer, whether we use config.playPosition depends on the relevant dirty bit const u32 playPosition = config.playPositionDirty ? config.playPosition : 0; @@ -434,7 +440,7 @@ namespace Audio { decodeBuffer(source); } else { - constexpr uint maxSampleCount = Audio::samplesInFrame; + uint maxSampleCount = uint(float(Audio::samplesInFrame) * source.rateMultiplier); uint outputCount = 0; while (outputCount < maxSampleCount) { @@ -447,9 +453,9 @@ namespace Audio { } const uint sampleCount = std::min(maxSampleCount - outputCount, source.currentSamples.size()); - // samples.insert(samples.end(), source.currentSamples.begin(), source.currentSamples.begin() + sampleCount); - source.currentSamples.erase(source.currentSamples.begin(), source.currentSamples.begin() + sampleCount); + // samples.insert(samples.end(), source.currentSamples.begin(), source.currentSamples.begin() + sampleCount); + source.currentSamples.erase(source.currentSamples.begin(), std::next(source.currentSamples.begin(), sampleCount)); source.samplePosition += sampleCount; outputCount += sampleCount; } @@ -618,6 +624,7 @@ namespace Audio { previousBufferID = 0; currentBufferID = 0; syncCount = 0; + rateMultiplier = 1.f; buffers = {}; } diff --git a/src/core/audio/null_core.cpp b/src/core/audio/null_core.cpp index ec073ae7..93c746cb 100644 --- a/src/core/audio/null_core.cpp +++ b/src/core/audio/null_core.cpp @@ -74,7 +74,7 @@ namespace Audio { scheduler.removeEvent(Scheduler::EventType::RunDSP); } - void NullDSP::runAudioFrame() { + void NullDSP::runAudioFrame(u64 eventTimestamp) { // Signal audio pipe when an audio frame is done if (dspState == DSPState::On) [[likely]] { dspService.triggerPipeEvent(DSPPipeType::Audio); diff --git a/src/emulator.cpp b/src/emulator.cpp index 921af08f..8ce71e43 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -167,7 +167,7 @@ void Emulator::pollScheduler() { case Scheduler::EventType::UpdateTimers: kernel.pollTimers(); break; case Scheduler::EventType::RunDSP: { - dsp->runAudioFrame(); + dsp->runAudioFrame(time); break; }