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 #include "audio/fmopl.h"
24
25 #include "audio/mixer.h"
26 #include "audio/softsynth/opl/dosbox.h"
27 #include "audio/softsynth/opl/mame.h"
28 #include "audio/softsynth/opl/nuked.h"
29
30 #include "common/config-manager.h"
31 #include "common/system.h"
32 #include "common/textconsole.h"
33 #include "common/timer.h"
34 #include "common/translation.h"
35
36 namespace OPL {
37
38 // Factory functions
39
40 #ifdef USE_ALSA
41 namespace ALSA {
42 OPL *create(Config::OplType type);
43 } // End of namespace ALSA
44 #endif // USE_ALSA
45
46 #ifdef ENABLE_OPL2LPT
47 namespace OPL2LPT {
48 OPL *create(Config::OplType type);
49 } // End of namespace OPL2LPT
50 #endif // ENABLE_OPL2LPT
51
52 // Config implementation
53
54 enum OplEmulator {
55 kAuto = 0,
56 kMame = 1,
57 kDOSBox = 2,
58 kALSA = 3,
59 kNuked = 4,
60 kOPL2LPT = 5,
61 kOPL3LPT = 6
62 };
63
OPL()64 OPL::OPL() {
65 if (_hasInstance)
66 error("There are multiple OPL output instances running");
67 _hasInstance = true;
68 }
69
70 const Config::EmulatorDescription Config::_drivers[] = {
71 { "auto", "<default>", kAuto, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
72 { "mame", _s("MAME OPL emulator"), kMame, kFlagOpl2 },
73 #ifndef DISABLE_DOSBOX_OPL
74 { "db", _s("DOSBox OPL emulator"), kDOSBox, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
75 #endif
76 #ifndef DISABLE_NUKED_OPL
77 { "nuked", _s("Nuked OPL emulator"), kNuked, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
78 #endif
79 #ifdef USE_ALSA
80 { "alsa", _s("ALSA Direct FM"), kALSA, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
81 #endif
82 #ifdef ENABLE_OPL2LPT
83 { "opl2lpt", _s("OPL2LPT"), kOPL2LPT, kFlagOpl2},
84 { "opl3lpt", _s("OPL3LPT"), kOPL3LPT, kFlagOpl2 | kFlagOpl3 },
85 #endif
86 { 0, 0, 0, 0 }
87 };
88
parse(const Common::String & name)89 Config::DriverId Config::parse(const Common::String &name) {
90 for (int i = 0; _drivers[i].name; ++i) {
91 if (name.equalsIgnoreCase(_drivers[i].name))
92 return _drivers[i].id;
93 }
94
95 return -1;
96 }
97
findDriver(DriverId id)98 const Config::EmulatorDescription *Config::findDriver(DriverId id) {
99 for (int i = 0; _drivers[i].name; ++i) {
100 if (_drivers[i].id == id)
101 return &_drivers[i];
102 }
103
104 return 0;
105 }
106
detect(OplType type)107 Config::DriverId Config::detect(OplType type) {
108 uint32 flags = 0;
109 switch (type) {
110 case kOpl2:
111 flags = kFlagOpl2;
112 break;
113
114 case kDualOpl2:
115 flags = kFlagDualOpl2;
116 break;
117
118 case kOpl3:
119 flags = kFlagOpl3;
120 break;
121 }
122
123 DriverId drv = parse(ConfMan.get("opl_driver"));
124 if (drv == kAuto) {
125 // Since the "auto" can be explicitly set for a game, and this
126 // driver shows up in the GUI as "<default>", check if there is
127 // a global setting for it before resorting to auto-detection.
128 drv = parse(ConfMan.get("opl_driver", Common::ConfigManager::kApplicationDomain));
129 }
130
131 // When a valid driver is selected, check whether it supports
132 // the requested OPL chip.
133 if (drv != -1 && drv != kAuto) {
134 const EmulatorDescription *driverDesc = findDriver(drv);
135 // If the chip is supported, just use the driver.
136 if (!driverDesc) {
137 warning("The selected OPL driver %d could not be found", drv);
138 } else if ((flags & driverDesc->flags)) {
139 return drv;
140 } else {
141 // Else we will output a warning and just
142 // return that no valid driver is found.
143 warning("Your selected OPL driver \"%s\" does not support type %d emulation, which is requested by your game", _drivers[drv].description, type);
144 return -1;
145 }
146 }
147
148 // Detect the first matching emulator
149 drv = -1;
150
151 for (int i = 1; _drivers[i].name; ++i) {
152 if (_drivers[i].flags & flags) {
153 drv = _drivers[i].id;
154 break;
155 }
156 }
157
158 return drv;
159 }
160
create(OplType type)161 OPL *Config::create(OplType type) {
162 return create(kAuto, type);
163 }
164
create(DriverId driver,OplType type)165 OPL *Config::create(DriverId driver, OplType type) {
166 // On invalid driver selection, we try to do some fallback detection
167 if (driver == -1) {
168 warning("Invalid OPL driver selected, trying to detect a fallback emulator");
169 driver = kAuto;
170 }
171
172 // If autodetection is selected, we search for a matching
173 // driver.
174 if (driver == kAuto) {
175 driver = detect(type);
176
177 // No emulator for the specified OPL chip could
178 // be found, thus stop here.
179 if (driver == -1) {
180 warning("No OPL emulator available for type %d", type);
181 return 0;
182 }
183 }
184
185 switch (driver) {
186 case kMame:
187 if (type == kOpl2)
188 return new MAME::OPL();
189 else
190 warning("MAME OPL emulator only supports OPL2 emulation");
191 return 0;
192
193 #ifndef DISABLE_DOSBOX_OPL
194 case kDOSBox:
195 return new DOSBox::OPL(type);
196 #endif
197
198 #ifndef DISABLE_NUKED_OPL
199 case kNuked:
200 return new NUKED::OPL(type);
201 #endif
202
203 #ifdef USE_ALSA
204 case kALSA:
205 return ALSA::create(type);
206 #endif
207
208 #ifdef ENABLE_OPL2LPT
209 case kOPL2LPT:
210 if (type == kOpl2) {
211 return OPL2LPT::create(type);
212 }
213
214 warning("OPL2LPT only supprts OPL2");
215 return 0;
216 case kOPL3LPT:
217 if (type == kOpl2 || type == kOpl3) {
218 return OPL2LPT::create(type);
219 }
220
221 warning("OPL3LPT does not support dual OPL2");
222 return 0;
223 #endif
224
225 default:
226 warning("Unsupported OPL emulator %d", driver);
227 // TODO: Maybe we should add some dummy emulator too, which just outputs
228 // silence as sound?
229 return 0;
230 }
231 }
232
start(TimerCallback * callback,int timerFrequency)233 void OPL::start(TimerCallback *callback, int timerFrequency) {
234 _callback.reset(callback);
235 startCallbacks(timerFrequency);
236 }
237
stop()238 void OPL::stop() {
239 stopCallbacks();
240 _callback.reset();
241 }
242
243 bool OPL::_hasInstance = false;
244
RealOPL()245 RealOPL::RealOPL() : _baseFreq(0), _remainingTicks(0) {
246 }
247
~RealOPL()248 RealOPL::~RealOPL() {
249 // Stop callbacks, just in case. If it's still playing at this
250 // point, there's probably a bigger issue, though. The subclass
251 // needs to call stop() or the pointer can still use be used in
252 // the mixer thread at the same time.
253 stop();
254 }
255
setCallbackFrequency(int timerFrequency)256 void RealOPL::setCallbackFrequency(int timerFrequency) {
257 stopCallbacks();
258 startCallbacks(timerFrequency);
259 }
260
startCallbacks(int timerFrequency)261 void RealOPL::startCallbacks(int timerFrequency) {
262 _baseFreq = timerFrequency;
263 assert(_baseFreq > 0);
264
265 // We can't request more a timer faster than 100Hz. We'll handle this by calling
266 // the proc multiple times in onTimer() later on.
267 if (timerFrequency > kMaxFreq)
268 timerFrequency = kMaxFreq;
269
270 _remainingTicks = 0;
271 g_system->getTimerManager()->installTimerProc(timerProc, 1000000 / timerFrequency, this, "RealOPL");
272 }
273
stopCallbacks()274 void RealOPL::stopCallbacks() {
275 g_system->getTimerManager()->removeTimerProc(timerProc);
276 _baseFreq = 0;
277 _remainingTicks = 0;
278 }
279
timerProc(void * refCon)280 void RealOPL::timerProc(void *refCon) {
281 static_cast<RealOPL *>(refCon)->onTimer();
282 }
283
onTimer()284 void RealOPL::onTimer() {
285 uint callbacks = 1;
286
287 if (_baseFreq > kMaxFreq) {
288 // We run faster than our max, so run the callback multiple
289 // times to approximate the actual timer callback frequency.
290 uint totalTicks = _baseFreq + _remainingTicks;
291 callbacks = totalTicks / kMaxFreq;
292 _remainingTicks = totalTicks % kMaxFreq;
293 }
294
295 // Call the callback multiple times. The if is on the inside of the
296 // loop in case the callback removes itself.
297 for (uint i = 0; i < callbacks; i++)
298 if (_callback && _callback->isValid())
299 (*_callback)();
300 }
301
EmulatedOPL()302 EmulatedOPL::EmulatedOPL() :
303 _nextTick(0),
304 _samplesPerTick(0),
305 _baseFreq(0),
306 _handle(new Audio::SoundHandle()) {
307 }
308
~EmulatedOPL()309 EmulatedOPL::~EmulatedOPL() {
310 // Stop callbacks, just in case. If it's still playing at this
311 // point, there's probably a bigger issue, though. The subclass
312 // needs to call stop() or the pointer can still use be used in
313 // the mixer thread at the same time.
314 stop();
315
316 delete _handle;
317 }
318
readBuffer(int16 * buffer,const int numSamples)319 int EmulatedOPL::readBuffer(int16 *buffer, const int numSamples) {
320 const int stereoFactor = isStereo() ? 2 : 1;
321 int len = numSamples / stereoFactor;
322 int step;
323
324 do {
325 step = len;
326 if (step > (_nextTick >> FIXP_SHIFT))
327 step = (_nextTick >> FIXP_SHIFT);
328
329 generateSamples(buffer, step * stereoFactor);
330
331 _nextTick -= step << FIXP_SHIFT;
332 if (!(_nextTick >> FIXP_SHIFT)) {
333 if (_callback && _callback->isValid())
334 (*_callback)();
335
336 _nextTick += _samplesPerTick;
337 }
338
339 buffer += step * stereoFactor;
340 len -= step;
341 } while (len);
342
343 return numSamples;
344 }
345
getRate() const346 int EmulatedOPL::getRate() const {
347 return g_system->getMixer()->getOutputRate();
348 }
349
startCallbacks(int timerFrequency)350 void EmulatedOPL::startCallbacks(int timerFrequency) {
351 setCallbackFrequency(timerFrequency);
352 g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
353 }
354
stopCallbacks()355 void EmulatedOPL::stopCallbacks() {
356 g_system->getMixer()->stopHandle(*_handle);
357 }
358
setCallbackFrequency(int timerFrequency)359 void EmulatedOPL::setCallbackFrequency(int timerFrequency) {
360 _baseFreq = timerFrequency;
361 assert(_baseFreq != 0);
362
363 int d = getRate() / _baseFreq;
364 int r = getRate() % _baseFreq;
365
366 // This is equivalent to (getRate() << FIXP_SHIFT) / BASE_FREQ
367 // but less prone to arithmetic overflow.
368
369 _samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq;
370 }
371
372 } // End of namespace OPL
373