commit d1705aabaf2f68036142492a64d2c1ad3ec75b95 from: Omar Polo date: Sat Oct 07 19:47:10 2023 UTC add an libao audio backend This backend uses libao to play music. It's a bit convoluted since libao doesn't provide an async interface, so we have to run it in a separate thread. Then, there's some notification via a shared socketpair because the main loop is around poll(). This actually doesn't work OOTB on OpenBSD due to a restrictive pledge(), but it's not a issue since sndio should be used there. libao is the last resort. 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 + * + * 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 + +#include +#include +#include +#include +#include + +#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 +#include + +int +main(void) +{ + pthread_mutex_t mutex; + + return (pthread_mutex_init(&mutex, NULL)); +} +#endif/* TEST_LIB_PTHREAD */ #if TEST_LIB_OPUSFILE #include #include @@ -928,6 +940,18 @@ main(void) return 0; } #endif /* TEST_LIB_VORBISFILE */ +#if TEST_LIB_AO +#include +#include +#include + +int +main(void) +{ + ao_initialize(); + return (0); +} +#endif /* TEST_LIB_AO */ #if TEST_LIB_ASOUND #include