diff --git a/audio/rsound.c b/audio/rsound.c index 83f4cda5b0..bdf965106a 100644 --- a/audio/rsound.c +++ b/audio/rsound.c @@ -181,6 +181,24 @@ static void rs_free(void *data) free(rsd); } +static size_t rs_write_avail(void *data) +{ + rsd_t *rsd = data; + + if (rsd->has_error) + return 0; + rsd_callback_lock(rsd->rd); + size_t val = fifo_write_avail(rsd->buffer); + rsd_callback_unlock(rsd->rd); + return val; +} + +static size_t rs_buffer_size(void *data) +{ + (void)data; + return 1024 * 4; +} + const audio_driver_t audio_rsound = { rs_init, rs_write, @@ -189,6 +207,8 @@ const audio_driver_t audio_rsound = { rs_set_nonblock_state, rs_free, NULL, - "rsound" + "rsound", + rs_write_avail, + rs_buffer_size, }; diff --git a/config.def.h b/config.def.h index 57e26bf29c..5980cc6de5 100644 --- a/config.def.h +++ b/config.def.h @@ -231,6 +231,16 @@ static const int out_latency = 64; // Will sync audio. (recommended) static const bool audio_sync = true; +// Experimental rate control +static const bool rate_control = false; + +// Rate control delta. Defines how much rate_control is allowed to adjust input rate. +static const float rate_control_delta = 0.005; + +////////////// +// Misc +////////////// + // Enables use of rewind. This will incur some memory footprint depending on the save state buffer. // This rewind only works when using bSNES core atm. static const bool rewind_enable = false; diff --git a/driver.c b/driver.c index ad773ca2ae..a27c6eeae3 100644 --- a/driver.c +++ b/driver.c @@ -335,9 +335,21 @@ void init_audio(void) ssnes_assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO); ssnes_assert(g_extern.audio_data.outsamples = (float*)malloc(max_bufsamples * sizeof(float) * AUDIO_MAX_RATIO)); - g_extern.audio_data.src_ratio = + g_extern.audio_data.orig_src_ratio = + g_extern.audio_data.src_ratio = (double)g_settings.audio.out_rate / g_settings.audio.in_rate; + if (g_settings.audio.rate_control) + { + if (driver.audio->buffer_size && driver.audio->write_avail) + { + g_extern.audio_data.driver_buffer_size = driver.audio->buffer_size(driver.audio_data); + g_extern.audio_data.rate_control = true; + } + else + SSNES_WARN("Audio rate control was desired, but driver does not support needed features.\n"); + } + #ifdef HAVE_DYLIB init_dsp_plugin(); #endif diff --git a/driver.h b/driver.h index dce12bbfbb..35069c5341 100644 --- a/driver.h +++ b/driver.h @@ -98,6 +98,9 @@ typedef struct audio_driver void (*free)(void *data); bool (*use_float)(void *data); // Defines if driver will take standard floating point samples, or int16_t samples. const char *ident; + + size_t (*write_avail)(void *data); // Optional + size_t (*buffer_size)(void *data); // Optional } audio_driver_t; #define AXIS_NEG(x) (((uint32_t)(x) << 16) | UINT16_C(0xFFFF)) diff --git a/general.h b/general.h index 2a9cc8b331..0ec9ab387f 100644 --- a/general.h +++ b/general.h @@ -138,6 +138,9 @@ struct settings char dsp_plugin[PATH_MAX]; char external_driver[PATH_MAX]; + + bool rate_control; + float rate_control_delta; } audio; struct @@ -299,6 +302,10 @@ struct global dylib_t dsp_lib; const ssnes_dsp_plugin_t *dsp_plugin; void *dsp_handle; + + bool rate_control; + double orig_src_ratio; + size_t driver_buffer_size; } audio_data; struct diff --git a/settings.c b/settings.c index 5980071922..55cf817650 100644 --- a/settings.c +++ b/settings.c @@ -195,6 +195,8 @@ void config_set_defaults(void) strlcpy(g_settings.audio.device, audio_device, sizeof(g_settings.audio.device)); g_settings.audio.latency = out_latency; g_settings.audio.sync = audio_sync; + g_settings.audio.rate_control = rate_control; + g_settings.audio.rate_control_delta = rate_control_delta; g_settings.rewind_enable = rewind_enable; g_settings.rewind_buffer_size = rewind_buffer_size; @@ -429,6 +431,8 @@ bool config_load_file(const char *path) CONFIG_GET_STRING(audio.device, "audio_device"); CONFIG_GET_INT(audio.latency, "audio_latency"); CONFIG_GET_BOOL(audio.sync, "audio_sync"); + CONFIG_GET_BOOL(audio.rate_control, "audio_rate_control"); + CONFIG_GET_FLOAT(audio.rate_control_delta, "audio_rate_control_delta"); CONFIG_GET_STRING(video.driver, "video_driver"); CONFIG_GET_STRING(audio.driver, "audio_driver"); diff --git a/ssnes.c b/ssnes.c index f0cec564b4..13c55559c6 100644 --- a/ssnes.c +++ b/ssnes.c @@ -246,6 +246,24 @@ void ssnes_render_cached_frame(void) #endif } +static void readjust_audio_input_rate(void) +{ + int avail = driver.audio->write_avail(driver.audio_data); + //fprintf(stderr, "Audio buffer is %u%% full\n", + // (unsigned)(100 - (avail * 100) / g_extern.audio_data.driver_buffer_size)); + + int half_size = g_extern.audio_data.driver_buffer_size / 2; + int delta_mid = avail - half_size; + double direction = (double)delta_mid / half_size; + + double adjust = 1.0 + g_settings.audio.rate_control_delta * direction; + + g_extern.audio_data.src_ratio = g_extern.audio_data.orig_src_ratio * adjust; + + //fprintf(stderr, "New rate: %lf, Orig rate: %lf\n", + // g_extern.audio_data.src_ratio, g_extern.audio_data.orig_src_ratio); +} + static bool audio_flush(const int16_t *data, size_t samples) { #ifdef HAVE_FFMPEG @@ -284,6 +302,10 @@ static bool audio_flush(const int16_t *data, size_t samples) src_data.data_in = dsp_output.samples ? dsp_output.samples : g_extern.audio_data.data; src_data.data_out = g_extern.audio_data.outsamples; src_data.input_frames = dsp_output.samples ? dsp_output.frames : (samples / 2); + + if (g_extern.audio_data.rate_control) + readjust_audio_input_rate(); + src_data.ratio = g_extern.audio_data.src_ratio; hermite_process(g_extern.audio_data.source, &src_data); diff --git a/ssnes.cfg b/ssnes.cfg index 6045182034..f018ec4795 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -146,6 +146,12 @@ # Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency. # audio_latency = 64 +# Enable experimental audio rate control. +# audio_rate_control = false + +# Controls audio rate control delta. Defines how much input rate can be adjusted dynamically. +# Input rate = in_rate * (1.0 +/- audio_rate_control_delta) +# audio_rate_control_delta = 0.005 #### Input