shithub: drawterm

ref: dd78daef41e76d4ec915afc6b468800510c077d9
dir: /kern/devaudio-pipewire.c/

View raw version
#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");
}