From b112043638eee059e1f38bb3cd100bef2deb53c7 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 23 Feb 2024 22:31:45 +0200 Subject: [PATCH] Add audio enable and vsync settings --- include/audio/dsp_core.hpp | 3 ++ include/audio/miniaudio_device.hpp | 7 +++-- include/audio/teakra_core.hpp | 1 + include/config.hpp | 3 ++ include/emulator.hpp | 1 + src/config.cpp | 4 +++ src/core/audio/miniaudio_device.cpp | 46 ++++++++++++++++++++++------- src/core/audio/teakra_core.cpp | 25 ++++++++++++---- src/emulator.cpp | 29 ++++++++++++++++-- src/panda_qt/main_window.cpp | 2 +- src/panda_sdl/frontend_sdl.cpp | 2 ++ third_party/teakra | 2 +- 12 files changed, 103 insertions(+), 22 deletions(-) diff --git a/include/audio/dsp_core.hpp b/include/audio/dsp_core.hpp index 27e84cbf..6f46d49b 100644 --- a/include/audio/dsp_core.hpp +++ b/include/audio/dsp_core.hpp @@ -32,6 +32,7 @@ namespace Audio { DSPService& dspService; Samples sampleBuffer; + bool audioEnabled = false; MAKE_LOG_FUNCTION(log, dspLogger) @@ -55,7 +56,9 @@ namespace Audio { static Audio::DSPCore::Type typeFromString(std::string inString); static const char* typeToString(Audio::DSPCore::Type type); + Samples& getSamples() { return sampleBuffer; } + virtual void setAudioEnabled(bool enable) { audioEnabled = enable; } }; std::unique_ptr makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService); diff --git a/include/audio/miniaudio_device.hpp b/include/audio/miniaudio_device.hpp index 63a54edb..f4d126d8 100644 --- a/include/audio/miniaudio_device.hpp +++ b/include/audio/miniaudio_device.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -7,8 +8,8 @@ class MiniAudioDevice { using Samples = Common::RingBuffer; - // static constexpr ma_uint32 sampleRateIn = 32768; // 3DS sample rate - // static constexpr ma_uint32 sampleRateOut = 44100; // Output sample rate + static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate + static constexpr ma_uint32 channelCount = 2; // Audio output is stereo ma_context context; ma_device_config deviceConfig; @@ -24,5 +25,7 @@ class MiniAudioDevice { MiniAudioDevice(); // If safe is on, we create a null audio device void init(Samples& samples, bool safe = false); + void start(); + void stop(); }; \ No newline at end of file diff --git a/include/audio/teakra_core.hpp b/include/audio/teakra_core.hpp index 57db0e4a..95831ff7 100644 --- a/include/audio/teakra_core.hpp +++ b/include/audio/teakra_core.hpp @@ -84,6 +84,7 @@ namespace Audio { scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2); } + void setAudioEnabled(bool enable) override; u8* getDspMemory() override { return teakra.GetDspMemory().data(); } u16 recvData(u32 regId) override { return teakra.RecvData(regId); } diff --git a/include/config.hpp b/include/config.hpp index e5c10f4b..8c0d2e12 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -22,6 +22,9 @@ struct EmulatorConfig { bool sdWriteProtected = false; bool usePortableBuild = false; + bool audioEnabled = false; + bool vsyncEnabled = true; + bool chargerPlugged = true; // Default to 3% battery to make users suffer int batteryPercentage = 3; diff --git a/include/emulator.hpp b/include/emulator.hpp index 9420948d..920fd7f7 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -77,6 +77,7 @@ class Emulator { #ifdef PANDA3DS_ENABLE_DISCORD_RPC Discord::RPC discordRpc; #endif + void setAudioEnabled(bool enable); void updateDiscord(); // Keep the handle for the ROM here to reload when necessary and to prevent deleting it diff --git a/src/config.cpp b/src/config.cpp index 12b112dc..f19ff06d 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -59,6 +59,7 @@ void EmulatorConfig::load() { } shaderJitEnabled = toml::find_or(gpu, "EnableShaderJIT", shaderJitDefault); + vsyncEnabled = toml::find_or(gpu, "EnableVSync", true); } } @@ -69,6 +70,7 @@ void EmulatorConfig::load() { auto dspCoreName = toml::find_or(audio, "DSPEmulation", "Null"); dspType = Audio::DSPCore::typeFromString(dspCoreName); + audioEnabled = toml::find_or(audio, "EnableAudio", false); } } @@ -119,7 +121,9 @@ void EmulatorConfig::save() { data["General"]["UsePortableBuild"] = usePortableBuild; data["GPU"]["EnableShaderJIT"] = shaderJitEnabled; data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType)); + data["GPU"]["EnableVSync"] = vsyncEnabled; data["Audio"]["DSPEmulation"] = std::string(Audio::DSPCore::typeToString(dspType)); + data["Audio"]["EnableAudio"] = audioEnabled; data["Battery"]["ChargerPlugged"] = chargerPlugged; data["Battery"]["BatteryPercentage"] = batteryPercentage; diff --git a/src/core/audio/miniaudio_device.cpp b/src/core/audio/miniaudio_device.cpp index 4753fe4e..ae45e9ab 100644 --- a/src/core/audio/miniaudio_device.cpp +++ b/src/core/audio/miniaudio_device.cpp @@ -2,16 +2,15 @@ #include "helpers.hpp" -static constexpr uint channelCount = 2; - MiniAudioDevice::MiniAudioDevice() : initialized(false), running(false), samples(nullptr) {} void MiniAudioDevice::init(Samples& samples, bool safe) { this->samples = &samples; + running = false; // Probe for device and available backends and initialize audio ma_backend backends[ma_backend_null + 1]; - unsigned count = 0; + uint count = 0; if (safe) { backends[0] = ma_backend_null; @@ -82,7 +81,7 @@ void MiniAudioDevice::init(Samples& samples, bool safe) { // The 3DS outputs s16 stereo audio @ 32768 Hz deviceConfig.playback.format = ma_format_s16; deviceConfig.playback.channels = channelCount; - deviceConfig.sampleRate = 32768; + deviceConfig.sampleRate = sampleRate; //deviceConfig.periodSizeInFrames = 64; //deviceConfig.periods = 16; deviceConfig.pUserData = this; @@ -93,8 +92,16 @@ void MiniAudioDevice::init(Samples& samples, bool safe) { auto self = reinterpret_cast(device->pUserData); s16* output = reinterpret_cast(out); - while (self->samples->size() < frameCount * channelCount) {} - self->samples->pop(output, frameCount * 2); + // Wait until there's enough samples to pop + while (self->samples->size() < frameCount * channelCount) { + printf("Waiting\n"); + // If audio output is disabled from the emulator thread, make sure that this callback will return and not hang + if (!self->running) { + return; + } + } + + self->samples->pop(output, frameCount * channelCount); }; if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) { @@ -109,14 +116,31 @@ void MiniAudioDevice::init(Samples& samples, bool safe) { void MiniAudioDevice::start() { if (!initialized) { - Helpers::warn("MiniAudio device not initialize, won't start"); + Helpers::warn("MiniAudio device not initialized, won't start"); return; } - if (ma_device_start(&device) == MA_SUCCESS) { - running = true; - } else { + // Ignore the call to start if the device is already running + if (!running) { + if (ma_device_start(&device) == MA_SUCCESS) { + running = true; + } else { + Helpers::warn("Failed to start audio device"); + } + } +} + +void MiniAudioDevice::stop() { + if (!initialized) { + Helpers::warn("MiniAudio device not initialized, can't start"); + return; + } + + if (running) { running = false; - Helpers::warn("Failed to start audio device"); + + if (ma_device_stop(&device) != MA_SUCCESS) { + Helpers::warn("Failed to stop audio device"); + } } } \ No newline at end of file diff --git a/src/core/audio/teakra_core.cpp b/src/core/audio/teakra_core.cpp index 432e05a0..347d365b 100644 --- a/src/core/audio/teakra_core.cpp +++ b/src/core/audio/teakra_core.cpp @@ -55,11 +55,7 @@ TeakraDSP::TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) ahbm.write32 = [&](u32 addr, u32 value) { *(u32*)&mem.getFCRAM()[addr - PhysicalAddrs::FCRAM] = value; }; teakra.SetAHBMCallback(ahbm); - teakra.SetAudioCallback([=](std::array sample) { - while (sampleBuffer.size() + 2 > sampleBuffer.Capacity()) {} - - sampleBuffer.push(sample.data(), 2); - }); + teakra.SetAudioCallback([=](std::array sample) { /* Do nothing */ }); // Set up event handlers. These handlers forward a hardware interrupt to the DSP service, which is responsible // For triggering the appropriate DSP kernel events @@ -121,6 +117,25 @@ void TeakraDSP::reset() { signalledData = signalledSemaphore = false; } +void TeakraDSP::setAudioEnabled(bool enable) { + if (audioEnabled != enable) { + audioEnabled = enable; + + // Set the appropriate audio callback for Teakra + if (audioEnabled) { + teakra.SetAudioCallback([=](std::array sample) { + // Wait until we can push our samples + while (sampleBuffer.size() + 2 > sampleBuffer.Capacity()) { + printf("shit\n"); + } + sampleBuffer.push(sample.data(), 2); + }); + } else { + teakra.SetAudioCallback([=](std::array sample) { /* Do nothing */ }); + } + } +} + // https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { size &= 0xffff; diff --git a/src/emulator.cpp b/src/emulator.cpp index 36c9611d..528590b6 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -25,10 +25,12 @@ Emulator::Emulator() #endif { DSPService& dspService = kernel.getServiceManager().getDSP(); + dsp = Audio::makeDSPCore(config.dspType, memory, scheduler, dspService); dspService.setDSPCore(dsp.get()); audioDevice.init(dsp->getSamples()); + setAudioEnabled(config.audioEnabled); #ifdef PANDA3DS_ENABLE_DISCORD_RPC if (config.discordRpcEnabled) { @@ -103,8 +105,19 @@ void Emulator::step() {} void Emulator::render() {} // Only resume if a ROM is properly loaded -void Emulator::resume() { running = (romType != ROMType::None); } -void Emulator::pause() { running = false; } +void Emulator::resume() { + running = (romType != ROMType::None); + + if (running) { + audioDevice.start(); + } +} + +void Emulator::pause() { + running = false; + audioDevice.stop(); +} + void Emulator::togglePause() { running ? pause() : resume(); } void Emulator::runFrame() { @@ -388,4 +401,16 @@ RomFS::DumpingResult Emulator::dumpRomFS(const std::filesystem::path& path) { dumpRomFSNode(*node, (const char*)&romFS[0], path); return DumpingResult::Success; +} + +void Emulator::setAudioEnabled(bool enable) { + if (!enable) { + audioDevice.stop(); + } else if (enable && romType != ROMType::None && running) { + // Don't start the audio device yet if there's no ROM loaded or the emulator is paused + // Resume and Pause will handle it + audioDevice.start(); + } + + dsp->setAudioEnabled(enable); } \ No newline at end of file diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index de70cc18..dff4c171 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -87,7 +87,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) // Make GL context current for this thread, enable VSync GL::Context* glContext = screen.getGLContext(); glContext->MakeCurrent(); - glContext->SetSwapInterval(1); + glContext->SetSwapInterval(emu->getConfig().vsyncEnabled ? 1 : 0); emu->initGraphicsContext(glContext); } else if (usingVk) { diff --git a/src/panda_sdl/frontend_sdl.cpp b/src/panda_sdl/frontend_sdl.cpp index 29b1dcb8..04b582e1 100644 --- a/src/panda_sdl/frontend_sdl.cpp +++ b/src/panda_sdl/frontend_sdl.cpp @@ -49,6 +49,8 @@ FrontendSDL::FrontendSDL() { if (!gladLoadGLLoader(reinterpret_cast(SDL_GL_GetProcAddress))) { Helpers::panic("OpenGL init failed"); } + + SDL_GL_SetSwapInterval(config.vsyncEnabled ? 1 : 0); } #ifdef PANDA3DS_ENABLE_VULKAN diff --git a/third_party/teakra b/third_party/teakra index 01db7cdd..a686a138 160000 --- a/third_party/teakra +++ b/third_party/teakra @@ -1 +1 @@ -Subproject commit 01db7cdd00aabcce559a8dddce8798dabb71949b +Subproject commit a686a1384f3a871c2bd65553d48aeba26246c704