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