ALSA: seq: Automatic conversion of UMP events

This patch enables the automatic conversion of UMP events from/to the
legacy ALSA sequencer MIDI events.  Also, as UMP itself has two
different modes (MIDI 1.0 and MIDI 2.0), yet another converters
between them are needed, too.  Namely, we have conversions between the
legacy and UMP like:
  - seq legacy event -> seq UMP MIDI 1.0 event
  - seq legacy event -> seq UMP MIDI 2.0 event
  - seq UMP MIDI 1.0 event -> seq legacy event
  - seq UMP MIDI 2.0 event -> seq legacy event

and the conversions between UMP MIDI 1.0 and 2.0 clients like:
  - seq UMP MIDI 1.0 event -> seq UMP MIDI 2.0 event
  - seq UMP MIDI 2.0 event -> seq UMP MIDI 1.0 event

The translation is per best-effort; some MIDI 2.0 specific events are
ignored when translated to MIDI 1.0.

Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-31-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2023-05-23 09:53:51 +02:00
parent a3ca3b3080
commit e9e02819a9
7 changed files with 1280 additions and 15 deletions

View File

@ -66,5 +66,7 @@ config SND_SEQ_UMP
Say Y here to enable the support for handling UMP (Universal MIDI
Packet) events via ALSA sequencer infrastructure, which is an
essential feature for enabling MIDI 2.0 support.
It includes the automatic conversion of ALSA sequencer events
among legacy and UMP clients.
endif # SND_SEQUENCER

View File

@ -8,6 +8,7 @@ snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
seq_fifo.o seq_prioq.o seq_timer.o \
seq_system.o seq_ports.o
snd-seq-$(CONFIG_SND_PROC_FS) += seq_info.o
snd-seq-$(CONFIG_SND_SEQ_UMP) += seq_ump_convert.o
snd-seq-midi-objs := seq_midi.o
snd-seq-midi-emul-objs := seq_midi_emul.o
snd-seq-midi-event-objs := seq_midi_event.o

View File

@ -20,6 +20,7 @@
#include "seq_timer.h"
#include "seq_info.h"
#include "seq_system.h"
#include "seq_ump_convert.h"
#include <sound/seq_device.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
@ -612,6 +613,27 @@ static int update_timestamp_of_queue(struct snd_seq_event *event,
return 1;
}
/* deliver a single event; called from below and UMP converter */
int __snd_seq_deliver_single_event(struct snd_seq_client *dest,
struct snd_seq_client_port *dest_port,
struct snd_seq_event *event,
int atomic, int hop)
{
switch (dest->type) {
case USER_CLIENT:
if (!dest->data.user.fifo)
return 0;
return snd_seq_fifo_event_in(dest->data.user.fifo, event);
case KERNEL_CLIENT:
if (!dest_port->event_input)
return 0;
return dest_port->event_input(event,
snd_seq_ev_is_direct(event),
dest_port->private_data,
atomic, hop);
}
return 0;
}
/*
* deliver an event to the specified destination.
@ -648,22 +670,20 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client,
update_timestamp_of_queue(event, dest_port->time_queue,
dest_port->time_real);
switch (dest->type) {
case USER_CLIENT:
if (dest->data.user.fifo)
result = snd_seq_fifo_event_in(dest->data.user.fifo, event);
break;
case KERNEL_CLIENT:
if (dest_port->event_input == NULL)
break;
result = dest_port->event_input(event, direct,
dest_port->private_data,
atomic, hop);
break;
default:
break;
#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
if (snd_seq_ev_is_ump(event)) {
result = snd_seq_deliver_from_ump(client, dest, dest_port,
event, atomic, hop);
goto __skip;
} else if (snd_seq_client_is_ump(dest)) {
result = snd_seq_deliver_to_ump(client, dest, dest_port,
event, atomic, hop);
goto __skip;
}
#endif /* CONFIG_SND_SEQ_UMP */
result = __snd_seq_deliver_single_event(dest, dest_port, event,
atomic, hop);
__skip:
if (dest_port)

View File

@ -85,6 +85,11 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table
int snd_seq_client_notify_subscription(int client, int port,
struct snd_seq_port_subscribe *info, int evtype);
int __snd_seq_deliver_single_event(struct snd_seq_client *dest,
struct snd_seq_client_port *dest_port,
struct snd_seq_event *event,
int atomic, int hop);
/* only for OSS sequencer */
bool snd_seq_client_ioctl_lock(int clientid);
void snd_seq_client_ioctl_unlock(int clientid);
@ -95,4 +100,14 @@ extern int seq_client_load[15];
struct snd_seq_client *snd_seq_kernel_client_get(int client);
void snd_seq_kernel_client_put(struct snd_seq_client *cptr);
static inline bool snd_seq_client_is_ump(struct snd_seq_client *c)
{
return c->midi_version != SNDRV_SEQ_CLIENT_LEGACY_MIDI;
}
static inline bool snd_seq_client_is_midi2(struct snd_seq_client *c)
{
return c->midi_version == SNDRV_SEQ_CLIENT_UMP_MIDI_2_0;
}
#endif

View File

@ -42,6 +42,17 @@ struct snd_seq_port_subs_info {
int (*close)(void *private_data, struct snd_seq_port_subscribe *info);
};
/* context for converting from legacy control event to UMP packet */
struct snd_seq_ump_midi2_bank {
bool rpn_set;
bool nrpn_set;
bool bank_set;
unsigned char cc_rpn_msb, cc_rpn_lsb;
unsigned char cc_nrpn_msb, cc_nrpn_lsb;
unsigned char cc_data_msb, cc_data_lsb;
unsigned char cc_bank_msb, cc_bank_lsb;
};
struct snd_seq_client_port {
struct snd_seq_addr addr; /* client/port number */
@ -75,6 +86,10 @@ struct snd_seq_client_port {
/* UMP direction and group */
unsigned char direction;
unsigned char ump_group;
#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
struct snd_seq_ump_midi2_bank midi2_bank[16]; /* per channel */
#endif
};
struct snd_seq_client;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer event conversion between UMP and legacy clients
*/
#ifndef __SEQ_UMP_CONVERT_H
#define __SEQ_UMP_CONVERT_H
#include "seq_clientmgr.h"
#include "seq_ports.h"
int snd_seq_deliver_from_ump(struct snd_seq_client *source,
struct snd_seq_client *dest,
struct snd_seq_client_port *dest_port,
struct snd_seq_event *event,
int atomic, int hop);
int snd_seq_deliver_to_ump(struct snd_seq_client *source,
struct snd_seq_client *dest,
struct snd_seq_client_port *dest_port,
struct snd_seq_event *event,
int atomic, int hop);
#endif /* __SEQ_UMP_CONVERT_H */