ref: 784b47ad2b1ae5975fd17f428129e014ecc43944
parent: 89c60481afd8307f7825281bd561995f83a7bfb2
author: qwx <qwx@sciops.net>
date: Thu Jan 12 01:48:02 EST 2023
games/midi: fix desyncs after tempo changes in multitrack files same as 89c60481afd8307f7825281bd561995f83a7bfb2.
--- a/sys/src/games/midi.c
+++ b/sys/src/games/midi.c
@@ -6,7 +6,8 @@
struct Tracker {
uchar *data;
char ended;
- uvlong t;
+ vlong t;
+ vlong Δ;
uchar notes[16][128];
int cmd;
} *tr;
@@ -13,8 +14,8 @@
typedef struct Tracker Tracker;
+int debug;
int fd, ofd, div, tempo = 500000, ntrack;
-uvlong T;
int freq[128];
uchar out[8192], *outp = out;
@@ -30,6 +31,20 @@
return v;
}
+void
+dprint(char *fmt, ...)
+{
+ char s[256];
+ va_list arg;
+
+ if(!debug)
+ return;
+ va_start(arg, fmt);
+ vseprint(s, s+sizeof s, fmt, arg);
+ va_end(arg);
+ fprint(2, "%s", s);
+}
+
int
get8(Tracker *src)
{
@@ -40,6 +55,7 @@
sysfatal("unexpected eof");
return c;
}
+ dprint("%#p:%02ux", src, *src->data);
return *src->data++;
}
@@ -64,7 +80,7 @@
getvar(Tracker *src)
{
int k, x;
-
+
x = get8(src);
k = x & 0x7F;
while(x & 0x80){
@@ -95,10 +111,10 @@
while(--x);
}
-uvlong
+double
tconv(int n)
{
- uvlong v;
+ double v;
v = n;
v *= tempo;
@@ -109,14 +125,20 @@
}
void
-run(uvlong n)
+run(double n)
{
- int samp, j, k, no[128];
+ int j, k, no[128];
int t, f;
short u;
+ uvlong samp;
+ double Δ;
Tracker *x;
-
- samp = n - T;
+ static double T, ε;
+ static uvlong τ;
+
+ Δ = tconv(n) + ε;
+ samp = Δ;
+ ε = Δ - samp;
if(samp <= 0)
return;
memset(no, 0, sizeof no);
@@ -130,7 +152,7 @@
while(samp--){
t = 0;
for(k = 0; k < 128; k++){
- f = (T % freq[k]) >= freq[k]/2 ? 1 : 0;
+ f = (τ % freq[k]) >= freq[k]/2 ? 1 : 0;
t += f * no[k];
}
u = t*10;
@@ -141,7 +163,7 @@
write(ofd, out, sizeof out);
outp = out;
}
- T++;
+ τ++;
}
}
@@ -148,11 +170,9 @@
void
readevent(Tracker *src)
{
- uvlong l;
int n,t;
-
- l = tconv(getvar(src));
- run(src->t += l);
+
+ dprint(" [%zd] ", src - tr);
t = get8(src);
if((t & 0x80) == 0){
src->data--;
@@ -161,6 +181,7 @@
sysfatal("invalid midi");
}else
src->cmd = t;
+ dprint("(%02ux) ", t >> 4);
switch(t >> 4){
case 0x8:
n = get8(src);
@@ -171,12 +192,12 @@
n = get8(src);
src->notes[t & 15][n] = get8(src);
break;
- case 0xB:
- get16(src);
- break;
+ case 0xA:
+ case 0xD:
case 0xC:
get8(src);
break;
+ case 0xB:
case 0xE:
get16(src);
break;
@@ -201,7 +222,7 @@
skip(src, n);
break;
default:
- fprint(2, "unknown meta event type %.2x\n", t);
+ dprint("unknown meta event type %.2x\n", t);
case 3: case 1: case 2: case 0x58: case 0x59: case 0x21:
skip(src, n);
}
@@ -209,16 +230,20 @@
default:
sysfatal("unknown event type %x", t>>4);
}
+ dprint("\n");
}
void
main(int argc, char **argv)
{
- int i, size;
- uvlong t, mint;
- Tracker *x, *minx;
+ int i, size, end;
+ uvlong z;
+ Tracker *x;
ARGBEGIN{
+ case 'D':
+ debug = 1;
+ break;
case 'c':
ofd = 1;
break;
@@ -235,31 +260,36 @@
ntrack = get16(nil);
div = get16(nil);
tr = emallocz(ntrack * sizeof(*tr));
- for(i = 0; i < ntrack; i++){
+ for(x=tr, z=-1UL; x<tr+ntrack; x++){
if(get32(nil) != 0x4D54726B)
sysfatal("invalid track header");
size = get32(nil);
- tr[i].data = emallocz(size);
- readn(fd, tr[i].data, size);
+ x->data = emallocz(size);
+ readn(fd, x->data, size);
+ x->Δ = getvar(x); /* prearm */
+ if(x->Δ < z)
+ z = x->Δ;
}
+ for(x=tr; x<tr+ntrack; x++)
+ x->Δ -= z;
for(i = 0; i < 128; i++)
freq[i] = SAMPLE / (440 * pow(1.05946, i - 69));
- for(;;){
- minx = nil;
- mint = 0;
- for(x = tr; x < tr + ntrack; x++){
+ for(end=0; !end;){
+ end = 1;
+ for(x=tr; x<tr+ntrack; x++){
if(x->ended)
continue;
- t = tconv(peekvar(x)) + x->t;
- if(t < mint || minx == nil){
- mint = t;
- minx = x;
+ end = 0;
+ x->Δ--;
+ x->t += tconv(1);
+ while(x->Δ <= 0){
+ readevent(x);
+ if(x->ended)
+ break;
+ x->Δ = getvar(x);
}
}
- if(minx == nil){
- write(ofd, out, outp - out);
- exits(nil);
- }
- readevent(minx);
+ run(1);
}
+ exits(nil);
}