From 4c741e93c315d6e3848371000eee9bda49e96f49 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sun, 9 Apr 2017 15:59:19 +0300 Subject: [PATCH] ALSA Audio Renderer (Linux) --- .travis.yml | 1 + README.md | 2 +- rpcs3/CMakeLists.txt | 2 +- rpcs3/Emu/Audio/ALSA/ALSAThread.cpp | 136 ++++++++++++++++++++++++++++ rpcs3/Emu/Audio/ALSA/ALSAThread.h | 20 ++++ rpcs3/rpcs3.cpp | 5 + 6 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 rpcs3/Emu/Audio/ALSA/ALSAThread.cpp create mode 100644 rpcs3/Emu/Audio/ALSA/ALSAThread.h diff --git a/.travis.yml b/.travis.yml index 5bc6a6610d..b8c3919868 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,6 +66,7 @@ addons: - llvm-toolchain-trusty-4.0 packages: - cmake + - libasound2-dev - libopenal-dev - freeglut3-dev - libglew-dev diff --git a/README.md b/README.md index 8ffb7bf110..5c682d3f73 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ __Windows__ __Linux__ * GCC 5.1+ or Clang 3.5.0+ ([not GCC 6.1](https://github.com/RPCS3/rpcs3/issues/1691)) -* Debian & Ubuntu: `sudo apt-get install cmake build-essential libopenal-dev libwxgtk3.0-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev git` +* Debian & Ubuntu: `sudo apt-get install cmake build-essential libasound2-dev libopenal-dev libwxgtk3.0-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev git` * Arch: `sudo pacman -S glew openal wxgtk cmake llvm` __Mac OSX__ diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index 48bd8f6b5e..030e1934f7 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -49,7 +49,7 @@ endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") #on some Linux distros shm_unlink and similar functions are in librt only - set(ADDITIONAL_LIBS "rt" "X11") + set(ADDITIONAL_LIBS "rt" "X11" "asound") elseif(UNIX OR NOT MSVC) #it seems like glibc includes the iconv functions we use but other libc #implementations like the one on OSX don't seem implement them diff --git a/rpcs3/Emu/Audio/ALSA/ALSAThread.cpp b/rpcs3/Emu/Audio/ALSA/ALSAThread.cpp new file mode 100644 index 0000000000..1035992024 --- /dev/null +++ b/rpcs3/Emu/Audio/ALSA/ALSAThread.cpp @@ -0,0 +1,136 @@ +#include "stdafx.h" +#include "Utilities/Config.h" +#include "Utilities/GSL.h" +#include "Emu/System.h" + +#include "ALSAThread.h" + +#ifdef __linux__ + +#include + +extern cfg::bool_entry g_cfg_audio_convert_to_u16; + +thread_local static snd_pcm_t* s_tls_handle{nullptr}; + +static void error(int err, const char* reason) +{ + fprintf(stderr, "ALSA: %s failed: %s\n", reason, snd_strerror(err)); +} + +static bool check(int err, const char* reason) +{ + if (err < 0) + { + error(err, reason); + return false; + } + + return true; +} + +ALSAThread::ALSAThread() +{ + snd_pcm_hw_params_t* hw_params{nullptr}; + + auto at_ret = gsl::finally([&]() + { + if (hw_params) + { + snd_pcm_hw_params_free(hw_params); + } + }); + + if (!check(snd_pcm_open(&s_tls_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK), "snd_pcm_open")) + return; + + if (!check(snd_pcm_hw_params_malloc(&hw_params), "snd_pcm_hw_params_malloc")) + return; + + if (!check(snd_pcm_hw_params_any(s_tls_handle, hw_params), "snd_pcm_hw_params_any")) + return; + + if (!check(snd_pcm_hw_params_set_access(s_tls_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "snd_pcm_hw_params_set_access")) + return; + + if (!check(snd_pcm_hw_params_set_format(s_tls_handle, hw_params, g_cfg_audio_convert_to_u16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_FLOAT_LE), "snd_pcm_hw_params_set_format")) + return; + + if (!check(snd_pcm_hw_params_set_rate(s_tls_handle, hw_params, 48000, 0), "snd_pcm_hw_params_set_rate_near")) + return; + + if (!check(snd_pcm_hw_params_set_channels(s_tls_handle, hw_params, 8), "snd_pcm_hw_params_set_channels")) + return; + + if (!check(snd_pcm_hw_params_set_buffer_size(s_tls_handle, hw_params, 64 * 256), "snd_pcm_hw_params_set_buffer_size")) + return; + + if (!check(snd_pcm_hw_params_set_period_size(s_tls_handle, hw_params, 256, 0), "snd_pcm_hw_params_set_period_size")) + return; + + //if (!check(snd_pcm_hw_params_set_periods(s_tls_handle, hw_params, 2, 0), "snd_pcm_hw_params_set_periods")) + // return; + + if (!check(snd_pcm_hw_params(s_tls_handle, hw_params), "snd_pcm_hw_params")) + return; + + if (!check(snd_pcm_prepare(s_tls_handle), "snd_pcm_prepare")) + return; +} + +ALSAThread::~ALSAThread() +{ + if (s_tls_handle) + { + snd_pcm_close(s_tls_handle); + } +} + +void ALSAThread::Play() +{ +} + +void ALSAThread::Close() +{ +} + +void ALSAThread::Stop() +{ +} + +void ALSAThread::Open(const void* src, int size) +{ + AddData(src, size); +} + +void ALSAThread::AddData(const void* src, int size) +{ + size /= g_cfg_audio_convert_to_u16 ? 2 * 8 : 4 * 8; + + int res; + + while (true) + { + res = snd_pcm_writei(s_tls_handle, src, size); + + if (res == -EAGAIN) + { + continue; + } + else if (res == -EPIPE) + { + snd_pcm_prepare(s_tls_handle); + } + else + { + break; + } + } + + if (res != size) + { + error(res, "snd_pcm_writei"); + } +} + +#endif diff --git a/rpcs3/Emu/Audio/ALSA/ALSAThread.h b/rpcs3/Emu/Audio/ALSA/ALSAThread.h new file mode 100644 index 0000000000..e8956e048a --- /dev/null +++ b/rpcs3/Emu/Audio/ALSA/ALSAThread.h @@ -0,0 +1,20 @@ +#pragma once + +#ifdef __linux__ + +#include "Emu/Audio/AudioThread.h" + +class ALSAThread : public AudioThread +{ +public: + ALSAThread(); + virtual ~ALSAThread() override; + + virtual void Play() override; + virtual void Open(const void* src, int size) override; + virtual void Close() override; + virtual void Stop() override; + virtual void AddData(const void* src, int size) override; +}; + +#endif diff --git a/rpcs3/rpcs3.cpp b/rpcs3/rpcs3.cpp index 716888e794..2fe7e5d071 100644 --- a/rpcs3/rpcs3.cpp +++ b/rpcs3/rpcs3.cpp @@ -42,6 +42,9 @@ #include "Emu/Audio/XAudio2/XAudio2Thread.h" #include #endif +#ifdef __linux__ +#include "Emu/Audio/ALSA/ALSAThread.h" +#endif #ifdef __unix__ #include @@ -107,6 +110,8 @@ cfg::map_entry()>> g_cfg_audio_render { "Null", &std::make_shared }, #ifdef _WIN32 { "XAudio2", &std::make_shared }, +#elif __linux__ + { "ALSA", &std::make_shared }, #endif { "OpenAL", &std::make_shared }, });