mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 22:55:40 +12:00
HLE DSP: Handle cycle drifting
This commit is contained in:
parent
e666afd1a3
commit
85bae2e94e
7 changed files with 22 additions and 13 deletions
|
@ -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;
|
||||||
|
|
|
@ -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(); }
|
||||||
|
|
||||||
|
|
|
@ -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(); }
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue