1 /* $NetBSD: midiplay.c,v 1.29 2011/11/25 01:39:47 jmcneill 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.29 2011/11/25 01:39:47 jmcneill 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 #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 static void __attribute__((__noreturn__)) 149 usage(void) 150 { 151 fprintf(stderr, "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 static int showmeta = 0; 158 static int verbose = 0; 159 #define BASETEMPO 400000 /* us/beat(=24 clks or qn) (150 bpm) */ 160 static u_int tempo_set = 0; 161 static u_int tempo_abs = 0; 162 static u_int ttempo = 100; 163 static int unit = 0; 164 static int play = 1; 165 static int fd = -1; 166 static int sameprogram = 0; 167 static int insysex = 0; 168 static int svsysex = 0; /* number of sysex bytes saved internally */ 169 170 static 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 static 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 static 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 static 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 #if 0 279 static void 280 midireset(void) 281 { 282 /* General MIDI reset sequence */ 283 send_event(&SEQ_MK_SYSEX0(unit, 0x7e, 0x7f, 0x09, 0x01, 0xf7, 0xff)); 284 } 285 #endif 286 287 #define SYSEX_CHUNK 6 288 static void 289 send_sysex(u_char *p, u_int l) 290 { 291 seq_event_t event; 292 static u_char bf[6]; 293 294 if (0 == l) { 295 warnx("zero-length system-exclusive event"); 296 return; 297 } 298 299 /* 300 * This block is needed only to handle the possibility that a sysex 301 * message is broken into multiple events in a MIDI file that do not 302 * have length six; the /dev/music sequencer assumes a sysex message is 303 * finished with the first SYSEX event carrying fewer than six bytes, 304 * even if the last is not MIDI_SYSEX_END. So, we need to be careful 305 * not to send a short sysex event until we have seen the end byte. 306 * Instead, save some straggling bytes in bf, and send when we have a 307 * full six (or an end byte). Note bf/saved/insysex should be per- 308 * device, if we supported output to more than one device at a time. 309 */ 310 if (svsysex > 0) { 311 if (l > sizeof bf - svsysex) { 312 memcpy(bf + svsysex, p, sizeof bf - svsysex); 313 l -= sizeof bf - svsysex; 314 p += sizeof bf - svsysex; 315 send_event(&SEQ_MK_SYSEX0(unit, 316 bf[0], bf[1], bf[2], bf[3], bf[4], bf[5])); 317 svsysex = 0; 318 } else { 319 memcpy(bf + svsysex, p, l); 320 svsysex += l; 321 p += l; 322 if (MIDI_SYSEX_END == bf[svsysex-1]) { 323 event = SEQ_MK_SYSEX(unit); 324 memcpy(event.sysex.buffer, bf, svsysex); 325 send_event(&event); 326 svsysex = insysex = 0; 327 } else 328 insysex = 1; 329 return; 330 } 331 } 332 333 /* 334 * l > 0. May as well test now whether we will be left 'insysex' 335 * after processing this event. 336 */ 337 insysex = (MIDI_SYSEX_END != p[l-1]); 338 339 /* 340 * If not for multi-event sysexes and chunk-size weirdness, this 341 * function could pretty much start here. :) 342 */ 343 while (l >= SYSEX_CHUNK) { 344 send_event(&SEQ_MK_SYSEX0(unit, p[0], p[1], p[2], p[3], p[4], p[5])); 345 p += SYSEX_CHUNK; 346 l -= SYSEX_CHUNK; 347 } 348 if (l > 0) { 349 if (insysex) { 350 memcpy(bf, p, l); 351 svsysex = l; 352 } else { /* a <6 byte chunk is ok if it's REALLY the end */ 353 event = SEQ_MK_SYSEX(unit); 354 memcpy(event.sysex.buffer, p, l); 355 send_event(&event); 356 } 357 } 358 } 359 360 static void 361 playfile(FILE *f, const char *name) 362 { 363 u_char *buf, *nbuf; 364 u_int tot, n, size, nread; 365 366 /* 367 * We need to read the whole file into memory for easy processing. 368 * Using mmap() would be nice, but some file systems do not support 369 * it, nor does reading from e.g. a pipe. The latter also precludes 370 * finding out the file size without reading it. 371 */ 372 size = 1000; 373 buf = malloc(size); 374 if (buf == 0) 375 errx(1, "malloc() failed"); 376 nread = size; 377 tot = 0; 378 for (;;) { 379 n = fread(buf + tot, 1, nread, f); 380 tot += n; 381 if (n < nread) 382 break; 383 /* There must be more to read. */ 384 nread = size; 385 nbuf = realloc(buf, size * 2); 386 if (nbuf == NULL) 387 errx(1, "realloc() failed"); 388 buf = nbuf; 389 size *= 2; 390 } 391 playdata(buf, tot, name); 392 free(buf); 393 } 394 395 static void 396 playdata(u_char *buf, u_int tot, const char *name) 397 { 398 int format, ntrks, divfmt, ticks, t; 399 u_int len, mlen, status, chan; 400 u_char *p, *end, byte, meta, *msg; 401 struct synth_info info; 402 struct track *tracks; 403 struct track *tp; 404 405 /* verify that the requested midi unit exists */ 406 info.device = unit; 407 if (ioctl(fd, SEQUENCER_INFO, &info) < 0) 408 err(1, "ioctl(SEQUENCER_INFO) failed"); 409 410 end = buf + tot; 411 if (verbose) 412 printf("Playing %s (%d bytes) on %s (unit %d)... \n", 413 name, tot, info.name, info.device); 414 415 if (tot < MARK_LEN + 4) { 416 warnx("Not a MIDI file, too short"); 417 return; 418 } 419 420 if (memcmp(buf, RMID_SIG, MARK_LEN) == 0) { 421 u_char *eod; 422 /* Detected a RMID file, let's just check if it's 423 * a MIDI file */ 424 if ((u_int)GET32_LE(buf + MARK_LEN) != tot - 8) { 425 warnx("Not a RMID file, bad header"); 426 return; 427 } 428 429 buf += MARK_LEN + 4; 430 if (memcmp(buf, RMID_MIDI_ID, MARK_LEN) != 0) { 431 warnx("Not a RMID file, bad ID"); 432 return; 433 } 434 435 /* Now look for the 'data' chunk, which contains 436 * MIDI data */ 437 buf += MARK_LEN; 438 439 /* Test against end-8 since we must have at least 8 bytes 440 * left to read */ 441 while(buf < end-8 && memcmp(buf, RMID_DATA_ID, MARK_LEN)) 442 buf += GET32_LE(buf+4) + 8; /* MARK_LEN + 4 */ 443 444 if (buf >= end-8) { 445 warnx("Not a valid RMID file, no data chunk"); 446 return; 447 } 448 449 buf += MARK_LEN; /* "data" */ 450 eod = buf + 4 + GET32_LE(buf); 451 if (eod >= end) { 452 warnx("Not a valid RMID file, bad data chunk size"); 453 return; 454 } 455 456 end = eod; 457 buf += 4; 458 } 459 460 if (memcmp(buf, MARK_HEADER, MARK_LEN) != 0) { 461 warnx("Not a MIDI file, missing header"); 462 return; 463 } 464 465 if (GET32(buf + MARK_LEN) != HEADER_LEN) { 466 warnx("Not a MIDI file, bad header"); 467 return; 468 } 469 format = GET16(buf + MARK_LEN + SIZE_LEN); 470 ntrks = GET16(buf + MARK_LEN + SIZE_LEN + 2); 471 divfmt = GET8(buf + MARK_LEN + SIZE_LEN + 4); 472 ticks = GET8(buf + MARK_LEN + SIZE_LEN + 5); 473 p = buf + MARK_LEN + SIZE_LEN + HEADER_LEN; 474 /* 475 * Set the timebase (or timebase and tempo, for absolute-timed files). 476 * PORTABILITY: some sequencers actually check the timebase against 477 * available timing sources and may adjust it accordingly (storing a 478 * new value in the ioctl arg) which would require us to compensate 479 * somehow. That possibility is ignored for now, as NetBSD's sequencer 480 * currently synthesizes all timebases, for better or worse, from the 481 * system clock. 482 * 483 * For a non-absolute file, if timebase is set to the file's divisions 484 * value, and tempo set in the obvious way, then the timing deltas in 485 * the MTrks require no scaling. A downside to this approach is that 486 * the sequencer API wants tempo in (integer) beats per minute, which 487 * limits how finely tempo can be specified. That might be got around 488 * in some cases by frobbing tempo and timebase more obscurely, but this 489 * player is meant to be simple and clear. 490 */ 491 if ((divfmt & 0x80) == 0) { 492 ticks |= divfmt << 8; 493 if (ioctl(fd, SEQUENCER_TMR_TIMEBASE, &(int){ticks}) < 0) 494 err(1, "SEQUENCER_TMR_TIMEBASE"); 495 } else { 496 tempo_abs = tempo_set = 1; 497 divfmt = -(int8_t)divfmt; 498 /* 499 * divfmt is frames per second; multiplying by 60 to set tempo 500 * in frames per minute could exceed sequencer's (arbitrary) 501 * tempo limits, so factor 60 as 12*5, set tempo in frames per 502 * 12 seconds, and account for the 5 in timebase. 503 */ 504 send_event(&SEQ_MK_TIMING(TEMPO, 505 .bpm=(12*divfmt) * (ttempo/100.) + 0.5)); 506 if (ioctl(fd, SEQUENCER_TMR_TIMEBASE, &(int){5*ticks}) < 0) 507 err(1, "SEQUENCER_TMR_TIMEBASE"); 508 } 509 if (verbose > 1) 510 printf(tempo_abs ? 511 "format=%d ntrks=%d abs fps=%u subdivs=%u\n" : 512 "format=%d ntrks=%d divisions=%u\n", 513 format, ntrks, tempo_abs ? divfmt : ticks, ticks); 514 if (format != 0 && format != 1) { 515 warnx("Cannot play MIDI file of type %d", format); 516 return; 517 } 518 if (ntrks == 0) 519 return; 520 tracks = malloc(ntrks * sizeof(struct track)); 521 if (tracks == NULL) 522 errx(1, "malloc() tracks failed"); 523 for (t = 0; t < ntrks;) { 524 if (p >= end - MARK_LEN - SIZE_LEN) { 525 warnx("Cannot find track %d", t); 526 goto ret; 527 } 528 len = GET32(p + MARK_LEN); 529 if (len > 1000000) { /* a safe guard */ 530 warnx("Crazy track length"); 531 goto ret; 532 } 533 if (memcmp(p, MARK_TRACK, MARK_LEN) == 0) { 534 tracks[t].start = p + MARK_LEN + SIZE_LEN; 535 tracks[t].end = tracks[t].start + len; 536 tracks[t].delta = getvar(&tracks[t]); 537 tracks[t].indirect = &tracks[t]; /* -> self for now */ 538 t++; 539 } 540 p += MARK_LEN + SIZE_LEN + len; 541 } 542 543 /* 544 * Force every channel to the same patch if requested by the user. 545 */ 546 if (sameprogram) { 547 for(t = 0; t < 16; t++) { 548 send_event(&SEQ_MK_CHN(PGM_CHANGE, .device=unit, 549 .channel=t, .program=sameprogram-1)); 550 } 551 } 552 /* 553 * Play MIDI events by selecting the track with the lowest 554 * delta. Execute the event, update the delta and repeat. 555 * 556 * The ticks variable is the number of ticks that make up a beat 557 * (beat: 24 MIDI clocks always, a quarter note by usual convention) 558 * and is used as a reference value for the delays between 559 * the MIDI events. 560 */ 561 BuildHeap(tracks, ntrks); /* tracks[0].indirect is always next */ 562 for (;;) { 563 tp = tracks[0].indirect; 564 if ((verbose > 2 && tp->delta > 0) || verbose > 3) { 565 printf("DELAY %4ld TRACK %2td%s", 566 tp->delta, tp - tracks, verbose>3?" ":"\n"); 567 fflush(stdout); 568 } 569 if (tp->delta > 0) { 570 if (!tempo_set) { 571 if (verbose || showmeta) 572 printf("No initial tempo;" 573 " defaulting:\n"); 574 dometa(META_SET_TEMPO, (u_char[]){ 575 BASETEMPO >> 16, 576 (BASETEMPO >> 8) & 0xff, 577 BASETEMPO & 0xff}, 578 3); 579 } 580 send_event(&SEQ_MK_TIMING(WAIT_REL, 581 .divisions=tp->delta)); 582 } 583 byte = *tp->start++; 584 if (byte == MIDI_META) { 585 meta = *tp->start++; 586 mlen = getlen(tp); 587 if (verbose > 3) 588 printf("META %02x (%d)\n", meta, mlen); 589 dometa(meta, tp->start, mlen); 590 tp->start += mlen; 591 } else { 592 if (MIDI_IS_STATUS(byte)) 593 tp->status = byte; 594 else 595 tp->start--; 596 mlen = MIDI_LENGTH(tp->status); 597 msg = tp->start; 598 if (verbose > 3) { 599 if (mlen == 1) 600 printf("MIDI %02x (%d) %02x\n", 601 tp->status, mlen, msg[0]); 602 else 603 printf("MIDI %02x (%d) %02x %02x\n", 604 tp->status, mlen, msg[0], msg[1]); 605 } 606 if (insysex && tp->status != MIDI_SYSEX_END) { 607 warnx("incomplete system exclusive message" 608 " aborted"); 609 svsysex = insysex = 0; 610 } 611 status = MIDI_GET_STATUS(tp->status); 612 chan = MIDI_GET_CHAN(tp->status); 613 switch (status) { 614 case MIDI_NOTEOFF: 615 send_event(&SEQ_MK_CHN(NOTEOFF, .device=unit, 616 .channel=chan, .key=msg[0], .velocity=msg[1])); 617 break; 618 case MIDI_NOTEON: 619 send_event(&SEQ_MK_CHN(NOTEON, .device=unit, 620 .channel=chan, .key=msg[0], .velocity=msg[1])); 621 break; 622 case MIDI_KEY_PRESSURE: 623 send_event(&SEQ_MK_CHN(KEY_PRESSURE, 624 .device=unit, .channel=chan, 625 .key=msg[0], .pressure=msg[1])); 626 break; 627 case MIDI_CTL_CHANGE: 628 send_event(&SEQ_MK_CHN(CTL_CHANGE, 629 .device=unit, .channel=chan, 630 .controller=msg[0], .value=msg[1])); 631 break; 632 case MIDI_PGM_CHANGE: 633 if (!sameprogram) 634 send_event(&SEQ_MK_CHN(PGM_CHANGE, 635 .device=unit, .channel=chan, 636 .program=msg[0])); 637 break; 638 case MIDI_CHN_PRESSURE: 639 send_event(&SEQ_MK_CHN(CHN_PRESSURE, 640 .device=unit, .channel=chan, .pressure=msg[0])); 641 break; 642 case MIDI_PITCH_BEND: 643 send_event(&SEQ_MK_CHN(PITCH_BEND, 644 .device=unit, .channel=chan, 645 .value=(msg[0] & 0x7f) | ((msg[1] & 0x7f)<<7))); 646 break; 647 case MIDI_SYSTEM_PREFIX: 648 mlen = getlen(tp); 649 if (tp->status == MIDI_SYSEX_START) { 650 send_sysex(tp->start, mlen); 651 break; 652 } else if (tp->status == MIDI_SYSEX_END) { 653 /* SMF uses SYSEX_END as CONTINUATION/ESCAPE */ 654 if (insysex) { /* CONTINUATION */ 655 send_sysex(tp->start, mlen); 656 } else { /* ESCAPE */ 657 for (; mlen > 0 ; -- mlen) { 658 send_event( 659 &SEQ_MK_EVENT(putc, 660 SEQOLD_MIDIPUTC, 661 .device=unit, 662 .byte=*(tp->start++) 663 )); 664 } 665 } 666 break; 667 } 668 /* Sorry, can't do this yet; FALLTHROUGH */ 669 default: 670 if (verbose) 671 printf("MIDI event 0x%02x ignored\n", 672 tp->status); 673 } 674 tp->start += mlen; 675 } 676 if (tp->start >= tp->end) { 677 ntrks = ShrinkHeap(tracks, ntrks); /* track gone */ 678 if (0 == ntrks) 679 break; 680 } else 681 tp->delta = getvar(tp); 682 Heapify(tracks, ntrks, 0); 683 } 684 if (ioctl(fd, SEQUENCER_SYNC, 0) < 0) 685 err(1, "SEQUENCER_SYNC"); 686 687 ret: 688 free(tracks); 689 } 690 691 static int 692 parse_unit(const char *sunit) 693 { 694 const char *osunit = sunit; 695 long n; 696 char *ep; 697 698 if (strncmp(sunit, "midi", strlen("midi")) == 0) 699 sunit += strlen("midi"); 700 701 errno = 0; 702 n = strtol(sunit, &ep, 10); 703 if (n < 0 || n > INT_MAX || *ep != '\0' || 704 (errno == ERANGE && 705 (n == LONG_MAX || n == LONG_MIN))) 706 errx(1, "bad midi unit -- %s", osunit); 707 708 return (int)n; 709 } 710 711 int 712 main(int argc, char **argv) 713 { 714 int ch; 715 int listdevs = 0; 716 int example = 0; 717 int nmidi; 718 const char *file = DEVMUSIC; 719 const char *sunit; 720 struct synth_info info; 721 FILE *f; 722 723 if ((sunit = getenv("MIDIUNIT"))) 724 unit = parse_unit(sunit); 725 726 while ((ch = getopt(argc, argv, "?d:f:lmp:qt:vx")) != -1) { 727 switch(ch) { 728 case 'd': 729 unit = parse_unit(optarg); 730 break; 731 case 'f': 732 file = optarg; 733 break; 734 case 'l': 735 listdevs++; 736 break; 737 case 'm': 738 showmeta++; 739 break; 740 case 'p': 741 sameprogram = atoi(optarg); 742 break; 743 case 'q': 744 play = 0; 745 break; 746 case 't': 747 ttempo = atoi(optarg); 748 break; 749 case 'v': 750 verbose++; 751 break; 752 case 'x': 753 example++; 754 break; 755 case '?': 756 default: 757 usage(); 758 } 759 } 760 argc -= optind; 761 argv += optind; 762 763 if (!play) 764 goto output; 765 766 fd = open(file, O_WRONLY); 767 if (fd < 0) 768 err(1, "%s", file); 769 if (ioctl(fd, SEQUENCER_NRMIDIS, &nmidi) < 0) 770 err(1, "ioctl(SEQUENCER_NRMIDIS) failed, "); 771 if (nmidi == 0) 772 errx(1, "Sorry, no MIDI devices available"); 773 if (listdevs) { 774 for (info.device = 0; info.device < nmidi; info.device++) { 775 if (ioctl(fd, SEQUENCER_INFO, &info) < 0) 776 err(1, "ioctl(SEQUENCER_INFO) failed, "); 777 printf("%d: %s\n", info.device, info.name); 778 } 779 exit(0); 780 } 781 782 output: 783 if (example) 784 while (example--) 785 playdata(sample, sizeof sample, "<Gubben Noa>"); 786 else if (argc == 0) 787 playfile(stdin, "<stdin>"); 788 else 789 while (argc--) { 790 f = fopen(*argv, "r"); 791 if (f == NULL) 792 err(1, "%s", *argv); 793 else { 794 playfile(f, *argv); 795 fclose(f); 796 } 797 argv++; 798 } 799 800 exit(0); 801 } 802 803 /* 804 * relative-time priority queue (min-heap). Properties: 805 * 1. The delta time at a node is relative to the node's parent's time. 806 * 2. When an event is dequeued from a track, the delta time of the new head 807 * event is relative to the time of the event just dequeued. 808 * Therefore: 809 * 3. After dequeueing the head event from the track at heap root, the next 810 * event's time is directly comparable to the root's children. 811 * These properties allow the heap to be maintained with delta times throughout. 812 * Insert is also implementable, but not needed: all the tracks are present 813 * at first; they just go away as they end. 814 */ 815 816 #define PARENT(i) ((i - 1) >> 1) 817 #define LEFT(i) ((i << 1) + 1) 818 #define RIGHT(i) ((i + 1) << 1) 819 #define DTIME(i) (t[i].indirect->delta) 820 #define SWAP(i, j) do { \ 821 struct track *_t = t[i].indirect; \ 822 t[i].indirect = t[j].indirect; \ 823 t[j].indirect = _t; \ 824 } while (/*CONSTCOND*/ 0) 825 826 static void 827 Heapify(struct track *t, int ntrks, int node) 828 { 829 int lc, rc, mn; 830 831 lc = LEFT(node); 832 rc = RIGHT(node); 833 834 if (rc >= ntrks) { /* no right child */ 835 if (lc >= ntrks) /* node is a leaf */ 836 return; 837 if (DTIME(node) > DTIME(lc)) 838 SWAP(node, lc); 839 DTIME(lc) -= DTIME(node); 840 return; /* no rc ==> lc is a leaf */ 841 } 842 843 mn = lc; 844 if (DTIME(lc) > DTIME(rc)) 845 mn = rc; 846 if (DTIME(node) <= DTIME(mn)) { 847 DTIME(rc) -= DTIME(node); 848 DTIME(lc) -= DTIME(node); 849 return; 850 } 851 852 SWAP(node, mn); 853 DTIME(rc) -= DTIME(node); 854 DTIME(lc) -= DTIME(node); 855 Heapify(t, ntrks, mn); /* gcc groks tail recursion */ 856 } 857 858 static void 859 BuildHeap(struct track *t, int ntrks) 860 { 861 int node; 862 863 for (node = PARENT(ntrks - 1); node --> 0;) 864 Heapify(t, ntrks, node); 865 } 866 867 /* 868 * Make the heap 1 item smaller by discarding the track at the root. Move the 869 * rightmost bottom-level leaf to the root and decrement ntrks. It remains to 870 * run Heapify, which the caller is expected to do. Returns the new ntrks. 871 */ 872 static int 873 ShrinkHeap(struct track *t, int ntrks) 874 { 875 int ancest; 876 877 --ntrks; 878 for (ancest = PARENT(ntrks); ancest > 0; ancest = PARENT(ancest)) 879 DTIME(ntrks) += DTIME(ancest); 880 t[0].indirect = t[ntrks].indirect; 881 return ntrks; 882 } 883