Compare commits

...

13 commits

Author SHA1 Message Date
Ranjani Sridharan
6642ee90ef
Merge ffc63f76de into d480eac6f2 2024-07-23 04:24:36 +08:00
Takashi Iwai
d480eac6f2 aseqsend: Support long options
Add the support for long-style options such as --verbose.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-22 19:48:56 +02:00
Takashi Iwai
c95db638c0 aseqsend: Support UMP mode
Add a new option -u to specify the UMP MIDI1 or MIDI2 mode.  As
default (-u 0), the program reads the legacy MIDI 1.0 byte stream,
while in UMP mode, it reads as UMP packets and send to the target.
The UMP packet bytes are encoded in big endian.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-22 19:47:25 +02:00
Takashi Iwai
5703d27773 aseqdump: Show SysEx prefix to UMP SysEx data dump
Show the event prefix "SysEx" for UMP SysEx data.  Otherwise it's
difficult to know what it is.

Fixes: 506097ebb1 ("aseqdump: Show UMP SysEx messages")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-22 19:47:21 +02:00
Takashi Iwai
39053b90d5 configure: Drop unused conditionals
Since the required alsa-lib version was bumped and the relevant code
cleanup, some conditionals are no longer referred.  Drop them.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-22 19:47:10 +02:00
Takashi Iwai
397c198955 aseqdump: Check the -u option value properly
Instead of passing the value as is, check the value passed to -u
option and bail out for bad values.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-22 19:47:02 +02:00
Takashi Iwai
946ea467cf aseqdump: Drop ifdef for UMP support
Now that the latest alsa-lib 1.2.12 is mandatory, drop the ugly
ifdefs.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-22 19:46:52 +02:00
Takashi Iwai
c7b342db82 aconnect: Drop superfluous ifdefs
Now that the latest alsa-lib 1.2.12 became mandatory, all ugly ifdefs
can be dropped.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-22 19:46:45 +02:00
Takashi Iwai
d26b66f881 aplaymidi: Allow to pass 0 to -u option, too
Specifying -u 0 shouldn't be treated as an error but it should mean
the legacy MIDI 1.0 handling.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-22 19:46:32 +02:00
Takashi Iwai
cac4935ba2 aplaymidi: Drop ifdef for UMP support
Now that the latest alsa-lib 1.2.12 became mandatory, all ugly ifdefs
can be dropped.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-22 19:46:00 +02:00
Takashi Iwai
4aae5a770f configure: Requires the latest ALSA-lib release 1.2.12
New features such as MIDI 2.0 should be always enabled for the
builds.  Update the dependency to alsa-lib 1.2.12.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-22 17:31:50 +02:00
Takashi Iwai
cdcfcddd95 aseqdump: Fix bogus velocity value output in UMP MIDI2 mode
The printf format for a normalized velocity in MIDI2 mode had a typo,
resulting in a bogus value.  Fix it.

Fixes: 7e9bebad0b199 ("aseqdump: Add options to switch view mode")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-20 19:19:54 +02:00
Ranjani Sridharan
ffc63f76de 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-07-12 10:25:21 -07:00
7 changed files with 273 additions and 125 deletions

View file

