/* * 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); elseif ((x) > (b)) (x) = (b); } while (0) #define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
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; unsignedlong 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);
}
/* * Modulate the voices which belong to the channel
*/ void
snd_emux_update_channel(struct snd_emux_port *port, struct snd_midi_channel *chan, intupdate)
{ struct snd_emux *emu; struct snd_emux_voice *vp; int i; unsignedlong 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; unsignedlong 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
*/ staticvoid
terminate_note1(struct snd_emux *emu, int note, struct snd_midi_channel *chan, int free)
{ int i; struct snd_emux_voice *vp; unsignedlong 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);
}
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; unsignedlong 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; unsignedlong 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.
*/ staticvoid
exclusive_note_off(struct snd_emux *emu, struct snd_emux_port *port, int exclass)
{ struct snd_emux_voice *vp; int i; unsignedlong 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
*/ staticvoid
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
*/ staticvoid
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);
}
/* * Sets up the voice structure by calculating some values that * will be needed later.
*/ staticvoid
setup_voice(struct snd_emux_voice *vp)
{ struct soundfont_voice_parm *parm; int pitch;
/* copy the original register values */
vp->reg = vp->zone->v;
/* * 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 (-96dB or silence).
*/
/* * Magic to calculate the volume (actually attenuation) from all the * voice and channels parameters.
*/ staticint
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;
/* * Get the bank number assigned to the channel
*/ staticint
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.
*/ staticint
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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.