diff --git a/plugins/spu2-x/src/PS2E-spu2.cpp b/plugins/spu2-x/src/PS2E-spu2.cpp index 10b2006c1b..d7432b1f1e 100644 --- a/plugins/spu2-x/src/PS2E-spu2.cpp +++ b/plugins/spu2-x/src/PS2E-spu2.cpp @@ -160,6 +160,20 @@ EXPORT_C_(s32) SPU2test() { 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(); if( SndBuffer::Test() != 0 ) { diff --git a/plugins/spu2-x/src/SndOut.cpp b/plugins/spu2-x/src/SndOut.cpp index 34812fc90b..2359db0487 100644 --- a/plugins/spu2-x/src/SndOut.cpp +++ b/plugins/spu2-x/src/SndOut.cpp @@ -87,7 +87,7 @@ SndOutModule* mods[]= { &NullOut, #ifdef _MSC_VER - XAudio2Out, + XAudio2_27_Out, DSoundOut, WaveOut, #endif diff --git a/plugins/spu2-x/src/SndOut.h b/plugins/spu2-x/src/SndOut.h index 520e08572b..8cfab252b9 100644 --- a/plugins/spu2-x/src/SndOut.h +++ b/plugins/spu2-x/src/SndOut.h @@ -670,6 +670,7 @@ public: //internal extern SndOutModule* WaveOut; extern SndOutModule* DSoundOut; +extern SndOutModule* XAudio2_27_Out; extern SndOutModule* XAudio2Out; #endif extern SndOutModule* PortaudioOut; diff --git a/plugins/spu2-x/src/Spu2replay.cpp b/plugins/spu2-x/src/Spu2replay.cpp index 5a059e7bcc..2558ab05c8 100644 --- a/plugins/spu2-x/src/Spu2replay.cpp +++ b/plugins/spu2-x/src/Spu2replay.cpp @@ -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); + if (IsWindows8OrGreater()) + { + for (int n = 0; mods[n] != nullptr; ++n) + { + if (mods[n] == XAudio2_27_Out) + { + mods[n] = XAudio2Out; + break; + } + } + } + #endif // load file diff --git a/plugins/spu2-x/src/Windows/Config.cpp b/plugins/spu2-x/src/Windows/Config.cpp index 8434e11900..c7d4f2cbd8 100644 --- a/plugins/spu2-x/src/Windows/Config.cpp +++ b/plugins/spu2-x/src/Windows/Config.cpp @@ -82,20 +82,6 @@ int numSpeakers = 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() { @@ -140,12 +126,11 @@ void ReadSettings() // portaudio occasionally has issues selecting the proper default audio device. // 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()); -// } -// else { +// CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, PortaudioOut->GetIdent()); + if (IsWindows8OrGreater()) 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: OutputModule = FindOutputModuleById( omodid ); diff --git a/plugins/spu2-x/src/Windows/SndOut_XAudio2.cpp b/plugins/spu2-x/src/Windows/SndOut_XAudio2.cpp index 58928eee28..523cac20a7 100644 --- a/plugins/spu2-x/src/Windows/SndOut_XAudio2.cpp +++ b/plugins/spu2-x/src/Windows/SndOut_XAudio2.cpp @@ -17,26 +17,26 @@ #include "Global.h" -#define _WIN32_DCOM #include "Dialogs.h" - -#include #include +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0602 +#include + namespace Exception { class XAudio2Error : public std::runtime_error { protected: - static const char* SomeKindaErrorString( HRESULT hr ) + static const char* SomeKindaErrorString(HRESULT hr) { - switch( hr ) - { - case XAUDIO2_E_INVALID_CALL: - return "Invalid call for the XA2 object state."; + 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."; + case XAUDIO2_E_DEVICE_INVALIDATED: + return "Device is unavailable, unplugged, unsupported, or has been consumed by The Nothing."; } return "Unknown error code!"; } @@ -51,21 +51,21 @@ namespace Exception } virtual ~XAudio2Error() throw() {} - XAudio2Error( const HRESULT result, const std::string& msg ) : - runtime_error( msg ), - ErrorCode( result ), + XAudio2Error(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 ) ); + 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)); +static const double SndOutNormalizer = (double)(1UL << (SndOutVolumeShift + 16)); -class XAudio2Mod: public SndOutModule +class XAudio2Mod : public SndOutModule { private: static const int PacketsPerBuffer = 8; @@ -88,91 +88,89 @@ private: int GetEmptySampleCount() { XAUDIO2_VOICE_STATE state; - pSourceVoice->GetState( &state ); - return state.SamplesPlayed & (m_BufferSize-1); + 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) ) + 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; + 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 ) + 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.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; + 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::XAudio2Error( hr, "XAudio2 CreateSourceVoice failure." ); + if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&wfx, + XAUDIO2_VOICE_NOSRC, 1.0f, this))) { + throw Exception::XAudio2Error(hr, "XAudio2 CreateSourceVoice failure."); } - InitializeCriticalSection( &cs ); - EnterCriticalSection( &cs ); + InitializeCriticalSection(&cs); + EnterCriticalSection(&cs); pSourceVoice->FlushSourceBuffers(); - pSourceVoice->Start( 0, 0 ); + pSourceVoice->Start(0, 0); qbuffer = new s16[m_nBuffers * m_BufferSize]; - ZeroMemory( qbuffer, m_BufferSizeBytes * m_nBuffers ); + ZeroMemory(qbuffer, m_BufferSizeBytes * m_nBuffers); // Start some buffers. - for( uint i=0; iSubmitSourceBuffer( &buf ); + pSourceVoice->SubmitSourceBuffer(&buf); } - LeaveCriticalSection( &cs ); + LeaveCriticalSection(&cs); } STDMETHOD_(void, OnVoiceProcessingPassStart) () {} - STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) { }; + 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) { }; + 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 ) ) + StreamingVoice(IXAudio2* pXAudio2) : + BaseStreamingVoice(sizeof(T) / sizeof(s16)) { } @@ -183,58 +181,56 @@ private: killMe->FlushSourceBuffers(); killMe->DestroyVoice(); - EnterCriticalSection( &cs ); - safe_delete_array( qbuffer ); - LeaveCriticalSection( &cs ); - DeleteCriticalSection( &cs ); + EnterCriticalSection(&cs); + safe_delete_array(qbuffer); + LeaveCriticalSection(&cs); + DeleteCriticalSection(&cs); } - void Init( IXAudio2* pXAudio2 ) + 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; + 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 ); + _init(pXAudio2, chanMask); } 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. - if( pSourceVoice == NULL || context == NULL ) - { - LeaveCriticalSection( &cs ); + if (pSourceVoice == NULL || context == NULL) { + LeaveCriticalSection(&cs); return; } T* qb = (T*)context; - for(int p=0; pSubmitSourceBuffer( &buf ); - LeaveCriticalSection( &cs ); + pSourceVoice->SubmitSourceBuffer(&buf); + LeaveCriticalSection(&cs); } }; -#if _WIN32_WINNT < 0x602 + HMODULE xAudio2DLL; -#endif + decltype(&XAudio2Create) pXAudio2Create; CComPtr pXAudio2; IXAudio2MasteringVoice* pMasteringVoice; BaseStreamingVoice* voiceContext; @@ -245,124 +241,96 @@ public: { HRESULT hr; - jASSUME( pXAudio2 == NULL ); -#if _WIN32_WINNT < 0x602 - // 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 -#endif + jASSUME(pXAudio2 == NULL); + + xAudio2DLL = LoadLibraryEx(XAUDIO2_DLL, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (xAudio2DLL == nullptr) + throw std::runtime_error("Could not load " XAUDIO2_DLL_A ". Error code:" + std::to_string(GetLastError())); + + pXAudio2Create = reinterpret_cast(GetProcAddress(xAudio2DLL, "XAudio2Create")); + if (pXAudio2Create == nullptr) + throw std::runtime_error("XAudio2Create not found. Error code: " + std::to_string(GetLastError())); // // Initialize XAudio2 // - CoInitializeEx( NULL, COINIT_MULTITHREADED ); + CoInitializeEx(NULL, COINIT_MULTITHREADED); - UINT32 flags = 0; -#if _WIN32_WINNT < 0x602 - if( IsDebugBuild ) - 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 + try { + if (FAILED(hr = pXAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR))) + throw Exception::XAudio2Error(hr, "Failed to init XAudio2 engine. Error Details:"); // 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; ? + 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; + 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; } -#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 // - if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasteringVoice, speakers, SampleRate ) ) ) - { - SysMessage( "Failed creating mastering voice: %#X\n", hr ); + if (FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasteringVoice, speakers, SampleRate))) { + SysMessage("Failed creating mastering voice: %#X\n", hr); CoUninitialize(); return -1; } - switch( speakers ) - { + switch (speakers) { + case 2: + ConLog("* SPU2 > Using normal 2 speaker stereo output.\n"); + voiceContext = new StreamingVoice(pXAudio2); + break; + + case 3: + ConLog("* SPU2 > 2.1 speaker expansion enabled.\n"); + voiceContext = new StreamingVoice(pXAudio2); + break; + + case 4: + ConLog("* SPU2 > 4 speaker expansion enabled [quadraphenia]\n"); + voiceContext = new StreamingVoice(pXAudio2); + break; + + case 5: + ConLog("* SPU2 > 4.1 speaker expansion enabled.\n"); + voiceContext = new StreamingVoice(pXAudio2); + break; + + case 6: + case 7: + switch (dplLevel) { + case 0: + ConLog("* SPU2 > 5.1 speaker expansion enabled.\n"); + voiceContext = new StreamingVoice(pXAudio2); //"normal" stereo upmix + break; + case 1: + ConLog("* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled.\n"); + voiceContext = new StreamingVoice(pXAudio2); // basic Dpl decoder without rear stereo balancing + break; case 2: - ConLog( "* SPU2 > Using normal 2 speaker stereo output.\n" ); - voiceContext = new StreamingVoice( pXAudio2 ); + ConLog("* SPU2 > 5.1 speaker expansion with experimental ProLogicII dematrixing enabled.\n"); + voiceContext = new StreamingVoice(pXAudio2); //gigas PLII + break; + } break; - case 3: - ConLog( "* SPU2 > 2.1 speaker expansion enabled.\n" ); - voiceContext = new StreamingVoice( pXAudio2 ); - break; - - case 4: - ConLog( "* SPU2 > 4 speaker expansion enabled [quadraphenia]\n" ); - voiceContext = new StreamingVoice( pXAudio2 ); - break; - - case 5: - ConLog( "* SPU2 > 4.1 speaker expansion enabled.\n" ); - voiceContext = new StreamingVoice( pXAudio2 ); - break; - - case 6: - case 7: - switch(dplLevel) - { - case 0: - ConLog( "* SPU2 > 5.1 speaker expansion enabled.\n" ); - voiceContext = new StreamingVoice( pXAudio2 ); //"normal" stereo upmix - break; - case 1: - ConLog( "* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled.\n" ); - voiceContext = new StreamingVoice( 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( 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( pXAudio2 ); + default: // anything 8 or more gets the 7.1 treatment! + ConLog("* SPU2 > 7.1 speaker expansion enabled.\n"); + voiceContext = new StreamingVoice(pXAudio2); break; } - voiceContext->Init( pXAudio2 ); - } - catch( Exception::XAudio2Error& ex ) - { - SysMessage( ex.CMessage() ); + voiceContext->Init(pXAudio2); + } catch (Exception::XAudio2Error& ex) { + SysMessage(ex.CMessage()); pXAudio2.Release(); CoUninitialize(); return -1; @@ -373,20 +341,11 @@ public: 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 ); + safe_delete(voiceContext); voiceContext = NULL; - if( pMasteringVoice != NULL ) + if (pMasteringVoice != NULL) pMasteringVoice->DestroyVoice(); pMasteringVoice = NULL; @@ -394,18 +353,17 @@ public: pXAudio2.Release(); CoUninitialize(); -#if _WIN32_WINNT < 0x602 if (xAudio2DLL) { FreeLibrary(xAudio2DLL); xAudio2DLL = nullptr; + pXAudio2Create = nullptr; } -#endif } virtual void Configure(uptr parent) { } - + s32 Test() const { return 0; @@ -413,7 +371,7 @@ public: int GetEmptySampleCount() { - if( voiceContext == NULL ) return 0; + if (voiceContext == NULL) return 0; return voiceContext->GetEmptySampleCount(); } diff --git a/plugins/spu2-x/src/Windows/SndOut_XAudio2_27.cpp b/plugins/spu2-x/src/Windows/SndOut_XAudio2_27.cpp new file mode 100644 index 0000000000..ba39334128 --- /dev/null +++ b/plugins/spu2-x/src/Windows/SndOut_XAudio2_27.cpp @@ -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 . + */ + +#include "Global.h" + +#include "Dialogs.h" + +#include +#include + +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; iSubmitSourceBuffer( &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; pSubmitSourceBuffer( &buf ); + LeaveCriticalSection( &cs ); + } + + }; + + HMODULE xAudio2DLL; + CComPtr 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( pXAudio2 ); + break; + + case 3: + ConLog( "* SPU2 > 2.1 speaker expansion enabled.\n" ); + voiceContext = new StreamingVoice( pXAudio2 ); + break; + + case 4: + ConLog( "* SPU2 > 4 speaker expansion enabled [quadraphenia]\n" ); + voiceContext = new StreamingVoice( pXAudio2 ); + break; + + case 5: + ConLog( "* SPU2 > 4.1 speaker expansion enabled.\n" ); + voiceContext = new StreamingVoice( pXAudio2 ); + break; + + case 6: + case 7: + switch(dplLevel) + { + case 0: + ConLog( "* SPU2 > 5.1 speaker expansion enabled.\n" ); + voiceContext = new StreamingVoice( pXAudio2 ); //"normal" stereo upmix + break; + case 1: + ConLog( "* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled.\n" ); + voiceContext = new StreamingVoice( 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( 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( 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; diff --git a/plugins/spu2-x/src/Windows/Spu2-X.vcxproj b/plugins/spu2-x/src/Windows/Spu2-X.vcxproj index 900911bdac..3c3a7430b0 100644 --- a/plugins/spu2-x/src/Windows/Spu2-X.vcxproj +++ b/plugins/spu2-x/src/Windows/Spu2-X.vcxproj @@ -107,8 +107,6 @@ $(ProjectName)-dev false - $(DXSDK_DIR)\include;$(IncludePath) - $(DXSDK_DIR)\Lib\x86;$(LibraryPath) @@ -121,7 +119,7 @@ - $(DXSDK_DIR)include;$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories) FLOAT_SAMPLES;NDEBUG;_USRDLL;%(PreprocessorDefinitions) Async StreamingSIMDExtensions2 @@ -160,7 +158,7 @@ - $(DXSDK_DIR)include;$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories) FLOAT_SAMPLES;_USRDLL;%(PreprocessorDefinitions) Async Use @@ -193,7 +191,7 @@ - $(DXSDK_DIR)include;$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories) FLOAT_SAMPLES;NDEBUG;_USRDLL;%(PreprocessorDefinitions) Async StreamingSIMDExtensions2 @@ -234,7 +232,7 @@ - $(DXSDK_DIR)include;$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories) wxUSE_GUI=0;FLOAT_SAMPLES;DEBUG_FAST;_USRDLL;%(PreprocessorDefinitions) Async Use @@ -345,6 +343,9 @@ + + $(DXSDK_DIR)include;%(AdditionalIncludeDirectories) + true true diff --git a/plugins/spu2-x/src/Windows/Spu2-X.vcxproj.filters b/plugins/spu2-x/src/Windows/Spu2-X.vcxproj.filters index d456189de8..3f164873fd 100644 --- a/plugins/spu2-x/src/Windows/Spu2-X.vcxproj.filters +++ b/plugins/spu2-x/src/Windows/Spu2-X.vcxproj.filters @@ -144,7 +144,7 @@ Source Files\Sound Output\Windows - + Source Files\Sound Output\Windows @@ -213,6 +213,9 @@ Source Files\Sound Output + + Source Files\Sound Output\Windows + diff --git a/plugins/spu2-x/src/Windows/WinConfig.h b/plugins/spu2-x/src/Windows/WinConfig.h index 269ff0c465..3169a9d4e2 100644 --- a/plugins/spu2-x/src/Windows/WinConfig.h +++ b/plugins/spu2-x/src/Windows/WinConfig.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "resource.h"