Add audio enable and vsync settings

This commit is contained in:
wheremyfoodat 2024-02-23 22:31:45 +02:00
parent e0c1d4511b
commit 8cba0405b9
12 changed files with 102 additions and 22 deletions

View file

@ -32,6 +32,7 @@ namespace Audio {
DSPService& dspService; DSPService& dspService;
Samples sampleBuffer; Samples sampleBuffer;
bool audioEnabled = false;
MAKE_LOG_FUNCTION(log, dspLogger) MAKE_LOG_FUNCTION(log, dspLogger)
@ -55,7 +56,9 @@ namespace Audio {
static Audio::DSPCore::Type typeFromString(std::string inString); static Audio::DSPCore::Type typeFromString(std::string inString);
static const char* typeToString(Audio::DSPCore::Type type); static const char* typeToString(Audio::DSPCore::Type type);
Samples& getSamples() { return sampleBuffer; } Samples& getSamples() { return sampleBuffer; }
virtual void setAudioEnabled(bool enable) { audioEnabled = enable; }
}; };
std::unique_ptr<DSPCore> makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService); std::unique_ptr<DSPCore> makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService);

View file

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <atomic>
#include <string> #include <string>
#include <vector> #include <vector>
@ -7,8 +8,8 @@
class MiniAudioDevice { class MiniAudioDevice {
using Samples = Common::RingBuffer<ma_int16, 1024>; using Samples = Common::RingBuffer<ma_int16, 1024>;
// static constexpr ma_uint32 sampleRateIn = 32768; // 3DS sample rate static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate
// static constexpr ma_uint32 sampleRateOut = 44100; // Output sample rate static constexpr ma_uint32 channelCount = 2; // Audio output is stereo
ma_context context; ma_context context;
ma_device_config deviceConfig; ma_device_config deviceConfig;
@ -24,5 +25,7 @@ class MiniAudioDevice {
MiniAudioDevice(); MiniAudioDevice();
// If safe is on, we create a null audio device // If safe is on, we create a null audio device
void init(Samples& samples, bool safe = false); void init(Samples& samples, bool safe = false);
void start(); void start();
void stop();
}; };

View file

@ -84,6 +84,7 @@ namespace Audio {
scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2); scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2);
} }
void setAudioEnabled(bool enable) override;
u8* getDspMemory() override { return teakra.GetDspMemory().data(); } u8* getDspMemory() override { return teakra.GetDspMemory().data(); }
u16 recvData(u32 regId) override { return teakra.RecvData(regId); } u16 recvData(u32 regId) override { return teakra.RecvData(regId); }

View file

@ -22,6 +22,9 @@ struct EmulatorConfig {
bool sdWriteProtected = false; bool sdWriteProtected = false;
bool usePortableBuild = false; bool usePortableBuild = false;
bool audioEnabled = false;
bool vsyncEnabled = true;
bool chargerPlugged = true; bool chargerPlugged = true;
// Default to 3% battery to make users suffer // Default to 3% battery to make users suffer
int batteryPercentage = 3; int batteryPercentage = 3;

View file

@ -77,6 +77,7 @@ class Emulator {
#ifdef PANDA3DS_ENABLE_DISCORD_RPC #ifdef PANDA3DS_ENABLE_DISCORD_RPC
Discord::RPC discordRpc; Discord::RPC discordRpc;
#endif #endif
void setAudioEnabled(bool enable);
void updateDiscord(); void updateDiscord();
// Keep the handle for the ROM here to reload when necessary and to prevent deleting it // Keep the handle for the ROM here to reload when necessary and to prevent deleting it

View file

@ -59,6 +59,7 @@ void EmulatorConfig::load() {
} }
shaderJitEnabled = toml::find_or<toml::boolean>(gpu, "EnableShaderJIT", shaderJitDefault); shaderJitEnabled = toml::find_or<toml::boolean>(gpu, "EnableShaderJIT", shaderJitDefault);
vsyncEnabled = toml::find_or<toml::boolean>(gpu, "EnableVSync", true);
} }
} }
@ -69,6 +70,7 @@ void EmulatorConfig::load() {
auto dspCoreName = toml::find_or<std::string>(audio, "DSPEmulation", "Null"); auto dspCoreName = toml::find_or<std::string>(audio, "DSPEmulation", "Null");
dspType = Audio::DSPCore::typeFromString(dspCoreName); dspType = Audio::DSPCore::typeFromString(dspCoreName);
audioEnabled = toml::find_or<toml::boolean>(audio, "EnableAudio", false);
} }
} }
@ -119,7 +121,9 @@ void EmulatorConfig::save() {
data["General"]["UsePortableBuild"] = usePortableBuild; data["General"]["UsePortableBuild"] = usePortableBuild;
data["GPU"]["EnableShaderJIT"] = shaderJitEnabled; data["GPU"]["EnableShaderJIT"] = shaderJitEnabled;
data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType)); data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType));
data["GPU"]["EnableVSync"] = vsyncEnabled;
data["Audio"]["DSPEmulation"] = std::string(Audio::DSPCore::typeToString(dspType)); data["Audio"]["DSPEmulation"] = std::string(Audio::DSPCore::typeToString(dspType));
data["Audio"]["EnableAudio"] = audioEnabled;
data["Battery"]["ChargerPlugged"] = chargerPlugged; data["Battery"]["ChargerPlugged"] = chargerPlugged;
data["Battery"]["BatteryPercentage"] = batteryPercentage; data["Battery"]["BatteryPercentage"] = batteryPercentage;

