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

📄 opl3_midi.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  Copyright (c) by Uros Bizjak <uros@kss-loka.si> * *  Midi synth routines for OPL2/OPL3/OPL4 FM * *   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 * */#undef DEBUG_ALLOC#undef DEBUG_MIDI#include "opl3_voice.h"#include <sound/asoundef.h>extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];extern int use_internal_drums;/* * The next table looks magical, but it certainly is not. Its values have * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception * for i=0. This log-table converts a linear volume-scaling (0..127) to a * logarithmic scaling as present in the FM-synthesizer chips. so :    Volume * 64 =  0 db = relative volume  0 and:    Volume 32 = -6 db = relative * volume -8 it was implemented as a table because it is only 128 bytes and * it saves a lot of log() calculations. (Rob Hooft <hooft@chem.ruu.nl>) */static char opl3_volume_table[128] ={	-63, -48, -40, -35, -32, -29, -27, -26,	-24, -23, -21, -20, -19, -18, -18, -17,	-16, -15, -15, -14, -13, -13, -12, -12,	-11, -11, -10, -10, -10, -9, -9, -8,	-8, -8, -7, -7, -7, -6, -6, -6,	-5, -5, -5, -5, -4, -4, -4, -4,	-3, -3, -3, -3, -2, -2, -2, -2,	-2, -1, -1, -1, -1, 0, 0, 0,	0, 0, 0, 1, 1, 1, 1, 1,	1, 2, 2, 2, 2, 2, 2, 2,	3, 3, 3, 3, 3, 3, 3, 4,	4, 4, 4, 4, 4, 4, 4, 5,	5, 5, 5, 5, 5, 5, 5, 5,	6, 6, 6, 6, 6, 6, 6, 6,	6, 7, 7, 7, 7, 7, 7, 7,	7, 7, 7, 8, 8, 8, 8, 8};void snd_opl3_calc_volume(unsigned char *volbyte, int vel,			  snd_midi_channel_t *chan){	int oldvol, newvol, n;	int volume;	volume = (vel * chan->gm_volume * chan->gm_expression) / (127*127);	if (volume > 127)		volume = 127;	oldvol = OPL3_TOTAL_LEVEL_MASK - (*volbyte & OPL3_TOTAL_LEVEL_MASK);	newvol = opl3_volume_table[volume] + oldvol;	if (newvol > OPL3_TOTAL_LEVEL_MASK)		newvol = OPL3_TOTAL_LEVEL_MASK;	else if (newvol < 0)		newvol = 0;	n = OPL3_TOTAL_LEVEL_MASK - (newvol & OPL3_TOTAL_LEVEL_MASK);	*volbyte = (*volbyte & OPL3_KSL_MASK) | (n & OPL3_TOTAL_LEVEL_MASK);}/* * Converts the note frequency to block and fnum values for the FM chip */static short opl3_note_table[16] ={	305, 323,	/* for pitch bending, -2 semitones */	343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647,	686, 726	/* for pitch bending, +2 semitones */};static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum,				int note, snd_midi_channel_t *chan){	int block = ((note / 12) & 0x07) - 1;	int idx = (note % 12) + 2;	int freq;	if (chan->midi_pitchbend) {		int pitchbend = chan->midi_pitchbend;		int segment;		if (pitchbend > 0x1FFF)			pitchbend = 0x1FFF;		segment = pitchbend / 0x1000;		freq = opl3_note_table[idx+segment];		freq += ((opl3_note_table[idx+segment+1] - freq) *			 (pitchbend % 0x1000)) / 0x1000;	} else {		freq = opl3_note_table[idx];	}	*fnum = (unsigned char) freq;	*blocknum = ((freq >> 8) & OPL3_FNUM_HIGH_MASK) |		((block << 2) & OPL3_BLOCKNUM_MASK);}#ifdef DEBUG_ALLOCstatic void debug_alloc(opl3_t *opl3, char *s, int voice) {	int i;	char *str = "x.24";	printk("time %.5i: %s [%.2i]: ", opl3->use_time, s, voice);	for (i = 0; i < opl3->max_voices; i++)		printk("%c", *(str + opl3->voices[i].state + 1));	printk("\n");}#endif/* * Get a FM voice (channel) to play a note on. */static int opl3_get_voice(opl3_t *opl3, int instr_4op,			  snd_midi_channel_t *chan) {	int chan_4op_1;		/* first voice for 4op instrument */	int chan_4op_2;		/* second voice for 4op instrument */	snd_opl3_voice_t *vp, *vp2;	unsigned int voice_time;	int i;#ifdef DEBUG_ALLOC	char *alloc_type[3] = { "FREE     ", "CHEAP    ", "EXPENSIVE" };#endif	/* This is our "allocation cost" table */	enum {		FREE = 0, CHEAP, EXPENSIVE, END	};	/* Keeps track of what we are finding */	struct best {		unsigned int time;		int voice;	} best[END];	struct best *bp;	for (i = 0; i < END; i++) {		best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;		best[i].voice = -1;	}	/* Look through all the channels for the most suitable. */	for (i = 0; i < opl3->max_voices; i++) {		vp = &opl3->voices[i];		if (vp->state == SNDRV_OPL3_ST_NOT_AVAIL)		  /* skip unavailable channels, allocated by		     drum voices or by bounded 4op voices) */			continue;		voice_time = vp->time;		bp = best;		chan_4op_1 = ((i < 3) || (i > 8 && i < 12));		chan_4op_2 = ((i > 2 && i < 6) || (i > 11 && i < 15));		if (instr_4op) {			/* allocate 4op voice */			/* skip channels unavailable to 4op instrument */			if (!chan_4op_1)				continue;			if (vp->state)				/* kill one voice, CHEAP */				bp++;			/* get state of bounded 2op channel			   to be allocated for 4op instrument */			vp2 = &opl3->voices[i + 3];			if (vp2->state == SNDRV_OPL3_ST_ON_2OP) {				/* kill two voices, EXPENSIVE */				bp++;				voice_time = (voice_time > vp->time) ?					voice_time : vp->time;			}		} else {			/* allocate 2op voice */			if ((chan_4op_1) || (chan_4op_2))				/* use bounded channels for 2op, CHEAP */				bp++;			else if (vp->state)				/* kill one voice on 2op channel, CHEAP */				bp++;			/* raise kill cost to EXPENSIVE for all channels */			if (vp->state)				bp++;		}		if (voice_time < bp->time) {			bp->time = voice_time;			bp->voice = i;		}	}	for (i = 0; i < END; i++) {		if (best[i].voice >= 0) {#ifdef DEBUG_ALLOC			printk("%s %iop allocation on voice %i\n",			       alloc_type[i], instr_4op ? 4 : 2,			       best[i].voice);#endif			return best[i].voice;		}	}	/* not found */	return -1;}/* ------------------------------ *//* * System timer interrupt function */void snd_opl3_timer_func(unsigned long data){	opl3_t *opl3 = (opl3_t *)data;	int again = 0;	int i;	spin_lock(&opl3->sys_timer_lock);	for (i = 0; i < opl3->max_voices; i++) {		snd_opl3_voice_t *vp = &opl3->voices[i];		if (vp->state > 0 && vp->note_off_check) {			if (vp->note_off == jiffies)				snd_opl3_note_off(opl3, vp->note, 0, vp->chan);			else				again++;		}	}	if (again) {		opl3->tlist.expires = jiffies + 1;	/* invoke again */		add_timer(&opl3->tlist);	} else {		opl3->sys_timer_status = 0;	}	spin_unlock(&opl3->sys_timer_lock);}/* * Start system timer */static void snd_opl3_start_timer(opl3_t *opl3){	unsigned long flags;	spin_lock_irqsave(&opl3->sys_timer_lock, flags);	if (! opl3->sys_timer_status) {		opl3->tlist.expires = jiffies + 1;		add_timer(&opl3->tlist);		opl3->sys_timer_status = 1;	}	spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);}/* ------------------------------ */static int snd_opl3_oss_map[MAX_OPL3_VOICES] = {	0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17, 3, 4 ,5, 12, 13, 14};/* * Start a note. */void snd_opl3_note_on(void *p, int note, int vel, snd_midi_channel_t *chan){	opl3_t *opl3;	snd_seq_instr_t wanted;	snd_seq_kinstr_t *kinstr;	int instr_4op;	int voice;	snd_opl3_voice_t *vp, *vp2;	unsigned short connect_mask;	unsigned char connection;	unsigned char vol_op[4];	int extra_prg = 0;	unsigned short reg_side;	unsigned char op_offset;	unsigned char voice_offset;	unsigned short opl3_reg;	unsigned char reg_val;	int key = note;	unsigned char fnum, blocknum;	int i;	fm_instrument_t *fm;	unsigned long flags;	opl3 = p;#ifdef DEBUG_MIDI	snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n",		   chan->number, chan->midi_program, note, vel);#endif	wanted.cluster = 0;	wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3;	/* in SYNTH mode, application takes care of voices */	/* in SEQ mode, drum voice numbers are notes on drum channel */	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {		if (chan->drum_channel) {			/* percussion instruments are located in bank 128 */			wanted.bank = 128;			wanted.prg = note;		} else {			wanted.bank = chan->gm_bank_select;			wanted.prg = chan->midi_program;		}	} else {		/* Prepare for OSS mode */		if (chan->number >= MAX_OPL3_VOICES)			return;		/* OSS instruments are located in bank 127 */		wanted.bank = 127;		wanted.prg = chan->midi_program;	}	spin_lock_irqsave(&opl3->voice_lock, flags);	if (use_internal_drums) {		snd_opl3_drum_switch(opl3, note, vel, 1, chan);		spin_unlock_irqrestore(&opl3->voice_lock, flags);		return;	} __extra_prg:	kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0);	if (kinstr == NULL) {		spin_unlock_irqrestore(&opl3->voice_lock, flags);		return;	}	fm = KINSTR_DATA(kinstr);	switch (fm->type) {	case FM_PATCH_OPL2:		instr_4op = 0;		break;	case FM_PATCH_OPL3:		if (opl3->hardware >= OPL3_HW_OPL3) {			instr_4op = 1;			break;		}	default:		snd_seq_instr_free_use(opl3->ilist, kinstr);		spin_unlock_irqrestore(&opl3->voice_lock, flags);		return;	}#ifdef DEBUG_MIDI	snd_printk("  --> OPL%i instrument: %s\n",		   instr_4op ? 3 : 2, kinstr->name);#endif	/* in SYNTH mode, application takes care of voices */	/* in SEQ mode, allocate voice on free OPL3 channel */	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {		voice = opl3_get_voice(opl3, instr_4op, chan);	} else {		/* remap OSS voice */		voice = snd_opl3_oss_map[chan->number];			}	if (voice < MAX_OPL2_VOICES) {		/* Left register block for voices 0 .. 8 */		reg_side = OPL3_LEFT;		voice_offset = voice;		connect_mask = (OPL3_LEFT_4OP_0 << voice_offset) & 0x07;	} else {		/* Right register block for voices 9 .. 17 */		reg_side = OPL3_RIGHT;		voice_offset = voice - MAX_OPL2_VOICES;		connect_mask = (OPL3_RIGHT_4OP_0 << voice_offset) & 0x38;	}	/* kill voice on channel */	vp = &opl3->voices[voice];	if (vp->state > 0) {		opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);		reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT;		opl3->command(opl3, opl3_reg, reg_val);	}	if (instr_4op) {		vp2 = &opl3->voices[voice + 3];		if (vp->state > 0) {			opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK +					       voice_offset + 3);			reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT;			opl3->command(opl3, opl3_reg, reg_val);		}	}	/* set connection register */	if (instr_4op) {		if ((opl3->connection_reg ^ connect_mask) & connect_mask) {			opl3->connection_reg |= connect_mask;			/* set connection bit */			opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT;			opl3->command(opl3, opl3_reg, opl3->connection_reg);		}	} else {		if ((opl3->connection_reg ^ ~connect_mask) & connect_mask) {			opl3->connection_reg &= ~connect_mask;			/* clear connection bit */			opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT;			opl3->command(opl3, opl3_reg, opl3->connection_reg);		}	}#ifdef DEBUG_MIDI

⌨️ 快捷键说明

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