all repos — mgba @ baeaf8729ff5591969bed96c115d3545886013a6

mGBA Game Boy Advance Emulator

Audio rendering in Qt
Jeffrey Pfau jeffrey@endrift.com
Wed, 29 Jan 2014 00:45:25 -0800
commit

baeaf8729ff5591969bed96c115d3545886013a6

parent

8a82144ceb56398f2827c6abf8668972fb9caefb

A 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<GBAStereoSample*>(data), maxSize / sizeof(GBAStereoSample)) * sizeof(GBAStereoSample); +} + +qint64 AudioDevice::writeData(const char*, qint64) { + return 0; +}
A src/platform/qt/AudioDevice.h

@@ -0,0 +1,31 @@

+#ifndef QGBA_AUDIO_DEVICE +#define QGBA_AUDIO_DEVICE + +#include <QAudioFormat> +#include <QIODevice> + +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
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -14,11 +14,11 @@

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})
M src/platform/qt/GameController.cppsrc/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 @@

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 @@ m_threadContext.frameskip = 0;

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<GameController*>(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<GameController*>(context->userData); controller->frameAvailable(controller->m_drawContext); }; m_threadContext.userData = this;

@@ -48,3 +53,12 @@ m_threadContext.fname = path.toLocal8Bit().constData();

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); +}
M src/platform/qt/GameController.hsrc/platform/qt/GameController.h

@@ -6,10 +6,13 @@ #include <QImage>

#include <QObject> #include <QString> +#include "AudioDevice.h" + extern "C" { #include "gba-thread.h" } +struct GBAAudio; struct GBAVideoSoftwareRenderer; namespace QGBA {

@@ -23,12 +26,16 @@ ~GameController();

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;
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -11,6 +11,7 @@ m_controller = new GameController(this);

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 @@ if (!filename.isEmpty()) {

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); +}
M src/platform/qt/Window.hsrc/platform/qt/Window.h

@@ -1,6 +1,7 @@

#ifndef QGBA_WINDOW #define QGBA_WINDOW +#include <QAudioOutput> #include <QMainWindow> #include "GameController.h"

@@ -19,7 +20,11 @@

public slots: void selectROM(); +private slots: + void setupAudio(AudioDevice*); + private: + QAudioOutput* m_audio; GameController* m_controller; Display* m_display; };