add audio/mixfs to allow simultanious playback streams over a single /dev/audio
This commit is contained in:
parent
cad92eedd9
commit
b9bec46b98
4 changed files with 300 additions and 1 deletions
|
@ -53,6 +53,17 @@ q ] [
|
||||||
.I length
|
.I length
|
||||||
]
|
]
|
||||||
.PP
|
.PP
|
||||||
|
.B audio/mixfs
|
||||||
|
[
|
||||||
|
.B -D
|
||||||
|
] [
|
||||||
|
.B -s
|
||||||
|
.I srvname
|
||||||
|
] [
|
||||||
|
.B -m
|
||||||
|
.I mtpt
|
||||||
|
]
|
||||||
|
.PP
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
These programs decode and encode various audio formats from and to
|
These programs decode and encode various audio formats from and to
|
||||||
16-bit stereo PCM (little endian). The decoders read the compressed
|
16-bit stereo PCM (little endian). The decoders read the compressed
|
||||||
|
@ -260,6 +271,38 @@ until it reached end of file or, if
|
||||||
was given, a number of
|
was given, a number of
|
||||||
.I length
|
.I length
|
||||||
bytes have been consumed from input.
|
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
|
.SH EXAMPLE
|
||||||
Play back an
|
Play back an
|
||||||
.L .mp3
|
.L .mp3
|
||||||
|
|
248
sys/src/cmd/audio/mixfs/mixfs.c
Normal file
248
sys/src/cmd/audio/mixfs/mixfs.c
Normal file
|
@ -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);
|
||||||
|
}
|
8
sys/src/cmd/audio/mixfs/mkfile
Normal file
8
sys/src/cmd/audio/mixfs/mkfile
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
</$objtype/mkfile
|
||||||
|
<../config
|
||||||
|
|
||||||
|
OFILES=mixfs.$O
|
||||||
|
|
||||||
|
TARG=mixfs
|
||||||
|
|
||||||
|
</sys/src/cmd/mkone
|
|
@ -1,7 +1,7 @@
|
||||||
</$objtype/mkfile
|
</$objtype/mkfile
|
||||||
|
|
||||||
LIBS=libogg libvorbis libFLAC
|
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
|
#libs must be made first
|
||||||
DIRS=$LIBS $PROGS
|
DIRS=$LIBS $PROGS
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue