Qt: Use SDL audio, when available

This commit is contained in:
Jeffrey Pfau 2014-11-19 23:14:44 -08:00
parent 853bcec0c2
commit 51896cdcd6
11 changed files with 242 additions and 92 deletions

View File

@ -2,6 +2,12 @@
#include "AudioDevice.h"
#ifdef BUILD_SDL
#include "AudioProcessorSDL.h"
#else
#include "AudioProcessorQt.h"
#endif
#include <QAudioOutput>
extern "C" {
@ -10,63 +16,19 @@ extern "C" {
using namespace QGBA;
AudioProcessor* AudioProcessor::create() {
#ifdef BUILD_SDL
return new AudioProcessorSDL();
#else
return new AudioProcessorQt();
#endif
}
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());
m_audioOutput->setBufferSize(m_context->audioBuffers * 4);
m_audioOutput->start(m_device);
}
void AudioProcessor::pause() {
if (m_audioOutput) {
m_audioOutput->stop();
}
}
void AudioProcessor::setBufferSamples(int samples) {
if (m_audioOutput) {
m_audioOutput->stop();
m_audioOutput->setBufferSize(samples * 4);
m_audioOutput->start(m_device);
}
}
void AudioProcessor::inputParametersChanged() {
if (m_device) {
m_device->setFormat(m_audioOutput->format());
}
}

View File

@ -4,31 +4,28 @@
struct GBAThread;
class QAudioOutput;
namespace QGBA {
class AudioDevice;
class AudioProcessor : public QObject {
Q_OBJECT
public:
static AudioProcessor* create();
AudioProcessor(QObject* parent = nullptr);
void setInput(GBAThread* input);
virtual void setInput(GBAThread* input);
public slots:
void start();
void pause();
virtual void start() = 0;
virtual void pause() = 0;
void setBufferSamples(int samples);
void inputParametersChanged();
virtual void setBufferSamples(int samples) = 0;
virtual void inputParametersChanged() = 0;
protected:
GBAThread* input() { return m_context; }
private:
GBAThread* m_context;
QAudioOutput* m_audioOutput;
AudioDevice* m_device;
};
}

View File

@ -0,0 +1,72 @@
#include "AudioProcessorQt.h"
#include "AudioDevice.h"
#include <QAudioOutput>
extern "C" {
#include "gba-thread.h"
}
using namespace QGBA;
AudioProcessorQt::AudioProcessorQt(QObject* parent)
: AudioProcessor(parent)
, m_audioOutput(nullptr)
, m_device(nullptr)
{
}
void AudioProcessorQt::setInput(GBAThread* input) {
AudioProcessor::setInput(input);
if (m_device) {
m_device->setInput(input);
if (m_audioOutput) {
m_device->setFormat(m_audioOutput->format());
}
}
}
void AudioProcessorQt::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(input());
m_device->setFormat(m_audioOutput->format());
m_audioOutput->setBufferSize(input()->audioBuffers * 4);
m_audioOutput->start(m_device);
}
void AudioProcessorQt::pause() {
if (m_audioOutput) {
m_audioOutput->stop();
}
}
void AudioProcessorQt::setBufferSamples(int samples) {
if (m_audioOutput) {
m_audioOutput->stop();
m_audioOutput->setBufferSize(samples * 4);
m_audioOutput->start(m_device);
}
}
void AudioProcessorQt::inputParametersChanged() {
if (m_device) {
m_device->setFormat(m_audioOutput->format());
}
}

View File

@ -0,0 +1,31 @@
#ifndef QGBA_AUDIO_PROCESSOR_QT
#define QGBA_AUDIO_PROCESSOR_QT
#include "AudioProcessor.h"
namespace QGBA {
class AudioDevice;
class AudioProcessorQt : public AudioProcessor {
Q_OBJECT
public:
AudioProcessorQt(QObject* parent = nullptr);
virtual void setInput(GBAThread* input);
public slots:
virtual void start();
virtual void pause();
virtual void setBufferSamples(int samples);
virtual void inputParametersChanged();
private:
QAudioOutput* m_audioOutput;
AudioDevice* m_device;
};
}
#endif

View File

