[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>
This commit is contained in:
Jonian Guveli 2025-02-09 05:04:36 +02:00 committed by GitHub
parent 4cb66217c2
commit 042ab6de03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 125 additions and 23 deletions

View file

@ -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(

View file

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

View file

@ -0,0 +1,36 @@
#pragma once
#include <array>
#include "config.hpp"
#include "helpers.hpp"
#include "ring_buffer.hpp"
class AudioDeviceInterface {
protected:
static constexpr usize maxFrameCount = 0x2000;
using Samples = Common::RingBuffer<s16, maxFrameCount * 2>;
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<s16, 2> 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) {}
};

View file

@ -0,0 +1,60 @@
#pragma once
#include <cstring>
#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; }
};

View file

@ -3,39 +3,31 @@
#include <string>
#include <vector>
#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<ma_int16, 0x2000 * 2>;
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<s16, 2> lastStereoSample;
std::vector<std::string> 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; }
};

View file

@ -7,8 +7,8 @@
#include <span>
#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,7 +48,7 @@ class Emulator {
Scheduler scheduler;
Crypto::AESEngine aesEngine;
MiniAudioDevice audioDevice;
AudioDevice audioDevice;
Cheats cheats;
public:
@ -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(); }

View file

@ -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;

View file

@ -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) {}