Compare commits

...

12 commits

Author SHA1 Message Date
Ranjani Sridharan
71413387fd
Merge 938a26df25 into 74daf3a93a 2024-07-10 12:21:21 +03:00
Takashi Iwai
74daf3a93a arecordmidi2: Add options to put meta data texts
For convenience, add more options to embed the meta data texts given
via command line.  The song name etc can be given directly via the
respective option directly, e.g. --song="text..."

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-09 11:52:39 +02:00
Takashi Iwai
274f1b9ebf arecordmidi2: Add --profile option
Allow to add arbitrary profile UMP data to be put into the
configuration of the recorded stream via --profile option.
The file must contain valid UMP data encoded in big-endian.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-09 08:09:45 +02:00
Takashi Iwai
f0df4b4cfe arecordmidi2: Add stdout output and --silent option
When the output file is '-', it's recorded to stdout.

For avoiding the corruption, this mode also suppresses the messages to
stdout, too, which can be enabled also via -s / --silent option.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-09 08:03:27 +02:00
Takashi Iwai
b1269eefdd aplaymidi2: Add --silent option
For suppressing the messages, add -s / --silent option.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-09 07:48:05 +02:00
Takashi Iwai
99ce4b1d8a Revert "arecordmidi2: Correct the MIDI FB direction"
This reverts commit e609d66807.

It turned out that the failure was rather in alsa-lib API; the
input and output have been incorrectly implemented.
Now that the alsa-lib code got fixed, let's revert the bad fix.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-09 07:32:52 +02:00
Takashi Iwai
68491dd464 aplaymidi2: Show meta data texts
Now aplaymidi2 shows the meta data texts embedded in Flex Data
messages such as copyright and lyrics.  The text output isn't
synchronized yet with the actual position, though.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-08 18:32:18 +02:00
Takashi Iwai
330741d523 aconnect: Fix the indication of inactive ports
The inactive port should have been shown in each port line instead of
the client name line.

Fixes: 64b1d486b1 ("aconnect: Add UMP support")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-08 17:21:13 +02:00
Takashi Iwai
2ee6c170a8 arecordmidi2: Fix the tick in 1us tempo-base
The recorded tick is incorrectly converted for 1us tempo-base on the
old kernels.  Since we correct the queue tempo, we don't have to
adjust the returned tick value any longer.  The current code applies
it doubly, resulting in 100 times slower.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-08 17:14:06 +02:00
Takashi Iwai
e609d66807 arecordmidi2: Correct the MIDI FB direction
The direction was wrongly passed to the FB setup.  It has to be
"OUTPUT" instead of "INPUT, so that other applications can write to
arecordmidi2 port.

Fixes: 2cdf5ebedb ("arecordmidi2: Add initial version")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-08 16:26:31 +02:00
Takashi Iwai
16533f81de arecordmidi2: Start queue at starting the stream
The queue should be started at the very same time of the start of the
stream itself in the interactive mode.  Otherwise it'll get bogus long
waits until the start of the clip.

Move the code to start the queue in start_bar(), so that it's always
tied with the start sequence.

Fixes: 1205dd5f6c ("arecordmidi2: Add passive mode and interactive mode")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-08 16:26:09 +02:00
Ranjani Sridharan
938a26df25 topology: pre-processor: Introduce a new feature for subtree copies
Introduce a new kyword "SubTreeCopy" for extneding existing conf nodes
with additional nodes. This feature is useful for extending previous
pipeline class definitions with the addition of one or more widgets
without having to duplicate everything in the new class definition.

For example: Consider a pipeline class definition as below. Note that
only the widgets & routes are shown here.

Class.Pipeline.mixout-gain-dai-copier-playback {
	Object.Widget {
		mixout."1" {}
		dai-copier."1" {}
		gain."1" {}
		pipeline."1" {}
	}

	Object.Base {
		!route [
			{
				source mixout.$index.1
				sink	gain.$index.1
			}
		]
	}
}

If we want to extend this pipeline with the addition of an eqiir/eqfir,
we can create a SubTreeCopy node with type extend as follows:

Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback {
	SubTreeCopy.baseclass {
		source "Class.Pipeline.mixout-gain-dai-copier-playback"
		type extend

                tree {
			Object.Widget {
				eqiir.1 {}
				eqfir.1 {}
			}

			Object.Base {
				!route [
					{
						source gain.$index.1
						sink   eqiir.$index.1
					}
					{
						source eqiir.$index.1
						sink   eqfir.$index.1
					}
				]
			}
		}
	}
}

Note that the target is left undefined, which means that the newly
created subtree will be merged to the parent node that contains the
"SubTreeCopy" node i.e. in this case
Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback".

But if we want to modify an existing pipeline class while modifying the
order of widgets and/or inserting new widgets, we should use the type
"override" instead. This allows for adding new widgets to the list of
widgets in the base class definition while also allowing overriding the
routes to allow inserting the new widgets and reordering the widgets in
the base class. For example, if we want to add a drc widget between the
gain and the eqiir modules in the above class, we can do the following:

Class.Pipeline.mixout-efx-dai-copier-playback {
	# This copy will override all widgets/routes in the base class
	SubTreeCopy.baseclass {
		source "Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback"
		target "Class.Pipeline"
		type override

		tree {
			Object.Widget {
				drc.1 {}
			}

			Object.Base {
				!route [
					{
						source mixout.$index.1
						sink	gain.$index.1
					}
					{
						source gain.$index.1
						sink	drc.$index.1
					}
					{
						source	drc.$index.1
						sink	eqiir.$index.1
					}
					{
						source	eqiir.$index.1
						sink	eqfir.$index.1
					}
				]
			}
		}
	}

	# Explicitly copy the widgets from the base class now
	SubTreeCopy.widgets {
		source "Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback.Object.Widget"
		target "Class.Pipeline.mixout-efx-dai-copier-playback.Object.Widget"
	}
}

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
2024-06-10 11:31:05 -07:00
6 changed files with 525 additions and 28 deletions

View file

@ -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,

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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;
}

View file

@ -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);