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