ref: d55aa5b1876c6e7c7cfbfd823022eea0be6ba13e
parent: 639f9e8de7ad75f1c985d2cec2ef8b39b3d73070
author: rodri <rgl@antares-labs.eu>
date: Thu May 1 16:36:25 EDT 2025
audio/mixfs: add support for a null sink for computers with a faulty or non-existent audio(3) device, you can now set dev to /dev/null to avoid writing into the underlying /dev/audio. it's meant to be used with screamsend(1) to target an external receiver with a functional audio system.
--- a/sys/src/cmd/audio/mixfs/mixfs.c
+++ b/sys/src/cmd/audio/mixfs/mixfs.c
@@ -1,5 +1,6 @@
#include <u.h>
#include <libc.h>
+#include <tos.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
@@ -8,10 +9,11 @@
enum {
NBUF = 8*1024,
- NDELAY = 1764, /* 25.0ms */
+ NDELAY = 1764, /* 40.0ms */
NQUANTA = 64, /* ~1.45ms */
NCHAN = 2,
ABUF = NBUF*NCHAN*2,
+ Nsec = 1000000000ULL,
};
#define MIN(a,b) ((a)<(b)?(a):(b))
@@ -50,6 +52,7 @@
Pcmdesc fmt;
Lock fmtlock;
int fmtchanged;
+int isnulldev;
int delay;
int
@@ -129,7 +132,8 @@
/* 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)){
+ && (strncmp(name, "#A/audio", 8) != 0 || strchr(name+8, '/') != nil)
+ && (strncmp(name, "/dev/null", 9) != 0 || strchr(name+9, '/') != nil)){
werrstr("name doesnt look like an audio device");
return -1;
}
@@ -169,6 +173,7 @@
qlock(&devlock);
free(devaudio);
devaudio = name;
+ isnulldev = 0;
audiofd = dup(afd, audiofd);
qunlock(&devlock);
@@ -188,6 +193,9 @@
if(volfd >= 0)
fprint(volfd, "delay %d\n", delay);
updfmt();
+ }else if(strncmp(p, "null", 4) == 0){
+ isnulldev = 1;
+ updfmt();
}
return 0;
}
@@ -256,11 +264,40 @@
&& a->fmt == b->fmt;
}
+uvlong
+nanosec(void)
+{
+ static uvlong fasthz, xstart;
+ uvlong x;
+
+ if(fasthz == ~0ULL)
+ return nsec() - xstart;
+
+ if(fasthz == 0){
+ if(_tos->cyclefreq){
+ fasthz = _tos->cyclefreq;
+ cycles(&xstart);
+ } else {
+ fasthz = ~0ULL;
+ xstart = nsec();
+ }
+ return 0;
+ }
+ cycles(&x);
+ x -= xstart;
+
+ uvlong q = x / fasthz;
+ uvlong r = x % fasthz;
+
+ return q*Nsec + r*Nsec/fasthz;
+}
+
void
audioproc(void *)
{
static uchar buf[ABUF];
int sweep, i, j, n, m, v, rate;
+ uvlong t0, dt, ns;
ulong rp;
Stream *s;
uchar *p, *bufconv;
@@ -272,7 +309,9 @@
sweep = 0;
rate = fmt.rate;
delay = NDELAY;
+ ns = Nsec/pcmdescdef.rate;
+ t0 = nanosec();
for(;;){
m = NBUF;
for(s = streams; s < streams+nelem(streams); s++){
@@ -323,8 +362,14 @@
continue;
}
- if(m > NQUANTA)
- m = NQUANTA;
+ if(isnulldev){
+ dt = nanosec() - t0;
+ m = (dt+ns-1)/ns;
+ m = MAX(NQUANTA, m);
+ m = MIN(m, NBUF);
+ t0 += dt;
+ }else
+ m = MIN(m, NQUANTA);
p = buf;
rp = mixrp;
@@ -344,6 +389,11 @@
mixrp = rp;
unlock(&rplock);
+ if(isnulldev){
+ sleep(5);
+ continue;
+ }
+
lock(&fmtlock);
if(fmtchanged){
fmtchanged = 0;
@@ -484,8 +534,10 @@
x[0] += volume[0];
x[1] += volume[1];
}
- volume[0] = MIN(MAX(0, x[0]), 100);
- volume[1] = MIN(MAX(0, x[1]), 100);
+ volume[0] = MAX(0, x[0]);
+ volume[0] = MIN(volume[0], 100);
+ volume[1] = MAX(0, x[1]);
+ volume[1] = MIN(volume[1], 100);
/* ≈60dB dynamic range; [0-100] → [0-65536] */
vol64k[0] = 65.536 * (exp(volume[0] * 0.0690876) - 1.0);
vol64k[1] = 65.536 * (exp(volume[1] * 0.0690876) - 1.0);
--
⑨