tarina

git clone https://git.tarina.org/tarina
Log | Files | Refs | README | LICENSE

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(&param);
    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 }