]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/midi.c
devcons: fix permissions for reboot and sysstat
[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                 if((t & 0xF) == 0){
185                         while(get8(src) != 0xF7)
186                                 ;
187                         return;
188                 }
189                 t = get8(src);
190                 n = get8(src);
191                 switch(t){
192                 case 0x2F:
193                         src->ended = 1;
194                         break;
195                 case 0x51:
196                         tempo = get16(src) << 8;
197                         tempo |= get8(src);
198                         break;
199                 case 5:
200                         write(2, src->data, n);
201                         skip(src, n);
202                         break;
203                 default:
204                         fprint(2, "unknown meta event type %.2x\n", t);
205                 case 3: case 1: case 2: case 0x58: case 0x59: case 0x21:
206                         skip(src, n);
207                 }
208                 break;
209         default:
210                 sysfatal("unknown event type %x", t>>4);
211         }
212 }
213
214 void
215 main(int argc, char **argv)
216 {
217         int i, size;
218         uvlong t, mint;
219         Tracker *x, *minx;
220
221         ARGBEGIN{
222         case 'c':
223                 ofd = 1;
224                 break;
225         }ARGEND;
226         if(*argv != nil)
227                 fd = open(*argv, OREAD);
228         if(ofd == 0)
229                 ofd = open("/dev/audio", OWRITE);
230         if(fd < 0 || ofd < 0)
231                 sysfatal("open: %r");
232         if(get32(nil) != 0x4D546864 || get32(nil) != 6)
233                 sysfatal("invalid file header");
234         get16(nil);
235         ntrack = get16(nil);
236         div = get16(nil);
237         tr = emallocz(ntrack * sizeof(*tr));
238         for(i = 0; i < ntrack; i++){
239                 if(get32(nil) != 0x4D54726B)
240                         sysfatal("invalid track header");
241                 size = get32(nil);
242                 tr[i].data = emallocz(size);
243                 readn(fd, tr[i].data, size);
244         }
245         for(i = 0; i < 128; i++)
246                 freq[i] = SAMPLE / (440 * pow(1.05946, i - 69));
247         for(;;){
248                 minx = nil;
249                 mint = 0;
250                 for(x = tr; x < tr + ntrack; x++){
251                         if(x->ended)
252                                 continue;
253                         t = tconv(peekvar(x)) + x->t;
254                         if(t < mint || minx == nil){
255                                 mint = t;
256                                 minx = x;
257                         }
258                 }
259                 if(minx == nil){
260                         write(ofd, out, outp - out);
261                         exits(nil);
262                 }
263                 readevent(minx);
264         }
265 }