ref: a54b3cc31a0711e96c03e309613adfc3726695d1
author: qwx <qwx@sciops.net>
date: Mon May 27 19:50:23 EDT 2019
initial import
--- /dev/null
+++ b/lib/metallica.and.justice.for.all.mst
@@ -1,0 +1,243 @@
+# done by hand; very incomplete; two guitar tracks only; inaccuracies exist
+t 96
+# 3/4
+# bar 1 - intro riff 1
+i 0 27
+i 1 27
+8 0e3- 0e4- 0g♯4
+8 0a4
+8 0b4-
+8 0e4-
+8 0b4,0 0a4
+8/3 0g♯4
+8/3 0a4
+8/3 0g♯4
+# bar 2
+8 0e3,0 0e4,0 0d4- 0f♯4
+8 0g4
+8 0g4
+8 0d4- 0f♯4
+8 0a4
+8 0d4- 0f♯4
+# bar 3
+8 0d4,0 0c4- 0e4
+8 0f4
+8 0g4
+8 0c4-
+8 0f4
+8/3 0e4
+8/3 0f4
+8/3 0e4
+# bar 4
+8 0c4,0 0b3- 0d4
+8 0e4
+8 0e4
+8 0b3- 0d4
+8 0e4
+8 0b3- 0d4
+# bar 5
+8 0b3,0 0a♯3- 0d4
+8 0e4
+8 0e4
+8 0a♯3- 0d4
+8 0e4
+8 0a♯3,0 0a4
+# bar 6
+2. 0a♯3 0f4
+
+# bar 7
+8 0e3- 0e4- 0g♯4
+8 0a4
+8 0b4-
+8 0e4-
+8 0b4,0 0a4
+8/3 0g♯4
+8/3 0a4
+8/3 0g♯4
+# bar 8
+8 0e3,0 0e4,0 0d4- 0f♯4
+8 0g4
+8 0g4
+8 0d4- 0f♯4
+8 0a4
+8 0d4- 0f♯4
+# bar 9
+8 0d4,0 0c4- 0e4
+8 0f4
+8 0g4
+8 0c4-
+8 0f4
+8/3 0e4
+8/3 0f4
+8/3 0e4
+# bar 10
+8 0c4,0 0b3- 0d4
+8 0e4
+8 0e4
+8 0b3- 0d4
+8 0e4
+8 0b3- 0d4
+# bar 11
+8 0b3,0 0a♯3- 0d4
+8 0e4
+8 0e4
+8 0a♯3- 0d4
+8 0e4
+8 0a♯3,0 0a4
+# bar 12
+2. 0a♯3 0f4
+
+# bar 13 - intro riff 2
+i 1 29
+8 0e3- 0e4- 0g♯4 1g♯4
+8 0a4 1a4
+8 0b4- 1b4
+32 0e4- 1d5
+16. 1e5-
+8 0b4,0 0a4 1b4,0
+8/3 0g♯4
+8/3 0a4
+8/3 0g♯4
+# bar 14
+8 0e3,0 0e4,0 0d4- 0f♯4 1e5,0 1f♯4
+8 0g4 1a4
+16 0g4- 1d5
+16 1d5,0
+16 0g4,0 0d4- 0f♯4- 1d5
+16 1d♯5
+8 0f♯4,0 0a4 1d5-
+8 0d4- 0f♯4
+# bar 15
+8 0d4,0 0c4- 0e4 1d5,0 1e4
+8 0f4 1f4
+8 0g4 1g4
+8 0c4- 1c5-
+8 0f4
+8/3 0e4 1c5,0 1g4-
+8/3 0f4
+8/3 0e4
+# bar 16
+# FIXME: needs staccato here
+i 0 30
+i 1 30
+8 0c4,0 0b3- 0d4 1g4,0 1b3- 1d4
+8 0e4 1e4
+8 0e4 1e4
+8 0b3- 0d4 1d4
+8 0e4 1e4
+8 0b3- 0d4 1d4
+# bar 17
+8 0b3,0 0a♯3- 0d4 1b3,0 1a♯3-
+8 0e4 1e4
+8 0e4 1e4
+8 0a♯3- 0d4 1d4
+8 0e4 1e4
+8 0a♯3,0 0a4 1a♯3,0 1a4
+# bar 18
+4 0a♯3 0f4 0a♯4 1a♯3 1f4 1a♯4
+4 0a♯3 0f4 0a♯4 1a♯3 1f4 1a♯4
+4 0a♯3 0f4 0a♯4 1a♯3 1f4 1a♯4
+
+# bar 19
+i 0 27
+i 1 29
+8 0e3- 0e4- 0g♯4 1g♯4
+8 0a4 1a4
+8 0b4- 1b4
+32 0e4- 1d5
+16. 1e5-
+8 0b4,0 0a4 1b4,0
+8/3 0g♯4
+8/3 0a4
+8/3 0g♯4
+# bar 20
+8 0e3,0 0e4,0 0d4- 0f♯4 1e5,0 1f♯4
+8 0g4 1a4
+16 0g4- 1d5
+16 1d5,0
+16 0g4,0 0d4- 0f♯4- 1d5
+16 1d♯5
+8 0f♯4,0 0a4 1d5-
+8 0d4- 0f♯4
+# bar 21
+8 0d4,0 0c4- 0e4 1d5,0 1e4
+8 0f4 1f4
+8 0g4 1g4
+8 0c4- 1c5-
+8 0f4
+8/3 0e4 1c5,0 1g4-
+8/3 0f4
+8/3 0e4
+# bar 22
+i 0 30
+i 1 30
+8 0c4,0 0b3- 0d4 1g4,0 1b3- 1d4
+8 0e4 1e4
+8 0e4 1e4
+8 0b3- 0d4 1d4
+8 0e4 1e4
+8 0b3- 0d4 1d4
+# bar 23
+8 0b3,0 0a♯3- 0d4 1b3,0 1a♯3-
+8 0e4 1e4
+8 0e4 1e4
+8 0a♯3- 0d4 1d4
+8 0e4 1e4
+8 0a♯3,0 0a4 1a♯3,0 1a4
+# bar 24
+4 0a♯3 0f4 0a♯4 1a♯3 1f4 1a♯4
+4 0a♯3 0f4 0a♯4 1a♯3 1f4 1a♯4
+4 0a♯3 0f4 0a♯4 1a♯3 1f4 1a♯4
+
+# bar 25 - intro riff 3
+i 0 27
+8 0e4 0g♯4 1g♯4
+8 0e4 0a4 1b4
+4 0e4 0b4 1e5
+8 0e4
+8/3 0e4- 0g♯4- 1e5
+8/3 1f♯5
+8/3 1e5
+# bar 26
+8 0e4,0 0g♯4,0 0d4 0f♯4 1f♯4
+8 0d4 0g4 1a4
+16 0d4 0a4 # ghost note on ch 1
+16 0d4 0a4 1d5
+8 0d4 0f♯4 1a4
+8 0d4 0a4 1d5 1f♯5
+8 0d4 0f♯4 1a4 1d5
+# bar 27
+8 0c4 0e4 1e4
+8 0c4 0f4 1g4
+4 0c4 0g4 1c5
+8 0c4 0f4 1g4
+8/3 0c4- 0e4- 1c5
+8/3 1d5
+8/3 1c5
+# bar 28
+i 0 30
+8 0c4,0 0e4,0 0b3 0d4 1d4
+8 0b3 0e4 1e4
+16 # ghost notes
+16 0b3 0e4
+32 0b3- 0e4- 1d4
+16. 1e4
+32 0b3- 0e4- 1d4
+16. 1f♯4
+32 0b3- 0e4- 1d4
+16. 1e4
+# bar 29
+8 0b3,0 0e4,0 0a♯3 0d4 1d4
+8 0a♯3 0e4 1e4
+16 # ghost notes
+16 0a♯3 0e4
+8 0a♯3 0e4 1e4
+8 0a♯3 0e4 1e4
+8 0a4 1e4
+
+# 4/4
+# bar 30
+1 0a♯3 0f4 0a♯4 1a♯4 1d5
+
+t 140
+# bar 31 - intro riff 4
--- /dev/null
+++ b/lib/u3.alive.mst
@@ -1,0 +1,38 @@
+t 114
+i 0 52
+i 1 74
+i 2 52
+i 3 69
+i 4 74
+4 0c5 1g5 2g4 3c6 4c5
+4 0g5 1c6 2c5 3g6 4g5
+4 0c6 1g6 2g5 3c7 4c6
+4 0c6,0 0d6 +
+4 0a♯5 1f6- 2f5- 3d7 4d6
+4 0d6 3a♯6 4a♯5
+4 0c6 1f6,0 1g6 2f5,0 2g5 3c7 4c6
+4 0c6,0 0g5 +
+4 0d♯5 1a♯6 2a♯5 3d♯7 4d♯6
+4 0d♯5,0 0a♯5 +
+4 0f5 1a6- 2a5- 3d7 4d6
+4 0c6 3c7 4c6
+4 0g5 1a6,0 1g6- 2a5,0 2g5- 3a♯6 4a♯5
+4 0d6 3c7 4c6
+4 0a5 1g6,0 1f♯6 2g5,0 2f♯5 3a6 4a5
+4 0a5,0 0d5 +
+4 0g5 1d6 2d5 3g6 4g5
+4 0d6 1g6 2g5 3d7 4d6
+4 0g6 1d7 2d6 3g7 4g6
+4 0g6,0 0a6 +
+4 0f6 1c7- 2c6- 3a7 4a6
+4 0a6 3f7 4f6
+4 0g6 1c7,0 1d7 2c6,0 2d6 3g7 4g6
+4 0g6,0 0d6 +
+4 0a♯5 1f7 2f6 3a♯7 4a♯6
+4 0a♯5,0 0f6 +
+4 0c6 1e7- 2e6- 3a7 4a6
+4 0g6 3g7 4g6
+4 0d6 1e7,0 1d7- 2e6,0 2d6- 3f7 4f6
+4 0a6 3g7 4g6
+4 0e6 1d7,0 1c♯7 2d6,0 2c♯6 3e7 4e6
+4 0e6,0 0a5 +
--- /dev/null
+++ b/lib/waltergreene.pinkpanther.mst
@@ -1,0 +1,82 @@
+t 124
+i 0 34
+i 1 19
+4/3 0e3 9d3 9a♯3
+4/3 +
+4/3 0e3 1a♯5 9d3 9a♯3
+4/3 0b3 1b5 9a♯3
+4/3 0a♯3 9a♯3
+4/3 0b3 1e6 9a♯3
+
+4/3 0e3 1g6 9d3 9a♯3
+4/3 +
+4/3 0e3 1b6- 9d3 9a♯3
+4/3 0b3 9a♯3
+4/3 0a♯3 9a♯3
+4/3 0b3 9a♯3
+
+4/3 0e3 1b6,0 1a6- 9d3 9a♯3
+4/3 +
+4/3 0e3 1a6,0 1g6 9d3 9a♯3
+4/3 0b3 1e6 9a♯3
+4/3 0a♯3 9a♯3
+4/3 0b3 1d6- 9a♯3
+
+4/3 0e3 9d3 9a♯3
+4/3 +
+4/3 0e3 9d3 9a♯3
+4/3 0b3 9a♯3
+4/3 0a♯3 1d6,0 9a♯3
+4/3 0b3 9a♯3
+
+4/3 0f♯3 1b5- 9d3 9a♯3
+4/3 +
+4/3 0f♯3 1b5,0 1a5- 9d3 9a♯3
+4/3 0b3 9a♯3
+4/3 0a♯3 9a♯3
+4/3 0b3 9a♯3
+
+4/3 0f♯3 9d3 9a♯3
+4/3 +
+4/3 0f♯3 9d3 9a♯3
+4/3 0b3 1a5,0 9a♯3
+4/3 0a♯3 9a♯3
+4/3 0b3 9a♯3
+
+4/3 0f♯3 1a5- 9d3 9a♯3
+4/3 +
+4/3 0f♯3 1a5,0 1f♯5- 9d3 9a♯3
+4/3 0b3 9a♯3
+4/3 0a♯3 1f♯5,0 1a5- 9a♯3
+4/3 0b3 9a♯3
+
+4/3 0f♯3 1a5,0 1f♯5 9d3 9a♯3
+4/3 +
+4/3 0f♯3 9d3 9a♯3
+4/3 0b3 1b5 9a♯3
+4/3 0a♯3 9a♯3
+4/3 0b3 1b5 9a♯3
+
+# wrong timing
+4/3 0f♯3 1c♯6 9d3 9a♯3
+4/3 1c♯6,0 +
+4/3 0f♯3 1c♯6 9d3 9a♯3
+# wrong timing
+4/3 0b3 1f♯6 9a♯3
+8/3 0a♯3- 1f6 9a♯3-
+8/3 1f♯6
+4/3 0a♯3,0 0b3 1f6 9a♯3
+
+4/3 0b3,0 0f♯3 1b5 9d3 9a♯3
+4/3 1b5,0 +
+4/3 0f♯3 1f♯6 9d3 9a♯3
+4/3 0b3 9a♯3
+4/3 0a♯3 9a♯3
+4/3 0b3 9a♯3
+
+#4/3 0e3 9d3 9a♯3
+#4/3 +
+#4/3 0e3 9d3 9a♯3
+#4/3 0b3 9a♯3
+#4/3 0a♯3 9a♯3
+#4/3 0b3 9a♯3
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,9 @@
+</$objtype/mkfile
+TARG=\
+ mst\
+ tomst\
+
+OFILES=
+HFILES=
+</sys/src/cmd/mkmany
+BIN=$home/bin/$objtype
--- /dev/null
+++ b/mst.c
@@ -1,0 +1,314 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <bio.h>
+
+enum{
+ Nmn = 60000000,
+ Nbuf = 64*1024,
+ Held = 1<<7
+};
+u32int lastdt;
+int div, ch[16*128], line;
+char *arg[64];
+uchar *outb, *oute, *outp, *last;
+uchar hdr[] = {
+ 'M', 'T', 'h', 'd',
+ 0x00, 0x00, 0x00, 0x06,
+ 0x00, 0x00,
+ 0x00, 0x01,
+ 0x00, 0x00,
+ 'M', 'T', 'r', 'k',
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00
+};
+
+#define PUT16(p,v) (p)[0]=(v)>>8;(p)[1]=(v)
+#define PUT24(p,v) (p)[0]=(v)>>16;(p)[1]=(v)>>8;(p)[2]=(v)
+#define PUT32(p,v) (p)[0]=(v)>>24;(p)[1]=(v)>>16;(p)[2]=(v)>>8;(p)[3]=(v)
+
+int
+out(void *u, int n)
+{
+ uintptr off;
+ ulong len;
+
+ if(outp + n >= oute){
+ off = outp - outb;
+ len = oute - outb + Nbuf;
+ outb = realloc(outb, len);
+ if(outb == nil)
+ sysfatal("realloc: %r");
+ outp = outb + off;
+ oute = outb + len;
+ }
+ memmove(outp, u, n);
+ outp += n;
+ last = outp;
+ lastdt = 0;
+ return n;
+}
+
+void
+barf(void)
+{
+ uchar eot[] = {0xff, 0x2f, 0x00};
+
+ out(eot, sizeof eot);
+ PUT16(outb+4+4+2+2, div);
+ PUT32(outb+4+4+2+2+2+4, outp - outb - sizeof hdr + 1);
+ write(1, outb, outp - outb);
+}
+
+void
+setnt(int c, int n, int v)
+{
+ uchar u[4];
+
+ u[0] = c | 0x80 | (v != 0) << 4;
+ u[1] = n;
+ u[2] = v;
+ u[3] = 0;
+ out(u, sizeof u);
+ last--;
+}
+
+void
+setdt(u32int dt)
+{
+ int n;
+ u32int v;
+ uchar u[4], *up;
+
+ memset(u, 0, sizeof u);
+ if(last == nil)
+ last = outp;
+ if(lastdt + dt < dt)
+ setnt(0, 0, ch[0] & 0x7f);
+ v = lastdt += dt;
+ up = u + sizeof(u) - 1;
+ *up-- = lastdt & 0x7f;
+ while(n = lastdt >>= 7 & 0x7f)
+ *up-- = 1<<7 | n;
+ outp = last;
+ last -= out(up+1, sizeof(u) + u - up - 1);
+ lastdt = v;
+}
+
+void
+setdiv(int n, char **arg)
+{
+ char *p;
+
+ if(line != 1)
+ sysfatal("line %d: div setting not on first line", line);
+ if(n != 1)
+ sysfatal("line %d: invalid argument", line);
+ /* FIXME: maybe wrap strtol in a function or something (va?) */
+ div = strtol(*arg, &p, 0);
+ if(div <= 0)
+ sysfatal("line %d: invalid div value %d", line, div);
+}
+
+void
+setbpm(int n, char **arg)
+{
+ static uchar u[] = {0xff, 0x51, 0x03, 0x00, 0x00, 0x00, 0x00};
+ int T;
+ char *p;
+
+ if(n != 1)
+ sysfatal("line %d: invalid argument", line);
+ n = strtol(*arg, &p, 0);
+ T = 0;
+ if(n <= 0 || (T = Nmn / n) <= 0)
+ sysfatal("line %d: invalid bpm value %d", line, n);
+ PUT24(u+3, T);
+ out(u, sizeof u);
+ last--;
+}
+
+void
+setinst(int n, char **arg)
+{
+ static uchar u[] = {0xc0, 0x00, 0x00};
+ char *p;
+
+ if(n != 2)
+ sysfatal("line %d; invalid argument", line);
+ n = strtol(*arg, &p, 0);
+ if(p == *arg || n & ~15 || n == 9)
+ sysfatal("line %d: invalid channel number %d", line, n);
+ u[0] |= n;
+ n = strtol(*++arg, &p, 0);
+ if(p == *arg || n & ~127)
+ sysfatal("line %d: invalid instrument number %d", line, n);
+ u[1] = n;
+ out(u, sizeof u);
+ last--;
+}
+
+void
+parsent(char *s)
+{
+ static tt[] = {9, 11, 0, 2, 4, 5, 7};
+ int c, n, o, v, *np;
+ char *p;
+ Rune r;
+
+ c = strtol(s, &p, 10);
+ if(p == s || c < 0 || c > 15)
+ sysfatal("line %d: invalid channel number %d", line, c);
+ n = tolower(*p++);
+ if(n < 'a' || n > 'g')
+ sysfatal("line %d: invalid note name", line);
+ n = tt[n - 'a'];
+ s = p + chartorune(&r, p);
+ if(r == 'b' || r == L'♭'){
+ n--;
+ p = s;
+ }else if(r == '#' || r == L'♯'){
+ n++;
+ p = s;
+ }
+ s = p;
+ o = strtol(s, &p, 10);
+ if(p == s || o < 0 || o > 10)
+ sysfatal("line %d: invalid octave number", line);
+ n += 12 * o;
+ if(n < 0 || n > 127)
+ sysfatal("line %d: invalid note number", line);
+ np = ch + c * 128 + n;
+ v = 64;
+ if(*p == ','){
+ s = p + 1;
+ v = strtol(s, &p, 10);
+ if(p == s || v < 0 || v > 127)
+ sysfatal("line %d: invalid velocity", line);
+ }
+ if(*np & 0x7f)
+ setnt(c, n, 0);
+ *np = v;
+ if(*p == '-')
+ *np |= Held;
+ setnt(c, n, v);
+}
+
+u32int
+parsedt(char *s)
+{
+ ulong d;
+ u32int dt;
+ char *p;
+
+ if(*s == 'c'){
+ d = strtoul(s+1, &p, 10);
+ if(p == s+1)
+ sysfatal("line %d: invalid note duration", line);
+ return d;
+ }
+ d = strtoul(s, &p, 10);
+ if(d == 0 || d & ~0xff || d != 1 && d & d - 1)
+ sysfatal("line %d: invalid note duration", line);
+ dt = div * 4;
+ while(d >>= 1)
+ dt >>= 1;
+ if(*p == '/'){
+ d = *++p - '0';
+ if(d != 3 && d != 5)
+ sysfatal("line %d: unsupported tuplet %c", line, (char)d);
+ dt /= d;
+ }
+ if(*p == '.')
+ dt = dt * 3 >> 1;
+ return dt;
+}
+
+void
+parse(int n)
+{
+ static int gdt;
+ int *np, hold, grace;
+ u32int dt;
+ char c, **sp, **se;
+
+ line++;
+ if(n < 1)
+ return;
+ hold = grace = 0;
+ for(sp=arg, se=arg+n; sp<se; sp++){
+ c = **sp;
+ if(c == '#' || c == '+'){
+ n = sp - arg;
+ hold = c == '+';
+ break;
+ }
+ }
+ if(n < 1)
+ return;
+ sp = arg;
+ switch(**arg){
+ case 'd': setdiv(n-1, arg+1); return;
+ /* FIXME: grace note: g [dt] [note]...; take dt from next note's
+ * duration, is additive (multiple grace notes) and no updates occur
+ * when one is struck */
+ /* FIXME: more intelligent argument parsing, wrt parsedt and parsent
+ * and arguments list */
+ case 'g': grace = 1; sp++; n--; sysfatal("unimplemented"); break;
+ case 'i': setinst(n-1, arg+1); return;
+ case 't': setbpm(n-1, arg+1); return;
+ }
+ dt = parsedt(*(sp++));
+ if(!hold && !grace)
+ for(np=ch; np<ch+nelem(ch); np++)
+ if(*np & 0x7f && (n == 1 || (*np & Held) == 0))
+ setnt((np - ch) / 128, (np - ch) % 128, 0);
+ while(--n > 0){
+ if(*sp[0] == '#' || *sp[0] == '+')
+ break;
+ parsent(*(sp++));
+ }
+ setdt(dt);
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-d div] [file]\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int n;
+ char *s;
+ Biobuf *bf;
+
+ div = 96*5;
+ ARGBEGIN{
+ default: usage();
+ case 'd': div = strtol(EARGF(usage()), nil, 0); break;
+ }ARGEND
+ bf = *argv != nil ? Bopen(*argv, OREAD) : Bfdopen(0, OREAD);
+ if(bf == nil)
+ sysfatal("init: %r");
+ outb = mallocz(Nbuf, 1);
+ if(outb == nil)
+ sysfatal("mallocz: %r");
+ oute = outb + Nbuf;
+ memcpy(outb, hdr, sizeof hdr);
+ outp = outb + sizeof hdr;
+ setbpm(1, (arg[0] = "120", arg)); /* FIXME: >:( */
+ for(;;){
+ s = Brdstr(bf, '\n', 1);
+ if(s == nil)
+ break;
+ n = getfields(s, arg, nelem(arg), 1, " \t");
+ parse(n);
+ free(s);
+ }
+ Bterm(bf);
+ barf();
+ exits(nil);
+}
--- /dev/null
+++ b/tomst.c
@@ -1,0 +1,239 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+struct Tracker {
+ uchar *data;
+ char ended;
+ uvlong t;
+ int cmd;
+} *tr;
+
+typedef struct Tracker Tracker;
+
+int fd, tempo = 500000, ntrack;
+Biobuf *bf;
+uvlong T;
+
+char *ntab[] = {
+ "c0","c♯0","d0","d♯0","e0","f0","f♯0","g0","g♯0","a0","a♯0","b0",
+ "c1","c♯1","d1","d♯1","e1","f1","f♯1","g1","g♯1","a1","a♯1","b1",
+ "c2","c♯2","d2","d♯2","e2","f2","f♯2","g2","g♯2","a2","a♯2","b2",
+ "c3","c♯3","d3","d♯3","e3","f3","f♯3","g3","g♯3","a3","a♯3","b3",
+ "c4","c♯4","d4","d♯4","e4","f4","f♯4","g4","g♯4","a4","a♯4","b4",
+ "c5","c♯5","d5","d♯5","e5","f5","f♯5","g5","g♯5","a5","a♯5","b5",
+ "c6","c♯6","d6","d♯6","e6","f6","f♯6","g6","g♯6","a6","a♯6","b6",
+ "c7","c♯7","d7","d♯7","e7","f7","f♯7","g7","g♯7","a7","a♯7","b7",
+ "c8","c♯8","d8","d♯8","e8","f8","f♯8","g8","g♯8","a8","a♯8","b8",
+ "c9","c♯9","d9","d♯9","e9","f9","f♯9","g9","g♯9","a9","a♯9","b9",
+ "c10","c♯10","d10","d♯10","e10","f10","f♯10","g10"
+};
+char nts[512], *ntp = nts;
+
+void *
+emallocz(int size)
+{
+ void *v;
+
+ v = malloc(size);
+ if(v == nil)
+ sysfatal("malloc: %r");
+ memset(v, 0, size);
+ return v;
+}
+
+int
+get8(Tracker *src)
+{
+ uchar c;
+
+ if(src == nil){
+ if(read(fd, &c, 1) == 0)
+ sysfatal("unexpected eof");
+ return c;
+ }
+ return *src->data++;
+}
+
+int
+get16(Tracker *src)
+{
+ int x;
+
+ x = get8(src) << 8;
+ return x | get8(src);
+}
+
+int
+get32(Tracker *src)
+{
+ int x;
+ x = get16(src) << 16;
+ return x | get16(src);
+}
+
+int
+getvar(Tracker *src)
+{
+ int k, x;
+
+ x = get8(src);
+ k = x & 0x7F;
+ while(x & 0x80){
+ k <<= 7;
+ x = get8(src);
+ k |= x & 0x7F;
+ }
+ return k;
+}
+
+int
+peekvar(Tracker *src)
+{
+ uchar *p;
+ int v;
+
+ p = src->data;
+ v = getvar(src);
+ src->data = p;
+ return v;
+}
+
+void
+skip(Tracker *src, int x)
+{
+ if(x) do
+ get8(src);
+ while(--x);
+}
+
+void
+paste(int dt)
+{
+ /* FIXME: attempt to use note duration instead of clocks */
+ Bprint(bf, "c%d %s\n", dt, ntp!=nts?nts:"+");
+}
+
+void
+readevent(Tracker *src, int dt)
+{
+ int n, v, t;
+
+ if(dt != 0){
+ paste(dt);
+ ntp = nts;
+ }
+ src->t += getvar(src);
+ t = get8(src);
+ if((t & 0x80) == 0){
+ src->data--;
+ t = src->cmd;
+ if((t & 0x80) == 0)
+ sysfatal("invalid midi");
+ }else
+ src->cmd = t;
+ n = t >> 4;
+ switch(n){
+ case 0x8:
+ n = get8(src);
+ get8(src);
+ off:
+ ntp = seprint(ntp, nts + sizeof nts, " %d%s,0", t & 15, ntab[n]);
+ break;
+ case 0x9:
+ n = get8(src);
+ v = get8(src);
+ if(v == 0)
+ goto off;
+ ntp = seprint(ntp, nts + sizeof nts, " %d%s", t & 15, ntab[n]);
+ if(v != 64)
+ ntp = seprint(ntp, nts + sizeof nts, ",%d", v);
+ ntp = strecpy(ntp, nts + sizeof nts, "-");
+ break;
+ case 0xB:
+ get16(src);
+ break;
+ case 0xC:
+ get8(src);
+ break;
+ case 0xE:
+ get16(src);
+ break;
+ case 0xF:
+ if((t & 0xF) == 0){
+ while(get8(src) != 0xF7)
+ ;
+ return;
+ }
+ v = get8(src);
+ n = get8(src);
+ switch(v){
+ case 0x2F:
+ src->ended = 1;
+ break;
+ case 0x51:
+ v = get16(src) << 8;
+ v |= get8(src);
+ if(v != tempo){
+ Bprint(bf, "t %d\n", 60000000/v);
+ tempo = v;
+ }
+ break;
+ default:
+ skip(src, n);
+ }
+ break;
+ default:
+ sysfatal("unknown event type %x", t>>4);
+ }
+}
+
+void
+main(int argc, char **argv)
+{
+ int i, size;
+ uvlong T, t, mint, dt;
+ Tracker *x, *minx;
+
+ if(argc > 1)
+ fd = open(argv[1], OREAD);
+ if(fd < 0)
+ sysfatal("open: %r");
+ bf = Bfdopen(1, OWRITE);
+ if(bf == nil)
+ sysfatal("Bfdopen: %r");
+ if(get32(nil) != 0x4D546864 || get32(nil) != 6)
+ sysfatal("invalid file header");
+ i = get16(nil);
+ if(i != 0 && i != 1)
+ sysfatal("unsupported midi format %d\n", i);
+ ntrack = get16(nil);
+ Bprint(bf, "div %d\n", get16(nil));
+ tr = emallocz(ntrack * sizeof(*tr));
+ for(i = 0; i < ntrack; i++){
+ if(get32(nil) != 0x4D54726B)
+ sysfatal("invalid track header");
+ size = get32(nil);
+ tr[i].data = emallocz(size);
+ readn(fd, tr[i].data, size);
+ }
+ T = 0;
+ for(;;){
+ minx = nil;
+ mint = 0;
+ for(x = tr; x < tr + ntrack; x++){
+ if(x->ended)
+ continue;
+ t = peekvar(x) + x->t;
+ if(t < mint || minx == nil){
+ mint = t;
+ minx = x;
+ }
+ }
+ if(minx == nil)
+ exits(nil);
+ dt = mint - T;
+ readevent(minx, dt);
+ T += dt;
+ }
+}