Source code of Windows XP (NT5)
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.

1211 lines
39 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1999, 2000 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: mixserver.cpp
  6. * Content: Implements the mixing server portion of the server class
  7. *
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 11/01/2000 rodtoll Split out from dvsereng.cpp
  12. * 12/14/2000 rodtoll DPVOICE: [Mixing Server] Mixer may create infinite loop
  13. * 02/20/2001 rodtoll WINBUG #321297 - DPVOICE: Access violation in DPVoice.dll while running DVSalvo server
  14. * 04/09/2001 rodtoll WINBUG #364126 - DPVoice : Memory leak when Initializing 2 Voice Servers with same DPlay transport
  15. *
  16. ***************************************************************************/
  17. #include "dxvoicepch.h"
  18. #define IsEmpty(x) (x.next == x.prev && x.next == &x)
  19. //#define IsEmpty(x) (FALSE)
  20. #define CONVERTTORECORD(x,y,z) ((y *)(x->pvObject))
  21. #undef DPF_MODNAME
  22. #define DPF_MODNAME "CDirectVoiceServerEngine::AddPlayerToMixingAddList"
  23. void CDirectVoiceServerEngine::AddPlayerToMixingAddList( CVoicePlayer *pPlayer )
  24. {
  25. CDVCSPlayer *pVoicePlayer = (CDVCSPlayer *) pPlayer;
  26. DNASSERT( pVoicePlayer );
  27. for( DWORD dwIndex = 0; dwIndex < m_dwNumMixingThreads; dwIndex++ )
  28. {
  29. DNEnterCriticalSection( &m_prWorkerControl[dwIndex].m_csMixingAddList );
  30. pVoicePlayer->AddToMixingList( dwIndex, &m_prWorkerControl[dwIndex].m_blMixingAddPlayers );
  31. pVoicePlayer->AddRef();
  32. DNLeaveCriticalSection( &m_prWorkerControl[dwIndex].m_csMixingAddList );
  33. }
  34. }
  35. #undef DPF_MODNAME
  36. #define DPF_MODNAME "CDirectVoiceServerEngine::StartWorkerThreads"
  37. // StartWorkerThreads
  38. //
  39. // This function starts mixer worker threads. The number started is based on the
  40. // m_dwNumMixingThreads variable which must be initialized before this is called.
  41. //
  42. HRESULT CDirectVoiceServerEngine::StartWorkerThreads()
  43. {
  44. HRESULT hr = DV_OK;
  45. DWORD dwIndex;
  46. m_prWorkerControl = new MIXERTHREAD_CONTROL[m_dwNumMixingThreads];
  47. if( m_prWorkerControl == NULL )
  48. {
  49. DPFX(DPFPREP, DVF_ERRORLEVEL, "Out of memory!" );
  50. return DVERR_OUTOFMEMORY;
  51. }
  52. // Zero memory so everything is initialized.
  53. ZeroMemory( m_prWorkerControl, sizeof( MIXERTHREAD_CONTROL )*m_dwNumMixingThreads );
  54. for( dwIndex = 0; dwIndex < m_dwNumMixingThreads; dwIndex++ )
  55. {
  56. m_prWorkerControl[dwIndex].dwThreadIndex = dwIndex;
  57. m_prWorkerControl[dwIndex].hThreadDone = CreateEvent( NULL, FALSE, FALSE, NULL );
  58. m_prWorkerControl[dwIndex].hThreadDoWork = CreateEvent( NULL, FALSE, FALSE, NULL );
  59. m_prWorkerControl[dwIndex].hThreadIdle = CreateEvent( NULL, FALSE, FALSE, NULL );
  60. m_prWorkerControl[dwIndex].hThreadQuit = CreateEvent( NULL, FALSE, FALSE, NULL );
  61. m_prWorkerControl[dwIndex].m_pServerObject = this;
  62. InitBilink( &m_prWorkerControl[dwIndex].m_blMixingAddPlayers, NULL );
  63. InitBilink( &m_prWorkerControl[dwIndex].m_blMixingActivePlayers, NULL );
  64. if (!DNInitializeCriticalSection( &m_prWorkerControl[dwIndex].m_csMixingAddList ))
  65. {
  66. hr = DVERR_OUTOFMEMORY;
  67. goto EXIT_ERROR;
  68. }
  69. if( m_prWorkerControl[dwIndex].hThreadDone == NULL ||
  70. m_prWorkerControl[dwIndex].hThreadDoWork == NULL ||
  71. m_prWorkerControl[dwIndex].hThreadIdle == NULL ||
  72. m_prWorkerControl[dwIndex].hThreadQuit == NULL )
  73. {
  74. hr = GetLastError();
  75. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error creating events hr=0x%x", hr );
  76. hr = DVERR_GENERIC;
  77. goto EXIT_ERROR;
  78. }
  79. m_prWorkerControl[dwIndex].m_mixerBuffer = new BYTE[m_dwUnCompressedFrameSize];
  80. m_prWorkerControl[dwIndex].m_realMixerBuffer = new LONG[m_dwMixerSize];
  81. if( m_prWorkerControl[dwIndex].m_mixerBuffer == NULL ||
  82. m_prWorkerControl[dwIndex].m_realMixerBuffer == NULL )
  83. {
  84. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error allocating memory" );
  85. hr = DVERR_OUTOFMEMORY;
  86. goto EXIT_ERROR;
  87. }
  88. m_prWorkerControl[dwIndex].hThread = (HANDLE) CreateThread( NULL, 0, MixerWorker, &m_prWorkerControl[dwIndex], 0, &m_prWorkerControl[dwIndex].dwThreadID );
  89. if( m_prWorkerControl[dwIndex].hThread == NULL )
  90. {
  91. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error creating events/thread" );
  92. hr = DVERR_GENERIC;
  93. goto EXIT_ERROR;
  94. }
  95. ::SetThreadPriority( m_prWorkerControl[dwIndex].hThread, THREAD_PRIORITY_TIME_CRITICAL );
  96. }
  97. return DV_OK;
  98. EXIT_ERROR:
  99. ShutdownWorkerThreads();
  100. return hr;
  101. }
  102. HRESULT CDirectVoiceServerEngine::ShutdownWorkerThreads()
  103. {
  104. DWORD dwIndex;
  105. if( m_prWorkerControl )
  106. {
  107. for( dwIndex = 0; dwIndex < m_dwNumMixingThreads; dwIndex++ )
  108. {
  109. if( m_prWorkerControl[dwIndex].hThread )
  110. {
  111. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "[%d]: Shutting down ID=[0x%x]", dwIndex, m_prWorkerControl[dwIndex].dwThreadID );
  112. SetEvent( m_prWorkerControl[dwIndex].hThreadQuit );
  113. WaitForSingleObject( m_prWorkerControl[dwIndex].hThreadDone, INFINITE );
  114. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "[%d]: Shutting down complete", dwIndex );
  115. CloseHandle( m_prWorkerControl[dwIndex].hThread );
  116. m_prWorkerControl[dwIndex].hThread = NULL;
  117. DNDeleteCriticalSection( &m_prWorkerControl[dwIndex].m_csMixingAddList );
  118. }
  119. if( m_prWorkerControl[dwIndex].hThreadDone )
  120. CloseHandle( m_prWorkerControl[dwIndex].hThreadDone );
  121. if( m_prWorkerControl[dwIndex].hThreadDoWork )
  122. CloseHandle( m_prWorkerControl[dwIndex].hThreadDoWork );
  123. if( m_prWorkerControl[dwIndex].hThreadIdle )
  124. CloseHandle( m_prWorkerControl[dwIndex].hThreadIdle );
  125. if( m_prWorkerControl[dwIndex].hThreadQuit )
  126. CloseHandle( m_prWorkerControl[dwIndex].hThreadQuit );
  127. if( m_prWorkerControl[dwIndex].m_mixerBuffer )
  128. delete [] m_prWorkerControl[dwIndex].m_mixerBuffer;
  129. if( m_prWorkerControl[dwIndex].m_realMixerBuffer )
  130. delete [] m_prWorkerControl[dwIndex].m_realMixerBuffer;
  131. DNASSERT( (IsEmpty( m_prWorkerControl[dwIndex].m_blMixingAddPlayers )) );
  132. DNASSERT( (IsEmpty( m_prWorkerControl[dwIndex].m_blMixingActivePlayers )) );
  133. }
  134. delete [] m_prWorkerControl;
  135. m_prWorkerControl = NULL;
  136. }
  137. return 0;
  138. }
  139. #undef DPF_MODNAME
  140. #define DPF_MODNAME "CDirectVoiceServerEngine::StartupClientServer"
  141. //
  142. // StartupClientServer
  143. //
  144. // This function is called to initialize the Mixer portion of the server object.
  145. // Only called for Mixing Sessions. Initialization includes the startup of
  146. // the mixing thread and startup of the mixer multimedia timer.
  147. //
  148. // Called By:
  149. // - StartSession
  150. //
  151. // Locks Required:
  152. // - None
  153. //
  154. HRESULT CDirectVoiceServerEngine::StartupClientServer()
  155. {
  156. HRESULT hr;
  157. HANDLE tmpThreadHandle;
  158. SYSTEM_INFO sysInfo;
  159. DWORD dwIndex;
  160. m_pFramePool = NULL;
  161. m_dwCompressedFrameSize = m_lpdvfCompressionInfo->dwFrameLength;
  162. m_dwUnCompressedFrameSize = DVCDB_CalcUnCompressedFrameSize( m_lpdvfCompressionInfo, s_lpwfxMixerFormat );
  163. m_dwNumPerBuffer = m_lpdvfCompressionInfo->dwFramesPerBuffer;
  164. m_pFramePool = new CFramePool( m_dwCompressedFrameSize );
  165. if( m_pFramePool == NULL )
  166. {
  167. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to allocate frame pool" );
  168. return DVERR_OUTOFMEMORY;
  169. }
  170. if (!m_pFramePool->Init())
  171. {
  172. delete m_pFramePool;
  173. m_pFramePool = NULL;
  174. return DVERR_OUTOFMEMORY;
  175. }
  176. m_mixerEightBit = (s_lpwfxMixerFormat->wBitsPerSample==8) ? TRUE : FALSE;
  177. GetSystemInfo( &sysInfo );
  178. m_dwNumMixingThreads = sysInfo.dwNumberOfProcessors;
  179. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXER: There will be %d worker threads", m_dwNumMixingThreads );
  180. if( m_mixerEightBit )
  181. {
  182. m_dwMixerSize = m_dwUnCompressedFrameSize;
  183. }
  184. else
  185. {
  186. // Mixer size is / 2 because 16-bit samples, only need 1 LONG for
  187. // each 16-bit sample = 2 * 8bit.
  188. m_dwMixerSize = m_dwUnCompressedFrameSize / 2;
  189. }
  190. m_pStats->m_dwNumMixingThreads = m_dwNumMixingThreads;
  191. hr = StartWorkerThreads();
  192. if( FAILED( hr ) )
  193. {
  194. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed starting worker threads hr=0x%x", hr );
  195. goto EXIT_CLIENTSERVERSTARTUP;
  196. }
  197. // General info
  198. m_timer = new Timer;
  199. if( m_timer == NULL )
  200. {
  201. DPFX(DPFPREP, DVF_ERRORLEVEL, "Out of memory!" );
  202. hr = DVERR_OUTOFMEMORY;
  203. goto EXIT_CLIENTSERVERSTARTUP;
  204. }
  205. m_hTickSemaphore = CreateSemaphore( NULL, 0, 0xFFFFFF, NULL );
  206. if( m_hTickSemaphore == NULL )
  207. {
  208. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create semaphore" );
  209. hr = DVERR_GENERIC;
  210. goto EXIT_CLIENTSERVERSTARTUP;
  211. }
  212. m_hShutdownMixerEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  213. m_hMixerDoneEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  214. if( m_hShutdownMixerEvent == NULL ||
  215. m_hMixerDoneEvent == NULL )
  216. {
  217. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create events" );
  218. hr = DVERR_GENERIC;
  219. goto EXIT_CLIENTSERVERSTARTUP;
  220. }
  221. m_hMixerControlThread = CreateThread( NULL, 0, MixerControl, this, 0, &m_dwMixerControlThreadID );
  222. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXER: Controller Started: ID=0x%x", m_dwMixerControlThreadID );
  223. if( m_hMixerControlThread == NULL )
  224. {
  225. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error creating events/thread" );
  226. hr = DVERR_GENERIC;
  227. goto EXIT_CLIENTSERVERSTARTUP;
  228. }
  229. ::SetThreadPriority( m_hMixerControlThread, THREAD_PRIORITY_TIME_CRITICAL );
  230. if( !m_timer->Create( m_lpdvfCompressionInfo->dwTimeout, 2, (DWORD_PTR) &m_hTickSemaphore, MixingServerWakeupProc ) )
  231. {
  232. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create multimedia timer" );
  233. hr = DVERR_GENERIC;
  234. goto EXIT_CLIENTSERVERSTARTUP;
  235. }
  236. return DV_OK;
  237. EXIT_CLIENTSERVERSTARTUP:
  238. ShutdownClientServer();
  239. return hr;
  240. }
  241. #undef DPF_MODNAME
  242. #define DPF_MODNAME "CDirectVoiceServerEngine::ShutdownClientServer"
  243. //
  244. // ShutdownClientServer
  245. //
  246. // This function is responsible for shutting down the mixer portion of the
  247. // server object. This function should only be called for mixing sessions.
  248. //
  249. // This function will stop the mixer thread and the mixer multimedia timer.
  250. //
  251. // Called By:
  252. // - StopSession
  253. //
  254. // Locks Required:
  255. // - None
  256. //
  257. HRESULT CDirectVoiceServerEngine::ShutdownClientServer()
  258. {
  259. if( m_hMixerControlThread )
  260. {
  261. SetEvent( m_hShutdownMixerEvent );
  262. WaitForSingleObject( m_hMixerDoneEvent, INFINITE );
  263. CloseHandle( m_hMixerControlThread );
  264. m_hMixerControlThread = NULL;
  265. // Cleanup the mixing list
  266. CleanupMixingList();
  267. }
  268. if( m_hShutdownMixerEvent )
  269. {
  270. CloseHandle( m_hShutdownMixerEvent );
  271. m_hShutdownMixerEvent = NULL;
  272. }
  273. if( m_hMixerDoneEvent )
  274. {
  275. CloseHandle( m_hMixerDoneEvent );
  276. m_hMixerDoneEvent = NULL;
  277. }
  278. ShutdownWorkerThreads();
  279. if( m_timer )
  280. {
  281. delete m_timer;
  282. m_timer = NULL;
  283. }
  284. if( m_hTickSemaphore )
  285. {
  286. CloseHandle( m_hTickSemaphore );
  287. m_hTickSemaphore = NULL;
  288. }
  289. return DV_OK;
  290. }
  291. #undef DPF_MODNAME
  292. #define DPF_MODNAME "CDirectVoiceServerEngine::Mixer_Buffer_Reset"
  293. // Mixer_Buffer_Reset
  294. //
  295. // This function resets the mixer buffer back to silence.
  296. void CDirectVoiceServerEngine::Mixer_Buffer_Reset( DWORD dwThreadIndex )
  297. {
  298. FillBufferWithSilence( m_prWorkerControl[dwThreadIndex].m_realMixerBuffer,
  299. m_prWorkerControl[dwThreadIndex].m_pServerObject->m_mixerEightBit,
  300. m_prWorkerControl[dwThreadIndex].m_pServerObject->m_dwUnCompressedFrameSize );
  301. }
  302. #undef DPF_MODNAME
  303. #define DPF_MODNAME "CDirectVoiceServerEngine::Mixer_Buffer_MixBuffer"
  304. // Mixer_Buffer_MixBuffer
  305. //
  306. // This function mixes the speech pointed to by the source parameter
  307. // into the mixer buffer.
  308. //
  309. // Parameters:
  310. // unsigned char *source -
  311. // Pointer to source data in uncompressed format
  312. void CDirectVoiceServerEngine::Mixer_Buffer_MixBuffer( DWORD dwThreadIndex, unsigned char *source )
  313. {
  314. MixInBuffer( m_prWorkerControl[dwThreadIndex].m_realMixerBuffer, source,
  315. m_prWorkerControl[dwThreadIndex].m_pServerObject->m_mixerEightBit,
  316. m_prWorkerControl[dwThreadIndex].m_pServerObject->m_dwUnCompressedFrameSize );
  317. }
  318. #undef DPF_MODNAME
  319. #define DPF_MODNAME "CDirectVoiceServerEngine::Mixer_Buffer_Normalize"
  320. // Mixer_Buffer_Normalize
  321. //
  322. // This function takes the mixed audio data from the mixer
  323. // buffer and transfers it back to the mixer format
  324. // and places it into the m_mixerBuffer buffer.
  325. //
  326. void CDirectVoiceServerEngine::Mixer_Buffer_Normalize( DWORD dwThreadIndex )
  327. {
  328. NormalizeBuffer( m_prWorkerControl[dwThreadIndex].m_mixerBuffer,
  329. m_prWorkerControl[dwThreadIndex].m_realMixerBuffer,
  330. m_prWorkerControl[dwThreadIndex].m_pServerObject->m_mixerEightBit,
  331. m_prWorkerControl[dwThreadIndex].m_pServerObject->m_dwUnCompressedFrameSize );
  332. }
  333. #undef DPF_MODNAME
  334. #define DPF_MODNAME "CDirectVoiceServerEngine::HandleMixerThreadError"
  335. //
  336. // HandleMixerThreadError
  337. //
  338. // This function is called by the mixer when an unrecoverable error
  339. // occurs.
  340. //
  341. void CDirectVoiceServerEngine::HandleMixerThreadError( HRESULT hr )
  342. {
  343. DPFX(DPFPREP, DVF_ERRORLEVEL, "Mixer Thread Encountered an error. hr=0x%x", hr );
  344. SetEvent( m_hMixerDoneEvent );
  345. StopSession( 0, FALSE, hr );
  346. }
  347. #undef DPF_MODNAME
  348. #define DPF_MODNAME "CDirectVoiceServerEngine::MixerControl"
  349. DWORD WINAPI CDirectVoiceServerEngine::MixerControl( void *pvContext )
  350. {
  351. CDirectVoiceServerEngine *This = (CDirectVoiceServerEngine *) pvContext;
  352. HANDLE hEvents[3];
  353. HANDLE *hIdleEvents = new HANDLE[This->m_dwNumMixingThreads+1];
  354. DWORD dwIndex = 0;
  355. LONG lFreeThreadIndex = 0;
  356. DWORD dwNumToMix = 0;
  357. DWORD dwTickCountStart;
  358. LONG lWaitResult;
  359. if( !hIdleEvents )
  360. {
  361. DPFX(DPFPREP, DVF_ERRORLEVEL, "MIXCTRL: Error allocating array" );
  362. DNASSERT( FALSE );
  363. SetEvent( This->m_hMixerDoneEvent );
  364. return 0;
  365. }
  366. hEvents[0] = This->m_hShutdownMixerEvent;
  367. hEvents[1] = This->m_hTickSemaphore;
  368. hEvents[2] = (HANDLE) ((DWORD_PTR) 0xFFFFFFFF);
  369. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXCTRL: Starting up" );
  370. for( dwIndex = 0; dwIndex < This->m_dwNumMixingThreads; dwIndex++ )
  371. {
  372. hIdleEvents[dwIndex] = This->m_prWorkerControl[dwIndex].hThreadIdle;
  373. }
  374. hIdleEvents[This->m_dwNumMixingThreads] = (HANDLE) ((DWORD_PTR) 0xFFFFFFFF);
  375. // Wait for tick or for quit command
  376. while( (lWaitResult = WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE )) != WAIT_OBJECT_0 )
  377. {
  378. // On Win9X we may occationally over run the end of the wait list
  379. // and the result is we hit the FFFFFFFFF which will cause
  380. // a failure.
  381. if( lWaitResult == WAIT_FAILED )
  382. continue;
  383. // Update statistics block
  384. InterlockedIncrement( &This->m_pStats->m_dwNumMixingPasses );
  385. dwTickCountStart = GetTickCount();
  386. // On Win95 you may occasionally encounter a situation where the waitformultiple runs
  387. // off the end of the list and ends up with the invalid handle above. Just continue
  388. // in this case.
  389. lFreeThreadIndex = WAIT_FAILED;
  390. while( lFreeThreadIndex == WAIT_FAILED )
  391. {
  392. // Wait for a single mixing thread to be free
  393. lFreeThreadIndex = WaitForMultipleObjects( This->m_dwNumMixingThreads, hIdleEvents, FALSE, INFINITE );
  394. //// TODO: Error checking!
  395. }
  396. lFreeThreadIndex -= WAIT_OBJECT_0;
  397. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXCTRL: Worker [%d] is elected to do work", lFreeThreadIndex );
  398. This->SpinWorkToThread( lFreeThreadIndex );
  399. }
  400. delete [] hIdleEvents;
  401. SetEvent( This->m_hMixerDoneEvent );
  402. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXCTRL: Shutting down" );
  403. return 0;
  404. }
  405. #undef DPF_MODNAME
  406. #define DPF_MODNAME "CDirectVoiceServerEngine::MixerWorker"
  407. DWORD WINAPI CDirectVoiceServerEngine::MixerWorker( void *pvContext )
  408. {
  409. PMIXERTHREAD_CONTROL This = (PMIXERTHREAD_CONTROL) pvContext;
  410. HANDLE hEvents[3];
  411. BILINK *pblSearch, *pblSubSearch;
  412. CDVCSPlayer *pCurrentPlayer = NULL, *pTmpPlayer = NULL;
  413. PDVID pdvidTargets = NULL;
  414. DWORD dwNumTargets = 0;
  415. DWORD dwTargetIndex = 0;
  416. DWORD dwResultSize = 0;
  417. DWORD dwIndex = 0;
  418. DWORD dwThreadIndex = This->dwThreadIndex;
  419. HRESULT hr;
  420. CDVCSPlayer **ppThreadHearList = NULL;
  421. PDVPROTOCOLMSG_SPEECHHEADER pdvmSpeechHeader = NULL;
  422. PDVTRANSPORT_BUFFERDESC pdvbTransmitBufferDesc = NULL;
  423. PVOID pvSendContext = NULL;
  424. DVID dvidSendTarget;
  425. DWORD dwTickCountStart;
  426. DWORD dwTickCountDecStart;
  427. DWORD dwTickCountMixStart;
  428. DWORD dwTickCountDupStart;
  429. DWORD dwTickCountRetStart;
  430. DWORD dwStatIndex;
  431. DWORD dwTickCountEnd;
  432. DWORD dwTotalMix, dwForwardMix, dwReuseMix, dwOriginalMix;
  433. LONG lWaitResult;
  434. MixingServerStats *pStats = This->m_pServerObject->m_pStats;
  435. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXWORKER: [%d] Started [0x%x] Thread", This->dwThreadIndex, GetCurrentThreadId() );
  436. hEvents[0] = This->hThreadQuit;
  437. hEvents[1] = This->hThreadDoWork;
  438. hEvents[2] = (HANDLE) ((DWORD_PTR) 0xFFFFFFFF);
  439. SetEvent( This->hThreadIdle );
  440. while( (lWaitResult = WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE )) != WAIT_OBJECT_0 )
  441. {
  442. // On Win95 it may occationally move off the end of the list and hit the guard value
  443. if( lWaitResult == WAIT_FAILED )
  444. continue;
  445. // Statistics update
  446. dwTickCountStart = GetTickCount();
  447. InterlockedIncrement( &pStats->m_dwNumMixingThreadsActive );
  448. pStats->m_dwNumMixingPassesPerThread[dwThreadIndex]++;
  449. if( pStats->m_dwNumMixingThreadsActive >
  450. pStats->m_dwMaxMixingThreadsActive )
  451. {
  452. pStats->m_dwMaxMixingThreadsActive = pStats->m_dwNumMixingThreadsActive;
  453. }
  454. dwStatIndex = pStats->m_dwCurrentMixingHistoryLoc[dwThreadIndex];
  455. pStats->m_lCurrentPlayerCount[dwThreadIndex][dwStatIndex] = This->dwNumToMix;
  456. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXWORKER: [%d] Starting work", This->dwThreadIndex );
  457. if( This->dwNumToMix == 0 )
  458. {
  459. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXWORKER: No players to process!" );
  460. goto WORK_COMPLETE;
  461. }
  462. dwTickCountDecStart = GetTickCount();
  463. pStats->m_lCurrentDecCountHistory[dwThreadIndex][dwStatIndex] = 0;
  464. // Pass through player list and decompress those who need decompression
  465. //
  466. pblSearch = This->m_blMixingSpeakingPlayers.next;
  467. while( pblSearch != &This->m_blMixingSpeakingPlayers )
  468. {
  469. pCurrentPlayer = CONVERTTORECORD( pblSearch, CDVCSPlayer, m_blMixingSpeakingPlayers[dwThreadIndex] );
  470. // Dereference the array of can hear players for this player
  471. ppThreadHearList = pCurrentPlayer->m_pppCanHear[dwThreadIndex];
  472. // Player needs to have their voice decompressed
  473. if( pCurrentPlayer->m_pfNeedsDecompression[dwThreadIndex] )
  474. {
  475. DNASSERT( pCurrentPlayer );
  476. DNASSERT( pCurrentPlayer->m_pSourceFrame[dwThreadIndex] );
  477. DNASSERT( !pCurrentPlayer->m_pSourceFrame[dwThreadIndex]->GetIsSilence() );
  478. dwResultSize = This->m_pServerObject->m_dwUnCompressedFrameSize;
  479. hr = pCurrentPlayer->DeCompressInBound(
  480. pCurrentPlayer->m_pSourceFrame[dwThreadIndex],
  481. &pCurrentPlayer->m_sourceUnCompressed[pCurrentPlayer->m_pdwUnCompressedBufferOffset[dwThreadIndex]],
  482. &dwResultSize );
  483. pStats->m_lCurrentDecCountHistory[dwThreadIndex][dwStatIndex]++;
  484. if( FAILED( hr ) )
  485. {
  486. DNASSERT( FALSE );
  487. // TODO: ERROR Handling for failed decompression
  488. }
  489. else
  490. {
  491. pCurrentPlayer->m_pfDecompressed[dwThreadIndex] = TRUE;
  492. }
  493. DNASSERT( dwResultSize == This->m_pServerObject->m_dwUnCompressedFrameSize );
  494. }
  495. // Integrity checks
  496. //
  497. // Check to ensure that each player who this person can hear is supposed to be decompressed
  498. #ifdef _DEBUG
  499. DNASSERT( pCurrentPlayer->m_pdwHearCount[dwThreadIndex] < This->dwNumToMix );
  500. if( pCurrentPlayer->m_pdwHearCount[dwThreadIndex] > 1 )
  501. {
  502. for( dwIndex; dwIndex < pCurrentPlayer->m_pdwHearCount[dwThreadIndex]; dwIndex++ )
  503. {
  504. DNASSERT( ppThreadHearList[dwIndex] );
  505. DNASSERT( ppThreadHearList[dwIndex]->m_pfNeedsDecompression[dwThreadIndex] );
  506. }
  507. }
  508. #endif
  509. pblSearch = pblSearch->next;
  510. }
  511. dwTickCountDupStart = GetTickCount();
  512. pStats->m_lCurrentDecTimeHistory[dwThreadIndex][dwStatIndex] = dwTickCountDupStart - dwTickCountDecStart;
  513. // Check for duplicates in the sending. If there is duplicates then we need
  514. // to setup the reuse
  515. pblSearch = This->m_blMixingHearingPlayers.next;
  516. while( pblSearch != &This->m_blMixingHearingPlayers )
  517. {
  518. pCurrentPlayer = CONVERTTORECORD( pblSearch, CDVCSPlayer, m_blMixingHearingPlayers[dwThreadIndex] );
  519. // If we don't hear anybody, this step is irrelevant
  520. if( pCurrentPlayer->m_pdwHearCount[dwThreadIndex] < 2 )
  521. goto DUPLICATE_CHECK_LOOP_DONE;
  522. // Dereference the array of can hear players for this player
  523. ppThreadHearList = pCurrentPlayer->m_pppCanHear[dwThreadIndex];
  524. pblSubSearch = This->m_blMixingHearingPlayers.next;
  525. // Only do the people who come before them.
  526. while( pblSubSearch != pblSearch )
  527. {
  528. pTmpPlayer = CONVERTTORECORD( pblSubSearch, CDVCSPlayer, m_blMixingHearingPlayers[dwThreadIndex] );
  529. // This person's mix is the same, re-use it!
  530. if( pTmpPlayer->ComparePlayerMix( dwThreadIndex, pCurrentPlayer ) )
  531. {
  532. pCurrentPlayer->m_pReuseMixFromThisPlayer[dwThreadIndex] = pTmpPlayer;
  533. pTmpPlayer->m_pfMixToBeReused[dwThreadIndex] = TRUE;
  534. break;
  535. }
  536. pblSubSearch = pblSubSearch->next;
  537. }
  538. DUPLICATE_CHECK_LOOP_DONE:
  539. pblSearch = pblSearch->next;
  540. }
  541. dwTickCountMixStart = GetTickCount();
  542. pStats->m_lCurrentDupTimeHistory[dwThreadIndex][dwStatIndex] = dwTickCountMixStart - dwTickCountDupStart;
  543. dwTotalMix = 0;
  544. dwForwardMix = 0;
  545. dwReuseMix = 0;
  546. dwOriginalMix = 0;
  547. // Pass through player list and compress and send mixes as appropriate
  548. pblSearch = This->m_blMixingHearingPlayers.next;
  549. while( pblSearch != &This->m_blMixingHearingPlayers )
  550. {
  551. pCurrentPlayer = CONVERTTORECORD( pblSearch, CDVCSPlayer, m_blMixingHearingPlayers[dwThreadIndex] );
  552. // Dereference the array of can hear players for this player
  553. ppThreadHearList = pCurrentPlayer->m_pppCanHear[dwThreadIndex];
  554. // Pre-set next so we can continue() below and still go to next item
  555. pblSearch = pblSearch->next;
  556. if( !pCurrentPlayer->m_pdwHearCount[dwThreadIndex] )
  557. {
  558. continue;
  559. }
  560. dwTotalMix++;
  561. // Get a transmission buffer and description
  562. pdvbTransmitBufferDesc = This->m_pServerObject->GetTransmitBuffer( This->m_pServerObject->m_dwCompressedFrameSize+sizeof(DVPROTOCOLMSG_SPEECHHEADER)+COMPRESSION_SLUSH,
  563. &pvSendContext );
  564. if( pdvbTransmitBufferDesc == NULL )
  565. {
  566. // TODO: Error handling for out of memory condition
  567. DNASSERT( FALSE );
  568. }
  569. // Setup the packet header
  570. pdvmSpeechHeader = (PDVPROTOCOLMSG_SPEECHHEADER) pdvbTransmitBufferDesc->pBufferData;
  571. pdvmSpeechHeader->dwType = DVMSGID_SPEECHBOUNCE;
  572. pdvmSpeechHeader->bMsgNum = pCurrentPlayer->m_pbMsgNumToSend[dwThreadIndex];
  573. pdvmSpeechHeader->bSeqNum = pCurrentPlayer->m_pbSeqNumToSend[dwThreadIndex];
  574. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXWORKER: [%d] Sending Packet to 0x%x Msg=0x%x Seq=0x%x",
  575. dwThreadIndex,
  576. pCurrentPlayer->GetPlayerID(),
  577. pdvmSpeechHeader->bMsgNum,
  578. pdvmSpeechHeader->bSeqNum );
  579. // If this player hears something they will be getting a packet
  580. //
  581. // Only hear one person -- forward the packet
  582. //
  583. if( pCurrentPlayer->m_pdwHearCount[dwThreadIndex] == 1)
  584. {
  585. dwResultSize = ppThreadHearList[0]->m_pSourceFrame[dwThreadIndex]->GetFrameLength();
  586. memcpy( &pdvmSpeechHeader[1],
  587. ppThreadHearList[0]->m_pSourceFrame[dwThreadIndex]->GetDataPointer(),
  588. dwResultSize );
  589. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXWORKER: [%d] Forwarding already compressed packet", dwThreadIndex );
  590. pCurrentPlayer->m_pfMixed[dwThreadIndex] = TRUE;
  591. dwForwardMix++;
  592. }
  593. else if( pCurrentPlayer->m_pdwHearCount[dwThreadIndex] > 1)
  594. {
  595. pTmpPlayer = pCurrentPlayer->m_pReuseMixFromThisPlayer[dwThreadIndex];
  596. // We are re-using a previous player's mix
  597. if( pTmpPlayer )
  598. {
  599. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXERWORKER: [%d] Forwarding pre-built mix", dwThreadIndex );
  600. DNASSERT( pTmpPlayer->m_pfMixed[dwThreadIndex] );
  601. DNASSERT( pTmpPlayer->m_pfMixToBeReused[dwThreadIndex] );
  602. DNASSERT( pTmpPlayer->m_pdwResultLength[dwThreadIndex] );
  603. dwResultSize = pTmpPlayer->m_pdwResultLength[dwThreadIndex];
  604. memcpy( &pdvmSpeechHeader[1],
  605. &pTmpPlayer->m_targetCompressed[pTmpPlayer->m_pdwCompressedBufferOffset[dwThreadIndex]],
  606. dwResultSize );
  607. dwReuseMix++;
  608. }
  609. else
  610. {
  611. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXERWORKER: [%d] Creating original mix", dwThreadIndex );
  612. dwOriginalMix++;
  613. dwResultSize = This->m_pServerObject->m_dwCompressedFrameSize;
  614. // Reset the high resolution mixer buffer
  615. This->m_pServerObject->Mixer_Buffer_Reset(dwThreadIndex);
  616. // Mix in specified player's audio.
  617. for( dwIndex = 0; dwIndex < pCurrentPlayer->m_pdwHearCount[dwThreadIndex]; dwIndex++ )
  618. {
  619. DNASSERT( !ppThreadHearList[dwIndex]->m_pfSilence[dwThreadIndex] );
  620. This->m_pServerObject->Mixer_Buffer_MixBuffer(dwThreadIndex,ppThreadHearList[dwIndex]->m_sourceUnCompressed );
  621. }
  622. // Normalize the buffer back to the thread's mix buffer
  623. This->m_pServerObject->Mixer_Buffer_Normalize(dwThreadIndex);
  624. hr = pCurrentPlayer->CompressOutBound( This->m_mixerBuffer,
  625. This->m_pServerObject->m_dwUnCompressedFrameSize,
  626. (BYTE *) &pdvmSpeechHeader[1],
  627. &dwResultSize );
  628. if( FAILED( hr ) )
  629. {
  630. DNASSERT( FALSE );
  631. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed compressing outbound audio" );
  632. }
  633. pCurrentPlayer->m_pfMixed[dwThreadIndex] = TRUE;
  634. pCurrentPlayer->m_pdwResultLength[dwThreadIndex] = dwResultSize;
  635. // This player's mix will be re-used, ensure that we cache it
  636. if( pCurrentPlayer->m_pfMixToBeReused[dwThreadIndex] )
  637. {
  638. memcpy( &pCurrentPlayer->m_targetCompressed[pCurrentPlayer->m_pdwCompressedBufferOffset[dwThreadIndex]],
  639. &pdvmSpeechHeader[1],
  640. pCurrentPlayer->m_pdwResultLength[dwThreadIndex] );
  641. }
  642. }
  643. }
  644. else
  645. {
  646. DNASSERT(FALSE);
  647. }
  648. dvidSendTarget = pCurrentPlayer->GetPlayerID();
  649. pdvbTransmitBufferDesc->dwBufferSize= dwResultSize + sizeof( DVPROTOCOLMSG_SPEECHHEADER );
  650. hr = This->m_pServerObject->m_lpSessionTransport->SendToIDS( &dvidSendTarget, 1, pdvbTransmitBufferDesc, pvSendContext, 0 );
  651. if( hr == DVERR_PENDING )
  652. hr = DV_OK;
  653. if( FAILED( hr ) )
  654. {
  655. DPFX(DPFPREP, DVF_ERRORLEVEL, "MIXWORKER: [%d] Unable to transmit to target [0x%x]", pCurrentPlayer->GetPlayerID() );
  656. }
  657. }
  658. dwTickCountRetStart = GetTickCount();
  659. pStats->m_lCurrentMixTimeHistory[dwThreadIndex][dwStatIndex] = dwTickCountRetStart - dwTickCountMixStart;
  660. pStats->m_lCurrentMixCountTotalHistory[dwThreadIndex][dwStatIndex] = dwTotalMix;
  661. pStats->m_lCurrentMixCountFwdHistory[dwThreadIndex][dwStatIndex] = dwForwardMix;
  662. pStats->m_lCurrentMixCountReuseHistory[dwThreadIndex][dwStatIndex] = dwReuseMix;
  663. pStats->m_lCurrentMixCountOriginalHistory[dwThreadIndex][dwStatIndex] = dwOriginalMix;
  664. WORK_COMPLETE:
  665. // Pass through player list and return frames
  666. pblSearch = This->m_blMixingActivePlayers.next;
  667. while( pblSearch != &This->m_blMixingActivePlayers )
  668. {
  669. pCurrentPlayer = CONVERTTORECORD( pblSearch, CDVCSPlayer, m_pblMixingActivePlayers[dwThreadIndex] );
  670. DNASSERT( pCurrentPlayer->m_pSourceFrame[dwThreadIndex] );
  671. pCurrentPlayer->CompleteRun( dwThreadIndex );
  672. pblSearch = pblSearch->next;
  673. }
  674. dwTickCountEnd = GetTickCount();
  675. pStats->m_lCurrentRetTimeHistory[dwThreadIndex][dwStatIndex] = dwTickCountEnd - dwTickCountRetStart;
  676. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXWORKER: [%d] Work complete", This->dwThreadIndex );
  677. // Statistics update
  678. InterlockedDecrement( &This->m_pServerObject->m_pStats->m_dwNumMixingThreadsActive );
  679. SetEvent( This->hThreadIdle );
  680. // Statistics update
  681. pStats->m_dwMixingPassesTimeHistory[dwThreadIndex][dwStatIndex] = dwTickCountEnd - dwTickCountStart;
  682. pStats->m_dwCurrentMixingHistoryLoc[dwThreadIndex]++;
  683. pStats->m_dwCurrentMixingHistoryLoc[dwThreadIndex] %= MIXING_HISTORY;
  684. }
  685. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "MIXWORKER: [%d] Shutting down", This->dwThreadIndex );
  686. SetEvent( This->hThreadDone );
  687. return 0;
  688. }
  689. #undef DPF_MODNAME
  690. #define DPF_MODNAME "CDirectVoiceServerEngine::SpinWorkToThread"
  691. //
  692. // SpinWorkToThread
  693. //
  694. // This function performs the first step of a mixing server pass and then
  695. // passes the work off to the specified thread
  696. //
  697. // Responsible for:
  698. // 1. Updating the
  699. // 2. running the list of players, determinging who they can hear
  700. //
  701. void CDirectVoiceServerEngine::SpinWorkToThread( LONG lThreadIndex )
  702. {
  703. BILINK *pblSearch = NULL, *pblSubSearch = NULL;
  704. CDVCSPlayer *pCurrentPlayer = NULL, *pTmpPlayer = NULL, *pComparePlayer = NULL;
  705. HRESULT hr;
  706. PDVID pdvidTargets = NULL;
  707. DWORD dwNumTargets = 0;
  708. DWORD dwTargetIndex = 0;
  709. DWORD dwTickCountStart = GetTickCount();
  710. // Update the list of players from the pending lists to the individual bilinks
  711. UpdateActiveMixingPendingList( lThreadIndex, &m_prWorkerControl[lThreadIndex].dwNumToMix );
  712. InitBilink( &m_prWorkerControl[lThreadIndex].m_blMixingSpeakingPlayers, NULL );
  713. InitBilink( &m_prWorkerControl[lThreadIndex].m_blMixingHearingPlayers, NULL );
  714. // Pass 1 through player list.
  715. //
  716. // Reset state variables for specified thread, create any converters that need creating
  717. pblSearch = m_prWorkerControl[lThreadIndex].m_blMixingActivePlayers.next;
  718. while( pblSearch != &m_prWorkerControl[lThreadIndex].m_blMixingActivePlayers )
  719. {
  720. pTmpPlayer = CONVERTTORECORD( pblSearch, CDVCSPlayer, m_pblMixingActivePlayers[lThreadIndex] );
  721. pblSearch = pblSearch->next;
  722. // Reset for the next pass
  723. pTmpPlayer->ResetForNextRun(lThreadIndex,TRUE);
  724. // Resize the can hear array
  725. pTmpPlayer->ResizeIfRequired( lThreadIndex, m_prWorkerControl[lThreadIndex].dwNumToMix );
  726. // Lock the player -- only one person should be creating converter at a time
  727. pTmpPlayer->Lock();
  728. // Create outbound converter if required
  729. if( !pTmpPlayer->IsOutBoundConverterInitialized() )
  730. {
  731. hr = pTmpPlayer->CreateOutBoundConverter( s_lpwfxMixerFormat, m_dvSessionDesc.guidCT );
  732. if( FAILED( hr ) )
  733. {
  734. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to create outbound converter hr=0x%x", hr );
  735. DNASSERT( FALSE );
  736. }
  737. }
  738. // Create inbound converter if required
  739. if( !pTmpPlayer->IsInBoundConverterInitialized() )
  740. {
  741. hr = pTmpPlayer->CreateInBoundConverter( m_dvSessionDesc.guidCT, s_lpwfxMixerFormat );
  742. if( FAILED( hr ) )
  743. {
  744. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to create converter" );
  745. DNASSERT( FALSE );
  746. }
  747. }
  748. pTmpPlayer->UnLock();
  749. }
  750. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "SST: 2" );
  751. // Pass 2.
  752. //
  753. // For each player:
  754. // 1. Figure out who they hear.
  755. // 2. TODO: If they hear anyone, add them to the "to send to" list of people
  756. // 3. TODO: If they hear > 1, add the people they hear to the "to decompress" list of people.
  757. // 4. Setup the appropriate sequence # / msg # for the transmission
  758. //
  759. pblSearch = m_prWorkerControl[lThreadIndex].m_blMixingActivePlayers.next;
  760. while( pblSearch != &m_prWorkerControl[lThreadIndex].m_blMixingActivePlayers )
  761. {
  762. pCurrentPlayer = CONVERTTORECORD( pblSearch, CDVCSPlayer, m_pblMixingActivePlayers[lThreadIndex] );
  763. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "0x%x Can hear: ", pCurrentPlayer->GetPlayerID() );
  764. pblSearch = pblSearch->next;
  765. pblSubSearch = m_prWorkerControl[lThreadIndex].m_blMixingActivePlayers.next;
  766. // Search the list of people in the session
  767. while( pblSubSearch != &m_prWorkerControl[lThreadIndex].m_blMixingActivePlayers )
  768. {
  769. pComparePlayer = CONVERTTORECORD( pblSubSearch, CDVCSPlayer, m_pblMixingActivePlayers[lThreadIndex] );
  770. pblSubSearch = pblSubSearch->next;
  771. // This record contains a silent record -- ignore
  772. if( pComparePlayer->m_pfSilence[lThreadIndex] )
  773. continue;
  774. // If this isn't the player themselves
  775. if( pblSearch != pblSubSearch )
  776. {
  777. DNASSERT( pComparePlayer->m_pSourceFrame[lThreadIndex] );
  778. pdvidTargets = pComparePlayer->m_pSourceFrame[lThreadIndex]->GetTargetList();
  779. dwNumTargets = pComparePlayer->m_pSourceFrame[lThreadIndex]->GetNumTargets();
  780. // The target of the subIndex user's frame is this user OR
  781. // The user is in the group which is target of subIndex user's frame
  782. for( dwTargetIndex = 0; dwTargetIndex < dwNumTargets; dwTargetIndex++ )
  783. {
  784. if( pCurrentPlayer->GetPlayerID() == pdvidTargets[dwTargetIndex] ||
  785. m_lpSessionTransport->IsPlayerInGroup( pdvidTargets[dwTargetIndex], pCurrentPlayer->GetPlayerID() ) )
  786. {
  787. *((*(pCurrentPlayer->m_pppCanHear+lThreadIndex))+pCurrentPlayer->m_pdwHearCount[lThreadIndex]) = pComparePlayer;
  788. pCurrentPlayer->m_pdwHearCount[lThreadIndex]++;
  789. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "0x%x", pComparePlayer->GetPlayerID() );
  790. // Setup the appropriate msg num / sequence number so when it's sent
  791. // we ensure it gets re-assembled on the other side in the right order
  792. if( pCurrentPlayer->m_pdwHearCount[lThreadIndex] == 1 )
  793. {
  794. if( pCurrentPlayer->m_bLastSent == FALSE )
  795. {
  796. pCurrentPlayer->m_bMsgNum++;
  797. pCurrentPlayer->m_bSeqNum = 0;
  798. pCurrentPlayer->m_bLastSent = TRUE;
  799. }
  800. else
  801. {
  802. pCurrentPlayer->m_bSeqNum++;
  803. }
  804. pCurrentPlayer->m_pbMsgNumToSend[lThreadIndex] = pCurrentPlayer->m_bMsgNum;
  805. pCurrentPlayer->m_pbSeqNumToSend[lThreadIndex] = pCurrentPlayer->m_bSeqNum;
  806. pCurrentPlayer->AddToHearingList( lThreadIndex, &m_prWorkerControl[lThreadIndex].m_blMixingHearingPlayers );
  807. }
  808. // We can hear > 1 person, we need to mark each person as needing decompression
  809. else if( pCurrentPlayer->m_pdwHearCount[lThreadIndex] > 1 )
  810. {
  811. if( !pComparePlayer->m_pfNeedsDecompression[lThreadIndex] )
  812. {
  813. // Add this player to the list of people who need to be decompressed
  814. pComparePlayer->AddToSpeakingList( lThreadIndex, &m_prWorkerControl[lThreadIndex].m_blMixingSpeakingPlayers );
  815. pComparePlayer->m_pfNeedsDecompression[lThreadIndex] = TRUE;
  816. }
  817. // Special case, we just transitioned to having > 1 people heard by this player,
  818. // we should mark the first person we can hear for decompression as well
  819. if( pCurrentPlayer->m_pdwHearCount[lThreadIndex] == 2 )
  820. {
  821. pTmpPlayer = (pCurrentPlayer->m_pppCanHear[lThreadIndex])[0];
  822. if( !pTmpPlayer->m_pfNeedsDecompression[lThreadIndex] )
  823. {
  824. pTmpPlayer->AddToSpeakingList( lThreadIndex, &m_prWorkerControl[lThreadIndex].m_blMixingSpeakingPlayers );
  825. pTmpPlayer->m_pfNeedsDecompression[lThreadIndex] = TRUE;
  826. }
  827. }
  828. }
  829. // We need to break out of the loop as we only need to add an individual player once to the
  830. // list of people a player can hear.
  831. break;
  832. }
  833. }
  834. }
  835. }
  836. if( !pCurrentPlayer->m_pdwHearCount[lThreadIndex] )
  837. {
  838. pCurrentPlayer->m_bLastSent = FALSE;
  839. }
  840. else
  841. {
  842. pCurrentPlayer->m_bLastSent = TRUE;
  843. }
  844. }
  845. m_pStats->m_dwPreMixingPassTimeHistoryLoc++;
  846. m_pStats->m_dwPreMixingPassTimeHistoryLoc %= MIXING_HISTORY;
  847. m_pStats->m_dwPreMixingPassTimeHistory[m_pStats->m_dwPreMixingPassTimeHistoryLoc] = GetTickCount() - dwTickCountStart;
  848. SetEvent( m_prWorkerControl[lThreadIndex].hThreadDoWork );
  849. }
  850. #undef DPF_MODNAME
  851. #define DPF_MODNAME "CDirectVoiceServerEngine::MixingServerWakeupProc"
  852. // MixingServerWakeupProc
  853. //
  854. // This function is called by the windows timer used by this
  855. // class each time the timer goes off. The function signals
  856. // a semaphore provided by the creator of the timer.
  857. //
  858. // Parameters:
  859. // DWORD param - A recast pointer to a HANDLE
  860. BOOL CDirectVoiceServerEngine::MixingServerWakeupProc( DWORD_PTR param )
  861. {
  862. HANDLE *semaphore = (HANDLE *) param;
  863. ReleaseSemaphore( *semaphore, 1, NULL );
  864. return TRUE;
  865. }
  866. #undef DPF_MODNAME
  867. #define DPF_MODNAME "CDirectVoiceServerEngine::HandleMixingReceive"
  868. HRESULT CDirectVoiceServerEngine::HandleMixingReceive( CDVCSPlayer *pTargetPlayer, PDVPROTOCOLMSG_SPEECHWITHTARGET pdvSpeechWithtarget, DWORD dwSpeechSize, PBYTE pSourceSpeech )
  869. {
  870. HRESULT hr;
  871. DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "Mixing Server Speech Handler" );
  872. hr = pTargetPlayer->HandleMixingReceive( &pdvSpeechWithtarget->dvHeader, pSourceSpeech, dwSpeechSize, (PDVID) &pdvSpeechWithtarget[1], pdvSpeechWithtarget->dwNumTargets );
  873. DPFX(DPFPREP, DVF_CLIENT_SEQNUM_DEBUG_LEVEL, "SEQ: Receive: Msg [%d] Seq [%d]", pdvSpeechWithtarget->dvHeader.bMsgNum, pdvSpeechWithtarget->dvHeader.bSeqNum );
  874. return hr;
  875. }
  876. #undef DPF_MODNAME
  877. #define DPF_MODNAME "CDirectVoiceClientEngine::UpdateActiveMixingPendingList"
  878. void CDirectVoiceServerEngine::UpdateActiveMixingPendingList( DWORD dwThreadIndex, DWORD *pdwNumActive)
  879. {
  880. BILINK *pblSearch;
  881. CDVCSPlayer *pVoicePlayer;
  882. DNEnterCriticalSection( &m_prWorkerControl[dwThreadIndex].m_csMixingAddList );
  883. // Add players who are pending
  884. pblSearch = m_prWorkerControl[dwThreadIndex].m_blMixingAddPlayers.next;
  885. while( pblSearch != &m_prWorkerControl[dwThreadIndex].m_blMixingAddPlayers )
  886. {
  887. pVoicePlayer = CONVERTTORECORD( pblSearch, CDVCSPlayer, m_pblMixingActivePlayers[dwThreadIndex] );
  888. pVoicePlayer->RemoveFromMixingList(dwThreadIndex);
  889. pVoicePlayer->AddToMixingList( dwThreadIndex, &m_prWorkerControl[dwThreadIndex].m_blMixingActivePlayers );
  890. pblSearch = m_prWorkerControl[dwThreadIndex].m_blMixingAddPlayers.next;
  891. }
  892. DNASSERT( (IsEmpty( m_prWorkerControl[dwThreadIndex].m_blMixingAddPlayers) ) );
  893. DNLeaveCriticalSection( &m_prWorkerControl[dwThreadIndex].m_csMixingAddList );
  894. *pdwNumActive = 0;
  895. // Remove players who have disconnected
  896. pblSearch = m_prWorkerControl[dwThreadIndex].m_blMixingActivePlayers.next;
  897. while( pblSearch != &m_prWorkerControl[dwThreadIndex].m_blMixingActivePlayers )
  898. {
  899. pVoicePlayer = CONVERTTORECORD( pblSearch, CDVCSPlayer, m_pblMixingActivePlayers[dwThreadIndex] );
  900. pblSearch = pblSearch->next;
  901. // If current player has disconnected, remove them from active list
  902. // and release the reference the list has
  903. if( pVoicePlayer->IsDisconnected() )
  904. {
  905. pVoicePlayer->RemoveFromMixingList(dwThreadIndex);
  906. pVoicePlayer->Release();
  907. }
  908. else
  909. {
  910. (*pdwNumActive)++;
  911. }
  912. }
  913. }
  914. #undef DPF_MODNAME
  915. #define DPF_MODNAME "CDirectVoiceClientEngine::CleanupMixingList"
  916. void CDirectVoiceServerEngine::CleanupMixingList()
  917. {
  918. BILINK *pblSearch;
  919. CDVCSPlayer *pVoicePlayer;
  920. for( DWORD dwIndex = 0; dwIndex < m_dwNumMixingThreads; dwIndex++ )
  921. {
  922. DNEnterCriticalSection( &m_prWorkerControl[dwIndex].m_csMixingAddList );
  923. // Add players who are pending
  924. pblSearch = m_prWorkerControl[dwIndex].m_blMixingAddPlayers.next;
  925. while( pblSearch != &m_prWorkerControl[dwIndex].m_blMixingAddPlayers )
  926. {
  927. pVoicePlayer = CONVERTTORECORD( pblSearch, CDVCSPlayer, m_pblMixingActivePlayers[dwIndex] );
  928. pblSearch = pblSearch->next;
  929. pVoicePlayer->RemoveFromMixingList(dwIndex);
  930. pVoicePlayer->Release();
  931. }
  932. DNLeaveCriticalSection( &m_prWorkerControl[dwIndex].m_csMixingAddList );
  933. DNASSERT( (IsEmpty(m_prWorkerControl[dwIndex].m_blMixingAddPlayers )) );
  934. pblSearch = m_prWorkerControl[dwIndex].m_blMixingActivePlayers.next;
  935. while( pblSearch != &m_prWorkerControl[dwIndex].m_blMixingActivePlayers )
  936. {
  937. pVoicePlayer = CONVERTTORECORD( pblSearch, CDVCSPlayer, m_pblMixingActivePlayers[dwIndex] );
  938. pblSearch = pblSearch->next;
  939. pVoicePlayer->RemoveFromMixingList(dwIndex);
  940. pVoicePlayer->Release();
  941. }
  942. }
  943. }