From fd0bb1b3ee873ab7f3b9f69f3b36137466ce1812 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Fri, 4 Jan 2013 14:58:34 -0500 Subject: [PATCH 1/2] make alsathread a seperate audio driver --- Makefile | 2 +- audio/alsathread.c | 314 +++++++++++++++++++++++++++++++++++++++++++++ config.def.h | 3 + driver.c | 1 + driver.h | 1 + settings.c | 2 + 6 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 audio/alsathread.c diff --git a/Makefile b/Makefile index 3639f7cc37..3e421d4da0 100644 --- a/Makefile +++ b/Makefile @@ -107,7 +107,7 @@ ifeq ($(HAVE_OSS_LIB), 1) endif ifeq ($(HAVE_ALSA), 1) - OBJ += audio/alsa.o + OBJ += audio/alsa.o audio/alsathread.o LIBS += $(ALSA_LIBS) DEFINES += $(ALSA_CFLAGS) endif diff --git a/audio/alsathread.c b/audio/alsathread.c new file mode 100644 index 0000000000..647dd9c06b --- /dev/null +++ b/audio/alsathread.c @@ -0,0 +1,314 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + + +#include "../driver.h" +#include +#include +#include "../general.h" +#include "../thread.h" +#include "../fifo_buffer.h" + +#define TRY_ALSA(x) if (x < 0) { \ + goto error; \ + } + +typedef struct alsa +{ + snd_pcm_t *pcm; + bool nonblock; + bool has_float; + volatile bool thread_dead; + + size_t buffer_size; + size_t period_size; + snd_pcm_uframes_t period_frames; + + fifo_buffer_t *buffer; + sthread_t *worker_thread; + slock_t *fifo_lock; + scond_t *cond; + slock_t *cond_lock; +} alsa_t; + +static void alsa_worker_thread(void *data) +{ + alsa_t *alsa = (alsa_t*)data; + + uint8_t *buf = (uint8_t *)calloc(1, alsa->period_size); + if (!buf) + { + RARCH_ERR("failed to allocate audio buffer"); + goto end; + } + + while (!alsa->thread_dead) + { + slock_lock(alsa->fifo_lock); + size_t avail = fifo_read_avail(alsa->buffer); + size_t fifo_size = min(alsa->period_size, avail); + fifo_read(alsa->buffer, buf, fifo_size); + scond_signal(alsa->cond); + slock_unlock(alsa->fifo_lock); + + // If underrun, fill rest with silence. + memset(buf + fifo_size, 0, alsa->period_size - fifo_size); + + snd_pcm_sframes_t frames = snd_pcm_writei(alsa->pcm, buf, alsa->period_frames); + + if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) + { + if (snd_pcm_recover(alsa->pcm, frames, 1) < 0) + { + RARCH_ERR("[ALSA]: (#2) Failed to recover from error (%s)\n", + snd_strerror(frames)); + break; + } + + continue; + } + else if (frames < 0) + { + RARCH_ERR("[ALSA]: Unknown error occured (%s).\n", snd_strerror(frames)); + break; + } + } + +end: + slock_lock(alsa->cond_lock); + alsa->thread_dead = true; + scond_signal(alsa->cond); + slock_unlock(alsa->cond_lock); + free(buf); +} + +static bool alsa_use_float(void *data) +{ + alsa_t *alsa = (alsa_t*)data; + return alsa->has_float; +} + +static bool find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + if (snd_pcm_hw_params_test_format(pcm, params, SND_PCM_FORMAT_FLOAT) == 0) + { + RARCH_LOG("ALSA: Using floating point format.\n"); + return true; + } + RARCH_LOG("ALSA: Using signed 16-bit format.\n"); + return false; +} + +static void alsa_free(void *data) +{ + alsa_t *alsa = (alsa_t*)data; + + if (alsa) + { + if (alsa->worker_thread) + { + alsa->thread_dead = true; + sthread_join(alsa->worker_thread); + } + if (alsa->buffer) + fifo_free(alsa->buffer); + if (alsa->cond) + scond_free(alsa->cond); + if (alsa->fifo_lock) + slock_free(alsa->fifo_lock); + if (alsa->cond_lock) + slock_free(alsa->cond_lock); + if (alsa->pcm) + { + snd_pcm_drop(alsa->pcm); + snd_pcm_close(alsa->pcm); + } + free(alsa); + } +} + +static void *alsa_init(const char *device, unsigned rate, unsigned latency) +{ + alsa_t *alsa = (alsa_t*)calloc(1, sizeof(alsa_t)); + if (!alsa) + return NULL; + + snd_pcm_hw_params_t *params = NULL; + snd_pcm_sw_params_t *sw_params = NULL; + + const char *alsa_dev = "default"; + if (device) + alsa_dev = device; + + unsigned latency_usec = latency * 1000 / 2; + + unsigned channels = 2; + unsigned periods = 4; + snd_pcm_uframes_t buffer_size; + snd_pcm_format_t format; + + TRY_ALSA(snd_pcm_open(&alsa->pcm, alsa_dev, SND_PCM_STREAM_PLAYBACK, 0)); + + TRY_ALSA(snd_pcm_hw_params_malloc(¶ms)); + alsa->has_float = find_float_format(alsa->pcm, params); + format = alsa->has_float ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16; + + TRY_ALSA(snd_pcm_hw_params_any(alsa->pcm, params)); + TRY_ALSA(snd_pcm_hw_params_set_access(alsa->pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED)); + TRY_ALSA(snd_pcm_hw_params_set_format(alsa->pcm, params, format)); + TRY_ALSA(snd_pcm_hw_params_set_channels(alsa->pcm, params, channels)); + TRY_ALSA(snd_pcm_hw_params_set_rate(alsa->pcm, params, rate, 0)); + + TRY_ALSA(snd_pcm_hw_params_set_buffer_time_near(alsa->pcm, params, &latency_usec, NULL)); + TRY_ALSA(snd_pcm_hw_params_set_periods_near(alsa->pcm, params, &periods, NULL)); + + TRY_ALSA(snd_pcm_hw_params(alsa->pcm, params)); + + snd_pcm_hw_params_get_period_size(params, &alsa->period_frames, NULL); + RARCH_LOG("ALSA: Period size: %d frames\n", (int)alsa->period_frames); + snd_pcm_hw_params_get_buffer_size(params, &buffer_size); + RARCH_LOG("ALSA: Buffer size: %d frames\n", (int)buffer_size); + alsa->buffer_size = snd_pcm_frames_to_bytes(alsa->pcm, buffer_size); + alsa->period_size = snd_pcm_frames_to_bytes(alsa->pcm, alsa->period_frames); + + TRY_ALSA(snd_pcm_sw_params_malloc(&sw_params)); + TRY_ALSA(snd_pcm_sw_params_current(alsa->pcm, sw_params)); + TRY_ALSA(snd_pcm_sw_params_set_start_threshold(alsa->pcm, sw_params, buffer_size / 2)); + TRY_ALSA(snd_pcm_sw_params(alsa->pcm, sw_params)); + + snd_pcm_hw_params_free(params); + snd_pcm_sw_params_free(sw_params); + + alsa->fifo_lock = slock_new(); + alsa->cond_lock = slock_new(); + alsa->cond = scond_new(); + alsa->buffer = fifo_new(alsa->buffer_size); + if (!alsa->fifo_lock || !alsa->cond_lock || !alsa->cond || !alsa->buffer) + goto error; + + alsa->worker_thread = sthread_create(alsa_worker_thread, alsa); + if (!alsa->worker_thread) + { + RARCH_ERR("error initializing worker thread"); + goto error; + } + + return alsa; + +error: + RARCH_ERR("ALSA: Failed to initialize...\n"); + if (params) + snd_pcm_hw_params_free(params); + + if (sw_params) + snd_pcm_sw_params_free(sw_params); + + alsa_free(alsa); + + return NULL; +} + +static ssize_t alsa_write(void *data, const void *buf, size_t size) +{ + alsa_t *alsa = (alsa_t*)data; + + if (alsa->thread_dead) + return -1; + + if (alsa->nonblock) + { + slock_lock(alsa->fifo_lock); + size_t avail = fifo_write_avail(alsa->buffer); + size_t write_amt = min(avail, size); + fifo_write(alsa->buffer, buf, write_amt); + slock_unlock(alsa->fifo_lock); + return write_amt; + } + else + { + size_t written = 0; + while (written < size && !alsa->thread_dead) + { + slock_lock(alsa->fifo_lock); + size_t avail = fifo_write_avail(alsa->buffer); + + if (avail == 0) + { + slock_unlock(alsa->fifo_lock); + slock_lock(alsa->cond_lock); + if (!alsa->thread_dead) + scond_wait(alsa->cond, alsa->cond_lock); + slock_unlock(alsa->cond_lock); + } + else + { + size_t write_amt = min(size - written, avail); + fifo_write(alsa->buffer, (const char*)buf + written, write_amt); + slock_unlock(alsa->fifo_lock); + written += write_amt; + } + } + return written; + } +} + +static bool alsa_stop(void *data) +{ + (void)data; + return true; +} + +static void alsa_set_nonblock_state(void *data, bool state) +{ + alsa_t *alsa = (alsa_t*)data; + alsa->nonblock = state; +} + +static bool alsa_start(void *data) +{ + (void)data; + return true; +} + +static size_t alsa_write_avail(void *data) +{ + alsa_t *alsa = (alsa_t*)data; + + if (alsa->thread_dead) + return 0; + slock_lock(alsa->fifo_lock); + size_t val = fifo_write_avail(alsa->buffer); + slock_unlock(alsa->fifo_lock); + return val; +} + +static size_t alsa_buffer_size(void *data) +{ + alsa_t *alsa = (alsa_t*)data; + return alsa->buffer_size; +} + +const audio_driver_t audio_alsathread = { + alsa_init, + alsa_write, + alsa_stop, + alsa_start, + alsa_set_nonblock_state, + alsa_free, + alsa_use_float, + "alsathread", + alsa_write_avail, + alsa_buffer_size, +}; diff --git a/config.def.h b/config.def.h index 342b93f7f2..15b28112c8 100644 --- a/config.def.h +++ b/config.def.h @@ -46,6 +46,7 @@ enum AUDIO_RSOUND, AUDIO_OSS, AUDIO_ALSA, + AUDIO_ALSATHREAD, AUDIO_ROAR, AUDIO_AL, AUDIO_SL, @@ -109,6 +110,8 @@ enum #define AUDIO_DEFAULT_DRIVER AUDIO_XDK360 #elif defined(GEKKO) #define AUDIO_DEFAULT_DRIVER AUDIO_WII +#elif defined(HAVE_ALSA) && defined(HAVE_VIDEOCORE) +#define AUDIO_DEFAULT_DRIVER AUDIO_ALSATHREAD #elif defined(HAVE_ALSA) #define AUDIO_DEFAULT_DRIVER AUDIO_ALSA #elif defined(HAVE_PULSE) diff --git a/driver.c b/driver.c index 16f5d470ad..4153fe7711 100644 --- a/driver.c +++ b/driver.c @@ -33,6 +33,7 @@ static const audio_driver_t *audio_drivers[] = { #ifdef HAVE_ALSA &audio_alsa, + &audio_alsathread, #endif #if defined(HAVE_OSS) || defined(HAVE_OSS_BSD) &audio_oss, diff --git a/driver.h b/driver.h index cff09a9cc1..d73afa989a 100644 --- a/driver.h +++ b/driver.h @@ -322,6 +322,7 @@ extern driver_t driver; extern const audio_driver_t audio_rsound; extern const audio_driver_t audio_oss; extern const audio_driver_t audio_alsa; +extern const audio_driver_t audio_alsathread; extern const audio_driver_t audio_roar; extern const audio_driver_t audio_openal; extern const audio_driver_t audio_opensl; diff --git a/settings.c b/settings.c index f21b92f345..f9456edbc4 100644 --- a/settings.c +++ b/settings.c @@ -43,6 +43,8 @@ const char *config_get_default_audio(void) return "oss"; case AUDIO_ALSA: return "alsa"; + case AUDIO_ALSATHREAD: + return "alsathread"; case AUDIO_ROAR: return "roar"; case AUDIO_COREAUDIO: From aed2aa146cd2bcb19e8e3d3fe14c40df9639a8ae Mon Sep 17 00:00:00 2001 From: ToadKing Date: Fri, 4 Jan 2013 15:02:40 -0500 Subject: [PATCH 2/2] copyrights --- audio/alsathread.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/audio/alsathread.c b/audio/alsathread.c index 647dd9c06b..d87e62d8ca 100644 --- a/audio/alsathread.c +++ b/audio/alsathread.c @@ -1,5 +1,6 @@ /* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * Copyright (C) 2012-2013 - Michael Lelli * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found-