diff --git a/audio/jack.c b/audio/jack.c index 01ae1c9ef6..d4fb440cb5 100644 --- a/audio/jack.c +++ b/audio/jack.c @@ -29,8 +29,7 @@ #include #include -#define FRAMES(x) (x / (sizeof(int16_t) * 2)) -#define SAMPLES(x) (x / sizeof(int16_t)) +#define FRAMES(x) (x / (sizeof(float) * 2)) typedef struct jack { @@ -61,13 +60,6 @@ static int process_cb(jack_nframes_t nframes, void *data) if (min_avail > nframes) min_avail = nframes; - //static int underrun = 0; - //if (min_avail < nframes) - //{ - // SSNES_LOG("JACK: Underrun count: %d\n", underrun++); - // fprintf(stderr, "required %d frames, got %d.\n", (int)nframes, (int)min_avail); - //} - for (int i = 0; i < 2; i++) { jack_default_audio_sample_t *out = jack_port_get_buffer(jd->ports[i], nframes); @@ -90,12 +82,6 @@ static void shutdown_cb(void *data) pthread_cond_signal(&jd->cond); } -static inline void s16_to_float(jack_default_audio_sample_t * restrict out, const int16_t * restrict in, size_t samples) -{ - for (int i = 0; i < samples; i++) - out[i] = (float)in[i] / 0x8000; -} - static void parse_ports(const char **dest_ports, const char **jports) { int parsed = 0; @@ -142,7 +128,7 @@ static void* __jack_init(const char* device, int rate, int latency) bufsize = (latency * g_settings.audio.out_rate / 1000) > jack_bufsize * 2 ? (latency * g_settings.audio.out_rate / 1000) : jack_bufsize * 2; bufsize *= sizeof(jack_default_audio_sample_t); - //fprintf(stderr, "jack buffer size: %d\n", (int)bufsize); + SSNES_LOG("Jack buffer size: %d bytes: (~%.2f msec latency)\n", (int)bufsize, (float)bufsize * 1000 / (g_settings.audio.out_rate * sizeof(jack_default_audio_sample_t))); for (int i = 0; i < 2; i++) { jd->buffer[i] = jack_ringbuffer_create(bufsize); @@ -181,7 +167,6 @@ static void* __jack_init(const char* device, int rate, int latency) pthread_cond_init(&jd->cond, NULL); pthread_mutex_init(&jd->cond_lock, NULL); - jack_free(jports); return jd; @@ -191,17 +176,13 @@ error: return NULL; } -static size_t write_buffer(jack_t *jd, const void *buf, size_t size) +static size_t write_buffer(jack_t *jd, const float *buf, size_t size) { - //fprintf(stderr, "write_buffer: size: %zu\n", size); - // Convert our data to float, deinterleave and write. - jack_default_audio_sample_t out_buffer[size / sizeof(int16_t)]; jack_default_audio_sample_t out_deinterleaved_buffer[2][FRAMES(size)]; - s16_to_float(out_buffer, buf, SAMPLES(size)); for (int i = 0; i < 2; i++) for (size_t j = 0; j < FRAMES(size); j++) - out_deinterleaved_buffer[i][j] = out_buffer[j * 2 + i]; + out_deinterleaved_buffer[i][j] = buf[j * 2 + i]; for(;;) { @@ -216,13 +197,11 @@ static size_t write_buffer(jack_t *jd, const void *buf, size_t size) if (jd->nonblock) { if (min_avail < FRAMES(size) * sizeof(jack_default_audio_sample_t)) - size = min_avail * 2 * sizeof(int16_t) / sizeof(jack_default_audio_sample_t); + size = min_avail * 2; break; } - else { - //fprintf(stderr, "Write avail is: %d\n", (int)min_avail); if (min_avail >= FRAMES(size) * sizeof(jack_default_audio_sample_t)) break; } @@ -290,6 +269,7 @@ const audio_driver_t audio_jack = { .start = __jack_start, .set_nonblock_state = __jack_set_nonblock_state, .free = __jack_free, + .float_samples = true, .ident = "jack" }; diff --git a/driver.c b/driver.c index 67ce93c661..4e78e6439b 100644 --- a/driver.c +++ b/driver.c @@ -21,6 +21,7 @@ #include #include #include "hqflt/filters.h" +#include #ifdef HAVE_CONFIG_H #include "config.h" @@ -128,6 +129,9 @@ void uninit_drivers(void) uninit_audio(); } +#define AUDIO_CHUNK_SIZE_BLOCKING 64 +#define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio. +#define AUDIO_MAX_RATIO 16 void init_audio(void) { if (!g_settings.audio.enable) @@ -138,17 +142,35 @@ void init_audio(void) find_audio_driver(); + g_extern.audio_data.block_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING; + g_extern.audio_data.nonblock_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING; + driver.audio_data = driver.audio->init(strlen(g_settings.audio.device) ? g_settings.audio.device : NULL, g_settings.audio.out_rate, g_settings.audio.latency); if ( driver.audio_data == NULL ) g_extern.audio_active = false; + if (!g_settings.audio.sync && g_extern.audio_active) + { driver.audio->set_nonblock_state(driver.audio_data, true); + g_extern.audio_data.chunk_size = g_extern.audio_data.nonblock_chunk_size; + } + else + g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size; int err; g_extern.source = src_new(g_settings.audio.src_quality, 2, &err); if (!g_extern.source) g_extern.audio_active = false; + + size_t max_bufsamples = g_extern.audio_data.block_chunk_size > g_extern.audio_data.nonblock_chunk_size ? + g_extern.audio_data.block_chunk_size : g_extern.audio_data.nonblock_chunk_size; + + assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO); + assert((g_extern.audio_data.data = malloc(max_bufsamples * sizeof(float)))); + g_extern.audio_data.data_ptr = 0; + assert((g_extern.audio_data.outsamples = malloc(max_bufsamples * sizeof(float) * AUDIO_MAX_RATIO))); + assert((g_extern.audio_data.conv_outsamples = malloc(max_bufsamples * sizeof(int16_t) * AUDIO_MAX_RATIO))); } void uninit_audio(void) @@ -164,6 +186,10 @@ void uninit_audio(void) if ( g_extern.source ) src_delete(g_extern.source); + + free(g_extern.audio_data.data); g_extern.audio_data.data = NULL; + free(g_extern.audio_data.outsamples); g_extern.audio_data.outsamples = NULL; + free(g_extern.audio_data.conv_outsamples); g_extern.audio_data.conv_outsamples = NULL; } void init_video_input(void) diff --git a/driver.h b/driver.h index 73ae142c48..7b8303b1be 100644 --- a/driver.h +++ b/driver.h @@ -61,6 +61,7 @@ typedef struct audio_driver bool (*start)(void* data); void (*set_nonblock_state)(void* data, bool toggle); // Should we care about blocking in audio thread? Fast forwarding. void (*free)(void* data); + bool float_samples; // Defines if driver will take standard floating point samples, or int16_t samples. const char *ident; } audio_driver_t; diff --git a/general.h b/general.h index e835948c1a..174257b3f5 100644 --- a/general.h +++ b/general.h @@ -117,6 +117,18 @@ struct global char savefile_name_bsrm[512]; char savestate_name[256]; + struct + { + float *data; + size_t data_ptr; + size_t chunk_size; + size_t nonblock_chunk_size; + size_t block_chunk_size; + + float *outsamples; + int16_t *conv_outsamples; + } audio_data; + #ifdef HAVE_FFMPEG ffemu_t *rec; char record_path[256]; diff --git a/ssnes.c b/ssnes.c index 979a789b6d..ab6201ceb3 100644 --- a/ssnes.c +++ b/ssnes.c @@ -40,10 +40,6 @@ struct global g_extern = { }; // To avoid continous switching if we hold the button down, we require that the button must go from pressed, unpressed back to pressed to be able to toggle between then. - -#define AUDIO_CHUNK_SIZE_BLOCKING 64 -#define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio. -static size_t audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING; static void set_fast_forward_button(bool new_button_state) { static bool old_button_state = false; @@ -55,10 +51,11 @@ static void set_fast_forward_button(bool new_button_state) driver.video->set_nonblock_state(driver.video_data, syncing_state); if (g_extern.audio_active) driver.audio->set_nonblock_state(driver.audio_data, (g_settings.audio.sync) ? syncing_state : true); + if (syncing_state) - audio_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING; + g_extern.audio_data.chunk_size = g_extern.audio_data.nonblock_chunk_size; else - audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING; + g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size; } old_button_state = new_button_state; } @@ -163,37 +160,42 @@ static void audio_sample(uint16_t left, uint16_t right) } #endif - static float data[AUDIO_CHUNK_SIZE_NONBLOCKING]; - static int data_ptr = 0; + g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(*(int16_t*)&left)/0x8000; + g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(*(int16_t*)&right)/0x8000; - data[data_ptr++] = (float)(*(int16_t*)&left)/0x8000; - data[data_ptr++] = (float)(*(int16_t*)&right)/0x8000; - - if ( data_ptr >= audio_chunk_size ) + if (g_extern.audio_data.data_ptr >= g_extern.audio_data.chunk_size) { - float outsamples[audio_chunk_size * 16]; - int16_t temp_outsamples[audio_chunk_size * 16]; - SRC_DATA src_data; - src_data.data_in = data; - src_data.data_out = outsamples; - src_data.input_frames = audio_chunk_size / 2; - src_data.output_frames = audio_chunk_size * 8; + src_data.data_in = g_extern.audio_data.data; + src_data.data_out = g_extern.audio_data.outsamples; + src_data.input_frames = g_extern.audio_data.chunk_size / 2; + src_data.output_frames = g_extern.audio_data.chunk_size * 8; src_data.end_of_input = 0; src_data.src_ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate; src_process(g_extern.source, &src_data); - src_float_to_short_array(outsamples, temp_outsamples, src_data.output_frames_gen * 2); - - if ( driver.audio->write(driver.audio_data, temp_outsamples, src_data.output_frames_gen * 4) < 0 ) + if (driver.audio->float_samples) { - fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n"); - g_extern.audio_active = false; + if (driver.audio->write(driver.audio_data, g_extern.audio_data.outsamples, src_data.output_frames_gen * sizeof(float) * 2) < 0) + { + fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n"); + g_extern.audio_active = false; + } + } + else + { + src_float_to_short_array(g_extern.audio_data.outsamples, g_extern.audio_data.conv_outsamples, src_data.output_frames_gen * 2); + + if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples, src_data.output_frames_gen * sizeof(int16_t) * 2) < 0) + { + fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n"); + g_extern.audio_active = false; + } } - data_ptr = 0; + g_extern.audio_data.data_ptr = 0; } }