]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/dmid.c
dmid: better fnumber calculation and fix fine tuning for 2nd voice
[plan9front.git] / sys / src / games / dmid.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4
5 typedef struct Inst Inst;
6 typedef struct Opl Opl;
7 typedef struct Chan Chan;
8 typedef struct Trk Trk;
9 enum{
10         Rate = 44100,
11         Ninst = 128 + 81-35+1,
12
13         Rwse = 0x01,
14                 Mwse = 1<<5,    /* wave selection enable */
15         Rctl = 0x20,
16         Rsca = 0x40,
17                 Mlvl = 63<<0,   /* total level */
18                 Mscl = 3<<6,    /* scaling level */
19         Ratk = 0x60,
20         Rsus = 0x80,
21         Rnum = 0xa0,            /* f number lsb */
22         Roct = 0xb0,
23                 Mmsb = 3<<0,    /* f number msb */
24                 Moct = 7<<2,
25                 Mkon = 1<<5,
26         Rfed = 0xc0,
27         Rwav = 0xe0,
28         Rop3 = 0x105,
29 };
30
31 struct Inst{
32         int fixed;
33         int dbl;
34         int fine;
35         uchar n;
36         uchar i[13];
37         uchar i2[13];
38         s16int base[2];
39 };
40 Inst inst[Ninst];
41
42 struct Opl{
43         Chan *c;
44         int n;
45         int midn;
46         int blk;
47         int v;
48         vlong t;
49         uchar *i;
50 };
51 Opl opl[18], *ople = opl + nelem(opl);
52 int port[] = {
53         0x0, 0x1, 0x2, 0x8, 0x9, 0xa, 0x10, 0x11, 0x12,
54         0x100, 0x101, 0x102, 0x108, 0x109, 0x10a, 0x110, 0x111, 0x112
55 };
56 int sport[] = {
57         0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
58         0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108
59 };
60 uchar ovol[] = {
61         0, 32, 48, 58, 64, 70, 74, 77, 80, 83, 86, 88, 90, 92, 93, 95, 96,
62         98, 99, 100, 102, 103, 104, 105, 106, 107, 108, 108, 109, 110, 111,
63         112, 112, 113, 114, 114, 115, 116, 116, 117, 118, 118, 119, 119,
64         120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 124, 125, 125,
65         126, 126, 126, 127, 127, 127, 128, 128
66 };
67
68 struct Chan{
69         Inst *i;
70         int v;
71         int bend;
72         int pan;
73 };
74 Chan chan[16];
75 struct Trk{
76         u8int *s;
77         u8int *p;
78         u8int *e;
79         uvlong t;
80         int ev;
81 };
82 Trk *tr;
83
84 double freq[128];
85 int mfmt, ntrk, div, tempo, opl2;
86 uvlong T;
87 Biobuf *ib, *ob;
88
89 void *
90 emalloc(ulong n)
91 {
92         void *p;
93
94         p = mallocz(n, 1);
95         if(p == nil)
96                 sysfatal("mallocz: %r");
97         setmalloctag(p, getcallerpc(&n));
98         return p;
99 }
100
101 Biobuf *
102 bfdopen(int fd, int mode)
103 {
104         Biobuf *bf;
105
106         bf = Bfdopen(fd, mode);
107         if(bf == nil)
108                 sysfatal("bfdopen: %r");
109         Blethal(bf, nil);
110         return bf;
111 }
112
113 Biobuf *
114 bopen(char *file, int mode)
115 {
116         int fd;
117
118         fd = open(file, mode);
119         if(fd < 0)
120                 sysfatal("bopen: %r");
121         return bfdopen(fd, mode);
122 }
123
124 void
125 bread(void *u, int n)
126 {
127         if(Bread(ib, u, n) != n)
128                 sysfatal("bread: short read");
129 }
130
131 u8int
132 get8(Trk *x)
133 {
134         u8int v;
135
136         if(x == nil){
137                 Bread(ib, &v, 1);
138                 return v;
139         }
140         if(x->p >= x->e)
141                 sysfatal("track overflow");
142         return *x->p++;
143 }
144
145 u16int
146 get16(Trk *x)
147 {
148         u16int v;
149
150         v = get8(x) << 8;
151         return v | get8(x);
152 }
153
154 u32int
155 get32(Trk *x)
156 {
157         u32int v;
158
159         v = get16(x) << 16;
160         return v | get16(x);
161 }
162
163 void
164 putcmd(u16int r, u8int v, u16int dt)
165 {
166         uchar *p, u[5];
167
168         p = u;
169         *p++ = r;
170         if(!opl2)
171                 *p++ = r >> 8;
172         *p++ = v;
173         *p++ = dt;
174         *p++ = dt >> 8;
175         Bwrite(ob, u, p-u);
176 }
177
178 void
179 setinst(Opl *o, uchar *i)
180 {
181         int p;
182
183         p = sport[o - opl];
184         putcmd(Roct+p, o->blk, 0);
185         putcmd(Rfed+p, i[6] & ~0x30 | o->c->pan, 0);
186         p = port[o - opl];
187         putcmd(Rctl+p, i[0], 0);
188         putcmd(Ratk+p, i[1], 0);
189         putcmd(Rsus+p, i[2], 0);
190         putcmd(Rwav+p, i[3] & 3, 0);
191         putcmd(Rctl+3+p, i[7], 0);
192         putcmd(Ratk+3+p, i[8], 0);
193         putcmd(Rsus+3+p, i[9], 0);
194         putcmd(Rwav+3+p, i[10] & 3, 0);
195         o->i = i;
196 }
197
198 void
199 noteoff(Chan *c, int n, int)
200 {
201         Opl *o;
202
203         for(o=opl; o<ople; o++)
204                 if(o->c == c && o->midn == n){
205                         putcmd(Roct+sport[o-opl], o->blk, 0);
206                         o->n = -1;
207                 }
208 }
209
210 Opl *
211 getch(void)
212 {
213         Opl *o, *p;
214
215         p = opl;
216         for(o=opl; o<ople; o++){
217                 if(o->n < 0)
218                         return o;
219                 if(o->t < p->t)
220                         p = o;
221         }
222         return p;
223 }
224
225 void
226 setoct(Opl *o)
227 {
228         int n, b, f, d;
229         double e;
230
231         d = o->c->bend;
232         d += o->i == o->c->i->i2 ? o->c->i->fine : 0;
233         n = o->n + d / 0x1000 & 0x7f;
234         e = freq[n] + (d % 0x1000) * (freq[n+1] - freq[n]) / 0x1000;
235         if(o->c->i->fixed)
236                 e = (double)(int)e;
237         f = (e * (1 << 20)) / 49716;
238         for(b=1; b<8; b++, f>>=1)
239                 if(f < 1024)
240                         break;
241         o->blk = b << 2 & Moct | f >> 8 & Mmsb;
242         putcmd(Rnum+sport[o-opl], f & 0xff, 0);
243         putcmd(Roct+sport[o-opl], Mkon | o->blk, 0);
244 }
245
246 void
247 setvol(Opl *o)
248 {
249         int p, w, x;
250
251         p = port[o - opl];
252         w = o->v * o->c->v / 127;
253         w = ovol[w * 64 / 127] * 63 / 128;
254         x = 63 + (o->i[5] & Mlvl) * w / 63 - w;
255         putcmd(Rsca+p, o->i[4] & Mscl | x, 0);
256         x = 63 + (o->i[12] & Mlvl) * w / 63 - w;
257         putcmd(Rsca+p+3, o->i[11] & Mscl | x, 0);
258 }
259
260 void
261 putnote(Chan *c, int midn, int n, int v, vlong t, uchar *i)
262 {
263         Opl *o;
264
265         o = getch();
266         o->c = c;
267         o->n = n;
268         o->midn = midn;
269         o->v = v;
270         o->t = t;
271         if(o->i != i)
272                 setinst(o, i);
273         setvol(o);
274         setoct(o);
275 }
276
277 void
278 noteon(Chan *c, int n, int v, vlong t)
279 {
280         int x, m;
281
282         m = n;
283         if(c - chan == 9){
284                 /* asspull workaround for percussions above gm set */
285                 if(m == 85)
286                         m = 37;
287                 if(m == 82)
288                         m = 44;
289                 if(m < 35 || m > 81)
290                         return;
291                 c->i = inst + 128 + m - 35;
292         }
293         if(c->i->fixed)
294                 m = c->i->n;
295         if(v == 0){
296                 noteoff(c, n, 0);
297                 return;
298         }
299         x = m + (c->i->fixed ? 0 : c->i->base[0]);
300         while(x < 0)
301                 x += 12;
302         while(x > 8*12-1)
303                 x -= 12;
304         putnote(c, n, x & 0xff, v, t, c->i->i);
305         if(c->i->dbl){
306                 x = m + (c->i->fixed ? 0 : c->i->base[1]);
307                 while(x < 0)
308                         x += 12;
309                 while(x > 95)
310                         x -= 12;
311                 putnote(c, n, x & 0xff, v, t, c->i->i2);
312         }
313 }
314
315 void
316 resetchan(Chan *c)
317 {
318         Opl *o;
319
320         for(o=opl; o<ople; o++)
321                 if(o->c == c && o->n >= 0){
322                         putcmd(Rfed+sport[o-opl], o->i[6] & ~0x30 | c->pan, 0);
323                         setvol(o);
324                         setoct(o);
325                 }
326 }
327
328 uvlong
329 tc(int n)
330 {
331         return ((uvlong)n * tempo * Rate / div) / 1000000;
332 }
333
334 void
335 skip(Trk *x, int n)
336 {
337         while(n-- > 0)
338                 get8(x);
339 }
340
341 int
342 getvar(Trk *x)
343 {
344         int v, w;
345
346         w = get8(x);
347         v = w & 0x7f;
348         while(w & 0x80){
349                 if(v & 0xff000000)
350                         sysfatal("invalid variable-length number");
351                 v <<= 7;
352                 w = get8(x);
353                 v |= w & 0x7f;
354         }
355         return v;
356 }
357
358 int
359 peekvar(Trk *x)
360 {
361         int v;
362         uchar *p;
363
364         p = x->p;
365         v = getvar(x);
366         x->p = p;
367         return v;
368 }
369
370 void
371 samp(uvlong t´)
372 {
373         int dt;
374         static uvlong t;
375
376         dt = t´ - t;
377         t += dt;
378         while(dt > 0){
379                 putcmd(0, 0, dt > 0xffff ? 0xffff : dt);
380                 dt -= 0xffff;
381         }
382 }
383
384 void
385 ev(Trk *x)
386 {
387         int e, n, m;
388         Chan *c;
389
390         samp(x->t += tc(getvar(x)));
391         e = get8(x);
392         if((e & 0x80) == 0){
393                 x->p--;
394                 e = x->ev;
395                 if((e & 0x80) == 0)
396                         sysfatal("invalid event");
397         }else
398                 x->ev = e;
399         c = chan + (e & 15);
400         n = get8(x);
401         switch(e >> 4){
402         case 0x8: noteoff(c, n, get8(x)); break;
403         case 0x9: noteon(c, n, get8(x), x->t); break;
404         case 0xb:
405                 m = get8(x);
406                 switch(n){
407                 case 0x00: case 0x01: case 0x20: break;
408                 case 0x07: c->v = m; resetchan(c); break;
409                 case 0x0a: c->pan = m < 32 ? 1<<4 : m > 96 ? 1<<5 : 3<<4; resetchan(c); break;
410                 default: fprint(2, "unknown controller %d\n", n);
411                 }
412                 break;
413         case 0xc: c->i = inst + n; break;
414         case 0xe:
415                 n = get8(x) << 7 | n;
416                 c->bend = n - 0x4000 / 2;
417                 resetchan(c);
418                 break;
419         case 0xf:
420                 if((e & 0xf) == 0){
421                         while(get8(x) != 0xf7)
422                                 ;
423                         return;
424                 }
425                 m = get8(x);
426                 switch(n){
427                 case 0x2f: x->p = x->e; return;
428                 case 0x51: tempo = get16(x) << 8; tempo |= get8(x); break;
429                 default: skip(x, m);
430                 }
431                 break;
432         case 0xa:
433         case 0xd: get8(x); break;
434         default: sysfatal("invalid event %#ux\n", e >> 4);
435         }
436 }
437
438 void
439 readinst(char *file)
440 {
441         int n;
442         uchar u[8];
443         Inst *i;
444
445         ib = bopen(file, OREAD);
446         bread(u, sizeof u);
447         if(memcmp(u, "#OPL_II#", sizeof u) != 0)
448                 sysfatal("invalid patch file");
449         for(i=inst; i<inst+nelem(inst); i++){
450                 n = get8(nil);
451                 i->fixed = n & 1<<0;
452                 i->dbl = opl2 ? 0 : n & 1<<2;
453                 get8(nil);
454                 i->fine = (get8(nil) - 128) * 64;
455                 i->n = get8(nil);
456                 bread(i->i, sizeof i->i);
457                 get8(nil);
458                 n = get8(nil);
459                 n |= get8(nil) << 8;
460                 i->base[0] = (s16int)n;
461                 bread(i->i2, sizeof i->i2);
462                 get8(nil);
463                 n = get8(nil);
464                 n |= get8(nil) << 8;
465                 i->base[1] = (s16int)n;
466         }
467         Bterm(ib);
468 }
469
470 void
471 readmid(char *file)
472 {
473         u32int n;
474         uchar *s;
475         Trk *x;
476
477         ib = file != nil ? bopen(file, OREAD) : bfdopen(0, OREAD);
478         if(get32(nil) != 0x4d546864 || get32(nil) != 6)
479                 sysfatal("invalid header");
480         mfmt = get16(nil);
481         ntrk = get16(nil);
482         if(ntrk == 1)
483                 mfmt = 0;
484         if(mfmt < 0 || mfmt > 1)
485                 sysfatal("unsupported format %d", mfmt);
486         div = get16(nil);
487         tr = emalloc(ntrk * sizeof *tr);
488         for(x=tr; x<tr+ntrk; x++){
489                 if(get32(nil) != 0x4d54726b)
490                         sysfatal("invalid track");
491                 n = get32(nil);
492                 s = emalloc(n);
493                 bread(s, n);
494                 x->s = s;
495                 x->p = s;
496                 x->e = s + n;
497         }
498         Bterm(ib);
499 }
500
501 void
502 usage(void)
503 {
504         fprint(2, "usage: %s [-2] [-i inst] [mid]\n", argv0);
505         exits("usage");
506 }
507
508 void
509 main(int argc, char **argv)
510 {
511         int n, t, mint;
512         char *i;
513         double f;
514         Chan *c;
515         Opl *o;
516         Trk *x, *minx;
517
518         i = "/mnt/wad/genmidi";
519         ARGBEGIN{
520         case '2': opl2 = 1; ople = opl + 9; break;
521         case 'i': i = EARGF(usage()); break;
522         default: usage();
523         }ARGEND
524         readinst(i);
525         readmid(*argv);
526         ob = bfdopen(1, OWRITE);
527         f = pow(2, 1./12);
528         for(n=0; n<nelem(freq); n++)
529                 freq[n] = 440 * pow(f, n - 69);
530         for(c=chan; c<chan+nelem(chan); c++){
531                 c->v = 0x5a;
532                 c->bend = 0;
533                 c->pan = 3<<4;
534                 c->i = inst;
535         }
536         for(o=opl; o<ople; o++)
537                 o->n = -1;
538         tempo = 500000;
539         putcmd(Rwse, Mwse, 0);
540         putcmd(Rop3, 1, 0);
541         for(;;){
542                 minx = nil;
543                 mint = 0;
544                 for(x=tr; x<tr+ntrk; x++){
545                         if(x->p >= x->e)
546                                 continue;
547                         t = x->t + tc(peekvar(x));
548                         if(t < mint || minx == nil){
549                                 mint = t;
550                                 minx = x;
551                         }
552                 }
553                 if(minx == nil)
554                         exits(nil);
555                 ev(minx);
556         }
557 }