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