add custom sample rate support

change default buffer latency to 67 ms
don't auto-repeat buttons bound to keyboard
use enums for somewhat more robust gambattesource button setup


git-svn-id: https://gambatte.svn.sourceforge.net/svnroot/gambatte@154 9dfb2916-2d38-0410-aef4-c5fe6c9ffc24
This commit is contained in:
sinamas 2008-05-28 19:29:49 +00:00
parent 3b7abdd585
commit d4dd9a2a85
8 changed files with 200 additions and 100 deletions

View File

@ -31,31 +31,6 @@ static const MediaSource::ButtonInfo constructButtonInfo(const char *label, cons
return bi;
}
const std::vector<MediaSource::ButtonInfo> GambatteSource::generateButtonInfos() {
std::vector<MediaSource::ButtonInfo> v(18);
v[0] = constructButtonInfo("Up", "Game", Qt::Key_Up);
v[1] = constructButtonInfo("Down", "Game", Qt::Key_Down);
v[2] = constructButtonInfo("Left", "Game", Qt::Key_Left);
v[3] = constructButtonInfo("Right", "Game", Qt::Key_Right);
v[4] = constructButtonInfo("A", "Game", Qt::Key_D);
v[5] = constructButtonInfo("B", "Game", Qt::Key_C);
v[6] = constructButtonInfo("Start", "Game", Qt::Key_Return);
v[7] = constructButtonInfo("Select", "Game", Qt::Key_Shift);
v[8] = constructButtonInfo("Pause", "Play", Qt::Key_Pause);
v[9] = constructButtonInfo("Frame step", "Play", Qt::Key_F1);
v[10] = constructButtonInfo("Decrease frame rate", "Play", Qt::Key_F2);
v[11] = constructButtonInfo("Increase frame rate", "Play", Qt::Key_F3);
v[12] = constructButtonInfo("Reset frame rate", "Play", Qt::Key_F4);
v[13] = constructButtonInfo("Fast forward", "Play", Qt::Key_Tab);
v[14] = constructButtonInfo("Save state", "State", Qt::Key_F5);
v[15] = constructButtonInfo("Load state", "State", Qt::Key_F8);
v[16] = constructButtonInfo("Previous state slot", "State", Qt::Key_F6);
v[17] = constructButtonInfo("Next state slot", "State", Qt::Key_F7);
return v;
}
const std::vector<MediaSource::VideoSourceInfo> GambatteSource::generateVideoSourceInfos() {
const std::vector<const Gambatte::FilterInfo*> &fi = gb.filterInfo();
std::vector<MediaSource::VideoSourceInfo> v(fi.size());
@ -69,49 +44,114 @@ const std::vector<MediaSource::VideoSourceInfo> GambatteSource::generateVideoSou
return v;
}
const std::vector<int> GambatteSource::generateSampleRates() {
std::vector<int> v(2);
const MediaSource::SampleRateInfo GambatteSource::generateSampleRateInfo() {
SampleRateInfo srinfo;
v[0] = 48000;
v[1] = 44100;
srinfo.rates.push_back(48000);
srinfo.rates.push_back(44100);
srinfo.defaultRateIndex = 0;
srinfo.minCustomRate = 32000;
srinfo.maxCustomRate = 192000;
return srinfo;
}
enum {
UP_BUTTON, DOWN_BUTTON, LEFT_BUTTON, RIGHT_BUTTON,
A_BUTTON, B_BUTTON, START_BUTTON, SELECT_BUTTON,
PAUSE_BUTTON, FRAME_STEP_BUTTON, DECREASE_FRAME_RATE_BUTTON, INCREASE_FRAME_RATE_BUTTON,
RESET_FRAME_RATE_BUTTON, FAST_FORWARD_BUTTON, SAVE_STATE_BUTTON, LOAD_STATE_BUTTON,
PREVIOUS_STATE_SLOT_BUTTON, NEXT_STATE_SLOT_BUTTON,
NUMBER_OF_BUTTONS
};
const std::vector<MediaSource::ButtonInfo> GambatteSource::generateButtonInfos() {
std::vector<MediaSource::ButtonInfo> v(NUMBER_OF_BUTTONS);
v[UP_BUTTON] = constructButtonInfo("Up", "Game", Qt::Key_Up);
v[DOWN_BUTTON] = constructButtonInfo("Down", "Game", Qt::Key_Down);
v[LEFT_BUTTON] = constructButtonInfo("Left", "Game", Qt::Key_Left);
v[RIGHT_BUTTON] = constructButtonInfo("Right", "Game", Qt::Key_Right);
v[A_BUTTON] = constructButtonInfo("A", "Game", Qt::Key_D);
v[B_BUTTON] = constructButtonInfo("B", "Game", Qt::Key_C);
v[START_BUTTON] = constructButtonInfo("Start", "Game", Qt::Key_Return);
v[SELECT_BUTTON] = constructButtonInfo("Select", "Game", Qt::Key_Shift);
v[PAUSE_BUTTON] = constructButtonInfo("Pause", "Play", Qt::Key_Pause);
v[FRAME_STEP_BUTTON] = constructButtonInfo("Frame step", "Play", Qt::Key_F1);
v[DECREASE_FRAME_RATE_BUTTON] = constructButtonInfo("Decrease frame rate", "Play", Qt::Key_F2);
v[INCREASE_FRAME_RATE_BUTTON] = constructButtonInfo("Increase frame rate", "Play", Qt::Key_F3);
v[RESET_FRAME_RATE_BUTTON] = constructButtonInfo("Reset frame rate", "Play", Qt::Key_F4);
v[FAST_FORWARD_BUTTON] = constructButtonInfo("Fast forward", "Play", Qt::Key_Tab);
v[SAVE_STATE_BUTTON] = constructButtonInfo("Save state", "State", Qt::Key_F5);
v[LOAD_STATE_BUTTON] = constructButtonInfo("Load state", "State", Qt::Key_F8);
v[PREVIOUS_STATE_SLOT_BUTTON] = constructButtonInfo("Previous state slot", "State", Qt::Key_F6);
v[NEXT_STATE_SLOT_BUTTON] = constructButtonInfo("Next state slot", "State", Qt::Key_F7);
return v;
}
void GambatteSource::buttonPressEvent(unsigned buttonIndex) {
switch (buttonIndex) {
case 0: inputGetter.is.dpadUp = true; inputGetter.is.dpadDown = false; break;
case 1: inputGetter.is.dpadDown = true; inputGetter.is.dpadUp = false; break;
case 2: inputGetter.is.dpadLeft = true; inputGetter.is.dpadRight = false; break;
case 3: inputGetter.is.dpadRight = true; inputGetter.is.dpadLeft = false; break;
case 4: inputGetter.is.aButton = true; break;
case 5: inputGetter.is.bButton = true; break;
case 6: inputGetter.is.startButton = true; break;
case 7: inputGetter.is.selectButton = true; break;
case 8: emit togglePause(); break;
case 9: emit frameStep(); break;
case 10: emit decFrameRate(); break;
case 11: emit incFrameRate(); break;
case 12: emit resetFrameRate(); break;
case 13: emit setTurbo(true); break;
case 14: saveState(); break;
case 15: loadState(); break;
case 16: emit prevStateSlot(); break;
case 17: emit nextStateSlot(); break;
case UP_BUTTON:
inputGetter.is.dpadUp = true; inputGetter.is.dpadDown = false; break;
case DOWN_BUTTON:
inputGetter.is.dpadDown = true; inputGetter.is.dpadUp = false; break;
case LEFT_BUTTON:
inputGetter.is.dpadLeft = true; inputGetter.is.dpadRight = false; break;
case RIGHT_BUTTON:
inputGetter.is.dpadRight = true; inputGetter.is.dpadLeft = false; break;
case A_BUTTON:
inputGetter.is.aButton = true; break;
case B_BUTTON:
inputGetter.is.bButton = true; break;
case START_BUTTON:
inputGetter.is.startButton = true; break;
case SELECT_BUTTON:
inputGetter.is.selectButton = true; break;
case PAUSE_BUTTON:
emit togglePause(); break;
case FRAME_STEP_BUTTON:
emit frameStep(); break;
case DECREASE_FRAME_RATE_BUTTON:
emit decFrameRate(); break;
case INCREASE_FRAME_RATE_BUTTON:
emit incFrameRate(); break;
case RESET_FRAME_RATE_BUTTON:
emit resetFrameRate(); break;
case FAST_FORWARD_BUTTON:
emit setTurbo(true); break;
case SAVE_STATE_BUTTON:
saveState(); break;
case LOAD_STATE_BUTTON:
loadState(); break;
case PREVIOUS_STATE_SLOT_BUTTON:
emit prevStateSlot(); break;
case NEXT_STATE_SLOT_BUTTON:
emit nextStateSlot(); break;
}
}
void GambatteSource::buttonReleaseEvent(unsigned buttonIndex) {
switch (buttonIndex) {
case 0: inputGetter.is.dpadUp = false; break;
case 1: inputGetter.is.dpadDown = false; break;
case 2: inputGetter.is.dpadLeft = false; break;
case 3: inputGetter.is.dpadRight = false; break;
case 4: inputGetter.is.aButton = false; break;
case 5: inputGetter.is.bButton = false; break;
case 6: inputGetter.is.startButton = false; break;
case 7: inputGetter.is.selectButton = false; break;
case 13: emit setTurbo(false); break;
case UP_BUTTON:
inputGetter.is.dpadUp = false; break;
case DOWN_BUTTON:
inputGetter.is.dpadDown = false; break;
case LEFT_BUTTON:
inputGetter.is.dpadLeft = false; break;
case RIGHT_BUTTON:
inputGetter.is.dpadRight = false; break;
case A_BUTTON:
inputGetter.is.aButton = false; break;
case B_BUTTON:
inputGetter.is.bButton = false; break;
case START_BUTTON:
inputGetter.is.startButton = false; break;
case SELECT_BUTTON:
inputGetter.is.selectButton = false; break;
case FAST_FORWARD_BUTTON:
emit setTurbo(false); break;
}
}

View File

@ -55,7 +55,7 @@ public:
static const std::vector<ButtonInfo> generateButtonInfos();
const std::vector<MediaSource::VideoSourceInfo> generateVideoSourceInfos();
static const std::vector<int> generateSampleRates();
static const SampleRateInfo generateSampleRateInfo();
void emitBlit() { emit blit(); }

View File

@ -34,7 +34,7 @@ int main(int argc, char *argv[]) {
source.generateVideoSourceInfos(),
MainWindow::tr("Video filter:"),
QSize(160, 144),
source.generateSampleRates());
source.generateSampleRateInfo());
GambatteMenuHandler mh(mw, &source, argc, argv);
mw->show();
return app.exec();

