Commit Diff


commit - 933d15ce38dbe4081bdc11e05ea094577b3dca72
commit + d1705aabaf2f68036142492a64d2c1ad3ec75b95
blob - 1b2ec6f49b1c89af789fb934acc5f1135d325e6a
blob + a994af5c7645accbb068605407520b0175a008ad
--- Makefile
+++ Makefile
@@ -40,6 +40,7 @@ DISTFILES =	CHANGES \
 		${HEADERS} \
 		${SOURCES} \
 		audio_alsa.c \
+		audio_ao.c \
 		audio_sndio.c
 
 all: ${PROG}
@@ -108,6 +109,7 @@ ${DISTNAME}.tar.gz: ${DISTFILES}
 
 -include amused.d
 -include audio_alsa.d
+-include audio_ao.d
 -include audio_sndio.d
 -include compats.d
 -include control.d
blob - /dev/null
blob + 3c2ef4735c191ec4e6363ed6172a8e04bd9671f9 (mode 644)
--- /dev/null
+++ audio_ao.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <sys/socket.h>
+
+#include <ao/ao.h>
+#include <limits.h>
+#include <poll.h>
+#include <pthread.h>
+#include <string.h>
+
+#include "amused.h"
+#include "log.h"
+
+static void		(*onmove_cb)(void *, int);
+static int		 sp[2]; /* main, audio thread */
+static pthread_t	 at;
+
+static int		 bpf;
+static ao_sample_format	 fmt;
+static char		 buf[BUFSIZ];
+static size_t		 buflen;
+
+static void *
+aworker(void *d)
+{
+	ao_sample_format f;
+	ao_device	*device = NULL;
+	ssize_t		 r;
+	int		 sock = sp[1];
+	char		 ch;
+
+	memset(&f, 0, sizeof(f));
+
+	log_info("%s: starting", __func__);
+
+	for (;;) {
+		ch = 1;
+		if ((r = write(sock, &ch, 1)) == -1)
+			fatal("write");
+		if (r == 0)
+			break;
+
+		if ((r = read(sock, &ch, 1)) == -1)
+			fatal("read");
+		if (r == 0)
+			break;
+
+		if (memcmp(&fmt, &f, sizeof(f)) != 0) {
+			if (device != NULL)
+				ao_close(device);
+			device = ao_open_live(ao_default_driver_id(),
+			    &fmt, NULL);
+			if (device == NULL) {
+				switch (errno) {
+				case AO_ENODRIVER:
+					log_warnx("ao: no driver found");
+					break;
+				case AO_ENOTLIVE:
+					log_warnx("ao: not a live device");
+					break;
+				case AO_EBADOPTION:
+					log_warnx("ao: bad option(s)");
+					break;
+				case AO_EOPENDEVICE:
+					log_warnx("ao: failed to open device");
+					break;
+				case AO_EFAIL:
+				default:
+					log_warnx("ao: failed opening driver");
+					break;
+				}
+				errno = EINVAL;
+				break;
+			}
+			log_info("%s: device (re)opened", __func__);
+			memcpy(&f, &fmt, sizeof(f));
+		}
+
+		if (ao_play(device, buf, buflen) == 0) {
+			log_warnx("ao_play failed");
+			break;
+		}
+	}
+
+	log_info("quitting audio thread");
+	close(sock);
+	return NULL;
+}
+
+int
+audio_open(void (*cb)(void *, int))
+{
+	ao_initialize();
+	onmove_cb = cb;
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == -1) {
+		log_warn("socketpair");
+		return (-1);
+	}
+
+	if (pthread_create(&at, NULL, aworker, NULL) == -1) {
+		log_warn("pthread_create");
+		return (-1);
+	}
+
+	return 0;
+}
+
+int
+audio_setup(unsigned int bits, unsigned int rate, unsigned int channels,
+    struct pollfd *pfds, int nfds)
+{
+	fmt.bits = bits;
+	fmt.rate = rate;
+	fmt.channels = channels;
+	fmt.byte_format = AO_FMT_NATIVE;
+	fmt.matrix = NULL;
+
+	if (bits == 8)
+		bpf = 1;
+	else if (bits == 16)
+		bpf = 2;
+	else if (bits == 24 || bits == 32)
+		bpf = 4;
+	else {
+		log_warnx("can't handle %d bits", bits);
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+audio_nfds(void)
+{
+	return 1;
+}
+
+int
+audio_pollfd(struct pollfd *pfds, int nfds, int events)
+{
+	if (nfds != 1) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	pfds[0].fd = sp[0];
+	pfds[0].events = POLLIN;
+	return 0;
+}
+
+int
+audio_revents(struct pollfd *pfds, int nfds)
+{
+	if (nfds != 1) {
+		log_warnx("%s: called with nfds=%d", __func__, nfds);
+		return 0;
+	}
+
+	/* don't need to check; if we're here the audio thread is ready */
+	return POLLOUT;
+}
+
+size_t
+audio_write(const void *data, size_t len)
+{
+	char	 ch;
+	ssize_t	 r;
+
+	if ((r = read(sp[0], &ch, 1)) == -1) {
+		log_warn("ao/%s: read failed", __func__);
+		return 0;
+	}
+	if (r == 0)
+		return 0;
+
+	if (len > sizeof(buf))
+		len = sizeof(buf);
+
+	memcpy(buf, data, len);
+	buflen = len;
+
+	ch = 1;
+	if ((r = write(sp[0], &ch, 1)) == -1) {
+		log_warn("ao/%s: write failed", __func__);
+		return 0;
+	}
+	if (r == 0) {
+		log_warnx("ao/%s: write got EOF", __func__);
+		return 0;
+	}
+
+	if (onmove_cb)
+		onmove_cb(NULL, len / bpf);
+
+	return len;
+}
+
+int
+audio_flush(void)
+{
+	return 0;
+}
+
+int
+audio_stop(void)
+{
+	return 0;
+}
blob - c9ff87fb3bdcf35acdb6b527cd7cf3940b0542fd
blob + 478ac4b58ff524c05d96c620edcf889183dd58e7
--- configure
+++ configure
@@ -60,16 +60,18 @@ The options are as follows:
 
 Variables available:
 
-    BACKEND                audio backend to use; can be "sndio" or "alsa"
+    BACKEND                audio backend to use; can be "sndio", "ao" or "alsa"
     CC                     C compiler
     CFLAGS                 generic C compiler flags
     CPPFLAGS               C preprocessors flags
     LDADD                  generic linker flags
+    LDADD_LIB_AO           linker flags for libao
     LDADD_LIB_ASOUND       linker flags for libasound
     LDADD_LIB_FLAC         linker flags for libflac
     LDADD_LIB_IMSG         linker flags for libimsg
     LDADD_LIB_MD           linker flags for libmd
     LDADD_LIB_MPG123       linker flags for libmpg123
+    LDADD_LIB_PTHREAD      linker flags for pthread
     LDADD_LIB_OPUSFILE     linker flags for libopusfile
     LDADD_LIB_SNDIO        linker flags for libsndio
     LDADD_LIB_SOCKET       linker flags for libsocket
@@ -140,11 +142,13 @@ CFLAGS="${CFLAGS} -g -W -Wall -Wextra -Wmissing-protot
 CFLAGS="${CFLAGS} -Wstrict-prototypes -Wmissing-declarations"
 CFLAGS="${CFLAGS} -Wno-unused-parameter -Wno-sign-compare -Wno-pointer-sign"
 LDADD=
+LDADD_LIB_AO=
 LDADD_LIB_ASOUND=
 LDADD_LIB_FLAC=
 LDADD_LIB_IMSG=
 LDADD_LIB_MD=
 LDADD_LIB_MPG123=
+LDADD_LIB_PTHREAD=
 LDADD_LIB_OPUSFILE=
 LDADD_LIB_SNDIO=
 LDADD_LIB_SOCKET=
@@ -290,6 +294,7 @@ while [ $# -gt 0 ]; do
 	BACKEND)
 		case "$val" in
 		alsa)	BACKEND=alsa ;;
+		ao)	BACKEND=ao ;;
 		sndio)	BACKEND=sndio ;;
 		*)
 			echo "unknown audio backend: $val" 1>&2
