Move AudioThread into AudioProcessor, run in its own thread

This commit is contained in:
Jeffrey Pfau 2014-07-22 00:10:38 -07:00
parent d5c0dffb29
commit 62f6471c0d
9 changed files with 128 additions and 96 deletions

View File

@ -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();
}

View File

@ -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:
@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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)

View File

@ -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 @@ 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<GameController*>(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);
}
}

View File

@ -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;
};

View File

@ -5,7 +5,6 @@
#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 @@ 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) {

View File

@ -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<QAction*> m_gameActions;
#ifdef USE_GDB_STUB