mirror of https://github.com/mgba-emu/mgba.git
Move AudioThread into AudioProcessor, run in its own thread
This commit is contained in:
parent
d5c0dffb29
commit
62f6471c0d
|
@ -8,17 +8,23 @@ extern "C" {
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
AudioDevice::AudioDevice(GBAThread* threadContext, QObject* parent)
|
AudioDevice::AudioDevice(QObject* parent)
|
||||||
: QIODevice(parent)
|
: QIODevice(parent)
|
||||||
, m_context(threadContext)
|
, m_context(nullptr)
|
||||||
{
|
{
|
||||||
setOpenMode(ReadOnly);
|
setOpenMode(ReadOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDevice::setFormat(const QAudioFormat& format) {
|
void AudioDevice::setFormat(const QAudioFormat& format) {
|
||||||
// TODO: merge where the fudge rate exists
|
if (!GBAThreadHasStarted(m_context)) {
|
||||||
float fudgeRate = 16853760.0f / GBA_ARM7TDMI_FREQUENCY;
|
return;
|
||||||
m_ratio = format.sampleRate() / (float) (m_context->gba->audio.sampleRate * fudgeRate);
|
}
|
||||||
|
// 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) {
|
qint64 AudioDevice::readData(char* data, qint64 maxSize) {
|
||||||
|
@ -36,53 +42,3 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) {
|
||||||
qint64 AudioDevice::writeData(const char*, qint64) {
|
qint64 AudioDevice::writeData(const char*, qint64) {
|
||||||
return 0;
|
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();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
#ifndef QGBA_AUDIO_DEVICE
|
#ifndef QGBA_AUDIO_DEVICE
|
||||||
#define QGBA_AUDIO_DEVICE
|
#define QGBA_AUDIO_DEVICE
|
||||||
|
|
||||||
#include <QAudioFormat>
|
#include <QAudioFormat>
|
||||||
#include <QAudioOutput>
|
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
struct GBAThread;
|
struct GBAThread;
|
||||||
|
|
||||||
|
@ -14,8 +11,9 @@ class AudioDevice : public QIODevice {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AudioDevice(GBAThread* threadContext, QObject* parent = nullptr);
|
AudioDevice(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
void setInput(GBAThread* input);
|
||||||
void setFormat(const QAudioFormat& format);
|
void setFormat(const QAudioFormat& format);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -28,28 +26,6 @@ private:
|
||||||
float m_ratio;
|
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
|
#endif
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
|
@ -21,7 +21,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
find_package(Qt5Widgets REQUIRED)
|
find_package(Qt5Widgets REQUIRED)
|
||||||
find_package(OpenGL 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)
|
if(USE_GDB_STUB)
|
||||||
set(SOURCE_FILES ${PLATFORM_SRC} ${SOURCE_FILES} GDBController.cpp GDBWindow.cpp)
|
set(SOURCE_FILES ${PLATFORM_SRC} ${SOURCE_FILES} GDBController.cpp GDBWindow.cpp)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
#include "GameController.h"
|
#include "GameController.h"
|
||||||
|
|
||||||
|
#include "AudioProcessor.h"
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "gba.h"
|
#include "gba.h"
|
||||||
|
#include "gba-audio.h"
|
||||||
#include "renderers/video-software.h"
|
#include "renderers/video-software.h"
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
}
|
}
|
||||||
|
@ -13,6 +18,8 @@ GameController::GameController(QObject* parent)
|
||||||
, m_drawContext(new uint32_t[256 * 256])
|
, m_drawContext(new uint32_t[256 * 256])
|
||||||
, m_activeKeys(0)
|
, m_activeKeys(0)
|
||||||
, m_rom(nullptr)
|
, m_rom(nullptr)
|
||||||
|
, m_audioThread(new QThread(this))
|
||||||
|
, m_audioProcessor(new AudioProcessor)
|
||||||
{
|
{
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
|
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
|
||||||
|
@ -34,6 +41,7 @@ GameController::GameController(QObject* parent)
|
||||||
};
|
};
|
||||||
m_threadContext.startCallback = [] (GBAThread* context) {
|
m_threadContext.startCallback = [] (GBAThread* context) {
|
||||||
GameController* controller = static_cast<GameController*>(context->userData);
|
GameController* controller = static_cast<GameController*>(context->userData);
|
||||||
|
controller->m_audioProcessor->setInput(context);
|
||||||
controller->gameStarted(context);
|
controller->gameStarted(context);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,12 +61,18 @@ GameController::GameController(QObject* parent)
|
||||||
controller->frameAvailable(controller->m_drawContext);
|
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
|
#ifdef BUILD_SDL
|
||||||
connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(testSDLEvents()));
|
connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(testSDLEvents()));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
GameController::~GameController() {
|
GameController::~GameController() {
|
||||||
|
m_audioProcessor->pause();
|
||||||
|
m_audioThread->quit();
|
||||||
if (GBAThreadIsPaused(&m_threadContext)) {
|
if (GBAThreadIsPaused(&m_threadContext)) {
|
||||||
GBAThreadUnpause(&m_threadContext);
|
GBAThreadUnpause(&m_threadContext);
|
||||||
}
|
}
|
||||||
|
@ -98,6 +112,7 @@ void GameController::loadGame(const QString& path) {
|
||||||
|
|
||||||
m_threadContext.rom = VFileFromFD(m_rom->handle());
|
m_threadContext.rom = VFileFromFD(m_rom->handle());
|
||||||
m_threadContext.fname = path.toLocal8Bit().constData();
|
m_threadContext.fname = path.toLocal8Bit().constData();
|
||||||
|
|
||||||
GBAThreadStart(&m_threadContext);
|
GBAThreadStart(&m_threadContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +140,9 @@ void GameController::setPaused(bool paused) {
|
||||||
}
|
}
|
||||||
if (paused) {
|
if (paused) {
|
||||||
GBAThreadPause(&m_threadContext);
|
GBAThreadPause(&m_threadContext);
|
||||||
|
m_audioProcessor->pause();
|
||||||
} else {
|
} else {
|
||||||
|
m_audioProcessor->start();
|
||||||
GBAThreadUnpause(&m_threadContext);
|
GBAThreadUnpause(&m_threadContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,11 @@ extern "C" {
|
||||||
struct GBAAudio;
|
struct GBAAudio;
|
||||||
struct GBAVideoSoftwareRenderer;
|
struct GBAVideoSoftwareRenderer;
|
||||||
|
|
||||||
|
class QThread;
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
class AudioDevice;
|
class AudioProcessor;
|
||||||
|
|
||||||
class GameController : public QObject {
|
class GameController : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -70,6 +72,9 @@ private:
|
||||||
QFile* m_rom;
|
QFile* m_rom;
|
||||||
QFile* m_bios;
|
QFile* m_bios;
|
||||||
|
|
||||||
|
QThread* m_audioThread;
|
||||||
|
AudioProcessor* m_audioProcessor;
|
||||||
|
|
||||||
QMutex m_pauseMutex;
|
QMutex m_pauseMutex;
|
||||||
bool m_pauseAfterFrame;
|
bool m_pauseAfterFrame;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include <QKeySequence>
|
#include <QKeySequence>
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
|
|
||||||
#include "AudioDevice.h"
|
|
||||||
#include "GameController.h"
|
#include "GameController.h"
|
||||||
#include "GDBWindow.h"
|
#include "GDBWindow.h"
|
||||||
#include "GDBController.h"
|
#include "GDBController.h"
|
||||||
|
@ -14,7 +13,6 @@ using namespace QGBA;
|
||||||
|
|
||||||
Window::Window(QWidget* parent)
|
Window::Window(QWidget* parent)
|
||||||
: QMainWindow(parent)
|
: QMainWindow(parent)
|
||||||
, m_audioThread(nullptr)
|
|
||||||
#ifdef USE_GDB_STUB
|
#ifdef USE_GDB_STUB
|
||||||
, m_gdbController(nullptr)
|
, m_gdbController(nullptr)
|
||||||
#endif
|
#endif
|
||||||
|
@ -126,21 +124,12 @@ void Window::gameStarted(GBAThread* context) {
|
||||||
foreach (QAction* action, m_gameActions) {
|
foreach (QAction* action, m_gameActions) {
|
||||||
action->setDisabled(false);
|
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() {
|
void Window::gameStopped() {
|
||||||
foreach (QAction* action, m_gameActions) {
|
foreach (QAction* action, m_gameActions) {
|
||||||
action->setDisabled(true);
|
action->setDisabled(true);
|
||||||
}
|
}
|
||||||
m_audioThread->pause();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setupMenu(QMenuBar* menubar) {
|
void Window::setupMenu(QMenuBar* menubar) {
|
||||||
|
|
|
@ -12,7 +12,6 @@ extern "C" {
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
class AudioThread;
|
|
||||||
class GameController;
|
class GameController;
|
||||||
class GDBController;
|
class GDBController;
|
||||||
|
|
||||||
|
@ -47,7 +46,6 @@ private:
|
||||||
void setupMenu(QMenuBar*);
|
void setupMenu(QMenuBar*);
|
||||||
GameController* m_controller;
|
GameController* m_controller;
|
||||||
Display* m_display;
|
Display* m_display;
|
||||||
AudioThread* m_audioThread;
|
|
||||||
QList<QAction*> m_gameActions;
|
QList<QAction*> m_gameActions;
|
||||||
|
|
||||||
#ifdef USE_GDB_STUB
|
#ifdef USE_GDB_STUB
|
||||||
|
|
Loading…
Reference in New Issue