Commit Diff


commit - 4e457e622a3cc784da56111553ff67082014d33b
commit + c2297fa38e218ad8f91175bbb242fb719b1a8435
blob - ad1273c0a22318d0fc4e2061e5313da3d9857ee7
blob + b17bc268d86b86f7a2c753aa6d2766d68141635b
--- Makefile
+++ Makefile
@@ -42,6 +42,7 @@ DISTFILES =	CHANGES \
 		${SOURCES} \
 		audio_alsa.c \
 		audio_ao.c \
+		audio_oboe.cpp \
 		audio_sndio.c
 
 all: ${PROG}
@@ -111,6 +112,7 @@ ${DISTNAME}.tar.gz: ${DISTFILES}
 -include amused.d
 -include audio_alsa.d
 -include audio_ao.d
+-include audio_oboe.d
 -include audio_sndio.d
 -include compats.d
 -include control.d
blob - /dev/null
blob + 3fb5b36cce11850d3ae4b0ecbb84f150c46de9fa (mode 644)
--- /dev/null
+++ audio_oboe.cpp
@@ -0,0 +1,239 @@
+/*
+ * 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.
+ */
+
+extern "C" {
+#include "config.h"
+}
+
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+
+#include <oboe/Oboe.h>
+
+extern "C" {
+#include "amused.h"
+#include "log.h"
+}
+
+#define ext extern "C"
+
+static void		(*onmove_cb)(void *, int);
+static int		 sp[2]; /* main, audio thread */
+static pthread_t	 at;
+
+static int		 bpf;
+static unsigned int	 bits, rate, chan;
+static oboe::AudioFormat fmt;
+static char		 buf[BUFSIZ];
+static size_t		 buflen;
+
+static std::shared_ptr<oboe::AudioStream> stream;
+
+static void *
+aworker(void *d)
+{
+	unsigned int	 last_bits, last_rate, last_chan;
+	ssize_t		 r;
+	int		 sock = sp[1];
+	char		 ch;
+
+	stream = nullptr;
+	last_bits = last_rate = last_chan = 0;
+
+	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 (bits != last_bits ||
+		    rate != last_rate ||
+		    chan != last_chan) {
+			if (stream) {
+				stream->close();
+				stream = nullptr;
+			}
+
+			last_bits = bits;
+			last_rate = rate;
+			last_chan = chan;
+
+			log_debug("setting bits=%d rate=%d chan=%d bpf=%d",
+			    bits, rate, chan, bpf);
+
+			oboe::AudioStreamBuilder streamBuilder;
+			streamBuilder.setFormat(fmt);
+			streamBuilder.setSampleRate(rate);
+			streamBuilder.setChannelCount(chan);
+			oboe::Result result = streamBuilder.openStream(stream);
+			if (result != oboe::Result::OK)
+				fatalx("Error opening stream %s",
+				    oboe::convertToText(result));
+
+			stream->requestStart();
+		}
+
+		// oboe works in terms of FRAMES not BYTES!
+		unsigned int len = buflen / bpf;
+
+		// XXX should be the timeout in nanoseconds...
+		auto ret = stream->write(buf, len, 1000000000);
+		if (!ret) {
+			fatalx("write failed: %s",
+			    oboe::convertToText(ret.error()));
+		}
+	}
+
+	return nullptr;
+}
+
+ext int
+audio_open(void (*cb)(void *, int))
+{
+	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);
+}
+
+ext int
+audio_setup(unsigned int p_bits, unsigned int p_rate, unsigned int p_chan,
+    struct pollfd *pfds, int nfds)
+{
+	bits = p_bits;
+	rate = p_rate;
+	chan = p_chan;
+
+	if (bits == 8) {
+		log_warnx("would require a conversion layer...");
+		return (-1);
+	} else if (bits == 16) {
+		bpf = 2;
+		fmt = oboe::AudioFormat::I16;
+	} else if (bits == 24) {
+		bpf = 4;
+		fmt = oboe::AudioFormat::I24;
+	} else if (bits == 32) {
+		// XXX not so sure...
+		bpf = 4;
+		fmt = oboe::AudioFormat::I24;
+	} else {
+		log_warnx("can't handle %d bits", bits);
+		return (-1);
+	}
+
+	bpf *= chan;
+
+	return (0);
+}
+
+ext int
+audio_nfds(void)
+{
+	return 1;
+}
+
+ext 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);
+}
+
+ext int
+audio_revents(struct pollfd *pfds, int nfds)
+{
+	if (nfds != 1) {
+		log_warnx("%s: called with %d nfds", __func__, nfds);
+		return 0;
+	}
+
+	/* don't need to check; if we're here the audio thread is ready */
+	return POLLOUT;
+}
+
+ext 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("oboe/%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("oboe/%s: write failed", __func__);
+		return 0;
+	}
+	if (r == 0) {
+		log_warnx("oboe/%s: write got EOF", __func__);
+		return 0;
+	}
+
+	if (onmove_cb)
+		onmove_cb(NULL, len / bpf);
+
+	return len;
+}
+
+ext int
+audio_flush(void)
+{
+	return 0; // XXX request flush
+}
+
+ext int
+audio_stop(void)
+{
+	return 0; // XXX request stop
+}
blob - 26f15a0acdb7fca63a4345efbd1163deb204f9ba
blob + 83d656a5ddf332bb0cb27a4bf8fc2b8b9d7a0407
--- compats.c
+++ compats.c
@@ -245,7 +245,8 @@ explicit_bzero(void *p, size_t n)
  * Indirect memset through a volatile pointer to hopefully avoid
  * dead-store optimisation eliminating the call.
  */
