ref: 89c60481afd8307f7825281bd561995f83a7bfb2
parent: f7936560394233da7558aa3b3c51c14ba6185191
author: qwx <qwx@sciops.net>
date: Wed Jan 11 03:42:35 EST 2023
games/dmid: fix tempo changes causing desyncs in multitrack files if tempo changes while older events are in flight at different timings in a multitrack file, and since there's no compensation for it, tracks desyncronise, the order of events get jumbled up and everything just falls apart. rather than doing dances with the old code to figure out what corrective factor to apply and where, just use the simplest and most robust way, advance one tic at a time. also add a tracing mode and don't always print useless and annoying warnings.
--- a/sys/src/games/dmid.c
+++ b/sys/src/games/dmid.c
@@ -77,14 +77,15 @@
u8int *s;
u8int *p;
u8int *e;
- uvlong t;
+ vlong Δ;
+ vlong t;
int ev;
};
Trk *tr;
+int trace;
double freq[128];
int mfmt, ntrk, div = 1, tempo, opl2, stream;
-uvlong T;
Biobuf *ib;
void *
@@ -129,6 +130,20 @@
sysfatal("bread: short read");
}
+void
+dprint(char *fmt, ...)
+{
+ char s[256];
+ va_list arg;
+
+ if(!trace)
+ return;
+ va_start(arg, fmt);
+ vseprint(s, s+sizeof s, fmt, arg);
+ va_end(arg);
+ fprint(2, "%s", s);
+}
+
u8int
get8(Trk *x)
{
@@ -140,7 +155,9 @@
}
if(x->p >= x->e)
sysfatal("track overflow");
- return *x->p++;
+ v = *x->p++;
+ dprint("%02ux", v);
+ return v;
}
u16int
@@ -326,10 +343,10 @@
}
}
-uvlong
-tc(int n)
+double
+tc(double n)
{
- return ((uvlong)n * tempo * Rate / div) / 1000000;
+ return (n * tempo * Rate / div) / 1e6;
}
void
@@ -356,29 +373,19 @@
return v;
}
-int
-peekvar(Trk *x)
-{
- int v;
- uchar *p;
-
- p = x->p;
- v = getvar(x);
- x->p = p;
- return v;
-}
-
void
-samp(uvlong t´)
+samp(double n)
{
- int dt;
- static uvlong t;
+ vlong t;
+ double Δ;
+ static double ε;
- dt = t´ - t;
- t += dt;
- while(dt > 0){
- putcmd(0, 0, dt > 0xffff ? 0xffff : dt);
- dt -= 0xffff;
+ Δ = tc(n) + ε;
+ t = Δ;
+ ε = Δ - t;
+ while(t > 0){
+ putcmd(0, 0, t > 0xffff ? 0xffff : t);
+ t -= 0xffff;
}
}
@@ -388,7 +395,7 @@
int e, n, m;
Chan *c;
- samp(x->t += tc(getvar(x)));
+ dprint(" [%zd] ", x - tr);
e = get8(x);
if((e & 0x80) == 0){
x->p--;
@@ -398,6 +405,7 @@
}else
x->ev = e;
c = chan + (e & 15);
+ dprint("(%02ux) ", e);
n = get8(x);
switch(e >> 4){
case 0x8: noteoff(c, n, get8(x)); break;
@@ -408,7 +416,7 @@
case 0x00: case 0x01: case 0x20: break;
case 0x07: c->v = m; resetchan(c); break;
case 0x0a: c->pan = m < 32 ? 1<<4 : m > 96 ? 1<<5 : 3<<4; resetchan(c); break;
- default: fprint(2, "unknown controller %d\n", n);
+ default: dprint("\nunknown controller %d", n);
}
break;
case 0xc: c->i = inst + n; break;
@@ -421,11 +429,11 @@
if((e & 0xf) == 0){
while(get8(x) != 0xf7)
;
- return;
+ break;
}
m = get8(x);
switch(n){
- case 0x2f: x->p = x->e; return;
+ case 0x2f: x->p = x->e; break;
case 0x51: tempo = get16(x) << 8; tempo |= get8(x); break;
default: skip(x, m);
}
@@ -434,6 +442,7 @@
case 0xd: get8(x); break;
default: sysfatal("invalid event %#ux\n", e >> 4);
}
+ dprint("\n");
}
void
@@ -489,7 +498,7 @@
void
readmid(char *file)
{
- u32int n;
+ u32int n, z;
uchar *s;
Trk *x;
@@ -506,7 +515,7 @@
sysfatal("unsupported format %d", mfmt);
div = get16(nil);
tr = emalloc(ntrk * sizeof *tr);
- for(x=tr; x<tr+ntrk; x++){
+ for(x=tr, z=-1UL; x<tr+ntrk; x++){
if(get32(nil) != 0x4d54726b)
sysfatal("invalid track");
n = get32(nil);
@@ -515,7 +524,12 @@
x->s = s;
x->p = s;
x->e = s + n;
+ x->Δ = getvar(x); /* prearm */
+ if(x->Δ < z)
+ z = x->Δ;
}
+ for(x=tr; x<tr+ntrk; x++)
+ x->Δ -= z;
Bterm(ib);
}
@@ -522,7 +536,7 @@
void
usage(void)
{
- fprint(2, "usage: %s [-2s] [-i inst] [mid]\n", argv0);
+ fprint(2, "usage: %s [-2Ds] [-i inst] [mid]\n", argv0);
exits("usage");
}
@@ -529,17 +543,19 @@
void
threadmain(int argc, char **argv)
{
- int n, t, mint;
+ int n, end, debug;
char *i;
double f;
uchar u[4];
Chan *c;
Opl *o;
- Trk xs, *x, *minx;
+ Trk xs, *x;
i = "/mnt/wad/genmidi";
+ debug = 0;
ARGBEGIN{
case '2': opl2 = 1; ople = opl + 9; break;
+ case 'D': debug = 1; break;
case 'i': i = EARGF(usage()); break;
case 's': stream = 1; break;
default: usage();
@@ -560,6 +576,7 @@
tempo = 500000;
putcmd(Rwse, Mwse, 0);
putcmd(Rop3, 1, 0);
+ trace = debug;
if(stream){
if(proccreate(tproc, nil, mainstacksize) < 0)
sysfatal("proccreate: %r");
@@ -574,20 +591,22 @@
}
threadexitsall(n < 0 ? "read: %r" : nil);
}
- for(;;){
- minx = nil;
- mint = 0;
+ for(end=0; !end;){
+ end = 1;
for(x=tr; x<tr+ntrk; x++){
if(x->p >= x->e)
continue;
- t = x->t + tc(peekvar(x));
- if(t < mint || minx == nil){
- mint = t;
- minx = x;
+ end = 0;
+ x->Δ--;
+ x->t += tc(1);
+ while(x->Δ <= 0){
+ ev(x);
+ if(x->p >= x->e)
+ break;
+ x->Δ = getvar(x);
}
}
- if(minx == nil)
- exits(nil);
- ev(minx);
+ samp(1);
}
+ exits(nil);
}