diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2017-02-26 19:23:52 +0100 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2017-02-26 19:23:52 +0100 |
commit | 297bf25f612d2dde0d28d7feaae0ea381be114a7 (patch) | |
tree | a2c8f5a5cba86e696f98b024cabecb2602fca16e | |
parent | 009bec07521287ebfc9f4dcfddac18d8f27f1fb9 (diff) |
add games/mus midi converter (by qu7uux)
-rw-r--r-- | sys/man/1/mus | 25 | ||||
-rw-r--r-- | sys/src/games/mkfile | 1 | ||||
-rw-r--r-- | sys/src/games/mus.c | 267 |
3 files changed, 293 insertions, 0 deletions
diff --git a/sys/man/1/mus b/sys/man/1/mus new file mode 100644 index 000000000..31ec0fa27 --- /dev/null +++ b/sys/man/1/mus @@ -0,0 +1,25 @@ +.TH MUS 1 +.SH NAME +mus \- MUS to MIDI converter +.SH SYNOPSIS +.B games/mus +[ +.I musfile +] +.SH DESCRIPTION +The MUS format is a simplified MIDI music format used in +.IR doom (1) +and several related games. +.PP +.I Mus +decodes MIDI music encoded in MUS format, either from +.B musfile +or from standard input, and produces a MIDI format file on standard output. +.SH "SEE ALSO" +.IR doom (1) , +.IR midi (1) +.SH SOURCE +.B /sys/src/games/mus.c +.SH HISTORY +.I Mus +appeared first for 9front (September, 2015). diff --git a/sys/src/games/mkfile b/sys/src/games/mkfile index e1c470201..039d5cd36 100644 --- a/sys/src/games/mkfile +++ b/sys/src/games/mkfile @@ -10,6 +10,7 @@ TARG=4s\ life\ memo\ mole\ + mus\ glendy\ packet\ mandel\ diff --git a/sys/src/games/mus.c b/sys/src/games/mus.c new file mode 100644 index 000000000..703207b01 --- /dev/null +++ b/sys/src/games/mus.c @@ -0,0 +1,267 @@ +#include <u.h> +#include <libc.h> + +typedef struct Trk Trk; +typedef struct Ch Ch; + +struct Ch{ + uchar n[128]; + uchar pitch; + uchar ctl[10]; +}; +struct Trk{ + u32int len; + uchar *dat; + uchar *p; + uchar *end; + Ch c[16]; + long delay; + int done; +}; +Trk t; +uchar *mcmd, *mp, *me; +int fd; + +#define PBIT16(p,v) (p)[0]=(v);(p)[1]=(v)>>8 +#define BBIT32(p,v) (p)[3]=(v);(p)[2]=(v)>>8;(p)[1]=(v)>>16;(p)[0]=(v)>>24 + +void +eread(int fd, void *u, long n) +{ + if(readn(fd, u, n) != n) + sysfatal("readn: %r"); +} + +uchar +r8(void) +{ + return *t.p++; +} + +u32int +delay(void) +{ + u32int t; + uchar v; + + t = 0; + do{ + v = r8(); + t = t << 7 | v & 0x7f; + *mp++ = v; + }while(v & 0x80); + return t; +} + +void +putcmd(uchar *cmd, int n) +{ + if(mp + n >= me){ + me += 8192; + mcmd = realloc(mcmd, me - mcmd); + if(mcmd == nil) + sysfatal("realloc: %r"); + } + memcpy(mp, cmd, n); + mp += n; +} + +void +ev(void) +{ + uchar e, v; + Ch *c; + uchar cmd[3], *p; + + e = r8(); + c = &t.c[e & 15]; + p = cmd; + switch(e >> 4 & 7){ + case 0: + v = r8() & 0x7f; + c->n[v] = 0; + *p++ = e | 0x80; + *p++ = v; + *p++ = 0x40; + break; + case 1: + v = r8(); + if(v & 0x80){ + c->n[v] = r8() & 0x7f; + c->ctl[3] = c->n[v]; + }else + c->n[v] = c->ctl[3]; + *p++ = e | 0x80; + *p++ = v & 0x7f; + *p++ = c->n[v]; + break; + case 2: + c->pitch = r8(); + *p++ = e | 0xc0; + PBIT16(p, c->pitch << 7 & 0x7f7f); + p += 2; + break; + case 3: + v = r8(); + *p++ = 0xb | e & 15; + switch(v){ + case 10: + for(v=0; v<128; v++) + c->n[v] = 0; + *p++ = 0x78; + break; + case 11: + for(v=0; v<128; v++) + c->n[v] = 0; + *p++ = 0x7b; + break; + case 12: + *p++ = 0x7e; + break; + case 13: + *p++ = 0x7f; + break; + case 14: + memset(c->n, 0, sizeof c->n); + memset(c->ctl, 0, sizeof c->ctl); + c->ctl[3] = 0x7f; + c->ctl[4] = 64; + if((e & 15) == 15) + c->pitch = 60; + else + c->pitch = 128; + *p++ = 0x79; + break; + default: + sysfatal("unknown system event %ux\n", v); + } + *p++ = 0; + break; + case 4: + v = r8(); + if(v > 9) + sysfatal("unknown controller %ux\n", v); + c->ctl[v] = r8() & 0x7f; + *p++ = 0xb0 | e & 15; + switch(v){ + case 1: *p++ = 0x00; break; + case 2: *p++ = 0x01; break; + case 3: *p++ = 0x07; break; + case 4: *p++ = 0x0a; break; + case 5: *p++ = 0x0b; break; + case 6: *p++ = 0x5b; break; + case 7: *p++ = 0x5d; break; + case 8: *p++ = 0x40; break; + case 9: *p++ = 0x43; break; + } + *p++ = c->ctl[v]; + if(v == 0) + cmd[0] += 0x10; + break; + case 6: + *p++ = 0xff; + *p++ = 0x2f; + e = 0; + t.done++; + break; + default: + sysfatal("unknown event %ux\n", e >> 4 & 7); + } + if((e & 15) == 9) + cmd[0] += 1; + if((e & 15) == 15) + cmd[0] &= ~6; + putcmd(cmd, p-cmd); + t.delay = 0; + if(e & 0x80) + t.delay = delay(); + else + *mp++ = 0; +} + +void +reset(void) +{ + Ch *c; + + c = t.c; + while(c < t.c + nelem(t.c)){ + c->pitch = 128; + c->ctl[3] = 0x7f; + c->ctl[4] = 64; + c++; + } + t.c[15].pitch = 60; + mcmd = mallocz(t.len * 2, 1); + if(mcmd == nil) + sysfatal("mallocz: %r"); + mp = mcmd; + me = mcmd + t.len * 2; +} + +void +barf(void) +{ + static uchar hdr[] = { + 'M', 'T', 'h', 'd', + 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, + 0x00, 0x01, + 0x01, 0x01, + 'M', 'T', 'r', 'k', + 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb0, 0x07, 0x7f, + 0x00, 0xb1, 0x07, 0x7f, + 0x00, 0xb2, 0x07, 0x7f, + 0x00, 0xb3, 0x07, 0x7f, + 0x00, 0xb4, 0x07, 0x7f, + 0x00, 0xb5, 0x07, 0x7f, + 0x00, 0xb6, 0x07, 0x7f, + 0x00, 0xb7, 0x07, 0x7f, + 0x00, 0xb8, 0x07, 0x7f, + 0x00, 0xb9, 0x07, 0x7f, + 0x00, 0xba, 0x07, 0x7f, + 0x00, 0xff, 0x51, 0x03, 0x1b, 0x8a, 0x06, + 0x00 + }; + int n; + + n = sizeof(hdr) - 22 + mp - mcmd; + BBIT32(hdr + 18, n); + write(1, hdr, sizeof hdr); + write(1, mcmd, mp - mcmd); +} + +void +main(int argc, char *argv[]) +{ + int n, ofs; + uchar s[8], b[1024]; + + if(argc > 1){ + fd = open(argv[1], OREAD); + if(fd < 0) + sysfatal("open: %r"); + } + eread(fd, s, sizeof s); + if(memcmp(s, "MUS\x1a", 4) != 0) + sysfatal("invalid mus file: %r"); + t.len = s[5] << 8 | s[4]; + ofs = (s[7] << 8 | s[6]) - 8; + while(ofs > 0){ + n = ofs > sizeof b ? sizeof b : ofs; + eread(fd, b, n); + ofs -= n; + } + t.dat = malloc(t.len); + if(t.dat == nil) + sysfatal("malloc: %r"); + t.p = t.dat; + t.end = t.dat + t.len; + eread(fd, t.dat, t.len); + reset(); + while(!t.done && t.p < t.end) + ev(); + barf(); + exits(nil); +} |