(libretro) Move audio batch callback onto main thread + improve audio sample pacing

This commit is contained in:
jdgleaver 2021-12-16 15:51:44 +00:00 committed by flyinghead
parent 04be40767b
commit 96560e4914
2 changed files with 127 additions and 26 deletions

View File

@ -21,24 +21,124 @@
#include <libretro.h>
#define SAMPLE_COUNT 512
struct SoundFrame { s16 l; s16 r; };
#include <vector>
#include <mutex>
#define AUDIO_BUFFER_SIZE_DEFAULT (1 << 11)
#define AUDIO_BUFFER_SIZE_MAX (1 << 20) /* 1 MiB */
extern retro_audio_sample_batch_t audio_batch_cb;
static std::mutex audio_buffer_mutex;
static std::vector<int16_t> audio_buffer;
static size_t audio_buffer_idx;
static size_t audio_batch_frames_max;
static int16_t *audio_out_buffer = nullptr;
static size_t audio_out_buffer_size;
void retro_audio_init(void)
{
const std::lock_guard<std::mutex> lock(audio_buffer_mutex);
audio_buffer.resize(AUDIO_BUFFER_SIZE_DEFAULT);
audio_buffer_idx = 0;
audio_batch_frames_max = std::numeric_limits<size_t>::max();
audio_out_buffer_size = AUDIO_BUFFER_SIZE_DEFAULT;
audio_out_buffer = (int16_t*)malloc(audio_out_buffer_size * sizeof(int16_t));
}
void retro_audio_deinit(void)
{
const std::lock_guard<std::mutex> lock(audio_buffer_mutex);
audio_buffer.clear();
audio_buffer_idx = 0;
if (audio_out_buffer != nullptr)
free(audio_out_buffer);
audio_out_buffer = nullptr;
audio_out_buffer_size = 0;
}
void retro_audio_flush_buffer(void)
{
const std::lock_guard<std::mutex> lock(audio_buffer_mutex);
audio_buffer_idx = 0;
}
void retro_audio_upload(void)
{
audio_buffer_mutex.lock();
if (audio_out_buffer_size < audio_buffer_idx)
{
int16_t *tmp = (int16_t *)realloc(audio_out_buffer,
audio_buffer_idx * sizeof(int16_t));
if (!tmp)
{
audio_buffer_idx = 0;
audio_buffer_mutex.unlock();
return;
}
audio_out_buffer_size = audio_buffer_idx;
audio_out_buffer = tmp;
}
for (size_t i = 0; i < audio_buffer_idx; i++)
audio_out_buffer[i] = audio_buffer[i];
size_t num_frames = audio_buffer_idx >> 1;
audio_buffer_idx = 0;
audio_buffer_mutex.unlock();
int16_t *audio_out_buffer_ptr = audio_out_buffer;
while (num_frames > 0)
{
size_t frames_to_write = (num_frames > audio_batch_frames_max) ?
audio_batch_frames_max : num_frames;
size_t frames_written = audio_batch_cb(audio_out_buffer_ptr,
frames_to_write);
if ((frames_written < frames_to_write) &&
(frames_written > 0))
audio_batch_frames_max = frames_written;
num_frames -= frames_to_write;
audio_out_buffer_ptr += frames_to_write << 1;
}
}
void WriteSample(s16 r, s16 l)
{
static SoundFrame Buffer[SAMPLE_COUNT];
static u32 writePtr; // next sample index
Buffer[writePtr].r = r;
Buffer[writePtr].l = l;
const std::lock_guard<std::mutex> lock(audio_buffer_mutex);
if (++writePtr == SAMPLE_COUNT)
{
if (dc_is_running() && (!config::ThreadedRendering || config::LimitFPS))
audio_batch_cb((const int16_t*)Buffer, SAMPLE_COUNT);
writePtr = 0;
}
if (audio_buffer.size() < audio_buffer_idx + 2)
{
if (audio_buffer_idx + 2 > AUDIO_BUFFER_SIZE_MAX)
{
audio_buffer_idx = 0;
return;
}
try
{
audio_buffer.resize(audio_buffer_idx + 2 + AUDIO_BUFFER_SIZE_DEFAULT);
}
catch (std::bad_alloc &)
{
audio_buffer_idx = 0;
return;
}
}
audio_buffer[audio_buffer_idx++] = l;
audio_buffer[audio_buffer_idx++] = r;
}
void InitAudio()

