[Qt] PulseAudio (Linux) sound driver

This commit is contained in:
Marcos Medeiros 2014-09-08 06:37:11 +00:00
parent 1b50f1aa44
commit efed708824
5 changed files with 263 additions and 4 deletions

View File

@ -6,6 +6,7 @@ TARGET = fbaqt
linux:QT += x11extras
#===============================================================================
# DRIVERS
#===============================================================================
@ -20,7 +21,7 @@ DRV_SMS = true
DRV_MEGADRIVE = true
DRV_NEOGEO = true
DRV_PCE = true
DRV_PGM = true
DRV_PGM = false
DRV_PRE90S = true
DRV_PST90S = true
DRV_SEGA = true
@ -86,6 +87,9 @@ DEFINES += BUILD_QT \
WITH_QTCREATOR \
INCLUDE_LIB_PNGH
linux: DEFINES += BUILD_QT_LINUX
macx: DEFINES += BUILD_QT_MACX
# no warnings...
QMAKE_CXXFLAGS += -w
QMAKE_CFLAGS += -w
@ -352,6 +356,7 @@ PRE_TARGETDEPS += \
$$M68K_LIB.target \
LIBS += -lSDL
linux:LIBS += -lpulse-simple
QMAKE_CLEAN += $$GEN/*
#===============================================================================
@ -754,7 +759,9 @@ SOURCES += \
../../src/dep/libs/zlib/zutil.c \
../../src/burn/devices/nmk004.cpp \
../../src/cpu/tlcs90/tlcs90.cpp \
../../src/cpu/tlcs90_intf.cpp
../../src/cpu/tlcs90_intf.cpp \
../../src/burn/devices/kaneko_tmap.cpp \
../../src/intf/audio/linux/aud_pulse_simple.cpp
HEADERS += \
../../src/burn/devices/8255ppi.h \
@ -923,7 +930,9 @@ HEADERS += \
../../src/dep/libs/zlib/zconf.h.in \
../../src/dep/libs/zlib/zlib.h \
../../src/dep/libs/zlib/zutil.h \
../../src/burn/devices/nmk004.h
../../src/burn/devices/nmk004.h \
../../src/burn/devices/kaneko_tmap.h \
../../src/intf/audio/linux/ringbuffer.h
OTHER_FILES +=

View File

@ -30,7 +30,7 @@ MainWindow::MainWindow(QWidget *parent) :
show();
ruby::video.driver("X-Video");
ruby::video.driver("OpenGL");
ruby::video.set(ruby::Video::Handle, m_viewport->id());
ruby::video.set(ruby::Video::Depth, 24u);

View File

@ -24,6 +24,9 @@ static UINT32 nAudActive = 0;
#elif defined (_XBOX)
extern struct AudOut AudOutXAudio2;
#elif defined (BUILD_QT)
#ifdef BUILD_QT_LINUX
extern struct AudOut AudOutPulseSimple;
#endif
extern struct AudOut AudOutQtSound;
#endif
@ -37,6 +40,9 @@ static struct AudOut *pAudOut[]=
#elif defined (_XBOX)
&AudOutXAudio2,
#elif defined (BUILD_QT)
#ifdef BUILD_QT_LINUX
&AudOutPulseSimple,
#endif
&AudOutQtSound,
#endif
};

View File

@ -0,0 +1,190 @@
// C++11 simple pulse audio driver
#include <iostream>
#include <thread>
#include <chrono>
#include <pulse/simple.h>
#include <pulse/error.h>
#include "burner.h"
#include "ringbuffer.h"
static ring_buffer<short> *buffer = nullptr;
static pa_simple *pa_stream = nullptr;
static std::thread *streamer_thread = nullptr;
static volatile bool streamer_stop = false;
static volatile bool streamer_is_running = false;
// Samples per segment
static int samples_per_segment = 0;
static unsigned int pas_sound_fps;
static int (*pas_get_next_sound)(int);
static int pas_default_sound_filler(int)
{
if (nAudNextSound == nullptr)
return 1;
memset(nAudNextSound, 0, nAudSegLen * 4);
return 0;
}
static int pas_blank_sound()
{
if (nAudNextSound != nullptr)
AudWriteSilence();
return 0;
}
static int pas_sound_check()
{
// 5 segments ahead...
while (buffer->size() > (samples_per_segment * 5)) {
std::this_thread::sleep_for(std::chrono::microseconds(5));
return 0;
}
pas_get_next_sound(1);
buffer->write(nAudNextSound, samples_per_segment);
return 0;
}
static int pas_exit()
{
nAudNextSound = NULL;
return 0;
}
static int pas_set_callback(int (*callback)(int))
{
if (callback == NULL) {
pas_get_next_sound = pas_default_sound_filler;
} else {
pas_get_next_sound = callback;
}
return 0;
}
static void pas_audio_streamer(void)
{
short *buf = new short[samples_per_segment];
streamer_is_running = true;
while (!streamer_stop) {
// playing...
if (bAudPlaying) {
if (buffer->size() >= samples_per_segment) {
buffer->read(buf, samples_per_segment);
} else {
memset(buf, 0, samples_per_segment * 2);
}
pa_simple_write(pa_stream, buf, samples_per_segment * 2, NULL);
} else {
std::this_thread::sleep_for(std::chrono::microseconds(10));
}
}
delete [] buf;
streamer_is_running = false;
streamer_stop = false;
}
static int pas_init()
{
pas_sound_fps = nAppVirtualFps;
nAudSegLen = (nAudSampleRate[0] * 100 + (pas_sound_fps / 2)) / pas_sound_fps;
// samples per segment
samples_per_segment = nAudSegLen * 2;
// seglen * 2 channels * 2 bytes per sample (16bits)
nAudAllocSegLen = samples_per_segment * 2;
nAudNextSound = new short[samples_per_segment];
pas_set_callback(nullptr);
pas_default_sound_filler(0);
pBurnSoundOut = nAudNextSound;
nBurnSoundRate = nAudSampleRate[0];
nBurnSoundLen = nAudAllocSegLen;
pa_sample_spec specs;
specs.channels = 2;
specs.format = PA_SAMPLE_S16LE;
specs.rate = nAudSampleRate[0];
pa_buffer_attr attributes;
attributes.maxlength = nAudAllocSegLen * nAudSegCount;
attributes.minreq = nAudAllocSegLen;
attributes.prebuf = nAudAllocSegLen;
attributes.tlength = nAudAllocSegLen * 2;
if (streamer_thread) {
streamer_stop = true;
while (streamer_is_running) {
std::this_thread::yield();
}
streamer_stop = false;
delete streamer_thread;
}
// destroy previous pulse audio stream
if (pa_stream) {
pa_simple_flush(pa_stream, NULL);
pa_simple_free(pa_stream);
}
// destroy previous ring buffer
if (buffer) {
delete buffer;
}
// 6 segmentos no buffer
buffer = new ring_buffer<short>(samples_per_segment * nAudSegCount);
pa_stream = pa_simple_new(NULL,
"fbalpha",
PA_STREAM_PLAYBACK,
NULL,
"fbalpha",
&specs,
NULL,
&attributes,
NULL);
streamer_thread = new std::thread(pas_audio_streamer);
streamer_thread->detach();
bAudOkay = 1;
return 0;
}
static int pas_play()
{
bAudPlaying = 1;
return 0;
}
static int pas_stop()
{
bAudPlaying = 0;
return 0;
}
static int pas_set_volume()
{
return 1;
}
static int pas_get_settings(InterfaceInfo *)
{
return 0;
}
struct AudOut AudOutPulseSimple = {
pas_blank_sound,
pas_sound_check,
pas_init,
pas_set_callback,
pas_play,
pas_stop,
pas_exit,
pas_set_volume,
pas_get_settings,
_T("PulseAudio (Simple) audio output")
};

View File

@ -0,0 +1,54 @@
#ifndef RINGBUFFER_H
#define RINGBUFFER_H
#include <cstdint>
template<class T>
class ring_buffer {
T *buffer;
uint64_t head;
uint64_t tail;
size_t buffer_size;
public:
ring_buffer(size_t buffer_size_) : buffer_size(buffer_size_) {
buffer = new T[buffer_size];
head = 0;
tail = 0;
}
~ring_buffer() {
delete [] buffer;
}
bool available() {
return (tail > head);
}
size_t size() const {
return tail - head;
}
void write(const T *buf, size_t lenght) {
uint64_t tail_ = tail;
for (auto i = 0; i < lenght; i++) {
buffer[tail_ % buffer_size] = buf[i];
++tail_;
}
tail = tail_;
}
size_t read(T *buf, size_t lenght) {
uint64_t head_ = head;
if (lenght > size())
lenght = size();
for (auto i = 0; i < lenght; i++) {
buf[i] = buffer[head_ % buffer_size];
++head_;
}
head = head_;
return lenght;
}
};
#endif // RINGBUFFER_H