diff options
-rw-r--r-- | audio/fmopl.cpp | 76 | ||||
-rw-r--r-- | audio/fmopl.h | 77 | ||||
-rw-r--r-- | audio/softsynth/opl/dosbox.cpp | 6 | ||||
-rw-r--r-- | audio/softsynth/opl/dosbox.h | 6 | ||||
-rw-r--r-- | audio/softsynth/opl/mame.cpp | 11 | ||||
-rw-r--r-- | audio/softsynth/opl/mame.h | 6 |
6 files changed, 174 insertions, 8 deletions
diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp index 3ee557366c..c9cb1040d6 100644 --- a/audio/fmopl.cpp +++ b/audio/fmopl.cpp @@ -22,10 +22,12 @@ #include "audio/fmopl.h" +#include "audio/mixer.h" #include "audio/softsynth/opl/dosbox.h" #include "audio/softsynth/opl/mame.h" #include "common/config-manager.h" +#include "common/system.h" #include "common/textconsole.h" #include "common/translation.h" @@ -171,6 +173,80 @@ OPL *Config::create(DriverId driver, OplType type) { } } +void OPL::start(TimerCallback *callback, int timerFrequency) { + _callback.reset(callback); + startCallbacks(timerFrequency); +} + +void OPL::stop() { + stopCallbacks(); + _callback.reset(); +} + bool OPL::_hasInstance = false; +EmulatedOPL::EmulatedOPL() : + _nextTick(0), + _samplesPerTick(0), + _baseFreq(0) { +} + +EmulatedOPL::~EmulatedOPL() { + // Stop callbacks, just in case. If it's still playing at this + // point, there's probably a bigger issue, though. + stopCallbacks(); +} + +int EmulatedOPL::readBuffer(int16 *buffer, const int numSamples) { + const int stereoFactor = isStereo() ? 2 : 1; + int len = numSamples / stereoFactor; + int step; + + do { + step = len; + if (step > (_nextTick >> FIXP_SHIFT)) + step = (_nextTick >> FIXP_SHIFT); + + generateSamples(buffer, step * stereoFactor); + + _nextTick -= step << FIXP_SHIFT; + if (!(_nextTick >> FIXP_SHIFT)) { + if (_callback && _callback->isValid()) + (*_callback)(); + + _nextTick += _samplesPerTick; + } + + buffer += step * stereoFactor; + len -= step; + } while (len); + + return numSamples; +} + +int EmulatedOPL::getRate() const { + return g_system->getMixer()->getOutputRate(); +} + +void EmulatedOPL::startCallbacks(int timerFrequency) { + _baseFreq = timerFrequency; + assert(_baseFreq != 0); + + int d = getRate() / _baseFreq; + int r = getRate() % _baseFreq; + + // This is equivalent to (getRate() << FIXP_SHIFT) / BASE_FREQ + // but less prone to arithmetic overflow. + + _samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq; + + // TODO: Eventually start mixer playback here + //g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); +} + +void EmulatedOPL::stopCallbacks() { + // TODO: Eventually stop mixer playback here + //g_system->getMixer()->stopHandle(*_handle); +} + } // End of namespace OPL diff --git a/audio/fmopl.h b/audio/fmopl.h index aaa8edd42d..0e2e2b2dda 100644 --- a/audio/fmopl.h +++ b/audio/fmopl.h @@ -23,6 +23,8 @@ #ifndef AUDIO_FMOPL_H #define AUDIO_FMOPL_H +#include "common/func.h" +#include "common/ptr.h" #include "common/scummsys.h" namespace Common { @@ -98,6 +100,8 @@ private: static const EmulatorDescription _drivers[]; }; +typedef Common::Functor0<void> TimerCallback; + class OPL { private: static bool _hasInstance; @@ -154,12 +158,83 @@ public: * So if you request 4 samples from a stereo OPL, you will get * a total of two left channel and two right channel samples. */ - virtual void readBuffer(int16 *buffer, int length) = 0; + virtual int readBuffer(int16 *buffer, const int numSamples) = 0; /** * Returns whether the setup OPL mode is stereo or not */ virtual bool isStereo() const = 0; + + /** + * Start the OPL with callbacks. + */ + void start(TimerCallback *callback, int timerFrequency = kDefaultCallbackFrequency); + + /** + * Stop the OPL + */ + void stop(); + + enum { + /** + * The default callback frequency that start() uses + */ + kDefaultCallbackFrequency = 250 + }; + +protected: + /** + * Start the callbacks. + */ + virtual void startCallbacks(int timerFrequency) = 0; + + /** + * Stop the callbacks. + */ + virtual void stopCallbacks() = 0; + + /** + * The functor for callbacks. + */ + Common::ScopedPtr<TimerCallback> _callback; +}; + +class EmulatedOPL : public OPL { +public: + EmulatedOPL(); + virtual ~EmulatedOPL(); + + // OPL API + int readBuffer(int16 *buffer, const int numSamples); + + int getRate() const; + +protected: + // OPL API + void startCallbacks(int timerFrequency); + void stopCallbacks(); + + /** + * Read up to 'length' samples. + * + * Data will be in native endianess, 16 bit per sample, signed. + * For stereo OPL, buffer will be filled with interleaved + * left and right channel samples, starting with a left sample. + * Furthermore, the samples in the left and right are summed up. + * So if you request 4 samples from a stereo OPL, you will get + * a total of two left channel and two right channel samples. + */ + virtual void generateSamples(int16 *buffer, int numSamples) = 0; + +private: + int _baseFreq; + + enum { + FIXP_SHIFT = 16 + }; + + int _nextTick; + int _samplesPerTick; }; } // End of namespace OPL diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp index bcc73a9b9e..f6f17c5e3c 100644 --- a/audio/softsynth/opl/dosbox.cpp +++ b/audio/softsynth/opl/dosbox.cpp @@ -153,6 +153,7 @@ OPL::~OPL() { } void OPL::free() { + stopCallbacks(); delete _emulator; _emulator = 0; } @@ -176,6 +177,9 @@ bool OPL::init() { _emulator->WriteReg(0x105, 1); } + // FIXME: Remove this once EmulatedOPL is actually controlling playback + start(0); + return true; } @@ -308,7 +312,7 @@ void OPL::dualWrite(uint8 index, uint8 reg, uint8 val) { _emulator->WriteReg(fullReg, val); } -void OPL::readBuffer(int16 *buffer, int length) { +void OPL::generateSamples(int16 *buffer, int length) { // For stereo OPL cards, we divide the sample count by 2, // to match stereo AudioStream behavior. if (_type != Config::kOpl2) diff --git a/audio/softsynth/opl/dosbox.h b/audio/softsynth/opl/dosbox.h index d3df6ba6ed..c52f06761a 100644 --- a/audio/softsynth/opl/dosbox.h +++ b/audio/softsynth/opl/dosbox.h @@ -69,7 +69,7 @@ namespace DBOPL { struct Chip; } // end of namespace DBOPL -class OPL : public ::OPL::OPL { +class OPL : public ::OPL::EmulatedOPL { private: Config::OplType _type; uint _rate; @@ -95,8 +95,10 @@ public: void writeReg(int r, int v); - void readBuffer(int16 *buffer, int length); bool isStereo() const { return _type != Config::kOpl2; } + +protected: + void generateSamples(int16 *buffer, int length); }; } // End of namespace DOSBox diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp index 1a5810f6c8..fe23d300fa 100644 --- a/audio/softsynth/opl/mame.cpp +++ b/audio/softsynth/opl/mame.cpp @@ -48,15 +48,22 @@ namespace OPL { namespace MAME { OPL::~OPL() { + stopCallbacks(); MAME::OPLDestroy(_opl); _opl = 0; } bool OPL::init() { - if (_opl) + if (_opl) { + stopCallbacks(); MAME::OPLDestroy(_opl); + } _opl = MAME::makeAdLibOPL(g_system->getMixer()->getOutputRate()); + + // FIXME: Remove this once EmulatedOPL is actually controlling playback + start(0); + return (_opl != 0); } @@ -76,7 +83,7 @@ void OPL::writeReg(int r, int v) { MAME::OPLWriteReg(_opl, r, v); } -void OPL::readBuffer(int16 *buffer, int length) { +void OPL::generateSamples(int16 *buffer, int length) { MAME::YM3812UpdateOne(_opl, buffer, length); } diff --git a/audio/softsynth/opl/mame.h b/audio/softsynth/opl/mame.h index 080cc6d171..67d80bb193 100644 --- a/audio/softsynth/opl/mame.h +++ b/audio/softsynth/opl/mame.h @@ -174,7 +174,7 @@ void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length); FM_OPL *makeAdLibOPL(int rate); // OPL API implementation -class OPL : public ::OPL::OPL { +class OPL : public ::OPL::EmulatedOPL { private: FM_OPL *_opl; public: @@ -189,8 +189,10 @@ public: void writeReg(int r, int v); - void readBuffer(int16 *buffer, int length); bool isStereo() const { return false; } + +protected: + void generateSamples(int16 *buffer, int length); }; } // End of namespace MAME |