⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 emux_synth.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  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, &note, 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 + -