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