View File

@ -101,7 +101,7 @@ MainWindow::MainWindow(MediaSource *source,
const std::vector<MediaSource::VideoSourceInfo> &videoSourceInfos,
const QString &videoSourceLabel,
const QSize &aspectRatio,
const std::vector<int> &sampleRates) :
const MediaSource::SampleRateInfo &sampleRateInfo) :
source(source),
buttonHandlers(buttonInfos.size(), ButtonHandler(0, 0)),
blitter(NULL),
@ -139,7 +139,7 @@ MainWindow::MainWindow(MediaSource *source,
addAudioEngines(audioEngines, winId());
audioEngines.push_back(new NullAudioEngine);
soundDialog = new SoundDialog(audioEngines, sampleRates, this);
soundDialog = new SoundDialog(audioEngines, sampleRateInfo, this);
connect(soundDialog, SIGNAL(accepted()), this, SLOT(soundSettingsChange()));
inputDialog = new InputDialog(buttonInfos, this);
@ -416,8 +416,8 @@ void MainWindow::setAspectRatio(const QSize &aspectRatio) {
videoDialog->setAspectRatio(aspectRatio);
}
void MainWindow::setSampleRates(const std::vector<int> &sampleRates) {
soundDialog->setRates(sampleRates);
void MainWindow::setSampleRates(const MediaSource::SampleRateInfo &sampleRateInfo) {
soundDialog->setRates(sampleRateInfo);
}
void MainWindow::setVideoSources(const std::vector<MediaSource::VideoSourceInfo> &sourceInfos) {
@ -636,7 +636,7 @@ void MainWindow::showCursor() {
void MainWindow::keyPressEvent(QKeyEvent *e) {
e->ignore();
if (isRunning()) {
if (isRunning() && !e->isAutoRepeat()) {
std::pair<keymap_t::iterator,keymap_t::iterator> range = keyInputs.equal_range(e->key());
while (range.first != range.second) {
@ -651,7 +651,7 @@ void MainWindow::keyPressEvent(QKeyEvent *e) {
void MainWindow::keyReleaseEvent(QKeyEvent *e) {
e->ignore();
if (isRunning()) {
if (isRunning() && !e->isAutoRepeat()) {
std::pair<keymap_t::iterator,keymap_t::iterator> range = keyInputs.equal_range(e->key());
while (range.first != range.second) {

View File

@ -154,15 +154,15 @@ public:
* to get fewer window sizes listed. Can be changed later with the setAspectRatio
* method.
*
* @param sampleRates Sample rates (samples pr second) selectable in the sound dialog. At least
* one is required. Can be changed later with the setSampleRates method.
* @param sampleRateInfo Information about sample rates selectable in the sound dialog.
* Can be changed later with the setSampleRates method.
*/
MainWindow(MediaSource *source,
const std::vector<MediaSource::ButtonInfo> &buttonInfos,
const std::vector<MediaSource::VideoSourceInfo> &videoSourceInfos,
const QString &videoSourceLabel,
const QSize &aspectRatio,
const std::vector<int> &sampleRates);
const MediaSource::SampleRateInfo &sampleRateInfo);
~MainWindow();
const QSize& aspectRatio() const;
@ -189,7 +189,7 @@ public:
* from MainWindow, and unpaused when the dialog is closed. pauseOnDialogExec is on by default.
*/
void setPauseOnDialogExec(bool enable) { pauseOnDialogExec = enable; }
void setSampleRates(const std::vector<int> &sampleRates);
void setSampleRates(const MediaSource::SampleRateInfo &sampleRateInfo);
void setVideoSources(const std::vector<MediaSource::VideoSourceInfo> &sourceInfos);
/**

View File

@ -21,6 +21,7 @@
#include <QtGlobal>
#include <QString>
#include <vector>
//TODO: stop dictating audio/video formats.
// Choice based on:
@ -73,6 +74,21 @@ public:
int defaultAltKey;
};
struct SampleRateInfo {
enum { NOT_SUPPORTED = -1 };
// Distinct sample rate (stereo samples per second) alternatives selectable in the sound settings dialog.
std::vector<int> rates;
// The index of the rate in the rates list to be selected by default.
std::size_t defaultRateIndex;
// Minimum and maximum custom sample rates selectable in the sound settings dialog.
// Set to NOT_SUPPORTED if you don't want to allow custom sample rates.
int minCustomRate;
int maxCustomRate;
};
/**
* Reimplement to get buttonPress events for buttons of corresponding index to the
* buttonInfos given to MainWindow.
@ -97,9 +113,7 @@ public:
* signed 16-bit interleaved stereo for now.) Can be assumed to be big enough to have space
* for the number of samples requested on each update call. Also gives the currently active
* sample rate. which may differ slightly from the alternatives given to MainWindow, since
* some engines set a near sample rate if they can't give an exact native one. sampleRate
* may be somewhat redundant information, since the number of samples wanted pr frame is given to
* the update method, but it's there for convenience (mainly for fixed sample rate sources).
* some engines set a near sample rate if they can't give an exact native one.
*/
virtual void setSampleBuffer(qint16 *sampleBuffer, unsigned sampleRate) = 0;

View File

@ -25,6 +25,8 @@
#include <QLabel>
#include <QPushButton>
#include <QSettings>
#include <QSize>
#include <QInputDialog>
#include <cassert>
#include "audioengine.h"
@ -36,7 +38,47 @@ static int filterValue(const int value, const int upper, const int lower = 0, co
return value;
}
SoundDialog::SoundDialog(const std::vector<AudioEngine*> &engines, const std::vector<int> &rates, QWidget *parent) :
static void populateRateSelector(QComboBox *rateSelector, const MediaSource::SampleRateInfo &rateInfo) {
assert(!rateInfo.rates.empty());
assert(rateInfo.defaultRateIndex < rateInfo.rates.size());
for (std::size_t i = 0; i < rateInfo.rates.size(); ++i)
rateSelector->addItem(QString::number(rateInfo.rates[i]) + " Hz", rateInfo.rates[i]);
if (rateInfo.minCustomRate > 0 && rateInfo.maxCustomRate > rateInfo.minCustomRate) {
rateSelector->addItem(SoundDialog::tr("Other..."), QSize(rateInfo.minCustomRate, rateInfo.maxCustomRate));
}
rateSelector->setCurrentIndex(rateInfo.defaultRateIndex);
}
static int getCustomIndex(const QComboBox *rateSelector) {
int i = rateSelector->count() - 2;
while (i < rateSelector->count() && rateSelector->itemText(i).at(0).isNumber())
++i;
return i;
}
static void setRate(QComboBox *rateSelector, const int r) {
const int customIndex = getCustomIndex(rateSelector);
const int newIndex = rateSelector->findData(r);
if (newIndex < 0) {
if (customIndex < rateSelector->count()) {
rateSelector->addItem(QString::number(r) + " Hz", r);
if (customIndex + 2 != rateSelector->count())
rateSelector->removeItem(customIndex + 1);
rateSelector->setCurrentIndex(customIndex + 1);
}
} else
rateSelector->setCurrentIndex(newIndex);
}
SoundDialog::SoundDialog(const std::vector<AudioEngine*> &engines, const MediaSource::SampleRateInfo &rateInfo, QWidget *parent) :
QDialog(parent),
engines(engines),
topLayout(new QVBoxLayout),
@ -45,8 +87,6 @@ SoundDialog::SoundDialog(const std::vector<AudioEngine*> &engines, const std::ve
latencySelector(new QSpinBox(this)),
engineWidget(NULL)
{
assert(!rates.empty());
setWindowTitle(tr("Sound Settings"));
QVBoxLayout *const mainLayout = new QVBoxLayout;
@ -66,8 +106,7 @@ SoundDialog::SoundDialog(const std::vector<AudioEngine*> &engines, const std::ve
QHBoxLayout *const hLayout = new QHBoxLayout;
hLayout->addWidget(new QLabel(tr("Sample rate:")));
for (unsigned i = 0; i < rates.size(); ++i)
rateSelector->addItem(QString::number(rates[i]) + " Hz", rates[i]);
populateRateSelector(rateSelector, rateInfo);
hLayout->addWidget(rateSelector);
topLayout->addLayout(hLayout);
@ -106,12 +145,14 @@ SoundDialog::SoundDialog(const std::vector<AudioEngine*> &engines, const std::ve
QSettings settings;
settings.beginGroup("sound");
engineIndex = filterValue(settings.value("engineIndex", 0).toInt(), engineSelector->count());
rateIndex = filterValue(settings.value("rateIndex", 0).toInt(), rateSelector->count());
latency = filterValue(settings.value("latency", 133).toInt(), latencySelector->maximum(), latencySelector->minimum());
setRate(rateSelector, settings.value("rate", rateSelector->itemData(rateSelector->currentIndex())).toInt());
latency = filterValue(settings.value("latency", 67).toInt(), latencySelector->maximum(), latencySelector->minimum(), 67);
settings.endGroup();
rate = rateSelector->itemData(rateSelector->currentIndex()).toInt();
engineChange(engineSelector->currentIndex());
connect(engineSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(engineChange(int)));
connect(rateSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(rateIndexChange(int)));
restore();
}
@ -120,7 +161,7 @@ SoundDialog::~SoundDialog() {
QSettings settings;
settings.beginGroup("sound");
settings.setValue("engineIndex", engineIndex);
settings.setValue("rateIndex", rateIndex);
settings.setValue("rate", rate);
settings.setValue("latency", latency);
settings.endGroup();
}
@ -135,12 +176,27 @@ void SoundDialog::engineChange(int index) {
topLayout->insertWidget(1, engineWidget);
}
void SoundDialog::rateIndexChange(const int index) {
if (getCustomIndex(rateSelector) == index) {
const QSize &sz = rateSelector->itemData(index).toSize();
const int currentRate = getRate();
bool ok = false;
int r = QInputDialog::getInteger(this, tr("Set Sample Rate"), tr("Sample rate (Hz):"), getRate(), sz.width(), sz.height(), 1, &ok);
if (!ok)
r = currentRate;
setRate(rateSelector, r);
}
}
void SoundDialog::store() {
for (std::size_t i = 0; i < engines.size(); ++i)
engines[i]->acceptSettings();
engineIndex = engineSelector->currentIndex();
rateIndex = rateSelector->currentIndex();
rate = rateSelector->itemData(rateSelector->currentIndex()).toInt();
latency = latencySelector->value();
}
@ -149,23 +205,16 @@ void SoundDialog::restore() {
engines[i]->rejectSettings();
engineSelector->setCurrentIndex(engineIndex);
rateSelector->setCurrentIndex(rateIndex);
setRate(rateSelector, rate);
latencySelector->setValue(latency);
}
void SoundDialog::setRates(const std::vector<int> &rates) {
void SoundDialog::setRates(const MediaSource::SampleRateInfo &rateInfo) {
restore();
const int oldVal = rateSelector->itemData(rateIndex).toInt();
rateSelector->clear();
for (unsigned i = 0; i < rates.size(); ++i)
rateSelector->addItem(QString::number(rates[i]) + " Hz", rates[i]);
const int newIndex = rateSelector->findData(oldVal);
if (newIndex >= 0)
rateSelector->setCurrentIndex(newIndex);
populateRateSelector(rateSelector, rateInfo);
setRate(rateSelector, rate);
store();
emit accepted();
@ -180,7 +229,3 @@ void SoundDialog::reject() {
restore();
QDialog::reject();
}
int SoundDialog::getRate() const {
return rateSelector->itemData(rateIndex).toInt();
}

View File

@ -26,7 +26,7 @@ class QComboBox;
class QSpinBox;
#include <QDialog>
#include <vector>
#include "mediasource.h"
class SoundDialog : public QDialog {
Q_OBJECT
@ -38,7 +38,7 @@ class SoundDialog : public QDialog {
QSpinBox *const latencySelector;
QWidget *engineWidget;
int engineIndex;
int rateIndex;
int rate;
int latency;
void store();
@ -46,14 +46,15 @@ class SoundDialog : public QDialog {
private slots:
void engineChange(int index);
void rateIndexChange(int index);
public:
SoundDialog(const std::vector<AudioEngine*> &engines, const std::vector<int> &rates, QWidget *parent = 0);
SoundDialog(const std::vector<AudioEngine*> &engines, const MediaSource::SampleRateInfo &rateInfo, QWidget *parent = 0);
~SoundDialog();
int getEngineIndex() const { return engineIndex; }
int getRate() const;
int getRate() const { return rate; }
int getLatency() const { return latency; };
void setRates(const std::vector<int> &rates);
void setRates(const MediaSource::SampleRateInfo &rateInfo);
public slots:
void accept();