View file

@ -2,16 +2,15 @@
#include "helpers.hpp" #include "helpers.hpp"
static constexpr uint channelCount = 2;
MiniAudioDevice::MiniAudioDevice() : initialized(false), running(false), samples(nullptr) {} MiniAudioDevice::MiniAudioDevice() : initialized(false), running(false), samples(nullptr) {}
void MiniAudioDevice::init(Samples& samples, bool safe) { void MiniAudioDevice::init(Samples& samples, bool safe) {
this->samples = &samples; this->samples = &samples;
running = false;
// Probe for device and available backends and initialize audio // Probe for device and available backends and initialize audio
ma_backend backends[ma_backend_null + 1]; ma_backend backends[ma_backend_null + 1];
unsigned count = 0; uint count = 0;
if (safe) { if (safe) {
backends[0] = ma_backend_null; backends[0] = ma_backend_null;
@ -82,7 +81,7 @@ void MiniAudioDevice::init(Samples& samples, bool safe) {
// The 3DS outputs s16 stereo audio @ 32768 Hz // The 3DS outputs s16 stereo audio @ 32768 Hz
deviceConfig.playback.format = ma_format_s16; deviceConfig.playback.format = ma_format_s16;
deviceConfig.playback.channels = channelCount; deviceConfig.playback.channels = channelCount;
deviceConfig.sampleRate = 32768; deviceConfig.sampleRate = sampleRate;
//deviceConfig.periodSizeInFrames = 64; //deviceConfig.periodSizeInFrames = 64;
//deviceConfig.periods = 16; //deviceConfig.periods = 16;
deviceConfig.pUserData = this; deviceConfig.pUserData = this;
@ -93,8 +92,16 @@ void MiniAudioDevice::init(Samples& samples, bool safe) {
auto self = reinterpret_cast<MiniAudioDevice*>(device->pUserData); auto self = reinterpret_cast<MiniAudioDevice*>(device->pUserData);
s16* output = reinterpret_cast<ma_int16*>(out); s16* output = reinterpret_cast<ma_int16*>(out);
while (self->samples->size() < frameCount * channelCount) {} // Wait until there's enough samples to pop
self->samples->pop(output, frameCount * 2); 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) { if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) {
@ -109,14 +116,31 @@ void MiniAudioDevice::init(Samples& samples, bool safe) {
void MiniAudioDevice::start() { void MiniAudioDevice::start() {
if (!initialized) { if (!initialized) {
Helpers::warn("MiniAudio device not initialize, won't start"); Helpers::warn("MiniAudio device not initialized, won't start");
return; return;
} }
if (ma_device_start(&device) == MA_SUCCESS) { // Ignore the call to start if the device is already running
running = true; if (!running) {
} else { if (ma_device_start(&device) == MA_SUCCESS) {
running = false; running = true;
Helpers::warn("Failed to start audio device"); } 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;
if (ma_device_stop(&device) != MA_SUCCESS) {
Helpers::warn("Failed to stop audio device");
}
} }
} }

View file

@ -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; }; ahbm.write32 = [&](u32 addr, u32 value) { *(u32*)&mem.getFCRAM()[addr - PhysicalAddrs::FCRAM] = value; };
teakra.SetAHBMCallback(ahbm); teakra.SetAHBMCallback(ahbm);
teakra.SetAudioCallback([=](std::array<s16, 2> sample) { teakra.SetAudioCallback([=](std::array<s16, 2> sample) { /* Do nothing */ });
while (sampleBuffer.size() + 2 > sampleBuffer.Capacity()) {}
sampleBuffer.push(sample.data(), 2);
});
// Set up event handlers. These handlers forward a hardware interrupt to the DSP service, which is responsible // Set up event handlers. These handlers forward a hardware interrupt to the DSP service, which is responsible
// For triggering the appropriate DSP kernel events // For triggering the appropriate DSP kernel events
@ -121,6 +117,24 @@ void TeakraDSP::reset() {
signalledData = signalledSemaphore = false; 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<s16, 2> sample) {
// Wait until we can push our samples
while (sampleBuffer.size() + 2 > sampleBuffer.Capacity()) {
}
sampleBuffer.push(sample.data(), 2);
});
} else {
teakra.SetAudioCallback([=](std::array<s16, 2> sample) { /* Do nothing */ });
}
}
}
// https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp // https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp
void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) {
size &= 0xffff; size &= 0xffff;

View file

@ -25,10 +25,12 @@ Emulator::Emulator()
#endif #endif
{ {
DSPService& dspService = kernel.getServiceManager().getDSP(); DSPService& dspService = kernel.getServiceManager().getDSP();
dsp = Audio::makeDSPCore(config.dspType, memory, scheduler, dspService); dsp = Audio::makeDSPCore(config.dspType, memory, scheduler, dspService);
dspService.setDSPCore(dsp.get()); dspService.setDSPCore(dsp.get());
audioDevice.init(dsp->getSamples()); audioDevice.init(dsp->getSamples());
setAudioEnabled(config.audioEnabled);
#ifdef PANDA3DS_ENABLE_DISCORD_RPC #ifdef PANDA3DS_ENABLE_DISCORD_RPC
if (config.discordRpcEnabled) { if (config.discordRpcEnabled) {
@ -103,8 +105,19 @@ void Emulator::step() {}
void Emulator::render() {} void Emulator::render() {}
// Only resume if a ROM is properly loaded // Only resume if a ROM is properly loaded
void Emulator::resume() { running = (romType != ROMType::None); } void Emulator::resume() {
void Emulator::pause() { running = false; } running = (romType != ROMType::None);
if (running) {
audioDevice.start();
}
}
void Emulator::pause() {
running = false;
audioDevice.stop();
}
void Emulator::togglePause() { running ? pause() : resume(); } void Emulator::togglePause() { running ? pause() : resume(); }
void Emulator::runFrame() { void Emulator::runFrame() {
@ -389,3 +402,15 @@ RomFS::DumpingResult Emulator::dumpRomFS(const std::filesystem::path& path) {
return DumpingResult::Success; 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);
}

View file

@ -87,7 +87,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
// Make GL context current for this thread, enable VSync // Make GL context current for this thread, enable VSync
GL::Context* glContext = screen.getGLContext(); GL::Context* glContext = screen.getGLContext();
glContext->MakeCurrent(); glContext->MakeCurrent();
glContext->SetSwapInterval(1); glContext->SetSwapInterval(emu->getConfig().vsyncEnabled ? 1 : 0);
emu->initGraphicsContext(glContext); emu->initGraphicsContext(glContext);
} else if (usingVk) { } else if (usingVk) {

View file

@ -49,6 +49,8 @@ FrontendSDL::FrontendSDL() {
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
Helpers::panic("OpenGL init failed"); Helpers::panic("OpenGL init failed");
} }
SDL_GL_SetSwapInterval(config.vsyncEnabled ? 1 : 0);
} }
#ifdef PANDA3DS_ENABLE_VULKAN #ifdef PANDA3DS_ENABLE_VULKAN

2
third_party/teakra vendored

@ -1 +1 @@
Subproject commit 01db7cdd00aabcce559a8dddce8798dabb71949b Subproject commit a686a1384f3a871c2bd65553d48aeba26246c704