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 *   OPL2/OPL3/OPL4 FM routines for internal percussion channels
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "opl3_voice.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistatic const char snd_opl3_drum_table[47] =
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	OPL3_BASSDRUM_ON,  OPL3_BASSDRUM_ON,  OPL3_HIHAT_ON,	/* 35 - 37 */
1362306a36Sopenharmony_ci	OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON,     OPL3_SNAREDRUM_ON, /* 38 - 40 */
1462306a36Sopenharmony_ci	OPL3_BASSDRUM_ON,  OPL3_HIHAT_ON,     OPL3_BASSDRUM_ON,	/* 41 - 43 */
1562306a36Sopenharmony_ci	OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,    OPL3_HIHAT_ON,	/* 44 - 46 */
1662306a36Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_CYMBAL_ON,	/* 47 - 49 */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,	/* 50 - 52 */
1962306a36Sopenharmony_ci	OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,	/* 53 - 55 */
2062306a36Sopenharmony_ci	OPL3_HIHAT_ON,     OPL3_CYMBAL_ON,    OPL3_TOMTOM_ON,	/* 56 - 58 */
2162306a36Sopenharmony_ci	OPL3_CYMBAL_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 59 - 61 */
2262306a36Sopenharmony_ci	OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 62 - 64 */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 65 - 67 */
2562306a36Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_HIHAT_ON,     OPL3_HIHAT_ON,	/* 68 - 70 */
2662306a36Sopenharmony_ci	OPL3_HIHAT_ON,     OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,	/* 71 - 73 */
2762306a36Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 74 - 76 */
2862306a36Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 77 - 79 */
2962306a36Sopenharmony_ci	OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON			/* 80 - 81 */
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct snd_opl3_drum_voice {
3362306a36Sopenharmony_ci	int voice;
3462306a36Sopenharmony_ci	int op;
3562306a36Sopenharmony_ci	unsigned char am_vib;
3662306a36Sopenharmony_ci	unsigned char ksl_level;
3762306a36Sopenharmony_ci	unsigned char attack_decay;
3862306a36Sopenharmony_ci	unsigned char sustain_release;
3962306a36Sopenharmony_ci	unsigned char feedback_connection;
4062306a36Sopenharmony_ci	unsigned char wave_select;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct snd_opl3_drum_note {
4462306a36Sopenharmony_ci	int voice;
4562306a36Sopenharmony_ci	unsigned char fnum;
4662306a36Sopenharmony_ci	unsigned char octave_f;
4762306a36Sopenharmony_ci	unsigned char feedback_connection;
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic const struct snd_opl3_drum_voice bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00};
5162306a36Sopenharmony_cistatic const struct snd_opl3_drum_voice bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00};
5262306a36Sopenharmony_cistatic const struct snd_opl3_drum_note bass_note = {6, 0x90, 0x09};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic const struct snd_opl3_drum_voice hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic const struct snd_opl3_drum_voice snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02};
5762306a36Sopenharmony_cistatic const struct snd_opl3_drum_note snare_note = {7, 0xf4, 0x0d};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic const struct snd_opl3_drum_voice tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00};
6062306a36Sopenharmony_cistatic const struct snd_opl3_drum_note tomtom_note = {8, 0xf4, 0x09};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const struct snd_opl3_drum_voice cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/*
6562306a36Sopenharmony_ci * set drum voice characteristics
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic void snd_opl3_drum_voice_set(struct snd_opl3 *opl3,
6862306a36Sopenharmony_ci				    const struct snd_opl3_drum_voice *data)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
7162306a36Sopenharmony_ci	unsigned char voice_offset = data->voice;
7262306a36Sopenharmony_ci	unsigned short opl3_reg;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Set OPL3 AM_VIB register */
7562306a36Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_AM_VIB + op_offset);
7662306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->am_vib);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* Set OPL3 KSL_LEVEL register */
7962306a36Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset);
8062306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->ksl_level);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* Set OPL3 ATTACK_DECAY register */
8362306a36Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_ATTACK_DECAY + op_offset);
8462306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->attack_decay);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* Set OPL3 SUSTAIN_RELEASE register */
8762306a36Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_SUSTAIN_RELEASE + op_offset);
8862306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->sustain_release);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* Set OPL3 FEEDBACK_CONNECTION register */
9162306a36Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
9262306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->feedback_connection);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Select waveform */
9562306a36Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_WAVE_SELECT + op_offset);
9662306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->wave_select);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * Set drum voice pitch
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistatic void snd_opl3_drum_note_set(struct snd_opl3 *opl3,
10362306a36Sopenharmony_ci				   const struct snd_opl3_drum_note *data)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	unsigned char voice_offset = data->voice;
10662306a36Sopenharmony_ci	unsigned short opl3_reg;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Set OPL3 FNUM_LOW register */
10962306a36Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_FNUM_LOW + voice_offset);
11062306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->fnum);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* Set OPL3 KEYON_BLOCK register */
11362306a36Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_KEYON_BLOCK + voice_offset);
11462306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->octave_f);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*
11862306a36Sopenharmony_ci * Set drum voice volume and position
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistatic void snd_opl3_drum_vol_set(struct snd_opl3 *opl3,
12162306a36Sopenharmony_ci				  const struct snd_opl3_drum_voice *data,
12262306a36Sopenharmony_ci				  int vel, struct snd_midi_channel *chan)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
12562306a36Sopenharmony_ci	unsigned char voice_offset = data->voice;
12662306a36Sopenharmony_ci	unsigned char reg_val;
12762306a36Sopenharmony_ci	unsigned short opl3_reg;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* Set OPL3 KSL_LEVEL register */
13062306a36Sopenharmony_ci	reg_val = data->ksl_level;
13162306a36Sopenharmony_ci	snd_opl3_calc_volume(&reg_val, vel, chan);
13262306a36Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset);
13362306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, reg_val);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* Set OPL3 FEEDBACK_CONNECTION register */
13662306a36Sopenharmony_ci	/* Set output voice connection */
13762306a36Sopenharmony_ci	reg_val = data->feedback_connection | OPL3_STEREO_BITS;
13862306a36Sopenharmony_ci	if (chan->gm_pan < 43)
13962306a36Sopenharmony_ci		reg_val &= ~OPL3_VOICE_TO_RIGHT;
14062306a36Sopenharmony_ci	if (chan->gm_pan > 85)
14162306a36Sopenharmony_ci		reg_val &= ~OPL3_VOICE_TO_LEFT;
14262306a36Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
14362306a36Sopenharmony_ci	opl3->command(opl3, opl3_reg, reg_val);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci/*
14762306a36Sopenharmony_ci * Loads drum voices at init time
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_civoid snd_opl3_load_drums(struct snd_opl3 *opl3)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &bass_op0);
15262306a36Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &bass_op1);
15362306a36Sopenharmony_ci	snd_opl3_drum_note_set(opl3, &bass_note);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &hihat);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &snare);
15862306a36Sopenharmony_ci	snd_opl3_drum_note_set(opl3, &snare_note);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &tomtom);
16162306a36Sopenharmony_ci	snd_opl3_drum_note_set(opl3, &tomtom_note);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &cymbal);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/*
16762306a36Sopenharmony_ci * Switch drum voice on or off
16862306a36Sopenharmony_ci */
16962306a36Sopenharmony_civoid snd_opl3_drum_switch(struct snd_opl3 *opl3, int note, int vel, int on_off,
17062306a36Sopenharmony_ci			  struct snd_midi_channel *chan)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	unsigned char drum_mask;
17362306a36Sopenharmony_ci	const struct snd_opl3_drum_voice *drum_voice;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE))
17662306a36Sopenharmony_ci		return;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if ((note < 35) || (note > 81))
17962306a36Sopenharmony_ci		return;
18062306a36Sopenharmony_ci	drum_mask = snd_opl3_drum_table[note - 35];
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (on_off) {
18362306a36Sopenharmony_ci		switch (drum_mask) {
18462306a36Sopenharmony_ci		case OPL3_BASSDRUM_ON:
18562306a36Sopenharmony_ci			drum_voice = &bass_op1;
18662306a36Sopenharmony_ci			break;
18762306a36Sopenharmony_ci		case OPL3_HIHAT_ON:
18862306a36Sopenharmony_ci			drum_voice = &hihat;
18962306a36Sopenharmony_ci			break;
19062306a36Sopenharmony_ci		case OPL3_SNAREDRUM_ON:
19162306a36Sopenharmony_ci			drum_voice = &snare;
19262306a36Sopenharmony_ci			break;
19362306a36Sopenharmony_ci		case OPL3_TOMTOM_ON:
19462306a36Sopenharmony_ci			drum_voice = &tomtom;
19562306a36Sopenharmony_ci			break;
19662306a36Sopenharmony_ci		case OPL3_CYMBAL_ON:
19762306a36Sopenharmony_ci			drum_voice = &cymbal;
19862306a36Sopenharmony_ci			break;
19962306a36Sopenharmony_ci		default:
20062306a36Sopenharmony_ci			drum_voice = &tomtom;
20162306a36Sopenharmony_ci		}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		snd_opl3_drum_vol_set(opl3, drum_voice, vel, chan);
20462306a36Sopenharmony_ci		opl3->drum_reg |= drum_mask;
20562306a36Sopenharmony_ci	} else {
20662306a36Sopenharmony_ci		opl3->drum_reg &= ~drum_mask;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION,
20962306a36Sopenharmony_ci			 opl3->drum_reg);
21062306a36Sopenharmony_ci}
211