Move AudioThread into AudioProcessor, run in its own thread
Jeffrey Pfau jeffrey@endrift.com
Tue, 22 Jul 2014 00:10:38 -0700
9 files changed,
128 insertions(+),
96 deletions(-)
M
src/platform/qt/AudioDevice.cpp
→
src/platform/qt/AudioDevice.cpp
@@ -8,17 +8,23 @@ }
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::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(); -}
M
src/platform/qt/AudioDevice.h
→
src/platform/qt/AudioDevice.h
@@ -1,10 +1,7 @@
#ifndef QGBA_AUDIO_DEVICE #define QGBA_AUDIO_DEVICE - #include <QAudioFormat> -#include <QAudioOutput> #include <QIODevice> -#include <QThread> 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:@@ -26,28 +24,6 @@ private:
GBAThread* m_context; float m_drift; 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; }; }
A
src/platform/qt/AudioProcessor.cpp
@@ -0,0 +1,58 @@
+#include "AudioProcessor.h" + +#include "AudioDevice.h" + +#include <QAudioOutput> + +extern "C" { +#include "gba-thread.h" +} + +#include <cassert> + +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(); +}
A
src/platform/qt/AudioProcessor.h
@@ -0,0 +1,33 @@
+#ifndef QGBA_AUDIO_PROCESSOR +#define QGBA_AUDIO_PROCESSOR +#include <QObject> + +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
M
src/platform/qt/CMakeLists.txt
→
src/platform/qt/CMakeLists.txt
@@ -21,7 +21,7 @@
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)
M
src/platform/qt/GameController.cpp
→
src/platform/qt/GameController.cpp
@@ -1,7 +1,12 @@
#include "GameController.h" +#include "AudioProcessor.h" + +#include <QThread> + extern "C" { #include "gba.h" +#include "gba-audio.h" #include "renderers/video-software.h" #include "util/vfs.h" }@@ -13,6 +18,8 @@ : 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 @@ .rewindBufferCapacity = 0
}; m_threadContext.startCallback = [] (GBAThread* context) { GameController* controller = static_cast<GameController*>(context->userData); + controller->m_audioProcessor->setInput(context); controller->gameStarted(context); };@@ -53,12 +61,18 @@ controller->m_pauseMutex.unlock();
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 @@ m_pauseAfterFrame = false;
m_threadContext.rom = VFileFromFD(m_rom->handle()); m_threadContext.fname = path.toLocal8Bit().constData(); + GBAThreadStart(&m_threadContext); }@@ -125,7 +140,9 @@ return;
} if (paused) { GBAThreadPause(&m_threadContext); + m_audioProcessor->pause(); } else { + m_audioProcessor->start(); GBAThreadUnpause(&m_threadContext); } }
M
src/platform/qt/GameController.h
→
src/platform/qt/GameController.h
@@ -17,9 +17,11 @@
struct GBAAudio; struct GBAVideoSoftwareRenderer; +class QThread; + namespace QGBA { -class AudioDevice; +class AudioProcessor; class GameController : public QObject { Q_OBJECT@@ -69,6 +71,9 @@ int m_activeKeys;
QFile* m_rom; QFile* m_bios; + + QThread* m_audioThread; + AudioProcessor* m_audioProcessor; QMutex m_pauseMutex; bool m_pauseAfterFrame;
M
src/platform/qt/Window.cpp
→
src/platform/qt/Window.cpp
@@ -5,7 +5,6 @@ #include <QKeyEvent>
#include <QKeySequence> #include <QMenuBar> -#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 @@ emit startDrawing(m_controller->drawContext(), 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) {
M
src/platform/qt/Window.h
→
src/platform/qt/Window.h
@@ -12,7 +12,6 @@ #include "Display.h"
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<QAction*> m_gameActions; #ifdef USE_GDB_STUB