ref: ee13f7a19374b691339702a4b0197215c58d2190
parent: e0873ae0adda493fd8e3923591831af37a22bf9b
author: qwx <qwx@sciops.net>
date: Sat Jan 21 13:12:12 EST 2023
games/opl3: implement real time streaming mode this pushes all responsibility for timing to opl3, absolving dmid(1) and other writers from constantly sending sync events to force opl3 to synthesize audio in between events. it reduces cpu usage and syscalls dramatically and enables applications other than dmid(1) to modify its state in real time, for instance to use it as a synth.
--- a/sys/src/games/opl3/opl3m.c
+++ b/sys/src/games/opl3/opl3m.c
@@ -13,7 +13,7 @@
void
usage(void)
{
- fprint(2, "usage: %s [-r rate] [file]\n", argv0);
+ fprint(2, "usage: %s [-s] [-r rate] [file]\n", argv0);
exits("usage");
}
@@ -20,12 +20,15 @@
void
main(int argc, char **argv)
{
- int rate, n, r, v, fd, pfd[2];
- uchar sb[65536 * 4], u[5];
- double f, dt;
+ int rate, stream, n, r, v, fd, pfd[2];
+ uchar sb[64*1024], u[5];
+ double f;
+ vlong dt, T;
Biobuf *bi;
+ QLock slock;
fd = 0;
+ stream = 0;
rate = OPLrate;
ARGBEGIN{
case 'r':
@@ -33,6 +36,9 @@
if(rate <= 0 || rate > OPLrate)
usage();
break;
+ case 's':
+ stream = 1;
+ break;
default:
usage();
}ARGEND;
@@ -60,19 +66,44 @@
}
f = (double)OPLrate / rate;
dt = 0;
+ T = nsec();
+ if(stream){
+ switch(rfork(RFPROC|RFMEM)){
+ case -1:
+ sysfatal("rfork: %r");
+ case 0:
+ for(;;){
+ qlock(&slock);
+ n = OPLrate / 1e3;
+ T += n * (1e9 / OPLrate);
+ n *= 4;
+ opl3out(sb, n);
+ n = write(pfd[1], sb, n);
+ dt = (T - nsec()) / 1e6;
+ qunlock(&slock);
+ if(n <= 0)
+ break;
+ if(dt > 0)
+ sleep(dt);
+ }
+ }
+ }
while((n = Bread(bi, u, sizeof u)) > 0){
r = u[1] << 8 | u[0];
v = u[2];
opl3wr(r, v);
dt += (u[4] << 8 | u[3]) * f;
+ qlock(&slock);
while((n = dt) > 0){
if(n > sizeof sb / 4)
n = sizeof sb / 4;
dt -= n;
+ T += n * (1e9 / OPLrate);
n *= 4;
opl3out(sb, n);
write(pfd[1], sb, n);
}
+ qunlock(&slock);
}
if(n < 0)
sysfatal("read: %r");
--
⑨