aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--audio/fmopl.cpp76
-rw-r--r--audio/fmopl.h77
-rw-r--r--audio/softsynth/opl/dosbox.cpp6
-rw-r--r--audio/softsynth/opl/dosbox.h6
-rw-r--r--audio/softsynth/opl/mame.cpp11
-rw-r--r--audio/softsynth/opl/mame.h6
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