@ -0,0 +1,41 @@
#include "AudioProcessorSDL.h"
extern "C" {
#include "gba-thread.h"
}
using namespace QGBA;
AudioProcessorSDL::AudioProcessorSDL(QObject* parent)
: AudioProcessor(parent)
, m_audio()
{
}
AudioProcessorSDL::~AudioProcessorSDL() {
GBASDLDeinitAudio(&m_audio);
}
void AudioProcessorSDL::start() {
if (m_audio.thread) {
GBASDLResumeAudio(&m_audio);
} else {
m_audio.samples = input()->audioBuffers;
GBASDLInitAudio(&m_audio, input());
}
}
void AudioProcessorSDL::pause() {
GBASDLPauseAudio(&m_audio);
}
void AudioProcessorSDL::setBufferSamples(int samples) {
if (m_audio.thread) {
GBASDLDeinitAudio(&m_audio);
m_audio.samples = samples;
GBASDLInitAudio(&m_audio, input());
}
}
void AudioProcessorSDL::inputParametersChanged() {
}

View File

@ -0,0 +1,35 @@
#ifndef QGBA_AUDIO_PROCESSOR_SDL
#define QGBA_AUDIO_PROCESSOR_SDL
#include "AudioProcessor.h"
#ifdef BUILD_SDL
extern "C" {
#include "platform/sdl/sdl-audio.h"
}
namespace QGBA {
class AudioProcessorSDL : public AudioProcessor {
Q_OBJECT
public:
AudioProcessorSDL(QObject* parent = nullptr);
~AudioProcessorSDL();
public slots:
virtual void start();
virtual void pause();
virtual void setBufferSamples(int samples);
virtual void inputParametersChanged();
private:
GBASDLAudio m_audio;
};
}
#endif
#endif

View File

@ -11,7 +11,7 @@ if(BUILD_SDL)
link_directories(${SDL2_LIBDIR})
endif()
set(PLATFORM_LIBRARY "${PLATFORM_LIBRARY};${SDL_LIBRARY};${SDLMAIN_LIBRARY}")
set(PLATFORM_SRC ${PLATFORM_SRC} ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-events.c)
set(PLATFORM_SRC ${PLATFORM_SRC} ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-events.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-audio.c)
include_directories(${SDL_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/src/platform/sdl)
endif()
@ -23,7 +23,8 @@ find_package(Qt5OpenGL)
find_package(Qt5Widgets)
find_package(OpenGL)
if(NOT Qt5Multimedia_FOUND OR NOT Qt5OpenGL_FOUND OR NOT Qt5Widgets_FOUND OR NOT OPENGL_FOUND)
if(NOT Qt5OpenGL_FOUND OR NOT Qt5Widgets_FOUND OR NOT OPENGL_FOUND)
message(WARNING "Cannot find Qt modules")
set(BUILD_QT OFF PARENT_SCOPE)
return()
endif()
@ -52,6 +53,16 @@ qt5_wrap_ui(UI_FILES
LogView.ui
VideoView.ui)
if(BUILD_SDL)
list(APPEND SOURCE_FILES AudioProcessorSDL.cpp)
elseif(Qt5Multimedia_FOUND)
list(APPEND SOURCE_FILES AudioProcessorQt.cpp)
else()
message(WARNING "No supported audio modules found")
set(BUILD_QT OFF PARENT_SCOPE)
return()
endif()
if(USE_GDB_STUB)
set(SOURCE_FILES ${PLATFORM_SRC} ${SOURCE_FILES} GDBController.cpp GDBWindow.cpp)
endif()
@ -68,5 +79,8 @@ endif()
add_executable(mGBA WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/mgba.icns ${SOURCE_FILES} ${UI_FILES} ${RESOURCES})
set_target_properties(mGBA PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in)
qt5_use_modules(mGBA Widgets Multimedia OpenGL)
qt5_use_modules(mGBA Widgets OpenGL)
if(Qt5Multimedia_FOUND)
qt5_use_modules(mGBA Multimedia)
endif()
target_link_libraries(mGBA ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY} ${BINARY_NAME} Qt5::Widgets)

View File

@ -22,7 +22,7 @@ GameController::GameController(QObject* parent)
, m_activeKeys(0)
, m_gameOpen(false)
, m_audioThread(new QThread(this))
, m_audioProcessor(new AudioProcessor)
, m_audioProcessor(AudioProcessor::create())
, m_videoSync(VIDEO_SYNC)
, m_audioSync(AUDIO_SYNC)
, m_turbo(false)

