opl3_drums.c (6531B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) by Uros Bizjak <uros@kss-loka.si> 4 * 5 * OPL2/OPL3/OPL4 FM routines for internal percussion channels 6 */ 7 8#include "opl3_voice.h" 9 10static const char snd_opl3_drum_table[47] = 11{ 12 OPL3_BASSDRUM_ON, OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, /* 35 - 37 */ 13 OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON, OPL3_SNAREDRUM_ON, /* 38 - 40 */ 14 OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, OPL3_BASSDRUM_ON, /* 41 - 43 */ 15 OPL3_HIHAT_ON, OPL3_TOMTOM_ON, OPL3_HIHAT_ON, /* 44 - 46 */ 16 OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_CYMBAL_ON, /* 47 - 49 */ 17 18 OPL3_TOMTOM_ON, OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, /* 50 - 52 */ 19 OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, /* 53 - 55 */ 20 OPL3_HIHAT_ON, OPL3_CYMBAL_ON, OPL3_TOMTOM_ON, /* 56 - 58 */ 21 OPL3_CYMBAL_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 59 - 61 */ 22 OPL3_HIHAT_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 62 - 64 */ 23 24 OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 65 - 67 */ 25 OPL3_TOMTOM_ON, OPL3_HIHAT_ON, OPL3_HIHAT_ON, /* 68 - 70 */ 26 OPL3_HIHAT_ON, OPL3_HIHAT_ON, OPL3_TOMTOM_ON, /* 71 - 73 */ 27 OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 74 - 76 */ 28 OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 77 - 79 */ 29 OPL3_CYMBAL_ON, OPL3_CYMBAL_ON /* 80 - 81 */ 30}; 31 32struct snd_opl3_drum_voice { 33 int voice; 34 int op; 35 unsigned char am_vib; 36 unsigned char ksl_level; 37 unsigned char attack_decay; 38 unsigned char sustain_release; 39 unsigned char feedback_connection; 40 unsigned char wave_select; 41}; 42 43struct snd_opl3_drum_note { 44 int voice; 45 unsigned char fnum; 46 unsigned char octave_f; 47 unsigned char feedback_connection; 48}; 49 50static const struct snd_opl3_drum_voice bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00}; 51static const struct snd_opl3_drum_voice bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00}; 52static const struct snd_opl3_drum_note bass_note = {6, 0x90, 0x09}; 53 54static const struct snd_opl3_drum_voice hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00}; 55 56static const struct snd_opl3_drum_voice snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02}; 57static const struct snd_opl3_drum_note snare_note = {7, 0xf4, 0x0d}; 58 59static const struct snd_opl3_drum_voice tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00}; 60static const struct snd_opl3_drum_note tomtom_note = {8, 0xf4, 0x09}; 61 62static const struct snd_opl3_drum_voice cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00}; 63 64/* 65 * set drum voice characteristics 66 */ 67static void snd_opl3_drum_voice_set(struct snd_opl3 *opl3, 68 const struct snd_opl3_drum_voice *data) 69{ 70 unsigned char op_offset = snd_opl3_regmap[data->voice][data->op]; 71 unsigned char voice_offset = data->voice; 72 unsigned short opl3_reg; 73 74 /* Set OPL3 AM_VIB register */ 75 opl3_reg = OPL3_LEFT | (OPL3_REG_AM_VIB + op_offset); 76 opl3->command(opl3, opl3_reg, data->am_vib); 77 78 /* Set OPL3 KSL_LEVEL register */ 79 opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset); 80 opl3->command(opl3, opl3_reg, data->ksl_level); 81 82 /* Set OPL3 ATTACK_DECAY register */ 83 opl3_reg = OPL3_LEFT | (OPL3_REG_ATTACK_DECAY + op_offset); 84 opl3->command(opl3, opl3_reg, data->attack_decay); 85 86 /* Set OPL3 SUSTAIN_RELEASE register */ 87 opl3_reg = OPL3_LEFT | (OPL3_REG_SUSTAIN_RELEASE + op_offset); 88 opl3->command(opl3, opl3_reg, data->sustain_release); 89 90 /* Set OPL3 FEEDBACK_CONNECTION register */ 91 opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); 92 opl3->command(opl3, opl3_reg, data->feedback_connection); 93 94 /* Select waveform */ 95 opl3_reg = OPL3_LEFT | (OPL3_REG_WAVE_SELECT + op_offset); 96 opl3->command(opl3, opl3_reg, data->wave_select); 97} 98 99/* 100 * Set drum voice pitch 101 */ 102static void snd_opl3_drum_note_set(struct snd_opl3 *opl3, 103 const struct snd_opl3_drum_note *data) 104{ 105 unsigned char voice_offset = data->voice; 106 unsigned short opl3_reg; 107 108 /* Set OPL3 FNUM_LOW register */ 109 opl3_reg = OPL3_LEFT | (OPL3_REG_FNUM_LOW + voice_offset); 110 opl3->command(opl3, opl3_reg, data->fnum); 111 112 /* Set OPL3 KEYON_BLOCK register */ 113 opl3_reg = OPL3_LEFT | (OPL3_REG_KEYON_BLOCK + voice_offset); 114 opl3->command(opl3, opl3_reg, data->octave_f); 115} 116 117/* 118 * Set drum voice volume and position 119 */ 120static void snd_opl3_drum_vol_set(struct snd_opl3 *opl3, 121 const struct snd_opl3_drum_voice *data, 122 int vel, struct snd_midi_channel *chan) 123{ 124 unsigned char op_offset = snd_opl3_regmap[data->voice][data->op]; 125 unsigned char voice_offset = data->voice; 126 unsigned char reg_val; 127 unsigned short opl3_reg; 128 129 /* Set OPL3 KSL_LEVEL register */ 130 reg_val = data->ksl_level; 131 snd_opl3_calc_volume(®_val, vel, chan); 132 opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset); 133 opl3->command(opl3, opl3_reg, reg_val); 134 135 /* Set OPL3 FEEDBACK_CONNECTION register */ 136 /* Set output voice connection */ 137 reg_val = data->feedback_connection | OPL3_STEREO_BITS; 138 if (chan->gm_pan < 43) 139 reg_val &= ~OPL3_VOICE_TO_RIGHT; 140 if (chan->gm_pan > 85) 141 reg_val &= ~OPL3_VOICE_TO_LEFT; 142 opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); 143 opl3->command(opl3, opl3_reg, reg_val); 144} 145 146/* 147 * Loads drum voices at init time 148 */ 149void snd_opl3_load_drums(struct snd_opl3 *opl3) 150{ 151 snd_opl3_drum_voice_set(opl3, &bass_op0); 152 snd_opl3_drum_voice_set(opl3, &bass_op1); 153 snd_opl3_drum_note_set(opl3, &bass_note); 154 155 snd_opl3_drum_voice_set(opl3, &hihat); 156 157 snd_opl3_drum_voice_set(opl3, &snare); 158 snd_opl3_drum_note_set(opl3, &snare_note); 159 160 snd_opl3_drum_voice_set(opl3, &tomtom); 161 snd_opl3_drum_note_set(opl3, &tomtom_note); 162 163 snd_opl3_drum_voice_set(opl3, &cymbal); 164} 165 166/* 167 * Switch drum voice on or off 168 */ 169void snd_opl3_drum_switch(struct snd_opl3 *opl3, int note, int vel, int on_off, 170 struct snd_midi_channel *chan) 171{ 172 unsigned char drum_mask; 173 const struct snd_opl3_drum_voice *drum_voice; 174 175 if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE)) 176 return; 177 178 if ((note < 35) || (note > 81)) 179 return; 180 drum_mask = snd_opl3_drum_table[note - 35]; 181 182 if (on_off) { 183 switch (drum_mask) { 184 case OPL3_BASSDRUM_ON: 185 drum_voice = &bass_op1; 186 break; 187 case OPL3_HIHAT_ON: 188 drum_voice = &hihat; 189 break; 190 case OPL3_SNAREDRUM_ON: 191 drum_voice = &snare; 192 break; 193 case OPL3_TOMTOM_ON: 194 drum_voice = &tomtom; 195 break; 196 case OPL3_CYMBAL_ON: 197 drum_voice = &cymbal; 198 break; 199 default: 200 drum_voice = &tomtom; 201 } 202 203 snd_opl3_drum_vol_set(opl3, drum_voice, vel, chan); 204 opl3->drum_reg |= drum_mask; 205 } else { 206 opl3->drum_reg &= ~drum_mask; 207 } 208 opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 209 opl3->drum_reg); 210}