mirror of
https://github.com/libretro/mgba.git
synced 2024-11-27 10:11:00 +00:00
SDL, Qt: Configurable audio sample rate
This commit is contained in:
parent
805e0b17eb
commit
9c5852e89e
1
CHANGES
1
CHANGES
@ -31,6 +31,7 @@ Features:
|
|||||||
- Implement BIOS call Stop, for sleep mode
|
- Implement BIOS call Stop, for sleep mode
|
||||||
- Automatically load patches, if found
|
- Automatically load patches, if found
|
||||||
- Improved video synchronization
|
- Improved video synchronization
|
||||||
|
- Configurable audio output sample rate
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- ARM7: Fix SWI and IRQ timings
|
- ARM7: Fix SWI and IRQ timings
|
||||||
- GBA Audio: Force audio FIFOs to 32-bit
|
- GBA Audio: Force audio FIFOs to 32-bit
|
||||||
|
@ -247,6 +247,7 @@ void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) {
|
|||||||
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
|
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
|
||||||
opts->audioBuffers = audioBuffers;
|
opts->audioBuffers = audioBuffers;
|
||||||
}
|
}
|
||||||
|
_lookupUIntValue(config, "sampleRate", &opts->sampleRate);
|
||||||
|
|
||||||
int fakeBool;
|
int fakeBool;
|
||||||
if (_lookupIntValue(config, "useBios", &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);
|
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval);
|
||||||
ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
|
ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
|
||||||
ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
|
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, "audioSync", opts->audioSync);
|
||||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync);
|
ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync);
|
||||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen);
|
ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen);
|
||||||
|
@ -29,6 +29,7 @@ struct GBAOptions {
|
|||||||
int rewindBufferInterval;
|
int rewindBufferInterval;
|
||||||
float fpsTarget;
|
float fpsTarget;
|
||||||
size_t audioBuffers;
|
size_t audioBuffers;
|
||||||
|
unsigned sampleRate;
|
||||||
|
|
||||||
int fullscreen;
|
int fullscreen;
|
||||||
int width;
|
int width;
|
||||||
|
@ -31,6 +31,7 @@ public:
|
|||||||
|
|
||||||
virtual void setInput(GBAThread* input);
|
virtual void setInput(GBAThread* input);
|
||||||
int getBufferSamples() const { return m_samples; }
|
int getBufferSamples() const { return m_samples; }
|
||||||
|
virtual unsigned sampleRate() const = 0;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void start() = 0;
|
virtual void start() = 0;
|
||||||
@ -39,7 +40,7 @@ public slots:
|
|||||||
virtual void setBufferSamples(int samples) = 0;
|
virtual void setBufferSamples(int samples) = 0;
|
||||||
virtual void inputParametersChanged() = 0;
|
virtual void inputParametersChanged() = 0;
|
||||||
|
|
||||||
virtual unsigned sampleRate() const = 0;
|
virtual void requestSampleRate(unsigned) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
GBAThread* input() { return m_context; }
|
GBAThread* input() { return m_context; }
|
||||||
|
@ -20,6 +20,7 @@ AudioProcessorQt::AudioProcessorQt(QObject* parent)
|
|||||||
: AudioProcessor(parent)
|
: AudioProcessor(parent)
|
||||||
, m_audioOutput(nullptr)
|
, m_audioOutput(nullptr)
|
||||||
, m_device(nullptr)
|
, m_device(nullptr)
|
||||||
|
, m_sampleRate(44100)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ void AudioProcessorQt::start() {
|
|||||||
|
|
||||||
if (!m_audioOutput) {
|
if (!m_audioOutput) {
|
||||||
QAudioFormat format;
|
QAudioFormat format;
|
||||||
format.setSampleRate(44100);
|
format.setSampleRate(m_sampleRate);
|
||||||
format.setChannelCount(2);
|
format.setChannelCount(2);
|
||||||
format.setSampleSize(16);
|
format.setSampleSize(16);
|
||||||
format.setCodec("audio/pcm");
|
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 {
|
unsigned AudioProcessorQt::sampleRate() const {
|
||||||
if (!m_audioOutput) {
|
if (!m_audioOutput) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -20,6 +20,7 @@ public:
|
|||||||
AudioProcessorQt(QObject* parent = nullptr);
|
AudioProcessorQt(QObject* parent = nullptr);
|
||||||
|
|
||||||
virtual void setInput(GBAThread* input);
|
virtual void setInput(GBAThread* input);
|
||||||
|
virtual unsigned sampleRate() const override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void start();
|
virtual void start();
|
||||||
@ -28,11 +29,12 @@ public slots:
|
|||||||
virtual void setBufferSamples(int samples);
|
virtual void setBufferSamples(int samples);
|
||||||
virtual void inputParametersChanged();
|
virtual void inputParametersChanged();
|
||||||
|
|
||||||
virtual unsigned sampleRate() const override;
|
virtual void requestSampleRate(unsigned) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QAudioOutput* m_audioOutput;
|
QAudioOutput* m_audioOutput;
|
||||||
AudioDevice* m_device;
|
AudioDevice* m_device;
|
||||||
|
unsigned m_sampleRate;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ using namespace QGBA;
|
|||||||
|
|
||||||
AudioProcessorSDL::AudioProcessorSDL(QObject* parent)
|
AudioProcessorSDL::AudioProcessorSDL(QObject* parent)
|
||||||
: AudioProcessor(parent)
|
: AudioProcessor(parent)
|
||||||
, m_audio()
|
, m_audio{ 2048, 44100 }
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +55,18 @@ void AudioProcessorSDL::setBufferSamples(int samples) {
|
|||||||
void AudioProcessorSDL::inputParametersChanged() {
|
void AudioProcessorSDL::inputParametersChanged() {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned AudioProcessorSDL::sampleRate() const {
|
void AudioProcessorSDL::requestSampleRate(unsigned rate) {
|
||||||
return m_audio.obtainedSpec.freq;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ public:
|
|||||||
AudioProcessorSDL(QObject* parent = nullptr);
|
AudioProcessorSDL(QObject* parent = nullptr);
|
||||||
~AudioProcessorSDL();
|
~AudioProcessorSDL();
|
||||||
|
|
||||||
|
virtual unsigned sampleRate() const override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void start();
|
virtual void start();
|
||||||
virtual void pause();
|
virtual void pause();
|
||||||
@ -29,7 +31,7 @@ public slots:
|
|||||||
virtual void setBufferSamples(int samples);
|
virtual void setBufferSamples(int samples);
|
||||||
virtual void inputParametersChanged();
|
virtual void inputParametersChanged();
|
||||||
|
|
||||||
virtual unsigned sampleRate() const override;
|
virtual void requestSampleRate(unsigned) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GBASDLAudio m_audio;
|
GBASDLAudio m_audio;
|
||||||
|
@ -53,6 +53,7 @@ GBAApp::GBAApp(int& argc, char* argv[])
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt()));
|
||||||
Window* w = new Window(&m_configController);
|
Window* w = new Window(&m_configController);
|
||||||
connect(w, &Window::destroyed, [this]() {
|
connect(w, &Window::destroyed, [this]() {
|
||||||
m_windows[0] = nullptr;
|
m_windows[0] = nullptr;
|
||||||
@ -67,9 +68,6 @@ GBAApp::GBAApp(int& argc, char* argv[])
|
|||||||
freeArguments(&args);
|
freeArguments(&args);
|
||||||
w->show();
|
w->show();
|
||||||
|
|
||||||
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt()));
|
|
||||||
w->controller()->reloadAudioDriver();
|
|
||||||
|
|
||||||
w->controller()->setMultiplayerController(&m_multiplayer);
|
w->controller()->setMultiplayerController(&m_multiplayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,9 @@ GameController::GameController(QObject* parent)
|
|||||||
|
|
||||||
m_threadContext.startCallback = [](GBAThread* context) {
|
m_threadContext.startCallback = [](GBAThread* context) {
|
||||||
GameController* controller = static_cast<GameController*>(context->userData);
|
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;
|
context->gba->luminanceSource = &controller->m_lux;
|
||||||
GBARTCGenericSourceInit(&controller->m_rtc, context->gba);
|
GBARTCGenericSourceInit(&controller->m_rtc, context->gba);
|
||||||
context->gba->rtcSource = &controller->m_rtc.d;
|
context->gba->rtcSource = &controller->m_rtc.d;
|
||||||
@ -588,10 +590,21 @@ void GameController::clearKeys() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GameController::setAudioBufferSamples(int samples) {
|
void GameController::setAudioBufferSamples(int samples) {
|
||||||
threadInterrupt();
|
if (m_audioProcessor) {
|
||||||
redoSamples(samples);
|
threadInterrupt();
|
||||||
threadContinue();
|
redoSamples(samples);
|
||||||
QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, 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) {
|
void GameController::setAudioChannelEnabled(int channel, bool enable) {
|
||||||
@ -644,7 +657,9 @@ void GameController::setFPSTarget(float fps) {
|
|||||||
if (m_turbo && m_turboSpeed > 0) {
|
if (m_turbo && m_turboSpeed > 0) {
|
||||||
m_threadContext.fpsTarget *= m_turboSpeed;
|
m_threadContext.fpsTarget *= m_turboSpeed;
|
||||||
}
|
}
|
||||||
redoSamples(m_audioProcessor->getBufferSamples());
|
if (m_audioProcessor) {
|
||||||
|
redoSamples(m_audioProcessor->getBufferSamples());
|
||||||
|
}
|
||||||
threadContinue();
|
threadContinue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -801,7 +816,9 @@ void GameController::enableTurbo() {
|
|||||||
m_threadContext.sync.audioWait = true;
|
m_threadContext.sync.audioWait = true;
|
||||||
m_threadContext.sync.videoFrameWait = false;
|
m_threadContext.sync.videoFrameWait = false;
|
||||||
}
|
}
|
||||||
redoSamples(m_audioProcessor->getBufferSamples());
|
if (m_audioProcessor) {
|
||||||
|
redoSamples(m_audioProcessor->getBufferSamples());
|
||||||
|
}
|
||||||
threadContinue();
|
threadContinue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -830,11 +847,21 @@ void GameController::screenshot() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void GameController::reloadAudioDriver() {
|
void GameController::reloadAudioDriver() {
|
||||||
QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
|
int samples = 0;
|
||||||
int samples = m_audioProcessor->getBufferSamples();
|
unsigned sampleRate = 0;
|
||||||
delete m_audioProcessor;
|
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 = AudioProcessor::create();
|
||||||
m_audioProcessor->setBufferSamples(samples);
|
if (samples) {
|
||||||
|
m_audioProcessor->setBufferSamples(samples);
|
||||||
|
}
|
||||||
|
if (sampleRate) {
|
||||||
|
m_audioProcessor->requestSampleRate(sampleRate);
|
||||||
|
}
|
||||||
m_audioProcessor->moveToThread(m_audioThread);
|
m_audioProcessor->moveToThread(m_audioThread);
|
||||||
connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
|
connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
|
||||||
connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));
|
connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));
|
||||||
|
@ -119,6 +119,7 @@ public slots:
|
|||||||
void keyReleased(int key);
|
void keyReleased(int key);
|
||||||
void clearKeys();
|
void clearKeys();
|
||||||
void setAudioBufferSamples(int samples);
|
void setAudioBufferSamples(int samples);
|
||||||
|
void setAudioSampleRate(unsigned rate);
|
||||||
void setAudioChannelEnabled(int channel, bool enable = true);
|
void setAudioChannelEnabled(int channel, bool enable = true);
|
||||||
void setVideoLayerEnabled(int layer, bool enable = true);
|
void setVideoLayerEnabled(int layer, bool enable = true);
|
||||||
void setFPSTarget(float fps);
|
void setFPSTarget(float fps);
|
||||||
|
@ -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_controller, SLOT(closeGame()));
|
||||||
connect(this, SIGNAL(shutdown()), m_logView, SLOT(hide()));
|
connect(this, SIGNAL(shutdown()), m_logView, SLOT(hide()));
|
||||||
connect(this, SIGNAL(audioBufferSamplesChanged(int)), m_controller, SLOT(setAudioBufferSamples(int)));
|
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(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float)));
|
||||||
connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS()));
|
connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS()));
|
||||||
connect(m_display, &Display::hideCursor, [this]() {
|
connect(m_display, &Display::hideCursor, [this]() {
|
||||||
@ -195,6 +196,7 @@ void Window::loadConfig() {
|
|||||||
m_controller->loadBIOS(opts->bios);
|
m_controller->loadBIOS(opts->bios);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move these to ConfigController
|
||||||
if (opts->fpsTarget) {
|
if (opts->fpsTarget) {
|
||||||
emit fpsTargetChanged(opts->fpsTarget);
|
emit fpsTargetChanged(opts->fpsTarget);
|
||||||
}
|
}
|
||||||
@ -203,6 +205,10 @@ void Window::loadConfig() {
|
|||||||
emit audioBufferSamplesChanged(opts->audioBuffers);
|
emit audioBufferSamplesChanged(opts->audioBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts->sampleRate) {
|
||||||
|
emit sampleRateChanged(opts->sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
if (opts->width && opts->height) {
|
if (opts->width && opts->height) {
|
||||||
resizeFrame(opts->width, opts->height);
|
resizeFrame(opts->width, opts->height);
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ signals:
|
|||||||
void startDrawing(GBAThread*);
|
void startDrawing(GBAThread*);
|
||||||
void shutdown();
|
void shutdown();
|
||||||
void audioBufferSamplesChanged(int samples);
|
void audioBufferSamplesChanged(int samples);
|
||||||
|
void sampleRateChanged(unsigned samples);
|
||||||
void fpsTargetChanged(float target);
|
void fpsTargetChanged(float target);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -112,6 +112,10 @@ int main(int argc, char** argv) {
|
|||||||
bool didFail = false;
|
bool didFail = false;
|
||||||
|
|
||||||
renderer.audio.samples = context.audioBuffers;
|
renderer.audio.samples = context.audioBuffers;
|
||||||
|
renderer.audio.sampleRate = 44100;
|
||||||
|
if (opts.sampleRate) {
|
||||||
|
renderer.audio.sampleRate = opts.sampleRate;
|
||||||
|
}
|
||||||
if (!GBASDLInitAudio(&renderer.audio, &context)) {
|
if (!GBASDLInitAudio(&renderer.audio, &context)) {
|
||||||
didFail = true;
|
didFail = true;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContex
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
context->desiredSpec.freq = 44100;
|
context->desiredSpec.freq = context->sampleRate;
|
||||||
context->desiredSpec.format = AUDIO_S16SYS;
|
context->desiredSpec.format = AUDIO_S16SYS;
|
||||||
context->desiredSpec.channels = 2;
|
context->desiredSpec.channels = 2;
|
||||||
context->desiredSpec.samples = context->samples;
|
context->desiredSpec.samples = context->samples;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
struct GBASDLAudio {
|
struct GBASDLAudio {
|
||||||
// Input
|
// Input
|
||||||
size_t samples;
|
size_t samples;
|
||||||
|
unsigned sampleRate;
|
||||||
|
|
||||||
// State
|
// State
|
||||||
SDL_AudioSpec desiredSpec;
|
SDL_AudioSpec desiredSpec;
|
||||||
|
Loading…
Reference in New Issue
Block a user