From 2e696deccff7edc5802df49e203e4d025bab292a Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 5 Apr 2024 00:44:31 +0300 Subject: [PATCH 01/10] Add HLE DSP files --- CMakeLists.txt | 3 +- include/audio/dsp_core.hpp | 2 +- include/audio/hle_core.hpp | 46 ++++++++++ src/core/audio/dsp_core.cpp | 9 +- src/core/audio/hle_core.cpp | 166 ++++++++++++++++++++++++++++++++++++ 5 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 include/audio/hle_core.hpp create mode 100644 src/core/audio/hle_core.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b763dff..3dc7e467 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,7 +197,7 @@ set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selecto src/core/applets/error_applet.cpp ) set(AUDIO_SOURCE_FILES src/core/audio/dsp_core.cpp src/core/audio/null_core.cpp src/core/audio/teakra_core.cpp - src/core/audio/miniaudio_device.cpp + src/core/audio/miniaudio_device.cpp src/core/audio/hle_core.cpp ) set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp) @@ -234,6 +234,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/PICA/dynapica/shader_rec_emitter_arm64.hpp include/scheduler.hpp include/applets/error_applet.hpp include/audio/dsp_core.hpp include/audio/null_core.hpp include/audio/teakra_core.hpp include/audio/miniaudio_device.hpp include/ring_buffer.hpp include/bitfield.hpp include/audio/dsp_shared_mem.hpp + include/audio/hle_core.hpp ) cmrc_add_resource_library( diff --git a/include/audio/dsp_core.hpp b/include/audio/dsp_core.hpp index 1a556f28..a4fb1ab1 100644 --- a/include/audio/dsp_core.hpp +++ b/include/audio/dsp_core.hpp @@ -37,7 +37,7 @@ namespace Audio { MAKE_LOG_FUNCTION(log, dspLogger) public: - enum class Type { Null, Teakra }; + enum class Type { Null, Teakra, HLE }; DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService) : mem(mem), scheduler(scheduler), dspService(dspService) {} virtual ~DSPCore() {} diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp new file mode 100644 index 00000000..ae3c29cf --- /dev/null +++ b/include/audio/hle_core.hpp @@ -0,0 +1,46 @@ +#pragma once +#include + +#include "audio/dsp_core.hpp" +#include "audio/dsp_shared_mem.hpp" +#include "memory.hpp" + +namespace Audio { + class HLE_DSP : public DSPCore { + enum class DSPState : u32 { + Off, + On, + Slep, + }; + + // Number of DSP pipes + static constexpr size_t pipeCount = 8; + DSPState dspState; + + std::array, pipeCount> pipeData; // The data of each pipe + std::array dspRam; + + void resetAudioPipe(); + bool loaded = false; // Have we loaded a component? + + public: + HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {} + ~HLE_DSP() override {} + + void reset() override; + void runAudioFrame() override; + + u8* getDspMemory() override { return dspRam.data(); } + + u16 recvData(u32 regId) override; + bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready + void writeProcessPipe(u32 channel, u32 size, u32 buffer) override; + std::vector readPipe(u32 channel, u32 peer, u32 size, u32 buffer) override; + + void loadComponent(std::vector& data, u32 programMask, u32 dataMask) override; + void unloadComponent() override; + void setSemaphore(u16 value) override {} + void setSemaphoreMask(u16 value) override {} + }; + +} // namespace Audio \ No newline at end of file diff --git a/src/core/audio/dsp_core.cpp b/src/core/audio/dsp_core.cpp index e4162e93..ee134fa2 100644 --- a/src/core/audio/dsp_core.cpp +++ b/src/core/audio/dsp_core.cpp @@ -1,5 +1,6 @@ #include "audio/dsp_core.hpp" +#include "audio/hle_core.hpp" #include "audio/null_core.hpp" #include "audio/teakra_core.hpp" @@ -13,6 +14,7 @@ std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& m switch (type) { case DSPCore::Type::Null: core = std::make_unique(mem, scheduler, dspService); break; case DSPCore::Type::Teakra: core = std::make_unique(mem, scheduler, dspService); break; + case DSPCore::Type::HLE: core = std::make_unique(mem, scheduler, dspService); break; default: Helpers::warn("Invalid DSP core selected!"); @@ -29,10 +31,8 @@ Audio::DSPCore::Type Audio::DSPCore::typeFromString(std::string inString) { std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); }); static const std::unordered_map map = { - {"null", Audio::DSPCore::Type::Null}, - {"none", Audio::DSPCore::Type::Null}, - {"lle", Audio::DSPCore::Type::Teakra}, - {"teakra", Audio::DSPCore::Type::Teakra}, + {"null", Audio::DSPCore::Type::Null}, {"none", Audio::DSPCore::Type::Null}, {"lle", Audio::DSPCore::Type::Teakra}, + {"teakra", Audio::DSPCore::Type::Teakra}, {"hle", Audio::DSPCore::Type::HLE}, {"fast", Audio::DSPCore::Type::HLE}, }; if (auto search = map.find(inString); search != map.end()) { @@ -47,6 +47,7 @@ const char* Audio::DSPCore::typeToString(Audio::DSPCore::Type type) { switch (type) { case Audio::DSPCore::Type::Null: return "null"; case Audio::DSPCore::Type::Teakra: return "teakra"; + case Audio::DSPCore::Type::HLE: return "hle"; default: return "invalid"; } } diff --git a/src/core/audio/hle_core.cpp b/src/core/audio/hle_core.cpp new file mode 100644 index 00000000..52576f63 --- /dev/null +++ b/src/core/audio/hle_core.cpp @@ -0,0 +1,166 @@ +#include "audio/hle_core.hpp" + +#include "services/dsp.hpp" + +namespace Audio { + namespace DSPPipeType { + enum : u32 { + Debug = 0, + DMA = 1, + Audio = 2, + Binary = 3, + }; + } + + void HLE_DSP::resetAudioPipe() { + // Hardcoded responses for now + // These are DSP DRAM offsets for various variables + // https://www.3dbrew.org/wiki/DSP_Memory_Region + static constexpr std::array responses = { + 0x000F, // Number of responses + 0xBFFF, // Frame counter + 0x9E92, // Source configs + 0x8680, // Source statuses + 0xA792, // ADPCM coefficients + 0x9430, // DSP configs + 0x8400, // DSP status + 0x8540, // Final samples + 0x9492, // Intermediate mix samples + 0x8710, // Compressor + 0x8410, // Debug + 0xA912, // ?? + 0xAA12, // ?? + 0xAAD2, // ?? + 0xAC52, // Surround sound biquad filter 1 + 0xAC5C // Surround sound biquad filter 2 + }; + + std::vector& audioPipe = pipeData[DSPPipeType::Audio]; + audioPipe.resize(responses.size() * sizeof(u16)); + + // Push back every response to the audio pipe + size_t index = 0; + for (auto e : responses) { + audioPipe[index++] = e & 0xff; + audioPipe[index++] = e >> 8; + } + } + + void HLE_DSP::reset() { + loaded = false; + for (auto& e : pipeData) { + e.clear(); + } + + // Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted + resetAudioPipe(); + } + + void HLE_DSP::loadComponent(std::vector& data, u32 programMask, u32 dataMask) { + if (loaded) { + Helpers::warn("Loading DSP component when already loaded"); + } + + loaded = true; + scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::cyclesPerFrame); + } + + void HLE_DSP::unloadComponent() { + if (!loaded) { + Helpers::warn("Audio: unloadComponent called without a running program"); + } + + loaded = false; + scheduler.removeEvent(Scheduler::EventType::RunDSP); + } + + void HLE_DSP::runAudioFrame() { + // Signal audio pipe when an audio frame is done + if (dspState == DSPState::On) [[likely]] { + dspService.triggerPipeEvent(DSPPipeType::Audio); + } + + scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::cyclesPerFrame); + } + + u16 HLE_DSP::recvData(u32 regId) { + if (regId != 0) { + Helpers::panic("Audio: invalid register in null frontend"); + } + + return dspState == DSPState::On; + } + + void HLE_DSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { + enum class StateChange : u8 { + Initialize = 0, + Shutdown = 1, + Wakeup = 2, + Sleep = 3, + }; + + switch (channel) { + case DSPPipeType::Audio: { + if (size != 4) { + printf("Invalid size written to DSP Audio Pipe\n"); + break; + } + + // Get new state + const u8 state = mem.read8(buffer); + if (state > 3) { + log("WriteProcessPipe::Audio: Unknown state change type"); + } else { + switch (static_cast(state)) { + case StateChange::Initialize: + // TODO: Other initialization stuff here + dspState = DSPState::On; + resetAudioPipe(); + + dspService.triggerPipeEvent(DSPPipeType::Audio); + break; + + case StateChange::Shutdown: + dspState = DSPState::Off; + break; + + default: Helpers::panic("Unimplemented DSP audio pipe state change %d", state); + } + } + break; + } + + case DSPPipeType::Binary: + Helpers::warn("Unimplemented write to binary pipe! Size: %d\n", size); + + // This pipe and interrupt are normally used for requests like AAC decode + dspService.triggerPipeEvent(DSPPipeType::Binary); + break; + + default: log("Audio::HLE_DSP: Wrote to unimplemented pipe %d\n", channel); break; + } + } + + std::vector HLE_DSP::readPipe(u32 pipe, u32 peer, u32 size, u32 buffer) { + if (size & 1) Helpers::panic("Tried to read odd amount of bytes from DSP pipe"); + if (pipe >= pipeCount || size > 0xffff) { + return {}; + } + + if (pipe != DSPPipeType::Audio) { + log("Reading from non-audio pipe! This might be broken, might need to check what pipe is being read from and implement writing to it\n"); + } + + std::vector& data = pipeData[pipe]; + size = std::min(size, data.size()); // Clamp size to the maximum available data size + + if (size == 0) { + return {}; + } + + // Return "size" bytes from the audio pipe and erase them + std::vector out(data.begin(), data.begin() + size); + data.erase(data.begin(), data.begin() + size); + return out; + } +} // namespace Audio From a85ca0459a517aefc1ecdd51ad1fbce53a3e0f42 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 5 Apr 2024 01:13:02 +0300 Subject: [PATCH 02/10] HLE DSP: Proper audio pipe responses --- src/core/audio/hle_core.cpp | 38 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/core/audio/hle_core.cpp b/src/core/audio/hle_core.cpp index 52576f63..791d14cc 100644 --- a/src/core/audio/hle_core.cpp +++ b/src/core/audio/hle_core.cpp @@ -13,27 +13,29 @@ namespace Audio { } void HLE_DSP::resetAudioPipe() { - // Hardcoded responses for now - // These are DSP DRAM offsets for various variables +#define DSPOffset(var) (0x8000 + offsetof(Audio::HLE::SharedMemory, var) / 2) + + // These are DSP shared memory offsets for various variables // https://www.3dbrew.org/wiki/DSP_Memory_Region static constexpr std::array responses = { - 0x000F, // Number of responses - 0xBFFF, // Frame counter - 0x9E92, // Source configs - 0x8680, // Source statuses - 0xA792, // ADPCM coefficients - 0x9430, // DSP configs - 0x8400, // DSP status - 0x8540, // Final samples - 0x9492, // Intermediate mix samples - 0x8710, // Compressor - 0x8410, // Debug - 0xA912, // ?? - 0xAA12, // ?? - 0xAAD2, // ?? - 0xAC52, // Surround sound biquad filter 1 - 0xAC5C // Surround sound biquad filter 2 + 0x000F, // Number of responses + DSPOffset(frameCounter), // Frame counter + DSPOffset(sourceConfigurations), // Source configs + DSPOffset(sourceStatuses), // Source statuses + DSPOffset(adpcmCoefficients), // ADPCM coefficients + DSPOffset(dspConfiguration), // DSP configs + DSPOffset(dspStatus), // DSP status + DSPOffset(finalSamples), // Final samples + DSPOffset(intermediateMixSamples), // Intermediate mix samples + DSPOffset(compressor), // Compressor + DSPOffset(dspDebug), // Debug + DSPOffset(unknown10), // ?? + DSPOffset(unknown11), // ?? + DSPOffset(unknown12), // ?? + DSPOffset(unknown13), // Surround sound biquad filter 1 + DSPOffset(unknown14) // Surround sound biquad filter 2 }; +#undef DSPOffset std::vector& audioPipe = pipeData[DSPPipeType::Audio]; audioPipe.resize(responses.size() * sizeof(u16)); From 4070bea697984b13e9a32128f5c2f04ab2c2ff5c Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 5 Apr 2024 01:31:56 +0300 Subject: [PATCH 03/10] HLE DSP: Add region handling --- include/audio/hle_core.hpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp index ae3c29cf..c9d36c11 100644 --- a/include/audio/hle_core.hpp +++ b/include/audio/hle_core.hpp @@ -18,11 +18,31 @@ namespace Audio { DSPState dspState; std::array, pipeCount> pipeData; // The data of each pipe - std::array dspRam; + Audio::HLE::DspMemory dspRam; void resetAudioPipe(); bool loaded = false; // Have we loaded a component? + // Get the index for the current region we'll be reading. Returns the region with the highest frame counter + // Accounting for whether one of the frame counters has wrapped around + usize readRegionIndex() const { + const auto counter0 = dspRam.region0.frameCounter; + const auto counter1 = dspRam.region1.frameCounter; + + // Handle wraparound cases first + if (counter0 == 0xffff && counter1 != 0xfffe) { + return 1; + } else if (counter1 == 0xffff && counter0 != 0xfffe) { + return 0; + } else { + return counter0 > counter1 ? 0 : 0; + } + } + + // DSP shared memory is double buffered; One region is being written to while the other one is being read from + Audio::HLE::SharedMemory& readRegion() { return readRegionIndex() == 0 ? dspRam.region0 : dspRam.region1; } + Audio::HLE::SharedMemory& writeRegion() { return readRegionIndex() == 0 ? dspRam.region1 : dspRam.region0; } + public: HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {} ~HLE_DSP() override {} @@ -30,7 +50,7 @@ namespace Audio { void reset() override; void runAudioFrame() override; - u8* getDspMemory() override { return dspRam.data(); } + u8* getDspMemory() override { return dspRam.rawMemory.data(); } u16 recvData(u32 regId) override; bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready From 2548bde538837d5a76dd95721dcf4ecfbc110354 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 5 Apr 2024 01:48:01 +0300 Subject: [PATCH 04/10] HLE DSP: Add frame types --- include/audio/hle_core.hpp | 20 ++++++++++++++++++++ src/core/audio/hle_core.cpp | 6 ++++++ 2 files changed, 26 insertions(+) diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp index c9d36c11..202b9308 100644 --- a/include/audio/hle_core.hpp +++ b/include/audio/hle_core.hpp @@ -7,6 +7,24 @@ namespace Audio { class HLE_DSP : public DSPCore { + // The audio frame types are public in case we want to use them for unit tests + public: + template + using Sample = std::array; + + template + using Frame = std::array, 160>; + + template + using MonoFrame = Frame; + + template + using StereoFrame = Frame; + + template + using QuadFrame = Frame; + + private: enum class DSPState : u32 { Off, On, @@ -43,6 +61,8 @@ namespace Audio { Audio::HLE::SharedMemory& readRegion() { return readRegionIndex() == 0 ? dspRam.region0 : dspRam.region1; } Audio::HLE::SharedMemory& writeRegion() { return readRegionIndex() == 0 ? dspRam.region1 : dspRam.region0; } + StereoFrame generateFrame(); + void outputFrame(); public: HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {} ~HLE_DSP() override {} diff --git a/src/core/audio/hle_core.cpp b/src/core/audio/hle_core.cpp index 791d14cc..32697534 100644 --- a/src/core/audio/hle_core.cpp +++ b/src/core/audio/hle_core.cpp @@ -82,6 +82,7 @@ namespace Audio { dspService.triggerPipeEvent(DSPPipeType::Audio); } + outputFrame(); scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::cyclesPerFrame); } @@ -165,4 +166,9 @@ namespace Audio { data.erase(data.begin(), data.begin() + size); return out; } + + void HLE_DSP::outputFrame() { + StereoFrame frame = generateFrame(); + Helpers::panic("HLE DSP: Output frame"); + } } // namespace Audio From 5da93d17bd584fee525a46ba632c9616d34b96b4 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 5 Apr 2024 19:42:18 +0300 Subject: [PATCH 05/10] HLE DSP: More of it --- include/audio/hle_core.hpp | 24 +++++++++++++++++++++++- src/core/audio/hle_core.cpp | 27 +++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp index 202b9308..f957284d 100644 --- a/include/audio/hle_core.hpp +++ b/include/audio/hle_core.hpp @@ -6,6 +6,27 @@ #include "memory.hpp" namespace Audio { + struct DSPSource { + std::array gain0, gain1, gain2; + + // Audio buffer information + // https://www.3dbrew.org/wiki/DSP_Memory_Region + struct Buffer { + u32 paddr; // Physical address of the buffer + u32 sampleCount; // Total number of samples + u8 adpcmScale; // ADPCM predictor/scale + u8 pad; // Unknown + + std::array previousSamples; // ADPCM y[n-1] and y[n-2] + bool adpcmDirty; + bool looping; + u16 bufferID; + }; + + void reset(); + DSPSource() { reset(); } + }; + class HLE_DSP : public DSPCore { // The audio frame types are public in case we want to use them for unit tests public: @@ -24,6 +45,7 @@ namespace Audio { template using QuadFrame = Frame; + using Source = Audio::DSPSource; private: enum class DSPState : u32 { Off, @@ -61,7 +83,7 @@ namespace Audio { Audio::HLE::SharedMemory& readRegion() { return readRegionIndex() == 0 ? dspRam.region0 : dspRam.region1; } Audio::HLE::SharedMemory& writeRegion() { return readRegionIndex() == 0 ? dspRam.region1 : dspRam.region0; } - StereoFrame generateFrame(); + void generateFrame(StereoFrame& frame); void outputFrame(); public: HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {} diff --git a/src/core/audio/hle_core.cpp b/src/core/audio/hle_core.cpp index 32697534..65bcb2c6 100644 --- a/src/core/audio/hle_core.cpp +++ b/src/core/audio/hle_core.cpp @@ -1,5 +1,7 @@ #include "audio/hle_core.hpp" +#include + #include "services/dsp.hpp" namespace Audio { @@ -168,7 +170,28 @@ namespace Audio { } void HLE_DSP::outputFrame() { - StereoFrame frame = generateFrame(); - Helpers::panic("HLE DSP: Output frame"); + StereoFrame frame; + generateFrame(frame); + + if (audioEnabled) { + // Wait until we've actually got room to push our frame + while (sampleBuffer.size() + 2 > sampleBuffer.Capacity()) { + std::this_thread::sleep_for(std::chrono::milliseconds{1}); + } + + sampleBuffer.push(frame.data(), frame.size()); + } } + + void HLE_DSP::generateFrame(StereoFrame& frame) { + using namespace Audio::HLE; + SharedMemory& read = readRegion(); + SharedMemory& write = writeRegion(); + + for (int source = 0; source < sourceCount; source++) { + Helpers::panic("Panda"); + } + } + + void DSPSource::reset() {} } // namespace Audio From 43a1c894783edf902b1de64fc23eafd3a789e5c7 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 5 Apr 2024 20:53:17 +0300 Subject: [PATCH 06/10] HLE DSP: Init/deinit sources better --- include/audio/hle_core.hpp | 7 +++++-- src/core/audio/hle_core.cpp | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp index f957284d..75d02bca 100644 --- a/include/audio/hle_core.hpp +++ b/include/audio/hle_core.hpp @@ -23,6 +23,8 @@ namespace Audio { u16 bufferID; }; + int index = 0; // Index of the voice in [0, 23] for debugging + void reset(); DSPSource() { reset(); } }; @@ -57,7 +59,8 @@ namespace Audio { static constexpr size_t pipeCount = 8; DSPState dspState; - std::array, pipeCount> pipeData; // The data of each pipe + std::array, pipeCount> pipeData; // The data of each pipe + std::array sources; // DSP voices Audio::HLE::DspMemory dspRam; void resetAudioPipe(); @@ -86,7 +89,7 @@ namespace Audio { void generateFrame(StereoFrame& frame); void outputFrame(); public: - HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {} + HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService); ~HLE_DSP() override {} void reset() override; diff --git a/src/core/audio/hle_core.cpp b/src/core/audio/hle_core.cpp index 65bcb2c6..6b0a32c9 100644 --- a/src/core/audio/hle_core.cpp +++ b/src/core/audio/hle_core.cpp @@ -14,6 +14,13 @@ namespace Audio { }; } + HLE_DSP::HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) { + // Set up source indices + for (int i = 0; i < sources.size(); i++) { + sources[i].index = i; + } + } + void HLE_DSP::resetAudioPipe() { #define DSPOffset(var) (0x8000 + offsetof(Audio::HLE::SharedMemory, var) / 2) @@ -56,6 +63,10 @@ namespace Audio { e.clear(); } + for (auto& source : sources) { + source.reset(); + } + // Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted resetAudioPipe(); } @@ -189,6 +200,7 @@ namespace Audio { SharedMemory& write = writeRegion(); for (int source = 0; source < sourceCount; source++) { + //updateSourceConfig(sources[source]); Helpers::panic("Panda"); } } From 37f9f5d894cedd752e28683d0af782b55c61f3f1 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 6 Apr 2024 00:54:15 +0300 Subject: [PATCH 07/10] HLE DSP: Track voice status better --- include/audio/hle_core.hpp | 3 +++ src/core/audio/hle_core.cpp | 53 ++++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp index 75d02bca..d4d72b62 100644 --- a/include/audio/hle_core.hpp +++ b/include/audio/hle_core.hpp @@ -8,6 +8,8 @@ namespace Audio { struct DSPSource { std::array gain0, gain1, gain2; + u16 syncCount; + bool enabled; // Audio buffer information // https://www.3dbrew.org/wiki/DSP_Memory_Region @@ -86,6 +88,7 @@ namespace Audio { Audio::HLE::SharedMemory& readRegion() { return readRegionIndex() == 0 ? dspRam.region0 : dspRam.region1; } Audio::HLE::SharedMemory& writeRegion() { return readRegionIndex() == 0 ? dspRam.region1 : dspRam.region0; } + void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config); void generateFrame(StereoFrame& frame); void outputFrame(); public: diff --git a/src/core/audio/hle_core.cpp b/src/core/audio/hle_core.cpp index 6b0a32c9..b27dd10b 100644 --- a/src/core/audio/hle_core.cpp +++ b/src/core/audio/hle_core.cpp @@ -199,11 +199,56 @@ namespace Audio { SharedMemory& read = readRegion(); SharedMemory& write = writeRegion(); - for (int source = 0; source < sourceCount; source++) { - //updateSourceConfig(sources[source]); - Helpers::panic("Panda"); + for (int i = 0; i < sourceCount; i++) { + // Update source configuration from the read region of shared memory + auto& config = read.sourceConfigurations.config[i]; + auto& source = sources[i]; + updateSourceConfig(source, config); + + // Generate audio + + // Update write region of shared memory + auto& status = write.sourceStatuses.status[i]; + status.isEnabled = source.enabled; + status.syncCount = source.syncCount; + //status.lastBufferID=0,status.currentBufferID = 1; status.currentBufferIDDirty = 1; + //status.bufferPosition = } } - void DSPSource::reset() {} + void HLE_DSP::updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config) { + // Check if the any dirty bit is set, otherwise exit early + if (!config.dirtyRaw) { + return; + } + + if (config.enableDirty) { + config.enableDirty = 0; + source.enabled = config.enable != 0; + + printf("Voice %d enable set to %d\n", source.index, source.enabled); + } + + if (config.syncCountDirty) { + config.syncCountDirty = 0; + source.syncCount = config.syncCount; + } + + if (config.resetFlag) { + config.resetFlag = 0; + printf("Reset voice %d\n", source.index); + } + + if (config.partialResetFlag) { + config.partialResetFlag = 0; + printf("Partially reset voice %d\n", source.index); + } + + config.dirtyRaw = 0; + } + + void DSPSource::reset() { + enabled = false; + syncCount = 0; + } } // namespace Audio From 1c355041fad319d60b1ef064bd6df2ea76cd0a95 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 6 Apr 2024 16:53:29 +0300 Subject: [PATCH 08/10] HLE DSP: Add embedded buffers --- include/audio/hle_core.hpp | 26 ++++++++++++++++----- src/core/audio/hle_core.cpp | 45 +++++++++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp index d4d72b62..46caf2f5 100644 --- a/include/audio/hle_core.hpp +++ b/include/audio/hle_core.hpp @@ -1,5 +1,7 @@ #pragma once #include +#include +#include #include "audio/dsp_core.hpp" #include "audio/dsp_shared_mem.hpp" @@ -7,24 +9,38 @@ namespace Audio { struct DSPSource { - std::array gain0, gain1, gain2; - u16 syncCount; - bool enabled; - // Audio buffer information // https://www.3dbrew.org/wiki/DSP_Memory_Region struct Buffer { u32 paddr; // Physical address of the buffer u32 sampleCount; // Total number of samples u8 adpcmScale; // ADPCM predictor/scale - u8 pad; // Unknown + u8 pad1; // Unknown std::array previousSamples; // ADPCM y[n-1] and y[n-2] bool adpcmDirty; bool looping; u16 bufferID; + u8 pad2; + + u32 playPosition = 0; // Current position in the buffer + bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer? + bool hasPlayedOnce = false; // Has the buffer been played at least once before? + + bool operator<(const Buffer& other) const { + // Lower ID = Higher priority + // If this buffer ID is greater than the other one, then this buffer has a lower priority + return this->bufferID > other.bufferID; + } }; + using BufferQueue = std::priority_queue; + + std::array gain0, gain1, gain2; + u16 syncCount; + bool enabled; + + BufferQueue buffers; int index = 0; // Index of the voice in [0, 23] for debugging void reset(); diff --git a/src/core/audio/hle_core.cpp b/src/core/audio/hle_core.cpp index b27dd10b..06d15945 100644 --- a/src/core/audio/hle_core.cpp +++ b/src/core/audio/hle_core.cpp @@ -1,6 +1,7 @@ #include "audio/hle_core.hpp" #include +#include #include "services/dsp.hpp" @@ -206,6 +207,9 @@ namespace Audio { updateSourceConfig(source, config); // Generate audio + if (source.enabled) { + + } // Update write region of shared memory auto& status = write.sourceStatuses.status[i]; @@ -225,8 +229,6 @@ namespace Audio { if (config.enableDirty) { config.enableDirty = 0; source.enabled = config.enable != 0; - - printf("Voice %d enable set to %d\n", source.index, source.enabled); } if (config.syncCountDirty) { @@ -236,12 +238,45 @@ namespace Audio { if (config.resetFlag) { config.resetFlag = 0; - printf("Reset voice %d\n", source.index); + source.reset(); } if (config.partialResetFlag) { config.partialResetFlag = 0; - printf("Partially reset voice %d\n", source.index); + source.buffers = {}; + } + + if (config.embeddedBufferDirty) { + config.embeddedBufferDirty = 0; + if (s32(config.length) >= 0) [[likely]] { + // TODO: Add sample format and channel count + Source::Buffer buffer{ + .paddr = config.physicalAddress, + .sampleCount = config.length, + .adpcmScale = u8(config.adpcm_ps), + .previousSamples = {s16(config.adpcm_yn[0]), s16(config.adpcm_yn[1])}, + .adpcmDirty = config.adpcmDirty != 0, + .looping = config.isLooping != 0, + .bufferID = config.bufferID, + .playPosition = config.playPosition, + .fromQueue = false, + .hasPlayedOnce = false, + }; + + source.buffers.emplace(std::move(buffer)); + } else { + log("Invalid embedded buffer size for DSP voice %d\n", source.index); + } + } + + if (config.partialEmbeddedBufferDirty) { + config.partialEmbeddedBufferDirty = 0; + printf("Partial embedded buffer dirty for voice %d\n", source.index); + } + + if (config.bufferQueueDirty) { + config.bufferQueueDirty = 0; + printf("Buffer queue dirty for voice %d\n", source.index); } config.dirtyRaw = 0; @@ -250,5 +285,7 @@ namespace Audio { void DSPSource::reset() { enabled = false; syncCount = 0; + + buffers = {}; } } // namespace Audio From 1cc3bbf68da370b52396e60842235041a87b8e8b Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 6 Apr 2024 19:12:58 +0300 Subject: [PATCH 09/10] HLE DSP: Fix format and source type for audio buffers --- include/audio/hle_core.hpp | 27 +++++++++++++++++++++++++++ include/memory.hpp | 5 ++++- src/core/audio/hle_core.cpp | 28 ++++++++++++++++++++++++---- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp index 46caf2f5..6d7b3ad1 100644 --- a/include/audio/hle_core.hpp +++ b/include/audio/hle_core.hpp @@ -8,6 +8,9 @@ #include "memory.hpp" namespace Audio { + using SampleFormat = HLE::SourceConfiguration::Configuration::Format; + using SourceType = HLE::SourceConfiguration::Configuration::MonoOrStereo; + struct DSPSource { // Audio buffer information // https://www.3dbrew.org/wiki/DSP_Memory_Region @@ -24,6 +27,9 @@ namespace Audio { u8 pad2; u32 playPosition = 0; // Current position in the buffer + SampleFormat format; + SourceType sourceType; + bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer? bool hasPlayedOnce = false; // Has the buffer been played at least once before? @@ -81,6 +87,9 @@ namespace Audio { std::array sources; // DSP voices Audio::HLE::DspMemory dspRam; + SampleFormat sampleFormat = SampleFormat::ADPCM; + SourceType sourceType = SourceType::Stereo; + void resetAudioPipe(); bool loaded = false; // Have we loaded a component? @@ -104,9 +113,27 @@ namespace Audio { Audio::HLE::SharedMemory& readRegion() { return readRegionIndex() == 0 ? dspRam.region0 : dspRam.region1; } Audio::HLE::SharedMemory& writeRegion() { return readRegionIndex() == 0 ? dspRam.region1 : dspRam.region0; } + // Get a pointer of type T* to the data starting from physical address paddr + template + T* getPointerPhys(u32 paddr, u32 size = 0) { + if (paddr >= PhysicalAddrs::FCRAM && paddr + size <= PhysicalAddrs::FCRAMEnd) { + u8* fcram = mem.getFCRAM(); + u32 index = paddr - PhysicalAddrs::FCRAM; + + return (T*)&fcram[index]; + } else if (paddr >= PhysicalAddrs::DSP_RAM && paddr + size <= PhysicalAddrs::DSP_RAM_End) { + u32 index = paddr - PhysicalAddrs::DSP_RAM; + return (T*)&dspRam.rawMemory[index]; + } else [[unlikely]] { + Helpers::warn("[DSP] Tried to access unknown physical address: %08X", paddr); + return nullptr; + } + } + void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config); void generateFrame(StereoFrame& frame); void outputFrame(); + void dumpBuffer(const Source::Buffer& buffer); public: HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService); ~HLE_DSP() override {} diff --git a/include/memory.hpp b/include/memory.hpp index 1b6e622c..33ccbae5 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -19,7 +19,10 @@ namespace PhysicalAddrs { VRAM = 0x18000000, VRAMEnd = VRAM + 0x005FFFFF, FCRAM = 0x20000000, - FCRAMEnd = FCRAM + 0x07FFFFFF + FCRAMEnd = FCRAM + 0x07FFFFFF, + + DSP_RAM = 0x1FF00000, + DSP_RAM_End = DSP_RAM + 0x0007FFFF }; } diff --git a/src/core/audio/hle_core.cpp b/src/core/audio/hle_core.cpp index 06d15945..f9f1248c 100644 --- a/src/core/audio/hle_core.cpp +++ b/src/core/audio/hle_core.cpp @@ -59,7 +59,13 @@ namespace Audio { } void HLE_DSP::reset() { + dspState = DSPState::Off; loaded = false; + + // Initialize these to some sane defaults + sampleFormat = SampleFormat::ADPCM; + sourceType = SourceType::Stereo; + for (auto& e : pipeData) { e.clear(); } @@ -207,16 +213,19 @@ namespace Audio { updateSourceConfig(source, config); // Generate audio - if (source.enabled) { - + if (source.enabled && !source.buffers.empty()) { + const auto& buffer = source.buffers.top(); + const u8* data = getPointerPhys(buffer.paddr); + + if (data != nullptr) { + // TODO + } } // Update write region of shared memory auto& status = write.sourceStatuses.status[i]; status.isEnabled = source.enabled; status.syncCount = source.syncCount; - //status.lastBufferID=0,status.currentBufferID = 1; status.currentBufferIDDirty = 1; - //status.bufferPosition = } } @@ -245,6 +254,15 @@ namespace Audio { config.partialResetFlag = 0; source.buffers = {}; } + + // TODO: Should we check bufferQueueDirty here too? + if (config.formatDirty || config.embeddedBufferDirty) { + sampleFormat = config.format; + } + + if (config.monoOrStereoDirty || config.embeddedBufferDirty) { + sourceType = config.monoOrStereo; + } if (config.embeddedBufferDirty) { config.embeddedBufferDirty = 0; @@ -259,6 +277,8 @@ namespace Audio { .looping = config.isLooping != 0, .bufferID = config.bufferID, .playPosition = config.playPosition, + .format = sampleFormat, + .sourceType = sourceType, .fromQueue = false, .hasPlayedOnce = false, }; From ea6818eb4ba67750df8f87fe47f889083650c22c Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 14 Apr 2024 13:13:19 +0300 Subject: [PATCH 10/10] HLE DSP: Formatting --- src/core/audio/dsp_core.cpp | 12 ++++++------ src/core/audio/hle_core.cpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/audio/dsp_core.cpp b/src/core/audio/dsp_core.cpp index ee134fa2..01cee11e 100644 --- a/src/core/audio/dsp_core.cpp +++ b/src/core/audio/dsp_core.cpp @@ -1,20 +1,20 @@ #include "audio/dsp_core.hpp" -#include "audio/hle_core.hpp" -#include "audio/null_core.hpp" -#include "audio/teakra_core.hpp" - #include #include #include +#include "audio/hle_core.hpp" +#include "audio/null_core.hpp" +#include "audio/teakra_core.hpp" + 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, scheduler, dspService); break; case DSPCore::Type::Teakra: core = std::make_unique(mem, scheduler, dspService); break; - case DSPCore::Type::HLE: core = std::make_unique(mem, scheduler, dspService); break; + case DSPCore::Type::HLE: core = std::make_unique(mem, scheduler, dspService); break; default: Helpers::warn("Invalid DSP core selected!"); @@ -47,7 +47,7 @@ const char* Audio::DSPCore::typeToString(Audio::DSPCore::Type type) { switch (type) { case Audio::DSPCore::Type::Null: return "null"; case Audio::DSPCore::Type::Teakra: return "teakra"; - case Audio::DSPCore::Type::HLE: return "hle"; + case Audio::DSPCore::Type::HLE: return "hle"; default: return "invalid"; } } diff --git a/src/core/audio/hle_core.cpp b/src/core/audio/hle_core.cpp index f9f1248c..245894ce 100644 --- a/src/core/audio/hle_core.cpp +++ b/src/core/audio/hle_core.cpp @@ -108,7 +108,7 @@ namespace Audio { u16 HLE_DSP::recvData(u32 regId) { if (regId != 0) { - Helpers::panic("Audio: invalid register in null frontend"); + Helpers::panic("Audio: invalid register in HLE frontend"); } return dspState == DSPState::On;