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