PageRenderTime 72ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/alsa-utils-1.0.25/amidi/amidi.c

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