📄 emux_synth.c
字号:
/* * 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 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#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(snd_emux_t *emu, snd_emux_port_t *port, int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table);static int get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan);static void terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free);static void exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass);static void terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free);static void update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update);static void setup_voice(snd_emux_voice_t *vp);static int calc_pan(snd_emux_voice_t *vp);static int calc_volume(snd_emux_voice_t *vp);static int calc_pitch(snd_emux_voice_t *vp);/* * Start a note. */voidsnd_emux_note_on(void *p, int note, int vel, snd_midi_channel_t *chan){ snd_emux_t *emu; int i, key, nvoices; snd_emux_voice_t *vp; snd_sf_zone_t *table[SNDRV_EMUX_MAX_MULTI_VOICES]; unsigned long flags; snd_emux_port_t *port; port = p; snd_assert(port != NULL && chan != NULL, return); emu = port->emu; snd_assert(emu != NULL, return); snd_assert(emu->ops.get_voice != NULL, return); snd_assert(emu->ops.trigger != NULL, 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++) { snd_sf_zone_t *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 */ snd_emux_effect_table_t *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. */voidsnd_emux_note_off(void *p, int note, int vel, snd_midi_channel_t *chan){ int ch; snd_emux_t *emu; snd_emux_voice_t *vp; unsigned long flags; snd_emux_port_t *port; port = p; snd_assert(port != NULL && chan != NULL, return); emu = port->emu; snd_assert(emu != NULL, return); snd_assert(emu->ops.release != NULL, 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) { emu->tlist.expires = jiffies + 1; add_timer(&emu->tlist); 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(unsigned long data){ snd_emux_t *emu = (snd_emux_t*) data; snd_emux_voice_t *vp; int ch, do_again = 0; spin_lock(&emu->voice_lock); 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) { emu->tlist.expires = jiffies + 1; add_timer(&emu->tlist); emu->timer_active = 1; } else emu->timer_active = 0; spin_unlock(&emu->voice_lock);}/* * key pressure change */voidsnd_emux_key_press(void *p, int note, int vel, snd_midi_channel_t *chan){ int ch; snd_emux_t *emu; snd_emux_voice_t *vp; unsigned long flags; snd_emux_port_t *port; port = p; snd_assert(port != NULL && chan != NULL, return); emu = port->emu; snd_assert(emu != NULL, return); snd_assert(emu->ops.update != NULL, 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 */voidsnd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update){ snd_emux_t *emu; snd_emux_voice_t *vp; int i; unsigned long flags; if (! update) return; emu = port->emu; snd_assert(emu != NULL, return); snd_assert(emu->ops.update != NULL, 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. */voidsnd_emux_update_port(snd_emux_port_t *port, int update){ snd_emux_t *emu; snd_emux_voice_t *vp; int i; unsigned long flags; if (! update) return; emu = port->emu; snd_assert(emu != NULL, return); snd_assert(emu->ops.update != NULL, 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 controler type event. This includes all types of * control events, not just the midi controllers */voidsnd_emux_control(void *p, int type, snd_midi_channel_t *chan){ snd_emux_port_t *port; port = p; snd_assert(port != NULL && chan != NULL, 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 */ snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, -160, EMUX_FX_FLAG_ADD);#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 voidterminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free){ int i; snd_emux_voice_t *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 */voidsnd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan){ snd_emux_t *emu; snd_emux_port_t *port; port = p; snd_assert(port != NULL && chan != NULL, return); emu = port->emu; snd_assert(emu != NULL, return); snd_assert(emu->ops.terminate != NULL, return); terminate_note1(emu, note, chan, 1);}/* * Terminate all the notes */voidsnd_emux_terminate_all(snd_emux_t *emu){ int i; snd_emux_voice_t *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);}/* * Terminate all voices associated with the given port */voidsnd_emux_sounds_off_all(snd_emux_port_t *port){ int i; snd_emux_t *emu; snd_emux_voice_t *vp; unsigned long flags; snd_assert(port != NULL, return); emu = port->emu; snd_assert(emu != NULL, return); snd_assert(emu->ops.terminate != NULL, 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 voidexclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass){ snd_emux_voice_t *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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -