allow dynamically setting samples per frame
git-svn-id: https://gambatte.svn.sourceforge.net/svnroot/gambatte@175 9dfb2916-2d38-0410-aef4-c5fe6c9ffc24
This commit is contained in:
parent
5e596dcfa3
commit
248dee24a5
@ -1,5 +1,5 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -174,6 +174,7 @@ mw(mw), source(source), frameTime(4389, 262144) {
|
||||
mw->addAction(hideMenuAct);
|
||||
|
||||
mw->setFrameTime(frameTime.get().num, frameTime.get().denom);
|
||||
mw->setSamplesPerFrame(35112);
|
||||
connect(source, SIGNAL(blit()), mw, SLOT(blit()));
|
||||
connect(source, SIGNAL(setTurbo(bool)), mw, SLOT(setTurbo(bool)));
|
||||
connect(source, SIGNAL(togglePause()), pauseAction, SLOT(trigger()));
|
||||
@ -294,7 +295,7 @@ void GambatteMenuHandler::about() {
|
||||
mw,
|
||||
tr("About Gambatte"),
|
||||
tr("<h3>Gambatte Qt svn</h3>\
|
||||
<p><b>Author:</b> Sindre Aamås (<a href=\"mailto:aamas@stud.ntnu.no\">aamas@stud.ntnu.no</a>).<br>\
|
||||
<p><b>Author:</b> Sindre Aam<EFBFBD>s (<a href=\"mailto:aamas@stud.ntnu.no\">aamas@stud.ntnu.no</a>).<br>\
|
||||
<b>Homepage:</b> <a href=\"http://sourceforge.net/projects/gambatte\">http://sourceforge.net/projects/gambatte</a>.</p>\
|
||||
<p>Gambatte is an accuracy-focused, open-source, cross-platform Game Boy / Game Boy Color emulator written in C++. It is based on hundreds of corner case hardware tests, as well as previous documentation and reverse engineering efforts.</p>")
|
||||
);
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "gambattesource.h"
|
||||
|
||||
GambatteSource::GambatteSource() :
|
||||
MediaSource(Rational(35112), 2064),
|
||||
MediaSource(2064),
|
||||
blitter(*this) {
|
||||
gb.setInputStateGetter(&inputGetter);
|
||||
gb.setVideoBlitter(&blitter);
|
||||
|
@ -98,12 +98,17 @@ MainWindow::JoystickIniter::~JoystickIniter() {
|
||||
SDL_JoystickQuit();
|
||||
}
|
||||
|
||||
MainWindow::SampleBuffer::SampleBuffer(const std::size_t maxInSamples) : sndInBuffer(maxInSamples * 2), spfnum(0), samplesBuffered(0) {}
|
||||
void MainWindow::SampleBuffer::reset(const Rational &spf, const unsigned overupdate) {
|
||||
sndInBuffer.reset((spf.ceil() + overupdate) * 2);
|
||||
this->spf = spf;
|
||||
num = 0;
|
||||
samplesBuffered = 0;
|
||||
}
|
||||
|
||||
std::size_t MainWindow::SampleBuffer::update(qint16 *const out, MediaSource *const source, Resampler *const resampler) {
|
||||
spfnum += source->samplesPerFrame.num;
|
||||
const long insamples = spfnum / source->samplesPerFrame.denom;
|
||||
spfnum -= insamples * source->samplesPerFrame.denom;
|
||||
num += spf.num;
|
||||
const long insamples = num / spf.denom;
|
||||
num -= insamples * spf.denom;
|
||||
|
||||
samplesBuffered += source->update(sndInBuffer + samplesBuffered * 2, insamples - samplesBuffered);
|
||||
samplesBuffered -= insamples;
|
||||
@ -133,7 +138,7 @@ MainWindow::MainWindow(MediaSource *source,
|
||||
buttonHandlers(buttonInfos.size(), ButtonHandler(0, 0)),
|
||||
blitter(NULL),
|
||||
fullModeToggler(getFullModeToggler(winId())),
|
||||
sampleBuffer((source->samplesPerFrame.num - 1) / source->samplesPerFrame.denom + 1 + source->overupdate),
|
||||
sampleBuffer(Rational(735, 1), source->overupdate),
|
||||
sndOutBuffer(0),
|
||||
ae(NULL),
|
||||
cursorTimer(NULL),
|
||||
@ -471,10 +476,6 @@ void MainWindow::setFrameTime(unsigned num, unsigned denom) {
|
||||
setSampleRate();
|
||||
}
|
||||
|
||||
static long maxSamplesPerFrame(const MediaSource *const source) {
|
||||
return (source->samplesPerFrame.num - 1) / source->samplesPerFrame.denom + 1;
|
||||
}
|
||||
|
||||
static void adjustResamplerRate(Array<qint16> &sndOutBuf, Resampler *const resampler, const long maxspf, const long outRate) {
|
||||
resampler->adjustRate(resampler->inRate(), outRate);
|
||||
|
||||
@ -487,8 +488,8 @@ static void adjustResamplerRate(Array<qint16> &sndOutBuf, Resampler *const resam
|
||||
void MainWindow::setSampleRate() {
|
||||
if (ae) {
|
||||
const Rational fr(ftDenom, ftNum);
|
||||
const long insrate = fr.toDouble() * source->samplesPerFrame.toDouble() + 0.5;
|
||||
const long maxspf = maxSamplesPerFrame(source);
|
||||
const long insrate = fr.toDouble() * sampleBuffer.samplesPerFrame().toDouble() + 0.5;
|
||||
const long maxspf = sampleBuffer.samplesPerFrame().ceil();
|
||||
|
||||
resampler.reset();
|
||||
resampler.reset(ResamplerInfo::get(soundDialog->getResamplerNum()).create(insrate, ae->rate(), maxspf));
|
||||
@ -496,6 +497,11 @@ void MainWindow::setSampleRate() {
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setSamplesPerFrame(const long num, const long denom) {
|
||||
sampleBuffer.reset(Rational(num, denom), source->overupdate);
|
||||
setSampleRate();
|
||||
}
|
||||
|
||||
void MainWindow::initAudio() {
|
||||
if (ae)
|
||||
ae->uninit();
|
||||
@ -550,9 +556,9 @@ void MainWindow::timerEvent(QTimerEvent */*event*/) {
|
||||
est += var;
|
||||
|
||||
if (std::fabs(est - resampler->outRate() * static_cast<float>(usecft - (usecft >> 11))) > var * 2)
|
||||
adjustResamplerRate(sndOutBuffer, resampler.get(), maxSamplesPerFrame(source), est / (usecft - (usecft >> 11)));
|
||||
adjustResamplerRate(sndOutBuffer, resampler.get(), sampleBuffer.samplesPerFrame().ceil(), est / (usecft - (usecft >> 11)));
|
||||
} else if (resampler->outRate() != ae->rate())
|
||||
adjustResamplerRate(sndOutBuffer, resampler.get(), maxSamplesPerFrame(source), ae->rate());
|
||||
adjustResamplerRate(sndOutBuffer, resampler.get(), sampleBuffer.samplesPerFrame().ceil(), ae->rate());
|
||||
}
|
||||
|
||||
if (blitter->sync(syncft) < 0) {
|
||||
|
@ -70,11 +70,14 @@ private:
|
||||
|
||||
class SampleBuffer {
|
||||
Array<qint16> sndInBuffer;
|
||||
unsigned spfnum;
|
||||
Rational spf;
|
||||
unsigned num;
|
||||
unsigned samplesBuffered;
|
||||
|
||||
public:
|
||||
SampleBuffer(std::size_t maxInSamples);
|
||||
SampleBuffer(const Rational &spf, unsigned overupdate) { reset(spf, overupdate); }
|
||||
const Rational& samplesPerFrame() const { return spf; }
|
||||
void reset(const Rational &spf, unsigned overupdate);
|
||||
std::size_t update(qint16 *out, MediaSource *source, Resampler *resampler);
|
||||
};
|
||||
|
||||
@ -190,10 +193,17 @@ public:
|
||||
|
||||
/**
|
||||
* Sets time period in seconds between calls to source->update(). Eg. for 60 Hz updates
|
||||
* use setFrameTime(1, 60). Too high values of numerator or denominator _may_ lead to overflows.
|
||||
* use setFrameTime(1, 60).
|
||||
*/
|
||||
void setFrameTime(unsigned numerator, unsigned denominator);
|
||||
|
||||
/**
|
||||
* Sets the number of audio stereo source samples per video frame.
|
||||
* Eg. for a source sample rate of 44100 Hz at a frame rate of 60 fps use setSamplesPerFrame(44100, 60)
|
||||
* or setSamplesPerFrame(735).
|
||||
*/
|
||||
void setSamplesPerFrame(long num, long denom = 1);
|
||||
|
||||
/**
|
||||
* When pauseOnDialogExec is set, source->update()s will be paused when a dialog is launched
|
||||
* from MainWindow, and unpaused when the dialog is closed. pauseOnDialogExec is on by default.
|
||||
|
@ -33,6 +33,8 @@
|
||||
// (note that the source may support formats of higher details than it needs)
|
||||
|
||||
class MediaSource {
|
||||
protected:
|
||||
MediaSource(const unsigned overupdate = 0) : overupdate(overupdate) {}
|
||||
public:
|
||||
/**
|
||||
* Formats that you'll have to deal with if a BlitterWidget decides to give you a buffer with such a format.
|
||||
@ -90,11 +92,8 @@ public:
|
||||
int maxCustomRate;
|
||||
};
|
||||
|
||||
const Rational samplesPerFrame;
|
||||
const unsigned overupdate;
|
||||
|
||||
MediaSource(const Rational &samplesPerFrame, const unsigned overupdate = 0) : samplesPerFrame(samplesPerFrame), overupdate(overupdate) {}
|
||||
|
||||
/**
|
||||
* Reimplement to get buttonPress events for buttons of corresponding index to the
|
||||
* buttonInfos given to MainWindow.
|
||||
|
@ -26,6 +26,11 @@ struct Rational {
|
||||
Rational(const long num = 1, const long denom = 1) : num(num), denom(denom) {}
|
||||
float toFloat() const { return static_cast<float>(num) / denom; }
|
||||
double toDouble() const { return static_cast<double>(num) / denom; }
|
||||
|
||||
// assumes positive num and denom
|
||||
long ceil() const { return (num - 1) / denom + 1; }
|
||||
long floor() const { return num / denom; }
|
||||
long round() const { return (num + (denom >> 1)) / denom; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user