diff --git a/projectfiles/qtcreator/fba_qt.pro b/projectfiles/qtcreator/fba_qt.pro index 1a21b8416..866c87e40 100644 --- a/projectfiles/qtcreator/fba_qt.pro +++ b/projectfiles/qtcreator/fba_qt.pro @@ -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 += diff --git a/src/burner/qt/mainwindow.cpp b/src/burner/qt/mainwindow.cpp index ae4e1fa05..4f543a062 100644 --- a/src/burner/qt/mainwindow.cpp +++ b/src/burner/qt/mainwindow.cpp @@ -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); diff --git a/src/intf/audio/aud_interface.cpp b/src/intf/audio/aud_interface.cpp index a434c390b..6bc454bb7 100644 --- a/src/intf/audio/aud_interface.cpp +++ b/src/intf/audio/aud_interface.cpp @@ -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 }; diff --git a/src/intf/audio/linux/aud_pulse_simple.cpp b/src/intf/audio/linux/aud_pulse_simple.cpp new file mode 100644 index 000000000..619e25289 --- /dev/null +++ b/src/intf/audio/linux/aud_pulse_simple.cpp @@ -0,0 +1,190 @@ +// C++11 simple pulse audio driver +#include +#include +#include +#include +#include +#include "burner.h" +#include "ringbuffer.h" + +static ring_buffer *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(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") +}; diff --git a/src/intf/audio/linux/ringbuffer.h b/src/intf/audio/linux/ringbuffer.h new file mode 100644 index 000000000..b034809b8 --- /dev/null +++ b/src/intf/audio/linux/ringbuffer.h @@ -0,0 +1,54 @@ +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +#include + +template +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