1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 /* OPL implementation for hardware OPL using ALSA Direct FM API.
24 *
25 * Caveats and limitations:
26 * - Pretends to be a softsynth (emitting silence).
27 * - Dual OPL2 mode requires OPL3 hardware.
28 * - Every register write leads to a series of register writes on the hardware,
29 * due to the lack of direct register access in the ALSA Direct FM API.
30 * - No timers
31 */
32
33 #define FORBIDDEN_SYMBOL_ALLOW_ALL
34 #include "common/scummsys.h"
35
36 #include "common/debug.h"
37 #include "common/str.h"
38 #include "audio/fmopl.h"
39
40 #include <sys/ioctl.h>
41 #include <alsa/asoundlib.h>
42 #include <sound/asound_fm.h>
43
44 namespace OPL {
45 namespace ALSA {
46
47 class OPL : public ::OPL::RealOPL {
48 private:
49 enum {
50 kOpl2Voices = 9,
51 kVoices = 18,
52 kOpl2Operators = 18,
53 kOperators = 36
54 };
55
56 Config::OplType _type;
57 int _iface;
58 snd_hwdep_t *_opl;
59 snd_dm_fm_voice _oper[kOperators];
60 snd_dm_fm_note _voice[kVoices];
61 snd_dm_fm_params _params;
62 int index[2];
63 static const int voiceToOper0[kVoices];
64 static const int regOffsetToOper[0x20];
65
66 void writeOplReg(int c, int r, int v);
67 void clear();
68
69 public:
70 OPL(Config::OplType type);
71 ~OPL();
72
73 bool init();
74 void reset();
75
76 void write(int a, int v);
77 byte read(int a);
78
79 void writeReg(int r, int v);
80 };
81
82 const int OPL::voiceToOper0[OPL::kVoices] =
83 { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 };
84
85 const int OPL::regOffsetToOper[0x20] =
86 { 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
87 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
88
OPL(Config::OplType type)89 OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) {
90 }
91
~OPL()92 OPL::~OPL() {
93 stop();
94
95 if (_opl) {
96 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
97 snd_hwdep_close(_opl);
98 }
99 }
100
clear()101 void OPL::clear() {
102 index[0] = index[1] = 0;
103
104 memset(_oper, 0, sizeof(_oper));
105 memset(_voice, 0, sizeof(_voice));
106 memset(&_params, 0, sizeof(_params));
107
108 for (int i = 0; i < kOperators; ++i) {
109 _oper[i].op = (i / 3) % 2;
110 _oper[i].voice = (i / 6) * 3 + (i % 3);
111 }
112
113 for (int i = 0; i < kVoices; ++i)
114 _voice[i].voice = i;
115
116 // For OPL3 hardware we need to set up the panning in OPL2 modes
117 if (_iface == SND_HWDEP_IFACE_OPL3) {
118 if (_type == Config::kDualOpl2) {
119 for (int i = 0; i < kOpl2Operators; ++i)
120 _oper[i].left = 1; // FIXME below
121 for (int i = kOpl2Operators; i < kOperators; ++i)
122 _oper[i].right = 1;
123 } else if (_type == Config::kOpl2) {
124 for (int i = 0; i < kOpl2Operators; ++i) {
125 _oper[i].left = 1;
126 _oper[i].right = 1;
127 }
128 }
129 }
130 }
131
init()132 bool OPL::init() {
133 clear();
134
135 int card = -1;
136 snd_ctl_t *ctl;
137 snd_hwdep_info_t *info;
138 snd_hwdep_info_alloca(&info);
139
140 int iface = SND_HWDEP_IFACE_OPL3;
141 if (_type == Config::kOpl2)
142 iface = SND_HWDEP_IFACE_OPL2;
143
144 // Look for OPL hwdep interface
145 while (!snd_card_next(&card) && card >= 0) {
146 int dev = -1;
147 Common::String name = Common::String::format("hw:%d", card);
148
149 if (snd_ctl_open(&ctl, name.c_str(), 0) < 0)
150 continue;
151
152 while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) {
153 name = Common::String::format("hw:%d,%d", card, dev);
154
155 if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0)
156 continue;
157
158 if (!snd_hwdep_info(_opl, info)) {
159 int found = snd_hwdep_info_get_iface(info);
160 // OPL3 can be used for (Dual) OPL2 mode
161 if (found == iface || found == SND_HWDEP_IFACE_OPL3) {
162 snd_ctl_close(ctl);
163 _iface = found;
164 reset();
165 return true;
166 }
167 }
168
169 // Wrong interface, try next device
170 snd_hwdep_close(_opl);
171 _opl = nullptr;
172 }
173
174 snd_ctl_close(ctl);
175 }
176
177 return false;
178 }
179
reset()180 void OPL::reset() {
181 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
182 if (_iface == SND_HWDEP_IFACE_OPL3)
183 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3);
184
185 clear();
186
187 // Sync up with the hardware
188 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
189 for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i)
190 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]);
191 for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i)
192 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]);
193 }
194
write(int port,int val)195 void OPL::write(int port, int val) {
196 val &= 0xff;
197 int chip = (port & 2) >> 1;
198
199 if (port & 1) {
200 switch(_type) {
201 case Config::kOpl2:
202 writeOplReg(0, index[0], val);
203 break;
204 case Config::kDualOpl2:
205 if (port & 8) {
206 writeOplReg(0, index[0], val);
207 writeOplReg(1, index[1], val);
208 } else
209 writeOplReg(chip, index[chip], val);
210 break;
211 case Config::kOpl3:
212 writeOplReg(chip, index[chip], val);
213 }
214 } else {
215 switch(_type) {
216 case Config::kOpl2:
217 index[0] = val;
218 break;
219 case Config::kDualOpl2:
220 if (port & 8) {
221 index[0] = val;
222 index[1] = val;
223 } else
224 index[chip] = val;
225 break;
226 case Config::kOpl3:
227 index[chip] = val;
228 }
229 }
230 }
231
read(int port)232 byte OPL::read(int port) {
233 return 0;
234 }
235
writeReg(int r,int v)236 void OPL::writeReg(int r, int v) {
237 switch (_type) {
238 case Config::kOpl2:
239 writeOplReg(0, r, v);
240 break;
241 case Config::kDualOpl2:
242 writeOplReg(0, r, v);
243 writeOplReg(1, r, v);
244 break;
245 case Config::kOpl3:
246 writeOplReg(r >= 0x100, r & 0xff, v);
247 }
248 }
249
writeOplReg(int c,int r,int v)250 void OPL::writeOplReg(int c, int r, int v) {
251 if (r == 0x04 && c == 1 && _type == Config::kOpl3) {
252 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast<void *>(v & 0x3f));
253 } else if (r == 0x08 && c == 0) {
254 _params.kbd_split = (v >> 6) & 0x1;
255 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
256 } else if (r == 0xbd && c == 0) {
257 _params.hihat = v & 0x1;
258 _params.cymbal = (v >> 1) & 0x1;
259 _params.tomtom = (v >> 2) & 0x1;
260 _params.snare = (v >> 3) & 0x1;
261 _params.bass = (v >> 4) & 0x1;
262 _params.rhythm = (v >> 5) & 0x1;
263 _params.vib_depth = (v >> 6) & 0x1;
264 _params.am_depth = (v >> 7) & 0x1;
265 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
266 } else if (r < 0xa0 || r >= 0xe0) {
267 // Operator
268 int idx = regOffsetToOper[r & 0x1f];
269
270 if (idx == -1)
271 return;
272
273 if (c == 1)
274 idx += kOpl2Operators;
275
276 switch (r & 0xf0) {
277 case 0x20:
278 case 0x30:
279 _oper[idx].harmonic = v & 0xf;
280 _oper[idx].kbd_scale = (v >> 4) & 0x1;
281 _oper[idx].do_sustain = (v >> 5) & 0x1;
282 _oper[idx].vibrato = (v >> 6) & 0x1;
283 _oper[idx].am = (v >> 7) & 0x1;
284 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
285 break;
286 case 0x40:
287 case 0x50:
288 _oper[idx].volume = ~v & 0x3f;
289 _oper[idx].scale_level = (v >> 6) & 0x3;
290 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
291 break;
292 case 0x60:
293 case 0x70:
294 _oper[idx].decay = v & 0xf;
295 _oper[idx].attack = (v >> 4) & 0xf;
296 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
297 break;
298 case 0x80:
299 case 0x90:
300 _oper[idx].release = v & 0xf;
301 _oper[idx].sustain = (v >> 4) & 0xf;
302 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
303 break;
304 case 0xe0:
305 case 0xf0:
306 _oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3);
307 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
308 }
309 } else {
310 // Voice
311 int idx = r & 0xf;
312
313 if (idx >= kOpl2Voices)
314 return;
315
316 if (c == 1)
317 idx += kOpl2Voices;
318
319 int opIdx = voiceToOper0[idx];
320
321 switch (r & 0xf0) {
322 case 0xa0:
323 _voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff);
324 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
325 break;
326 case 0xb0:
327 _voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff);
328 _voice[idx].octave = (v >> 2) & 0x7;
329 _voice[idx].key_on = (v >> 5) & 0x1;
330 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
331 break;
332 case 0xc0:
333 _oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1;
334 _oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7;
335 if (_type == Config::kOpl3) {
336 _oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1;
337 _oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1;
338 }
339 snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]);
340 }
341 }
342 }
343
create(Config::OplType type)344 OPL *create(Config::OplType type) {
345 return new OPL(type);
346 }
347
348 } // End of namespace ALSA
349 } // End of namespace OPL
350