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