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(®_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