diff --git a/include/audio/dsp_core.hpp b/include/audio/dsp_core.hpp index 498e111f..cc674f07 100644 --- a/include/audio/dsp_core.hpp +++ b/include/audio/dsp_core.hpp @@ -7,20 +7,30 @@ #include "helpers.hpp" #include "logger.hpp" #include "memory.hpp" +#include "scheduler.hpp" // The DSP core must have access to the DSP service to be able to trigger interrupts properly class DSPService; namespace Audio { + // There are 160 stereo samples in 1 audio frame, so 320 samples total + static constexpr u64 samplesInFrame = 160; + // 1 frame = 4096 DSP cycles = 8192 ARM11 cycles + static constexpr u64 cyclesPerFrame = samplesInFrame * 8192; + // For LLE DSP cores, we run the DSP for N cycles at a time, every N*2 arm11 cycles since the ARM11 runs twice as fast + static constexpr u64 lleSlice = 16384; + class DSPCore { protected: Memory& mem; + Scheduler& scheduler; DSPService& dspService; + MAKE_LOG_FUNCTION(log, dspLogger) public: enum class Type { Null, Teakra }; - DSPCore(Memory& mem, DSPService& dspService) : mem(mem), dspService(dspService) {} + DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService) : mem(mem), scheduler(scheduler), dspService(dspService) {} virtual void reset() = 0; virtual void runAudioFrame() = 0; @@ -36,5 +46,5 @@ namespace Audio { virtual void setSemaphoreMask(u16 value) = 0; }; - std::unique_ptr makeDSPCore(DSPCore::Type type, Memory& mem, DSPService& dspService); + std::unique_ptr makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService); } // namespace Audio \ No newline at end of file diff --git a/include/audio/null_core.hpp b/include/audio/null_core.hpp index 2a01fc07..e06d9988 100644 --- a/include/audio/null_core.hpp +++ b/include/audio/null_core.hpp @@ -20,7 +20,7 @@ namespace Audio { void resetAudioPipe(); public: - NullDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspService) {} + NullDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {} void reset() override; void runAudioFrame() override {} diff --git a/include/audio/teakra_core.hpp b/include/audio/teakra_core.hpp index 400a9e71..380d5404 100644 --- a/include/audio/teakra_core.hpp +++ b/include/audio/teakra_core.hpp @@ -65,14 +65,22 @@ namespace Audio { bool signalledData; bool signalledSemaphore; + // Run 1 slice of DSP instructions + void runSlice() { + if (running) { + teakra.Run(Audio::lleSlice); + } + } + public: - TeakraDSP(Memory& mem, DSPService& dspService); + TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService); void reset() override; + + // Run 1 slice of DSP instructions and schedule the next audio frame void runAudioFrame() override { - if (running) { - teakra.Run(16384); - } + runSlice(); + scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2); } u8* getDspMemory() override { return teakra.GetDspMemory().data(); } diff --git a/include/scheduler.hpp b/include/scheduler.hpp index e251c92a..97c50afc 100644 --- a/include/scheduler.hpp +++ b/include/scheduler.hpp @@ -50,7 +50,6 @@ struct Scheduler { // Clear any pending events events.clear(); addEvent(Scheduler::EventType::VBlank, arm11Clock / 60); - addEvent(Scheduler::EventType::RunDSP, 16384 * 2); // Add a dummy event to always keep the scheduler non-empty addEvent(EventType::Panic, std::numeric_limits::max()); diff --git a/src/core/audio/dsp_core.cpp b/src/core/audio/dsp_core.cpp index f29af59a..5f82c657 100644 --- a/src/core/audio/dsp_core.cpp +++ b/src/core/audio/dsp_core.cpp @@ -3,16 +3,16 @@ #include "audio/null_core.hpp" #include "audio/teakra_core.hpp" -std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& mem, DSPService& dspService) { +std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService) { std::unique_ptr core; switch (type) { - case DSPCore::Type::Null: core = std::make_unique(mem, dspService); break; - case DSPCore::Type::Teakra: core = std::make_unique(mem, dspService); break; + case DSPCore::Type::Null: core = std::make_unique(mem, scheduler, dspService); break; + case DSPCore::Type::Teakra: core = std::make_unique(mem, scheduler, dspService); break; default: Helpers::warn("Invalid DSP core selected!"); - core = std::make_unique(mem, dspService); + core = std::make_unique(mem, scheduler, dspService); break; } diff --git a/src/core/audio/teakra_core.cpp b/src/core/audio/teakra_core.cpp index 7c263b1f..1f609187 100644 --- a/src/core/audio/teakra_core.cpp +++ b/src/core/audio/teakra_core.cpp @@ -34,7 +34,8 @@ struct Dsp1 { Segment segments[10]; }; -TeakraDSP::TeakraDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspService), pipeBaseAddr(0), running(false) { +TeakraDSP::TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) + : DSPCore(mem, scheduler, dspService), pipeBaseAddr(0), running(false) { // Set up callbacks for Teakra Teakra::AHBMCallback ahbm; @@ -117,7 +118,6 @@ void TeakraDSP::reset() { // https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { - Helpers::warn("Teakra: Write process pipe"); size &= 0xffff; PipeStatus status = getPipeStatus(channel, PipeDirection::CPUtoDSP); @@ -167,7 +167,7 @@ void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { if (needUpdate) { updatePipeStatus(status); while (!teakra.SendDataIsEmpty(2)) { - runAudioFrame(); + runSlice(); } teakra.SendData(2, status.slot); @@ -175,7 +175,6 @@ void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { } std::vector TeakraDSP::readPipe(u32 channel, u32 peer, u32 size, u32 buffer) { - Helpers::warn("Teakra: Read pipe"); size &= 0xffff; PipeStatus status = getPipeStatus(channel, PipeDirection::DSPtoCPU); @@ -216,7 +215,7 @@ std::vector TeakraDSP::readPipe(u32 channel, u32 peer, u32 size, u32 buffer) if (needUpdate) { updatePipeStatus(status); while (!teakra.SendDataIsEmpty(2)) { - runAudioFrame(); + runSlice(); } teakra.SendData(2, status.slot); @@ -273,20 +272,20 @@ void TeakraDSP::loadComponent(std::vector& data, u32 programMask, u32 dataMa for (int i = 0; i < 3; i++) { do { while (!teakra.RecvDataIsReady(i)) { - runAudioFrame(); + runSlice(); } } while (teakra.RecvData(i) != 1); } } - printf("DSP::LoadComponent: Semaphore value: %X\n", teakra.GetSemaphore()); - // Retrieve the pipe base address while (!teakra.RecvDataIsReady(2)) { - runAudioFrame(); + runSlice(); } - pipeBaseAddr = teakra.RecvData(2); + + // Schedule next DSP event + scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2); loaded = true; } @@ -296,17 +295,19 @@ void TeakraDSP::unloadComponent() { return; } loaded = false; + // Stop scheduling DSP events + scheduler.removeEvent(Scheduler::EventType::RunDSP); // Wait for SEND2 to be ready, then send the shutdown command to the DSP while (!teakra.SendDataIsEmpty(2)) { - runAudioFrame(); + runSlice(); } teakra.SendData(2, 0x8000); // Wait for shutdown to be acknowledged while (!teakra.RecvDataIsReady(2)) { - runAudioFrame(); + runSlice(); } // Read the value and discard it, completing shutdown diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 19a40a18..98b091b8 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -125,7 +125,7 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) { void GPUService::requestInterrupt(GPUInterrupt type) { // HACK: Signal DSP events on GPU interrupt for now until we have the DSP since games need DSP events // Maybe there's a better alternative? - // kernel.signalDSPEvents(); + //kernel.signalDSPEvents(); if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet return; diff --git a/src/emulator.cpp b/src/emulator.cpp index 642b1a02..fcab4acf 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -26,7 +26,7 @@ Emulator::Emulator() { DSPService& dspService = kernel.getServiceManager().getDSP(); - dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Teakra, memory, dspService); + dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Teakra, memory, scheduler, dspService); dspService.setDSPCore(dsp.get()); #ifdef PANDA3DS_ENABLE_DISCORD_RPC @@ -151,7 +151,6 @@ void Emulator::pollScheduler() { case Scheduler::EventType::UpdateTimers: kernel.pollTimers(); break; case Scheduler::EventType::RunDSP: { - scheduler.addEvent(Scheduler::EventType::RunDSP, time + 16384 * 2); dsp->runAudioFrame(); break; }