mirror of https://github.com/PCSX2/pcsx2.git
SPU2: Add Cubeb SndOut driver
This commit is contained in:
parent
bd489647e9
commit
88ce192610
|
@ -60,6 +60,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common\common.vcx
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glad", "3rdparty\glad\glad.vcxproj", "{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glad", "3rdparty\glad\glad.vcxproj", "{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cubeb", "3rdparty\cubeb\cubeb.vcxproj", "{BF74C473-DC04-44B3-92E8-4145F4E77342}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug AVX2|Win32 = Debug AVX2|Win32
|
Debug AVX2|Win32 = Debug AVX2|Win32
|
||||||
|
@ -604,6 +606,30 @@ Global
|
||||||
{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release|Win32.Build.0 = Release|Win32
|
{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release|Win32.Build.0 = Release|Win32
|
||||||
{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release|x64.ActiveCfg = Release|x64
|
{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release|x64.ActiveCfg = Release|x64
|
||||||
{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release|x64.Build.0 = Release|x64
|
{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release|x64.Build.0 = Release|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug AVX2|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug AVX2|Win32.Build.0 = Debug|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug AVX2|x64.ActiveCfg = Debug|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug AVX2|x64.Build.0 = Debug|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel AVX2|Win32.ActiveCfg = Devel|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel AVX2|Win32.Build.0 = Devel|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel AVX2|x64.ActiveCfg = Devel|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel AVX2|x64.Build.0 = Devel|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel|Win32.ActiveCfg = Devel|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel|Win32.Build.0 = Devel|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel|x64.ActiveCfg = Devel|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel|x64.Build.0 = Devel|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Release AVX2|Win32.ActiveCfg = Release|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Release AVX2|Win32.Build.0 = Release|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Release AVX2|x64.ActiveCfg = Release|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Release AVX2|x64.Build.0 = Release|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342}.Release|x64.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -629,6 +655,7 @@ Global
|
||||||
{A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
{A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
||||||
{ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
{ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
||||||
{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
||||||
|
{BF74C473-DC04-44B3-92E8-4145F4E77342} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {0BC474EA-3628-45D3-9DBC-E22D0B7E0F77}
|
SolutionGuid = {0BC474EA-3628-45D3-9DBC-E22D0B7E0F77}
|
||||||
|
|
|
@ -304,6 +304,12 @@ if(TARGET PkgConfig::PORTAUDIO)
|
||||||
list(APPEND pcsx2SPU2Sources SPU2/SndOut_Portaudio.cpp)
|
list(APPEND pcsx2SPU2Sources SPU2/SndOut_Portaudio.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CUBEB_API)
|
||||||
|
list(APPEND pcsx2SPU2Sources SPU2/SndOut_Cubeb.cpp)
|
||||||
|
target_compile_definitions(PCSX2_FLAGS INTERFACE "SPU2X_CUBEB")
|
||||||
|
target_link_libraries(PCSX2_FLAGS INTERFACE cubeb)
|
||||||
|
endif()
|
||||||
|
|
||||||
# SPU2 headers
|
# SPU2 headers
|
||||||
set(pcsx2SPU2Headers
|
set(pcsx2SPU2Headers
|
||||||
SPU2/Config.h
|
SPU2/Config.h
|
||||||
|
|
|
@ -88,6 +88,9 @@ SndOutModule* mods[] =
|
||||||
#if defined(SPU2X_PORTAUDIO)
|
#if defined(SPU2X_PORTAUDIO)
|
||||||
PortaudioOut,
|
PortaudioOut,
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(SPU2X_CUBEB)
|
||||||
|
CubebOut,
|
||||||
|
#endif
|
||||||
#if defined(__linux__) || defined(__APPLE__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
SDLOut,
|
SDLOut,
|
||||||
#endif
|
#endif
|
||||||
|
@ -138,6 +141,7 @@ bool SndBuffer::CheckUnderrunStatus(int& nSamples, int& quietSampleCount)
|
||||||
if (data < toFill)
|
if (data < toFill)
|
||||||
{
|
{
|
||||||
quietSampleCount = nSamples;
|
quietSampleCount = nSamples;
|
||||||
|
nSamples = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,8 +152,8 @@ bool SndBuffer::CheckUnderrunStatus(int& nSamples, int& quietSampleCount)
|
||||||
}
|
}
|
||||||
else if (data < nSamples)
|
else if (data < nSamples)
|
||||||
{
|
{
|
||||||
|
quietSampleCount = nSamples - data;
|
||||||
nSamples = data;
|
nSamples = data;
|
||||||
quietSampleCount = SndOutPacketSize - data;
|
|
||||||
m_underrun_freeze = true;
|
m_underrun_freeze = true;
|
||||||
|
|
||||||
if (SynchMode == 0) // TimeStrech on
|
if (SynchMode == 0) // TimeStrech on
|
||||||
|
@ -236,10 +240,8 @@ void SndBuffer::_ReadSamples_Safe(StereoOut32* bData, int nSamples)
|
||||||
// the sample output is determined by the SndOutVolumeShift, which is the number of bits
|
// the sample output is determined by the SndOutVolumeShift, which is the number of bits
|
||||||
// to shift right to get a 16 bit result.
|
// to shift right to get a 16 bit result.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void SndBuffer::ReadSamples(T* bData)
|
void SndBuffer::ReadSamples(T* bData, int nSamples)
|
||||||
{
|
{
|
||||||
int nSamples = SndOutPacketSize;
|
|
||||||
|
|
||||||
// Problem:
|
// Problem:
|
||||||
// If the SPU2 gets even the least bit out of sync with the SndOut device,
|
// If the SPU2 gets even the least bit out of sync with the SndOut device,
|
||||||
// the readpos of the circular buffer will overtake the writepos,
|
// the readpos of the circular buffer will overtake the writepos,
|
||||||
|
@ -293,29 +295,30 @@ void SndBuffer::ReadSamples(T* bData)
|
||||||
// If quietSamples != 0 it means we have an underrun...
|
// If quietSamples != 0 it means we have an underrun...
|
||||||
// Let's just dull out some silence, because that's usually the least
|
// Let's just dull out some silence, because that's usually the least
|
||||||
// painful way of dealing with underruns:
|
// painful way of dealing with underruns:
|
||||||
std::fill_n(bData, quietSamples, T{});
|
if (quietSamples > 0)
|
||||||
|
std::memset(bData + nSamples, 0, sizeof(T) * quietSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
template void SndBuffer::ReadSamples(StereoOut16*);
|
template void SndBuffer::ReadSamples(StereoOut16*, int);
|
||||||
template void SndBuffer::ReadSamples(StereoOut32*);
|
template void SndBuffer::ReadSamples(StereoOut32*, int);
|
||||||
|
|
||||||
//template void SndBuffer::ReadSamples(StereoOutFloat*);
|
//template void SndBuffer::ReadSamples(StereoOutFloat*);
|
||||||
template void SndBuffer::ReadSamples(Stereo21Out16*);
|
template void SndBuffer::ReadSamples(Stereo21Out16*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo40Out16*);
|
template void SndBuffer::ReadSamples(Stereo40Out16*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo41Out16*);
|
template void SndBuffer::ReadSamples(Stereo41Out16*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out16*);
|
template void SndBuffer::ReadSamples(Stereo51Out16*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out16Dpl*);
|
template void SndBuffer::ReadSamples(Stereo51Out16Dpl*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out16DplII*);
|
template void SndBuffer::ReadSamples(Stereo51Out16DplII*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo71Out16*);
|
template void SndBuffer::ReadSamples(Stereo71Out16*, int);
|
||||||
|
|
||||||
template void SndBuffer::ReadSamples(Stereo20Out32*);
|
template void SndBuffer::ReadSamples(Stereo20Out32*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo21Out32*);
|
template void SndBuffer::ReadSamples(Stereo21Out32*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo40Out32*);
|
template void SndBuffer::ReadSamples(Stereo40Out32*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo41Out32*);
|
template void SndBuffer::ReadSamples(Stereo41Out32*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out32*);
|
template void SndBuffer::ReadSamples(Stereo51Out32*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out32Dpl*);
|
template void SndBuffer::ReadSamples(Stereo51Out32Dpl*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out32DplII*);
|
template void SndBuffer::ReadSamples(Stereo51Out32DplII*, int);
|
||||||
template void SndBuffer::ReadSamples(Stereo71Out32*);
|
template void SndBuffer::ReadSamples(Stereo71Out32*, int);
|
||||||
|
|
||||||
void SndBuffer::_WriteSamples(StereoOut32* bData, int nSamples)
|
void SndBuffer::_WriteSamples(StereoOut32* bData, int nSamples)
|
||||||
{
|
{
|
||||||
|
|
|
@ -625,7 +625,7 @@ public:
|
||||||
// the sample output is determined by the SndOutVolumeShift, which is the number of bits
|
// the sample output is determined by the SndOutVolumeShift, which is the number of bits
|
||||||
// to shift right to get a 16 bit result.
|
// to shift right to get a 16 bit result.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static void ReadSamples(T* bData);
|
static void ReadSamples(T* bData, int nSamples = SndOutPacketSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SndOutModule
|
class SndOutModule
|
||||||
|
@ -670,6 +670,9 @@ extern SndOutModule* XAudio2Out;
|
||||||
#if defined(SPU2X_PORTAUDIO)
|
#if defined(SPU2X_PORTAUDIO)
|
||||||
extern SndOutModule* PortaudioOut;
|
extern SndOutModule* PortaudioOut;
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(SPU2X_CUBEB)
|
||||||
|
extern SndOutModule* CubebOut;
|
||||||
|
#endif
|
||||||
extern SndOutModule* const SDLOut;
|
extern SndOutModule* const SDLOut;
|
||||||
extern SndOutModule* mods[];
|
extern SndOutModule* mods[];
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,370 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PrecompiledHeader.h"
|
||||||
|
|
||||||
|
#include "common/Console.h"
|
||||||
|
#include "common/StringUtil.h"
|
||||||
|
#include "common/RedtapeWindows.h"
|
||||||
|
#include "cubeb/cubeb.h"
|
||||||
|
|
||||||
|
#include "Global.h"
|
||||||
|
#include "SndOut.h"
|
||||||
|
|
||||||
|
extern bool CfgReadBool(const wchar_t* Section, const wchar_t* Name, bool Default);
|
||||||
|
extern int CfgReadInt(const wchar_t* Section, const wchar_t* Name, int Default);
|
||||||
|
extern void CfgReadStr(const wchar_t* Section, const wchar_t* Name, wxString& Data, const wchar_t* Default);
|
||||||
|
|
||||||
|
class Cubeb : public SndOutModule
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static constexpr int MINIMUM_LATENCY_MS = 20;
|
||||||
|
static constexpr int MAXIMUM_LATENCY_MS = 200;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Stuff necessary for speaker expansion
|
||||||
|
class SampleReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void ReadSamples(void* outputBuffer, long frames) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class ConvertedSampleReader final : public SampleReader
|
||||||
|
{
|
||||||
|
u64* const written;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ConvertedSampleReader() = delete;
|
||||||
|
|
||||||
|
ConvertedSampleReader(u64* pWritten)
|
||||||
|
: written(pWritten)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadSamples(void* outputBuffer, long frames) override
|
||||||
|
{
|
||||||
|
T* p1 = static_cast<T*>(outputBuffer);
|
||||||
|
|
||||||
|
while (frames > 0)
|
||||||
|
{
|
||||||
|
const long frames_to_read = std::min<long>(frames, SndOutPacketSize);
|
||||||
|
SndBuffer::ReadSamples(p1, frames_to_read);
|
||||||
|
p1 += frames_to_read;
|
||||||
|
frames -= frames_to_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*written) += frames;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void DestroyContextAndStream()
|
||||||
|
{
|
||||||
|
if (stream)
|
||||||
|
{
|
||||||
|
cubeb_stream_stop(stream);
|
||||||
|
cubeb_stream_destroy(stream);
|
||||||
|
stream = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_context)
|
||||||
|
{
|
||||||
|
cubeb_destroy(m_context);
|
||||||
|
m_context = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActualReader.reset();
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (m_COMInitializedByUs)
|
||||||
|
{
|
||||||
|
CoUninitialize();
|
||||||
|
m_COMInitializedByUs = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LogCallback(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
FastFormatAscii msg;
|
||||||
|
std::va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
msg.WriteV(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
Console.WriteLn("(Cubeb): %s", msg.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Configuration Vars
|
||||||
|
bool m_COMInitializedByUs = false;
|
||||||
|
bool m_SuggestedLatencyMinimal = false;
|
||||||
|
int m_SuggestedLatencyMS = 20;
|
||||||
|
std::string m_Backend;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Instance vars
|
||||||
|
u64 writtenSoFar = 0;
|
||||||
|
u64 writtenLastTime = 0;
|
||||||
|
u64 positionLastTime = 0;
|
||||||
|
|
||||||
|
u32 channels = 0;
|
||||||
|
cubeb* m_context = nullptr;
|
||||||
|
cubeb_stream* stream = nullptr;
|
||||||
|
std::unique_ptr<SampleReader> ActualReader;
|
||||||
|
bool m_paused = false;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
Cubeb() = default;
|
||||||
|
|
||||||
|
~Cubeb()
|
||||||
|
{
|
||||||
|
DestroyContextAndStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 Init() override
|
||||||
|
{
|
||||||
|
ReadSettings();
|
||||||
|
|
||||||
|
// TODO(Stenzek): Migrate the errors to Host::ReportErrorAsync() once more Qt stuff is merged.
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||||
|
m_COMInitializedByUs = SUCCEEDED(hr);
|
||||||
|
if (FAILED(hr) && hr != RPC_E_CHANGED_MODE)
|
||||||
|
{
|
||||||
|
Console.Error("Failed to initialize COM");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PCSX2_DEVBUILD
|
||||||
|
cubeb_set_log_callback(CUBEB_LOG_NORMAL, LogCallback);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int rv = cubeb_init(&m_context, "PCSX2", m_Backend.empty() ? nullptr : m_Backend.c_str());
|
||||||
|
if (rv != CUBEB_OK)
|
||||||
|
{
|
||||||
|
Console.Error("Could not initialize cubeb context: %d", rv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (numSpeakers) // speakers = (numSpeakers + 1) *2; ?
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
channels = 2;
|
||||||
|
break; // Stereo
|
||||||
|
case 1:
|
||||||
|
channels = 4;
|
||||||
|
break; // Quadrafonic
|
||||||
|
case 2:
|
||||||
|
channels = 6;
|
||||||
|
break; // Surround 5.1
|
||||||
|
case 3:
|
||||||
|
channels = 8;
|
||||||
|
break; // Surround 7.1
|
||||||
|
default:
|
||||||
|
channels = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cubeb_channel_layout layout = CUBEB_LAYOUT_UNDEFINED;
|
||||||
|
switch (channels)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
Console.WriteLn("(Cubeb) Using normal 2 speaker stereo output.");
|
||||||
|
ActualReader = std::make_unique<ConvertedSampleReader<StereoOut16>>(&writtenSoFar);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
Console.WriteLn("(Cubeb) 2.1 speaker expansion enabled.");
|
||||||
|
ActualReader = std::make_unique<ConvertedSampleReader<Stereo21Out16>>(&writtenSoFar);
|
||||||
|
layout = CUBEB_LAYOUT_STEREO_LFE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
Console.WriteLn("(Cubeb) 4 speaker expansion enabled [quadraphenia]");
|
||||||
|
ActualReader = std::make_unique<ConvertedSampleReader<Stereo40Out16>>(&writtenSoFar);
|
||||||
|
layout = CUBEB_LAYOUT_QUAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
Console.WriteLn("(Cubeb) 4.1 speaker expansion enabled.");
|
||||||
|
ActualReader = std::make_unique<ConvertedSampleReader<Stereo41Out16>>(&writtenSoFar);
|
||||||
|
layout = CUBEB_LAYOUT_QUAD_LFE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
switch (dplLevel)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
Console.WriteLn("(Cubeb) 5.1 speaker expansion enabled.");
|
||||||
|
ActualReader = std::make_unique<ConvertedSampleReader<Stereo51Out16>>(&writtenSoFar); //"normal" stereo upmix
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
Console.WriteLn("(Cubeb) 5.1 speaker expansion with basic ProLogic dematrixing enabled.");
|
||||||
|
ActualReader = std::make_unique<ConvertedSampleReader<Stereo51Out16Dpl>>(&writtenSoFar); // basic Dpl decoder without rear stereo balancing
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Console.WriteLn("(Cubeb) 5.1 speaker expansion with experimental ProLogicII dematrixing enabled.");
|
||||||
|
ActualReader = std::make_unique<ConvertedSampleReader<Stereo51Out16DplII>>(&writtenSoFar); //gigas PLII
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
channels = 6; // we do not support 7.0 or 6.2 configurations, downgrade to 5.1
|
||||||
|
layout = CUBEB_LAYOUT_3F2_LFE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // anything 8 or more gets the 7.1 treatment!
|
||||||
|
Console.WriteLn("(Cubeb) 7.1 speaker expansion enabled.");
|
||||||
|
ActualReader = std::make_unique<ConvertedSampleReader<Stereo71Out16>>(&writtenSoFar);
|
||||||
|
channels = 8; // we do not support 7.2 or more, downgrade to 7.1
|
||||||
|
layout = CUBEB_LAYOUT_3F4_LFE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cubeb_stream_params params = {};
|
||||||
|
params.format = CUBEB_SAMPLE_S16LE;
|
||||||
|
params.rate = SampleRate;
|
||||||
|
params.channels = channels;
|
||||||
|
params.layout = layout;
|
||||||
|
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||||
|
|
||||||
|
const u32 requested_latency_frames = static_cast<u32>((m_SuggestedLatencyMS * static_cast<u32>(SampleRate)) / 1000u);
|
||||||
|
u32 latency_frames = 0;
|
||||||
|
rv = cubeb_get_min_latency(m_context, ¶ms, &latency_frames);
|
||||||
|
if (rv == CUBEB_ERROR_NOT_SUPPORTED)
|
||||||
|
{
|
||||||
|
Console.WriteLn("(Cubeb) Cubeb backend does not support latency queries, using latency of %d ms (%u frames).",
|
||||||
|
m_SuggestedLatencyMS, requested_latency_frames);
|
||||||
|
latency_frames = requested_latency_frames;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (rv != CUBEB_OK)
|
||||||
|
{
|
||||||
|
Console.Error("Could not get minimum latency: %d", rv);
|
||||||
|
DestroyContextAndStream();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float minimum_latency_ms = static_cast<float>(latency_frames * 1000u) / static_cast<float>(SampleRate);
|
||||||
|
Console.WriteLn("(Cubeb) Minimum latency: %.2f ms (%u audio frames)", minimum_latency_ms, latency_frames);
|
||||||
|
if (!m_SuggestedLatencyMinimal)
|
||||||
|
{
|
||||||
|
if (latency_frames > requested_latency_frames)
|
||||||
|
{
|
||||||
|
Console.Warning("(Cubeb) Minimum latency is above requested latency: %u vs %u, adjusting to compensate.",
|
||||||
|
latency_frames, requested_latency_frames);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
latency_frames = requested_latency_frames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char stream_name[32];
|
||||||
|
std::snprintf(stream_name, sizeof(stream_name), "%p", this);
|
||||||
|
|
||||||
|
rv = cubeb_stream_init(m_context, &stream, stream_name, nullptr, nullptr, nullptr, ¶ms,
|
||||||
|
latency_frames, &Cubeb::DataCallback, &Cubeb::StateCallback, this);
|
||||||
|
if (rv != CUBEB_OK)
|
||||||
|
{
|
||||||
|
Console.Error("Could not create stream: %d", rv);
|
||||||
|
DestroyContextAndStream();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = cubeb_stream_start(stream);
|
||||||
|
if (rv != CUBEB_OK)
|
||||||
|
{
|
||||||
|
Console.Error("Could not start stream: %d", rv);
|
||||||
|
DestroyContextAndStream();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
DestroyContextAndStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StateCallback(cubeb_stream* stream, void* user_ptr, cubeb_state state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static long DataCallback(cubeb_stream* stm, void* user_ptr, const void* input_buffer, void* output_buffer, long nframes)
|
||||||
|
{
|
||||||
|
static_cast<Cubeb*>(user_ptr)->ActualReader->ReadSamples(output_buffer, nframes);
|
||||||
|
return nframes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Configure(uptr parent) override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 Test() const override
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetEmptySampleCount() override
|
||||||
|
{
|
||||||
|
u64 pos;
|
||||||
|
if (cubeb_stream_get_position(stream, &pos) != CUBEB_OK)
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
const int playedSinceLastTime = (writtenSoFar - writtenLastTime) + (pos - positionLastTime);
|
||||||
|
writtenLastTime = writtenSoFar;
|
||||||
|
positionLastTime = pos;
|
||||||
|
return playedSinceLastTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* GetIdent() const override
|
||||||
|
{
|
||||||
|
return L"cubeb";
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* GetLongName() const override
|
||||||
|
{
|
||||||
|
return L"Cubeb (Cross-platform)";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadSettings() override
|
||||||
|
{
|
||||||
|
m_SuggestedLatencyMinimal = CfgReadBool(L"Cubeb", L"MinimalSuggestedLatency", false);
|
||||||
|
m_SuggestedLatencyMS = std::clamp(CfgReadInt(L"Cubeb", L"ManualSuggestedLatencyMS", MINIMUM_LATENCY_MS), MINIMUM_LATENCY_MS, MAXIMUM_LATENCY_MS);
|
||||||
|
|
||||||
|
// TODO: Once the config stuff gets merged, drop the wxString here.
|
||||||
|
wxString backend;
|
||||||
|
CfgReadStr(L"Cubeb", L"BackendName", backend, L"");
|
||||||
|
m_Backend = StringUtil::wxStringToUTF8String(backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetApiSettings(wxString api) override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteSettings() const override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static Cubeb s_Cubeb;
|
||||||
|
SndOutModule* CubebOut = &s_Cubeb;
|
|
@ -41,13 +41,14 @@
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libpng;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libpng;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\glad\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\glad\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\cubeb\cubeb\include;$(SolutionDir)3rdparty\cubeb\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<ExceptionHandling>Async</ExceptionHandling>
|
<ExceptionHandling>Async</ExceptionHandling>
|
||||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
||||||
<ForcedIncludeFiles>PrecompiledHeader.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
|
<ForcedIncludeFiles>PrecompiledHeader.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
|
||||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||||
<AdditionalOptions>/Zc:externConstexpr %(AdditionalOptions)</AdditionalOptions>
|
<AdditionalOptions>/Zc:externConstexpr %(AdditionalOptions)</AdditionalOptions>
|
||||||
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;LZMA_API_STATIC;BUILD_DX=1;SPU2X_PORTAUDIO;DIRECTINPUT_VERSION=0x0800;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;LZMA_API_STATIC;BUILD_DX=1;SPU2X_CUBEB;SPU2X_PORTAUDIO;DIRECTINPUT_VERSION=0x0800;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Debug))">PCSX2_DEBUG;PCSX2_DEVBUILD;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="$(Configuration.Contains(Debug))">PCSX2_DEBUG;PCSX2_DEVBUILD;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Devel))">PCSX2_DEVEL;PCSX2_DEVBUILD;NDEBUG;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="$(Configuration.Contains(Devel))">PCSX2_DEVEL;PCSX2_DEVBUILD;NDEBUG;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Release))">NDEBUG;_SECURE_SCL_=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="$(Configuration.Contains(Release))">NDEBUG;_SECURE_SCL_=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
@ -357,6 +358,7 @@
|
||||||
<ClCompile Include="SPU2\DplIIdecoder.cpp" />
|
<ClCompile Include="SPU2\DplIIdecoder.cpp" />
|
||||||
<ClCompile Include="SPU2\debug.cpp" />
|
<ClCompile Include="SPU2\debug.cpp" />
|
||||||
<ClCompile Include="SPU2\RegLog.cpp" />
|
<ClCompile Include="SPU2\RegLog.cpp" />
|
||||||
|
<ClCompile Include="SPU2\SndOut_Cubeb.cpp" />
|
||||||
<ClCompile Include="SPU2\SndOut_Portaudio.cpp" />
|
<ClCompile Include="SPU2\SndOut_Portaudio.cpp" />
|
||||||
<ClCompile Include="SPU2\wavedump_wav.cpp" />
|
<ClCompile Include="SPU2\wavedump_wav.cpp" />
|
||||||
<ClCompile Include="SPU2\SndOut.cpp" />
|
<ClCompile Include="SPU2\SndOut.cpp" />
|
||||||
|
@ -1157,6 +1159,9 @@
|
||||||
<ProjectReference Include="$(SolutionDir)3rdparty\jpgd\jpgd.vcxproj">
|
<ProjectReference Include="$(SolutionDir)3rdparty\jpgd\jpgd.vcxproj">
|
||||||
<Project>{ed2f21fd-0a36-4a8f-9b90-e7d92a2acb63}</Project>
|
<Project>{ed2f21fd-0a36-4a8f-9b90-e7d92a2acb63}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="$(SolutionDir)3rdparty\cubeb\cubeb.vcxproj">
|
||||||
|
<Project>{bf74c473-dc04-44b3-92e8-4145f4e77342}</Project>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\common\common.vcxproj">
|
<ProjectReference Include="..\common\common.vcxproj">
|
||||||
<Project>{4639972e-424e-4e13-8b07-ca403c481346}</Project>
|
<Project>{4639972e-424e-4e13-8b07-ca403c481346}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
|
|
@ -1647,6 +1647,9 @@
|
||||||
<ClCompile Include="PerformanceMetrics.cpp">
|
<ClCompile Include="PerformanceMetrics.cpp">
|
||||||
<Filter>System</Filter>
|
<Filter>System</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="SPU2\SndOut_Cubeb.cpp">
|
||||||
|
<Filter>System\Ps2\SPU2</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Patch.h">
|
<ClInclude Include="Patch.h">
|
||||||
|
|
Loading…
Reference in New Issue