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