@@ -301,6 +306,11 @@ while [ $# -gt 0 ]; do
 		CFLAGS="$val" ;;
 	LDADD)
 		LDADD="$val" ;;
+	LDADD_LIB_AO)
+		LDADD_LIB_AO="$val"
+		HAVE_LIB_AO=1
+		BACKEND=ao
+		;;
 	LDADD_LIB_ASOUND)
 		LDADD_LIB_ASOUND="$val"
 		HAVE_LIB_ASOUND=1
@@ -322,6 +332,10 @@ while [ $# -gt 0 ]; do
 		LDADD_LIB_MPG123="$val"
 		HAVE_LIB_MPG123=1
 		;;
+	LDADD_LIB_PTHREAD)
+		LDADD_LIB_PTHREAD="$val"
+		HAVE_LIB_PTHREAD=1
+		;;
 	LDADD_LIB_OPUSFILE)
 		LDADD_LIB_OPUSFILE="$val"
 		HAVE_LIB_OPUSFILE=1
@@ -567,7 +581,7 @@ runtest timespecsub	TIMESPECSUB			  || true
 runtest unveil		UNVEIL				  || true
 runtest __progname	__PROGNAME			  || true
 
-if [ $BACKEND != alsa ]; then # auto or sndio
+if [ $BACKEND = auto -o $BACKEND = sndio ]; then
 	runtest lib_sndio LIB_SNDIO "" "" "-lsndio" "sndio" || true
 	runtest sio_flush SIO_FLUSH "" "" "${LDADD_LIB_SNDIO}" || true
 
