Merge pull request #11451 from jdgleaver/api-audio-buffer-status

Add API extension for cores to monitor frontend audio buffer occupancy
This commit is contained in:
Autechre 2020-10-17 17:56:11 +02:00 committed by GitHub
commit aa04d3ccdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 92 additions and 0 deletions

View File

@ -1335,6 +1335,15 @@ enum retro_mod
* should be considered active. * should be considered active.
*/ */
#define RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK 62
/* const struct retro_audio_buffer_status_callback * --
* Lets the core know the occupancy level of the frontend
* audio buffer. Can be used by a core to attempt frame
* skipping in order to avoid buffer under-runs.
* A core may pass NULL to disable buffer status reporting
* in the frontend.
*/
/* VFS functionality */ /* VFS functionality */
/* File paths: /* File paths:
@ -2224,6 +2233,30 @@ struct retro_frame_time_callback
retro_usec_t reference; retro_usec_t reference;
}; };
/* Notifies a libretro core of the current occupancy
* level of the frontend audio buffer.
*
* - active: 'true' if audio buffer is currently
* in use. Will be 'false' if audio is
* disabled in the frontend
*
* - occupancy: Given as a value in the range [0,100],
* corresponding to the occupancy percentage
* of the audio buffer
*
* - underrun_likely: 'true' if the frontend expects an
* audio buffer underrun during the
* next frame (indicates that a core
* should attempt frame skipping)
*
* It will be called right before retro_run() every frame. */
typedef void (RETRO_CALLCONV *retro_audio_buffer_status_callback_t)(
bool active, unsigned occupancy, bool underrun_likely);
struct retro_audio_buffer_status_callback
{
retro_audio_buffer_status_callback_t callback;
};
/* Pass this to retro_video_refresh_t if rendering to hardware. /* Pass this to retro_video_refresh_t if rendering to hardware.
* Passing NULL to retro_video_refresh_t is still a frame dupe as normal. * Passing NULL to retro_video_refresh_t is still a frame dupe as normal.
* */ * */

View File

@ -2156,6 +2156,7 @@ struct rarch_state
gfx_display_t dispgfx; /* ptr alignment */ gfx_display_t dispgfx; /* ptr alignment */
input_keyboard_press_t keyboard_press_cb; /* ptr alignment */ input_keyboard_press_t keyboard_press_cb; /* ptr alignment */
struct retro_frame_time_callback runloop_frame_time; /* ptr alignment */ struct retro_frame_time_callback runloop_frame_time; /* ptr alignment */
struct retro_audio_buffer_status_callback runloop_audio_buffer_status; /* ptr alignment */
retro_input_state_t input_state_callback_original; /* ptr alignment */ retro_input_state_t input_state_callback_original; /* ptr alignment */
struct retro_audio_callback audio_callback; /* ptr alignment */ struct retro_audio_callback audio_callback; /* ptr alignment */
retro_keyboard_event_t runloop_key_event; /* ptr alignment */ retro_keyboard_event_t runloop_key_event; /* ptr alignment */
@ -19891,6 +19892,21 @@ static bool rarch_environment_cb(unsigned cmd, void *data)
break; break;
} }
case RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK:
{
const struct retro_audio_buffer_status_callback *info =
(const struct retro_audio_buffer_status_callback*)data;
RARCH_LOG("[Environ]: SET_AUDIO_BUFFER_STATUS_CALLBACK.\n");
if (info)
p_rarch->runloop_audio_buffer_status.callback = info->callback;
else
p_rarch->runloop_audio_buffer_status.callback = NULL;
break;
}
case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE: case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE:
{ {
struct retro_rumble_interface *iface = struct retro_rumble_interface *iface =
@ -20880,6 +20896,8 @@ static void uninit_libretro_symbols(
retroarch_deinit_core_options(p_rarch); retroarch_deinit_core_options(p_rarch);
retroarch_system_info_free(p_rarch); retroarch_system_info_free(p_rarch);
retroarch_frame_time_free(p_rarch); retroarch_frame_time_free(p_rarch);
memset(&p_rarch->runloop_audio_buffer_status, 0,
sizeof(struct retro_audio_buffer_status_callback));
p_rarch->camera_driver_active = false; p_rarch->camera_driver_active = false;
p_rarch->location_driver_active = false; p_rarch->location_driver_active = false;
@ -38049,6 +38067,8 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data)
#endif #endif
p_rarch->runloop_autosave = false; p_rarch->runloop_autosave = false;
retroarch_frame_time_free(p_rarch); retroarch_frame_time_free(p_rarch);
memset(&p_rarch->runloop_audio_buffer_status, 0,
sizeof(struct retro_audio_buffer_status_callback));
break; break;
case RARCH_CTL_IS_IDLE: case RARCH_CTL_IS_IDLE:
return p_rarch->runloop_idle; return p_rarch->runloop_idle;
@ -40079,6 +40099,45 @@ int runloop_iterate(void)
p_rarch->runloop_frame_time.callback(delta); p_rarch->runloop_frame_time.callback(delta);
} }
/* Update audio buffer occupancy if buffer status
* callback is in use by the core */
if (p_rarch->runloop_audio_buffer_status.callback)
{
bool audio_buf_active = false;
unsigned audio_buf_occupancy = 0;
bool audio_buf_underrun = false;
if (!(p_rarch->runloop_paused ||
!p_rarch->audio_driver_active ||
!p_rarch->audio_driver_output_samples_buf) &&
p_rarch->current_audio->write_avail &&
p_rarch->audio_driver_context_audio_data &&
p_rarch->audio_driver_buffer_size)
{
size_t audio_buf_avail = p_rarch->current_audio->write_avail(
p_rarch->audio_driver_context_audio_data);
audio_buf_avail = (audio_buf_avail > p_rarch->audio_driver_buffer_size) ?
p_rarch->audio_driver_buffer_size : audio_buf_avail;
audio_buf_occupancy = (unsigned)(100 - (audio_buf_avail * 100) /
p_rarch->audio_driver_buffer_size);
/* Elsewhere, we standardise on a 'low water mark'
* of 25% of the total audio buffer size - use
* the same metric here (can be made more sophisticated
* if required - i.e. determine buffer occupancy in
* terms of usec, and weigh this against the expected
* frame time) */
audio_buf_underrun = audio_buf_occupancy < 25;
audio_buf_active = true;
}
p_rarch->runloop_audio_buffer_status.callback(
audio_buf_active, audio_buf_occupancy, audio_buf_underrun);
}
switch ((enum runloop_state)runloop_check_state(p_rarch, switch ((enum runloop_state)runloop_check_state(p_rarch,
settings, current_time)) settings, current_time))
{ {