2023-05-23 10:53:51 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer event conversion between UMP and legacy clients
*/
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <sound/core.h>
# include <sound/ump.h>
# include <sound/ump_msg.h>
# include "seq_ump_convert.h"
/*
* Upgrade / downgrade value bits
*/
static u8 downscale_32_to_7bit ( u32 src )
{
return src > > 25 ;
}
static u16 downscale_32_to_14bit ( u32 src )
{
return src > > 18 ;
}
static u8 downscale_16_to_7bit ( u16 src )
{
return src > > 9 ;
}
static u16 upscale_7_to_16bit ( u8 src )
{
u16 val , repeat ;
val = ( u16 ) src < < 9 ;
if ( src < = 0x40 )
return val ;
repeat = src & 0x3f ;
return val | ( repeat < < 3 ) | ( repeat > > 3 ) ;
}
static u32 upscale_7_to_32bit ( u8 src )
{
u32 val , repeat ;
val = src < < 25 ;
if ( src < = 0x40 )
return val ;
repeat = src & 0x3f ;
return val | ( repeat < < 19 ) | ( repeat < < 13 ) |
( repeat < < 7 ) | ( repeat < < 1 ) | ( repeat > > 5 ) ;
}
static u32 upscale_14_to_32bit ( u16 src )
{
u32 val , repeat ;
val = src < < 18 ;
if ( src < = 0x2000 )
return val ;
repeat = src & 0x1fff ;
return val | ( repeat < < 5 ) | ( repeat > > 8 ) ;
}
static unsigned char get_ump_group ( struct snd_seq_client_port * port )
{
return port - > ump_group ? ( port - > ump_group - 1 ) : 0 ;
}
/* create a UMP header */
# define make_raw_ump(port, type) \
ump_compose ( type , get_ump_group ( port ) , 0 , 0 )
/*
* UMP - > MIDI1 sequencer event
*/
/* MIDI 1.0 CVM */
/* encode note event */
static void ump_midi1_to_note_ev ( const union snd_ump_midi1_msg * val ,
struct snd_seq_event * ev )
{
ev - > data . note . channel = val - > note . channel ;
ev - > data . note . note = val - > note . note ;
ev - > data . note . velocity = val - > note . velocity ;
}
/* encode one parameter controls */
static void ump_midi1_to_ctrl_ev ( const union snd_ump_midi1_msg * val ,
struct snd_seq_event * ev )
{
ev - > data . control . channel = val - > caf . channel ;
ev - > data . control . value = val - > caf . data ;
}
/* encode pitch wheel change */
static void ump_midi1_to_pitchbend_ev ( const union snd_ump_midi1_msg * val ,
struct snd_seq_event * ev )
{
ev - > data . control . channel = val - > pb . channel ;
ev - > data . control . value = ( val - > pb . data_msb < < 7 ) | val - > pb . data_lsb ;
ev - > data . control . value - = 8192 ;
}
/* encode midi control change */
static void ump_midi1_to_cc_ev ( const union snd_ump_midi1_msg * val ,
struct snd_seq_event * ev )
{
ev - > data . control . channel = val - > cc . channel ;
ev - > data . control . param = val - > cc . index ;
ev - > data . control . value = val - > cc . data ;
}
/* Encoding MIDI 1.0 UMP packet */
struct seq_ump_midi1_to_ev {
int seq_type ;
void ( * encode ) ( const union snd_ump_midi1_msg * val , struct snd_seq_event * ev ) ;
} ;
/* Encoders for MIDI1 status 0x80-0xe0 */
static struct seq_ump_midi1_to_ev midi1_msg_encoders [ ] = {
{ SNDRV_SEQ_EVENT_NOTEOFF , ump_midi1_to_note_ev } , /* 0x80 */
{ SNDRV_SEQ_EVENT_NOTEON , ump_midi1_to_note_ev } , /* 0x90 */
{ SNDRV_SEQ_EVENT_KEYPRESS , ump_midi1_to_note_ev } , /* 0xa0 */
{ SNDRV_SEQ_EVENT_CONTROLLER , ump_midi1_to_cc_ev } , /* 0xb0 */
{ SNDRV_SEQ_EVENT_PGMCHANGE , ump_midi1_to_ctrl_ev } , /* 0xc0 */
{ SNDRV_SEQ_EVENT_CHANPRESS , ump_midi1_to_ctrl_ev } , /* 0xd0 */
{ SNDRV_SEQ_EVENT_PITCHBEND , ump_midi1_to_pitchbend_ev } , /* 0xe0 */
} ;
static int cvt_ump_midi1_to_event ( const union snd_ump_midi1_msg * val ,
struct snd_seq_event * ev )
{
unsigned char status = val - > note . status ;
if ( status < 0x8 | | status > 0xe )
return 0 ; /* invalid - skip */
status - = 8 ;
ev - > type = midi1_msg_encoders [ status ] . seq_type ;
ev - > flags = SNDRV_SEQ_EVENT_LENGTH_FIXED ;
midi1_msg_encoders [ status ] . encode ( val , ev ) ;
return 1 ;
}
/* MIDI System message */
/* encode one parameter value*/
static void ump_system_to_one_param_ev ( const union snd_ump_midi1_msg * val ,
struct snd_seq_event * ev )
{
ev - > data . control . value = val - > system . parm1 ;
}
/* encode song position */
static void ump_system_to_songpos_ev ( const union snd_ump_midi1_msg * val ,
struct snd_seq_event * ev )
{
ev - > data . control . value = ( val - > system . parm1 < < 7 ) | val - > system . parm2 ;
}
/* Encoders for 0xf0 - 0xff */
static struct seq_ump_midi1_to_ev system_msg_encoders [ ] = {
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0xf0 */
{ SNDRV_SEQ_EVENT_QFRAME , ump_system_to_one_param_ev } , /* 0xf1 */
{ SNDRV_SEQ_EVENT_SONGPOS , ump_system_to_songpos_ev } , /* 0xf2 */
{ SNDRV_SEQ_EVENT_SONGSEL , ump_system_to_one_param_ev } , /* 0xf3 */
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0xf4 */
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0xf5 */
{ SNDRV_SEQ_EVENT_TUNE_REQUEST , NULL } , /* 0xf6 */
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0xf7 */
{ SNDRV_SEQ_EVENT_CLOCK , NULL } , /* 0xf8 */
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0xf9 */
{ SNDRV_SEQ_EVENT_START , NULL } , /* 0xfa */
{ SNDRV_SEQ_EVENT_CONTINUE , NULL } , /* 0xfb */
{ SNDRV_SEQ_EVENT_STOP , NULL } , /* 0xfc */
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0xfd */
{ SNDRV_SEQ_EVENT_SENSING , NULL } , /* 0xfe */
{ SNDRV_SEQ_EVENT_RESET , NULL } , /* 0xff */
} ;
static int cvt_ump_system_to_event ( const union snd_ump_midi1_msg * val ,
struct snd_seq_event * ev )
{
unsigned char status = val - > system . status ;
if ( ( status & 0xf0 ) ! = UMP_MIDI1_MSG_REALTIME )
return 0 ; /* invalid status - skip */
status & = 0x0f ;
ev - > type = system_msg_encoders [ status ] . seq_type ;
ev - > flags = SNDRV_SEQ_EVENT_LENGTH_FIXED ;
if ( ev - > type = = SNDRV_SEQ_EVENT_NONE )
return 0 ;
if ( system_msg_encoders [ status ] . encode )
system_msg_encoders [ status ] . encode ( val , ev ) ;
return 1 ;
}
/* MIDI 2.0 CVM */
/* encode note event */
static int ump_midi2_to_note_ev ( const union snd_ump_midi2_msg * val ,
struct snd_seq_event * ev )
{
ev - > data . note . channel = val - > note . channel ;
ev - > data . note . note = val - > note . note ;
ev - > data . note . velocity = downscale_16_to_7bit ( val - > note . velocity ) ;
/* correct note-on velocity 0 to 1;
* it ' s no longer equivalent as not - off for MIDI 2.0
*/
if ( ev - > type = = SNDRV_SEQ_EVENT_NOTEON & &
! ev - > data . note . velocity )
ev - > data . note . velocity = 1 ;
return 1 ;
}
/* encode pitch wheel change */
static int ump_midi2_to_pitchbend_ev ( const union snd_ump_midi2_msg * val ,
struct snd_seq_event * ev )
{
ev - > data . control . channel = val - > pb . channel ;
ev - > data . control . value = downscale_32_to_14bit ( val - > pb . data ) ;
ev - > data . control . value - = 8192 ;
return 1 ;
}
/* encode midi control change */
static int ump_midi2_to_cc_ev ( const union snd_ump_midi2_msg * val ,
struct snd_seq_event * ev )
{
ev - > data . control . channel = val - > cc . channel ;
ev - > data . control . param = val - > cc . index ;
ev - > data . control . value = downscale_32_to_7bit ( val - > cc . data ) ;
return 1 ;
}
/* encode midi program change */
static int ump_midi2_to_pgm_ev ( const union snd_ump_midi2_msg * val ,
struct snd_seq_event * ev )
{
int size = 1 ;
ev - > data . control . channel = val - > pg . channel ;
if ( val - > pg . bank_valid ) {
ev - > type = SNDRV_SEQ_EVENT_CONTROL14 ;
ev - > data . control . param = UMP_CC_BANK_SELECT ;
ev - > data . control . value = ( val - > pg . bank_msb < < 7 ) | val - > pg . bank_lsb ;
ev [ 1 ] = ev [ 0 ] ;
ev + + ;
ev - > type = SNDRV_SEQ_EVENT_PGMCHANGE ;
size = 2 ;
}
ev - > data . control . value = val - > pg . program ;
return size ;
}
/* encode one parameter controls */
static int ump_midi2_to_ctrl_ev ( const union snd_ump_midi2_msg * val ,
struct snd_seq_event * ev )
{
ev - > data . control . channel = val - > caf . channel ;
ev - > data . control . value = downscale_32_to_7bit ( val - > caf . data ) ;
return 1 ;
}
/* encode RPN/NRPN */
static int ump_midi2_to_rpn_ev ( const union snd_ump_midi2_msg * val ,
struct snd_seq_event * ev )
{
ev - > data . control . channel = val - > rpn . channel ;
ev - > data . control . param = ( val - > rpn . bank < < 7 ) | val - > rpn . index ;
ev - > data . control . value = downscale_32_to_14bit ( val - > rpn . data ) ;
return 1 ;
}
/* Encoding MIDI 2.0 UMP Packet */
struct seq_ump_midi2_to_ev {
int seq_type ;
int ( * encode ) ( const union snd_ump_midi2_msg * val , struct snd_seq_event * ev ) ;
} ;
/* Encoders for MIDI2 status 0x00-0xf0 */
static struct seq_ump_midi2_to_ev midi2_msg_encoders [ ] = {
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0x00 */
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0x10 */
{ SNDRV_SEQ_EVENT_REGPARAM , ump_midi2_to_rpn_ev } , /* 0x20 */
{ SNDRV_SEQ_EVENT_NONREGPARAM , ump_midi2_to_rpn_ev } , /* 0x30 */
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0x40 */
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0x50 */
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0x60 */
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0x70 */
{ SNDRV_SEQ_EVENT_NOTEOFF , ump_midi2_to_note_ev } , /* 0x80 */
{ SNDRV_SEQ_EVENT_NOTEON , ump_midi2_to_note_ev } , /* 0x90 */
{ SNDRV_SEQ_EVENT_KEYPRESS , ump_midi2_to_note_ev } , /* 0xa0 */
{ SNDRV_SEQ_EVENT_CONTROLLER , ump_midi2_to_cc_ev } , /* 0xb0 */
{ SNDRV_SEQ_EVENT_PGMCHANGE , ump_midi2_to_pgm_ev } , /* 0xc0 */
{ SNDRV_SEQ_EVENT_CHANPRESS , ump_midi2_to_ctrl_ev } , /* 0xd0 */
{ SNDRV_SEQ_EVENT_PITCHBEND , ump_midi2_to_pitchbend_ev } , /* 0xe0 */
{ SNDRV_SEQ_EVENT_NONE , NULL } , /* 0xf0 */
} ;
static int cvt_ump_midi2_to_event ( const union snd_ump_midi2_msg * val ,
struct snd_seq_event * ev )
{
unsigned char status = val - > note . status ;
ev - > type = midi2_msg_encoders [ status ] . seq_type ;
if ( ev - > type = = SNDRV_SEQ_EVENT_NONE )
return 0 ; /* skip */
ev - > flags = SNDRV_SEQ_EVENT_LENGTH_FIXED ;
return midi2_msg_encoders [ status ] . encode ( val , ev ) ;
}
/* parse and compose for a sysex var-length event */
static int cvt_ump_sysex7_to_event ( const u32 * data , unsigned char * buf ,
struct snd_seq_event * ev )
{
unsigned char status ;
unsigned char bytes ;
u32 val ;
int size = 0 ;
val = data [ 0 ] ;
status = ump_sysex_message_status ( val ) ;
bytes = ump_sysex_message_length ( val ) ;
if ( bytes > 6 )
return 0 ; // skip
if ( status = = UMP_SYSEX_STATUS_SINGLE | |
status = = UMP_SYSEX_STATUS_START ) {
buf [ 0 ] = UMP_MIDI1_MSG_SYSEX_START ;
size = 1 ;
}
if ( bytes > 0 )
buf [ size + + ] = ( val > > 8 ) & 0x7f ;
if ( bytes > 1 )
buf [ size + + ] = val & 0x7f ;
val = data [ 1 ] ;
if ( bytes > 2 )
buf [ size + + ] = ( val > > 24 ) & 0x7f ;
if ( bytes > 3 )
buf [ size + + ] = ( val > > 16 ) & 0x7f ;
if ( bytes > 4 )
buf [ size + + ] = ( val > > 8 ) & 0x7f ;
if ( bytes > 5 )
buf [ size + + ] = val & 0x7f ;
if ( status = = UMP_SYSEX_STATUS_SINGLE | |
status = = UMP_SYSEX_STATUS_END )
buf [ size + + ] = UMP_MIDI1_MSG_SYSEX_END ;
ev - > type = SNDRV_SEQ_EVENT_SYSEX ;
ev - > flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE ;
ev - > data . ext . len = size ;
ev - > data . ext . ptr = buf ;
return 1 ;
}
/* convert UMP packet from MIDI 1.0 to MIDI 2.0 and deliver it */
static int cvt_ump_midi1_to_midi2 ( struct snd_seq_client * dest ,
struct snd_seq_client_port * dest_port ,
struct snd_seq_event * __event ,
int atomic , int hop )
{
struct snd_seq_ump_event * event = ( struct snd_seq_ump_event * ) __event ;
struct snd_seq_ump_event ev_cvt ;
const union snd_ump_midi1_msg * midi1 = ( const union snd_ump_midi1_msg * ) event - > ump ;
union snd_ump_midi2_msg * midi2 = ( union snd_ump_midi2_msg * ) ev_cvt . ump ;
ev_cvt = * event ;
memset ( & ev_cvt . ump , 0 , sizeof ( ev_cvt . ump ) ) ;
midi2 - > note . type = UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE ;
midi2 - > note . group = midi1 - > note . group ;
midi2 - > note . status = midi1 - > note . status ;
midi2 - > note . channel = midi1 - > note . channel ;
switch ( midi1 - > note . status ) {
case UMP_MSG_STATUS_NOTE_ON :
case UMP_MSG_STATUS_NOTE_OFF :
midi2 - > note . note = midi1 - > note . note ;
midi2 - > note . velocity = upscale_7_to_16bit ( midi1 - > note . velocity ) ;
break ;
case UMP_MSG_STATUS_POLY_PRESSURE :
midi2 - > paf . note = midi1 - > paf . note ;
midi2 - > paf . data = upscale_7_to_32bit ( midi1 - > paf . data ) ;
break ;
case UMP_MSG_STATUS_CC :
midi2 - > cc . index = midi1 - > cc . index ;
midi2 - > cc . data = upscale_7_to_32bit ( midi1 - > cc . data ) ;
break ;
case UMP_MSG_STATUS_PROGRAM :
midi2 - > pg . program = midi1 - > pg . program ;
break ;
case UMP_MSG_STATUS_CHANNEL_PRESSURE :
midi2 - > caf . data = upscale_7_to_32bit ( midi1 - > caf . data ) ;
break ;
case UMP_MSG_STATUS_PITCH_BEND :
midi2 - > pb . data = upscale_14_to_32bit ( ( midi1 - > pb . data_msb < < 7 ) |
midi1 - > pb . data_lsb ) ;
break ;
default :
return 0 ;
}
return __snd_seq_deliver_single_event ( dest , dest_port ,
( struct snd_seq_event * ) & ev_cvt ,
atomic , hop ) ;
}
/* convert UMP packet from MIDI 2.0 to MIDI 1.0 and deliver it */
static int cvt_ump_midi2_to_midi1 ( struct snd_seq_client * dest ,
struct snd_seq_client_port * dest_port ,
struct snd_seq_event * __event ,
int atomic , int hop )
{
struct snd_seq_ump_event * event = ( struct snd_seq_ump_event * ) __event ;
struct snd_seq_ump_event ev_cvt ;
union snd_ump_midi1_msg * midi1 = ( union snd_ump_midi1_msg * ) ev_cvt . ump ;
const union snd_ump_midi2_msg * midi2 = ( const union snd_ump_midi2_msg * ) event - > ump ;
u16 v ;
ev_cvt = * event ;
memset ( & ev_cvt . ump , 0 , sizeof ( ev_cvt . ump ) ) ;
midi1 - > note . type = UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE ;
midi1 - > note . group = midi2 - > note . group ;
midi1 - > note . status = midi2 - > note . status ;
midi1 - > note . channel = midi2 - > note . channel ;
switch ( midi2 - > note . status < < 4 ) {
case UMP_MSG_STATUS_NOTE_ON :
case UMP_MSG_STATUS_NOTE_OFF :
midi1 - > note . note = midi2 - > note . note ;
midi1 - > note . velocity = downscale_16_to_7bit ( midi2 - > note . velocity ) ;
break ;
case UMP_MSG_STATUS_POLY_PRESSURE :
midi1 - > paf . note = midi2 - > paf . note ;
midi1 - > paf . data = downscale_32_to_7bit ( midi2 - > paf . data ) ;
break ;
case UMP_MSG_STATUS_CC :
midi1 - > cc . index = midi2 - > cc . index ;
midi1 - > cc . data = downscale_32_to_7bit ( midi2 - > cc . data ) ;
break ;
case UMP_MSG_STATUS_PROGRAM :
midi1 - > pg . program = midi2 - > pg . program ;
break ;
case UMP_MSG_STATUS_CHANNEL_PRESSURE :
midi1 - > caf . data = downscale_32_to_7bit ( midi2 - > caf . data ) ;
break ;
case UMP_MSG_STATUS_PITCH_BEND :
v = downscale_32_to_14bit ( midi2 - > pb . data ) ;
midi1 - > pb . data_msb = v > > 7 ;
midi1 - > pb . data_lsb = v & 0x7f ;
break ;
default :
return 0 ;
}
return __snd_seq_deliver_single_event ( dest , dest_port ,
( struct snd_seq_event * ) & ev_cvt ,
atomic , hop ) ;
}
/* convert UMP to a legacy ALSA seq event and deliver it */
static int cvt_ump_to_any ( struct snd_seq_client * dest ,
struct snd_seq_client_port * dest_port ,
struct snd_seq_event * event ,
unsigned char type ,
int atomic , int hop )
{
struct snd_seq_event ev_cvt [ 2 ] ; /* up to two events */
struct snd_seq_ump_event * ump_ev = ( struct snd_seq_ump_event * ) event ;
/* use the second event as a temp buffer for saving stack usage */
unsigned char * sysex_buf = ( unsigned char * ) ( ev_cvt + 1 ) ;
unsigned char flags = event - > flags & ~ SNDRV_SEQ_EVENT_UMP ;
int i , len , err ;
ev_cvt [ 0 ] = ev_cvt [ 1 ] = * event ;
ev_cvt [ 0 ] . flags = flags ;
ev_cvt [ 1 ] . flags = flags ;
switch ( type ) {
case UMP_MSG_TYPE_SYSTEM :
len = cvt_ump_system_to_event ( ( union snd_ump_midi1_msg * ) ump_ev - > ump ,
ev_cvt ) ;
break ;
case UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE :
len = cvt_ump_midi1_to_event ( ( union snd_ump_midi1_msg * ) ump_ev - > ump ,
ev_cvt ) ;
break ;
case UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE :
len = cvt_ump_midi2_to_event ( ( union snd_ump_midi2_msg * ) ump_ev - > ump ,
ev_cvt ) ;
break ;
case UMP_MSG_TYPE_DATA :
len = cvt_ump_sysex7_to_event ( ump_ev - > ump , sysex_buf , ev_cvt ) ;
break ;
default :
return 0 ;
}
for ( i = 0 ; i < len ; i + + ) {
err = __snd_seq_deliver_single_event ( dest , dest_port ,
& ev_cvt [ i ] , atomic , hop ) ;
if ( err < 0 )
return err ;
}
return 0 ;
}
/* Replace UMP group field with the destination and deliver */
static int deliver_with_group_convert ( struct snd_seq_client * dest ,
struct snd_seq_client_port * dest_port ,
struct snd_seq_ump_event * ump_ev ,
int atomic , int hop )
{
struct snd_seq_ump_event ev = * ump_ev ;
/* rewrite the group to the destination port */
ev . ump [ 0 ] & = ~ ( 0xfU < < 24 ) ;
/* fill with the new group; the dest_port->ump_group field is 1-based */
ev . ump [ 0 ] | = ( ( dest_port - > ump_group - 1 ) < < 24 ) ;
return __snd_seq_deliver_single_event ( dest , dest_port ,
( struct snd_seq_event * ) & ev ,
atomic , hop ) ;
}
2023-05-23 10:53:57 +03:00
/* apply the UMP event filter; return true to skip the event */
static bool ump_event_filtered ( struct snd_seq_client * dest ,
const struct snd_seq_ump_event * ev )
{
unsigned char group ;
group = ump_message_group ( ev - > ump [ 0 ] ) ;
2023-06-12 11:10:49 +03:00
if ( ump_is_groupless_msg ( ump_message_type ( ev - > ump [ 0 ] ) ) )
return dest - > group_filter & ( 1U < < 0 ) ;
2023-05-23 10:53:57 +03:00
/* check the bitmap for 1-based group number */
return dest - > group_filter & ( 1U < < ( group + 1 ) ) ;
}
2023-05-23 10:53:51 +03:00
/* Convert from UMP packet and deliver */
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 )
{
struct snd_seq_ump_event * ump_ev = ( struct snd_seq_ump_event * ) event ;
unsigned char type ;
if ( snd_seq_ev_is_variable ( event ) )
return 0 ; // skip, no variable event for UMP, so far
2023-05-23 10:53:57 +03:00
if ( ump_event_filtered ( dest , ump_ev ) )
return 0 ; // skip if group filter is set and matching
2023-05-23 10:53:51 +03:00
type = ump_message_type ( ump_ev - > ump [ 0 ] ) ;
if ( snd_seq_client_is_ump ( dest ) ) {
if ( snd_seq_client_is_midi2 ( dest ) & &
type = = UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE )
return cvt_ump_midi1_to_midi2 ( dest , dest_port ,
event , atomic , hop ) ;
else if ( ! snd_seq_client_is_midi2 ( dest ) & &
type = = UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE )
return cvt_ump_midi2_to_midi1 ( dest , dest_port ,
event , atomic , hop ) ;
/* non-EP port and different group is set? */
if ( dest_port - > ump_group & &
2023-06-12 11:10:49 +03:00
! ump_is_groupless_msg ( type ) & &
2023-05-23 10:53:51 +03:00
ump_message_group ( * ump_ev - > ump ) + 1 ! = dest_port - > ump_group )
return deliver_with_group_convert ( dest , dest_port ,
ump_ev , atomic , hop ) ;
/* copy as-is */
return __snd_seq_deliver_single_event ( dest , dest_port ,
event , atomic , hop ) ;
}
return cvt_ump_to_any ( dest , dest_port , event , type , atomic , hop ) ;
}
/*
* MIDI1 sequencer event - > UMP conversion
*/
/* Conversion to UMP MIDI 1.0 */
/* convert note on/off event to MIDI 1.0 UMP */
static int note_ev_to_ump_midi1 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi1_msg * data ,
unsigned char status )
{
if ( ! event - > data . note . velocity )
status = UMP_MSG_STATUS_NOTE_OFF ;
data - > note . status = status ;
data - > note . channel = event - > data . note . channel & 0x0f ;
data - > note . velocity = event - > data . note . velocity & 0x7f ;
data - > note . note = event - > data . note . note & 0x7f ;
return 1 ;
}
/* convert CC event to MIDI 1.0 UMP */
static int cc_ev_to_ump_midi1 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi1_msg * data ,
unsigned char status )
{
data - > cc . status = status ;
data - > cc . channel = event - > data . control . channel & 0x0f ;
data - > cc . index = event - > data . control . param ;
data - > cc . data = event - > data . control . value ;
return 1 ;
}
/* convert one-parameter control event to MIDI 1.0 UMP */
static int ctrl_ev_to_ump_midi1 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi1_msg * data ,
unsigned char status )
{
data - > caf . status = status ;
data - > caf . channel = event - > data . control . channel & 0x0f ;
data - > caf . data = event - > data . control . value & 0x7f ;
return 1 ;
}
/* convert pitchbend event to MIDI 1.0 UMP */
static int pitchbend_ev_to_ump_midi1 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi1_msg * data ,
unsigned char status )
{
int val = event - > data . control . value + 8192 ;
val = clamp ( val , 0 , 0x3fff ) ;
data - > pb . status = status ;
data - > pb . channel = event - > data . control . channel & 0x0f ;
data - > pb . data_msb = ( val > > 7 ) & 0x7f ;
data - > pb . data_lsb = val & 0x7f ;
return 1 ;
}
/* convert 14bit control event to MIDI 1.0 UMP; split to two events */
static int ctrl14_ev_to_ump_midi1 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi1_msg * data ,
unsigned char status )
{
data - > cc . status = UMP_MSG_STATUS_CC ;
data - > cc . channel = event - > data . control . channel & 0x0f ;
data - > cc . index = event - > data . control . param & 0x7f ;
if ( event - > data . control . param < 0x20 ) {
data - > cc . data = ( event - > data . control . value > > 7 ) & 0x7f ;
data [ 1 ] = data [ 0 ] ;
data [ 1 ] . cc . index = event - > data . control . param | 0x20 ;
data [ 1 ] . cc . data = event - > data . control . value & 0x7f ;
return 2 ;
}
data - > cc . data = event - > data . control . value & 0x7f ;
return 1 ;
}
/* convert RPN/NRPN event to MIDI 1.0 UMP; split to four events */
static int rpn_ev_to_ump_midi1 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi1_msg * data ,
unsigned char status )
{
bool is_rpn = ( status = = UMP_MSG_STATUS_RPN ) ;
data - > cc . status = UMP_MSG_STATUS_CC ;
data - > cc . channel = event - > data . control . channel & 0x0f ;
data [ 1 ] = data [ 2 ] = data [ 3 ] = data [ 0 ] ;
data [ 0 ] . cc . index = is_rpn ? UMP_CC_RPN_MSB : UMP_CC_NRPN_MSB ;
data [ 0 ] . cc . data = ( event - > data . control . param > > 7 ) & 0x7f ;
data [ 1 ] . cc . index = is_rpn ? UMP_CC_RPN_LSB : UMP_CC_NRPN_LSB ;
data [ 1 ] . cc . data = event - > data . control . param & 0x7f ;
data [ 2 ] . cc . index = UMP_CC_DATA ;
data [ 2 ] . cc . data = ( event - > data . control . value > > 7 ) & 0x7f ;
data [ 3 ] . cc . index = UMP_CC_DATA_LSB ;
data [ 3 ] . cc . data = event - > data . control . value & 0x7f ;
return 4 ;
}
/* convert system / RT message to UMP */
static int system_ev_to_ump_midi1 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi1_msg * data ,
unsigned char status )
{
data - > system . status = status ;
return 1 ;
}
/* convert system / RT message with 1 parameter to UMP */
static int system_1p_ev_to_ump_midi1 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi1_msg * data ,
unsigned char status )
{
data - > system . status = status ;
data - > system . parm1 = event - > data . control . value & 0x7f ;
return 1 ;
}
/* convert system / RT message with two parameters to UMP */
static int system_2p_ev_to_ump_midi1 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi1_msg * data ,
unsigned char status )
{
data - > system . status = status ;
data - > system . parm1 = ( event - > data . control . value > > 7 ) & 0x7f ;
2023-07-05 12:35:45 +03:00
data - > system . parm2 = event - > data . control . value & 0x7f ;
2023-05-23 10:53:51 +03:00
return 1 ;
}
/* Conversion to UMP MIDI 2.0 */
/* convert note on/off event to MIDI 2.0 UMP */
static int note_ev_to_ump_midi2 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status )
{
if ( ! event - > data . note . velocity )
status = UMP_MSG_STATUS_NOTE_OFF ;
data - > note . status = status ;
data - > note . channel = event - > data . note . channel & 0x0f ;
data - > note . note = event - > data . note . note & 0x7f ;
data - > note . velocity = upscale_7_to_16bit ( event - > data . note . velocity & 0x7f ) ;
return 1 ;
}
/* convert PAF event to MIDI 2.0 UMP */
static int paf_ev_to_ump_midi2 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status )
{
data - > paf . status = status ;
data - > paf . channel = event - > data . note . channel & 0x0f ;
data - > paf . note = event - > data . note . note & 0x7f ;
data - > paf . data = upscale_7_to_32bit ( event - > data . note . velocity & 0x7f ) ;
return 1 ;
}
/* set up the MIDI2 RPN/NRPN packet data from the parsed info */
static void fill_rpn ( struct snd_seq_ump_midi2_bank * cc ,
union snd_ump_midi2_msg * data )
{
if ( cc - > rpn_set ) {
data - > rpn . status = UMP_MSG_STATUS_RPN ;
data - > rpn . bank = cc - > cc_rpn_msb ;
data - > rpn . index = cc - > cc_rpn_lsb ;
cc - > rpn_set = 0 ;
cc - > cc_rpn_msb = cc - > cc_rpn_lsb = 0 ;
} else {
data - > rpn . status = UMP_MSG_STATUS_NRPN ;
data - > rpn . bank = cc - > cc_nrpn_msb ;
data - > rpn . index = cc - > cc_nrpn_lsb ;
cc - > nrpn_set = 0 ;
cc - > cc_nrpn_msb = cc - > cc_nrpn_lsb = 0 ;
}
data - > rpn . data = upscale_14_to_32bit ( ( cc - > cc_data_msb < < 7 ) |
cc - > cc_data_lsb ) ;
cc - > cc_data_msb = cc - > cc_data_lsb = 0 ;
}
/* convert CC event to MIDI 2.0 UMP */
static int cc_ev_to_ump_midi2 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status )
{
unsigned char channel = event - > data . control . channel & 0x0f ;
unsigned char index = event - > data . control . param & 0x7f ;
unsigned char val = event - > data . control . value & 0x7f ;
struct snd_seq_ump_midi2_bank * cc = & dest_port - > midi2_bank [ channel ] ;
/* process special CC's (bank/rpn/nrpn) */
switch ( index ) {
case UMP_CC_RPN_MSB :
cc - > rpn_set = 1 ;
cc - > cc_rpn_msb = val ;
return 0 ; // skip
case UMP_CC_RPN_LSB :
cc - > rpn_set = 1 ;
cc - > cc_rpn_lsb = val ;
return 0 ; // skip
case UMP_CC_NRPN_MSB :
cc - > nrpn_set = 1 ;
cc - > cc_nrpn_msb = val ;
return 0 ; // skip
case UMP_CC_NRPN_LSB :
cc - > nrpn_set = 1 ;
cc - > cc_nrpn_lsb = val ;
return 0 ; // skip
case UMP_CC_DATA :
cc - > cc_data_msb = val ;
return 0 ; // skip
case UMP_CC_BANK_SELECT :
cc - > bank_set = 1 ;
cc - > cc_bank_msb = val ;
return 0 ; // skip
case UMP_CC_BANK_SELECT_LSB :
cc - > bank_set = 1 ;
cc - > cc_bank_lsb = val ;
return 0 ; // skip
case UMP_CC_DATA_LSB :
cc - > cc_data_lsb = val ;
if ( ! ( cc - > rpn_set | | cc - > nrpn_set ) )
return 0 ; // skip
fill_rpn ( cc , data ) ;
return 1 ;
}
data - > cc . status = status ;
data - > cc . channel = channel ;
data - > cc . index = index ;
data - > cc . data = upscale_7_to_32bit ( event - > data . control . value & 0x7f ) ;
return 1 ;
}
/* convert one-parameter control event to MIDI 2.0 UMP */
static int ctrl_ev_to_ump_midi2 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status )
{
data - > caf . status = status ;
data - > caf . channel = event - > data . control . channel & 0x0f ;
data - > caf . data = upscale_7_to_32bit ( event - > data . control . value & 0x7f ) ;
return 1 ;
}
/* convert program change event to MIDI 2.0 UMP */
static int pgm_ev_to_ump_midi2 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status )
{
unsigned char channel = event - > data . control . channel & 0x0f ;
struct snd_seq_ump_midi2_bank * cc = & dest_port - > midi2_bank [ channel ] ;
data - > pg . status = status ;
data - > pg . channel = channel ;
data - > pg . program = event - > data . control . value & 0x7f ;
if ( cc - > bank_set ) {
data - > pg . bank_valid = 1 ;
data - > pg . bank_msb = cc - > cc_bank_msb ;
data - > pg . bank_lsb = cc - > cc_bank_lsb ;
cc - > bank_set = 0 ;
cc - > cc_bank_msb = cc - > cc_bank_lsb = 0 ;
}
return 1 ;
}
/* convert pitchbend event to MIDI 2.0 UMP */
static int pitchbend_ev_to_ump_midi2 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status )
{
int val = event - > data . control . value + 8192 ;
val = clamp ( val , 0 , 0x3fff ) ;
data - > pb . status = status ;
data - > pb . channel = event - > data . control . channel & 0x0f ;
data - > pb . data = upscale_14_to_32bit ( val ) ;
return 1 ;
}
/* convert 14bit control event to MIDI 2.0 UMP; split to two events */
static int ctrl14_ev_to_ump_midi2 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status )
{
unsigned char channel = event - > data . control . channel & 0x0f ;
unsigned char index = event - > data . control . param & 0x7f ;
struct snd_seq_ump_midi2_bank * cc = & dest_port - > midi2_bank [ channel ] ;
unsigned char msb , lsb ;
msb = ( event - > data . control . value > > 7 ) & 0x7f ;
lsb = event - > data . control . value & 0x7f ;
/* process special CC's (bank/rpn/nrpn) */
switch ( index ) {
case UMP_CC_BANK_SELECT :
cc - > cc_bank_msb = msb ;
fallthrough ;
case UMP_CC_BANK_SELECT_LSB :
cc - > bank_set = 1 ;
cc - > cc_bank_lsb = lsb ;
return 0 ; // skip
case UMP_CC_RPN_MSB :
cc - > cc_rpn_msb = msb ;
fallthrough ;
case UMP_CC_RPN_LSB :
cc - > rpn_set = 1 ;
cc - > cc_rpn_lsb = lsb ;
return 0 ; // skip
case UMP_CC_NRPN_MSB :
cc - > cc_nrpn_msb = msb ;
fallthrough ;
case UMP_CC_NRPN_LSB :
cc - > nrpn_set = 1 ;
cc - > cc_nrpn_lsb = lsb ;
return 0 ; // skip
case UMP_CC_DATA :
cc - > cc_data_msb = msb ;
fallthrough ;
case UMP_CC_DATA_LSB :
cc - > cc_data_lsb = lsb ;
if ( ! ( cc - > rpn_set | | cc - > nrpn_set ) )
return 0 ; // skip
fill_rpn ( cc , data ) ;
return 1 ;
}
data - > cc . status = UMP_MSG_STATUS_CC ;
data - > cc . channel = channel ;
data - > cc . index = index ;
if ( event - > data . control . param < 0x20 ) {
data - > cc . data = upscale_7_to_32bit ( msb ) ;
data [ 1 ] = data [ 0 ] ;
data [ 1 ] . cc . index = event - > data . control . param | 0x20 ;
data [ 1 ] . cc . data = upscale_7_to_32bit ( lsb ) ;
return 2 ;
}
data - > cc . data = upscale_7_to_32bit ( lsb ) ;
return 1 ;
}
/* convert RPN/NRPN event to MIDI 2.0 UMP */
static int rpn_ev_to_ump_midi2 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status )
{
data - > rpn . status = status ;
data - > rpn . channel = event - > data . control . channel ;
data - > rpn . bank = ( event - > data . control . param > > 7 ) & 0x7f ;
data - > rpn . index = event - > data . control . param & 0x7f ;
data - > rpn . data = upscale_14_to_32bit ( event - > data . control . value & 0x3fff ) ;
return 1 ;
}
/* convert system / RT message to UMP */
static int system_ev_to_ump_midi2 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status )
{
return system_ev_to_ump_midi1 ( event , dest_port ,
( union snd_ump_midi1_msg * ) data ,
status ) ;
}
/* convert system / RT message with 1 parameter to UMP */
static int system_1p_ev_to_ump_midi2 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status )
{
return system_1p_ev_to_ump_midi1 ( event , dest_port ,
( union snd_ump_midi1_msg * ) data ,
status ) ;
}
/* convert system / RT message with two parameters to UMP */
static int system_2p_ev_to_ump_midi2 ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status )
{
return system_1p_ev_to_ump_midi1 ( event , dest_port ,
( union snd_ump_midi1_msg * ) data ,
status ) ;
}
struct seq_ev_to_ump {
int seq_type ;
unsigned char status ;
int ( * midi1_encode ) ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi1_msg * data ,
unsigned char status ) ;
int ( * midi2_encode ) ( const struct snd_seq_event * event ,
struct snd_seq_client_port * dest_port ,
union snd_ump_midi2_msg * data ,
unsigned char status ) ;
} ;
static const struct seq_ev_to_ump seq_ev_ump_encoders [ ] = {
{ SNDRV_SEQ_EVENT_NOTEON , UMP_MSG_STATUS_NOTE_ON ,
note_ev_to_ump_midi1 , note_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_NOTEOFF , UMP_MSG_STATUS_NOTE_OFF ,
note_ev_to_ump_midi1 , note_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_KEYPRESS , UMP_MSG_STATUS_POLY_PRESSURE ,
note_ev_to_ump_midi1 , paf_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_CONTROLLER , UMP_MSG_STATUS_CC ,
cc_ev_to_ump_midi1 , cc_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_PGMCHANGE , UMP_MSG_STATUS_PROGRAM ,
ctrl_ev_to_ump_midi1 , pgm_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_CHANPRESS , UMP_MSG_STATUS_CHANNEL_PRESSURE ,
ctrl_ev_to_ump_midi1 , ctrl_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_PITCHBEND , UMP_MSG_STATUS_PITCH_BEND ,
pitchbend_ev_to_ump_midi1 , pitchbend_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_CONTROL14 , 0 ,
ctrl14_ev_to_ump_midi1 , ctrl14_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_NONREGPARAM , UMP_MSG_STATUS_NRPN ,
rpn_ev_to_ump_midi1 , rpn_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_REGPARAM , UMP_MSG_STATUS_RPN ,
rpn_ev_to_ump_midi1 , rpn_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_QFRAME , UMP_SYSTEM_STATUS_MIDI_TIME_CODE ,
system_1p_ev_to_ump_midi1 , system_1p_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_SONGPOS , UMP_SYSTEM_STATUS_SONG_POSITION ,
system_2p_ev_to_ump_midi1 , system_2p_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_SONGSEL , UMP_SYSTEM_STATUS_SONG_SELECT ,
system_1p_ev_to_ump_midi1 , system_1p_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_TUNE_REQUEST , UMP_SYSTEM_STATUS_TUNE_REQUEST ,
system_ev_to_ump_midi1 , system_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_CLOCK , UMP_SYSTEM_STATUS_TIMING_CLOCK ,
system_ev_to_ump_midi1 , system_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_START , UMP_SYSTEM_STATUS_START ,
system_ev_to_ump_midi1 , system_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_CONTINUE , UMP_SYSTEM_STATUS_CONTINUE ,
system_ev_to_ump_midi1 , system_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_STOP , UMP_SYSTEM_STATUS_STOP ,
system_ev_to_ump_midi1 , system_ev_to_ump_midi2 } ,
{ SNDRV_SEQ_EVENT_SENSING , UMP_SYSTEM_STATUS_ACTIVE_SENSING ,
system_ev_to_ump_midi1 , system_ev_to_ump_midi2 } ,
} ;
static const struct seq_ev_to_ump * find_ump_encoder ( int type )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( seq_ev_ump_encoders ) ; i + + )
if ( seq_ev_ump_encoders [ i ] . seq_type = = type )
return & seq_ev_ump_encoders [ i ] ;
return NULL ;
}
static void setup_ump_event ( struct snd_seq_ump_event * dest ,
const struct snd_seq_event * src )
{
memcpy ( dest , src , sizeof ( * src ) ) ;
dest - > type = 0 ;
dest - > flags | = SNDRV_SEQ_EVENT_UMP ;
dest - > flags & = ~ SNDRV_SEQ_EVENT_LENGTH_MASK ;
memset ( dest - > ump , 0 , sizeof ( dest - > ump ) ) ;
}
/* Convert ALSA seq event to UMP MIDI 1.0 and deliver it */
static int cvt_to_ump_midi1 ( struct snd_seq_client * dest ,
struct snd_seq_client_port * dest_port ,
struct snd_seq_event * event ,
int atomic , int hop )
{
const struct seq_ev_to_ump * encoder ;
struct snd_seq_ump_event ev_cvt ;
union snd_ump_midi1_msg data [ 4 ] ;
int i , n , err ;
encoder = find_ump_encoder ( event - > type ) ;
if ( ! encoder )
return __snd_seq_deliver_single_event ( dest , dest_port ,
event , atomic , hop ) ;
data - > raw = make_raw_ump ( dest_port , UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE ) ;
n = encoder - > midi1_encode ( event , dest_port , data , encoder - > status ) ;
if ( ! n )
return 0 ;
setup_ump_event ( & ev_cvt , event ) ;
for ( i = 0 ; i < n ; i + + ) {
ev_cvt . ump [ 0 ] = data [ i ] . raw ;
err = __snd_seq_deliver_single_event ( dest , dest_port ,
( struct snd_seq_event * ) & ev_cvt ,
atomic , hop ) ;
if ( err < 0 )
return err ;
}
return 0 ;
}
/* Convert ALSA seq event to UMP MIDI 2.0 and deliver it */
static int cvt_to_ump_midi2 ( struct snd_seq_client * dest ,
struct snd_seq_client_port * dest_port ,
struct snd_seq_event * event ,
int atomic , int hop )
{
const struct seq_ev_to_ump * encoder ;
struct snd_seq_ump_event ev_cvt ;
union snd_ump_midi2_msg data [ 2 ] ;
int i , n , err ;
encoder = find_ump_encoder ( event - > type ) ;
if ( ! encoder )
return __snd_seq_deliver_single_event ( dest , dest_port ,
event , atomic , hop ) ;
data - > raw [ 0 ] = make_raw_ump ( dest_port , UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE ) ;
data - > raw [ 1 ] = 0 ;
n = encoder - > midi2_encode ( event , dest_port , data , encoder - > status ) ;
if ( ! n )
return 0 ;
setup_ump_event ( & ev_cvt , event ) ;
for ( i = 0 ; i < n ; i + + ) {
memcpy ( ev_cvt . ump , & data [ i ] , sizeof ( data [ i ] ) ) ;
err = __snd_seq_deliver_single_event ( dest , dest_port ,
( struct snd_seq_event * ) & ev_cvt ,
atomic , hop ) ;
if ( err < 0 )
return err ;
}
return 0 ;
}
/* Fill up a sysex7 UMP from the byte stream */
static void fill_sysex7_ump ( struct snd_seq_client_port * dest_port ,
u32 * val , u8 status , u8 * buf , int len )
{
memset ( val , 0 , 8 ) ;
memcpy ( ( u8 * ) val + 2 , buf , len ) ;
# ifdef __LITTLE_ENDIAN
swab32_array ( val , 2 ) ;
# endif
val [ 0 ] | = ump_compose ( UMP_MSG_TYPE_DATA , get_ump_group ( dest_port ) ,
status , len ) ;
}
/* Convert sysex var event to UMP sysex7 packets and deliver them */
static int cvt_sysex_to_ump ( struct snd_seq_client * dest ,
struct snd_seq_client_port * dest_port ,
struct snd_seq_event * event ,
int atomic , int hop )
{
struct snd_seq_ump_event ev_cvt ;
unsigned char status ;
u8 buf [ 6 ] , * xbuf ;
int offset = 0 ;
int len , err ;
if ( ! snd_seq_ev_is_variable ( event ) )
return 0 ;
setup_ump_event ( & ev_cvt , event ) ;
for ( ; ; ) {
len = snd_seq_expand_var_event_at ( event , sizeof ( buf ) , buf , offset ) ;
if ( len < = 0 )
break ;
if ( WARN_ON ( len > 6 ) )
break ;
offset + = len ;
xbuf = buf ;
if ( * xbuf = = UMP_MIDI1_MSG_SYSEX_START ) {
status = UMP_SYSEX_STATUS_START ;
xbuf + + ;
len - - ;
if ( len > 0 & & xbuf [ len - 1 ] = = UMP_MIDI1_MSG_SYSEX_END ) {
status = UMP_SYSEX_STATUS_SINGLE ;
len - - ;
}
} else {
if ( xbuf [ len - 1 ] = = UMP_MIDI1_MSG_SYSEX_END ) {
status = UMP_SYSEX_STATUS_END ;
len - - ;
} else {
status = UMP_SYSEX_STATUS_CONTINUE ;
}
}
fill_sysex7_ump ( dest_port , ev_cvt . ump , status , xbuf , len ) ;
err = __snd_seq_deliver_single_event ( dest , dest_port ,
( struct snd_seq_event * ) & ev_cvt ,
atomic , hop ) ;
if ( err < 0 )
return err ;
}
return 0 ;
}
/* Convert to UMP packet and deliver */
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 )
{
if ( event - > type = = SNDRV_SEQ_EVENT_SYSEX )
return cvt_sysex_to_ump ( dest , dest_port , event , atomic , hop ) ;
else if ( snd_seq_client_is_midi2 ( dest ) )
return cvt_to_ump_midi2 ( dest , dest_port , event , atomic , hop ) ;
else
return cvt_to_ump_midi1 ( dest , dest_port , event , atomic , hop ) ;
}