mirror of https://github.com/mgba-emu/mgba.git
Qt: Use SDL audio, when available
This commit is contained in:
parent
853bcec0c2
commit
51896cdcd6
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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() {
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue