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