mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-10-06 08:28:00 +02:00
Compare commits
4 commits
68491dd464
...
274f1b9ebf
Author | SHA1 | Date | |
---|---|---|---|
|
274f1b9ebf | ||
|
f0df4b4cfe | ||
|
b1269eefdd | ||
|
99ce4b1d8a |
4 changed files with 109 additions and 19 deletions
|
@ -44,8 +44,9 @@ environment variable if none is given on the command line.
|
||||||
.B aplaymidi2
|
.B aplaymidi2
|
||||||
supports only basic UMP events: in addition to the standard MIDI1 and
|
supports only basic UMP events: in addition to the standard MIDI1 and
|
||||||
MIDI2 CVMs and 7bit SysEx, only the following are supported:
|
MIDI2 CVMs and 7bit SysEx, only the following are supported:
|
||||||
DCTPQ, DC, Set Tempo, Start Clip and End Clip.
|
DCTPQ, DC, Set Tempo, Start Clip, End Clip.
|
||||||
Lyrics and other meta data in Flex Data are skipped, so far.
|
Lyrics and other meta data in Flex Data are printed, too, unless
|
||||||
|
\fI\-s\fP option is given.
|
||||||
|
|
||||||
The multiple output ports are useful when the given MIDI Clip file
|
The multiple output ports are useful when the given MIDI Clip file
|
||||||
contains the UMP packets for multiple Groups.
|
contains the UMP packets for multiple Groups.
|
||||||
|
@ -62,6 +63,10 @@ Specifies how long to wait after the end of each MIDI Clip file,
|
||||||
to allow the last notes to die away.
|
to allow the last notes to die away.
|
||||||
Default is 2 seconds.
|
Default is 2 seconds.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.I \-s, \-\-silent
|
||||||
|
Don't show message texts.
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
pmidi(1)
|
pmidi(1)
|
||||||
.br
|
.br
|
||||||
|
|
|
@ -20,6 +20,7 @@ static int port_count;
|
||||||
static snd_seq_addr_t ports[16];
|
static snd_seq_addr_t ports[16];
|
||||||
static int queue;
|
static int queue;
|
||||||
static int end_delay = 2;
|
static int end_delay = 2;
|
||||||
|
static int silent;
|
||||||
|
|
||||||
static unsigned int _current_tempo = 50000000; /* default 120 bpm */
|
static unsigned int _current_tempo = 50000000; /* default 120 bpm */
|
||||||
static unsigned int tempo_base = 10;
|
static unsigned int tempo_base = 10;
|
||||||
|
@ -432,6 +433,7 @@ static void play_midi(FILE *file)
|
||||||
|
|
||||||
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_METADATA ||
|
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_METADATA ||
|
||||||
fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT) {
|
fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT) {
|
||||||
|
if (!silent)
|
||||||
show_text(ump);
|
show_text(ump);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -493,7 +495,8 @@ static void usage(const char *argv0)
|
||||||
"-h, --help this help\n"
|
"-h, --help this help\n"
|
||||||
"-V, --version print current version\n"
|
"-V, --version print current version\n"
|
||||||
"-p, --port=client:port,... set port(s) to play to\n"
|
"-p, --port=client:port,... set port(s) to play to\n"
|
||||||
"-d, --delay=seconds delay after song ends\n",
|
"-d, --delay=seconds delay after song ends\n"
|
||||||
|
"-s, --silent don't show texts\n",
|
||||||
argv0);
|
argv0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,13 +512,14 @@ int main(int argc, char *argv[])
|
||||||
{"version", 0, NULL, 'V'},
|
{"version", 0, NULL, 'V'},
|
||||||
{"port", 1, NULL, 'p'},
|
{"port", 1, NULL, 'p'},
|
||||||
{"delay", 1, NULL, 'd'},
|
{"delay", 1, NULL, 'd'},
|
||||||
|
{"silent", 1, NULL, 's'},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
init_seq();
|
init_seq();
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, "hVp:d:",
|
while ((c = getopt_long(argc, argv, "hVp:d:s",
|
||||||
long_options, NULL)) != -1) {
|
long_options, NULL)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
|
@ -530,6 +534,9 @@ int main(int argc, char *argv[])
|
||||||
case 'd':
|
case 'd':
|
||||||
end_delay = atoi(optarg);
|
end_delay = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
silent = 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -14,6 +14,9 @@ more ALSA sequencer ports.
|
||||||
|
|
||||||
To stop recording, press Ctrl+C.
|
To stop recording, press Ctrl+C.
|
||||||
|
|
||||||
|
When \fB\-\fP is passed to the MIDI Clip file argument,
|
||||||
|
it's recorded to stdout. It implies \fI\-s\fP option, too.
|
||||||
|
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
|
@ -80,6 +83,16 @@ the recording ends when another RETURN key is input from the
|
||||||
terminal. The received events before the start of recording are
|
terminal. The received events before the start of recording are
|
||||||
discarded.
|
discarded.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.I \-s,\-\-silent
|
||||||
|
Don't print messages to stdout.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.I \-P,\-\-profile=file
|
||||||
|
Read the UMP data from the given file and put them into the
|
||||||
|
configuration section of the recorded output.
|
||||||
|
The file must contain only valid UMP data encoded in big-endian.
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
arecordmidi(1)
|
arecordmidi(1)
|
||||||
.br
|
.br
|
||||||
|
|
|
@ -26,6 +26,8 @@ static volatile sig_atomic_t stop;
|
||||||
static int ts_num = 4; /* time signature: numerator */
|
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 last_tick;
|
static int last_tick;
|
||||||
|
static int silent;
|
||||||
|
static const char *profile_ump_file;
|
||||||
|
|
||||||
/* Parse a decimal number from a command line argument. */
|
/* Parse a decimal number from a command line argument. */
|
||||||
static long arg_parse_decimal_num(const char *str, int *err)
|
static long arg_parse_decimal_num(const char *str, int *err)
|
||||||
|
@ -124,7 +126,7 @@ static void create_ump_client(void)
|
||||||
|
|
||||||
sprintf(blkname, "Group %d", i + 1);
|
sprintf(blkname, "Group %d", i + 1);
|
||||||
snd_ump_block_info_set_name(blk, blkname);
|
snd_ump_block_info_set_name(blk, blkname);
|
||||||
snd_ump_block_info_set_direction(blk, SND_UMP_DIR_OUTPUT);
|
snd_ump_block_info_set_direction(blk, SND_UMP_DIR_INPUT);
|
||||||
snd_ump_block_info_set_first_group(blk, i);
|
snd_ump_block_info_set_first_group(blk, i);
|
||||||
snd_ump_block_info_set_num_groups(blk, 1);
|
snd_ump_block_info_set_num_groups(blk, 1);
|
||||||
snd_ump_block_info_set_ui_hint(blk, SND_UMP_BLOCK_UI_HINT_RECEIVER);
|
snd_ump_block_info_set_ui_hint(blk, SND_UMP_BLOCK_UI_HINT_RECEIVER);
|
||||||
|
@ -336,6 +338,47 @@ static void record_event(FILE *file, const snd_seq_ump_event_t *ev)
|
||||||
write_ump(file, ev->ump);
|
write_ump(file, ev->ump);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* read a UMP raw (big-endian) packet, return the packet length in words */
|
||||||
|
static int read_ump_raw(FILE *file, uint32_t *buf)
|
||||||
|
{
|
||||||
|
uint32_t v;
|
||||||
|
int i, num;
|
||||||
|
|
||||||
|
if (fread(buf, 4, 1, file) != 1)
|
||||||
|
return 0;
|
||||||
|
v = be32toh(v);
|
||||||
|
num = snd_ump_packet_length(snd_ump_msg_hdr_type(v));
|
||||||
|
for (i = 1; i < num; i++) {
|
||||||
|
if (fread(buf + i, 4, 1, file) != 1)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read the profile UMP data and write to the configuration */
|
||||||
|
static void write_profiles(FILE *file)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
uint32_t ump[4];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (!profile_ump_file)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fp = fopen(profile_ump_file, "rb");
|
||||||
|
if (!fp)
|
||||||
|
fatal("cannot open the profile '%s'", profile_ump_file);
|
||||||
|
|
||||||
|
while (!feof(fp)) {
|
||||||
|
len = read_ump_raw(fp, ump);
|
||||||
|
if (!len)
|
||||||
|
break;
|
||||||
|
fwrite(ump, 4, len, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
/* write MIDI Clip file header and the configuration packets */
|
/* write MIDI Clip file header and the configuration packets */
|
||||||
static void write_file_header(FILE *file)
|
static void write_file_header(FILE *file)
|
||||||
{
|
{
|
||||||
|
@ -343,7 +386,7 @@ static void write_file_header(FILE *file)
|
||||||
fwrite("SMF2CLIP", 1, 8, file);
|
fwrite("SMF2CLIP", 1, 8, file);
|
||||||
|
|
||||||
/* clip configuration header */
|
/* clip configuration header */
|
||||||
/* FIXME: add profiles */
|
write_profiles(file);
|
||||||
|
|
||||||
/* first DCS */
|
/* first DCS */
|
||||||
write_dcs(file, 0);
|
write_dcs(file, 0);
|
||||||
|
@ -377,7 +420,9 @@ static void help(const char *argv0)
|
||||||
" -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"
|
" -n,--num-events=events fixed number of events to record, then exit\n"
|
||||||
" -u,--ump=version UMP MIDI version (1 or 2)\n"
|
" -u,--ump=version UMP MIDI version (1 or 2)\n"
|
||||||
" -r,--interactive Interactive mode\n",
|
" -r,--interactive Interactive mode\n"
|
||||||
|
" -s,--silent don't print messages\n"
|
||||||
|
" -P,--profile=file configuration profile UMP\n",
|
||||||
argv0);
|
argv0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +438,7 @@ static void sighandler(int sig ATTRIBUTE_UNUSED)
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
static const char short_options[] = "hVp:b:t:n:u:r";
|
static const char short_options[] = "hVp:b:t:n:u:rsP:";
|
||||||
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'},
|
||||||
|
@ -404,6 +449,8 @@ int main(int argc, char *argv[])
|
||||||
{"num-events", 1, NULL, 'n'},
|
{"num-events", 1, NULL, 'n'},
|
||||||
{"ump", 1, NULL, 'u'},
|
{"ump", 1, NULL, 'u'},
|
||||||
{"interactive", 0, NULL, 'r'},
|
{"interactive", 0, NULL, 'r'},
|
||||||
|
{"silent", 0, NULL, 's'},
|
||||||
|
{"profile", 1, NULL, 'P'},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -463,6 +510,12 @@ int main(int argc, char *argv[])
|
||||||
case 'r':
|
case 'r':
|
||||||
interactive = 1;
|
interactive = 1;
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
silent = 1;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
profile_ump_file = optarg;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
help(argv[0]);
|
help(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -481,14 +534,21 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
filename = argv[optind];
|
filename = argv[optind];
|
||||||
|
|
||||||
|
if (!strcmp(filename, "-")) {
|
||||||
|
file = stdout;
|
||||||
|
silent = 1; // imply silent mode
|
||||||
|
} else {
|
||||||
file = fopen(filename, "wb");
|
file = fopen(filename, "wb");
|
||||||
if (!file)
|
if (!file)
|
||||||
fatal("Cannot open %s - %s", filename, strerror(errno));
|
fatal("Cannot open %s - %s", filename, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
write_file_header(file);
|
write_file_header(file);
|
||||||
if (interactive) {
|
if (interactive) {
|
||||||
|
if (!silent) {
|
||||||
printf("Press RETURN to start recording:");
|
printf("Press RETURN to start recording:");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
start_bar(file);
|
start_bar(file);
|
||||||
start = 1;
|
start = 1;
|
||||||
|
@ -515,8 +575,10 @@ int main(int argc, char *argv[])
|
||||||
if (!start) {
|
if (!start) {
|
||||||
start_bar(file);
|
start_bar(file);
|
||||||
start = 1;
|
start = 1;
|
||||||
|
if (!silent) {
|
||||||
printf("Press RETURN to stop recording:");
|
printf("Press RETURN to stop recording:");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
stop = 1;
|
stop = 1;
|
||||||
|
@ -544,10 +606,13 @@ int main(int argc, char *argv[])
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_events && events_received < num_events)
|
if (num_events && events_received < num_events) {
|
||||||
|
if (!silent)
|
||||||
fputs("Warning: Received signal before num_events\n", stdout);
|
fputs("Warning: Received signal before num_events\n", stdout);
|
||||||
|
}
|
||||||
|
|
||||||
write_end_clip(file);
|
write_end_clip(file);
|
||||||
|
if (file != stdout)
|
||||||
fclose(file);
|
fclose(file);
|
||||||
snd_seq_close(seq);
|
snd_seq_close(seq);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in a new issue