From 4cb66217c21325c9f5ead231d4ea52d78ffd7fed Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 8 Feb 2025 15:32:04 +0200 Subject: [PATCH 1/7] Try to cross-compile Libretro core for arm64 (#717) * Try to cross-compile Libretro core for arm64 * Bonk * Update Hydra_Build.yml --- .github/workflows/Hydra_Build.yml | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/Hydra_Build.yml b/.github/workflows/Hydra_Build.yml index df851b69..e040a1f8 100644 --- a/.github/workflows/Hydra_Build.yml +++ b/.github/workflows/Hydra_Build.yml @@ -180,3 +180,36 @@ jobs: with: name: Android Hydra core path: '${{github.workspace}}/build/libAlber.so' + + ARM-Libretro: + runs-on: ubuntu-24.04-arm + + steps: + - uses: actions/checkout@v4 + - name: Fetch submodules + run: git submodule update --init --recursive + + - name: Install misc packages + run: | + sudo apt-get update && sudo apt install libx11-dev libxext-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev + + - name: Install newer Clang + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x ./llvm.sh + sudo ./llvm.sh 17 + + - name: Configure CMake + run: | + cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DENABLE_USER_BUILD=ON -DBUILD_LIBRETRO_CORE=ON -DENABLE_VULKAN=OFF -DCRYPTOPP_OPT_DISABLE_ASM=ON + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Upload Libretro core + uses: actions/upload-artifact@v4 + with: + name: Linux arm64 Libretro core + path: | + ${{github.workspace}}/build/panda3ds_libretro.so + ${{github.workspace}}/docs/libretro/panda3ds_libretro.info From 042ab6de0326a4fadccc6eeff8f9ef3ca7ed9641 Mon Sep 17 00:00:00 2001 From: Jonian Guveli Date: Sun, 9 Feb 2025 05:04:36 +0200 Subject: [PATCH 2/7] [WIP] Libretro: Add audio support (#714) * Libretro: Add audio support * Adding audio interface part 1 * Audio device pt 2 * More audio device * More audio device * Morea uudi odevice * More audio device * More audio device * More audio device --------- Co-authored-by: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> --- CMakeLists.txt | 3 +- include/audio/audio_device.hpp | 9 ++++ include/audio/audio_device_interface.hpp | 36 ++++++++++++++ include/audio/libretro_audio_device.hpp | 60 ++++++++++++++++++++++++ include/audio/miniaudio_device.hpp | 24 ++++------ include/emulator.hpp | 7 +-- src/core/audio/miniaudio_device.cpp | 7 +-- src/libretro_core.cpp | 2 + 8 files changed, 125 insertions(+), 23 deletions(-) create mode 100644 include/audio/audio_device.hpp create mode 100644 include/audio/audio_device_interface.hpp create mode 100644 include/audio/libretro_audio_device.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index be70036c..f37ce28a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -402,7 +402,8 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp include/services/dsp_firmware_db.hpp include/frontend_settings.hpp include/fs/archive_twl_photo.hpp - include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp include/services/ns.hpp + include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp include/services/ns.hpp include/audio/audio_device.hpp + include/audio/audio_device_interface.hpp include/audio/libretro_audio_device.hpp ) cmrc_add_resource_library( diff --git a/include/audio/audio_device.hpp b/include/audio/audio_device.hpp new file mode 100644 index 00000000..5c1f5fc1 --- /dev/null +++ b/include/audio/audio_device.hpp @@ -0,0 +1,9 @@ +#pragma once + +#ifdef __LIBRETRO__ +#include "audio/libretro_audio_device.hpp" +using AudioDevice = LibretroAudioDevice; +#else +#include "audio/miniaudio_device.hpp" +using AudioDevice = MiniAudioDevice; +#endif \ No newline at end of file diff --git a/include/audio/audio_device_interface.hpp b/include/audio/audio_device_interface.hpp new file mode 100644 index 00000000..de70c77a --- /dev/null +++ b/include/audio/audio_device_interface.hpp @@ -0,0 +1,36 @@ +#pragma once +#include + +#include "config.hpp" +#include "helpers.hpp" +#include "ring_buffer.hpp" + +class AudioDeviceInterface { + protected: + static constexpr usize maxFrameCount = 0x2000; + + using Samples = Common::RingBuffer; + using RenderBatchCallback = usize (*)(const s16*, usize); + + Samples* samples = nullptr; + + const AudioDeviceConfig& audioSettings; + // Store the last stereo sample we output. We play this when underruning to avoid pops. + std::array lastStereoSample{}; + + public: + AudioDeviceInterface(Samples* samples, const AudioDeviceConfig& audioSettings) : samples(samples), audioSettings(audioSettings) {} + + bool running = false; + Samples* getSamples() { return samples; } + + // If safe is on, we create a null audio device + virtual void init(Samples& samples, bool safe = false) = 0; + virtual void close() = 0; + + virtual void start() = 0; + virtual void stop() = 0; + + // Only used for audio devices that render multiple audio frames in one go, eg the libretro audio device. + virtual void renderBatch(RenderBatchCallback callback) {} +}; \ No newline at end of file diff --git a/include/audio/libretro_audio_device.hpp b/include/audio/libretro_audio_device.hpp new file mode 100644 index 00000000..047427ce --- /dev/null +++ b/include/audio/libretro_audio_device.hpp @@ -0,0 +1,60 @@ +#pragma once +#include + +#include "audio/audio_device_interface.hpp" + +class LibretroAudioDevice : public AudioDeviceInterface { + bool initialized = false; + + public: + LibretroAudioDevice(const AudioDeviceConfig& audioSettings) : AudioDeviceInterface(nullptr, audioSettings), initialized(false) { + running = false; + } + + void init(Samples& samples, bool safe = false) override { + this->samples = &samples; + + initialized = true; + running = false; + } + + void close() override { + initialized = false; + running = false; + }; + + void start() override { running = true; } + void stop() override { running = false; }; + + void renderBatch(RenderBatchCallback callback) override { + if (running) { + static constexpr usize frameCount = 774; + static constexpr usize channelCount = 2; + static s16 audioBuffer[frameCount * channelCount]; + + usize samplesWritten = 0; + samplesWritten += samples->pop(audioBuffer, frameCount * channelCount); + + // Get the last sample for underrun handling + if (samplesWritten != 0) { + std::memcpy(&lastStereoSample[0], &audioBuffer[(samplesWritten - 1) * 2], sizeof(lastStereoSample)); + } + + // If underruning, copy the last output sample + { + s16* pointer = &audioBuffer[samplesWritten * 2]; + s16 l = lastStereoSample[0]; + s16 r = lastStereoSample[1]; + + for (usize i = samplesWritten; i < frameCount; i++) { + *pointer++ = l; + *pointer++ = r; + } + } + + callback(audioBuffer, sizeof(audioBuffer) / (channelCount * sizeof(s16))); + } + } + + bool isInitialized() const { return initialized; } +}; \ No newline at end of file diff --git a/include/audio/miniaudio_device.hpp b/include/audio/miniaudio_device.hpp index 0363aa44..deb6e31a 100644 --- a/include/audio/miniaudio_device.hpp +++ b/include/audio/miniaudio_device.hpp @@ -3,39 +3,31 @@ #include #include -#include "config.hpp" -#include "helpers.hpp" +#include "audio/audio_device_interface.hpp" #include "miniaudio.h" -#include "ring_buffer.hpp" -class MiniAudioDevice { - using Samples = Common::RingBuffer; +class MiniAudioDevice : public AudioDeviceInterface { static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate static constexpr ma_uint32 channelCount = 2; // Audio output is stereo + bool initialized = false; + ma_device device; ma_context context; ma_device_config deviceConfig; - Samples* samples = nullptr; - - const AudioDeviceConfig& audioSettings; - - bool initialized = false; - bool running = false; // Store the last stereo sample we output. We play this when underruning to avoid pops. - std::array lastStereoSample; std::vector audioDevices; public: MiniAudioDevice(const AudioDeviceConfig& audioSettings); // If safe is on, we create a null audio device - void init(Samples& samples, bool safe = false); - void close(); + void init(Samples& samples, bool safe = false) override; + void close() override; - void start(); - void stop(); + void start() override; + void stop() override; bool isInitialized() const { return initialized; } }; \ No newline at end of file diff --git a/include/emulator.hpp b/include/emulator.hpp index a222a021..326eb232 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -7,8 +7,8 @@ #include #include "PICA/gpu.hpp" +#include "audio/audio_device.hpp" #include "audio/dsp_core.hpp" -#include "audio/miniaudio_device.hpp" #include "cheats.hpp" #include "config.hpp" #include "cpu.hpp" @@ -48,14 +48,14 @@ class Emulator { Scheduler scheduler; Crypto::AESEngine aesEngine; - MiniAudioDevice audioDevice; + AudioDevice audioDevice; Cheats cheats; public: static constexpr u32 width = 400; static constexpr u32 height = 240 * 2; // * 2 because 2 screens ROMType romType = ROMType::None; - bool running = false; // Is the emulator running a game? + bool running = false; // Is the emulator running a game? private: #ifdef PANDA3DS_ENABLE_HTTP_SERVER @@ -126,6 +126,7 @@ class Emulator { LuaManager& getLua() { return lua; } Scheduler& getScheduler() { return scheduler; } Memory& getMemory() { return memory; } + AudioDeviceInterface& getAudioDevice() { return audioDevice; } RendererType getRendererType() const { return config.rendererType; } Renderer* getRenderer() { return gpu.getRenderer(); } diff --git a/src/core/audio/miniaudio_device.cpp b/src/core/audio/miniaudio_device.cpp index 550fb039..8e2a1e71 100644 --- a/src/core/audio/miniaudio_device.cpp +++ b/src/core/audio/miniaudio_device.cpp @@ -7,8 +7,9 @@ #include "helpers.hpp" -MiniAudioDevice::MiniAudioDevice(const AudioDeviceConfig& audioSettings) - : initialized(false), running(false), samples(nullptr), audioSettings(audioSettings) {} +MiniAudioDevice::MiniAudioDevice(const AudioDeviceConfig& audioSettings) : AudioDeviceInterface(nullptr, audioSettings), initialized(false) { + running = false; +} void MiniAudioDevice::init(Samples& samples, bool safe) { this->samples = &samples; @@ -212,4 +213,4 @@ void MiniAudioDevice::close() { ma_device_uninit(&device); ma_context_uninit(&context); } -} +} \ No newline at end of file diff --git a/src/libretro_core.cpp b/src/libretro_core.cpp index 727da8d2..a9783320 100644 --- a/src/libretro_core.cpp +++ b/src/libretro_core.cpp @@ -389,6 +389,8 @@ void retro_run() { emulator->runFrame(); videoCallback(RETRO_HW_FRAME_BUFFER_VALID, emulator->width, emulator->height, 0); + // Call audio batch callback + emulator->getAudioDevice().renderBatch(audioBatchCallback); } void retro_set_controller_port_device(uint port, uint device) {} From a376bb5c9bcb22bbfaf765ba78c9e46fd13e257a Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 9 Feb 2025 14:31:19 +0200 Subject: [PATCH 3/7] Libretro audio device: Fix frame count --- include/audio/libretro_audio_device.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/audio/libretro_audio_device.hpp b/include/audio/libretro_audio_device.hpp index 047427ce..5b3a70c1 100644 --- a/include/audio/libretro_audio_device.hpp +++ b/include/audio/libretro_audio_device.hpp @@ -28,7 +28,8 @@ class LibretroAudioDevice : public AudioDeviceInterface { void renderBatch(RenderBatchCallback callback) override { if (running) { - static constexpr usize frameCount = 774; + static constexpr usize sampleRate = 32768; // 3DS samples per second + static constexpr usize frameCount = sampleRate / 60; // 3DS samples per video frame static constexpr usize channelCount = 2; static s16 audioBuffer[frameCount * channelCount]; @@ -57,4 +58,4 @@ class LibretroAudioDevice : public AudioDeviceInterface { } bool isInitialized() const { return initialized; } -}; \ No newline at end of file +}; From d42974b5db701e7d65650a5cb9689fe60a674b17 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 9 Feb 2025 14:45:56 +0200 Subject: [PATCH 4/7] Mark audio devices as final --- include/audio/libretro_audio_device.hpp | 2 +- include/audio/miniaudio_device.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/audio/libretro_audio_device.hpp b/include/audio/libretro_audio_device.hpp index 5b3a70c1..9c6d19f8 100644 --- a/include/audio/libretro_audio_device.hpp +++ b/include/audio/libretro_audio_device.hpp @@ -3,7 +3,7 @@ #include "audio/audio_device_interface.hpp" -class LibretroAudioDevice : public AudioDeviceInterface { +class LibretroAudioDevice final : public AudioDeviceInterface { bool initialized = false; public: diff --git a/include/audio/miniaudio_device.hpp b/include/audio/miniaudio_device.hpp index deb6e31a..5cf7c801 100644 --- a/include/audio/miniaudio_device.hpp +++ b/include/audio/miniaudio_device.hpp @@ -6,7 +6,7 @@ #include "audio/audio_device_interface.hpp" #include "miniaudio.h" -class MiniAudioDevice : public AudioDeviceInterface { +class MiniAudioDevice final : public AudioDeviceInterface { static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate static constexpr ma_uint32 channelCount = 2; // Audio output is stereo From 7d5cedf4766c7b39f5df3d567a0c219cf9be6f2c Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 9 Feb 2025 14:58:17 +0200 Subject: [PATCH 5/7] Add toggle for libretro audio device (#719) --- CMakeLists.txt | 6 +++++- include/audio/audio_device.hpp | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f37ce28a..92bff3d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,7 @@ option(BUILD_HYDRA_CORE "Build a Hydra core" OFF) option(BUILD_LIBRETRO_CORE "Build a Libretro core" OFF) option(ENABLE_RENDERDOC_API "Build with support for Renderdoc's capture API for graphics debugging" ON) option(DISABLE_SSE4 "Build with SSE4 instructions disabled, may reduce performance" OFF) +option(USE_LIBRETRO_AUDIO "Enable to use the LR audio device with the LR core. Otherwise our own device is used" OFF) set(OPENGL_PROFILE ${DEFAULT_OPENGL_PROFILE} CACHE STRING "OpenGL profile to use if OpenGL is enabled. Valid values are 'OpenGL' and 'OpenGLES'.") set_property(CACHE OPENGL_PROFILE PROPERTY STRINGS OpenGL OpenGLES) @@ -80,6 +81,10 @@ endif() if(BUILD_LIBRETRO_CORE) set(CMAKE_POSITION_INDEPENDENT_CODE ON) add_compile_definitions(__LIBRETRO__) + + if(USE_LIBRETRO_AUDIO) + add_compile_definitions(USE_LIBRETRO_AUDIO_DEVICE) + endif() endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND ENABLE_USER_BUILD) @@ -157,7 +162,6 @@ if(ENABLE_DISCORD_RPC AND NOT ANDROID) include_directories(third_party/discord-rpc/include) endif() - if (NOT ANDROID) if (USE_SYSTEM_SDL2) find_package(SDL2 CONFIG REQUIRED) diff --git a/include/audio/audio_device.hpp b/include/audio/audio_device.hpp index 5c1f5fc1..966fd667 100644 --- a/include/audio/audio_device.hpp +++ b/include/audio/audio_device.hpp @@ -1,6 +1,6 @@ #pragma once -#ifdef __LIBRETRO__ +#if defined(__LIBRETRO__) && defined(USE_LIBRETRO_AUDIO_DEVICE) #include "audio/libretro_audio_device.hpp" using AudioDevice = LibretroAudioDevice; #else From b1a1b4caa76ece7492c1c5daca6e9e45d5511bb4 Mon Sep 17 00:00:00 2001 From: Paris Oplopoios Date: Sun, 9 Feb 2025 22:47:57 +0200 Subject: [PATCH 6/7] Very important work (#720) * Very important work * Most important fix --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index f58f316c..44b622d1 100644 --- a/readme.md +++ b/readme.md @@ -117,7 +117,7 @@ Panda3DS also supports controller input using the SDL2 GameController API. - [Kaizen](https://github.com/SimoneN64/Kaizen): Experimental work-in-progress low-level N64 emulator - [ChonkyStation](https://github.com/liuk7071/ChonkyStation): Work-in-progress PlayStation emulator - [shadPS4](https://github.com/shadps4-emu/shadPS4): Work-in-progress PS4 emulator by the founder of PCSX, PCSX2 and more -- [Hydra](https://github.com/hydra-emu/hydra): Cross-platform GameBoy, NES, N64 and Chip-8 emulator +- [felix86](https://github.com/OFFTKP/felix86): A new x86-64 → RISC-V Linux userspace emulator - [Tanuki3DS](https://github.com/burhanr13/Tanuki3DS/): A new 3DS emulator for MacOS and Linux # Support If you find this project exciting and want to support the founder, check out [his Patreon](https://www.patreon.com/wheremyfoodat) or [Ko-fi](https://ko-fi.com/wheremyfoodat) From da797831ba8a8c304612d7618c30201695279d89 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 10 Feb 2025 01:00:12 +0200 Subject: [PATCH 7/7] Add more HLE service calls for eshop (#721) --- include/services/cfg.hpp | 1 + src/core/services/cfg.cpp | 20 ++++++++++++++++++++ src/core/services/http.cpp | 6 +++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/services/cfg.hpp b/include/services/cfg.hpp index 6e6f697a..bac2cd87 100644 --- a/include/services/cfg.hpp +++ b/include/services/cfg.hpp @@ -23,6 +23,7 @@ class CFGService { void getConfigInfoBlk2(u32 messagePointer); void getConfigInfoBlk8(u32 messagePointer, u32 commandWord); void getCountryCodeID(u32 messagePointer); + void getCountryCodeString(u32 messagePointer); void getLocalFriendCodeSeed(u32 messagePointer); void getRegionCanadaUSA(u32 messagePointer); void getSystemModel(u32 messagePointer); diff --git a/src/core/services/cfg.cpp b/src/core/services/cfg.cpp index a9b80472..b4e16895 100644 --- a/src/core/services/cfg.cpp +++ b/src/core/services/cfg.cpp @@ -18,6 +18,7 @@ namespace CFGCommands { GetSystemModel = 0x00050000, TranslateCountryInfo = 0x00080080, + GetCountryCodeString = 0x00090040, GetCountryCodeID = 0x000A0040, IsFangateSupported = 0x000B0000, SetConfigInfoBlk4 = 0x04020082, @@ -50,6 +51,7 @@ void CFGService::handleSyncRequest(u32 messagePointer, CFGService::Type type) { if (type != Type::NOR) { switch (command) { case CFGCommands::GetConfigInfoBlk2: [[likely]] getConfigInfoBlk2(messagePointer); break; + case CFGCommands::GetCountryCodeString: getCountryCodeString(messagePointer); break; case CFGCommands::GetCountryCodeID: getCountryCodeID(messagePointer); break; case CFGCommands::GetRegionCanadaUSA: getRegionCanadaUSA(messagePointer); break; case CFGCommands::GetSystemModel: getSystemModel(messagePointer); break; @@ -315,6 +317,24 @@ void CFGService::getCountryCodeID(u32 messagePointer) { } } +void CFGService::getCountryCodeString(u32 messagePointer) { + const u16 id = mem.read16(messagePointer + 4); + log("CFG::getCountryCodeString (id = %04X)\n", id); + + mem.write32(messagePointer, IPC::responseHeader(0x09, 2, 0)); + + for (auto [string, code] : countryCodeToTableIDMap) { + if (code == id) { + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, u32(string)); + return; + } + } + + // Code is not a valid country code, return an appropriate error + mem.write32(messagePointer + 4, 0xD90103FA); +} + void CFGService::secureInfoGetByte101(u32 messagePointer) { log("CFG::SecureInfoGetByte101\n"); diff --git a/src/core/services/http.cpp b/src/core/services/http.cpp index 076afa06..801cdabb 100644 --- a/src/core/services/http.cpp +++ b/src/core/services/http.cpp @@ -19,7 +19,11 @@ void HTTPService::handleSyncRequest(u32 messagePointer) { case HTTPCommands::CreateRootCertChain: createRootCertChain(messagePointer); break; case HTTPCommands::Initialize: initialize(messagePointer); break; case HTTPCommands::RootCertChainAddDefaultCert: rootCertChainAddDefaultCert(messagePointer); break; - default: Helpers::panic("HTTP service requested. Command: %08X\n", command); + + default: + Helpers::warn("HTTP service requested. Command: %08X\n", command); + mem.write32(messagePointer + 4, Result::FailurePlaceholder); + break; } }