Qt: Fix hang if audio sync is enabled and audio fails to initialize

This commit is contained in:
Jeffrey Pfau 2016-02-09 02:01:21 -08:00
parent a763b764ed
commit 899a74d1c0
7 changed files with 32 additions and 13 deletions

View File

@ -2,6 +2,7 @@
Bugfixes: Bugfixes:
- VFS: Fix reading 7z archives without rewinding first - VFS: Fix reading 7z archives without rewinding first
- Qt: Fix sending gameStopped twice - Qt: Fix sending gameStopped twice
- Qt: Fix hang if audio sync is enabled and audio fails to initialize
Misc: Misc:
- GBA: Slightly optimize GBAProcessEvents - GBA: Slightly optimize GBAProcessEvents

View File

@ -34,7 +34,7 @@ public:
virtual unsigned sampleRate() const = 0; virtual unsigned sampleRate() const = 0;
public slots: public slots:
virtual void start() = 0; virtual bool start() = 0;
virtual void pause() = 0; virtual void pause() = 0;
virtual void setBufferSamples(int samples) = 0; virtual void setBufferSamples(int samples) = 0;

View File

@ -34,10 +34,10 @@ void AudioProcessorQt::setInput(GBAThread* input) {
} }
} }
void AudioProcessorQt::start() { bool AudioProcessorQt::start() {
if (!input()) { if (!input()) {
LOG(WARN) << tr("Can't start an audio processor without input"); LOG(WARN) << tr("Can't start an audio processor without input");
return; return false;
} }
if (!m_device) { if (!m_device) {
@ -62,6 +62,7 @@ void AudioProcessorQt::start() {
m_audioOutput->setBufferSize(input()->audioBuffers * 4); m_audioOutput->setBufferSize(input()->audioBuffers * 4);
m_audioOutput->start(m_device); m_audioOutput->start(m_device);
return m_audioOutput->state() == QAudio::ActiveState;
} }
void AudioProcessorQt::pause() { void AudioProcessorQt::pause() {

View File

@ -23,7 +23,7 @@ public:
virtual unsigned sampleRate() const override; virtual unsigned sampleRate() const override;
public slots: public slots:
virtual void start() override; virtual bool start() override;
virtual void pause() override; virtual void pause() override;
virtual void setBufferSamples(int samples) override; virtual void setBufferSamples(int samples) override;

View File

@ -23,19 +23,20 @@ AudioProcessorSDL::~AudioProcessorSDL() {
GBASDLDeinitAudio(&m_audio); GBASDLDeinitAudio(&m_audio);
} }
void AudioProcessorSDL::start() { bool AudioProcessorSDL::start() {
if (!input()) { if (!input()) {
LOG(WARN) << tr("Can't start an audio processor without input"); LOG(WARN) << tr("Can't start an audio processor without input");
return; return false;
} }
if (m_audio.thread) { if (m_audio.thread) {
GBASDLResumeAudio(&m_audio); GBASDLResumeAudio(&m_audio);
return true;
} else { } else {
if (!m_audio.samples) { if (!m_audio.samples) {
m_audio.samples = input()->audioBuffers; m_audio.samples = input()->audioBuffers;
} }
GBASDLInitAudio(&m_audio, input()); return GBASDLInitAudio(&m_audio, input());
} }
} }

View File

@ -25,7 +25,7 @@ public:
virtual unsigned sampleRate() const override; virtual unsigned sampleRate() const override;
public slots: public slots:
virtual void start() override; virtual bool start() override;
virtual void pause() override; virtual void pause() override;
virtual void setBufferSamples(int samples) override; virtual void setBufferSamples(int samples) override;

View File

@ -204,9 +204,7 @@ GameController::GameController(QObject* parent)
m_audioThread->setObjectName("Audio Thread"); m_audioThread->setObjectName("Audio Thread");
m_audioThread->start(QThread::TimeCriticalPriority); m_audioThread->start(QThread::TimeCriticalPriority);
m_audioProcessor->moveToThread(m_audioThread); m_audioProcessor->moveToThread(m_audioThread);
connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause())); connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
connect(this, SIGNAL(gameUnpaused(GBAThread*)), m_audioProcessor, SLOT(start()));
connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(pollEvents())); connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(pollEvents()));
connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updateAutofire())); connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updateAutofire()));
} }
@ -341,6 +339,17 @@ void GameController::openGame(bool biosOnly) {
if (!GBAThreadStart(&m_threadContext)) { if (!GBAThreadStart(&m_threadContext)) {
m_gameOpen = false; m_gameOpen = false;
emit gameFailed(); emit gameFailed();
} else if (m_audioProcessor) {
bool started = false;
QMetaObject::invokeMethod(m_audioProcessor, "start", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, started));
if (!started) {
LOG(ERROR) << tr("Failed to start audio processor");
// Don't freeze!
m_audioSync = false;
m_videoSync = true;
m_threadContext.sync.audioWait = false;
m_threadContext.sync.videoFrameWait = true;
}
} }
} }
@ -900,12 +909,19 @@ void GameController::reloadAudioDriver() {
m_audioProcessor->requestSampleRate(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(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause())); connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
connect(this, SIGNAL(gameUnpaused(GBAThread*)), m_audioProcessor, SLOT(start()));
if (isLoaded()) { if (isLoaded()) {
m_audioProcessor->setInput(&m_threadContext); m_audioProcessor->setInput(&m_threadContext);
QMetaObject::invokeMethod(m_audioProcessor, "start"); bool started = false;
QMetaObject::invokeMethod(m_audioProcessor, "start", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, started));
if (!started) {
LOG(ERROR) << tr("Failed to start audio processor");
// Don't freeze!
m_audioSync = false;
m_videoSync = true;
m_threadContext.sync.audioWait = false;
m_threadContext.sync.videoFrameWait = true;
}
} }
} }