]> git.alsa-project.org Git - alsa-lib.git/commitdiff
seq: Add API helper functions for creating UMP Endpoint and Blocks
authorTakashi Iwai <tiwai@suse.de>
Wed, 19 Jun 2024 12:35:36 +0000 (14:35 +0200)
committerTakashi Iwai <tiwai@suse.de>
Wed, 19 Jun 2024 15:12:24 +0000 (17:12 +0200)
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
include/ump.h
src/seq/seq.c
src/seq/seq_local.h
src/seq/seqmid.c

index 4464c2d3af0bee0da260c5d18c53f9f6812f8103..bf968a5b2c7b630a4d433d4420f7a1712e640505 100644 (file)
@@ -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
index 45b3ad270db77dffd7b4b0562d28ea8e8b29967c..01363a329fa7af294c66f438f90c7122a09224e0 100644 (file)
@@ -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
index 5eac4848b9c79fba6ad6ded1027411b5662d9212..ff046814017765dab6acd4aec97ddd247612af9a 100644 (file)
@@ -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;
 }
index 468248062638378f930cef02c57db7d42231f03c..263029702739617aa721179f829ee3a06f6afd9c 100644 (file)
@@ -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);
index 9ec93ee8ade1b30277eb920c4ef82edd5db8fbea..d7eac6cafa0ed9bb5ff1c2f772d98c504f32b045 100644 (file)
@@ -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;
+}