xref: /netbsd/usr.bin/midiplay/midiplay.c (revision d51a8cc9)
1 /*	$NetBSD: midiplay.c,v 1.34 2021/11/27 22:16:41 rillig 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@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.34 2021/11/27 22:16:41 rillig 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__))
usage(void)156 usage(void)
157 {
158 	fprintf(stderr, "usage: %s [-lmqsvx] [-d devno] [-f file] "
159 		"[-p pgm] [-t tempo] [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
send_event(seq_event_t * ev)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
getvar(struct track * tp)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
getlen(struct track * tp)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
dometa(u_int meta,u_char * p,u_int len)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
send_sysex(u_char * p,u_int l)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
playfile(FILE * f,const char * name)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
playdata(u_char * buf,u_int tot,const char * name)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 */
681 				/* FALLTHROUGH */
682 			default:
683 				if (verbose)
684 					printf("MIDI event 0x%02x ignored\n",
685 					       tp->status);
686 			}
687 			tp->start += mlen;
688 		}
689 		if (tp->start >= tp->end) {
690 			ntrks = ShrinkHeap(tracks, ntrks); /* track gone */
691 			if (0 == ntrks)
692 				break;
693 		} else
694 			tp->delta = getvar(tp);
695 		Heapify(tracks, ntrks, 0);
696 	}
697 	if (play && ioctl(fd, SEQUENCER_SYNC, 0) < 0)
698 		err(1, "SEQUENCER_SYNC");
699 
700  ret:
701 	free(tracks);
702 }
703 
704 static int
parse_unit(const char * sunit)705 parse_unit(const char *sunit)
706 {
707 	const char *osunit = sunit;
708 	long n;
709 	char *ep;
710 
711 	if (strncmp(sunit, "midi", strlen("midi")) == 0)
712 		sunit += strlen("midi");
713 
714 	errno = 0;
715 	n = strtol(sunit, &ep, 10);
716 	if (n < 0 || n > INT_MAX || *ep != '\0' ||
717 	    (errno == ERANGE &&
718 	    (n == LONG_MAX || n == LONG_MIN)))
719 		errx(1, "bad midi unit -- %s", osunit);
720 
721 	return (int)n;
722 }
723 
724 int
main(int argc,char ** argv)725 main(int argc, char **argv)
726 {
727 	int ch;
728 	int listdevs = 0;
729 	int example = 0;
730 	int silence = 0;
731 	int nmidi;
732 	const char *file = DEVMUSIC;
733 	const char *sunit;
734 	struct synth_info info;
735 	FILE *f;
736 
737 	if ((sunit = getenv("MIDIUNIT")))
738 		unit = parse_unit(sunit);
739 
740 	while ((ch = getopt(argc, argv, "?d:f:lmp:qst:vx")) != -1) {
741 		switch(ch) {
742 		case 'd':
743 			unit = parse_unit(optarg);
744 			break;
745 		case 'f':
746 			file = optarg;
747 			break;
748 		case 'l':
749 			listdevs++;
750 			break;
751 		case 'm':
752 			showmeta++;
753 			break;
754 		case 'p':
755 			sameprogram = atoi(optarg);
756 			break;
757 		case 'q':
758 			play = 0;
759 			break;
760 		case 's':
761 			silence++;
762 			break;
763 		case 't':
764 			ttempo = atoi(optarg);
765 			break;
766 		case 'v':
767 			verbose++;
768 			break;
769 		case 'x':
770 			example++;
771 			break;
772 		case '?':
773 		default:
774 			usage();
775 		}
776 	}
777 	argc -= optind;
778 	argv += optind;
779 
780 	if (!play)
781 		goto output;
782 
783 	fd = open(file, O_WRONLY);
784 	if (fd < 0)
785 		err(1, "%s", file);
786 	if (ioctl(fd, SEQUENCER_NRMIDIS, &nmidi) < 0)
787 		err(1, "ioctl(SEQUENCER_NRMIDIS) failed, ");
788 	if (nmidi == 0)
789 		errx(1, "Sorry, no MIDI devices available");
790 	if (listdevs) {
791 		for (info.device = 0; info.device < nmidi; info.device++) {
792 			if (ioctl(fd, SEQUENCER_INFO, &info) < 0)
793 				err(1, "ioctl(SEQUENCER_INFO) failed, ");
794 			printf("%d: %s\n", info.device, info.name);
795 		}
796 		exit(0);
797 	}
798 
799  output:
800 	if (example)
801 		while (example--)
802 			playdata(sample, sizeof sample, "<Gubben Noa>");
803 	else if (silence)
804 		while (silence--)
805 			playdata(silence_sample, sizeof silence_sample,
806 				 "<Silence>");
807 	else if (argc == 0)
808 		playfile(stdin, "<stdin>");
809 	else
810 		while (argc--) {
811 			f = fopen(*argv, "r");
812 			if (f == NULL)
813 				err(1, "%s", *argv);
814 			else {
815 				playfile(f, *argv);
816 				fclose(f);
817 			}
818 			argv++;
819 		}
820 
821 	exit(0);
822 }
823 
824 /*
825  * relative-time priority queue (min-heap). Properties:
826  * 1. The delta time at a node is relative to the node's parent's time.
827  * 2. When an event is dequeued from a track, the delta time of the new head
828  *    event is relative to the time of the event just dequeued.
829  * Therefore:
830  * 3. After dequeueing the head event from the track at heap root, the next
831  *    event's time is directly comparable to the root's children.
832  * These properties allow the heap to be maintained with delta times throughout.
833  * Insert is also implementable, but not needed: all the tracks are present
834  * at first; they just go away as they end.
835  */
836 
837 #define PARENT(i) ((i - 1) >> 1)
838 #define LEFT(i)   ((i << 1) + 1)
839 #define RIGHT(i)  ((i + 1) << 1)
840 #define DTIME(i)  (t[i].indirect->delta)
841 #define SWAP(i, j) do { \
842     struct track *_t = t[i].indirect; \
843     t[i].indirect = t[j].indirect; \
844     t[j].indirect = _t; \
845 } while (0)
846 
847 static void
Heapify(struct track * t,int ntrks,int node)848 Heapify(struct track *t, int ntrks, int node)
849 {
850 	int lc, rc, mn;
851 
852 	lc = LEFT(node);
853 	rc = RIGHT(node);
854 
855 	if (rc >= ntrks) {			/* no right child */
856 		if (lc >= ntrks)		/* node is a leaf */
857 			return;
858 		if (DTIME(node) > DTIME(lc))
859 			SWAP(node, lc);
860 		DTIME(lc) -= DTIME(node);
861 		return;				/* no rc ==> lc is a leaf */
862 	}
863 
864 	mn = lc;
865 	if (DTIME(lc) > DTIME(rc))
866 		mn = rc;
867 	if (DTIME(node) <= DTIME(mn)) {
868 		DTIME(rc) -= DTIME(node);
869 		DTIME(lc) -= DTIME(node);
870 		return;
871 	}
872 
873 	SWAP(node, mn);
874 	DTIME(rc) -= DTIME(node);
875 	DTIME(lc) -= DTIME(node);
876 	Heapify(t, ntrks, mn); /* gcc groks tail recursion */
877 }
878 
879 static void
BuildHeap(struct track * t,int ntrks)880 BuildHeap(struct track *t, int ntrks)
881 {
882 	int node;
883 
884 	for (node = PARENT(ntrks - 1); node --> 0;)
885 		Heapify(t, ntrks, node);
886 }
887 
888 /*
889  * Make the heap 1 item smaller by discarding the track at the root. Move the
890  * rightmost bottom-level leaf to the root and decrement ntrks. It remains to
891  * run Heapify, which the caller is expected to do. Returns the new ntrks.
892  */
893 static int
ShrinkHeap(struct track * t,int ntrks)894 ShrinkHeap(struct track *t, int ntrks)
895 {
896 	int ancest;
897 
898 	--ntrks;
899 	for (ancest = PARENT(ntrks); ancest > 0; ancest = PARENT(ancest))
900 		DTIME(ntrks) += DTIME(ancest);
901 	t[0].indirect = t[ntrks].indirect;
902 	return ntrks;
903 }
904