diff --git a/audio/opensl.c b/audio/opensl.c index 7ca05601eb..c2ab6bea2a 100644 --- a/audio/opensl.c +++ b/audio/opensl.c @@ -16,7 +16,6 @@ #include "../driver.h" #include "../general.h" -#include "../fifo_buffer.h" #include "../thread.h" #include @@ -35,20 +34,24 @@ #define SLPlayItf_SetPlayState(a, ...) ((*(a))->SetPlayState(a, __VA_ARGS__)) // TODO: Are these sane? -#define BUFFER_SIZE 4096 +#define BUFFER_SIZE 8192 +#define BUFFER_COUNT 4 typedef struct sl { - uint8_t buffer[BUFFER_SIZE]; + uint8_t buffer[BUFFER_COUNT][BUFFER_SIZE]; + unsigned buffer_index; + unsigned buffer_ptr; + volatile unsigned buffered_blocks; SLObjectItf engine_object; SLEngineItf engine; SLObjectItf output_mix; SLObjectItf buffer_queue_object; + SLAndroidSimpleBufferQueueItf buffer_queue; SLPlayItf player; - fifo_buffer_t *fifo; slock_t *lock; scond_t *cond; bool nonblock; @@ -60,15 +63,9 @@ static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *ctx) sl_t *sl = (sl_t*)ctx; slock_lock(sl->lock); - size_t read_avail = fifo_read_avail(sl->fifo); - if (read_avail > BUFFER_SIZE) - read_avail = BUFFER_SIZE; - fifo_read(sl->fifo, sl->buffer, read_avail); + sl->buffered_blocks--; slock_unlock(sl->lock); - memset(sl->buffer + read_avail, 0, BUFFER_SIZE - read_avail); - (*bq)->Enqueue(bq, sl->buffer, BUFFER_SIZE); - scond_signal(sl->cond); } @@ -95,8 +92,6 @@ static void sl_free(void *data) if (sl->engine_object) SLObjectItf_Destroy(sl->engine_object); - if (sl->fifo) - fifo_free(sl->fifo); if (sl->lock) slock_free(sl->lock); if (sl->cond) @@ -119,8 +114,6 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency) SLInterfaceID id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; SLboolean req = SL_BOOLEAN_TRUE; - SLAndroidSimpleBufferQueueItf buffer_queue = NULL; - SLresult res = 0; sl_t *sl = (sl_t*)calloc(1, sizeof(sl_t)); if (!sl) @@ -132,7 +125,7 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency) GOTO_IF_FAIL(SLEngineItf_CreateOutputMix(sl->engine, &sl->output_mix, 0, NULL, NULL)); GOTO_IF_FAIL(SLObjectItf_Realize(sl->output_mix, SL_BOOLEAN_FALSE)); - sl->buf_count = 8; + sl->buf_count = BUFFER_COUNT; RARCH_LOG("[SLES] : Setting audio latency (buffer size: [%d])..\n", sl->buf_count * BUFFER_SIZE); @@ -161,21 +154,22 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency) GOTO_IF_FAIL(SLObjectItf_Realize(sl->buffer_queue_object, SL_BOOLEAN_FALSE)); GOTO_IF_FAIL(SLObjectItf_GetInterface(sl->buffer_queue_object, SL_IID_BUFFERQUEUE, - &buffer_queue)); + &sl->buffer_queue)); sl->cond = scond_new(); sl->lock = slock_new(); - sl->fifo = fifo_new(BUFFER_SIZE * sl->buf_count); - (*buffer_queue)->RegisterCallback(buffer_queue, opensl_callback, sl); - (*buffer_queue)->Enqueue(buffer_queue, sl->buffer, BUFFER_SIZE); + (*sl->buffer_queue)->RegisterCallback(sl->buffer_queue, opensl_callback, sl); + + // Enqueue a bit to get stuff rolling. + sl->buffered_blocks = BUFFER_COUNT; + sl->buffer_index = 0; + for (unsigned i = 0; i < BUFFER_COUNT; i++) + (*sl->buffer_queue)->Enqueue(sl->buffer_queue, sl->buffer[i], BUFFER_SIZE); GOTO_IF_FAIL(SLObjectItf_GetInterface(sl->buffer_queue_object, SL_IID_PLAY, &sl->player)); GOTO_IF_FAIL(SLPlayItf_SetPlayState(sl->player, SL_PLAYSTATE_PLAYING)); - g_settings.audio.rate_control_delta = 0.006; - g_settings.audio.rate_control = true; - return sl; error: @@ -213,31 +207,44 @@ static ssize_t sl_write(void *data, const void *buf_, size_t size) while (size) { slock_lock(sl->lock); - - size_t write_avail = fifo_write_avail(sl->fifo); - if (write_avail > size) - write_avail = size; - - if (write_avail) + if (sl->nonblock) { - fifo_write(sl->fifo, buf, write_avail); - slock_unlock(sl->lock); - written += write_avail; - size -= write_avail; - buf += write_avail; - } - else if (!sl->nonblock) - { - scond_wait(sl->cond, sl->lock); - slock_unlock(sl->lock); + if (sl->buffered_blocks == BUFFER_COUNT) + { + slock_unlock(sl->lock); + break; + } } else { + while (sl->buffered_blocks == BUFFER_COUNT) + scond_wait(sl->cond, sl->lock); + } + slock_unlock(sl->lock); + + size_t avail_write = min(BUFFER_SIZE - sl->buffer_ptr, size); + if (avail_write) + { + memcpy(sl->buffer[sl->buffer_index] + sl->buffer_ptr, buf, avail_write); + sl->buffer_ptr += avail_write; + buf += avail_write; + size -= avail_write; + written += avail_write; + } + + if (sl->buffer_ptr >= BUFFER_SIZE) + { + slock_lock(sl->lock); + (*sl->buffer_queue)->Enqueue(sl->buffer_queue, sl->buffer[sl->buffer_index], BUFFER_SIZE); + sl->buffer_index = (sl->buffer_index + 1) & (BUFFER_COUNT - 1); + sl->buffered_blocks++; slock_unlock(sl->lock); - break; + sl->buffer_ptr = 0; } } + //RARCH_LOG("Blocks: %u\n", sl->buffered_blocks); + return written; } @@ -245,15 +252,15 @@ static size_t sl_write_avail(void *data) { sl_t *sl = (sl_t*)data; slock_lock(sl->lock); - size_t avail = fifo_write_avail(sl->fifo); + size_t avail = (BUFFER_COUNT - (int)sl->buffered_blocks - 1) * BUFFER_SIZE + (BUFFER_SIZE - (int)sl->buffer_ptr); slock_unlock(sl->lock); return avail; } static size_t sl_buffer_size(void *data) { - sl_t *sl = (sl_t*)data; - return BUFFER_SIZE * sl->buf_count; + (void)data; + return BUFFER_SIZE * BUFFER_COUNT; } const audio_driver_t audio_opensl = { diff --git a/driver.c b/driver.c index 6f1a387b87..28a360c2cf 100644 --- a/driver.c +++ b/driver.c @@ -434,6 +434,52 @@ void init_audio(void) #ifdef HAVE_DYLIB init_dsp_plugin(); #endif + + g_extern.audio_data.buffer_free_samples_count = 0; +} + +static void compute_audio_buffer_statistics(void) +{ + unsigned samples = min(g_extern.audio_data.buffer_free_samples_count, AUDIO_BUFFER_FREE_SAMPLES_COUNT); + if (!samples) + return; + + uint64_t accum = 0; + for (unsigned i = 0; i < samples; i++) + accum += g_extern.audio_data.buffer_free_samples[i]; + + int avg = accum / samples; + + uint64_t accum_var = 0; + for (unsigned i = 0; i < samples; i++) + { + int diff = avg - g_extern.audio_data.buffer_free_samples[i]; + accum_var += diff * diff; + } + + unsigned stddev = (unsigned)sqrtf((float)accum_var / samples); + + float avg_filled = 1.0f - (float)avg / g_extern.audio_data.driver_buffer_size; + float deviation = (float)stddev / g_extern.audio_data.driver_buffer_size; + + unsigned low_water_size = g_extern.audio_data.driver_buffer_size * 3 / 4; + unsigned high_water_size = g_extern.audio_data.driver_buffer_size / 4; + + unsigned low_water_count = 0; + unsigned high_water_count = 0; + for (unsigned i = 0; i < samples; i++) + { + if (g_extern.audio_data.buffer_free_samples[i] >= low_water_size) + low_water_count++; + else if (g_extern.audio_data.buffer_free_samples[i] <= high_water_size) + high_water_count++; + } + + RARCH_LOG("Average audio buffer saturation: %.2f %%, standard deviation (percentage points): %.2f %%.\n", + avg_filled * 100.0, deviation * 100.0); + RARCH_LOG("Amount of time spent close to underrun: %.2f %%. Close to blocking: %.2f %%.\n", + (100.0 * low_water_count) / samples, + (100.0 * high_water_count) / samples); } void uninit_audio(void) @@ -466,6 +512,8 @@ void uninit_audio(void) #ifdef HAVE_DYLIB deinit_dsp_plugin(); #endif + + compute_audio_buffer_statistics(); } #ifdef HAVE_DYLIB diff --git a/general.h b/general.h index d456a76592..38275bb9c3 100644 --- a/general.h +++ b/general.h @@ -401,6 +401,10 @@ struct global float volume_db; float volume_gain; + +#define AUDIO_BUFFER_FREE_SAMPLES_COUNT (8 * 1024) + unsigned buffer_free_samples[AUDIO_BUFFER_FREE_SAMPLES_COUNT]; + uint64_t buffer_free_samples_count; } audio_data; struct diff --git a/retroarch.c b/retroarch.c index f1c40397fa..fbf78512b8 100644 --- a/retroarch.c +++ b/retroarch.c @@ -182,6 +182,9 @@ static void readjust_audio_input_rate(void) //RARCH_LOG_OUTPUT("Audio buffer is %u%% full\n", // (unsigned)(100 - (avail * 100) / g_extern.audio_data.driver_buffer_size)); + unsigned write_index = (g_extern.audio_data.buffer_free_samples_count++) & (AUDIO_BUFFER_FREE_SAMPLES_COUNT - 1); + g_extern.audio_data.buffer_free_samples[write_index] = avail; + int half_size = g_extern.audio_data.driver_buffer_size / 2; int delta_mid = avail - half_size; double direction = (double)delta_mid / half_size;