SDL, Qt: Configurable audio sample rate

This commit is contained in:
Jeffrey Pfau 2015-08-09 21:36:43 -07:00
parent 805e0b17eb
commit 9c5852e89e
16 changed files with 91 additions and 22 deletions

View File

@ -31,6 +31,7 @@ Features:
- Implement BIOS call Stop, for sleep mode
- Automatically load patches, if found
- Improved video synchronization
- Configurable audio output sample rate
Bugfixes:
- ARM7: Fix SWI and IRQ timings
- GBA Audio: Force audio FIFOs to 32-bit

View File

@ -247,6 +247,7 @@ void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) {
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
opts->audioBuffers = audioBuffers;
}
_lookupUIntValue(config, "sampleRate", &opts->sampleRate);
int fakeBool;
if (_lookupIntValue(config, "useBios", &fakeBool)) {
@ -305,6 +306,7 @@ void GBAConfigLoadDefaults(struct GBAConfig* config, const struct GBAOptions* op
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval);
ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate);
ConfigurationSetIntValue(&config->defaultsTable, 0, "audioSync", opts->audioSync);
ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync);
ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen);

View File

@ -29,6 +29,7 @@ struct GBAOptions {
int rewindBufferInterval;
float fpsTarget;
size_t audioBuffers;
unsigned sampleRate;
int fullscreen;
int width;

View File

@ -31,6 +31,7 @@ public:
virtual void setInput(GBAThread* input);
int getBufferSamples() const { return m_samples; }
virtual unsigned sampleRate() const = 0;
public slots:
virtual void start() = 0;
@ -39,7 +40,7 @@ public slots:
virtual void setBufferSamples(int samples) = 0;
virtual void inputParametersChanged() = 0;
virtual unsigned sampleRate() const = 0;
virtual void requestSampleRate(unsigned) = 0;
protected:
GBAThread* input() { return m_context; }

View File

@ -20,6 +20,7 @@ AudioProcessorQt::AudioProcessorQt(QObject* parent)
: AudioProcessor(parent)
, m_audioOutput(nullptr)
, m_device(nullptr)
, m_sampleRate(44100)
{
}
@ -45,7 +46,7 @@ void AudioProcessorQt::start() {
if (!m_audioOutput) {
QAudioFormat format;
format.setSampleRate(44100);
format.setSampleRate(m_sampleRate);
format.setChannelCount(2);
format.setSampleSize(16);
format.setCodec("audio/pcm");
@ -84,6 +85,15 @@ void AudioProcessorQt::inputParametersChanged() {
}
}
void AudioProcessorQt::requestSampleRate(unsigned rate) {
m_sampleRate = rate;
if (m_device) {
QAudioFormat format(m_audioOutput->format());
format.setSampleRate(rate);
m_device->setFormat(format);
}
}
unsigned AudioProcessorQt::sampleRate() const {
if (!m_audioOutput) {
return 0;

View File

@ -20,6 +20,7 @@ public:
AudioProcessorQt(QObject* parent = nullptr);
virtual void setInput(GBAThread* input);
virtual unsigned sampleRate() const override;
public slots:
virtual void start();
@ -28,11 +29,12 @@ public slots:
virtual void setBufferSamples(int samples);
virtual void inputParametersChanged();
virtual unsigned sampleRate() const override;
virtual void requestSampleRate(unsigned) override;
private:
QAudioOutput* m_audioOutput;
AudioDevice* m_device;
unsigned m_sampleRate;
};
}

View File

@ -15,7 +15,7 @@ using namespace QGBA;
AudioProcessorSDL::AudioProcessorSDL(QObject* parent)
: AudioProcessor(parent)
, m_audio()
, m_audio{ 2048, 44100 }
{
}
@ -55,6 +55,18 @@ void AudioProcessorSDL::setBufferSamples(int samples) {
void AudioProcessorSDL::inputParametersChanged() {
}
unsigned AudioProcessorSDL::sampleRate() const {
return m_audio.obtainedSpec.freq;
void AudioProcessorSDL::requestSampleRate(unsigned rate) {
m_audio.sampleRate = rate;
if (m_audio.thread) {
GBASDLDeinitAudio(&m_audio);
GBASDLInitAudio(&m_audio, input());
}
}
unsigned AudioProcessorSDL::sampleRate() const {
if (m_audio.thread) {
return m_audio.obtainedSpec.freq;
} else {
return 0;
}
}

View File

@ -22,6 +22,8 @@ public:
AudioProcessorSDL(QObject* parent = nullptr);
~AudioProcessorSDL();
virtual unsigned sampleRate() const override;
public slots:
virtual void start();
virtual void pause();
@ -29,7 +31,7 @@ public slots:
virtual void setBufferSamples(int samples);
virtual void inputParametersChanged();
virtual unsigned sampleRate() const override;
virtual void requestSampleRate(unsigned) override;
private:
GBASDLAudio m_audio;

View File

@ -53,6 +53,7 @@ GBAApp::GBAApp(int& argc, char* argv[])
return;
}
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt()));
Window* w = new Window(&m_configController);
connect(w, &Window::destroyed, [this]() {
m_windows[0] = nullptr;
@ -67,9 +68,6 @@ GBAApp::GBAApp(int& argc, char* argv[])
freeArguments(&args);
w->show();
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt()));
w->controller()->reloadAudioDriver();
w->controller()->setMultiplayerController(&m_multiplayer);
}

View File

