mirror of https://github.com/mgba-emu/mgba.git
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
|
||||
- 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 @@ 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);
|
||||
|
|
|
@ -29,6 +29,7 @@ struct GBAOptions {
|
|||
int rewindBufferInterval;
|
||||
float fpsTarget;
|
||||
size_t audioBuffers;
|
||||
unsigned sampleRate;
|
||||
|
||||
int fullscreen;
|
||||
int width;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -87,7 +87,9 @@ GameController::GameController(QObject* parent)
|
|||
|
||||
m_threadContext.startCallback = [](GBAThread* context) {
|
||||
GameController* controller = static_cast<GameController*>(context->userData);
|
||||
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,11 +590,22 @@ void GameController::clearKeys() {
|
|||
}
|
||||
|
||||
void GameController::setAudioBufferSamples(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) {
|
||||
if (channel > 5 || channel < 0) {
|
||||
|
@ -644,7 +657,9 @@ void GameController::setFPSTarget(float fps) {
|
|||
if (m_turbo && m_turboSpeed > 0) {
|
||||
m_threadContext.fpsTarget *= m_turboSpeed;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (m_audioProcessor) {
|
||||
redoSamples(m_audioProcessor->getBufferSamples());
|
||||
}
|
||||
threadContinue();
|
||||
}
|
||||
|
||||
|
@ -830,11 +847,21 @@ void GameController::screenshot() {
|
|||
#endif
|
||||
|
||||
void GameController::reloadAudioDriver() {
|
||||
int samples = 0;
|
||||
unsigned sampleRate = 0;
|
||||
if (m_audioProcessor) {
|
||||
QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
|
||||
int samples = m_audioProcessor->getBufferSamples();
|
||||
samples = m_audioProcessor->getBufferSamples();
|
||||
sampleRate = m_audioProcessor->sampleRate();
|
||||
delete m_audioProcessor;
|
||||
}
|
||||
m_audioProcessor = AudioProcessor::create();
|
||||
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 @@ 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 @@ 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
struct GBASDLAudio {
|
||||
// Input
|
||||
size_t samples;
|
||||
unsigned sampleRate;
|
||||
|
||||
// State
|
||||
SDL_AudioSpec desiredSpec;
|
||||
|
|
Loading…
Reference in New Issue