SDL, Qt: Configurable audio sample rate
jump to
@@ -31,6 +31,7 @@ - Libretro now supports BIOS, rumble and solar sensor
- 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
@@ -247,6 +247,7 @@ unsigned audioBuffers;
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) { opts->audioBuffers = audioBuffers; } + _lookupUIntValue(config, "sampleRate", &opts->sampleRate); int fakeBool; if (_lookupIntValue(config, "useBios", &fakeBool)) {@@ -305,6 +306,7 @@ ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
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);
@@ -29,6 +29,7 @@ int rewindBufferCapacity;
int rewindBufferInterval; float fpsTarget; size_t audioBuffers; + unsigned sampleRate; int fullscreen; int width;
@@ -31,6 +31,7 @@ AudioProcessor(QObject* parent = nullptr);
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 @@
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; }
@@ -20,6 +20,7 @@ AudioProcessorQt::AudioProcessorQt(QObject* parent)
: AudioProcessor(parent) , m_audioOutput(nullptr) , m_device(nullptr) + , m_sampleRate(44100) { }@@ -45,7 +46,7 @@ }
if (!m_audioOutput) { QAudioFormat format; - format.setSampleRate(44100); + format.setSampleRate(m_sampleRate); format.setChannelCount(2); format.setSampleSize(16); format.setCodec("audio/pcm");@@ -81,6 +82,15 @@
void AudioProcessorQt::inputParametersChanged() { if (m_device) { m_device->setFormat(m_audioOutput->format()); + } +} + +void AudioProcessorQt::requestSampleRate(unsigned rate) { + m_sampleRate = rate; + if (m_device) { + QAudioFormat format(m_audioOutput->format()); + format.setSampleRate(rate); + m_device->setFormat(format); } }
@@ -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 @@
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; }; }
@@ -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::inputParametersChanged() { } +void AudioProcessorSDL::requestSampleRate(unsigned rate) { + m_audio.sampleRate = rate; + if (m_audio.thread) { + GBASDLDeinitAudio(&m_audio); + GBASDLInitAudio(&m_audio, input()); + } +} + unsigned AudioProcessorSDL::sampleRate() const { - return m_audio.obtainedSpec.freq; + if (m_audio.thread) { + return m_audio.obtainedSpec.freq; + } else { + return 0; + } }
@@ -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 @@
virtual void setBufferSamples(int samples); virtual void inputParametersChanged(); - virtual unsigned sampleRate() const override; + virtual void requestSampleRate(unsigned) override; private: GBASDLAudio m_audio;
@@ -53,6 +53,7 @@ ::exit(0);
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;@@ -66,9 +67,6 @@ w->loadConfig();
} freeArguments(&args); w->show(); - - AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt())); - w->controller()->reloadAudioDriver(); w->controller()->setMultiplayerController(&m_multiplayer); }
@@ -87,7 +87,9 @@ setLuminanceLevel(0);
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 @@ updateKeys();
} 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 @@ m_threadContext.fpsTarget = 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 @@ m_threadContext.fpsTarget = m_fpsTarget * m_turboSpeed;
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 @@ }
#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()));
@@ -119,6 +119,7 @@ void keyPressed(int key);
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);
@@ -127,6 +127,7 @@ connect(this, SIGNAL(shutdown()), m_display, SLOT(stopDrawing()));
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,12 +196,17 @@ if (opts->bios) {
m_controller->loadBIOS(opts->bios); } + // TODO: Move these to ConfigController if (opts->fpsTarget) { emit fpsTargetChanged(opts->fpsTarget); } if (opts->audioBuffers) { emit audioBufferSamplesChanged(opts->audioBuffers); + } + + if (opts->sampleRate) { + emit sampleRateChanged(opts->sampleRate); } if (opts->width && opts->height) {
@@ -54,6 +54,7 @@ signals:
void startDrawing(GBAThread*); void shutdown(); void audioBufferSamplesChanged(int samples); + void sampleRateChanged(unsigned samples); void fpsTargetChanged(float target); public slots:
@@ -112,6 +112,10 @@
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; }
@@ -22,7 +22,7 @@ GBALog(0, GBA_LOG_ERROR, "Could not initialize SDL sound system: %s", SDL_GetError());
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;
@@ -15,6 +15,7 @@
struct GBASDLAudio { // Input size_t samples; + unsigned sampleRate; // State SDL_AudioSpec desiredSpec;