// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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
*/
#include <linux/export.h>
#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 )
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);
/*
* Start a note .
*/
void
snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
{
struct snd_emux *emu;
int i, key, nvoices;
struct snd_emux_voice *vp;
struct snd_sf_zone *table[SNDRV_EMUX_MAX_MULTI_VOICES];
unsigned long flags;
struct snd_emux_port *port;
port = p;
if (snd_BUG_ON(!port || !chan))
return ;
emu = port->emu;
if (snd_BUG_ON(!emu || !emu->ops.get_voice || !emu->ops.trigger))
return ;
key = note; /* remember the original note */
nvoices = get_zone(emu, port, ¬e, vel, chan, table);
if (! nvoices)
return ;
/* exclusive note off */
for (i = 0 ; i < nvoices; i++) {
struct snd_sf_zone *zp = table[i];
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 */
struct snd_emux_effect_table *fx = chan->private ;
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
snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan)
{
int ch;
struct snd_emux *emu;
struct snd_emux_voice *vp;
unsigned long flags;
struct snd_emux_port *port;
port = p;
if (snd_BUG_ON(!port || !chan))
return ;
emu = port->emu;
if (snd_BUG_ON(!emu || !emu->ops.release))
return ;
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) {
mod_timer(&emu->tlist, jiffies + 1 );
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
*/
void snd_emux_timer_callback(struct timer_list *t)
{
struct snd_emux *emu = timer_container_of(emu, t, tlist);
struct snd_emux_voice *vp;
unsigned long flags;
int ch, do_again = 0 ;
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_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) {
mod_timer(&emu->tlist, jiffies + 1 );
emu->timer_active = 1 ;
} else
emu->timer_active = 0 ;
spin_unlock_irqrestore(&emu->voice_lock, flags);
}
/*
* key pressure change
*/
void
snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
{
int ch;
struct snd_emux *emu;
struct snd_emux_voice *vp;
unsigned long flags;
struct snd_emux_port *port;
port = p;
if (snd_BUG_ON(!port || !chan))
return ;
emu = port->emu;
if (snd_BUG_ON(!emu || !emu->ops.update))
return ;
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
snd_emux_update_channel(struct snd_emux_port *port, struct snd_midi_channel *chan, int update)
{
struct snd_emux *emu;
struct snd_emux_voice *vp;
int i;
unsigned long flags;
if (! update)
return ;
emu = port->emu;
if (snd_BUG_ON(!emu || !emu->ops.update))
return ;
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
snd_emux_update_port(struct snd_emux_port *port, int update)
{
struct snd_emux *emu;
struct snd_emux_voice *vp;
int i;
unsigned long flags;
if (! update)
return ;
emu = port->emu;
if (snd_BUG_ON(!emu || !emu->ops.update))
return ;
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);
}
/*
* Deal with a controller type event . This includes all types of
* control events , not just the midi controllers
*/
void
snd_emux_control(void *p, int type, struct snd_midi_channel *chan)
{
struct snd_emux_port *port;
port = p;
if (snd_BUG_ON(!port || !chan))
return ;
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 */
if (chan->control[type] >= 64 )
snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, -160 ,
EMUX_FX_FLAG_ADD);
else
snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, 0 ,
EMUX_FX_FLAG_OFF);
#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
terminate_note1(struct snd_emux *emu, int note, struct snd_midi_channel *chan, int free)
{
int i;
struct snd_emux_voice *vp;
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
snd_emux_terminate_note(void *p, int note, struct snd_midi_channel *chan)
{
struct snd_emux *emu;
struct snd_emux_port *port;
port = p;
if (snd_BUG_ON(!port || !chan))
return ;
emu = port->emu;
if (snd_BUG_ON(!emu || !emu->ops.terminate))
return ;
terminate_note1(emu, note, chan, 1 );
}
/*
* Terminate all the notes
*/
void
snd_emux_terminate_all(struct snd_emux *emu)
{
int i;
struct snd_emux_voice *vp;
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);
}
EXPORT_SYMBOL(snd_emux_terminate_all);
/*
* Terminate all voices associated with the given port
*/
void
snd_emux_sounds_off_all(struct snd_emux_port *port)
{
int i;
struct snd_emux *emu;
struct snd_emux_voice *vp;
unsigned long flags;
if (snd_BUG_ON(!port))
return ;
emu = port->emu;
if (snd_BUG_ON(!emu || !emu->ops.terminate))
return ;
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
exclusive_note_off(struct snd_emux *emu, struct snd_emux_port *port, int exclass)
{
struct snd_emux_voice *vp;
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
terminate_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int free)
{
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
update_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int update)
{
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 */
static const unsigned short voltarget[16 ] = {
0 xEAC0, 0 xE0C8, 0 xD740, 0 xCE20, 0 xC560, 0 xBD08, 0 xB500, 0 xAD58,
0 xA5F8, 0 x9EF0, 0 x9830, 0 x91C0, 0 x8B90, 0 x85A8, 0 x8000, 0 x7A90
};
#endif
#define LO_BYTE(v) ((v) & 0 xff)
#define HI_BYTE(v) (((v) >> 8 ) & 0 xff)
/*
* Sets up the voice structure by calculating some values that
* will be needed later .
*/
static void
setup_voice(struct snd_emux_voice *vp)
{
struct soundfont_voice_parm *parm;
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) >= 0 x80 && parm->moddelay >= 0 x8000) {
parm->moddelay = 0 xbfff;
pitch = (HI_BYTE(parm->pefe) << 4 ) + vp->apitch;
if (pitch > 0 xffff)
pitch = 0 xffff;
/* 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 != 0 xffff) {
vp->ptarget = 1 << (pitch >> 12 );
if (pitch & 0 x800) vp->ptarget += (vp->ptarget*0 x102e)/0 x2710;
if (pitch & 0 x400) vp->ptarget += (vp->ptarget*0 x764)/0 x2710;
if (pitch & 0 x200) vp->ptarget += (vp->ptarget*0 x389)/0 x2710;
vp->ptarget += (vp->ptarget >> 1 );
if (vp->ptarget > 0 xffff) vp->ptarget = 0 xffff;
} else
vp->ptarget = 0 xffff;
if (LO_BYTE(parm->modatkhld) >= 0 x80) {
parm->modatkhld &= ~0 xff;
parm->modatkhld |= 0 x7f;
}
/* compute volume target and correct volume parameters */
vp->vtarget = 0 ;
#if 0 /* FIXME: this leads to some clicks.. */
if (LO_BYTE(parm->volatkhld) >= 0 x80 && parm->voldelay >= 0 x8000) {
parm->voldelay = 0 xbfff;
vp->vtarget = voltarget[vp->avol % 0 x10] >> (vp->avol >> 4 );
}
#endif
if (LO_BYTE(parm->volatkhld) >= 0 x80) {
parm->volatkhld &= ~0 xff;
parm->volatkhld |= 0 x7f;
}
}
/*
* calculate pitch parameter
*/
static const unsigned char pan_volumes[256 ] = {
0 x00,0 x03,0 x06,0 x09,0 x0c,0 x0f,0 x12,0 x14,0 x17,0 x1a,0 x1d,0 x20,0 x22,0 x25,0 x28,0 x2a,
0 x2d,0 x30,0 x32,0 x35,0 x37,0 x3a,0 x3c,0 x3f,0 x41,0 x44,0 x46,0 x49,0 x4b,0 x4d,0 x50,0 x52,
0 x54,0 x57,0 x59,0 x5b,0 x5d,0 x60,0 x62,0 x64,0 x66,0 x68,0 x6a,0 x6c,0 x6f,0 x71,0 x73,0 x75,
0 x77,0 x79,0 x7b,0 x7c,0 x7e,0 x80,0 x82,0 x84,0 x86,0 x88,0 x89,0 x8b,0 x8d,0 x8f,0 x90,0 x92,
0 x94,0 x96,0 x97,0 x99,0 x9a,0 x9c,0 x9e,0 x9f,0 xa1,0 xa2,0 xa4,0 xa5,0 xa7,0 xa8,0 xaa,0 xab,
0 xad,0 xae,0 xaf,0 xb1,0 xb2,0 xb3,0 xb5,0 xb6,0 xb7,0 xb9,0 xba,0 xbb,0 xbc,0 xbe,0 xbf,0 xc0,
0 xc1,0 xc2,0 xc3,0 xc5,0 xc6,0 xc7,0 xc8,0 xc9,0 xca,0 xcb,0 xcc,0 xcd,0 xce,0 xcf,0 xd0,0 xd1,
0 xd2,0 xd3,0 xd4,0 xd5,0 xd6,0 xd7,0 xd7,0 xd8,0 xd9,0 xda,0 xdb,0 xdc,0 xdc,0 xdd,0 xde,0 xdf,
0 xdf,0 xe0,0 xe1,0 xe2,0 xe2,0 xe3,0 xe4,0 xe4,0 xe5,0 xe6,0 xe6,0 xe7,0 xe8,0 xe8,0 xe9,0 xe9,
0 xea,0 xeb,0 xeb,0 xec,0 xec,0 xed,0 xed,0 xee,0 xee,0 xef,0 xef,0 xf0,0 xf0,0 xf1,0 xf1,0 xf1,
0 xf2,0 xf2,0 xf3,0 xf3,0 xf3,0 xf4,0 xf4,0 xf5,0 xf5,0 xf5,0 xf6,0 xf6,0 xf6,0 xf7,0 xf7,0 xf7,
0 xf7,0 xf8,0 xf8,0 xf8,0 xf9,0 xf9,0 xf9,0 xf9,0 xf9,0 xfa,0 xfa,0 xfa,0 xfa,0 xfb,0 xfb,0 xfb,
0 xfb,0 xfb,0 xfc,0 xfc,0 xfc,0 xfc,0 xfc,0 xfc,0 xfc,0 xfd,0 xfd,0 xfd,0 xfd,0 xfd,0 xfd,0 xfd,
0 xfd,0 xfe,0 xfe,0 xfe,0 xfe,0 xfe,0 xfe,0 xfe,0 xfe,0 xfe,0 xfe,0 xfe,0 xfe,0 xfe,0 xfe,0 xfe,
0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,
0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,0 xff,
};
static int
calc_pan(struct snd_emux_voice *vp)
{
struct snd_midi_channel *chan = vp->chan;
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 = 0 xff;
else
vp->aaux = (-pan) & 0 xff;
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 */
static const unsigned char voltab1[128 ] = {
0 x63, 0 x63, 0 x63, 0 x63, 0 x63, 0 x63, 0 x63, 0 x63, 0 x63, 0 x63,
0 x63, 0 x2b, 0 x29, 0 x28, 0 x27, 0 x26, 0 x25, 0 x24, 0 x23, 0 x22,
0 x21, 0 x20, 0 x1f, 0 x1e, 0 x1e, 0 x1d, 0 x1c, 0 x1b, 0 x1b, 0 x1a,
0 x19, 0 x19, 0 x18, 0 x17, 0 x17, 0 x16, 0 x16, 0 x15, 0 x15, 0 x14,
0 x14, 0 x13, 0 x13, 0 x13, 0 x12, 0 x12, 0 x11, 0 x11, 0 x11, 0 x10,
0 x10, 0 x10, 0 x0f, 0 x0f, 0 x0f, 0 x0e, 0 x0e, 0 x0e, 0 x0e, 0 x0d,
0 x0d, 0 x0d, 0 x0c, 0 x0c, 0 x0c, 0 x0c, 0 x0c, 0 x0b, 0 x0b, 0 x0b,
0 x0b, 0 x0a, 0 x0a, 0 x0a, 0 x0a, 0 x09, 0 x09, 0 x09, 0 x09, 0 x09,
0 x08, 0 x08, 0 x08, 0 x08, 0 x08, 0 x07, 0 x07, 0 x07, 0 x07, 0 x06,
0 x06, 0 x06, 0 x06, 0 x06, 0 x05, 0 x05, 0 x05, 0 x05, 0 x05, 0 x04,
0 x04, 0 x04, 0 x04, 0 x04, 0 x03, 0 x03, 0 x03, 0 x03, 0 x03, 0 x02,
0 x02, 0 x02, 0 x02, 0 x02, 0 x02, 0 x01, 0 x01, 0 x01, 0 x01, 0 x01,
0 x01, 0 x01, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00
};
static const unsigned char voltab2[128 ] = {
0 x32, 0 x31, 0 x30, 0 x2f, 0 x2e, 0 x2d, 0 x2c, 0 x2b, 0 x2a, 0 x2a,
0 x29, 0 x28, 0 x27, 0 x26, 0 x25, 0 x24, 0 x24, 0 x23, 0 x22, 0 x21,
0 x21, 0 x20, 0 x1f, 0 x1e, 0 x1e, 0 x1d, 0 x1c, 0 x1c, 0 x1b, 0 x1a,
0 x1a, 0 x19, 0 x19, 0 x18, 0 x18, 0 x17, 0 x16, 0 x16, 0 x15, 0 x15,
0 x14, 0 x14, 0 x13, 0 x13, 0 x13, 0 x12, 0 x12, 0 x11, 0 x11, 0 x10,
0 x10, 0 x10, 0 x0f, 0 x0f, 0 x0f, 0 x0e, 0 x0e, 0 x0e, 0 x0d, 0 x0d,
0 x0d, 0 x0c, 0 x0c, 0 x0c, 0 x0b, 0 x0b, 0 x0b, 0 x0b, 0 x0a, 0 x0a,
0 x0a, 0 x0a, 0 x09, 0 x09, 0 x09, 0 x09, 0 x09, 0 x08, 0 x08, 0 x08,
0 x08, 0 x08, 0 x07, 0 x07, 0 x07, 0 x07, 0 x07, 0 x06, 0 x06, 0 x06,
0 x06, 0 x06, 0 x06, 0 x05, 0 x05, 0 x05, 0 x05, 0 x05, 0 x05, 0 x05,
0 x04, 0 x04, 0 x04, 0 x04, 0 x04, 0 x04, 0 x03, 0 x03, 0 x03, 0 x03,
0 x03, 0 x03, 0 x02, 0 x02, 0 x02, 0 x02, 0 x02, 0 x02, 0 x01, 0 x01,
0 x01, 0 x01, 0 x01, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00
};
static const unsigned char expressiontab[128 ] = {
0 x7f, 0 x6c, 0 x62, 0 x5a, 0 x54, 0 x50, 0 x4b, 0 x48, 0 x45, 0 x42,
0 x40, 0 x3d, 0 x3b, 0 x39, 0 x38, 0 x36, 0 x34, 0 x33, 0 x31, 0 x30,
0 x2f, 0 x2d, 0 x2c, 0 x2b, 0 x2a, 0 x29, 0 x28, 0 x27, 0 x26, 0 x25,
0 x24, 0 x24, 0 x23, 0 x22, 0 x21, 0 x21, 0 x20, 0 x1f, 0 x1e, 0 x1e,
0 x1d, 0 x1d, 0 x1c, 0 x1b, 0 x1b, 0 x1a, 0 x1a, 0 x19, 0 x18, 0 x18,
0 x17, 0 x17, 0 x16, 0 x16, 0 x15, 0 x15, 0 x15, 0 x14, 0 x14, 0 x13,
0 x13, 0 x12, 0 x12, 0 x11, 0 x11, 0 x11, 0 x10, 0 x10, 0 x0f, 0 x0f,
0 x0f, 0 x0e, 0 x0e, 0 x0e, 0 x0d, 0 x0d, 0 x0d, 0 x0c, 0 x0c, 0 x0c,
0 x0b, 0 x0b, 0 x0b, 0 x0a, 0 x0a, 0 x0a, 0 x09, 0 x09, 0 x09, 0 x09,
0 x08, 0 x08, 0 x08, 0 x07, 0 x07, 0 x07, 0 x07, 0 x06, 0 x06, 0 x06,
0 x06, 0 x05, 0 x05, 0 x05, 0 x04, 0 x04, 0 x04, 0 x04, 0 x04, 0 x03,
0 x03, 0 x03, 0 x03, 0 x02, 0 x02, 0 x02, 0 x02, 0 x01, 0 x01, 0 x01,
0 x01, 0 x01, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00
};
/*
* Magic to calculate the volume ( actually attenuation ) from all the
* voice and channels parameters .
*/
static int
calc_volume(struct snd_emux_voice *vp)
{
int vol;
int main_vol, expression_vol, master_vol;
struct snd_midi_channel *chan = vp->chan;
struct snd_emux_port *port = vp->port;
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 += ((0 x100 - 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 ) {
struct snd_emux_effect_table *fx = chan->private ;
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) < 0 x7d) {
int atten;
if (vp->velocity < 70 )
atten = 70 ;
else
atten = vp->velocity;
vp->acutoff = (atten * vp->reg.parm.cutoff + 0 xa0) >> 7 ;
} else {
vp->acutoff = vp->reg.parm.cutoff;
}
return 1 ; /* value changed */
}
/*
* calculate pitch offset
*
* 0 xE000 is no pitch offset at 44100 Hz sample .
* Every 4096 is one octave .
*/
static int
calc_pitch(struct snd_emux_voice *vp)
{
struct snd_midi_channel *chan = vp->chan;
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 ) {
struct snd_emux_effect_table *fx = chan->private ;
if (fx->flag[EMUX_FX_INIT_PITCH])
offset += fx->val[EMUX_FX_INIT_PITCH];
}
#endif
/* 0xe000: root pitch */
offset += 0 xe000 + vp->reg.rate_offset;
if (vp->emu->ops.get_pitch_shift)
offset += vp->emu->ops.get_pitch_shift(vp->emu);
LIMITVALUE(offset, 0 , 0 xffff);
if (offset == vp->apitch)
return 0 ; /* unchanged */
vp->apitch = offset;
return 1 ; /* value changed */
}
/*
* Get the bank number assigned to the channel
*/
static int
get_bank(struct snd_emux_port *port, struct snd_midi_channel *chan)
{
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
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)
{
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
snd_emux_init_voices(struct snd_emux *emu)
{
struct snd_emux_voice *vp;
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);
}
/*
*/
void snd_emux_lock_voice(struct snd_emux *emu, int voice)
{
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
dev_warn(emu->card->dev,
"invalid voice for lock %d (state = %x)\n" ,
voice, emu->voices[voice].state);
spin_unlock_irqrestore(&emu->voice_lock, flags);
}
EXPORT_SYMBOL(snd_emux_lock_voice);
/*
*/
void snd_emux_unlock_voice(struct snd_emux *emu, int voice)
{
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
dev_warn(emu->card->dev,
"invalid voice for unlock %d (state = %x)\n" ,
voice, emu->voices[voice].state);
spin_unlock_irqrestore(&emu->voice_lock, flags);
}
EXPORT_SYMBOL(snd_emux_unlock_voice);
Messung V0.5 in Prozent C=94 H=94 G=93
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland