6 typedef struct Inst Inst;
7 typedef struct Opl Opl;
8 typedef struct Chan Chan;
9 typedef struct Trk Trk;
12 Ninst = 128 + 81-35+1,
15 Mwse = 1<<5, /* wave selection enable */
18 Mlvl = 63<<0, /* total level */
19 Mscl = 3<<6, /* scaling level */
22 Rnum = 0xa0, /* f number lsb */
24 Mmsb = 3<<0, /* f number msb */
52 Opl opl[18], *ople = opl + nelem(opl);
54 0x0, 0x1, 0x2, 0x8, 0x9, 0xa, 0x10, 0x11, 0x12,
55 0x100, 0x101, 0x102, 0x108, 0x109, 0x10a, 0x110, 0x111, 0x112
58 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
59 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108
62 0, 32, 48, 58, 64, 70, 74, 77, 80, 83, 86, 88, 90, 92, 93, 95, 96,
63 98, 99, 100, 102, 103, 104, 105, 106, 107, 108, 108, 109, 110, 111,
64 112, 112, 113, 114, 114, 115, 116, 116, 117, 118, 118, 119, 119,
65 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 124, 125, 125,
66 126, 126, 126, 127, 127, 127, 128, 128
86 int mfmt, ntrk, div = 1, tempo, opl2, stream;
98 sysfatal("mallocz: %r");
99 setmalloctag(p, getcallerpc(&n));
104 bfdopen(int fd, int mode)
108 bf = Bfdopen(fd, mode);
110 sysfatal("bfdopen: %r");
116 bopen(char *file, int mode)
120 fd = open(file, mode);
122 sysfatal("bopen: %r");
123 return bfdopen(fd, mode);
127 bread(void *u, int n)
129 if(Bread(ib, u, n) != n)
130 sysfatal("bread: short read");
143 sysfatal("track overflow");
166 putcmd(u16int r, u8int v, u16int dt)
181 setinst(Opl *o, uchar *i)
186 putcmd(Roct+p, o->blk, 0);
187 putcmd(Rfed+p, i[6] & ~0x30 | o->c->pan, 0);
189 putcmd(Rctl+p, i[0], 0);
190 putcmd(Ratk+p, i[1], 0);
191 putcmd(Rsus+p, i[2], 0);
192 putcmd(Rwav+p, i[3] & 3, 0);
193 putcmd(Rctl+3+p, i[7], 0);
194 putcmd(Ratk+3+p, i[8], 0);
195 putcmd(Rsus+3+p, i[9], 0);
196 putcmd(Rwav+3+p, i[10] & 3, 0);
201 noteoff(Chan *c, int n, int)
205 for(o=opl; o<ople; o++)
206 if(o->c == c && o->midn == n){
207 putcmd(Roct+sport[o-opl], o->blk, 0);
218 for(o=opl; o<ople; o++){
234 d += o->i == o->c->i->i2 ? o->c->i->fine : 0;
235 n = o->n + d / 0x1000 & 0x7f;
236 e = freq[n] + (d % 0x1000) * (freq[n+1] - freq[n]) / 0x1000;
239 f = (e * (1 << 20)) / 49716;
240 for(b=1; b<8; b++, f>>=1)
243 o->blk = b << 2 & Moct | f >> 8 & Mmsb;
244 putcmd(Rnum+sport[o-opl], f & 0xff, 0);
245 putcmd(Roct+sport[o-opl], Mkon | o->blk, 0);
254 w = o->v * o->c->v / 127;
255 w = ovol[w * 64 / 127] * 63 / 128;
256 x = 63 + (o->i[5] & Mlvl) * w / 63 - w;
257 putcmd(Rsca+p, o->i[4] & Mscl | x, 0);
258 x = 63 + (o->i[12] & Mlvl) * w / 63 - w;
259 putcmd(Rsca+p+3, o->i[11] & Mscl | x, 0);
263 putnote(Chan *c, int midn, int n, int v, vlong t, uchar *i)
280 noteon(Chan *c, int n, int v, vlong t)
286 /* asspull workaround for percussions above gm set */
293 c->i = inst + 128 + m - 35;
301 x = m + (c->i->fixed ? 0 : c->i->base[0]);
306 putnote(c, n, x & 0xff, v, t, c->i->i);
308 x = m + (c->i->fixed ? 0 : c->i->base[1]);
313 putnote(c, n, x & 0xff, v, t, c->i->i2);
322 for(o=opl; o<ople; o++)
323 if(o->c == c && o->n >= 0){
324 putcmd(Rfed+sport[o-opl], o->i[6] & ~0x30 | c->pan, 0);
333 return ((uvlong)n * tempo * Rate / div) / 1000000;
352 sysfatal("invalid variable-length number");
381 putcmd(0, 0, dt > 0xffff ? 0xffff : dt);
392 samp(x->t += tc(getvar(x)));
398 sysfatal("invalid event");
404 case 0x8: noteoff(c, n, get8(x)); break;
405 case 0x9: noteon(c, n, get8(x), x->t); break;
409 case 0x00: case 0x01: case 0x20: break;
410 case 0x07: c->v = m; resetchan(c); break;
411 case 0x0a: c->pan = m < 32 ? 1<<4 : m > 96 ? 1<<5 : 3<<4; resetchan(c); break;
412 default: fprint(2, "unknown controller %d\n", n);
415 case 0xc: c->i = inst + n; break;
417 n = get8(x) << 7 | n;
418 c->bend = n - 0x4000 / 2;
423 while(get8(x) != 0xf7)
429 case 0x2f: x->p = x->e; return;
430 case 0x51: tempo = get16(x) << 8; tempo |= get8(x); break;
435 case 0xd: get8(x); break;
436 default: sysfatal("invalid event %#ux\n", e >> 4);
450 if(nbrecv(echan, u) > 0){
456 t += 10000000 / (Rate / 100);
457 Δt = (t - nsec()) / 1000000;
470 ib = bopen(file, OREAD);
472 if(memcmp(u, "#OPL_II#", sizeof u) != 0)
473 sysfatal("invalid patch file");
474 for(i=inst; i<inst+nelem(inst); i++){
477 i->dbl = opl2 ? 0 : n & 1<<2;
479 i->fine = (get8(nil) - 128) * 64;
481 bread(i->i, sizeof i->i);
485 i->base[0] = (s16int)n;
486 bread(i->i2, sizeof i->i2);
490 i->base[1] = (s16int)n;
502 ib = file != nil ? bopen(file, OREAD) : bfdopen(0, OREAD);
505 if(get32(nil) != 0x4d546864 || get32(nil) != 6)
506 sysfatal("invalid header");
511 if(mfmt < 0 || mfmt > 1)
512 sysfatal("unsupported format %d", mfmt);
514 tr = emalloc(ntrk * sizeof *tr);
515 for(x=tr; x<tr+ntrk; x++){
516 if(get32(nil) != 0x4d54726b)
517 sysfatal("invalid track");
531 fprint(2, "usage: %s [-2s] [-i inst] [mid]\n", argv0);
536 threadmain(int argc, char **argv)
546 i = "/mnt/wad/genmidi";
548 case '2': opl2 = 1; ople = opl + 9; break;
549 case 'i': i = EARGF(usage()); break;
550 case 's': stream = 1; break;
555 ob = bfdopen(1, OWRITE);
557 for(n=0; n<nelem(freq); n++)
558 freq[n] = 440 * pow(f, n - 69);
559 for(c=chan; c<chan+nelem(chan); c++){
565 for(o=opl; o<ople; o++)
568 putcmd(Rwse, Mwse, 0);
571 if(proccreate(tproc, nil, mainstacksize) < 0)
572 sysfatal("proccreate: %r");
573 if((echan = chancreate(sizeof u, 0)) == nil)
574 sysfatal("chancreate: %r");
576 if((n = Bread(ib, u, sizeof u)) != sizeof u)
580 threadexitsall(n < 0 ? "read: %r" : nil);
585 for(x=tr; x<tr+ntrk; x++){
588 t = x->t + tc(peekvar(x));
589 if(t < mint || minx == nil){