Audio rendering in Qt

This commit is contained in:
Jeffrey Pfau 2014-01-29 00:45:25 -08:00
parent 8a82144ceb
commit baeaf8729f
7 changed files with 113 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -6,10 +6,13 @@
#include <QObject>
#include <QString>
#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;

View File

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

View File

@ -1,6 +1,7 @@
#ifndef QGBA_WINDOW
#define QGBA_WINDOW
#include <QAudioOutput>
#include <QMainWindow>
#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;
};