18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *   OPL2/OPL3/OPL4 FM routines for internal percussion channels
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "opl3_voice.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cistatic const char snd_opl3_drum_table[47] =
118c2ecf20Sopenharmony_ci{
128c2ecf20Sopenharmony_ci	OPL3_BASSDRUM_ON,  OPL3_BASSDRUM_ON,  OPL3_HIHAT_ON,	/* 35 - 37 */
138c2ecf20Sopenharmony_ci	OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON,     OPL3_SNAREDRUM_ON, /* 38 - 40 */
148c2ecf20Sopenharmony_ci	OPL3_BASSDRUM_ON,  OPL3_HIHAT_ON,     OPL3_BASSDRUM_ON,	/* 41 - 43 */
158c2ecf20Sopenharmony_ci	OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,    OPL3_HIHAT_ON,	/* 44 - 46 */
168c2ecf20Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_CYMBAL_ON,	/* 47 - 49 */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,	/* 50 - 52 */
198c2ecf20Sopenharmony_ci	OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,	/* 53 - 55 */
208c2ecf20Sopenharmony_ci	OPL3_HIHAT_ON,     OPL3_CYMBAL_ON,    OPL3_TOMTOM_ON,	/* 56 - 58 */
218c2ecf20Sopenharmony_ci	OPL3_CYMBAL_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 59 - 61 */
228c2ecf20Sopenharmony_ci	OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 62 - 64 */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 65 - 67 */
258c2ecf20Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_HIHAT_ON,     OPL3_HIHAT_ON,	/* 68 - 70 */
268c2ecf20Sopenharmony_ci	OPL3_HIHAT_ON,     OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,	/* 71 - 73 */
278c2ecf20Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 74 - 76 */
288c2ecf20Sopenharmony_ci	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 77 - 79 */
298c2ecf20Sopenharmony_ci	OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON			/* 80 - 81 */
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct snd_opl3_drum_voice {
338c2ecf20Sopenharmony_ci	int voice;
348c2ecf20Sopenharmony_ci	int op;
358c2ecf20Sopenharmony_ci	unsigned char am_vib;
368c2ecf20Sopenharmony_ci	unsigned char ksl_level;
378c2ecf20Sopenharmony_ci	unsigned char attack_decay;
388c2ecf20Sopenharmony_ci	unsigned char sustain_release;
398c2ecf20Sopenharmony_ci	unsigned char feedback_connection;
408c2ecf20Sopenharmony_ci	unsigned char wave_select;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct snd_opl3_drum_note {
448c2ecf20Sopenharmony_ci	int voice;
458c2ecf20Sopenharmony_ci	unsigned char fnum;
468c2ecf20Sopenharmony_ci	unsigned char octave_f;
478c2ecf20Sopenharmony_ci	unsigned char feedback_connection;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic const struct snd_opl3_drum_voice bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00};
518c2ecf20Sopenharmony_cistatic const struct snd_opl3_drum_voice bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00};
528c2ecf20Sopenharmony_cistatic const struct snd_opl3_drum_note bass_note = {6, 0x90, 0x09};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic const struct snd_opl3_drum_voice hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic const struct snd_opl3_drum_voice snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02};
578c2ecf20Sopenharmony_cistatic const struct snd_opl3_drum_note snare_note = {7, 0xf4, 0x0d};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic const struct snd_opl3_drum_voice tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00};
608c2ecf20Sopenharmony_cistatic const struct snd_opl3_drum_note tomtom_note = {8, 0xf4, 0x09};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic const struct snd_opl3_drum_voice cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * set drum voice characteristics
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_cistatic void snd_opl3_drum_voice_set(struct snd_opl3 *opl3,
688c2ecf20Sopenharmony_ci				    const struct snd_opl3_drum_voice *data)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
718c2ecf20Sopenharmony_ci	unsigned char voice_offset = data->voice;
728c2ecf20Sopenharmony_ci	unsigned short opl3_reg;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* Set OPL3 AM_VIB register */
758c2ecf20Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_AM_VIB + op_offset);
768c2ecf20Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->am_vib);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* Set OPL3 KSL_LEVEL register */
798c2ecf20Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset);
808c2ecf20Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->ksl_level);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* Set OPL3 ATTACK_DECAY register */
838c2ecf20Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_ATTACK_DECAY + op_offset);
848c2ecf20Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->attack_decay);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/* Set OPL3 SUSTAIN_RELEASE register */
878c2ecf20Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_SUSTAIN_RELEASE + op_offset);
888c2ecf20Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->sustain_release);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* Set OPL3 FEEDBACK_CONNECTION register */
918c2ecf20Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
928c2ecf20Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->feedback_connection);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Select waveform */
958c2ecf20Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_WAVE_SELECT + op_offset);
968c2ecf20Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->wave_select);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/*
1008c2ecf20Sopenharmony_ci * Set drum voice pitch
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic void snd_opl3_drum_note_set(struct snd_opl3 *opl3,
1038c2ecf20Sopenharmony_ci				   const struct snd_opl3_drum_note *data)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	unsigned char voice_offset = data->voice;
1068c2ecf20Sopenharmony_ci	unsigned short opl3_reg;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* Set OPL3 FNUM_LOW register */
1098c2ecf20Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_FNUM_LOW + voice_offset);
1108c2ecf20Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->fnum);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* Set OPL3 KEYON_BLOCK register */
1138c2ecf20Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_KEYON_BLOCK + voice_offset);
1148c2ecf20Sopenharmony_ci	opl3->command(opl3, opl3_reg, data->octave_f);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/*
1188c2ecf20Sopenharmony_ci * Set drum voice volume and position
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_cistatic void snd_opl3_drum_vol_set(struct snd_opl3 *opl3,
1218c2ecf20Sopenharmony_ci				  const struct snd_opl3_drum_voice *data,
1228c2ecf20Sopenharmony_ci				  int vel, struct snd_midi_channel *chan)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
1258c2ecf20Sopenharmony_ci	unsigned char voice_offset = data->voice;
1268c2ecf20Sopenharmony_ci	unsigned char reg_val;
1278c2ecf20Sopenharmony_ci	unsigned short opl3_reg;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* Set OPL3 KSL_LEVEL register */
1308c2ecf20Sopenharmony_ci	reg_val = data->ksl_level;
1318c2ecf20Sopenharmony_ci	snd_opl3_calc_volume(&reg_val, vel, chan);
1328c2ecf20Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset);
1338c2ecf20Sopenharmony_ci	opl3->command(opl3, opl3_reg, reg_val);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* Set OPL3 FEEDBACK_CONNECTION register */
1368c2ecf20Sopenharmony_ci	/* Set output voice connection */
1378c2ecf20Sopenharmony_ci	reg_val = data->feedback_connection | OPL3_STEREO_BITS;
1388c2ecf20Sopenharmony_ci	if (chan->gm_pan < 43)
1398c2ecf20Sopenharmony_ci		reg_val &= ~OPL3_VOICE_TO_RIGHT;
1408c2ecf20Sopenharmony_ci	if (chan->gm_pan > 85)
1418c2ecf20Sopenharmony_ci		reg_val &= ~OPL3_VOICE_TO_LEFT;
1428c2ecf20Sopenharmony_ci	opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
1438c2ecf20Sopenharmony_ci	opl3->command(opl3, opl3_reg, reg_val);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/*
1478c2ecf20Sopenharmony_ci * Loads drum voices at init time
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_civoid snd_opl3_load_drums(struct snd_opl3 *opl3)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &bass_op0);
1528c2ecf20Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &bass_op1);
1538c2ecf20Sopenharmony_ci	snd_opl3_drum_note_set(opl3, &bass_note);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &hihat);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &snare);
1588c2ecf20Sopenharmony_ci	snd_opl3_drum_note_set(opl3, &snare_note);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &tomtom);
1618c2ecf20Sopenharmony_ci	snd_opl3_drum_note_set(opl3, &tomtom_note);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	snd_opl3_drum_voice_set(opl3, &cymbal);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/*
1678c2ecf20Sopenharmony_ci * Switch drum voice on or off
1688c2ecf20Sopenharmony_ci */
1698c2ecf20Sopenharmony_civoid snd_opl3_drum_switch(struct snd_opl3 *opl3, int note, int vel, int on_off,
1708c2ecf20Sopenharmony_ci			  struct snd_midi_channel *chan)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	unsigned char drum_mask;
1738c2ecf20Sopenharmony_ci	const struct snd_opl3_drum_voice *drum_voice;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE))
1768c2ecf20Sopenharmony_ci		return;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if ((note < 35) || (note > 81))
1798c2ecf20Sopenharmony_ci		return;
1808c2ecf20Sopenharmony_ci	drum_mask = snd_opl3_drum_table[note - 35];
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (on_off) {
1838c2ecf20Sopenharmony_ci		switch (drum_mask) {
1848c2ecf20Sopenharmony_ci		case OPL3_BASSDRUM_ON:
1858c2ecf20Sopenharmony_ci			drum_voice = &bass_op1;
1868c2ecf20Sopenharmony_ci			break;
1878c2ecf20Sopenharmony_ci		case OPL3_HIHAT_ON:
1888c2ecf20Sopenharmony_ci			drum_voice = &hihat;
1898c2ecf20Sopenharmony_ci			break;
1908c2ecf20Sopenharmony_ci		case OPL3_SNAREDRUM_ON:
1918c2ecf20Sopenharmony_ci			drum_voice = &snare;
1928c2ecf20Sopenharmony_ci			break;
1938c2ecf20Sopenharmony_ci		case OPL3_TOMTOM_ON:
1948c2ecf20Sopenharmony_ci			drum_voice = &tomtom;
1958c2ecf20Sopenharmony_ci			break;
1968c2ecf20Sopenharmony_ci		case OPL3_CYMBAL_ON:
1978c2ecf20Sopenharmony_ci			drum_voice = &cymbal;
1988c2ecf20Sopenharmony_ci			break;
1998c2ecf20Sopenharmony_ci		default:
2008c2ecf20Sopenharmony_ci			drum_voice = &tomtom;
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		snd_opl3_drum_vol_set(opl3, drum_voice, vel, chan);
2048c2ecf20Sopenharmony_ci		opl3->drum_reg |= drum_mask;
2058c2ecf20Sopenharmony_ci	} else {
2068c2ecf20Sopenharmony_ci		opl3->drum_reg &= ~drum_mask;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION,
2098c2ecf20Sopenharmony_ci			 opl3->drum_reg);
2108c2ecf20Sopenharmony_ci}
211