ref: 094fc8b15f98807833143e29f1f7d36a1ca4b28c
dir: /sys/src/games/midi.c/
#include <u.h> #include <libc.h> enum { SAMPLE = 44100 }; struct Tracker { uchar *data; char ended; uvlong t; uchar notes[16][128]; int cmd; } *tr; typedef struct Tracker Tracker; int fd, ofd, div, tempo = 500000, ntrack; uvlong T; int freq[128]; uchar out[8192], *outp = out; 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); } uvlong tconv(int n) { uvlong v; v = n; v *= tempo; v *= SAMPLE; v /= div; v /= 1000000; return v; } void run(uvlong n) { int samp, j, k, no[128]; int t, f; short u; Tracker *x; samp = n - T; if(samp <= 0) return; memset(no, 0, sizeof no); for(x = tr; x < tr + ntrack; x++){ if(x->ended) continue; for(j = 0; j < 16; j++) for(k = 0; k < 128; k++) no[k] += x->notes[j][k]; } while(samp--){ t = 0; for(k = 0; k < 128; k++){ f = (T % freq[k]) >= freq[k]/2 ? 1 : 0; t += f * no[k]; } u = t*10; outp[0] = outp[2] = u; outp[1] = outp[3] = u >> 8; outp += 4; if(outp == out + sizeof out){ 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); t = get8(src); if((t & 0x80) == 0){ src->data--; t = src->cmd; if((t & 0x80) == 0) sysfatal("invalid midi"); }else src->cmd = t; switch(t >> 4){ case 0x8: n = get8(src); get8(src); src->notes[t & 15][n] = 0; break; case 0x9: n = get8(src); src->notes[t & 15][n] = get8(src); break; case 0xB: get16(src); break; case 0xC: get8(src); break; case 0xE: get16(src); break; case 0xF: t = get8(src); n = get8(src); switch(t){ case 0x2F: src->ended = 1; break; case 0x51: tempo = get16(src) << 8; tempo |= get8(src); break; case 5: write(2, src->data, n); skip(src, n); break; default: fprint(2, "unknown meta event type %.2x\n", t); case 3: case 1: case 2: case 0x58: case 0x59: case 0x21: skip(src, n); } break; default: sysfatal("unknown event type %x", t>>4); } } void main(int argc, char **argv) { int i, size; uvlong t, mint; Tracker *x, *minx; ARGBEGIN{ case 'c': ofd = 1; break; }ARGEND; if(*argv != nil) fd = open(*argv, OREAD); if(ofd == 0) ofd = open("/dev/audio", OWRITE); if(fd < 0 || ofd < 0) sysfatal("open: %r"); if(get32(nil) != 0x4D546864 || get32(nil) != 6) sysfatal("invalid file header"); get16(nil); ntrack = get16(nil); div = 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); } 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++){ if(x->ended) continue; t = tconv(peekvar(x)) + x->t; if(t < mint || minx == nil){ mint = t; minx = x; } } if(minx == nil){ write(ofd, out, outp - out); exits(nil); } readevent(minx); } }