diff --git a/Makefile.common b/Makefile.common index 573f866cc7..cf455191a6 100644 --- a/Makefile.common +++ b/Makefile.common @@ -527,6 +527,10 @@ ifeq ($(HAVE_LIBNX), 1) OBJ += gfx/drivers_font/switch_font.o endif +ifeq ($(HAVE_AUDIOIO), 1) + OBJ += audio/drivers/audioio.o +endif + ifeq ($(HAVE_OSS), 1) OBJ += audio/drivers/oss.o else ifeq ($(HAVE_OSS_BSD), 1) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index cae227e790..4baaf7b753 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -65,6 +65,9 @@ static const audio_driver_t *audio_drivers[] = { #ifdef HAVE_TINYALSA &audio_tinyalsa, #endif +#if defined(HAVE_AUDIOIO) + &audio_audioio, +#endif #if defined(HAVE_OSS) || defined(HAVE_OSS_BSD) &audio_oss, #endif diff --git a/audio/audio_driver.h b/audio/audio_driver.h index c90db9873c..de77f750d3 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -316,6 +316,7 @@ const char *audio_driver_mixer_get_stream_name(unsigned i); bool compute_audio_buffer_statistics(audio_statistics_t *stats); extern audio_driver_t audio_rsound; +extern audio_driver_t audio_audioio; extern audio_driver_t audio_oss; extern audio_driver_t audio_alsa; extern audio_driver_t audio_alsathread; diff --git a/audio/drivers/audioio.c b/audio/drivers/audioio.c new file mode 100644 index 0000000000..7b426c0c1b --- /dev/null +++ b/audio/drivers/audioio.c @@ -0,0 +1,204 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../audio_driver.h" +#include "../../verbosity.h" + +#define DEFAULT_DEV "/dev/audio" + +static void *audioio_init(const char *device, unsigned rate, unsigned latency, + unsigned block_frames, + unsigned *new_out_rate) +{ + int *fd = (int*)calloc(1, sizeof(int)); + const char *audiodev = device ? device : DEFAULT_DEV; + struct audio_info info; + + if (!fd) + return NULL; + + AUDIO_INITINFO(&info); + +#ifdef AUMODE_PLAY_ALL + info.mode = AUMODE_PLAY_ALL; +#endif + info.play.sample_rate = rate; + info.play.channels = 2; + info.play.precision = 16; +#ifdef AUDIO_ENCODING_SLINEAR + info.play.encoding = AUDIO_ENCODING_SLINEAR; +#else + info.play.encoding = AUDIO_ENCODING_LINEAR; +#endif + + if ((*fd = open(audiodev, O_WRONLY)) < 0) + { + free(fd); + perror("open"); + return NULL; + } + + if (ioctl(*fd, AUDIO_SETINFO, &info) < 0) + goto error; + + if (ioctl(*fd, AUDIO_GETINFO, &info) < 0) + goto error; + + *new_out_rate = info.play.sample_rate; + + return fd; +error: + close(*fd); + free(fd); + perror("ioctl"); + return NULL; +} + +static ssize_t audioio_write(void *data, const void *buf, size_t size) +{ + ssize_t ret; + int *fd = (int*)data; + + if (size == 0) + return 0; + + if ((ret = write(*fd, buf, size)) < 0) + { + if (errno == EAGAIN && (fcntl(*fd, F_GETFL) & O_NONBLOCK)) + return 0; + + return -1; + } + + return ret; +} + +static bool audioio_stop(void *data) +{ + struct audio_info info; + int *fd = (int*)data; + + if (ioctl(*fd, AUDIO_FLUSH, NULL) < 0) + return false; + + if (ioctl(*fd, AUDIO_GETINFO, &info) < 0) + return false; + + info.play.pause = true; + + return ioctl(*fd, AUDIO_SETINFO, &info) == 0; +} + +static bool audioio_start(void *data, bool is_shutdown) +{ + struct audio_info info; + int *fd = (int*)data; + + if (ioctl(*fd, AUDIO_FLUSH, NULL) < 0) + return false; + + if (ioctl(*fd, AUDIO_GETINFO, &info) < 0) + return false; + + info.play.pause = false; + + return ioctl(*fd, AUDIO_SETINFO, &info) == 0; +} + +static bool audioio_alive(void *data) +{ + struct audio_info info; + int *fd = (int*)data; + + if (ioctl(*fd, AUDIO_GETINFO, &info) < 0) + return false; + + return !info.play.pause; +} + +static void audioio_set_nonblock_state(void *data, bool state) +{ + int rc; + int *fd = (int*)data; + + if (state) + rc = fcntl(*fd, F_SETFL, fcntl(*fd, F_GETFL) | O_NONBLOCK); + else + rc = fcntl(*fd, F_SETFL, fcntl(*fd, F_GETFL) & (~O_NONBLOCK)); + if (rc != 0) + RARCH_WARN("Could not set nonblocking on audio file descriptor. Will not be able to fast-forward.\n"); +} + +static void audioio_free(void *data) +{ + int *fd = (int*)data; + + (void)ioctl(*fd, AUDIO_FLUSH, NULL); + + close(*fd); + free(fd); +} + +static size_t audioio_buffer_size(void *data) +{ + struct audio_info info; + int *fd = (int*)data; + + if (ioctl(*fd, AUDIO_GETINFO, &info) < 0) + return false; + + return info.play.buffer_size; +} + +static size_t audioio_write_avail(void *data) +{ + return audioio_buffer_size(data); +} + +static bool audioio_use_float(void *data) +{ + (void)data; + return false; +} + +audio_driver_t audio_audioio = { + audioio_init, + audioio_write, + audioio_stop, + audioio_start, + audioio_alive, + audioio_set_nonblock_state, + audioio_free, + audioio_use_float, + "audioio", + NULL, + NULL, + audioio_write_avail, + audioio_buffer_size, +}; diff --git a/qb/config.libs.sh b/qb/config.libs.sh index aa0aa1ba38..209886ef9a 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -236,6 +236,10 @@ check_val '' ALSA -lasound alsa check_lib '' CACA -lcaca check_lib '' SIXEL -lsixel +if [ "$HAVE_AUDIOIO" != 'no' ]; then + check_macro AUDIOIO AUDIO_SETINFO sys/audioio.h +fi + if [ "$HAVE_OSS" != 'no' ]; then check_header OSS sys/soundcard.h check_header OSS_BSD soundcard.h diff --git a/qb/config.params.sh b/qb/config.params.sh index feb4f262c8..bf62a58029 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -81,6 +81,7 @@ HAVE_ZLIB=auto # zlib support (ZIP extract, PNG decoding/encoding) HAVE_ALSA=auto # ALSA support C89_ALSA=no HAVE_TINYALSA=auto # TinyALSA support +HAVE_AUDIOIO=auto # AudioIO support HAVE_OSS=auto # OSS support HAVE_RSOUND=auto # RSound support HAVE_ROAR=auto # RoarAudio support diff --git a/qb/qb.libs.sh b/qb/qb.libs.sh index ccd101be1a..891082eba7 100644 --- a/qb/qb.libs.sh +++ b/qb/qb.libs.sh @@ -137,11 +137,18 @@ check_header() #$1 = HAVE_$1 $2, $3, ... = header files die 1 "Build assumed that $header exists, but cannot locate. Exiting ..." } -check_macro() #$1 = HAVE_$1 $2 = macro name +check_macro() #$1 = HAVE_$1 $2 = macro name $3 = header name [included only if non-empty] { tmpval="$(eval "printf %s \"\$HAVE_$1\"")" [ "$tmpval" = 'no' ] && return 0 - ECHOBUF="Checking presence of predefined macro $2" + if [ $3 ]; then + ECHOBUF="Checking presence of predefined macro $2 in $3" + header_include=$(printf '#include <%s>' "$3") + else + ECHOBUF="Checking presence of predefined macro $2" + header_include="" + fi cat << EOF > "$TEMP_C" +$header_include #ifndef $2 #error $2 is not defined #endif