seq: arecordmidi: Add num-events option

Add a command line option to automatically exit after recording a fixed
number of MIDI events. This allows a program using arecordmidi to expect
a MIDI file to be written automatically when the specified number of
events have been received, instead of having to send a SIGINT or SIGTERM
programmatically.

It also avoids the need to have the arecordmidi process running in the
background, and then constantly stat the output file to check if any
bytes have been written to it (this makes for less predictable and
longer-running tests).

This functionality finds use in Chrome OS functional testing, since
having to send SIGTERM/SIGINT programmatically and then wait for the
output file adds unpredictability and delay to the tests.

The addition of this command-line option should (hopefully) not break
any existing usage.

Signed-off-by: Prashant Malani <pmalani@chromium.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Prashant Malani 2019-01-29 01:58:26 -08:00 committed by Takashi Iwai
parent 6fdaa0c524
commit 52c9cc9571

View file

@ -86,6 +86,25 @@ static int ts_num = 4; /* time signature: numerator */
static int ts_div = 4; /* time signature: denominator */ static int ts_div = 4; /* time signature: denominator */
static int ts_dd = 2; /* time signature: denominator as a power of two */ static int ts_dd = 2; /* time signature: denominator as a power of two */
/* Parse a decimal number from a command line argument. */
static long arg_parse_decimal_num(const char *str, int *err)
{
long val;
char *endptr;
errno = 0;
val = strtol(str, &endptr, 0);
if (errno > 0) {
*err = -errno;
return 0;
}
if (*endptr != '\0') {
*err = -EINVAL;
return 0;
}
return val;
}
/* prints an error message to stderr, and dies */ /* prints an error message to stderr, and dies */
static void fatal(const char *msg, ...) static void fatal(const char *msg, ...)
@ -690,7 +709,8 @@ static void help(const char *argv0)
" -t,--ticks=ticks resolution in ticks per beat or frame\n" " -t,--ticks=ticks resolution in ticks per beat or frame\n"
" -s,--split-channels create a track for each channel\n" " -s,--split-channels create a track for each channel\n"
" -m,--metronome=client:port play a metronome signal\n" " -m,--metronome=client:port play a metronome signal\n"
" -i,--timesig=nn:dd time signature\n", " -i,--timesig=nn:dd time signature\n"
" -n,--num-events=events fixed number of events to record, then exit\n",
argv0); argv0);
} }
@ -706,7 +726,7 @@ static void sighandler(int sig)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
static const char short_options[] = "hVlp:b:f:t:sdm:i:"; static const char short_options[] = "hVlp:b:f:t:sdm:i:n:";
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'},
@ -719,6 +739,7 @@ int main(int argc, char *argv[])
{"dump", 0, NULL, 'd'}, {"dump", 0, NULL, 'd'},
{"metronome", 1, NULL, 'm'}, {"metronome", 1, NULL, 'm'},
{"timesig", 1, NULL, 'i'}, {"timesig", 1, NULL, 'i'},
{"num-events", 1, NULL, 'n'},
{ } { }
}; };
@ -727,6 +748,9 @@ int main(int argc, char *argv[])
struct pollfd *pfds; struct pollfd *pfds;
int npfds; int npfds;
int c, err; int c, err;
/* If |num_events| isn't specified, leave it at 0. */
long num_events = 0;
long events_received = 0;
init_seq(); init_seq();
@ -775,6 +799,16 @@ int main(int argc, char *argv[])
case 'i': case 'i':
time_signature(optarg); time_signature(optarg);
break; break;
case 'n':
err = 0;
num_events = arg_parse_decimal_num(optarg, &err);
if (err != 0) {
fatal("Couldn't parse num_events argument: %s\n",
strerror(-err));
}
if (num_events <= 0)
fatal("num_events must be greater than 0");
break;
default: default:
help(argv[0]); help(argv[0]);
return 1; return 1;
@ -864,13 +898,20 @@ int main(int argc, char *argv[])
err = snd_seq_event_input(seq, &event); err = snd_seq_event_input(seq, &event);
if (err < 0) if (err < 0)
break; break;
if (event) if (event) {
record_event(event); record_event(event);
events_received++;
}
} while (err > 0); } while (err > 0);
if (stop) if (stop)
break; break;
if (num_events && (events_received == num_events))
break;
} }
if (num_events && events_received < num_events)
fputs("Warning: Received signal before num_events\n", stdout);
finish_tracks(); finish_tracks();
write_file(); write_file();