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