Commit e9e02819 authored by Takashi Iwai's avatar Takashi Iwai

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: default avatarJaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-31-tiwai@suse.deSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent a3ca3b30
...@@ -66,5 +66,7 @@ config SND_SEQ_UMP ...@@ -66,5 +66,7 @@ config SND_SEQ_UMP
Say Y here to enable the support for handling UMP (Universal MIDI Say Y here to enable the support for handling UMP (Universal MIDI
Packet) events via ALSA sequencer infrastructure, which is an Packet) events via ALSA sequencer infrastructure, which is an
essential feature for enabling MIDI 2.0 support. 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 endif # SND_SEQUENCER
...@@ -8,6 +8,7 @@ snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ ...@@ -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_fifo.o seq_prioq.o seq_timer.o \
seq_system.o seq_ports.o seq_system.o seq_ports.o
snd-seq-$(CONFIG_SND_PROC_FS) += seq_info.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-objs := seq_midi.o
snd-seq-midi-emul-objs := seq_midi_emul.o snd-seq-midi-emul-objs := seq_midi_emul.o
snd-seq-midi-event-objs := seq_midi_event.o snd-seq-midi-event-objs := seq_midi_event.o
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "seq_timer.h" #include "seq_timer.h"
#include "seq_info.h" #include "seq_info.h"
#include "seq_system.h" #include "seq_system.h"
#include "seq_ump_convert.h"
#include <sound/seq_device.h> #include <sound/seq_device.h>
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
#include <linux/compat.h> #include <linux/compat.h>
...@@ -612,6 +613,27 @@ static int update_timestamp_of_queue(struct snd_seq_event *event, ...@@ -612,6 +613,27 @@ static int update_timestamp_of_queue(struct snd_seq_event *event,
return 1; 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. * deliver an event to the specified destination.
...@@ -648,22 +670,20 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client, ...@@ -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, update_timestamp_of_queue(event, dest_port->time_queue,
dest_port->time_real); dest_port->time_real);
switch (dest->type) { #if IS_ENABLED(CONFIG_SND_SEQ_UMP)
case USER_CLIENT: if (snd_seq_ev_is_ump(event)) {
if (dest->data.user.fifo) result = snd_seq_deliver_from_ump(client, dest, dest_port,
result = snd_seq_fifo_event_in(dest->data.user.fifo, event); event, atomic, hop);
break; 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 */
case KERNEL_CLIENT: result = __snd_seq_deliver_single_event(dest, dest_port, event,
if (dest_port->event_input == NULL)
break;
result = dest_port->event_input(event, direct,
dest_port->private_data,
atomic, hop); atomic, hop);
break;
default:
break;
}
__skip: __skip:
if (dest_port) if (dest_port)
......
...@@ -85,6 +85,11 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table ...@@ -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, int snd_seq_client_notify_subscription(int client, int port,
struct snd_seq_port_subscribe *info, int evtype); 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 */ /* only for OSS sequencer */
bool snd_seq_client_ioctl_lock(int clientid); bool snd_seq_client_ioctl_lock(int clientid);
void snd_seq_client_ioctl_unlock(int clientid); void snd_seq_client_ioctl_unlock(int clientid);
...@@ -95,4 +100,14 @@ extern int seq_client_load[15]; ...@@ -95,4 +100,14 @@ extern int seq_client_load[15];
struct snd_seq_client *snd_seq_kernel_client_get(int client); struct snd_seq_client *snd_seq_kernel_client_get(int client);
void snd_seq_kernel_client_put(struct snd_seq_client *cptr); 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 #endif
...@@ -42,6 +42,17 @@ struct snd_seq_port_subs_info { ...@@ -42,6 +42,17 @@ struct snd_seq_port_subs_info {
int (*close)(void *private_data, struct snd_seq_port_subscribe *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_client_port {
struct snd_seq_addr addr; /* client/port number */ struct snd_seq_addr addr; /* client/port number */
...@@ -75,6 +86,10 @@ struct snd_seq_client_port { ...@@ -75,6 +86,10 @@ struct snd_seq_client_port {
/* UMP direction and group */ /* UMP direction and group */
unsigned char direction; unsigned char direction;
unsigned char ump_group; 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; struct snd_seq_client;
......
This diff is collapsed.
// 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment