]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/midi.c
vt: handle insert/delete/home/end keys for vt220/xterm
[plan9front.git] / sys / src / games / midi.c
1 #include <u.h>
2 #include <libc.h>
3
4 enum { SAMPLE = 44100 };
5
6 struct Tracker {
7         uchar *data;
8         char ended;
9         uvlong t;
10         uchar notes[16][128];
11         int cmd;
12 } *tr;
13
14 typedef struct Tracker Tracker;
15
16 int fd, ofd, div, tempo = 500000, ntrack;
17 uvlong T;
18 int freq[128];
19 uchar out[8192], *outp = out;
20
21 void *
22 emallocz(int size)
23 {
24         void *v;
25         
26         v = malloc(size);
27         if(v == nil)
28                 sysfatal("malloc: %r");
29         memset(v, 0, size);
30         return v;
31 }
32
33 int
34 get8(Tracker *src)
35 {
36         uchar c;
37
38         if(src == nil){
39                 if(read(fd, &c, 1) == 0)
40                         sysfatal("unexpected eof");
41                 return c;
42         }
43         return *src->data++;
44 }
45
46 int
47 get16(Tracker *src)
48 {
49         int x;
50         
51         x = get8(src) << 8;
52         return x | get8(src);
53 }
54
55 int
56 get32(Tracker *src)
57 {
58         int x;
59         x = get16(src) << 16;
60         return x | get16(src);
61 }
62
63 int
64 getvar(Tracker *src)
65 {
66         int k, x;
67         
68         x = get8(src);
69         k = x & 0x7F;
70         while(x & 0x80){
71                 k <<= 7;
72                 x = get8(src);
73                 k |= x & 0x7F;
74         }
75         return k;
76 }
77
78 int
79 peekvar(Tracker *src)
80 {
81         uchar *p;
82         int v;
83         
84         p = src->data;
85         v = getvar(src);
86         src->data = p;
87         return v;
88 }
89
90 void
91 skip(Tracker *src, int x)
92 {
93         if(x) do
94                 get8(src);
95         while(--x);
96 }
97
98 uvlong
99 tconv(int n)
100 {
101         uvlong v;
102         
103         v = n;
104         v *= tempo;
105         v *= SAMPLE;
106         v /= div;
107         v /= 1000000;
108         return v;
109 }
110
111 void
112 run(uvlong n)
113 {
114         int samp, j, k, no[128];
115         int t, f;
116         short u;
117         Tracker *x;
118         
119         samp = n - T;
120         if(samp <= 0)
121                 return;
122         memset(no, 0, sizeof no);
123         for(x = tr; x < tr + ntrack; x++){
124                 if(x->ended)
125                         continue;
126                 for(j = 0; j < 16; j++)
127                         for(k = 0; k < 128; k++)
128                                 no[k] += x->notes[j][k];
129         }
130         while(samp--){
131                 t = 0;
132                 for(k = 0; k < 128; k++){
133                         f = (T % freq[k]) >= freq[k]/2 ? 1 : 0;
134                         t += f * no[k];
135                 }
136                 u = t*10;
137                 outp[0] = outp[2] = u;
138                 outp[1] = outp[3] = u >> 8;
139                 outp += 4;
140                 if(outp == out + sizeof out){
141                         write(ofd, out, sizeof out);
142                         outp = out;
143                 }
144                 T++;
145         }
146 }
147
148 void
149 readevent(Tracker *src)
150 {
151         uvlong l;
152         int n,t;
153         
154         l = tconv(getvar(src));
155         run(src->t += l);
156         t = get8(src);
157         if((t & 0x80) == 0){
158                 src->data--;
159                 t = src->cmd;
160                 if((t & 0x80) == 0)
161                         sysfatal("invalid midi");
162         }else
163                 src->cmd = t;
164         switch(t >> 4){
165         case 0x8:
166                 n = get8(src);
167                 get8(src);
168                 src->notes[t & 15][n] = 0;
169                 break;
170         case 0x9:
171                 n = get8(src);
172                 src->notes[t & 15][n] = get8(src);
173                 break;
174         case 0xB:
175                 get16(src);
176                 break;
177         case 0xC:
178                 get8(src);
179                 break;
180         case 0xE:
181                 get16(src);
182                 break;
183         case 0xF:
184                 t = get8(src);
185                 n = get8(src);
186                 switch(t){
187                 case 0x2F:
188                         src->ended = 1;
189                         break;
190                 case 0x51:
191                         tempo = get16(src) << 8;
192                         tempo |= get8(src);
193                         break;
194                 case 5:
195                         write(2, src->data, n);
196                         skip(src, n);
197                         break;
198                 default:
199                         fprint(2, "unknown meta event type %.2x\n", t);
200                 case 3: case 1: case 2: case 0x58: case 0x59: case 0x21:
201                         skip(src, n);
202                 }
203                 break;
204         default:
205                 sysfatal("unknown event type %x", t>>4);
206         }
207 }
208
209 void
210 main(int argc, char **argv)
211 {
212         int i, size;
213         uvlong t, mint;
214         Tracker *x, *minx;
215
216         ARGBEGIN{
217         case 'c':
218                 ofd = 1;
219                 break;
220         }ARGEND;
221         if(*argv != nil)
222                 fd = open(*argv, OREAD);
223         if(ofd == 0)
224                 ofd = open("/dev/audio", OWRITE);
225         if(fd < 0 || ofd < 0)
226                 sysfatal("open: %r");
227         if(get32(nil) != 0x4D546864 || get32(nil) != 6)
228                 sysfatal("invalid file header");
229         get16(nil);
230         ntrack = get16(nil);
231         div = get16(nil);
232         tr = emallocz(ntrack * sizeof(*tr));
233         for(i = 0; i < ntrack; i++){
234                 if(get32(nil) != 0x4D54726B)
235                         sysfatal("invalid track header");
236                 size = get32(nil);
237                 tr[i].data = emallocz(size);
238                 readn(fd, tr[i].data, size);
239         }
240         for(i = 0; i < 128; i++)
241                 freq[i] = SAMPLE / (440 * pow(1.05946, i - 69));
242         for(;;){
243                 minx = nil;
244                 mint = 0;
245                 for(x = tr; x < tr + ntrack; x++){
246                         if(x->ended)
247                                 continue;
248                         t = tconv(peekvar(x)) + x->t;
249                         if(t < mint || minx == nil){
250                                 mint = t;
251                                 minx = x;
252                         }
253                 }
254                 if(minx == nil){
255                         write(ofd, out, outp - out);
256                         exits(nil);
257                 }
258                 readevent(minx);
259         }
260 }