Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

494 lines
14 KiB

  1. #include "basetypes.h"
  2. #include "commonmacros.h"
  3. #if !defined( _X360 ) && defined( WIN32 )
  4. #define _WIN32_DCOM
  5. #include <windows.h>
  6. #endif
  7. #include <xaudio2.h>
  8. #include "mix.h"
  9. #include "soundsystem/lowlevel.h"
  10. static IXAudio2 *g_pXAudio2 = NULL;
  11. static int g_XAudio2Refcount = 0;
  12. extern CInterlockedInt g_nDetectedAudioError;
  13. extern CInterlockedInt g_nDetectedBufferStarvation;
  14. //-----------------------------------------------------------------------------
  15. // Purpose: Implementation of XAudio2 device for source2
  16. //-----------------------------------------------------------------------------
  17. class CAudioXAudio2 : public IAudioDevice2, public IXAudio2VoiceCallback
  18. {
  19. public:
  20. CAudioXAudio2()
  21. {
  22. g_XAudio2Refcount++;
  23. m_pName = "XAudio2 Device";
  24. m_nChannels = 2;
  25. m_bIsHeadphone = false;
  26. m_bSupportsBufferStarvationDetection = true;
  27. m_bIsCaptureDevice = false;
  28. m_nSampleBits = 16;
  29. m_nSampleRate = 44100;
  30. m_bIsActive = true;
  31. m_pMasterVoice = NULL;
  32. m_pSourceVoice = NULL;
  33. m_pBuffer = NULL;
  34. m_nBufferSizeBytes = 0;
  35. m_nBufferCount = 0;
  36. m_nSubmitIndex = 0;
  37. m_nActiveBuffers = 0;
  38. m_nBufferErrors = 0;
  39. m_bHasFocus = true;
  40. m_bVoiceStarted = false;
  41. }
  42. ~CAudioXAudio2( void );
  43. bool Init( const audio_device_init_params_t &params, int nDeviceIndex );
  44. void Shutdown( void ) OVERRIDE;
  45. void OutputBuffer( int nChannels, CAudioMixBuffer *pChannelArray ) OVERRIDE;
  46. const wchar_t *GetDeviceID() const OVERRIDE { return m_deviceID; }
  47. int QueuedBufferCount() OVERRIDE;
  48. int EmptyBufferCount() OVERRIDE;
  49. void CancelOutput() OVERRIDE;
  50. void WaitForComplete() OVERRIDE;
  51. void UpdateFocus( bool bWindowHasFocus ) OVERRIDE;
  52. void ClearBuffer() OVERRIDE {}
  53. void OutputDebugInfo() const OVERRIDE;
  54. bool SetShouldPlayWhenNotInFocus( bool bPlayEvenWhenNotInFocus ) OVERRIDE
  55. {
  56. m_savedParams.m_bPlayEvenWhenNotInFocus = bPlayEvenWhenNotInFocus;
  57. return true;
  58. }
  59. inline int BytesPerSample() { return BitsPerSample()>>3; }
  60. // Singleton object
  61. // IXAudio2VoiceCallback
  62. // Called just before this voice's processing pass begins.
  63. virtual void __stdcall OnVoiceProcessingPassStart( UINT32 nBytesRequired ) OVERRIDE {}
  64. virtual void __stdcall OnVoiceProcessingPassEnd() OVERRIDE {}
  65. virtual void __stdcall OnStreamEnd() OVERRIDE {}
  66. virtual void __stdcall OnBufferStart( void* pBufferContext ) OVERRIDE
  67. {
  68. }
  69. virtual void __stdcall OnBufferEnd( void* pBufferContext ) OVERRIDE
  70. {
  71. Assert( m_nActiveBuffers > 0 );
  72. m_nActiveBuffers--;
  73. if ( m_nActiveBuffers == 0 )
  74. {
  75. m_nBufferErrors++;
  76. if ( m_nBufferErrors > 10 )
  77. {
  78. g_nDetectedBufferStarvation++;
  79. }
  80. }
  81. }
  82. virtual void __stdcall OnLoopEnd( void* pBufferContext ) OVERRIDE {}
  83. virtual void __stdcall OnVoiceError( void* pBufferContext, HRESULT nError ) OVERRIDE
  84. {
  85. g_nDetectedAudioError = 1;
  86. Warning("Xaudio2 Voice Error %x\n", uint(nError) );
  87. }
  88. private:
  89. // no copies of this class ever
  90. CAudioXAudio2( const CAudioXAudio2 & );
  91. CAudioXAudio2 & operator=( const CAudioXAudio2 & );
  92. int SamplesPerBuffer() { return MIX_BUFFER_SIZE; }
  93. int BytesPerBuffer() { return m_nBufferSizeBytes; }
  94. IXAudio2MasteringVoice *m_pMasterVoice;
  95. IXAudio2SourceVoice *m_pSourceVoice;
  96. short *m_pBuffer;
  97. int m_nBufferSizeBytes; // size of a single hardware output buffer, in bytes
  98. int m_nBufferCount;
  99. int m_nSubmitIndex;
  100. int m_nBufferErrors;
  101. CInterlockedInt m_nActiveBuffers;
  102. // for error recovery
  103. audio_device_init_params_t m_savedParams;
  104. int m_nSavedPreferred;
  105. wchar_t m_deviceID[256];
  106. char m_displayName[256];
  107. bool m_bHasFocus;
  108. bool m_bVoiceStarted;
  109. };
  110. class CXAudioCallbacks : public IXAudio2EngineCallback
  111. {
  112. public:
  113. CXAudioCallbacks() : m_bRegistered(false) {}
  114. // IXAudio2EngineCallback
  115. // ------------------------------------
  116. STDMETHOD_(void, OnProcessingPassStart) (THIS);
  117. STDMETHOD_(void, OnProcessingPassEnd) (THIS);
  118. // Called in the event of a critical system error which requires XAudio2
  119. // to be closed down and restarted. The error code is given in Error.
  120. STDMETHOD_(void, OnCriticalError) (THIS_ HRESULT Error);
  121. // ------------------------------------
  122. void Init( IXAudio2 *pInterface )
  123. {
  124. pInterface->RegisterForCallbacks( this );
  125. m_bRegistered = true;
  126. }
  127. void Shutdown( IXAudio2 *pInterface )
  128. {
  129. if ( m_bRegistered )
  130. {
  131. pInterface->UnregisterForCallbacks( this );
  132. m_bRegistered = false;
  133. }
  134. }
  135. bool m_bRegistered;
  136. };
  137. void CXAudioCallbacks::OnProcessingPassStart() {}
  138. void CXAudioCallbacks::OnProcessingPassEnd() {}
  139. void CXAudioCallbacks::OnCriticalError( HRESULT nError )
  140. {
  141. g_nDetectedAudioError = 1;
  142. Warning("Xaudio2 Error %x\n", uint(nError) );
  143. }
  144. static CXAudioCallbacks g_XAudioErrors;
  145. static bool InitXAudio()
  146. {
  147. if ( !g_pXAudio2 )
  148. {
  149. HRESULT hr;
  150. InitCOM();
  151. #ifdef _DEBUG
  152. // UNDONE: Figure out why this fails - I believe it requires a separate install
  153. if ( FAILED(hr = XAudio2Create( &g_pXAudio2, XAUDIO2_DEBUG_ENGINE, XAUDIO2_DEFAULT_PROCESSOR ) ) )
  154. #endif
  155. if ( FAILED(hr = XAudio2Create( &g_pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR ) ) )
  156. return false;
  157. g_XAudioErrors.Init( g_pXAudio2 );
  158. // if someone calls CoFreeUnusedLibrariesEx (some shell extensions do this), then XAudio2 will get freed, go ahead an load the library explicitly
  159. // to prevent this unloading
  160. #if defined(PLATFORM_WINDOWS_PC)
  161. #ifdef _DEBUG
  162. const char *pDLLName = "XAudioD2_7.dll";
  163. #else
  164. const char *pDLLName = "XAudio2_7.dll";
  165. #endif
  166. // The DLL is already loaded, check the name to make sure we aren't loading an additional DLL
  167. if ( GetModuleHandle( pDLLName ) )
  168. {
  169. LoadLibrary( pDLLName );
  170. }
  171. #endif
  172. }
  173. return true;
  174. }
  175. static void ShutdownXAudio()
  176. {
  177. if ( g_pXAudio2 )
  178. {
  179. g_XAudioErrors.Shutdown( g_pXAudio2 );
  180. g_pXAudio2->Release();
  181. g_pXAudio2 = NULL;
  182. ShutdownCOM();
  183. }
  184. }
  185. // enumerate the available devices so the app can select one
  186. // fills out app-supplied list & returns count of available devices. If the list is too small, the count
  187. // will signal the app to call again with a larger list
  188. int Audio_EnumerateXAudio2Devices( audio_device_description_t *pDeviceListOut, int nListCount )
  189. {
  190. if ( !InitXAudio() )
  191. return 0;
  192. UINT32 nDeviceCountWindows = 0;
  193. HRESULT hr = g_pXAudio2->GetDeviceCount(&nDeviceCountWindows);
  194. Assert( hr == S_OK );
  195. if ( hr != S_OK )
  196. return 0;
  197. int nDeviceCount = (int)nDeviceCountWindows;
  198. XAUDIO2_DEVICE_DETAILS deviceDetails;
  199. bool bWroteDefault = false;
  200. int nMaxChannelDevice = -1;
  201. // now get each device's details that will fit into the supplied list
  202. int nIterateCount = min(nListCount, nDeviceCount);
  203. for ( int i = 0; i < nIterateCount; i++ )
  204. {
  205. g_pXAudio2->GetDeviceDetails(i,&deviceDetails);
  206. V_wcscpy_safe( pDeviceListOut[i].m_deviceName, deviceDetails.DeviceID );
  207. V_wcstostr( deviceDetails.DisplayName, -1, pDeviceListOut[i].m_friendlyName, sizeof(pDeviceListOut[i].m_friendlyName) );
  208. pDeviceListOut[i].m_nChannelCount = deviceDetails.OutputFormat.Format.nChannels;
  209. pDeviceListOut[i].m_bIsDefault = false;
  210. pDeviceListOut[i].m_bIsAvailable = true;
  211. pDeviceListOut[i].m_nSubsystemId = AUDIO_SUBSYSTEM_XAUDIO;
  212. // anything marked as default game device will be selected by default
  213. if ( deviceDetails.Role & DefaultGameDevice )
  214. {
  215. pDeviceListOut[i].m_bIsDefault = true;
  216. bWroteDefault = true;
  217. }
  218. if ( nMaxChannelDevice < 0 || deviceDetails.OutputFormat.Format.nChannels > pDeviceListOut[nMaxChannelDevice].m_nChannelCount )
  219. {
  220. nMaxChannelDevice = i;
  221. }
  222. }
  223. // no default? Select first device with max # of channels
  224. // If there are no channels then nMaxChannelDevice will be negative so we need to
  225. // check before using it as an index.
  226. if ( !bWroteDefault && nMaxChannelDevice >= 0 && nMaxChannelDevice < nListCount )
  227. {
  228. pDeviceListOut[nMaxChannelDevice].m_bIsDefault = true;
  229. }
  230. return nIterateCount;
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Class factory
  234. //-----------------------------------------------------------------------------
  235. IAudioDevice2 *Audio_CreateXAudio2Device( const audio_device_init_params_t &params )
  236. {
  237. if ( !InitXAudio() )
  238. return NULL;
  239. int nPreferredDevice = 0;
  240. UINT32 nDeviceCountWindows = 0;
  241. int nCount = 0;
  242. HRESULT hr = g_pXAudio2->GetDeviceCount(&nDeviceCountWindows);
  243. CUtlVector<audio_device_description_t> desc;
  244. if ( hr == S_OK )
  245. {
  246. desc.SetCount( nDeviceCountWindows );
  247. nCount = Audio_EnumerateXAudio2Devices( desc.Base(), desc.Count() );
  248. }
  249. // If there are no devices then device Init will fail. We might as well
  250. // fail early.
  251. // This was happening when running test_source2.bat in a loop and
  252. // disconnecting partway through the past -- as soon as the machine was
  253. // running headless the enumeration would return zero devices.
  254. if ( !nCount )
  255. return NULL;
  256. for ( int i = 0; i < nCount; i++ )
  257. {
  258. if ( desc[i].m_bIsDefault )
  259. {
  260. nPreferredDevice = i;
  261. }
  262. }
  263. if ( params.m_bOverrideDevice )
  264. {
  265. for ( int i = 0; i < nCount; i++ )
  266. {
  267. if ( !V_wcscmp( desc[i].m_deviceName, params.m_overrideDeviceName ) )
  268. {
  269. nPreferredDevice = i;
  270. break;
  271. }
  272. }
  273. }
  274. CAudioXAudio2 *pDevice = new CAudioXAudio2;
  275. if ( pDevice->Init( params, nPreferredDevice ) )
  276. return pDevice;
  277. delete pDevice;
  278. return NULL;
  279. }
  280. CAudioXAudio2::~CAudioXAudio2()
  281. {
  282. Shutdown();
  283. if ( m_pBuffer )
  284. {
  285. MemAlloc_FreeAligned( m_pBuffer );
  286. }
  287. g_XAudio2Refcount--;
  288. if ( !g_XAudio2Refcount )
  289. {
  290. ShutdownXAudio();
  291. }
  292. }
  293. bool CAudioXAudio2::Init( const audio_device_init_params_t &params, int nDeviceIndex )
  294. {
  295. HRESULT hr;
  296. // NOTE: sample rate was XAUDIO2_DEFAULT_SAMPLERATE
  297. if ( FAILED(hr = g_pXAudio2->CreateMasteringVoice( &m_pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nDeviceIndex, NULL ) ) )
  298. return false;
  299. XAUDIO2_DEVICE_DETAILS deviceDetails;
  300. g_pXAudio2->GetDeviceDetails( nDeviceIndex, &deviceDetails );
  301. V_wcscpy_safe( m_deviceID, deviceDetails.DeviceID );
  302. V_wcstostr( deviceDetails.DisplayName, -1, m_displayName, sizeof(m_displayName) );
  303. // save for error recovery
  304. m_nSavedPreferred = nDeviceIndex;
  305. m_savedParams = params;
  306. XAUDIO2_VOICE_DETAILS details;
  307. m_pMasterVoice->GetVoiceDetails( &details );
  308. WAVEFORMATEX wfx = { 0 };
  309. // setup the format structure
  310. {
  311. int nChannels = details.InputChannels;
  312. if ( params.m_bOverrideSpeakerConfig )
  313. {
  314. nChannels = SpeakerConfigValueToChannelCount( params.m_nOverrideSpeakerConfig );
  315. if ( params.m_nOverrideSpeakerConfig == 0 )
  316. {
  317. m_bIsHeadphone = true;
  318. }
  319. }
  320. m_nChannels = nChannels;
  321. }
  322. wfx.wFormatTag = WAVE_FORMAT_PCM;
  323. wfx.nChannels = m_nChannels;
  324. wfx.nSamplesPerSec = SampleRate();
  325. wfx.wBitsPerSample = BitsPerSample();
  326. wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
  327. wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
  328. wfx.cbSize = 0;
  329. if( FAILED( hr = g_pXAudio2->CreateSourceVoice( &m_pSourceVoice, &wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO, this ) ) )
  330. {
  331. return false;
  332. }
  333. m_nBufferCount = params.m_nOutputBufferCount;
  334. int nBufferSize = MIX_BUFFER_SIZE * m_nChannels * BytesPerSample();
  335. m_nBufferSizeBytes = nBufferSize;
  336. m_nChannels = wfx.nChannels;
  337. Assert( m_nChannels <= SOUND_DEVICE_MAX_CHANNELS );
  338. m_pBuffer = (short *)MemAlloc_AllocAligned( nBufferSize * m_nBufferCount, 16 );
  339. m_nActiveBuffers = 0;
  340. m_nSubmitIndex = 0;
  341. m_bVoiceStarted = false;
  342. return true;
  343. }
  344. void CAudioXAudio2::Shutdown()
  345. {
  346. if ( m_pMasterVoice )
  347. {
  348. if ( m_pSourceVoice )
  349. {
  350. m_pSourceVoice->Stop();
  351. m_pSourceVoice->DestroyVoice();
  352. m_pSourceVoice = nullptr;
  353. m_bVoiceStarted = false;
  354. }
  355. m_pMasterVoice->DestroyVoice();
  356. m_pMasterVoice = nullptr;
  357. }
  358. }
  359. void CAudioXAudio2::OutputDebugInfo() const
  360. {
  361. Msg( "XAudio2 Sound Device: %s\n", m_displayName );
  362. Msg( "Channels:\t%d\n", ChannelCount() );
  363. Msg( "Bits/Sample:\t%d\n", BitsPerSample() );
  364. Msg( "Rate:\t\t%d\n", SampleRate() );
  365. }
  366. void CAudioXAudio2::OutputBuffer( int nChannels, CAudioMixBuffer *pChannelArray )
  367. {
  368. // start the voice as soon as we have data to output
  369. if ( !m_bVoiceStarted )
  370. {
  371. m_pSourceVoice->Start();
  372. m_bVoiceStarted = true;
  373. }
  374. int nBufferSize = BytesPerBuffer();
  375. short *pWaveData = m_pBuffer + ( m_nSubmitIndex * (nBufferSize>>1) );
  376. XAUDIO2_BUFFER buffer = {0};
  377. buffer.pAudioData = (BYTE *)pWaveData;
  378. buffer.Flags = 0;
  379. buffer.AudioBytes = nBufferSize;
  380. if ( nChannels == 2 && nChannels == m_nChannels )
  381. {
  382. ConvertFloat32Int16_Clamp_Interleave2( pWaveData, pChannelArray[0].m_flData, pChannelArray[1].m_flData, MIX_BUFFER_SIZE );
  383. }
  384. else
  385. {
  386. ConvertFloat32Int16_Clamp_InterleaveStride( pWaveData, m_nChannels, MIX_BUFFER_SIZE, pChannelArray[0].m_flData, nChannels, MIX_BUFFER_SIZE );
  387. }
  388. m_nActiveBuffers++;
  389. m_pSourceVoice->SubmitSourceBuffer( &buffer );
  390. m_nSubmitIndex = ( m_nSubmitIndex + 1 ) % m_nBufferCount;
  391. }
  392. int CAudioXAudio2::QueuedBufferCount()
  393. {
  394. // NOTE: If callbacks work on all clients then we do not need to do the potentially expensive GetState() call
  395. // we already know if buffers are retired
  396. // UNDONE: If this is causing problems, just change to #if 0 - the other code in this changelist will not interact with anything
  397. #if 1
  398. return m_nActiveBuffers;
  399. #else
  400. XAUDIO2_VOICE_STATE state;
  401. m_pSourceVoice->GetState( &state );
  402. return state.BuffersQueued;
  403. #endif
  404. }
  405. int CAudioXAudio2::EmptyBufferCount()
  406. {
  407. return m_nBufferCount - QueuedBufferCount();
  408. }
  409. void CAudioXAudio2::CancelOutput()
  410. {
  411. m_pSourceVoice->FlushSourceBuffers();
  412. }
  413. void CAudioXAudio2::WaitForComplete()
  414. {
  415. m_pSourceVoice->Discontinuity();
  416. while( QueuedBufferCount() )
  417. {
  418. ThreadSleep(0);
  419. }
  420. }
  421. void CAudioXAudio2::UpdateFocus( bool bWindowHasFocus )
  422. {
  423. if ( m_pMasterVoice && !m_savedParams.m_bPlayEvenWhenNotInFocus )
  424. {
  425. if ( bWindowHasFocus != m_bHasFocus )
  426. {
  427. m_bHasFocus = bWindowHasFocus;
  428. float flVolume = 1.0f;
  429. m_pMasterVoice->GetVolume( &flVolume );
  430. m_pMasterVoice->SetVolume( bWindowHasFocus ? 1.0f : 0.0f );
  431. }
  432. }
  433. }