ref: 1f5a91b06083d47de0a85f432e1c4213c0a12095
parent: 671477fdba0362f603b387bc121d6fdedf67a7af
author: Jacob Moody <moody@posixcafe.org>
date: Tue Oct 12 20:58:21 EDT 2021
add pipewire playback support
--- a/Make.linux
+++ b/Make.linux
@@ -5,15 +5,15 @@
AS=as
RANLIB=ranlib
CC=gcc
-CFLAGS=-Wall -Wno-missing-braces -ggdb -I$(ROOT) -I$(ROOT)/include -I$(ROOT)/kern -c -D_THREAD_SAFE -DPTHREAD $(PTHREAD) -O2
+CFLAGS=-Wall -Wno-missing-braces -ggdb -I$(ROOT) -I$(ROOT)/include -I$(ROOT)/kern -c -D_THREAD_SAFE -DPTHREAD $(PTHREAD) -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -O2
O=o
OS=posix
GUI=wl
-LDADD=-lwayland-client -lxkbcommon -ggdb -lm -lrt
+LDADD=-lwayland-client -lxkbcommon -ggdb -lm -lrt -lpipewire-0.3
LDFLAGS=$(PTHREAD)
TARG=drawterm
# AUDIO=none
-AUDIO=unix
+AUDIO=pipewire
all: default
--- /dev/null
+++ b/kern/devaudio-pipewire.c
@@ -1,0 +1,181 @@
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "devaudio.h"
+
+#undef long
+#include <pipewire/pipewire.h>
+#include <spa/param/audio/format-utils.h>
+
+static struct {
+ Lock lk;
+ Rendez z;
+ int init;
+ struct pw_main_loop *loop;
+ struct pw_stream *output;
+
+ char buf[8192];
+ int written; /* 0 means empty buffer */
+} pwstate;
+
+static char *argv[] = { "drawterm" };
+static int argc = 1;
+
+static void
+on_process(void *data)
+{
+ struct pw_buffer *b;
+ struct spa_buffer *buf;
+ int16_t *dst;
+
+ lock(&pwstate.lk);
+ if(pwstate.written == 0){
+ unlock(&pwstate.lk);
+ return;
+ }
+ b = pw_stream_dequeue_buffer(pwstate.output);
+ if(b == NULL)
+ sysfatal("pipewire ran out of buffers");
+
+ buf = b->buffer;
+ dst = buf->datas[0].data;
+ if(dst == NULL)
+ sysfatal("can't write anywhere");
+ if(buf->datas[0].maxsize < pwstate.written)
+ sysfatal("we buffered too much, or pipewire has comically low output rate");
+
+ memcpy(dst, pwstate.buf, pwstate.written);
+ buf->datas[0].chunk->offset = 0;
+ buf->datas[0].chunk->stride = sizeof(int16_t) * 2;
+ buf->datas[0].chunk->size = pwstate.written;
+
+ pw_stream_queue_buffer(pwstate.output, b);
+ pwstate.written = 0;
+ unlock(&pwstate.lk);
+ wakeup(&pwstate.z);
+}
+
+static const struct pw_stream_events stream_events = {
+ PW_VERSION_STREAM_EVENTS,
+ .process = on_process,
+};
+
+static void
+pwproc(void *arg)
+{
+ struct pw_main_loop *loop;
+
+ loop = arg;
+ fprintf(stderr, "running main pipewire loop\n");
+ pw_main_loop_run(loop);
+}
+
+void
+audiodevopen(void)
+{
+ struct spa_pod *params[1];
+ struct spa_pod_builder b = SPA_POD_BUILDER_INIT(pwstate.buf, sizeof(pwstate.buf));
+ int err;
+
+ fprintf(stderr, "opening...\n");
+ lock(&pwstate.lk);
+ if(pwstate.init > 0){
+ kproc("pipewire main loop", pwproc, pwstate.loop);
+ unlock(&pwstate.lk);
+ return;
+ }
+
+ pwstate.init++;
+ fprintf(stderr, "initint...\n");
+ pw_init(&argc, &argv);
+ pwstate.loop = pw_main_loop_new(NULL);
+ if(pwstate.loop == NULL)
+ sysfatal("could not create loop");
+ pwstate.output = pw_stream_new_simple(
+ pw_main_loop_get_loop(pwstate.loop),
+ "drawterm",
+ pw_properties_new(
+ PW_KEY_MEDIA_TYPE, "Audio",
+ PW_KEY_MEDIA_CATEGORY, "Playback",
+ PW_KEY_MEDIA_ROLE, "Music",
+ NULL),
+ &stream_events,
+ NULL);
+
+ if(pwstate.output == NULL)
+ sysfatal("could not create output");
+ params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
+ &SPA_AUDIO_INFO_RAW_INIT(
+ .format = SPA_AUDIO_FORMAT_S16_LE,
+ .channels = 2,
+ .rate = 44100 ));
+
+ err = pw_stream_connect(pwstate.output,
+ PW_DIRECTION_OUTPUT,
+ PW_ID_ANY,
+ PW_STREAM_FLAG_AUTOCONNECT |
+ PW_STREAM_FLAG_MAP_BUFFERS |
+ PW_STREAM_FLAG_RT_PROCESS,
+ params, 1);
+
+ if(err < 0)
+ sysfatal("failed to connect");
+
+ unlock(&pwstate.lk);
+ kproc("pipewire main loop", pwproc, pwstate.loop);
+}
+
+void
+audiodevclose(void)
+{
+ lock(&pwstate.lk);
+ pw_main_loop_quit(pwstate.loop);
+ unlock(&pwstate.lk);
+}
+
+int
+audiodevread(void *a, int n)
+{
+ error("no record support");
+ return -1;
+}
+
+static int
+canwrite(void *arg)
+{
+ return pwstate.written == 0;
+}
+
+int
+audiodevwrite(void *a, int n)
+{
+ if(n > sizeof(pwstate.buf)){
+ sysfatal("write too large of size:", n);
+ n = sizeof(pwstate.buf);
+ }
+ lock(&pwstate.lk);
+ if(pwstate.written != 0){
+ unlock(&pwstate.lk);
+ sleep(&pwstate.z, canwrite, 0);
+ lock(&pwstate.lk);
+ }
+ memcpy(pwstate.buf, a, n);
+ pwstate.written = n;
+ unlock(&pwstate.lk);
+ return n;
+}
+
+void
+audiodevsetvol(int what, int left, int right)
+{
+ error("no audio support");
+}
+
+void
+audiodevgetvol(int what, int *left, int *right)
+{
+ error("no audio support");
+}
+