From 3b9b7a6ab8b6e8c5eb37937b849d1e9f6106a386 Mon Sep 17 00:00:00 2001 From: aiju Date: Sat, 14 Apr 2012 14:44:15 +0200 Subject: [PATCH] added midi player --- sys/src/games/midi.c | 250 +++++++++++++++++++++++++++++++++++++++++++ sys/src/games/mkfile | 1 + 2 files changed, 251 insertions(+) create mode 100644 sys/src/games/midi.c diff --git a/sys/src/games/midi.c b/sys/src/games/midi.c new file mode 100644 index 000000000..0fc9944c9 --- /dev/null +++ b/sys/src/games/midi.c @@ -0,0 +1,250 @@ +#include +#include + +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]; + +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, l, no[128]; + uchar *s; + 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]; + } + s = emallocz(samp * 4); + for(l = 0; l < samp; l++){ + t = 0; + for(k = 0; k < 128; k++){ + f = (T % freq[k]) >= freq[k]/2 ? 1 : 0; + t += f * no[k]; + } + u = t*10; + s[4 * l] = s[4 * l + 2] = u; + s[4 * l + 1] = s[4 * l + 3] = u >> 8; + T++; + } + write(ofd, s, samp * 4); + free(s); +} + +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 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(1, src->data, n); + skip(src, n); + break; + default: + print("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; + + if(argc != 2) + sysfatal("invalid arguments"); + fd = open(argv[1], OREAD); + if(fd < 0) + sysfatal("open: %r"); + ofd = open("/dev/audio", OWRITE); + if(ofd < 0) + sysfatal("ofd: %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); + read(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) + exits(nil); + readevent(minx); + } +} diff --git a/sys/src/games/mkfile b/sys/src/games/mkfile index 6a8f32f9e..f17cd645c 100644 --- a/sys/src/games/mkfile +++ b/sys/src/games/mkfile @@ -12,6 +12,7 @@ TARG=4s\ glendy\ packet\ mandel\ + midi\ OFILES= HFILES= -- 2.44.0