View File

@ -81,8 +81,6 @@ int main(int argc, char** argv) {
struct GBAThread context = {
.renderer = &renderer.d.d,
.startCallback = _GBASDLStart,
.cleanCallback = _GBASDLClean,
.userData = &renderer
};
@ -92,10 +90,7 @@ int main(int argc, char** argv) {
GBAMapArgumentsToContext(&args, &context);
renderer.audio.samples = context.audioBuffers;
GBASDLInitAudio(&renderer.audio);
if (renderer.audio.samples > context.audioBuffers) {
context.audioBuffers = renderer.audio.samples * 2;
}
GBASDLInitAudio(&renderer.audio, &context);
renderer.events.bindings = &inputMap;
GBASDLInitBindings(&inputMap);
@ -140,14 +135,3 @@ static void _GBASDLDeinit(struct SDLSoftwareRenderer* renderer) {
SDL_Quit();
}
static void _GBASDLStart(struct GBAThread* threadContext) {
struct SDLSoftwareRenderer* renderer = threadContext->userData;
renderer->audio.audio = &threadContext->gba->audio;
renderer->audio.thread = threadContext;
}
static void _GBASDLClean(struct GBAThread* threadContext) {
struct SDLSoftwareRenderer* renderer = threadContext->userData;
renderer->audio.audio = 0;
}

View File

@ -7,7 +7,7 @@
static void _GBASDLAudioCallback(void* context, Uint8* data, int len);
bool GBASDLInitAudio(struct GBASDLAudio* context) {
bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContext) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
GBALog(0, GBA_LOG_ERROR, "Could not initialize SDL sound system");
return false;
@ -19,14 +19,17 @@ bool GBASDLInitAudio(struct GBASDLAudio* context) {
context->desiredSpec.samples = context->samples;
context->desiredSpec.callback = _GBASDLAudioCallback;
context->desiredSpec.userdata = context;
context->audio = 0;
context->thread = 0;
context->drift = 0.f;
if (SDL_OpenAudio(&context->desiredSpec, &context->obtainedSpec) < 0) {
GBALog(0, GBA_LOG_ERROR, "Could not open SDL sound system");
return false;
}
context->thread = threadContext;
context->samples = context->obtainedSpec.samples;
if (context->samples > threadContext->audioBuffers) {
threadContext->audioBuffers = context->samples * 2;
}
SDL_PauseAudio(0);
return true;
}
@ -38,16 +41,26 @@ void GBASDLDeinitAudio(struct GBASDLAudio* context) {
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
void GBASDLPauseAudio(struct GBASDLAudio* context) {
UNUSED(context);
SDL_PauseAudio(1);
}
void GBASDLResumeAudio(struct GBASDLAudio* context) {
UNUSED(context);
SDL_PauseAudio(0);
}
static void _GBASDLAudioCallback(void* context, Uint8* data, int len) {
struct GBASDLAudio* audioContext = context;
if (!context || !audioContext->audio) {
if (!context || !audioContext->thread) {
memset(data, 0, len);
return;
}
audioContext->ratio = GBAAudioCalculateRatio(audioContext->audio, audioContext->thread->fpsTarget, audioContext->obtainedSpec.freq);
audioContext->ratio = GBAAudioCalculateRatio(&audioContext->thread->gba->audio, audioContext->thread->fpsTarget, audioContext->obtainedSpec.freq);
struct GBAStereoSample* ssamples = (struct GBAStereoSample*) data;
len /= 2 * audioContext->obtainedSpec.channels;
if (audioContext->obtainedSpec.channels == 2) {
GBAAudioResampleNN(audioContext->audio, audioContext->ratio, &audioContext->drift, ssamples, len);
GBAAudioResampleNN(&audioContext->thread->gba->audio, audioContext->ratio, &audioContext->drift, ssamples, len);
}
}

View File

@ -15,11 +15,12 @@ struct GBASDLAudio {
float drift;
float ratio;
struct GBAAudio* audio;
struct GBAThread* thread;
};
bool GBASDLInitAudio(struct GBASDLAudio* context);
bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContext);
void GBASDLDeinitAudio(struct GBASDLAudio* context);
void GBASDLPauseAudio(struct GBASDLAudio* context);
void GBASDLResumeAudio(struct GBASDLAudio* context);
#endif