diff --git a/src/platform/qt/AudioDevice.cpp b/src/platform/qt/AudioDevice.cpp index 6fdb32fdb..17ba9e7e6 100644 --- a/src/platform/qt/AudioDevice.cpp +++ b/src/platform/qt/AudioDevice.cpp @@ -3,13 +3,14 @@ extern "C" { #include "gba.h" #include "gba-audio.h" +#include "gba-thread.h" } using namespace QGBA; -AudioDevice::AudioDevice(GBAAudio* audio, QObject* parent) +AudioDevice::AudioDevice(GBAThread* threadContext, QObject* parent) : QIODevice(parent) - , m_audio(audio) + , m_context(threadContext) { setOpenMode(ReadOnly); } @@ -17,7 +18,7 @@ AudioDevice::AudioDevice(GBAAudio* audio, QObject* parent) void AudioDevice::setFormat(const QAudioFormat& format) { // TODO: merge where the fudge rate exists float fudgeRate = 16853760.0f / GBA_ARM7TDMI_FREQUENCY; - m_ratio = format.sampleRate() / (float) (m_audio->sampleRate * fudgeRate); + m_ratio = format.sampleRate() / (float) (m_context->gba->audio.sampleRate * fudgeRate); } qint64 AudioDevice::readData(char* data, qint64 maxSize) { @@ -25,7 +26,11 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) { maxSize = 0xFFFFFFFF; } - return GBAAudioResampleNN(m_audio, m_ratio, &m_drift, reinterpret_cast(data), maxSize / sizeof(GBAStereoSample)) * sizeof(GBAStereoSample); + if (!m_context->gba) { + return 0; + } + + return GBAAudioResampleNN(&m_context->gba->audio, m_ratio, &m_drift, reinterpret_cast(data), maxSize / sizeof(GBAStereoSample)) * sizeof(GBAStereoSample); } qint64 AudioDevice::writeData(const char*, qint64) { @@ -38,11 +43,12 @@ AudioThread::AudioThread(QObject* parent) // Nothing to do } -void AudioThread::setInput(GBAAudio* input) { +void AudioThread::setInput(GBAThread* input) { m_input = input; } void AudioThread::shutdown() { + disconnect(); m_audioOutput->stop(); quit(); } diff --git a/src/platform/qt/AudioDevice.h b/src/platform/qt/AudioDevice.h index 5bddac4c9..5665c17f0 100644 --- a/src/platform/qt/AudioDevice.h +++ b/src/platform/qt/AudioDevice.h @@ -6,19 +6,17 @@ #include #include -struct GBAAudio; +struct GBAThread; namespace QGBA { - - class AudioThread : public QThread { Q_OBJECT public: AudioThread(QObject* parent = nullptr); - void setInput(GBAAudio* input); + void setInput(GBAThread* input); public slots: void shutdown(); @@ -27,7 +25,7 @@ protected: void run(); private: - GBAAudio* m_input; + GBAThread* m_input; QAudioOutput* m_audioOutput; }; @@ -35,7 +33,7 @@ class AudioDevice : public QIODevice { Q_OBJECT public: - AudioDevice(GBAAudio* audio, QObject* parent = nullptr); + AudioDevice(GBAThread* threadContext, QObject* parent = nullptr); void setFormat(const QAudioFormat& format); @@ -44,7 +42,7 @@ protected: virtual qint64 writeData(const char* data, qint64 maxSize) override; private: - GBAAudio* m_audio; + GBAThread* m_context; float m_drift; float m_ratio; }; diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index a0f17c261..492437dd2 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -1,5 +1,6 @@ #include "Display.h" +#include #include extern "C" { @@ -32,6 +33,9 @@ Display::Display(QWidget* parent) } void Display::startDrawing(const uint32_t* buffer, GBAThread* thread) { + if (m_drawThread) { + return; + } m_drawThread = new QThread(this); m_painter = new Painter(this); m_painter->setGLContext(this); @@ -48,9 +52,16 @@ void Display::stopDrawing() { if (m_drawThread) { QMetaObject::invokeMethod(m_painter, "stop", Qt::BlockingQueuedConnection); m_drawThread->exit(); + m_drawThread = nullptr; } } +void Display::initializeGL() { + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + swapBuffers(); +} + void Display::resizeEvent(QResizeEvent* event) { if (m_painter) { m_painter->resize(event->size()); @@ -123,5 +134,8 @@ void Painter::stop() { delete m_drawTimer; m_gl->makeCurrent(); glDeleteTextures(1, &m_tex); + glClear(GL_COLOR_BUFFER_BIT); + m_gl->swapBuffers(); m_gl->doneCurrent(); + m_gl->context()->moveToThread(QApplication::instance()->thread()); } diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index 7d626cace..6d6d91ed2 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -21,6 +21,7 @@ public slots: void stopDrawing(); protected: + virtual void initializeGL() override; virtual void paintEvent(QPaintEvent*) override {}; virtual void resizeEvent(QResizeEvent*) override; diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 8a39c0cda..f189da04c 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -11,6 +11,7 @@ GameController::GameController(QObject* parent) : QObject(parent) , m_drawContext(new uint32_t[256 * 256]) , m_audioContext(nullptr) + , m_rom(nullptr) { m_renderer = new GBAVideoSoftwareRenderer; GBAVideoSoftwareRendererCreate(m_renderer); @@ -21,15 +22,19 @@ GameController::GameController(QObject* parent) .frameskip = 0, .biosFd = -1, .renderer = &m_renderer->d, - .sync.videoFrameWait = 0, - .sync.audioWait = 1, .userData = this, .rewindBufferCapacity = 0 }; m_threadContext.startCallback = [] (GBAThread* context) { GameController* controller = static_cast(context->userData); - controller->audioDeviceAvailable(&context->gba->audio); + controller->gameStarted(context); }; + + m_threadContext.cleanCallback = [] (GBAThread* context) { + GameController* controller = static_cast(context->userData); + controller->gameStopped(context); + }; + m_threadContext.frameCallback = [] (GBAThread* context) { GameController* controller = static_cast(context->userData); controller->m_pauseMutex.lock(); @@ -69,6 +74,9 @@ void GameController::setDebugger(ARMDebugger* debugger) { } void GameController::loadGame(const QString& path) { + closeGame(); + m_threadContext.sync.videoFrameWait = 0; + m_threadContext.sync.audioWait = 1; m_rom = new QFile(path); if (!m_rom->open(QIODevice::ReadOnly)) { delete m_rom; @@ -80,7 +88,20 @@ void GameController::loadGame(const QString& path) { m_threadContext.fd = m_rom->handle(); m_threadContext.fname = path.toLocal8Bit().constData(); GBAThreadStart(&m_threadContext); - emit gameStarted(&m_threadContext); +} + +void GameController::closeGame() { + // TODO: Make this threadsafe + if (m_threadContext.state >= THREAD_EXITING) { + return; + } + GBAThreadEnd(&m_threadContext); + GBAThreadJoin(&m_threadContext); + if (m_rom) { + m_rom->close(); + delete m_rom; + } + emit gameStopped(&m_threadContext); } bool GameController::isPaused() { diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index abdfb22d6..a64f853b2 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -36,11 +36,12 @@ public: signals: void frameAvailable(const uint32_t*); - void audioDeviceAvailable(GBAAudio*); void gameStarted(GBAThread*); + void gameStopped(GBAThread*); public slots: void loadGame(const QString& path); + void closeGame(); void setPaused(bool paused); void frameAdvance(); void keyPressed(int key); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 69eab7713..e0c5919c3 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -22,8 +22,8 @@ Window::Window(QWidget* parent) m_controller = new GameController(this); m_display = new Display(); setCentralWidget(m_display); - connect(m_controller, SIGNAL(audioDeviceAvailable(GBAAudio*)), this, SLOT(setupAudio(GBAAudio*))); connect(m_controller, SIGNAL(gameStarted(GBAThread*)), this, SLOT(gameStarted(GBAThread*))); + connect(m_controller, SIGNAL(gameStopped(GBAThread*)), m_display, SLOT(stopDrawing())); connect(this, SIGNAL(startDrawing(const uint32_t*, GBAThread*)), m_display, SLOT(startDrawing(const uint32_t*, GBAThread*)), Qt::QueuedConnection); connect(this, SIGNAL(shutdown()), m_display, SLOT(stopDrawing())); @@ -122,19 +122,18 @@ void Window::gameStarted(GBAThread* context) { foreach (QAction* action, m_gameActions) { action->setDisabled(false); } -} - -void Window::setupAudio(GBAAudio* audio) { AudioThread* thread = new AudioThread(this); - thread->setInput(audio); + thread->setInput(context); thread->start(QThread::HighPriority); connect(this, SIGNAL(shutdown()), thread, SLOT(shutdown())); + connect(m_controller, SIGNAL(gameStopped(GBAThread*)), thread, SLOT(shutdown())); } void Window::setupMenu(QMenuBar* menubar) { menubar->clear(); QMenu* fileMenu = menubar->addMenu(tr("&File")); fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open); + fileMenu->addAction(tr("Sh&utdown"), m_controller, SLOT(closeGame())); QMenu* emulationMenu = menubar->addMenu(tr("&Emulation")); QAction* pause = new QAction(tr("&Pause"), 0); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index d02045492..8e2b2ec9b 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -40,7 +40,6 @@ protected: private slots: void gameStarted(GBAThread*); - void setupAudio(GBAAudio*); private: void setupMenu(QMenuBar*);