summaryrefslogtreecommitdiff
path: root/sys/src/games
diff options
context:
space:
mode:
authorqwx <qwx@sciops.net>2023-01-12 06:48:02 +0000
committerqwx <qwx@sciops.net>2023-01-12 06:48:02 +0000
commit784b47ad2b1ae5975fd17f428129e014ecc43944 (patch)
tree097e019257b5c41067a97ae395669e23cd96b028 /sys/src/games
parent89c60481afd8307f7825281bd561995f83a7bfb2 (diff)
games/midi: fix desyncs after tempo changes in multitrack files
same as 89c60481afd8307f7825281bd561995f83a7bfb2.
Diffstat (limited to 'sys/src/games')
-rw-r--r--sys/src/games/midi.c106
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);
}