mirror of https://github.com/PCSX2/pcsx2.git
Merge pull request #1253 from turtleli/spu2x-xaudio
spu2-x:windows:Use XAudio2.8+ for Windows 8 and later
This commit is contained in:
commit
e6bf77d148
|
@ -160,6 +160,20 @@ EXPORT_C_(s32) SPU2test()
|
||||||
{
|
{
|
||||||
if( !CheckSSE() ) return -1;
|
if( !CheckSSE() ) return -1;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (IsWindows8OrGreater())
|
||||||
|
{
|
||||||
|
for (int n = 0; mods[n] != nullptr; ++n)
|
||||||
|
{
|
||||||
|
if (mods[n] == XAudio2_27_Out)
|
||||||
|
{
|
||||||
|
mods[n] = XAudio2Out;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
ReadSettings();
|
ReadSettings();
|
||||||
if( SndBuffer::Test() != 0 )
|
if( SndBuffer::Test() != 0 )
|
||||||
{
|
{
|
||||||
|
|
|
@ -87,7 +87,7 @@ SndOutModule* mods[]=
|
||||||
{
|
{
|
||||||
&NullOut,
|
&NullOut,
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
XAudio2Out,
|
XAudio2_27_Out,
|
||||||
DSoundOut,
|
DSoundOut,
|
||||||
WaveOut,
|
WaveOut,
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -670,6 +670,7 @@ public:
|
||||||
//internal
|
//internal
|
||||||
extern SndOutModule* WaveOut;
|
extern SndOutModule* WaveOut;
|
||||||
extern SndOutModule* DSoundOut;
|
extern SndOutModule* DSoundOut;
|
||||||
|
extern SndOutModule* XAudio2_27_Out;
|
||||||
extern SndOutModule* XAudio2Out;
|
extern SndOutModule* XAudio2Out;
|
||||||
#endif
|
#endif
|
||||||
extern SndOutModule* PortaudioOut;
|
extern SndOutModule* PortaudioOut;
|
||||||
|
|
|
@ -225,6 +225,18 @@ EXPORT_C_(void) s2r_replay(HWND hwnd, HINSTANCE hinst, LPSTR filename, int nCmdS
|
||||||
|
|
||||||
conprintf("Playing %s file on %x...",filename,hwnd);
|
conprintf("Playing %s file on %x...",filename,hwnd);
|
||||||
|
|
||||||
|
if (IsWindows8OrGreater())
|
||||||
|
{
|
||||||
|
for (int n = 0; mods[n] != nullptr; ++n)
|
||||||
|
{
|
||||||
|
if (mods[n] == XAudio2_27_Out)
|
||||||
|
{
|
||||||
|
mods[n] = XAudio2Out;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// load file
|
// load file
|
||||||
|
|
|
@ -82,20 +82,6 @@ int numSpeakers = 0;
|
||||||
int dplLevel = 0;
|
int dplLevel = 0;
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
BOOL IsVistaOrGreater2() // workaround for XP toolset missing VersionHelpers.h
|
|
||||||
{
|
|
||||||
OSVERSIONINFOEX osvi;
|
|
||||||
DWORDLONG dwlConditionMask = 0;
|
|
||||||
int op = VER_GREATER_EQUAL;
|
|
||||||
|
|
||||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
|
||||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
||||||
osvi.dwMajorVersion = 6;
|
|
||||||
|
|
||||||
VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op);
|
|
||||||
|
|
||||||
return VerifyVersionInfo(&osvi, VER_MAJORVERSION, dwlConditionMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
|
@ -140,12 +126,11 @@ void ReadSettings()
|
||||||
// portaudio occasionally has issues selecting the proper default audio device.
|
// portaudio occasionally has issues selecting the proper default audio device.
|
||||||
// let's use xaudio2 until this is sorted (rama)
|
// let's use xaudio2 until this is sorted (rama)
|
||||||
|
|
||||||
// if ( IsVistaOrGreater2() ) { // XA2 for WinXP, morder modern gets Portaudio
|
// CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, PortaudioOut->GetIdent());
|
||||||
// CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, PortaudioOut->GetIdent());
|
if (IsWindows8OrGreater())
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, XAudio2Out->GetIdent());
|
CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, XAudio2Out->GetIdent());
|
||||||
// }
|
else
|
||||||
|
CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, XAudio2_27_Out->GetIdent());
|
||||||
|
|
||||||
// find the driver index of this module:
|
// find the driver index of this module:
|
||||||
OutputModule = FindOutputModuleById( omodid );
|
OutputModule = FindOutputModuleById( omodid );
|
||||||
|
|
|
@ -17,26 +17,26 @@
|
||||||
|
|
||||||
#include "Global.h"
|
#include "Global.h"
|
||||||
|
|
||||||
#define _WIN32_DCOM
|
|
||||||
#include "Dialogs.h"
|
#include "Dialogs.h"
|
||||||
|
|
||||||
#include <xaudio2.h>
|
|
||||||
#include <atlcomcli.h>
|
#include <atlcomcli.h>
|
||||||
|
|
||||||
|
#undef _WIN32_WINNT
|
||||||
|
#define _WIN32_WINNT 0x0602
|
||||||
|
#include <xaudio2.h>
|
||||||
|
|
||||||
namespace Exception
|
namespace Exception
|
||||||
{
|
{
|
||||||
class XAudio2Error : public std::runtime_error
|
class XAudio2Error : public std::runtime_error
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
static const char* SomeKindaErrorString( HRESULT hr )
|
static const char* SomeKindaErrorString(HRESULT hr)
|
||||||
{
|
{
|
||||||
switch( hr )
|
switch (hr) {
|
||||||
{
|
case XAUDIO2_E_INVALID_CALL:
|
||||||
case XAUDIO2_E_INVALID_CALL:
|
return "Invalid call for the XA2 object state.";
|
||||||
return "Invalid call for the XA2 object state.";
|
|
||||||
|
|
||||||
case XAUDIO2_E_DEVICE_INVALIDATED:
|
case XAUDIO2_E_DEVICE_INVALIDATED:
|
||||||
return "Device is unavailable, unplugged, unsupported, or has been consumed by The Nothing.";
|
return "Device is unavailable, unplugged, unsupported, or has been consumed by The Nothing.";
|
||||||
}
|
}
|
||||||
return "Unknown error code!";
|
return "Unknown error code!";
|
||||||
}
|
}
|
||||||
|
@ -51,21 +51,21 @@ namespace Exception
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~XAudio2Error() throw() {}
|
virtual ~XAudio2Error() throw() {}
|
||||||
XAudio2Error( const HRESULT result, const std::string& msg ) :
|
XAudio2Error(const HRESULT result, const std::string& msg) :
|
||||||
runtime_error( msg ),
|
runtime_error(msg),
|
||||||
ErrorCode( result ),
|
ErrorCode(result),
|
||||||
m_Message()
|
m_Message()
|
||||||
{
|
{
|
||||||
char omg[1024];
|
char omg[1024];
|
||||||
sprintf_s( omg, "%s (code 0x%x)\n\n%s", what(), ErrorCode, SomeKindaErrorString( ErrorCode ) );
|
sprintf_s(omg, "%s (code 0x%x)\n\n%s", what(), ErrorCode, SomeKindaErrorString(ErrorCode));
|
||||||
m_Message = omg;
|
m_Message = omg;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static const double SndOutNormalizer = (double)(1UL<<(SndOutVolumeShift+16));
|
static const double SndOutNormalizer = (double)(1UL << (SndOutVolumeShift + 16));
|
||||||
|
|
||||||
class XAudio2Mod: public SndOutModule
|
class XAudio2Mod : public SndOutModule
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static const int PacketsPerBuffer = 8;
|
static const int PacketsPerBuffer = 8;
|
||||||
|
@ -88,91 +88,89 @@ private:
|
||||||
int GetEmptySampleCount()
|
int GetEmptySampleCount()
|
||||||
{
|
{
|
||||||
XAUDIO2_VOICE_STATE state;
|
XAUDIO2_VOICE_STATE state;
|
||||||
pSourceVoice->GetState( &state );
|
pSourceVoice->GetState(&state);
|
||||||
return state.SamplesPlayed & (m_BufferSize-1);
|
return state.SamplesPlayed & (m_BufferSize - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~BaseStreamingVoice()
|
virtual ~BaseStreamingVoice()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseStreamingVoice( uint numChannels ) :
|
BaseStreamingVoice(uint numChannels) :
|
||||||
m_nBuffers( Config_XAudio2.NumBuffers ),
|
m_nBuffers(Config_XAudio2.NumBuffers),
|
||||||
m_nChannels( numChannels ),
|
m_nChannels(numChannels),
|
||||||
m_BufferSize( SndOutPacketSize * m_nChannels * PacketsPerBuffer ),
|
m_BufferSize(SndOutPacketSize * m_nChannels * PacketsPerBuffer),
|
||||||
m_BufferSizeBytes( m_BufferSize * sizeof(s16) )
|
m_BufferSizeBytes(m_BufferSize * sizeof(s16))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Init( IXAudio2* pXAudio2 ) = 0;
|
virtual void Init(IXAudio2* pXAudio2) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Several things must be initialized separate of the constructor, due to the fact that
|
// Several things must be initialized separate of the constructor, due to the fact that
|
||||||
// virtual calls can't be made from the constructor's context.
|
// virtual calls can't be made from the constructor's context.
|
||||||
void _init( IXAudio2* pXAudio2, uint chanConfig )
|
void _init(IXAudio2* pXAudio2, uint chanConfig)
|
||||||
{
|
{
|
||||||
WAVEFORMATEXTENSIBLE wfx;
|
WAVEFORMATEXTENSIBLE wfx;
|
||||||
|
|
||||||
memset(&wfx, 0, sizeof(WAVEFORMATEXTENSIBLE));
|
memset(&wfx, 0, sizeof(WAVEFORMATEXTENSIBLE));
|
||||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||||
wfx.Format.nSamplesPerSec = SampleRate;
|
wfx.Format.nSamplesPerSec = SampleRate;
|
||||||
wfx.Format.nChannels = m_nChannels;
|
wfx.Format.nChannels = m_nChannels;
|
||||||
wfx.Format.wBitsPerSample = 16;
|
wfx.Format.wBitsPerSample = 16;
|
||||||
wfx.Format.nBlockAlign = wfx.Format.nChannels*wfx.Format.wBitsPerSample/8;
|
wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
|
||||||
wfx.Format.nAvgBytesPerSec = SampleRate * wfx.Format.nBlockAlign;
|
wfx.Format.nAvgBytesPerSec = SampleRate * wfx.Format.nBlockAlign;
|
||||||
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
|
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||||
wfx.Samples.wValidBitsPerSample = 16;
|
wfx.Samples.wValidBitsPerSample = 16;
|
||||||
wfx.dwChannelMask = chanConfig;
|
wfx.dwChannelMask = chanConfig;
|
||||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Create an XAudio2 voice to stream this wave
|
// Create an XAudio2 voice to stream this wave
|
||||||
//
|
//
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
if( FAILED(hr = pXAudio2->CreateSourceVoice( &pSourceVoice, (WAVEFORMATEX*)&wfx,
|
if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&wfx,
|
||||||
XAUDIO2_VOICE_NOSRC, 1.0f, this ) ) )
|
XAUDIO2_VOICE_NOSRC, 1.0f, this))) {
|
||||||
{
|
throw Exception::XAudio2Error(hr, "XAudio2 CreateSourceVoice failure.");
|
||||||
throw Exception::XAudio2Error( hr, "XAudio2 CreateSourceVoice failure." );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InitializeCriticalSection( &cs );
|
InitializeCriticalSection(&cs);
|
||||||
EnterCriticalSection( &cs );
|
EnterCriticalSection(&cs);
|
||||||
|
|
||||||
pSourceVoice->FlushSourceBuffers();
|
pSourceVoice->FlushSourceBuffers();
|
||||||
pSourceVoice->Start( 0, 0 );
|
pSourceVoice->Start(0, 0);
|
||||||
|
|
||||||
qbuffer = new s16[m_nBuffers * m_BufferSize];
|
qbuffer = new s16[m_nBuffers * m_BufferSize];
|
||||||
ZeroMemory( qbuffer, m_BufferSizeBytes * m_nBuffers );
|
ZeroMemory(qbuffer, m_BufferSizeBytes * m_nBuffers);
|
||||||
|
|
||||||
// Start some buffers.
|
// Start some buffers.
|
||||||
|
|
||||||
for( uint i=0; i<m_nBuffers; i++ )
|
for (uint i = 0; i < m_nBuffers; i++) {
|
||||||
{
|
XAUDIO2_BUFFER buf = { 0 };
|
||||||
XAUDIO2_BUFFER buf = {0};
|
|
||||||
buf.AudioBytes = m_BufferSizeBytes;
|
buf.AudioBytes = m_BufferSizeBytes;
|
||||||
buf.pContext = &qbuffer[i*m_BufferSize];
|
buf.pContext = &qbuffer[i*m_BufferSize];
|
||||||
buf.pAudioData = (BYTE*)buf.pContext;
|
buf.pAudioData = (BYTE*)buf.pContext;
|
||||||
pSourceVoice->SubmitSourceBuffer( &buf );
|
pSourceVoice->SubmitSourceBuffer(&buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
LeaveCriticalSection( &cs );
|
LeaveCriticalSection(&cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassStart) () {}
|
STDMETHOD_(void, OnVoiceProcessingPassStart) () {}
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) { };
|
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
||||||
STDMETHOD_(void, OnStreamEnd) () {}
|
STDMETHOD_(void, OnStreamEnd) () {}
|
||||||
STDMETHOD_(void, OnBufferStart) ( void* ) {}
|
STDMETHOD_(void, OnBufferStart) (void*) {}
|
||||||
STDMETHOD_(void, OnLoopEnd) ( void* ) {}
|
STDMETHOD_(void, OnLoopEnd) (void*) {}
|
||||||
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) { };
|
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
class StreamingVoice : public BaseStreamingVoice
|
class StreamingVoice : public BaseStreamingVoice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StreamingVoice( IXAudio2* pXAudio2 ) :
|
StreamingVoice(IXAudio2* pXAudio2) :
|
||||||
BaseStreamingVoice( sizeof(T) / sizeof( s16 ) )
|
BaseStreamingVoice(sizeof(T) / sizeof(s16))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,58 +181,56 @@ private:
|
||||||
killMe->FlushSourceBuffers();
|
killMe->FlushSourceBuffers();
|
||||||
killMe->DestroyVoice();
|
killMe->DestroyVoice();
|
||||||
|
|
||||||
EnterCriticalSection( &cs );
|
EnterCriticalSection(&cs);
|
||||||
safe_delete_array( qbuffer );
|
safe_delete_array(qbuffer);
|
||||||
LeaveCriticalSection( &cs );
|
LeaveCriticalSection(&cs);
|
||||||
DeleteCriticalSection( &cs );
|
DeleteCriticalSection(&cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init( IXAudio2* pXAudio2 )
|
void Init(IXAudio2* pXAudio2)
|
||||||
{
|
{
|
||||||
int chanMask = 0;
|
int chanMask = 0;
|
||||||
switch(m_nChannels)
|
switch (m_nChannels) {
|
||||||
{
|
case 1: chanMask |= SPEAKER_FRONT_CENTER; break;
|
||||||
case 1: chanMask |= SPEAKER_FRONT_CENTER; break;
|
case 2: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
|
||||||
case 2: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
|
case 3: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY; break;
|
||||||
case 3: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY; break;
|
case 4: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
|
||||||
case 4: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
|
case 5: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
|
||||||
case 5: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
|
case 6: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY; break;
|
||||||
case 6: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY; break;
|
case 8: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT | SPEAKER_LOW_FREQUENCY; break;
|
||||||
case 8: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT | SPEAKER_LOW_FREQUENCY; break;
|
|
||||||
}
|
}
|
||||||
_init( pXAudio2, chanMask );
|
_init(pXAudio2, chanMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
STDMETHOD_(void, OnBufferEnd) ( void* context )
|
STDMETHOD_(void, OnBufferEnd) (void* context)
|
||||||
{
|
{
|
||||||
EnterCriticalSection( &cs );
|
EnterCriticalSection(&cs);
|
||||||
|
|
||||||
// All of these checks are necessary because XAudio2 is wonky shizat.
|
// All of these checks are necessary because XAudio2 is wonky shizat.
|
||||||
if( pSourceVoice == NULL || context == NULL )
|
if (pSourceVoice == NULL || context == NULL) {
|
||||||
{
|
LeaveCriticalSection(&cs);
|
||||||
LeaveCriticalSection( &cs );
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
T* qb = (T*)context;
|
T* qb = (T*)context;
|
||||||
|
|
||||||
for(int p=0; p<PacketsPerBuffer; p++, qb+=SndOutPacketSize )
|
for (int p = 0; p < PacketsPerBuffer; p++, qb += SndOutPacketSize)
|
||||||
SndBuffer::ReadSamples( qb );
|
SndBuffer::ReadSamples(qb);
|
||||||
|
|
||||||
XAUDIO2_BUFFER buf = {0};
|
XAUDIO2_BUFFER buf = { 0 };
|
||||||
buf.AudioBytes = m_BufferSizeBytes;
|
buf.AudioBytes = m_BufferSizeBytes;
|
||||||
buf.pAudioData = (BYTE*)context;
|
buf.pAudioData = (BYTE*)context;
|
||||||
buf.pContext = context;
|
buf.pContext = context;
|
||||||
|
|
||||||
pSourceVoice->SubmitSourceBuffer( &buf );
|
pSourceVoice->SubmitSourceBuffer(&buf);
|
||||||
LeaveCriticalSection( &cs );
|
LeaveCriticalSection(&cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
#if _WIN32_WINNT < 0x602
|
|
||||||
HMODULE xAudio2DLL;
|
HMODULE xAudio2DLL;
|
||||||
#endif
|
decltype(&XAudio2Create) pXAudio2Create;
|
||||||
CComPtr<IXAudio2> pXAudio2;
|
CComPtr<IXAudio2> pXAudio2;
|
||||||
IXAudio2MasteringVoice* pMasteringVoice;
|
IXAudio2MasteringVoice* pMasteringVoice;
|
||||||
BaseStreamingVoice* voiceContext;
|
BaseStreamingVoice* voiceContext;
|
||||||
|
@ -245,124 +241,96 @@ public:
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
jASSUME( pXAudio2 == NULL );
|
jASSUME(pXAudio2 == NULL);
|
||||||
#if _WIN32_WINNT < 0x602
|
|
||||||
// On some systems XAudio2.7 can unload itself and cause PCSX2 to crash.
|
xAudio2DLL = LoadLibraryEx(XAUDIO2_DLL, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||||
// Maintain an extra library reference so it can't do so. Does not
|
if (xAudio2DLL == nullptr)
|
||||||
// affect XAudio 2.8+, but that's Win8+. See
|
throw std::runtime_error("Could not load " XAUDIO2_DLL_A ". Error code:" + std::to_string(GetLastError()));
|
||||||
// http://blogs.msdn.com/b/chuckw/archive/2015/10/09/known-issues-xaudio-2-7.aspx
|
|
||||||
#ifdef _DEBUG
|
pXAudio2Create = reinterpret_cast<decltype(&XAudio2Create)>(GetProcAddress(xAudio2DLL, "XAudio2Create"));
|
||||||
xAudio2DLL = LoadLibrary(L"XAudioD2_7.dll");
|
if (pXAudio2Create == nullptr)
|
||||||
#else
|
throw std::runtime_error("XAudio2Create not found. Error code: " + std::to_string(GetLastError()));
|
||||||
xAudio2DLL = LoadLibrary(L"XAudio2_7.dll");
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Initialize XAudio2
|
// Initialize XAudio2
|
||||||
//
|
//
|
||||||
CoInitializeEx( NULL, COINIT_MULTITHREADED );
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||||
|
|
||||||
UINT32 flags = 0;
|
try {
|
||||||
#if _WIN32_WINNT < 0x602
|
if (FAILED(hr = pXAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
||||||
if( IsDebugBuild )
|
throw Exception::XAudio2Error(hr, "Failed to init XAudio2 engine. Error Details:");
|
||||||
flags |= XAUDIO2_DEBUG_ENGINE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if ( FAILED(hr = XAudio2Create( &pXAudio2, flags ) ) )
|
|
||||||
throw Exception::XAudio2Error( hr,
|
|
||||||
"Failed to init XAudio2 engine. XA2 may not be available on your system.\n"
|
|
||||||
"Ensure that you have the latest DirectX runtimes installed, or use \n"
|
|
||||||
"DirectX / WaveOut drivers instead. Error Details:"
|
|
||||||
);
|
|
||||||
#if _WIN32_WINNT < 0x602
|
|
||||||
XAUDIO2_DEVICE_DETAILS deviceDetails;
|
|
||||||
pXAudio2->GetDeviceDetails( 0, &deviceDetails );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Stereo Expansion was planned to grab the currently configured number of
|
// Stereo Expansion was planned to grab the currently configured number of
|
||||||
// Speakers from Windows's audio config.
|
// Speakers from Windows's audio config.
|
||||||
// This doesn't always work though, so let it be a user configurable option.
|
// This doesn't always work though, so let it be a user configurable option.
|
||||||
|
|
||||||
int speakers;
|
int speakers;
|
||||||
switch(numSpeakers) // speakers = (numSpeakers + 1) *2; ?
|
switch (numSpeakers) // speakers = (numSpeakers + 1) *2; ?
|
||||||
{
|
{
|
||||||
case 0: speakers = 2; break; // Stereo
|
case 0: speakers = 2; break; // Stereo
|
||||||
case 1: speakers = 4; break; // Quadrafonic
|
case 1: speakers = 4; break; // Quadrafonic
|
||||||
case 2: speakers = 6; break; // Surround 5.1
|
case 2: speakers = 6; break; // Surround 5.1
|
||||||
case 3: speakers = 8; break; // Surround 7.1
|
case 3: speakers = 8; break; // Surround 7.1
|
||||||
default: speakers = 2;
|
default: speakers = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _WIN32_WINNT < 0x602
|
|
||||||
// Any windows driver should support stereo at the software level, I should think!
|
|
||||||
jASSUME( deviceDetails.OutputFormat.Format.nChannels > 1 );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Create a mastering voice
|
// Create a mastering voice
|
||||||
//
|
//
|
||||||
if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasteringVoice, speakers, SampleRate ) ) )
|
if (FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasteringVoice, speakers, SampleRate))) {
|
||||||
{
|
SysMessage("Failed creating mastering voice: %#X\n", hr);
|
||||||
SysMessage( "Failed creating mastering voice: %#X\n", hr );
|
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch( speakers )
|
switch (speakers) {
|
||||||
{
|
case 2:
|
||||||
|
ConLog("* SPU2 > Using normal 2 speaker stereo output.\n");
|
||||||
|
voiceContext = new StreamingVoice<StereoOut16>(pXAudio2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
ConLog("* SPU2 > 2.1 speaker expansion enabled.\n");
|
||||||
|
voiceContext = new StreamingVoice<Stereo21Out16>(pXAudio2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
ConLog("* SPU2 > 4 speaker expansion enabled [quadraphenia]\n");
|
||||||
|
voiceContext = new StreamingVoice<Stereo40Out16>(pXAudio2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
ConLog("* SPU2 > 4.1 speaker expansion enabled.\n");
|
||||||
|
voiceContext = new StreamingVoice<Stereo41Out16>(pXAudio2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
switch (dplLevel) {
|
||||||
|
case 0:
|
||||||
|
ConLog("* SPU2 > 5.1 speaker expansion enabled.\n");
|
||||||
|
voiceContext = new StreamingVoice<Stereo51Out16>(pXAudio2); //"normal" stereo upmix
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ConLog("* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled.\n");
|
||||||
|
voiceContext = new StreamingVoice<Stereo51Out16Dpl>(pXAudio2); // basic Dpl decoder without rear stereo balancing
|
||||||
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
ConLog( "* SPU2 > Using normal 2 speaker stereo output.\n" );
|
ConLog("* SPU2 > 5.1 speaker expansion with experimental ProLogicII dematrixing enabled.\n");
|
||||||
voiceContext = new StreamingVoice<StereoOut16>( pXAudio2 );
|
voiceContext = new StreamingVoice<Stereo51Out16DplII>(pXAudio2); //gigas PLII
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
default: // anything 8 or more gets the 7.1 treatment!
|
||||||
ConLog( "* SPU2 > 2.1 speaker expansion enabled.\n" );
|
ConLog("* SPU2 > 7.1 speaker expansion enabled.\n");
|
||||||
voiceContext = new StreamingVoice<Stereo21Out16>( pXAudio2 );
|
voiceContext = new StreamingVoice<Stereo51Out16>(pXAudio2);
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
ConLog( "* SPU2 > 4 speaker expansion enabled [quadraphenia]\n" );
|
|
||||||
voiceContext = new StreamingVoice<Stereo40Out16>( pXAudio2 );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
|
||||||
ConLog( "* SPU2 > 4.1 speaker expansion enabled.\n" );
|
|
||||||
voiceContext = new StreamingVoice<Stereo41Out16>( pXAudio2 );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6:
|
|
||||||
case 7:
|
|
||||||
switch(dplLevel)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
ConLog( "* SPU2 > 5.1 speaker expansion enabled.\n" );
|
|
||||||
voiceContext = new StreamingVoice<Stereo51Out16>( pXAudio2 ); //"normal" stereo upmix
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
ConLog( "* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled.\n" );
|
|
||||||
voiceContext = new StreamingVoice<Stereo51Out16Dpl>( pXAudio2 ); // basic Dpl decoder without rear stereo balancing
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
ConLog( "* SPU2 > 5.1 speaker expansion with experimental ProLogicII dematrixing enabled.\n" );
|
|
||||||
voiceContext = new StreamingVoice<Stereo51Out16DplII>( pXAudio2 ); //gigas PLII
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: // anything 8 or more gets the 7.1 treatment!
|
|
||||||
ConLog( "* SPU2 > 7.1 speaker expansion enabled.\n" );
|
|
||||||
voiceContext = new StreamingVoice<Stereo51Out16>( pXAudio2 );
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
voiceContext->Init( pXAudio2 );
|
voiceContext->Init(pXAudio2);
|
||||||
}
|
} catch (Exception::XAudio2Error& ex) {
|
||||||
catch( Exception::XAudio2Error& ex )
|
SysMessage(ex.CMessage());
|
||||||
{
|
|
||||||
SysMessage( ex.CMessage() );
|
|
||||||
pXAudio2.Release();
|
pXAudio2.Release();
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -373,20 +341,11 @@ public:
|
||||||
|
|
||||||
void Close()
|
void Close()
|
||||||
{
|
{
|
||||||
// Clean up?
|
safe_delete(voiceContext);
|
||||||
// All XAudio2 interfaces are released when the engine is destroyed,
|
|
||||||
// but being tidy never hurt.
|
|
||||||
|
|
||||||
// Actually it can hurt. As of DXSDK Aug 2008, doing a full cleanup causes
|
|
||||||
// XA2 on Vista to crash. Even if you copy/paste code directly from Microsoft.
|
|
||||||
// But doing no cleanup at all causes XA2 under XP to crash. So after much trial
|
|
||||||
// and error we found a happy compromise as follows:
|
|
||||||
|
|
||||||
safe_delete( voiceContext );
|
|
||||||
|
|
||||||
voiceContext = NULL;
|
voiceContext = NULL;
|
||||||
|
|
||||||
if( pMasteringVoice != NULL )
|
if (pMasteringVoice != NULL)
|
||||||
pMasteringVoice->DestroyVoice();
|
pMasteringVoice->DestroyVoice();
|
||||||
|
|
||||||
pMasteringVoice = NULL;
|
pMasteringVoice = NULL;
|
||||||
|
@ -394,18 +353,17 @@ public:
|
||||||
pXAudio2.Release();
|
pXAudio2.Release();
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
|
|
||||||
#if _WIN32_WINNT < 0x602
|
|
||||||
if (xAudio2DLL) {
|
if (xAudio2DLL) {
|
||||||
FreeLibrary(xAudio2DLL);
|
FreeLibrary(xAudio2DLL);
|
||||||
xAudio2DLL = nullptr;
|
xAudio2DLL = nullptr;
|
||||||
|
pXAudio2Create = nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Configure(uptr parent)
|
virtual void Configure(uptr parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 Test() const
|
s32 Test() const
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -413,7 +371,7 @@ public:
|
||||||
|
|
||||||
int GetEmptySampleCount()
|
int GetEmptySampleCount()
|
||||||
{
|
{
|
||||||
if( voiceContext == NULL ) return 0;
|
if (voiceContext == NULL) return 0;
|
||||||
return voiceContext->GetEmptySampleCount();
|
return voiceContext->GetEmptySampleCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,434 @@
|
||||||
|
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||||
|
* Developed and maintained by the Pcsx2 Development Team.
|
||||||
|
*
|
||||||
|
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||||
|
*
|
||||||
|
* SPU2-X 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.
|
||||||
|
*
|
||||||
|
* SPU2-X 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Global.h"
|
||||||
|
|
||||||
|
#include "Dialogs.h"
|
||||||
|
|
||||||
|
#include <xaudio2.h>
|
||||||
|
#include <atlcomcli.h>
|
||||||
|
|
||||||
|
namespace Exception
|
||||||
|
{
|
||||||
|
class XAudio2_27Error : public std::runtime_error
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
static const char* SomeKindaErrorString( HRESULT hr )
|
||||||
|
{
|
||||||
|
switch( hr )
|
||||||
|
{
|
||||||
|
case XAUDIO2_E_INVALID_CALL:
|
||||||
|
return "Invalid call for the XA2 object state.";
|
||||||
|
|
||||||
|
case XAUDIO2_E_DEVICE_INVALIDATED:
|
||||||
|
return "Device is unavailable, unplugged, unsupported, or has been consumed by The Nothing.";
|
||||||
|
}
|
||||||
|
return "Unknown error code!";
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const HRESULT ErrorCode;
|
||||||
|
std::string m_Message;
|
||||||
|
|
||||||
|
const char* CMessage() const
|
||||||
|
{
|
||||||
|
return m_Message.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~XAudio2_27Error() throw() {}
|
||||||
|
XAudio2_27Error( const HRESULT result, const std::string& msg ) :
|
||||||
|
runtime_error( msg ),
|
||||||
|
ErrorCode( result ),
|
||||||
|
m_Message()
|
||||||
|
{
|
||||||
|
char omg[1024];
|
||||||
|
sprintf_s( omg, "%s (code 0x%x)\n\n%s", what(), ErrorCode, SomeKindaErrorString( ErrorCode ) );
|
||||||
|
m_Message = omg;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static const double SndOutNormalizer = (double)(1UL<<(SndOutVolumeShift+16));
|
||||||
|
|
||||||
|
class XAudio2_27_Mod: public SndOutModule
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static const int PacketsPerBuffer = 8;
|
||||||
|
static const int MAX_BUFFER_COUNT = 3;
|
||||||
|
|
||||||
|
class BaseStreamingVoice : public IXAudio2VoiceCallback
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
IXAudio2SourceVoice* pSourceVoice;
|
||||||
|
s16* qbuffer;
|
||||||
|
|
||||||
|
const uint m_nBuffers;
|
||||||
|
const uint m_nChannels;
|
||||||
|
const uint m_BufferSize;
|
||||||
|
const uint m_BufferSizeBytes;
|
||||||
|
|
||||||
|
CRITICAL_SECTION cs;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int GetEmptySampleCount()
|
||||||
|
{
|
||||||
|
XAUDIO2_VOICE_STATE state;
|
||||||
|
pSourceVoice->GetState( &state );
|
||||||
|
return state.SamplesPlayed & (m_BufferSize-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~BaseStreamingVoice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseStreamingVoice( uint numChannels ) :
|
||||||
|
m_nBuffers( Config_XAudio2.NumBuffers ),
|
||||||
|
m_nChannels( numChannels ),
|
||||||
|
m_BufferSize( SndOutPacketSize * m_nChannels * PacketsPerBuffer ),
|
||||||
|
m_BufferSizeBytes( m_BufferSize * sizeof(s16) )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Init( IXAudio2* pXAudio2 ) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Several things must be initialized separate of the constructor, due to the fact that
|
||||||
|
// virtual calls can't be made from the constructor's context.
|
||||||
|
void _init( IXAudio2* pXAudio2, uint chanConfig )
|
||||||
|
{
|
||||||
|
WAVEFORMATEXTENSIBLE wfx;
|
||||||
|
|
||||||
|
memset(&wfx, 0, sizeof(WAVEFORMATEXTENSIBLE));
|
||||||
|
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||||
|
wfx.Format.nSamplesPerSec = SampleRate;
|
||||||
|
wfx.Format.nChannels = m_nChannels;
|
||||||
|
wfx.Format.wBitsPerSample = 16;
|
||||||
|
wfx.Format.nBlockAlign = wfx.Format.nChannels*wfx.Format.wBitsPerSample/8;
|
||||||
|
wfx.Format.nAvgBytesPerSec = SampleRate * wfx.Format.nBlockAlign;
|
||||||
|
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
|
||||||
|
wfx.Samples.wValidBitsPerSample = 16;
|
||||||
|
wfx.dwChannelMask = chanConfig;
|
||||||
|
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Create an XAudio2 voice to stream this wave
|
||||||
|
//
|
||||||
|
HRESULT hr;
|
||||||
|
if( FAILED(hr = pXAudio2->CreateSourceVoice( &pSourceVoice, (WAVEFORMATEX*)&wfx,
|
||||||
|
XAUDIO2_VOICE_NOSRC, 1.0f, this ) ) )
|
||||||
|
{
|
||||||
|
throw Exception::XAudio2_27Error( hr, "XAudio2 CreateSourceVoice failure." );
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeCriticalSection( &cs );
|
||||||
|
EnterCriticalSection( &cs );
|
||||||
|
|
||||||
|
pSourceVoice->FlushSourceBuffers();
|
||||||
|
pSourceVoice->Start( 0, 0 );
|
||||||
|
|
||||||
|
qbuffer = new s16[m_nBuffers * m_BufferSize];
|
||||||
|
ZeroMemory( qbuffer, m_BufferSizeBytes * m_nBuffers );
|
||||||
|
|
||||||
|
// Start some buffers.
|
||||||
|
|
||||||
|
for( uint i=0; i<m_nBuffers; i++ )
|
||||||
|
{
|
||||||
|
XAUDIO2_BUFFER buf = {0};
|
||||||
|
buf.AudioBytes = m_BufferSizeBytes;
|
||||||
|
buf.pContext = &qbuffer[i*m_BufferSize];
|
||||||
|
buf.pAudioData = (BYTE*)buf.pContext;
|
||||||
|
pSourceVoice->SubmitSourceBuffer( &buf );
|
||||||
|
}
|
||||||
|
|
||||||
|
LeaveCriticalSection( &cs );
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHOD_(void, OnVoiceProcessingPassStart) () {}
|
||||||
|
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) { };
|
||||||
|
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
||||||
|
STDMETHOD_(void, OnStreamEnd) () {}
|
||||||
|
STDMETHOD_(void, OnBufferStart) ( void* ) {}
|
||||||
|
STDMETHOD_(void, OnLoopEnd) ( void* ) {}
|
||||||
|
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) { };
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class StreamingVoice : public BaseStreamingVoice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StreamingVoice( IXAudio2* pXAudio2 ) :
|
||||||
|
BaseStreamingVoice( sizeof(T) / sizeof( s16 ) )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~StreamingVoice()
|
||||||
|
{
|
||||||
|
IXAudio2SourceVoice* killMe = pSourceVoice;
|
||||||
|
pSourceVoice = NULL;
|
||||||
|
killMe->FlushSourceBuffers();
|
||||||
|
killMe->DestroyVoice();
|
||||||
|
|
||||||
|
EnterCriticalSection( &cs );
|
||||||
|
safe_delete_array( qbuffer );
|
||||||
|
LeaveCriticalSection( &cs );
|
||||||
|
DeleteCriticalSection( &cs );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init( IXAudio2* pXAudio2 )
|
||||||
|
{
|
||||||
|
int chanMask = 0;
|
||||||
|
switch(m_nChannels)
|
||||||
|
{
|
||||||
|
case 1: chanMask |= SPEAKER_FRONT_CENTER; break;
|
||||||
|
case 2: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
|
||||||
|
case 3: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY; break;
|
||||||
|
case 4: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
|
||||||
|
case 5: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
|
||||||
|
case 6: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY; break;
|
||||||
|
case 8: chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT | SPEAKER_LOW_FREQUENCY; break;
|
||||||
|
}
|
||||||
|
_init( pXAudio2, chanMask );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
STDMETHOD_(void, OnBufferEnd) ( void* context )
|
||||||
|
{
|
||||||
|
EnterCriticalSection( &cs );
|
||||||
|
|
||||||
|
// All of these checks are necessary because XAudio2 is wonky shizat.
|
||||||
|
if( pSourceVoice == NULL || context == NULL )
|
||||||
|
{
|
||||||
|
LeaveCriticalSection( &cs );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* qb = (T*)context;
|
||||||
|
|
||||||
|
for(int p=0; p<PacketsPerBuffer; p++, qb+=SndOutPacketSize )
|
||||||
|
SndBuffer::ReadSamples( qb );
|
||||||
|
|
||||||
|
XAUDIO2_BUFFER buf = {0};
|
||||||
|
buf.AudioBytes = m_BufferSizeBytes;
|
||||||
|
buf.pAudioData = (BYTE*)context;
|
||||||
|
buf.pContext = context;
|
||||||
|
|
||||||
|
pSourceVoice->SubmitSourceBuffer( &buf );
|
||||||
|
LeaveCriticalSection( &cs );
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
HMODULE xAudio2DLL;
|
||||||
|
CComPtr<IXAudio2> pXAudio2;
|
||||||
|
IXAudio2MasteringVoice* pMasteringVoice;
|
||||||
|
BaseStreamingVoice* voiceContext;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
s32 Init()
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
jASSUME( pXAudio2 == NULL );
|
||||||
|
|
||||||
|
// On some systems XAudio2.7 can unload itself and cause PCSX2 to crash.
|
||||||
|
// Maintain an extra library reference so it can't do so. Does not
|
||||||
|
// affect XAudio 2.8+, but that's Win8+. See
|
||||||
|
// http://blogs.msdn.com/b/chuckw/archive/2015/10/09/known-issues-xaudio-2-7.aspx
|
||||||
|
#ifdef _DEBUG
|
||||||
|
xAudio2DLL = LoadLibrary(L"XAudioD2_7.dll");
|
||||||
|
#else
|
||||||
|
xAudio2DLL = LoadLibrary(L"XAudio2_7.dll");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize XAudio2
|
||||||
|
//
|
||||||
|
CoInitializeEx( NULL, COINIT_MULTITHREADED );
|
||||||
|
|
||||||
|
UINT32 flags = 0;
|
||||||
|
if( IsDebugBuild )
|
||||||
|
flags |= XAUDIO2_DEBUG_ENGINE;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ( FAILED(hr = XAudio2Create( &pXAudio2, flags ) ) )
|
||||||
|
throw Exception::XAudio2_27Error( hr,
|
||||||
|
"Failed to init XAudio2 engine. XA2 may not be available on your system.\n"
|
||||||
|
"Ensure that you have the latest DirectX runtimes installed, or use \n"
|
||||||
|
"DirectX / WaveOut drivers instead. Error Details:"
|
||||||
|
);
|
||||||
|
|
||||||
|
XAUDIO2_DEVICE_DETAILS deviceDetails;
|
||||||
|
pXAudio2->GetDeviceDetails( 0, &deviceDetails );
|
||||||
|
|
||||||
|
// Stereo Expansion was planned to grab the currently configured number of
|
||||||
|
// Speakers from Windows's audio config.
|
||||||
|
// This doesn't always work though, so let it be a user configurable option.
|
||||||
|
|
||||||
|
int speakers;
|
||||||
|
switch(numSpeakers) // speakers = (numSpeakers + 1) *2; ?
|
||||||
|
{
|
||||||
|
case 0: speakers = 2; break; // Stereo
|
||||||
|
case 1: speakers = 4; break; // Quadrafonic
|
||||||
|
case 2: speakers = 6; break; // Surround 5.1
|
||||||
|
case 3: speakers = 8; break; // Surround 7.1
|
||||||
|
default: speakers = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any windows driver should support stereo at the software level, I should think!
|
||||||
|
jASSUME( deviceDetails.OutputFormat.Format.nChannels > 1 );
|
||||||
|
|
||||||
|
//
|
||||||
|
// Create a mastering voice
|
||||||
|
//
|
||||||
|
if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasteringVoice, speakers, SampleRate ) ) )
|
||||||
|
{
|
||||||
|
SysMessage( "Failed creating mastering voice: %#X\n", hr );
|
||||||
|
CoUninitialize();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( speakers )
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
ConLog( "* SPU2 > Using normal 2 speaker stereo output.\n" );
|
||||||
|
voiceContext = new StreamingVoice<StereoOut16>( pXAudio2 );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
ConLog( "* SPU2 > 2.1 speaker expansion enabled.\n" );
|
||||||
|
voiceContext = new StreamingVoice<Stereo21Out16>( pXAudio2 );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
ConLog( "* SPU2 > 4 speaker expansion enabled [quadraphenia]\n" );
|
||||||
|
voiceContext = new StreamingVoice<Stereo40Out16>( pXAudio2 );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
ConLog( "* SPU2 > 4.1 speaker expansion enabled.\n" );
|
||||||
|
voiceContext = new StreamingVoice<Stereo41Out16>( pXAudio2 );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
switch(dplLevel)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
ConLog( "* SPU2 > 5.1 speaker expansion enabled.\n" );
|
||||||
|
voiceContext = new StreamingVoice<Stereo51Out16>( pXAudio2 ); //"normal" stereo upmix
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ConLog( "* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled.\n" );
|
||||||
|
voiceContext = new StreamingVoice<Stereo51Out16Dpl>( pXAudio2 ); // basic Dpl decoder without rear stereo balancing
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ConLog( "* SPU2 > 5.1 speaker expansion with experimental ProLogicII dematrixing enabled.\n" );
|
||||||
|
voiceContext = new StreamingVoice<Stereo51Out16DplII>( pXAudio2 ); //gigas PLII
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // anything 8 or more gets the 7.1 treatment!
|
||||||
|
ConLog( "* SPU2 > 7.1 speaker expansion enabled.\n" );
|
||||||
|
voiceContext = new StreamingVoice<Stereo51Out16>( pXAudio2 );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
voiceContext->Init( pXAudio2 );
|
||||||
|
}
|
||||||
|
catch( Exception::XAudio2_27Error& ex )
|
||||||
|
{
|
||||||
|
SysMessage( ex.CMessage() );
|
||||||
|
pXAudio2.Release();
|
||||||
|
CoUninitialize();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close()
|
||||||
|
{
|
||||||
|
// Clean up?
|
||||||
|
// All XAudio2 interfaces are released when the engine is destroyed,
|
||||||
|
// but being tidy never hurt.
|
||||||
|
|
||||||
|
// Actually it can hurt. As of DXSDK Aug 2008, doing a full cleanup causes
|
||||||
|
// XA2 on Vista to crash. Even if you copy/paste code directly from Microsoft.
|
||||||
|
// But doing no cleanup at all causes XA2 under XP to crash. So after much trial
|
||||||
|
// and error we found a happy compromise as follows:
|
||||||
|
|
||||||
|
safe_delete( voiceContext );
|
||||||
|
|
||||||
|
voiceContext = NULL;
|
||||||
|
|
||||||
|
if( pMasteringVoice != NULL )
|
||||||
|
pMasteringVoice->DestroyVoice();
|
||||||
|
|
||||||
|
pMasteringVoice = NULL;
|
||||||
|
|
||||||
|
pXAudio2.Release();
|
||||||
|
CoUninitialize();
|
||||||
|
|
||||||
|
if (xAudio2DLL) {
|
||||||
|
FreeLibrary(xAudio2DLL);
|
||||||
|
xAudio2DLL = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Configure(uptr parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 Test() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetEmptySampleCount()
|
||||||
|
{
|
||||||
|
if( voiceContext == NULL ) return 0;
|
||||||
|
return voiceContext->GetEmptySampleCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* GetIdent() const
|
||||||
|
{
|
||||||
|
return L"xaudio2";
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* GetLongName() const
|
||||||
|
{
|
||||||
|
return L"XAudio 2.7 (Recommended)";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadSettings()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetApiSettings(wxString api)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteSettings() const
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} static XA2;
|
||||||
|
|
||||||
|
SndOutModule *XAudio2_27_Out = &XA2;
|
|
@ -107,8 +107,6 @@
|
||||||
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
|
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
|
||||||
<TargetName Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">$(ProjectName)-dev</TargetName>
|
<TargetName Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">$(ProjectName)-dev</TargetName>
|
||||||
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">false</LinkIncremental>
|
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">false</LinkIncremental>
|
||||||
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(DXSDK_DIR)\include;$(IncludePath)</IncludePath>
|
|
||||||
<LibraryPath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(DXSDK_DIR)\Lib\x86;$(LibraryPath)</LibraryPath>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">
|
||||||
<Midl>
|
<Midl>
|
||||||
|
@ -121,7 +119,7 @@
|
||||||
</HeaderFileName>
|
</HeaderFileName>
|
||||||
</Midl>
|
</Midl>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<AdditionalIncludeDirectories>$(DXSDK_DIR)include;$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>FLOAT_SAMPLES;NDEBUG;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>FLOAT_SAMPLES;NDEBUG;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ExceptionHandling>Async</ExceptionHandling>
|
<ExceptionHandling>Async</ExceptionHandling>
|
||||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||||
|
@ -160,7 +158,7 @@
|
||||||
</HeaderFileName>
|
</HeaderFileName>
|
||||||
</Midl>
|
</Midl>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<AdditionalIncludeDirectories>$(DXSDK_DIR)include;$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>FLOAT_SAMPLES;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>FLOAT_SAMPLES;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ExceptionHandling>Async</ExceptionHandling>
|
<ExceptionHandling>Async</ExceptionHandling>
|
||||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
|
@ -193,7 +191,7 @@
|
||||||
</HeaderFileName>
|
</HeaderFileName>
|
||||||
</Midl>
|
</Midl>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<AdditionalIncludeDirectories>$(DXSDK_DIR)include;$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>FLOAT_SAMPLES;NDEBUG;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>FLOAT_SAMPLES;NDEBUG;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ExceptionHandling>Async</ExceptionHandling>
|
<ExceptionHandling>Async</ExceptionHandling>
|
||||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||||
|
@ -234,7 +232,7 @@
|
||||||
</HeaderFileName>
|
</HeaderFileName>
|
||||||
</Midl>
|
</Midl>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<AdditionalIncludeDirectories>$(DXSDK_DIR)include;$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>wxUSE_GUI=0;FLOAT_SAMPLES;DEBUG_FAST;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>wxUSE_GUI=0;FLOAT_SAMPLES;DEBUG_FAST;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ExceptionHandling>Async</ExceptionHandling>
|
<ExceptionHandling>Async</ExceptionHandling>
|
||||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
|
@ -345,6 +343,9 @@
|
||||||
<ClCompile Include="SndOut_DSound.cpp" />
|
<ClCompile Include="SndOut_DSound.cpp" />
|
||||||
<ClCompile Include="SndOut_waveOut.cpp" />
|
<ClCompile Include="SndOut_waveOut.cpp" />
|
||||||
<ClCompile Include="SndOut_XAudio2.cpp" />
|
<ClCompile Include="SndOut_XAudio2.cpp" />
|
||||||
|
<ClCompile Include="SndOut_XAudio2_27.cpp">
|
||||||
|
<AdditionalIncludeDirectories>$(DXSDK_DIR)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\Linux\Alsa.cpp">
|
<ClCompile Include="..\Linux\Alsa.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugStrict|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugStrict|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||||
|
|
|
@ -144,7 +144,7 @@
|
||||||
<ClCompile Include="SndOut_waveOut.cpp">
|
<ClCompile Include="SndOut_waveOut.cpp">
|
||||||
<Filter>Source Files\Sound Output\Windows</Filter>
|
<Filter>Source Files\Sound Output\Windows</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="SndOut_XAudio2.cpp">
|
<ClCompile Include="SndOut_XAudio2_27.cpp">
|
||||||
<Filter>Source Files\Sound Output\Windows</Filter>
|
<Filter>Source Files\Sound Output\Windows</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\Linux\Alsa.cpp">
|
<ClCompile Include="..\Linux\Alsa.cpp">
|
||||||
|
@ -213,6 +213,9 @@
|
||||||
<ClCompile Include="..\DplIIdecoder.cpp">
|
<ClCompile Include="..\DplIIdecoder.cpp">
|
||||||
<Filter>Source Files\Sound Output</Filter>
|
<Filter>Source Files\Sound Output</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="SndOut_XAudio2.cpp">
|
||||||
|
<Filter>Source Files\Sound Output\Windows</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\..\LGPL.txt">
|
<None Include="..\..\LGPL.txt">
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <commctrl.h>
|
#include <commctrl.h>
|
||||||
#include <initguid.h>
|
#include <initguid.h>
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
|
#include <VersionHelpers.h>
|
||||||
|
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue