summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2024-01-06 19:40:00 +0000
committercinap_lenrek <cinap_lenrek@felloff.net>2024-01-06 19:40:00 +0000
commit5e370061e07ff96868ead6138fa552f6c3a5c4bd (patch)
tree56b50256637bbf100c45f8054d919cf9532083ac
parent3d706f962137ee594173323e65ad1afd442eb0a4 (diff)
mixfs: allow switching between audio devices (/dev/audio*)
At startup, look for audio devices like /dev/audio* and open the first one that shows up. We allow to later changing the audio device using the new "new" control message to /dev/audio. Only devices named /dev/audio*, #u/audio* and #A/audio* are allowd so far to prevent accidents.
-rw-r--r--sys/man/1/audio4
-rw-r--r--sys/src/cmd/audio/mixfs/mixfs.c158
2 files changed, 127 insertions, 35 deletions
diff --git a/sys/man/1/audio b/sys/man/1/audio
index 7d9effdb1..1bdc37dee 100644
--- a/sys/man/1/audio
+++ b/sys/man/1/audio
@@ -92,6 +92,8 @@ q ] [
] [
.B -m
.I mtpt
+] [
+/dev/audio
]
.PP
.SH DESCRIPTION
@@ -352,6 +354,8 @@ over
.B /dev/volume
from the parent namespace is proxied with an additional control "mix"
which is used to set the output volume of the mixer.
+Another additional control "dev" can be used to switch between
+audio devices.
A alternative mountpoint
.I mtpt
can be specified with the
diff --git a/sys/src/cmd/audio/mixfs/mixfs.c b/sys/src/cmd/audio/mixfs/mixfs.c
index 4aadf30f4..0b515ee3d 100644
--- a/sys/src/cmd/audio/mixfs/mixfs.c
+++ b/sys/src/cmd/audio/mixfs/mixfs.c
@@ -6,7 +6,8 @@
enum {
NBUF = 8*1024,
- NDELAY = 2048,
+ NDELAY = 512, /* ~11.6ms */
+ NQUANTA = 64, /* ~1.45ms */
NCHAN = 2,
FREQ = 44100,
};
@@ -34,9 +35,13 @@ int lbbuf[NBUF][NCHAN];
int mixbuf[NBUF][NCHAN];
Lock mixlock;
-Stream streams[16];
+Stream streams[64];
+
+char *devaudio;
+QLock devlock;
+int audiofd = -1;
+int volfd = -1;
-int volfd;
int volume[2] = {100, 100};
int vol64k[2] = {65536, 65536};
@@ -61,6 +66,86 @@ clip16(int v)
}
void
+closeaudiodev(void)
+{
+ qlock(&devlock);
+ if(audiofd >= 0){
+ close(audiofd);
+ audiofd = -1;
+ }
+ qunlock(&devlock);
+}
+
+int
+reopendevs(char *name)
+{
+ static char dir[] = "/dev/";
+ int i, n, dfd, afd;
+ char *p;
+ Dir *d;
+
+ if(name != nil){
+ if(name != devaudio){
+ /* hack: restrict to known audio device names */
+ if((strncmp(name, "/dev/audio", 10) != 0 || strchr(name+10, '/') != nil)
+ && (strncmp(name, "#u/audio", 8) != 0 || strchr(name+8, '/') != nil)
+ && (strncmp(name, "#A/audio", 8) != 0 || strchr(name+8, '/') != nil)){
+ werrstr("name doesnt look like an audio device");
+ return -1;
+ }
+ }
+ if((afd = open(name, OWRITE)) >= 0){
+ name = strdup(name);
+ goto found;
+ }
+ if(name != devaudio)
+ return -1;
+ }
+ if((dfd = open(dir, OREAD)) >= 0){
+ while((n = dirread(dfd, &d)) > 0){
+ for(i = 0; i < n; i++){
+ if((d[i].mode & DMDIR) != 0
+ || strncmp(d[i].name, "audio", 5) != 0)
+ continue;
+ name = smprint("%s%s", dir, d[i].name);
+ if((afd = open(name, OWRITE)) >= 0){
+ close(dfd);
+ free(d);
+ goto found;
+ }
+ free(name);
+ }
+ free(d);
+ }
+ close(dfd);
+ werrstr("no devices found");
+ }
+ return -1;
+found:
+ qlock(&devlock);
+ free(devaudio);
+ devaudio = name;
+ audiofd = dup(afd, audiofd);
+ qunlock(&devlock);
+
+ close(afd);
+ if(volfd >= 0){
+ close(volfd);
+ volfd = -1;
+ }
+ if((p = utfrrune(name, '/')) != nil)
+ p++;
+ else
+ p = name;
+ if(strncmp(p, "audio", 5) == 0){
+ name = smprint("%.*svolume%s", (int)(p - name), name, p+5);
+ volfd = open(name, ORDWR);
+ free(name);
+ }
+ return 0;
+}
+
+void
fsopen(Req *r)
{
Stream *s;
@@ -117,14 +202,13 @@ void
audioproc(void *)
{
static uchar buf[NBUF*NCHAN*2];
- int sweep, fd, i, j, n, m, v;
+ int sweep, i, j, n, m, v;
ulong rp;
Stream *s;
uchar *p;
threadsetname("audioproc");
- fd = -1;
sweep = 0;
for(;;){
m = NBUF;
@@ -155,18 +239,14 @@ audioproc(void *)
int ms;
ms = 100;
- if(fd >= 0){
- if(sweep){
- close(fd);
- fd = -1;
- } else {
+ if(audiofd >= 0){
+ if(sweep)
+ closeaudiodev();
+ else {
/* attempt to sleep just shortly before buffer underrun */
- ms = seek(fd, 0, 2);
- if(ms > 0){
- ms *= 800;
- ms /= FREQ*NCHAN*2;
- } else
- ms = 4;
+ ms = seek(audiofd, 0, 2);
+ ms *= 800;
+ ms /= FREQ*NCHAN*2;
}
sweep = 1;
}
@@ -174,13 +254,15 @@ audioproc(void *)
continue;
}
sweep = 0;
- if(fd < 0)
- if((fd = open("/dev/audio", OWRITE)) < 0){
- fprint(2, "%s: open /dev/audio: %r\n", argv0);
+ if(audiofd < 0 && reopendevs(devaudio) < 0){
+ fprint(2, "%s: reopendevs: %r\n", argv0);
sleep(1000);
continue;
}
+ if(m > NQUANTA)
+ m = NQUANTA;
+
p = buf;
rp = mixrp;
for(i=0; i<m; i++){
@@ -199,7 +281,9 @@ audioproc(void *)
mixrp = rp;
unlock(&rplock);
- write(fd, buf, p - buf);
+ n = p - buf;
+ if(write(audiofd, buf, n) != n)
+ closeaudiodev();
}
}
@@ -214,8 +298,11 @@ fsread(Req *r)
if(r->fid->file->aux == &volfd){
static char svol[4096];
if(r->ifcall.offset == 0){
- m = snprint(svol, sizeof(svol), "mix %d %d\n", volume[0], volume[1]);
- if((n = pread(volfd, svol+m, sizeof(svol)-m-1, 0)) > 0)
+ n = 0;
+ m = snprint(svol, sizeof(svol), "dev %s\nmix %d %d\n",
+ devaudio?devaudio:"",
+ volume[0], volume[1]);
+ if(volfd < 0 || (n = pread(volfd, svol+m, sizeof(svol)-m-1, 0)) > 0)
svol[m+n] = 0;
}
readstr(r, svol);
@@ -286,7 +373,12 @@ fswrite(Req *r)
snprint(msg, sizeof(msg), "%.*s",
utfnlen((char*)r->ifcall.data, r->ifcall.count), (char*)r->ifcall.data);
nf = tokenize(msg, f, nelem(f));
- if(nf > 1 && strcmp(f[0], "mix") == 0){
+ if(nf > 1 && strcmp(f[0], "dev") == 0){
+ if(reopendevs(f[1]) < 0){
+ responderror(r);
+ return;
+ }
+ }else if(nf > 1 && strcmp(f[0], "mix") == 0){
x[0] = atoi(f[1]);
x[1] = nf < 3 ? x[0] : atoi(f[2]);
if(f[1][0] == '+' || f[1][0] == '-'){
@@ -432,26 +524,22 @@ threadmain(int argc, char **argv)
usage();
}ARGEND;
- if(argc)
+ if(argc > 1)
usage();
- volfd = open("/dev/volume", ORDWR);
+ reopendevs(argv[0]);
+ closeaudiodev();
+
fs.tree = alloctree(nil, nil, DMDIR|0777, nil);
createfile(fs.tree->root, "audio", nil, 0666, nil);
createfile(fs.tree->root, "volume", nil, 0666, &volfd);
threadpostmountsrv(&fs, srv, mtpt, MREPL);
+ bind(mtpt, "/dev", MAFTER);
m = smprint("%s/audio", mtpt);
- if(bind(m, "/dev/audio", MREPL) < 0)
- sysfatal("bind: %r");
- free(m);
-
- if(volfd >= 0){
- m = smprint("%s/volume", mtpt);
- if(bind(m, "/dev/volume", MREPL) < 0)
- sysfatal("bind: %r");
- free(m);
- }
+ bind(m, "/dev/audio", MREPL);
+ m = smprint("%s/volume", mtpt);
+ bind(m, "/dev/volume", MREPL);
threadexits(0);
}