ref: 5e370061e07ff96868ead6138fa552f6c3a5c4bd
parent: 3d706f962137ee594173323e65ad1afd442eb0a4
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Jan 6 14:40:00 EST 2024
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.
--- a/sys/man/1/audio
+++ b/sys/man/1/audio
@@ -92,6 +92,8 @@
] [
.B -m
.I mtpt
+] [
+/dev/audio
]
.PP
.SH DESCRIPTION
@@ -352,6 +354,8 @@
.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
--- 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 mixbuf[NBUF][NCHAN];
Lock mixlock;
-Stream streams[16];
+Stream streams[64];
-int volfd;
+char *devaudio;
+QLock devlock;
+int audiofd = -1;
+int volfd = -1;
+
int volume[2] = {100, 100};
int vol64k[2] = {65536, 65536};
@@ -61,6 +66,86 @@
}
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,7 +202,7 @@
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;
@@ -124,7 +209,6 @@
threadsetname("audioproc");
- fd = -1;
sweep = 0;
for(;;){
m = NBUF;
@@ -155,18 +239,14 @@
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 @@
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 @@
mixrp = rp;
unlock(&rplock);
- write(fd, buf, p - buf);
+ n = p - buf;
+ if(write(audiofd, buf, n) != n)
+ closeaudiodev();
}
}
@@ -214,8 +298,11 @@
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 @@
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 @@
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);
}