shithub: riscv

Download patch

ref: b9bec46b989577209c88a8dc8ee8bb171f8c96e4
parent: cad92eedd9d2eca65216046e3ad735fd18c933e7
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Thu Dec 26 16:04:25 EST 2013

add audio/mixfs to allow simultanious playback streams over a single /dev/audio

--- a/sys/man/1/audio
+++ b/sys/man/1/audio
@@ -53,6 +53,17 @@
 .I length
 ]
 .PP
+.B audio/mixfs
+[
+.B -D
+] [
+.B -s
+.I srvname
+] [
+.B -m
+.I mtpt
+]
+.PP
 .SH DESCRIPTION
 These programs decode and encode various audio formats from and to
 16-bit stereo PCM (little endian). The decoders read the compressed
@@ -260,6 +271,38 @@
 was given, a number of
 .I length
 bytes have been consumed from input.
+.PP
+.I Mixfs
+is a fileserver serving a single
+.B audio
+file which allows simultanious playback of audio streams. When
+run, it binds over
+.B /dev/audio
+and mixes the audio samples that get written to it.
+A service name
+.I srvname
+can be given with the
+.B -s
+option which gets posted to
+.BR /srv .
+By default,
+.I mixfs
+mounts itself on
+.B /mnt/mix
+and then binds
+.B /mnt/mix/audio
+over
+.BR /dev .
+A alternative mountpoint 
+.I mtpt
+can be specified with the
+.B -m
+option.
+The
+.B -D
+option causes
+.B 9p
+debug messages to be written to file-descriptor 2.
 .SH EXAMPLE
 Play back an
 .L .mp3
