From 2e7801db1a1c7a6fa5a54ba3310cb0ea2adde408 Mon Sep 17 00:00:00 2001 From: Random06457 <28494085+Random06457@users.noreply.github.com> Date: Wed, 11 May 2022 20:15:34 +0900 Subject: [PATCH] add pulseaudio backend --- .../libultraship/PulseAudioPlayer.cpp | 173 ++++++++++++++++++ libultraship/libultraship/PulseAudioPlayer.h | 26 +++ libultraship/libultraship/WasapiAudioPlayer.h | 4 + libultraship/libultraship/Window.cpp | 6 +- soh/Makefile | 1 + 5 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 libultraship/libultraship/PulseAudioPlayer.cpp create mode 100644 libultraship/libultraship/PulseAudioPlayer.h diff --git a/libultraship/libultraship/PulseAudioPlayer.cpp b/libultraship/libultraship/PulseAudioPlayer.cpp new file mode 100644 index 000000000..955b225d1 --- /dev/null +++ b/libultraship/libultraship/PulseAudioPlayer.cpp @@ -0,0 +1,173 @@ +#if defined(__linux__) || defined(__BSD__) + +#include "PulseAudioPlayer.h" +#include + +namespace Ship +{ + static void pas_context_state_cb(pa_context *c, void *userdata) { + switch (pa_context_get_state(c)) { + case PA_CONTEXT_READY: + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + *(bool*)userdata = true; + break; + default: + break; + } + } + + static void pas_stream_state_cb(pa_stream *s, void *userdata) { + switch (pa_stream_get_state(s)) { + case PA_STREAM_READY: + case PA_STREAM_FAILED: + case PA_STREAM_TERMINATED: + *(bool*)userdata = true; + break; + default: + break; + } + } + + static void pas_stream_write_cb(pa_stream* s, size_t length, void* userdata) { + } + + static void pas_update_complete(pa_stream* stream, int success, void* userdata) { + *(bool*)userdata = true; + } + + static void pas_write_complete(void* userdata) { + *(bool*)userdata = true; + } + + bool PulseAudioPlayer::Init() + { + bool done = false; + const pa_buffer_attr* applied_attr = nullptr; + + // Create mainloop + m_MainLoop = pa_mainloop_new(); + if (m_MainLoop == NULL) { + return false; + } + + // Create context and connect + m_Context = pa_context_new(pa_mainloop_get_api(m_MainLoop), "Ocarina of Time"); + if (m_Context == NULL) { + goto fail; + } + + pa_context_set_state_callback(m_Context, pas_context_state_cb, &done); + + if (pa_context_connect(m_Context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) { + goto fail; + } + + while (!done) { + pa_mainloop_iterate(m_MainLoop, true, NULL); + } + pa_context_set_state_callback(m_Context, NULL, NULL); + if (pa_context_get_state(m_Context) != PA_CONTEXT_READY) { + goto fail; + } + + // Create stream + pa_sample_spec ss; + ss.format = PA_SAMPLE_S16LE; + ss.rate = 32000; + ss.channels = 2; + + pa_buffer_attr attr; + attr.maxlength = (1600 + 544 + 528 + 1600) * 4; + attr.tlength = (528*2 + 544) * 4; + attr.prebuf = 1500 * 4; + attr.minreq = 161 * 4; + attr.fragsize = (uint32_t)-1; + + m_Stream = pa_stream_new(m_Context, "zelda", &ss, NULL); + if (m_Stream == NULL) { + goto fail; + } + + done = false; + pa_stream_set_state_callback(m_Stream, pas_stream_state_cb, &done); + pa_stream_set_write_callback(m_Stream, pas_stream_write_cb, NULL); + if (pa_stream_connect_playback(m_Stream, NULL, &attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) { + goto fail; + } + + while (!done) { + pa_mainloop_iterate(m_MainLoop, true, NULL); + } + pa_stream_set_state_callback(m_Stream, NULL, NULL); + if (pa_stream_get_state(m_Stream) != PA_STREAM_READY) { + goto fail; + } + + applied_attr = pa_stream_get_buffer_attr(m_Stream); + SPDLOG_TRACE("maxlength: {}\ntlength: {}\nprebuf: {}\nminreq: {}\nfragsize: {}\n", + applied_attr->maxlength, applied_attr->tlength, applied_attr->prebuf, applied_attr->minreq, applied_attr->fragsize); + m_Attr = *applied_attr; + + return true; + + fail: + if (m_Stream != NULL) { + pa_stream_unref(m_Stream); + m_Stream = NULL; + } + if (m_Context != NULL) { + pa_context_disconnect(m_Context); + pa_context_unref(m_Context); + m_Context = NULL; + } + if (m_MainLoop != NULL) { + pa_mainloop_free(m_MainLoop); + m_MainLoop = NULL; + } + return false; + } + + int PulseAudioPlayer::Buffered() + { + if (m_Stream == NULL) { + return 0; + } + + bool done = false; + pa_stream_update_timing_info(m_Stream, pas_update_complete, &done); + while (!done) { + pa_mainloop_iterate(m_MainLoop, true, NULL); + } + + const pa_timing_info *info = pa_stream_get_timing_info(m_Stream); + if (info == NULL) { + SPDLOG_ERROR("pa_stream_get_timing_info failed, state is %d\n", pa_stream_get_state(m_Stream)); + } + return (info->write_index - info->read_index) / 4; + } + + int PulseAudioPlayer::GetDesiredBuffered() + { + // return 1100; + return 1680; + } + + void PulseAudioPlayer::Play(const uint8_t* buff, uint32_t len) + { + size_t ws = m_Attr.maxlength - Buffered() * 4; + if (ws < len) { + len = ws; + } + if (pa_stream_write_ext_free(m_Stream, buff, len, pas_write_complete, &m_WriteComplete, 0LL, PA_SEEK_RELATIVE) < 0) { + SPDLOG_ERROR("pa_stream_write failed"); + return; + } + while (!m_WriteComplete) { + pa_mainloop_iterate(m_MainLoop, true, NULL); + } + m_WriteComplete = false; + } +} + +#endif \ No newline at end of file diff --git a/libultraship/libultraship/PulseAudioPlayer.h b/libultraship/libultraship/PulseAudioPlayer.h new file mode 100644 index 000000000..7bc72b097 --- /dev/null +++ b/libultraship/libultraship/PulseAudioPlayer.h @@ -0,0 +1,26 @@ +#pragma once + +#if defined(__linux__) || defined(__BSD__) + +#include "AudioPlayer.h" +#include + +namespace Ship { + class PulseAudioPlayer : public AudioPlayer { + public: + PulseAudioPlayer() {} + + bool Init() override; + int Buffered() override; + int GetDesiredBuffered() override; + void Play(const uint8_t* buff, uint32_t len) override; + + private: + pa_context* m_Context = nullptr; + pa_stream* m_Stream = nullptr; + pa_mainloop* m_MainLoop = nullptr; + bool m_WriteComplete = false; + pa_buffer_attr m_Attr = {0}; + }; +} +#endif diff --git a/libultraship/libultraship/WasapiAudioPlayer.h b/libultraship/libultraship/WasapiAudioPlayer.h index 5067d01a2..dac7dd7a7 100644 --- a/libultraship/libultraship/WasapiAudioPlayer.h +++ b/libultraship/libultraship/WasapiAudioPlayer.h @@ -1,4 +1,7 @@ #pragma once + +#ifdef _WIN32 + #include "AudioPlayer.h" #include #include @@ -39,3 +42,4 @@ namespace Ship { bool started; }; } +#endif diff --git a/libultraship/libultraship/Window.cpp b/libultraship/libultraship/Window.cpp index b9ebd2cbb..e7c221e41 100644 --- a/libultraship/libultraship/Window.cpp +++ b/libultraship/libultraship/Window.cpp @@ -11,11 +11,9 @@ #include "Blob.h" #include "Matrix.h" #include "AudioPlayer.h" -#ifdef _WIN32 #include "WasapiAudioPlayer.h" -#else +#include "PulseAudioPlayer.h" #include "SDLAudioPlayer.h" -#endif #include "Lib/Fast3D/gfx_pc.h" #include "Lib/Fast3D/gfx_sdl.h" #include "Lib/Fast3D/gfx_opengl.h" @@ -403,6 +401,8 @@ namespace Ship { void Window::SetAudioPlayer() { #ifdef _WIN32 APlayer = std::make_shared(); +#elif defined(__linux) + APlayer = std::make_shared(); #else APlayer = std::make_shared(); #endif diff --git a/soh/Makefile b/soh/Makefile index d3b88d34a..be46f55e7 100644 --- a/soh/Makefile +++ b/soh/Makefile @@ -71,6 +71,7 @@ LDLIBS := \ GL \ GLEW \ storm \ + pulse\ ultraship \ ) \