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.

994 lines
26 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. {
  74. CAudioPlaybackBuffer *lpdsBuffer;
  75. LPVOID lpvBuffer1, lpvBuffer2;
  76. DWORD dwBufferSize1, dwBufferSize2;
  77. Stats_Init();
  78. m_hrInitResult = lpPlaybackDevice->CreateBuffer( lpdsBufferDesc, dwFrameSize, &lpdsBuffer );
  79. if( FAILED( m_hrInitResult ) )
  80. {
  81. DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not create the sound buffer hr=0x%x", m_hrInitResult );
  82. return;
  83. }
  84. m_hrInitResult = lpdsBuffer->Lock( 0, 0, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, DSBLOCK_ENTIREBUFFER );
  85. if( FAILED( m_hrInitResult ) )
  86. {
  87. DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not lock the sound buffer hr=0x%x", m_hrInitResult );
  88. m_hrInitResult = DVERR_LOCKEDBUFFER;
  89. return;
  90. }
  91. if( lpdsBufferDesc->lpwfxFormat->wBitsPerSample == 8 )
  92. {
  93. memset( lpvBuffer1, 0x80, dwBufferSize1 );
  94. }
  95. else
  96. {
  97. memset( lpvBuffer1, 0x00, dwBufferSize1 );
  98. }
  99. m_hrInitResult = lpdsBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
  100. if( FAILED( m_hrInitResult ) )
  101. {
  102. DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not unlock the sound buffer hr=0x%x", m_hrInitResult );
  103. return;
  104. }
  105. // Required always
  106. dwFlags |= DSBPLAY_LOOPING;
  107. m_hrInitResult = lpdsBuffer->Play( dwPriority, dwFlags );
  108. if( FAILED( m_hrInitResult ) )
  109. {
  110. DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not play the sound buffer hr=0x%x", m_hrInitResult );
  111. return;
  112. }
  113. m_hrInitResult = Initialize( dvidTarget, lpdsBuffer, (lpdsBufferDesc->lpwfxFormat->wBitsPerSample==8), dwPriority, dwFlags, dwFrameSize );
  114. }
  115. #undef DPF_MODNAME
  116. #define DPF_MODNAME "CSoundTarget::RestoreLostBuffer"
  117. //
  118. // RestoreLostBuffer
  119. //
  120. // Handles restoration of lost directsound buffers.
  121. //
  122. HRESULT CSoundTarget::RestoreLostBuffer()
  123. {
  124. HRESULT hr = DSERR_BUFFERLOST;
  125. DPFX(DPFPREP, 0, "Restoring lost buffer" );
  126. while( hr == DSERR_BUFFERLOST )
  127. {
  128. hr = m_lpAudioPlaybackBuffer->Restore();
  129. DPFX(DPFPREP, 0, "Buffer result for restore was 0x%x", hr );
  130. if( hr == DS_OK )
  131. {
  132. hr = m_lpAudioPlaybackBuffer->GetCurrentPosition( &m_dwWritePos );
  133. DPFX(DPFPREP, 0, "GetCurrentPos returned 0x%x", hr );
  134. if( hr != DSERR_BUFFERLOST && FAILED( hr ) )
  135. {
  136. DPFX(DPFPREP, DVF_ERRORLEVEL, "Lost buffer while getting pos, failed to get pos hr=0x%x", hr );
  137. return hr;
  138. }
  139. }
  140. else if( hr != DSERR_BUFFERLOST )
  141. {
  142. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error restoring buffer hr=0x%x", hr );
  143. return hr;
  144. }
  145. }
  146. // STATBLOCK: Begin
  147. m_statPlay.m_dwNumBL++;
  148. // STATBLOCK: End
  149. m_dwNextWritePos = m_dwWritePos + (m_dwFrameSize*SOUNDTARGET_WRITEAHEAD);
  150. m_dwNextWritePos %= m_dwBufferSize;
  151. m_dwLastWritePos = m_dwWritePos;
  152. return hr;
  153. }
  154. #undef DPF_MODNAME
  155. #define DPF_MODNAME "CSoundTarget::CSoundTarget"
  156. //
  157. // Constructor
  158. //
  159. // This constructor is used when you wish to attach a soundtarget to an existing
  160. // DirectSond buffer.
  161. //
  162. CSoundTarget::CSoundTarget(
  163. DVID dvidTarget, CAudioPlaybackDevice *lpads,
  164. CAudioPlaybackBuffer *lpdsBuffer, LPDSBUFFERDESC lpdsBufferDesc,
  165. DWORD dwPriority, DWORD dwFlags, DWORD dwFrameSize
  166. ): m_lpds3dBuffer(NULL),
  167. m_lpAudioPlaybackBuffer(NULL),
  168. m_lpMixBuffer(NULL),
  169. m_dwSignature(VSIG_SOUNDTARGET)
  170. {
  171. m_hrInitResult = Initialize( dvidTarget, lpdsBuffer, (lpdsBufferDesc->lpwfxFormat->wBitsPerSample==8), dwPriority, dwFlags, dwFrameSize );
  172. }
  173. #undef DPF_MODNAME
  174. #define DPF_MODNAME "CSoundTarget::CSoundTarget"
  175. CSoundTarget::CSoundTarget(
  176. DVID dvidTarget,
  177. CAudioPlaybackDevice *lpads,
  178. LPDIRECTSOUNDBUFFER lpdsBuffer,
  179. BOOL fEightBit,
  180. DWORD dwPriority,
  181. DWORD dwFlags,
  182. DWORD dwFrameSize
  183. ): m_lpds3dBuffer(NULL),
  184. m_lpAudioPlaybackBuffer(NULL),
  185. m_lpMixBuffer(NULL),
  186. m_dwSignature(VSIG_SOUNDTARGET)
  187. {
  188. CDirectSoundPlaybackBuffer *pAudioBuffer = NULL;
  189. // Create audio buffer to wrap buffer for call to initialize
  190. pAudioBuffer = new CDirectSoundPlaybackBuffer( lpdsBuffer );
  191. if( pAudioBuffer == NULL )
  192. {
  193. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error allocating memory" );
  194. m_hrInitResult = DVERR_OUTOFMEMORY;
  195. return;
  196. }
  197. // Required always
  198. dwFlags |= DSBPLAY_LOOPING;
  199. m_hrInitResult = pAudioBuffer->Play( dwPriority, dwFlags );
  200. if( FAILED( m_hrInitResult ) )
  201. {
  202. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed playing new buffer hr=0x%x", m_hrInitResult );
  203. return;
  204. }
  205. m_hrInitResult = Initialize( dvidTarget, pAudioBuffer, fEightBit, dwPriority, dwFlags, dwFrameSize );
  206. }
  207. #undef DPF_MODNAME
  208. #define DPF_MODNAME "CSoundTarget::~CSoundTarget"
  209. //
  210. // Destructor
  211. //
  212. // Destroys the 3d and buffer pointers and frees memory.
  213. //
  214. CSoundTarget::~CSoundTarget()
  215. {
  216. Stats_End();
  217. if( m_lpds3dBuffer != NULL )
  218. {
  219. m_lpds3dBuffer->Release();
  220. m_lpds3dBuffer = NULL;
  221. }
  222. if( m_lpAudioPlaybackBuffer != NULL )
  223. {
  224. m_lpAudioPlaybackBuffer->Stop();
  225. delete m_lpAudioPlaybackBuffer;
  226. m_lpAudioPlaybackBuffer = NULL;
  227. }
  228. if( m_lpMixBuffer != NULL )
  229. {
  230. delete [] m_lpMixBuffer;
  231. m_lpMixBuffer = NULL;
  232. }
  233. if( SUCCEEDED( m_hrInitResult ) )
  234. {
  235. DNDeleteCriticalSection( &m_csGuard );
  236. }
  237. m_dwSignature = VSIG_SOUNDTARGET_FREE;
  238. }
  239. #undef DPF_MODNAME
  240. #define DPF_MODNAME "CSoundTarget::AdjustWritePtr"
  241. //
  242. // AdjustWritePtr
  243. //
  244. // This function ensures that the directsoundbuffer is acting as it should. It handles
  245. // checking the write pointer and determining if some form of error or problem has
  246. // occured and taking the appropriate corrective action.
  247. //
  248. // E.g. the buffer hasn't moved since the last frame, the buffer hasn't moved enough,
  249. // the buffer has skipped ahead etc.
  250. //
  251. // This function should be called once per mixing pass.
  252. //
  253. // Call before writing the frame
  254. //
  255. HRESULT CSoundTarget::AdjustWritePtr()
  256. {
  257. HRESULT hr;
  258. LONG lDifference, lHalfSize;
  259. DWORD dwCurrentTick;
  260. hr = m_lpAudioPlaybackBuffer->GetCurrentPosition( &m_dwWritePos );
  261. if( FAILED( hr ) )
  262. {
  263. DPFX(DPFPREP, DVF_ERRORLEVEL, "GetCurrentPosition Failed hr=0x%x", hr );
  264. DSASSERT( FALSE );
  265. return hr;
  266. }
  267. // STATSBLOCK: Begin
  268. m_statPlay.m_dwNumRuns++;
  269. // STATSBLOCK: End
  270. DWORD dwTmpAdvance, dwTmpLead;
  271. dwCurrentTick = GetTickCount();
  272. if( m_dwLastWritePos > m_dwWritePos )
  273. {
  274. dwTmpAdvance = (m_dwBufferSize - m_dwLastWritePos) + m_dwWritePos;
  275. }
  276. else
  277. {
  278. dwTmpAdvance = m_dwWritePos - m_dwLastWritePos;
  279. }
  280. if( m_dwNextWritePos < m_dwWritePos )
  281. {
  282. dwTmpLead = (m_dwBufferSize-m_dwWritePos) + m_dwNextWritePos;
  283. }
  284. else
  285. {
  286. dwTmpLead = m_dwNextWritePos - m_dwWritePos;
  287. }
  288. DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], %d, %d, %d, %d, %d, %d", m_dvidTarget, m_dwWritePos, dwTmpAdvance,
  289. dwCurrentTick - m_dwLastWriteTime, m_dwNextWritePos, dwTmpLead, m_dwFrameSize );
  290. // STATSBLOCK: Begin
  291. DWORD dwTmpDiff = dwCurrentTick - m_dwLastWriteTime;
  292. if( dwTmpDiff > m_statPlay.m_dwPMMSMax )
  293. {
  294. m_statPlay.m_dwPMMSMax = dwTmpDiff;
  295. }
  296. if( dwTmpDiff < m_statPlay.m_dwPMMSMin )
  297. {
  298. m_statPlay.m_dwPMMSMin = dwTmpDiff;
  299. }
  300. m_statPlay.m_dwPMMSTotal += dwTmpDiff;
  301. if( dwTmpAdvance > m_statPlay.m_dwPMBMax )
  302. {
  303. m_statPlay.m_dwPMBMax = dwTmpAdvance;
  304. }
  305. if( dwTmpAdvance < m_statPlay.m_dwPMBMin )
  306. {
  307. m_statPlay.m_dwPMBMin = dwTmpAdvance;
  308. }
  309. m_statPlay.m_dwPMBTotal += dwTmpAdvance;
  310. if( dwTmpLead > m_statPlay.m_dwPLMax )
  311. {
  312. m_statPlay.m_dwPLMax = dwTmpLead;
  313. }
  314. if( dwTmpLead < m_statPlay.m_dwPLMin )
  315. {
  316. m_statPlay.m_dwPLMin = dwTmpLead;
  317. }
  318. m_statPlay.m_dwPLTotal += dwTmpLead;
  319. // STATSBLOCK: End
  320. lHalfSize = m_dwBufferSize / 2;
  321. lDifference = m_dwNextWritePos - m_dwWritePos;
  322. // If the write position is somehow ahead of position AND
  323. //
  324. if( lDifference < 0 &&
  325. lDifference > (-1*lHalfSize) )
  326. {
  327. m_dwNextWritePos = m_dwWritePos;
  328. m_dwNextWritePos += (m_dwFrameSize * SOUNDTARGET_WRITEAHEAD);
  329. m_dwNextWritePos %= m_dwBufferSize;
  330. DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], Punt --> %d", m_dvidTarget, m_dwNextWritePos );
  331. // STATSBLOCK: Begin
  332. m_statPlay.m_dwPPunts++;
  333. DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: [0x%x] Playback: Write pointer has fallen behind buffer pointer. Compensating", m_dvidTarget );
  334. m_statPlay.m_dwGlitches++;
  335. // STATSBLOCK: End
  336. }
  337. m_dwLastWritePos = m_dwWritePos;
  338. m_dwLastWriteTime = dwCurrentTick;
  339. if( m_dwNextWritePos < m_dwWritePos &&
  340. (m_dwNextWritePos + m_dwFrameSize) > m_dwWritePos )
  341. {
  342. DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], Ignore - Crossing", m_dvidTarget );
  343. m_fIgnoreFrame = TRUE;
  344. // STATSBLOCK: Begin
  345. m_statPlay.m_dwPIgnore++;
  346. DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: [0x%x] Playback: Current frame will cross buffer pointer. Ignoring", m_dvidTarget );
  347. DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: [0x%x] Playback: May be catching up with buiffer pointer", m_dvidTarget );
  348. m_statPlay.m_dwGlitches++;
  349. // STATSBLOCK: End
  350. }
  351. else
  352. {
  353. m_fIgnoreFrame = FALSE;
  354. }
  355. return DV_OK;
  356. }
  357. #undef DPF_MODNAME
  358. #define DPF_MODNAME "CSoundTarget::WriteAheadSilence"
  359. //
  360. // WriteAheadSilence
  361. //
  362. // This function is responsible for writing frames of silence ahead of the latest frame
  363. // placed in the buffer. This way if because of high CPU writer thread donesn't get woken
  364. // up silence will be played instead of old speech.
  365. //
  366. // Called AFTER latest frame of data has been written.
  367. //
  368. HRESULT CSoundTarget::WriteAheadSilence()
  369. {
  370. HRESULT hr;
  371. DWORD dwBufferSize1, dwBufferSize2;
  372. LPVOID lpvBuffer1, lpvBuffer2;
  373. if( m_dwNextWritePos < m_dwWritePos &&
  374. (m_dwNextWritePos + (m_dwFrameSize*SOUNDTARGET_MAX_WRITEAHEAD)) > m_dwWritePos )
  375. {
  376. DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], Ignore2 - Crossing", m_dvidTarget );
  377. // STATSBLOCK: Begin
  378. m_statPlay.m_dwSIgnore++;
  379. DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: Playback: Silence will cross buffer pointer. Ignoring" );
  380. DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: Playback: May be catching up with buiffer pointer" );
  381. // STATSBLOCK: End
  382. return DV_OK;
  383. }
  384. hr = m_lpAudioPlaybackBuffer->Lock( m_dwNextWritePos, m_dwFrameSize*SOUNDTARGET_MAX_WRITEAHEAD, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, 0 );
  385. if( FAILED( hr ) )
  386. {
  387. DSASSERT( FALSE );
  388. DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() Failed hr=0x%x", hr );
  389. return hr;
  390. }
  391. memset( lpvBuffer1, (m_fEightBit) ? 0x80 : 0x00, dwBufferSize1 );
  392. memset( lpvBuffer2, (m_fEightBit) ? 0x80 : 0x00, dwBufferSize2 );
  393. hr = m_lpAudioPlaybackBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
  394. if( FAILED( hr ) )
  395. {
  396. DSASSERT( FALSE );
  397. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unlock() Failed hr=0x%x", hr );
  398. return hr;
  399. }
  400. return DV_OK;
  401. }
  402. #undef DPF_MODNAME
  403. #define DPF_MODNAME "CSoundTarget::MixInSingle"
  404. //
  405. // MixInSingle
  406. //
  407. // This function is an optimization.
  408. //
  409. // If there is only one frame to mix into this buffer, this function performs the
  410. // MixIn and Commit all in one step. You still must call Commit before the next
  411. // frame hwoever.
  412. //
  413. HRESULT CSoundTarget::MixInSingle( LPBYTE lpbBuffer )
  414. {
  415. HRESULT hr;
  416. DWORD dwBufferSize1, dwBufferSize2;
  417. LPVOID lpvBuffer1, lpvBuffer2;
  418. hr = AdjustWritePtr();
  419. if( FAILED( hr ) )
  420. {
  421. DSASSERT( FALSE );
  422. DPFX(DPFPREP, DVF_ERRORLEVEL, "AdjustWritePtr Failed hr=0x%x", hr );
  423. return hr;
  424. }
  425. if( !m_fIgnoreFrame )
  426. {
  427. hr = m_lpAudioPlaybackBuffer->Lock( m_dwNextWritePos, m_dwFrameSize, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, 0 );
  428. if( FAILED( hr ) )
  429. {
  430. DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() Failed hr=0x%x", hr );
  431. return hr;
  432. }
  433. memcpy( lpvBuffer1, lpbBuffer, dwBufferSize1 );
  434. if( dwBufferSize2 )
  435. {
  436. memcpy( lpvBuffer2, &lpbBuffer[dwBufferSize1], dwBufferSize2 );
  437. }
  438. hr = m_lpAudioPlaybackBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
  439. if( FAILED( hr ) )
  440. {
  441. DSASSERT( FALSE );
  442. DPFX(DPFPREP, DVF_ERRORLEVEL, "UnLock() Failed hr=0x%x", hr );
  443. return hr;
  444. }
  445. }
  446. m_dwNextWritePos += m_dwFrameSize;
  447. m_dwNextWritePos %= m_dwBufferSize;
  448. m_bCommited = TRUE;
  449. m_fMixed = TRUE;
  450. return DV_OK;
  451. }
  452. #undef DPF_MODNAME
  453. #define DPF_MODNAME "CSoundTarget::MixIn"
  454. // Mix in a user's audio.
  455. //
  456. // Simply copies and promotes the audio samples into the buffer with LONG's
  457. // in it.
  458. //
  459. HRESULT CSoundTarget::MixIn( LPBYTE lpbBuffer )
  460. {
  461. DWORD dwIndex;
  462. if( !m_fMixed )
  463. {
  464. FillBufferWithSilence( m_lpMixBuffer, m_fEightBit, m_dwFrameSize );
  465. m_fMixed = TRUE;
  466. }
  467. if( !m_fIgnoreFrame )
  468. MixInBuffer( m_lpMixBuffer, lpbBuffer, m_fEightBit, m_dwFrameSize );
  469. m_bCommited = FALSE;
  470. m_fMixed = TRUE;
  471. return DV_OK;
  472. }
  473. #undef DPF_MODNAME
  474. #define DPF_MODNAME "CSoundTarget::Commit"
  475. // Commit
  476. //
  477. // If we didn't do a single direct mix, commit the mixed audio to the buffer
  478. //
  479. HRESULT CSoundTarget::Commit()
  480. {
  481. DWORD dwBufferSize1, dwBufferSize2;
  482. LPVOID lpvBuffer1, lpvBuffer2;
  483. DWORD dwIndex = 0;
  484. LPBYTE lpbBufferPtr;
  485. LPWORD lpsBufferPtr;
  486. HRESULT hr;
  487. if( !m_fMixed )
  488. {
  489. FillBufferWithSilence( m_lpMixBuffer, m_fEightBit, m_dwFrameSize );
  490. // STATSBLOCK: Begin
  491. m_statPlay.m_dwNumSilentMixed++;
  492. // STATSBLOCK: End
  493. }
  494. else
  495. {
  496. if( !m_fIgnoreFrame )
  497. {
  498. // STATSBLOCK: Begin
  499. m_statPlay.m_dwNumMixed++;
  500. // STATSBLOCK: End
  501. }
  502. }
  503. if( !m_bCommited || !m_fMixed )
  504. {
  505. hr = AdjustWritePtr();
  506. if( FAILED( hr ) )
  507. {
  508. DSASSERT( FALSE );
  509. DPFX(DPFPREP, DVF_ERRORLEVEL, "AdjustWritePtr() Failed hr=0x%x", hr );
  510. return hr;
  511. }
  512. if( !m_fIgnoreFrame )
  513. {
  514. hr = m_lpAudioPlaybackBuffer->Lock( m_dwNextWritePos, m_dwFrameSize, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, 0 );
  515. if( FAILED( hr ) )
  516. {
  517. DSASSERT( FALSE );
  518. DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() Failed hr=0x%x", hr );
  519. return hr;
  520. }
  521. // Mix first half
  522. NormalizeBuffer( (BYTE *) lpvBuffer1, m_lpMixBuffer, m_fEightBit, dwBufferSize1 );
  523. if( dwBufferSize2 > 0 )
  524. {
  525. if( m_fEightBit )
  526. {
  527. // Mix second half
  528. NormalizeBuffer( (BYTE *) lpvBuffer2, &m_lpMixBuffer[dwBufferSize1], m_fEightBit, dwBufferSize2 );
  529. }
  530. else
  531. {
  532. // Mix second half
  533. NormalizeBuffer( (BYTE *) lpvBuffer2, &m_lpMixBuffer[(dwBufferSize1 >> 1)], m_fEightBit, dwBufferSize2 );
  534. }
  535. }
  536. hr = m_lpAudioPlaybackBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
  537. if( FAILED( hr ) )
  538. {
  539. DSASSERT( FALSE );
  540. DPFX(DPFPREP, DVF_ERRORLEVEL, "UnLock Failed hr=0x%x", hr );
  541. return hr;
  542. }
  543. }
  544. m_dwNextWritePos += m_dwFrameSize;
  545. m_dwNextWritePos %= m_dwBufferSize;
  546. m_bCommited = FALSE;
  547. m_fMixed = FALSE;
  548. return DV_OK;
  549. }
  550. hr = WriteAheadSilence();
  551. if( FAILED( hr ) )
  552. {
  553. DSASSERT( FALSE );
  554. DPFX(DPFPREP, 0, "WriteAhead Failed hr=0x%x", hr );
  555. }
  556. m_fMixed = FALSE;
  557. m_bCommited = FALSE;
  558. return DV_OK;
  559. }
  560. #undef DPF_MODNAME
  561. #define DPF_MODNAME "CSoundTarget::Initialize"
  562. //
  563. // Initialize
  564. //
  565. // NOTE: Takes a reference to the buffer
  566. //
  567. // Attaches a sound target to the specified sound buffer, initializes the object and creates
  568. // the associated 3d buffer.
  569. //
  570. HRESULT CSoundTarget::Initialize( DVID dvidTarget, CAudioPlaybackBuffer *lpdsBuffer, BOOL fEightBit, DWORD dwPriority, DWORD dwFlags, DWORD dwFrameSize )
  571. {
  572. HRESULT hr = DV_OK;
  573. PVOID pvBuffer1 = NULL, pvBuffer2 = NULL;
  574. DWORD dwBufferSize1, dwBufferSize2;
  575. if (!DNInitializeCriticalSection( &m_csGuard ))
  576. {
  577. return DVERR_OUTOFMEMORY;
  578. }
  579. // Determine the buffer size (in bytes)
  580. hr = lpdsBuffer->Lock( 0, 0, &pvBuffer1, &dwBufferSize1, &pvBuffer2, &dwBufferSize2, DSBLOCK_ENTIREBUFFER );
  581. if( FAILED( hr ) )
  582. {
  583. DSASSERT( FALSE );
  584. DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() failed hr=0x%x", hr );
  585. return hr;
  586. }
  587. hr = lpdsBuffer->UnLock( pvBuffer1, dwBufferSize1, pvBuffer2, dwBufferSize2 );
  588. if( FAILED( hr ) )
  589. {
  590. DSASSERT( FALSE );
  591. DPFX(DPFPREP, DVF_ERRORLEVEL, "UnLock() failed hr=0x%x", hr );
  592. return hr;
  593. }
  594. m_lpds3dBuffer = NULL;
  595. m_lpAudioPlaybackBuffer = NULL;
  596. m_lpMixBuffer = NULL;
  597. // We always need the looping flag
  598. dwFlags |= DSBPLAY_LOOPING;
  599. m_dwPlayFlags = dwFlags;
  600. m_dwPriority = dwPriority;
  601. m_fIgnoreFrame = FALSE;
  602. m_dwLastWriteTime = GetTickCount();
  603. m_lRefCount = 1;
  604. m_lpstNext = NULL;
  605. m_dwNumResets = 0;
  606. m_dwNumSinceMove = 1;
  607. m_bCommited = FALSE;
  608. m_dvidTarget = dvidTarget;
  609. m_dwFrameSize = dwFrameSize;
  610. m_dwBufferSize = dwBufferSize1;
  611. // STATSBLOCK: Begin
  612. Stats_Init();
  613. // STATSBLOCK: End
  614. m_fLastFramePushed = FALSE;
  615. m_lpAudioPlaybackBuffer = lpdsBuffer;
  616. hr = m_lpAudioPlaybackBuffer->Get3DBuffer(&m_lpds3dBuffer);
  617. if( FAILED( hr ) )
  618. {
  619. DSASSERT( FALSE );
  620. DPFX(DPFPREP, DVF_ERRORLEVEL, "QueryInterface failed hr=0x%x", hr );
  621. }
  622. m_dwNextWritePos = 0;
  623. if( fEightBit )
  624. {
  625. m_fEightBit = TRUE;
  626. m_dwMixSize = dwFrameSize;
  627. }
  628. else
  629. {
  630. m_fEightBit = FALSE;
  631. m_dwMixSize = dwFrameSize / 2;
  632. }
  633. m_lpMixBuffer = new LONG[m_dwMixSize];
  634. if( m_lpMixBuffer == NULL )
  635. {
  636. DPFX(DPFPREP, DVF_ERRORLEVEL, "Out of memory" );
  637. m_lpds3dBuffer->Release();
  638. m_lpds3dBuffer = NULL;
  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. }