mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-21 18:22:33 +01:00
SoundTouch resampler integration
This commit is contained in:
parent
3a804674c9
commit
107107107c
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -72,3 +72,7 @@
|
||||
path = 3rdparty/cubeb/cubeb
|
||||
url = ../../mozilla/cubeb.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/SoundTouch/soundtouch"]
|
||||
path = 3rdparty/SoundTouch/soundtouch
|
||||
url = ../../RPCS3/soundtouch.git
|
||||
ignore = dirty
|
||||
|
4
3rdparty/CMakeLists.txt
vendored
4
3rdparty/CMakeLists.txt
vendored
@ -131,6 +131,9 @@ add_subdirectory(discord-rpc)
|
||||
# Cubeb
|
||||
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
|
||||
|
||||
# SoundTouch
|
||||
add_subdirectory(SoundTouch EXCLUDE_FROM_ALL)
|
||||
|
||||
# libevdev
|
||||
set(LIBEVDEV_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_LIBEVDEV)
|
||||
@ -312,3 +315,4 @@ add_library(3rdparty::ffmpeg ALIAS 3rdparty_ffmpeg)
|
||||
add_library(3rdparty::glew ALIAS 3rdparty_glew)
|
||||
add_library(3rdparty::wolfssl ALIAS wolfssl)
|
||||
add_library(3rdparty::libcurl ALIAS libcurl)
|
||||
add_library(3rdparty::soundtouch ALIAS soundtouch)
|
||||
|
34
3rdparty/SoundTouch/CMakeLists.txt
vendored
Normal file
34
3rdparty/SoundTouch/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
add_library(soundtouch STATIC EXCLUDE_FROM_ALL
|
||||
soundtouch/source/SoundTouch/AAFilter.cpp
|
||||
soundtouch/source/SoundTouch/FIFOSampleBuffer.cpp
|
||||
soundtouch/source/SoundTouch/FIRFilter.cpp
|
||||
soundtouch/source/SoundTouch/InterpolateCubic.cpp
|
||||
soundtouch/source/SoundTouch/InterpolateLinear.cpp
|
||||
soundtouch/source/SoundTouch/InterpolateShannon.cpp
|
||||
soundtouch/source/SoundTouch/RateTransposer.cpp
|
||||
soundtouch/source/SoundTouch/SoundTouch.cpp
|
||||
soundtouch/source/SoundTouch/sse_optimized.cpp
|
||||
soundtouch/source/SoundTouch/TDStretch.cpp
|
||||
)
|
||||
|
||||
target_include_directories(soundtouch PRIVATE
|
||||
soundtouch/source/SoundTouch
|
||||
soundtouch/include)
|
||||
|
||||
target_include_directories(soundtouch INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/soundtouch/include>
|
||||
$<INSTALL_INTERFACE:/soundtouch/include>)
|
||||
|
||||
set_property(TARGET soundtouch PROPERTY FOLDER "3rdparty/")
|
||||
|
||||
target_compile_definitions(soundtouch PUBLIC
|
||||
ST_NO_EXCEPTION_HANDLING
|
||||
USE_MULTICH_ALWAYS
|
||||
SOUNDTOUCH_FLOAT_SAMPLES;
|
||||
)
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86|X86|amd64|AMD64|em64t|EM64T)")
|
||||
target_compile_definitions(soundtouch PUBLIC
|
||||
SOUNDTOUCH_ALLOW_SSE
|
||||
)
|
||||
endif ()
|
1
3rdparty/SoundTouch/soundtouch
vendored
Submodule
1
3rdparty/SoundTouch/soundtouch
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 83cfba67b6af80bb9bfafc0b324718c4841f2991
|
77
3rdparty/SoundTouch/soundtouch.vcxproj
vendored
Normal file
77
3rdparty/SoundTouch/soundtouch.vcxproj
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="soundtouch\include\FIFOSampleBuffer.h" />
|
||||
<ClInclude Include="soundtouch\include\FIFOSamplePipe.h" />
|
||||
<ClInclude Include="soundtouch\include\SoundTouch.h" />
|
||||
<ClInclude Include="soundtouch\include\STTypes.h" />
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\AAFilter.h" />
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\FIRFilter.h" />
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\InterpolateCubic.h" />
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\InterpolateLinear.h" />
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\InterpolateShannon.h" />
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\RateTransposer.h" />
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\TDStretch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\AAFilter.cpp" />
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\FIFOSampleBuffer.cpp" />
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\FIRFilter.cpp" />
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\InterpolateCubic.cpp" />
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\InterpolateLinear.cpp" />
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\InterpolateShannon.cpp" />
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\RateTransposer.cpp" />
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\SoundTouch.cpp" />
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\sse_optimized.cpp" />
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\TDStretch.cpp" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{508c291a-3d18-49f5-b25d-f7c8db92cb21}</ProjectGuid>
|
||||
<RootNamespace>soundtouch</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\common_default_macros.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_default.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_release.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>SOUNDTOUCH_ALLOW_SSE;ST_NO_EXCEPTION_HANDLING;USE_MULTICH_ALWAYS;SOUNDTOUCH_FLOAT_SAMPLES;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./soundtouch/source/SoundTouch;./soundtouch/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
78
3rdparty/SoundTouch/soundtouch.vcxproj.filters
vendored
Normal file
78
3rdparty/SoundTouch/soundtouch.vcxproj.filters
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="soundtouch\include\FIFOSampleBuffer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soundtouch\include\FIFOSamplePipe.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soundtouch\include\SoundTouch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soundtouch\include\STTypes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\FIRFilter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\InterpolateCubic.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\InterpolateLinear.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\InterpolateShannon.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\RateTransposer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\TDStretch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soundtouch\source\SoundTouch\AAFilter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\InterpolateLinear.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\InterpolateShannon.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\RateTransposer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\SoundTouch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\sse_optimized.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\TDStretch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\AAFilter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\FIFOSampleBuffer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\FIRFilter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="soundtouch\source\SoundTouch\InterpolateCubic.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -33,7 +33,7 @@ simple_ringbuf& simple_ringbuf::operator=(simple_ringbuf&& other)
|
||||
return *this;
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::get_free_size()
|
||||
u32 simple_ringbuf::get_free_size() const
|
||||
{
|
||||
const u64 _rw_ptr = rw_ptr;
|
||||
const u32 rd = static_cast<u32>(_rw_ptr);
|
||||
@ -42,12 +42,12 @@ u32 simple_ringbuf::get_free_size()
|
||||
return wr >= rd ? buf_size - 1 - (wr - rd) : rd - wr - 1U;
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::get_used_size()
|
||||
u32 simple_ringbuf::get_used_size() const
|
||||
{
|
||||
return buf_size - 1 - get_free_size();
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::get_total_size()
|
||||
u32 simple_ringbuf::get_total_size() const
|
||||
{
|
||||
return buf_size;
|
||||
}
|
||||
|
@ -25,9 +25,9 @@ public:
|
||||
simple_ringbuf(simple_ringbuf&& other);
|
||||
simple_ringbuf& operator=(simple_ringbuf&& other);
|
||||
|
||||
u32 get_free_size();
|
||||
u32 get_used_size();
|
||||
u32 get_total_size();
|
||||
u32 get_free_size() const;
|
||||
u32 get_used_size() const;
|
||||
u32 get_total_size() const;
|
||||
|
||||
// Thread unsafe functions.
|
||||
void set_buf_size(u32 size);
|
||||
|
@ -50,6 +50,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rpcs3", "rpcs3\rpcs3.vcxpro
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E} = {D6973076-9317-4EF2-A0B8-B7A18AC0713E}
|
||||
{9610627D-20FE-4B07-8CE3-9FF68A5F1EC2} = {9610627D-20FE-4B07-8CE3-9FF68A5F1EC2}
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A} = {FDA7B080-03B0-48C8-B24F-88118981422A}
|
||||
{508c291a-3d18-49f5-b25d-f7c8db92cb21} = {508c291a-3d18-49f5-b25d-f7c8db92cb21}
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB} = {DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}
|
||||
{FDC361C5-7734-493B-8CFB-037308B35122} = {FDC361C5-7734-493B-8CFB-037308B35122}
|
||||
{8F85B6CC-250F-4ACA-A617-E820A74E3E3C} = {8F85B6CC-250F-4ACA-A617-E820A74E3E3C}
|
||||
@ -82,6 +83,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Cubeb", "rpcs3\Cubeb.vcxpro
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcubeb", "3rdparty\cubeb\libcubeb.vcxproj", "{FDA7B080-03B0-48C8-B24F-88118981422A}"
|
||||
EndProject
|
||||
Project("{508c291a-3d18-49f5-b25d-f7c8db92cb21}") = "soundtouch", "3rdparty\SoundTouch\soundtouch.vcxproj", "{508c291a-3d18-49f5-b25d-f7c8db92cb21}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
@ -168,6 +171,10 @@ Global
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A}.Debug|x64.Build.0 = Debug|x64
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A}.Release|x64.ActiveCfg = Release|x64
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A}.Release|x64.Build.0 = Release|x64
|
||||
{508c291a-3d18-49f5-b25d-f7c8db92cb21}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{508c291a-3d18-49f5-b25d-f7c8db92cb21}.Debug|x64.Build.0 = Debug|x64
|
||||
{508c291a-3d18-49f5-b25d-f7c8db92cb21}.Release|x64.ActiveCfg = Release|x64
|
||||
{508c291a-3d18-49f5-b25d-f7c8db92cb21}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -194,6 +201,7 @@ Global
|
||||
{A37E4273-85DB-4217-B775-CE971B87D9DF} = {B0AC29FD-7B01-4B5E-9C8D-0A081E4C5668}
|
||||
{9610627D-20FE-4B07-8CE3-9FF68A5F1EC2} = {10FBF193-D532-4CCF-B875-4C7091A7F6C2}
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
{508c291a-3d18-49f5-b25d-f7c8db92cb21} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {06CC7920-E085-4B81-9582-8DE8AAD42510}
|
||||
|
@ -27,24 +27,10 @@ bool AudioBackend::get_convert_to_s16() const
|
||||
return m_sample_size == AudioSampleSize::S16;
|
||||
}
|
||||
|
||||
bool AudioBackend::has_capability(u32 cap) const
|
||||
void AudioBackend::convert_to_s16(u32 cnt, const f32* src, void* dst)
|
||||
{
|
||||
return (cap & GetCapabilities()) == cap;
|
||||
}
|
||||
|
||||
void AudioBackend::dump_capabilities(std::string& out) const
|
||||
{
|
||||
u32 count = 0;
|
||||
const u32 capabilities = GetCapabilities();
|
||||
|
||||
if (capabilities & SET_FREQUENCY_RATIO)
|
||||
for (usz i = 0; i < cnt; i++)
|
||||
{
|
||||
fmt::append(out, "%sSET_FREQUENCY_RATIO", count > 0 ? " | " : "");
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
fmt::append(out, "NONE");
|
||||
static_cast<s16 *>(dst)[i] = static_cast<s16>(std::clamp(src[i] * 32768.5f, -32768.0f, 32767.0f));
|
||||
}
|
||||
}
|
||||
|
@ -39,11 +39,6 @@ enum class AudioChannelCnt : u32
|
||||
class AudioBackend
|
||||
{
|
||||
public:
|
||||
enum Capabilities : u32
|
||||
{
|
||||
SET_FREQUENCY_RATIO = 0x1, // Implements SetFrequencyRatio
|
||||
};
|
||||
|
||||
AudioBackend();
|
||||
|
||||
virtual ~AudioBackend() = default;
|
||||
@ -52,7 +47,6 @@ public:
|
||||
* Pure virtual methods
|
||||
*/
|
||||
virtual std::string_view GetName() const = 0;
|
||||
virtual u32 GetCapabilities() const = 0;
|
||||
|
||||
virtual void Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) = 0;
|
||||
virtual void Close() = 0;
|
||||
@ -84,19 +78,6 @@ public:
|
||||
*/
|
||||
virtual bool Operational() { return true; }
|
||||
|
||||
/*
|
||||
* Virtual methods - should be implemented depending on backend capabilities
|
||||
*/
|
||||
|
||||
// Sets a new frequency ratio. Backend is allowed to modify the ratio value, e.g. clamping it to the allowed range
|
||||
// Returns the new frequency ratio set
|
||||
// Should be implemented if capabilities & SET_FREQUENCY_RATIO
|
||||
virtual f32 SetFrequencyRatio(f32 /* new_ratio */) // returns the new ratio
|
||||
{
|
||||
fmt::throw_exception("SetFrequencyRatio() not implemented");
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper methods
|
||||
*/
|
||||
@ -108,9 +89,10 @@ public:
|
||||
|
||||
bool get_convert_to_s16() const;
|
||||
|
||||
bool has_capability(u32 cap) const;
|
||||
|
||||
void dump_capabilities(std::string& out) const;
|
||||
/*
|
||||
* Convert float buffer to s16 one. src and dst could be the same. cnt is number of buffer elements.
|
||||
*/
|
||||
static void convert_to_s16(u32 cnt, const f32* src, void* dst);
|
||||
|
||||
protected:
|
||||
AudioSampleSize m_sample_size = AudioSampleSize::FLOAT;
|
||||
|
@ -4,11 +4,22 @@
|
||||
#include "Utilities/date_time.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
AudioDumper::AudioDumper(u16 ch, u32 sample_rate, u32 sample_size)
|
||||
: m_header(ch, sample_rate, sample_size)
|
||||
AudioDumper::AudioDumper()
|
||||
{
|
||||
if (GetCh())
|
||||
}
|
||||
|
||||
AudioDumper::~AudioDumper()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void AudioDumper::Open(u16 ch, u32 sample_rate, u32 sample_size)
|
||||
{
|
||||
Close();
|
||||
|
||||
if (ch)
|
||||
{
|
||||
m_header = WAVHeader(ch, sample_rate, sample_size);
|
||||
std::string path = fs::get_cache_dir() + "audio_";
|
||||
if (const std::string id = Emu.GetTitleID(); !id.empty())
|
||||
{
|
||||
@ -20,12 +31,14 @@ AudioDumper::AudioDumper(u16 ch, u32 sample_rate, u32 sample_size)
|
||||
}
|
||||
}
|
||||
|
||||
AudioDumper::~AudioDumper()
|
||||
void AudioDumper::Close()
|
||||
{
|
||||
if (GetCh())
|
||||
{
|
||||
m_output.seek(0);
|
||||
m_output.write(m_header); // rewrite file header
|
||||
m_output.close();
|
||||
m_header.FMT.NumChannels = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,9 +67,12 @@ class AudioDumper
|
||||
fs::file m_output{};
|
||||
|
||||
public:
|
||||
AudioDumper(u16 ch, u32 sample_rate, u32 sample_size);
|
||||
AudioDumper();
|
||||
~AudioDumper();
|
||||
|
||||
void Open(u16 ch, u32 sample_rate, u32 sample_size);
|
||||
void Close();
|
||||
|
||||
void WriteData(const void* buffer, u32 size);
|
||||
u16 GetCh() const { return m_header.FMT.NumChannels; }
|
||||
};
|
||||
|
@ -18,9 +18,6 @@ public:
|
||||
|
||||
std::string_view GetName() const override { return "Cubeb"sv; }
|
||||
|
||||
static const u32 capabilities = 0;
|
||||
u32 GetCapabilities() const override { return capabilities; }
|
||||
|
||||
bool Initialized() override;
|
||||
bool Operational() override;
|
||||
|
||||
|
@ -189,26 +189,6 @@ bool FAudioBackend::IsPlaying()
|
||||
return m_playing;
|
||||
}
|
||||
|
||||
f32 FAudioBackend::SetFrequencyRatio(f32 new_ratio)
|
||||
{
|
||||
new_ratio = std::clamp(new_ratio, FAUDIO_MIN_FREQ_RATIO, FAUDIO_DEFAULT_FREQ_RATIO);
|
||||
|
||||
if (m_source_voice == nullptr)
|
||||
{
|
||||
FAudio_.error("SetFrequencyRatio() called uninitialized");
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
const u32 res = FAudioSourceVoice_SetFrequencyRatio(m_source_voice, new_ratio, FAUDIO_COMMIT_NOW);
|
||||
if (res)
|
||||
{
|
||||
FAudio_.error("FAudioSourceVoice_SetFrequencyRatio() failed(0x%08x)", res);
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return new_ratio;
|
||||
}
|
||||
|
||||
void FAudioBackend::SetWriteCallback(std::function<u32(u32, void *)> cb)
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
|
@ -21,9 +21,6 @@ public:
|
||||
|
||||
std::string_view GetName() const override { return "FAudio"sv; }
|
||||
|
||||
static const u32 capabilities = SET_FREQUENCY_RATIO;
|
||||
u32 GetCapabilities() const override { return capabilities; }
|
||||
|
||||
bool Initialized() override;
|
||||
bool Operational() override;
|
||||
|
||||
@ -37,8 +34,6 @@ public:
|
||||
void Pause() override;
|
||||
bool IsPlaying() override;
|
||||
|
||||
f32 SetFrequencyRatio(f32 new_ratio) override;
|
||||
|
||||
private:
|
||||
static constexpr u32 INTERNAL_BUF_SIZE_MS = 25;
|
||||
|
||||
|
@ -10,9 +10,6 @@ public:
|
||||
|
||||
std::string_view GetName() const override { return "Null"sv; }
|
||||
|
||||
static const u32 capabilities = 0;
|
||||
u32 GetCapabilities() const override { return capabilities; }
|
||||
|
||||
void Open(AudioFreq /* freq */, AudioSampleSize /* sample_size */, AudioChannelCnt /* ch_cnt */) override { m_playing = false; }
|
||||
void Close() override { m_playing = false; }
|
||||
|
||||
|
@ -210,26 +210,6 @@ bool XAudio2Backend::IsPlaying()
|
||||
return m_playing;
|
||||
}
|
||||
|
||||
f32 XAudio2Backend::SetFrequencyRatio(f32 new_ratio)
|
||||
{
|
||||
if (m_source_voice == nullptr)
|
||||
{
|
||||
XAudio.error("SetFrequencyRatio() called uninitialized");
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
new_ratio = std::clamp(new_ratio, XAUDIO2_MIN_FREQ_RATIO, XAUDIO2_DEFAULT_FREQ_RATIO);
|
||||
|
||||
const HRESULT hr = m_source_voice->SetFrequencyRatio(new_ratio);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.error("SetFrequencyRatio() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return new_ratio;
|
||||
}
|
||||
|
||||
void XAudio2Backend::SetWriteCallback(std::function<u32(u32, void *)> cb)
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
|
@ -23,9 +23,6 @@ public:
|
||||
|
||||
std::string_view GetName() const override { return "XAudio2"sv; }
|
||||
|
||||
static const u32 capabilities = SET_FREQUENCY_RATIO;
|
||||
u32 GetCapabilities() const override { return capabilities; }
|
||||
|
||||
bool Initialized() override;
|
||||
bool Operational() override;
|
||||
|
||||
@ -39,8 +36,6 @@ public:
|
||||
void Pause() override;
|
||||
bool IsPlaying() override;
|
||||
|
||||
f32 SetFrequencyRatio(f32 new_ratio) override;
|
||||
|
||||
private:
|
||||
static constexpr u32 INTERNAL_BUF_SIZE_MS = 25;
|
||||
|
||||
|
53
rpcs3/Emu/Audio/audio_resampler.cpp
Normal file
53
rpcs3/Emu/Audio/audio_resampler.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Audio/audio_resampler.h"
|
||||
#include <algorithm>
|
||||
|
||||
audio_resampler::audio_resampler()
|
||||
{
|
||||
resampler.setSetting(SETTING_SEQUENCE_MS, 20); // Resampler frame size (reduce latency at cost of slight sound quality degradation)
|
||||
resampler.setSetting(SETTING_USE_QUICKSEEK, 1); // Use fast quick seeking algorithm (substantally reduces computation time)
|
||||
}
|
||||
|
||||
audio_resampler::~audio_resampler()
|
||||
{
|
||||
}
|
||||
|
||||
void audio_resampler::set_params(AudioChannelCnt ch_cnt, AudioFreq freq)
|
||||
{
|
||||
flush();
|
||||
resampler.setChannels(static_cast<u32>(ch_cnt));
|
||||
resampler.setSampleRate(static_cast<u32>(freq));
|
||||
}
|
||||
|
||||
f64 audio_resampler::set_tempo(f64 new_tempo)
|
||||
{
|
||||
new_tempo = std::clamp(new_tempo, RESAMPLER_MIN_FREQ_VAL, RESAMPLER_MAX_FREQ_VAL);
|
||||
resampler.setTempo(new_tempo);
|
||||
return new_tempo;
|
||||
}
|
||||
|
||||
void audio_resampler::put_samples(const f32* buf, u32 sample_cnt)
|
||||
{
|
||||
resampler.putSamples(buf, sample_cnt);
|
||||
}
|
||||
|
||||
std::pair<f32* /* buffer */, u32 /* samples */> audio_resampler::get_samples(u32 sample_cnt)
|
||||
{
|
||||
f32 *const buf = resampler.bufBegin();
|
||||
return std::make_pair(buf, resampler.receiveSamples(sample_cnt));
|
||||
}
|
||||
|
||||
u32 audio_resampler::samples_available() const
|
||||
{
|
||||
return resampler.numSamples();
|
||||
}
|
||||
|
||||
f64 audio_resampler::get_resample_ratio()
|
||||
{
|
||||
return resampler.getInputOutputSampleRatio();
|
||||
}
|
||||
|
||||
void audio_resampler::flush()
|
||||
{
|
||||
resampler.clear();
|
||||
}
|
37
rpcs3/Emu/Audio/audio_resampler.h
Normal file
37
rpcs3/Emu/Audio/audio_resampler.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wsuggest-override"
|
||||
#endif
|
||||
#include "SoundTouch.h"
|
||||
#ifndef _MSC_VER
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
constexpr f64 RESAMPLER_MAX_FREQ_VAL = 1.0;
|
||||
constexpr f64 RESAMPLER_MIN_FREQ_VAL = 0.1;
|
||||
|
||||
class audio_resampler
|
||||
{
|
||||
public:
|
||||
audio_resampler();
|
||||
~audio_resampler();
|
||||
|
||||
void set_params(AudioChannelCnt ch_cnt, AudioFreq freq);
|
||||
f64 set_tempo(f64 new_tempo);
|
||||
|
||||
void put_samples(const f32* buf, u32 sample_cnt);
|
||||
std::pair<f32* /* buffer */, u32 /* samples */> get_samples(u32 sample_cnt);
|
||||
|
||||
u32 samples_available() const;
|
||||
f64 get_resample_ratio();
|
||||
|
||||
void flush();
|
||||
|
||||
private:
|
||||
soundtouch::SoundTouch resampler{};
|
||||
};
|
@ -114,6 +114,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
# Audio
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Audio/audio_device_listener.cpp
|
||||
Audio/audio_resampler.cpp
|
||||
Audio/AudioDumper.cpp
|
||||
Audio/AudioBackend.cpp
|
||||
Audio/Cubeb/CubebBackend.cpp
|
||||
@ -144,6 +145,10 @@ target_link_libraries(rpcs3_emu
|
||||
PUBLIC
|
||||
3rdparty::cubeb)
|
||||
|
||||
target_link_libraries(rpcs3_emu
|
||||
PUBLIC
|
||||
3rdparty::soundtouch)
|
||||
|
||||
# Cell
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Cell/MFC.cpp
|
||||
|
@ -8,10 +8,6 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
#include "emmintrin.h"
|
||||
#endif
|
||||
|
||||
LOG_CHANNEL(cellAudio);
|
||||
|
||||
vm::gvar<char, AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT> g_audio_buffer;
|
||||
@ -60,11 +56,7 @@ void cell_audio_config::reset(bool backend_changed)
|
||||
backend = Emu.GetCallbacks().get_audio();
|
||||
}
|
||||
|
||||
{
|
||||
std::string str;
|
||||
backend->dump_capabilities(str);
|
||||
cellAudio.notice("cellAudio initializing. Backend: %s, Capabilities: %s", backend->GetName(), str.c_str());
|
||||
}
|
||||
cellAudio.notice("cellAudio initializing. Backend: %s", backend->GetName());
|
||||
|
||||
const AudioFreq freq = AudioFreq::FREQ_48K;
|
||||
const AudioSampleSize sample_size = raw.convert_to_s16 ? AudioSampleSize::S16 : AudioSampleSize::FLOAT;
|
||||
@ -85,14 +77,14 @@ void cell_audio_config::reset(bool backend_changed)
|
||||
|
||||
audio_channels = backend->get_channels();
|
||||
audio_sampling_rate = backend->get_sampling_rate();
|
||||
audio_block_period = AUDIO_BUFFER_SAMPLES * 1000000 / audio_sampling_rate;
|
||||
audio_block_period = AUDIO_BUFFER_SAMPLES * 1'000'000 / audio_sampling_rate;
|
||||
audio_sample_size = backend->get_sample_size();
|
||||
audio_min_buffer_duration = backend->GetCallbackFrameLen();
|
||||
audio_min_buffer_duration = backend->GetCallbackFrameLen() + u32{AUDIO_BUFFER_SAMPLES} * 2.0 / audio_sampling_rate; // Add 2 blocks to allow jitter compensation
|
||||
|
||||
audio_buffer_length = AUDIO_BUFFER_SAMPLES * audio_channels;
|
||||
audio_buffer_size = audio_buffer_length * audio_sample_size;
|
||||
|
||||
desired_buffer_duration = raw.desired_buffer_duration * 1000llu;
|
||||
desired_buffer_duration = std::max(static_cast<s64>(audio_min_buffer_duration * 1000), raw.desired_buffer_duration) * 1000llu;
|
||||
buffering_enabled = raw.buffering_enabled && raw.renderer != audio_renderer::null;
|
||||
|
||||
minimum_block_period = audio_block_period / 2;
|
||||
@ -106,18 +98,18 @@ void cell_audio_config::reset(bool backend_changed)
|
||||
|
||||
const bool raw_time_stretching_enabled = buffering_enabled && raw.enable_time_stretching && (raw.time_stretching_threshold > 0);
|
||||
|
||||
time_stretching_enabled = raw_time_stretching_enabled && backend->has_capability(AudioBackend::SET_FREQUENCY_RATIO);
|
||||
|
||||
time_stretching_enabled = raw_time_stretching_enabled;
|
||||
time_stretching_threshold = raw.time_stretching_threshold / 100.0f;
|
||||
|
||||
// Warn if audio backend does not support all requested features
|
||||
if (raw.buffering_enabled && !buffering_enabled)
|
||||
{
|
||||
cellAudio.error("Audio backend %s does not support buffering, this option will be ignored.", backend->GetName());
|
||||
}
|
||||
if (raw_time_stretching_enabled && !time_stretching_enabled)
|
||||
{
|
||||
cellAudio.error("Audio backend %s does not support time stretching, this option will be ignored.", backend->GetName());
|
||||
|
||||
if (raw.enable_time_stretching)
|
||||
{
|
||||
cellAudio.error("Audio backend %s does not support time stretching, this option will be ignored.", backend->GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +117,6 @@ audio_ringbuffer::audio_ringbuffer(cell_audio_config& _cfg)
|
||||
: backend(_cfg.backend)
|
||||
, cfg(_cfg)
|
||||
, buf_sz(AUDIO_BUFFER_SAMPLES * _cfg.audio_channels)
|
||||
, emu_paused(Emu.IsPaused())
|
||||
{
|
||||
// Initialize buffers
|
||||
if (cfg.num_allocated_buffers > MAX_AUDIO_BUFFERS)
|
||||
@ -139,23 +130,26 @@ audio_ringbuffer::audio_ringbuffer(cell_audio_config& _cfg)
|
||||
}
|
||||
|
||||
// Init audio dumper if enabled
|
||||
if (g_cfg.audio.dump_to_file)
|
||||
if (cfg.raw.dump_to_file)
|
||||
{
|
||||
m_dump.reset(new AudioDumper(cfg.audio_channels, cfg.audio_sampling_rate, cfg.audio_sample_size));
|
||||
m_dump.Open(cfg.audio_channels, cfg.audio_sampling_rate, cfg.audio_sample_size);
|
||||
}
|
||||
|
||||
// Configure resampler
|
||||
resampler.set_params(static_cast<AudioChannelCnt>(cfg.audio_channels), AudioFreq::FREQ_48K);
|
||||
resampler.set_tempo(RESAMPLER_MAX_FREQ_VAL);
|
||||
|
||||
const f64 buffer_dur_mult = [&]()
|
||||
{
|
||||
const f64 min_buf_dur = _cfg.audio_min_buffer_duration + 0.01; // Add 10ms to allow jitter compensation
|
||||
if (cfg.raw.buffering_enabled)
|
||||
if (cfg.buffering_enabled)
|
||||
{
|
||||
return std::max<f64>(min_buf_dur, cfg.raw.desired_buffer_duration / 1000.0 * 2); // Allocate 2x buffer to keep buffering algorithm happy
|
||||
return cfg.desired_buffer_duration / 1'000'000.0 + 0.02; // Add 20ms to buffer to keep buffering algorithm happy
|
||||
}
|
||||
|
||||
return min_buf_dur;
|
||||
return cfg.audio_min_buffer_duration;
|
||||
}();
|
||||
|
||||
cb_ringbuf.set_buf_size(static_cast<u32>(_cfg.audio_channels * _cfg.audio_sampling_rate * _cfg.audio_sample_size * buffer_dur_mult));
|
||||
cb_ringbuf.set_buf_size(static_cast<u32>(cfg.audio_channels * cfg.audio_sampling_rate * cfg.audio_sample_size * buffer_dur_mult));
|
||||
backend->SetWriteCallback(std::bind(&audio_ringbuffer::backend_write_callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
@ -171,19 +165,18 @@ audio_ringbuffer::~audio_ringbuffer()
|
||||
|
||||
f32 audio_ringbuffer::set_frequency_ratio(f32 new_ratio)
|
||||
{
|
||||
if (!has_capability(AudioBackend::SET_FREQUENCY_RATIO))
|
||||
{
|
||||
ensure(new_ratio == 1.0f);
|
||||
frequency_ratio = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
frequency_ratio = backend->SetFrequencyRatio(new_ratio);
|
||||
//cellAudio.trace("set_frequency_ratio(%1.2f) -> %1.2f", new_ratio, frequency_ratio);
|
||||
}
|
||||
frequency_ratio = resampler.set_tempo(new_ratio);
|
||||
|
||||
return frequency_ratio;
|
||||
}
|
||||
|
||||
float* audio_ringbuffer::get_buffer(u32 num) const
|
||||
{
|
||||
AUDIT(num < cfg.num_allocated_buffers);
|
||||
AUDIT(buffer[num]);
|
||||
return buffer[num].get();
|
||||
}
|
||||
|
||||
u32 audio_ringbuffer::backend_write_callback(u32 size, void *buf)
|
||||
{
|
||||
if (!backend_active.observe()) backend_active = true;
|
||||
@ -196,50 +189,96 @@ u64 audio_ringbuffer::get_timestamp()
|
||||
return get_system_time();
|
||||
}
|
||||
|
||||
void audio_ringbuffer::enqueue(const float* in_buffer)
|
||||
float* audio_ringbuffer::get_current_buffer() const
|
||||
{
|
||||
return get_buffer(cur_pos);
|
||||
}
|
||||
|
||||
u64 audio_ringbuffer::get_enqueued_samples() const
|
||||
{
|
||||
AUDIT(cfg.buffering_enabled);
|
||||
const u64 ringbuf_samples = cb_ringbuf.get_used_size() / (cfg.audio_sample_size * cfg.audio_channels);
|
||||
|
||||
if (cfg.time_stretching_enabled)
|
||||
{
|
||||
return ringbuf_samples + resampler.samples_available();
|
||||
}
|
||||
|
||||
return ringbuf_samples;
|
||||
}
|
||||
|
||||
u64 audio_ringbuffer::get_enqueued_playtime() const
|
||||
{
|
||||
AUDIT(cfg.buffering_enabled);
|
||||
|
||||
return get_enqueued_samples() * 1'000'000 / cfg.audio_sampling_rate;
|
||||
}
|
||||
|
||||
void audio_ringbuffer::enqueue(bool enqueue_silence, bool force)
|
||||
{
|
||||
AUDIT(cur_pos < cfg.num_allocated_buffers);
|
||||
|
||||
// Prepare buffer
|
||||
const void* buf = in_buffer;
|
||||
static float silence_buffer[u32{AUDIO_MAX_CHANNELS_COUNT} * u32{AUDIO_BUFFER_SAMPLES}]{};
|
||||
float* buf = silence_buffer;
|
||||
|
||||
if (buf == nullptr)
|
||||
if (!enqueue_silence)
|
||||
{
|
||||
buf = buffer[cur_pos].get();
|
||||
cur_pos = (cur_pos + 1) % cfg.num_allocated_buffers;
|
||||
}
|
||||
|
||||
// Dump audio if enabled
|
||||
if (m_dump)
|
||||
{
|
||||
m_dump->WriteData(buf, cfg.audio_buffer_size);
|
||||
}
|
||||
m_dump.WriteData(buf, cfg.audio_buffer_size);
|
||||
|
||||
enqueued_samples += AUDIO_BUFFER_SAMPLES;
|
||||
|
||||
// Start playing audio
|
||||
play();
|
||||
|
||||
if (!backend_active.observe())
|
||||
if (!backend_active.observe() && !force)
|
||||
{
|
||||
// backend is not ready yet
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue audio
|
||||
const u32 data_size = AUDIO_BUFFER_SAMPLES * cfg.audio_sample_size * cfg.audio_channels;
|
||||
|
||||
if (cb_ringbuf.get_free_size() >= data_size)
|
||||
if (cfg.time_stretching_enabled)
|
||||
{
|
||||
cb_ringbuf.push(buf, data_size);
|
||||
resampler.put_samples(buf, AUDIO_BUFFER_SAMPLES);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Since time stretching step is skipped, we can commit to buffer directly
|
||||
commit_data(buf, AUDIO_BUFFER_SAMPLES);
|
||||
}
|
||||
}
|
||||
|
||||
void audio_ringbuffer::enqueue_silence(u32 buf_count)
|
||||
void audio_ringbuffer::enqueue_silence(u32 buf_count, bool force)
|
||||
{
|
||||
for (u32 i = 0; i < buf_count; i++)
|
||||
{
|
||||
enqueue(silence_buffer);
|
||||
enqueue(true, force);
|
||||
}
|
||||
}
|
||||
|
||||
void audio_ringbuffer::process_resampled_data()
|
||||
{
|
||||
if (!cfg.time_stretching_enabled) return;
|
||||
|
||||
const auto samples = resampler.get_samples(cb_ringbuf.get_free_size() / (cfg.audio_sample_size * cfg.audio_channels));
|
||||
commit_data(samples.first, samples.second);
|
||||
}
|
||||
|
||||
void audio_ringbuffer::commit_data(f32* buf, u32 sample_cnt)
|
||||
{
|
||||
sample_cnt *= cfg.audio_channels;
|
||||
|
||||
if (cfg.backend->get_convert_to_s16())
|
||||
{
|
||||
AudioBackend::convert_to_s16(sample_cnt, buf, buf);
|
||||
}
|
||||
|
||||
sample_cnt *= cfg.audio_sample_size;
|
||||
|
||||
if (cb_ringbuf.get_free_size() >= sample_cnt)
|
||||
{
|
||||
cb_ringbuf.push(buf, sample_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,82 +289,44 @@ void audio_ringbuffer::play()
|
||||
return;
|
||||
}
|
||||
|
||||
if (frequency_ratio != 1.0f)
|
||||
{
|
||||
set_frequency_ratio(1.0f);
|
||||
}
|
||||
|
||||
playing = true;
|
||||
|
||||
ensure(enqueued_samples > 0);
|
||||
|
||||
play_timestamp = get_timestamp();
|
||||
backend->Play();
|
||||
}
|
||||
|
||||
void audio_ringbuffer::flush()
|
||||
{
|
||||
//cellAudio.trace("Flushing an estimated %llu enqueued samples", enqueued_samples);
|
||||
|
||||
backend->Pause();
|
||||
cb_ringbuf.flush();
|
||||
resampler.flush();
|
||||
backend_active = false;
|
||||
playing = false;
|
||||
|
||||
if (frequency_ratio != 1.0f)
|
||||
if (frequency_ratio != RESAMPLER_MAX_FREQ_VAL)
|
||||
{
|
||||
set_frequency_ratio(1.0f);
|
||||
frequency_ratio = set_frequency_ratio(RESAMPLER_MAX_FREQ_VAL);
|
||||
}
|
||||
|
||||
enqueued_samples = 0;
|
||||
}
|
||||
|
||||
u64 audio_ringbuffer::update()
|
||||
u64 audio_ringbuffer::update(bool emu_is_paused)
|
||||
{
|
||||
// Check emulator pause state
|
||||
if (Emu.IsPaused())
|
||||
if (emu_is_paused)
|
||||
{
|
||||
// Emulator paused
|
||||
if (playing)
|
||||
{
|
||||
flush();
|
||||
}
|
||||
emu_paused = true;
|
||||
}
|
||||
else if (emu_paused)
|
||||
else
|
||||
{
|
||||
// Emulator unpaused
|
||||
if (enqueued_samples > 0)
|
||||
{
|
||||
play();
|
||||
}
|
||||
emu_paused = false;
|
||||
play();
|
||||
}
|
||||
|
||||
// Prepare timestamp and playing status
|
||||
// Prepare timestamp
|
||||
const u64 timestamp = get_timestamp();
|
||||
const bool new_playing = !emu_paused && get_backend_playing();
|
||||
|
||||
// Calculate how many audio samples have played since last time
|
||||
if (cfg.buffering_enabled && (playing || new_playing))
|
||||
{
|
||||
enqueued_samples = cb_ringbuf.get_used_size() / (cfg.audio_sample_size * cfg.audio_channels);
|
||||
}
|
||||
|
||||
// Update playing state
|
||||
if (playing != new_playing)
|
||||
{
|
||||
if (!new_playing)
|
||||
{
|
||||
cellAudio.warning("Audio backend stopped unexpectedly, likely due to a buffer underrun");
|
||||
|
||||
flush();
|
||||
playing = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
playing = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Store and return timestamp
|
||||
update_timestamp = timestamp;
|
||||
@ -445,15 +446,14 @@ void cell_audio_thread::reset_ports(s32 offset)
|
||||
}
|
||||
}
|
||||
|
||||
void cell_audio_thread::advance(u64 timestamp, bool reset)
|
||||
void cell_audio_thread::advance(u64 timestamp)
|
||||
{
|
||||
ringbuffer->process_resampled_data();
|
||||
|
||||
std::unique_lock lock(mutex);
|
||||
|
||||
// update ports
|
||||
if (reset)
|
||||
{
|
||||
reset_ports(0);
|
||||
}
|
||||
reset_ports(0);
|
||||
|
||||
for (auto& port : ports)
|
||||
{
|
||||
@ -470,9 +470,7 @@ void cell_audio_thread::advance(u64 timestamp, bool reset)
|
||||
if (cfg.buffering_enabled)
|
||||
{
|
||||
// Calculate rolling average of enqueued playtime
|
||||
const u64 enqueued_playtime = ringbuffer->get_enqueued_playtime(/* raw */ true);
|
||||
m_average_playtime = cfg.period_average_alpha * enqueued_playtime + (1.0f - cfg.period_average_alpha) * m_average_playtime;
|
||||
//cellAudio.error("m_average_playtime=%4.2f, enqueued_playtime=%u", m_average_playtime, enqueued_playtime);
|
||||
m_average_playtime = cfg.period_average_alpha * ringbuffer->get_enqueued_playtime() + (1.0f - cfg.period_average_alpha) * m_average_playtime;
|
||||
}
|
||||
|
||||
m_counter++;
|
||||
@ -539,6 +537,7 @@ namespace audio
|
||||
.enable_time_stretching = static_cast<bool>(g_cfg.audio.enable_time_stretching),
|
||||
.time_stretching_threshold = g_cfg.audio.time_stretching_threshold,
|
||||
.convert_to_s16 = static_cast<bool>(g_cfg.audio.convert_to_s16),
|
||||
.dump_to_file = static_cast<bool>(g_cfg.audio.dump_to_file),
|
||||
.downmix = g_cfg.audio.audio_channel_downmix,
|
||||
.renderer = g_cfg.audio.renderer,
|
||||
.provider = g_cfg.audio.provider
|
||||
@ -564,7 +563,8 @@ namespace audio
|
||||
raw.enable_time_stretching != new_raw.enable_time_stretching ||
|
||||
raw.convert_to_s16 != new_raw.convert_to_s16 ||
|
||||
raw.downmix != new_raw.downmix ||
|
||||
raw.renderer != new_raw.renderer)
|
||||
raw.renderer != new_raw.renderer ||
|
||||
raw.dump_to_file != new_raw.dump_to_file)
|
||||
{
|
||||
g_audio.cfg.raw = new_raw;
|
||||
g_audio.m_update_configuration = raw.renderer != new_raw.renderer ? audio_backend_update::ALL : audio_backend_update::PARAM;
|
||||
@ -597,6 +597,7 @@ void cell_audio_thread::reset_counters()
|
||||
m_last_period_end = m_start_time;
|
||||
m_dynamic_period = 0;
|
||||
m_backend_failed = false;
|
||||
m_audio_should_restart = true;
|
||||
}
|
||||
|
||||
cell_audio_thread::cell_audio_thread()
|
||||
@ -658,10 +659,13 @@ void cell_audio_thread::operator()()
|
||||
m_backend_failed = false;
|
||||
}
|
||||
|
||||
const u64 timestamp = ringbuffer->update();
|
||||
const bool emu_paused = Emu.IsPaused();
|
||||
const u64 timestamp = ringbuffer->update(emu_paused);
|
||||
|
||||
if (Emu.IsPaused())
|
||||
if (emu_paused)
|
||||
{
|
||||
m_audio_should_restart = true;
|
||||
ringbuffer->flush();
|
||||
thread_ctrl::wait_for(10000);
|
||||
continue;
|
||||
}
|
||||
@ -670,6 +674,30 @@ void cell_audio_thread::operator()()
|
||||
|
||||
const u64 time_since_last_period = timestamp - m_last_period_end;
|
||||
|
||||
// Handle audio restart
|
||||
if (m_audio_should_restart)
|
||||
{
|
||||
// align to 5.(3)ms on global clock - some games seem to prefer this
|
||||
const s64 audio_period_alignment_delta = (timestamp - m_start_time) % cfg.audio_block_period;
|
||||
if (audio_period_alignment_delta > cfg.period_comparison_margin)
|
||||
{
|
||||
thread_ctrl::wait_for(audio_period_alignment_delta - cfg.period_comparison_margin);
|
||||
}
|
||||
|
||||
if (cfg.buffering_enabled)
|
||||
{
|
||||
// Restart algorithm
|
||||
cellAudio.trace("restarting audio");
|
||||
ringbuffer->enqueue_silence(cfg.desired_full_buffers, true);
|
||||
finish_port_volume_stepping();
|
||||
m_average_playtime = static_cast<f32>(ringbuffer->get_enqueued_playtime());
|
||||
untouched_expected = 0;
|
||||
}
|
||||
|
||||
m_audio_should_restart = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cfg.buffering_enabled)
|
||||
{
|
||||
const u64 period_end = (m_counter * cfg.audio_block_period) + m_start_time;
|
||||
@ -684,95 +712,72 @@ void cell_audio_thread::operator()()
|
||||
else
|
||||
{
|
||||
const u64 enqueued_samples = ringbuffer->get_enqueued_samples();
|
||||
f32 frequency_ratio = ringbuffer->get_frequency_ratio();
|
||||
u64 enqueued_playtime = ringbuffer->get_enqueued_playtime();
|
||||
const f32 frequency_ratio = ringbuffer->get_frequency_ratio();
|
||||
const u64 enqueued_playtime = ringbuffer->get_enqueued_playtime();
|
||||
const u64 enqueued_buffers = enqueued_samples / AUDIO_BUFFER_SAMPLES;
|
||||
|
||||
const bool playing = ringbuffer->is_playing();
|
||||
|
||||
const auto tag_info = count_port_buffer_tags();
|
||||
const u32 active_ports = std::get<0>(tag_info);
|
||||
const u32 in_progress = std::get<1>(tag_info);
|
||||
const u32 untouched = std::get<2>(tag_info);
|
||||
const u32 incomplete = std::get<3>(tag_info);
|
||||
|
||||
// Wait for a dynamic period - try to maintain an average as close as possible to 5.(3)ms
|
||||
if (!playing)
|
||||
// Ratio between the rolling average of the audio period, and the desired audio period
|
||||
const f32 average_playtime_ratio = m_average_playtime / cfg.audio_buffer_length;
|
||||
|
||||
// Use the above average ratio to decide how much buffer we should be aiming for
|
||||
f32 desired_duration_adjusted = cfg.desired_buffer_duration + (cfg.audio_block_period / 2.0f);
|
||||
if (average_playtime_ratio < 1.0f)
|
||||
{
|
||||
// When the buffer is empty, always use the correct block period
|
||||
m_dynamic_period = cfg.audio_block_period;
|
||||
desired_duration_adjusted /= std::max(average_playtime_ratio, 0.25f);
|
||||
}
|
||||
else
|
||||
|
||||
if (cfg.time_stretching_enabled)
|
||||
{
|
||||
// Ratio between the rolling average of the audio period, and the desired audio period
|
||||
const f32 average_playtime_ratio = m_average_playtime / cfg.audio_buffer_length;
|
||||
|
||||
// Use the above average ratio to decide how much buffer we should be aiming for
|
||||
f32 desired_duration_adjusted = cfg.desired_buffer_duration + (cfg.audio_block_period / 2.0f);
|
||||
if (average_playtime_ratio < 1.0f)
|
||||
{
|
||||
desired_duration_adjusted /= std::max(average_playtime_ratio, 0.25f);
|
||||
}
|
||||
|
||||
if (cfg.time_stretching_enabled)
|
||||
{
|
||||
// Calculate what the playtime is without a frequency ratio
|
||||
const u64 raw_enqueued_playtime = ringbuffer->get_enqueued_playtime(/* raw= */ true);
|
||||
|
||||
// 1.0 means exactly as desired
|
||||
// <1.0 means not as full as desired
|
||||
// >1.0 means more full than desired
|
||||
const f32 desired_duration_rate = raw_enqueued_playtime / desired_duration_adjusted;
|
||||
|
||||
// update frequency ratio if necessary
|
||||
f32 new_ratio = frequency_ratio;
|
||||
if (desired_duration_rate < cfg.time_stretching_threshold)
|
||||
{
|
||||
const f32 normalized_desired_duration_rate = desired_duration_rate / cfg.time_stretching_threshold;
|
||||
const f32 request_ratio = normalized_desired_duration_rate * cfg.time_stretching_scale;
|
||||
AUDIT(request_ratio <= 1.0f);
|
||||
|
||||
// change frequency ratio in steps
|
||||
if (std::abs(frequency_ratio - request_ratio) > cfg.time_stretching_step)
|
||||
{
|
||||
new_ratio = ringbuffer->set_frequency_ratio(request_ratio);
|
||||
}
|
||||
}
|
||||
else if (frequency_ratio != 1.0f)
|
||||
{
|
||||
new_ratio = ringbuffer->set_frequency_ratio(1.0f);
|
||||
}
|
||||
|
||||
if (new_ratio != frequency_ratio)
|
||||
{
|
||||
// ratio changed, calculate new dynamic period
|
||||
frequency_ratio = new_ratio;
|
||||
enqueued_playtime = ringbuffer->get_enqueued_playtime();
|
||||
m_dynamic_period = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 1.0 means exactly as desired
|
||||
// <1.0 means not as full as desired
|
||||
// >1.0 means more full than desired
|
||||
const f32 desired_duration_rate = enqueued_playtime / desired_duration_adjusted;
|
||||
|
||||
if (desired_duration_rate >= 1.0f)
|
||||
// update frequency ratio if necessary
|
||||
if (desired_duration_rate < cfg.time_stretching_threshold)
|
||||
{
|
||||
// more full than desired
|
||||
const f32 multiplier = 1.0f / desired_duration_rate;
|
||||
m_dynamic_period = cfg.maximum_block_period - static_cast<u64>((cfg.maximum_block_period - cfg.audio_block_period) * multiplier);
|
||||
const f32 normalized_desired_duration_rate = desired_duration_rate / cfg.time_stretching_threshold;
|
||||
const f32 request_ratio = normalized_desired_duration_rate * cfg.time_stretching_scale;
|
||||
AUDIT(request_ratio <= RESAMPLER_MAX_FREQ_VAL);
|
||||
|
||||
// change frequency ratio in steps
|
||||
const f32 req_time_stretching_step = (request_ratio + frequency_ratio) / 2.0f;
|
||||
if (req_time_stretching_step > cfg.time_stretching_step)
|
||||
{
|
||||
ringbuffer->set_frequency_ratio(req_time_stretching_step);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (frequency_ratio != RESAMPLER_MAX_FREQ_VAL)
|
||||
{
|
||||
// not as full as desired
|
||||
const f32 multiplier = desired_duration_rate * desired_duration_rate; // quite aggressive, but helps more times than it hurts
|
||||
m_dynamic_period = cfg.minimum_block_period + static_cast<u64>((cfg.audio_block_period - cfg.minimum_block_period) * multiplier);
|
||||
ringbuffer->set_frequency_ratio(RESAMPLER_MAX_FREQ_VAL);
|
||||
}
|
||||
}
|
||||
|
||||
s64 time_left = m_dynamic_period - time_since_last_period;
|
||||
// 1.0 means exactly as desired
|
||||
// <1.0 means not as full as desired
|
||||
// >1.0 means more full than desired
|
||||
const f32 desired_duration_rate = enqueued_playtime / desired_duration_adjusted;
|
||||
|
||||
if (desired_duration_rate >= 1.0f)
|
||||
{
|
||||
// more full than desired
|
||||
const f32 multiplier = 1.0f / desired_duration_rate;
|
||||
m_dynamic_period = cfg.maximum_block_period - static_cast<u64>((cfg.maximum_block_period - cfg.audio_block_period) * multiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
// not as full as desired
|
||||
const f32 multiplier = desired_duration_rate * desired_duration_rate; // quite aggressive, but helps more times than it hurts
|
||||
m_dynamic_period = cfg.minimum_block_period + static_cast<u64>((cfg.audio_block_period - cfg.minimum_block_period) * multiplier);
|
||||
}
|
||||
|
||||
const s64 time_left = m_dynamic_period - time_since_last_period;
|
||||
if (time_left > cfg.period_comparison_margin)
|
||||
{
|
||||
thread_ctrl::wait_for(get_thread_wait_delay(time_left));
|
||||
@ -784,10 +789,7 @@ void cell_audio_thread::operator()()
|
||||
{
|
||||
// no need to mix, just enqueue silence and advance time
|
||||
cellAudio.trace("enqueuing silence: no active ports, enqueued_buffers=%llu", enqueued_buffers);
|
||||
if (playing)
|
||||
{
|
||||
ringbuffer->enqueue_silence(1);
|
||||
}
|
||||
ringbuffer->enqueue_silence();
|
||||
untouched_expected = 0;
|
||||
advance(timestamp);
|
||||
continue;
|
||||
@ -797,16 +799,6 @@ void cell_audio_thread::operator()()
|
||||
//cellAudio.error("active=%u, in_progress=%u, untouched=%u, incomplete=%u", active_ports, in_progress, untouched, incomplete);
|
||||
if (untouched > untouched_expected)
|
||||
{
|
||||
if (!playing)
|
||||
{
|
||||
// We ran out of buffer, probably because we waited too long
|
||||
// Don't enqueue anything, just advance time
|
||||
cellAudio.trace("advancing time: untouched=%u/%u (expected=%u), enqueued_buffers=0", untouched, active_ports, untouched_expected);
|
||||
untouched_expected = untouched;
|
||||
advance(timestamp);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Games may sometimes "skip" audio periods entirely if they're falling behind (a sort of "frameskip" for audio)
|
||||
// As such, if the game doesn't touch buffers for too long we advance time hoping the game recovers
|
||||
if (
|
||||
@ -831,10 +823,7 @@ void cell_audio_thread::operator()()
|
||||
{
|
||||
// There's no audio in the buffers, simply advance time
|
||||
cellAudio.trace("enqueuing silence: untouched=%u/%u (expected=%u), enqueued_buffers=%llu", untouched, active_ports, untouched_expected, enqueued_buffers);
|
||||
if (playing)
|
||||
{
|
||||
ringbuffer->enqueue_silence(1);
|
||||
}
|
||||
ringbuffer->enqueue_silence();
|
||||
untouched_expected = untouched;
|
||||
advance(timestamp);
|
||||
continue;
|
||||
@ -858,25 +847,6 @@ void cell_audio_thread::operator()()
|
||||
{
|
||||
cellAudio.trace("enqueueing: untouched=%u/%u (expected=%u), incomplete=%u/%u enqueued_buffers=%llu", untouched, active_ports, untouched_expected, incomplete, active_ports, enqueued_buffers);
|
||||
}
|
||||
|
||||
// Handle audio restart
|
||||
if (!playing)
|
||||
{
|
||||
// We are not playing (likely buffer underrun)
|
||||
// align to 5.(3)ms on global clock - some games seem to prefer this
|
||||
const s64 audio_period_alignment_delta = (timestamp - m_start_time) % cfg.audio_block_period;
|
||||
if (audio_period_alignment_delta > cfg.period_comparison_margin)
|
||||
{
|
||||
thread_ctrl::wait_for(audio_period_alignment_delta - cfg.period_comparison_margin);
|
||||
}
|
||||
|
||||
// Flush, add silence, restart algorithm
|
||||
cellAudio.trace("play/resume audio: received first audio buffer");
|
||||
ringbuffer->flush();
|
||||
ringbuffer->enqueue_silence(cfg.desired_full_buffers);
|
||||
finish_port_volume_stepping();
|
||||
m_average_playtime = static_cast<f32>(ringbuffer->get_enqueued_playtime());
|
||||
}
|
||||
}
|
||||
|
||||
// Mix
|
||||
@ -1112,27 +1082,6 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
|
||||
{
|
||||
std::memset(out_buffer, 0, out_buffer_sz * sizeof(float));
|
||||
}
|
||||
else if (cfg.backend->get_convert_to_s16())
|
||||
{
|
||||
// convert the data from float to s16 with clipping:
|
||||
// 2x MULPS
|
||||
// 2x MAXPS (optional)
|
||||
// 2x MINPS (optional)
|
||||
// 2x CVTPS2DQ (converts float to s32)
|
||||
// PACKSSDW (converts s32 to s16 with signed saturation)
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
for (usz i = 0; i < out_buffer_sz; i += 8)
|
||||
{
|
||||
const auto scale = _mm_set1_ps(0x8000);
|
||||
_mm_store_ps(out_buffer + i / 2, _mm_castsi128_ps(_mm_packs_epi32(
|
||||
_mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(out_buffer + i), scale)),
|
||||
_mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(out_buffer + i + 4), scale)))));
|
||||
}
|
||||
#else
|
||||
fmt::throw_exception("Not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void cell_audio_thread::finish_port_volume_stepping()
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
#include "Emu/Audio/AudioDumper.h"
|
||||
#include "Emu/Audio/audio_resampler.h"
|
||||
#include "Emu/system_config_types.h"
|
||||
|
||||
struct lv2_event_queue;
|
||||
@ -204,6 +205,7 @@ struct cell_audio_config
|
||||
bool enable_time_stretching = false;
|
||||
s64 time_stretching_threshold = 0;
|
||||
bool convert_to_s16 = false;
|
||||
bool dump_to_file = false;
|
||||
audio_downmix downmix = audio_downmix::downmix_to_stereo;
|
||||
audio_renderer renderer = audio_renderer::null;
|
||||
audio_provider provider = audio_provider::none;
|
||||
@ -236,9 +238,9 @@ struct cell_audio_config
|
||||
u32 desired_full_buffers = 0;
|
||||
u32 num_allocated_buffers = 0; // number of ringbuffer buffers
|
||||
|
||||
const f32 period_average_alpha = 0.02f; // alpha factor for the m_average_period rolling average
|
||||
static constexpr f32 period_average_alpha = 0.02f; // alpha factor for the m_average_period rolling average
|
||||
|
||||
const s64 period_comparison_margin = 250; // when comparing the current period time with the desired period, if it is below this number of usecs we do not wait any longer
|
||||
static constexpr s64 period_comparison_margin = 250; // when comparing the current period time with the desired period, if it is below this number of usecs we do not wait any longer
|
||||
|
||||
u64 fully_untouched_timeout = 0; // timeout if the game has not touched any audio buffer yet
|
||||
u64 partially_untouched_timeout = 0; // timeout if the game has not touched all audio buffers yet
|
||||
@ -251,8 +253,8 @@ struct cell_audio_config
|
||||
bool time_stretching_enabled = false;
|
||||
|
||||
f32 time_stretching_threshold = 0.0f; // we only apply time stretching below this buffer fill rate (adjusted for average period)
|
||||
const f32 time_stretching_step = 0.1f; // will only reduce/increase the frequency ratio in steps of at least this value
|
||||
const f32 time_stretching_scale = 0.9f;
|
||||
static constexpr f32 time_stretching_step = 0.1f; // will only reduce/increase the frequency ratio in steps of at least this value
|
||||
static constexpr f32 time_stretching_scale = 0.9f;
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
@ -274,24 +276,22 @@ private:
|
||||
|
||||
const u32 buf_sz;
|
||||
|
||||
std::unique_ptr<AudioDumper> m_dump;
|
||||
AudioDumper m_dump{};
|
||||
|
||||
std::unique_ptr<float[]> buffer[MAX_AUDIO_BUFFERS];
|
||||
const float silence_buffer[u32{AUDIO_MAX_CHANNELS_COUNT} * u32{AUDIO_BUFFER_SAMPLES}] = { 0 };
|
||||
|
||||
simple_ringbuf cb_ringbuf{};
|
||||
audio_resampler resampler{};
|
||||
|
||||
atomic_t<bool> backend_active = false;
|
||||
bool playing = false;
|
||||
bool emu_paused = false;
|
||||
|
||||
u64 update_timestamp = 0;
|
||||
u64 play_timestamp = 0;
|
||||
|
||||
u64 last_remainder = 0;
|
||||
u64 enqueued_samples = 0;
|
||||
|
||||
f32 frequency_ratio = 1.0f;
|
||||
f32 frequency_ratio = RESAMPLER_MAX_FREQ_VAL;
|
||||
|
||||
u32 cur_pos = 0;
|
||||
|
||||
@ -300,6 +300,7 @@ private:
|
||||
return backend->IsPlaying();
|
||||
}
|
||||
|
||||
void commit_data(f32* buf, u32 sample_cnt);
|
||||
u32 backend_write_callback(u32 size, void *buf);
|
||||
|
||||
public:
|
||||
@ -307,38 +308,19 @@ public:
|
||||
~audio_ringbuffer();
|
||||
|
||||
void play();
|
||||
void enqueue(const float* in_buffer = nullptr);
|
||||
void flush();
|
||||
u64 update();
|
||||
void enqueue_silence(u32 buf_count = 1);
|
||||
u64 update(bool emu_is_paused);
|
||||
void enqueue(bool enqueue_silence = false, bool force = false);
|
||||
void enqueue_silence(u32 buf_count = 1, bool force = false);
|
||||
void process_resampled_data();
|
||||
f32 set_frequency_ratio(f32 new_ratio);
|
||||
|
||||
float* get_buffer(u32 num) const
|
||||
{
|
||||
AUDIT(num < cfg.num_allocated_buffers);
|
||||
AUDIT(buffer[num].get() != nullptr);
|
||||
return buffer[num].get();
|
||||
}
|
||||
|
||||
float* get_buffer(u32 num) const;
|
||||
static u64 get_timestamp();
|
||||
float* get_current_buffer() const;
|
||||
|
||||
float* get_current_buffer() const
|
||||
{
|
||||
return get_buffer(cur_pos);
|
||||
}
|
||||
|
||||
u64 get_enqueued_samples() const
|
||||
{
|
||||
AUDIT(cfg.buffering_enabled);
|
||||
return enqueued_samples;
|
||||
}
|
||||
|
||||
u64 get_enqueued_playtime(bool raw = false) const
|
||||
{
|
||||
AUDIT(cfg.buffering_enabled);
|
||||
u64 sampling_rate = raw ? cfg.audio_sampling_rate : static_cast<u64>(cfg.audio_sampling_rate * frequency_ratio);
|
||||
return enqueued_samples * 1'000'000 / sampling_rate;
|
||||
}
|
||||
u64 get_enqueued_samples() const;
|
||||
u64 get_enqueued_playtime() const;
|
||||
|
||||
bool is_playing() const
|
||||
{
|
||||
@ -350,11 +332,6 @@ public:
|
||||
return frequency_ratio;
|
||||
}
|
||||
|
||||
u32 has_capability(u32 cap) const
|
||||
{
|
||||
return backend->has_capability(cap);
|
||||
}
|
||||
|
||||
bool get_operational_status() const
|
||||
{
|
||||
return backend->Operational();
|
||||
@ -373,7 +350,7 @@ private:
|
||||
std::unique_ptr<audio_ringbuffer> ringbuffer;
|
||||
|
||||
void reset_ports(s32 offset = 0);
|
||||
void advance(u64 timestamp, bool reset = true);
|
||||
void advance(u64 timestamp);
|
||||
std::tuple<u32, u32, u32, u32> count_port_buffer_tags();
|
||||
template <audio_downmix downmix>
|
||||
void mix(float *out_buffer, s32 offset = 0);
|
||||
@ -414,6 +391,7 @@ public:
|
||||
u64 m_dynamic_period = 0;
|
||||
f32 m_average_playtime = 0.0f;
|
||||
bool m_backend_failed = false;
|
||||
bool m_audio_should_restart = false;
|
||||
|
||||
cell_audio_thread();
|
||||
|
||||
@ -421,11 +399,6 @@ public:
|
||||
|
||||
audio_port* open_port();
|
||||
|
||||
bool has_capability(u32 cap) const
|
||||
{
|
||||
return ringbuffer->has_capability(cap);
|
||||
}
|
||||
|
||||
static constexpr auto thread_name = "cellAudio Thread"sv;
|
||||
};
|
||||
|
||||
|
@ -227,7 +227,7 @@ struct cfg_root : cfg::node
|
||||
|
||||
cfg::_enum<audio_renderer> renderer{ this, "Renderer", audio_renderer::cubeb, true };
|
||||
cfg::_enum<audio_provider> provider{ this, "Audio provider", audio_provider::cell_audio, false };
|
||||
cfg::_bool dump_to_file{ this, "Dump to file" };
|
||||
cfg::_bool dump_to_file{ this, "Dump to file", false, true };
|
||||
cfg::_bool convert_to_s16{ this, "Convert to 16 bit", false, true };
|
||||
cfg::_enum<audio_downmix> audio_channel_downmix{ this, "Audio Channels", audio_downmix::downmix_to_stereo, true };
|
||||
cfg::_int<0, 200> volume{ this, "Master Volume", 100, true };
|
||||
|
@ -40,7 +40,7 @@
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\zlib\zlib;..\llvm\include;..\llvm_build\include;$(VULKAN_SDK)\Include</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\zlib\zlib;..\llvm\include;..\llvm_build\include;$(VULKAN_SDK)\Include</AdditionalIncludeDirectories>
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">HAVE_VULKAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">HAVE_VULKAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
@ -53,6 +53,7 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Utilities\cheat_info.cpp" />
|
||||
<ClCompile Include="Emu\Audio\audio_device_listener.cpp" />
|
||||
<ClCompile Include="Emu\Audio\audio_resampler.cpp" />
|
||||
<ClCompile Include="Emu\Audio\FAudio\FAudioBackend.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
@ -435,6 +436,7 @@
|
||||
<ClInclude Include="..\Utilities\address_range.h" />
|
||||
<ClInclude Include="..\Utilities\cheat_info.h" />
|
||||
<ClInclude Include="Emu\Audio\audio_device_listener.h" />
|
||||
<ClInclude Include="Emu\Audio\audio_resampler.h" />
|
||||
<ClInclude Include="Emu\Audio\FAudio\FAudioBackend.h">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
@ -790,4 +792,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -858,7 +858,7 @@
|
||||
<ClCompile Include="Emu\Io\Buzz.cpp">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Io\usio.cpp">
|
||||
<ClCompile Include="Emu\Io\usio.cpp">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Cell\lv2\sys_crypto_engine.cpp">
|
||||
@ -1024,6 +1024,9 @@
|
||||
<ClCompile Include="Emu\RSX\rsx_vertex_data.cpp">
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Audio\audio_resampler.cpp">
|
||||
<Filter>Emu\Audio</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
@ -2020,6 +2023,24 @@
|
||||
<ClInclude Include="Emu\Io\camera_config.h">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Audio\audio_resampler.h">
|
||||
<Filter>Emu\Audio</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\NP\np_allocator.h">
|
||||
<Filter>Emu\NP</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\NP\np_cache.h">
|
||||
<Filter>Emu\NP</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\NP\np_dnshook.h">
|
||||
<Filter>Emu\NP</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\NP\np_event_data.h">
|
||||
<Filter>Emu\NP</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\NP\np_helpers.h">
|
||||
<Filter>Emu\NP</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">
|
||||
@ -2029,4 +2050,4 @@
|
||||
<Filter>Emu\GPU\RSX\Common\Interpreter</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
@ -71,7 +71,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\release;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\release;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>-Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AssemblerListingLocation>release\</AssemblerListingLocation>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
@ -88,7 +88,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimedia.lib;Qt5MultimediaWidgets.lib;Qt5Svg.lib;libcubeb.lib;cubeb.lib;Avrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimedia.lib;Qt5MultimediaWidgets.lib;Qt5Svg.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Release;..\3rdparty\glslang\build\SPIRV\Release;..\3rdparty\glslang\build\OGLCompilersDLL\Release;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Release;..\3rdparty\glslang\build\glslang\Release;..\3rdparty\SPIRV\build\source\Release;..\3rdparty\SPIRV\build\source\opt\Release;..\lib\$(CONFIGURATION)-$(PLATFORM);..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
|
||||
<DataExecutionPrevention>true</DataExecutionPrevention>
|
||||
@ -122,7 +122,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>-Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AssemblerListingLocation>debug\</AssemblerListingLocation>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
@ -139,7 +139,7 @@
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimediad.lib;Qt5MultimediaWidgetsd.lib;Qt5Svgd.lib;libcubeb.lib;cubeb.lib;Avrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimediad.lib;Qt5MultimediaWidgetsd.lib;Qt5Svgd.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Debug;..\3rdparty\glslang\build\SPIRV\Debug;..\3rdparty\glslang\build\OGLCompilersDLL\Debug;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Debug;..\3rdparty\glslang\build\glslang\Debug;..\3rdparty\SPIRV\build\source\opt\Debug;..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;..\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /VERBOSE %(AdditionalOptions)</AdditionalOptions>
|
||||
<DataExecutionPrevention>true</DataExecutionPrevention>
|
||||
@ -1517,4 +1517,4 @@
|
||||
<UserProperties MocDir=".\QTGeneratedFiles\$(ConfigurationName)" Qt5Version_x0020_x64="$(DefaultQtVersion)" RccDir=".\QTGeneratedFiles" UicDir=".\QTGeneratedFiles" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
||||
</Project>
|
||||
|
Loading…
Reference in New Issue
Block a user