View File

@ -73,13 +73,6 @@ constexpr char slash = path_default_slash_c();
* background or not.
*/
#define RETRO_ENVIRONMENT_GET_CLEAR_ALL_THREAD_WAITS_CB (3 | RETRO_ENVIRONMENT_RETROARCH_START_BLOCK)
/* retro_environment_t * --
* Provides the callback to the frontend method which will cancel
* all currently waiting threads. Used when coordination is needed
* between the core and the frontend to gracefully stop all threads.
*/
#define RETRO_ENVIRONMENT_POLL_TYPE_OVERRIDE (4 | RETRO_ENVIRONMENT_RETROARCH_START_BLOCK)
/* unsigned * --
* Tells the frontend to override the poll type behavior.
@ -98,6 +91,11 @@ constexpr char slash = path_default_slash_c();
#include "libretro_core_options.h"
#include "vmu_xhair.h"
extern void retro_audio_init(void);
extern void retro_audio_deinit(void);
extern void retro_audio_flush_buffer(void);
extern void retro_audio_upload(void);
std::string arcadeFlashPath;
static bool boot_to_bios;
@ -145,7 +143,6 @@ static retro_input_poll_t poll_cb;
static retro_input_state_t input_cb;
retro_audio_sample_batch_t audio_batch_cb;
static retro_environment_t environ_cb;
static retro_environment_t frontend_clear_thread_waits_cb;
static retro_rumble_interface rumble;
@ -264,8 +261,6 @@ void retro_init()
unsigned color_mode = RETRO_PIXEL_FORMAT_XRGB8888;
environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &color_mode);
environ_cb(RETRO_ENVIRONMENT_GET_CLEAR_ALL_THREAD_WAITS_CB, &frontend_clear_thread_waits_cb);
init_kb_map();
struct retro_keyboard_callback kb_callback = { &retro_keyboard_event };
environ_cb(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &kb_callback);
@ -274,6 +269,7 @@ void retro_init()
libretro_supports_bitmasks = true;
init_disk_control_interface();
retro_audio_init();
if (!_vmem_reserve())
ERROR_LOG(VMEM, "Cannot reserve memory space");
@ -294,6 +290,8 @@ void retro_deinit()
os_UninstallFaultHandler();
libretro_supports_bitmasks = false;
LogManager::Shutdown();
retro_audio_deinit();
}
static void set_variable_visibility()
@ -891,6 +889,11 @@ void retro_run()
if (config::RendererType.isOpenGL())
glsm_ctl(GLSM_CTL_STATE_UNBIND, nullptr);
if (!config::ThreadedRendering || config::LimitFPS)
retro_audio_upload();
else
retro_audio_flush_buffer();
video_cb(is_dupe ? 0 : RETRO_HW_FRAME_BUFFER_VALID, framebufferWidth, framebufferHeight, 0);
if (!config::ThreadedRendering)
@ -931,6 +934,7 @@ void retro_reset()
setGameGeometry(geometry);
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &geometry);
blankVmus();
retro_audio_flush_buffer();
if (config::ThreadedRendering)
dc_resume();
@ -1722,11 +1726,7 @@ bool retro_load_game_special(unsigned game_type, const struct retro_game_info *i
void retro_unload_game()
{
INFO_LOG(COMMON, "Flycast unloading game");
if (frontend_clear_thread_waits_cb != nullptr)
frontend_clear_thread_waits_cb(1, nullptr);
dc_stop();
if (frontend_clear_thread_waits_cb != nullptr)
frontend_clear_thread_waits_cb(0, nullptr);
free(game_data);
game_data = nullptr;
disk_paths.clear();
@ -1825,6 +1825,7 @@ bool retro_unserialize(const void * data, size_t size)
}
bool result = dc_loadstate(&data, size);
retro_audio_flush_buffer();
if (config::ThreadedRendering)
{