Leaked source code of windows server 2003
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.

994 lines
27 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: dvsndt.cpp
  6. * Content: Implementation of CSoundTarget class
  7. *
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 09/02/99 rodtoll Created
  12. * 09/08/99 rodtoll Updated to handle lockup of playback buffer
  13. * rodtoll Added handling for restarting playback buffer or
  14. * handling slowdown/speedup of buffer playback
  15. * because of high cpu loads.
  16. * rodtoll Added write-ahead of silence to the buffers to that
  17. * in high CPU conditions silence will be played instead
  18. * of old voice.
  19. * 09/14/99 rodtoll Added new WriteAheadSilence which writes silence ahead
  20. * of the current write location to prevent high CPU from
  21. * playing old data.
  22. * 09/20/99 rodtoll Added checks for memory allocation failures
  23. * rodtoll Added handlers for buffer loss
  24. * 10/05/99 rodtoll Added additional comments
  25. * 10/25/99 rodtoll Fix: Bug #114223 - Debug messages being printed at error level when inappropriate
  26. * 11/02/99 pnewson Fix: Bug #116365 - using wrong DSBUFFERDESC
  27. * 11/12/99 rodtoll Updated to use new abstractions for playback (allows use
  28. * of waveOut with this class).
  29. * 11/13/99 rodtoll Re-activated code which pushes write pointer ahead if
  30. * buffer pointer passes us.
  31. * 01/24/2000 rodtoll Fix: Bug #129427 - Destroying transport before calling Delete3DSound
  32. * 01/27/2000 rodtoll Bug #129934 - Update SoundTargets to take DSBUFFERDESC
  33. * 02/17/2000 rodtoll Bug #133691 - Choppy audio - queue was not adapting
  34. * Added instrumentation
  35. * 04/14/2000 rodtoll Bug #32215 - Voice Conference Lost after resume from hibernation
  36. * Updated code to use new restore handling in dsound layer
  37. * 05/17/2000 rodtoll Bug #35110 Simultaneous playback of 2 voices results in distorted playback
  38. * 06/21/2000 rodtoll Fix: Bug #35767 - Must implement ability for dsound effects on voice buffers
  39. * Added new constructor/init that takes pre-built buffers
  40. * 07/09/2000 rodtoll Added signature bytes
  41. * 07/28/2000 rodtoll Bug #40665 - DirectSound reports 1 buffer leaked
  42. * 11/16/2000 rodtoll Bug #47783 - DPVOICE: Improve debugging of failures caused by DirectSound errors.
  43. * 04/02/2001 simonpow Bug #354859 Fixes for PREfast (initialising local variables in RestoreLostBuffer)
  44. * 04/21/2001 rodtoll MANBUG #50058 DPVOICE: VoicePosition: No sound for couple of seconds when position bars are moved
  45. * - Added initialization for uninitialized variables
  46. * - Removed ifdef'ed out code.
  47. *
  48. ***************************************************************************/
  49. #include "dxvoicepch.h"
  50. #define SOUNDTARGET_WRITEAHEAD 2
  51. // Max # of restarts to attempt on a buffer
  52. #define SOUNDTARGET_MAX_RESTARTS 10
  53. // Max # of frames of silence which are written ahead of the latest frame
  54. // of audio
  55. #define SOUNDTARGET_MAX_WRITEAHEAD 3
  56. #undef DPF_MODNAME
  57. #define DPF_MODNAME "CSoundTarget::CSoundTarget"
  58. //
  59. // Constructor
  60. //
  61. // This constructor is used when a DIRECTSOUND buffer needs to be created. If there is already
  62. // a DIRECTSOUNDBUFFER you wish to attach the sound target object to, use the other constructor
  63. // type.
  64. //
  65. CSoundTarget::CSoundTarget(
  66. DVID dvidTarget, CAudioPlaybackDevice *lpPlaybackDevice,
  67. LPDSBUFFERDESC lpdsBufferDesc, DWORD dwPriority,
  68. DWORD dwFlags, DWORD dwFrameSize
  69. ): m_lpds3dBuffer(NULL),
  70. m_lpAudioPlaybackBuffer(NULL),
  71. m_lpMixBuffer(NULL),
  72. m_dwSignature(VSIG_SOUNDTARGET),
  73. m_lRefCount(1)
  74. {
  75. CAudioPlaybackBuffer *lpdsBuffer;
  76. LPVOID lpvBuffer1, lpvBuffer2;
  77. DWORD dwBufferSize1, dwBufferSize2;
  78. Stats_Init();
  79. m_hrInitResult = lpPlaybackDevice->CreateBuffer( lpdsBufferDesc, dwFrameSize, &lpdsBuffer );
  80. if( FAILED( m_hrInitResult ) )
  81. {
  82. DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not create the sound buffer hr=0x%x", m_hrInitResult );
  83. return;
  84. }
  85. m_hrInitResult = lpdsBuffer->Lock( 0, 0, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, DSBLOCK_ENTIREBUFFER );
  86. if( FAILED( m_hrInitResult ) )
  87. {
  88. DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not lock the sound buffer hr=0x%x", m_hrInitResult );
  89. m_hrInitResult = DVERR_LOCKEDBUFFER;
  90. return;
  91. }
  92. if( lpdsBufferDesc->lpwfxFormat->wBitsPerSample == 8 )
  93. {
  94. memset( lpvBuffer1, 0x80, dwBufferSize1 );
  95. }
  96. else
  97. {
  98. memset( lpvBuffer1, 0x00, dwBufferSize1 );
  99. }
  100. m_hrInitResult = lpdsBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
  101. if( FAILED( m_hrInitResult ) )
  102. {
  103. DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not unlock the sound buffer hr=0x%x", m_hrInitResult );
  104. return;
  105. }
  106. // Required always
  107. dwFlags |= DSBPLAY_LOOPING;
  108. m_hrInitResult = lpdsBuffer->Play( dwPriority, dwFlags );
  109. if( FAILED( m_hrInitResult ) )
  110. {
  111. DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not play the sound buffer hr=0x%x", m_hrInitResult );
  112. return;
  113. }
  114. m_hrInitResult = Initialize( dvidTarget, lpdsBuffer, (lpdsBufferDesc->lpwfxFormat->wBitsPerSample==8), dwPriority, dwFlags, dwFrameSize );
  115. }
  116. #undef DPF_MODNAME
  117. #define DPF_MODNAME "CSoundTarget::RestoreLostBuffer"
  118. //
  119. // RestoreLostBuffer
  120. //
  121. // Handles restoration of lost directsound buffers.
  122. //
  123. HRESULT CSoundTarget::RestoreLostBuffer()
  124. {
  125. HRESULT hr = DSERR_BUFFERLOST;
  126. DPFX(DPFPREP, 0, "Restoring lost buffer" );
  127. while( hr == DSERR_BUFFERLOST )
  128. {
  129. hr = m_lpAudioPlaybackBuffer->Restore();
  130. DPFX(DPFPREP, 0, "Buffer result for restore was 0x%x", hr );
  131. if( hr == DS_OK )
  132. {
  133. hr = m_lpAudioPlaybackBuffer->GetCurrentPosition( &m_dwWritePos );
  134. DPFX(DPFPREP, 0, "GetCurrentPos returned 0x%x", hr );
  135. if( hr != DSERR_BUFFERLOST && FAILED( hr ) )
  136. {
  137. DPFX(DPFPREP, DVF_ERRORLEVEL, "Lost buffer while getting pos, failed to get pos hr=0x%x", hr );
  138. return hr;
  139. }
  140. }
  141. else if( hr != DSERR_BUFFERLOST )
  142. {
  143. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error restoring buffer hr=0x%x", hr );
  144. return hr;
  145. }
  146. }
  147. // STATBLOCK: Begin
  148. m_statPlay.m_dwNumBL++;
  149. // STATBLOCK: End
  150. m_dwNextWritePos = m_dwWritePos + (m_dwFrameSize*SOUNDTARGET_WRITEAHEAD);
  151. m_dwNextWritePos %= m_dwBufferSize;
  152. m_dwLastWritePos = m_dwWritePos;
  153. return hr;
  154. }
  155. #undef DPF_MODNAME
  156. #define DPF_MODNAME "CSoundTarget::CSoundTarget"
  157. //
  158. // Constructor
  159. //
  160. // This constructor is used when you wish to attach a soundtarget to an existing
  161. // DirectSond buffer.
  162. //
  163. CSoundTarget::CSoundTarget(
  164. DVID dvidTarget, CAudioPlaybackDevice *lpads,
  165. CAudioPlaybackBuffer *lpdsBuffer, LPDSBUFFERDESC lpdsBufferDesc,
  166. DWORD dwPriority, DWORD dwFlags, DWORD dwFrameSize
  167. ): m_lpds3dBuffer(NULL),
  168. m_lpAudioPlaybackBuffer(NULL),
  169. m_lpMixBuffer(NULL),
  170. m_dwSignature(VSIG_SOUNDTARGET),
  171. m_lRefCount(1)
  172. {
  173. m_hrInitResult = Initialize( dvidTarget, lpdsBuffer, (lpdsBufferDesc->lpwfxFormat->wBitsPerSample==8), dwPriority, dwFlags, dwFrameSize );
  174. }
  175. #undef DPF_MODNAME
  176. #define DPF_MODNAME "CSoundTarget::CSoundTarget"
  177. CSoundTarget::CSoundTarget(
  178. DVID dvidTarget,
  179. CAudioPlaybackDevice *lpads,
  180. LPDIRECTSOUNDBUFFER lpdsBuffer,
  181. BOOL fEightBit,
  182. DWORD dwPriority,
  183. DWORD dwFlags,
  184. DWORD dwFrameSize
  185. ): m_lpds3dBuffer(NULL),
  186. m_lpAudioPlaybackBuffer(NULL),
  187. m_lpMixBuffer(NULL),
  188. m_dwSignature(VSIG_SOUNDTARGET),
  189. m_lRefCount(1)
  190. {
  191. CDirectSoundPlaybackBuffer *pAudioBuffer = NULL;
  192. // Create audio buffer to wrap buffer for call to initialize
  193. pAudioBuffer = new CDirectSoundPlaybackBuffer( lpdsBuffer );
  194. if( pAudioBuffer == NULL )
  195. {
  196. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error allocating memory" );
  197. m_hrInitResult = DVERR_OUTOFMEMORY;
  198. return;
  199. }
  200. // Required always
  201. dwFlags |= DSBPLAY_LOOPING;
  202. m_hrInitResult = pAudioBuffer->Play( dwPriority, dwFlags );
  203. if( FAILED( m_hrInitResult ) )
  204. {
  205. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed playing new buffer hr=0x%x", m_hrInitResult );
  206. return;
  207. }
  208. m_hrInitResult = Initialize( dvidTarget, pAudioBuffer, fEightBit, dwPriority, dwFlags, dwFrameSize );
  209. }
  210. #undef DPF_MODNAME
  211. #define DPF_MODNAME "CSoundTarget::~CSoundTarget"
  212. //
  213. // Destructor
  214. //
  215. // Destroys the 3d and buffer pointers and frees memory.
  216. //
  217. CSoundTarget::~CSoundTarget()
  218. {
  219. Stats_End();
  220. if( m_lpds3dBuffer != NULL )
  221. {
  222. m_lpds3dBuffer->Release();
  223. m_lpds3dBuffer = NULL;
  224. }
  225. if( m_lpAudioPlaybackBuffer != NULL )
  226. {
  227. m_lpAudioPlaybackBuffer->Stop();
  228. delete m_lpAudioPlaybackBuffer;
  229. m_lpAudioPlaybackBuffer = NULL;
  230. }
  231. if( m_lpMixBuffer != NULL )
  232. {
  233. delete [] m_lpMixBuffer;
  234. m_lpMixBuffer = NULL;
  235. }
  236. if( SUCCEEDED( m_hrInitResult ) )
  237. {
  238. DNDeleteCriticalSection( &m_csGuard );
  239. }
  240. m_dwSignature = VSIG_SOUNDTARGET_FREE;
  241. }
  242. #undef DPF_MODNAME
  243. #define DPF_MODNAME "CSoundTarget::AdjustWritePtr"
  244. //
  245. // AdjustWritePtr
  246. //
  247. // This function ensures that the directsoundbuffer is acting as it should. It handles
  248. // checking the write pointer and determining if some form of error or problem has
  249. // occured and taking the appropriate corrective action.
  250. //
  251. // E.g. the buffer hasn't moved since the last frame, the buffer hasn't moved enough,
  252. // the buffer has skipped ahead etc.
  253. //
  254. // This function should be called once per mixing pass.
  255. //
  256. // Call before writing the frame
  257. //
  258. HRESULT CSoundTarget::AdjustWritePtr()
  259. {
  260. HRESULT hr;
  261. LONG lDifference, lHalfSize;
  262. DWORD dwCurrentTick;
  263. hr = m_lpAudioPlaybackBuffer->GetCurrentPosition( &m_dwWritePos );
  264. if( FAILED( hr ) )
  265. {
  266. DPFX(DPFPREP, DVF_ERRORLEVEL, "GetCurrentPosition Failed hr=0x%x", hr );
  267. DSASSERT( FALSE );
  268. return hr;
  269. }
  270. // STATSBLOCK: Begin
  271. m_statPlay.m_dwNumRuns++;
  272. // STATSBLOCK: End
  273. DWORD dwTmpAdvance, dwTmpLead;
  274. dwCurrentTick = GetTickCount();
  275. if( m_dwLastWritePos > m_dwWritePos )
  276. {
  277. dwTmpAdvance = (m_dwBufferSize - m_dwLastWritePos) + m_dwWritePos;
  278. }
  279. else
  280. {
  281. dwTmpAdvance = m_dwWritePos - m_dwLastWritePos;
  282. }
  283. if( m_dwNextWritePos < m_dwWritePos )
  284. {
  285. dwTmpLead = (m_dwBufferSize-m_dwWritePos) + m_dwNextWritePos;
  286. }
  287. else
  288. {
  289. dwTmpLead = m_dwNextWritePos - m_dwWritePos;
  290. }
  291. DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], %d, %d, %d, %d, %d, %d", m_dvidTarget, m_dwWritePos, dwTmpAdvance,
  292. dwCurrentTick - m_dwLastWriteTime, m_dwNextWritePos, dwTmpLead, m_dwFrameSize );
  293. // STATSBLOCK: Begin
  294. DWORD dwTmpDiff = dwCurrentTick - m_dwLastWriteTime;
  295. if( dwTmpDiff > m_statPlay.m_dwPMMSMax )
  296. {
  297. m_statPlay.m_dwPMMSMax = dwTmpDiff;
  298. }
  299. if( dwTmpDiff < m_statPlay.m_dwPMMSMin )
  300. {
  301. m_statPlay.m_dwPMMSMin = dwTmpDiff;
  302. }
  303. m_statPlay.m_dwPMMSTotal += dwTmpDiff;
  304. if( dwTmpAdvance > m_statPlay.m_dwPMBMax )
  305. {
  306. m_statPlay.m_dwPMBMax = dwTmpAdvance;
  307. }
  308. if( dwTmpAdvance < m_statPlay.m_dwPMBMin )
  309. {
  310. m_statPlay.m_dwPMBMin = dwTmpAdvance;
  311. }
  312. m_statPlay.m_dwPMBTotal += dwTmpAdvance;
  313. if( dwTmpLead > m_statPlay.m_dwPLMax )
  314. {
  315. m_statPlay.m_dwPLMax = dwTmpLead;
  316. }
  317. if( dwTmpLead < m_statPlay.m_dwPLMin )
  318. {
  319. m_statPlay.m_dwPLMin = dwTmpLead;
  320. }
  321. m_statPlay.m_dwPLTotal += dwTmpLead;
  322. // STATSBLOCK: End
  323. lHalfSize = m_dwBufferSize / 2;
  324. lDifference = m_dwNextWritePos - m_dwWritePos;
  325. // If the write position is somehow ahead of position AND
  326. //
  327. if( lDifference < 0 &&
  328. lDifference > (-1*lHalfSize) )
  329. {
  330. m_dwNextWritePos = m_dwWritePos;
  331. m_dwNextWritePos += (m_dwFrameSize * SOUNDTARGET_WRITEAHEAD);
  332. m_dwNextWritePos %= m_dwBufferSize;
  333. DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], Punt --> %d", m_dvidTarget, m_dwNextWritePos );
  334. // STATSBLOCK: Begin
  335. m_statPlay.m_dwPPunts++;
  336. DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: [0x%x] Playback: Write pointer has fallen behind buffer pointer. Compensating", m_dvidTarget );
  337. m_statPlay.m_dwGlitches++;
  338. // STATSBLOCK: End
  339. }
  340. m_dwLastWritePos = m_dwWritePos;
  341. m_dwLastWriteTime = dwCurrentTick;
  342. if( m_dwNextWritePos < m_dwWritePos &&
  343. (m_dwNextWritePos + m_dwFrameSize) > m_dwWritePos )
  344. {
  345. DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], Ignore - Crossing", m_dvidTarget );
  346. m_fIgnoreFrame = TRUE;
  347. // STATSBLOCK: Begin
  348. m_statPlay.m_dwPIgnore++;
  349. DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: [0x%x] Playback: Current frame will cross buffer pointer. Ignoring", m_dvidTarget );
  350. DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: [0x%x] Playback: May be catching up with buiffer pointer", m_dvidTarget );
  351. m_statPlay.m_dwGlitches++;
  352. // STATSBLOCK: End
  353. }
  354. else
  355. {
  356. m_fIgnoreFrame = FALSE;
  357. }
  358. return DV_OK;
  359. }
  360. #undef DPF_MODNAME
  361. #define DPF_MODNAME "CSoundTarget::WriteAheadSilence"
  362. //
  363. // WriteAheadSilence
  364. //
  365. // This function is responsible for writing frames of silence ahead of the latest frame
  366. // placed in the buffer. This way if because of high CPU writer thread donesn't get woken
  367. // up silence will be played instead of old speech.
  368. //
  369. // Called AFTER latest frame of data has been written.
  370. //
  371. HRESULT CSoundTarget::WriteAheadSilence()
  372. {
  373. HRESULT hr;
  374. DWORD dwBufferSize1, dwBufferSize2;
  375. LPVOID lpvBuffer1, lpvBuffer2;
  376. if( m_dwNextWritePos < m_dwWritePos &&
  377. (m_dwNextWritePos + (m_dwFrameSize*SOUNDTARGET_MAX_WRITEAHEAD)) > m_dwWritePos )
  378. {
  379. DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], Ignore2 - Crossing", m_dvidTarget );
  380. // STATSBLOCK: Begin
  381. m_statPlay.m_dwSIgnore++;
  382. DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: Playback: Silence will cross buffer pointer. Ignoring" );
  383. DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: Playback: May be catching up with buiffer pointer" );
  384. // STATSBLOCK: End
  385. return DV_OK;
  386. }
  387. hr = m_lpAudioPlaybackBuffer->Lock( m_dwNextWritePos, m_dwFrameSize*SOUNDTARGET_MAX_WRITEAHEAD, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, 0 );
  388. if( FAILED( hr ) )
  389. {
  390. DSASSERT( FALSE );
  391. DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() Failed hr=0x%x", hr );
  392. return hr;
  393. }
  394. memset( lpvBuffer1, (m_fEightBit) ? 0x80 : 0x00, dwBufferSize1 );
  395. memset( lpvBuffer2, (m_fEightBit) ? 0x80 : 0x00, dwBufferSize2 );
  396. hr = m_lpAudioPlaybackBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
  397. if( FAILED( hr ) )
  398. {
  399. DSASSERT( FALSE );
  400. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unlock() Failed hr=0x%x", hr );
  401. return hr;
  402. }
  403. return DV_OK;
  404. }
  405. #undef DPF_MODNAME
  406. #define DPF_MODNAME "CSoundTarget::MixInSingle"
  407. //
  408. // MixInSingle
  409. //
  410. // This function is an optimization.
  411. //
  412. // If there is only one frame to mix into this buffer, this function performs the
  413. // MixIn and Commit all in one step. You still must call Commit before the next
  414. // frame hwoever.
  415. //
  416. HRESULT CSoundTarget::MixInSingle( LPBYTE lpbBuffer )
  417. {
  418. HRESULT hr;
  419. DWORD dwBufferSize1, dwBufferSize2;
  420. LPVOID lpvBuffer1, lpvBuffer2;
  421. hr = AdjustWritePtr();
  422. if( FAILED( hr ) )
  423. {
  424. DSASSERT( FALSE );
  425. DPFX(DPFPREP, DVF_ERRORLEVEL, "AdjustWritePtr Failed hr=0x%x", hr );
  426. return hr;
  427. }
  428. if( !m_fIgnoreFrame )
  429. {
  430. hr = m_lpAudioPlaybackBuffer->Lock( m_dwNextWritePos, m_dwFrameSize, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, 0 );
  431. if( FAILED( hr ) )
  432. {
  433. DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() Failed hr=0x%x", hr );
  434. return hr;
  435. }
  436. memcpy( lpvBuffer1, lpbBuffer, dwBufferSize1 );
  437. if( dwBufferSize2 )
  438. {
  439. memcpy( lpvBuffer2, &lpbBuffer[dwBufferSize1], dwBufferSize2 );
  440. }
  441. hr = m_lpAudioPlaybackBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
  442. if( FAILED( hr ) )
  443. {
  444. DSASSERT( FALSE );
  445. DPFX(DPFPREP, DVF_ERRORLEVEL, "UnLock() Failed hr=0x%x", hr );
  446. return hr;
  447. }
  448. }
  449. m_dwNextWritePos += m_dwFrameSize;
  450. m_dwNextWritePos %= m_dwBufferSize;
  451. m_bCommited = TRUE;
  452. m_fMixed = TRUE;
  453. return DV_OK;
  454. }
  455. #undef DPF_MODNAME
  456. #define DPF_MODNAME "CSoundTarget::MixIn"
  457. // Mix in a user's audio.
  458. //
  459. // Simply copies and promotes the audio samples into the buffer with LONG's
  460. // in it.
  461. //
  462. HRESULT CSoundTarget::MixIn( const BYTE* lpbBuffer )
  463. {
  464. DWORD dwIndex;
  465. if( !m_fMixed )
  466. {
  467. FillBufferWithSilence( m_lpMixBuffer, m_fEightBit, m_dwFrameSize );
  468. m_fMixed = TRUE;
  469. }
  470. if( !m_fIgnoreFrame )
  471. MixInBuffer( m_lpMixBuffer, lpbBuffer, m_fEightBit, m_dwFrameSize );
  472. m_bCommited = FALSE;
  473. m_fMixed = TRUE;
  474. return DV_OK;
  475. }
  476. #undef DPF_MODNAME
  477. #define DPF_MODNAME "CSoundTarget::Commit"
  478. // Commit
  479. //
  480. // If we didn't do a single direct mix, commit the mixed audio to the buffer
  481. //
  482. HRESULT CSoundTarget::Commit()
  483. {
  484. DWORD dwBufferSize1, dwBufferSize2;
  485. LPVOID lpvBuffer1, lpvBuffer2;
  486. DWORD dwIndex = 0;
  487. HRESULT hr;
  488. if( !m_fMixed )
  489. {
  490. FillBufferWithSilence( m_lpMixBuffer, m_fEightBit, m_dwFrameSize );
  491. // STATSBLOCK: Begin
  492. m_statPlay.m_dwNumSilentMixed++;
  493. // STATSBLOCK: End
  494. }
  495. else
  496. {
  497. if( !m_fIgnoreFrame )
  498. {
  499. // STATSBLOCK: Begin
  500. m_statPlay.m_dwNumMixed++;
  501. // STATSBLOCK: End
  502. }
  503. }
  504. if( !m_bCommited || !m_fMixed )
  505. {
  506. hr = AdjustWritePtr();
  507. if( FAILED( hr ) )
  508. {
  509. DSASSERT( FALSE );
  510. DPFX(DPFPREP, DVF_ERRORLEVEL, "AdjustWritePtr() Failed hr=0x%x", hr );
  511. return hr;
  512. }
  513. if( !m_fIgnoreFrame )
  514. {
  515. hr = m_lpAudioPlaybackBuffer->Lock( m_dwNextWritePos, m_dwFrameSize, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, 0 );
  516. if( FAILED( hr ) )
  517. {
  518. DSASSERT( FALSE );
  519. DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() Failed hr=0x%x", hr );
  520. return hr;
  521. }
  522. // Mix first half
  523. NormalizeBuffer( (BYTE *) lpvBuffer1, m_lpMixBuffer, m_fEightBit, dwBufferSize1 );
  524. if( dwBufferSize2 > 0 )
  525. {
  526. if( m_fEightBit )
  527. {
  528. // Mix second half
  529. NormalizeBuffer( (BYTE *) lpvBuffer2, &m_lpMixBuffer[dwBufferSize1], m_fEightBit, dwBufferSize2 );
  530. }
  531. else
  532. {
  533. // Mix second half
  534. NormalizeBuffer( (BYTE *) lpvBuffer2, &m_lpMixBuffer[(dwBufferSize1 >> 1)], m_fEightBit, dwBufferSize2 );
  535. }
  536. }
  537. hr = m_lpAudioPlaybackBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
  538. if( FAILED( hr ) )
  539. {
  540. DSASSERT( FALSE );
  541. DPFX(DPFPREP, DVF_ERRORLEVEL, "UnLock Failed hr=0x%x", hr );
  542. return hr;
  543. }
  544. }
  545. m_dwNextWritePos += m_dwFrameSize;
  546. m_dwNextWritePos %= m_dwBufferSize;
  547. m_bCommited = FALSE;
  548. m_fMixed = FALSE;
  549. return DV_OK;
  550. }
  551. hr = WriteAheadSilence();
  552. if( FAILED( hr ) )
  553. {
  554. DSASSERT( FALSE );
  555. DPFX(DPFPREP, 0, "WriteAhead Failed hr=0x%x", hr );
  556. }
  557. m_fMixed = FALSE;
  558. m_bCommited = FALSE;
  559. return DV_OK;
  560. }
  561. #undef DPF_MODNAME
  562. #define DPF_MODNAME "CSoundTarget::Initialize"
  563. //
  564. // Initialize
  565. //
  566. // NOTE: Takes a reference to the buffer
  567. //
  568. // Attaches a sound target to the specified sound buffer, initializes the object and creates
  569. // the associated 3d buffer.
  570. //
  571. HRESULT CSoundTarget::Initialize( DVID dvidTarget, CAudioPlaybackBuffer *lpdsBuffer, BOOL fEightBit, DWORD dwPriority, DWORD dwFlags, DWORD dwFrameSize )
  572. {
  573. HRESULT hr = DV_OK;
  574. PVOID pvBuffer1 = NULL, pvBuffer2 = NULL;
  575. DWORD dwBufferSize1, dwBufferSize2;
  576. // Determine the buffer size (in bytes)
  577. hr = lpdsBuffer->Lock( 0, 0, &pvBuffer1, &dwBufferSize1, &pvBuffer2, &dwBufferSize2, DSBLOCK_ENTIREBUFFER );
  578. if( FAILED( hr ) )
  579. {
  580. DSASSERT( FALSE );
  581. DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() failed hr=0x%x", hr );
  582. return hr;
  583. }
  584. hr = lpdsBuffer->UnLock( pvBuffer1, dwBufferSize1, pvBuffer2, dwBufferSize2 );
  585. if( FAILED( hr ) )
  586. {
  587. DSASSERT( FALSE );
  588. DPFX(DPFPREP, DVF_ERRORLEVEL, "UnLock() failed hr=0x%x", hr );
  589. return hr;
  590. }
  591. m_lpds3dBuffer = NULL;
  592. m_lpAudioPlaybackBuffer = NULL;
  593. m_lpMixBuffer = NULL;
  594. // We always need the looping flag
  595. dwFlags |= DSBPLAY_LOOPING;
  596. m_dwPlayFlags = dwFlags;
  597. m_dwPriority = dwPriority;
  598. m_fIgnoreFrame = FALSE;
  599. m_dwLastWriteTime = GetTickCount();
  600. m_lpstNext = NULL;
  601. m_dwNumResets = 0;
  602. m_dwNumSinceMove = 1;
  603. m_bCommited = FALSE;
  604. m_dvidTarget = dvidTarget;
  605. m_dwFrameSize = dwFrameSize;
  606. m_dwBufferSize = dwBufferSize1;
  607. // STATSBLOCK: Begin
  608. Stats_Init();
  609. // STATSBLOCK: End
  610. m_fLastFramePushed = FALSE;
  611. m_lpAudioPlaybackBuffer = lpdsBuffer;
  612. hr = m_lpAudioPlaybackBuffer->Get3DBuffer(&m_lpds3dBuffer);
  613. if( FAILED( hr ) )
  614. {
  615. DSASSERT( FALSE );
  616. DPFX(DPFPREP, DVF_ERRORLEVEL, "QueryInterface failed hr=0x%x", hr );
  617. }
  618. m_dwNextWritePos = 0;
  619. if( fEightBit )
  620. {
  621. m_fEightBit = TRUE;
  622. m_dwMixSize = dwFrameSize;
  623. }
  624. else
  625. {
  626. m_fEightBit = FALSE;
  627. m_dwMixSize = dwFrameSize / 2;
  628. }
  629. m_lpMixBuffer = new LONG[m_dwMixSize];
  630. if( m_lpMixBuffer == NULL )
  631. {
  632. DPFX(DPFPREP, DVF_ERRORLEVEL, "Out of memory" );
  633. m_lpds3dBuffer->Release();
  634. m_lpds3dBuffer = NULL;
  635. return DVERR_OUTOFMEMORY;
  636. }
  637. if (!DNInitializeCriticalSection( &m_csGuard ))
  638. {
  639. return DVERR_OUTOFMEMORY;
  640. }
  641. m_fMixed = FALSE;
  642. return DV_OK;
  643. }
  644. #undef DPF_MODNAME
  645. #define DPF_MODNAME "CSoundTarget::AddRef"
  646. LONG CSoundTarget::AddRef()
  647. {
  648. LONG lNewCount;
  649. DNEnterCriticalSection( &m_csGuard );
  650. DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] lRefCount %d --> %d", m_dvidTarget, m_lRefCount, m_lRefCount+1 );
  651. m_lRefCount++;
  652. lNewCount = m_lRefCount;
  653. DNLeaveCriticalSection( &m_csGuard );
  654. return lNewCount;
  655. }
  656. #undef DPF_MODNAME
  657. #define DPF_MODNAME "CSoundTarget::Release"
  658. LONG CSoundTarget::Release()
  659. {
  660. LONG lNewCount;
  661. DNEnterCriticalSection( &m_csGuard );
  662. DNASSERT( m_lRefCount > 0 );
  663. DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] lRefCount %d --> %d", m_dvidTarget, m_lRefCount, m_lRefCount-1 );
  664. m_lRefCount--;
  665. lNewCount = m_lRefCount;
  666. DNLeaveCriticalSection( &m_csGuard );
  667. // Reference reaches 0, destroy!
  668. if( lNewCount == 0 )
  669. {
  670. DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] DESTROYING", m_dvidTarget, m_lRefCount, m_lRefCount+1 );
  671. delete this;
  672. }
  673. return lNewCount;
  674. }
  675. #undef DPF_MODNAME
  676. #define DPF_MODNAME "CSoundTarget::StartMix"
  677. //
  678. // StartMix
  679. //
  680. // Called ONCE only.
  681. //
  682. // Call this function right before you wish to perform the first mix on the buffer.
  683. // Initializes the object to match the current state of the associated directsound
  684. // buffer
  685. //
  686. HRESULT CSoundTarget::StartMix()
  687. {
  688. HRESULT hr;
  689. hr = m_lpAudioPlaybackBuffer->GetCurrentPosition( &m_dwWritePos );
  690. if( FAILED( hr ) )
  691. {
  692. DSASSERT( FALSE );
  693. DPFX(DPFPREP, DVF_ERRORLEVEL, "GetCurrentPosition Failed hr=0x%x", hr );
  694. return hr;
  695. }
  696. m_dwNextWritePos = m_dwWritePos + (m_dwFrameSize*SOUNDTARGET_WRITEAHEAD);
  697. m_dwNextWritePos %= m_dwBufferSize;
  698. m_dwLastWritePos = m_dwWritePos;
  699. // STATBLOCK: Begin
  700. Stats_Begin();
  701. // STATBLOCK: End
  702. return DV_OK;
  703. }
  704. void CSoundTarget::GetStats( PlaybackStats *statPlayback )
  705. {
  706. memcpy( statPlayback, &m_statPlay, sizeof( PlaybackStats ) );
  707. }
  708. void CSoundTarget::Stats_Init()
  709. {
  710. memset( &m_statPlay, 0x00, sizeof( PlaybackStats ) );
  711. m_statPlay.m_dwFrameSize = m_dwFrameSize;
  712. m_statPlay.m_dwBufferSize = m_dwBufferSize;
  713. m_statPlay.m_dwPMMSMin = 0xFFFFFFFF;
  714. m_statPlay.m_dwPMBMin = 0xFFFFFFFF;
  715. m_statPlay.m_dwPLMin = 0xFFFFFFFF;
  716. m_statPlay.m_dwTimeStart = GetTickCount();
  717. }
  718. void CSoundTarget::Stats_Begin()
  719. {
  720. m_statPlay.m_dwStartLag = GetTickCount() - m_statPlay.m_dwTimeStart;
  721. }
  722. void CSoundTarget::Stats_End()
  723. {
  724. char tmpBuffer[200];
  725. m_statPlay.m_dwTimeStop = GetTickCount();
  726. DWORD dwPlayRunLength = m_statPlay.m_dwTimeStop - m_statPlay.m_dwTimeStart;
  727. if( dwPlayRunLength == 0 )
  728. dwPlayRunLength = 1;
  729. DWORD dwNumInternalRuns = m_statPlay.m_dwNumRuns;
  730. if( dwNumInternalRuns == 0 )
  731. dwNumInternalRuns = 1;
  732. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "--- PLAYBACK BUFFER STATISTICS --------------------------------------[End]" );
  733. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Buffer for ID : 0x%x", m_dvidTarget );
  734. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Play Run Length (ms) : %u", dwPlayRunLength );
  735. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Start Lag : %u", m_statPlay.m_dwStartLag );
  736. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Speech Size (Uncomp.) : %u", m_statPlay.m_dwFrameSize );
  737. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Frames / Buffer : %u", m_statPlay.m_dwBufferSize / m_statPlay.m_dwFrameSize);
  738. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "# of wakeups : %u", m_statPlay.m_dwNumRuns );
  739. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Ignored Frames (Speech): %u", m_statPlay.m_dwPIgnore );
  740. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Ignored Frames (Silent): %u", m_statPlay.m_dwSIgnore );
  741. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Mixed Frames (Speech) : %u", m_statPlay.m_dwNumMixed );
  742. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Mixed Frames (Silent) : %u", m_statPlay.m_dwNumSilentMixed );
  743. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Punted Frames : %u", m_statPlay.m_dwPPunts );
  744. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Audio Glitches : %u", m_statPlay.m_dwGlitches );
  745. sprintf( tmpBuffer, "Play Movement (ms) : Avg: %u [%u..%u]",
  746. m_statPlay.m_dwPMMSTotal / dwNumInternalRuns,
  747. m_statPlay.m_dwPMMSMin,
  748. m_statPlay.m_dwPMMSMax );
  749. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  750. sprintf( tmpBuffer, "Play Movement (bytes) : Avg: %u [%u..%u]",
  751. m_statPlay.m_dwPMBTotal / dwNumInternalRuns,
  752. m_statPlay.m_dwPMBMin,
  753. m_statPlay.m_dwPMBMax );
  754. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  755. sprintf( tmpBuffer, "Play Movement (frames) : Avg: %.2f [%.2f..%.2f]",
  756. (((float) m_statPlay.m_dwPMBTotal) / ((float) dwNumInternalRuns)) / ((float) m_statPlay.m_dwFrameSize),
  757. ((float) m_statPlay.m_dwPMBMin) / ((float) m_statPlay.m_dwFrameSize),
  758. ((float) m_statPlay.m_dwPMBMax) / ((float) m_statPlay.m_dwFrameSize) );
  759. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  760. sprintf( tmpBuffer, "Play Lead (bytes) : Avg: %u [%u..%u]",
  761. m_statPlay.m_dwPLTotal / dwNumInternalRuns,
  762. m_statPlay.m_dwPLMin ,
  763. m_statPlay.m_dwPLMax );
  764. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  765. sprintf( tmpBuffer, "Play Lag (frames) : Avg: %.2f [%.2f..%.2f]",
  766. (float) ((float) m_statPlay.m_dwPLTotal / (float) dwNumInternalRuns) / ((float) m_statPlay.m_dwFrameSize),
  767. (float) ((float) m_statPlay.m_dwPLMin) / (float) m_statPlay.m_dwFrameSize,
  768. (float) ((float) m_statPlay.m_dwPLMax) / (float) m_statPlay.m_dwFrameSize );
  769. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  770. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "--- PLAYBACK BUFFER STATISTICS ------------------------------------[Begin]" );
  771. }
  772. HRESULT CSoundTarget::GetCurrentLead( PDWORD pdwLead )
  773. {
  774. HRESULT hr;
  775. hr = m_lpAudioPlaybackBuffer->GetCurrentPosition( &m_dwWritePos );
  776. if( m_dwNextWritePos < m_dwWritePos )
  777. {
  778. *pdwLead = (m_dwBufferSize-m_dwWritePos) + m_dwNextWritePos;
  779. }
  780. else
  781. {
  782. *pdwLead = m_dwNextWritePos - m_dwWritePos;
  783. }
  784. return DV_OK;
  785. }
  786. LPDIRECTSOUND3DBUFFER CSoundTarget::Get3DBuffer()
  787. {
  788. m_lpDummy = m_lpds3dBuffer;
  789. return m_lpds3dBuffer;
  790. }