1 /*
2 * gbsplay is a Gameboy sound player
3 *
4 * 2006 (C) by Tobias Diedrich <ranma+gbsplay@tdiedrich.de>
5 *
6 * MIDI output plugin
7 *
8 * 2008,2018 (C) by Vegard Nossum
9 * Christian Garbs <mitch@cgarbs.de>
10 *
11 * Licensed under GNU GPL v1 or, at your option, any later version.
12 */
13
14 #include "common.h"
15
16 #include <math.h>
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <time.h>
21 #include <unistd.h>
22
23 #include "plugout.h"
24
25 #define FILENAMESIZE 32
26 #define LN2 .69314718055994530941
27 #define MAGIC 5.78135971352465960412
28 #define FREQ(x) (262144 / (x))
29 #define NOTE(x) ((long)((log(FREQ(x))/LN2 - MAGIC)*12 + .2))
30
midi_open(enum plugout_endian endian,long rate)31 static long regparm midi_open(enum plugout_endian endian, long rate)
32 {
33 return 0;
34 }
35
36 static FILE *file = NULL;
37
38 static long track_length;
39 static long track_length_offset;
40
41 static long cycles_prev = 0;
42
43 static int note[4] = {0, 0, 0, 0};
44
midi_write_u32(uint32_t x)45 static int midi_write_u32(uint32_t x)
46 {
47 uint8_t s[4];
48
49 s[0] = x >> 24;
50 s[1] = x >> 16;
51 s[2] = x >> 8;
52 s[3] = x;
53
54 if (fwrite(s, 1, 4, file) != 4)
55 return 1;
56 return 0;
57 }
58
midi_write_u16(uint16_t x)59 static int midi_write_u16(uint16_t x)
60 {
61 uint8_t s[2];
62
63 s[0] = x >> 8;
64 s[1] = x;
65
66 if (fwrite(s, 1, 2, file) != 2)
67 return 1;
68 return 0;
69 }
70
midi_write_varlen(unsigned long x)71 static int midi_write_varlen(unsigned long x)
72 {
73 uint8_t data[4];
74 unsigned int i;
75
76 for (i = 0; i < 4; ++i) {
77 data[3 - i] = (x & 0x7f) | 0x80;
78 x >>= 7;
79
80 if (!x) {
81 ++i;
82 break;
83 }
84 }
85
86 data[3] &= ~0x80;
87 if (fwrite(data + 4 - i, 1, i, file) != i)
88 return 1;
89
90 track_length += i;
91 return 0;
92 }
93
midi_write_event(long cycles,const uint8_t * s,unsigned int n)94 static int midi_write_event(long cycles, const uint8_t *s, unsigned int n)
95 {
96 if (midi_write_varlen((cycles - cycles_prev) >> 14))
97 return 1;
98
99 if (fwrite(s, 1, n, file) != n)
100 return 1;
101
102 track_length += n;
103
104 cycles_prev = cycles;
105 return 0;
106 }
107
midi_open_track(int subsong)108 static int midi_open_track(int subsong)
109 {
110 char *filename;
111 if ((filename = malloc(FILENAMESIZE)) == NULL)
112 goto out;
113
114 if (snprintf(filename, FILENAMESIZE, "gbsplay-%d.mid", subsong + 1) >= FILENAMESIZE)
115 goto out;
116
117 file = fopen(filename, "wb");
118 if (!file) {
119 free(filename);
120 goto out;
121 }
122
123 free(filename);
124
125 /* Header type */
126 if (fwrite("MThd", 1, 4, file) != 4)
127 goto out_file;
128
129 /* Header length */
130 if (midi_write_u32(6))
131 goto out_file;
132
133 /* Format */
134 if (midi_write_u16(0))
135 goto out_file;
136
137 /* Tracks */
138 if (midi_write_u16(1))
139 goto out_file;
140
141 /* Division */
142 /* XXX: Do some real calculation instead of this magic number :-) */
143 if (midi_write_u16(124))
144 goto out_file;
145
146 /* Track type */
147 if (fwrite("MTrk", 1, 4, file) != 4)
148 goto out_file;
149
150 /* Track length (placeholder) */
151 track_length = 0;
152 track_length_offset = ftell(file);
153 if (track_length_offset == -1)
154 goto out_file;
155
156 if (midi_write_u32(0))
157 goto out_file;
158
159 return 0;
160
161 out_file:
162 fclose(file);
163 file = NULL;
164 out:
165 return 1;
166 }
167
midi_close_track()168 static int midi_close_track()
169 {
170 uint8_t event[3];
171
172 /* End of track */
173 event[0] = 0xff;
174 event[1] = 0x2f;
175 event[2] = 0x00;
176
177 if (midi_write_event(cycles_prev, event, 3))
178 goto out;
179
180 if (fseek(file, track_length_offset, SEEK_SET) == -1)
181 goto out;
182
183 if (midi_write_u32(track_length))
184 goto out;
185
186 fclose(file);
187 file = NULL;
188 return 0;
189
190 out:
191 file = NULL;
192 return 1;
193 }
194
midi_skip(int subsong)195 static int regparm midi_skip(int subsong)
196 {
197 cycles_prev = 0;
198
199 if (file) {
200 if (midi_close_track())
201 return 1;
202 }
203
204 return midi_open_track(subsong);
205 }
206
note_on(long cycles,int channel,int new_note,int velocity)207 static int note_on(long cycles, int channel, int new_note, int velocity)
208 {
209 uint8_t event[3];
210
211 event[0] = 0x90 | channel;
212 event[1] = new_note;
213 event[2] = velocity;
214
215 if (midi_write_event(cycles, event, 3))
216 return 1;
217
218 note[channel] = new_note;
219
220 return 0;
221 }
222
note_off(long cycles,int channel)223 static int note_off(long cycles, int channel)
224 {
225 uint8_t event[3];
226
227 if (!note[channel])
228 return 0;
229
230 event[0] = 0x80 | channel;
231 event[1] = note[channel];
232 event[2] = 0;
233
234 if (midi_write_event(cycles, event, 3))
235 return 1;
236
237 note[channel] = 0;
238
239 return 0;
240 }
241
pan(long cycles,int channel,int pan)242 static int pan(long cycles, int channel, int pan)
243 {
244 uint8_t event[3];
245
246 event[0] = 0xb0 | channel;
247 event[1] = 0x0a;
248 event[2] = pan;
249
250 if (midi_write_event(cycles, event, 3))
251 return 1;
252
253 return 0;
254 }
255
midi_io(long cycles,uint32_t addr,uint8_t val)256 static int regparm midi_io(long cycles, uint32_t addr, uint8_t val)
257 {
258 static long div[4] = {0, 0, 0, 0};
259 static int volume[4] = {0, 0, 0, 0};
260 static int running[4] = {0, 0, 0, 0};
261 static int master[4] = {0, 0, 0, 0};
262 int new_note;
263
264 long chan = (addr - 0xff10) / 5;
265
266 if (!file)
267 return 1;
268
269 switch (addr) {
270 case 0xff12:
271 case 0xff17:
272 volume[chan] = 8 * (val >> 4);
273 master[chan] = (val & 0xf8) != 0;
274 if (!master[chan] && running[chan]) {
275 /* DAC turned off, disable channel */
276 if (note_off(cycles, chan))
277 return 1;
278 running[chan] = 0;
279 }
280 if (volume[chan]) {
281 /* volume set to >0, restart current note */
282 if (running[chan] && !note[chan]) {
283 new_note = NOTE(2048 - div[chan]) + 21;
284 if (new_note < 0 || new_note >= 0x80)
285 break;
286 if (note_on(cycles, chan, new_note, volume[chan]))
287 return 1;
288 }
289 } else {
290 /* volume set to 0, stop note (if any) */
291 if (note_off(cycles, chan))
292 return 1;
293 }
294 break;
295 case 0xff13:
296 case 0xff18:
297 case 0xff1d:
298 div[chan] &= 0xff00;
299 div[chan] |= val;
300
301 if (running[chan]) {
302 new_note = NOTE(2048 - div[chan]) + 21;
303
304 if (new_note != note[chan]) {
305 /* portamento: retrigger with new note */
306 if (note_off(cycles, chan))
307 return 1;
308
309 if (new_note < 0 || new_note >= 0x80)
310 break;
311
312 if (note_on(cycles, chan, new_note, volume[chan]))
313 return 1;
314 }
315 }
316
317 break;
318 case 0xff14:
319 case 0xff19:
320 case 0xff1e:
321 div[chan] &= 0x00ff;
322 div[chan] |= ((long) (val & 7)) << 8;
323
324 new_note = NOTE(2048 - div[chan]) + 21;
325
326 /* Channel start trigger */
327 if ((val & 0x80) == 0x80) {
328 if (note_off(cycles, chan))
329 return 1;
330
331 if (new_note < 0 || new_note >= 0x80)
332 break;
333
334 if (master[chan]) {
335 if (note_on(cycles, chan, new_note, volume[chan]))
336 return 1;
337 running[chan] = 1;
338 }
339 } else {
340 if (running[chan]) {
341 if (new_note != note[chan]) {
342 /* portamento: retrigger with new note */
343 if (note_off(cycles, chan))
344 return 1;
345
346 if (new_note < 0 || new_note >= 0x80)
347 break;
348
349 if (note_on(cycles, chan, new_note, volume[chan]))
350 return 1;
351 }
352 }
353 }
354
355 break;
356 case 0xff1a:
357 master[2] = (val & 0x80) == 0x80;
358 if (!master[2] && running[2]) {
359 /* DAC turned off, disable channel */
360 if (note_off(cycles, 2))
361 return 1;
362 running[2] = 0;
363 }
364 break;
365 case 0xff1c:
366 volume[2] = 32 * ((4 - (val >> 5)) & 3);
367 if (volume[2]) {
368 /* volume set to >0, restart current note */
369 if (running[2] && !note[2]) {
370 new_note = NOTE(2048 - div[chan]) + 21;
371 if (new_note < 0 || new_note >= 0x80)
372 break;
373 if (note_on(cycles, 2, new_note, volume[2]))
374 return 1;
375 }
376 } else {
377 /* volume set to 0, stop note (if any) */
378 if (note_off(cycles, 2))
379 return 1;
380 }
381 break;
382 case 0xff25:
383 for (chan = 0; chan < 4; chan++)
384 switch ((val >> chan) & 0x11) {
385 case 0x10:
386 pan(cycles, chan, 0);
387 break;
388 case 0x01:
389 pan(cycles, chan, 127);
390 break;
391 default:
392 pan(cycles, chan, 64);
393 }
394 break;
395 case 0xff26:
396 if ((val & 0x80) == 0) {
397 for (chan = 0; chan < 4; chan++) {
398 div[chan] = 0;
399 volume[chan] = 0;
400 running[chan] = 0;
401 master[chan] = 1;
402 if (note_off(cycles, 2))
403 return 1;
404 }
405 }
406 break;
407 }
408
409 return 0;
410 }
411
midi_close(void)412 static void regparm midi_close(void)
413 {
414 int chan;
415
416 if (!file)
417 return;
418
419 for (chan = 0; chan < 4; chan++)
420 if (note_off(cycles_prev + 1, chan))
421 return;
422
423 midi_close_track();
424 }
425
426 const struct output_plugin plugout_midi = {
427 .name = "midi",
428 .description = "MIDI file writer",
429 .open = midi_open,
430 .skip = midi_skip,
431 .io = midi_io,
432 .close = midi_close,
433 };
434