-static void (* volatile ssh_memset)(void *, int, size_t) = memset;
+static void (* volatile ssh_memset)(void *, int, size_t) =
+    (void (*volatile)(void *, int, size_t))memset;
 
 void
 explicit_bzero(void *p, size_t n)
blob - fc890e034e60584ffbbdb798b8235be8ad3af3dc
blob + 497e87b133dfeb21e3bc7097dc37fa81fed4cd58
--- configure
+++ configure
@@ -137,10 +137,13 @@ fi
 
 BACKEND=auto
 CC=`printf "all:\\n\\t@echo \\\$(CC)\\n" | make ${MAKE_FLAGS} -sf -`
+CXX=`printf "all:\\n\\t@echo \\\$(CXX)\\n" | make ${MAKE_FLAGS} -sf -`
 CFLAGS=`printf "all:\\n\\t@echo \\\$(CFLAGS)\\n" | make ${MAKE_FLAGS} -sf -`
 CFLAGS="${CFLAGS} -g -W -Wall -Wextra -Wmissing-prototypes"
 CFLAGS="${CFLAGS} -Wstrict-prototypes -Wmissing-declarations"
 CFLAGS="${CFLAGS} -Wno-unused-parameter -Wno-sign-compare -Wno-pointer-sign"
+CXXFLAGS=`printf "all:\\n\\t@echo \\\$(CXXFLAGS)\\n" | make ${MAKE_FLAGS} -sf -`
+CXXFLAGS="${CXXFLAGS} -g -W -Wall -Wextra -Wno-unused-parameter"
 LDADD=
 LDADD_LIB_AO=
 LDADD_LIB_ASOUND=
@@ -298,6 +301,7 @@ while [ $# -gt 0 ]; do
 		case "$val" in
 		alsa)	BACKEND=alsa ;;
 		ao)	BACKEND=ao ;;
+		oboe)	BACKEND=oboe ;;
 		sndio)	BACKEND=sndio ;;
 		*)
 			echo "unknown audio backend: $val" 1>&2
@@ -307,6 +311,8 @@ while [ $# -gt 0 ]; do
 		CC="$val" ;;
 	CFLAGS)
 		CFLAGS="$val" ;;
+	CXXFLAGS)
+		CXXFLAGS="$val" ;;
 	LDADD)
 		LDADD="$val" ;;
 	LDADD_LIB_AO)
@@ -535,8 +541,9 @@ runtest() {
 
 if runtest -MMD _MMD -MMD; then
 	CFLAGS="${CFLAGS} -MMD"
-	echo "adding -MMD to CFLAGS" 1>&2
-	echo "adding -MMD to CFLAGS" 1>&3
+	CXXFLAGS="${CXXFLAGS} -MMD"
+	echo "adding -MMD to CFLAGS and CXXFLAGS" 1>&2
+	echo "adding -MMD to CFLAGS and CXXFLAGS" 1>&3
 fi
 
 runtest capsicum	CAPSICUM			  || true
@@ -639,6 +646,18 @@ if [ $BACKEND = auto -o $BACKEND = ao ]; then
 	CFLAGS="${CFLAGS} -pthread"
 fi
 
+if [ $BACKEND = auto -o $BACKEND = oboe ]; then
+	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"
+
+	LDADD="${LDADD} -lstdc++ -lm -llog -lOpenSLES"
+fi
+
 if [ "${HAVE_ENDIAN_H}" -eq 0 ]; then
 	CFLAGS="${CFLAGS} -I."
 fi
@@ -668,14 +687,19 @@ cat << __HEREDOC__
 #ifndef OCONFIGURE_CONFIG_H
 #define OCONFIGURE_CONFIG_H
 
+/* the oboe (android) backend uses c++ */
+#ifndef __ANDROID__
 #ifdef __cplusplus
 # error "Do not use C++: this is a C application."
 #endif
+#endif /* __ANDROID__ */
 #if !defined(__GNUC__) || (__GNUC__ < 4)
 # define __attribute__(x)
 #endif
 #if defined(__linux__) || defined(__MINT__)
-# define _GNU_SOURCE /* memmem, memrchr, setresuid... */
+# ifndef __ANDROID__
+#  define _GNU_SOURCE /* memmem, memrchr, setresuid... */
+# endif
 # define _DEFAULT_SOURCE /* le32toh, crypt, ... */
 #endif
 #if defined(__NetBSD__)
@@ -1032,6 +1056,7 @@ cat << __HEREDOC__
 BACKEND		 = ${BACKEND}
 CC		 = ${CC}
 CFLAGS		 = ${CFLAGS}
+CXXFLAGS	 = ${CXXFLAGS}
 CPPFLAGS	 = ${CPPFLAGS}
 LDADD		 = ${LDADD}
 LDADD_LIB_IMSG	 = ${LDADD_LIB_IMSG}