mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-10-06 04:27:59 +02:00
Compare commits
12 commits
d0be034836
...
71413387fd
Author | SHA1 | Date | |
---|---|---|---|
|
71413387fd | ||
|
74daf3a93a | ||
|
274f1b9ebf | ||
|
f0df4b4cfe | ||
|
b1269eefdd | ||
|
99ce4b1d8a | ||
|
68491dd464 | ||
|
330741d523 | ||
|
2ee6c170a8 | ||
|
e609d66807 | ||
|
16533f81de | ||
|
938a26df25 |
6 changed files with 525 additions and 28 deletions
|
@ -215,10 +215,6 @@ static void print_port(snd_seq_t *seq ATTRIBUTE_UNUSED,
|
|||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef HANDLE_SHOW_ALL
|
||||
if (snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_INACTIVE)
|
||||
printf(",INACTIVE");
|
||||
#endif
|
||||
#ifdef HAVE_SEQ_CLIENT_INFO_GET_CARD
|
||||
card = snd_seq_client_info_get_card(cinfo);
|
||||
#endif
|
||||
|
@ -232,9 +228,14 @@ static void print_port(snd_seq_t *seq ATTRIBUTE_UNUSED,
|
|||
printf(",pid=%d", pid);
|
||||
printf("]\n");
|
||||
}
|
||||
printf(" %3d '%-16s'\n",
|
||||
printf(" %3d '%-16s'",
|
||||
snd_seq_port_info_get_port(pinfo),
|
||||
snd_seq_port_info_get_name(pinfo));
|
||||
#ifdef HANDLE_SHOW_ALL
|
||||
if (snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_INACTIVE)
|
||||
printf(" [INACTIVE]");
|
||||
#endif
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void print_port_and_subs(snd_seq_t *seq, snd_seq_client_info_t *cinfo,
|
||||
|
|
|
@ -44,8 +44,9 @@ environment variable if none is given on the command line.
|
|||
.B aplaymidi2
|
||||
supports only basic UMP events: in addition to the standard MIDI1 and
|
||||
MIDI2 CVMs and 7bit SysEx, only the following are supported:
|
||||
DCTPQ, DC, Set Tempo, Start Clip and End Clip.
|
||||
Lyrics and other meta data in Flex Data are skipped, so far.
|
||||
DCTPQ, DC, Set Tempo, Start Clip, End Clip.
|
||||
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
|
||||
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.
|
||||
Default is 2 seconds.
|
||||
|
||||
.TP
|
||||
.I \-s, \-\-silent
|
||||
Don't show message texts.
|
||||
|
||||
.SH SEE ALSO
|
||||
pmidi(1)
|
||||
.br
|
||||
|
|
|
@ -20,6 +20,7 @@ static int port_count;
|
|||
static snd_seq_addr_t ports[16];
|
||||
static int queue;
|
||||
static int end_delay = 2;
|
||||
static int silent;
|
||||
|
||||
static unsigned int _current_tempo = 50000000; /* default 120 bpm */
|
||||
static unsigned int tempo_base = 10;
|
||||
|
@ -288,6 +289,117 @@ static void send_ump(const uint32_t *ump, int len)
|
|||
snd_seq_ump_event_output(seq, &ev);
|
||||
}
|
||||
|
||||
struct flexdata_text_prefix {
|
||||
unsigned char status_bank;
|
||||
unsigned char status;
|
||||
const char *prefix;
|
||||
};
|
||||
|
||||
static struct flexdata_text_prefix text_prefix[] = {
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_PROJECT_NAME,
|
||||
.prefix = "Project" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_SONG_NAME,
|
||||
.prefix = "Song" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_MIDI_CLIP_NAME,
|
||||
.prefix = "MIDI Clip" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_COPYRIGHT_NOTICE,
|
||||
.prefix = "Copyright" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_COMPOSER_NAME,
|
||||
.prefix = "Composer" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_LYRICIST_NAME,
|
||||
.prefix = "Lyricist" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_ARRANGER_NAME,
|
||||
.prefix = "Arranger" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_PUBLISHER_NAME,
|
||||
.prefix = "Publisher" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_PRIMARY_PERFORMER,
|
||||
.prefix = "Performer" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_ACCOMPANY_PERFORMAER,
|
||||
.prefix = "Accompany Performer" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RECORDING_DATE,
|
||||
.prefix = "Recording Date" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RECORDING_LOCATION,
|
||||
.prefix = "Recording Location" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_LYRICS,
|
||||
.prefix = "Lyrics" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_LYRICS_LANGUAGE,
|
||||
.prefix = "Lyrics Language" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RUBY,
|
||||
.prefix = "Ruby" },
|
||||
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
|
||||
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RUBY_LANGUAGE,
|
||||
.prefix = "Ruby Language" },
|
||||
{}
|
||||
};
|
||||
|
||||
static void show_text(const uint32_t *ump)
|
||||
{
|
||||
static unsigned char textbuf[256];
|
||||
static int len;
|
||||
const snd_ump_msg_flex_data_t *fh =
|
||||
(const snd_ump_msg_flex_data_t *)ump;
|
||||
const char *prefix;
|
||||
int i;
|
||||
|
||||
if (fh->meta.format == SND_UMP_FLEX_DATA_MSG_FORMAT_SINGLE ||
|
||||
fh->meta.format == SND_UMP_FLEX_DATA_MSG_FORMAT_START)
|
||||
len = 0;
|
||||
|
||||
for (i = 0; i < 12 && len < (int)sizeof(textbuf); i++) {
|
||||
textbuf[len] = fh->meta.data[i / 4] >> ((3 - (i % 4)) * 8);
|
||||
if (!textbuf[len])
|
||||
break;
|
||||
switch (textbuf[len]) {
|
||||
case 0x0a: /* end of paragraph */
|
||||
case 0x0d: /* end of line */
|
||||
textbuf[len] = '\n';
|
||||
break;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
if (fh->meta.format != SND_UMP_FLEX_DATA_MSG_FORMAT_SINGLE &&
|
||||
fh->meta.format != SND_UMP_FLEX_DATA_MSG_FORMAT_END)
|
||||
return;
|
||||
|
||||
if (len >= (int)sizeof(textbuf))
|
||||
len = sizeof(textbuf) - 1;
|
||||
textbuf[len] = 0;
|
||||
|
||||
prefix = NULL;
|
||||
for (i = 0; text_prefix[i].status_bank; i++) {
|
||||
if (text_prefix[i].status_bank == fh->meta.status_bank &&
|
||||
text_prefix[i].status == fh->meta.status) {
|
||||
prefix = text_prefix[i].prefix;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
printf("%s: %s\n", prefix, textbuf);
|
||||
} else {
|
||||
printf("(%d:%d): %s\n", fh->meta.status_bank, fh->meta.status,
|
||||
textbuf);
|
||||
}
|
||||
|
||||
len = 0;
|
||||
}
|
||||
|
||||
/* play the given MIDI Clip File content */
|
||||
static void play_midi(FILE *file)
|
||||
{
|
||||
|
@ -318,6 +430,13 @@ static void play_midi(FILE *file)
|
|||
set_tempo(fh->set_tempo.tempo);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_METADATA ||
|
||||
fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT) {
|
||||
if (!silent)
|
||||
show_text(ump);
|
||||
continue;
|
||||
}
|
||||
} else if (h->type == SND_UMP_MSG_TYPE_STREAM) {
|
||||
const snd_ump_msg_stream_t *sh =
|
||||
(const snd_ump_msg_stream_t *)ump;
|
||||
|
@ -376,7 +495,8 @@ static void usage(const char *argv0)
|
|||
"-h, --help this help\n"
|
||||
"-V, --version print current version\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);
|
||||
}
|
||||
|
||||
|
@ -392,13 +512,14 @@ int main(int argc, char *argv[])
|
|||
{"version", 0, NULL, 'V'},
|
||||
{"port", 1, NULL, 'p'},
|
||||
{"delay", 1, NULL, 'd'},
|
||||
{"silent", 1, NULL, 's'},
|
||||
{0}
|
||||
};
|
||||
int c;
|
||||
|
||||
init_seq();
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hVp:d:",
|
||||
while ((c = getopt_long(argc, argv, "hVp:d:s",
|
||||
long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
|
@ -413,6 +534,9 @@ int main(int argc, char *argv[])
|
|||
case 'd':
|
||||
end_delay = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
silent = 1;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
|
|
|
@ -14,6 +14,9 @@ more ALSA sequencer ports.
|
|||
|
||||
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
|
||||
|
||||
.TP
|
||||
|
@ -80,6 +83,22 @@ the recording ends when another RETURN key is input from the
|
|||
terminal. The received events before the start of recording are
|
||||
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.
|
||||
|
||||
.TP
|
||||
.I \-\-song=text, \-\-clip=text, \-\-copyright=text, \-\-composer=text, \
|
||||
\-\-lyricist=text, \-\-arranger=text, \-\-publisher=text, \
|
||||
\-\-performer=text \-\-accompany=text, \-\-date=text, \-\-location=text
|
||||
Put the given meta data text in the configuration section.
|
||||
|
||||
.SH SEE ALSO
|
||||
arecordmidi(1)
|
||||
.br
|
||||
|
|
|
@ -26,6 +26,13 @@ static volatile sig_atomic_t stop;
|
|||
static int ts_num = 4; /* time signature: numerator */
|
||||
static int ts_div = 4; /* time signature: denominator */
|
||||
static int last_tick;
|
||||
static int silent;
|
||||
static const char *profile_ump_file;
|
||||
|
||||
#define MAX_METADATA 16
|
||||
static int metadata_num;
|
||||
static unsigned int metadata_types[MAX_METADATA];
|
||||
static const char *metadata_texts[MAX_METADATA];
|
||||
|
||||
/* Parse a decimal number from a command line argument. */
|
||||
static long arg_parse_decimal_num(const char *str, int *err)
|
||||
|
@ -321,8 +328,6 @@ static void delta_time(FILE *file, const snd_seq_ump_event_t *ev)
|
|||
|
||||
if (diff <= 0)
|
||||
return;
|
||||
if (tempo_base == 1000)
|
||||
diff *= 100;
|
||||
write_dcs(file, diff);
|
||||
last_tick = ev->time.tick;
|
||||
}
|
||||
|
@ -338,14 +343,96 @@ static void record_event(FILE *file, const snd_seq_ump_event_t *ev)
|
|||
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 Flex Data metadata text given by command lines */
|
||||
static void write_metadata(FILE *file, unsigned int type, const char *text)
|
||||
{
|
||||
int len = strlen(text), size;
|
||||
unsigned int format = SND_UMP_FLEX_DATA_MSG_FORMAT_START;
|
||||
|
||||
while (len > 0) {
|
||||
snd_ump_msg_flex_data_t d = {};
|
||||
|
||||
if (len <= 12) {
|
||||
if (format == SND_UMP_FLEX_DATA_MSG_FORMAT_CONTINUE)
|
||||
format = SND_UMP_FLEX_DATA_MSG_FORMAT_END;
|
||||
else
|
||||
format = SND_UMP_FLEX_DATA_MSG_FORMAT_SINGLE;
|
||||
size = len;
|
||||
} else {
|
||||
size = 12;
|
||||
}
|
||||
|
||||
d.meta.type = SND_UMP_MSG_TYPE_FLEX_DATA;
|
||||
d.meta.addrs = SND_UMP_FLEX_DATA_MSG_ADDR_GROUP;
|
||||
d.meta.status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA;
|
||||
d.meta.status = type;
|
||||
d.meta.format = format;
|
||||
|
||||
/* keep the data in big endian */
|
||||
d.raw[0] = htobe32(d.raw[0]);
|
||||
/* strings are copied as-is in big-endian */
|
||||
memcpy(d.meta.data, text, size);
|
||||
|
||||
fwrite(d.raw, 4, 4, file);
|
||||
len -= size;
|
||||
format = SND_UMP_FLEX_DATA_MSG_FORMAT_CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* write MIDI Clip file header and the configuration packets */
|
||||
static void write_file_header(FILE *file)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* header id */
|
||||
fwrite("SMF2CLIP", 1, 8, file);
|
||||
|
||||
/* clip configuration header */
|
||||
/* FIXME: add profiles */
|
||||
write_profiles(file);
|
||||
|
||||
for (i = 0; i < metadata_num; i++)
|
||||
write_metadata(file, metadata_types[i], metadata_texts[i]);
|
||||
|
||||
/* first DCS */
|
||||
write_dcs(file, 0);
|
||||
|
@ -355,6 +442,13 @@ static void write_file_header(FILE *file)
|
|||
/* write start bar */
|
||||
static void start_bar(FILE *file)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* start the queue */
|
||||
err = snd_seq_start_queue(seq, queue, NULL);
|
||||
check_snd("start queue", err);
|
||||
snd_seq_drain_output(seq);
|
||||
|
||||
write_start_clip(file);
|
||||
write_tempo(file);
|
||||
write_time_sig(file);
|
||||
|
@ -372,7 +466,23 @@ static void help(const char *argv0)
|
|||
" -i,--timesig=nn:dd time signature\n"
|
||||
" -n,--num-events=events fixed number of events to record, then exit\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"
|
||||
" --project=text put project name meta data text\n"
|
||||
" --song=text put song name meta data text\n"
|
||||
" --clip=text put MIDI clip name meta data text\n"
|
||||
" --copyright=text put copyright notice meta data text\n"
|
||||
" --composer=text put composer name meta data text\n"
|
||||
" --lyricist=text put lyricist name meta data text\n"
|
||||
" --arranger=text put arranger name meta data text\n"
|
||||
" --publisher=text put publisher name meta data text\n"
|
||||
" --publisher=text put publisher name meta data text\n"
|
||||
" --publisher=text put publisher name meta data text\n"
|
||||
" --performer=text put performer name meta data text\n"
|
||||
" --accompany=text put accompany performer name meta data text\n"
|
||||
" --date=text put recording date meta data text\n"
|
||||
" --location=text put recording location meta data text\n",
|
||||
argv0);
|
||||
}
|
||||
|
||||
|
@ -386,9 +496,25 @@ static void sighandler(int sig ATTRIBUTE_UNUSED)
|
|||
stop = 1;
|
||||
}
|
||||
|
||||
#define OPT_META_BIT 0x1000
|
||||
enum {
|
||||
OPT_META_PROJECT = 0x1001,
|
||||
OPT_META_SONG = 0x1002,
|
||||
OPT_META_CLIP = 0x1003,
|
||||
OPT_META_COPYRIGHT = 0x1004,
|
||||
OPT_META_COMPOSER = 0x1005,
|
||||
OPT_META_LYRICIST = 0x1006,
|
||||
OPT_META_ARRANGER = 0x1007,
|
||||
OPT_META_PUBLISHER = 0x1008,
|
||||
OPT_META_PERFORMER = 0x1009,
|
||||
OPT_META_ACCOMPANY = 0x100a,
|
||||
OPT_META_DATE = 0x100b,
|
||||
OPT_META_LOCATION = 0x100c,
|
||||
};
|
||||
|
||||
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[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"version", 0, NULL, 'V'},
|
||||
|
@ -399,6 +525,21 @@ int main(int argc, char *argv[])
|
|||
{"num-events", 1, NULL, 'n'},
|
||||
{"ump", 1, NULL, 'u'},
|
||||
{"interactive", 0, NULL, 'r'},
|
||||
{"silent", 0, NULL, 's'},
|
||||
{"profile", 1, NULL, 'P'},
|
||||
/* meta data texts */
|
||||
{"project", 1, NULL, OPT_META_PROJECT},
|
||||
{"song", 1, NULL, OPT_META_SONG},
|
||||
{"clip", 1, NULL, OPT_META_CLIP},
|
||||
{"copyright", 1, NULL, OPT_META_COPYRIGHT},
|
||||
{"composer", 1, NULL, OPT_META_COMPOSER},
|
||||
{"lyricist", 1, NULL, OPT_META_LYRICIST},
|
||||
{"arranger", 1, NULL, OPT_META_ARRANGER},
|
||||
{"publisher", 1, NULL, OPT_META_PUBLISHER},
|
||||
{"performer", 1, NULL, OPT_META_PERFORMER},
|
||||
{"accompany", 1, NULL, OPT_META_ACCOMPANY},
|
||||
{"date", 1, NULL, OPT_META_DATE},
|
||||
{"location", 1, NULL, OPT_META_LOCATION},
|
||||
{0}
|
||||
};
|
||||
|
||||
|
@ -458,7 +599,21 @@ int main(int argc, char *argv[])
|
|||
case 'r':
|
||||
interactive = 1;
|
||||
break;
|
||||
case 's':
|
||||
silent = 1;
|
||||
break;
|
||||
case 'P':
|
||||
profile_ump_file = optarg;
|
||||
break;
|
||||
default:
|
||||
if (c & OPT_META_BIT) {
|
||||
if (metadata_num >= MAX_METADATA)
|
||||
fatal("Too many metadata given");
|
||||
metadata_types[metadata_num] = c & 0x0f;
|
||||
metadata_texts[metadata_num] = optarg;
|
||||
metadata_num++;
|
||||
break;
|
||||
}
|
||||
help(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
@ -476,23 +631,26 @@ int main(int argc, char *argv[])
|
|||
|
||||
filename = argv[optind];
|
||||
|
||||
file = fopen(filename, "wb");
|
||||
if (!file)
|
||||
fatal("Cannot open %s - %s", filename, strerror(errno));
|
||||
if (!strcmp(filename, "-")) {
|
||||
file = stdout;
|
||||
silent = 1; // imply silent mode
|
||||
} else {
|
||||
file = fopen(filename, "wb");
|
||||
if (!file)
|
||||
fatal("Cannot open %s - %s", filename, strerror(errno));
|
||||
}
|
||||
|
||||
write_file_header(file);
|
||||
if (interactive) {
|
||||
printf("Press RETURN to start recording:");
|
||||
fflush(stdout);
|
||||
if (!silent) {
|
||||
printf("Press RETURN to start recording:");
|
||||
fflush(stdout);
|
||||
}
|
||||
} else {
|
||||
start_bar(file);
|
||||
start = 1;
|
||||
}
|
||||
|
||||
err = snd_seq_start_queue(seq, queue, NULL);
|
||||
check_snd("start queue", err);
|
||||
snd_seq_drain_output(seq);
|
||||
|
||||
err = snd_seq_nonblock(seq, 1);
|
||||
check_snd("set nonblock mode", err);
|
||||
|
||||
|
@ -514,8 +672,10 @@ int main(int argc, char *argv[])
|
|||
if (!start) {
|
||||
start_bar(file);
|
||||
start = 1;
|
||||
printf("Press RETURN to stop recording:");
|
||||
fflush(stdout);
|
||||
if (!silent) {
|
||||
printf("Press RETURN to stop recording:");
|
||||
fflush(stdout);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
stop = 1;
|
||||
|
@ -543,11 +703,14 @@ int main(int argc, char *argv[])
|
|||
break;
|
||||
}
|
||||
|
||||
if (num_events && events_received < num_events)
|
||||
fputs("Warning: Received signal before num_events\n", stdout);
|
||||
if (num_events && events_received < num_events) {
|
||||
if (!silent)
|
||||
fputs("Warning: Received signal before num_events\n", stdout);
|
||||
}
|
||||
|
||||
write_end_clip(file);
|
||||
fclose(file);
|
||||
if (file != stdout)
|
||||
fclose(file);
|
||||
snd_seq_close(seq);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -921,6 +921,185 @@ 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. create an empty tree if it dosen't exist */
|
||||
ret = snd_config_search(n, "tree", &subtree_cfg);
|
||||
if (ret < 0) {
|
||||
char *s;
|
||||
|
||||
/* create an empty tree */
|
||||
s = tplg_snprintf("%s {}", "tree");
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = snd_config_load_string(&subtree_cfg, s, 0);
|
||||
free(s);
|
||||
if (ret < 0) {
|
||||
SNDERR("Cannot create empty tree node\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 +1159,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