summaryrefslogtreecommitdiff
path: root/sys/src/cmd/audio
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2013-12-26 21:04:25 +0100
committercinap_lenrek <cinap_lenrek@felloff.net>2013-12-26 21:04:25 +0100
commitb9bec46b989577209c88a8dc8ee8bb171f8c96e4 (patch)
treecc8ae22323c1b2a81c1830cad0e0def29c19d0ce /sys/src/cmd/audio
parentcad92eedd9d2eca65216046e3ad735fd18c933e7 (diff)
add audio/mixfs to allow simultanious playback streams over a single /dev/audio
Diffstat (limited to 'sys/src/cmd/audio')
-rw-r--r--sys/src/cmd/audio/mixfs/mixfs.c248
-rw-r--r--sys/src/cmd/audio/mixfs/mkfile8
-rw-r--r--sys/src/cmd/audio/mkfile2
3 files changed, 257 insertions, 1 deletions
diff --git a/sys/src/cmd/audio/mixfs/mixfs.c b/sys/src/cmd/audio/mixfs/mixfs.c
new file mode 100644
index 000000000..6e3b722ea
--- /dev/null
+++ b/sys/src/cmd/audio/mixfs/mixfs.c
@@ -0,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);
+}
diff --git a/sys/src/cmd/audio/mixfs/mkfile b/sys/src/cmd/audio/mixfs/mkfile
new file mode 100644
index 000000000..b839d5e52
--- /dev/null
+++ b/sys/src/cmd/audio/mixfs/mkfile
@@ -0,0 +1,8 @@
+</$objtype/mkfile
+<../config
+
+OFILES=mixfs.$O
+
+TARG=mixfs
+
+</sys/src/cmd/mkone
diff --git a/sys/src/cmd/audio/mkfile b/sys/src/cmd/audio/mkfile
index f5d59a139..abc209502 100644
--- 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