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:
parent
a32f979814
commit
06433d614a
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
12
3rdparty/CMakeLists.txt
vendored
12
3rdparty/CMakeLists.txt
vendored
@ -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
1
3rdparty/FAudio
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit ceadb9b14ca256f82b0c0c344aef742dfea92779
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -55,6 +55,7 @@ install:
|
||||
3rdparty/xxHash `
|
||||
3rdparty/yaml-cpp `
|
||||
3rdparty/zlib `
|
||||
3rdparty/FAudio `
|
||||
asmjit `
|
||||
Vulkan/glslang
|
||||
|
||||
|
179
rpcs3/Emu/Audio/FAudio/FAudioBackend.cpp
Normal file
179
rpcs3/Emu/Audio/FAudio/FAudioBackend.cpp
Normal 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
|
44
rpcs3/Emu/Audio/FAudio/FAudioBackend.h
Normal file
44
rpcs3/Emu/Audio/FAudio/FAudioBackend.h
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -103,6 +103,9 @@ enum class audio_renderer
|
||||
#ifdef HAVE_PULSE
|
||||
pulse,
|
||||
#endif
|
||||
#ifdef HAVE_FAUDIO
|
||||
faudio,
|
||||
#endif
|
||||
};
|
||||
|
||||
enum class camera_handler
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user