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

📄 emu8000_callback.c

📁 linux-2.6.15.6
💻 C
字号:
/* *  synth callback routines for the emu8000 (AWE32/64) * *  Copyright (C) 1999 Steve Ratcliffe *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de> * *   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 "emu8000_local.h"#include <sound/asoundef.h>/* * prototypes */static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port);static int start_voice(snd_emux_voice_t *vp);static void trigger_voice(snd_emux_voice_t *vp);static void release_voice(snd_emux_voice_t *vp);static void update_voice(snd_emux_voice_t *vp, int update);static void reset_voice(snd_emux_t *emu, int ch);static void terminate_voice(snd_emux_voice_t *vp);static void sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset);#ifdef CONFIG_SND_SEQUENCER_OSSstatic int oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2);#endifstatic int load_fx(snd_emux_t *emu, int type, int mode, const void __user *buf, long len);static void set_pitch(emu8000_t *hw, snd_emux_voice_t *vp);static void set_volume(emu8000_t *hw, snd_emux_voice_t *vp);static void set_pan(emu8000_t *hw, snd_emux_voice_t *vp);static void set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp);static void set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp);static void set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp);static void set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp);static void snd_emu8000_tweak_voice(emu8000_t *emu, int ch);/* * 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)/* * set up operators */static snd_emux_operators_t emu8000_ops = {	.owner =	THIS_MODULE,	.get_voice =	get_voice,	.prepare =	start_voice,	.trigger =	trigger_voice,	.release =	release_voice,	.update =	update_voice,	.terminate =	terminate_voice,	.reset =	reset_voice,	.sample_new =	snd_emu8000_sample_new,	.sample_free =	snd_emu8000_sample_free,	.sample_reset = snd_emu8000_sample_reset,	.load_fx =	load_fx,	.sysex =	sysex,#ifdef CONFIG_SND_SEQUENCER_OSS	.oss_ioctl =	oss_ioctl,#endif};voidsnd_emu8000_ops_setup(emu8000_t *hw){	hw->emu->ops = emu8000_ops;}/* * Terminate a voice */static voidrelease_voice(snd_emux_voice_t *vp){	int dcysusv;	emu8000_t *hw;	hw = vp->hw;	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;	EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv);	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease;	EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv);}/* */static voidterminate_voice(snd_emux_voice_t *vp){	emu8000_t *hw; 	hw = vp->hw;	EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F);}/* */static voidupdate_voice(snd_emux_voice_t *vp, int update){	emu8000_t *hw;	hw = vp->hw;	if (update & SNDRV_EMUX_UPDATE_VOLUME)		set_volume(hw, vp);	if (update & SNDRV_EMUX_UPDATE_PITCH)		set_pitch(hw, vp);	if ((update & SNDRV_EMUX_UPDATE_PAN) &&	    vp->port->ctrls[EMUX_MD_REALTIME_PAN])		set_pan(hw, vp);	if (update & SNDRV_EMUX_UPDATE_FMMOD)		set_fmmod(hw, vp);	if (update & SNDRV_EMUX_UPDATE_TREMFREQ)		set_tremfreq(hw, vp);	if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)		set_fm2frq2(hw, vp);	if (update & SNDRV_EMUX_UPDATE_Q)		set_filterQ(hw, vp);}/* * Find a channel (voice) within the EMU that is not in use or at least * less in use than other channels.  Always returns a valid pointer * no matter what.  If there is a real shortage of voices then one * will be cut. Such is life. * * The channel index (vp->ch) must be initialized in this routine. * In Emu8k, it is identical with the array index. */static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port){	int  i;	snd_emux_voice_t *vp;	emu8000_t *hw;	/* what we are looking for, in order of preference */	enum {		OFF=0, RELEASED, PLAYING, END	};	/* Keeps track of what we are finding */	struct best {		unsigned int  time;		int voice;	} best[END];	struct best *bp;	hw = emu->hw;	for (i = 0; i < END; i++) {		best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;		best[i].voice = -1;	}	/*	 * Go through them all and get a best one to use.	 */	for (i = 0; i < emu->max_voices; i++) {		int state, val;		vp = &emu->voices[i];		state = vp->state;		if (state == SNDRV_EMUX_ST_OFF)			bp = best + OFF;		else if (state == SNDRV_EMUX_ST_RELEASED ||			 state == SNDRV_EMUX_ST_PENDING) {			bp = best + RELEASED;			val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff;			if (! val)				bp = best + OFF;		}		else if (state & SNDRV_EMUX_ST_ON)			bp = best + PLAYING;		else			continue;		/* check if sample is finished playing (non-looping only) */		if (state != SNDRV_EMUX_ST_OFF &&		    (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {			val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;			if (val >= vp->reg.loopstart)				bp = best + OFF;		}		if (vp->time < bp->time) {			bp->time = vp->time;			bp->voice = i;		}	}	for (i = 0; i < END; i++) {		if (best[i].voice >= 0) {			vp = &emu->voices[best[i].voice];			vp->ch = best[i].voice;			return vp;		}	}	/* not found */	return NULL;}/* */static intstart_voice(snd_emux_voice_t *vp){	unsigned int temp;	int ch;	int addr;	snd_midi_channel_t *chan;	emu8000_t *hw;	hw = vp->hw;	ch = vp->ch;	chan = vp->chan;	/* channel to be silent and idle */	EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080);	EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF);	EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF);	EMU8000_PTRX_WRITE(hw, ch, 0);	EMU8000_CPF_WRITE(hw, ch, 0);	/* set pitch offset */	set_pitch(hw, vp);	/* set envelope parameters */	EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay);	EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld);	EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus);	EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay);	EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld);	/* decay/sustain parameter for volume envelope is used	   for triggerg the voice */	/* cutoff and volume */	set_volume(hw, vp);	/* modulation envelope heights */	EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe);	/* lfo1/2 delay */	EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay);	EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay);	/* lfo1 pitch & cutoff shift */	set_fmmod(hw, vp);	/* lfo1 volume & freq */	set_tremfreq(hw, vp);	/* lfo2 pitch & freq */	set_fm2frq2(hw, vp);	/* pan & loop start */	set_pan(hw, vp);	/* chorus & loop end (chorus 8bit, MSB) */	addr = vp->reg.loopend - 1;	temp = vp->reg.parm.chorus;	temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;	LIMITMAX(temp, 255);	temp = (temp <<24) | (unsigned int)addr;	EMU8000_CSL_WRITE(hw, ch, temp);	/* Q & current address (Q 4bit value, MSB) */	addr = vp->reg.start - 1;	temp = vp->reg.parm.filterQ;	temp = (temp<<28) | (unsigned int)addr;	EMU8000_CCCA_WRITE(hw, ch, temp);	/* clear unknown registers */	EMU8000_00A0_WRITE(hw, ch, 0);	EMU8000_0080_WRITE(hw, ch, 0);	/* reset volume */	temp = vp->vtarget << 16;	EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget);	EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00);	return 0;}/* * Start envelope */static voidtrigger_voice(snd_emux_voice_t *vp){	int ch = vp->ch;	unsigned int temp;	emu8000_t *hw;	hw = vp->hw;	/* set reverb and pitch target */	temp = vp->reg.parm.reverb;	temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;	LIMITMAX(temp, 255);	temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux;	EMU8000_PTRX_WRITE(hw, ch, temp);	EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16);	EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus);}/* * reset voice parameters */static voidreset_voice(snd_emux_t *emu, int ch){	emu8000_t *hw;	hw = emu->hw;	EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);	snd_emu8000_tweak_voice(hw, ch);}/* * Set the pitch of a possibly playing note. */static voidset_pitch(emu8000_t *hw, snd_emux_voice_t *vp){	EMU8000_IP_WRITE(hw, vp->ch, vp->apitch);}/* * Set the volume of a possibly already playing note */static voidset_volume(emu8000_t *hw, snd_emux_voice_t *vp){	int  ifatn;	ifatn = (unsigned char)vp->acutoff;	ifatn = (ifatn << 8);	ifatn |= (unsigned char)vp->avol;	EMU8000_IFATN_WRITE(hw, vp->ch, ifatn);}/* * Set pan and loop start address. */static voidset_pan(emu8000_t *hw, snd_emux_voice_t *vp){	unsigned int temp;	temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1);	EMU8000_PSST_WRITE(hw, vp->ch, temp);}#define MOD_SENSE 18static voidset_fmmod(emu8000_t *hw, snd_emux_voice_t *vp){	unsigned short fmmod;	short pitch;	unsigned char cutoff;	int modulation;	pitch = (char)(vp->reg.parm.fmmod>>8);	cutoff = (vp->reg.parm.fmmod & 0xff);	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;	pitch += (MOD_SENSE * modulation) / 1200;	LIMITVALUE(pitch, -128, 127);	fmmod = ((unsigned char)pitch<<8) | cutoff;	EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod);}/* set tremolo (lfo1) volume & frequency */static voidset_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp){	EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq);}/* set lfo2 pitch & frequency */static voidset_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp){	unsigned short fm2frq2;	short pitch;	unsigned char freq;	int modulation;	pitch = (char)(vp->reg.parm.fm2frq2>>8);	freq = vp->reg.parm.fm2frq2 & 0xff;	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;	pitch += (MOD_SENSE * modulation) / 1200;	LIMITVALUE(pitch, -128, 127);	fm2frq2 = ((unsigned char)pitch<<8) | freq;	EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2);}/* set filterQ */static voidset_filterQ(emu8000_t *hw, snd_emux_voice_t *vp){	unsigned int addr;	addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;	addr |= (vp->reg.parm.filterQ << 28);	EMU8000_CCCA_WRITE(hw, vp->ch, addr);}/* * set the envelope & LFO parameters to the default values */static voidsnd_emu8000_tweak_voice(emu8000_t *emu, int i){	/* set all mod/vol envelope shape to minimum */	EMU8000_ENVVOL_WRITE(emu, i, 0x8000);	EMU8000_ENVVAL_WRITE(emu, i, 0x8000);	EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F);	EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F);	EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F);	EMU8000_PEFE_WRITE(emu, i, 0);  /* mod envelope height to zero */	EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */	EMU8000_LFO2VAL_WRITE(emu, i, 0x8000);	EMU8000_IP_WRITE(emu, i, 0xE000);	/* no pitch shift */	EMU8000_IFATN_WRITE(emu, i, 0xFF00);	/* volume to minimum */	EMU8000_FMMOD_WRITE(emu, i, 0);	EMU8000_TREMFRQ_WRITE(emu, i, 0);	EMU8000_FM2FRQ2_WRITE(emu, i, 0);}/* * sysex callback */static voidsysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset){	emu8000_t *hw;	hw = emu->hw;	switch (parsed) {	case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE:		hw->chorus_mode = chset->gs_chorus_mode;		snd_emu8000_update_chorus_mode(hw);		break;	case SNDRV_MIDI_SYSEX_GS_REVERB_MODE:		hw->reverb_mode = chset->gs_reverb_mode;		snd_emu8000_update_reverb_mode(hw);		break;	}}#ifdef CONFIG_SND_SEQUENCER_OSS/* * OSS ioctl callback */static intoss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2){	emu8000_t *hw;	hw = emu->hw;	switch (cmd) {	case _EMUX_OSS_REVERB_MODE:		hw->reverb_mode = p1;		snd_emu8000_update_reverb_mode(hw);		break;	case _EMUX_OSS_CHORUS_MODE:		hw->chorus_mode = p1;		snd_emu8000_update_chorus_mode(hw);		break;	case _EMUX_OSS_INITIALIZE_CHIP:		/* snd_emu8000_init(hw); */ /*ignored*/		break;	case _EMUX_OSS_EQUALIZER:		hw->bass_level = p1;		hw->treble_level = p2;		snd_emu8000_update_equalizer(hw);		break;	}	return 0;}#endif/* * additional patch keys */#define SNDRV_EMU8000_LOAD_CHORUS_FX	0x10	/* optarg=mode */#define SNDRV_EMU8000_LOAD_REVERB_FX	0x11	/* optarg=mode *//* * callback routine */static intload_fx(snd_emux_t *emu, int type, int mode, const void __user *buf, long len){	emu8000_t *hw;	hw = emu->hw;	/* skip header */	buf += 16;	len -= 16;	switch (type) {	case SNDRV_EMU8000_LOAD_CHORUS_FX:		return snd_emu8000_load_chorus_fx(hw, mode, buf, len);	case SNDRV_EMU8000_LOAD_REVERB_FX:		return snd_emu8000_load_reverb_fx(hw, mode, buf, len);	}	return -EINVAL;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -