[PATCH alsa-lib 0/4] Add API helper functions for creating UMP Endpoint and Blocks
Hi,
this is a patch set to alsa-lib to add the new API functions for creating a virtual UMP Endpoint and UMP Blocks arbitrarily from a user-space, that is, to create a virtual UMP device.
An example program is included in test directory.
Takashi
===
Takashi Iwai (4): ump: Add missing *_set variants for snd_ump_endpoint_info and snd_ump_block_info seq: Add API helper functions for creating UMP Endpoint and Blocks test: Add an example program to create a virtual UMP Endpoint Add test/seq-ump-example to .gitignore
.gitignore | 1 + include/seqmid.h | 7 + include/ump.h | 36 ++++- src/rawmidi/ump.c | 304 +++++++++++++++++++++++++++++++++++++++-- src/seq/seq.c | 6 +- src/seq/seq_local.h | 4 + src/seq/seqmid.c | 249 +++++++++++++++++++++++++++++++++ test/Makefile.am | 3 +- test/seq-ump-example.c | 187 +++++++++++++++++++++++++ 9 files changed, 783 insertions(+), 14 deletions(-) create mode 100644 test/seq-ump-example.c
The API functions to fill the data on snd_ump_endpoint_info and snd_ump_block_info were missing. Let's add them.
They can be used to construct a virtual UMP endpoint and block.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/ump.h | 30 ++++- src/rawmidi/ump.c | 304 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 322 insertions(+), 12 deletions(-)
diff --git a/include/ump.h b/include/ump.h index 1e5e053454b3..45b3ad270db7 100644 --- a/include/ump.h +++ b/include/ump.h @@ -77,6 +77,7 @@ size_t snd_ump_endpoint_info_sizeof(void); #define snd_ump_endpoint_info_alloca(ptr) __snd_alloca(ptr, snd_ump_endpoint_info) int snd_ump_endpoint_info_malloc(snd_ump_endpoint_info_t **info); void snd_ump_endpoint_info_free(snd_ump_endpoint_info_t *info); +void snd_ump_endpoint_info_clear(snd_ump_endpoint_info_t *info); void snd_ump_endpoint_info_copy(snd_ump_endpoint_info_t *dst, const snd_ump_endpoint_info_t *src); int snd_ump_endpoint_info_get_card(const snd_ump_endpoint_info_t *info); int snd_ump_endpoint_info_get_device(const snd_ump_endpoint_info_t *info); @@ -93,6 +94,20 @@ const char *snd_ump_endpoint_info_get_name(const snd_ump_endpoint_info_t *info); const char *snd_ump_endpoint_info_get_product_id(const snd_ump_endpoint_info_t *info); int snd_ump_endpoint_info(snd_ump_t *ump, snd_ump_endpoint_info_t *info);
+void snd_ump_endpoint_info_set_card(snd_ump_endpoint_info_t *info, unsigned int card); +void snd_ump_endpoint_info_set_device(snd_ump_endpoint_info_t *info, unsigned int device); +void snd_ump_endpoint_info_set_flags(snd_ump_endpoint_info_t *info, unsigned int flags); +void snd_ump_endpoint_info_set_protocol_caps(snd_ump_endpoint_info_t *info, unsigned int caps); +void snd_ump_endpoint_info_set_protocol(snd_ump_endpoint_info_t *info, unsigned int protocols); +void snd_ump_endpoint_info_set_num_blocks(snd_ump_endpoint_info_t *info, unsigned int num_blocks); +void snd_ump_endpoint_info_set_version(snd_ump_endpoint_info_t *info, unsigned int version); +void snd_ump_endpoint_info_set_manufacturer_id(snd_ump_endpoint_info_t *info, unsigned int id); +void snd_ump_endpoint_info_set_family_id(snd_ump_endpoint_info_t *info, unsigned int id); +void snd_ump_endpoint_info_set_model_id(snd_ump_endpoint_info_t *info, unsigned int id); +void snd_ump_endpoint_info_set_sw_revision(snd_ump_endpoint_info_t *info, const unsigned char *id); +void snd_ump_endpoint_info_set_name(snd_ump_endpoint_info_t *info, const char *name); +void snd_ump_endpoint_info_set_product_id(snd_ump_endpoint_info_t *info, const char *id); + /** Bit flag for MIDI 1.0 port w/o restrict in UMP Block info flags */ #define SND_UMP_BLOCK_IS_MIDI1 (1U << 0) /** Bit flag for 31.25Kbps B/W MIDI1 port in UMP Block info flags */ @@ -118,11 +133,11 @@ size_t snd_ump_block_info_sizeof(void); #define snd_ump_block_info_alloca(ptr) __snd_alloca(ptr, snd_ump_block_info) int snd_ump_block_info_malloc(snd_ump_block_info_t **info); void snd_ump_block_info_free(snd_ump_block_info_t *info); +void snd_ump_block_info_clear(snd_ump_block_info_t *info); void snd_ump_block_info_copy(snd_ump_block_info_t *dst, const snd_ump_block_info_t *src); int snd_ump_block_info_get_card(const snd_ump_block_info_t *info); int snd_ump_block_info_get_device(const snd_ump_block_info_t *info); unsigned int snd_ump_block_info_get_block_id(const snd_ump_block_info_t *info); -void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, unsigned int id); unsigned int snd_ump_block_info_get_active(const snd_ump_block_info_t *info); unsigned int snd_ump_block_info_get_flags(const snd_ump_block_info_t *info); unsigned int snd_ump_block_info_get_direction(const snd_ump_block_info_t *info); @@ -134,6 +149,19 @@ unsigned int snd_ump_block_info_get_ui_hint(const snd_ump_block_info_t *info); const char *snd_ump_block_info_get_name(const snd_ump_block_info_t *info); int snd_ump_block_info(snd_ump_t *ump, snd_ump_block_info_t *info);
+void snd_ump_block_info_set_card(snd_ump_block_info_t *info, unsigned int card); +void snd_ump_block_info_set_device(snd_ump_block_info_t *info, unsigned int device); +void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, unsigned int id); +void snd_ump_block_info_set_active(snd_ump_block_info_t *info, unsigned int active); +void snd_ump_block_info_set_flags(snd_ump_block_info_t *info, unsigned int flags); +void snd_ump_block_info_set_direction(snd_ump_block_info_t *info, unsigned int direction); +void snd_ump_block_info_set_first_group(snd_ump_block_info_t *info, unsigned int first_group); +void snd_ump_block_info_set_num_groups(snd_ump_block_info_t *info, unsigned int num_groups); +void snd_ump_block_info_set_midi_ci_version(snd_ump_block_info_t *info, unsigned int version); +void snd_ump_block_info_set_sysex8_streams(snd_ump_block_info_t *info, unsigned int streams); +void snd_ump_block_info_set_ui_hint(snd_ump_block_info_t *info, unsigned int hint); +void snd_ump_block_info_set_name(snd_ump_block_info_t *info, const char *name); + #ifdef __cplusplus } #endif diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index 39c1c4a91928..6c1097a7452b 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -337,6 +337,17 @@ void snd_ump_endpoint_info_free(snd_ump_endpoint_info_t *info) free(info); }
+/** + * \brief clears the snd_ump_endpoint_info_t structure + * \param info pointer to the snd_ump_endpoint_info_t structure to clear + * + * Zero-clear the snd_ump_endpoint_info_t object. + */ +void snd_ump_endpoint_info_clear(snd_ump_endpoint_info_t *info) +{ + memset(info, 0, sizeof(*info)); +} + /** * \brief copy one snd_ump_endpoint_info_t structure to another * \param dst destination snd_ump_endpoint_info_t structure @@ -478,6 +489,149 @@ const char *snd_ump_endpoint_info_get_product_id(const snd_ump_endpoint_info_t * return (const char *)info->product_id; }
+/** + * \brief set card number of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param card the card number of the given UMP endpoint + */ +void snd_ump_endpoint_info_set_card(snd_ump_endpoint_info_t *info, + unsigned int card) +{ + info->card = card; +} + +/** + * \brief set device number of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param device the device number of the given UMP endpoint + */ +void snd_ump_endpoint_info_set_device(snd_ump_endpoint_info_t *info, + unsigned int device) +{ + info->device = device; +} + +/** + * \brief set info flags of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param flags UMP endpoint flag bits + */ +void snd_ump_endpoint_info_set_flags(snd_ump_endpoint_info_t *info, + unsigned int flags) +{ + info->flags = flags; +} + +/** + * \brief set protocol capability bits of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param caps UMP endpoint protocol capability bits + */ +void snd_ump_endpoint_info_set_protocol_caps(snd_ump_endpoint_info_t *info, + unsigned int caps) +{ + info->protocol_caps = caps; +} + +/** + * \brief set the current protocol of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param caps the UMP endpoint protocol bits + */ +void snd_ump_endpoint_info_set_protocol(snd_ump_endpoint_info_t *info, + unsigned int protocol) +{ + info->protocol = protocol; +} + +/** + * \brief set the number of UMP blocks of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param blocks the number of UMP blocks + */ +void snd_ump_endpoint_info_set_num_blocks(snd_ump_endpoint_info_t *info, + unsigned int blocks) +{ + info->num_blocks = blocks; +} + +/** + * \brief set the UMP version number of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param version the UMP version number + */ +void snd_ump_endpoint_info_set_version(snd_ump_endpoint_info_t *info, + unsigned int version) +{ + info->version = version; +} + +/** + * \brief set the UMP manufacturer ID of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param id UMP manufacturer ID + */ +void snd_ump_endpoint_info_set_manufacturer_id(snd_ump_endpoint_info_t *info, + unsigned int id) +{ + info->manufacturer_id = id; +} + +/** + * \brief set the UMP family ID of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param id UMP family ID + */ +void snd_ump_endpoint_info_set_family_id(snd_ump_endpoint_info_t *info, + unsigned int id) +{ + info->family_id = id; +} + +/** + * \brief set the UMP model ID of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param id UMP model ID + */ +void snd_ump_endpoint_info_set_model_id(snd_ump_endpoint_info_t *info, + unsigned int id) +{ + info->model_id = id; +} + +/** + * \brief set the UMP software revision of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param id UMP software revision in 4 bytes array + */ +void snd_ump_endpoint_info_set_sw_revision(snd_ump_endpoint_info_t *info, + const unsigned char *id) +{ + memcpy(info->sw_revision, id, sizeof(info->sw_revision)); +} + +/** + * \brief set the name of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param name UMP endpoint name string + */ +void snd_ump_endpoint_info_set_name(snd_ump_endpoint_info_t *info, + const char *name) +{ + snd_strlcpy((char *)info->name, name, sizeof(info->name)); +} + +/** + * \brief set the product ID string of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param id UMP endpoint product ID string + */ +void snd_ump_endpoint_info_set_product_id(snd_ump_endpoint_info_t *info, + const char *id) +{ + snd_strlcpy((char *)info->product_id, id, sizeof(info->product_id)); +} + /** * \brief get endpoint information about UMP handle * \param ump UMP handle @@ -526,6 +680,17 @@ void snd_ump_block_info_free(snd_ump_block_info_t *info) free(info); }
+/** + * \brief clears the snd_ump_block_info_t structure + * \param info pointer to the snd_ump_block_info_t structure to clear + * + * Zero-clear the snd_ump_block_info_t object. + */ +void snd_ump_block_info_clear(snd_ump_block_info_t *info) +{ + memset(info, 0, sizeof(*info)); +} + /** * \brief copy one snd_ump_block_info_t structure to another * \param dst destination snd_ump_block_info_t structure @@ -567,17 +732,6 @@ unsigned int snd_ump_block_info_get_block_id(const snd_ump_block_info_t *info) return info->block_id; }
-/** - * \brief set UMP block ID for query - * \param info pointer to a snd_ump_block_info_t structure - * \param id the ID number for query - */ -void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, - unsigned int id) -{ - info->block_id = id; -} - /** * \brief get UMP block activeness * \param info pointer to a snd_ump_block_info_t structure @@ -668,6 +822,134 @@ const char *snd_ump_block_info_get_name(const snd_ump_block_info_t *info) return (const char *)info->name; }
+/** + * \brief set card number to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param card the card number + */ +void snd_ump_block_info_set_card(snd_ump_block_info_t *info, unsigned int card) +{ + info->card = card; +} + +/** + * \brief set device number to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param device the device number + */ +void snd_ump_block_info_set_device(snd_ump_block_info_t *info, unsigned int device) +{ + info->device = device; +} + +/** + * \brief set UMP block ID to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param id the ID number + * + * This function is mostly used for setting the block ID to query. + */ +void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, + unsigned int id) +{ + info->block_id = id; +} + +/** + * \brief set activeness to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param active 1 if the block is active or 0 if inactive + */ +void snd_ump_block_info_set_active(snd_ump_block_info_t *info, unsigned int active) +{ + info->active = !!active; +} + +/** + * \brief set UMP block information flags to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param flags flag bits for the given UMP block + */ +void snd_ump_block_info_set_flags(snd_ump_block_info_t *info, unsigned int flags) +{ + info->flags = flags; +} + +/** + * \brief set UMP block direction to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param direction direction of UMP block (input,output,bidirectional) + */ +void snd_ump_block_info_set_direction(snd_ump_block_info_t *info, unsigned int direction) +{ + info->direction = direction; +} + +/** + * \brief set first UMP group to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param first_group the first UMP group ID belonging to the block + */ +void snd_ump_block_info_set_first_group(snd_ump_block_info_t *info, + unsigned int first_group) +{ + info->first_group = first_group; +} + +/** + * \brief set number of UMP groups to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param num_groups the number of UMP groups belonging to the block + */ +void snd_ump_block_info_set_num_groups(snd_ump_block_info_t *info, + unsigned int num_groups) +{ + info->num_groups = num_groups; +} + +/** + * \brief set MIDI-CI version number to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param version MIDI-CI version number + */ +void snd_ump_block_info_set_midi_ci_version(snd_ump_block_info_t *info, + unsigned int version) +{ + info->midi_ci_version = version; +} + +/** + * \brief set number of supported SysEx8 streams to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param streams number of supported SysEx8 streams + */ +void snd_ump_block_info_set_sysex8_streams(snd_ump_block_info_t *info, + unsigned int streams) +{ + info->sysex8_streams = streams; +} + +/** + * \brief set UI Hint to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param hint the hint bits + */ +void snd_ump_block_info_set_ui_hint(snd_ump_block_info_t *info, unsigned int hint) +{ + info->ui_hint = hint; +} + +/** + * \brief set the name string to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param name the name string of UMP block + */ +void snd_ump_block_info_set_name(snd_ump_block_info_t *info, + const char *name) +{ + snd_strlcpy((char *)info->name, name, sizeof(info->name)); +} + /** * \brief get UMP block information * \param ump UMP handle
For making it easer for applications to create a virtual UMP Endpoint and UMP blocks, add two API helper functions.
snd_seq_create_ump_endpoint() creates (unsurprisingly) a UMP Endpoint, based on the given snd_ump_endpoint_info_t information. The number of (max) UMP groups belonging to this Endpoint has to be specified. This function sets up the Endpoint info on the sequencer client, and creates a MIDI 2.0 UMP port as well as UMP Group ports automatically. The name of the sequencer client is updated from the Endpoint name, too.
After creating a UMP Endpoint, create each UMP Block via snd_seq_create_ump_block() function with a snd_ump_block_info_t info. The associated groups for each block have to be specified there. The port names and capability bits are updated accordingly after setting each block information.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/seqmid.h | 7 ++ include/ump.h | 6 ++ src/seq/seq.c | 6 +- src/seq/seq_local.h | 4 + src/seq/seqmid.c | 249 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 271 insertions(+), 1 deletion(-)
diff --git a/include/seqmid.h b/include/seqmid.h index 4464c2d3af0b..bf968a5b2c7b 100644 --- a/include/seqmid.h +++ b/include/seqmid.h @@ -520,6 +520,13 @@ int snd_seq_reset_pool_input(snd_seq_t *seq); ((ev)->type = SND_SEQ_EVENT_SYSEX,\ snd_seq_ev_set_variable(ev, datalen, dataptr))
+/* Helper API functions for UMP endpoint and block creations */ +int snd_seq_create_ump_endpoint(snd_seq_t *seq, + const snd_ump_endpoint_info_t *info, + unsigned int num_groups); +int snd_seq_create_ump_block(snd_seq_t *seq, int blkid, + const snd_ump_block_info_t *info); + /** } */
#ifdef __cplusplus diff --git a/include/ump.h b/include/ump.h index 45b3ad270db7..01363a329fa7 100644 --- a/include/ump.h +++ b/include/ump.h @@ -69,6 +69,9 @@ enum _snd_ump_direction { /** Bit flag for JRTS in Receive */ #define SND_UMP_EP_INFO_PROTO_JRTS_RX 0x0002
+/** Default version passed to UMP Endpoint info */ +#define SND_UMP_EP_INFO_DEFAULT_VERSION 0x0101 + size_t snd_ump_endpoint_info_sizeof(void); /** \hideinitializer * \brief allocate an invalid #snd_ump_endpoint_info_t using standard alloca @@ -125,6 +128,9 @@ enum _snd_ump_block_ui_hint { SND_UMP_BLOCK_UI_HINT_BOTH = 0x03, };
+/** Default MIDI CI version passed to UMP Block info */ +#define SND_UMP_BLOCK_INFO_DEFAULT_MIDI_CI_VERSION 0x01 + size_t snd_ump_block_info_sizeof(void); /** \hideinitializer * \brief allocate an invalid #snd_ump_block_info_t using standard alloca diff --git a/src/seq/seq.c b/src/seq/seq.c index 5eac4848b9c7..ff0468140177 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -1042,7 +1042,8 @@ int _snd_seq_open_lconf(snd_seq_t **seqp, const char *name, */ int snd_seq_close(snd_seq_t *seq) { - int err; + int i, err; + assert(seq); err = seq->ops->close(seq); if (seq->dl_handle) @@ -1051,6 +1052,9 @@ int snd_seq_close(snd_seq_t *seq) free(seq->ibuf); free(seq->tmpbuf); free(seq->name); + free(seq->ump_ep); + for (i = 0; i < 16; i++) + free(seq->ump_blks[i]); free(seq); return err; } diff --git a/src/seq/seq_local.h b/src/seq/seq_local.h index 468248062638..263029702739 100644 --- a/src/seq/seq_local.h +++ b/src/seq/seq_local.h @@ -94,6 +94,10 @@ struct _snd_seq { size_t tmpbufsize; /* size of errbuf */ size_t packet_size; /* input packet alignment size */ int midi_version; /* current protocol version */ + + unsigned int num_ump_groups; /* number of UMP groups */ + snd_ump_endpoint_info_t *ump_ep; /* optional UMP info */ + snd_ump_block_info_t *ump_blks[16]; /* optional UMP block info */ };
int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode); diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 9ec93ee8ade1..d7eac6cafa0e 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -493,3 +493,252 @@ int snd_seq_parse_address(snd_seq_t *seq, snd_seq_addr_t *addr, const char *arg) return 0; }
+/** + * \brief create a UMP Endpoint for the given sequencer client + * \param seq sequencer handle + * \param info UMP Endpoint information to initialize + * \param num_groups max number of groups in the endpoint + * \return 0 on success or negative error code + * + * This function initializes the sequencer client to the corresponding + * MIDI 2.0 mode (either MIDI 1.0 or MIDI 2.0 protocol) depending on the + * given snd_ump_endpoint_info_t info. + * + * This function should be called right after opening a sequencer client. + * The client name is updated from the UMP Endpoint name, and a primary + * MIDI 2.0 UMP port and each UMP Group port are created. + * The application should pass each UMP block info via succeeding + * snd_seq_create_ump_block() call. + */ +int snd_seq_create_ump_endpoint(snd_seq_t *seq, + const snd_ump_endpoint_info_t *info, + unsigned int num_groups) +{ + int err, version; + unsigned int i; + snd_seq_port_info_t *pinfo; + + if (seq->ump_ep) + return -EBUSY; + + if (num_groups < 1 || num_groups > SND_UMP_MAX_GROUPS) + return -EINVAL; + + if (!(info->protocol_caps & info->protocol)) { + SNDERR("Inconsistent UMP protocol_caps and protocol\n"); + return -EINVAL; + } + + if (info->protocol & SND_UMP_EP_INFO_PROTO_MIDI2) { + version = SND_SEQ_CLIENT_UMP_MIDI_2_0; + } else if (info->protocol & SND_UMP_EP_INFO_PROTO_MIDI1) { + version = SND_SEQ_CLIENT_UMP_MIDI_1_0; + } else { + SNDERR("Invalid UMP protocol set 0x%x\n", info->protocol); + return -EINVAL; + } + + err = snd_seq_set_client_midi_version(seq, version); + if (err < 0) { + SNDERR("Failed to set to MIDI protocol 0x%x\n", version); + return err; + } + + seq->ump_ep = malloc(sizeof(*info)); + if (!seq->ump_ep) + return -ENOMEM; + + *seq->ump_ep = *info; + if (!seq->ump_ep->version) + seq->ump_ep->version = SND_UMP_EP_INFO_DEFAULT_VERSION; + + if (info->name) { + err = snd_seq_set_client_name(seq, (const char *)info->name); + if (err < 0) + goto error_free; + } + + err = snd_seq_set_ump_endpoint_info(seq, seq->ump_ep); + if (err < 0) { + SNDERR("Failed to set UMP EP info\n"); + goto error_free; + } + + snd_seq_port_info_alloca(&pinfo); + + snd_seq_port_info_set_port(pinfo, 0); + snd_seq_port_info_set_port_specified(pinfo, 1); + snd_seq_port_info_set_name(pinfo, "MIDI 2.0"); + snd_seq_port_info_set_capability(pinfo, + SNDRV_SEQ_PORT_CAP_READ | + SNDRV_SEQ_PORT_CAP_SYNC_READ | + SNDRV_SEQ_PORT_CAP_SUBS_READ | + SNDRV_SEQ_PORT_CAP_WRITE | + SNDRV_SEQ_PORT_CAP_SYNC_WRITE | + SNDRV_SEQ_PORT_CAP_SUBS_WRITE | + SNDRV_SEQ_PORT_CAP_DUPLEX); + snd_seq_port_info_set_type(pinfo, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_UMP | + SND_SEQ_PORT_TYPE_APPLICATION | + SNDRV_SEQ_PORT_TYPE_PORT); + snd_seq_port_info_set_ump_group(pinfo, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_UMP | + SND_SEQ_PORT_TYPE_APPLICATION | + SNDRV_SEQ_PORT_TYPE_PORT); + err = snd_seq_create_port(seq, pinfo); + if (err < 0) { + SNDERR("Failed to create MIDI 2.0 port\n"); + goto error_free; + } + + for (i = 0; i < num_groups; i++) { + char name[32]; + + snd_seq_port_info_set_port(pinfo, i + 1); + snd_seq_port_info_set_port_specified(pinfo, 1); + sprintf(name, "Group %d", i + 1); + snd_seq_port_info_set_capability(pinfo, 0); /* set later */ + snd_seq_port_info_set_name(pinfo, name); + snd_seq_port_info_set_ump_group(pinfo, i + 1); + err = snd_seq_create_port(seq, pinfo); + if (err < 0) { + SNDERR("Failed to create Group port %d\n", i + 1); + goto error; + } + } + + seq->num_ump_groups = num_groups; + return 0; + + error: + /* delete all ports including port 0 */ + for (i = 0; i <= num_groups; i++) + snd_seq_delete_port(seq, i); + error_free: + free(seq->ump_ep); + seq->ump_ep = NULL; + return err; +} + +/* update each port name and capability from the block list */ +static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) +{ + unsigned int i, b; + snd_seq_port_info_t *pinfo; + snd_ump_block_info_t *bp; + + snd_seq_port_info_alloca(&pinfo); + + for (i = 0; i < seq->num_ump_groups; i++) { + char blknames[64]; + char name[64]; + unsigned int caps = 0; + + blknames[0] = 0; + for (b = 0; b < ep->num_blocks; b++) { + bp = seq->ump_blks[b]; + if (!bp) + continue; + if (i < bp->first_group || + i >= bp->first_group + bp->num_groups) + continue; + switch (bp->direction) { + case SNDRV_UMP_DIR_INPUT: + caps |= SNDRV_SEQ_PORT_CAP_READ | + SNDRV_SEQ_PORT_CAP_SYNC_READ | + SNDRV_SEQ_PORT_CAP_SUBS_READ; + break; + case SNDRV_UMP_DIR_OUTPUT: + caps |= SNDRV_SEQ_PORT_CAP_WRITE | + SNDRV_SEQ_PORT_CAP_SYNC_WRITE | + SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + break; + case SNDRV_UMP_DIR_BIDIRECTION: + caps |= SNDRV_SEQ_PORT_CAP_READ | + SNDRV_SEQ_PORT_CAP_SYNC_READ | + SNDRV_SEQ_PORT_CAP_SUBS_READ | + SNDRV_SEQ_PORT_CAP_WRITE | + SNDRV_SEQ_PORT_CAP_SYNC_WRITE | + SNDRV_SEQ_PORT_CAP_SUBS_WRITE | + SNDRV_SEQ_PORT_CAP_DUPLEX; + break; + } + + if (!*bp->name) + continue; + if (*blknames) { + strlcat(blknames, ", ", sizeof(blknames)); + strlcat(blknames, (const char *)bp->name, + sizeof(blknames)); + } else { + snd_strlcpy(blknames, (const char *)bp->name, + sizeof(blknames)); + } + } + + if (!*blknames) + continue; + + snprintf(name, sizeof(name), "Group %d (%s)", i + 1, blknames); + if (snd_seq_get_port_info(seq, i + 1, pinfo) < 0) + continue; + + if (strcmp(name, snd_seq_port_info_get_name(pinfo)) || + snd_seq_port_info_get_capability(pinfo) != caps) { + snd_seq_port_info_set_name(pinfo, name); + snd_seq_port_info_set_capability(pinfo, caps); + snd_seq_set_port_info(seq, i + 1, pinfo); + } + } +} + +/** + * \brief create a UMP block for the given sequencer client + * \param seq sequencer handle + * \param blkid 0-based block id + * \param info UMP block info to initialize + * \return 0 on success or negative error code + * + * This function sets up the UMP block info of the given block id. + * The sequencer port name is updated accordingly with the associated + * block name automatically. + */ +int snd_seq_create_ump_block(snd_seq_t *seq, int blkid, + const snd_ump_block_info_t *info) +{ + snd_ump_block_info_t *bp; + snd_ump_endpoint_info_t *ep = seq->ump_ep; + int err; + + if (!ep) + return -EINVAL; + if (info->first_group >= seq->num_ump_groups || + info->first_group + info->num_groups > seq->num_ump_groups) + return -EINVAL; + if (blkid < 0 || blkid >= (int)ep->num_blocks) + return -EINVAL; + + if (seq->ump_blks[blkid]) + return -EBUSY; + seq->ump_blks[blkid] = bp = malloc(sizeof(*info)); + if (!bp) + return -ENOMEM; + *bp = *info; + + if (!bp->midi_ci_version) + bp->midi_ci_version = SND_UMP_BLOCK_INFO_DEFAULT_MIDI_CI_VERSION; + bp->active = 1; + + err = snd_seq_set_ump_block_info(seq, blkid, bp); + if (err < 0) { + SNDERR("Failed to set UMP EP info\n"); + free(bp); + seq->ump_blks[blkid] = NULL; + return err; + } + + update_group_ports(seq, ep); + return 0; +}
On 6/19/2024 5:28 PM, Takashi Iwai wrote:
For making it easer for applications to create a virtual UMP Endpoint and UMP blocks, add two API helper functions.
snd_seq_create_ump_endpoint() creates (unsurprisingly) a UMP Endpoint, based on the given snd_ump_endpoint_info_t information. The number of (max) UMP groups belonging to this Endpoint has to be specified. This function sets up the Endpoint info on the sequencer client, and creates a MIDI 2.0 UMP port as well as UMP Group ports automatically. The name of the sequencer client is updated from the Endpoint name, too.
After creating a UMP Endpoint, create each UMP Block via snd_seq_create_ump_block() function with a snd_ump_block_info_t info. The associated groups for each block have to be specified there. The port names and capability bits are updated accordingly after setting each block information.
Signed-off-by: Takashi Iwai tiwai@suse.de
...
if (*blknames) {
strlcat(blknames, ", ", sizeof(blknames));
strlcat(blknames, (const char *)bp->name,
sizeof(blknames));
FYI, this seems to introduce build problems on systems that do not have strlcpy:
During build: seqmid.c: In function ‘update_group_ports’: seqmid.c:672:33: warning: implicit declaration of function ‘strlcat’; did you mean ‘strncat’? [-Wimplicit-function-declaration] 672 | strlcat(blknames, ", ", sizeof(blknames)); | ^~~~~~~ | strncat
And then during linking: /usr/bin/ld: seq/.libs/libseq.a(seqmid.o): in function `update_group_ports': /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:672: undefined reference to `strlcat' /usr/bin/ld: /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:673: undefined reference to `strlcat' collect2: error: ld returned 1 exit status
On Wed, 31 Jul 2024 10:46:08 +0200, Amadeusz Sławiński wrote:
On 6/19/2024 5:28 PM, Takashi Iwai wrote:
For making it easer for applications to create a virtual UMP Endpoint and UMP blocks, add two API helper functions.
snd_seq_create_ump_endpoint() creates (unsurprisingly) a UMP Endpoint, based on the given snd_ump_endpoint_info_t information. The number of (max) UMP groups belonging to this Endpoint has to be specified. This function sets up the Endpoint info on the sequencer client, and creates a MIDI 2.0 UMP port as well as UMP Group ports automatically. The name of the sequencer client is updated from the Endpoint name, too.
After creating a UMP Endpoint, create each UMP Block via snd_seq_create_ump_block() function with a snd_ump_block_info_t info. The associated groups for each block have to be specified there. The port names and capability bits are updated accordingly after setting each block information.
Signed-off-by: Takashi Iwai tiwai@suse.de
...
if (*blknames) {
strlcat(blknames, ", ", sizeof(blknames));
strlcat(blknames, (const char *)bp->name,
sizeof(blknames));
FYI, this seems to introduce build problems on systems that do not have strlcpy:
During build: seqmid.c: In function ‘update_group_ports’: seqmid.c:672:33: warning: implicit declaration of function ‘strlcat’; did you mean ‘strncat’? [-Wimplicit-function-declaration] 672 | strlcat(blknames, ", ", sizeof(blknames)); | ^~~~~~~ | strncat
And then during linking: /usr/bin/ld: seq/.libs/libseq.a(seqmid.o): in function `update_group_ports': /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:672: undefined reference to `strlcat' /usr/bin/ld: /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:673: undefined reference to `strlcat' collect2: error: ld returned 1 exit status
Thanks, I'll modify it to avoid strlcat() like below.
Takashi
-- 8< -- Subject: [PATCH] seq: Avoid strlcat()
strlcat() isn't available in every system, so better to avoid it. Rewrite the code without strlcat().
Fixes: 6167b8ce3e80 ("seq: Add API helper functions for creating UMP Endpoint and Blocks") Link: https://lore.kernel.org/0796c157-1ac3-47a3-9d54-ba86f59d64d5@linux.intel.com Signed-off-by: Takashi Iwai tiwai@suse.de --- src/seq/seqmid.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 08c62d5c24b8..b30db4075254 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -635,6 +635,7 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) char blknames[64]; char name[64]; unsigned int caps = 0; + int len;
blknames[0] = 0; for (b = 0; b < ep->num_blocks; b++) { @@ -668,14 +669,13 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep)
if (!*bp->name) continue; - if (*blknames) { - strlcat(blknames, ", ", sizeof(blknames)); - strlcat(blknames, (const char *)bp->name, - sizeof(blknames)); - } else { + len = strlen(blknames); + if (len) + snprintf(blknames + len, sizeof(blknames) - len, + ", %s", bp->name); + else snd_strlcpy(blknames, (const char *)bp->name, sizeof(blknames)); - } }
if (!*blknames)
On 7/31/2024 11:21 AM, Takashi Iwai wrote:
On Wed, 31 Jul 2024 10:46:08 +0200, Amadeusz Sławiński wrote:
On 6/19/2024 5:28 PM, Takashi Iwai wrote:
For making it easer for applications to create a virtual UMP Endpoint and UMP blocks, add two API helper functions.
snd_seq_create_ump_endpoint() creates (unsurprisingly) a UMP Endpoint, based on the given snd_ump_endpoint_info_t information. The number of (max) UMP groups belonging to this Endpoint has to be specified. This function sets up the Endpoint info on the sequencer client, and creates a MIDI 2.0 UMP port as well as UMP Group ports automatically. The name of the sequencer client is updated from the Endpoint name, too.
After creating a UMP Endpoint, create each UMP Block via snd_seq_create_ump_block() function with a snd_ump_block_info_t info. The associated groups for each block have to be specified there. The port names and capability bits are updated accordingly after setting each block information.
Signed-off-by: Takashi Iwai tiwai@suse.de
...
if (*blknames) {
strlcat(blknames, ", ", sizeof(blknames));
strlcat(blknames, (const char *)bp->name,
sizeof(blknames));
FYI, this seems to introduce build problems on systems that do not have strlcpy:
During build: seqmid.c: In function ‘update_group_ports’: seqmid.c:672:33: warning: implicit declaration of function ‘strlcat’; did you mean ‘strncat’? [-Wimplicit-function-declaration] 672 | strlcat(blknames, ", ", sizeof(blknames)); | ^~~~~~~ | strncat
And then during linking: /usr/bin/ld: seq/.libs/libseq.a(seqmid.o): in function `update_group_ports': /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:672: undefined reference to `strlcat' /usr/bin/ld: /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:673: undefined reference to `strlcat' collect2: error: ld returned 1 exit status
Thanks, I'll modify it to avoid strlcat() like below.
Takashi
-- 8< -- Subject: [PATCH] seq: Avoid strlcat()
strlcat() isn't available in every system, so better to avoid it. Rewrite the code without strlcat().
Fixes: 6167b8ce3e80 ("seq: Add API helper functions for creating UMP Endpoint and Blocks") Link: https://lore.kernel.org/0796c157-1ac3-47a3-9d54-ba86f59d64d5@linux.intel.com Signed-off-by: Takashi Iwai tiwai@suse.de
src/seq/seqmid.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 08c62d5c24b8..b30db4075254 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -635,6 +635,7 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) char blknames[64]; char name[64]; unsigned int caps = 0;
int len;
blknames[0] = 0; for (b = 0; b < ep->num_blocks; b++) {
@@ -668,14 +669,13 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep)
if (!*bp->name) continue;
if (*blknames) {
strlcat(blknames, ", ", sizeof(blknames));
strlcat(blknames, (const char *)bp->name,
sizeof(blknames));
} else {
len = strlen(blknames);
if (len)
snprintf(blknames + len, sizeof(blknames) - len,
", %s", bp->name);
else snd_strlcpy(blknames, (const char *)bp->name, sizeof(blknames));
}
}
if (!*blknames)
Builds now, but still gives warning:
seqmid.c: In function ‘update_group_ports’: seqmid.c:675:45: warning: ‘%s’ directive output may be truncated writing up to 127 bytes into a region of size 61 [-Wformat-truncation=] 675 | ", %s", bp->name); | ^~ In file included from /usr/include/stdio.h:894, from ../../include/local.h:28, from seq_local.h:26, from seqmid.c:23: In function ‘snprintf’, inlined from ‘update_group_ports’ at seqmid.c:674:5: /usr/include/x86_64-linux-gnu/bits/stdio2.h:71:10: note: ‘__builtin___snprintf_chk’ output between 3 and 130 bytes into a destination of size 63 71 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 72 | __glibc_objsize (__s), __fmt, | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 73 | __va_arg_pack ()); | ~~~~~~~~~~~~~~~~~
On Wed, 31 Jul 2024 11:29:41 +0200, Amadeusz Sławiński wrote:
On 7/31/2024 11:21 AM, Takashi Iwai wrote:
On Wed, 31 Jul 2024 10:46:08 +0200, Amadeusz Sławiński wrote:
On 6/19/2024 5:28 PM, Takashi Iwai wrote:
For making it easer for applications to create a virtual UMP Endpoint and UMP blocks, add two API helper functions.
snd_seq_create_ump_endpoint() creates (unsurprisingly) a UMP Endpoint, based on the given snd_ump_endpoint_info_t information. The number of (max) UMP groups belonging to this Endpoint has to be specified. This function sets up the Endpoint info on the sequencer client, and creates a MIDI 2.0 UMP port as well as UMP Group ports automatically. The name of the sequencer client is updated from the Endpoint name, too.
After creating a UMP Endpoint, create each UMP Block via snd_seq_create_ump_block() function with a snd_ump_block_info_t info. The associated groups for each block have to be specified there. The port names and capability bits are updated accordingly after setting each block information.
Signed-off-by: Takashi Iwai tiwai@suse.de
...
if (*blknames) {
strlcat(blknames, ", ", sizeof(blknames));
strlcat(blknames, (const char *)bp->name,
sizeof(blknames));
FYI, this seems to introduce build problems on systems that do not have strlcpy:
During build: seqmid.c: In function ‘update_group_ports’: seqmid.c:672:33: warning: implicit declaration of function ‘strlcat’; did you mean ‘strncat’? [-Wimplicit-function-declaration] 672 | strlcat(blknames, ", ", sizeof(blknames)); | ^~~~~~~ | strncat
And then during linking: /usr/bin/ld: seq/.libs/libseq.a(seqmid.o): in function `update_group_ports': /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:672: undefined reference to `strlcat' /usr/bin/ld: /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:673: undefined reference to `strlcat' collect2: error: ld returned 1 exit status
Thanks, I'll modify it to avoid strlcat() like below.
Takashi
-- 8< -- Subject: [PATCH] seq: Avoid strlcat()
strlcat() isn't available in every system, so better to avoid it. Rewrite the code without strlcat().
Fixes: 6167b8ce3e80 ("seq: Add API helper functions for creating UMP Endpoint and Blocks") Link: https://lore.kernel.org/0796c157-1ac3-47a3-9d54-ba86f59d64d5@linux.intel.com Signed-off-by: Takashi Iwai tiwai@suse.de
src/seq/seqmid.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 08c62d5c24b8..b30db4075254 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -635,6 +635,7 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) char blknames[64]; char name[64]; unsigned int caps = 0;
for (b = 0; b < ep->num_blocks; b++) {int len; blknames[0] = 0;
@@ -668,14 +669,13 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) if (!*bp->name) continue;
if (*blknames) {
strlcat(blknames, ", ", sizeof(blknames));
strlcat(blknames, (const char *)bp->name,
sizeof(blknames));
} else {
len = strlen(blknames);
if (len)
snprintf(blknames + len, sizeof(blknames) - len,
", %s", bp->name);
else snd_strlcpy(blknames, (const char *)bp->name, sizeof(blknames));
} if (!*blknames)}
Builds now, but still gives warning:
seqmid.c: In function ‘update_group_ports’: seqmid.c:675:45: warning: ‘%s’ directive output may be truncated writing up to 127 bytes into a region of size 61 [-Wformat-truncation=] 675 | ", %s", bp->name); | ^~ In file included from /usr/include/stdio.h:894, from ../../include/local.h:28, from seq_local.h:26, from seqmid.c:23: In function ‘snprintf’, inlined from ‘update_group_ports’ at seqmid.c:674:5: /usr/include/x86_64-linux-gnu/bits/stdio2.h:71:10: note: ‘__builtin___snprintf_chk’ output between 3 and 130 bytes into a destination of size 63 71 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 72 | __glibc_objsize (__s), __fmt, | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 73 | __va_arg_pack ()); | ~~~~~~~~~~~~~~~~~
The compiler gives too much false positives.
Takashi
Provide an example program to demonstrate how to create a UMP Endpoint and Blocks, i.e. a virtual UMP device.
It's a simple filtering application that just haves the incoming note on/off velocity and sends out to the output. The UMP Endpoint and Block attributes can be adjusted via command-line options.
Signed-off-by: Takashi Iwai tiwai@suse.de --- test/Makefile.am | 3 +- test/seq-ump-example.c | 187 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 test/seq-ump-example.c
diff --git a/test/Makefile.am b/test/Makefile.am index 99c2c4ff9f06..635fa39bb7e3 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ SUBDIRS=. lsb
-check_PROGRAMS=control pcm pcm_min latency seq \ +check_PROGRAMS=control pcm pcm_min latency seq seq-ump-example \ playmidi1 timer rawmidi midiloop \ oldapi queue_timer namehint client_event_filter \ chmap audio_time user-ctl-element-set pcm-multi-thread @@ -12,6 +12,7 @@ pcm_min_LDADD=../src/libasound.la latency_LDADD=../src/libasound.la latency_LDFLAGS= -lm seq_LDADD=../src/libasound.la +seq_ump_example_LDADD=../src/libasound.la playmidi1_LDADD=../src/libasound.la timer_LDADD=../src/libasound.la rawmidi_LDADD=../src/libasound.la diff --git a/test/seq-ump-example.c b/test/seq-ump-example.c new file mode 100644 index 000000000000..7f6286827f36 --- /dev/null +++ b/test/seq-ump-example.c @@ -0,0 +1,187 @@ +// An example program to create a virtual UMP Endpoint +// +// A client simply reads each UMP packet and sends to subscribers +// while the note on/off velocity is halved + +#include <stdio.h> +#include <getopt.h> +#include <alsa/asoundlib.h> +#include <alsa/ump_msg.h> + +/* make the note on/off velocity half for MIDI1 CVM */ +static void midi1_half_note_velocity(snd_seq_ump_event_t *ev) +{ + snd_ump_msg_midi1_t *midi1 = (snd_ump_msg_midi1_t *)ev->ump; + + switch (snd_ump_msg_status(ev->ump)) { + case SND_UMP_MSG_NOTE_OFF: + case SND_UMP_MSG_NOTE_ON: + midi1->note_on.velocity >>= 1; + break; + } +} + +/* make the note on/off velocity half for MIDI2 CVM */ +static void midi2_half_note_velocity(snd_seq_ump_event_t *ev) +{ + snd_ump_msg_midi2_t *midi2 = (snd_ump_msg_midi2_t *)ev->ump; + + switch (snd_ump_msg_status(ev->ump)) { + case SND_UMP_MSG_NOTE_OFF: + case SND_UMP_MSG_NOTE_ON: + midi2->note_on.velocity >>= 1; + break; + } +} + +static void help(void) +{ + printf("seq-ump-example: Create a virtual UMP Endpoint and Blocks\n" + "\n" + "Usage: seq-ump-example [OPTIONS]\n" + "\n" + "-n,--num-blocks blocks Number of blocks (groups) to create\n" + "-m,--midi-version version MIDI protocol version (1 or 2)\n" + "-N--name UMP Endpoint name string\n" + "-P,--product name UMP Product ID string\n" + "-M,--manufacturer id UMP Manufacturer ID value (24bit)\n" + "-F,--family id UMP Family ID value (16bit)\n" + "-O,--model id UMP Model ID value (16bit)\n" + "-R,--sw-revision id UMP Software Revision ID (32bit)\n"); +} + +int main(int argc, char **argv) +{ + int midi_version = 2; + int num_blocks = 1; + const char *name = "ACMESynth"; + const char *product = "Halfmoon"; + unsigned int manufacturer = 0x123456; + unsigned int family = 0x1234; + unsigned int model = 0xabcd; + unsigned int sw_revision = 0x12345678; + snd_seq_t *seq; + snd_ump_endpoint_info_t *ep; + snd_ump_block_info_t *blk; + snd_seq_ump_event_t *ev; + int i, c, err; + unsigned char tmp[4]; + + static const struct option long_option[] = { + {"num-blocks", required_argument, 0, 'n'}, + {"midi-version", required_argument, 0, 'm'}, + {"name", required_argument, 0, 'N'}, + {"product", required_argument, 0, 'P'}, + {"manufacturer", required_argument, 0, 'M'}, + {"family", required_argument, 0, 'F'}, + {"model", required_argument, 0, 'O'}, + {"sw-revision", required_argument, 0, 'R'}, + {0, 0, 0, 0} + }; + + while ((c = getopt_long(argc, argv, "n:m:N:P:M:F:O:R:", + long_option, NULL)) >= 0) { + switch (c) { + case 'n': + num_blocks = atoi(optarg); + break; + case 'm': + midi_version = atoi(optarg); + break; + case 'N': + name = optarg; + break; + case 'P': + product = optarg; + break; + case 'M': + manufacturer = strtol(optarg, NULL, 0); + break; + case 'F': + family = strtol(optarg, NULL, 0); + break; + case 'O': + model = strtol(optarg, NULL, 0); + break; + case 'R': + sw_revision = strtol(optarg, NULL, 0); + break; + default: + help(); + return 1; + } + } + + err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0); + if (err < 0) { + fprintf(stderr, "failed to open sequencer: %d\n", err); + return 1; + } + + snd_ump_endpoint_info_alloca(&ep); + snd_ump_endpoint_info_set_name(ep, name); + snd_ump_endpoint_info_set_product_id(ep, product); + if (midi_version == 1) { + snd_ump_endpoint_info_set_protocol_caps(ep, SND_UMP_EP_INFO_PROTO_MIDI1); + snd_ump_endpoint_info_set_protocol(ep, SND_UMP_EP_INFO_PROTO_MIDI1); + } else { + snd_ump_endpoint_info_set_protocol_caps(ep, SND_UMP_EP_INFO_PROTO_MIDI2); + snd_ump_endpoint_info_set_protocol(ep, SND_UMP_EP_INFO_PROTO_MIDI2); + } + snd_ump_endpoint_info_set_num_blocks(ep, num_blocks); + snd_ump_endpoint_info_set_manufacturer_id(ep, manufacturer); + snd_ump_endpoint_info_set_family_id(ep, family); + snd_ump_endpoint_info_set_model_id(ep, model); + for (i = 0; i < 4; i++) + tmp[i] = (sw_revision >> ((3 - i) * 8)) & 0xff; + snd_ump_endpoint_info_set_sw_revision(ep, tmp); + + err = snd_seq_create_ump_endpoint(seq, ep, num_blocks); + if (err < 0) { + fprintf(stderr, "failed to set UMP EP info: %d\n", err); + return 1; + } + + snd_ump_block_info_alloca(&blk); + + for (i = 0; i < num_blocks; i++) { + char blkname[32]; + + sprintf(blkname, "Filter %d", i + 1); + snd_ump_block_info_set_name(blk, blkname); + snd_ump_block_info_set_direction(blk, SND_UMP_DIR_BIDIRECTION); + snd_ump_block_info_set_first_group(blk, i); + snd_ump_block_info_set_num_groups(blk, 1); + snd_ump_block_info_set_ui_hint(blk, SND_UMP_BLOCK_UI_HINT_BOTH); + + err = snd_seq_create_ump_block(seq, i, blk); + if (err < 0) { + fprintf(stderr, "failed to set UMP block info %d: %d\n", + i, err); + return 1; + } + } + + /* halve the incoming note-on / off velocity and pass through + * to subscribers + */ + while (snd_seq_ump_event_input(seq, &ev) >= 0) { + if (!snd_seq_ev_is_ump(ev)) + continue; + switch (snd_ump_msg_type(ev->ump)) { + case SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE: + midi1_half_note_velocity(ev); + break; + case SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE: + midi2_half_note_velocity(ev); + break; + } + + snd_seq_ev_set_subs(ev); + snd_seq_ev_set_direct(ev); + snd_seq_ump_event_output(seq, ev); + snd_seq_drain_output(seq); + } + + return 0; +}
Signed-off-by: Takashi Iwai tiwai@suse.de --- .gitignore | 1 + 1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore index a74b5195a7d9..947eba45c685 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,7 @@ test/playmidi1 test/queue_timer test/rawmidi test/seq +test/seq-ump-example test/timer test/lsb/config test/lsb/midi_event
participants (2)
-
Amadeusz Sławiński
-
Takashi Iwai