1 /* $NetBSD: midirecord.c,v 1.13 2023/05/09 20:42:19 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.13 2023/05/09 20:42:19 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 (%zu valid)", 580 e.tag, 581 e.unknown.byte[0], e.unknown.byte[1], 582 e.unknown.byte[2], e.unknown.byte[3], 583 e.unknown.byte[4], e.unknown.byte[5], 584 e.unknown.byte[6], bufsize); 585 586 switch (e.tag) { 587 case SEQ_LOCAL: 588 size = midi_event_local_to_output(e, buffer, bufsize); 589 break; 590 591 case SEQ_TIMING: 592 size = midi_event_timer_to_output(e, buffer, bufsize); 593 break; 594 595 case SEQ_CHN_COMMON: 596 size = midi_event_chn_common_to_output(e, buffer, bufsize); 597 break; 598 599 case SEQ_CHN_VOICE: 600 size = midi_event_chn_voice_to_output(e, buffer, bufsize); 601 break; 602 603 case SEQ_SYSEX: 604 size = midi_event_sysex_to_output(e, buffer, bufsize); 605 break; 606 607 case SEQ_FULLSIZE: 608 size = midi_event_fullsize_to_output(e, buffer, bufsize); 609 break; 610 611 default: 612 errx(1, "don't understand midi tag %x", e.tag); 613 } 614 615 return size; 616 } 617 618 static bool 619 filter_array(unsigned val, unsigned *array, size_t arraylen) 620 { 621 622 if (array == NULL) 623 return false; 624 625 for (; arraylen; arraylen--) 626 if (array[arraylen - 1] == val) 627 return false; 628 629 return true; 630 } 631 632 static bool 633 filter_dev(unsigned device) 634 { 635 636 if (filter_array(device, filt_devnos, num_filt_devnos)) 637 return true; 638 639 return false; 640 } 641 642 static bool 643 filter_chan(unsigned channel) 644 { 645 646 if (filter_array(channel, filt_chans, num_filt_chans)) 647 return true; 648 649 return false; 650 } 651 652 static bool 653 filter_devchan(unsigned device, unsigned channel) 654 { 655 656 if (filter_dev(device) || filter_chan(channel)) 657 return true; 658 659 return false; 660 } 661 662 static int 663 timeleft(struct timeval *start_tvp, struct timeval *record_tvp) 664 { 665 struct timeval now, diff; 666 667 (void)gettimeofday(&now, NULL); 668 timersub(&now, start_tvp, &diff); 669 timersub(record_tvp, &diff, &now); 670 671 return (now.tv_sec > 0 || (now.tv_sec == 0 && now.tv_usec > 0)); 672 } 673 674 static void 675 parse_ints(const char *str, unsigned **arrayp, unsigned *sizep, const char *msg) 676 { 677 unsigned count = 1, u, longest = 0, c = 0; 678 unsigned *ip; 679 const char *s, *os; 680 char *num_buf; 681 682 /* 683 * count all the comma separated values, and figre out 684 * the longest one. 685 */ 686 for (s = str; *s; s++) { 687 c++; 688 if (*s == ',') { 689 count++; 690 if (c > longest) 691 longest = c; 692 c = 0; 693 } 694 } 695 *sizep = count; 696 697 num_buf = malloc(longest + 1); 698 ip = malloc(sizeof(*ip) * count); 699 if (!ip || !num_buf) 700 errx(1, "malloc failed"); 701 702 for (count = 0, s = os = str, u = 0; *s; s++) { 703 if (*s == ',') { 704 num_buf[u] = '\0'; 705 decode_uint(num_buf, &ip[count++]); 706 os = s + 1; 707 u = 0; 708 } else 709 num_buf[u++] = *s; 710 } 711 num_buf[u] = '\0'; 712 decode_uint(num_buf, &ip[count++]); 713 *arrayp = ip; 714 715 if (verbose) { 716 fprintf(stderr, "Filtering %s in:", msg); 717 for (size_t i = 0; i < *sizep; i++) 718 fprintf(stderr, " %u", ip[i]); 719 fprintf(stderr, "\n"); 720 } 721 722 free(num_buf); 723 } 724 725 static void 726 cleanup(int signo) 727 { 728 729 write_midi_trailer(); 730 rewrite_header(); 731 732 if (ioctl(midifd, SEQUENCER_TMR_STOP, NULL) < 0) { 733 if (ignore_timer_fail) 734 warn("failed to stop midi timer"); 735 else 736 err(1, "failed to stop midi timer"); 737 } 738 739 if (close(outfd) != 0) 740 warn("couldn't close output"); 741 if (close(midifd) != 0) 742 warn("couldn't close midi device"); 743 if (signo != 0) 744 (void)raise_default_signal(signo); 745 746 exit(0); 747 } 748 749 static void 750 rewrite_header(void) 751 { 752 753 /* can't do this here! */ 754 if (stdout_mode) 755 return; 756 757 if (lseek(outfd, (off_t)0, SEEK_SET) == (off_t)-1) 758 err(1, "could not seek to start of file for header rewrite"); 759 write_midi_header(); 760 } 761 762 #define BYTE1(x) ((x) & 0xff) 763 #define BYTE2(x) (((x) >> 8) & 0xff) 764 #define BYTE3(x) (((x) >> 16) & 0xff) 765 #define BYTE4(x) (((x) >> 24) & 0xff) 766 767 static void 768 write_midi_header(void) 769 { 770 unsigned char header[] = { 771 'M', 'T', 'h', 'd', 772 0, 0, 0, 6, 773 0, 1, 774 0, 0, /* ntracks */ 775 0, 0, /* notes per beat */ 776 }; 777 /* XXX only support one track so far */ 778 unsigned ntracks = 1; 779 unsigned char track[] = { 780 'M', 'T', 'r', 'k', 781 0, 0, 0, 0, 782 }; 783 unsigned char bpm[] = { 784 0, 0xff, 0x51, 0x3, 785 0, 0, 0, /* inverse tempo */ 786 }; 787 unsigned total_size = data_size + sizeof header + sizeof track + sizeof bpm; 788 789 header[10] = BYTE2(ntracks); 790 header[11] = BYTE1(ntracks); 791 header[12] = BYTE2(notes_per_beat); 792 header[13] = BYTE1(notes_per_beat); 793 794 track[4] = BYTE4(total_size); 795 track[5] = BYTE3(total_size); 796 track[6] = BYTE2(total_size); 797 track[7] = BYTE1(total_size); 798 799 #define TEMPO_INV(x) (60000000UL / (x)) 800 bpm[4] = BYTE3(TEMPO_INV(tempo)); 801 bpm[5] = BYTE2(TEMPO_INV(tempo)); 802 bpm[6] = BYTE1(TEMPO_INV(tempo)); 803 804 if (write(outfd, header, sizeof header) != sizeof header) 805 err(1, "write of header failed"); 806 if (write(outfd, track, sizeof track) != sizeof track) 807 err(1, "write of track header failed"); 808 if (write(outfd, bpm, sizeof bpm) != sizeof bpm) 809 err(1, "write of bpm header failed"); 810 811 LOG("wrote header: ntracks=%u notes_per_beat=%u tempo=%d total_size=%u", 812 ntracks, notes_per_beat, tempo, total_size); 813 } 814 815 static void 816 write_midi_trailer(void) 817 { 818 unsigned char trailer[] = { 819 0, 0xff, 0x2f, 0, 820 }; 821 822 if (write(outfd, trailer, sizeof trailer) != sizeof trailer) 823 err(1, "write of trailer failed"); 824 } 825 826 static void 827 usage(void) 828 { 829 830 fprintf(stderr, "Usage: %s [-aDfhqV] [options] {outfile|-}\n", 831 getprogname()); 832 fprintf(stderr, "Options:\n" 833 "\t-B buffer size\n" 834 "\t-c channels\n" 835 "\t-d devices\n" 836 "\t-f sequencerdev\n" 837 "\t-n notesperbeat\n" 838 "\t-r raw_output\n" 839 "\t-T tempo\n" 840 "\t-t recording time\n"); 841 exit(EXIT_FAILURE); 842 } 843