From 4f0768b9096aab65961d697ca60d6e939f36eae2 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Tue, 12 May 2009 18:42:44 +0000 Subject: - Add support for selecting the OPL emulator being used (config entry: "opl_driver") - Make MAME FM OPL the default emulator again - Add GUI support for selecting the active OPL emulator - Update themes svn-id: r40496 --- sound/fmopl.cpp | 104 +++++++++++++++++++++++++++++++++++------ sound/fmopl.h | 70 +++++++++++++++++++++++---- sound/softsynth/opl/dosbox.cpp | 36 +++++++------- sound/softsynth/opl/dosbox.h | 6 +-- 4 files changed, 172 insertions(+), 44 deletions(-) (limited to 'sound') diff --git a/sound/fmopl.cpp b/sound/fmopl.cpp index eb84d37366..f0f7327fd4 100644 --- a/sound/fmopl.cpp +++ b/sound/fmopl.cpp @@ -27,27 +27,101 @@ #include "sound/softsynth/opl/dosbox.h" #include "sound/softsynth/opl/mame.h" +#include "common/config-manager.h" + namespace OPL { -bool OPL::_hasInstance = false; +// Config implementation -OPL *OPL::create(kOplType type) { - // Simple hack to assure we only create one instance, - // since the DOSBox OPL emulator does not allow more than - // one instance. - assert(!_hasInstance); - _hasInstance = true; +enum OplEmulator { + kMame = 0, + kDOSBox = 1 +}; -#ifdef DISABLE_DOSBOX_OPL - if (type != kOpl2) - return 0; - else - return new MAME::OPL(); -#else - return new DOSBox::OPL(type); +const Config::EmulatorDescription Config::_drivers[] = { + { "auto", "", 0, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 }, + { "mame", "MAME OPL emulator", kMame, kFlagOpl2 }, +#ifndef DISABLE_DOSBOX_OPL + { "db", "DOSBox OPL emulator (experimental)", kDOSBox, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 }, #endif + { 0, 0, 0, 0 } +}; + +Config::DriverId Config::parse(const Common::String &name) { + for (int i = 0; _drivers[i].name; ++i) { + if (name.equalsIgnoreCase(_drivers[i].name)) + return _drivers[i].id; + } + + return 0; } +Config::DriverId Config::detect(OplType type) { + uint32 flags = 0; + switch (type) { + case kOpl2: + flags = kFlagOpl2; + break; + + case kDualOpl2: + flags = kFlagDualOpl2; + break; + + case kOpl3: + flags = kFlagOpl3; + break; + } + + DriverId drv = parse(ConfMan.get("opl_driver")); + + // When a valid driver is selected, check whether it supports + // the requested OPL chip. + if (validDriver(drv)) { + // If the chip is supported, just use the driver. + if ((flags & _drivers[drv].flags)) + return drv; + + // When it doesn't support the flags fall back to auto detection + } + + // Detect the first matching emulator + for (int i = 1; _drivers[i].name; ++i) { + if (_drivers[i].flags & flags) { + drv = _drivers[i].id; + break; + } + } + + return drv; +} + +OPL *Config::create(DriverId driver, OplType type) { + if (!validDriver(driver)) + driver = detect(type); + + switch (driver) { + case kMame: + if (type == kOpl2) + return new MAME::OPL(); + else + warning("MAME OPL emulator only supports OPL2 emulation."); + return 0; + +#ifndef DISABLE_DOSBOX_OPL + case kDOSBox: + return new DOSBox::OPL(type); +#endif + + default: + warning("Unsupported OPL emulator %d", driver); + // TODO: Maybe we should add some dummy emulator too, which just outputs + // silence as sound? + return 0; + } +} + +bool OPL::_hasInstance = false; + } // end of namespace OPL void OPLDestroy(FM_OPL *OPL) { @@ -75,7 +149,7 @@ void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length) { } FM_OPL *makeAdlibOPL(int rate) { - FM_OPL *opl = OPL::OPL::create(OPL::OPL::kOpl2); + FM_OPL *opl = OPL::Config::create(OPL::Config::kOpl2); if (opl) { if (!opl->init(rate)) { diff --git a/sound/fmopl.h b/sound/fmopl.h index 7713f76e6a..d84b62f693 100644 --- a/sound/fmopl.h +++ b/sound/fmopl.h @@ -26,25 +26,81 @@ #define SOUND_FMOPL_H #include "common/scummsys.h" +#include "common/str.h" namespace OPL { -class OPL { -private: - // TODO: This is part of a temporary HACK to allow only 1 instance - static bool _hasInstance; +class OPL; + +class Config { public: - virtual ~OPL() { _hasInstance = false; } + enum OplFlags { + kFlagOpl2 = (1 << 0), + kFlagDualOpl2 = (1 << 1), + kFlagOpl3 = (1 << 2) + }; /** * OPL type to emulate. */ - enum kOplType { + enum OplType { kOpl2, kDualOpl2, kOpl3 }; + typedef int8 DriverId; + struct EmulatorDescription { + const char *name; + const char *description; + + DriverId id; // A unique ID for each driver + uint32 flags; // Capabilities of this driver + }; + + /** + * Get a list of all available OPL emulators. + * @return list of all available OPL emulators, terminated by a zero entry + */ + static const EmulatorDescription *getAvailable() { return _drivers; } + + /** + * Returns the driver id of a given name. + */ + static DriverId parse(const Common::String &name); + + /** + * Detects a driver for the specific type. + */ + static DriverId detect(OplType type = kOpl2); + + /** + * Checks whether the driver id is valid. + */ + static bool validDriver(DriverId id) { return (id > 0); } + + /** + * Creates the specific driver with a specific type setup. + */ + static OPL *create(DriverId driver, OplType type); + + /** + * Wrapper to easily init an OPL chip, without specifing an emulator. + */ + static OPL *create(OplType type) { return create(detect(type), type); } + +private: + static const EmulatorDescription _drivers[]; +}; + +class OPL { +private: + // TODO: This is part of a temporary HACK to allow only 1 instance + static bool _hasInstance; +public: + OPL() { _hasInstance = true; } + virtual ~OPL() { _hasInstance = false; } + /** * Initializes the OPL emulator. * @@ -99,8 +155,6 @@ public: * Returns whether the setup OPL mode is stereo or not */ virtual bool isStereo() const = 0; - - static OPL *create(kOplType type); }; } // end of namespace OPL diff --git a/sound/softsynth/opl/dosbox.cpp b/sound/softsynth/opl/dosbox.cpp index db8f8109d7..cbb3090608 100644 --- a/sound/softsynth/opl/dosbox.cpp +++ b/sound/softsynth/opl/dosbox.cpp @@ -190,7 +190,7 @@ struct Handler : public DOSBox::Handler { }; } // end of namespace OPL3 -OPL::OPL(kOplType type) : _type(type), _rate(0), _handler(0) { +OPL::OPL(Config::OplType type) : _type(type), _rate(0), _handler(0) { } OPL::~OPL() { @@ -209,12 +209,12 @@ bool OPL::init(int rate) { memset(_chip, 0, sizeof(_chip)); switch (_type) { - case kOpl2: + case Config::kOpl2: _handler = new OPL2::Handler(); break; - case kDualOpl2: - case kOpl3: + case Config::kDualOpl2: + case Config::kOpl3: _handler = new OPL3::Handler(); break; @@ -224,7 +224,7 @@ bool OPL::init(int rate) { _handler->init(rate); - if (_type == kDualOpl2) { + if (_type == Config::kDualOpl2) { // Setup opl3 mode in the hander _handler->writeReg(0x105, 1); } @@ -240,12 +240,12 @@ void OPL::reset() { void OPL::write(int port, int val) { if (port&1) { switch (_type) { - case kOpl2: - case kOpl3: + case Config::kOpl2: + case Config::kOpl3: if (!_chip[0].write(_reg.normal, val)) _handler->writeReg(_reg.normal, val); break; - case kDualOpl2: + case Config::kDualOpl2: // Not a 0x??8 port, then write to a specific port if (!(port & 0x8)) { byte index = (port & 2) >> 1; @@ -261,13 +261,13 @@ void OPL::write(int port, int val) { // Ask the handler to write the address // Make sure to clip them in the right range switch (_type) { - case kOpl2: + case Config::kOpl2: _reg.normal = _handler->writeAddr(port, val) & 0xff; break; - case kOpl3: + case Config::kOpl3: _reg.normal = _handler->writeAddr(port, val) & 0x1ff; break; - case kDualOpl2: + case Config::kDualOpl2: // Not a 0x?88 port, when write to a specific side if (!(port & 0x8)) { byte index = (port & 2) >> 1; @@ -283,16 +283,16 @@ void OPL::write(int port, int val) { byte OPL::read(int port) { switch (_type) { - case kOpl2: + case Config::kOpl2: if (!(port & 1)) //Make sure the low bits are 6 on opl2 return _chip[0].read() | 0x6; break; - case kOpl3: + case Config::kOpl3: if (!(port & 1)) return _chip[0].read(); break; - case kDualOpl2: + case Config::kDualOpl2: // Only return for the lower ports if (port & 1) return 0xff; @@ -305,9 +305,9 @@ byte OPL::read(int port) { void OPL::writeReg(int r, int v) { byte tempReg = 0; switch (_type) { - case kOpl2: - case kDualOpl2: - case kOpl3: + case Config::kOpl2: + case Config::kDualOpl2: + case Config::kOpl3: // We can't use _handler->writeReg here directly, since it would miss timer changes. // Backup old setup register @@ -350,7 +350,7 @@ void OPL::dualWrite(uint8 index, uint8 reg, uint8 val) { void OPL::readBuffer(int16 *buffer, int length) { // For stereo OPL cards, we divide the sample count by 2, // to match stereo AudioStream behavior. - if (_type != kOpl2) + if (_type != Config::kOpl2) length >>= 1; _handler->generate(buffer, length); diff --git a/sound/softsynth/opl/dosbox.h b/sound/softsynth/opl/dosbox.h index 68a53276f0..488cc3d82a 100644 --- a/sound/softsynth/opl/dosbox.h +++ b/sound/softsynth/opl/dosbox.h @@ -85,7 +85,7 @@ public: class OPL : public ::OPL::OPL { private: - kOplType _type; + Config::OplType _type; uint _rate; Handler *_handler; @@ -98,7 +98,7 @@ private: void free(); void dualWrite(uint8 index, uint8 reg, uint8 val); public: - OPL(kOplType type); + OPL(Config::OplType type); ~OPL(); bool init(int rate); @@ -110,7 +110,7 @@ public: void writeReg(int r, int v); void readBuffer(int16 *buffer, int length); - bool isStereo() const { return _type != kOpl2; } + bool isStereo() const { return _type != Config::kOpl2; } }; } // end of namespace DOSBox -- cgit v1.2.3