diff --git a/src/platform/qt/AudioDevice.cpp b/src/platform/qt/AudioDevice.cpp index 190157f34..67f751359 100644 --- a/src/platform/qt/AudioDevice.cpp +++ b/src/platform/qt/AudioDevice.cpp @@ -8,17 +8,23 @@ extern "C" { using namespace QGBA; -AudioDevice::AudioDevice(GBAThread* threadContext, QObject* parent) +AudioDevice::AudioDevice(QObject* parent) : QIODevice(parent) - , m_context(threadContext) + , m_context(nullptr) { setOpenMode(ReadOnly); } 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_context->gba->audio.sampleRate * fudgeRate); + if (!GBAThreadHasStarted(m_context)) { + return; + } + // TODO: make this thread-safe + m_ratio = GBAAudioCalculateRatio(&m_context->gba->audio, 60, format.sampleRate()); +} + +void AudioDevice::setInput(GBAThread* input) { + m_context = input; } qint64 AudioDevice::readData(char* data, qint64 maxSize) { @@ -36,53 +42,3 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) { qint64 AudioDevice::writeData(const char*, qint64) { return 0; } - -AudioThread::AudioThread(QObject* parent) - : QThread(parent) -{ - // Nothing to do -} - -void AudioThread::setInput(GBAThread* input) { - m_input = input; -} - -void AudioThread::shutdown() { - disconnect(); - if (m_audioOutput) { - m_audioOutput->stop(); - delete m_audioOutput; - m_audioOutput = nullptr; - } - if (m_device) { - delete m_device; - m_device = nullptr; - } - quit(); -} - -void AudioThread::pause() { - m_audioOutput->stop(); -} - -void AudioThread::resume() { - m_audioOutput->start(m_device); -} - -void AudioThread::run() { - QAudioFormat format; - format.setSampleRate(44100); - format.setChannelCount(2); - format.setSampleSize(16); - format.setCodec("audio/pcm"); - format.setByteOrder(QAudioFormat::LittleEndian); - format.setSampleType(QAudioFormat::SignedInt); - - m_device = new AudioDevice(m_input); - m_audioOutput = new QAudioOutput(format); - m_audioOutput->setBufferSize(1024); - m_device->setFormat(m_audioOutput->format()); - m_audioOutput->start(m_device); - - exec(); -} diff --git a/src/platform/qt/AudioDevice.h b/src/platform/qt/AudioDevice.h index a4950299b..843ca1d99 100644 --- a/src/platform/qt/AudioDevice.h +++ b/src/platform/qt/AudioDevice.h @@ -1,10 +1,7 @@ #ifndef QGBA_AUDIO_DEVICE #define QGBA_AUDIO_DEVICE - #include -#include #include -#include struct GBAThread; @@ -14,8 +11,9 @@ class AudioDevice : public QIODevice { Q_OBJECT public: - AudioDevice(GBAThread* threadContext, QObject* parent = nullptr); + AudioDevice(QObject* parent = nullptr); + void setInput(GBAThread* input); void setFormat(const QAudioFormat& format); protected: @@ -28,28 +26,6 @@ private: float m_ratio; }; -class AudioThread : public QThread { -Q_OBJECT - -public: - AudioThread(QObject* parent = nullptr); - - void setInput(GBAThread* input); - -public slots: - void shutdown(); - void pause(); - void resume(); - -protected: - void run(); - -private: - GBAThread* m_input; - QAudioOutput* m_audioOutput; - AudioDevice* m_device; -}; - } #endif diff --git a/src/platform/qt/AudioProcessor.cpp b/src/platform/qt/AudioProcessor.cpp new file mode 100644 index 000000000..36b47b833 --- /dev/null +++ b/src/platform/qt/AudioProcessor.cpp @@ -0,0 +1,58 @@ +#include "AudioProcessor.h" + +#include "AudioDevice.h" + +#include + +extern "C" { +#include "gba-thread.h" +} + +#include + +using namespace QGBA; + +AudioProcessor::AudioProcessor(QObject* parent) + : QObject(parent) + , m_audioOutput(nullptr) + , m_device(nullptr) +{ +} + +void AudioProcessor::setInput(GBAThread* input) { + m_context = input; + if (m_device) { + m_device->setInput(input); + if (m_audioOutput) { + m_device->setFormat(m_audioOutput->format()); + } + } +} + +void AudioProcessor::start() { + if (!m_device) { + m_device = new AudioDevice(this); + } + + if (!m_audioOutput) { + QAudioFormat format; + format.setSampleRate(44100); + format.setChannelCount(2); + format.setSampleSize(16); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + + m_audioOutput = new QAudioOutput(format, this); + } + + m_device->setInput(m_context); + m_device->setFormat(m_audioOutput->format()); + + assert(m_audioOutput->thread() == thread()); + m_audioOutput->start(m_device); +} + +void AudioProcessor::pause() { + m_audioOutput->stop(); +} diff --git a/src/platform/qt/AudioProcessor.h b/src/platform/qt/AudioProcessor.h new file mode 100644 index 000000000..84061a7b3 --- /dev/null +++ b/src/platform/qt/AudioProcessor.h @@ -0,0 +1,33 @@ +#ifndef QGBA_AUDIO_PROCESSOR +#define QGBA_AUDIO_PROCESSOR +#include + +struct GBAThread; + +class QAudioOutput; + +namespace QGBA { + +class AudioDevice; + +class AudioProcessor : public QObject { +Q_OBJECT + +public: + AudioProcessor(QObject* parent = nullptr); + + void setInput(GBAThread* input); + +public slots: + void start(); + void pause(); + +private: + GBAThread* m_context; + QAudioOutput* m_audioOutput; + AudioDevice* m_device; +}; + +} + +#endif diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 189bb53e0..2f7da181b 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -21,7 +21,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) find_package(Qt5Widgets REQUIRED) find_package(OpenGL REQUIRED) -set(SOURCE_FILES AudioDevice.cpp Display.cpp GameController.cpp Window.cpp) +set(SOURCE_FILES AudioDevice.cpp AudioProcessor.cpp Display.cpp GameController.cpp Window.cpp) if(USE_GDB_STUB) set(SOURCE_FILES ${PLATFORM_SRC} ${SOURCE_FILES} GDBController.cpp GDBWindow.cpp) diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 2893436b8..3e67b5ca3 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -1,7 +1,12 @@ #include "GameController.h" +#include "AudioProcessor.h" + +#include + extern "C" { #include "gba.h" +#include "gba-audio.h" #include "renderers/video-software.h" #include "util/vfs.h" } @@ -13,6 +18,8 @@ GameController::GameController(QObject* parent) , m_drawContext(new uint32_t[256 * 256]) , m_activeKeys(0) , m_rom(nullptr) + , m_audioThread(new QThread(this)) + , m_audioProcessor(new AudioProcessor) { #ifdef BUILD_SDL SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE); @@ -34,6 +41,7 @@ GameController::GameController(QObject* parent) }; m_threadContext.startCallback = [] (GBAThread* context) { GameController* controller = static_cast(context->userData); + controller->m_audioProcessor->setInput(context); controller->gameStarted(context); }; @@ -53,12 +61,18 @@ GameController::GameController(QObject* parent) controller->frameAvailable(controller->m_drawContext); }; + m_audioThread->start(); + m_audioProcessor->moveToThread(m_audioThread); + connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start())); + #ifdef BUILD_SDL connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(testSDLEvents())); #endif } GameController::~GameController() { + m_audioProcessor->pause(); + m_audioThread->quit(); if (GBAThreadIsPaused(&m_threadContext)) { GBAThreadUnpause(&m_threadContext); } @@ -98,6 +112,7 @@ void GameController::loadGame(const QString& path) { m_threadContext.rom = VFileFromFD(m_rom->handle()); m_threadContext.fname = path.toLocal8Bit().constData(); + GBAThreadStart(&m_threadContext); } @@ -125,7 +140,9 @@ void GameController::setPaused(bool paused) { } if (paused) { GBAThreadPause(&m_threadContext); + m_audioProcessor->pause(); } else { + m_audioProcessor->start(); GBAThreadUnpause(&m_threadContext); } } diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index be64fbaa2..f0ea6b90a 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -17,9 +17,11 @@ extern "C" { struct GBAAudio; struct GBAVideoSoftwareRenderer; +class QThread; + namespace QGBA { -class AudioDevice; +class AudioProcessor; class GameController : public QObject { Q_OBJECT @@ -70,6 +72,9 @@ private: QFile* m_rom; QFile* m_bios; + QThread* m_audioThread; + AudioProcessor* m_audioProcessor; + QMutex m_pauseMutex; bool m_pauseAfterFrame; }; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 45d2ca543..aec94f933 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -5,7 +5,6 @@ #include #include -#include "AudioDevice.h" #include "GameController.h" #include "GDBWindow.h" #include "GDBController.h" @@ -14,7 +13,6 @@ using namespace QGBA; Window::Window(QWidget* parent) : QMainWindow(parent) - , m_audioThread(nullptr) #ifdef USE_GDB_STUB , m_gdbController(nullptr) #endif @@ -126,21 +124,12 @@ void Window::gameStarted(GBAThread* context) { foreach (QAction* action, m_gameActions) { action->setDisabled(false); } - if (!m_audioThread) { - m_audioThread = new AudioThread(this); - connect(this, SIGNAL(shutdown()), m_audioThread, SLOT(shutdown())); - m_audioThread->setInput(context); - m_audioThread->start(QThread::HighPriority); - } else { - m_audioThread->resume(); - } } void Window::gameStopped() { foreach (QAction* action, m_gameActions) { action->setDisabled(true); } - m_audioThread->pause(); } void Window::setupMenu(QMenuBar* menubar) { diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 429c16741..090723461 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -12,7 +12,6 @@ extern "C" { namespace QGBA { -class AudioThread; class GameController; class GDBController; @@ -47,7 +46,6 @@ private: void setupMenu(QMenuBar*); GameController* m_controller; Display* m_display; - AudioThread* m_audioThread; QList m_gameActions; #ifdef USE_GDB_STUB