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