aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Hoops2015-05-14 21:07:46 -0400
committerMatthew Hoops2015-07-07 20:19:47 -0400
commit4d5658511228328a9e56d32448f9b598ecda696c (patch)
tree164d68f9ca5a6bed5e7c0b4526045067a0bd8c5e
parentbed9da8b9dbbaa19d317f71663e42875c1717fda (diff)
downloadscummvm-rg350-4d5658511228328a9e56d32448f9b598ecda696c.tar.gz
scummvm-rg350-4d5658511228328a9e56d32448f9b598ecda696c.tar.bz2
scummvm-rg350-4d5658511228328a9e56d32448f9b598ecda696c.zip
AUDIO: Add a class representing a real OPL
-rw-r--r--audio/fmopl.cpp58
-rw-r--r--audio/fmopl.h43
2 files changed, 101 insertions, 0 deletions
diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index 4bc902eba0..b0b3273e82 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -29,6 +29,7 @@
#include "common/config-manager.h"
#include "common/system.h"
#include "common/textconsole.h"
+#include "common/timer.h"
#include "common/translation.h"
namespace OPL {
@@ -185,6 +186,63 @@ void OPL::stop() {
bool OPL::_hasInstance = false;
+RealOPL::RealOPL() : _baseFreq(0), _remainingTicks(0) {
+}
+
+RealOPL::~RealOPL() {
+ // Stop callbacks, just in case. If it's still playing at this
+ // point, there's probably a bigger issue, though. The subclass
+ // needs to call stop() or the pointer can still use be used in
+ // the mixer thread at the same time.
+ stop();
+}
+
+void RealOPL::setCallbackFrequency(int timerFrequency) {
+ stopCallbacks();
+ startCallbacks(timerFrequency);
+}
+
+void RealOPL::startCallbacks(int timerFrequency) {
+ _baseFreq = timerFrequency;
+ assert(_baseFreq > 0);
+
+ // We can't request more a timer faster than 100Hz. We'll handle this by calling
+ // the proc multiple times in onTimer() later on.
+ if (timerFrequency > kMaxFreq)
+ timerFrequency = kMaxFreq;
+
+ _remainingTicks = 0;
+ g_system->getTimerManager()->installTimerProc(timerProc, 1000000 / timerFrequency, this, "RealOPL");
+}
+
+void RealOPL::stopCallbacks() {
+ g_system->getTimerManager()->removeTimerProc(timerProc);
+ _baseFreq = 0;
+ _remainingTicks = 0;
+}
+
+void RealOPL::timerProc(void *refCon) {
+ static_cast<RealOPL *>(refCon)->onTimer();
+}
+
+void RealOPL::onTimer() {
+ uint callbacks = 1;
+
+ if (_baseFreq > kMaxFreq) {
+ // We run faster than our max, so run the callback multiple
+ // times to approximate the actual timer callback frequency.
+ uint totalTicks = _baseFreq + _remainingTicks;
+ callbacks = totalTicks / kMaxFreq;
+ _remainingTicks = totalTicks % kMaxFreq;
+ }
+
+ // Call the callback multiple times. The if is on the inside of the
+ // loop in case the callback removes itself.
+ for (uint i = 0; i < callbacks; i++)
+ if (_callback && _callback->isValid())
+ (*_callback)();
+}
+
EmulatedOPL::EmulatedOPL() :
_nextTick(0),
_samplesPerTick(0),
diff --git a/audio/fmopl.h b/audio/fmopl.h
index 091e0bd88d..ba0872d87b 100644
--- a/audio/fmopl.h
+++ b/audio/fmopl.h
@@ -106,8 +106,14 @@ private:
static const EmulatorDescription _drivers[];
};
+/**
+ * The type of the OPL timer callback functor.
+ */
typedef Common::Functor0<void> TimerCallback;
+/**
+ * A representation of a Yamaha OPL chip.
+ */
class OPL {
private:
static bool _hasInstance;
@@ -194,6 +200,43 @@ protected:
Common::ScopedPtr<TimerCallback> _callback;
};
+/**
+ * An OPL that represents a real OPL, as opposed to an emulated one.
+ *
+ * This will use an actual timer instead of using one calculated from
+ * the number of samples in an AudioStream::readBuffer call.
+ */
+class RealOPL : public OPL {
+public:
+ RealOPL();
+ virtual ~RealOPL();
+
+ // OPL API
+ void setCallbackFrequency(int timerFrequency);
+
+protected:
+ // OPL API
+ void startCallbacks(int timerFrequency);
+ void stopCallbacks();
+
+private:
+ static void timerProc(void *refCon);
+ void onTimer();
+
+ uint _baseFreq;
+ uint _remainingTicks;
+
+ enum {
+ kMaxFreq = 100
+ };
+};
+
+/**
+ * An OPL that represents an emulated OPL.
+ *
+ * This will send callbacks based on the number of samples
+ * decoded in readBuffer().
+ */
class EmulatedOPL : public OPL, protected Audio::AudioStream {
public:
EmulatedOPL();