diff --git a/src/platform/qt/AudioDevice.cpp b/src/platform/qt/AudioDevice.cpp new file mode 100644 index 000000000..a4fdda378 --- /dev/null +++ b/src/platform/qt/AudioDevice.cpp @@ -0,0 +1,33 @@ +#include "AudioDevice.h" + +extern "C" { +#include "gba.h" +#include "gba-audio.h" +} + +using namespace QGBA; + +AudioDevice::AudioDevice(GBAAudio* audio, QObject* parent) + : QIODevice(parent) + , m_audio(audio) +{ + 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_audio->sampleRate * fudgeRate); +} + +qint64 AudioDevice::readData(char* data, qint64 maxSize) { + if (maxSize > 0xFFFFFFFF) { + maxSize = 0xFFFFFFFF; + } + + return GBAAudioResampleNN(m_audio, m_ratio, &m_drift, reinterpret_cast(data), maxSize / sizeof(GBAStereoSample)) * sizeof(GBAStereoSample); +} + +qint64 AudioDevice::writeData(const char*, qint64) { + return 0; +} diff --git a/src/platform/qt/AudioDevice.h b/src/platform/qt/AudioDevice.h new file mode 100644 index 000000000..d80211078 --- /dev/null +++ b/src/platform/qt/AudioDevice.h @@ -0,0 +1,31 @@ +#ifndef QGBA_AUDIO_DEVICE +#define QGBA_AUDIO_DEVICE + +#include +#include + +struct GBAAudio; + +namespace QGBA { + +class AudioDevice : public QIODevice { +Q_OBJECT + +public: + AudioDevice(GBAAudio* audio, QObject* parent = 0); + + void setFormat(const QAudioFormat& format); + +protected: + virtual qint64 readData(char* data, qint64 maxSize); + virtual qint64 writeData(const char* data, qint64 maxSize); + +private: + GBAAudio* m_audio; + float m_drift; + float m_ratio; +}; + +} + +#endif diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index d991aeebc..1798d6295 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -14,11 +14,11 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) find_package(Qt5Widgets REQUIRED) set(UI_FILES Window.ui) -set(SOURCE_FILES Display.cpp GameController.cpp Window.cpp) +set(SOURCE_FILES AudioDevice.cpp Display.cpp GameController.cpp Window.cpp) qt5_wrap_ui(UI_HEADERS ${UI_FILES}) add_executable(QGBAc WIN32 MACOSX_BUNDLE ${UI_FILES} ${UI_HEADERS} main.cpp ${SOURCE_FILES}) -qt5_use_modules(QGBAc Widgets OpenGL) +qt5_use_modules(QGBAc Widgets Multimedia OpenGL) target_link_libraries(QGBAc ${OPENGL_LIBRARY} ${BINARY_NAME}) diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 4d9012bed..0aea28aab 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -1,6 +1,7 @@ #include "GameController.h" extern "C" { +#include "gba.h" #include "renderers/video-software.h" } @@ -9,6 +10,7 @@ using namespace QGBA; GameController::GameController(QObject* parent) : QObject(parent) , m_drawContext(256, 256, QImage::Format_RGB32) + , m_audioContext(0) { m_renderer = new GBAVideoSoftwareRenderer; GBAVideoSoftwareRendererCreate(m_renderer); @@ -19,11 +21,14 @@ GameController::GameController(QObject* parent) m_threadContext.renderer = &m_renderer->d; m_threadContext.frameskip = 0; m_threadContext.sync.videoFrameWait = 0; - m_threadContext.sync.audioWait = 0; - m_threadContext.startCallback = 0; + m_threadContext.sync.audioWait = 1; + m_threadContext.startCallback = [] (GBAThread* context) { + GameController* controller = static_cast(context->userData); + controller->setupAudio(&context->gba->audio); + }; m_threadContext.cleanCallback = 0; m_threadContext.frameCallback = [] (GBAThread* context) { - GameController* controller = (GameController*) context->userData; + GameController* controller = static_cast(context->userData); controller->frameAvailable(controller->m_drawContext); }; m_threadContext.userData = this; @@ -48,3 +53,12 @@ bool GameController::loadGame(const QString& path) { GBAThreadStart(&m_threadContext); return true; } + +void GameController::setupAudio(GBAAudio* audio) { + if (m_audioContext) { + delete m_audioContext; + } + m_audioContext = new AudioDevice(audio); + + emit audioDeviceAvailable(m_audioContext); +} diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index c3e8b525c..0b88db964 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -6,10 +6,13 @@ #include #include +#include "AudioDevice.h" + extern "C" { #include "gba-thread.h" } +struct GBAAudio; struct GBAVideoSoftwareRenderer; namespace QGBA { @@ -23,12 +26,16 @@ public: signals: void frameAvailable(const QImage&); + void audioDeviceAvailable(AudioDevice*); public slots: bool loadGame(const QString& path); private: + void setupAudio(GBAAudio* audio); + QImage m_drawContext; + AudioDevice* m_audioContext; GBAThread m_threadContext; GBAVideoSoftwareRenderer* m_renderer; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index cd215d4e0..6efa938e9 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -11,6 +11,7 @@ Window::Window(QWidget* parent) : QMainWindow(parent) { m_display = new Display(this); setCentralWidget(m_display); connect(m_controller, SIGNAL(frameAvailable(const QImage&)), m_display, SLOT(draw(const QImage&))); + connect(m_controller, SIGNAL(audioDeviceAvailable(AudioDevice*)), this, SLOT(setupAudio(AudioDevice*))); connect(actionOpen, SIGNAL(triggered()), this, SLOT(selectROM())); } @@ -21,3 +22,20 @@ void Window::selectROM() { m_controller->loadGame(filename); } } + +void Window::setupAudio(AudioDevice* device) { + if (!m_audio) { + QAudioFormat format; + format.setSampleRate(44100); + format.setChannelCount(2); + format.setSampleSize(16); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + + m_audio = new QAudioOutput(format, this); + m_audio->setBufferSize(1024); + } + device->setFormat(m_audio->format()); + m_audio->start(device); +} diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index bbed0b405..304f739b1 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -1,6 +1,7 @@ #ifndef QGBA_WINDOW #define QGBA_WINDOW +#include #include #include "GameController.h" @@ -19,7 +20,11 @@ public: public slots: void selectROM(); +private slots: + void setupAudio(AudioDevice*); + private: + QAudioOutput* m_audio; GameController* m_controller; Display* m_display; };