162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Midi synth routines for OPL2/OPL3/OPL4 FM
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#undef DEBUG_ALLOC
962306a36Sopenharmony_ci#undef DEBUG_MIDI
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "opl3_voice.h"
1262306a36Sopenharmony_ci#include <sound/asoundef.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic void snd_opl3_note_off_unsafe(void *p, int note, int vel,
1562306a36Sopenharmony_ci				     struct snd_midi_channel *chan);
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci * The next table looks magical, but it certainly is not. Its values have
1862306a36Sopenharmony_ci * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
1962306a36Sopenharmony_ci * for i=0. This log-table converts a linear volume-scaling (0..127) to a
2062306a36Sopenharmony_ci * logarithmic scaling as present in the FM-synthesizer chips. so :    Volume
2162306a36Sopenharmony_ci * 64 =  0 db = relative volume  0 and:    Volume 32 = -6 db = relative
2262306a36Sopenharmony_ci * volume -8 it was implemented as a table because it is only 128 bytes and
2362306a36Sopenharmony_ci * it saves a lot of log() calculations. (Rob Hooft <hooft@chem.ruu.nl>)
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic const char opl3_volume_table[128] =
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	-63, -48, -40, -35, -32, -29, -27, -26,
2962306a36Sopenharmony_ci	-24, -23, -21, -20, -19, -18, -18, -17,
3062306a36Sopenharmony_ci	-16, -15, -15, -14, -13, -13, -12, -12,
3162306a36Sopenharmony_ci	-11, -11, -10, -10, -10, -9, -9, -8,
3262306a36Sopenharmony_ci	-8, -8, -7, -7, -7, -6, -6, -6,
3362306a36Sopenharmony_ci	-5, -5, -5, -5, -4, -4, -4, -4,
3462306a36Sopenharmony_ci	-3, -3, -3, -3, -2, -2, -2, -2,
3562306a36Sopenharmony_ci	-2, -1, -1, -1, -1, 0, 0, 0,
3662306a36Sopenharmony_ci	0, 0, 0, 1, 1, 1, 1, 1,
3762306a36Sopenharmony_ci	1, 2, 2, 2, 2, 2, 2, 2,
3862306a36Sopenharmony_ci	3, 3, 3, 3, 3, 3, 3, 4,
3962306a36Sopenharmony_ci	4, 4, 4, 4, 4, 4, 4, 5,
4062306a36Sopenharmony_ci	5, 5, 5, 5, 5, 5, 5, 5,
4162306a36Sopenharmony_ci	6, 6, 6, 6, 6, 6, 6, 6,
4262306a36Sopenharmony_ci	6, 7, 7, 7, 7, 7, 7, 7,
4362306a36Sopenharmony_ci	7, 7, 7, 8, 8, 8, 8, 8
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_civoid snd_opl3_calc_volume(unsigned char *volbyte, int vel,
4762306a36Sopenharmony_ci			  struct snd_midi_channel *chan)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	int oldvol, newvol, n;
5062306a36Sopenharmony_ci	int volume;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	volume = (vel * chan->gm_volume * chan->gm_expression) / (127*127);
5362306a36Sopenharmony_ci	if (volume > 127)
5462306a36Sopenharmony_ci		volume = 127;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	oldvol = OPL3_TOTAL_LEVEL_MASK - (*volbyte & OPL3_TOTAL_LEVEL_MASK);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	newvol = opl3_volume_table[volume] + oldvol;
5962306a36Sopenharmony_ci	if (newvol > OPL3_TOTAL_LEVEL_MASK)
6062306a36Sopenharmony_ci		newvol = OPL3_TOTAL_LEVEL_MASK;
6162306a36Sopenharmony_ci	else if (newvol < 0)
6262306a36Sopenharmony_ci		newvol = 0;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	n = OPL3_TOTAL_LEVEL_MASK - (newvol & OPL3_TOTAL_LEVEL_MASK);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	*volbyte = (*volbyte & OPL3_KSL_MASK) | (n & OPL3_TOTAL_LEVEL_MASK);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * Converts the note frequency to block and fnum values for the FM chip
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_cistatic const short opl3_note_table[16] =
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	305, 323,	/* for pitch bending, -2 semitones */
7562306a36Sopenharmony_ci	343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647,
7662306a36Sopenharmony_ci	686, 726	/* for pitch bending, +2 semitones */
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum,
8062306a36Sopenharmony_ci				int note, struct snd_midi_channel *chan)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	int block = ((note / 12) & 0x07) - 1;
8362306a36Sopenharmony_ci	int idx = (note % 12) + 2;
8462306a36Sopenharmony_ci	int freq;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (chan->midi_pitchbend) {
8762306a36Sopenharmony_ci		int pitchbend = chan->midi_pitchbend;
8862306a36Sopenharmony_ci		int segment;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci		if (pitchbend < -0x2000)
9162306a36Sopenharmony_ci			pitchbend = -0x2000;
9262306a36Sopenharmony_ci		if (pitchbend > 0x1FFF)
9362306a36Sopenharmony_ci			pitchbend = 0x1FFF;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		segment = pitchbend / 0x1000;
9662306a36Sopenharmony_ci		freq = opl3_note_table[idx+segment];
9762306a36Sopenharmony_ci		freq += ((opl3_note_table[idx+segment+1] - freq) *
9862306a36Sopenharmony_ci			 (pitchbend % 0x1000)) / 0x1000;
9962306a36Sopenharmony_ci	} else {
10062306a36Sopenharmony_ci		freq = opl3_note_table[idx];
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	*fnum = (unsigned char) freq;
10462306a36Sopenharmony_ci	*blocknum = ((freq >> 8) & OPL3_FNUM_HIGH_MASK) |
10562306a36Sopenharmony_ci		((block << 2) & OPL3_BLOCKNUM_MASK);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#ifdef DEBUG_ALLOC
11062306a36Sopenharmony_cistatic void debug_alloc(struct snd_opl3 *opl3, char *s, int voice) {
11162306a36Sopenharmony_ci	int i;
11262306a36Sopenharmony_ci	char *str = "x.24";
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	printk(KERN_DEBUG "time %.5i: %s [%.2i]: ", opl3->use_time, s, voice);
11562306a36Sopenharmony_ci	for (i = 0; i < opl3->max_voices; i++)
11662306a36Sopenharmony_ci		printk(KERN_CONT "%c", *(str + opl3->voices[i].state + 1));
11762306a36Sopenharmony_ci	printk(KERN_CONT "\n");
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci#endif
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/*
12262306a36Sopenharmony_ci * Get a FM voice (channel) to play a note on.
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_cistatic int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
12562306a36Sopenharmony_ci			  struct snd_midi_channel *chan) {
12662306a36Sopenharmony_ci	int chan_4op_1;		/* first voice for 4op instrument */
12762306a36Sopenharmony_ci	int chan_4op_2;		/* second voice for 4op instrument */
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	struct snd_opl3_voice *vp, *vp2;
13062306a36Sopenharmony_ci	unsigned int voice_time;
13162306a36Sopenharmony_ci	int i;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#ifdef DEBUG_ALLOC
13462306a36Sopenharmony_ci	char *alloc_type[3] = { "FREE     ", "CHEAP    ", "EXPENSIVE" };
13562306a36Sopenharmony_ci#endif
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* This is our "allocation cost" table */
13862306a36Sopenharmony_ci	enum {
13962306a36Sopenharmony_ci		FREE = 0, CHEAP, EXPENSIVE, END
14062306a36Sopenharmony_ci	};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* Keeps track of what we are finding */
14362306a36Sopenharmony_ci	struct best {
14462306a36Sopenharmony_ci		unsigned int time;
14562306a36Sopenharmony_ci		int voice;
14662306a36Sopenharmony_ci	} best[END];
14762306a36Sopenharmony_ci	struct best *bp;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	for (i = 0; i < END; i++) {
15062306a36Sopenharmony_ci		best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */
15162306a36Sopenharmony_ci		best[i].voice = -1;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* Look through all the channels for the most suitable. */
15562306a36Sopenharmony_ci	for (i = 0; i < opl3->max_voices; i++) {
15662306a36Sopenharmony_ci		vp = &opl3->voices[i];
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		if (vp->state == SNDRV_OPL3_ST_NOT_AVAIL)
15962306a36Sopenharmony_ci		  /* skip unavailable channels, allocated by
16062306a36Sopenharmony_ci		     drum voices or by bounded 4op voices) */
16162306a36Sopenharmony_ci			continue;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		voice_time = vp->time;
16462306a36Sopenharmony_ci		bp = best;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		chan_4op_1 = ((i < 3) || (i > 8 && i < 12));
16762306a36Sopenharmony_ci		chan_4op_2 = ((i > 2 && i < 6) || (i > 11 && i < 15));
16862306a36Sopenharmony_ci		if (instr_4op) {
16962306a36Sopenharmony_ci			/* allocate 4op voice */
17062306a36Sopenharmony_ci			/* skip channels unavailable to 4op instrument */
17162306a36Sopenharmony_ci			if (!chan_4op_1)
17262306a36Sopenharmony_ci				continue;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci			if (vp->state)
17562306a36Sopenharmony_ci				/* kill one voice, CHEAP */
17662306a36Sopenharmony_ci				bp++;
17762306a36Sopenharmony_ci			/* get state of bounded 2op channel
17862306a36Sopenharmony_ci			   to be allocated for 4op instrument */
17962306a36Sopenharmony_ci			vp2 = &opl3->voices[i + 3];
18062306a36Sopenharmony_ci			if (vp2->state == SNDRV_OPL3_ST_ON_2OP) {
18162306a36Sopenharmony_ci				/* kill two voices, EXPENSIVE */
18262306a36Sopenharmony_ci				bp++;
18362306a36Sopenharmony_ci				voice_time = max(voice_time, vp2->time);
18462306a36Sopenharmony_ci			}
18562306a36Sopenharmony_ci		} else {
18662306a36Sopenharmony_ci			/* allocate 2op voice */
18762306a36Sopenharmony_ci			if ((chan_4op_1) || (chan_4op_2))
18862306a36Sopenharmony_ci				/* use bounded channels for 2op, CHEAP */
18962306a36Sopenharmony_ci				bp++;
19062306a36Sopenharmony_ci			else if (vp->state)
19162306a36Sopenharmony_ci				/* kill one voice on 2op channel, CHEAP */
19262306a36Sopenharmony_ci				bp++;
19362306a36Sopenharmony_ci			/* raise kill cost to EXPENSIVE for all channels */
19462306a36Sopenharmony_ci			if (vp->state)
19562306a36Sopenharmony_ci				bp++;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci		if (voice_time < bp->time) {
19862306a36Sopenharmony_ci			bp->time = voice_time;
19962306a36Sopenharmony_ci			bp->voice = i;
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	for (i = 0; i < END; i++) {
20462306a36Sopenharmony_ci		if (best[i].voice >= 0) {
20562306a36Sopenharmony_ci#ifdef DEBUG_ALLOC
20662306a36Sopenharmony_ci			printk(KERN_DEBUG "%s %iop allocation on voice %i\n",
20762306a36Sopenharmony_ci			       alloc_type[i], instr_4op ? 4 : 2,
20862306a36Sopenharmony_ci			       best[i].voice);
20962306a36Sopenharmony_ci#endif
21062306a36Sopenharmony_ci			return best[i].voice;
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci	/* not found */
21462306a36Sopenharmony_ci	return -1;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/* ------------------------------ */
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/*
22062306a36Sopenharmony_ci * System timer interrupt function
22162306a36Sopenharmony_ci */
22262306a36Sopenharmony_civoid snd_opl3_timer_func(struct timer_list *t)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	struct snd_opl3 *opl3 = from_timer(opl3, t, tlist);
22662306a36Sopenharmony_ci	unsigned long flags;
22762306a36Sopenharmony_ci	int again = 0;
22862306a36Sopenharmony_ci	int i;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	spin_lock_irqsave(&opl3->voice_lock, flags);
23162306a36Sopenharmony_ci	for (i = 0; i < opl3->max_voices; i++) {
23262306a36Sopenharmony_ci		struct snd_opl3_voice *vp = &opl3->voices[i];
23362306a36Sopenharmony_ci		if (vp->state > 0 && vp->note_off_check) {
23462306a36Sopenharmony_ci			if (vp->note_off == jiffies)
23562306a36Sopenharmony_ci				snd_opl3_note_off_unsafe(opl3, vp->note, 0,
23662306a36Sopenharmony_ci							 vp->chan);
23762306a36Sopenharmony_ci			else
23862306a36Sopenharmony_ci				again++;
23962306a36Sopenharmony_ci		}
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	spin_unlock_irqrestore(&opl3->voice_lock, flags);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	spin_lock_irqsave(&opl3->sys_timer_lock, flags);
24462306a36Sopenharmony_ci	if (again)
24562306a36Sopenharmony_ci		mod_timer(&opl3->tlist, jiffies + 1);	/* invoke again */
24662306a36Sopenharmony_ci	else
24762306a36Sopenharmony_ci		opl3->sys_timer_status = 0;
24862306a36Sopenharmony_ci	spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/*
25262306a36Sopenharmony_ci * Start system timer
25362306a36Sopenharmony_ci */
25462306a36Sopenharmony_cistatic void snd_opl3_start_timer(struct snd_opl3 *opl3)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	unsigned long flags;
25762306a36Sopenharmony_ci	spin_lock_irqsave(&opl3->sys_timer_lock, flags);
25862306a36Sopenharmony_ci	if (! opl3->sys_timer_status) {
25962306a36Sopenharmony_ci		mod_timer(&opl3->tlist, jiffies + 1);
26062306a36Sopenharmony_ci		opl3->sys_timer_status = 1;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci	spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/* ------------------------------ */
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic const int snd_opl3_oss_map[MAX_OPL3_VOICES] = {
26962306a36Sopenharmony_ci	0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17, 3, 4 ,5, 12, 13, 14
27062306a36Sopenharmony_ci};
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/*
27362306a36Sopenharmony_ci * Start a note.
27462306a36Sopenharmony_ci */
27562306a36Sopenharmony_civoid snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct snd_opl3 *opl3;
27862306a36Sopenharmony_ci	int instr_4op;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	int voice;
28162306a36Sopenharmony_ci	struct snd_opl3_voice *vp, *vp2;
28262306a36Sopenharmony_ci	unsigned short connect_mask;
28362306a36Sopenharmony_ci	unsigned char connection;
28462306a36Sopenharmony_ci	unsigned char vol_op[4];
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	int extra_prg = 0;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	unsigned short reg_side;
28962306a36Sopenharmony_ci	unsigned char op_offset;
29062306a36Sopenharmony_ci	unsigned char voice_offset;
29162306a36Sopenharmony_ci	unsigned short opl3_reg;
29262306a36Sopenharmony_ci	unsigned char reg_val;
29362306a36Sopenharmony_ci	unsigned char prg, bank;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	int key = note;
29662306a36Sopenharmony_ci	unsigned char fnum, blocknum;
29762306a36Sopenharmony_ci	int i;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	struct fm_patch *patch;
30062306a36Sopenharmony_ci	struct fm_instrument *fm;
30162306a36Sopenharmony_ci	unsigned long flags;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	opl3 = p;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci#ifdef DEBUG_MIDI
30662306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "Note on, ch %i, inst %i, note %i, vel %i\n",
30762306a36Sopenharmony_ci		   chan->number, chan->midi_program, note, vel);
30862306a36Sopenharmony_ci#endif
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* in SYNTH mode, application takes care of voices */
31162306a36Sopenharmony_ci	/* in SEQ mode, drum voice numbers are notes on drum channel */
31262306a36Sopenharmony_ci	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
31362306a36Sopenharmony_ci		if (chan->drum_channel) {
31462306a36Sopenharmony_ci			/* percussion instruments are located in bank 128 */
31562306a36Sopenharmony_ci			bank = 128;
31662306a36Sopenharmony_ci			prg = note;
31762306a36Sopenharmony_ci		} else {
31862306a36Sopenharmony_ci			bank = chan->gm_bank_select;
31962306a36Sopenharmony_ci			prg = chan->midi_program;
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci	} else {
32262306a36Sopenharmony_ci		/* Prepare for OSS mode */
32362306a36Sopenharmony_ci		if (chan->number >= MAX_OPL3_VOICES)
32462306a36Sopenharmony_ci			return;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		/* OSS instruments are located in bank 127 */
32762306a36Sopenharmony_ci		bank = 127;
32862306a36Sopenharmony_ci		prg = chan->midi_program;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	spin_lock_irqsave(&opl3->voice_lock, flags);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (use_internal_drums) {
33462306a36Sopenharmony_ci		snd_opl3_drum_switch(opl3, note, vel, 1, chan);
33562306a36Sopenharmony_ci		spin_unlock_irqrestore(&opl3->voice_lock, flags);
33662306a36Sopenharmony_ci		return;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci __extra_prg:
34062306a36Sopenharmony_ci	patch = snd_opl3_find_patch(opl3, prg, bank, 0);
34162306a36Sopenharmony_ci	if (!patch) {
34262306a36Sopenharmony_ci		spin_unlock_irqrestore(&opl3->voice_lock, flags);
34362306a36Sopenharmony_ci		return;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	fm = &patch->inst;
34762306a36Sopenharmony_ci	switch (patch->type) {
34862306a36Sopenharmony_ci	case FM_PATCH_OPL2:
34962306a36Sopenharmony_ci		instr_4op = 0;
35062306a36Sopenharmony_ci		break;
35162306a36Sopenharmony_ci	case FM_PATCH_OPL3:
35262306a36Sopenharmony_ci		if (opl3->hardware >= OPL3_HW_OPL3) {
35362306a36Sopenharmony_ci			instr_4op = 1;
35462306a36Sopenharmony_ci			break;
35562306a36Sopenharmony_ci		}
35662306a36Sopenharmony_ci		fallthrough;
35762306a36Sopenharmony_ci	default:
35862306a36Sopenharmony_ci		spin_unlock_irqrestore(&opl3->voice_lock, flags);
35962306a36Sopenharmony_ci		return;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci#ifdef DEBUG_MIDI
36262306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "  --> OPL%i instrument: %s\n",
36362306a36Sopenharmony_ci		   instr_4op ? 3 : 2, patch->name);
36462306a36Sopenharmony_ci#endif
36562306a36Sopenharmony_ci	/* in SYNTH mode, application takes care of voices */
36662306a36Sopenharmony_ci	/* in SEQ mode, allocate voice on free OPL3 channel */
36762306a36Sopenharmony_ci	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
36862306a36Sopenharmony_ci		voice = opl3_get_voice(opl3, instr_4op, chan);
36962306a36Sopenharmony_ci	} else {
37062306a36Sopenharmony_ci		/* remap OSS voice */
37162306a36Sopenharmony_ci		voice = snd_opl3_oss_map[chan->number];
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (voice < 0) {
37562306a36Sopenharmony_ci		spin_unlock_irqrestore(&opl3->voice_lock, flags);
37662306a36Sopenharmony_ci		return;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (voice < MAX_OPL2_VOICES) {
38062306a36Sopenharmony_ci		/* Left register block for voices 0 .. 8 */
38162306a36Sopenharmony_ci		reg_side = OPL3_LEFT;
38262306a36Sopenharmony_ci		voice_offset = voice;
38362306a36Sopenharmony_ci		connect_mask = (OPL3_LEFT_4OP_0 << voice_offset) & 0x07;
38462306a36Sopenharmony_ci	} else {
38562306a36Sopenharmony_ci		/* Right register block for voices 9 .. 17 */
38662306a36Sopenharmony_ci		reg_side = OPL3_RIGHT;
38762306a36Sopenharmony_ci		voice_offset = voice - MAX_OPL2_VOICES;
38862306a36Sopenharmony_ci		connect_mask = (OPL3_RIGHT_4OP_0 << voice_offset) & 0x38;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* kill voice on channel */
39262306a36Sopenharmony_ci	vp = &opl3->voices[voice];
39362306a36Sopenharmony_ci	if (vp->state > 0) {
39462306a36Sopenharmony_ci		opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
39562306a36Sopenharmony_ci		reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT;
39662306a36Sopenharmony_ci		opl3->command(opl3, opl3_reg, reg_val);
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci	if (instr_4op) {
39962306a36Sopenharmony_ci		vp2 = &opl3->voices[voice + 3];
40062306a36Sopenharmony_ci		if (vp2->state > 0) {
40162306a36Sopenharmony_ci			opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK +
40262306a36Sopenharmony_ci					       voice_offset + 3);
40362306a36Sopenharmony_ci			reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT;
40462306a36Sopenharmony_ci			opl3->command(opl3, opl3_reg, reg_val);
40562306a36Sopenharmony_ci		}
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* set connection register */
40962306a36Sopenharmony_ci	if (instr_4op) {
41062306a36Sopenharmony_ci		if ((opl3->connection_reg ^ connect_mask) & connect_mask) {
41162306a36Sopenharmony_ci			opl3->connection_reg |= connect_mask;
41262306a36Sopenharmony_ci			/* set connection bit */
41362306a36Sopenharmony_ci			opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT;
41462306a36Sopenharmony_ci			opl3->command(opl3, opl3_reg, opl3->connection_reg);
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci	} else {
41762306a36Sopenharmony_ci		if ((opl3->connection_reg ^ ~connect_mask) & connect_mask) {
41862306a36Sopenharmony_ci			opl3->connection_reg &= ~connect_mask;
41962306a36Sopenharmony_ci			/* clear connection bit */
42062306a36Sopenharmony_ci			opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT;
42162306a36Sopenharmony_ci			opl3->command(opl3, opl3_reg, opl3->connection_reg);
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci#ifdef DEBUG_MIDI
42662306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "  --> setting OPL3 connection: 0x%x\n",
42762306a36Sopenharmony_ci		   opl3->connection_reg);
42862306a36Sopenharmony_ci#endif
42962306a36Sopenharmony_ci	/*
43062306a36Sopenharmony_ci	 * calculate volume depending on connection
43162306a36Sopenharmony_ci	 * between FM operators (see include/opl3.h)
43262306a36Sopenharmony_ci	 */
43362306a36Sopenharmony_ci	for (i = 0; i < (instr_4op ? 4 : 2); i++)
43462306a36Sopenharmony_ci		vol_op[i] = fm->op[i].ksl_level;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	connection = fm->feedback_connection[0] & 0x01;
43762306a36Sopenharmony_ci	if (instr_4op) {
43862306a36Sopenharmony_ci		connection <<= 1;
43962306a36Sopenharmony_ci		connection |= fm->feedback_connection[1] & 0x01;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		snd_opl3_calc_volume(&vol_op[3], vel, chan);
44262306a36Sopenharmony_ci		switch (connection) {
44362306a36Sopenharmony_ci		case 0x03:
44462306a36Sopenharmony_ci			snd_opl3_calc_volume(&vol_op[2], vel, chan);
44562306a36Sopenharmony_ci			fallthrough;
44662306a36Sopenharmony_ci		case 0x02:
44762306a36Sopenharmony_ci			snd_opl3_calc_volume(&vol_op[0], vel, chan);
44862306a36Sopenharmony_ci			break;
44962306a36Sopenharmony_ci		case 0x01:
45062306a36Sopenharmony_ci			snd_opl3_calc_volume(&vol_op[1], vel, chan);
45162306a36Sopenharmony_ci		}
45262306a36Sopenharmony_ci	} else {
45362306a36Sopenharmony_ci		snd_opl3_calc_volume(&vol_op[1], vel, chan);
45462306a36Sopenharmony_ci		if (connection)
45562306a36Sopenharmony_ci			snd_opl3_calc_volume(&vol_op[0], vel, chan);
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/* Program the FM voice characteristics */
45962306a36Sopenharmony_ci	for (i = 0; i < (instr_4op ? 4 : 2); i++) {
46062306a36Sopenharmony_ci#ifdef DEBUG_MIDI
46162306a36Sopenharmony_ci		snd_printk(KERN_DEBUG "  --> programming operator %i\n", i);
46262306a36Sopenharmony_ci#endif
46362306a36Sopenharmony_ci		op_offset = snd_opl3_regmap[voice_offset][i];
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		/* Set OPL3 AM_VIB register of requested voice/operator */
46662306a36Sopenharmony_ci		reg_val = fm->op[i].am_vib;
46762306a36Sopenharmony_ci		opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset);
46862306a36Sopenharmony_ci		opl3->command(opl3, opl3_reg, reg_val);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		/* Set OPL3 KSL_LEVEL register of requested voice/operator */
47162306a36Sopenharmony_ci		reg_val = vol_op[i];
47262306a36Sopenharmony_ci		opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset);
47362306a36Sopenharmony_ci		opl3->command(opl3, opl3_reg, reg_val);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		/* Set OPL3 ATTACK_DECAY register of requested voice/operator */
47662306a36Sopenharmony_ci		reg_val = fm->op[i].attack_decay;
47762306a36Sopenharmony_ci		opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset);
47862306a36Sopenharmony_ci		opl3->command(opl3, opl3_reg, reg_val);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		/* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */
48162306a36Sopenharmony_ci		reg_val = fm->op[i].sustain_release;
48262306a36Sopenharmony_ci		opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset);
48362306a36Sopenharmony_ci		opl3->command(opl3, opl3_reg, reg_val);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		/* Select waveform */
48662306a36Sopenharmony_ci		reg_val = fm->op[i].wave_select;
48762306a36Sopenharmony_ci		opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset);
48862306a36Sopenharmony_ci		opl3->command(opl3, opl3_reg, reg_val);
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	/* Set operator feedback and 2op inter-operator connection */
49262306a36Sopenharmony_ci	reg_val = fm->feedback_connection[0];
49362306a36Sopenharmony_ci	/* Set output voice connection */
49462306a36Sopenharmony_ci	reg_val |= OPL3_STEREO_BITS;
49562306a36Sopenharmony_ci	if (chan->gm_pan < 43)
49662306a36Sopenharmony_ci		reg_val &= ~OPL3_VOICE_TO_RIGHT;
49762306a36Sopenharmony_ci	if (chan->gm_pan > 85)
49862306a36Sopenharmony_ci		reg_val &= ~OPL3_VOICE_TO_LEFT;
49962306a36Sopenharmony_ci	opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
50062306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, reg_val);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (instr_4op) {
50362306a36Sopenharmony_ci		/* Set 4op inter-operator connection */
50462306a36Sopenharmony_ci		reg_val = fm->feedback_connection[1] & OPL3_CONNECTION_BIT;
50562306a36Sopenharmony_ci		/* Set output voice connection */
50662306a36Sopenharmony_ci		reg_val |= OPL3_STEREO_BITS;
50762306a36Sopenharmony_ci		if (chan->gm_pan < 43)
50862306a36Sopenharmony_ci			reg_val &= ~OPL3_VOICE_TO_RIGHT;
50962306a36Sopenharmony_ci		if (chan->gm_pan > 85)
51062306a36Sopenharmony_ci			reg_val &= ~OPL3_VOICE_TO_LEFT;
51162306a36Sopenharmony_ci		opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION +
51262306a36Sopenharmony_ci				       voice_offset + 3);
51362306a36Sopenharmony_ci		opl3->command(opl3, opl3_reg, reg_val);
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	/*
51762306a36Sopenharmony_ci	 * Special treatment of percussion notes for fm:
51862306a36Sopenharmony_ci	 * Requested pitch is really program, and pitch for
51962306a36Sopenharmony_ci	 * device is whatever was specified in the patch library.
52062306a36Sopenharmony_ci	 */
52162306a36Sopenharmony_ci	if (fm->fix_key)
52262306a36Sopenharmony_ci		note = fm->fix_key;
52362306a36Sopenharmony_ci	/*
52462306a36Sopenharmony_ci	 * use transpose if defined in patch library
52562306a36Sopenharmony_ci	 */
52662306a36Sopenharmony_ci	if (fm->trnsps)
52762306a36Sopenharmony_ci		note += (fm->trnsps - 64);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	snd_opl3_calc_pitch(&fnum, &blocknum, note, chan);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* Set OPL3 FNUM_LOW register of requested voice */
53262306a36Sopenharmony_ci	opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset);
53362306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, fnum);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	opl3->voices[voice].keyon_reg = blocknum;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* Set output sound flag */
53862306a36Sopenharmony_ci	blocknum |= OPL3_KEYON_BIT;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci#ifdef DEBUG_MIDI
54162306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "  --> trigger voice %i\n", voice);
54262306a36Sopenharmony_ci#endif
54362306a36Sopenharmony_ci	/* Set OPL3 KEYON_BLOCK register of requested voice */
54462306a36Sopenharmony_ci	opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
54562306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, blocknum);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* kill note after fixed duration (in centiseconds) */
54862306a36Sopenharmony_ci	if (fm->fix_dur) {
54962306a36Sopenharmony_ci		opl3->voices[voice].note_off = jiffies +
55062306a36Sopenharmony_ci			(fm->fix_dur * HZ) / 100;
55162306a36Sopenharmony_ci		snd_opl3_start_timer(opl3);
55262306a36Sopenharmony_ci		opl3->voices[voice].note_off_check = 1;
55362306a36Sopenharmony_ci	} else
55462306a36Sopenharmony_ci		opl3->voices[voice].note_off_check = 0;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* get extra pgm, but avoid possible loops */
55762306a36Sopenharmony_ci	extra_prg = (extra_prg) ? 0 : fm->modes;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* do the bookkeeping */
56062306a36Sopenharmony_ci	vp->time = opl3->use_time++;
56162306a36Sopenharmony_ci	vp->note = key;
56262306a36Sopenharmony_ci	vp->chan = chan;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (instr_4op) {
56562306a36Sopenharmony_ci		vp->state = SNDRV_OPL3_ST_ON_4OP;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci		vp2 = &opl3->voices[voice + 3];
56862306a36Sopenharmony_ci		vp2->time = opl3->use_time++;
56962306a36Sopenharmony_ci		vp2->note = key;
57062306a36Sopenharmony_ci		vp2->chan = chan;
57162306a36Sopenharmony_ci		vp2->state = SNDRV_OPL3_ST_NOT_AVAIL;
57262306a36Sopenharmony_ci	} else {
57362306a36Sopenharmony_ci		if (vp->state == SNDRV_OPL3_ST_ON_4OP) {
57462306a36Sopenharmony_ci			/* 4op killed by 2op, release bounded voice */
57562306a36Sopenharmony_ci			vp2 = &opl3->voices[voice + 3];
57662306a36Sopenharmony_ci			vp2->time = opl3->use_time++;
57762306a36Sopenharmony_ci			vp2->state = SNDRV_OPL3_ST_OFF;
57862306a36Sopenharmony_ci		}
57962306a36Sopenharmony_ci		vp->state = SNDRV_OPL3_ST_ON_2OP;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci#ifdef DEBUG_ALLOC
58362306a36Sopenharmony_ci	debug_alloc(opl3, "note on ", voice);
58462306a36Sopenharmony_ci#endif
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	/* allocate extra program if specified in patch library */
58762306a36Sopenharmony_ci	if (extra_prg) {
58862306a36Sopenharmony_ci		if (extra_prg > 128) {
58962306a36Sopenharmony_ci			bank = 128;
59062306a36Sopenharmony_ci			/* percussions start at 35 */
59162306a36Sopenharmony_ci			prg = extra_prg - 128 + 35 - 1;
59262306a36Sopenharmony_ci		} else {
59362306a36Sopenharmony_ci			bank = 0;
59462306a36Sopenharmony_ci			prg = extra_prg - 1;
59562306a36Sopenharmony_ci		}
59662306a36Sopenharmony_ci#ifdef DEBUG_MIDI
59762306a36Sopenharmony_ci		snd_printk(KERN_DEBUG " *** allocating extra program\n");
59862306a36Sopenharmony_ci#endif
59962306a36Sopenharmony_ci		goto __extra_prg;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci	spin_unlock_irqrestore(&opl3->voice_lock, flags);
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	unsigned short reg_side;
60762306a36Sopenharmony_ci	unsigned char voice_offset;
60862306a36Sopenharmony_ci	unsigned short opl3_reg;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	struct snd_opl3_voice *vp, *vp2;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (snd_BUG_ON(voice >= MAX_OPL3_VOICES))
61362306a36Sopenharmony_ci		return;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	vp = &opl3->voices[voice];
61662306a36Sopenharmony_ci	if (voice < MAX_OPL2_VOICES) {
61762306a36Sopenharmony_ci		/* Left register block for voices 0 .. 8 */
61862306a36Sopenharmony_ci		reg_side = OPL3_LEFT;
61962306a36Sopenharmony_ci		voice_offset = voice;
62062306a36Sopenharmony_ci	} else {
62162306a36Sopenharmony_ci		/* Right register block for voices 9 .. 17 */
62262306a36Sopenharmony_ci		reg_side = OPL3_RIGHT;
62362306a36Sopenharmony_ci		voice_offset = voice - MAX_OPL2_VOICES;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* kill voice */
62762306a36Sopenharmony_ci#ifdef DEBUG_MIDI
62862306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "  --> kill voice %i\n", voice);
62962306a36Sopenharmony_ci#endif
63062306a36Sopenharmony_ci	opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
63162306a36Sopenharmony_ci	/* clear Key ON bit */
63262306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, vp->keyon_reg);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	/* do the bookkeeping */
63562306a36Sopenharmony_ci	vp->time = opl3->use_time++;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	if (vp->state == SNDRV_OPL3_ST_ON_4OP) {
63862306a36Sopenharmony_ci		vp2 = &opl3->voices[voice + 3];
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		vp2->time = opl3->use_time++;
64162306a36Sopenharmony_ci		vp2->state = SNDRV_OPL3_ST_OFF;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci	vp->state = SNDRV_OPL3_ST_OFF;
64462306a36Sopenharmony_ci#ifdef DEBUG_ALLOC
64562306a36Sopenharmony_ci	debug_alloc(opl3, "note off", voice);
64662306a36Sopenharmony_ci#endif
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci/*
65162306a36Sopenharmony_ci * Release a note in response to a midi note off.
65262306a36Sopenharmony_ci */
65362306a36Sopenharmony_cistatic void snd_opl3_note_off_unsafe(void *p, int note, int vel,
65462306a36Sopenharmony_ci				     struct snd_midi_channel *chan)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci  	struct snd_opl3 *opl3;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	int voice;
65962306a36Sopenharmony_ci	struct snd_opl3_voice *vp;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	opl3 = p;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci#ifdef DEBUG_MIDI
66462306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "Note off, ch %i, inst %i, note %i\n",
66562306a36Sopenharmony_ci		   chan->number, chan->midi_program, note);
66662306a36Sopenharmony_ci#endif
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
66962306a36Sopenharmony_ci		if (chan->drum_channel && use_internal_drums) {
67062306a36Sopenharmony_ci			snd_opl3_drum_switch(opl3, note, vel, 0, chan);
67162306a36Sopenharmony_ci			return;
67262306a36Sopenharmony_ci		}
67362306a36Sopenharmony_ci		/* this loop will hopefully kill all extra voices, because
67462306a36Sopenharmony_ci		   they are grouped by the same channel and note values */
67562306a36Sopenharmony_ci		for (voice = 0; voice < opl3->max_voices; voice++) {
67662306a36Sopenharmony_ci			vp = &opl3->voices[voice];
67762306a36Sopenharmony_ci			if (vp->state > 0 && vp->chan == chan && vp->note == note) {
67862306a36Sopenharmony_ci				snd_opl3_kill_voice(opl3, voice);
67962306a36Sopenharmony_ci			}
68062306a36Sopenharmony_ci		}
68162306a36Sopenharmony_ci	} else {
68262306a36Sopenharmony_ci		/* remap OSS voices */
68362306a36Sopenharmony_ci		if (chan->number < MAX_OPL3_VOICES) {
68462306a36Sopenharmony_ci			voice = snd_opl3_oss_map[chan->number];
68562306a36Sopenharmony_ci			snd_opl3_kill_voice(opl3, voice);
68662306a36Sopenharmony_ci		}
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_civoid snd_opl3_note_off(void *p, int note, int vel,
69162306a36Sopenharmony_ci		       struct snd_midi_channel *chan)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct snd_opl3 *opl3 = p;
69462306a36Sopenharmony_ci	unsigned long flags;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	spin_lock_irqsave(&opl3->voice_lock, flags);
69762306a36Sopenharmony_ci	snd_opl3_note_off_unsafe(p, note, vel, chan);
69862306a36Sopenharmony_ci	spin_unlock_irqrestore(&opl3->voice_lock, flags);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci/*
70262306a36Sopenharmony_ci * key pressure change
70362306a36Sopenharmony_ci */
70462306a36Sopenharmony_civoid snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci#ifdef DEBUG_MIDI
70762306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "Key pressure, ch#: %i, inst#: %i\n",
70862306a36Sopenharmony_ci		   chan->number, chan->midi_program);
70962306a36Sopenharmony_ci#endif
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci/*
71362306a36Sopenharmony_ci * terminate note
71462306a36Sopenharmony_ci */
71562306a36Sopenharmony_civoid snd_opl3_terminate_note(void *p, int note, struct snd_midi_channel *chan)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci#ifdef DEBUG_MIDI
71862306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "Terminate note, ch#: %i, inst#: %i\n",
71962306a36Sopenharmony_ci		   chan->number, chan->midi_program);
72062306a36Sopenharmony_ci#endif
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic void snd_opl3_update_pitch(struct snd_opl3 *opl3, int voice)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	unsigned short reg_side;
72662306a36Sopenharmony_ci	unsigned char voice_offset;
72762306a36Sopenharmony_ci	unsigned short opl3_reg;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	unsigned char fnum, blocknum;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	struct snd_opl3_voice *vp;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (snd_BUG_ON(voice >= MAX_OPL3_VOICES))
73462306a36Sopenharmony_ci		return;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	vp = &opl3->voices[voice];
73762306a36Sopenharmony_ci	if (vp->chan == NULL)
73862306a36Sopenharmony_ci		return; /* not allocated? */
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	if (voice < MAX_OPL2_VOICES) {
74162306a36Sopenharmony_ci		/* Left register block for voices 0 .. 8 */
74262306a36Sopenharmony_ci		reg_side = OPL3_LEFT;
74362306a36Sopenharmony_ci		voice_offset = voice;
74462306a36Sopenharmony_ci	} else {
74562306a36Sopenharmony_ci		/* Right register block for voices 9 .. 17 */
74662306a36Sopenharmony_ci		reg_side = OPL3_RIGHT;
74762306a36Sopenharmony_ci		voice_offset = voice - MAX_OPL2_VOICES;
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	snd_opl3_calc_pitch(&fnum, &blocknum, vp->note, vp->chan);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	/* Set OPL3 FNUM_LOW register of requested voice */
75362306a36Sopenharmony_ci	opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset);
75462306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, fnum);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	vp->keyon_reg = blocknum;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	/* Set output sound flag */
75962306a36Sopenharmony_ci	blocknum |= OPL3_KEYON_BIT;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/* Set OPL3 KEYON_BLOCK register of requested voice */
76262306a36Sopenharmony_ci	opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
76362306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, blocknum);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	vp->time = opl3->use_time++;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci/*
76962306a36Sopenharmony_ci * Update voice pitch controller
77062306a36Sopenharmony_ci */
77162306a36Sopenharmony_cistatic void snd_opl3_pitch_ctrl(struct snd_opl3 *opl3, struct snd_midi_channel *chan)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	int voice;
77462306a36Sopenharmony_ci	struct snd_opl3_voice *vp;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	unsigned long flags;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	spin_lock_irqsave(&opl3->voice_lock, flags);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
78162306a36Sopenharmony_ci		for (voice = 0; voice < opl3->max_voices; voice++) {
78262306a36Sopenharmony_ci			vp = &opl3->voices[voice];
78362306a36Sopenharmony_ci			if (vp->state > 0 && vp->chan == chan) {
78462306a36Sopenharmony_ci				snd_opl3_update_pitch(opl3, voice);
78562306a36Sopenharmony_ci			}
78662306a36Sopenharmony_ci		}
78762306a36Sopenharmony_ci	} else {
78862306a36Sopenharmony_ci		/* remap OSS voices */
78962306a36Sopenharmony_ci		if (chan->number < MAX_OPL3_VOICES) {
79062306a36Sopenharmony_ci			voice = snd_opl3_oss_map[chan->number];
79162306a36Sopenharmony_ci			snd_opl3_update_pitch(opl3, voice);
79262306a36Sopenharmony_ci		}
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci	spin_unlock_irqrestore(&opl3->voice_lock, flags);
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci/*
79862306a36Sopenharmony_ci * Deal with a controller type event.  This includes all types of
79962306a36Sopenharmony_ci * control events, not just the midi controllers
80062306a36Sopenharmony_ci */
80162306a36Sopenharmony_civoid snd_opl3_control(void *p, int type, struct snd_midi_channel *chan)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci  	struct snd_opl3 *opl3;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	opl3 = p;
80662306a36Sopenharmony_ci#ifdef DEBUG_MIDI
80762306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "Controller, TYPE = %i, ch#: %i, inst#: %i\n",
80862306a36Sopenharmony_ci		   type, chan->number, chan->midi_program);
80962306a36Sopenharmony_ci#endif
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	switch (type) {
81262306a36Sopenharmony_ci	case MIDI_CTL_MSB_MODWHEEL:
81362306a36Sopenharmony_ci		if (chan->control[MIDI_CTL_MSB_MODWHEEL] > 63)
81462306a36Sopenharmony_ci			opl3->drum_reg |= OPL3_VIBRATO_DEPTH;
81562306a36Sopenharmony_ci		else
81662306a36Sopenharmony_ci			opl3->drum_reg &= ~OPL3_VIBRATO_DEPTH;
81762306a36Sopenharmony_ci		opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION,
81862306a36Sopenharmony_ci				 opl3->drum_reg);
81962306a36Sopenharmony_ci		break;
82062306a36Sopenharmony_ci	case MIDI_CTL_E2_TREMOLO_DEPTH:
82162306a36Sopenharmony_ci		if (chan->control[MIDI_CTL_E2_TREMOLO_DEPTH] > 63)
82262306a36Sopenharmony_ci			opl3->drum_reg |= OPL3_TREMOLO_DEPTH;
82362306a36Sopenharmony_ci		else
82462306a36Sopenharmony_ci			opl3->drum_reg &= ~OPL3_TREMOLO_DEPTH;
82562306a36Sopenharmony_ci		opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION,
82662306a36Sopenharmony_ci				 opl3->drum_reg);
82762306a36Sopenharmony_ci		break;
82862306a36Sopenharmony_ci	case MIDI_CTL_PITCHBEND:
82962306a36Sopenharmony_ci		snd_opl3_pitch_ctrl(opl3, chan);
83062306a36Sopenharmony_ci		break;
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci/*
83562306a36Sopenharmony_ci * NRPN events
83662306a36Sopenharmony_ci */
83762306a36Sopenharmony_civoid snd_opl3_nrpn(void *p, struct snd_midi_channel *chan,
83862306a36Sopenharmony_ci		   struct snd_midi_channel_set *chset)
83962306a36Sopenharmony_ci{
84062306a36Sopenharmony_ci#ifdef DEBUG_MIDI
84162306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "NRPN, ch#: %i, inst#: %i\n",
84262306a36Sopenharmony_ci		   chan->number, chan->midi_program);
84362306a36Sopenharmony_ci#endif
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci/*
84762306a36Sopenharmony_ci * receive sysex
84862306a36Sopenharmony_ci */
84962306a36Sopenharmony_civoid snd_opl3_sysex(void *p, unsigned char *buf, int len,
85062306a36Sopenharmony_ci		    int parsed, struct snd_midi_channel_set *chset)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci#ifdef DEBUG_MIDI
85362306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "SYSEX\n");
85462306a36Sopenharmony_ci#endif
85562306a36Sopenharmony_ci}
856