diff options
author | qwx <qwx@sciops.net> | 2023-01-12 06:48:02 +0000 |
---|---|---|
committer | qwx <qwx@sciops.net> | 2023-01-12 06:48:02 +0000 |
commit | 784b47ad2b1ae5975fd17f428129e014ecc43944 (patch) | |
tree | 097e019257b5c41067a97ae395669e23cd96b028 /sys/src/games/midi.c | |
parent | 89c60481afd8307f7825281bd561995f83a7bfb2 (diff) |
games/midi: fix desyncs after tempo changes in multitrack files
same as 89c60481afd8307f7825281bd561995f83a7bfb2.
Diffstat (limited to 'sys/src/games/midi.c')
-rw-r--r-- | sys/src/games/midi.c | 106 |
1 files changed, 68 insertions, 38 deletions
diff --git a/sys/src/games/midi.c b/sys/src/games/midi.c index c6535219d..0e93a5cd4 100644 --- a/sys/src/games/midi.c +++ b/sys/src/games/midi.c @@ -6,15 +6,16 @@ enum { SAMPLE = 44100 }; struct Tracker { uchar *data; char ended; - uvlong t; + vlong t; + vlong Δ; uchar notes[16][128]; int cmd; } *tr; 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 @@ emallocz(int size) 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 @@ get8(Tracker *src) sysfatal("unexpected eof"); return c; } + dprint("%#p:%02ux", src, *src->data); return *src->data++; } @@ -64,7 +80,7 @@ int getvar(Tracker *src) { int k, x; - + x = get8(src); k = x & 0x7F; while(x & 0x80){ @@ -95,10 +111,10 @@ skip(Tracker *src, int x) while(--x); } -uvlong +double tconv(int n) { - uvlong v; + double v; v = n; v *= tempo; @@ -109,14 +125,20 @@ tconv(int n) } 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 @@ run(uvlong n) 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,18 +163,16 @@ run(uvlong n) write(ofd, out, sizeof out); outp = out; } - T++; + τ++; } } 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 @@ readevent(Tracker *src) sysfatal("invalid midi"); }else src->cmd = t; + dprint("(%02ux) ", t >> 4); switch(t >> 4){ case 0x8: n = get8(src); @@ -171,12 +192,12 @@ readevent(Tracker *src) 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 @@ readevent(Tracker *src) 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 @@ readevent(Tracker *src) 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 @@ main(int argc, char **argv) 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); } |