@@ -584,21 +598,41 @@ else
 	HAVE_SIO_FLUSH=0
 fi
 
-if [ $BACKEND != sndio ]; then # auto or alsa
+if [ $BACKEND = auto -o $BACKEND = alsa ]; then
 	runtest lib_asound LIB_ASOUND "" "" "-lasound" "alsa" || true
 	if [ "${HAVE_LIB_ASOUND}" -eq 0 ]; then
 		if [ $BACKEND = alsa ]; then
 			echo "Fatal: missing libasound" 1>&2
 			echo "Fatal: missing libasound" 1>&3
-		else
-			echo "Fatal: missing libasound or libsndio" 1>&2
-			echo "Fatal: missing libasound or libsndio" 1>&3
+			exit 1
 		fi
-		exit 1
 	fi
 	BACKEND=alsa
 fi
 
+if [ $BACKEND = auto -o $BACKEND = ao ]; then
+	runtest lib_ao LIB_AO "" "" "-lao" "ao" || true
+	if [ "${HAVE_LIB_AO}" -eq 0 ]; then
+		if [ $BACKEND = ao ]; then
+			echo "Fatal: missing libao" 1>&2
+			echo "Fatal: missing libao" 1>&3
+		else
+			echo "Fatal: missing libasound, libao or libsndio" 1>&2
+			echo "Fatal: missing libasound, libao or libsndio" 1>&3
+		fi
+		exit 1
+	fi
+	BACKEND=ao
+
+	runtest pthread LIB_PTHREAD "" "-pthread" || true
+	if [ "${HAVE_LIB_PTHREAD}" -eq 0 ]; then
+		echo "Fatal: missing pthread" 1>&2
+		echo "Fatal: missing pthread" 1>&3
+		exit 1
+	fi
+	CFLAGS="${CFLAGS} -pthread"
+fi
+
 if [ "${HAVE_LIB_FLAC}" -eq 0 -o \
      "${HAVE_LIB_MPG123}" -eq 0 -o \
      "${HAVE_LIB_OPUSFILE}" -eq 0 -o \
@@ -988,7 +1022,8 @@ LDADD_DECODERS	 = ${LDADD_LIB_FLAC} ${LDADD_LIB_MPG123
 			${LDADD_LIB_VORBISFILE}
 LDADD_LIB_MD	 = ${LDADD_LIB_MD}
 LDADD_LIB_SOCKET = ${LDADD_LIB_SOCKET}
-LDADD_BACKEND	 = ${LDADD_LIB_SNDIO} ${LDADD_LIB_ASOUND}
+LDADD_BACKEND	 = ${LDADD_LIB_SNDIO} ${LDADD_LIB_ASOUND} ${LDADD_LIB_AO} \
+			${LDADD_LIB_PTHREAD}
 LDADD_STATIC	 = ${LDADD_STATIC}
 LDFLAGS		 = ${LDFLAGS}
 STATIC		 = ${STATIC}
blob - 54ade2b800a80f14edff8c860db21d17cbe84228
blob + 59b7b979f157153849d4d0dcd1db44b73979514d
--- tests.c
+++ tests.c
@@ -896,6 +896,18 @@ main(void)
 	return 0;
 }
 #endif/* TEST_LIB_MPG123 */
+#if TEST_LIB_PTHREAD
+#include <stdlib.h>
+#include <pthread.h>
+
+int
+main(void)
+{
+	pthread_mutex_t mutex;
+
+	return (pthread_mutex_init(&mutex, NULL));
+}
+#endif/* TEST_LIB_PTHREAD */
 #if TEST_LIB_OPUSFILE
 #include <stdio.h>
 #include <opusfile.h>
@@ -928,6 +940,18 @@ main(void)
 	return 0;
 }
 #endif /* TEST_LIB_VORBISFILE */
+#if TEST_LIB_AO
+#include <stdio.h>
+#include <string.h>
+#include <ao/ao.h>
+
+int
+main(void)
+{
+	ao_initialize();
+	return (0);
+}
+#endif /* TEST_LIB_AO */
 #if TEST_LIB_ASOUND
 #include <alsa/asoundlib.h>