midiplay.c revision 1.31 1 /* $NetBSD: midiplay.c,v 1.31 2018/05/03 01:12:57 mrg Exp $ */
2
3 /*
4 * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (augustss (at) NetBSD.org).
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <sys/cdefs.h>
32
33 #ifndef lint
34 __RCSID("$NetBSD: midiplay.c,v 1.31 2018/05/03 01:12:57 mrg Exp $");
35 #endif
36
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <fcntl.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <limits.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/ioctl.h>
49 #include <sys/midiio.h>
50
51 #define DEVMUSIC "/dev/music"
52
53 struct track {
54 struct track *indirect; /* for fast swaps in heap code */
55 u_char *start, *end;
56 u_long delta;
57 u_char status;
58 };
59
60 #define MIDI_META 0xff
61
62 #define META_SEQNO 0x00
63 #define META_TEXT 0x01
64 #define META_COPYRIGHT 0x02
65 #define META_TRACK 0x03
66 #define META_INSTRUMENT 0x04
67 #define META_LYRIC 0x05
68 #define META_MARKER 0x06
69 #define META_CUE 0x07
70 #define META_CHPREFIX 0x20
71 #define META_EOT 0x2f
72 #define META_SET_TEMPO 0x51
73 #define META_KEY 0x59
74 #define META_SMPTE 0x54
75 #define META_TIMESIGN 0x58
76
77 static const char *metanames[] = {
78 "", "Text", "Copyright", "Track", "Instrument",
79 "Lyric", "Marker", "Cue",
80 };
81
82 static int midi_lengths[] = { 2, 2, 2, 2, 1, 1, 2, 0 };
83 /* Number of bytes in a MIDI command */
84 #define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7])
85
86 #define SEQ_MK_SYSEX0(_dev,...) \
87 SEQ_MK_EVENT(sysex, 0x94, .device=(_dev), .buffer={__VA_ARGS__})
88
89
90 static void usage(void);
91 static void send_event(seq_event_t *);
92 static void dometa(u_int, u_char *, u_int);
93 #if 0
94 static void midireset(void);
95 #endif
96 static void send_sysex(u_char *, u_int);
97 static u_long getvar(struct track *);
98 static u_long getlen(struct track *);
99 static void playfile(FILE *, const char *);
100 static void playdata(u_char *, u_int, const char *);
101
102 static void Heapify(struct track *, int, int);
103 static void BuildHeap(struct track *, int);
104 static int ShrinkHeap(struct track *, int);
105
106 /*
107 * This sample plays at an apparent tempo of 120 bpm when the BASETEMPO is 150
108 * bpm, because the quavers are 5 divisions (4 on 1 off) rather than 4 total.
109 */
110 #define P(c) 1, 0x90, c, 0x7f, 4, 0x80, c, 0
111 #define PL(c) 1, 0x90, c, 0x7f, 8, 0x80, c, 0
112 #define C 0x3c
113 #define D 0x3e
114 #define E 0x40
115 #define F 0x41
116
117 static u_char sample[] = {
118 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 8,
119 'M', 'T', 'r', 'k', 0, 0, 0, 4+13*8,
120 P(C), P(C), P(C), P(E), P(D), P(D), P(D),
121 P(F), P(E), P(E), P(D), P(D), PL(C),
122 0, 0xff, 0x2f, 0
123 };
124 #undef P
125 #undef PL
126 #undef C
127 #undef D
128 #undef E
129 #undef F
130
131 static u_char silence_sample[] = {
132 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 8,
133 'M', 'T', 'r', 'k', 0, 0, 0, 8,
134 0, 0xb0, 0x78, 0x00,
135 0, 0xff, 0x2f, 0
136 };
137
138 #define MARK_HEADER "MThd"
139 #define MARK_TRACK "MTrk"
140 #define MARK_LEN 4
141
142 #define RMID_SIG "RIFF"
143 #define RMID_MIDI_ID "RMID"
144 #define RMID_DATA_ID "data"
145
146 #define SIZE_LEN 4
147 #define HEADER_LEN 6
148
149 #define GET8(p) ((p)[0])
150 #define GET16(p) (((p)[0] << 8) | (p)[1])
151 #define GET24(p) (((p)[0] << 16) | ((p)[1] << 8) | (p)[2])
152 #define GET32(p) (((p)[0] << 24) | ((p)[1] << 16) | ((p)[2] << 8) | (p)[3])
153 #define GET32_LE(p) (((p)[3] << 24) | ((p)[2] << 16) | ((p)[1] << 8) | (p)[0])
154
155 static void __attribute__((__noreturn__))
156 usage(void)
157 {
158 fprintf(stderr, "usage: %s [-d unit] [-f file] [-l] [-m] [-p pgm] [-q] "
159 "[-t %%tempo] [-v] [-x] [-s] [file ...]\n",
160 getprogname());
161 exit(1);
162 }
163
164 static int showmeta = 0;
165 static int verbose = 0;
166 #define BASETEMPO 400000 /* us/beat(=24 clks or qn) (150 bpm) */
167 static u_int tempo_set = 0;
168 static u_int tempo_abs = 0;
169 static u_int ttempo = 100;
170 static int unit = 0;
171 static int play = 1;
172 static int fd = -1;
173 static int sameprogram = 0;
174 static int insysex = 0;
175 static int svsysex = 0; /* number of sysex bytes saved internally */
176
177 static void
178 send_event(seq_event_t *ev)
179 {
180 /*
181 printf("%02x %02x %02x %02x %02x %02x %02x %02x\n",
182 ev->arr[0], ev->arr[1], ev->arr[2], ev->arr[3],
183 ev->arr[4], ev->arr[5], ev->arr[6], ev->arr[7]);
184 */
185 if (play)
186 write(fd, ev, sizeof *ev);
187 }
188
189 static u_long
190 getvar(struct track *tp)
191 {
192 u_long r, c;
193
194 r = 0;
195 do {
196 c = *tp->start++;
197 r = (r << 7) | (c & 0x7f);
198 } while ((c & 0x80) && tp->start < tp->end);
199 return r;
200 }
201
202 static u_long
203 getlen(struct track *tp)
204 {
205 u_long len;
206 len = getvar(tp);
207 if (tp->start + len > tp->end)
208 errx(1, "bogus item length exceeds remaining track size");
209 return len;
210 }
211
212 static void
213 dometa(u_int meta, u_char *p, u_int len)
214 {
215 static char const * const keys[] = {
216 "Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F",
217 "C",
218 "G", "D", "A", "E", "B", "F#", "C#",
219 "G#", "D#", "A#" /* for minors */
220 };
221 seq_event_t ev;
222 uint32_t usperbeat;
223
224 switch (meta) {
225 case META_TEXT:
226 case META_COPYRIGHT:
227 case META_TRACK:
228 case META_INSTRUMENT:
229 case META_LYRIC:
230 case META_MARKER:
231 case META_CUE:
232 if (showmeta) {
233 printf("%s: ", metanames[meta]);
234 fwrite(p, len, 1, stdout);
235 printf("\n");
236 }
237 break;
238 case META_SET_TEMPO:
239 usperbeat = GET24(p);
240 ev = SEQ_MK_TIMING(TEMPO,
241 .bpm=(60000000. / usperbeat) * (ttempo / 100.) + 0.5);
242 if (showmeta)
243 printf("Tempo: %u us/'beat'(24 midiclks)"
244 " at %u%%; adjusted bpm = %u\n",
245 usperbeat, ttempo, ev.t_TEMPO.bpm);
246 if (tempo_abs)
247 warnx("tempo event ignored"
248 " in absolute-timed MIDI file");
249 else {
250 send_event(&ev);
251 if (!tempo_set) {
252 tempo_set = 1;
253 send_event(&SEQ_MK_TIMING(START));
254 }
255 }
256 break;
257 case META_TIMESIGN:
258 ev = SEQ_MK_TIMING(TIMESIG,
259 .numerator=p[0], .lg2denom=p[1],
260 .clks_per_click=p[2], .dsq_per_24clks=p[3]);
261 if (showmeta) {
262 printf("Time signature: %d/%d."
263 " Click every %d midiclk%s"
264 " (24 midiclks = %d 32nd note%s)\n",
265 ev.t_TIMESIG.numerator,
266 1 << ev.t_TIMESIG.lg2denom,
267 ev.t_TIMESIG.clks_per_click,
268 1 == ev.t_TIMESIG.clks_per_click ? "" : "s",
269 ev.t_TIMESIG.dsq_per_24clks,
270 1 == ev.t_TIMESIG.dsq_per_24clks ? "" : "s");
271 }
272 /* send_event(&ev); not implemented in sequencer */
273 break;
274 case META_KEY:
275 if (showmeta)
276 printf("Key: %s %s\n",
277 keys[((char)p[0]) + p[1] ? 10 : 7],
278 p[1] ? "minor" : "major");
279 break;
280 default:
281 break;
282 }
283 }
284
285 #if 0
286 static void
287 midireset(void)
288 {
289 /* General MIDI reset sequence */
290 send_event(&SEQ_MK_SYSEX0(unit, 0x7e, 0x7f, 0x09, 0x01, 0xf7, 0xff));
291 }
292 #endif
293
294 #define SYSEX_CHUNK 6
295 static void
296 send_sysex(u_char *p, u_int l)
297 {
298 seq_event_t event;
299 static u_char bf[6];
300
301 if (0 == l) {
302 warnx("zero-length system-exclusive event");
303 return;
304 }
305
306 /*
307 * This block is needed only to handle the possibility that a sysex
308 * message is broken into multiple events in a MIDI file that do not
309 * have length six; the /dev/music sequencer assumes a sysex message is
310 * finished with the first SYSEX event carrying fewer than six bytes,
311 * even if the last is not MIDI_SYSEX_END. So, we need to be careful
312 * not to send a short sysex event until we have seen the end byte.
313 * Instead, save some straggling bytes in bf, and send when we have a
314 * full six (or an end byte). Note bf/saved/insysex should be per-
315 * device, if we supported output to more than one device at a time.
316 */
317 if (svsysex > 0) {
318 if (l > sizeof bf - svsysex) {
319 memcpy(bf + svsysex, p, sizeof bf - svsysex);
320 l -= sizeof bf - svsysex;
321 p += sizeof bf - svsysex;
322 send_event(&SEQ_MK_SYSEX0(unit,
323 bf[0], bf[1], bf[2], bf[3], bf[4], bf[5]));
324 svsysex = 0;
325 } else {
326 memcpy(bf + svsysex, p, l);
327 svsysex += l;
328 p += l;
329 if (MIDI_SYSEX_END == bf[svsysex-1]) {
330 event = SEQ_MK_SYSEX(unit);
331 memcpy(event.sysex.buffer, bf, svsysex);
332 send_event(&event);
333 svsysex = insysex = 0;
334 } else
335 insysex = 1;
336 return;
337 }
338 }
339
340 /*
341 * l > 0. May as well test now whether we will be left 'insysex'
342 * after processing this event.
343 */
344 insysex = (MIDI_SYSEX_END != p[l-1]);
345
346 /*
347 * If not for multi-event sysexes and chunk-size weirdness, this
348 * function could pretty much start here. :)
349 */
350 while (l >= SYSEX_CHUNK) {
351 send_event(&SEQ_MK_SYSEX0(unit, p[0], p[1], p[2], p[3], p[4], p[5]));
352 p += SYSEX_CHUNK;
353 l -= SYSEX_CHUNK;
354 }
355 if (l > 0) {
356 if (insysex) {
357 memcpy(bf, p, l);
358 svsysex = l;
359 } else { /* a <6 byte chunk is ok if it's REALLY the end */
360 event = SEQ_MK_SYSEX(unit);
361 memcpy(event.sysex.buffer, p, l);
362 send_event(&event);
363 }
364 }
365 }
366
367 static void
368 playfile(FILE *f, const char *name)
369 {
370 u_char *buf, *nbuf;
371 u_int tot, n, size, nread;
372
373 /*
374 * We need to read the whole file into memory for easy processing.
375 * Using mmap() would be nice, but some file systems do not support
376 * it, nor does reading from e.g. a pipe. The latter also precludes
377 * finding out the file size without reading it.
378 */
379 size = 1000;
380 buf = malloc(size);
381 if (buf == 0)
382 errx(1, "malloc() failed");
383 nread = size;
384 tot = 0;
385 for (;;) {
386 n = fread(buf + tot, 1, nread, f);
387 tot += n;
388 if (n < nread)
389 break;
390 /* There must be more to read. */
391 nread = size;
392 nbuf = realloc(buf, size * 2);
393 if (nbuf == NULL)
394 errx(1, "realloc() failed");
395 buf = nbuf;
396 size *= 2;
397 }
398 playdata(buf, tot, name);
399 free(buf);
400 }
401
402 static void
403 playdata(u_char *buf, u_int tot, const char *name)
404 {
405 int format, ntrks, divfmt, ticks, t;
406 u_int len, mlen, status, chan;
407 u_char *p, *end, byte, meta, *msg;
408 struct synth_info info;
409 struct track *tracks;
410 struct track *tp;
411
412 /* verify that the requested midi unit exists */
413 info.device = unit;
414 if (play && ioctl(fd, SEQUENCER_INFO, &info) < 0)
415 err(1, "ioctl(SEQUENCER_INFO) failed");
416
417 end = buf + tot;
418 if (verbose) {
419 printf("Playing %s (%d bytes)", name, tot);
420 if (play)
421 printf(" on %s (unit %d)...", info.name, info.device);
422 puts("\n");
423 }
424
425 if (tot < MARK_LEN + 4) {
426 warnx("Not a MIDI file, too short");
427 return;
428 }
429
430 if (memcmp(buf, RMID_SIG, MARK_LEN) == 0) {
431 u_char *eod;
432 /* Detected a RMID file, let's just check if it's
433 * a MIDI file */
434 if ((u_int)GET32_LE(buf + MARK_LEN) != tot - 8) {
435 warnx("Not a RMID file, bad header");
436 return;
437 }
438
439 buf += MARK_LEN + 4;
440 if (memcmp(buf, RMID_MIDI_ID, MARK_LEN) != 0) {
441 warnx("Not a RMID file, bad ID");
442 return;
443 }
444
445 /* Now look for the 'data' chunk, which contains
446 * MIDI data */
447 buf += MARK_LEN;
448
449 /* Test against end-8 since we must have at least 8 bytes
450 * left to read */
451 while(buf < end-8 && memcmp(buf, RMID_DATA_ID, MARK_LEN))
452 buf += GET32_LE(buf+4) + 8; /* MARK_LEN + 4 */
453
454 if (buf >= end-8) {
455 warnx("Not a valid RMID file, no data chunk");
456 return;
457 }
458
459 buf += MARK_LEN; /* "data" */
460 eod = buf + 4 + GET32_LE(buf);
461 if (eod >= end) {
462 warnx("Not a valid RMID file, bad data chunk size");
463 return;
464 }
465
466 end = eod;
467 buf += 4;
468 }
469
470 if (memcmp(buf, MARK_HEADER, MARK_LEN) != 0) {
471 warnx("Not a MIDI file, missing header");
472 return;
473 }
474
475 if (GET32(buf + MARK_LEN) != HEADER_LEN) {
476 warnx("Not a MIDI file, bad header");
477 return;
478 }
479 format = GET16(buf + MARK_LEN + SIZE_LEN);
480 ntrks = GET16(buf + MARK_LEN + SIZE_LEN + 2);
481 divfmt = GET8(buf + MARK_LEN + SIZE_LEN + 4);
482 ticks = GET8(buf + MARK_LEN + SIZE_LEN + 5);
483 p = buf + MARK_LEN + SIZE_LEN + HEADER_LEN;
484 /*
485 * Set the timebase (or timebase and tempo, for absolute-timed files).
486 * PORTABILITY: some sequencers actually check the timebase against
487 * available timing sources and may adjust it accordingly (storing a
488 * new value in the ioctl arg) which would require us to compensate
489 * somehow. That possibility is ignored for now, as NetBSD's sequencer
490 * currently synthesizes all timebases, for better or worse, from the
491 * system clock.
492 *
493 * For a non-absolute file, if timebase is set to the file's divisions
494 * value, and tempo set in the obvious way, then the timing deltas in
495 * the MTrks require no scaling. A downside to this approach is that
496 * the sequencer API wants tempo in (integer) beats per minute, which
497 * limits how finely tempo can be specified. That might be got around
498 * in some cases by frobbing tempo and timebase more obscurely, but this
499 * player is meant to be simple and clear.
500 */
501 if (!play)
502 /* do nothing */;
503 else if ((divfmt & 0x80) == 0) {
504 ticks |= divfmt << 8;
505 if (ioctl(fd, SEQUENCER_TMR_TIMEBASE, &(int){ticks}) < 0)
506 err(1, "SEQUENCER_TMR_TIMEBASE");
507 } else {
508 tempo_abs = tempo_set = 1;
509 divfmt = -(int8_t)divfmt;
510 /*
511 * divfmt is frames per second; multiplying by 60 to set tempo
512 * in frames per minute could exceed sequencer's (arbitrary)
513 * tempo limits, so factor 60 as 12*5, set tempo in frames per
514 * 12 seconds, and account for the 5 in timebase.
515 */
516 send_event(&SEQ_MK_TIMING(TEMPO,
517 .bpm=(12*divfmt) * (ttempo/100.) + 0.5));
518 if (ioctl(fd, SEQUENCER_TMR_TIMEBASE, &(int){5*ticks}) < 0)
519 err(1, "SEQUENCER_TMR_TIMEBASE");
520 }
521 if (verbose > 1)
522 printf(tempo_abs ?
523 "format=%d ntrks=%d abs fps=%u subdivs=%u\n" :
524 "format=%d ntrks=%d divisions=%u\n",
525 format, ntrks, tempo_abs ? divfmt : ticks, ticks);
526 if (format != 0 && format != 1) {
527 warnx("Cannot play MIDI file of type %d", format);
528 return;
529 }
530 if (ntrks == 0)
531 return;
532 tracks = malloc(ntrks * sizeof(struct track));
533 if (tracks == NULL)
534 errx(1, "malloc() tracks failed");
535 for (t = 0; t < ntrks;) {
536 if (p >= end - MARK_LEN - SIZE_LEN) {
537 warnx("Cannot find track %d", t);
538 goto ret;
539 }
540 len = GET32(p + MARK_LEN);
541 if (len > 1000000) { /* a safe guard */
542 warnx("Crazy track length");
543 goto ret;
544 }
545 if (memcmp(p, MARK_TRACK, MARK_LEN) == 0) {
546 tracks[t].start = p + MARK_LEN + SIZE_LEN;
547 tracks[t].end = tracks[t].start + len;
548 tracks[t].delta = getvar(&tracks[t]);
549 tracks[t].indirect = &tracks[t]; /* -> self for now */
550 t++;
551 }
552 p += MARK_LEN + SIZE_LEN + len;
553 }
554
555 /*
556 * Force every channel to the same patch if requested by the user.
557 */
558 if (sameprogram) {
559 for(t = 0; t < 16; t++) {
560 send_event(&SEQ_MK_CHN(PGM_CHANGE, .device=unit,
561 .channel=t, .program=sameprogram-1));
562 }
563 }
564 /*
565 * Play MIDI events by selecting the track with the lowest
566 * delta. Execute the event, update the delta and repeat.
567 *
568 * The ticks variable is the number of ticks that make up a beat
569 * (beat: 24 MIDI clocks always, a quarter note by usual convention)
570 * and is used as a reference value for the delays between
571 * the MIDI events.
572 */
573 BuildHeap(tracks, ntrks); /* tracks[0].indirect is always next */
574 for (;;) {
575 tp = tracks[0].indirect;
576 if ((verbose > 2 && tp->delta > 0) || verbose > 3) {
577 printf("DELAY %4ld TRACK %2td%s",
578 tp->delta, tp - tracks, verbose>3?" ":"\n");
579 fflush(stdout);
580 }
581 if (tp->delta > 0) {
582 if (!tempo_set) {
583 if (verbose || showmeta)
584 printf("No initial tempo;"
585 " defaulting:\n");
586 dometa(META_SET_TEMPO, (u_char[]){
587 BASETEMPO >> 16,
588 (BASETEMPO >> 8) & 0xff,
589 BASETEMPO & 0xff},
590 3);
591 }
592 send_event(&SEQ_MK_TIMING(WAIT_REL,
593 .divisions=tp->delta));
594 }
595 byte = *tp->start++;
596 if (byte == MIDI_META) {
597 meta = *tp->start++;
598 mlen = getlen(tp);
599 if (verbose > 3)
600 printf("META %02x (%d)\n", meta, mlen);
601 dometa(meta, tp->start, mlen);
602 tp->start += mlen;
603 } else {
604 if (MIDI_IS_STATUS(byte))
605 tp->status = byte;
606 else
607 tp->start--;
608 mlen = MIDI_LENGTH(tp->status);
609 msg = tp->start;
610 if (verbose > 3) {
611 if (mlen == 1)
612 printf("MIDI %02x (%d) %02x\n",
613 tp->status, mlen, msg[0]);
614 else
615 printf("MIDI %02x (%d) %02x %02x\n",
616 tp->status, mlen, msg[0], msg[1]);
617 }
618 if (insysex && tp->status != MIDI_SYSEX_END) {
619 warnx("incomplete system exclusive message"
620 " aborted");
621 svsysex = insysex = 0;
622 }
623 status = MIDI_GET_STATUS(tp->status);
624 chan = MIDI_GET_CHAN(tp->status);
625 switch (status) {
626 case MIDI_NOTEOFF:
627 send_event(&SEQ_MK_CHN(NOTEOFF, .device=unit,
628 .channel=chan, .key=msg[0], .velocity=msg[1]));
629 break;
630 case MIDI_NOTEON:
631 send_event(&SEQ_MK_CHN(NOTEON, .device=unit,
632 .channel=chan, .key=msg[0], .velocity=msg[1]));
633 break;
634 case MIDI_KEY_PRESSURE:
635 send_event(&SEQ_MK_CHN(KEY_PRESSURE,
636 .device=unit, .channel=chan,
637 .key=msg[0], .pressure=msg[1]));
638 break;
639 case MIDI_CTL_CHANGE:
640 send_event(&SEQ_MK_CHN(CTL_CHANGE,
641 .device=unit, .channel=chan,
642 .controller=msg[0], .value=msg[1]));
643 break;
644 case MIDI_PGM_CHANGE:
645 if (!sameprogram)
646 send_event(&SEQ_MK_CHN(PGM_CHANGE,
647 .device=unit, .channel=chan,
648 .program=msg[0]));
649 break;
650 case MIDI_CHN_PRESSURE:
651 send_event(&SEQ_MK_CHN(CHN_PRESSURE,
652 .device=unit, .channel=chan, .pressure=msg[0]));
653 break;
654 case MIDI_PITCH_BEND:
655 send_event(&SEQ_MK_CHN(PITCH_BEND,
656 .device=unit, .channel=chan,
657 .value=(msg[0] & 0x7f) | ((msg[1] & 0x7f)<<7)));
658 break;
659 case MIDI_SYSTEM_PREFIX:
660 mlen = getlen(tp);
661 if (tp->status == MIDI_SYSEX_START) {
662 send_sysex(tp->start, mlen);
663 break;
664 } else if (tp->status == MIDI_SYSEX_END) {
665 /* SMF uses SYSEX_END as CONTINUATION/ESCAPE */
666 if (insysex) { /* CONTINUATION */
667 send_sysex(tp->start, mlen);
668 } else { /* ESCAPE */
669 for (; mlen > 0 ; -- mlen) {
670 send_event(
671 &SEQ_MK_EVENT(putc,
672 SEQOLD_MIDIPUTC,
673 .device=unit,
674 .byte=*(tp->start++)
675 ));
676 }
677 }
678 break;
679 }
680 /* Sorry, can't do this yet; FALLTHROUGH */
681 default:
682 if (verbose)
683 printf("MIDI event 0x%02x ignored\n",
684 tp->status);
685 }
686 tp->start += mlen;
687 }
688 if (tp->start >= tp->end) {
689 ntrks = ShrinkHeap(tracks, ntrks); /* track gone */
690 if (0 == ntrks)
691 break;
692 } else
693 tp->delta = getvar(tp);
694 Heapify(tracks, ntrks, 0);
695 }
696 if (play && ioctl(fd, SEQUENCER_SYNC, 0) < 0)
697 err(1, "SEQUENCER_SYNC");
698
699 ret:
700 free(tracks);
701 }
702
703 static int
704 parse_unit(const char *sunit)
705 {
706 const char *osunit = sunit;
707 long n;
708 char *ep;
709
710 if (strncmp(sunit, "midi", strlen("midi")) == 0)
711 sunit += strlen("midi");
712
713 errno = 0;
714 n = strtol(sunit, &ep, 10);
715 if (n < 0 || n > INT_MAX || *ep != '\0' ||
716 (errno == ERANGE &&
717 (n == LONG_MAX || n == LONG_MIN)))
718 errx(1, "bad midi unit -- %s", osunit);
719
720 return (int)n;
721 }
722
723 int
724 main(int argc, char **argv)
725 {
726 int ch;
727 int listdevs = 0;
728 int example = 0;
729 int silence = 0;
730 int nmidi;
731 const char *file = DEVMUSIC;
732 const char *sunit;
733 struct synth_info info;
734 FILE *f;
735
736 if ((sunit = getenv("MIDIUNIT")))
737 unit = parse_unit(sunit);
738
739 while ((ch = getopt(argc, argv, "?d:f:lmp:qst:vx")) != -1) {
740 switch(ch) {
741 case 'd':
742 unit = parse_unit(optarg);
743 break;
744 case 'f':
745 file = optarg;
746 break;
747 case 'l':
748 listdevs++;
749 break;
750 case 'm':
751 showmeta++;
752 break;
753 case 'p':
754 sameprogram = atoi(optarg);
755 break;
756 case 'q':
757 play = 0;
758 break;
759 case 's':
760 silence++;
761 break;
762 case 't':
763 ttempo = atoi(optarg);
764 break;
765 case 'v':
766 verbose++;
767 break;
768 case 'x':
769 example++;
770 break;
771 case '?':
772 default:
773 usage();
774 }
775 }
776 argc -= optind;
777 argv += optind;
778
779 if (!play)
780 goto output;
781
782 fd = open(file, O_WRONLY);
783 if (fd < 0)
784 err(1, "%s", file);
785 if (ioctl(fd, SEQUENCER_NRMIDIS, &nmidi) < 0)
786 err(1, "ioctl(SEQUENCER_NRMIDIS) failed, ");
787 if (nmidi == 0)
788 errx(1, "Sorry, no MIDI devices available");
789 if (listdevs) {
790 for (info.device = 0; info.device < nmidi; info.device++) {
791 if (ioctl(fd, SEQUENCER_INFO, &info) < 0)
792 err(1, "ioctl(SEQUENCER_INFO) failed, ");
793 printf("%d: %s\n", info.device, info.name);
794 }
795 exit(0);
796 }
797
798 output:
799 if (example)
800 while (example--)
801 playdata(sample, sizeof sample, "<Gubben Noa>");
802 else if (silence)
803 while (silence--)
804 playdata(silence_sample, sizeof silence_sample,
805 "<Silence>");
806 else if (argc == 0)
807 playfile(stdin, "<stdin>");
808 else
809 while (argc--) {
810 f = fopen(*argv, "r");
811 if (f == NULL)
812 err(1, "%s", *argv);
813 else {
814 playfile(f, *argv);
815 fclose(f);
816 }
817 argv++;
818 }
819
820 exit(0);
821 }
822
823 /*
824 * relative-time priority queue (min-heap). Properties:
825 * 1. The delta time at a node is relative to the node's parent's time.
826 * 2. When an event is dequeued from a track, the delta time of the new head
827 * event is relative to the time of the event just dequeued.
828 * Therefore:
829 * 3. After dequeueing the head event from the track at heap root, the next
830 * event's time is directly comparable to the root's children.
831 * These properties allow the heap to be maintained with delta times throughout.
832 * Insert is also implementable, but not needed: all the tracks are present
833 * at first; they just go away as they end.
834 */
835
836 #define PARENT(i) ((i - 1) >> 1)
837 #define LEFT(i) ((i << 1) + 1)
838 #define RIGHT(i) ((i + 1) << 1)
839 #define DTIME(i) (t[i].indirect->delta)
840 #define SWAP(i, j) do { \
841 struct track *_t = t[i].indirect; \
842 t[i].indirect = t[j].indirect; \
843 t[j].indirect = _t; \
844 } while (/*CONSTCOND*/ 0)
845
846 static void
847 Heapify(struct track *t, int ntrks, int node)
848 {
849 int lc, rc, mn;
850
851 lc = LEFT(node);
852 rc = RIGHT(node);
853
854 if (rc >= ntrks) { /* no right child */
855 if (lc >= ntrks) /* node is a leaf */
856 return;
857 if (DTIME(node) > DTIME(lc))
858 SWAP(node, lc);
859 DTIME(lc) -= DTIME(node);
860 return; /* no rc ==> lc is a leaf */
861 }
862
863 mn = lc;
864 if (DTIME(lc) > DTIME(rc))
865 mn = rc;
866 if (DTIME(node) <= DTIME(mn)) {
867 DTIME(rc) -= DTIME(node);
868 DTIME(lc) -= DTIME(node);
869 return;
870 }
871
872 SWAP(node, mn);
873 DTIME(rc) -= DTIME(node);
874 DTIME(lc) -= DTIME(node);
875 Heapify(t, ntrks, mn); /* gcc groks tail recursion */
876 }
877
878 static void
879 BuildHeap(struct track *t, int ntrks)
880 {
881 int node;
882
883 for (node = PARENT(ntrks - 1); node --> 0;)
884 Heapify(t, ntrks, node);
885 }
886
887 /*
888 * Make the heap 1 item smaller by discarding the track at the root. Move the
889 * rightmost bottom-level leaf to the root and decrement ntrks. It remains to
890 * run Heapify, which the caller is expected to do. Returns the new ntrks.
891 */
892 static int
893 ShrinkHeap(struct track *t, int ntrks)
894 {
895 int ancest;
896
897 --ntrks;
898 for (ancest = PARENT(ntrks); ancest > 0; ancest = PARENT(ancest))
899 DTIME(ntrks) += DTIME(ancest);
900 t[0].indirect = t[ntrks].indirect;
901 return ntrks;
902 }
903