AUDIO: Add a class representing a real OPL

This commit is contained in:
Matthew Hoops 2015-05-14 21:07:46 -04:00
parent bed9da8b9d
commit 4d56585112
2 changed files with 101 additions and 0 deletions

View File

@ -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),

View File

@ -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();