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.

985 lines
29 KiB

  1. // Copyright (c) 1998-1999 Microsoft Corporation
  2. // READ THIS!!!!!!!!!!!!!!!!!!!!!!!!!!!
  3. //
  4. // 4530: C++ exception handler used, but unwind semantics are not enabled. Specify -GX
  5. //
  6. // We disable this because we use exceptions and do *not* specify -GX (USE_NATIVE_EH in
  7. // sources).
  8. //
  9. // The one place we use exceptions is around construction of objects that call
  10. // InitializeCriticalSection. We guarantee that it is safe to use in this case with
  11. // the restriction given by not using -GX (automatic objects in the call chain between
  12. // throw and handler are not destructed). Turning on -GX buys us nothing but +10% to code
  13. // size because of the unwind code.
  14. //
  15. // Any other use of exceptions must follow these restrictions or -GX must be turned on.
  16. //
  17. // READ THIS!!!!!!!!!!!!!!!!!!!!!!!!!!!
  18. //
  19. #pragma warning(disable:4530)
  20. // TempoTrk.cpp : Implementation of CTempoTrack
  21. #include "dmime.h"
  22. #include "TempoTrk.h"
  23. #include "dmusici.h"
  24. #include "dmusicf.h"
  25. #include "debug.h"
  26. #include "dmperf.h"
  27. #include "..\shared\Validate.h"
  28. #include "debug.h"
  29. #define ASSERT assert
  30. /////////////////////////////////////////////////////////////////////////////
  31. // CTempoTrack
  32. void CTempoTrack::Construct()
  33. {
  34. InterlockedIncrement(&g_cComponent);
  35. m_cRef = 1;
  36. m_dwValidate = 0;
  37. m_fCSInitialized = FALSE;
  38. InitializeCriticalSection(&m_CrSec);
  39. m_fCSInitialized = TRUE;
  40. }
  41. CTempoTrack::CTempoTrack()
  42. {
  43. Construct();
  44. m_fActive = TRUE;
  45. m_fStateSetBySetParam = FALSE;
  46. }
  47. CTempoTrack::CTempoTrack(
  48. const CTempoTrack& rTrack, MUSIC_TIME mtStart, MUSIC_TIME mtEnd)
  49. {
  50. Construct();
  51. m_fActive = rTrack.m_fActive;
  52. m_fStateSetBySetParam = rTrack.m_fStateSetBySetParam;
  53. TListItem<DMUS_IO_TEMPO_ITEM>* pScan = rTrack.m_TempoEventList.GetHead();
  54. //1////////////////////////////////////////
  55. TListItem<DMUS_IO_TEMPO_ITEM>* pPrevious = NULL;
  56. //1////////////////////////////////////////
  57. for(; pScan; pScan = pScan->GetNext())
  58. {
  59. DMUS_IO_TEMPO_ITEM& rScan = pScan->GetItemValue();
  60. //2////////////////////////////////////////
  61. if (rScan.lTime < mtStart)
  62. {
  63. pPrevious = pScan;
  64. }
  65. //2////////////////////////////////////////
  66. else if (rScan.lTime < mtEnd)
  67. {
  68. //3////////////////////////////////////////
  69. if (rScan.lTime == mtStart)
  70. {
  71. pPrevious = NULL;
  72. }
  73. //3////////////////////////////////////////
  74. TListItem<DMUS_IO_TEMPO_ITEM>* pNew = new TListItem<DMUS_IO_TEMPO_ITEM>;
  75. if (pNew)
  76. {
  77. DMUS_IO_TEMPO_ITEM& rNew = pNew->GetItemValue();
  78. memcpy( &rNew, &rScan, sizeof(DMUS_IO_TEMPO_ITEM) );
  79. rNew.lTime = rScan.lTime - mtStart;
  80. m_TempoEventList.AddHead(pNew); // instead of AddTail, which is n^2. We reverse below.
  81. }
  82. }
  83. else break;
  84. }
  85. m_TempoEventList.Reverse(); // for above AddHead.
  86. //4////////////////////////////////////////
  87. if (pPrevious)
  88. {
  89. DMUS_IO_TEMPO_ITEM& rPrevious = pPrevious->GetItemValue();
  90. TListItem<DMUS_IO_TEMPO_ITEM>* pNew = new TListItem<DMUS_IO_TEMPO_ITEM>;
  91. if (pNew)
  92. {
  93. DMUS_IO_TEMPO_ITEM& rNew = pNew->GetItemValue();
  94. memcpy( &rNew, &rPrevious, sizeof(DMUS_IO_TEMPO_ITEM) );
  95. rNew.lTime = 0;
  96. m_TempoEventList.AddHead(pNew);
  97. }
  98. }
  99. //4////////////////////////////////////////
  100. }
  101. CTempoTrack::~CTempoTrack()
  102. {
  103. if (m_fCSInitialized)
  104. {
  105. DeleteCriticalSection(&m_CrSec);
  106. }
  107. InterlockedDecrement(&g_cComponent);
  108. }
  109. // @method:(INTERNAL) HRESULT | IDirectMusicTempoTrack | QueryInterface | Standard QueryInterface implementation for <i IDirectMusicTempoTrack>
  110. //
  111. // @parm const IID & | iid | Interface to query for
  112. // @parm void ** | ppv | The requested interface will be returned here
  113. //
  114. // @rdesc Returns one of the following:
  115. //
  116. // @flag S_OK | If the interface is supported and was returned
  117. // @flag E_NOINTERFACE | If the object does not support the given interface.
  118. //
  119. // @mfunc:(INTERNAL)
  120. //
  121. //
  122. STDMETHODIMP CTempoTrack::QueryInterface(
  123. const IID &iid, // @parm Interface to query for
  124. void **ppv) // @parm The requested interface will be returned here
  125. {
  126. V_INAME(CTempoTrack::QueryInterface);
  127. V_PTRPTR_WRITE(ppv);
  128. V_REFGUID(iid);
  129. if (iid == IID_IUnknown || iid == IID_IDirectMusicTrack || iid == IID_IDirectMusicTrack8)
  130. {
  131. *ppv = static_cast<IDirectMusicTrack8*>(this);
  132. } else
  133. if (iid == IID_IPersistStream)
  134. {
  135. *ppv = static_cast<IPersistStream*>(this);
  136. } else
  137. {
  138. *ppv = NULL;
  139. Trace(4,"Warning: Request to query unknown interface on Tempo Track\n");
  140. return E_NOINTERFACE;
  141. }
  142. reinterpret_cast<IUnknown*>(this)->AddRef();
  143. return S_OK;
  144. }
  145. // @method:(INTERNAL) HRESULT | IDirectMusicTempoTrack | AddRef | Standard AddRef implementation for <i IDirectMusicTempoTrack>
  146. //
  147. // @rdesc Returns the new reference count for this object.
  148. //
  149. // @mfunc:(INTERNAL)
  150. //
  151. //
  152. STDMETHODIMP_(ULONG) CTempoTrack::AddRef()
  153. {
  154. return InterlockedIncrement(&m_cRef);
  155. }
  156. // @method:(INTERNAL) HRESULT | IDirectMusicTempoTrack | Release | Standard Release implementation for <i IDirectMusicTempoTrack>
  157. //
  158. // @rdesc Returns the new reference count for this object.
  159. //
  160. // @mfunc:(INTERNAL)
  161. //
  162. //
  163. STDMETHODIMP_(ULONG) CTempoTrack::Release()
  164. {
  165. if (!InterlockedDecrement(&m_cRef))
  166. {
  167. delete this;
  168. return 0;
  169. }
  170. return m_cRef;
  171. }
  172. /////////////////////////////////////////////////////////////////////////////
  173. // IPersist
  174. HRESULT CTempoTrack::GetClassID( CLSID* pClassID )
  175. {
  176. V_INAME(CTempoTrack::GetClassID);
  177. V_PTR_WRITE(pClassID, CLSID);
  178. *pClassID = CLSID_DirectMusicTempoTrack;
  179. return S_OK;
  180. }
  181. /////////////////////////////////////////////////////////////////////////////
  182. // IPersistStream functions
  183. HRESULT CTempoTrack::IsDirty()
  184. {
  185. return S_FALSE;
  186. }
  187. /*
  188. @method HRESULT | ITempoTrack | Load |
  189. Call this with an IStream filled with DMUS_IO_TEMPO_ITEM's, sorted in time order.
  190. @parm IStream* | pIStream |
  191. A stream of DMUS_IO_TEMPO_ITEM's, sorted in time order. The seek pointer should be
  192. set to the first event. The stream should only contain Tempo events and
  193. nothing more.
  194. @rvalue E_INVALIDARG | If pIStream == NULL
  195. @rvalue S_OK
  196. @comm The <p pIStream> will be AddRef'd inside this function and held
  197. until the TempoTrack is released.
  198. */
  199. HRESULT CTempoTrack::Load( IStream* pIStream )
  200. {
  201. V_INAME(CTempoTrack::Load);
  202. V_INTERFACE(pIStream);
  203. HRESULT hr = S_OK;
  204. EnterCriticalSection(&m_CrSec);
  205. m_dwValidate++; // used to validate state data that's out there
  206. if( m_TempoEventList.GetHead() )
  207. {
  208. TListItem<DMUS_IO_TEMPO_ITEM>* pItem;
  209. while( pItem = m_TempoEventList.RemoveHead() )
  210. {
  211. delete pItem;
  212. }
  213. }
  214. // copy contents of the stream into the list.
  215. LARGE_INTEGER li;
  216. DMUS_IO_TEMPO_ITEM tempoEvent;
  217. // read in the chunk id
  218. DWORD dwChunk, dwSubSize;
  219. long lSize;
  220. pIStream->Read( &dwChunk, sizeof(DWORD), NULL );
  221. if( dwChunk != DMUS_FOURCC_TEMPO_TRACK )
  222. {
  223. Trace(1,"Error: Invalid data in tempo track.\n");
  224. LeaveCriticalSection(&m_CrSec);
  225. return DMUS_E_CHUNKNOTFOUND;
  226. }
  227. // read in the overall size
  228. pIStream->Read( &lSize, sizeof(long), NULL );
  229. // read in the size of the data structures
  230. if( FAILED( pIStream->Read( &dwSubSize, sizeof(DWORD), NULL )))
  231. {
  232. // Check to make sure our reads are succeeding (we can safely
  233. // assume the previous reads worked if we got this far.)
  234. Trace(1,"Error: Unable to read tempo track.\n");
  235. LeaveCriticalSection(&m_CrSec);
  236. return DMUS_E_CANNOTREAD;
  237. }
  238. lSize -= sizeof(DWORD);
  239. DWORD dwRead, dwSeek;
  240. if( dwSubSize > sizeof(DMUS_IO_TEMPO_ITEM) )
  241. {
  242. dwRead = sizeof(DMUS_IO_TEMPO_ITEM);
  243. dwSeek = dwSubSize - dwRead;
  244. li.HighPart = 0;
  245. li.LowPart = dwSeek;
  246. }
  247. else
  248. {
  249. dwRead = dwSubSize;
  250. dwSeek = 0;
  251. }
  252. if( dwRead )
  253. {
  254. while( lSize > 0 )
  255. {
  256. if( FAILED( pIStream->Read( &tempoEvent, dwRead, NULL )))
  257. {
  258. Trace(1,"Error: Failure reading tempo track.\n");
  259. hr = DMUS_E_CANNOTREAD;
  260. break;
  261. }
  262. lSize -= dwRead;
  263. if( dwSeek )
  264. {
  265. if( FAILED( pIStream->Seek( li, STREAM_SEEK_CUR, NULL )))
  266. {
  267. Trace(1,"Error: Failure reading tempo track.\n");
  268. hr = DMUS_E_CANNOTSEEK;
  269. break;
  270. }
  271. lSize -= dwSeek;
  272. }
  273. TListItem<DMUS_IO_TEMPO_ITEM>* pNew =
  274. new TListItem<DMUS_IO_TEMPO_ITEM>(tempoEvent);
  275. if (pNew)
  276. {
  277. m_TempoEventList.AddHead(pNew); // instead of AddTail, which is n^2. We reverse below.
  278. }
  279. }
  280. m_TempoEventList.Reverse();
  281. }
  282. else
  283. {
  284. Trace(1,"Error: Failure reading tempo track.\n");
  285. hr = DMUS_E_CANNOTREAD;
  286. }
  287. LeaveCriticalSection(&m_CrSec);
  288. return hr;
  289. }
  290. HRESULT CTempoTrack::Save( IStream* pIStream, BOOL fClearDirty )
  291. {
  292. return E_NOTIMPL;
  293. }
  294. HRESULT CTempoTrack::GetSizeMax( ULARGE_INTEGER FAR* pcbSize )
  295. {
  296. return E_NOTIMPL;
  297. }
  298. // IDirectMusicTrack
  299. HRESULT STDMETHODCALLTYPE CTempoTrack::IsParamSupported(
  300. /* [in] */ REFGUID rguid)
  301. {
  302. V_INAME(IDirectMusicTrack::IsParamSupported);
  303. V_REFGUID(rguid);
  304. if (m_fStateSetBySetParam)
  305. {
  306. if( m_fActive )
  307. {
  308. if( rguid == GUID_DisableTempo ) return S_OK;
  309. if( rguid == GUID_TempoParam ) return S_OK;
  310. if( rguid == GUID_PrivateTempoParam ) return S_OK;
  311. if( rguid == GUID_EnableTempo ) return DMUS_E_TYPE_DISABLED;
  312. }
  313. else
  314. {
  315. if( rguid == GUID_EnableTempo ) return S_OK;
  316. if( rguid == GUID_DisableTempo ) return DMUS_E_TYPE_DISABLED;
  317. if( rguid == GUID_PrivateTempoParam ) return DMUS_E_TYPE_DISABLED;
  318. if( rguid == GUID_TempoParam ) return DMUS_E_TYPE_DISABLED;
  319. }
  320. }
  321. else
  322. {
  323. if(( rguid == GUID_DisableTempo ) ||
  324. ( rguid == GUID_TempoParam ) ||
  325. ( rguid == GUID_PrivateTempoParam ) ||
  326. ( rguid == GUID_EnableTempo )) return S_OK;
  327. }
  328. return DMUS_E_TYPE_UNSUPPORTED;
  329. }
  330. //////////////////////////////////////////////////////////////////////
  331. // IDirectMusicTrack::Init
  332. HRESULT CTempoTrack::Init(
  333. /* [in] */ IDirectMusicSegment *pSegment)
  334. {
  335. return S_OK;
  336. }
  337. HRESULT CTempoTrack::InitPlay(
  338. /* [in] */ IDirectMusicSegmentState *pSegmentState,
  339. /* [in] */ IDirectMusicPerformance *pPerformance,
  340. /* [out] */ void **ppStateData,
  341. /* [in] */ DWORD dwTrackID,
  342. /* [in] */ DWORD dwFlags)
  343. {
  344. V_INAME(IDirectMusicTrack::InitPlay);
  345. V_PTRPTR_WRITE(ppStateData);
  346. V_INTERFACE(pSegmentState);
  347. V_INTERFACE(pPerformance);
  348. TempoStateData* pStateData;
  349. pStateData = new TempoStateData;
  350. if( NULL == pStateData )
  351. return E_OUTOFMEMORY;
  352. *ppStateData = pStateData;
  353. if (m_fStateSetBySetParam)
  354. {
  355. pStateData->fActive = m_fActive;
  356. }
  357. else
  358. {
  359. pStateData->fActive = ((dwFlags & DMUS_SEGF_CONTROL) ||
  360. !(dwFlags & DMUS_SEGF_SECONDARY));
  361. }
  362. pStateData->dwVirtualTrackID = dwTrackID;
  363. pStateData->pPerformance = pPerformance; // weak reference, no addref.
  364. pStateData->pSegState = pSegmentState; // weak reference, no addref.
  365. pStateData->pCurrentTempo = m_TempoEventList.GetHead();
  366. pStateData->dwValidate = m_dwValidate;
  367. return S_OK;
  368. }
  369. HRESULT CTempoTrack::EndPlay(
  370. /* [in] */ void *pStateData)
  371. {
  372. ASSERT( pStateData );
  373. if( pStateData )
  374. {
  375. V_INAME(IDirectMusicTrack::EndPlay);
  376. V_BUFPTR_WRITE(pStateData, sizeof(TempoStateData));
  377. TempoStateData* pSD = (TempoStateData*)pStateData;
  378. delete pSD;
  379. }
  380. return S_OK;
  381. }
  382. STDMETHODIMP CTempoTrack::PlayEx(void* pStateData,REFERENCE_TIME rtStart,
  383. REFERENCE_TIME rtEnd,REFERENCE_TIME rtOffset,
  384. DWORD dwFlags,IDirectMusicPerformance* pPerf,
  385. IDirectMusicSegmentState* pSegSt,DWORD dwVirtualID)
  386. {
  387. V_INAME(IDirectMusicTrack::PlayEx);
  388. V_BUFPTR_WRITE( pStateData, sizeof(TempoStateData));
  389. V_INTERFACE(pPerf);
  390. V_INTERFACE(pSegSt);
  391. HRESULT hr;
  392. EnterCriticalSection(&m_CrSec);
  393. if (dwFlags & DMUS_TRACKF_CLOCK)
  394. {
  395. // Convert all reference times to millisecond times. Then, just use same MUSIC_TIME
  396. // variables.
  397. hr = Play(pStateData,(MUSIC_TIME)(rtStart / REF_PER_MIL),(MUSIC_TIME)(rtEnd / REF_PER_MIL),
  398. (MUSIC_TIME)(rtOffset / REF_PER_MIL),rtOffset,dwFlags,pPerf,pSegSt,dwVirtualID,TRUE);
  399. }
  400. else
  401. {
  402. hr = Play(pStateData,(MUSIC_TIME)rtStart,(MUSIC_TIME)rtEnd,
  403. (MUSIC_TIME)rtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE);
  404. }
  405. LeaveCriticalSection(&m_CrSec);
  406. return hr;
  407. }
  408. STDMETHODIMP CTempoTrack::Play(
  409. void *pStateData,
  410. MUSIC_TIME mtStart,
  411. MUSIC_TIME mtEnd,
  412. MUSIC_TIME mtOffset,
  413. DWORD dwFlags,
  414. IDirectMusicPerformance* pPerf,
  415. IDirectMusicSegmentState* pSegSt,
  416. DWORD dwVirtualID)
  417. {
  418. V_INAME(IDirectMusicTrack::Play);
  419. V_BUFPTR_WRITE( pStateData, sizeof(TempoStateData));
  420. V_INTERFACE(pPerf);
  421. V_INTERFACE(pSegSt);
  422. EnterCriticalSection(&m_CrSec);
  423. HRESULT hr = Play(pStateData,mtStart,mtEnd,mtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE);
  424. LeaveCriticalSection(&m_CrSec);
  425. return hr;
  426. }
  427. HRESULT CTempoTrack::Play(
  428. void *pStateData,
  429. MUSIC_TIME mtStart,
  430. MUSIC_TIME mtEnd,
  431. MUSIC_TIME mtOffset,
  432. REFERENCE_TIME rtOffset,
  433. DWORD dwFlags,
  434. IDirectMusicPerformance* pPerf,
  435. IDirectMusicSegmentState* pSegSt,
  436. DWORD dwVirtualID,
  437. BOOL fClockTime)
  438. {
  439. if (dwFlags & DMUS_TRACKF_PLAY_OFF)
  440. {
  441. return S_OK;
  442. }
  443. HRESULT hr = DMUS_S_END;
  444. IDirectMusicGraph* pGraph = NULL;
  445. TempoStateData* pSD = (TempoStateData*)pStateData;
  446. BOOL fSeek = (dwFlags & DMUS_TRACKF_SEEK) ? TRUE : FALSE;
  447. // if mtStart is 0 and dwFlags contains DMUS_TRACKF_START, we want to be sure to
  448. // send out any negative time events. So, we'll set mtStart to -768.
  449. if( (mtStart == 0) && ( dwFlags & DMUS_TRACKF_START ))
  450. {
  451. mtStart = -768;
  452. }
  453. // if pSD->pCurrentTempo is NULL, and we're in a normal Play call (dwFlags is 0)
  454. // this means that we either have no events, or we got to the end of the event
  455. // list previously. So, it's safe to just return.
  456. if( (pSD->pCurrentTempo == NULL) && (dwFlags == 0) )
  457. {
  458. return S_FALSE;
  459. }
  460. if( pSD->dwValidate != m_dwValidate )
  461. {
  462. pSD->dwValidate = m_dwValidate;
  463. pSD->pCurrentTempo = NULL;
  464. }
  465. if (!pSD->pCurrentTempo)
  466. {
  467. pSD->pCurrentTempo = m_TempoEventList.GetHead();
  468. }
  469. if (!pSD->pCurrentTempo)
  470. {
  471. return DMUS_S_END;
  472. }
  473. // if the previous end time isn't the same as the current start time,
  474. // we need to seek to the right position.
  475. if( fSeek || ( pSD->mtPrevEnd != mtStart ))
  476. {
  477. TempoStateData tempData;
  478. BOOL fFlag = TRUE;
  479. tempData = *pSD; // put this in so we can use Seek in other functions such as GetParam
  480. if( !fSeek && (dwFlags & DMUS_TRACKF_DIRTY ))
  481. {
  482. fFlag = FALSE;
  483. }
  484. Seek( &tempData, mtStart, fFlag );
  485. *pSD = tempData;
  486. }
  487. pSD->mtPrevEnd = mtEnd;
  488. if( FAILED( pSD->pSegState->QueryInterface( IID_IDirectMusicGraph,
  489. (void**)&pGraph )))
  490. {
  491. pGraph = NULL;
  492. }
  493. for (; pSD->pCurrentTempo; pSD->pCurrentTempo = pSD->pCurrentTempo->GetNext())
  494. {
  495. DMUS_IO_TEMPO_ITEM& rTempoEvent = pSD->pCurrentTempo->GetItemValue();
  496. if( rTempoEvent.lTime >= mtEnd )
  497. {
  498. // this time is in the future. Return now to retain the same
  499. // seek pointers for next time.
  500. hr = S_OK;
  501. break;
  502. }
  503. if( rTempoEvent.lTime < mtStart )
  504. {
  505. if( dwFlags & DMUS_TRACKF_FLUSH )
  506. {
  507. // this time is in the past, and this call to Play is in response to an
  508. // invalidate. We don't want to replay stuff before the start time.
  509. continue;
  510. }
  511. else if( !( dwFlags & DMUS_TRACKF_START) && !(dwFlags & DMUS_TRACKF_SEEK) )
  512. {
  513. // we really only want to play events earlier than mtStart on account
  514. // of a START or SEEK (that isn't a FLUSH.)
  515. continue;
  516. }
  517. }
  518. if( pSD->fActive )
  519. {
  520. DMUS_TEMPO_PMSG* pTempo;
  521. if( SUCCEEDED( pSD->pPerformance->AllocPMsg( sizeof(DMUS_TEMPO_PMSG),
  522. (DMUS_PMSG**)&pTempo )))
  523. {
  524. if( rTempoEvent.lTime < mtStart )
  525. {
  526. // this only happens in the case where we've puposefully seeked
  527. // and need to time stamp this event with the start time
  528. if (fClockTime)
  529. {
  530. pTempo->rtTime = (mtStart * REF_PER_MIL) + rtOffset;
  531. pTempo->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
  532. }
  533. else
  534. {
  535. pTempo->mtTime = mtStart + mtOffset;
  536. pTempo->dwFlags = DMUS_PMSGF_MUSICTIME;
  537. }
  538. }
  539. else
  540. {
  541. if (fClockTime)
  542. {
  543. pTempo->rtTime = (rTempoEvent.lTime * REF_PER_MIL) + rtOffset;
  544. pTempo->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
  545. }
  546. else
  547. {
  548. pTempo->mtTime = rTempoEvent.lTime + mtOffset;
  549. pTempo->dwFlags = DMUS_PMSGF_MUSICTIME;
  550. }
  551. }
  552. pTempo->dblTempo = rTempoEvent.dblTempo;
  553. pTempo->dwVirtualTrackID = pSD->dwVirtualTrackID;
  554. pTempo->dwType = DMUS_PMSGT_TEMPO;
  555. pTempo->dwGroupID = 0xffffffff;
  556. if( pGraph )
  557. {
  558. pGraph->StampPMsg( (DMUS_PMSG*)pTempo );
  559. }
  560. if(FAILED(pSD->pPerformance->SendPMsg( (DMUS_PMSG*)pTempo )))
  561. {
  562. pSD->pPerformance->FreePMsg( (DMUS_PMSG*)pTempo );
  563. }
  564. }
  565. }
  566. }
  567. if( pGraph )
  568. {
  569. pGraph->Release();
  570. }
  571. return hr;
  572. }
  573. // if fGetPrevious is TRUE, seek to the event prior to mtTime. Otherwise, seek to
  574. // the event on or after mtTime
  575. HRESULT CTempoTrack::Seek(
  576. /* [in] */ TempoStateData *pSD,
  577. /* [in] */ MUSIC_TIME mtTime, BOOL fGetPrevious)
  578. {
  579. TListItem<DMUS_IO_TEMPO_ITEM>* pScan = pSD->pCurrentTempo;
  580. if (!pScan)
  581. {
  582. pScan = m_TempoEventList.GetHead();
  583. }
  584. if (!pScan)
  585. {
  586. return S_FALSE;
  587. }
  588. // if the event's time is on or past mtTime, we need to go to the beginning
  589. if (pScan->GetItemValue().lTime >= mtTime)
  590. {
  591. pScan = m_TempoEventList.GetHead();
  592. }
  593. pSD->pCurrentTempo = pScan;
  594. for (; pScan; pScan = pScan->GetNext())
  595. {
  596. if (pScan->GetItemValue().lTime >= mtTime)
  597. {
  598. if (!fGetPrevious)
  599. {
  600. pSD->pCurrentTempo = pScan;
  601. }
  602. break;
  603. }
  604. pSD->pCurrentTempo = pScan;
  605. }
  606. return S_OK;
  607. }
  608. STDMETHODIMP CTempoTrack::GetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime,
  609. REFERENCE_TIME* prtNext,void* pParam,void * pStateData, DWORD dwFlags)
  610. {
  611. HRESULT hr;
  612. MUSIC_TIME mtNext;
  613. if (dwFlags & DMUS_TRACK_PARAMF_CLOCK)
  614. {
  615. hr = GetParam(rguidType,(MUSIC_TIME) (rtTime / REF_PER_MIL), &mtNext, pParam);
  616. if (prtNext)
  617. {
  618. *prtNext = mtNext * REF_PER_MIL;
  619. }
  620. }
  621. else
  622. {
  623. hr = GetParam(rguidType,(MUSIC_TIME) rtTime, &mtNext, pParam);
  624. if (prtNext)
  625. {
  626. *prtNext = mtNext;
  627. }
  628. }
  629. return hr;
  630. }
  631. STDMETHODIMP CTempoTrack::SetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime,
  632. void* pParam, void * pStateData, DWORD dwFlags)
  633. {
  634. if (dwFlags & DMUS_TRACK_PARAMF_CLOCK)
  635. {
  636. rtTime /= REF_PER_MIL;
  637. }
  638. return SetParam(rguidType, (MUSIC_TIME) rtTime , pParam);
  639. }
  640. HRESULT CTempoTrack::GetParam(
  641. REFGUID rguid,
  642. MUSIC_TIME mtTime,
  643. MUSIC_TIME* pmtNext,
  644. void *pData)
  645. {
  646. V_INAME(IDirectMusicTrack::GetParam);
  647. V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME);
  648. V_REFGUID(rguid);
  649. HRESULT hr = DMUS_E_GET_UNSUPPORTED;
  650. if( NULL == pData )
  651. {
  652. return E_POINTER;
  653. }
  654. if( rguid == GUID_PrivateTempoParam )
  655. {
  656. DMUS_TEMPO_PARAM TempoData;
  657. PrivateTempo* pPrivateTempoData = (PrivateTempo*)pData;
  658. hr = GetParam(GUID_TempoParam, mtTime, pmtNext, (void*)&TempoData);
  659. if (hr == S_OK)
  660. {
  661. pPrivateTempoData->dblTempo = TempoData.dblTempo;
  662. pPrivateTempoData->mtTime = 0; // must be set by the caller
  663. pPrivateTempoData->mtDelta = TempoData.mtTime;
  664. pPrivateTempoData->fLast = (pmtNext && !*pmtNext);
  665. }
  666. else if (hr == DMUS_E_NOT_FOUND) // the tempo track was empty
  667. {
  668. pPrivateTempoData->fLast = true;
  669. }
  670. }
  671. else if( rguid == GUID_TempoParam )
  672. {
  673. if( !m_fActive )
  674. {
  675. return DMUS_E_TYPE_DISABLED;
  676. }
  677. DMUS_TEMPO_PARAM* pTempoData = (DMUS_TEMPO_PARAM*)pData;
  678. TListItem<DMUS_IO_TEMPO_ITEM>* pScan = m_TempoEventList.GetHead();
  679. TListItem<DMUS_IO_TEMPO_ITEM>* pPrevious = pScan;
  680. if (!pScan)
  681. {
  682. return DMUS_E_NOT_FOUND;
  683. }
  684. for (; pScan; pScan = pScan->GetNext())
  685. {
  686. if (pScan->GetItemValue().lTime > mtTime)
  687. {
  688. break;
  689. }
  690. pPrevious = pScan;
  691. }
  692. DMUS_IO_TEMPO_ITEM& rTempoEvent = pPrevious->GetItemValue();
  693. pTempoData->dblTempo = rTempoEvent.dblTempo;
  694. pTempoData->mtTime = rTempoEvent.lTime - mtTime;
  695. if (pmtNext)
  696. {
  697. *pmtNext = 0;
  698. }
  699. if (pScan)
  700. {
  701. DMUS_IO_TEMPO_ITEM& rNextTempoEvent = pScan->GetItemValue();
  702. if (pmtNext)
  703. {
  704. *pmtNext = rNextTempoEvent.lTime - mtTime;
  705. }
  706. }
  707. hr = S_OK;
  708. }
  709. return hr;
  710. }
  711. // Q: if all tracks are time-stamped, why do we need mtTime?
  712. HRESULT CTempoTrack::SetParam(
  713. REFGUID rguid,
  714. MUSIC_TIME mtTime,
  715. void *pData)
  716. {
  717. V_INAME(IDirectMusicTrack::SetParam);
  718. V_REFGUID(rguid);
  719. EnterCriticalSection(&m_CrSec);
  720. HRESULT hr = DMUS_E_SET_UNSUPPORTED;
  721. if( rguid == GUID_DisableTempo )
  722. {
  723. if (m_fStateSetBySetParam && !m_fActive)
  724. { // Already been disabled.
  725. hr = DMUS_E_TYPE_DISABLED;
  726. }
  727. else
  728. {
  729. m_fStateSetBySetParam = TRUE;
  730. m_fActive = FALSE;
  731. hr = S_OK;
  732. }
  733. }
  734. else if( rguid == GUID_EnableTempo )
  735. {
  736. if (m_fStateSetBySetParam && m_fActive)
  737. { // Already been enabled.
  738. hr = DMUS_E_TYPE_DISABLED;
  739. }
  740. else
  741. {
  742. m_fStateSetBySetParam = TRUE;
  743. m_fActive = TRUE;
  744. hr = S_OK;
  745. }
  746. }
  747. else if( rguid == GUID_TempoParam )
  748. {
  749. if (!m_fActive)
  750. { // Oops, app intentionally disabled tempo.
  751. hr = DMUS_E_TYPE_DISABLED;
  752. }
  753. else
  754. {
  755. if( NULL == pData )
  756. {
  757. LeaveCriticalSection(&m_CrSec);
  758. return E_POINTER;
  759. }
  760. DMUS_TEMPO_PARAM* pTempoData = (DMUS_TEMPO_PARAM*)pData;
  761. TListItem<DMUS_IO_TEMPO_ITEM>* pScan = m_TempoEventList.GetHead();
  762. TListItem<DMUS_IO_TEMPO_ITEM>* pPrevious = NULL;
  763. for (; pScan; pScan = pScan->GetNext())
  764. {
  765. if (pScan->GetItemValue().lTime >= mtTime)
  766. {
  767. break;
  768. }
  769. pPrevious = pScan;
  770. }
  771. // Make a new DMUS_IO_TEMPO_ITEM and insert it after pPrevious
  772. TListItem<DMUS_IO_TEMPO_ITEM>* pNew = new TListItem<DMUS_IO_TEMPO_ITEM>;
  773. if (!pNew)
  774. {
  775. LeaveCriticalSection(&m_CrSec);
  776. return E_OUTOFMEMORY;
  777. }
  778. DMUS_IO_TEMPO_ITEM& rTempoEvent = pNew->GetItemValue();
  779. rTempoEvent.dblTempo = pTempoData->dblTempo;
  780. /*
  781. // I believe the fix for 204160 was supposed to change this line to what
  782. // follows the comment. RSW
  783. rTempoEvent.lTime = pTempoData->mtTime;
  784. */
  785. rTempoEvent.lTime = mtTime;
  786. if (pPrevious)
  787. {
  788. pNew->SetNext(pScan);
  789. pPrevious->SetNext(pNew);
  790. }
  791. else
  792. {
  793. m_TempoEventList.AddHead(pNew);
  794. }
  795. if (pScan && pScan->GetItemValue().lTime == mtTime)
  796. {
  797. // remove it
  798. pNew->SetNext(pScan->GetNext());
  799. pScan->SetNext(NULL);
  800. delete pScan;
  801. }
  802. m_dwValidate++;
  803. hr = S_OK;
  804. }
  805. }
  806. LeaveCriticalSection(&m_CrSec);
  807. return hr;
  808. }
  809. HRESULT STDMETHODCALLTYPE CTempoTrack::AddNotificationType(
  810. /* [in] */ REFGUID rguidNotification)
  811. {
  812. return E_NOTIMPL;
  813. }
  814. HRESULT STDMETHODCALLTYPE CTempoTrack::RemoveNotificationType(
  815. /* [in] */ REFGUID rguidNotification)
  816. {
  817. return E_NOTIMPL;
  818. }
  819. HRESULT STDMETHODCALLTYPE CTempoTrack::Clone(
  820. MUSIC_TIME mtStart,
  821. MUSIC_TIME mtEnd,
  822. IDirectMusicTrack** ppTrack)
  823. {
  824. V_INAME(IDirectMusicTrack::Clone);
  825. V_PTRPTR_WRITE(ppTrack);
  826. HRESULT hr = S_OK;
  827. if(mtStart < 0 )
  828. {
  829. return E_INVALIDARG;
  830. }
  831. if(mtStart > mtEnd)
  832. {
  833. return E_INVALIDARG;
  834. }
  835. EnterCriticalSection(&m_CrSec);
  836. CTempoTrack *pDM;
  837. try
  838. {
  839. pDM = new CTempoTrack(*this, mtStart, mtEnd);
  840. }
  841. catch( ... )
  842. {
  843. pDM = NULL;
  844. }
  845. LeaveCriticalSection(&m_CrSec);
  846. if (pDM == NULL) {
  847. return E_OUTOFMEMORY;
  848. }
  849. hr = pDM->QueryInterface(IID_IDirectMusicTrack, (void**)ppTrack);
  850. pDM->Release();
  851. return hr;
  852. }
  853. STDMETHODIMP CTempoTrack::Compose(
  854. IUnknown* pContext,
  855. DWORD dwTrackGroup,
  856. IDirectMusicTrack** ppResultTrack)
  857. {
  858. return E_NOTIMPL;
  859. }
  860. STDMETHODIMP CTempoTrack::Join(
  861. IDirectMusicTrack* pNewTrack,
  862. MUSIC_TIME mtJoin,
  863. IUnknown* pContext,
  864. DWORD dwTrackGroup,
  865. IDirectMusicTrack** ppResultTrack)
  866. {
  867. V_INAME(IDirectMusicTrack::Join);
  868. V_INTERFACE(pNewTrack);
  869. V_INTERFACE_OPT(pContext);
  870. V_PTRPTR_WRITE_OPT(ppResultTrack);
  871. HRESULT hr = S_OK;
  872. EnterCriticalSection(&m_CrSec);
  873. if (ppResultTrack)
  874. {
  875. hr = Clone(0, mtJoin, ppResultTrack);
  876. if (SUCCEEDED(hr))
  877. {
  878. hr = ((CTempoTrack*)*ppResultTrack)->JoinInternal(pNewTrack, mtJoin, dwTrackGroup);
  879. }
  880. }
  881. else
  882. {
  883. hr = JoinInternal(pNewTrack, mtJoin, dwTrackGroup);
  884. }
  885. LeaveCriticalSection(&m_CrSec);
  886. return hr;
  887. }
  888. HRESULT CTempoTrack::JoinInternal(
  889. IDirectMusicTrack* pNewTrack,
  890. MUSIC_TIME mtJoin,
  891. DWORD dwTrackGroup)
  892. {
  893. HRESULT hr = S_OK;
  894. CTempoTrack* pOtherTrack = (CTempoTrack*)pNewTrack;
  895. TListItem<DMUS_IO_TEMPO_ITEM>* pScan = pOtherTrack->m_TempoEventList.GetHead();
  896. for (; pScan; pScan = pScan->GetNext())
  897. {
  898. DMUS_IO_TEMPO_ITEM& rScan = pScan->GetItemValue();
  899. TListItem<DMUS_IO_TEMPO_ITEM>* pNew = new TListItem<DMUS_IO_TEMPO_ITEM>;
  900. if (pNew)
  901. {
  902. DMUS_IO_TEMPO_ITEM& rNew = pNew->GetItemValue();
  903. rNew.lTime = rScan.lTime + mtJoin;
  904. rNew.dblTempo = rScan.dblTempo;
  905. m_TempoEventList.AddTail(pNew);
  906. }
  907. else
  908. {
  909. hr = E_OUTOFMEMORY;
  910. break;
  911. }
  912. }
  913. return hr;
  914. }