HLE DSP: Handle cycle drifting

This commit is contained in:
wheremyfoodat 2024-08-04 16:46:43 +03:00
parent e666afd1a3
commit 85bae2e94e
7 changed files with 22 additions and 13 deletions

View file

@ -43,7 +43,7 @@ namespace Audio {
virtual ~DSPCore() {} virtual ~DSPCore() {}
virtual void reset() = 0; virtual void reset() = 0;
virtual void runAudioFrame() = 0; virtual void runAudioFrame(u64 eventTimestamp) = 0;
virtual u8* getDspMemory() = 0; virtual u8* getDspMemory() = 0;
virtual u16 recvData(u32 regId) = 0; virtual u16 recvData(u32 regId) = 0;

View file

@ -42,6 +42,7 @@ namespace Audio {
return this->bufferID > other.bufferID; return this->bufferID > other.bufferID;
} }
}; };
// Buffer of decoded PCM16 samples. TODO: Are there better alternatives to use over deque? // Buffer of decoded PCM16 samples. TODO: Are there better alternatives to use over deque?
using SampleBuffer = std::deque<std::array<s16, 2>>; using SampleBuffer = std::deque<std::array<s16, 2>>;
@ -53,6 +54,7 @@ namespace Audio {
std::array<float, 3> gain0, gain1, gain2; std::array<float, 3> gain0, gain1, gain2;
u32 samplePosition; // Sample number into the current audio buffer u32 samplePosition; // Sample number into the current audio buffer
float rateMultiplier;
u16 syncCount; u16 syncCount;
u16 currentBufferID; u16 currentBufferID;
u16 previousBufferID; u16 previousBufferID;
@ -185,7 +187,7 @@ namespace Audio {
~HLE_DSP() override {} ~HLE_DSP() override {}
void reset() override; void reset() override;
void runAudioFrame() override; void runAudioFrame(u64 eventTimestamp) override;
u8* getDspMemory() override { return dspRam.rawMemory.data(); } u8* getDspMemory() override { return dspRam.rawMemory.data(); }

View file

@ -27,7 +27,7 @@ namespace Audio {
~NullDSP() override {} ~NullDSP() override {}
void reset() override; void reset() override;
void runAudioFrame() override; void runAudioFrame(u64 eventTimestamp) override;
u8* getDspMemory() override { return dspRam.data(); } u8* getDspMemory() override { return dspRam.data(); }

View file

@ -83,7 +83,7 @@ namespace Audio {
void reset() override; void reset() override;
// Run 1 slice of DSP instructions and schedule the next audio frame // Run 1 slice of DSP instructions and schedule the next audio frame
void runAudioFrame() override { void runAudioFrame(u64 eventTimestamp) override {
runSlice(); runSlice();
scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2); scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2);
} }

View file

@ -95,7 +95,7 @@ namespace Audio {
scheduler.removeEvent(Scheduler::EventType::RunDSP); scheduler.removeEvent(Scheduler::EventType::RunDSP);
} }
void HLE_DSP::runAudioFrame() { void HLE_DSP::runAudioFrame(u64 eventTimestamp) {
// Signal audio pipe when an audio frame is done // Signal audio pipe when an audio frame is done
if (dspState == DSPState::On) [[likely]] { if (dspState == DSPState::On) [[likely]] {
dspService.triggerPipeEvent(DSPPipeType::Audio); dspService.triggerPipeEvent(DSPPipeType::Audio);
@ -103,7 +103,10 @@ namespace Audio {
// TODO: Should this be called if dspState != DSPState::On? // TODO: Should this be called if dspState != DSPState::On?
outputFrame(); 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) { u16 HLE_DSP::recvData(u32 regId) {
@ -237,10 +240,9 @@ namespace Audio {
auto& status = write.sourceStatuses.status[i]; auto& status = write.sourceStatuses.status[i];
status.enabled = source.enabled; status.enabled = source.enabled;
status.syncCount = source.syncCount; status.syncCount = source.syncCount;
status.currentBufferIDDirty = source.isBufferIDDirty ? 1 : 0; status.currentBufferIDDirty = (source.isBufferIDDirty ? 1 : 0);
status.currentBufferID = source.currentBufferID; status.currentBufferID = source.currentBufferID;
status.previousBufferID = source.previousBufferID; status.previousBufferID = source.previousBufferID;
// TODO: Properly update sample position
status.samplePosition = source.samplePosition; status.samplePosition = source.samplePosition;
source.isBufferIDDirty = false; source.isBufferIDDirty = false;
@ -292,6 +294,10 @@ namespace Audio {
source.sourceType = config.monoOrStereo; source.sourceType = config.monoOrStereo;
} }
if (config.rateMultiplierDirty) {
source.rateMultiplier = (config.rateMultiplier > 0.f) ? config.rateMultiplier : 1.f;
}
if (config.embeddedBufferDirty) { if (config.embeddedBufferDirty) {
// Annoyingly, and only for embedded buffer, whether we use config.playPosition depends on the relevant dirty bit // 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; const u32 playPosition = config.playPositionDirty ? config.playPosition : 0;
@ -434,7 +440,7 @@ namespace Audio {
decodeBuffer(source); decodeBuffer(source);
} else { } else {
constexpr uint maxSampleCount = Audio::samplesInFrame; uint maxSampleCount = uint(float(Audio::samplesInFrame) * source.rateMultiplier);
uint outputCount = 0; uint outputCount = 0;
while (outputCount < maxSampleCount) { while (outputCount < maxSampleCount) {
@ -447,9 +453,9 @@ namespace Audio {
} }
const uint sampleCount = std::min<s32>(maxSampleCount - outputCount, source.currentSamples.size()); const uint sampleCount = std::min<s32>(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; source.samplePosition += sampleCount;
outputCount += sampleCount; outputCount += sampleCount;
} }
@ -618,6 +624,7 @@ namespace Audio {
previousBufferID = 0; previousBufferID = 0;
currentBufferID = 0; currentBufferID = 0;
syncCount = 0; syncCount = 0;
rateMultiplier = 1.f;
buffers = {}; buffers = {};
} }

View file

@ -74,7 +74,7 @@ namespace Audio {
scheduler.removeEvent(Scheduler::EventType::RunDSP); scheduler.removeEvent(Scheduler::EventType::RunDSP);
} }
void NullDSP::runAudioFrame() { void NullDSP::runAudioFrame(u64 eventTimestamp) {
// Signal audio pipe when an audio frame is done // Signal audio pipe when an audio frame is done
if (dspState == DSPState::On) [[likely]] { if (dspState == DSPState::On) [[likely]] {
dspService.triggerPipeEvent(DSPPipeType::Audio); dspService.triggerPipeEvent(DSPPipeType::Audio);

View file

@ -167,7 +167,7 @@ void Emulator::pollScheduler() {
case Scheduler::EventType::UpdateTimers: kernel.pollTimers(); break; case Scheduler::EventType::UpdateTimers: kernel.pollTimers(); break;
case Scheduler::EventType::RunDSP: { case Scheduler::EventType::RunDSP: {
dsp->runAudioFrame(); dsp->runAudioFrame(time);
break; break;
} }