simpleplayer.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "Common/SimplePlayer.h"
  19. #include "Common/URLLaunch.h"
  20. ///////////////////////////////////////////////////////////////////////////////
  21. CSimplePlayer::CSimplePlayer( HRESULT* phr )
  22. {
  23. m_cRef = 1;
  24. m_cBuffersOutstanding = 0;
  25. m_pReader = NULL;
  26. m_pHeader = NULL;
  27. m_hwo = NULL;
  28. m_fEof = FALSE;
  29. m_pszUrl = NULL;
  30. *phr = S_OK;
  31. m_hOpenEvent = CreateEvent( NULL, FALSE, FALSE, SIMPLE_PLAYER_OPEN_EVENT );
  32. if ( NULL == m_hOpenEvent )
  33. {
  34. *phr = E_OUTOFMEMORY;
  35. }
  36. m_hCloseEvent = CreateEvent( NULL, FALSE, FALSE, SIMPLE_PLAYER_CLOSE_EVENT );
  37. if ( NULL == m_hCloseEvent )
  38. {
  39. *phr = E_OUTOFMEMORY;
  40. }
  41. m_hrOpen = S_OK;
  42. m_hCompletionEvent = NULL;
  43. InitializeCriticalSection( &m_CriSec );
  44. m_whdrHead = NULL;
  45. }
  46. ///////////////////////////////////////////////////////////////////////////////
  47. CSimplePlayer::~CSimplePlayer()
  48. {
  49. DEBUG_ASSERTCRASH( 0 == m_cBuffersOutstanding,("CSimplePlayer destructor m_cBuffersOutstanding != 0") );
  50. Close();
  51. //
  52. // final remove of everything in the wave header list
  53. //
  54. RemoveWaveHeaders();
  55. DeleteCriticalSection( &m_CriSec );
  56. if( m_pHeader != NULL )
  57. {
  58. m_pHeader->Release();
  59. m_pHeader = NULL;
  60. }
  61. if( m_pReader != NULL )
  62. {
  63. m_pReader->Release();
  64. m_pReader = NULL;
  65. }
  66. if( m_hwo != NULL )
  67. {
  68. waveOutClose( m_hwo );
  69. }
  70. delete [] m_pszUrl;
  71. if ( m_hOpenEvent )
  72. {
  73. CloseHandle( m_hOpenEvent );
  74. }
  75. if ( m_hCloseEvent )
  76. {
  77. CloseHandle( m_hCloseEvent );
  78. }
  79. }
  80. ///////////////////////////////////////////////////////////////////////////////
  81. HRESULT STDMETHODCALLTYPE CSimplePlayer::QueryInterface(
  82. REFIID riid,
  83. void **ppvObject )
  84. {
  85. return( E_NOINTERFACE );
  86. }
  87. ///////////////////////////////////////////////////////////////////////////////
  88. ULONG STDMETHODCALLTYPE CSimplePlayer::AddRef()
  89. {
  90. return( InterlockedIncrement( &m_cRef ) );
  91. }
  92. ///////////////////////////////////////////////////////////////////////////////
  93. ULONG STDMETHODCALLTYPE CSimplePlayer::Release()
  94. {
  95. ULONG uRet = InterlockedDecrement( &m_cRef );
  96. if( 0 == uRet )
  97. {
  98. delete this;
  99. }
  100. return( uRet );
  101. }
  102. ///////////////////////////////////////////////////////////////////////////////
  103. HRESULT STDMETHODCALLTYPE CSimplePlayer::OnSample(
  104. /* [in] */ DWORD dwOutputNum,
  105. /* [in] */ QWORD cnsSampleTime,
  106. /* [in] */ QWORD cnsSampleDuration,
  107. /* [in] */ DWORD dwFlags,
  108. /* [in] */ INSSBuffer __RPC_FAR *pSample,
  109. /* [in] */ VOID *pvContext )
  110. {
  111. if( 0 != dwOutputNum )
  112. {
  113. return( S_OK );
  114. }
  115. HRESULT hr = S_OK;
  116. BYTE *pData;
  117. DWORD cbData;
  118. //
  119. // first UnprepareHeader and remove everthing in the ready list
  120. //
  121. RemoveWaveHeaders( );
  122. hr = pSample->GetBufferAndLength( &pData, &cbData );
  123. if ( FAILED( hr ) )
  124. {
  125. return( E_UNEXPECTED );
  126. }
  127. DEBUG_LOG(( " New Sample of length %d and PS time %d ms\n",
  128. cbData, ( DWORD ) ( cnsSampleTime / 10000 ) ));
  129. LPWAVEHDR pwh = (LPWAVEHDR) new BYTE[ sizeof( WAVEHDR ) + cbData ];
  130. if( NULL == pwh )
  131. {
  132. DEBUG_LOG(( "OnSample OUT OF MEMORY! \n"));
  133. *m_phrCompletion = E_OUTOFMEMORY;
  134. SetEvent( m_hCompletionEvent );
  135. return( E_UNEXPECTED );
  136. }
  137. pwh->lpData = (LPSTR)&pwh[1];
  138. pwh->dwBufferLength = cbData;
  139. pwh->dwBytesRecorded = cbData;
  140. pwh->dwUser = 0;
  141. pwh->dwLoops = 0;
  142. pwh->dwFlags = 0;
  143. CopyMemory( pwh->lpData, pData, cbData );
  144. MMRESULT mmr;
  145. mmr = waveOutPrepareHeader( m_hwo, pwh, sizeof(WAVEHDR) );
  146. mmr = MMSYSERR_NOERROR;
  147. if( mmr != MMSYSERR_NOERROR )
  148. {
  149. DEBUG_LOG(( "failed to prepare wave buffer, error=%lu\n" , mmr ));
  150. *m_phrCompletion = E_UNEXPECTED;
  151. SetEvent( m_hCompletionEvent );
  152. return( E_UNEXPECTED );
  153. }
  154. mmr = waveOutWrite( m_hwo, pwh, sizeof(WAVEHDR) );
  155. mmr = MMSYSERR_NOERROR;
  156. if( mmr != MMSYSERR_NOERROR )
  157. {
  158. delete pwh;
  159. DEBUG_LOG(( "failed to write wave sample, error=%lu\n" , mmr ));
  160. *m_phrCompletion = E_UNEXPECTED;
  161. SetEvent( m_hCompletionEvent );
  162. return( E_UNEXPECTED );
  163. }
  164. InterlockedIncrement( &m_cBuffersOutstanding );
  165. return( S_OK );
  166. }
  167. ///////////////////////////////////////////////////////////////////////////////
  168. HRESULT CSimplePlayer::Play( LPCWSTR pszUrl, DWORD dwSecDuration, HANDLE hCompletionEvent, HRESULT *phrCompletion )
  169. {
  170. HRESULT hr;
  171. //
  172. // If the URL is not a UNC path, a full path, or an Internet-style URL then assume it is
  173. // a relative local file name that needs to be expanded to a full path.
  174. //
  175. WCHAR wszFullUrl[ MAX_PATH ];
  176. if( ( 0 == wcsstr( pszUrl, L"\\\\" ) )
  177. && ( 0 == wcsstr( pszUrl, L":\\" ) )
  178. && ( 0 == wcsstr( pszUrl, L"://" ) ) )
  179. {
  180. //
  181. // Expand to a full path name
  182. //
  183. LPWSTR pszCheck = _wfullpath( wszFullUrl, pszUrl, MAX_PATH );
  184. if( NULL == pszCheck )
  185. {
  186. DEBUG_LOG(( "internal error %lu\n" , GetLastError() ));
  187. return E_UNEXPECTED ;
  188. }
  189. pszUrl = wszFullUrl;
  190. }
  191. //
  192. // Save a copy of the URL
  193. //
  194. delete[] m_pszUrl;
  195. m_pszUrl = new WCHAR[ wcslen( pszUrl ) + 1 ];
  196. if( NULL == m_pszUrl )
  197. {
  198. DEBUG_LOG(( "insufficient Memory\n" )) ;
  199. return( E_OUTOFMEMORY );
  200. }
  201. wcscpy( m_pszUrl, pszUrl );
  202. //
  203. // Attempt to open the URL
  204. //
  205. m_hCompletionEvent = hCompletionEvent;
  206. m_phrCompletion = phrCompletion;
  207. #ifdef SUPPORT_DRM
  208. hr = WMCreateReader( NULL, WMT_RIGHT_PLAYBACK, &m_pReader );
  209. #else
  210. hr = WMCreateReader( NULL, 0, &m_pReader );
  211. #endif
  212. if( FAILED( hr ) )
  213. {
  214. DEBUG_LOG(( "failed to create audio reader (hr=0x%08x)\n" , hr ));
  215. return( hr );
  216. }
  217. //
  218. // Open the file
  219. //
  220. hr = m_pReader->Open( m_pszUrl, this, NULL );
  221. if ( SUCCEEDED( hr ) )
  222. {
  223. WaitForSingleObject( m_hOpenEvent, INFINITE );
  224. hr = m_hrOpen;
  225. }
  226. if ( NS_E_NO_STREAM == hr )
  227. {
  228. DEBUG_LOG(( "Waiting for transmission to begin...\n" ));
  229. WaitForSingleObject( m_hOpenEvent, INFINITE );
  230. hr = m_hrOpen;
  231. }
  232. if ( FAILED( hr ) )
  233. {
  234. DEBUG_LOG(( "failed to open (hr=0x%08x)\n", hr ));
  235. return( hr );
  236. }
  237. //
  238. // It worked! Display various attributes
  239. //
  240. hr = m_pReader->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&m_pHeader );
  241. if ( FAILED( hr ) )
  242. {
  243. DEBUG_LOG(( "failed to qi for header interface (hr=0x%08x)\n" , hr ));
  244. return( hr );
  245. }
  246. WORD i, wAttrCnt;
  247. hr = m_pHeader->GetAttributeCount( 0, &wAttrCnt );
  248. if ( FAILED( hr ) )
  249. {
  250. DEBUG_LOG(( "GetAttributeCount Failed (hr=0x%08x)\n" , hr ));
  251. return( hr );
  252. }
  253. WCHAR *pwszName = NULL;
  254. BYTE *pValue = NULL;
  255. for ( i = 0; i < wAttrCnt ; i++ )
  256. {
  257. WORD wStream = 0;
  258. WORD cchNamelen = 0;
  259. WMT_ATTR_DATATYPE type;
  260. WORD cbLength = 0;
  261. hr = m_pHeader->GetAttributeByIndex( i, &wStream, NULL, &cchNamelen, &type, NULL, &cbLength );
  262. if ( FAILED( hr ) )
  263. {
  264. DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
  265. break;
  266. }
  267. pwszName = new WCHAR[ cchNamelen ];
  268. pValue = new BYTE[ cbLength ];
  269. if( NULL == pwszName || NULL == pValue )
  270. {
  271. hr = E_OUTOFMEMORY;
  272. break;
  273. }
  274. hr = m_pHeader->GetAttributeByIndex( i, &wStream, pwszName, &cchNamelen, &type, pValue, &cbLength );
  275. if ( FAILED( hr ) )
  276. {
  277. DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
  278. break;
  279. }
  280. switch ( type )
  281. {
  282. case WMT_TYPE_DWORD:
  283. DEBUG_LOG(("%ws: %u\n" , pwszName, *((DWORD *) pValue) ));
  284. break;
  285. case WMT_TYPE_STRING:
  286. DEBUG_LOG(("%ws: %ws\n" , pwszName, (WCHAR *) pValue ));
  287. break;
  288. case WMT_TYPE_BINARY:
  289. DEBUG_LOG(("%ws: Type = Binary of Length %u\n" , pwszName, cbLength ));
  290. break;
  291. case WMT_TYPE_BOOL:
  292. DEBUG_LOG(("%ws: %s\n" , pwszName, ( * ( ( BOOL * ) pValue) ? _T( "true" ) : _T( "false" ) ) ));
  293. break;
  294. case WMT_TYPE_WORD:
  295. DEBUG_LOG(("%ws: %hu\n" , pwszName, *((WORD *) pValue) ));
  296. break;
  297. case WMT_TYPE_QWORD:
  298. DEBUG_LOG(("%ws: %I64u\n" , pwszName, *((QWORD *) pValue) ));
  299. break;
  300. case WMT_TYPE_GUID:
  301. DEBUG_LOG(("%ws: %I64x%I64x\n" , pwszName, *((QWORD *) pValue), *((QWORD *) pValue + 1) ));
  302. break;
  303. default:
  304. DEBUG_LOG(("%ws: Type = %d, Length %u\n" , pwszName, type, cbLength ));
  305. break;
  306. }
  307. delete pwszName;
  308. pwszName = NULL;
  309. delete pValue;
  310. pValue = NULL;
  311. }
  312. delete pwszName;
  313. pwszName = NULL;
  314. delete pValue;
  315. pValue = NULL;
  316. if ( FAILED( hr ) )
  317. {
  318. return( hr );
  319. }
  320. //
  321. // Make sure we're audio only
  322. //
  323. DWORD cOutputs;
  324. hr = m_pReader->GetOutputCount( &cOutputs );
  325. if ( FAILED( hr ) )
  326. {
  327. DEBUG_LOG(( "failed GetOutputCount(), (hr=0x%08x)\n" , hr ));
  328. return( hr );
  329. }
  330. if ( cOutputs != 1 )
  331. {
  332. DEBUG_LOG(( "Not audio only (cOutputs = %d).\n" , cOutputs ));
  333. // return( E_UNEXPECTED );
  334. }
  335. IWMOutputMediaProps *pProps;
  336. hr = m_pReader->GetOutputProps( 0, &pProps );
  337. if ( FAILED( hr ) )
  338. {
  339. DEBUG_LOG(( "failed GetOutputProps(), (hr=0x%08x)\n" , hr ));
  340. return( hr );
  341. }
  342. DWORD cbBuffer = 0;
  343. hr = pProps->GetMediaType( NULL, &cbBuffer );
  344. if ( FAILED( hr ) )
  345. {
  346. pProps->Release( );
  347. DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
  348. return( hr );
  349. }
  350. WM_MEDIA_TYPE *pMediaType = ( WM_MEDIA_TYPE * ) new BYTE[cbBuffer] ;
  351. hr = pProps->GetMediaType( pMediaType, &cbBuffer );
  352. if ( FAILED( hr ) )
  353. {
  354. pProps->Release( );
  355. DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
  356. return( hr );
  357. }
  358. pProps->Release( );
  359. if ( pMediaType->majortype != WMMEDIATYPE_Audio )
  360. {
  361. delete[] (BYTE *) pMediaType ;
  362. DEBUG_LOG(( "Not audio only (major type mismatch).\n" ));
  363. return( E_UNEXPECTED );
  364. }
  365. //
  366. // Set up for audio playback
  367. //
  368. WAVEFORMATEX *pwfx = ( WAVEFORMATEX * )pMediaType->pbFormat;
  369. memcpy( &m_wfx, pwfx, sizeof( WAVEFORMATEX ) + pwfx->cbSize );
  370. delete[] (BYTE *)pMediaType ;
  371. pMediaType = NULL ;
  372. MMRESULT mmr;
  373. mmr = waveOutOpen( &m_hwo,
  374. WAVE_MAPPER,
  375. &m_wfx,
  376. (DWORD)WaveProc,
  377. (DWORD)this,
  378. CALLBACK_FUNCTION );
  379. mmr = MMSYSERR_NOERROR;
  380. if( mmr != MMSYSERR_NOERROR )
  381. {
  382. DEBUG_LOG(( "failed to open wav output device, error=%lu\n" , mmr ));
  383. return( E_UNEXPECTED );
  384. }
  385. //
  386. // Start reading the data (and rendering the audio)
  387. //
  388. QWORD cnsDuration = ( QWORD ) dwSecDuration * 10000000;
  389. hr = m_pReader->Start( 0, cnsDuration, 1.0, NULL );
  390. if( FAILED( hr ) )
  391. {
  392. DEBUG_LOG(( "failed Start(), (hr=0x%08x)\n" , hr ));
  393. return( hr );
  394. }
  395. return( hr );
  396. }
  397. ///////////////////////////////////////////////////////////////////////////////
  398. HRESULT STDMETHODCALLTYPE CSimplePlayer::OnStatus(
  399. /* [in] */ WMT_STATUS Status,
  400. /* [in] */ HRESULT hr,
  401. /* [in] */ WMT_ATTR_DATATYPE dwType,
  402. /* [in] */ BYTE __RPC_FAR *pValue,
  403. /* [in] */ void __RPC_FAR *pvContext)
  404. {
  405. switch( Status )
  406. {
  407. case WMT_OPENED:
  408. DEBUG_LOG(( "OnStatus( WMT_OPENED )\n" ));
  409. m_hrOpen = hr;
  410. SetEvent( m_hOpenEvent );
  411. break;
  412. case WMT_SOURCE_SWITCH:
  413. DEBUG_LOG(( "OnStatus( WMT_SOURCE_SWITCH )\n" ));
  414. m_hrOpen = hr;
  415. SetEvent( m_hOpenEvent );
  416. break;
  417. case WMT_ERROR:
  418. DEBUG_LOG(( "OnStatus( WMT_ERROR )\n" ));
  419. break;
  420. case WMT_STARTED:
  421. DEBUG_LOG(( "OnStatus( WMT_STARTED )\n" ));
  422. break;
  423. case WMT_STOPPED:
  424. DEBUG_LOG(( "OnStatus( WMT_STOPPED )\n" ));
  425. break;
  426. case WMT_BUFFERING_START:
  427. DEBUG_LOG(( "OnStatus( WMT_BUFFERING START)\n" ));
  428. break;
  429. case WMT_BUFFERING_STOP:
  430. DEBUG_LOG(( "OnStatus( WMT_BUFFERING STOP)\n" ));
  431. break;
  432. case WMT_EOF:
  433. DEBUG_LOG(( "OnStatus( WMT_EOF )\n" ));
  434. //
  435. // cleanup and exit
  436. //
  437. m_fEof = TRUE;
  438. if( 0 == m_cBuffersOutstanding )
  439. {
  440. SetEvent( m_hCompletionEvent );
  441. }
  442. break;
  443. case WMT_END_OF_SEGMENT:
  444. DEBUG_LOG(( "OnStatus( WMT_END_OF_SEGMENT )\n" ));
  445. //
  446. // cleanup and exit
  447. //
  448. m_fEof = TRUE;
  449. if( 0 == m_cBuffersOutstanding )
  450. {
  451. SetEvent( m_hCompletionEvent );
  452. }
  453. break;
  454. case WMT_LOCATING:
  455. DEBUG_LOG(( "OnStatus( WMT_LOCATING )\n" ));
  456. break;
  457. case WMT_CONNECTING:
  458. DEBUG_LOG(( "OnStatus( WMT_CONNECTING )\n" ));
  459. break;
  460. case WMT_NO_RIGHTS:
  461. {
  462. LPWSTR pwszEscapedURL = NULL;
  463. hr = MakeEscapedURL( m_pszUrl, &pwszEscapedURL );
  464. if( SUCCEEDED( hr ) )
  465. {
  466. WCHAR wszURL[ 0x1000 ];
  467. swprintf( wszURL, L"%s&filename=%s&embedded=false", pValue, pwszEscapedURL );
  468. hr = LaunchURL( wszURL );
  469. if( FAILED( hr ) )
  470. {
  471. DEBUG_LOG(( "Unable to launch web browser to retrieve playback license (hr=0x%08x)\n" , hr ));
  472. }
  473. delete [] pwszEscapedURL;
  474. pwszEscapedURL = NULL ;
  475. }
  476. }
  477. break;
  478. case WMT_MISSING_CODEC:
  479. {
  480. DEBUG_LOG(( "Missing codec: (hr=0x%08x)\n" , hr ));
  481. break;
  482. }
  483. case WMT_CLOSED:
  484. SetEvent( m_hCloseEvent );
  485. break;
  486. };
  487. return( S_OK );
  488. }
  489. ///////////////////////////////////////////////////////////////////////////////
  490. HRESULT CSimplePlayer::Close()
  491. {
  492. HRESULT hr = S_OK;
  493. if( NULL != m_pReader )
  494. {
  495. hr = m_pReader->Close();
  496. if( SUCCEEDED( hr ) )
  497. {
  498. WaitForSingleObject( m_hCloseEvent, INFINITE );
  499. }
  500. }
  501. return( hr );
  502. }
  503. ///////////////////////////////////////////////////////////////////////////////
  504. void CSimplePlayer::OnWaveOutMsg( UINT uMsg, DWORD dwParam1, DWORD dwParam2 )
  505. {
  506. if( WOM_DONE == uMsg )
  507. {
  508. //
  509. // add the wave header to ready-to-free list for the caller
  510. // to pick up and free in the next OnSample call
  511. //
  512. AddWaveHeader( ( LPWAVEHDR )dwParam1 );
  513. InterlockedDecrement( &m_cBuffersOutstanding );
  514. if( m_fEof && ( 0 == m_cBuffersOutstanding ) )
  515. {
  516. SetEvent( m_hCompletionEvent );
  517. }
  518. }
  519. }
  520. ///////////////////////////////////////////////////////////////////////////////
  521. void CALLBACK CSimplePlayer::WaveProc(
  522. HWAVEOUT hwo,
  523. UINT uMsg,
  524. DWORD dwInstance,
  525. DWORD dwParam1,
  526. DWORD dwParam2 )
  527. {
  528. CSimplePlayer *pThis = (CSimplePlayer*)dwInstance;
  529. pThis->OnWaveOutMsg( uMsg, dwParam1, dwParam2 );
  530. }
  531. //////////////////////////////////////////////////////////////////////////////
  532. HRESULT CSimplePlayer::AddWaveHeader( LPWAVEHDR pwh )
  533. {
  534. WAVEHDR_LIST *tmp = new WAVEHDR_LIST;
  535. if( NULL == tmp )
  536. {
  537. return( E_OUTOFMEMORY );
  538. }
  539. tmp->pwh = pwh;
  540. EnterCriticalSection( &m_CriSec );
  541. tmp->next = m_whdrHead;
  542. m_whdrHead = tmp;
  543. LeaveCriticalSection( &m_CriSec );
  544. return( S_OK );
  545. }
  546. //////////////////////////////////////////////////////////////////////////////
  547. void CSimplePlayer::RemoveWaveHeaders( )
  548. {
  549. WAVEHDR_LIST *tmp;
  550. EnterCriticalSection( &m_CriSec );
  551. while( NULL != m_whdrHead )
  552. {
  553. tmp = m_whdrHead->next;
  554. DEBUG_ASSERTCRASH( m_whdrHead->pwh->dwFlags & WHDR_DONE, ("RemoveWaveHeaders!") );
  555. waveOutUnprepareHeader( m_hwo, m_whdrHead->pwh, sizeof( WAVEHDR ) );
  556. delete m_whdrHead->pwh;
  557. delete m_whdrHead;
  558. m_whdrHead = tmp;
  559. }
  560. LeaveCriticalSection( &m_CriSec );
  561. }