2019-05-27 08:55:05 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-16 15:20:36 -07:00
/*
* Midi synth routines for the Emu8k / Emu10k1
*
* Copyright ( C ) 1999 Steve Ratcliffe
* Copyright ( c ) 1999 - 2000 Takashi Iwai < tiwai @ suse . de >
*
* Contains code based on awe_wave . c by Takashi Iwai
*/
2011-09-22 09:34:58 -04:00
# include <linux/export.h>
2005-04-16 15:20:36 -07:00
# include "emux_voice.h"
# include <sound/asoundef.h>
/*
* Prototypes
*/
/*
* Ensure a value is between two points
* macro evaluates its args more than once , so changed to upper - case .
*/
# define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
# define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
2005-11-17 14:24:47 +01:00
static int get_zone ( struct snd_emux * emu , struct snd_emux_port * port ,
int * notep , int vel , struct snd_midi_channel * chan ,
struct snd_sf_zone * * table ) ;
static int get_bank ( struct snd_emux_port * port , struct snd_midi_channel * chan ) ;
static void terminate_note1 ( struct snd_emux * emu , int note ,
struct snd_midi_channel * chan , int free ) ;
static void exclusive_note_off ( struct snd_emux * emu , struct snd_emux_port * port ,
int exclass ) ;
static void terminate_voice ( struct snd_emux * emu , struct snd_emux_voice * vp , int free ) ;
static void update_voice ( struct snd_emux * emu , struct snd_emux_voice * vp , int update ) ;
static void setup_voice ( struct snd_emux_voice * vp ) ;
static int calc_pan ( struct snd_emux_voice * vp ) ;
static int calc_volume ( struct snd_emux_voice * vp ) ;
static int calc_pitch ( struct snd_emux_voice * vp ) ;
2005-04-16 15:20:36 -07:00
/*
* Start a note .
*/
void
2005-11-17 14:24:47 +01:00
snd_emux_note_on ( void * p , int note , int vel , struct snd_midi_channel * chan )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:24:47 +01:00
struct snd_emux * emu ;
2005-04-16 15:20:36 -07:00
int i , key , nvoices ;
2005-11-17 14:24:47 +01:00
struct snd_emux_voice * vp ;
struct snd_sf_zone * table [ SNDRV_EMUX_MAX_MULTI_VOICES ] ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
2005-11-17 14:24:47 +01:00
struct snd_emux_port * port ;
2005-04-16 15:20:36 -07:00
port = p ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! port | | ! chan ) )
return ;
2005-04-16 15:20:36 -07:00
emu = port - > emu ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! emu | | ! emu - > ops . get_voice | | ! emu - > ops . trigger ) )
return ;
2005-04-16 15:20:36 -07:00
key = note ; /* remember the original note */
nvoices = get_zone ( emu , port , & note , vel , chan , table ) ;
if ( ! nvoices )
return ;
/* exclusive note off */
for ( i = 0 ; i < nvoices ; i + + ) {
2005-11-17 14:24:47 +01:00
struct snd_sf_zone * zp = table [ i ] ;
2005-04-16 15:20:36 -07:00
if ( zp & & zp - > v . exclusiveClass )
exclusive_note_off ( emu , port , zp - > v . exclusiveClass ) ;
}
#if 0 // seems not necessary
/* Turn off the same note on the same channel. */
terminate_note1 ( emu , key , chan , 0 ) ;
# endif
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
for ( i = 0 ; i < nvoices ; i + + ) {
/* set up each voice parameter */
/* at this stage, we don't trigger the voice yet. */
if ( table [ i ] = = NULL )
continue ;
vp = emu - > ops . get_voice ( emu , port ) ;
if ( vp = = NULL | | vp - > ch < 0 )
continue ;
if ( STATE_IS_PLAYING ( vp - > state ) )
emu - > ops . terminate ( vp ) ;
vp - > time = emu - > use_time + + ;
vp - > chan = chan ;
vp - > port = port ;
vp - > key = key ;
vp - > note = note ;
vp - > velocity = vel ;
vp - > zone = table [ i ] ;
if ( vp - > zone - > sample )
vp - > block = vp - > zone - > sample - > block ;
else
vp - > block = NULL ;
setup_voice ( vp ) ;
vp - > state = SNDRV_EMUX_ST_STANDBY ;
if ( emu - > ops . prepare ) {
vp - > state = SNDRV_EMUX_ST_OFF ;
if ( emu - > ops . prepare ( vp ) > = 0 )
vp - > state = SNDRV_EMUX_ST_STANDBY ;
}
}
/* start envelope now */
for ( i = 0 ; i < emu - > max_voices ; i + + ) {
vp = & emu - > voices [ i ] ;
if ( vp - > state = = SNDRV_EMUX_ST_STANDBY & &
vp - > chan = = chan ) {
emu - > ops . trigger ( vp ) ;
vp - > state = SNDRV_EMUX_ST_ON ;
vp - > ontime = jiffies ; /* remember the trigger timing */
}
}
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
# ifdef SNDRV_EMUX_USE_RAW_EFFECT
if ( port - > port_mode = = SNDRV_EMUX_PORT_MODE_OSS_SYNTH ) {
/* clear voice position for the next note on this channel */
2005-11-17 14:24:47 +01:00
struct snd_emux_effect_table * fx = chan - > private ;
2005-04-16 15:20:36 -07:00
if ( fx ) {
fx - > flag [ EMUX_FX_SAMPLE_START ] = 0 ;
fx - > flag [ EMUX_FX_COARSE_SAMPLE_START ] = 0 ;
}
}
# endif
}
/*
* Release a note in response to a midi note off .
*/
void
2005-11-17 14:24:47 +01:00
snd_emux_note_off ( void * p , int note , int vel , struct snd_midi_channel * chan )
2005-04-16 15:20:36 -07:00
{
int ch ;
2005-11-17 14:24:47 +01:00
struct snd_emux * emu ;
struct snd_emux_voice * vp ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
2005-11-17 14:24:47 +01:00
struct snd_emux_port * port ;
2005-04-16 15:20:36 -07:00
port = p ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! port | | ! chan ) )
return ;
2005-04-16 15:20:36 -07:00
emu = port - > emu ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! emu | | ! emu - > ops . release ) )
return ;
2005-04-16 15:20:36 -07:00
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
for ( ch = 0 ; ch < emu - > max_voices ; ch + + ) {
vp = & emu - > voices [ ch ] ;
if ( STATE_IS_PLAYING ( vp - > state ) & &
vp - > chan = = chan & & vp - > key = = note ) {
vp - > state = SNDRV_EMUX_ST_RELEASED ;
if ( vp - > ontime = = jiffies ) {
/* if note-off is sent too shortly after
* note - on , emuX engine cannot produce the sound
* correctly . so we ' ll release this note
* a bit later via timer callback .
*/
vp - > state = SNDRV_EMUX_ST_PENDING ;
if ( ! emu - > timer_active ) {
2015-01-19 11:41:13 +01:00
mod_timer ( & emu - > tlist , jiffies + 1 ) ;
2005-04-16 15:20:36 -07:00
emu - > timer_active = 1 ;
}
} else
/* ok now release the note */
emu - > ops . release ( vp ) ;
}
}
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
}
/*
* timer callback
*
* release the pending note - offs
*/
2017-10-24 08:34:40 -07:00
void snd_emux_timer_callback ( struct timer_list * t )
2005-04-16 15:20:36 -07:00
{
2017-10-24 08:34:40 -07:00
struct snd_emux * emu = from_timer ( emu , t , tlist ) ;
2005-11-17 14:24:47 +01:00
struct snd_emux_voice * vp ;
2005-11-18 18:52:14 +01:00
unsigned long flags ;
2005-04-16 15:20:36 -07:00
int ch , do_again = 0 ;
2005-11-18 18:52:14 +01:00
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
2005-04-16 15:20:36 -07:00
for ( ch = 0 ; ch < emu - > max_voices ; ch + + ) {
vp = & emu - > voices [ ch ] ;
if ( vp - > state = = SNDRV_EMUX_ST_PENDING ) {
if ( vp - > ontime = = jiffies )
do_again + + ; /* release this at the next interrupt */
else {
emu - > ops . release ( vp ) ;
vp - > state = SNDRV_EMUX_ST_RELEASED ;
}
}
}
if ( do_again ) {
2015-01-19 11:41:13 +01:00
mod_timer ( & emu - > tlist , jiffies + 1 ) ;
2005-04-16 15:20:36 -07:00
emu - > timer_active = 1 ;
} else
emu - > timer_active = 0 ;
2005-11-18 18:52:14 +01:00
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
2005-04-16 15:20:36 -07:00
}
/*
* key pressure change
*/
void
2005-11-17 14:24:47 +01:00
snd_emux_key_press ( void * p , int note , int vel , struct snd_midi_channel * chan )
2005-04-16 15:20:36 -07:00
{
int ch ;
2005-11-17 14:24:47 +01:00
struct snd_emux * emu ;
struct snd_emux_voice * vp ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
2005-11-17 14:24:47 +01:00
struct snd_emux_port * port ;
2005-04-16 15:20:36 -07:00
port = p ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! port | | ! chan ) )
return ;
2005-04-16 15:20:36 -07:00
emu = port - > emu ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! emu | | ! emu - > ops . update ) )
return ;
2005-04-16 15:20:36 -07:00
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
for ( ch = 0 ; ch < emu - > max_voices ; ch + + ) {
vp = & emu - > voices [ ch ] ;
if ( vp - > state = = SNDRV_EMUX_ST_ON & &
vp - > chan = = chan & & vp - > key = = note ) {
vp - > velocity = vel ;
update_voice ( emu , vp , SNDRV_EMUX_UPDATE_VOLUME ) ;
}
}
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
}
/*
* Modulate the voices which belong to the channel
*/
void
2005-11-17 14:24:47 +01:00
snd_emux_update_channel ( struct snd_emux_port * port , struct snd_midi_channel * chan , int update )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:24:47 +01:00
struct snd_emux * emu ;
struct snd_emux_voice * vp ;
2005-04-16 15:20:36 -07:00
int i ;
unsigned long flags ;
if ( ! update )
return ;
emu = port - > emu ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! emu | | ! emu - > ops . update ) )
return ;
2005-04-16 15:20:36 -07:00
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
for ( i = 0 ; i < emu - > max_voices ; i + + ) {
vp = & emu - > voices [ i ] ;
if ( vp - > chan = = chan )
update_voice ( emu , vp , update ) ;
}
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
}
/*
* Modulate all the voices which belong to the port .
*/
void
2005-11-17 14:24:47 +01:00
snd_emux_update_port ( struct snd_emux_port * port , int update )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:24:47 +01:00
struct snd_emux * emu ;
struct snd_emux_voice * vp ;
2005-04-16 15:20:36 -07:00
int i ;
unsigned long flags ;
if ( ! update )
return ;
emu = port - > emu ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! emu | | ! emu - > ops . update ) )
return ;
2005-04-16 15:20:36 -07:00
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
for ( i = 0 ; i < emu - > max_voices ; i + + ) {
vp = & emu - > voices [ i ] ;
if ( vp - > port = = port )
update_voice ( emu , vp , update ) ;
}
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
}
/*
2007-10-19 23:10:43 +02:00
* Deal with a controller type event . This includes all types of
2005-04-16 15:20:36 -07:00
* control events , not just the midi controllers
*/
void
2005-11-17 14:24:47 +01:00
snd_emux_control ( void * p , int type , struct snd_midi_channel * chan )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:24:47 +01:00
struct snd_emux_port * port ;
2005-04-16 15:20:36 -07:00
port = p ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! port | | ! chan ) )
return ;
2005-04-16 15:20:36 -07:00
switch ( type ) {
case MIDI_CTL_MSB_MAIN_VOLUME :
case MIDI_CTL_MSB_EXPRESSION :
snd_emux_update_channel ( port , chan , SNDRV_EMUX_UPDATE_VOLUME ) ;
break ;
case MIDI_CTL_MSB_PAN :
snd_emux_update_channel ( port , chan , SNDRV_EMUX_UPDATE_PAN ) ;
break ;
case MIDI_CTL_SOFT_PEDAL :
# ifdef SNDRV_EMUX_USE_RAW_EFFECT
/* FIXME: this is an emulation */
2008-05-09 13:43:09 +02:00
if ( chan - > control [ type ] > = 64 )
snd_emux_send_effect ( port , chan , EMUX_FX_CUTOFF , - 160 ,
2005-04-16 15:20:36 -07:00
EMUX_FX_FLAG_ADD ) ;
2008-05-09 13:43:09 +02:00
else
snd_emux_send_effect ( port , chan , EMUX_FX_CUTOFF , 0 ,
EMUX_FX_FLAG_OFF ) ;
2005-04-16 15:20:36 -07:00
# endif
break ;
case MIDI_CTL_PITCHBEND :
snd_emux_update_channel ( port , chan , SNDRV_EMUX_UPDATE_PITCH ) ;
break ;
case MIDI_CTL_MSB_MODWHEEL :
case MIDI_CTL_CHAN_PRESSURE :
snd_emux_update_channel ( port , chan ,
SNDRV_EMUX_UPDATE_FMMOD |
SNDRV_EMUX_UPDATE_FM2FRQ2 ) ;
break ;
}
if ( port - > chset . midi_mode = = SNDRV_MIDI_MODE_XG ) {
snd_emux_xg_control ( port , chan , type ) ;
}
}
/*
* terminate note - if free flag is true , free the terminated voice
*/
static void
2005-11-17 14:24:47 +01:00
terminate_note1 ( struct snd_emux * emu , int note , struct snd_midi_channel * chan , int free )
2005-04-16 15:20:36 -07:00
{
int i ;
2005-11-17 14:24:47 +01:00
struct snd_emux_voice * vp ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
for ( i = 0 ; i < emu - > max_voices ; i + + ) {
vp = & emu - > voices [ i ] ;
if ( STATE_IS_PLAYING ( vp - > state ) & & vp - > chan = = chan & &
vp - > key = = note )
terminate_voice ( emu , vp , free ) ;
}
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
}
/*
* terminate note - exported for midi emulation
*/
void
2005-11-17 14:24:47 +01:00
snd_emux_terminate_note ( void * p , int note , struct snd_midi_channel * chan )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:24:47 +01:00
struct snd_emux * emu ;
struct snd_emux_port * port ;
2005-04-16 15:20:36 -07:00
port = p ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! port | | ! chan ) )
return ;
2005-04-16 15:20:36 -07:00
emu = port - > emu ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! emu | | ! emu - > ops . terminate ) )
return ;
2005-04-16 15:20:36 -07:00
terminate_note1 ( emu , note , chan , 1 ) ;
}
/*
* Terminate all the notes
*/
void
2005-11-17 14:24:47 +01:00
snd_emux_terminate_all ( struct snd_emux * emu )
2005-04-16 15:20:36 -07:00
{
int i ;
2005-11-17 14:24:47 +01:00
struct snd_emux_voice * vp ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
for ( i = 0 ; i < emu - > max_voices ; i + + ) {
vp = & emu - > voices [ i ] ;
if ( STATE_IS_PLAYING ( vp - > state ) )
terminate_voice ( emu , vp , 0 ) ;
if ( vp - > state = = SNDRV_EMUX_ST_OFF ) {
if ( emu - > ops . free_voice )
emu - > ops . free_voice ( vp ) ;
if ( emu - > ops . reset )
emu - > ops . reset ( emu , i ) ;
}
vp - > time = 0 ;
}
/* initialize allocation time */
emu - > use_time = 0 ;
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
}
2006-04-28 15:13:40 +02:00
EXPORT_SYMBOL ( snd_emux_terminate_all ) ;
2005-04-16 15:20:36 -07:00
/*
* Terminate all voices associated with the given port
*/
void
2005-11-17 14:24:47 +01:00
snd_emux_sounds_off_all ( struct snd_emux_port * port )
2005-04-16 15:20:36 -07:00
{
int i ;
2005-11-17 14:24:47 +01:00
struct snd_emux * emu ;
struct snd_emux_voice * vp ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! port ) )
return ;
2005-04-16 15:20:36 -07:00
emu = port - > emu ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! emu | | ! emu - > ops . terminate ) )
return ;
2005-04-16 15:20:36 -07:00
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
for ( i = 0 ; i < emu - > max_voices ; i + + ) {
vp = & emu - > voices [ i ] ;
if ( STATE_IS_PLAYING ( vp - > state ) & &
vp - > port = = port )
terminate_voice ( emu , vp , 0 ) ;
if ( vp - > state = = SNDRV_EMUX_ST_OFF ) {
if ( emu - > ops . free_voice )
emu - > ops . free_voice ( vp ) ;
if ( emu - > ops . reset )
emu - > ops . reset ( emu , i ) ;
}
}
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
}
/*
* Terminate all voices that have the same exclusive class . This
* is mainly for drums .
*/
static void
2005-11-17 14:24:47 +01:00
exclusive_note_off ( struct snd_emux * emu , struct snd_emux_port * port , int exclass )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:24:47 +01:00
struct snd_emux_voice * vp ;
2005-04-16 15:20:36 -07:00
int i ;
unsigned long flags ;
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
for ( i = 0 ; i < emu - > max_voices ; i + + ) {
vp = & emu - > voices [ i ] ;
if ( STATE_IS_PLAYING ( vp - > state ) & & vp - > port = = port & &
vp - > reg . exclusiveClass = = exclass ) {
terminate_voice ( emu , vp , 0 ) ;
}
}
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
}
/*
* terminate a voice
* if free flag is true , call free_voice after termination
*/
static void
2005-11-17 14:24:47 +01:00
terminate_voice ( struct snd_emux * emu , struct snd_emux_voice * vp , int free )
2005-04-16 15:20:36 -07:00
{
emu - > ops . terminate ( vp ) ;
vp - > time = emu - > use_time + + ;
vp - > chan = NULL ;
vp - > port = NULL ;
vp - > zone = NULL ;
vp - > block = NULL ;
vp - > state = SNDRV_EMUX_ST_OFF ;
if ( free & & emu - > ops . free_voice )
emu - > ops . free_voice ( vp ) ;
}
/*
* Modulate the voice
*/
static void
2005-11-17 14:24:47 +01:00
update_voice ( struct snd_emux * emu , struct snd_emux_voice * vp , int update )
2005-04-16 15:20:36 -07:00
{
if ( ! STATE_IS_PLAYING ( vp - > state ) )
return ;
if ( vp - > chan = = NULL | | vp - > port = = NULL )
return ;
if ( update & SNDRV_EMUX_UPDATE_VOLUME )
calc_volume ( vp ) ;
if ( update & SNDRV_EMUX_UPDATE_PITCH )
calc_pitch ( vp ) ;
if ( update & SNDRV_EMUX_UPDATE_PAN ) {
if ( ! calc_pan ( vp ) & & ( update = = SNDRV_EMUX_UPDATE_PAN ) )
return ;
}
emu - > ops . update ( vp , update ) ;
}
#if 0 // not used
/* table for volume target calculation */
2020-01-05 15:47:36 +01:00
static const unsigned short voltarget [ 16 ] = {
2005-04-16 15:20:36 -07:00
0xEAC0 , 0xE0C8 , 0xD740 , 0xCE20 , 0xC560 , 0xBD08 , 0xB500 , 0xAD58 ,
0xA5F8 , 0x9EF0 , 0x9830 , 0x91C0 , 0x8B90 , 0x85A8 , 0x8000 , 0x7A90
} ;
# endif
# define LO_BYTE(v) ((v) & 0xff)
# define HI_BYTE(v) (((v) >> 8) & 0xff)
/*
* Sets up the voice structure by calculating some values that
* will be needed later .
*/
static void
2005-11-17 14:24:47 +01:00
setup_voice ( struct snd_emux_voice * vp )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:24:47 +01:00
struct soundfont_voice_parm * parm ;
2005-04-16 15:20:36 -07:00
int pitch ;
/* copy the original register values */
vp - > reg = vp - > zone - > v ;
# ifdef SNDRV_EMUX_USE_RAW_EFFECT
snd_emux_setup_effect ( vp ) ;
# endif
/* reset status */
vp - > apan = - 1 ;
vp - > avol = - 1 ;
vp - > apitch = - 1 ;
calc_volume ( vp ) ;
calc_pitch ( vp ) ;
calc_pan ( vp ) ;
parm = & vp - > reg . parm ;
/* compute filter target and correct modulation parameters */
if ( LO_BYTE ( parm - > modatkhld ) > = 0x80 & & parm - > moddelay > = 0x8000 ) {
parm - > moddelay = 0xbfff ;
pitch = ( HI_BYTE ( parm - > pefe ) < < 4 ) + vp - > apitch ;
if ( pitch > 0xffff )
pitch = 0xffff ;
/* calculate filter target */
vp - > ftarget = parm - > cutoff + LO_BYTE ( parm - > pefe ) ;
LIMITVALUE ( vp - > ftarget , 0 , 255 ) ;
vp - > ftarget < < = 8 ;
} else {
vp - > ftarget = parm - > cutoff ;
vp - > ftarget < < = 8 ;
pitch = vp - > apitch ;
}
/* compute pitch target */
if ( pitch ! = 0xffff ) {
vp - > ptarget = 1 < < ( pitch > > 12 ) ;
if ( pitch & 0x800 ) vp - > ptarget + = ( vp - > ptarget * 0x102e ) / 0x2710 ;
if ( pitch & 0x400 ) vp - > ptarget + = ( vp - > ptarget * 0x764 ) / 0x2710 ;
if ( pitch & 0x200 ) vp - > ptarget + = ( vp - > ptarget * 0x389 ) / 0x2710 ;
vp - > ptarget + = ( vp - > ptarget > > 1 ) ;
if ( vp - > ptarget > 0xffff ) vp - > ptarget = 0xffff ;
} else
vp - > ptarget = 0xffff ;
if ( LO_BYTE ( parm - > modatkhld ) > = 0x80 ) {
parm - > modatkhld & = ~ 0xff ;
parm - > modatkhld | = 0x7f ;
}
/* compute volume target and correct volume parameters */
vp - > vtarget = 0 ;
#if 0 /* FIXME: this leads to some clicks.. */
if ( LO_BYTE ( parm - > volatkhld ) > = 0x80 & & parm - > voldelay > = 0x8000 ) {
parm - > voldelay = 0xbfff ;
vp - > vtarget = voltarget [ vp - > avol % 0x10 ] > > ( vp - > avol > > 4 ) ;
}
# endif
if ( LO_BYTE ( parm - > volatkhld ) > = 0x80 ) {
parm - > volatkhld & = ~ 0xff ;
parm - > volatkhld | = 0x7f ;
}
}
/*
* calculate pitch parameter
*/
2020-01-05 15:47:36 +01:00
static const unsigned char pan_volumes [ 256 ] = {
2005-04-16 15:20:36 -07:00
0x00 , 0x03 , 0x06 , 0x09 , 0x0c , 0x0f , 0x12 , 0x14 , 0x17 , 0x1a , 0x1d , 0x20 , 0x22 , 0x25 , 0x28 , 0x2a ,
0x2d , 0x30 , 0x32 , 0x35 , 0x37 , 0x3a , 0x3c , 0x3f , 0x41 , 0x44 , 0x46 , 0x49 , 0x4b , 0x4d , 0x50 , 0x52 ,
0x54 , 0x57 , 0x59 , 0x5b , 0x5d , 0x60 , 0x62 , 0x64 , 0x66 , 0x68 , 0x6a , 0x6c , 0x6f , 0x71 , 0x73 , 0x75 ,
0x77 , 0x79 , 0x7b , 0x7c , 0x7e , 0x80 , 0x82 , 0x84 , 0x86 , 0x88 , 0x89 , 0x8b , 0x8d , 0x8f , 0x90 , 0x92 ,
0x94 , 0x96 , 0x97 , 0x99 , 0x9a , 0x9c , 0x9e , 0x9f , 0xa1 , 0xa2 , 0xa4 , 0xa5 , 0xa7 , 0xa8 , 0xaa , 0xab ,
0xad , 0xae , 0xaf , 0xb1 , 0xb2 , 0xb3 , 0xb5 , 0xb6 , 0xb7 , 0xb9 , 0xba , 0xbb , 0xbc , 0xbe , 0xbf , 0xc0 ,
0xc1 , 0xc2 , 0xc3 , 0xc5 , 0xc6 , 0xc7 , 0xc8 , 0xc9 , 0xca , 0xcb , 0xcc , 0xcd , 0xce , 0xcf , 0xd0 , 0xd1 ,
0xd2 , 0xd3 , 0xd4 , 0xd5 , 0xd6 , 0xd7 , 0xd7 , 0xd8 , 0xd9 , 0xda , 0xdb , 0xdc , 0xdc , 0xdd , 0xde , 0xdf ,
0xdf , 0xe0 , 0xe1 , 0xe2 , 0xe2 , 0xe3 , 0xe4 , 0xe4 , 0xe5 , 0xe6 , 0xe6 , 0xe7 , 0xe8 , 0xe8 , 0xe9 , 0xe9 ,
0xea , 0xeb , 0xeb , 0xec , 0xec , 0xed , 0xed , 0xee , 0xee , 0xef , 0xef , 0xf0 , 0xf0 , 0xf1 , 0xf1 , 0xf1 ,
0xf2 , 0xf2 , 0xf3 , 0xf3 , 0xf3 , 0xf4 , 0xf4 , 0xf5 , 0xf5 , 0xf5 , 0xf6 , 0xf6 , 0xf6 , 0xf7 , 0xf7 , 0xf7 ,
0xf7 , 0xf8 , 0xf8 , 0xf8 , 0xf9 , 0xf9 , 0xf9 , 0xf9 , 0xf9 , 0xfa , 0xfa , 0xfa , 0xfa , 0xfb , 0xfb , 0xfb ,
0xfb , 0xfb , 0xfc , 0xfc , 0xfc , 0xfc , 0xfc , 0xfc , 0xfc , 0xfd , 0xfd , 0xfd , 0xfd , 0xfd , 0xfd , 0xfd ,
0xfd , 0xfe , 0xfe , 0xfe , 0xfe , 0xfe , 0xfe , 0xfe , 0xfe , 0xfe , 0xfe , 0xfe , 0xfe , 0xfe , 0xfe , 0xfe ,
0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
} ;
static int
2005-11-17 14:24:47 +01:00
calc_pan ( struct snd_emux_voice * vp )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:24:47 +01:00
struct snd_midi_channel * chan = vp - > chan ;
2005-04-16 15:20:36 -07:00
int pan ;
/* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */
if ( vp - > reg . fixpan > 0 ) /* 0-127 */
pan = 255 - ( int ) vp - > reg . fixpan * 2 ;
else {
pan = chan - > control [ MIDI_CTL_MSB_PAN ] - 64 ;
if ( vp - > reg . pan > = 0 ) /* 0-127 */
pan + = vp - > reg . pan - 64 ;
pan = 127 - ( int ) pan * 2 ;
}
LIMITVALUE ( pan , 0 , 255 ) ;
if ( vp - > emu - > linear_panning ) {
/* assuming linear volume */
if ( pan ! = vp - > apan ) {
vp - > apan = pan ;
if ( pan = = 0 )
vp - > aaux = 0xff ;
else
vp - > aaux = ( - pan ) & 0xff ;
return 1 ;
} else
return 0 ;
} else {
/* using volume table */
if ( vp - > apan ! = ( int ) pan_volumes [ pan ] ) {
vp - > apan = pan_volumes [ pan ] ;
vp - > aaux = pan_volumes [ 255 - pan ] ;
return 1 ;
}
return 0 ;
}
}
/*
* calculate volume attenuation
*
* Voice volume is controlled by volume attenuation parameter .
* So volume becomes maximum when avol is 0 ( no attenuation ) , and
* minimum when 255 ( - 96 dB or silence ) .
*/
/* tables for volume->attenuation calculation */
2020-01-05 15:47:36 +01:00
static const unsigned char voltab1 [ 128 ] = {
2005-04-16 15:20:36 -07:00
0x63 , 0x63 , 0x63 , 0x63 , 0x63 , 0x63 , 0x63 , 0x63 , 0x63 , 0x63 ,
0x63 , 0x2b , 0x29 , 0x28 , 0x27 , 0x26 , 0x25 , 0x24 , 0x23 , 0x22 ,
0x21 , 0x20 , 0x1f , 0x1e , 0x1e , 0x1d , 0x1c , 0x1b , 0x1b , 0x1a ,
0x19 , 0x19 , 0x18 , 0x17 , 0x17 , 0x16 , 0x16 , 0x15 , 0x15 , 0x14 ,
0x14 , 0x13 , 0x13 , 0x13 , 0x12 , 0x12 , 0x11 , 0x11 , 0x11 , 0x10 ,
0x10 , 0x10 , 0x0f , 0x0f , 0x0f , 0x0e , 0x0e , 0x0e , 0x0e , 0x0d ,
0x0d , 0x0d , 0x0c , 0x0c , 0x0c , 0x0c , 0x0c , 0x0b , 0x0b , 0x0b ,
0x0b , 0x0a , 0x0a , 0x0a , 0x0a , 0x09 , 0x09 , 0x09 , 0x09 , 0x09 ,
0x08 , 0x08 , 0x08 , 0x08 , 0x08 , 0x07 , 0x07 , 0x07 , 0x07 , 0x06 ,
0x06 , 0x06 , 0x06 , 0x06 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x04 ,
0x04 , 0x04 , 0x04 , 0x04 , 0x03 , 0x03 , 0x03 , 0x03 , 0x03 , 0x02 ,
0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
2020-01-05 15:47:36 +01:00
static const unsigned char voltab2 [ 128 ] = {
2005-04-16 15:20:36 -07:00
0x32 , 0x31 , 0x30 , 0x2f , 0x2e , 0x2d , 0x2c , 0x2b , 0x2a , 0x2a ,
0x29 , 0x28 , 0x27 , 0x26 , 0x25 , 0x24 , 0x24 , 0x23 , 0x22 , 0x21 ,
0x21 , 0x20 , 0x1f , 0x1e , 0x1e , 0x1d , 0x1c , 0x1c , 0x1b , 0x1a ,
0x1a , 0x19 , 0x19 , 0x18 , 0x18 , 0x17 , 0x16 , 0x16 , 0x15 , 0x15 ,
0x14 , 0x14 , 0x13 , 0x13 , 0x13 , 0x12 , 0x12 , 0x11 , 0x11 , 0x10 ,
0x10 , 0x10 , 0x0f , 0x0f , 0x0f , 0x0e , 0x0e , 0x0e , 0x0d , 0x0d ,
0x0d , 0x0c , 0x0c , 0x0c , 0x0b , 0x0b , 0x0b , 0x0b , 0x0a , 0x0a ,
0x0a , 0x0a , 0x09 , 0x09 , 0x09 , 0x09 , 0x09 , 0x08 , 0x08 , 0x08 ,
0x08 , 0x08 , 0x07 , 0x07 , 0x07 , 0x07 , 0x07 , 0x06 , 0x06 , 0x06 ,
0x06 , 0x06 , 0x06 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 ,
0x04 , 0x04 , 0x04 , 0x04 , 0x04 , 0x04 , 0x03 , 0x03 , 0x03 , 0x03 ,
0x03 , 0x03 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
2020-01-05 15:47:36 +01:00
static const unsigned char expressiontab [ 128 ] = {
2005-04-16 15:20:36 -07:00
0x7f , 0x6c , 0x62 , 0x5a , 0x54 , 0x50 , 0x4b , 0x48 , 0x45 , 0x42 ,
0x40 , 0x3d , 0x3b , 0x39 , 0x38 , 0x36 , 0x34 , 0x33 , 0x31 , 0x30 ,
0x2f , 0x2d , 0x2c , 0x2b , 0x2a , 0x29 , 0x28 , 0x27 , 0x26 , 0x25 ,
0x24 , 0x24 , 0x23 , 0x22 , 0x21 , 0x21 , 0x20 , 0x1f , 0x1e , 0x1e ,
0x1d , 0x1d , 0x1c , 0x1b , 0x1b , 0x1a , 0x1a , 0x19 , 0x18 , 0x18 ,
0x17 , 0x17 , 0x16 , 0x16 , 0x15 , 0x15 , 0x15 , 0x14 , 0x14 , 0x13 ,
0x13 , 0x12 , 0x12 , 0x11 , 0x11 , 0x11 , 0x10 , 0x10 , 0x0f , 0x0f ,
0x0f , 0x0e , 0x0e , 0x0e , 0x0d , 0x0d , 0x0d , 0x0c , 0x0c , 0x0c ,
0x0b , 0x0b , 0x0b , 0x0a , 0x0a , 0x0a , 0x09 , 0x09 , 0x09 , 0x09 ,
0x08 , 0x08 , 0x08 , 0x07 , 0x07 , 0x07 , 0x07 , 0x06 , 0x06 , 0x06 ,
0x06 , 0x05 , 0x05 , 0x05 , 0x04 , 0x04 , 0x04 , 0x04 , 0x04 , 0x03 ,
0x03 , 0x03 , 0x03 , 0x02 , 0x02 , 0x02 , 0x02 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
/*
* Magic to calculate the volume ( actually attenuation ) from all the
* voice and channels parameters .
*/
static int
2005-11-17 14:24:47 +01:00
calc_volume ( struct snd_emux_voice * vp )
2005-04-16 15:20:36 -07:00
{
int vol ;
int main_vol , expression_vol , master_vol ;
2005-11-17 14:24:47 +01:00
struct snd_midi_channel * chan = vp - > chan ;
struct snd_emux_port * port = vp - > port ;
2005-04-16 15:20:36 -07:00
expression_vol = chan - > control [ MIDI_CTL_MSB_EXPRESSION ] ;
LIMITMAX ( vp - > velocity , 127 ) ;
LIMITVALUE ( expression_vol , 0 , 127 ) ;
if ( port - > port_mode = = SNDRV_EMUX_PORT_MODE_OSS_SYNTH ) {
/* 0 - 127 */
main_vol = chan - > control [ MIDI_CTL_MSB_MAIN_VOLUME ] ;
vol = ( vp - > velocity * main_vol * expression_vol ) / ( 127 * 127 ) ;
vol = vol * vp - > reg . amplitude / 127 ;
LIMITVALUE ( vol , 0 , 127 ) ;
/* calc to attenuation */
vol = snd_sf_vol_table [ vol ] ;
} else {
main_vol = chan - > control [ MIDI_CTL_MSB_MAIN_VOLUME ] * vp - > reg . amplitude / 127 ;
LIMITVALUE ( main_vol , 0 , 127 ) ;
vol = voltab1 [ main_vol ] + voltab2 [ vp - > velocity ] ;
vol = ( vol * 8 ) / 3 ;
vol + = vp - > reg . attenuation ;
vol + = ( ( 0x100 - vol ) * expressiontab [ expression_vol ] ) / 128 ;
}
master_vol = port - > chset . gs_master_volume ;
LIMITVALUE ( master_vol , 0 , 127 ) ;
vol + = snd_sf_vol_table [ master_vol ] ;
vol + = port - > volume_atten ;
# ifdef SNDRV_EMUX_USE_RAW_EFFECT
if ( chan - > private ) {
2005-11-17 14:24:47 +01:00
struct snd_emux_effect_table * fx = chan - > private ;
2005-04-16 15:20:36 -07:00
vol + = fx - > val [ EMUX_FX_ATTEN ] ;
}
# endif
LIMITVALUE ( vol , 0 , 255 ) ;
if ( vp - > avol = = vol )
return 0 ; /* value unchanged */
vp - > avol = vol ;
if ( ! SF_IS_DRUM_BANK ( get_bank ( port , chan ) )
& & LO_BYTE ( vp - > reg . parm . volatkhld ) < 0x7d ) {
int atten ;
if ( vp - > velocity < 70 )
atten = 70 ;
else
atten = vp - > velocity ;
vp - > acutoff = ( atten * vp - > reg . parm . cutoff + 0xa0 ) > > 7 ;
} else {
vp - > acutoff = vp - > reg . parm . cutoff ;
}
return 1 ; /* value changed */
}
/*
* calculate pitch offset
*
* 0xE000 is no pitch offset at 44100 Hz sample .
* Every 4096 is one octave .
*/
static int
2005-11-17 14:24:47 +01:00
calc_pitch ( struct snd_emux_voice * vp )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:24:47 +01:00
struct snd_midi_channel * chan = vp - > chan ;
2005-04-16 15:20:36 -07:00
int offset ;
/* calculate offset */
if ( vp - > reg . fixkey > = 0 ) {
offset = ( vp - > reg . fixkey - vp - > reg . root ) * 4096 / 12 ;
} else {
offset = ( vp - > note - vp - > reg . root ) * 4096 / 12 ;
}
offset = ( offset * vp - > reg . scaleTuning ) / 100 ;
offset + = vp - > reg . tune * 4096 / 1200 ;
if ( chan - > midi_pitchbend ! = 0 ) {
/* (128 * 8192: 1 semitone) ==> (4096: 12 semitones) */
offset + = chan - > midi_pitchbend * chan - > gm_rpn_pitch_bend_range / 3072 ;
}
/* tuning via RPN:
* coarse = - 8192 to 8192 ( 100 cent per 128 )
* fine = - 8192 to 8192 ( max = 100 cent )
*/
/* 4096 = 1200 cents in emu8000 parameter */
offset + = chan - > gm_rpn_coarse_tuning * 4096 / ( 12 * 128 ) ;
offset + = chan - > gm_rpn_fine_tuning / 24 ;
# ifdef SNDRV_EMUX_USE_RAW_EFFECT
/* add initial pitch correction */
if ( chan - > private ) {
2005-11-17 14:24:47 +01:00
struct snd_emux_effect_table * fx = chan - > private ;
2005-04-16 15:20:36 -07:00
if ( fx - > flag [ EMUX_FX_INIT_PITCH ] )
offset + = fx - > val [ EMUX_FX_INIT_PITCH ] ;
}
# endif
/* 0xe000: root pitch */
offset + = 0xe000 + vp - > reg . rate_offset ;
offset + = vp - > emu - > pitch_shift ;
LIMITVALUE ( offset , 0 , 0xffff ) ;
if ( offset = = vp - > apitch )
return 0 ; /* unchanged */
vp - > apitch = offset ;
return 1 ; /* value changed */
}
/*
* Get the bank number assigned to the channel
*/
static int
2005-11-17 14:24:47 +01:00
get_bank ( struct snd_emux_port * port , struct snd_midi_channel * chan )
2005-04-16 15:20:36 -07:00
{
int val ;
switch ( port - > chset . midi_mode ) {
case SNDRV_MIDI_MODE_XG :
val = chan - > control [ MIDI_CTL_MSB_BANK ] ;
if ( val = = 127 )
return 128 ; /* return drum bank */
return chan - > control [ MIDI_CTL_LSB_BANK ] ;
case SNDRV_MIDI_MODE_GS :
if ( chan - > drum_channel )
return 128 ;
/* ignore LSB (bank map) */
return chan - > control [ MIDI_CTL_MSB_BANK ] ;
default :
if ( chan - > drum_channel )
return 128 ;
return chan - > control [ MIDI_CTL_MSB_BANK ] ;
}
}
/* Look for the zones matching with the given note and velocity.
* The resultant zones are stored on table .
*/
static int
2005-11-17 14:24:47 +01:00
get_zone ( struct snd_emux * emu , struct snd_emux_port * port ,
int * notep , int vel , struct snd_midi_channel * chan ,
struct snd_sf_zone * * table )
2005-04-16 15:20:36 -07:00
{
int preset , bank , def_preset , def_bank ;
bank = get_bank ( port , chan ) ;
preset = chan - > midi_program ;
if ( SF_IS_DRUM_BANK ( bank ) ) {
def_preset = port - > ctrls [ EMUX_MD_DEF_DRUM ] ;
def_bank = bank ;
} else {
def_preset = preset ;
def_bank = port - > ctrls [ EMUX_MD_DEF_BANK ] ;
}
return snd_soundfont_search_zone ( emu - > sflist , notep , vel , preset , bank ,
def_preset , def_bank ,
table , SNDRV_EMUX_MAX_MULTI_VOICES ) ;
}
/*
*/
void
2005-11-17 14:24:47 +01:00
snd_emux_init_voices ( struct snd_emux * emu )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:24:47 +01:00
struct snd_emux_voice * vp ;
2005-04-16 15:20:36 -07:00
int i ;
unsigned long flags ;
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
for ( i = 0 ; i < emu - > max_voices ; i + + ) {
vp = & emu - > voices [ i ] ;
vp - > ch = - 1 ; /* not used */
vp - > state = SNDRV_EMUX_ST_OFF ;
vp - > chan = NULL ;
vp - > port = NULL ;
vp - > time = 0 ;
vp - > emu = emu ;
vp - > hw = emu - > hw ;
}
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
}
/*
*/
2005-11-17 14:24:47 +01:00
void snd_emux_lock_voice ( struct snd_emux * emu , int voice )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
if ( emu - > voices [ voice ] . state = = SNDRV_EMUX_ST_OFF )
emu - > voices [ voice ] . state = SNDRV_EMUX_ST_LOCKED ;
else
2009-02-05 16:01:46 +01:00
snd_printk ( KERN_WARNING
" invalid voice for lock %d (state = %x) \n " ,
2005-04-16 15:20:36 -07:00
voice , emu - > voices [ voice ] . state ) ;
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
}
2006-04-28 15:13:40 +02:00
EXPORT_SYMBOL ( snd_emux_lock_voice ) ;
2005-04-16 15:20:36 -07:00
/*
*/
2005-11-17 14:24:47 +01:00
void snd_emux_unlock_voice ( struct snd_emux * emu , int voice )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
if ( emu - > voices [ voice ] . state = = SNDRV_EMUX_ST_LOCKED )
emu - > voices [ voice ] . state = SNDRV_EMUX_ST_OFF ;
else
2009-02-05 16:01:46 +01:00
snd_printk ( KERN_WARNING
" invalid voice for unlock %d (state = %x) \n " ,
2005-04-16 15:20:36 -07:00
voice , emu - > voices [ voice ] . state ) ;
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
}
2006-04-28 15:13:40 +02:00
EXPORT_SYMBOL ( snd_emux_unlock_voice ) ;