diff options
author | qwx <devnull@localhost> | 2018-07-12 09:33:33 +0200 |
---|---|---|
committer | qwx <devnull@localhost> | 2018-07-12 09:33:33 +0200 |
commit | 96e511d736ca4f18b70312980591014199afeb1a (patch) | |
tree | 97b3b17b6af03dc90317ba6402c0c2d9f7c555cd /sys/src/games/dmid.c | |
parent | 1e3790f7b5a7d5a01d63afe3956b36fa2d11ed0c (diff) |
add games/dmid and games/opl3
Diffstat (limited to 'sys/src/games/dmid.c')
-rw-r--r-- | sys/src/games/dmid.c | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/sys/src/games/dmid.c b/sys/src/games/dmid.c new file mode 100644 index 000000000..7778e19c0 --- /dev/null +++ b/sys/src/games/dmid.c @@ -0,0 +1,554 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +typedef struct Inst Inst; +typedef struct Opl Opl; +typedef struct Chan Chan; +typedef struct Trk Trk; +enum{ + Rate = 44100, + Ninst = 128 + 81-35+1, + + Rwse = 0x01, + Mwse = 1<<5, /* wave selection enable */ + Rctl = 0x20, + Rsca = 0x40, + Mlvl = 63<<0, /* total level */ + Mscl = 3<<6, /* scaling level */ + Ratk = 0x60, + Rsus = 0x80, + Rnum = 0xa0, /* f number lsb */ + Roct = 0xb0, + Mmsb = 3<<0, /* f number msb */ + Moct = 7<<2, + Mkon = 1<<5, + Rfed = 0xc0, + Rwav = 0xe0, + Rop3 = 0x105, +}; + +struct Inst{ + int fixed; + int dbl; + uchar fine; + uchar n; + uchar i[13]; + uchar i2[13]; + s16int base[2]; +}; +Inst inst[Ninst]; + +struct Opl{ + Chan *c; + int n; + int midn; + int blk; + int v; + vlong t; + uchar *i; +}; +Opl opl[18], *ople = opl + nelem(opl); +int port[] = { + 0x0, 0x1, 0x2, 0x8, 0x9, 0xa, 0x10, 0x11, 0x12, + 0x100, 0x101, 0x102, 0x108, 0x109, 0x10a, 0x110, 0x111, 0x112 +}; +int sport[] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, + 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108 +}; +uchar ovol[] = { + 0, 32, 48, 58, 64, 70, 74, 77, 80, 83, 86, 88, 90, 92, 93, 95, 96, + 98, 99, 100, 102, 103, 104, 105, 106, 107, 108, 108, 109, 110, 111, + 112, 112, 113, 114, 114, 115, 116, 116, 117, 118, 118, 119, 119, + 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 124, 125, 125, + 126, 126, 126, 127, 127, 127, 128, 128 +}; + +struct Chan{ + Inst *i; + int v; + int bend; + int pan; +}; +Chan chan[16]; +struct Trk{ + u8int *s; + u8int *p; + u8int *e; + uvlong t; + int ev; +}; +Trk *tr; + +double freq[128]; +int mfmt, ntrk, div, tempo, opl2; +uvlong T; +Biobuf *ib, *ob; + +void * +emalloc(ulong n) +{ + void *p; + + p = mallocz(n, 1); + if(p == nil) + sysfatal("mallocz: %r"); + setmalloctag(p, getcallerpc(&n)); + return p; +} + +Biobuf * +bfdopen(int fd, int mode) +{ + Biobuf *bf; + + bf = Bfdopen(fd, mode); + if(bf == nil) + sysfatal("bfdopen: %r"); + Blethal(bf, nil); + return bf; +} + +Biobuf * +bopen(char *file, int mode) +{ + int fd; + + fd = open(file, mode); + if(fd < 0) + sysfatal("bopen: %r"); + return bfdopen(fd, mode); +} + +void +bread(void *u, int n) +{ + if(Bread(ib, u, n) != n) + sysfatal("bread: short read"); +} + +u8int +get8(Trk *x) +{ + u8int v; + + if(x == nil){ + Bread(ib, &v, 1); + return v; + } + if(x->p >= x->e) + sysfatal("track overflow"); + return *x->p++; +} + +u16int +get16(Trk *x) +{ + u16int v; + + v = get8(x) << 8; + return v | get8(x); +} + +u32int +get32(Trk *x) +{ + u32int v; + + v = get16(x) << 16; + return v | get16(x); +} + +void +putcmd(u16int r, u8int v, u16int dt) +{ + uchar *p, u[5]; + + p = u; + *p++ = r; + if(!opl2) + *p++ = r >> 8; + *p++ = v; + *p++ = dt; + *p++ = dt >> 8; + Bwrite(ob, u, p-u); +} + +void +setinst(Opl *o, uchar *i) +{ + int p; + + p = sport[o - opl]; + putcmd(Roct+p, o->blk, 0); + putcmd(Rfed+p, i[6] & ~0x30 | o->c->pan, 0); + p = port[o - opl]; + putcmd(Rctl+p, i[0], 0); + putcmd(Ratk+p, i[1], 0); + putcmd(Rsus+p, i[2], 0); + putcmd(Rwav+p, i[3] & 3, 0); + putcmd(Rctl+3+p, i[7], 0); + putcmd(Ratk+3+p, i[8], 0); + putcmd(Rsus+3+p, i[9], 0); + putcmd(Rwav+3+p, i[10] & 3, 0); + o->i = i; +} + +void +noteoff(Chan *c, int n, int) +{ + Opl *o; + + for(o=opl; o<ople; o++) + if(o->c == c && o->midn == n){ + putcmd(Roct+sport[o-opl], o->blk, 0); + o->n = -1; + } +} + +Opl * +getch(void) +{ + Opl *o, *p; + + p = opl; + for(o=opl; o<ople; o++){ + if(o->n < 0) + return o; + if(o->t < p->t) + p = o; + } + return p; +} + +void +setoct(Opl *o) +{ + int n, b, f; + + n = o->n + o->c->bend / 0x1000 & 0x7f; + f = freq[n] + (o->c->bend % 0x1000) * (freq[n+1] - freq[n]) / 0x1000; + f = (f * (1 << 20)) / 49716; + //if(o->i == o->c->i->i2) + // f += o->c->i->fine; /* nope */ + for(b=1; b<8; b++, f>>=1) + if(f < 1024) + break; + o->blk = b << 2 & Moct | f >> 8 & Mmsb; + putcmd(Rnum+sport[o-opl], f & 0xff, 0); + putcmd(Roct+sport[o-opl], Mkon | o->blk, 0); +} + +void +setvol(Opl *o) +{ + int p, w, x; + + p = port[o - opl]; + w = o->v * o->c->v / 127; + w = ovol[w * 64 / 127] * 63 / 128; + x = 63 + (o->i[5] & Mlvl) * w / 63 - w; + putcmd(Rsca+p, o->i[4] & Mscl | x, 0); + x = 63 + (o->i[12] & Mlvl) * w / 63 - w; + putcmd(Rsca+p+3, o->i[11] & Mscl | x, 0); +} + +void +putnote(Chan *c, int midn, int n, int v, vlong t, uchar *i) +{ + Opl *o; + + o = getch(); + o->c = c; + o->n = n; + o->midn = midn; + o->v = v; + o->t = t; + if(o->i != i) + setinst(o, i); + setvol(o); + setoct(o); +} + +void +noteon(Chan *c, int n, int v, vlong t) +{ + int x, m; + + m = n; + if(c - chan == 9){ + /* asspull workaround for percussions above gm set */ + if(m == 85) + m = 37; + if(m == 82) + m = 44; + if(m < 35 || m > 81) + return; + c->i = inst + 128 + m - 35; + } + if(c->i->fixed) + m = c->i->n; + if(v == 0){ + noteoff(c, n, 0); + return; + } + x = m + (c->i->fixed ? 0 : c->i->base[0]); + while(x < 0) + x += 12; + while(x > 8*12-1) + x -= 12; + putnote(c, n, x & 0xff, v, t, c->i->i); + if(c->i->dbl){ + x = m + (c->i->fixed ? 0 : c->i->base[1]); + while(x < 0) + x += 12; + while(x > 95) + x -= 12; + putnote(c, n, x & 0xff, v, t, c->i->i2); + } +} + +void +resetchan(Chan *c) +{ + Opl *o; + + for(o=opl; o<ople; o++) + if(o->c == c && o->n >= 0){ + putcmd(Rfed+sport[o-opl], o->i[6] & ~0x30 | c->pan, 0); + setvol(o); + setoct(o); + } +} + +uvlong +tc(int n) +{ + return ((uvlong)n * tempo * Rate / div) / 1000000; +} + +void +skip(Trk *x, int n) +{ + while(n-- > 0) + get8(x); +} + +int +getvar(Trk *x) +{ + int v, w; + + w = get8(x); + v = w & 0x7f; + while(w & 0x80){ + if(v & 0xff000000) + sysfatal("invalid variable-length number"); + v <<= 7; + w = get8(x); + v |= w & 0x7f; + } + return v; +} + +int +peekvar(Trk *x) +{ + int v; + uchar *p; + + p = x->p; + v = getvar(x); + x->p = p; + return v; +} + +void +samp(uvlong t´) +{ + int dt; + static uvlong t; + + dt = t´ - t; + t += dt; + while(dt > 0){ + putcmd(0, 0, dt > 0xffff ? 0xffff : dt); + dt -= 0xffff; + } +} + +void +ev(Trk *x) +{ + int e, n, m; + Chan *c; + + samp(x->t += tc(getvar(x))); + e = get8(x); + if((e & 0x80) == 0){ + x->p--; + e = x->ev; + if((e & 0x80) == 0) + sysfatal("invalid event"); + }else + x->ev = e; + c = chan + (e & 15); + n = get8(x); + switch(e >> 4){ + case 0x8: noteoff(c, n, get8(x)); break; + case 0x9: noteon(c, n, get8(x), x->t); break; + case 0xb: + m = get8(x); + switch(n){ + case 0x00: case 0x01: case 0x20: break; + case 0x07: c->v = m; resetchan(c); break; + case 0x0a: c->pan = m < 32 ? 1<<4 : m > 96 ? 1<<5 : 3<<4; resetchan(c); break; + default: fprint(2, "unknown controller %d\n", n); + } + break; + case 0xc: c->i = inst + n; break; + case 0xe: + n = get8(x) << 7 | n; + c->bend = n - 0x4000 / 2; + resetchan(c); + break; + case 0xf: + if((e & 0xf) == 0){ + while(get8(x) != 0xf7) + ; + return; + } + m = get8(x); + switch(n){ + case 0x2f: x->p = x->e; return; + case 0x51: tempo = get16(x) << 8; tempo |= get8(x); break; + default: skip(x, m); + } + break; + case 0xa: + case 0xd: get8(x); break; + default: sysfatal("invalid event %#ux\n", e >> 4); + } +} + +void +readinst(char *file) +{ + int n; + uchar u[8]; + Inst *i; + + ib = bopen(file, OREAD); + bread(u, sizeof u); + if(memcmp(u, "#OPL_II#", sizeof u) != 0) + sysfatal("invalid patch file"); + for(i=inst; i<inst+nelem(inst); i++){ + n = get8(nil); + i->fixed = n & 1<<0; + i->dbl = opl2 ? 0 : n & 1<<2; + get8(nil); + i->fine = get8(nil) / 2 - 64; + i->n = get8(nil); + bread(i->i, sizeof i->i); + get8(nil); + n = get8(nil); + n |= get8(nil) << 8; + i->base[0] = (s16int)n; + bread(i->i2, sizeof i->i2); + get8(nil); + n = get8(nil); + n |= get8(nil) << 8; + i->base[1] = (s16int)n; + } + Bterm(ib); +} + +void +readmid(char *file) +{ + u32int n; + uchar *s; + Trk *x; + + ib = file != nil ? bopen(file, OREAD) : bfdopen(0, OREAD); + if(get32(nil) != 0x4d546864 || get32(nil) != 6) + sysfatal("invalid header"); + mfmt = get16(nil); + ntrk = get16(nil); + if(ntrk == 1) + mfmt = 0; + if(mfmt < 0 || mfmt > 1) + sysfatal("unsupported format %d", mfmt); + div = get16(nil); + tr = emalloc(ntrk * sizeof *tr); + for(x=tr; x<tr+ntrk; x++){ + if(get32(nil) != 0x4d54726b) + sysfatal("invalid track"); + n = get32(nil); + s = emalloc(n); + bread(s, n); + x->s = s; + x->p = s; + x->e = s + n; + } + Bterm(ib); +} + +void +usage(void) +{ + fprint(2, "usage: %s [-2] [-i inst] [mid]\n", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int n, t, mint; + char *i; + double f; + Chan *c; + Opl *o; + Trk *x, *minx; + + i = "/mnt/wad/genmidi"; + ARGBEGIN{ + case '2': opl2 = 1; ople = opl + 9; break; + case 'i': i = EARGF(usage()); break; + default: usage(); + }ARGEND + readinst(i); + readmid(*argv); + ob = bfdopen(1, OWRITE); + f = pow(2, 1./12); + for(n=0; n<nelem(freq); n++) + freq[n] = 440 * pow(f, n - 69); + for(c=chan; c<chan+nelem(chan); c++){ + c->v = 0x5a; + c->bend = 0; + c->pan = 3<<4; + c->i = inst; + } + for(o=opl; o<ople; o++) + o->n = -1; + tempo = 500000; + putcmd(Rwse, Mwse, 0); + putcmd(Rop3, 1, 0); + for(;;){ + minx = nil; + mint = 0; + for(x=tr; x<tr+ntrk; x++){ + if(x->p >= x->e) + continue; + t = x->t + tc(peekvar(x)); + if(t < mint || minx == nil){ + mint = t; + minx = x; + } + } + if(minx == nil) + exits(nil); + ev(minx); + } +} |