@ -87,7 +87,9 @@ GameController::GameController(QObject* parent)
m_threadContext.startCallback = [](GBAThread* context) {
GameController* controller = static_cast<GameController*>(context->userData);
controller->m_audioProcessor->setInput(context);
if (controller->m_audioProcessor) {
controller->m_audioProcessor->setInput(context);
}
context->gba->luminanceSource = &controller->m_lux;
GBARTCGenericSourceInit(&controller->m_rtc, context->gba);
context->gba->rtcSource = &controller->m_rtc.d;
@ -588,10 +590,21 @@ void GameController::clearKeys() {
}
void GameController::setAudioBufferSamples(int samples) {
threadInterrupt();
redoSamples(samples);
threadContinue();
QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples));
if (m_audioProcessor) {
threadInterrupt();
redoSamples(samples);
threadContinue();
QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples));
}
}
void GameController::setAudioSampleRate(unsigned rate) {
if (m_audioProcessor) {
threadInterrupt();
redoSamples(m_audioProcessor->getBufferSamples());
threadContinue();
QMetaObject::invokeMethod(m_audioProcessor, "requestSampleRate", Q_ARG(unsigned, rate));
}
}
void GameController::setAudioChannelEnabled(int channel, bool enable) {
@ -644,7 +657,9 @@ void GameController::setFPSTarget(float fps) {
if (m_turbo && m_turboSpeed > 0) {
m_threadContext.fpsTarget *= m_turboSpeed;
}
redoSamples(m_audioProcessor->getBufferSamples());
if (m_audioProcessor) {
redoSamples(m_audioProcessor->getBufferSamples());
}
threadContinue();
}
@ -801,7 +816,9 @@ void GameController::enableTurbo() {
m_threadContext.sync.audioWait = true;
m_threadContext.sync.videoFrameWait = false;
}
redoSamples(m_audioProcessor->getBufferSamples());
if (m_audioProcessor) {
redoSamples(m_audioProcessor->getBufferSamples());
}
threadContinue();
}
@ -830,11 +847,21 @@ void GameController::screenshot() {
#endif
void GameController::reloadAudioDriver() {
QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
int samples = m_audioProcessor->getBufferSamples();
delete m_audioProcessor;
int samples = 0;
unsigned sampleRate = 0;
if (m_audioProcessor) {
QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
samples = m_audioProcessor->getBufferSamples();
sampleRate = m_audioProcessor->sampleRate();
delete m_audioProcessor;
}
m_audioProcessor = AudioProcessor::create();
m_audioProcessor->setBufferSamples(samples);
if (samples) {
m_audioProcessor->setBufferSamples(samples);
}
if (sampleRate) {
m_audioProcessor->requestSampleRate(sampleRate);
}
m_audioProcessor->moveToThread(m_audioThread);
connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));

View File

@ -119,6 +119,7 @@ public slots:
void keyReleased(int key);
void clearKeys();
void setAudioBufferSamples(int samples);
void setAudioSampleRate(unsigned rate);
void setAudioChannelEnabled(int channel, bool enable = true);
void setVideoLayerEnabled(int layer, bool enable = true);
void setFPSTarget(float fps);

View File

@ -127,6 +127,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
connect(this, SIGNAL(shutdown()), m_controller, SLOT(closeGame()));
connect(this, SIGNAL(shutdown()), m_logView, SLOT(hide()));
connect(this, SIGNAL(audioBufferSamplesChanged(int)), m_controller, SLOT(setAudioBufferSamples(int)));
connect(this, SIGNAL(sampleRateChanged(unsigned)), m_controller, SLOT(setAudioSampleRate(unsigned)));
connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float)));
connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS()));
connect(m_display, &Display::hideCursor, [this]() {
@ -195,6 +196,7 @@ void Window::loadConfig() {
m_controller->loadBIOS(opts->bios);
}
// TODO: Move these to ConfigController
if (opts->fpsTarget) {
emit fpsTargetChanged(opts->fpsTarget);
}
@ -203,6 +205,10 @@ void Window::loadConfig() {
emit audioBufferSamplesChanged(opts->audioBuffers);
}
if (opts->sampleRate) {
emit sampleRateChanged(opts->sampleRate);
}
if (opts->width && opts->height) {
resizeFrame(opts->width, opts->height);
}

View File

@ -54,6 +54,7 @@ signals:
void startDrawing(GBAThread*);
void shutdown();
void audioBufferSamplesChanged(int samples);
void sampleRateChanged(unsigned samples);
void fpsTargetChanged(float target);
public slots:

View File

@ -112,6 +112,10 @@ int main(int argc, char** argv) {
bool didFail = false;
renderer.audio.samples = context.audioBuffers;
renderer.audio.sampleRate = 44100;
if (opts.sampleRate) {
renderer.audio.sampleRate = opts.sampleRate;
}
if (!GBASDLInitAudio(&renderer.audio, &context)) {
didFail = true;
}

View File

@ -22,7 +22,7 @@ bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContex
return false;
}
context->desiredSpec.freq = 44100;
context->desiredSpec.freq = context->sampleRate;
context->desiredSpec.format = AUDIO_S16SYS;
context->desiredSpec.channels = 2;
context->desiredSpec.samples = context->samples;

View File

@ -15,6 +15,7 @@
struct GBASDLAudio {
// Input
size_t samples;
unsigned sampleRate;
// State
SDL_AudioSpec desiredSpec;