diff --git a/dynamic.c b/dynamic.c index 0a479c59af..cbf2245b6f 100644 --- a/dynamic.c +++ b/dynamic.c @@ -289,6 +289,8 @@ void dylib_close(dylib_t lib) } #endif +unsigned audio_sample_batch(const int16_t *, unsigned); + #if defined(NEED_DYNAMIC) || defined(SSNES_CONSOLE) static bool environment_cb(unsigned cmd, void *data) { @@ -410,6 +412,11 @@ static bool environment_cb(unsigned cmd, void *data) break; } + case SNES_ENVIRONMENT_GET_AUDIO_BATCH_CB: + SSNES_LOG("Environ GET_AUDIO_BATCH_CB\n"); + *(snes_audio_sample_batch_t*)data = audio_sample_batch; + break; + default: SSNES_LOG("Environ UNSUPPORTED (#%u).\n", cmd); return false; diff --git a/libsnes.hpp b/libsnes.hpp index 4227af09d2..dffcaeca02 100755 --- a/libsnes.hpp +++ b/libsnes.hpp @@ -126,6 +126,11 @@ extern "C" { // #define SNES_ENVIRONMENT_SET_MESSAGE 12 // const struct snes_message * -- // Sets a message to be displayed in implementation-specific manner for a certain amount of 'frames'. + // +#define SNES_ENVIRONMENT_GET_AUDIO_BATCH_CB 13 // snes_audio_sample_batch_t * -- + // Retrieves callback to a more optimized audio callback. + + struct snes_message { @@ -164,6 +169,10 @@ void snes_set_environment(snes_environment_t); typedef void (*snes_video_refresh_t)(const uint16_t *data, unsigned width, unsigned height); typedef void (*snes_audio_sample_t)(uint16_t left, uint16_t right); + +// Performance extension. Retrieved from environ callback. +typedef unsigned (*snes_audio_sample_batch_t)(const int16_t *data, unsigned frames); + typedef void (*snes_input_poll_t)(void); typedef int16_t (*snes_input_state_t)(bool port, unsigned device, unsigned index, unsigned id); diff --git a/ssnes.c b/ssnes.c index e784182808..d5b97b584a 100644 --- a/ssnes.c +++ b/ssnes.c @@ -390,6 +390,28 @@ static void audio_sample(uint16_t left, uint16_t right) g_extern.audio_data.data_ptr = 0; } +// Non-standard, alternative callback better suited for systems that process audio in batch. +// Avoids tons of calls to audio_sample() ... +unsigned audio_sample_batch(const int16_t *data, unsigned frames) +{ + if (frames > (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1)) + frames = AUDIO_CHUNK_SIZE_NONBLOCKING >> 1; + + memcpy(g_extern.audio_data.conv_outsamples, + data, (frames << 1) * sizeof(int16_t)); + g_extern.audio_data.data_ptr = frames << 1; + + if (g_extern.audio_data.data_ptr >= g_extern.audio_data.chunk_size) + { + g_extern.audio_active = audio_flush(g_extern.audio_data.conv_outsamples, + g_extern.audio_data.data_ptr) && g_extern.audio_active; + + g_extern.audio_data.data_ptr = 0; + } + + return frames; +} + static void input_poll(void) { driver.input->poll(driver.input_data);