amidi: add sysex-interval option

This patch adds a new option to amidi tool: sysex-interval.

It adds a delay (in milliseconds) in between each SysEx message - it searches
for a 0xF7 byte.

This is very useful when sending firmware updates to a remote device via SysEx
or any other use that requires this delay in between SysEx messages.

`amidi' manual was updated with an example usage as well.

Signed-off-by: Felipe F. Tonello <eu@felipetonello.com>
Acked-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Felipe F. Tonello 2016-08-30 17:02:48 +01:00 committed by Takashi Iwai
parent 7b122fb62a
commit b59745978f
2 changed files with 86 additions and 18 deletions

View file

@ -1,4 +1,4 @@
.TH AMIDI 1 "16 Apr 2016" .TH AMIDI 1 "30 Aug 2016"
.SH NAME .SH NAME
amidi \- read from and write to ALSA RawMIDI ports amidi \- read from and write to ALSA RawMIDI ports
@ -120,6 +120,12 @@ received MIDI commands.
Does not ignore Clock bytes (F8h) when saving or printing received Does not ignore Clock bytes (F8h) when saving or printing received
MIDI commands. MIDI commands.
.TP
.I \-i, \-\-sysex-interval=mseconds
Adds a delay in between each SysEx message sent to a device. It is
useful when sending firmware updates via SysEx messages to a remote
device.
.SH EXAMPLES .SH EXAMPLES
.TP .TP
@ -129,6 +135,14 @@ will send the MIDI commands in
to port to port
.I hw:0. .I hw:0.
.TP
.B amidi \-p hw:1,0,0 -s firmware.syx \-i 100
will send the MIDI commands in
.I firmware.syx
to port
.I hw:1,0,0
with 100 milliseconds delay in between each SysEx message.
.TP .TP
.B amidi \-S 'F0 43 10 4C 00 00 7E 00 F7' .B amidi \-S 'F0 43 10 4C 00 00 7E 00 F7'
sends an XG Reset to the default port. sends an XG Reset to the default port.

View file

@ -52,6 +52,7 @@ static int receive_file;
static int dump; static int dump;
static float timeout; static float timeout;
static int stop; static int stop;
static int sysex_interval;
static snd_rawmidi_t *input, **inputp; static snd_rawmidi_t *input, **inputp;
static snd_rawmidi_t *output, **outputp; static snd_rawmidi_t *output, **outputp;
@ -82,7 +83,8 @@ static void usage(void)
"-t, --timeout=seconds exits when no data has been received\n" "-t, --timeout=seconds exits when no data has been received\n"
" for the specified duration\n" " for the specified duration\n"
"-a, --active-sensing include active sensing bytes\n" "-a, --active-sensing include active sensing bytes\n"
"-c, --clock include clock bytes\n"); "-c, --clock include clock bytes\n"
"-i, --sysex-interval=mseconds delay in between each SysEx message\n");
} }
static void version(void) static void version(void)
@ -230,6 +232,47 @@ static void rawmidi_list(void)
snd_output_close(output); snd_output_close(output);
} }
static int send_midi_interleaved(void)
{
int err;
char *data = send_data;
size_t buffer_size;
snd_rawmidi_params_t *param;
snd_rawmidi_status_t *st;
snd_rawmidi_status_alloca(&st);
snd_rawmidi_params_alloca(&param);
snd_rawmidi_params_current(output, param);
buffer_size = snd_rawmidi_params_get_buffer_size(param);
while (data < (send_data + send_data_length)) {
int len = send_data + send_data_length - data;
char *temp;
if (data > send_data) {
snd_rawmidi_status(output, st);
do {
/* 320 µs per byte as noted in Page 1 of MIDI spec */
usleep((buffer_size - snd_rawmidi_status_get_avail(st)) * 320);
snd_rawmidi_status(output, st);
} while(snd_rawmidi_status_get_avail(st) < buffer_size);
usleep(sysex_interval * 1000);
}
/* find end of SysEx */
if ((temp = memchr(data, 0xf7, len)) != NULL)
len = temp - data + 1;
if ((err = snd_rawmidi_write(output, data, len)) < 0)
return err;
data += len;
}
return 0;
}
static void load_file(void) static void load_file(void)
{ {
int fd; int fd;
@ -411,7 +454,7 @@ static void add_send_hex_data(const char *str)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
static const char short_options[] = "hVlLp:s:r:S::dt:ac"; static const char short_options[] = "hVlLp:s:r:S::dt:aci:";
static const struct option long_options[] = { static const struct option long_options[] = {
{"help", 0, NULL, 'h'}, {"help", 0, NULL, 'h'},
{"version", 0, NULL, 'V'}, {"version", 0, NULL, 'V'},
@ -425,6 +468,7 @@ int main(int argc, char *argv[])
{"timeout", 1, NULL, 't'}, {"timeout", 1, NULL, 't'},
{"active-sensing", 0, NULL, 'a'}, {"active-sensing", 0, NULL, 'a'},
{"clock", 0, NULL, 'c'}, {"clock", 0, NULL, 'c'},
{"sysex-interval", 1, NULL, 'i'},
{ } { }
}; };
int c, err, ok = 0; int c, err, ok = 0;
@ -474,6 +518,9 @@ int main(int argc, char *argv[])
case 'c': case 'c':
ignore_clock = 0; ignore_clock = 0;
break; break;
case 'i':
sysex_interval = atoi(optarg);
break;
default: default:
error("Try `amidi --help' for more information."); error("Try `amidi --help' for more information.");
return 1; return 1;
@ -549,9 +596,16 @@ int main(int argc, char *argv[])
error("cannot set blocking mode: %s", snd_strerror(err)); error("cannot set blocking mode: %s", snd_strerror(err));
goto _exit; goto _exit;
} }
if (!sysex_interval) {
if ((err = snd_rawmidi_write(output, send_data, send_data_length)) < 0) { if ((err = snd_rawmidi_write(output, send_data, send_data_length)) < 0) {
error("cannot send data: %s", snd_strerror(err)); error("cannot send data: %s", snd_strerror(err));
goto _exit; return err;
}
} else {
if ((err = send_midi_interleaved()) < 0) {
error("cannot send data: %s", snd_strerror(err));
return err;
}
} }
} }