amidi.c (16668B)
1 /* 2 * amidi.c - read from/write to RawMIDI ports 3 * 4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 5 * 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22 #define _GNU_SOURCE 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <stdarg.h> 26 #include <string.h> 27 #include <ctype.h> 28 #include <math.h> 29 #include <getopt.h> 30 #include <errno.h> 31 #include <signal.h> 32 #include <sys/timerfd.h> 33 #include <sys/types.h> 34 #include <sys/poll.h> 35 #include <sys/stat.h> 36 #include <unistd.h> 37 #include <fcntl.h> 38 #include <alsa/asoundlib.h> 39 #include "aconfig.h" 40 #include "version.h" 41 42 #define NSEC_PER_SEC 1000000000L 43 44 static int do_device_list, do_rawmidi_list; 45 static char *port_name = "default"; 46 static char *send_file_name; 47 static char *receive_file_name; 48 static char *send_hex; 49 static char *send_data; 50 static int send_data_length; 51 static int receive_file; 52 static int dump; 53 static float timeout; 54 static int stop; 55 static int sysex_interval; 56 static snd_rawmidi_t *input, **inputp; 57 static snd_rawmidi_t *output, **outputp; 58 59 static void error(const char *format, ...) 60 { 61 va_list ap; 62 63 va_start(ap, format); 64 vfprintf(stderr, format, ap); 65 va_end(ap); 66 putc('\n', stderr); 67 } 68 69 static void usage(void) 70 { 71 printf( 72 "Usage: amidi options\n" 73 "\n" 74 "-h, --help this help\n" 75 "-V, --version print current version\n" 76 "-l, --list-devices list all hardware ports\n" 77 "-L, --list-rawmidis list all RawMIDI definitions\n" 78 "-p, --port=name select port by name\n" 79 "-s, --send=file send the contents of a (.syx) file\n" 80 "-r, --receive=file write received data into a file\n" 81 "-S, --send-hex=\"...\" send hexadecimal bytes\n" 82 "-d, --dump print received data as hexadecimal bytes\n" 83 "-t, --timeout=seconds exits when no data has been received\n" 84 " for the specified duration\n" 85 "-a, --active-sensing include active sensing bytes\n" 86 "-c, --clock include clock bytes\n" 87 "-i, --sysex-interval=mseconds delay in between each SysEx message\n"); 88 } 89 90 static void version(void) 91 { 92 puts("amidi version " SND_UTIL_VERSION_STR); 93 } 94 95 static void *my_malloc(size_t size) 96 { 97 void *p = malloc(size); 98 if (!p) { 99 error("out of memory"); 100 exit(EXIT_FAILURE); 101 } 102 return p; 103 } 104 105 static void list_device(snd_ctl_t *ctl, int card, int device) 106 { 107 snd_rawmidi_info_t *info; 108 const char *name; 109 const char *sub_name; 110 int subs, subs_in, subs_out; 111 int sub; 112 int err; 113 114 snd_rawmidi_info_alloca(&info); 115 snd_rawmidi_info_set_device(info, device); 116 117 snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); 118 err = snd_ctl_rawmidi_info(ctl, info); 119 if (err >= 0) 120 subs_in = snd_rawmidi_info_get_subdevices_count(info); 121 else 122 subs_in = 0; 123 124 snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT); 125 err = snd_ctl_rawmidi_info(ctl, info); 126 if (err >= 0) 127 subs_out = snd_rawmidi_info_get_subdevices_count(info); 128 else 129 subs_out = 0; 130 131 subs = subs_in > subs_out ? subs_in : subs_out; 132 if (!subs) 133 return; 134 135 for (sub = 0; sub < subs; ++sub) { 136 snd_rawmidi_info_set_stream(info, sub < subs_in ? 137 SND_RAWMIDI_STREAM_INPUT : 138 SND_RAWMIDI_STREAM_OUTPUT); 139 snd_rawmidi_info_set_subdevice(info, sub); 140 err = snd_ctl_rawmidi_info(ctl, info); 141 if (err < 0) { 142 error("cannot get rawmidi information %d:%d:%d: %s\n", 143 card, device, sub, snd_strerror(err)); 144 return; 145 } 146 name = snd_rawmidi_info_get_name(info); 147 sub_name = snd_rawmidi_info_get_subdevice_name(info); 148 if (sub == 0 && sub_name[0] == '\0') { 149 printf("%c%c hw:%d,%d %s", 150 sub < subs_in ? 'I' : ' ', 151 sub < subs_out ? 'O' : ' ', 152 card, device, name); 153 if (subs > 1) 154 printf(" (%d subdevices)", subs); 155 putchar('\n'); 156 break; 157 } else { 158 printf("%c%c hw:%d,%d,%d %s\n", 159 sub < subs_in ? 'I' : ' ', 160 sub < subs_out ? 'O' : ' ', 161 card, device, sub, sub_name); 162 } 163 } 164 } 165 166 static void list_card_devices(int card) 167 { 168 snd_ctl_t *ctl; 169 char name[32]; 170 int device; 171 int err; 172 173 sprintf(name, "hw:%d", card); 174 if ((err = snd_ctl_open(&ctl, name, 0)) < 0) { 175 error("cannot open control for card %d: %s", card, snd_strerror(err)); 176 return; 177 } 178 device = -1; 179 for (;;) { 180 if ((err = snd_ctl_rawmidi_next_device(ctl, &device)) < 0) { 181 error("cannot determine device number: %s", snd_strerror(err)); 182 break; 183 } 184 if (device < 0) 185 break; 186 list_device(ctl, card, device); 187 } 188 snd_ctl_close(ctl); 189 } 190 191 static void device_list(void) 192 { 193 int card, err; 194 195 card = -1; 196 if ((err = snd_card_next(&card)) < 0) { 197 error("cannot determine card number: %s", snd_strerror(err)); 198 return; 199 } 200 if (card < 0) { 201 error("no sound card found"); 202 return; 203 } 204 puts("Dir Device Name"); 205 do { 206 list_card_devices(card); 207 if ((err = snd_card_next(&card)) < 0) { 208 error("cannot determine card number: %s", snd_strerror(err)); 209 break; 210 } 211 } while (card >= 0); 212 } 213 214 static void rawmidi_list(void) 215 { 216 snd_output_t *output; 217 snd_config_t *config; 218 int err; 219 220 if ((err = snd_config_update()) < 0) { 221 error("snd_config_update failed: %s", snd_strerror(err)); 222 return; 223 } 224 if ((err = snd_output_stdio_attach(&output, stdout, 0)) < 0) { 225 error("snd_output_stdio_attach failed: %s", snd_strerror(err)); 226 return; 227 } 228 if (snd_config_search(snd_config, "rawmidi", &config) >= 0) { 229 puts("RawMIDI list:"); 230 snd_config_save(config, output); 231 } 232 snd_output_close(output); 233 } 234 235 static int send_midi_interleaved(void) 236 { 237 int err; 238 char *data = send_data; 239 size_t buffer_size; 240 snd_rawmidi_params_t *param; 241 snd_rawmidi_status_t *st; 242 243 snd_rawmidi_status_alloca(&st); 244 245 snd_rawmidi_params_alloca(¶m); 246 snd_rawmidi_params_current(output, param); 247 buffer_size = snd_rawmidi_params_get_buffer_size(param); 248 249 while (data < (send_data + send_data_length)) { 250 int len = send_data + send_data_length - data; 251 char *temp; 252 253 if (data > send_data) { 254 snd_rawmidi_status(output, st); 255 do { 256 /* 320 µs per byte as noted in Page 1 of MIDI spec */ 257 usleep((buffer_size - snd_rawmidi_status_get_avail(st)) * 320); 258 snd_rawmidi_status(output, st); 259 } while(snd_rawmidi_status_get_avail(st) < buffer_size); 260 usleep(sysex_interval * 1000); 261 } 262 263 /* find end of SysEx */ 264 if ((temp = memchr(data, 0xf7, len)) != NULL) 265 len = temp - data + 1; 266 267 if ((err = snd_rawmidi_write(output, data, len)) < 0) 268 return err; 269 270 data += len; 271 } 272 273 return 0; 274 } 275 276 static void load_file(void) 277 { 278 int fd; 279 off_t length; 280 281 fd = open(send_file_name, O_RDONLY); 282 if (fd == -1) { 283 error("cannot open %s - %s", send_file_name, strerror(errno)); 284 return; 285 } 286 length = lseek(fd, 0, SEEK_END); 287 if (length == (off_t)-1) { 288 error("cannot determine length of %s: %s", send_file_name, strerror(errno)); 289 goto _error; 290 } 291 send_data = my_malloc(length); 292 lseek(fd, 0, SEEK_SET); 293 if (read(fd, send_data, length) != length) { 294 error("cannot read from %s: %s", send_file_name, strerror(errno)); 295 goto _error; 296 } 297 if (length >= 4 && !memcmp(send_data, "MThd", 4)) { 298 error("%s is a Standard MIDI File; use aplaymidi to send it", send_file_name); 299 goto _error; 300 } 301 send_data_length = length; 302 goto _exit; 303 _error: 304 free(send_data); 305 send_data = NULL; 306 _exit: 307 close(fd); 308 } 309 310 static int hex_value(char c) 311 { 312 if ('0' <= c && c <= '9') 313 return c - '0'; 314 if ('A' <= c && c <= 'F') 315 return c - 'A' + 10; 316 if ('a' <= c && c <= 'f') 317 return c - 'a' + 10; 318 error("invalid character %c", c); 319 return -1; 320 } 321 322 static void parse_data(void) 323 { 324 const char *p; 325 int i, value; 326 327 send_data = my_malloc(strlen(send_hex)); /* guesstimate */ 328 i = 0; 329 value = -1; /* value is >= 0 when the first hex digit of a byte has been read */ 330 for (p = send_hex; *p; ++p) { 331 int digit; 332 if (isspace((unsigned char)*p)) { 333 if (value >= 0) { 334 send_data[i++] = value; 335 value = -1; 336 } 337 continue; 338 } 339 digit = hex_value(*p); 340 if (digit < 0) { 341 send_data = NULL; 342 return; 343 } 344 if (value < 0) { 345 value = digit; 346 } else { 347 send_data[i++] = (value << 4) | digit; 348 value = -1; 349 } 350 } 351 if (value >= 0) 352 send_data[i++] = value; 353 send_data_length = i; 354 } 355 356 /* 357 * prints MIDI commands, formatting them nicely 358 */ 359 static void print_byte(unsigned char byte) 360 { 361 static enum { 362 STATE_UNKNOWN, 363 STATE_1PARAM, 364 STATE_1PARAM_CONTINUE, 365 STATE_2PARAM_1, 366 STATE_2PARAM_2, 367 STATE_2PARAM_1_CONTINUE, 368 STATE_SYSEX 369 } state = STATE_UNKNOWN; 370 int newline = 0; 371 372 if (byte >= 0xf8) 373 newline = 1; 374 else if (byte >= 0xf0) { 375 newline = 1; 376 switch (byte) { 377 case 0xf0: 378 state = STATE_SYSEX; 379 break; 380 case 0xf1: 381 case 0xf3: 382 state = STATE_1PARAM; 383 break; 384 case 0xf2: 385 state = STATE_2PARAM_1; 386 break; 387 case 0xf4: 388 case 0xf5: 389 case 0xf6: 390 state = STATE_UNKNOWN; 391 break; 392 case 0xf7: 393 newline = state != STATE_SYSEX; 394 state = STATE_UNKNOWN; 395 break; 396 } 397 } else if (byte >= 0x80) { 398 newline = 1; 399 if (byte >= 0xc0 && byte <= 0xdf) 400 state = STATE_1PARAM; 401 else 402 state = STATE_2PARAM_1; 403 } else /* b < 0x80 */ { 404 int running_status = 0; 405 newline = state == STATE_UNKNOWN; 406 switch (state) { 407 case STATE_1PARAM: 408 state = STATE_1PARAM_CONTINUE; 409 break; 410 case STATE_1PARAM_CONTINUE: 411 running_status = 1; 412 break; 413 case STATE_2PARAM_1: 414 state = STATE_2PARAM_2; 415 break; 416 case STATE_2PARAM_2: 417 state = STATE_2PARAM_1_CONTINUE; 418 break; 419 case STATE_2PARAM_1_CONTINUE: 420 running_status = 1; 421 state = STATE_2PARAM_2; 422 break; 423 default: 424 break; 425 } 426 if (running_status) 427 fputs("\n ", stdout); 428 } 429 printf("%c%02X", newline ? '\n' : ' ', byte); 430 } 431 432 static void sig_handler(int dummy) 433 { 434 stop = 1; 435 } 436 437 static void add_send_hex_data(const char *str) 438 { 439 int length; 440 char *s; 441 442 length = (send_hex ? strlen(send_hex) + 1 : 0) + strlen(str) + 1; 443 s = my_malloc(length); 444 if (send_hex) { 445 strcpy(s, send_hex); 446 strcat(s, " "); 447 } else { 448 s[0] = '\0'; 449 } 450 strcat(s, str); 451 free(send_hex); 452 send_hex = s; 453 } 454 455 int main(int argc, char *argv[]) 456 { 457 static const char short_options[] = "hVlLp:s:r:S::dt:aci:"; 458 static const struct option long_options[] = { 459 {"help", 0, NULL, 'h'}, 460 {"version", 0, NULL, 'V'}, 461 {"list-devices", 0, NULL, 'l'}, 462 {"list-rawmidis", 0, NULL, 'L'}, 463 {"port", 1, NULL, 'p'}, 464 {"send", 1, NULL, 's'}, 465 {"receive", 1, NULL, 'r'}, 466 {"send-hex", 2, NULL, 'S'}, 467 {"dump", 0, NULL, 'd'}, 468 {"timeout", 1, NULL, 't'}, 469 {"active-sensing", 0, NULL, 'a'}, 470 {"clock", 0, NULL, 'c'}, 471 {"sysex-interval", 1, NULL, 'i'}, 472 { } 473 }; 474 int c, err, ok = 0; 475 int ignore_active_sensing = 1; 476 int ignore_clock = 1; 477 int do_send_hex = 0; 478 struct itimerspec itimerspec = { .it_interval = { 0, 0 } }; 479 480 while ((c = getopt_long(argc, argv, short_options, 481 long_options, NULL)) != -1) { 482 switch (c) { 483 case 'h': 484 usage(); 485 return 0; 486 case 'V': 487 version(); 488 return 0; 489 case 'l': 490 do_device_list = 1; 491 break; 492 case 'L': 493 do_rawmidi_list = 1; 494 break; 495 case 'p': 496 port_name = optarg; 497 break; 498 case 's': 499 send_file_name = optarg; 500 break; 501 case 'r': 502 receive_file_name = optarg; 503 break; 504 case 'S': 505 do_send_hex = 1; 506 if (optarg) 507 add_send_hex_data(optarg); 508 break; 509 case 'd': 510 dump = 1; 511 break; 512 case 't': 513 timeout = atof(optarg); 514 break; 515 case 'a': 516 ignore_active_sensing = 0; 517 break; 518 case 'c': 519 ignore_clock = 0; 520 break; 521 case 'i': 522 sysex_interval = atoi(optarg); 523 break; 524 default: 525 error("Try `amidi --help' for more information."); 526 return 1; 527 } 528 } 529 if (do_send_hex) { 530 /* data for -S can be specified as multiple arguments */ 531 if (!send_hex && !argv[optind]) { 532 error("Please specify some data for --send-hex."); 533 return 1; 534 } 535 for (; argv[optind]; ++optind) 536 add_send_hex_data(argv[optind]); 537 } else { 538 if (argv[optind]) { 539 error("%s is not an option.", argv[optind]); 540 return 1; 541 } 542 } 543 544 if (do_rawmidi_list) 545 rawmidi_list(); 546 if (do_device_list) 547 device_list(); 548 if (do_rawmidi_list || do_device_list) 549 return 0; 550 551 if (!send_file_name && !receive_file_name && !send_hex && !dump) { 552 error("Please specify at least one of --send, --receive, --send-hex, or --dump."); 553 return 1; 554 } 555 if (send_file_name && send_hex) { 556 error("--send and --send-hex cannot be specified at the same time."); 557 return 1; 558 } 559 560 if (send_file_name) 561 load_file(); 562 else if (send_hex) 563 parse_data(); 564 if ((send_file_name || send_hex) && !send_data) 565 return 1; 566 567 if (receive_file_name) { 568 receive_file = creat(receive_file_name, 0666); 569 if (receive_file == -1) { 570 error("cannot create %s: %s", receive_file_name, strerror(errno)); 571 return -1; 572 } 573 } else { 574 receive_file = -1; 575 } 576 577 if (receive_file_name || dump) 578 inputp = &input; 579 else 580 inputp = NULL; 581 if (send_data) 582 outputp = &output; 583 else 584 outputp = NULL; 585 586 if ((err = snd_rawmidi_open(inputp, outputp, port_name, SND_RAWMIDI_NONBLOCK)) < 0) { 587 error("cannot open port \"%s\": %s", port_name, snd_strerror(err)); 588 goto _exit2; 589 } 590 591 if (inputp) 592 snd_rawmidi_read(input, NULL, 0); /* trigger reading */ 593 594 if (send_data) { 595 if ((err = snd_rawmidi_nonblock(output, 0)) < 0) { 596 error("cannot set blocking mode: %s", snd_strerror(err)); 597 goto _exit; 598 } 599 if (!sysex_interval) { 600 if ((err = snd_rawmidi_write(output, send_data, send_data_length)) < 0) { 601 error("cannot send data: %s", snd_strerror(err)); 602 return err; 603 } 604 } else { 605 if ((err = send_midi_interleaved()) < 0) { 606 error("cannot send data: %s", snd_strerror(err)); 607 return err; 608 } 609 } 610 } 611 612 if (inputp) { 613 int read = 0; 614 int npfds; 615 struct pollfd *pfds; 616 617 npfds = 1 + snd_rawmidi_poll_descriptors_count(input); 618 pfds = alloca(npfds * sizeof(struct pollfd)); 619 620 if (timeout > 0) { 621 pfds[0].fd = timerfd_create(CLOCK_MONOTONIC, 0); 622 if (pfds[0].fd == -1) { 623 error("cannot create timer: %s", strerror(errno)); 624 goto _exit; 625 } 626 pfds[0].events = POLLIN; 627 } else { 628 pfds[0].fd = -1; 629 } 630 631 snd_rawmidi_poll_descriptors(input, &pfds[1], npfds - 1); 632 633 signal(SIGINT, sig_handler); 634 635 if (timeout > 0) { 636 float timeout_int; 637 638 itimerspec.it_value.tv_nsec = modff(timeout, &timeout_int) * NSEC_PER_SEC; 639 itimerspec.it_value.tv_sec = timeout_int; 640 err = timerfd_settime(pfds[0].fd, 0, &itimerspec, NULL); 641 if (err < 0) { 642 error("cannot set timer: %s", strerror(errno)); 643 goto _exit; 644 } 645 } 646 for (;;) { 647 unsigned char buf[256]; 648 int i, length; 649 unsigned short revents; 650 651 err = poll(pfds, npfds, -1); 652 if (stop || (err < 0 && errno == EINTR)) 653 break; 654 if (err < 0) { 655 error("poll failed: %s", strerror(errno)); 656 break; 657 } 658 659 err = snd_rawmidi_poll_descriptors_revents(input, &pfds[1], npfds - 1, &revents); 660 if (err < 0) { 661 error("cannot get poll events: %s", snd_strerror(errno)); 662 break; 663 } 664 if (revents & (POLLERR | POLLHUP)) 665 break; 666 if (!(revents & POLLIN)) { 667 if (pfds[0].revents & POLLIN) 668 break; 669 continue; 670 } 671 672 err = snd_rawmidi_read(input, buf, sizeof(buf)); 673 if (err == -EAGAIN) 674 continue; 675 if (err < 0) { 676 error("cannot read from port \"%s\": %s", port_name, snd_strerror(err)); 677 break; 678 } 679 length = 0; 680 for (i = 0; i < err; ++i) 681 if ((buf[i] != MIDI_CMD_COMMON_CLOCK && 682 buf[i] != MIDI_CMD_COMMON_SENSING) || 683 (buf[i] == MIDI_CMD_COMMON_CLOCK && !ignore_clock) || 684 (buf[i] == MIDI_CMD_COMMON_SENSING && !ignore_active_sensing)) 685 buf[length++] = buf[i]; 686 if (length == 0) 687 continue; 688 read += length; 689 690 if (receive_file != -1) 691 write(receive_file, buf, length); 692 if (dump) { 693 for (i = 0; i < length; ++i) 694 print_byte(buf[i]); 695 fflush(stdout); 696 } 697 698 if (timeout > 0) { 699 err = timerfd_settime(pfds[0].fd, 0, &itimerspec, NULL); 700 if (err < 0) { 701 error("cannot set timer: %s", strerror(errno)); 702 break; 703 } 704 } 705 } 706 if (isatty(fileno(stdout))) 707 printf("\n%d bytes read\n", read); 708 } 709 710 ok = 1; 711 _exit: 712 if (inputp) 713 snd_rawmidi_close(input); 714 if (outputp) 715 snd_rawmidi_close(output); 716 _exit2: 717 if (receive_file != -1) 718 close(receive_file); 719 return !ok; 720 }