diff --git a/include/audio/dsp_core.hpp b/include/audio/dsp_core.hpp index cc674f07..3f1768ff 100644 --- a/include/audio/dsp_core.hpp +++ b/include/audio/dsp_core.hpp @@ -2,15 +2,17 @@ #include #include #include +#include +#include #include #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; +class Memory; namespace Audio { // There are 160 stereo samples in 1 audio frame, so 320 samples total @@ -44,6 +46,9 @@ namespace Audio { virtual void loadComponent(std::vector& data, u32 programMask, u32 dataMask) = 0; virtual void unloadComponent() = 0; virtual void setSemaphoreMask(u16 value) = 0; + + static Audio::DSPCore::Type typeFromString(std::string inString); + static const char* typeToString(Audio::DSPCore::Type type); }; std::unique_ptr makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService); diff --git a/include/audio/null_core.hpp b/include/audio/null_core.hpp index e06d9988..0c2ddbac 100644 --- a/include/audio/null_core.hpp +++ b/include/audio/null_core.hpp @@ -1,6 +1,8 @@ #pragma once #include + #include "audio/dsp_core.hpp" +#include "memory.hpp" namespace Audio { class NullDSP : public DSPCore { @@ -27,7 +29,7 @@ namespace Audio { u8* getDspMemory() override { return dspRam.data(); } u16 recvData(u32 regId) override; - bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready + 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; diff --git a/include/audio/teakra_core.hpp b/include/audio/teakra_core.hpp index 380d5404..57db0e4a 100644 --- a/include/audio/teakra_core.hpp +++ b/include/audio/teakra_core.hpp @@ -1,5 +1,6 @@ #pragma once #include "audio/dsp_core.hpp" +#include "memory.hpp" #include "swap.hpp" #include "teakra/teakra.h" @@ -35,9 +36,9 @@ namespace Audio { bool isFull() const { return (readPointer ^ writePointer) == wrapBit; } bool isEmpty() const { return (readPointer ^ writePointer) == 0; } - // isWrapped: Are read and write pointers in different memory passes. - // true: xxxx]----[xxxx (data is wrapping around the end of memory) - // false: ----[xxxx]---- + // isWrapped: Are read and write pointers in different memory passes. + // true: xxxx]----[xxxx (data is wrapping around the end of memory) + // false: ----[xxxx]---- bool isWrapped() const { return (readPointer ^ writePointer) >= wrapBit; } }; static_assert(sizeof(PipeStatus) == 10, "Teakra: Pipe Status size is wrong"); diff --git a/include/config.hpp b/include/config.hpp index 155f5961..e5c10f4b 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include "audio/dsp_core.hpp" #include "renderer.hpp" // Remember to initialize every field here to its default value otherwise bad things will happen @@ -15,6 +16,7 @@ struct EmulatorConfig { bool shaderJitEnabled = shaderJitDefault; bool discordRpcEnabled = false; RendererType rendererType = RendererType::OpenGL; + Audio::DSPCore::Type dspType = Audio::DSPCore::Type::Null; bool sdCardInserted = true; bool sdWriteProtected = false; diff --git a/src/config.cpp b/src/config.cpp index cd4e1f79..12b112dc 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -62,6 +62,16 @@ void EmulatorConfig::load() { } } + if (data.contains("Audio")) { + auto audioResult = toml::expect(data.at("Audio")); + if (audioResult.is_ok()) { + auto audio = audioResult.unwrap(); + + auto dspCoreName = toml::find_or(audio, "DSPEmulation", "Null"); + dspType = Audio::DSPCore::typeFromString(dspCoreName); + } + } + if (data.contains("Battery")) { auto batteryResult = toml::expect(data.at("Battery")); if (batteryResult.is_ok()) { @@ -109,6 +119,7 @@ void EmulatorConfig::save() { data["General"]["UsePortableBuild"] = usePortableBuild; data["GPU"]["EnableShaderJIT"] = shaderJitEnabled; data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType)); + data["Audio"]["DSPEmulation"] = std::string(Audio::DSPCore::typeToString(dspType)); data["Battery"]["ChargerPlugged"] = chargerPlugged; data["Battery"]["BatteryPercentage"] = batteryPercentage; diff --git a/src/core/audio/dsp_core.cpp b/src/core/audio/dsp_core.cpp index 5f82c657..e4162e93 100644 --- a/src/core/audio/dsp_core.cpp +++ b/src/core/audio/dsp_core.cpp @@ -3,6 +3,10 @@ #include "audio/null_core.hpp" #include "audio/teakra_core.hpp" +#include +#include +#include + std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService) { std::unique_ptr core; @@ -19,3 +23,30 @@ std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& m mem.setDSPMem(core->getDspMemory()); return core; } + +Audio::DSPCore::Type Audio::DSPCore::typeFromString(std::string inString) { + // Transform to lower-case to make the setting case-insensitive + 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}, + }; + + if (auto search = map.find(inString); search != map.end()) { + return search->second; + } + + printf("Invalid DSP type. Defaulting to null\n"); + return Audio::DSPCore::Type::Null; +} + +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"; + default: return "invalid"; + } +} diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index fafa0f16..f5a6dcae 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -84,7 +84,6 @@ void DSPService::loadComponent(u32 messagePointer) { for (u32 i = 0; i < size; i++) { data[i] = mem.read8(buffer + i); } - printf("Loado compartment: %08X %08X %08X %08X\n", data[0], data[1], data[2], data[3]); log("DSP::LoadComponent (size = %08X, program mask = %X, data mask = %X\n", size, programMask, dataMask); dsp->loadComponent(data, programMask, dataMask); diff --git a/src/emulator.cpp b/src/emulator.cpp index fcab4acf..dbbb0c37 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, scheduler, dspService); + dsp = Audio::makeDSPCore(config.dspType, memory, scheduler, dspService); dspService.setDSPCore(dsp.get()); #ifdef PANDA3DS_ENABLE_DISCORD_RPC