mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-10-06 06:27:59 +02:00
Compare commits
8 commits
6642ee90ef
...
70615ba71b
Author | SHA1 | Date | |
---|---|---|---|
|
70615ba71b | ||
|
02b0c3af56 | ||
|
df736ad67a | ||
|
7de3cd3b8d | ||
|
580ea3c85e | ||
|
dec1ef064a | ||
|
df68ec3343 | ||
|
ffc63f76de |
4 changed files with 329 additions and 263 deletions
|
@ -668,13 +668,10 @@ static unsigned char ump_sysex7_data(const unsigned int *ump,
|
|||
return (ump[offset / 4] >> ((3 - (offset & 3)) * 8)) & 0xff;
|
||||
}
|
||||
|
||||
static void dump_ump_sysex_event(const unsigned int *ump)
|
||||
static void dump_ump_sysex_status(const char *prefix, unsigned int status)
|
||||
{
|
||||
int i, length;
|
||||
|
||||
printf("Group %2d, ", group_number(snd_ump_msg_group(ump)));
|
||||
printf("SysEx ");
|
||||
switch (snd_ump_sysex_msg_status(ump)) {
|
||||
printf("%s ", prefix);
|
||||
switch (status) {
|
||||
case SND_UMP_SYSEX_STATUS_SINGLE:
|
||||
printf("Single ");
|
||||
break;
|
||||
|
@ -688,17 +685,49 @@ static void dump_ump_sysex_event(const unsigned int *ump)
|
|||
printf("End ");
|
||||
break;
|
||||
default:
|
||||
printf("Unknown(0x%x)", snd_ump_sysex_msg_status(ump));
|
||||
printf("(0x%04x)", status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_ump_sysex_event(const unsigned int *ump)
|
||||
{
|
||||
int i, length;
|
||||
|
||||
printf("Group %2d, ", group_number(snd_ump_msg_group(ump)));
|
||||
dump_ump_sysex_status("SysEx", snd_ump_sysex_msg_status(ump));
|
||||
length = snd_ump_sysex_msg_length(ump);
|
||||
printf(" length %d ", length);
|
||||
if (length > 14)
|
||||
length = 14;
|
||||
for (i = 0; i < length; i++)
|
||||
printf("%s%02x", i ? ":" : "", ump_sysex7_data(ump, i));
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static unsigned char ump_sysex8_data(const unsigned int *ump,
|
||||
unsigned int offset)
|
||||
{
|
||||
offset += 3;
|
||||
return (ump[offset / 4] >> ((3 - (offset & 3)) * 8)) & 0xff;
|
||||
}
|
||||
|
||||
static void dump_ump_sysex8_event(const unsigned int *ump)
|
||||
{
|
||||
int i, length;
|
||||
|
||||
printf("Group %2d, ", group_number(snd_ump_msg_group(ump)));
|
||||
dump_ump_sysex_status("SysEx8", snd_ump_sysex_msg_status(ump));
|
||||
length = snd_ump_sysex_msg_length(ump);
|
||||
printf(" length %d ", length);
|
||||
printf(" stream %d ", (ump[0] >> 8) & 0xff);
|
||||
if (length > 13)
|
||||
length = 13;
|
||||
for (i = 0; i < length; i++)
|
||||
printf("%s%02x", i ? ":" : "", ump_sysex8_data(ump, i));
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void print_ump_string(const unsigned int *ump, unsigned int fmt,
|
||||
unsigned int offset, int maxlen)
|
||||
{
|
||||
|
@ -965,6 +994,9 @@ static void dump_ump_event(const snd_seq_ump_event_t *ev)
|
|||
case SND_UMP_MSG_TYPE_DATA:
|
||||
dump_ump_sysex_event(ev->ump);
|
||||
break;
|
||||
case SND_UMP_MSG_TYPE_EXTENDED_DATA:
|
||||
dump_ump_sysex8_event(ev->ump);
|
||||
break;
|
||||
case SND_UMP_MSG_TYPE_FLEX_DATA:
|
||||
dump_ump_flex_data_event(ev->ump);
|
||||
break;
|
||||
|
|
|
@ -1,55 +1,63 @@
|
|||
.TH ASEQSEND 1 "11 Mar 2024"
|
||||
|
||||
.SH NAME
|
||||
.B aseqsend
|
||||
\- send arbitrary messages to selected ALSA MIDI seqencer port
|
||||
aseqsend \- send arbitrary messages to selected ALSA MIDI seqencer port
|
||||
|
||||
.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
|
||||
.B aseqsend
|
||||
is a command-line utility which allows one to send SysEx (system exclusive) data to ALSA MIDI seqencer port.
|
||||
\fBaseqsend\fP is a command-line utility which allows one to send
|
||||
SysEx (system exclusive) data to ALSA MIDI sequencer port.
|
||||
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.
|
||||
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.
|
||||
Messages to be sent can be given in the last argument as hex encoded
|
||||
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
|
||||
name. A port is specified by its number; for port 0 of a client, the
|
||||
":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
|
||||
|
||||
.TP
|
||||
\-h, \-\-help
|
||||
\fI\-h, \-\-help\fP
|
||||
Prints a list of options.
|
||||
|
||||
.TP
|
||||
\-V, \-\-version
|
||||
\fI\-V, \-\-version\fP
|
||||
Prints the current version.
|
||||
|
||||
.TP
|
||||
\-l, \-\-list
|
||||
\fI\-l, \-\-list\FP
|
||||
Prints a list of possible output ports.
|
||||
|
||||
.TP
|
||||
\-v, \-\-verbose
|
||||
\fI\-v, \-\-verbose\fP
|
||||
Prints number of bytes actually sent
|
||||
|
||||
.TP
|
||||
\-p, -\-port=client:port
|
||||
\fI\-p, -\-port=client:port\fP
|
||||
Target port by number or name
|
||||
|
||||
.TP
|
||||
\-s, \-\-file=filename
|
||||
\fI\-s, \-\-file=filename\fP
|
||||
Send raw binary data from given file name
|
||||
|
||||
.TP
|
||||
\-i, \-\-interval=msec
|
||||
Interval between SysEx messages in miliseconds
|
||||
\fI\-i, \-\-interval=msec\fP
|
||||
Interval between SysEx messages in milliseconds
|
||||
|
||||
.TP
|
||||
\-u, \-\-ump=version
|
||||
\fI\-u, \-\-ump=version\fP
|
||||
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.
|
||||
|
||||
|
@ -58,9 +66,12 @@ reads the input as raw UMP packets, 4 each byte in big endian.
|
|||
|
||||
.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
|
||||
Miroslav Kovac <mixxoo@gmail.com>
|
||||
|
|
|
@ -44,7 +44,10 @@ static char *send_hex;
|
|||
static mbyte_t *send_data;
|
||||
static snd_seq_addr_t addr;
|
||||
static int send_data_length;
|
||||
static int sent_data_c;
|
||||
static int ump_version;
|
||||
static int sysex_interval = 1000; //us
|
||||
static snd_midi_event_t *edev;
|
||||
|
||||
static void error(const char *format, ...)
|
||||
{
|
||||
|
@ -72,13 +75,14 @@ static void usage(void)
|
|||
{
|
||||
printf(
|
||||
"\nUsage: aseqsend -p target-port -s file-name|\"hex encoded bytes\"\n\n"
|
||||
" -h this help\n"
|
||||
" -V print current version\n"
|
||||
" -v verbose\n"
|
||||
" -l list all sequencer ports\n"
|
||||
" -p target port by number or name\n"
|
||||
" -s send binary data from given file name\n"
|
||||
" -i interval between SysEx messages in miliseconds\n\n");
|
||||
" -h,--help this help\n"
|
||||
" -V,--version print current version\n"
|
||||
" -v,--verbose verbose mode\n"
|
||||
" -l,--list list all sequencer ports\n"
|
||||
" -p,--port=c:p target port by number or name\n"
|
||||
" -s,--file=name send binary data from given file name\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)
|
||||
|
@ -211,6 +215,9 @@ static void init_seq(void)
|
|||
/* set our client's name */
|
||||
err = snd_seq_set_client_name(seq, "aseqsend");
|
||||
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)
|
||||
|
@ -224,6 +231,13 @@ static void create_port(void)
|
|||
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)
|
||||
{
|
||||
|
@ -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;
|
||||
int is_sysex = 0;
|
||||
int end;
|
||||
|
||||
snd_seq_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);
|
||||
|
||||
if (type == SND_SEQ_EVENT_SYSEX) {
|
||||
snd_seq_ev_set_sysex(&ev, len, data);
|
||||
} else {
|
||||
mbyte_t ch = data[0] & 0xF;
|
||||
|
||||
switch (type) {
|
||||
case SND_SEQ_EVENT_NOTEON:
|
||||
snd_seq_ev_set_noteon(&ev, ch, data[1], data[2]);
|
||||
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 (send_data[pos] == 0xf0) {
|
||||
is_sysex = 1;
|
||||
for (end = pos + 1; end < send_data_length; end++) {
|
||||
if (send_data[end] == 0xf7)
|
||||
break;
|
||||
}
|
||||
|
||||
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_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[])
|
||||
{
|
||||
static const struct option long_options[] = {
|
||||
|
@ -377,8 +363,6 @@ int main(int argc, char *argv[])
|
|||
char do_send_file = 0;
|
||||
char do_port_list = 0;
|
||||
char verbose = 0;
|
||||
int sysex_interval = 1000; //us
|
||||
int sent_data_c;
|
||||
int k;
|
||||
|
||||
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");
|
||||
|
||||
init_seq();
|
||||
snd_seq_set_client_midi_version(seq, ump_version);
|
||||
create_port();
|
||||
if (!ump_version)
|
||||
init_midi_event_encoder();
|
||||
|
||||
if (snd_seq_parse_address(seq, &addr, port_name) < 0) {
|
||||
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
|
||||
|
||||
k = 0;
|
||||
|
||||
while (k < send_data_length) {
|
||||
|
||||
if (ump_version) {
|
||||
sent_data_c += send_ump(send_data + k);
|
||||
k += 4;
|
||||
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 (ump_version)
|
||||
k = send_ump(k);
|
||||
else
|
||||
k = send_midi_bytes(k);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
|
|
|
@ -921,6 +921,172 @@ static int pre_process_arrays(struct tplg_pre_processor *tplg_pp, snd_config_t *
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pre_process_subtree_copy(struct tplg_pre_processor *tplg_pp, snd_config_t *curr,
|
||||
snd_config_t *top)
|
||||
{
|
||||
snd_config_iterator_t i, next;
|
||||
snd_config_t *subtrees;
|
||||
int ret;
|
||||
|
||||
ret = snd_config_search(curr, "SubTreeCopy", &subtrees);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
snd_config_for_each(i, next, subtrees) {
|
||||
snd_config_t *n, *source_cfg, *target_cfg, *type_cfg;
|
||||
snd_config_t *source_tree, *target_tree, *tmp, *subtree_cfg;
|
||||
const char *id, *source_id;
|
||||
const char *type = NULL;
|
||||
const char *target_id = NULL;
|
||||
bool override = false;
|
||||
|
||||
n = snd_config_iterator_entry(i);
|
||||
ret = snd_config_get_id(n, &id);
|
||||
if (ret < 0) {
|
||||
SNDERR("Failed to get ID for subtree copy\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get the type of copy ex: override/extend if set, by default override is false */
|
||||
ret = snd_config_search(n, "type", &type_cfg);
|
||||
if (ret >= 0) {
|
||||
ret = snd_config_get_string(type_cfg, &type);
|
||||
if (ret >= 0) {
|
||||
if (strcmp(type, "override") && strcmp(type, "extend")) {
|
||||
SNDERR("Invalid value for sub tree copy type %s\n", type);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!strcmp(type, "override"))
|
||||
override = true;
|
||||
}
|
||||
}
|
||||
|
||||
ret = snd_config_search(n, "source", &source_cfg);
|
||||
if (ret < 0) {
|
||||
SNDERR("failed to get source config for subtree %s\n", id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* if the target node is not set, the subtree will be copied to current node */
|
||||
ret = snd_config_search(n, "target", &target_cfg);
|
||||
if (ret >= 0) {
|
||||
snd_config_t *temp_target;
|
||||
char *s;
|
||||
|
||||
ret = snd_config_get_string(target_cfg, &target_id);
|
||||
if (ret < 0) {
|
||||
SNDERR("Invalid target id for subtree %s\n", id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a temporary node with target_id and merge with top to avoid
|
||||
* failure in case the target node ID doesn't exist already
|
||||
*/
|
||||
s = tplg_snprintf("%s {}", target_id);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = snd_config_load_string(&temp_target, s, 0);
|
||||
free(s);
|
||||
if (ret < 0) {
|
||||
SNDERR("Cannot create temp node with target id %s\n", target_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_config_merge(top, temp_target, false);
|
||||
if (ret < 0) {
|
||||
SNDERR("Cannot merge temp node with target id %s\n", target_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_config_search(top, target_id, &target_tree);
|
||||
if (ret < 0) {
|
||||
SNDERR("failed to get target tree %s\n", target_id);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
target_tree = curr;
|
||||
}
|
||||
|
||||
/* get the source tree node */
|
||||
ret = snd_config_get_string(source_cfg, &source_id);
|
||||
if (ret < 0) {
|
||||
SNDERR("Invalid source node id for subtree %s\n", id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_config_search(top, source_id, &source_tree);
|
||||
if (ret < 0) {
|
||||
SNDERR("failed to get source tree %s\n", source_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get the subtree to be merged */
|
||||
ret = snd_config_search(n, "tree", &subtree_cfg);
|
||||
if (ret < 0)
|
||||
subtree_cfg = NULL;
|
||||
|
||||
/* make a temp copy of the source tree */
|
||||
ret = snd_config_copy(&tmp, source_tree);
|
||||
if (ret < 0) {
|
||||
SNDERR("failed to copy source tree for subtreecopy %s\n", id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* merge the current block with the source tree */
|
||||
ret = snd_config_merge(tmp, subtree_cfg, override);
|
||||
if (ret < 0) {
|
||||
snd_config_delete(tmp);
|
||||
SNDERR("Failed to merge source tree w/ subtree %s\n", id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* merge the merged block to the target tree */
|
||||
ret = snd_config_merge(target_tree, tmp, override);
|
||||
if (ret < 0) {
|
||||
snd_config_delete(tmp);
|
||||
SNDERR("Failed to merge subtree %s w/ target\n", id);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* all subtree copies have been processed, remove the node */
|
||||
snd_config_delete(subtrees);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pre_process_subtree_copies(struct tplg_pre_processor *tplg_pp, snd_config_t *top,
|
||||
snd_config_t *curr)
|
||||
{
|
||||
snd_config_iterator_t i, next;
|
||||
int ret;
|
||||
|
||||
if (snd_config_get_type(curr) != SND_CONFIG_TYPE_COMPOUND)
|
||||
return 0;
|
||||
|
||||
/* process subtreecopies at this node */
|
||||
ret = pre_process_subtree_copy(tplg_pp, curr, top);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* process subtreecopies at all child nodes */
|
||||
snd_config_for_each(i, next, curr) {
|
||||
snd_config_t *n;
|
||||
|
||||
n = snd_config_iterator_entry(i);
|
||||
|
||||
/* process subtreecopies at this node */
|
||||
ret = pre_process_subtree_copies(tplg_pp, top, n);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* version < 1.2.6 */
|
||||
|
||||
int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size,
|
||||
|
@ -980,6 +1146,12 @@ int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_
|
|||
fprintf(stderr, "Failed to process object arrays in input config\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = pre_process_subtree_copies(tplg_pp, tplg_pp->input_cfg, tplg_pp->input_cfg);
|
||||
if (err < 0) {
|
||||
SNDERR("Failed to process subtree copies in input config\n");
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = pre_process_config(tplg_pp, tplg_pp->input_cfg);
|
||||
|
|
Loading…
Reference in a new issue