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.

882 lines
23 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. // TimeSigTrk.cpp : Implementation of CTimeSigTrack
  21. #include "dmime.h"
  22. #include "TSigTrk.h"
  23. #include "dmusici.h"
  24. #include "dmusicf.h"
  25. #include "debug.h"
  26. #include "..\shared\dmstrm.h"
  27. #include "..\shared\Validate.h"
  28. #include "debug.h"
  29. #define ASSERT assert
  30. CTimeSigItem::CTimeSigItem()
  31. {
  32. m_TimeSig.lTime = 0;
  33. m_TimeSig.bBeatsPerMeasure = 0;
  34. m_TimeSig.bBeat = 0;
  35. m_TimeSig.wGridsPerBeat = 0;
  36. }
  37. /////////////////////////////////////////////////////////////////////////////
  38. // CTimeSigTrack
  39. void CTimeSigTrack::Construct()
  40. {
  41. InterlockedIncrement(&g_cComponent);
  42. m_cRef = 1;
  43. m_fCSInitialized = FALSE;
  44. InitializeCriticalSection(&m_CrSec);
  45. m_fCSInitialized = TRUE;
  46. m_dwValidate = 0;
  47. m_fNotificationMeasureBeat = FALSE;
  48. }
  49. CTimeSigTrack::CTimeSigTrack()
  50. {
  51. Construct();
  52. m_fActive = TRUE;
  53. m_fStateSetBySetParam = FALSE;
  54. }
  55. CTimeSigTrack::CTimeSigTrack(
  56. CTimeSigTrack *pSourceTrack, MUSIC_TIME mtStart, MUSIC_TIME mtEnd)
  57. {
  58. Construct();
  59. m_fActive = pSourceTrack->m_fActive;
  60. m_fStateSetBySetParam = pSourceTrack->m_fStateSetBySetParam;
  61. // Clone the time signature list.
  62. CTimeSigItem* pScan = pSourceTrack->m_TSigEventList.GetHead();
  63. CTimeSigItem* pPrevious = NULL;
  64. for(; pScan; pScan = pScan->GetNext())
  65. {
  66. if (pScan->m_TimeSig.lTime < mtStart)
  67. {
  68. pPrevious = pScan;
  69. }
  70. else if (pScan->m_TimeSig.lTime < mtEnd)
  71. {
  72. if (pScan->m_TimeSig.lTime == mtStart)
  73. {
  74. pPrevious = NULL;
  75. }
  76. CTimeSigItem* pNew = new CTimeSigItem;
  77. if (pNew)
  78. {
  79. pNew->m_TimeSig = pScan->m_TimeSig;
  80. pNew->m_TimeSig.lTime = pScan->m_TimeSig.lTime - mtStart;
  81. m_TSigEventList.AddHead(pNew); // instead of AddTail, which is n^2. We reverse below.
  82. }
  83. }
  84. else break;
  85. }
  86. m_TSigEventList.Reverse(); // Now, put list in order.
  87. // Then, install the time signature that precedes the clone.
  88. if (pPrevious)
  89. {
  90. CTimeSigItem* pNew = new CTimeSigItem;
  91. if (pNew)
  92. {
  93. pNew->m_TimeSig = pPrevious->m_TimeSig;
  94. pNew->m_TimeSig.lTime = 0;
  95. m_TSigEventList.AddHead(pNew);
  96. }
  97. }
  98. }
  99. void CTimeSigTrack::Clear()
  100. {
  101. CTimeSigItem* pItem;
  102. while( pItem = m_TSigEventList.RemoveHead() )
  103. {
  104. delete pItem;
  105. }
  106. }
  107. CTimeSigTrack::~CTimeSigTrack()
  108. {
  109. Clear();
  110. if (m_fCSInitialized)
  111. {
  112. DeleteCriticalSection(&m_CrSec);
  113. }
  114. InterlockedDecrement(&g_cComponent);
  115. }
  116. // @method:(EXTERNAL) HRESULT | IDirectMusicTimeSigTrack | QueryInterface | Standard QueryInterface implementation for <i IDirectMusicTimeSigTrack>
  117. //
  118. // @parm const IID & | iid | Interface to query for
  119. // @parm void ** | ppv | The requested interface will be returned here
  120. //
  121. // @rdesc Returns one of the following:
  122. //
  123. // @flag S_OK | If the interface is supported and was returned
  124. // @flag E_NOINTERFACE | If the object does not support the given interface.
  125. //
  126. // @mfunc:(INTERNAL)
  127. //
  128. //
  129. STDMETHODIMP CTimeSigTrack::QueryInterface(
  130. const IID &iid, // @parm Interface to query for
  131. void **ppv) // @parm The requested interface will be returned here
  132. {
  133. V_INAME(CTimeSigTrack::QueryInterface);
  134. V_PTRPTR_WRITE(ppv);
  135. V_REFGUID(iid);
  136. if (iid == IID_IUnknown || iid == IID_IDirectMusicTrack)
  137. {
  138. *ppv = static_cast<IDirectMusicTrack*>(this);
  139. } else
  140. if (iid == IID_IPersistStream)
  141. {
  142. *ppv = static_cast<IPersistStream*>(this);
  143. } else
  144. {
  145. *ppv = NULL;
  146. Trace(4,"Warning: Request to query unknown interface on Time Signature Track\n");
  147. return E_NOINTERFACE;
  148. }
  149. reinterpret_cast<IUnknown*>(this)->AddRef();
  150. return S_OK;
  151. }
  152. // @method:(EXTERNAL) HRESULT | IDirectMusicTimeSigTrack | AddRef | Standard AddRef implementation for <i IDirectMusicTimeSigTrack>
  153. //
  154. // @rdesc Returns the new reference count for this object.
  155. //
  156. // @mfunc:(INTERNAL)
  157. //
  158. //
  159. STDMETHODIMP_(ULONG) CTimeSigTrack::AddRef()
  160. {
  161. return InterlockedIncrement(&m_cRef);
  162. }
  163. // @method:(EXTERNAL) HRESULT | IDirectMusicTimeSigTrack | Release | Standard Release implementation for <i IDirectMusicTimeSigTrack>
  164. //
  165. // @rdesc Returns the new reference count for this object.
  166. //
  167. // @mfunc:(INTERNAL)
  168. //
  169. //
  170. STDMETHODIMP_(ULONG) CTimeSigTrack::Release()
  171. {
  172. if (!InterlockedDecrement(&m_cRef))
  173. {
  174. delete this;
  175. return 0;
  176. }
  177. return m_cRef;
  178. }
  179. /////////////////////////////////////////////////////////////////////////////
  180. // IPersist
  181. HRESULT CTimeSigTrack::GetClassID( CLSID* pClassID )
  182. {
  183. V_INAME(CTimeSigTrack::GetClassID);
  184. V_PTR_WRITE(pClassID, CLSID);
  185. *pClassID = CLSID_DirectMusicTimeSigTrack;
  186. return S_OK;
  187. }
  188. /////////////////////////////////////////////////////////////////////////////
  189. // IPersistStream functions
  190. HRESULT CTimeSigTrack::IsDirty()
  191. {
  192. return S_FALSE;
  193. }
  194. /*
  195. @method HRESULT | ITimeSigTrack | Load |
  196. Call this with an IStream filled with DMUS_IO_TIMESIGNATURE_ITEM's, sorted in time order.
  197. @parm IStream* | pIStream |
  198. A stream of DMUS_IO_TIMESIGNATURE_ITEM's, sorted in time order. The seek pointer should be
  199. set to the first event. The stream should only contain TimeSig events and
  200. nothing more.
  201. @rvalue E_INVALIDARG | If pIStream == NULL
  202. @rvalue S_OK
  203. @comm The <p pIStream> will be AddRef'd inside this function and held
  204. until the TimeSigTrack is released.
  205. */
  206. HRESULT CTimeSigTrack::Load( IStream* pIStream )
  207. {
  208. V_INAME(CTimeSigTrack::Load);
  209. V_INTERFACE(pIStream);
  210. CRiffParser Parser(pIStream);
  211. EnterCriticalSection(&m_CrSec);
  212. m_dwValidate++; // used to validate state data that's out there
  213. RIFFIO ckMain;
  214. HRESULT hr = S_OK;
  215. Parser.EnterList(&ckMain);
  216. if (Parser.NextChunk(&hr))
  217. {
  218. if (ckMain.ckid == DMUS_FOURCC_TIMESIG_CHUNK)
  219. {
  220. hr = LoadTimeSigList(&Parser,ckMain.cksize);
  221. }
  222. else if ((ckMain.ckid == FOURCC_LIST) &&
  223. (ckMain.fccType == DMUS_FOURCC_TIMESIGTRACK_LIST))
  224. {
  225. Clear();
  226. RIFFIO ckNext; // Descends into the children chunks.
  227. Parser.EnterList(&ckNext);
  228. while (Parser.NextChunk(&hr))
  229. {
  230. switch(ckNext.ckid)
  231. {
  232. case DMUS_FOURCC_TIMESIG_CHUNK :
  233. hr = LoadTimeSigList(&Parser,ckNext.cksize);
  234. break;
  235. }
  236. }
  237. Parser.LeaveList();
  238. }
  239. else
  240. {
  241. Trace(1,"Error: Failure reading bad data in time signature track.\n");
  242. hr = DMUS_E_CHUNKNOTFOUND;
  243. }
  244. }
  245. LeaveCriticalSection(&m_CrSec);
  246. return hr;
  247. }
  248. HRESULT CTimeSigTrack::LoadTimeSigList( CRiffParser *pParser, long lChunkSize )
  249. {
  250. HRESULT hr;
  251. // copy contents of the stream into the list.
  252. DWORD dwSubSize;
  253. // read in the size of the data structures
  254. hr = pParser->Read( &dwSubSize, sizeof(DWORD));
  255. if (SUCCEEDED(hr))
  256. {
  257. lChunkSize -= sizeof(DWORD);
  258. DWORD dwRead, dwSeek;
  259. if( dwSubSize > sizeof(DMUS_IO_TIMESIGNATURE_ITEM) )
  260. {
  261. dwRead = sizeof(DMUS_IO_TIMESIGNATURE_ITEM);
  262. dwSeek = dwSubSize - dwRead;
  263. }
  264. else
  265. {
  266. dwRead = dwSubSize;
  267. dwSeek = 0;
  268. }
  269. if( 0 == dwRead )
  270. {
  271. Trace(1,"Error: Failure reading time signature track.\n");
  272. hr = DMUS_E_CANNOTREAD;
  273. }
  274. else
  275. {
  276. while( lChunkSize > 0 )
  277. {
  278. CTimeSigItem *pNew = new CTimeSigItem;
  279. if (pNew)
  280. {
  281. if( FAILED( pParser->Read( &pNew->m_TimeSig, dwRead )))
  282. {
  283. delete pNew;
  284. hr = DMUS_E_CANNOTREAD;
  285. break;
  286. }
  287. // make sure this time sig is OK
  288. if (!pNew->m_TimeSig.bBeatsPerMeasure)
  289. {
  290. Trace(1, "Warning: invalid content: DMUS_IO_TIMESIGNATURE_ITEM.bBeatsPerMeasure\n");
  291. pNew->m_TimeSig.bBeatsPerMeasure = 4;
  292. }
  293. if (!pNew->m_TimeSig.bBeat)
  294. {
  295. Trace(1, "Warning: invalid content: DMUS_IO_TIMESIGNATURE_ITEM.bBeat\n");
  296. pNew->m_TimeSig.bBeat = 4;
  297. }
  298. if (!pNew->m_TimeSig.wGridsPerBeat)
  299. {
  300. Trace(1, "Warning: invalid content: DMUS_IO_TIMESIGNATURE_ITEM.wGridsPerBeat\n");
  301. pNew->m_TimeSig.wGridsPerBeat = 4;
  302. }
  303. m_TSigEventList.AddHead(pNew);
  304. lChunkSize -= dwRead;
  305. if( dwSeek )
  306. {
  307. if( FAILED( pParser->Skip(dwSeek)))
  308. {
  309. hr = DMUS_E_CANNOTSEEK;
  310. break;
  311. }
  312. lChunkSize -= dwSeek;
  313. }
  314. }
  315. }
  316. m_TSigEventList.Reverse();
  317. // If there is no time signature at the start, make a copy of the
  318. // first time signature and stick it there. This resolves a bug in 6.1
  319. // where notification messages and GetParam() were inconsistent
  320. // in their behavior under this circumstance. This ensures they behave
  321. // the same.
  322. CTimeSigItem *pTop = m_TSigEventList.GetHead();
  323. if (pTop && (pTop->m_TimeSig.lTime > 0))
  324. {
  325. CTimeSigItem *pCopy = new CTimeSigItem;
  326. if (pCopy)
  327. {
  328. *pCopy = *pTop;
  329. pCopy->m_TimeSig.lTime = 0;
  330. m_TSigEventList.AddHead(pCopy);
  331. }
  332. }
  333. }
  334. }
  335. return hr;
  336. }
  337. HRESULT CTimeSigTrack::Save( IStream* pIStream, BOOL fClearDirty )
  338. {
  339. return E_NOTIMPL;
  340. }
  341. HRESULT CTimeSigTrack::GetSizeMax( ULARGE_INTEGER FAR* pcbSize )
  342. {
  343. return E_NOTIMPL;
  344. }
  345. // IDirectMusicTrack
  346. HRESULT STDMETHODCALLTYPE CTimeSigTrack::IsParamSupported(
  347. /* [in] */ REFGUID rguid)
  348. {
  349. V_INAME(CTimeSigTrack::IsParamSupported);
  350. V_REFGUID(rguid);
  351. if (m_fStateSetBySetParam)
  352. {
  353. if( m_fActive )
  354. {
  355. if( rguid == GUID_DisableTimeSig ) return S_OK;
  356. if( rguid == GUID_TimeSignature ) return S_OK;
  357. if( rguid == GUID_EnableTimeSig ) return DMUS_E_TYPE_DISABLED;
  358. }
  359. else
  360. {
  361. if( rguid == GUID_EnableTimeSig ) return S_OK;
  362. if( rguid == GUID_DisableTimeSig ) return DMUS_E_TYPE_DISABLED;
  363. if( rguid == GUID_TimeSignature ) return DMUS_E_TYPE_DISABLED;
  364. }
  365. }
  366. else
  367. {
  368. if(( rguid == GUID_DisableTimeSig ) ||
  369. ( rguid == GUID_TimeSignature ) ||
  370. ( rguid == GUID_EnableTimeSig )) return S_OK;
  371. }
  372. return DMUS_E_TYPE_UNSUPPORTED;
  373. }
  374. //////////////////////////////////////////////////////////////////////
  375. // IDirectMusicTrack::Init
  376. HRESULT CTimeSigTrack::Init(
  377. /* [in] */ IDirectMusicSegment *pSegment)
  378. {
  379. return S_OK;
  380. }
  381. HRESULT CTimeSigTrack::InitPlay(
  382. /* [in] */ IDirectMusicSegmentState *pSegmentState,
  383. /* [in] */ IDirectMusicPerformance *pPerformance,
  384. /* [out] */ void **ppStateData,
  385. /* [in] */ DWORD dwTrackID,
  386. /* [in] */ DWORD dwFlags)
  387. {
  388. V_INAME(IDirectMusicTrack::InitPlay);
  389. V_PTRPTR_WRITE(ppStateData);
  390. V_INTERFACE(pSegmentState);
  391. V_INTERFACE(pPerformance);
  392. EnterCriticalSection(&m_CrSec);
  393. CTimeSigStateData* pStateData;
  394. pStateData = new CTimeSigStateData;
  395. if( NULL == pStateData )
  396. return E_OUTOFMEMORY;
  397. *ppStateData = pStateData;
  398. if (m_fStateSetBySetParam)
  399. {
  400. pStateData->m_fActive = m_fActive;
  401. }
  402. else
  403. {
  404. pStateData->m_fActive = !(dwFlags & (DMUS_SEGF_CONTROL | DMUS_SEGF_SECONDARY));
  405. }
  406. pStateData->m_dwVirtualTrackID = dwTrackID;
  407. pStateData->m_pPerformance = pPerformance; // weak reference, no addref.
  408. pStateData->m_pSegState = pSegmentState; // weak reference, no addref.
  409. pStateData->m_pCurrentTSig = m_TSigEventList.GetHead();
  410. pStateData->m_dwValidate = m_dwValidate;
  411. LeaveCriticalSection(&m_CrSec);
  412. return S_OK;
  413. }
  414. HRESULT CTimeSigTrack::EndPlay(
  415. /* [in] */ void *pStateData)
  416. {
  417. ASSERT( pStateData );
  418. if( pStateData )
  419. {
  420. V_INAME(CTimeSigTrack::EndPlay);
  421. V_BUFPTR_WRITE(pStateData, sizeof(CTimeSigStateData));
  422. CTimeSigStateData* pSD = (CTimeSigStateData*)pStateData;
  423. delete pSD;
  424. }
  425. return S_OK;
  426. }
  427. HRESULT CTimeSigTrack::Play(
  428. /* [in] */ void *pStateData,
  429. /* [in] */ MUSIC_TIME mtStart,
  430. /* [in] */ MUSIC_TIME mtEnd,
  431. /* [in] */ MUSIC_TIME mtOffset,
  432. DWORD dwFlags,
  433. IDirectMusicPerformance* pPerf,
  434. IDirectMusicSegmentState* pSegSt,
  435. DWORD dwVirtualID
  436. )
  437. {
  438. V_INAME(IDirectMusicTrack::Play);
  439. V_BUFPTR_WRITE( pStateData, sizeof(CTimeSigStateData));
  440. V_INTERFACE(pPerf);
  441. V_INTERFACE(pSegSt);
  442. EnterCriticalSection(&m_CrSec);
  443. HRESULT hr = S_OK;
  444. IDirectMusicGraph* pGraph = NULL;
  445. DMUS_TIMESIG_PMSG* pTimeSig;
  446. CTimeSigStateData* pSD = (CTimeSigStateData*)pStateData;
  447. MUSIC_TIME mtNotification = mtStart;
  448. BOOL fSeek = (dwFlags & DMUS_TRACKF_SEEK) ? TRUE : FALSE;
  449. // if mtStart is 0 and dwFlags contains DMUS_TRACKF_START, we want to be sure to
  450. // send out any negative time events. So, we'll set mtStart to -768.
  451. if( (mtStart == 0) && ( dwFlags & DMUS_TRACKF_START ))
  452. {
  453. mtStart = -768;
  454. }
  455. if( pSD->m_dwValidate != m_dwValidate )
  456. {
  457. pSD->m_dwValidate = m_dwValidate;
  458. pSD->m_pCurrentTSig = NULL;
  459. }
  460. // if the previous end time isn't the same as the current start time,
  461. // we need to seek to the right position.
  462. if( fSeek || ( pSD->m_mtPrevEnd != mtStart ))
  463. {
  464. if( dwFlags & (DMUS_TRACKF_START | DMUS_TRACKF_LOOP) )
  465. {
  466. Seek( pStateData, mtStart, TRUE );
  467. }
  468. else
  469. {
  470. Seek( pStateData, mtStart, FALSE );
  471. }
  472. }
  473. pSD->m_mtPrevEnd = mtEnd;
  474. if( NULL == pSD->m_pCurrentTSig )
  475. {
  476. pSD->m_pCurrentTSig = m_TSigEventList.GetHead();
  477. }
  478. if( FAILED( pSD->m_pSegState->QueryInterface( IID_IDirectMusicGraph,
  479. (void**)&pGraph )))
  480. {
  481. pGraph = NULL;
  482. }
  483. for( ; pSD->m_pCurrentTSig; pSD->m_pCurrentTSig = pSD->m_pCurrentTSig->GetNext() )
  484. {
  485. DMUS_IO_TIMESIGNATURE_ITEM *pItem = &pSD->m_pCurrentTSig->m_TimeSig;
  486. if( pItem->lTime >= mtEnd )
  487. {
  488. break;
  489. }
  490. if( (pItem->lTime < mtStart) && !fSeek )
  491. {
  492. break;
  493. }
  494. if( pSD->m_fActive && !(dwFlags & DMUS_TRACKF_PLAY_OFF) && SUCCEEDED( pSD->m_pPerformance->AllocPMsg( sizeof(DMUS_TIMESIG_PMSG),
  495. (DMUS_PMSG**)&pTimeSig )))
  496. {
  497. if( pItem->lTime < mtStart )
  498. {
  499. // this only happens in the case where we've puposefully seeked
  500. // and need to time stamp this event with the start time
  501. pTimeSig->mtTime = mtStart + mtOffset;
  502. }
  503. else
  504. {
  505. pTimeSig->mtTime = pItem->lTime + mtOffset;
  506. }
  507. pTimeSig->bBeatsPerMeasure = pItem->bBeatsPerMeasure;
  508. pTimeSig->bBeat = pItem->bBeat;
  509. pTimeSig->wGridsPerBeat = pItem->wGridsPerBeat;
  510. pTimeSig->dwFlags |= DMUS_PMSGF_MUSICTIME;
  511. pTimeSig->dwVirtualTrackID = pSD->m_dwVirtualTrackID;
  512. pTimeSig->dwType = DMUS_PMSGT_TIMESIG;
  513. pTimeSig->dwGroupID = 0xffffffff;
  514. if( pGraph )
  515. {
  516. pGraph->StampPMsg( (DMUS_PMSG*)pTimeSig );
  517. }
  518. TraceI(3, "TimeSigtrk: TimeSig event\n");
  519. if(FAILED(pSD->m_pPerformance->SendPMsg( (DMUS_PMSG*)pTimeSig )))
  520. {
  521. pSD->m_pPerformance->FreePMsg( (DMUS_PMSG*)pTimeSig );
  522. }
  523. }
  524. if( pSD->m_fActive && m_fNotificationMeasureBeat && !(dwFlags & DMUS_TRACKF_NOTIFY_OFF))
  525. {
  526. // create beat and measure notifications for up to this time
  527. if (mtNotification < pItem->lTime)
  528. {
  529. mtNotification = NotificationMeasureBeat( mtNotification, pItem->lTime, pSD, mtOffset );
  530. }
  531. }
  532. // set the state data to the new beat and beats per measure, and time
  533. pSD->m_bBeat = pItem->bBeat;
  534. pSD->m_bBeatsPerMeasure = pItem->bBeatsPerMeasure;
  535. pSD->m_mtTimeSig = pItem->lTime;
  536. }
  537. if( pSD->m_fActive && m_fNotificationMeasureBeat && ( mtNotification < mtEnd )
  538. && !(dwFlags & DMUS_TRACKF_NOTIFY_OFF))
  539. {
  540. NotificationMeasureBeat( mtNotification, mtEnd, pSD, mtOffset );
  541. }
  542. if( pGraph )
  543. {
  544. pGraph->Release();
  545. }
  546. LeaveCriticalSection(&m_CrSec);
  547. return hr;
  548. }
  549. // seeks to the time sig. just before mtTime.
  550. HRESULT CTimeSigTrack::Seek(
  551. /* [in] */ void *pStateData,
  552. /* [in] */ MUSIC_TIME mtTime, BOOL fGetPrevious)
  553. {
  554. CTimeSigStateData* pSD = (CTimeSigStateData*)pStateData;
  555. if( m_TSigEventList.IsEmpty() )
  556. {
  557. return S_FALSE;
  558. }
  559. if( NULL == pSD->m_pCurrentTSig )
  560. {
  561. pSD->m_pCurrentTSig = m_TSigEventList.GetHead();
  562. }
  563. // if the current event's time is on or past mtTime, we need to rewind to the beginning
  564. if( pSD->m_pCurrentTSig->m_TimeSig.lTime >= mtTime )
  565. {
  566. pSD->m_pCurrentTSig = m_TSigEventList.GetHead();
  567. }
  568. // now start seeking until we find an event with time on or past mtTime
  569. CTimeSigItem* pTSig;
  570. for( pTSig = pSD->m_pCurrentTSig; pTSig ; pTSig = pTSig->GetNext() )
  571. {
  572. if( pTSig->m_TimeSig.lTime >= mtTime )
  573. {
  574. break;
  575. }
  576. pSD->m_pCurrentTSig = pTSig;
  577. }
  578. if( !fGetPrevious && pSD->m_pCurrentTSig )
  579. {
  580. pSD->m_pCurrentTSig = pSD->m_pCurrentTSig->GetNext();
  581. }
  582. return S_OK;
  583. }
  584. HRESULT CTimeSigTrack::GetParam(
  585. REFGUID rguid,
  586. MUSIC_TIME mtTime,
  587. MUSIC_TIME* pmtNext,
  588. void *pData)
  589. {
  590. V_INAME(CTimeSigTrack::GetParam);
  591. V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME);
  592. V_REFGUID(rguid);
  593. HRESULT hr = DMUS_E_GET_UNSUPPORTED;
  594. EnterCriticalSection(&m_CrSec);
  595. if( NULL == pData )
  596. {
  597. hr = E_POINTER;
  598. }
  599. else if( GUID_TimeSignature == rguid )
  600. {
  601. if( !m_fActive )
  602. {
  603. hr = DMUS_E_TYPE_DISABLED;
  604. }
  605. else
  606. {
  607. DMUS_TIMESIGNATURE* pTSigData = (DMUS_TIMESIGNATURE*)pData;
  608. CTimeSigItem* pScan = m_TSigEventList.GetHead();
  609. CTimeSigItem* pPrevious = pScan;
  610. if (pScan)
  611. {
  612. for (; pScan; pScan = pScan->GetNext())
  613. {
  614. if (pScan->m_TimeSig.lTime > mtTime)
  615. {
  616. break;
  617. }
  618. pPrevious = pScan;
  619. }
  620. pTSigData->mtTime = pPrevious->m_TimeSig.lTime - mtTime;
  621. pTSigData->bBeatsPerMeasure = pPrevious->m_TimeSig.bBeatsPerMeasure;
  622. pTSigData->bBeat = pPrevious->m_TimeSig.bBeat;
  623. pTSigData->wGridsPerBeat = pPrevious->m_TimeSig.wGridsPerBeat;
  624. if (pmtNext)
  625. {
  626. *pmtNext = 0;
  627. }
  628. if (pScan)
  629. {
  630. if (pmtNext)
  631. {
  632. *pmtNext = pScan->m_TimeSig.lTime - mtTime;
  633. }
  634. }
  635. hr = S_OK;
  636. }
  637. else
  638. {
  639. hr = DMUS_E_NOT_FOUND;
  640. }
  641. }
  642. }
  643. LeaveCriticalSection(&m_CrSec);
  644. return hr;
  645. }
  646. HRESULT CTimeSigTrack::SetParam(
  647. REFGUID rguid,
  648. MUSIC_TIME mtTime,
  649. void *pData)
  650. {
  651. V_INAME(CTimeSigTrack::SetParam);
  652. V_REFGUID(rguid);
  653. HRESULT hr = DMUS_E_SET_UNSUPPORTED;
  654. if( rguid == GUID_EnableTimeSig )
  655. {
  656. if (m_fStateSetBySetParam && m_fActive)
  657. { // Already been enabled.
  658. hr = DMUS_E_TYPE_DISABLED;
  659. }
  660. else
  661. {
  662. m_fStateSetBySetParam = TRUE;
  663. m_fActive = TRUE;
  664. hr = S_OK;
  665. }
  666. }
  667. else if( rguid == GUID_DisableTimeSig )
  668. {
  669. if (m_fStateSetBySetParam && !m_fActive)
  670. { // Already been disabled.
  671. hr = DMUS_E_TYPE_DISABLED;
  672. }
  673. else
  674. {
  675. m_fStateSetBySetParam = TRUE;
  676. m_fActive = FALSE;
  677. hr = S_OK;
  678. }
  679. }
  680. return hr;
  681. }
  682. HRESULT STDMETHODCALLTYPE CTimeSigTrack::AddNotificationType(
  683. /* [in] */ REFGUID rguidNotification)
  684. {
  685. V_INAME(IDirectMusicTrack::AddNotificationType);
  686. V_REFGUID(rguidNotification);
  687. HRESULT hr = S_FALSE;
  688. if( rguidNotification == GUID_NOTIFICATION_MEASUREANDBEAT )
  689. {
  690. m_fNotificationMeasureBeat = TRUE;
  691. hr = S_OK;
  692. }
  693. return hr;
  694. }
  695. HRESULT STDMETHODCALLTYPE CTimeSigTrack::RemoveNotificationType(
  696. /* [in] */ REFGUID rguidNotification)
  697. {
  698. V_INAME(IDirectMusicTrack::RemoveNotificationType);
  699. V_REFGUID(rguidNotification);
  700. HRESULT hr = S_FALSE;
  701. if( rguidNotification == GUID_NOTIFICATION_MEASUREANDBEAT )
  702. {
  703. m_fNotificationMeasureBeat = FALSE;
  704. hr = S_OK;
  705. }
  706. return hr;
  707. }
  708. // send measure and beat notifications
  709. MUSIC_TIME CTimeSigTrack::NotificationMeasureBeat( MUSIC_TIME mtStart, MUSIC_TIME mtEnd,
  710. CTimeSigStateData* pSD, MUSIC_TIME mtOffset )
  711. {
  712. DMUS_NOTIFICATION_PMSG* pEvent = NULL;
  713. MUSIC_TIME mtTime;
  714. DWORD dwMeasure;
  715. BYTE bCurrentBeat;
  716. if( pSD->m_mtTimeSig >= mtEnd )
  717. return mtStart;
  718. if( pSD->m_mtTimeSig > mtStart )
  719. {
  720. mtStart = pSD->m_mtTimeSig;
  721. }
  722. // now actually generate the beat events.
  723. // Generate events that are on beat boundaries, from mtStart to mtEnd
  724. long lQuantize = ( DMUS_PPQ * 4 ) / pSD->m_bBeat;
  725. mtTime = mtStart - pSD->m_mtTimeSig;
  726. if( mtTime ) // 0 stays 0
  727. {
  728. // quantize to next boundary
  729. mtTime = ((( mtTime - 1 ) / lQuantize ) + 1 ) * lQuantize;
  730. }
  731. mtStart += mtTime - ( mtStart - pSD->m_mtTimeSig );
  732. bCurrentBeat = (BYTE)(( ( mtStart - pSD->m_mtTimeSig ) / lQuantize ) % pSD->m_bBeatsPerMeasure);
  733. dwMeasure = mtStart / (pSD->m_bBeatsPerMeasure * lQuantize );
  734. while( mtStart < mtEnd )
  735. {
  736. if( SUCCEEDED( pSD->m_pPerformance->AllocPMsg( sizeof(DMUS_NOTIFICATION_PMSG),
  737. (DMUS_PMSG**)&pEvent )))
  738. {
  739. pEvent->dwType = DMUS_PMSGT_NOTIFICATION;
  740. pEvent->mtTime = mtStart + mtOffset;
  741. pEvent->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_ATTIME;
  742. pEvent->dwPChannel = 0;
  743. pSD->m_pSegState->QueryInterface(IID_IUnknown, (void**)&pEvent->punkUser);
  744. pEvent->dwNotificationOption = DMUS_NOTIFICATION_MEASUREBEAT;
  745. pEvent->dwField1 = bCurrentBeat;
  746. pEvent->dwField2 = dwMeasure;
  747. pEvent->guidNotificationType = GUID_NOTIFICATION_MEASUREANDBEAT;
  748. pEvent->dwGroupID = 0xffffffff;
  749. IDirectMusicGraph* pGraph;
  750. if( SUCCEEDED( pSD->m_pSegState->QueryInterface( IID_IDirectMusicGraph,
  751. (void**)&pGraph )))
  752. {
  753. pGraph->StampPMsg((DMUS_PMSG*) pEvent );
  754. pGraph->Release();
  755. }
  756. if(FAILED(pSD->m_pPerformance->SendPMsg((DMUS_PMSG*) pEvent )))
  757. {
  758. pSD->m_pPerformance->FreePMsg( (DMUS_PMSG*)pEvent );
  759. }
  760. }
  761. bCurrentBeat++;
  762. if( bCurrentBeat >= pSD->m_bBeatsPerMeasure )
  763. {
  764. bCurrentBeat = 0;
  765. dwMeasure += 1;
  766. }
  767. mtStart += lQuantize;
  768. }
  769. return mtEnd;
  770. }
  771. HRESULT STDMETHODCALLTYPE CTimeSigTrack::Clone(
  772. MUSIC_TIME mtStart,
  773. MUSIC_TIME mtEnd,
  774. IDirectMusicTrack** ppTrack)
  775. {
  776. V_INAME(IDirectMusicTrack::Clone);
  777. V_PTRPTR_WRITE(ppTrack);
  778. HRESULT hr = S_OK;
  779. if((mtStart < 0 ) ||(mtStart > mtEnd))
  780. {
  781. Trace(1,"Error: Clone failed on time signature track because of invalid start or end time.\n");
  782. return E_INVALIDARG;
  783. }
  784. EnterCriticalSection(&m_CrSec);
  785. CTimeSigTrack *pDM;
  786. try
  787. {
  788. pDM = new CTimeSigTrack(this, mtStart, mtEnd);
  789. }
  790. catch( ... )
  791. {
  792. pDM = NULL;
  793. }
  794. LeaveCriticalSection(&m_CrSec);
  795. if (pDM == NULL) {
  796. return E_OUTOFMEMORY;
  797. }
  798. hr = pDM->QueryInterface(IID_IDirectMusicTrack, (void**)ppTrack);
  799. pDM->Release();
  800. return hr;
  801. }