--- /dev/null
+++ b/sys/src/cmd/audio/mixfs/mixfs.c
@@ -1,0 +1,248 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+enum {
+	NBUF = 8*1024,
+	NDELAY = 2048,
+	NCHAN = 2,
+};
+
+typedef struct Stream Stream;
+struct Stream
+{
+	int	used;
+	int	run;
+	ulong	wp;
+	QLock;
+	Rendez;
+};
+
+ulong	mixrp;
+int	mixbuf[NBUF][NCHAN];
+Stream	streams[16];
+
+int
+s16(uchar *p)
+{
+	int v;
+
+	v = p[0]<<(sizeof(int)-2)*8 | p[1]<<(sizeof(int)-1)*8;
+	v >>= (sizeof(int)-2)*8;
+	return v;
+}
+
+int
+clip16(int v)
+{
+	if(v > 0x7fff)
+		return 0x7fff;
+	if(v < -0x8000)
+		return -0x8000;
+	return v;
+}
+
+void
+fsopen(Req *r)
+{
+	Stream *s;
+
+	if(strcmp(r->fid->file->name, "audio") != 0){
+		respond(r, nil);
+		return;
+	}
+	for(s = streams; s < streams+nelem(streams); s++){
+		qlock(s);
+		if(s->used == 0 && s->run == 0){
+			s->used = 1;
+			qunlock(s);
+
+			r->fid->aux = s;
+			respond(r, nil);
+			return;
+		}
+		qunlock(s);
+	}
+	respond(r, "all streams in use");
+}
+
+void
+fsclunk(Fid *f)
+{
+	Stream *s;
+
+	if(f->file != nil && strcmp(f->file->name, "audio") == 0 && (s = f->aux) != nil){
+		f->aux = nil;
+		s->used = 0;
+	}
+}
+
+void
+audioproc(void *)
+{
+	static uchar buf[NBUF*NCHAN*2];
+	int nsleep, fd, i, j, n, m, v;
+	Stream *s;
+	uchar *p;
+
+	threadsetname("audioproc");
+
+	fd = -1;
+	nsleep = 0;
+	for(;;){
+		m = NBUF;
+		for(s = streams; s < streams+nelem(streams); s++){
+			qlock(s);
+			if(s->run){
+				n = (long)(s->wp - mixrp);
+				if(n <= 0 && nsleep > 4)
+					s->run = 0;
+				else if(n < m)
+					m = n;
+				if(n < NDELAY)
+					rwakeup(s);
+			}
+			qunlock(s);
+		}
+		m %= NBUF;
+
+		if(m == 0){
+			sleep(1<<nsleep);
+			if(nsleep < 7)
+				nsleep++;
+			else {
+				close(fd);
+				fd = -1;
+			}
+			continue;
+		}
+		if(fd < 0)
+			if((fd = open("/dev/audio", OWRITE)) < 0)
+				continue;
+
+		nsleep = 0;
+
+		p = buf;
+		for(i=0; i<m; i++){
+			for(j=0; j<NCHAN; j++){
+				v = clip16(mixbuf[mixrp % NBUF][j]);
+				mixbuf[mixrp % NBUF][j] = 0;
+				*p++ = v & 0xFF;
+				*p++ = v >> 8;
+			}
+			mixrp++;
+		}
+		write(fd, buf, p - buf);
+	}
+}
+
+void
+fswrite(Req *r)
+{
+	Srv *srv;
+	int i, j, n, m;
+	Stream *s;
+	uchar *p;
+
+	p = (uchar*)r->ifcall.data;
+	n = r->ifcall.count;
+	m = n/(NCHAN*2);
+
+	srv = r->srv;
+	srvrelease(srv);
+	s = r->fid->aux;
+	qlock(s);
+	if(s->run == 0){
+		s->wp = mixrp;
+		s->run = 1;
+	}
+	for(i=0; i<m; i++){
+		while((long)(s->wp - mixrp) >= NBUF-1){
+			s->run = 1;
+			rsleep(s);
+		}
+		for(j=0; j<NCHAN; j++){
+			mixbuf[s->wp % NBUF][j] += s16(p);
+			p += 2;
+		}
+		s->wp++;
+	}
+	if((long)(s->wp - mixrp) >= NDELAY){
+		s->run = 1;
+		rsleep(s);
+	}
+	qunlock(s);
+	r->ofcall.count = n;
+	respond(r, nil);
+	srvacquire(srv);
+}
+
+void
+fsstart(Srv *)
+{
+	Stream *s;
+
+	for(s=streams; s < streams+nelem(streams); s++){
+		s->used = s->run = 0;
+		s->Rendez.l = &s->QLock;
+	}
+	proccreate(audioproc, nil, 16*1024);
+}
+
+void
+fsend(Srv *)
+{
+	threadexitsall(nil);
+}
+
+Srv fs = {
+	.open=		fsopen,
+	.write=		fswrite,
+	.destroyfid=	fsclunk,
+	.start=		fsstart,
+	.end=		fsend,
+};
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-D] [-s srvname] [-m mtpt]\n", argv0);
+	exits("usage");
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	char *srv = nil;
+	char *mtpt = "/mnt/mix";
+
+	ARGBEGIN{
+	case 'D':
+		chatty9p++;
+		break;
+	case 's':
+		srv = EARGF(usage());
+		break;
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	default:
+		usage();
+	}ARGEND;
+
+	if(argc)
+		usage();
+
+	fs.tree = alloctree(nil, nil, DMDIR|0777, nil);
+	createfile(fs.tree->root, "audio", nil, 0222, nil);
+	threadpostmountsrv(&fs, srv, mtpt, MREPL);
+
+	mtpt = smprint("%s/audio", mtpt);
+	if(bind(mtpt, "/dev/audio", MREPL) < 0)
+		sysfatal("bind: %r");
+	free(mtpt);
+
+	threadexits(0);
+}
--- /dev/null
+++ b/sys/src/cmd/audio/mixfs/mkfile
@@ -1,0 +1,8 @@
+</$objtype/mkfile
+<../config
+
+OFILES=mixfs.$O
+
+TARG=mixfs
+
+</sys/src/cmd/mkone
--- a/sys/src/cmd/audio/mkfile
+++ b/sys/src/cmd/audio/mkfile
@@ -1,7 +1,7 @@
 </$objtype/mkfile
 
 LIBS=libogg libvorbis libFLAC
-PROGS=pcmconv oggdec oggenc mp3dec mp3enc flacdec wavdec sundec
+PROGS=pcmconv oggdec oggenc mp3dec mp3enc flacdec wavdec sundec mixfs
 #libs must be made first
 DIRS=$LIBS $PROGS
 
--