Compare commits

...

6 commits

Author SHA1 Message Date
Péter Ujfalusi
79da1551f6
Merge 2185bc70a9 into 7de3cd3b8d 2024-07-23 09:28:59 -07:00
Takashi Iwai
7de3cd3b8d aseqsend: Simplify using the standard helper function
Use the standard MIDI event encoder function provided in alsa-lib and
simplify the code.  We can reduce a lot of lines.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-23 15:50:57 +02:00
Takashi Iwai
580ea3c85e aseqsend: Move snd_seq_set_client_midi_version() call out of main()
Move the low-level API function call to init_seq() instead of calling
directly from main().  Just a code refactoring.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-23 13:51:18 +02:00
Takashi Iwai
dec1ef064a aseqsend: Refine man page
Make man page a bit nicer; reformatting and setting bold/italic
properly, typo fixes, and adding a few more words and sections.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-23 13:44:05 +02:00
Takashi Iwai
df68ec3343 aseqsend: Update the help texts for long options
The help text was forgotten to be updated for the new long options.
Updated now.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-23 13:41:07 +02:00
Peter Ujfalusi
2185bc70a9 aplay: Print '=== PAUSE ===' only if it is supported
Instead of printing the '=== PAUSE ===' unconditionally before calling
do_pause(), move it to the function and only print it if the stream can
be paused.

If the stream cannot be paused that the '=== PAUSE ===' will be replaced
by `PAUSE command ignored (no hw support)` immediately, which is not
observable by users but automation scripts will catch the '=== PAUSE ==='
and might think that the stream is indeed got paused.

Move the print into do_pause() function after the snd_pcm_pause() have
returned without error to make sure it is only printed if the stream is
paused and we are waiting for the pause release from user to proceed.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
2024-07-22 09:03:00 +03:00
3 changed files with 120 additions and 258 deletions

View file