@ -21,7 +21,7 @@ AC_PROG_SED
AC_DISABLE_STATIC AC_DISABLE_STATIC
AM_PROG_LIBTOOL AM_PROG_LIBTOOL
PKG_PROG_PKG_CONFIG PKG_PROG_PKG_CONFIG
AM_PATH_ALSA(1.2.5) AM_PATH_ALSA(1.2.12)
if test "x$enable_alsatest" = "xyes"; then if test "x$enable_alsatest" = "xyes"; then
AC_CHECK_FUNC([snd_ctl_elem_add_enumerated], AC_CHECK_FUNC([snd_ctl_elem_add_enumerated],
, [AC_ERROR([No user enum control support in alsa-lib])]) , [AC_ERROR([No user enum control support in alsa-lib])])
@ -47,17 +47,6 @@ AC_CHECK_HEADERS([samplerate.h], [have_samplerate="yes"], [have_samplerate="no"]
[#include <samplerate.h>]) [#include <samplerate.h>])
AC_CHECK_LIB([asound], [snd_seq_client_info_get_card], [HAVE_SEQ_CLIENT_INFO_GET_CARD="yes"]) AC_CHECK_LIB([asound], [snd_seq_client_info_get_card], [HAVE_SEQ_CLIENT_INFO_GET_CARD="yes"])
if test "$HAVE_SEQ_CLIENT_INFO_GET_CARD" = "yes" ; then
AC_DEFINE([HAVE_SEQ_CLIENT_INFO_GET_CARD], 1, [alsa-lib supports snd_seq_client_info_get_card])
fi
AC_CHECK_LIB([asound], [snd_seq_client_info_get_pid], [HAVE_SEQ_CLIENT_INFO_GET_PID="yes"])
if test "$HAVE_SEQ_CLIENT_INFO_GET_PID" = "yes" ; then
AC_DEFINE([HAVE_SEQ_CLIENT_INFO_GET_PID], 1, [alsa-lib supports snd_seq_client_info_get_pid])
fi
AC_CHECK_LIB([asound], [snd_seq_client_info_get_midi_version], [HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION="yes"])
if test "$HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION" = "yes" -a "$have_rawmidi" = "yes"; then
AC_DEFINE([HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION], 1, [alsa-lib supports snd_seq_client_info_get_midi_version])
fi
AC_CHECK_LIB([atopology], [snd_tplg_save], [have_topology="yes"], [have_topology="no"]) AC_CHECK_LIB([atopology], [snd_tplg_save], [have_topology="yes"], [have_topology="no"])
# #

View file

@ -29,12 +29,7 @@
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "gettext.h" #include "gettext.h"
#ifdef SND_SEQ_PORT_CAP_INACTIVE
#define HANDLE_SHOW_ALL
static int show_all; static int show_all;
#else
#define show_all 0
#endif
static void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...) static void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
{ {
@ -67,9 +62,7 @@ static void usage(void)
printf(_(" aconnect -i|-o [-options]\n")); printf(_(" aconnect -i|-o [-options]\n"));
printf(_(" -i,--input list input (readable) ports\n")); printf(_(" -i,--input list input (readable) ports\n"));
printf(_(" -o,--output list output (writable) ports\n")); printf(_(" -o,--output list output (writable) ports\n"));
#ifdef HANDLE_SHOW_ALL
printf(_(" -a,--all show inactive ports, too\n")); printf(_(" -a,--all show inactive ports, too\n"));
#endif
printf(_(" -l,--list list current connections of each port\n")); printf(_(" -l,--list list current connections of each port\n"));
printf(_(" * Remove all exported connections\n")); printf(_(" * Remove all exported connections\n"));
printf(_(" -x, --removeall\n")); printf(_(" -x, --removeall\n"));
@ -84,15 +77,11 @@ static void usage(void)
#define perm_ok(cap,bits) (((cap) & (bits)) == (bits)) #define perm_ok(cap,bits) (((cap) & (bits)) == (bits))
#ifdef SND_SEQ_PORT_DIR_INPUT
static int check_direction(snd_seq_port_info_t *pinfo, int bit) static int check_direction(snd_seq_port_info_t *pinfo, int bit)
{ {
int dir = snd_seq_port_info_get_direction(pinfo); int dir = snd_seq_port_info_get_direction(pinfo);
return !dir || (dir & bit); return !dir || (dir & bit);
} }
#else
#define check_direction(x, y) 1
#endif
static int check_permission(snd_seq_port_info_t *pinfo, int perm) static int check_permission(snd_seq_port_info_t *pinfo, int perm)
{ {
@ -174,20 +163,16 @@ static void do_search_port(snd_seq_t *seq, int perm, action_func_t do_action)
/* reset query info */ /* reset query info */
snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
snd_seq_port_info_set_port(pinfo, -1); snd_seq_port_info_set_port(pinfo, -1);
#ifdef HANDLE_SHOW_ALL
if (show_all) if (show_all)
snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE); snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE);
#endif
count = 0; count = 0;
while (snd_seq_query_next_port(seq, pinfo) >= 0) { while (snd_seq_query_next_port(seq, pinfo) >= 0) {
if (check_permission(pinfo, perm)) { if (check_permission(pinfo, perm)) {
do_action(seq, cinfo, pinfo, count); do_action(seq, cinfo, pinfo, count);
count++; count++;
} }
#ifdef HANDLE_SHOW_ALL
if (show_all) if (show_all)
snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE); snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE);
#endif
} }
} }
} }
@ -205,7 +190,6 @@ static void print_port(snd_seq_t *seq ATTRIBUTE_UNUSED,
snd_seq_client_info_get_name(cinfo), snd_seq_client_info_get_name(cinfo),
(snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? (snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ?
_("user") : _("kernel"))); _("user") : _("kernel")));
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
switch (snd_seq_client_info_get_midi_version(cinfo)) { switch (snd_seq_client_info_get_midi_version(cinfo)) {
case SND_SEQ_CLIENT_UMP_MIDI_1_0: case SND_SEQ_CLIENT_UMP_MIDI_1_0:
printf(",UMP-MIDI1"); printf(",UMP-MIDI1");
@ -214,16 +198,11 @@ static void print_port(snd_seq_t *seq ATTRIBUTE_UNUSED,
printf(",UMP-MIDI2"); printf(",UMP-MIDI2");
break; break;
} }
#endif
#ifdef HAVE_SEQ_CLIENT_INFO_GET_CARD
card = snd_seq_client_info_get_card(cinfo); card = snd_seq_client_info_get_card(cinfo);
#endif
if (card != -1) if (card != -1)
printf(",card=%d", card); printf(",card=%d", card);
#ifdef HAVE_SEQ_CLIENT_INFO_GET_PID
pid = snd_seq_client_info_get_pid(cinfo); pid = snd_seq_client_info_get_pid(cinfo);
#endif
if (pid != -1) if (pid != -1)
printf(",pid=%d", pid); printf(",pid=%d", pid);
printf("]\n"); printf("]\n");
@ -231,10 +210,8 @@ static void print_port(snd_seq_t *seq ATTRIBUTE_UNUSED,
printf(" %3d '%-16s'", printf(" %3d '%-16s'",
snd_seq_port_info_get_port(pinfo), snd_seq_port_info_get_port(pinfo),
snd_seq_port_info_get_name(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) if (snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_INACTIVE)
printf(" [INACTIVE]"); printf(" [INACTIVE]");
#endif
printf("\n"); printf("\n");
} }
@ -299,11 +276,7 @@ enum {
SUBSCRIBE, UNSUBSCRIBE, LIST, REMOVE_ALL SUBSCRIBE, UNSUBSCRIBE, LIST, REMOVE_ALL
}; };
#ifdef HANDLE_SHOW_ALL
#define ACONNECT_OPTS "dior:t:elxa" #define ACONNECT_OPTS "dior:t:elxa"
#else
#define ACONNECT_OPTS "dior:t:elx"
#endif
static const struct option long_option[] = { static const struct option long_option[] = {
{"disconnect", 0, NULL, 'd'}, {"disconnect", 0, NULL, 'd'},
@ -314,9 +287,7 @@ static const struct option long_option[] = {
{"exclusive", 0, NULL, 'e'}, {"exclusive", 0, NULL, 'e'},
{"list", 0, NULL, 'l'}, {"list", 0, NULL, 'l'},
{"removeall", 0, NULL, 'x'}, {"removeall", 0, NULL, 'x'},
#ifdef HANDLE_SHOW_ALL
{"all", 0, NULL, 'a'}, {"all", 0, NULL, 'a'},
#endif
{NULL, 0, NULL, 0}, {NULL, 0, NULL, 0},
}; };
@ -370,12 +341,10 @@ int main(int argc, char **argv)
case 'x': case 'x':
command = REMOVE_ALL; command = REMOVE_ALL;
break; break;
#ifdef HANDLE_SHOW_ALL
case 'a': case 'a':
command = LIST; command = LIST;
show_all = 1; show_all = 1;
break; break;
#endif
default: default:
usage(); usage();
exit(1); exit(1);

View file

@ -30,9 +30,7 @@
#include <unistd.h> #include <unistd.h>
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "version.h" #include "version.h"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
#include <alsa/ump_msg.h> #include <alsa/ump_msg.h>
#endif
/* /*
* 31.25 kbaud, one start bit, eight data bits, two stop bits. * 31.25 kbaud, one start bit, eight data bits, two stop bits.
@ -78,9 +76,7 @@ static int file_offset; /* current offset in input file */
static int num_tracks; static int num_tracks;
static struct track *tracks; static struct track *tracks;
static int smpte_timing; static int smpte_timing;
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
static int ump_mode; static int ump_mode;
#endif
/* prints an error message to stderr */ /* prints an error message to stderr */
static void errormsg(const char *msg, ...) static void errormsg(const char *msg, ...)
@ -685,7 +681,6 @@ static int fill_legacy_event(struct event* event, snd_seq_event_t *ev)
return 0; return 0;
} }
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
static unsigned char to_ump_status(unsigned char ev_type) static unsigned char to_ump_status(unsigned char ev_type)
{ {
switch (ev_type) { switch (ev_type) {
@ -762,13 +757,10 @@ static int fill_ump_event(struct event* event, snd_seq_ump_event_t *ump_ev,
snd_seq_ev_set_ump_data(ump_ev, &ump, sizeof(ump)); snd_seq_ev_set_ump_data(ump_ev, &ump, sizeof(ump));
return 0; return 0;
} }
#endif /* HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION */
static void play_midi(void) static void play_midi(void)
{ {
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
snd_seq_ump_event_t ump_ev; snd_seq_ump_event_t ump_ev;
#endif
snd_seq_event_t ev; snd_seq_event_t ev;
int i, max_tick, err; int i, max_tick, err;
@ -830,7 +822,7 @@ static void play_midi(void)
if (err < 0) if (err < 0)
continue; continue;
} }
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
if (ump_mode) { if (ump_mode) {
err = fill_ump_event(event, &ump_ev, &ev); err = fill_ump_event(event, &ump_ev, &ev);
if (err < 0) if (err < 0)
@ -839,7 +831,6 @@ static void play_midi(void)
check_snd("output event", err); check_snd("output event", err);
continue; continue;
} }
#endif
/* this blocks when the output pool has been filled */ /* this blocks when the output pool has been filled */
err = snd_seq_event_output(seq, &ev); err = snd_seq_event_output(seq, &ev);
@ -957,9 +948,7 @@ static void usage(const char *argv0)
"-V, --version print current version\n" "-V, --version print current version\n"
"-l, --list list all possible output ports\n" "-l, --list list all possible output ports\n"
"-p, --port=client:port,... set port(s) to play to\n" "-p, --port=client:port,... set port(s) to play to\n"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
"-u, --ump=version UMP output (only version=1 is supported)\n" "-u, --ump=version UMP output (only version=1 is supported)\n"
#endif
"-d, --delay=seconds delay after song ends\n", "-d, --delay=seconds delay after song ends\n",
argv0); argv0);
} }
@ -969,12 +958,7 @@ static void version(void)
puts("aplaymidi version " SND_UTIL_VERSION_STR); puts("aplaymidi version " SND_UTIL_VERSION_STR);
} }
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
#define OPTIONS "hVlp:d:u:" #define OPTIONS "hVlp:d:u:"
#else
#define OPTIONS "hVlp:d:"
#endif
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -984,9 +968,7 @@ int main(int argc, char *argv[])
{"version", 0, NULL, 'V'}, {"version", 0, NULL, 'V'},
{"list", 0, NULL, 'l'}, {"list", 0, NULL, 'l'},
{"port", 1, NULL, 'p'}, {"port", 1, NULL, 'p'},
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
{"ump", 1, NULL, 'u'}, {"ump", 1, NULL, 'u'},
#endif
{"delay", 1, NULL, 'd'}, {"delay", 1, NULL, 'd'},
{0} {0}
}; };
@ -1013,15 +995,11 @@ int main(int argc, char *argv[])
case 'd': case 'd':
end_delay = atoi(optarg); end_delay = atoi(optarg);
break; break;
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
case 'u': case 'u':
if (strcmp(optarg, "1")) { ump_mode = atoi(optarg);
errormsg("Only MIDI 1.0 is supported"); if (ump_mode < 0 || ump_mode > 1)
return 1; fatal("Only MIDI 1.0 is supported");
}
ump_mode = 1;
break; break;
#endif
default: default:
usage(argv[0]); usage(argv[0]);
return 1; return 1;
@ -1029,13 +1007,11 @@ int main(int argc, char *argv[])
} }
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
if (ump_mode) { if (ump_mode) {
int err; int err;
err = snd_seq_set_client_midi_version(seq, SND_SEQ_CLIENT_UMP_MIDI_1_0); err = snd_seq_set_client_midi_version(seq, SND_SEQ_CLIENT_UMP_MIDI_1_0);
check_snd("set midi version", err); check_snd("set midi version", err);
} }
#endif
if (do_list) { if (do_list) {
list_ports(); list_ports();

View file

@ -29,9 +29,7 @@
#include <poll.h> #include <poll.h>
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "version.h" #include "version.h"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
#include <alsa/ump_msg.h> #include <alsa/ump_msg.h>
#endif
enum { enum {
VIEW_RAW, VIEW_NORMALIZED, VIEW_PERCENT VIEW_RAW, VIEW_NORMALIZED, VIEW_PERCENT
@ -41,11 +39,7 @@ static snd_seq_t *seq;
static int port_count; static int port_count;
static snd_seq_addr_t *ports; static snd_seq_addr_t *ports;
static volatile sig_atomic_t stop = 0; static volatile sig_atomic_t stop = 0;
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
static int ump_version; static int ump_version;
#else
#define ump_version 0
#endif
static int view_mode = VIEW_RAW; static int view_mode = VIEW_RAW;
/* prints an error message to stderr, and dies */ /* prints an error message to stderr, and dies */
@ -368,7 +362,6 @@ static void dump_event(const snd_seq_event_t *ev)
} }
} }
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
static int group_number(unsigned char c) static int group_number(unsigned char c)
{ {
if (view_mode != VIEW_RAW) if (view_mode != VIEW_RAW)
@ -442,7 +435,7 @@ static const char *midi2_velocity(unsigned int v)
snprintf(tmp, sizeof(tmp), "%.2f", snprintf(tmp, sizeof(tmp), "%.2f",
((double)v * 64.0) / 0x8000); ((double)v * 64.0) / 0x8000);
else else
snprintf(tmp, sizeof(tmp), ".2%f", snprintf(tmp, sizeof(tmp), "%.2f",
((double)(v - 0x8000) * 63.0) / 0x7fff + 64.0); ((double)(v - 0x8000) * 63.0) / 0x7fff + 64.0);
return tmp; return tmp;
} else if (view_mode == VIEW_PERCENT) { } else if (view_mode == VIEW_PERCENT) {
@ -680,6 +673,7 @@ static void dump_ump_sysex_event(const unsigned int *ump)
int i, length; int i, length;
printf("Group %2d, ", group_number(snd_ump_msg_group(ump))); printf("Group %2d, ", group_number(snd_ump_msg_group(ump)));
printf("SysEx ");
switch (snd_ump_sysex_msg_status(ump)) { switch (snd_ump_sysex_msg_status(ump)) {
case SND_UMP_SYSEX_STATUS_SINGLE: case SND_UMP_SYSEX_STATUS_SINGLE:
printf("Single "); printf("Single ");
@ -986,7 +980,6 @@ static void dump_ump_event(const snd_seq_ump_event_t *ev)
break; break;
} }
} }
#endif /* HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION */
static void list_ports(void) static void list_ports(void)
{ {
@ -1029,10 +1022,8 @@ static void help(const char *argv0)
" -N,--normalized-view show normalized values\n" " -N,--normalized-view show normalized values\n"
" -P,--percent-view show percent values\n" " -P,--percent-view show percent values\n"
" -R,--raw-view show raw values (default)\n" " -R,--raw-view show raw values (default)\n"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
" -u,--ump=version set client MIDI version (0=legacy, 1= UMP MIDI 1.0, 2=UMP MIDI2.0)\n" " -u,--ump=version set client MIDI version (0=legacy, 1= UMP MIDI 1.0, 2=UMP MIDI2.0)\n"
" -r,--raw do not convert UMP and legacy events\n" " -r,--raw do not convert UMP and legacy events\n"
#endif
" -p,--port=client:port,... source port(s)\n", " -p,--port=client:port,... source port(s)\n",
argv0); argv0);
} }
@ -1049,11 +1040,7 @@ static void sighandler(int sig ATTRIBUTE_UNUSED)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
static const char short_options[] = "hVlp:NPR" static const char short_options[] = "hVlp:NPRu:r";
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
"u:r"
#endif
;
static const struct option long_options[] = { static const struct option long_options[] = {
{"help", 0, NULL, 'h'}, {"help", 0, NULL, 'h'},
{"version", 0, NULL, 'V'}, {"version", 0, NULL, 'V'},
@ -1062,10 +1049,8 @@ int main(int argc, char *argv[])
{"normalized-view", 0, NULL, 'N'}, {"normalized-view", 0, NULL, 'N'},
{"percent-view", 0, NULL, 'P'}, {"percent-view", 0, NULL, 'P'},
{"raw-view", 0, NULL, 'R'}, {"raw-view", 0, NULL, 'R'},
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
{"ump", 1, NULL, 'u'}, {"ump", 1, NULL, 'u'},
{"raw", 0, NULL, 'r'}, {"raw", 0, NULL, 'r'},
#endif
{0} {0}
}; };
@ -1100,15 +1085,15 @@ int main(int argc, char *argv[])
case 'N': case 'N':
view_mode = VIEW_NORMALIZED; view_mode = VIEW_NORMALIZED;
break; break;
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
case 'u': case 'u':
ump_version = atoi(optarg); ump_version = atoi(optarg);
if (ump_version < 0 || ump_version > 2)
fatal("Invalid UMP version %d", ump_version);
snd_seq_set_client_midi_version(seq, ump_version); snd_seq_set_client_midi_version(seq, ump_version);
break; break;
case 'r': case 'r':
snd_seq_set_client_ump_conversion(seq, 0); snd_seq_set_client_ump_conversion(seq, 0);
break; break;
#endif
default: default:
help(argv[0]); help(argv[0]);
return 1; return 1;
@ -1150,7 +1135,6 @@ int main(int argc, char *argv[])
break; break;
for (;;) { for (;;) {
snd_seq_event_t *event; snd_seq_event_t *event;
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
snd_seq_ump_event_t *ump_ev; snd_seq_ump_event_t *ump_ev;
if (ump_version > 0) { if (ump_version > 0) {
err = snd_seq_ump_event_input(seq, &ump_ev); err = snd_seq_ump_event_input(seq, &ump_ev);
@ -1160,7 +1144,6 @@ int main(int argc, char *argv[])
dump_ump_event(ump_ev); dump_ump_event(ump_ev);
continue; continue;
} }
#endif
err = snd_seq_event_input(seq, &event); err = snd_seq_event_input(seq, &event);
if (err < 0) if (err < 0)

View file

@ -14,41 +14,48 @@ 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 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. 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.
.SH OPTIONS
.TP
\-h
Prints a list of options.
.TP
\-V
Prints the current version.
.TP
\-l
Prints a list of possible output ports.
.TP
\-v
Prints number of bytes actually sent
.TP
\-p
Target port by number or name
.TP
\-s
Send raw binary data from given file name
.TP
\-i
Interval between SysEx messages in miliseconds
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.
.SH OPTIONS
.TP
\-h, \-\-help
Prints a list of options.
.TP
\-V, \-\-version
Prints the current version.
.TP
\-l, \-\-list
Prints a list of possible output ports.
.TP
\-v, \-\-verbose
Prints number of bytes actually sent
.TP
\-p, -\-port=client:port
Target port by number or name
.TP
\-s, \-\-file=filename
Send raw binary data from given file name
.TP
\-i, \-\-interval=msec
Interval between SysEx messages in miliseconds
.TP
\-u, \-\-ump=version
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.
When UMP MIDI 1.0 or MIDI 2.0 protocol is specified, \fBaseqsend\fP
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" aseqsend -p 128:0 "F0 41 10 00 00 64 12 18 00 21 06 59 41 59 4E F7"

View file

@ -33,6 +33,7 @@
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include <alsa/ump_msg.h>
typedef unsigned char mbyte_t; typedef unsigned char mbyte_t;
@ -43,6 +44,7 @@ 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 ump_version;
static void error(const char *format, ...) static void error(const char *format, ...)
{ {
@ -320,6 +322,32 @@ static void send_midi_msg(snd_seq_event_type_t type, mbyte_t *data, int len)
snd_seq_drain_output(seq); snd_seq_drain_output(seq);
} }
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) static int msg_byte_in_range(mbyte_t *data, mbyte_t len)
{ {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
@ -334,6 +362,17 @@ static int msg_byte_in_range(mbyte_t *data, mbyte_t len)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
static const struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'V'},
{"verbose", 0, NULL, 'v'},
{"list", 0, NULL, 'l'},
{"port", 1, NULL, 'p'},
{"file", 1, NULL, 's'},
{"interval", 1, NULL, 'i'},
{"ump", 1, NULL, 'u'},
{0}
};
char c = 0; char c = 0;
char do_send_file = 0; char do_send_file = 0;
char do_port_list = 0; char do_port_list = 0;
@ -342,7 +381,7 @@ int main(int argc, char *argv[])
int sent_data_c; int sent_data_c;
int k; int k;
while ((c = getopt(argc, argv, "hi:Vvlp:s:")) != -1) { while ((c = getopt_long(argc, argv, "hi:Vvlp:s:u:", long_options, NULL)) != -1) {
switch (c) { switch (c) {
case 'h': case 'h':
usage(); usage();
@ -366,6 +405,9 @@ int main(int argc, char *argv[])
case 'i': case 'i':
sysex_interval = atoi(optarg) * 1000; //ms--->us sysex_interval = atoi(optarg) * 1000; //ms--->us
break; break;
case 'u':
ump_version = atoi(optarg);
break;
default: default:
error("Try 'aseqsend -h' for more information."); error("Try 'aseqsend -h' for more information.");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -401,7 +443,11 @@ int main(int argc, char *argv[])
if (!send_data) if (!send_data)
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
if (ump_version && (send_data_length % 4) != 0)
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 (snd_seq_parse_address(seq, &addr, port_name) < 0) { if (snd_seq_parse_address(seq, &addr, port_name) < 0) {
@ -414,6 +460,12 @@ int main(int argc, char *argv[])
while (k < send_data_length) { while (k < send_data_length) {
if (ump_version) {
sent_data_c += send_ump(send_data + k);
k += 4;
continue;
}
if (send_data[k] == 0xF0) { if (send_data[k] == 0xF0) {
int c1 = k; int c1 = k;

View file

@ -921,6 +921,172 @@ static int pre_process_arrays(struct tplg_pre_processor *tplg_pp, snd_config_t *
return 0; 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 */ #endif /* version < 1.2.6 */
int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size, 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"); fprintf(stderr, "Failed to process object arrays in input config\n");
goto err; 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 #endif
err = pre_process_config(tplg_pp, tplg_pp->input_cfg); err = pre_process_config(tplg_pp, tplg_pp->input_cfg);