1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-21 18:22:33 +01:00

Implement FAudio backend (#6374)

This commit is contained in:
Oschowa 2019-10-24 21:26:29 +02:00 committed by Ivan
parent a32f979814
commit 06433d614a
15 changed files with 265 additions and 5 deletions

3
.gitmodules vendored
View File

@ -52,3 +52,6 @@
path = 3rdparty/libusb
url = https://github.com/RPCS3/libusb.git
ignore = dirty
[submodule "3rdparty/FAudio"]
path = 3rdparty/FAudio
url = https://github.com/FNA-XNA/FAudio.git

View File

@ -18,6 +18,11 @@ matrix:
services: docker
cache: ccache
install: "docker pull rpcs3/rpcs3-travis-xenial:1.0"
addons:
apt:
packages:
- libsdl2-2.0
- libsdl2-dev
script: 'travis_wait docker run -v $(pwd):/rpcs3 -v "$HOME/.ccache":/root/.ccache --env-file .travis/travis.env rpcs3/rpcs3-travis-xenial:1.0 /bin/bash -ex /rpcs3/.travis/build-linux.bash'
- os: osx
osx_image: xcode10.2

View File

@ -8,7 +8,7 @@ export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib
cd rpcs3
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/xxHash 3rdparty/yaml-cpp 3rdparty/libusb Vulkan/glslang
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/xxHash 3rdparty/yaml-cpp 3rdparty/libusb 3rdparty/FAudio Vulkan/glslang
# Download pre-compiled llvm libs
curl -sLO https://github.com/RPCS3/llvm/releases/download/continuous-linux-master/llvmlibs-linux.tar.gz

View File

@ -15,7 +15,7 @@ cp target/release/libportability.dylib vulkan-sdk/lib/libVulkan.dylib
install_name_tool -id ${PWD}/vulkan-sdk/lib/libVulkan.dylib vulkan-sdk/lib/libVulkan.dylib
export VULKAN_SDK=${PWD}/vulkan-sdk
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/libusb 3rdparty/xxHash 3rdparty/yaml-cpp Vulkan/glslang
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/libusb 3rdparty/xxHash 3rdparty/yaml-cpp 3rdparty/FAudio Vulkan/glslang
mkdir build; cd build
cmake .. -DWITH_LLVM=OFF -DUSE_NATIVE_INSTRUCTIONS=OFF -G Ninja

View File

@ -337,6 +337,17 @@ add_library(3rdparty_openal INTERFACE)
target_include_directories(3rdparty_openal INTERFACE ${OPENAL_INCLUDE_DIR})
target_link_libraries(3rdparty_openal INTERFACE ${OPENAL_LIBRARY})
# FAudio
set(FAUDIO_TARGET 3rdparty_dummy_lib)
if(USE_FAUDIO)
# FAudio depends on SDL2
pkg_check_modules(SDL2 sdl2)
if(SDL2_FOUND)
add_subdirectory(FAudio EXCLUDE_FROM_ALL)
target_compile_definitions(FAudio INTERFACE -DHAVE_FAUDIO)
set(FAUDIO_TARGET FAudio)
endif()
endif()
# FFMPEG
add_library(3rdparty_ffmpeg INTERFACE)
@ -416,6 +427,7 @@ add_library(3rdparty::stblib ALIAS 3rdparty_stblib)
add_library(3rdparty::discord-rpc ALIAS 3rdparty_discord-rpc)
add_library(3rdparty::alsa ALIAS ${ALSA_TARGET})
add_library(3rdparty::pulse ALIAS ${PULSE_TARGET})
add_library(3rdparty::faudio ALIAS ${FAUDIO_TARGET})
add_library(3rdparty::libevdev ALIAS ${LIBEVDEV_TARGET})
add_library(3rdparty::vulkan ALIAS ${VULKAN_TARGET})
add_library(3rdparty::openal ALIAS 3rdparty_openal)

1
3rdparty/FAudio vendored Submodule

@ -0,0 +1 @@
Subproject commit ceadb9b14ca256f82b0c0c344aef742dfea92779

View File

@ -23,16 +23,17 @@ These are the essentials tools to build RPCS3 on Linux. Some of them can be inst
* [CMake 3.8.2+](https://www.cmake.org/download/)
* [Qt 5.10+](https://www.qt.io/download-qt-installer) (Avoid 5.11.1, due to a bug)
* [Vulkan SDK 1.1.97.0+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html))
* [SDL2](https://www.libsdl.org/download-2.0.php) (for the FAudio backend)
**If you have an NVIDIA GPU, you may need to install the libglvnd package.**
#### Arch Linux
sudo pacman -S glew openal cmake vulkan-validation-layers qt5-base qt5-declarative
sudo pacman -S glew openal cmake vulkan-validation-layers qt5-base qt5-declarative sdl2
#### Debian & Ubuntu
sudo apt-get install cmake build-essential libasound2-dev libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default libevdev-dev qtdeclarative5-dev qtbase5-private-dev
sudo apt-get install cmake build-essential libasound2-dev libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default libevdev-dev qtdeclarative5-dev qtbase5-private-dev libsdl2-2.0 libsdl2-dev
##### GCC 8.x installation

View File

@ -17,6 +17,7 @@ option(WITH_LLVM "Enable usage of LLVM library" ON)
option(BUILD_LLVM_SUBMODULE "Build LLVM from git submodule" ON)
option(USE_ALSA "ALSA audio backend" ON)
option(USE_PULSE "PulseAudio audio backend" ON)
option(USE_FAUDIO "FAudio audio backend" ON)
option(USE_LIBEVDEV "libevdev-based joystick support" ON)
option(USE_DISCORD_RPC "Discord rich presence integration" ON)
option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON)

View File

@ -55,6 +55,7 @@ install:
3rdparty/xxHash `
3rdparty/yaml-cpp `
3rdparty/zlib `
3rdparty/FAudio `
asmjit `
Vulkan/glslang

View File

@ -0,0 +1,179 @@
#include "FAudioBackend.h"
#ifdef HAVE_FAUDIO
FAudioBackend::FAudioBackend()
{
u32 res;
res = FAudioCreate(&m_instance, 0, FAUDIO_DEFAULT_PROCESSOR);
if (res)
{
fmt::throw_exception("FAudioCreate() failed(0x%08x)", res);
}
res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice, g_cfg.audio.downmix_to_2ch ? 2 : 8, 48000, 0, 0, nullptr);
if (res)
{
fmt::throw_exception("FAudio_CreateMasteringVoice() failed(0x%08x)", res);
}
}
FAudioBackend::~FAudioBackend()
{
if (m_source_voice != nullptr)
{
FAudioSourceVoice_Stop(m_source_voice, 0, FAUDIO_COMMIT_NOW);
FAudioVoice_DestroyVoice(m_source_voice);
}
if (m_master_voice != nullptr)
{
FAudioVoice_DestroyVoice(m_master_voice);
}
if (m_instance != nullptr)
{
FAudio_StopEngine(m_instance);
FAudio_Release(m_instance);
}
}
void FAudioBackend::Play()
{
AUDIT(m_source_voice != nullptr);
u32 res = FAudioSourceVoice_Start(m_source_voice, 0, FAUDIO_COMMIT_NOW);
if (res)
{
LOG_ERROR(GENERAL, "FAudioSourceVoice_Start() failed(0x%08x)", res);
Emu.Pause();
}
}
void FAudioBackend::Pause()
{
AUDIT(m_source_voice != nullptr);
u32 res = FAudioSourceVoice_Stop(m_source_voice, 0, FAUDIO_COMMIT_NOW);
if (res)
{
LOG_ERROR(GENERAL, "FAudioSourceVoice_Stop() failed(0x%08x)", res);
Emu.Pause();
}
}
void FAudioBackend::Flush()
{
AUDIT(m_source_voice != nullptr);
u32 res = FAudioSourceVoice_FlushSourceBuffers(m_source_voice);
if (res)
{
LOG_ERROR(GENERAL, "FAudioSourceVoice_FlushSourceBuffers() failed(0x%08x)", res);
Emu.Pause();
}
}
bool FAudioBackend::IsPlaying()
{
AUDIT(m_source_voice != nullptr);
FAudioVoiceState state;
FAudioSourceVoice_GetState(m_source_voice, &state, FAUDIO_VOICE_NOSAMPLESPLAYED);
return state.BuffersQueued > 0 || state.pCurrentBufferContext != nullptr;
}
void FAudioBackend::Close()
{
Pause();
Flush();
}
void FAudioBackend::Open(u32 /* num_buffers */)
{
const u32 sample_size = AudioBackend::get_sample_size();
const u32 channels = AudioBackend::get_channels();
const u32 sampling_rate = AudioBackend::get_sampling_rate();
FAudioWaveFormatEx waveformatex;
waveformatex.wFormatTag = g_cfg.audio.convert_to_u16 ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
waveformatex.nChannels = channels;
waveformatex.nSamplesPerSec = sampling_rate;
waveformatex.nAvgBytesPerSec = static_cast<u32>(sampling_rate * channels * sample_size);
waveformatex.nBlockAlign = channels * sample_size;
waveformatex.wBitsPerSample = sample_size * 8;
waveformatex.cbSize = 0;
u32 res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex, 0, FAUDIO_DEFAULT_FREQ_RATIO, nullptr, nullptr, nullptr);
if (res)
{
LOG_ERROR(GENERAL, "FAudio_CreateSourceVoice() failed(0x%08x)", res);
Emu.Pause();
}
AUDIT(m_source_voice != nullptr);
FAudioVoice_SetVolume(m_source_voice, channels == 2 ? 1.0f : 4.0f, FAUDIO_COMMIT_NOW);
}
bool FAudioBackend::AddData(const void* src, u32 num_samples)
{
AUDIT(m_source_voice != nullptr);
FAudioVoiceState state;
FAudioSourceVoice_GetState(m_source_voice, &state, FAUDIO_VOICE_NOSAMPLESPLAYED);
if (state.BuffersQueued >= MAX_AUDIO_BUFFERS)
{
LOG_WARNING(GENERAL, "XAudio2Backend : too many buffers enqueued (%d)", state.BuffersQueued);
return false;
}
FAudioBuffer buffer;
buffer.AudioBytes = num_samples * AudioBackend::get_sample_size();
buffer.Flags = 0;
buffer.LoopBegin = FAUDIO_NO_LOOP_REGION;
buffer.LoopCount = 0;
buffer.LoopLength = 0;
buffer.pAudioData = static_cast<const u8*>(src);
buffer.pContext = 0;
buffer.PlayBegin = 0;
buffer.PlayLength = AUDIO_BUFFER_SAMPLES;
u32 res = FAudioSourceVoice_SubmitSourceBuffer(m_source_voice, &buffer, nullptr);
if (res)
{
LOG_ERROR(GENERAL, "FAudioSourceVoice_SubmitSourceBuffer() failed(0x%08x)", res);
Emu.Pause();
return false;
}
return true;
}
u64 FAudioBackend::GetNumEnqueuedSamples()
{
FAudioVoiceState state;
FAudioSourceVoice_GetState(m_source_voice, &state, 0);
// all buffers contain AUDIO_BUFFER_SAMPLES, so we can easily calculate how many samples there are remaining
return (AUDIO_BUFFER_SAMPLES - state.SamplesPlayed % AUDIO_BUFFER_SAMPLES) + (state.BuffersQueued * AUDIO_BUFFER_SAMPLES);
}
f32 FAudioBackend::SetFrequencyRatio(f32 new_ratio)
{
new_ratio = std::clamp(new_ratio, FAUDIO_MIN_FREQ_RATIO, FAUDIO_DEFAULT_FREQ_RATIO);
u32 res = FAudioSourceVoice_SetFrequencyRatio(m_source_voice, new_ratio, FAUDIO_COMMIT_NOW);
if (res)
{
LOG_ERROR(GENERAL, "FAudioSourceVoice_SetFrequencyRatio() failed(0x%08x)", res);
Emu.Pause();
return 1.0f;
}
return new_ratio;
}
#endif

View File

@ -0,0 +1,44 @@
#pragma once
#ifdef HAVE_FAUDIO
#include "Emu/Audio/AudioBackend.h"
#include "3rdparty/FAudio/include/FAudio.h"
class FAudioBackend : public AudioBackend
{
private:
FAudio* m_instance;
FAudioMasteringVoice* m_master_voice;
FAudioSourceVoice* m_source_voice = nullptr;
public:
FAudioBackend();
virtual ~FAudioBackend() override;
virtual const char* GetName() const override
{
return "FAudio";
};
static const u32 capabilities = PLAY_PAUSE_FLUSH | IS_PLAYING | GET_NUM_ENQUEUED_SAMPLES | SET_FREQUENCY_RATIO;
virtual u32 GetCapabilities() const override
{
return capabilities;
};
virtual void Open(u32 /* num_buffers */) override;
virtual void Close() override;
virtual void Play() override;
virtual void Pause() override;
virtual bool IsPlaying() override;
virtual bool AddData(const void* src, u32 num_samples) override;
virtual void Flush() override;
virtual u64 GetNumEnqueuedSamples() override;
virtual f32 SetFrequencyRatio(f32 new_ratio) override;
};
#endif

View File

@ -90,6 +90,7 @@ target_sources(rpcs3_emu PRIVATE
Audio/AL/OpenALBackend.cpp
Audio/ALSA/ALSABackend.cpp
Audio/Pulse/PulseBackend.cpp
Audio/FAudio/FAudioBackend.cpp
)
if(WIN32)
@ -104,7 +105,7 @@ endif()
target_link_libraries(rpcs3_emu
PUBLIC
3rdparty::alsa 3rdparty::pulse 3rdparty::openal)
3rdparty::alsa 3rdparty::pulse 3rdparty::openal 3rdparty::faudio)
# Cell

View File

@ -212,6 +212,9 @@ void fmt_class_string<audio_renderer>::format(std::string& out, u64 arg)
case audio_renderer::pulse: return "PulseAudio";
#endif
case audio_renderer::openal: return "OpenAL";
#ifdef HAVE_FAUDIO
case audio_renderer::faudio: return "FAudio";
#endif
}
return unknown;

View File

@ -103,6 +103,9 @@ enum class audio_renderer
#ifdef HAVE_PULSE
pulse,
#endif
#ifdef HAVE_FAUDIO
faudio,
#endif
};
enum class camera_handler

View File

@ -31,6 +31,9 @@
#ifdef HAVE_PULSE
#include "Emu/Audio/Pulse/PulseBackend.h"
#endif
#ifdef HAVE_FAUDIO
#include "Emu/Audio/FAudio/FAudioBackend.h"
#endif
#include "Emu/RSX/GSRender.h"
#include "Emu/RSX/Null/NullGSRender.h"
@ -155,6 +158,9 @@ EmuCallbacks main_application::CreateCallbacks()
#endif
case audio_renderer::openal: return std::make_shared<OpenALBackend>();
#ifdef HAVE_FAUDIO
case audio_renderer::faudio: return std::make_shared<FAudioBackend>();
#endif
default: fmt::throw_exception("Invalid audio renderer: %s" HERE, type);
}
};