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] 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