1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) by Uros Bizjak <uros@kss-loka.si>
5 * OPL2/OPL3/OPL4 FM routines for internal percussion channels
8 #include "opl3_voice.h"
10 static const char snd_opl3_drum_table
[47] =
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 */
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 */
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 */
32 struct snd_opl3_drum_voice
{
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
;
43 struct snd_opl3_drum_note
{
46 unsigned char octave_f
;
47 unsigned char feedback_connection
;
50 static const struct snd_opl3_drum_voice bass_op0
= {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00};
51 static const struct snd_opl3_drum_voice bass_op1
= {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00};
52 static const struct snd_opl3_drum_note bass_note
= {6, 0x90, 0x09};
54 static const struct snd_opl3_drum_voice hihat
= {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00};
56 static const struct snd_opl3_drum_voice snare
= {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02};
57 static const struct snd_opl3_drum_note snare_note
= {7, 0xf4, 0x0d};
59 static const struct snd_opl3_drum_voice tomtom
= {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00};
60 static const struct snd_opl3_drum_note tomtom_note
= {8, 0xf4, 0x09};
62 static const struct snd_opl3_drum_voice cymbal
= {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00};
65 * set drum voice characteristics
67 static void snd_opl3_drum_voice_set(struct snd_opl3
*opl3
,
68 const struct snd_opl3_drum_voice
*data
)
70 unsigned char op_offset
= snd_opl3_regmap
[data
->voice
][data
->op
];
71 unsigned char voice_offset
= data
->voice
;
72 unsigned short opl3_reg
;
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
);
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
);
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
);
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
);
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
);
95 opl3_reg
= OPL3_LEFT
| (OPL3_REG_WAVE_SELECT
+ op_offset
);
96 opl3
->command(opl3
, opl3_reg
, data
->wave_select
);
100 * Set drum voice pitch
102 static void snd_opl3_drum_note_set(struct snd_opl3
*opl3
,
103 const struct snd_opl3_drum_note
*data
)
105 unsigned char voice_offset
= data
->voice
;
106 unsigned short opl3_reg
;
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
);
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
);
118 * Set drum voice volume and position
120 static 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
)
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
;
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
);
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
);
147 * Loads drum voices at init time
149 void snd_opl3_load_drums(struct snd_opl3
*opl3
)
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
);
155 snd_opl3_drum_voice_set(opl3
, &hihat
);
157 snd_opl3_drum_voice_set(opl3
, &snare
);
158 snd_opl3_drum_note_set(opl3
, &snare_note
);
160 snd_opl3_drum_voice_set(opl3
, &tomtom
);
161 snd_opl3_drum_note_set(opl3
, &tomtom_note
);
163 snd_opl3_drum_voice_set(opl3
, &cymbal
);
167 * Switch drum voice on or off
169 void snd_opl3_drum_switch(struct snd_opl3
*opl3
, int note
, int vel
, int on_off
,
170 struct snd_midi_channel
*chan
)
172 unsigned char drum_mask
;
173 const struct snd_opl3_drum_voice
*drum_voice
;
175 if (!(opl3
->drum_reg
& OPL3_PERCUSSION_ENABLE
))
178 if ((note
< 35) || (note
> 81))
180 drum_mask
= snd_opl3_drum_table
[note
- 35];
184 case OPL3_BASSDRUM_ON
:
185 drum_voice
= &bass_op1
;
190 case OPL3_SNAREDRUM_ON
:
194 drum_voice
= &tomtom
;
197 drum_voice
= &cymbal
;
200 drum_voice
= &tomtom
;
203 snd_opl3_drum_vol_set(opl3
, drum_voice
, vel
, chan
);
204 opl3
->drum_reg
|= drum_mask
;
206 opl3
->drum_reg
&= ~drum_mask
;
208 opl3
->command(opl3
, OPL3_LEFT
| OPL3_REG_PERCUSSION
,