(libretro) Move audio batch callback onto main thread + improve audio sample pacing
This commit is contained in:
parent
04be40767b
commit
96560e4914
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue