1 /* $NetBSD: midirecord.c,v 1.12 2017/06/03 21:31:14 mrg Exp $ */ 2 3 /* 4 * Copyright (c) 2014, 2015, 2017 Matthew R. Green 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 30 * midirecord(1), similar to audiorecord(1). 31 */ 32 33 #include <sys/cdefs.h> 34 35 #ifndef lint 36 __RCSID("$NetBSD: midirecord.c,v 1.12 2017/06/03 21:31:14 mrg Exp $"); 37 #endif 38 39 #include <sys/param.h> 40 #include <sys/midiio.h> 41 #include <sys/ioctl.h> 42 #include <sys/time.h> 43 #include <sys/uio.h> 44 45 #include <err.h> 46 #include <errno.h> 47 #include <ctype.h> 48 #include <fcntl.h> 49 #include <paths.h> 50 #include <signal.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <util.h> 56 #include <stdbool.h> 57 58 #include "libaudio.h" 59 60 static const char *midi_device; 61 static unsigned *filt_devnos = NULL; 62 static unsigned *filt_chans = NULL; 63 static unsigned num_filt_devnos, num_filt_chans; 64 static char *raw_output; 65 static int midifd; 66 static int aflag, qflag, oflag; 67 static bool debug = false; 68 int verbose; 69 static int outfd, rawfd = -1; 70 static ssize_t data_size; 71 static struct timeval record_time; 72 static struct timeval start_time; 73 static int tempo = 120; 74 static unsigned round_beats = 1; 75 static unsigned notes_per_beat = 24; 76 static bool ignore_timer_fail = false; 77 static bool stdout_mode = false; 78 79 static void debug_log(const char *, size_t, const char *, ...) 80 __printflike(3, 4); 81 static size_t midi_event_local_to_output(seq_event_t, u_char *, size_t); 82 static size_t midi_event_timer_wait_abs_to_output(seq_event_t, u_char *, 83 size_t); 84 static size_t midi_event_timer_to_output(seq_event_t, u_char *, size_t); 85 static size_t midi_event_chn_common_to_output(seq_event_t, u_char *, size_t); 86 static size_t midi_event_chn_voice_to_output(seq_event_t, u_char *, size_t); 87 static size_t midi_event_sysex_to_output(seq_event_t, u_char *, size_t); 88 static size_t midi_event_fullsize_to_output(seq_event_t, u_char *, size_t); 89 static size_t midi_event_to_output(seq_event_t, u_char *, size_t); 90 static int timeleft(struct timeval *, struct timeval *); 91 static bool filter_array(unsigned, unsigned *, size_t); 92 static bool filter_dev(unsigned); 93 static bool filter_chan(unsigned); 94 static bool filter_devchan(unsigned, unsigned); 95 static void parse_ints(const char *, unsigned **, unsigned *, const char *); 96 static void cleanup(int) __dead; 97 static void rewrite_header(void); 98 static void write_midi_header(void); 99 static void write_midi_trailer(void); 100 static void usage(void) __dead; 101 102 #define PATH_DEV_MUSIC "/dev/music" 103 104 int 105 main(int argc, char *argv[]) 106 { 107 u_char *buffer; 108 size_t bufsize = 0; 109 int ch, no_time_limit = 1; 110 111 while ((ch = getopt(argc, argv, "aB:c:Dd:f:hn:oqr:t:T:V")) != -1) { 112 switch (ch) { 113 case 'a': 114 aflag++; 115 break; 116 case 'B': 117 bufsize = strsuftoll("read buffer size", optarg, 118 1, UINT_MAX); 119 break; 120 case 'c': 121 parse_ints(optarg, &filt_chans, &num_filt_chans, 122 "channels"); 123 break; 124 case 'D': 125 debug = true; 126 break; 127 case 'd': 128 parse_ints(optarg, &filt_devnos, &num_filt_devnos, 129 "devices"); 130 break; 131 case 'f': 132 midi_device = optarg; 133 ignore_timer_fail = true; 134 break; 135 case 'n': 136 decode_uint(optarg, ¬es_per_beat); 137 break; 138 case 'o': 139 oflag++; /* time stamp starts at proc start */ 140 break; 141 case 'q': 142 qflag++; 143 break; 144 case 'r': 145 raw_output = optarg; 146 break; 147 case 'R': 148 decode_uint(optarg, &round_beats); 149 if (round_beats == 0) 150 errx(1, "-R <round_beats> must be a positive integer"); 151 break; 152 case 't': 153 no_time_limit = 0; 154 decode_time(optarg, &record_time); 155 break; 156 case 'T': 157 decode_int(optarg, &tempo); 158 break; 159 case 'V': 160 verbose++; 161 break; 162 /* case 'h': */ 163 default: 164 usage(); 165 /* NOTREACHED */ 166 } 167 } 168 argc -= optind; 169 argv += optind; 170 171 if (argc != 1) 172 usage(); 173 174 /* 175 * work out the buffer size to use, and allocate it. don't allow it 176 * to be too small. 177 */ 178 if (bufsize < 32) 179 bufsize = 32 * 1024; 180 buffer = malloc(bufsize); 181 if (buffer == NULL) 182 err(1, "couldn't malloc buffer of %d size", (int)bufsize); 183 184 /* 185 * open the music device 186 */ 187 if (midi_device == NULL && (midi_device = getenv("MIDIDEVICE")) == NULL) 188 midi_device = PATH_DEV_MUSIC; 189 midifd = open(midi_device, O_RDONLY); 190 if (midifd < 0) 191 err(1, "failed to open %s", midi_device); 192 193 /* open the output file */ 194 if (argv[0][0] != '-' || argv[0][1] != '\0') { 195 int mode = O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY; 196 197 outfd = open(*argv, mode, 0666); 198 if (outfd < 0) 199 err(1, "could not open %s", *argv); 200 } else { 201 stdout_mode = true; 202 outfd = STDOUT_FILENO; 203 } 204 205 /* open the raw output file */ 206 if (raw_output) { 207 int mode = O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY; 208 209 rawfd = open(raw_output, mode, 0666); 210 if (rawfd < 0) 211 err(1, "could not open %s", raw_output); 212 } 213 214 /* start the midi timer */ 215 if (ioctl(midifd, SEQUENCER_TMR_START, NULL) < 0) { 216 if (ignore_timer_fail) 217 warn("failed to start midi timer"); 218 else 219 err(1, "failed to start midi timer"); 220 } 221 222 /* set the timebase */ 223 if (ioctl(midifd, SEQUENCER_TMR_TIMEBASE, ¬es_per_beat) < 0) { 224 if (ignore_timer_fail) 225 warn("SEQUENCER_TMR_TIMEBASE: notes_per_beat %d", 226 notes_per_beat); 227 else 228 err(1, "SEQUENCER_TMR_TIMEBASE: notes_per_beat %d", 229 notes_per_beat); 230 } 231 232 /* set the tempo */ 233 if (ioctl(midifd, SEQUENCER_TMR_TEMPO, &tempo) < 0) { 234 if (ignore_timer_fail) 235 warn("SEQUENCER_TMR_TIMEBASE: tempo %d", tempo); 236 else 237 err(1, "SEQUENCER_TMR_TIMEBASE: tempo %d", tempo); 238 } 239 240 signal(SIGINT, cleanup); 241 242 data_size = 0; 243 244 if (verbose) 245 fprintf(stderr, "tempo=%d notes_per_beat=%u\n", 246 tempo, notes_per_beat); 247 248 if (!no_time_limit && verbose) 249 fprintf(stderr, "recording for %lu seconds, %lu microseconds\n", 250 (u_long)record_time.tv_sec, (u_long)record_time.tv_usec); 251 252 /* Create a midi header. */ 253 write_midi_header(); 254 255 (void)gettimeofday(&start_time, NULL); 256 while (no_time_limit || timeleft(&start_time, &record_time)) { 257 seq_event_t e; 258 size_t wrsize; 259 size_t rdsize; 260 261 rdsize = (size_t)read(midifd, &e, sizeof e); 262 if (rdsize == 0) 263 break; 264 265 if (rdsize != sizeof e) 266 err(1, "read failed"); 267 268 if (rawfd != -1 && write(rawfd, &e, sizeof e) != sizeof e) 269 err(1, "write to raw file failed"); 270 271 /* convert 'e' into something useful for output */ 272 wrsize = midi_event_to_output(e, buffer, bufsize); 273 274 if (wrsize) { 275 if ((size_t)write(outfd, buffer, wrsize) != wrsize) 276 err(1, "write failed"); 277 data_size += wrsize; 278 } 279 } 280 cleanup(0); 281 } 282 283 static void 284 debug_log(const char *file, size_t line, const char *fmt, ...) 285 { 286 va_list ap; 287 288 if (!debug) 289 return; 290 fprintf(stderr, "%s:%zu: ", file, line); 291 va_start(ap, fmt); 292 vfprintf(stderr, fmt, ap); 293 va_end(ap); 294 fprintf(stderr, "\n"); 295 } 296 297 #define LOG(fmt...) \ 298 debug_log(__func__, __LINE__, fmt) 299 300 301 /* 302 * handle a SEQ_LOCAL event. NetBSD /dev/music doesn't generate these. 303 */ 304 static size_t 305 midi_event_local_to_output(seq_event_t e, u_char *buffer, size_t bufsize) 306 { 307 size_t size = 0; 308 309 LOG("UNHANDLED SEQ_LOCAL"); 310 311 return size; 312 } 313 314 /* 315 * convert a midi absolute time event to a variable length delay 316 */ 317 static size_t 318 midi_event_timer_wait_abs_to_output( 319 seq_event_t e, 320 u_char *buffer, 321 size_t bufsize) 322 { 323 static unsigned prev_div; 324 static int prev_leftover; 325 unsigned cur_div; 326 unsigned val = 0, xdiv; 327 int vallen = 0, i; 328 329 if (bufsize < 4) { 330 warnx("too small bufsize: %zu", bufsize); 331 return 0; 332 } 333 334 if (prev_div == 0 && !oflag) 335 prev_div = e.t_WAIT_ABS.divisions; 336 cur_div = e.t_WAIT_ABS.divisions; 337 338 xdiv = cur_div - prev_div + prev_leftover; 339 if (round_beats != 1) { 340 // round to closest 341 prev_leftover = xdiv % round_beats; 342 xdiv -= prev_leftover; 343 if (verbose) 344 fprintf(stderr, "adjusted beat value to %x (leftover = %d)\n", 345 xdiv, prev_leftover); 346 } 347 if (xdiv) { 348 while (xdiv) { 349 uint32_t extra = val ? 0x80 : 0; 350 351 val <<= 8; 352 val |= (xdiv & 0x7f) | extra; 353 xdiv >>= 7; 354 vallen++; 355 } 356 } else 357 vallen = 1; 358 359 for (i = 0; i < vallen; i++) { 360 buffer[i] = val & 0xff; 361 val >>= 8; 362 } 363 for (; i < 4; i++) 364 buffer[i] = 0; 365 LOG("TMR_WAIT_ABS: new div %x (cur %x prev %x): bufval (len=%u): " 366 "%02x:%02x:%02x:%02x", 367 cur_div - prev_div, cur_div, prev_div, 368 vallen, buffer[0], buffer[1], buffer[2], buffer[3]); 369 370 prev_div = cur_div; 371 372 return vallen; 373 } 374 375 /* 376 * handle a SEQ_TIMING event. 377 */ 378 static size_t 379 midi_event_timer_to_output(seq_event_t e, u_char *buffer, size_t bufsize) 380 { 381 size_t size = 0; 382 383 LOG("SEQ_TIMING"); 384 switch (e.timing.op) { 385 case TMR_WAIT_REL: 386 /* NetBSD /dev/music doesn't generate these. */ 387 LOG("UNHANDLED TMR_WAIT_REL: divisions: %x", e.t_WAIT_REL.divisions); 388 break; 389 390 case TMR_WAIT_ABS: 391 size = midi_event_timer_wait_abs_to_output(e, buffer, bufsize); 392 break; 393 394 case TMR_STOP: 395 case TMR_START: 396 case TMR_CONTINUE: 397 case TMR_TEMPO: 398 case TMR_ECHO: 399 case TMR_CLOCK: 400 case TMR_SPP: 401 case TMR_TIMESIG: 402 /* NetBSD /dev/music doesn't generate these. */ 403 LOG("UNHANDLED timer op: %x", e.timing.op); 404 break; 405 406 default: 407 LOG("unknown timer op: %x", e.timing.op); 408 break; 409 } 410 411 return size; 412 } 413 414 /* 415 * handle a SEQ_CHN_COMMON event. 416 */ 417 static size_t 418 midi_event_chn_common_to_output(seq_event_t e, u_char *buffer, size_t bufsize) 419 { 420 size_t size = 0; 421 422 LOG("SEQ_CHN_COMMON"); 423 424 if (bufsize < 3) { 425 warnx("too small bufsize: %zu", bufsize); 426 return 0; 427 } 428 429 if (e.common.channel >= 16) { 430 warnx("invalid channel: %u", e.common.channel); 431 return 0; 432 } 433 434 if (filter_devchan(e.common.device, e.common.channel)) 435 return 0; 436 437 switch (e.common.op) { 438 case MIDI_CTL_CHANGE: 439 buffer[0] = MIDI_CTL_CHANGE | e.c_CTL_CHANGE.channel; 440 buffer[1] = e.c_CTL_CHANGE.controller; 441 buffer[2] = e.c_CTL_CHANGE.value; 442 LOG("MIDI_CTL_CHANGE: channel %x ctrl %x val %x", 443 e.c_CTL_CHANGE.channel, e.c_CTL_CHANGE.controller, 444 e.c_CTL_CHANGE.value); 445 size = 3; 446 break; 447 448 case MIDI_PGM_CHANGE: 449 buffer[0] = MIDI_PGM_CHANGE | e.c_PGM_CHANGE.channel; 450 buffer[1] = e.c_PGM_CHANGE.program; 451 LOG("MIDI_PGM_CHANGE: channel %x program %x", 452 e.c_PGM_CHANGE.channel, e.c_PGM_CHANGE.program); 453 size = 2; 454 break; 455 456 case MIDI_CHN_PRESSURE: 457 buffer[0] = MIDI_CHN_PRESSURE | e.c_CHN_PRESSURE.channel; 458 buffer[1] = e.c_CHN_PRESSURE.pressure; 459 LOG("MIDI_CHN_PRESSURE: channel %x pressure %x", 460 e.c_CHN_PRESSURE.channel, e.c_CHN_PRESSURE.pressure); 461 size = 2; 462 break; 463 464 case MIDI_PITCH_BEND: 465 buffer[0] = MIDI_PITCH_BEND | e.c_PITCH_BEND.channel; 466 /* 14 bits split over 2 data bytes, lsb first */ 467 buffer[1] = e.c_PITCH_BEND.value & 0x7f; 468 buffer[2] = (e.c_PITCH_BEND.value >> 7) & 0x7f; 469 LOG("MIDI_PITCH_BEND: channel %x val %x", 470 e.c_PITCH_BEND.channel, e.c_PITCH_BEND.value); 471 size = 3; 472 break; 473 474 default: 475 LOG("unknown common op: %x", e.voice.op); 476 break; 477 } 478 479 return size; 480 } 481 482 /* 483 * handle a SEQ_CHN_VOICE event. 484 */ 485 static size_t 486 midi_event_chn_voice_to_output(seq_event_t e, u_char *buffer, size_t bufsize) 487 { 488 size_t size = 0; 489 490 LOG("SEQ_CHN_VOICE"); 491 492 if (bufsize < 3) { 493 warnx("too small bufsize: %zu", bufsize); 494 return 0; 495 } 496 497 if (e.common.channel >= 16) { 498 warnx("invalid channel: %u", e.common.channel); 499 return 0; 500 } 501 502 if (filter_devchan(e.voice.device, e.voice.channel)) 503 return 0; 504 505 switch (e.voice.op) { 506 case MIDI_NOTEOFF: 507 buffer[0] = MIDI_NOTEOFF | e.c_NOTEOFF.channel; 508 buffer[1] = e.c_NOTEOFF.key; 509 buffer[2] = e.c_NOTEOFF.velocity; 510 511 LOG("MIDI_NOTEOFF: channel %x key %x velocity %x", 512 e.c_NOTEOFF.channel, e.c_NOTEOFF.key, e.c_NOTEOFF.velocity); 513 size = 3; 514 break; 515 516 case MIDI_NOTEON: 517 buffer[0] = MIDI_NOTEON | e.c_NOTEON.channel; 518 buffer[1] = e.c_NOTEON.key; 519 buffer[2] = e.c_NOTEON.velocity; 520 521 LOG("MIDI_NOTEON: channel %x key %x velocity %x", 522 e.c_NOTEON.channel, e.c_NOTEON.key, e.c_NOTEON.velocity); 523 size = 3; 524 break; 525 526 case MIDI_KEY_PRESSURE: 527 buffer[0] = MIDI_KEY_PRESSURE | e.c_KEY_PRESSURE.channel; 528 buffer[1] = e.c_KEY_PRESSURE.key; 529 buffer[2] = e.c_KEY_PRESSURE.pressure; 530 531 LOG("MIDI_KEY_PRESSURE: channel %x key %x pressure %x", 532 e.c_KEY_PRESSURE.channel, e.c_KEY_PRESSURE.key, 533 e.c_KEY_PRESSURE.pressure); 534 size = 3; 535 break; 536 537 default: 538 LOG("unknown voice op: %x", e.voice.op); 539 break; 540 } 541 542 return size; 543 } 544 545 /* 546 * handle a SEQ_SYSEX event. NetBSD /dev/music doesn't generate these. 547 */ 548 static size_t 549 midi_event_sysex_to_output(seq_event_t e, u_char *buffer, size_t bufsize) 550 { 551 size_t size = 0; 552 553 LOG("UNHANDLED SEQ_SYSEX"); 554 555 return size; 556 } 557 558 /* 559 * handle a SEQ_FULLSIZE event. NetBSD /dev/music doesn't generate these. 560 */ 561 static size_t 562 midi_event_fullsize_to_output(seq_event_t e, u_char *buffer, size_t bufsize) 563 { 564 size_t size = 0; 565 566 LOG("UNHANDLED SEQ_FULLSIZE"); 567 568 return size; 569 } 570 571 /* 572 * main handler for MIDI events. 573 */ 574 static size_t 575 midi_event_to_output(seq_event_t e, u_char *buffer, size_t bufsize) 576 { 577 size_t size = 0; 578 579 LOG("event: %02x:%02x:%02x:%02x %02x:%02x:%02x:%02x", e.tag, 580 e.unknown.byte[0], e.unknown.byte[1], 581 e.unknown.byte[2], e.unknown.byte[3], 582 e.unknown.byte[4], e.unknown.byte[5], 583 e.unknown.byte[6]); 584 585 switch (e.tag) { 586 case SEQ_LOCAL: 587 size = midi_event_local_to_output(e, buffer, bufsize); 588 break; 589 590 case SEQ_TIMING: 591 size = midi_event_timer_to_output(e, buffer, bufsize); 592 break; 593 594 case SEQ_CHN_COMMON: 595 size = midi_event_chn_common_to_output(e, buffer, bufsize); 596 break; 597 598 case SEQ_CHN_VOICE: 599 size = midi_event_chn_voice_to_output(e, buffer, bufsize); 600 break; 601 602 case SEQ_SYSEX: 603 size = midi_event_sysex_to_output(e, buffer, bufsize); 604 break; 605 606 case SEQ_FULLSIZE: 607 size = midi_event_fullsize_to_output(e, buffer, bufsize); 608 break; 609 610 default: 611 errx(1, "don't understand midi tag %x", e.tag); 612 } 613 614 return size; 615 } 616 617 static bool 618 filter_array(unsigned val, unsigned *array, size_t arraylen) 619 { 620 621 if (array == NULL) 622 return false; 623 624 for (; arraylen; arraylen--) 625 if (array[arraylen - 1] == val) 626 return false; 627 628 return true; 629 } 630 631 static bool 632 filter_dev(unsigned device) 633 { 634 635 if (filter_array(device, filt_devnos, num_filt_devnos)) 636 return true; 637 638 return false; 639 } 640 641 static bool 642 filter_chan(unsigned channel) 643 { 644 645 if (filter_array(channel, filt_chans, num_filt_chans)) 646 return true; 647 648 return false; 649 } 650 651 static bool 652 filter_devchan(unsigned device, unsigned channel) 653 { 654 655 if (filter_dev(device) || filter_chan(channel)) 656 return true; 657 658 return false; 659 } 660 661 static int 662 timeleft(struct timeval *start_tvp, struct timeval *record_tvp) 663 { 664 struct timeval now, diff; 665 666 (void)gettimeofday(&now, NULL); 667 timersub(&now, start_tvp, &diff); 668 timersub(record_tvp, &diff, &now); 669 670 return (now.tv_sec > 0 || (now.tv_sec == 0 && now.tv_usec > 0)); 671 } 672 673 static void 674 parse_ints(const char *str, unsigned **arrayp, unsigned *sizep, const char *msg) 675 { 676 unsigned count = 1, u, longest = 0, c = 0; 677 unsigned *ip; 678 const char *s, *os; 679 char *num_buf; 680 681 /* 682 * count all the comma separated values, and figre out 683 * the longest one. 684 */ 685 for (s = str; *s; s++) { 686 c++; 687 if (*s == ',') { 688 count++; 689 if (c > longest) 690 longest = c; 691 c = 0; 692 } 693 } 694 *sizep = count; 695 696 num_buf = malloc(longest + 1); 697 ip = malloc(sizeof(*ip) * count); 698 if (!ip || !num_buf) 699 errx(1, "malloc failed"); 700 701 for (count = 0, s = os = str, u = 0; *s; s++) { 702 if (*s == ',') { 703 num_buf[u] = '\0'; 704 decode_uint(num_buf, &ip[count++]); 705 os = s + 1; 706 u = 0; 707 } else 708 num_buf[u++] = *s; 709 } 710 num_buf[u] = '\0'; 711 decode_uint(num_buf, &ip[count++]); 712 *arrayp = ip; 713 714 if (verbose) { 715 fprintf(stderr, "Filtering %s in:", msg); 716 for (size_t i = 0; i < *sizep; i++) 717 fprintf(stderr, " %u", ip[i]); 718 fprintf(stderr, "\n"); 719 } 720 721 free(num_buf); 722 } 723 724 static void 725 cleanup(int signo) 726 { 727 728 write_midi_trailer(); 729 rewrite_header(); 730 731 if (ioctl(midifd, SEQUENCER_TMR_STOP, NULL) < 0) { 732 if (ignore_timer_fail) 733 warn("failed to stop midi timer"); 734 else 735 err(1, "failed to stop midi timer"); 736 } 737 738 if (close(outfd) != 0) 739 warn("couldn't close output"); 740 if (close(midifd) != 0) 741 warn("couldn't close midi device"); 742 if (signo != 0) 743 (void)raise_default_signal(signo); 744 745 exit(0); 746 } 747 748 static void 749 rewrite_header(void) 750 { 751 752 /* can't do this here! */ 753 if (stdout_mode) 754 return; 755 756 if (lseek(outfd, (off_t)0, SEEK_SET) == (off_t)-1) 757 err(1, "could not seek to start of file for header rewrite"); 758 write_midi_header(); 759 } 760 761 #define BYTE1(x) ((x) & 0xff) 762 #define BYTE2(x) (((x) >> 8) & 0xff) 763 #define BYTE3(x) (((x) >> 16) & 0xff) 764 #define BYTE4(x) (((x) >> 24) & 0xff) 765 766 static void 767 write_midi_header(void) 768 { 769 unsigned char header[] = { 770 'M', 'T', 'h', 'd', 771 0, 0, 0, 6, 772 0, 1, 773 0, 0, /* ntracks */ 774 0, 0, /* notes per beat */ 775 }; 776 /* XXX only support one track so far */ 777 unsigned ntracks = 1; 778 unsigned char track[] = { 779 'M', 'T', 'r', 'k', 780 0, 0, 0, 0, 781 }; 782 unsigned char bpm[] = { 783 0, 0xff, 0x51, 0x3, 784 0, 0, 0, /* inverse tempo */ 785 }; 786 unsigned total_size = data_size + sizeof header + sizeof track + sizeof bpm; 787 788 header[10] = BYTE2(ntracks); 789 header[11] = BYTE1(ntracks); 790 header[12] = BYTE2(notes_per_beat); 791 header[13] = BYTE1(notes_per_beat); 792 793 track[4] = BYTE4(total_size); 794 track[5] = BYTE3(total_size); 795 track[6] = BYTE2(total_size); 796 track[7] = BYTE1(total_size); 797 798 #define TEMPO_INV(x) (60000000UL / (x)) 799 bpm[4] = BYTE3(TEMPO_INV(tempo)); 800 bpm[5] = BYTE2(TEMPO_INV(tempo)); 801 bpm[6] = BYTE1(TEMPO_INV(tempo)); 802 803 if (write(outfd, header, sizeof header) != sizeof header) 804 err(1, "write of header failed"); 805 if (write(outfd, track, sizeof track) != sizeof track) 806 err(1, "write of track header failed"); 807 if (write(outfd, bpm, sizeof bpm) != sizeof bpm) 808 err(1, "write of bpm header failed"); 809 810 LOG("wrote header: ntracks=%u notes_per_beat=%u tempo=%d total_size=%u", 811 ntracks, notes_per_beat, tempo, total_size); 812 } 813 814 static void 815 write_midi_trailer(void) 816 { 817 unsigned char trailer[] = { 818 0, 0xff, 0x2f, 0, 819 }; 820 821 if (write(outfd, trailer, sizeof trailer) != sizeof trailer) 822 err(1, "write of trailer failed"); 823 } 824 825 static void 826 usage(void) 827 { 828 829 fprintf(stderr, "Usage: %s [-aDfhqV] [options] {outfile|-}\n", 830 getprogname()); 831 fprintf(stderr, "Options:\n" 832 "\t-B buffer size\n" 833 "\t-c channels\n" 834 "\t-d devices\n" 835 "\t-f sequencerdev\n" 836 "\t-n notesperbeat\n" 837 "\t-r raw_output\n" 838 "\t-T tempo\n" 839 "\t-t recording time\n"); 840 exit(EXIT_FAILURE); 841 } 842