@ -1618,6 +1618,8 @@ static void do_pause(void)
error(_("pause push error: %s"), snd_strerror(err)); error(_("pause push error: %s"), snd_strerror(err));
return; return;
} }
fprintf(stderr, _("\r=== PAUSE === "));
fflush(stderr);
while (1) { while (1) {
b = wait_for_input(); b = wait_for_input();
if (b == ' ' || b == '\r') { if (b == ' ' || b == '\r') {
@ -1642,8 +1644,6 @@ static void check_stdin(void)
while (read(fileno(stdin), &b, 1) == 1) { while (read(fileno(stdin), &b, 1) == 1) {
if (b == ' ' || b == '\r') { if (b == ' ' || b == '\r') {
while (read(fileno(stdin), &b, 1) == 1); while (read(fileno(stdin), &b, 1) == 1);
fprintf(stderr, _("\r=== PAUSE === "));
fflush(stderr);
do_pause(); do_pause();
fprintf(stderr, " \r"); fprintf(stderr, " \r");
fflush(stderr); fflush(stderr);

View file

@ -1,55 +1,63 @@
.TH ASEQSEND 1 "11 Mar 2024" .TH ASEQSEND 1 "11 Mar 2024"
.SH NAME .SH NAME
.B aseqsend aseqsend \- send arbitrary messages to selected ALSA MIDI seqencer port
\- send arbitrary messages to selected ALSA MIDI seqencer port
.SH SYNOPSIS .SH SYNOPSIS
aseqsend \-p client:port -s file-name|"hex encoded byte-string" \fBaseqsend\fP \-p client:port -s file-name
.br
\fBaseqsend\fP \-p client:port "hex encoded byte-string"
.SH DESCRIPTION .SH DESCRIPTION
.B aseqsend \fBaseqsend\fP is a command-line utility which allows one to send
is a command-line utility which allows one to send SysEx (system exclusive) data to ALSA MIDI seqencer port. SysEx (system exclusive) data to ALSA MIDI sequencer port.
It can also send any other MIDI commands. It can also send any other MIDI commands.
Messages to be send can be given in the last argument as hex encoded byte string or can be read from raw binary file. Messages to be sent can be given in the last argument as hex encoded
When sending several SysEx messages at once there is a delay of 1ms after each message as deafult and can be set to different value with option \-i. byte string or can be read from raw binary file.
When sending several SysEx messages at once there is a delay of 1ms
after each message as default and can be set to different value with
option \-i.
A client can be specified by its number, its name, or a prefix of its A client can be specified by its number, its name, or a prefix of its
name. A port is specified by its number; for port 0 of a client, the name. A port is specified by its number; for port 0 of a client, the
":0" part of the port specification can be omitted. ":0" part of the port specification can be omitted.
\fBaseqsend\fP can send UMP packets as MIDI 2.0 device by specifying
via \-u option as well, while the default operation is the legacy MIDI
1.0 byte stream.
.SH OPTIONS .SH OPTIONS
.TP .TP
\-h, \-\-help \fI\-h, \-\-help\fP
Prints a list of options. Prints a list of options.
.TP .TP
\-V, \-\-version \fI\-V, \-\-version\fP
Prints the current version. Prints the current version.
.TP .TP
\-l, \-\-list \fI\-l, \-\-list\FP
Prints a list of possible output ports. Prints a list of possible output ports.
.TP .TP
\-v, \-\-verbose \fI\-v, \-\-verbose\fP
Prints number of bytes actually sent Prints number of bytes actually sent
.TP .TP
\-p, -\-port=client:port \fI\-p, -\-port=client:port\fP
Target port by number or name Target port by number or name
.TP .TP
\-s, \-\-file=filename \fI\-s, \-\-file=filename\fP
Send raw binary data from given file name Send raw binary data from given file name
.TP .TP
\-i, \-\-interval=msec \fI\-i, \-\-interval=msec\fP
Interval between SysEx messages in miliseconds Interval between SysEx messages in milliseconds
.TP .TP
\-u, \-\-ump=version \fI\-u, \-\-ump=version\fP
Specify the MIDI version. 0 for the legacy MIDI 1.0 (default), Specify the MIDI version. 0 for the legacy MIDI 1.0 (default),
1 for UMP MIDI 1.0 protocol and 2 for UMP MIDI 2.0 protocol. 1 for UMP MIDI 1.0 protocol and 2 for UMP MIDI 2.0 protocol.
@ -58,9 +66,12 @@ reads the input as raw UMP packets, 4 each byte in big endian.
.SH EXAMPLES .SH EXAMPLES
aseqsend -p 128:0 "F0 41 10 00 00 64 12 18 00 21 06 59 41 59 4E F7" \fBaseqsend -p 128:0 "F0 41 10 00 00 64 12 18 00 21 06 59 41 59 4E F7"\fP
aseqsend -p 128:0 -s I7BulkDump.syx \fBaseqsend -p 128:0 -s I7BulkDump.syx\fP
.SH SEE ALSO
\fBaseqdump(1)\fP
.SH AUTHOR .SH AUTHOR
Miroslav Kovac <mixxoo@gmail.com> Miroslav Kovac <mixxoo@gmail.com>

View file

@ -44,7 +44,10 @@ static char *send_hex;
static mbyte_t *send_data; static mbyte_t *send_data;
static snd_seq_addr_t addr; static snd_seq_addr_t addr;
static int send_data_length; static int send_data_length;
static int sent_data_c;
static int ump_version; static int ump_version;
static int sysex_interval = 1000; //us
static snd_midi_event_t *edev;
static void error(const char *format, ...) static void error(const char *format, ...)
{ {
@ -72,13 +75,14 @@ static void usage(void)
{ {
printf( printf(
"\nUsage: aseqsend -p target-port -s file-name|\"hex encoded bytes\"\n\n" "\nUsage: aseqsend -p target-port -s file-name|\"hex encoded bytes\"\n\n"
" -h this help\n" " -h,--help this help\n"
" -V print current version\n" " -V,--version print current version\n"
" -v verbose\n" " -v,--verbose verbose mode\n"
" -l list all sequencer ports\n" " -l,--list list all sequencer ports\n"
" -p target port by number or name\n" " -p,--port=c:p target port by number or name\n"
" -s send binary data from given file name\n" " -s,--file=name send binary data from given file name\n"
" -i interval between SysEx messages in miliseconds\n\n"); " -i,--interval=v interval between SysEx messages in miliseconds\n"
" -u,--ump=version MIDI version: 0=legacy (default), 1=MIDI1, 2=MIDI2\n\n");
} }
static void version(void) static void version(void)
@ -211,6 +215,9 @@ static void init_seq(void)
/* set our client's name */ /* set our client's name */
err = snd_seq_set_client_name(seq, "aseqsend"); err = snd_seq_set_client_name(seq, "aseqsend");
check_snd("set client name", err); check_snd("set client name", err);
err = snd_seq_set_client_midi_version(seq, ump_version);
check_snd("set client midi version", err);
} }
static void create_port(void) static void create_port(void)
@ -224,6 +231,13 @@ static void create_port(void)
check_snd("create port", err); check_snd("create port", err);
} }
static void init_midi_event_encoder(void)
{
int err;
err = snd_midi_event_new(256, &edev);
check_snd("create midi event encoder", err);
}
static void list_ports(void) static void list_ports(void)
{ {
@ -256,110 +270,82 @@ static void list_ports(void)
} }
} }
static void send_midi_msg(snd_seq_event_type_t type, mbyte_t *data, int len) /* compose a UMP event, submit it, return the next data position */
static int send_ump(int pos)
{ {
int ump_len = 0, offset = 0;
unsigned int ump[4];
snd_seq_ump_event_t ev;
snd_seq_ump_ev_clear(&ev);
snd_seq_ev_set_source(&ev, 0);
snd_seq_ev_set_dest(&ev, addr.client, addr.port);
snd_seq_ev_set_direct(&ev);
do {
const mbyte_t *data = send_data + pos;
if (pos >= send_data_length)
return pos;
ump[offset] = (data[0] << 24) | (data[1] << 16) |
(data[2] << 8) | data[3];
if (!offset)
ump_len = snd_ump_packet_length(snd_ump_msg_type(ump));
offset++;
pos += 4;
} while (offset < ump_len);
snd_seq_ev_set_ump_data(&ev, ump, ump_len * 4);
snd_seq_ump_event_output(seq, &ev);
snd_seq_drain_output(seq);
sent_data_c += ump_len * 4;
return pos;
}
/* compose an event, submit it, return the next data position */
static int send_midi_bytes(int pos)
{
const mbyte_t *data = send_data + pos;
snd_seq_event_t ev; snd_seq_event_t ev;
int is_sysex = 0;
int end;
snd_seq_ev_clear(&ev); snd_seq_ev_clear(&ev);
snd_seq_ev_set_source(&ev, 0); snd_seq_ev_set_source(&ev, 0);
snd_seq_ev_set_dest(&ev, addr.client, addr.port); snd_seq_ev_set_dest(&ev, addr.client, addr.port);
snd_seq_ev_set_direct(&ev); snd_seq_ev_set_direct(&ev);
if (type == SND_SEQ_EVENT_SYSEX) { if (send_data[pos] == 0xf0) {
snd_seq_ev_set_sysex(&ev, len, data); is_sysex = 1;
} else { for (end = pos + 1; end < send_data_length; end++) {
mbyte_t ch = data[0] & 0xF; if (send_data[end] == 0xf7)
switch (type) {
case SND_SEQ_EVENT_NOTEON:
snd_seq_ev_set_noteon(&ev, ch, data[1], data[2]);
break; break;
case SND_SEQ_EVENT_NOTEOFF:
snd_seq_ev_set_noteoff(&ev, ch, data[1], data[2]);
break;
case SND_SEQ_EVENT_KEYPRESS:
snd_seq_ev_set_keypress(&ev, ch, data[1], data[2]);
break;
case SND_SEQ_EVENT_CONTROLLER:
snd_seq_ev_set_controller(&ev, ch, data[1], data[2]);
break;
case SND_SEQ_EVENT_PITCHBEND:
snd_seq_ev_set_pitchbend(&ev, ch, (data[1]<<7|data[2])-8192);
break;
case SND_SEQ_EVENT_PGMCHANGE:
snd_seq_ev_set_pgmchange(&ev, ch, data[1]);
break;
case SND_SEQ_EVENT_CHANPRESS:
snd_seq_ev_set_chanpress(&ev, ch, data[1]);
break;
case SND_SEQ_EVENT_QFRAME:
case SND_SEQ_EVENT_SONGSEL:
ev.type = type;
ev.data.control.channel = ch;
ev.data.control.value = data[1];
break;
case SND_SEQ_EVENT_SONGPOS:
ev.type = type;
ev.data.control.channel = ch;
ev.data.control.value = data[1] | (data[2] << 7);
break;
case SND_SEQ_EVENT_TUNE_REQUEST:
case SND_SEQ_EVENT_CLOCK:
case SND_SEQ_EVENT_START:
case SND_SEQ_EVENT_CONTINUE:
case SND_SEQ_EVENT_STOP:
case SND_SEQ_EVENT_SENSING:
case SND_SEQ_EVENT_RESET:
ev.type = type;
ev.data.control.channel = ch;
break;
default:
ev.type = SND_SEQ_EVENT_NONE;
} }
if (end == send_data_length)
fatal("SysEx is missing terminating byte (0xF7)");
end++;
snd_seq_ev_set_sysex(&ev, end - pos, send_data + pos);
} else {
end = pos;
while (!snd_midi_event_encode_byte(edev, *data++, &ev)) {
if (++end >= send_data_length)
return end;
}
end++;
} }
snd_seq_event_output(seq, &ev); snd_seq_event_output(seq, &ev);
snd_seq_drain_output(seq); snd_seq_drain_output(seq);
if (is_sysex)
usleep(sysex_interval);
sent_data_c += end - pos;
return end;
} }
static int send_ump(const unsigned char *data)
{
static int ump_len = 0, offset = 0;
unsigned int ump[4];
snd_seq_ump_event_t ev;
ump[offset] = (data[0] << 24) | (data[1] << 16) |
(data[2] << 8) | data[3];
if (!offset)
ump_len = snd_ump_packet_length(snd_ump_msg_type(ump));
offset++;
if (offset < ump_len)
return 0;
snd_seq_ump_ev_clear(&ev);
snd_seq_ev_set_source(&ev, 0);
snd_seq_ev_set_dest(&ev, addr.client, addr.port);
snd_seq_ev_set_direct(&ev);
snd_seq_ev_set_ump_data(&ev, ump, ump_len * 4);
snd_seq_ump_event_output(seq, &ev);
snd_seq_drain_output(seq);
offset = 0;
return ump_len * 4;
}
static int msg_byte_in_range(mbyte_t *data, mbyte_t len)
{
for (int i = 0; i < len; i++) {
if (data[i] > 0x7F) {
error("msg byte value out of range 0-127");
return 0;
}
}
return 1;
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
static const struct option long_options[] = { static const struct option long_options[] = {
@ -377,8 +363,6 @@ int main(int argc, char *argv[])
char do_send_file = 0; char do_send_file = 0;
char do_port_list = 0; char do_port_list = 0;
char verbose = 0; char verbose = 0;
int sysex_interval = 1000; //us
int sent_data_c;
int k; int k;
while ((c = getopt_long(argc, argv, "hi:Vvlp:s:u:", long_options, NULL)) != -1) { while ((c = getopt_long(argc, argv, "hi:Vvlp:s:u:", long_options, NULL)) != -1) {
@ -447,8 +431,9 @@ int main(int argc, char *argv[])
fatal("UMP data must be aligned to 4 bytes"); fatal("UMP data must be aligned to 4 bytes");
init_seq(); init_seq();
snd_seq_set_client_midi_version(seq, ump_version);
create_port(); create_port();
if (!ump_version)
init_midi_event_encoder();
if (snd_seq_parse_address(seq, &addr, port_name) < 0) { if (snd_seq_parse_address(seq, &addr, port_name) < 0) {
error("Unable to parse port name!"); error("Unable to parse port name!");
@ -456,147 +441,13 @@ int main(int argc, char *argv[])
} }
sent_data_c = 0; //counter of actually sent bytes sent_data_c = 0; //counter of actually sent bytes
k = 0; k = 0;
while (k < send_data_length) { while (k < send_data_length) {
if (ump_version)
if (ump_version) { k = send_ump(k);
sent_data_c += send_ump(send_data + k); else
k += 4; k = send_midi_bytes(k);
continue;
}
if (send_data[k] == 0xF0) {
int c1 = k;
while (c1 < send_data_length) {
if (send_data[c1] == 0xF7)
break;
c1++;
}
if (c1 == send_data_length)
fatal("SysEx is missing terminating byte (0xF7)");
int sl = c1-k+1;
sent_data_c += sl;
send_midi_msg(SND_SEQ_EVENT_SYSEX, send_data+k, sl);
usleep(sysex_interval);
k = c1+1;
} else {
mbyte_t tp = send_data[k] >> 4;
if (tp == 0x8) {
if (msg_byte_in_range(send_data + k + 1, 2)) {
send_midi_msg(SND_SEQ_EVENT_NOTEOFF, send_data+k, 3);
sent_data_c += 3;
}
k += 3;
} else if (tp == 0x9) {
if (msg_byte_in_range(send_data + k + 1, 2)) {
send_midi_msg(SND_SEQ_EVENT_NOTEON, send_data+k, 3);
sent_data_c += 3;
}
k += 3;
} else if (tp == 0xA) {
if (msg_byte_in_range(send_data + k + 1, 2)) {
send_midi_msg(SND_SEQ_EVENT_KEYPRESS, send_data+k, 3);
sent_data_c += 3;
}
k += 3;
} else if (tp == 0xB) {
if (msg_byte_in_range(send_data + k + 1, 2)) {
send_midi_msg(SND_SEQ_EVENT_CONTROLLER, send_data+k, 3);
sent_data_c += 3;
}
k += 3;
} else if (tp == 0xC) {
if (msg_byte_in_range(send_data + k + 1, 1)) {
send_midi_msg(SND_SEQ_EVENT_PGMCHANGE, send_data+k, 2);
sent_data_c += 2;
}
k += 2;
} else if (tp == 0xD) {
if (msg_byte_in_range(send_data + k + 1, 1)) {
send_midi_msg(SND_SEQ_EVENT_CHANPRESS, send_data+k, 2);
sent_data_c += 2;
}
k += 2;
} else if (tp == 0xE) {
if (msg_byte_in_range(send_data + k + 1, 2)) {
send_midi_msg(SND_SEQ_EVENT_PITCHBEND, send_data+k, 3);
sent_data_c += 3;
}
k += 3;
} else {
switch (send_data[k]) {
case 0xF1:
if (msg_byte_in_range(send_data + k + 1, 1)) {
send_midi_msg(SND_SEQ_EVENT_QFRAME, send_data+k, 2);
sent_data_c += 2;
}
k += 2;
break;
case 0xF2:
if (msg_byte_in_range(send_data + k + 1, 2)) {
send_midi_msg(SND_SEQ_EVENT_SONGPOS, send_data+k, 3);
sent_data_c += 3;
}
k += 3;
break;
case 0xF3:
if (msg_byte_in_range(send_data + k + 1, 1)) {
send_midi_msg(SND_SEQ_EVENT_SONGSEL, send_data+k, 2);
sent_data_c += 2;
}
k += 2;
break;
case 0xF6:
send_midi_msg(SND_SEQ_EVENT_TUNE_REQUEST, send_data+k, 1);
sent_data_c++;
k++;
break;
case 0xF8:
send_midi_msg(SND_SEQ_EVENT_CLOCK, send_data+k, 1);
sent_data_c++;
k++;
break;
case 0xFA:
send_midi_msg(SND_SEQ_EVENT_START, send_data+k, 1);
sent_data_c++;
k++;
break;
case 0xFB:
send_midi_msg(SND_SEQ_EVENT_CONTINUE, send_data+k, 1);
sent_data_c++;
k++;
break;
case 0xFC:
send_midi_msg(SND_SEQ_EVENT_STOP, send_data+k, 1);
sent_data_c++;
k++;
break;
case 0xFE:
send_midi_msg(SND_SEQ_EVENT_SENSING, send_data+k, 1);
sent_data_c++;
k++;
break;
case 0xFF:
send_midi_msg(SND_SEQ_EVENT_RESET, send_data+k, 1);
sent_data_c++;
k++;
break;
default:
k++;
break;
}
}
}
} }
if (verbose) if (verbose)