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.

1666 lines
62 KiB

  1. //
  2. // seqtrack.cpp
  3. //
  4. // Copyright (c) 1998-2001 Microsoft Corporation
  5. //
  6. // READ THIS!!!!!!!!!!!!!!!!!!!!!!!!!!!
  7. //
  8. // 4530: C++ exception handler used, but unwind semantics are not enabled. Specify -GX
  9. //
  10. // We disable this because we use exceptions and do *not* specify -GX (USE_NATIVE_EH in
  11. // sources).
  12. //
  13. // The one place we use exceptions is around construction of objects that call
  14. // InitializeCriticalSection. We guarantee that it is safe to use in this case with
  15. // the restriction given by not using -GX (automatic objects in the call chain between
  16. // throw and handler are not destructed). Turning on -GX buys us nothing but +10% to code
  17. // size because of the unwind code.
  18. //
  19. // Any other use of exceptions must follow these restrictions or -GX must be turned on.
  20. //
  21. #pragma warning(disable:4530)
  22. // SeqTrack.cpp : Implementation of CSeqTrack
  23. #include "dmime.h"
  24. #include "dmperf.h"
  25. #include "SeqTrack.h"
  26. #include "dmusici.h"
  27. #include "dmusicf.h"
  28. #include "debug.h"
  29. #include "..\shared\Validate.h"
  30. #include "debug.h"
  31. #define ASSERT assert
  32. // @doc EXTERNAL
  33. #define MIDI_NOTEOFF 0x80
  34. #define MIDI_NOTEON 0x90
  35. #define MIDI_PTOUCH 0xA0
  36. #define MIDI_CCHANGE 0xB0
  37. #define MIDI_PCHANGE 0xC0
  38. #define MIDI_MTOUCH 0xD0
  39. #define MIDI_PBEND 0xE0
  40. #define MIDI_SYSX 0xF0
  41. #define MIDI_MTC 0xF1
  42. #define MIDI_SONGPP 0xF2
  43. #define MIDI_SONGS 0xF3
  44. #define MIDI_EOX 0xF7
  45. #define MIDI_CLOCK 0xF8
  46. #define MIDI_START 0xFA
  47. #define MIDI_CONTINUE 0xFB
  48. #define MIDI_STOP 0xFC
  49. #define MIDI_SENSE 0xFE
  50. #define MIDI_CC_BS_MSB 0x00
  51. #define MIDI_CC_BS_LSB 0x20
  52. /////////////////////////////////////////////////////////////////////////////
  53. // CSeqTrack
  54. void CSeqTrack::Construct()
  55. {
  56. InterlockedIncrement(&g_cComponent);
  57. m_pSeqPartCache = NULL;
  58. m_dwPChannelsUsed = 0;
  59. m_aPChannels = NULL;
  60. m_dwValidate = 0;
  61. m_fCSInitialized = FALSE;
  62. InitializeCriticalSection(&m_CrSec);
  63. m_fCSInitialized = TRUE;
  64. m_cRef = 1;
  65. }
  66. CSeqTrack::CSeqTrack()
  67. {
  68. Construct();
  69. }
  70. CSeqTrack::CSeqTrack(
  71. const CSeqTrack& rTrack, MUSIC_TIME mtStart, MUSIC_TIME mtEnd)
  72. {
  73. Construct();
  74. m_dwPChannelsUsed = rTrack.m_dwPChannelsUsed;
  75. if( m_dwPChannelsUsed )
  76. {
  77. m_aPChannels = new DWORD[m_dwPChannelsUsed];
  78. if (m_aPChannels)
  79. {
  80. memcpy( m_aPChannels, rTrack.m_aPChannels, sizeof(DWORD) * m_dwPChannelsUsed );
  81. }
  82. }
  83. TListItem<SEQ_PART>* pPart = rTrack.m_SeqPartList.GetHead();
  84. for( ; pPart; pPart = pPart->GetNext() )
  85. {
  86. TListItem<SEQ_PART>* pNewPart = new TListItem<SEQ_PART>;
  87. if( pNewPart )
  88. {
  89. pNewPart->GetItemValue().dwPChannel = pPart->GetItemValue().dwPChannel;
  90. TListItem<DMUS_IO_SEQ_ITEM>* pScan = pPart->GetItemValue().seqList.GetHead();
  91. for(; pScan; pScan = pScan->GetNext())
  92. {
  93. DMUS_IO_SEQ_ITEM& rScan = pScan->GetItemValue();
  94. if( rScan.mtTime < mtStart )
  95. {
  96. continue;
  97. }
  98. if (rScan.mtTime < mtEnd)
  99. {
  100. TListItem<DMUS_IO_SEQ_ITEM>* pNew = new TListItem<DMUS_IO_SEQ_ITEM>;
  101. if (pNew)
  102. {
  103. DMUS_IO_SEQ_ITEM& rNew = pNew->GetItemValue();
  104. memcpy( &rNew, &rScan, sizeof(DMUS_IO_SEQ_ITEM) );
  105. rNew.mtTime = rScan.mtTime - mtStart;
  106. pNewPart->GetItemValue().seqList.AddHead(pNew); // AddTail can get expensive (n^2), so
  107. // AddHead instead and Reverse later.
  108. }
  109. }
  110. else break;
  111. }
  112. pNewPart->GetItemValue().seqList.Reverse(); // since we AddHead'd earlier.
  113. TListItem<DMUS_IO_CURVE_ITEM>* pScanCurve = pPart->GetItemValue().curveList.GetHead();
  114. for(; pScanCurve; pScanCurve = pScanCurve->GetNext())
  115. {
  116. DMUS_IO_CURVE_ITEM& rScan = pScanCurve->GetItemValue();
  117. if( rScan.mtStart < mtStart )
  118. {
  119. continue;
  120. }
  121. if (rScan.mtStart < mtEnd)
  122. {
  123. TListItem<DMUS_IO_CURVE_ITEM>* pNew = new TListItem<DMUS_IO_CURVE_ITEM>;
  124. if (pNew)
  125. {
  126. DMUS_IO_CURVE_ITEM& rNew = pNew->GetItemValue();
  127. memcpy( &rNew, &rScan, sizeof(DMUS_IO_CURVE_ITEM) );
  128. rNew.mtStart = rScan.mtStart - mtStart;
  129. pNewPart->GetItemValue().curveList.AddHead(pNew); // AddTail can get expensive (n^2), so
  130. // AddHead instead and Reverse later.
  131. }
  132. }
  133. else break;
  134. }
  135. pNewPart->GetItemValue().curveList.Reverse(); // since we AddHead'd earlier.
  136. m_SeqPartList.AddHead(pNewPart);
  137. }
  138. m_SeqPartList.Reverse();
  139. }
  140. }
  141. CSeqTrack::~CSeqTrack()
  142. {
  143. if (m_fCSInitialized)
  144. {
  145. DeleteSeqPartList(); // This will be empty if critical section
  146. // never got initialized.
  147. DeleteCriticalSection(&m_CrSec);
  148. }
  149. InterlockedDecrement(&g_cComponent);
  150. }
  151. // @method:(INTERNAL) HRESULT | IDirectMusicTrack | QueryInterface | Standard QueryInterface implementation for <i IDirectMusicSeqTrack>
  152. //
  153. // @rdesc Returns one of the following:
  154. //
  155. // @flag S_OK | If the interface is supported and was returned
  156. // @flag E_NOINTERFACE | If the object does not support the given interface.
  157. // @flag E_POINTER | <p ppv> is NULL or invalid.
  158. //
  159. STDMETHODIMP CSeqTrack::QueryInterface(
  160. const IID &iid, // @parm Interface to query for
  161. void **ppv) // @parm The requested interface will be returned here
  162. {
  163. V_INAME(CSeqTrack::QueryInterface);
  164. V_PTRPTR_WRITE(ppv);
  165. V_REFGUID(iid);
  166. if (iid == IID_IUnknown || iid == IID_IDirectMusicTrack || iid == IID_IDirectMusicTrack8)
  167. {
  168. *ppv = static_cast<IDirectMusicTrack*>(this);
  169. } else
  170. if (iid == IID_IPersistStream)
  171. {
  172. *ppv = static_cast<IPersistStream*>(this);
  173. } else
  174. {
  175. *ppv = NULL;
  176. Trace(4,"Warning: Request to query unknown interface on Sequence Track\n");
  177. return E_NOINTERFACE;
  178. }
  179. reinterpret_cast<IUnknown*>(this)->AddRef();
  180. return S_OK;
  181. }
  182. // @method:(INTERNAL) HRESULT | IDirectMusicTrack | AddRef | Standard AddRef implementation for <i IDirectMusicSeqTrack>
  183. //
  184. // @rdesc Returns the new reference count for this object.
  185. //
  186. STDMETHODIMP_(ULONG) CSeqTrack::AddRef()
  187. {
  188. return InterlockedIncrement(&m_cRef);
  189. }
  190. // @method:(INTERNAL) HRESULT | IDirectMusicTrack | Release | Standard Release implementation for <i IDirectMusicSeqTrack>
  191. //
  192. // @rdesc Returns the new reference count for this object.
  193. //
  194. STDMETHODIMP_(ULONG) CSeqTrack::Release()
  195. {
  196. if (!InterlockedDecrement(&m_cRef))
  197. {
  198. delete this;
  199. return 0;
  200. }
  201. return m_cRef;
  202. }
  203. /////////////////////////////////////////////////////////////////////////////
  204. // IPersist
  205. HRESULT CSeqTrack::GetClassID( CLSID* pClassID )
  206. {
  207. V_INAME(CSeqTrack::GetClassID);
  208. V_PTR_WRITE(pClassID, CLSID);
  209. *pClassID = CLSID_DirectMusicSeqTrack;
  210. return S_OK;
  211. }
  212. /////////////////////////////////////////////////////////////////////////////
  213. // IPersistStream functions
  214. HRESULT CSeqTrack::IsDirty()
  215. {
  216. return S_FALSE;
  217. }
  218. /*
  219. method HRESULT | ISeqTrack | LoadSeq |
  220. Call this with an IStream filled with SeqEvents, sorted in time order.
  221. parm IStream* | pIStream |
  222. A stream of SeqEvents, sorted in time order. The seek pointer should point
  223. to the first event. The stream should contain only SeqEvents and nothing more.
  224. rvalue E_POINTER | If pIStream == NULL or invalid.
  225. rvalue S_OK | Success.
  226. comm The <p pIStream> will be AddRef'd inside this function and held
  227. until the SeqTrack is released.
  228. */
  229. HRESULT CSeqTrack::LoadSeq( IStream* pIStream, long lSize )
  230. {
  231. HRESULT hr = S_OK;
  232. TListItem<SEQ_PART>* pPart;
  233. EnterCriticalSection(&m_CrSec);
  234. // copy contents of the stream into the list.
  235. LARGE_INTEGER li;
  236. DMUS_IO_SEQ_ITEM seqEvent;
  237. DWORD dwSubSize;
  238. // read in the size of the data structures
  239. if( FAILED( pIStream->Read( &dwSubSize, sizeof(DWORD), NULL )))
  240. {
  241. Trace(1,"Error: Failure reading sequence track.\n");
  242. hr = DMUS_E_CANNOTREAD;
  243. goto END;
  244. }
  245. lSize -= sizeof(DWORD);
  246. DWORD dwRead, dwSeek;
  247. if( dwSubSize > sizeof(DMUS_IO_SEQ_ITEM) )
  248. {
  249. dwRead = sizeof(DMUS_IO_SEQ_ITEM);
  250. dwSeek = dwSubSize - dwRead;
  251. li.HighPart = 0;
  252. li.LowPart = dwSeek;
  253. }
  254. else
  255. {
  256. if( dwSubSize == 0 )
  257. {
  258. Trace(1,"Error: Failure reading sequence track.\n");
  259. hr = DMUS_E_CHUNKNOTFOUND;
  260. goto END;
  261. }
  262. dwRead = dwSubSize;
  263. dwSeek = 0;
  264. }
  265. if( 0 == dwRead )
  266. {
  267. hr = DMUS_E_CANNOTREAD;
  268. goto END;
  269. }
  270. while( lSize > 0 )
  271. {
  272. if( FAILED( pIStream->Read( &seqEvent, dwRead, NULL )))
  273. {
  274. Trace(1,"Error: Failure reading sequence track.\n");
  275. hr = DMUS_E_CANNOTREAD;
  276. goto END;
  277. }
  278. lSize -= dwRead;
  279. if( dwSeek )
  280. {
  281. if( FAILED( pIStream->Seek( li, STREAM_SEEK_CUR, NULL )))
  282. {
  283. hr = DMUS_E_CANNOTSEEK;
  284. goto END;
  285. }
  286. lSize -= dwSeek;
  287. }
  288. pPart = FindPart(seqEvent.dwPChannel);
  289. if( pPart )
  290. {
  291. TListItem<DMUS_IO_SEQ_ITEM>* pEvent = new TListItem<DMUS_IO_SEQ_ITEM>(seqEvent);
  292. if( pEvent )
  293. {
  294. pPart->GetItemValue().seqList.AddHead(pEvent); // AddTail can get
  295. // expensive (n pow 2) so
  296. // AddHead instead and reverse later.
  297. }
  298. }
  299. }
  300. END:
  301. for( pPart = m_SeqPartList.GetHead(); pPart; pPart = pPart->GetNext() )
  302. {
  303. pPart->GetItemValue().seqList.Reverse(); // since we AddHead'd earlier
  304. }
  305. m_dwValidate++; // used to validate state data that's out there
  306. LeaveCriticalSection(&m_CrSec);
  307. return hr;
  308. }
  309. /*
  310. method HRESULT | LoadCurve
  311. Call this with an IStream filled with CurveEvents, sorted in time order.
  312. parm IStream* | pIStream |
  313. A stream of CurveEvents, sorted in time order. The seek pointer should point
  314. to the first event. The stream should contain only CurveEvents and nothing more.
  315. rvalue E_POINTER | If pIStream == NULL or invalid.
  316. rvalue S_OK | Success.
  317. There are also other error codes.
  318. comm The <p pIStream> will be AddRef'd inside this function and held
  319. until the CurveTrack is released.
  320. */
  321. HRESULT CSeqTrack::LoadCurve( IStream* pIStream, long lSize )
  322. {
  323. HRESULT hr = S_OK;
  324. TListItem<SEQ_PART>* pPart;
  325. EnterCriticalSection(&m_CrSec);
  326. DWORD dwSubSize;
  327. // copy contents of the stream into the list.
  328. LARGE_INTEGER li;
  329. DMUS_IO_CURVE_ITEM curveEvent;
  330. // read in the size of the data structures
  331. if( FAILED( pIStream->Read( &dwSubSize, sizeof(DWORD), NULL )))
  332. {
  333. Trace(1,"Error: Failure reading sequence track.\n");
  334. hr = DMUS_E_CANNOTREAD;
  335. goto END;
  336. }
  337. lSize -= sizeof(DWORD);
  338. DWORD dwRead, dwSeek;
  339. if( dwSubSize > sizeof(DMUS_IO_CURVE_ITEM) )
  340. {
  341. dwRead = sizeof(DMUS_IO_CURVE_ITEM);
  342. dwSeek = dwSubSize - dwRead;
  343. li.HighPart = 0;
  344. li.LowPart = dwSeek;
  345. }
  346. else
  347. {
  348. if( dwSubSize == 0 )
  349. {
  350. Trace(1,"Error: Failure reading sequence track - bad data.\n");
  351. hr = DMUS_E_CHUNKNOTFOUND;
  352. goto END;
  353. }
  354. dwRead = dwSubSize;
  355. dwSeek = 0;
  356. }
  357. if( 0 == dwRead )
  358. {
  359. Trace(1,"Error: Failure reading sequence track - bad data.\n");
  360. hr = DMUS_E_CANNOTREAD;
  361. goto END;
  362. }
  363. while( lSize > 0 )
  364. {
  365. curveEvent.wMergeIndex = 0; // Older format doesn't support this.
  366. if( FAILED( pIStream->Read( &curveEvent, dwRead, NULL )))
  367. {
  368. hr = DMUS_E_CANNOTREAD;
  369. break;
  370. }
  371. lSize -= dwRead;
  372. if( dwSeek )
  373. {
  374. pIStream->Seek( li, STREAM_SEEK_CUR, NULL );
  375. lSize -= dwSeek;
  376. }
  377. pPart = FindPart(curveEvent.dwPChannel);
  378. if( pPart )
  379. {
  380. TListItem<DMUS_IO_CURVE_ITEM>* pEvent = new TListItem<DMUS_IO_CURVE_ITEM>(curveEvent);
  381. if( pEvent )
  382. {
  383. pPart->GetItemValue().curveList.AddHead(pEvent); // AddTail can get
  384. // expensive (n pow 2) so
  385. // AddHead instead and reverse later.
  386. }
  387. }
  388. }
  389. END:
  390. for( pPart = m_SeqPartList.GetHead(); pPart; pPart = pPart->GetNext() )
  391. {
  392. pPart->GetItemValue().curveList.Reverse(); // since we AddHead'd earlier
  393. }
  394. m_dwValidate++; // used to validate state data that's out there
  395. LeaveCriticalSection(&m_CrSec);
  396. return hr;
  397. }
  398. HRESULT CSeqTrack::Load( IStream* pIStream )
  399. {
  400. V_INAME(CSeqTrack::Load);
  401. V_INTERFACE(pIStream);
  402. HRESULT hr = S_OK;
  403. EnterCriticalSection(&m_CrSec);
  404. m_dwValidate++; // used to validate state data that's out there
  405. DeleteSeqPartList();
  406. LeaveCriticalSection(&m_CrSec);
  407. // read in the chunk id
  408. long lSize;
  409. DWORD dwChunk;
  410. if( FAILED( pIStream->Read( &dwChunk, sizeof(DWORD), NULL )))
  411. {
  412. Trace(1,"Error: Failure reading sequence track.\n");
  413. hr = DMUS_E_CANNOTREAD;
  414. goto END;
  415. }
  416. if( dwChunk != DMUS_FOURCC_SEQ_TRACK )
  417. {
  418. Trace(1,"Error: Failure reading sequence track - bad data.\n");
  419. hr = DMUS_E_CHUNKNOTFOUND;
  420. goto END;
  421. }
  422. // read in the overall size
  423. if( FAILED( pIStream->Read( &lSize, sizeof(long), NULL )))
  424. {
  425. hr = DMUS_E_CANNOTREAD;
  426. goto END;
  427. }
  428. while( lSize )
  429. {
  430. DWORD dwSubChunk, dwSubSize;
  431. if( FAILED( pIStream->Read( &dwSubChunk, sizeof(DWORD), NULL )))
  432. {
  433. Trace(1,"Error: Failure reading sequence track.\n");
  434. hr = DMUS_E_CANNOTREAD;
  435. goto END;
  436. }
  437. lSize -= sizeof(DWORD);
  438. // read in the overall size
  439. if( FAILED( pIStream->Read( &dwSubSize, sizeof(DWORD), NULL )))
  440. {
  441. Trace(1,"Error: Failure reading sequence track.\n");
  442. hr = DMUS_E_CANNOTREAD;
  443. goto END;
  444. }
  445. if( (dwSubSize == 0) || (dwSubSize > (DWORD)lSize) )
  446. {
  447. Trace(1,"Error: Failure reading sequence track - bad data.\n");
  448. hr = DMUS_E_CHUNKNOTFOUND;
  449. goto END;
  450. }
  451. lSize -= sizeof(DWORD);
  452. switch( dwSubChunk )
  453. {
  454. case DMUS_FOURCC_SEQ_LIST:
  455. if( FAILED( hr = LoadSeq( pIStream, dwSubSize )))
  456. {
  457. goto END;
  458. }
  459. break;
  460. case DMUS_FOURCC_CURVE_LIST:
  461. if( FAILED( hr = LoadCurve( pIStream, dwSubSize )))
  462. {
  463. goto END;
  464. }
  465. break;
  466. default:
  467. LARGE_INTEGER li;
  468. li.HighPart = 0;
  469. li.LowPart = dwSubSize;
  470. if( FAILED( pIStream->Seek( li, STREAM_SEEK_CUR, NULL )))
  471. {
  472. hr = DMUS_E_CANNOTREAD;
  473. goto END;
  474. }
  475. break;
  476. }
  477. lSize -= dwSubSize;
  478. }
  479. END:
  480. return hr;
  481. }
  482. HRESULT CSeqTrack::Save( IStream* pIStream, BOOL fClearDirty )
  483. {
  484. return E_NOTIMPL;
  485. }
  486. HRESULT CSeqTrack::GetSizeMax( ULARGE_INTEGER FAR* pcbSize )
  487. {
  488. return E_NOTIMPL;
  489. }
  490. // IDirectMusicTrack
  491. /*
  492. @method HRESULT | IDirectMusicTrack | IsParamSupported |
  493. Check to see if the Track supports data types in <om .GetParam> and <om .SetParam>.
  494. @rvalue S_OK | It does support the type of data.
  495. @rvalue S_FALSE | It does not support the type of data.
  496. @rvalue E_NOTIMPL | (Or any other failure code) It does not support the type of data.
  497. @comm Note that it is valid for a Track to return different results for the same
  498. guid depending on its current state.
  499. */
  500. HRESULT STDMETHODCALLTYPE CSeqTrack::IsParamSupported(
  501. REFGUID rguidType) // @parm The guid identifying the type of data to check.
  502. {
  503. return E_NOTIMPL;
  504. }
  505. //////////////////////////////////////////////////////////////////////
  506. // IDirectMusicTrack::Init
  507. /*
  508. @method HRESULT | IDirectMusicTrack | Init |
  509. When a track is first added to a <i IDirectMusicSegment>, this method is called
  510. by that Segment.
  511. @rvalue S_OK | Success.
  512. @rvalue E_POINTER | <p pSegment> is NULL or invalid.
  513. @comm If the Track plays messages, it should call <om IDirectMusicSegment.SetPChannelsUsed>.
  514. */
  515. HRESULT CSeqTrack::Init(
  516. IDirectMusicSegment *pSegment) // @parm Pointer to the Segment to which this Track belongs.
  517. {
  518. if( m_dwPChannelsUsed && m_aPChannels )
  519. {
  520. pSegment->SetPChannelsUsed( m_dwPChannelsUsed, m_aPChannels );
  521. }
  522. return S_OK;
  523. }
  524. /*
  525. @method HRESULT | IDirectMusicTrack | InitPlay |
  526. This method is called when a Segment is ready to start playing. The <p ppStateData> field
  527. may return a pointer to a structure of state data, which is sent into <om .Play> and
  528. <om .EndPlay>, and allows the Track to keep track of variables on a <i SegmentState> by
  529. <i SegmentState> basis.
  530. @rvalue S_OK | Success. This is the only valid return value from this method.
  531. @rvalue E_POINTER | <p pSegmentState>, <p pPerf>, or <p ppStateData> is NULL or
  532. invalid.
  533. @comm Note that it is unneccessary for the Track to store the <p pSegmentState>, <p pPerf>,
  534. or <p dwTrackID> parameters, since they are also sent into <om .Play>.
  535. */
  536. HRESULT CSeqTrack::InitPlay(
  537. IDirectMusicSegmentState *pSegmentState, // @parm The calling <i IDirectMusicSegmentState> pointer.
  538. IDirectMusicPerformance *pPerf, // @parm The calling <i IDirectMusicPerformance> pointer.
  539. void **ppStateData, // @parm This method can return state data information here.
  540. DWORD dwTrackID, // @parm The virtual track ID assigned to this Track instance.
  541. DWORD dwFlags) // @parm Same flags that were set with the call
  542. // to PlaySegment. These are passed all the way down to the tracks, who may want to know
  543. // if the track was played as a primary, controlling, or secondary segment.
  544. {
  545. V_INAME(IDirectMusicTrack::InitPlay);
  546. V_PTRPTR_WRITE(ppStateData);
  547. V_INTERFACE(pSegmentState);
  548. V_INTERFACE(pPerf);
  549. SeqStateData* pStateData;
  550. pStateData = new SeqStateData;
  551. if( NULL == pStateData )
  552. return E_OUTOFMEMORY;
  553. *ppStateData = pStateData;
  554. SetUpStateCurrentPointers(pStateData);
  555. // need to know the group this track is in, for the mute track GetParam
  556. IDirectMusicSegment* pSegment;
  557. if( SUCCEEDED( pSegmentState->GetSegment(&pSegment)))
  558. {
  559. pSegment->GetTrackGroup( this, &pStateData->dwGroupBits );
  560. pSegment->Release();
  561. }
  562. return S_OK;
  563. }
  564. /*
  565. @method HRESULT | IDirectMusicTrack | EndPlay |
  566. This method is called when the <i IDirectMusicSegmentState> object that originally called
  567. <om .InitPlay> is destroyed.
  568. @rvalue S_OK | Success.
  569. @rvalue E_POINTER | <p pStateData> is invalid.
  570. @comm The return code isn't used, but S_OK is preferred.
  571. */
  572. HRESULT CSeqTrack::EndPlay(
  573. void *pStateData) // @parm The state data returned from <om .InitPlay>.
  574. {
  575. ASSERT( pStateData );
  576. if( pStateData )
  577. {
  578. V_INAME(IDirectMusicTrack::EndPlay);
  579. V_BUFPTR_WRITE(pStateData, sizeof(SeqStateData));
  580. SeqStateData* pSD = (SeqStateData*)pStateData;
  581. delete pSD;
  582. }
  583. return S_OK;
  584. }
  585. void CSeqTrack::SetUpStateCurrentPointers(SeqStateData* pStateData)
  586. {
  587. ASSERT(pStateData);
  588. pStateData->dwPChannelsUsed = m_dwPChannelsUsed;
  589. if( m_dwPChannelsUsed )
  590. {
  591. if( pStateData->apCurrentSeq )
  592. {
  593. delete [] pStateData->apCurrentSeq;
  594. pStateData->apCurrentSeq = NULL;
  595. }
  596. if( pStateData->apCurrentCurve )
  597. {
  598. delete [] pStateData->apCurrentCurve;
  599. pStateData->apCurrentCurve = NULL;
  600. }
  601. pStateData->apCurrentSeq = new TListItem<DMUS_IO_SEQ_ITEM>* [m_dwPChannelsUsed];
  602. pStateData->apCurrentCurve = new TListItem<DMUS_IO_CURVE_ITEM>* [m_dwPChannelsUsed];
  603. if( pStateData->apCurrentSeq )
  604. {
  605. memset( pStateData->apCurrentSeq, 0, sizeof(TListItem<DMUS_IO_SEQ_ITEM>*) * m_dwPChannelsUsed );
  606. }
  607. if( pStateData->apCurrentCurve )
  608. {
  609. memset( pStateData->apCurrentCurve, 0, sizeof(TListItem<DMUS_IO_CURVE_ITEM>*) * m_dwPChannelsUsed );
  610. }
  611. }
  612. pStateData->dwValidate = m_dwValidate;
  613. }
  614. // DeleteSeqPartList() - delete all parts in m_SeqPartList, and associated events.
  615. void CSeqTrack::DeleteSeqPartList(void)
  616. {
  617. EnterCriticalSection(&m_CrSec);
  618. m_dwPChannelsUsed = 0;
  619. if (m_aPChannels) delete [] m_aPChannels;
  620. m_aPChannels = NULL;
  621. m_pSeqPartCache = NULL;
  622. if( m_SeqPartList.GetHead() )
  623. {
  624. TListItem<SEQ_PART>* pItem;
  625. while( pItem = m_SeqPartList.RemoveHead() )
  626. {
  627. TListItem<DMUS_IO_SEQ_ITEM>* pEvent;
  628. while( pEvent = pItem->GetItemValue().seqList.RemoveHead() )
  629. {
  630. delete pEvent;
  631. }
  632. TListItem<DMUS_IO_CURVE_ITEM>* pCurve;
  633. while( pCurve = pItem->GetItemValue().curveList.RemoveHead() )
  634. {
  635. delete pCurve;
  636. }
  637. delete pItem;
  638. }
  639. }
  640. LeaveCriticalSection(&m_CrSec);
  641. }
  642. // FindPart() - return the SEQ_PART corresponding to dwPChannel, or create one.
  643. TListItem<SEQ_PART>* CSeqTrack::FindPart( DWORD dwPChannel )
  644. {
  645. TListItem<SEQ_PART>* pPart;
  646. if( m_pSeqPartCache && (m_pSeqPartCache->GetItemValue().dwPChannel == dwPChannel) )
  647. {
  648. return m_pSeqPartCache;
  649. }
  650. for( pPart = m_SeqPartList.GetHead(); pPart; pPart = pPart->GetNext() )
  651. {
  652. if( pPart->GetItemValue().dwPChannel == dwPChannel )
  653. {
  654. break;
  655. }
  656. }
  657. if( NULL == pPart )
  658. {
  659. pPart = new TListItem<SEQ_PART>;
  660. if( pPart )
  661. {
  662. pPart->GetItemValue().dwPChannel = dwPChannel;
  663. m_SeqPartList.AddHead( pPart );
  664. }
  665. m_dwPChannelsUsed++;
  666. DWORD* aPChannels = new DWORD[m_dwPChannelsUsed];
  667. if( aPChannels )
  668. {
  669. if( m_aPChannels )
  670. {
  671. memcpy( aPChannels, m_aPChannels, sizeof(DWORD) * (m_dwPChannelsUsed - 1) );
  672. }
  673. aPChannels[m_dwPChannelsUsed - 1] = dwPChannel;
  674. }
  675. if( m_aPChannels )
  676. {
  677. delete [] m_aPChannels;
  678. }
  679. m_aPChannels = aPChannels;
  680. }
  681. m_pSeqPartCache = pPart;
  682. return pPart;
  683. }
  684. void CSeqTrack::UpdateTimeSig(IDirectMusicSegmentState* pSegSt,
  685. SeqStateData* pSD,
  686. MUSIC_TIME mt)
  687. {
  688. // get a new time sig if needed
  689. if( (mt >= pSD->mtNextTimeSig) || (mt < pSD->mtCurTimeSig) )
  690. {
  691. IDirectMusicSegment* pSeg;
  692. DMUS_TIMESIGNATURE timesig;
  693. MUSIC_TIME mtNext;
  694. HRESULT hr;
  695. if(SUCCEEDED(hr = pSegSt->GetSegment(&pSeg)))
  696. {
  697. DWORD dwGroup;
  698. if( SUCCEEDED(hr = pSeg->GetTrackGroup( this, &dwGroup )))
  699. {
  700. if(SUCCEEDED(hr = pSeg->GetParam( GUID_TimeSignature, dwGroup,
  701. 0, mt, &mtNext, (void*)&timesig )))
  702. {
  703. timesig.mtTime += mt;
  704. if( pSD->dwlnMeasure )
  705. {
  706. pSD->dwMeasure = (timesig.mtTime - pSD->mtCurTimeSig) / pSD->dwlnMeasure;
  707. }
  708. else
  709. {
  710. pSD->dwMeasure = 0;
  711. }
  712. pSD->mtCurTimeSig = timesig.mtTime;
  713. if( mtNext == 0 ) mtNext = 0x7fffffff;
  714. pSD->mtNextTimeSig = mtNext;
  715. if( timesig.bBeat )
  716. {
  717. pSD->dwlnBeat = DMUS_PPQ * 4 / timesig.bBeat;
  718. }
  719. pSD->dwlnMeasure = pSD->dwlnBeat * timesig.bBeatsPerMeasure;
  720. if( timesig.wGridsPerBeat )
  721. {
  722. pSD->dwlnGrid = pSD->dwlnBeat / timesig.wGridsPerBeat;
  723. }
  724. }
  725. }
  726. pSeg->Release();
  727. }
  728. if( FAILED(hr) )
  729. {
  730. // couldn't get time sig, default to 4/4
  731. pSD->mtNextTimeSig = 0x7fffffff;
  732. pSD->dwlnBeat = DMUS_PPQ;
  733. pSD->dwlnMeasure = DMUS_PPQ * 4;
  734. pSD->dwlnGrid = DMUS_PPQ / 4;
  735. pSD->dwMeasure = 0;
  736. pSD->mtCurTimeSig = 0;
  737. }
  738. }
  739. // make absolutely sure there is no way these can be 0, since we divide
  740. // by them.
  741. if( 0 == pSD->dwlnGrid ) pSD->dwlnGrid = DMUS_PPQ / 4;
  742. if( 0 == pSD->dwlnBeat ) pSD->dwlnBeat = DMUS_PPQ;
  743. if( 0 == pSD->dwlnMeasure ) pSD->dwlnMeasure = DMUS_PPQ * 4;
  744. }
  745. STDMETHODIMP CSeqTrack::PlayEx(void* pStateData,REFERENCE_TIME rtStart,
  746. REFERENCE_TIME rtEnd,REFERENCE_TIME rtOffset,
  747. DWORD dwFlags,IDirectMusicPerformance* pPerf,
  748. IDirectMusicSegmentState* pSegSt,DWORD dwVirtualID)
  749. {
  750. V_INAME(IDirectMusicTrack::PlayEx);
  751. V_BUFPTR_WRITE( pStateData, sizeof(SeqStateData));
  752. V_INTERFACE(pPerf);
  753. V_INTERFACE(pSegSt);
  754. HRESULT hr;
  755. EnterCriticalSection(&m_CrSec);
  756. if (dwFlags & DMUS_TRACKF_CLOCK)
  757. {
  758. // Convert all reference times to millisecond times. Then, just use same MUSIC_TIME
  759. // variables.
  760. hr = Play(pStateData,(MUSIC_TIME)(rtStart / REF_PER_MIL),(MUSIC_TIME)(rtEnd / REF_PER_MIL),
  761. (MUSIC_TIME)(rtOffset / REF_PER_MIL),rtOffset,dwFlags,pPerf,pSegSt,dwVirtualID,TRUE);
  762. }
  763. else
  764. {
  765. hr = Play(pStateData,(MUSIC_TIME)rtStart,(MUSIC_TIME)rtEnd,
  766. (MUSIC_TIME)rtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE);
  767. }
  768. LeaveCriticalSection(&m_CrSec);
  769. return hr;
  770. }
  771. /*
  772. @enum DMUS_TRACKF_FLAGS | Sent in <om IDirectMusicTrack.Play>'s dwFlags parameter.
  773. @emem DMUS_TRACKF_SEEK | Play was called on account of seeking, meaning that mtStart is
  774. not necessarily the same as the previous Play call's mtEnd.
  775. @emem DMUS_TRACKF_LOOP | Play was called on account of a loop, e.g. repeat.
  776. @emem DMUS_TRACKF_START | This is the first call to Play. DMUS_TRACKF_SEEK may also be set if the
  777. Track is not playing from the beginning.
  778. @emem DMUS_TRACKF_FLUSH | The call to Play is on account of a flush or invalidate, that
  779. requires the Track to replay something it played previously. In this case, DMUS_TRACKF_SEEK
  780. will be set as well.
  781. @method HRESULT | IDirectMusicTrack | Play |
  782. Play method.
  783. @rvalue DMUS_DMUS_S_END | The Track is done playing.
  784. @rvalue S_OK | Success.
  785. @rvalue E_POINTER | <p pStateData>, <p pPerf>, or <p pSegSt> is NULL or invalid.
  786. */
  787. STDMETHODIMP CSeqTrack::Play(
  788. void *pStateData, // @parm State data pointer, from <om .InitPlay>.
  789. MUSIC_TIME mtStart, // @parm The start time to play.
  790. MUSIC_TIME mtEnd, // @parm The end time to play.
  791. MUSIC_TIME mtOffset,// @parm The offset to add to all messages sent to
  792. // <om IDirectMusicPerformance.SendPMsg>.
  793. DWORD dwFlags, // @parm Flags that indicate the state of this call.
  794. // See <t DMUS_TRACKF_FLAGS>. If dwFlags == 0, this is a
  795. // normal Play call continuing playback from the previous
  796. // Play call.
  797. IDirectMusicPerformance* pPerf, // @parm The <i IDirectMusicPerformance>, used to
  798. // call <om IDirectMusicPerformance.AllocPMsg>,
  799. // <om IDirectMusicPerformance.SendPMsg>, etc.
  800. IDirectMusicSegmentState* pSegSt, // @parm The <i IDirectMusicSegmentState> this
  801. // track belongs to. QueryInterface() can be called on this to
  802. // obtain the SegmentState's <i IDirectMusicGraph> in order to
  803. // call <om IDirectMusicGraph.StampPMsg>, for instance.
  804. DWORD dwVirtualID // @parm This track's virtual track id, which must be set
  805. // on any <t DMUS_PMSG>'s m_dwVirtualTrackID member that
  806. // will be queued to <om IDirectMusicPerformance.SendPMsg>.
  807. )
  808. {
  809. V_INAME(IDirectMusicTrack::Play);
  810. V_BUFPTR_WRITE( pStateData, sizeof(SeqStateData));
  811. V_INTERFACE(pPerf);
  812. V_INTERFACE(pSegSt);
  813. EnterCriticalSection(&m_CrSec);
  814. HRESULT hr = Play(pStateData,mtStart,mtEnd,mtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE);
  815. LeaveCriticalSection(&m_CrSec);
  816. return hr;
  817. }
  818. /* The Play method handles both music time and clock time versions, as determined by
  819. fClockTime. If running in clock time, rtOffset is used to identify the start time
  820. of the segment. Otherwise, mtOffset. The mtStart and mtEnd parameters are in MUSIC_TIME units
  821. or milliseconds, depending on which mode.
  822. */
  823. HRESULT CSeqTrack::Play(
  824. void *pStateData,
  825. MUSIC_TIME mtStart,
  826. MUSIC_TIME mtEnd,
  827. MUSIC_TIME mtOffset,
  828. REFERENCE_TIME rtOffset,
  829. DWORD dwFlags,
  830. IDirectMusicPerformance* pPerf,
  831. IDirectMusicSegmentState* pSegSt,
  832. DWORD dwVirtualID,
  833. BOOL fClockTime)
  834. {
  835. if (dwFlags & DMUS_TRACKF_PLAY_OFF)
  836. {
  837. return S_OK;
  838. }
  839. HRESULT hr = S_OK;
  840. IDirectMusicGraph* pGraph = NULL;
  841. DMUS_PMSG* pEvent = NULL;
  842. SeqStateData* pSD = (SeqStateData*)pStateData;
  843. BOOL fSeek = (dwFlags & DMUS_TRACKF_SEEK) ? TRUE : FALSE;
  844. if( dwFlags & (DMUS_TRACKF_SEEK | DMUS_TRACKF_FLUSH | DMUS_TRACKF_DIRTY |
  845. DMUS_TRACKF_LOOP) )
  846. {
  847. // need to reset the PChannel Map in case of any of these flags.
  848. m_PChMap.Reset();
  849. }
  850. if( pSD->dwValidate != m_dwValidate )
  851. {
  852. SetUpStateCurrentPointers(pSD);
  853. fSeek = TRUE;
  854. }
  855. if( fSeek )
  856. {
  857. if( dwFlags & DMUS_TRACKF_START )
  858. {
  859. Seek( pSegSt, pPerf, dwVirtualID, pSD, mtStart, TRUE, mtOffset, rtOffset, fClockTime );
  860. }
  861. else
  862. {
  863. Seek( pSegSt, pPerf, dwVirtualID, pSD, mtStart, FALSE, mtOffset, rtOffset, fClockTime );
  864. }
  865. }
  866. if( FAILED( pSegSt->QueryInterface( IID_IDirectMusicGraph,
  867. (void**)&pGraph )))
  868. {
  869. pGraph = NULL;
  870. }
  871. DWORD dwIndex;
  872. DWORD dwPChannel;
  873. DWORD dwMutePChannel;
  874. BOOL fMute;
  875. TListItem<SEQ_PART>* pPart = m_SeqPartList.GetHead();
  876. for( dwIndex = 0; pPart && (dwIndex < m_dwPChannelsUsed); dwIndex++,pPart = pPart->GetNext() )
  877. {
  878. dwPChannel = pPart->GetItemValue().dwPChannel;
  879. if( pSD->apCurrentCurve )
  880. {
  881. for( ; pSD->apCurrentCurve[dwIndex];
  882. pSD->apCurrentCurve[dwIndex] = pSD->apCurrentCurve[dwIndex]->GetNext() )
  883. {
  884. DMUS_IO_CURVE_ITEM& rItem = pSD->apCurrentCurve[dwIndex]->GetItemValue();
  885. if( rItem.mtStart >= mtEnd )
  886. {
  887. break;
  888. }
  889. m_PChMap.GetInfo( dwPChannel, rItem.mtStart, mtOffset, pSD->dwGroupBits,
  890. pPerf, &fMute, &dwMutePChannel, fClockTime );
  891. if( !fMute )
  892. {
  893. DMUS_CURVE_PMSG* pCurve;
  894. if( SUCCEEDED( pPerf->AllocPMsg( sizeof(DMUS_CURVE_PMSG),
  895. (DMUS_PMSG**)&pCurve )))
  896. {
  897. pEvent = (DMUS_PMSG*)pCurve;
  898. if (fClockTime)
  899. {
  900. pCurve->wMeasure = 0;
  901. pCurve->bBeat = 0;
  902. pCurve->bGrid = 0;
  903. pCurve->nOffset = rItem.nOffset;
  904. pCurve->rtTime = ((rItem.mtStart + rItem.nOffset) * REF_PER_MIL) + rtOffset;
  905. // Set the DX8 flag to indicate the wMergeIndex and wParamType fields are valid.
  906. pCurve->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME | DMUS_PMSGF_DX8;
  907. }
  908. else
  909. {
  910. UpdateTimeSig( pSegSt, pSD, rItem.mtStart);
  911. long lTemp = (rItem.mtStart - pSD->mtCurTimeSig);
  912. pCurve->wMeasure = (WORD)((lTemp / pSD->dwlnMeasure) + pSD->dwMeasure);
  913. lTemp = lTemp % pSD->dwlnMeasure;
  914. pCurve->bBeat = (BYTE)(lTemp / pSD->dwlnBeat);
  915. lTemp = lTemp % pSD->dwlnBeat;
  916. pCurve->bGrid = (BYTE)(lTemp / pSD->dwlnGrid);
  917. //pCurve->nOffset = (short)(lTemp % pSD->dwlnGrid);
  918. pCurve->nOffset = (short)(lTemp % pSD->dwlnGrid) + rItem.nOffset;
  919. pCurve->mtTime = rItem.mtStart + mtOffset + rItem.nOffset;
  920. // Set the DX8 flag to indicate the wMergeIndex and wParamType fields are valid.
  921. pCurve->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_DX8;
  922. }
  923. pCurve->dwPChannel = dwMutePChannel;
  924. pCurve->dwVirtualTrackID = dwVirtualID;
  925. pCurve->dwType = DMUS_PMSGT_CURVE;
  926. pCurve->mtDuration = rItem.mtDuration;
  927. pCurve->mtResetDuration = rItem.mtResetDuration;
  928. pCurve->nStartValue = rItem.nStartValue;
  929. pCurve->nEndValue = rItem.nEndValue;
  930. pCurve->nResetValue = rItem.nResetValue;
  931. pCurve->bType = rItem.bType;
  932. pCurve->bCurveShape = rItem.bCurveShape;
  933. pCurve->bCCData = rItem.bCCData;
  934. pCurve->bFlags = rItem.bFlags;
  935. pCurve->wParamType = rItem.wParamType;
  936. pCurve->wMergeIndex = rItem.wMergeIndex;
  937. pCurve->dwGroupID = pSD->dwGroupBits;
  938. if( pGraph )
  939. {
  940. pGraph->StampPMsg( pEvent );
  941. }
  942. if(FAILED(pPerf->SendPMsg( pEvent )))
  943. {
  944. pPerf->FreePMsg(pEvent);
  945. }
  946. }
  947. }
  948. }
  949. }
  950. if( pSD->apCurrentSeq )
  951. {
  952. for( ; pSD->apCurrentSeq[dwIndex];
  953. pSD->apCurrentSeq[dwIndex] = pSD->apCurrentSeq[dwIndex]->GetNext() )
  954. {
  955. DMUS_IO_SEQ_ITEM& rItem = pSD->apCurrentSeq[dwIndex]->GetItemValue();
  956. if( rItem.mtTime >= mtEnd )
  957. {
  958. break;
  959. }
  960. m_PChMap.GetInfo( dwPChannel, rItem.mtTime, mtOffset, pSD->dwGroupBits,
  961. pPerf, &fMute, &dwMutePChannel, fClockTime );
  962. if( !fMute )
  963. {
  964. if( (rItem.bStatus & 0xf0) == 0x90 )
  965. {
  966. // this is a note event
  967. DMUS_NOTE_PMSG* pNote;
  968. if( SUCCEEDED( pPerf->AllocPMsg( sizeof(DMUS_NOTE_PMSG),
  969. (DMUS_PMSG**)&pNote )))
  970. {
  971. pNote->bFlags = DMUS_NOTEF_NOTEON;
  972. pNote->mtDuration = rItem.mtDuration;
  973. pNote->bMidiValue = rItem.bByte1;
  974. pNote->bVelocity = rItem.bByte2;
  975. pNote->dwType = DMUS_PMSGT_NOTE;
  976. pNote->bPlayModeFlags = DMUS_PLAYMODE_FIXED;
  977. pNote->wMusicValue = pNote->bMidiValue;
  978. pNote->bSubChordLevel = 0; // SUBCHORD_BASS
  979. if (fClockTime)
  980. {
  981. pNote->rtTime = ((rItem.mtTime + rItem.nOffset) * REF_PER_MIL) + rtOffset;
  982. pNote->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
  983. pNote->wMeasure = 0;
  984. pNote->bBeat = 0;
  985. pNote->bGrid = 0;
  986. pNote->nOffset = rItem.nOffset;
  987. }
  988. else
  989. {
  990. pNote->mtTime = rItem.mtTime + mtOffset + rItem.nOffset;
  991. UpdateTimeSig( pSegSt, pSD, rItem.mtTime );
  992. pNote->dwFlags = DMUS_PMSGF_MUSICTIME;
  993. long lTemp = (rItem.mtTime - pSD->mtCurTimeSig);
  994. pNote->wMeasure = (WORD)((lTemp / pSD->dwlnMeasure) + pSD->dwMeasure);
  995. lTemp = lTemp % pSD->dwlnMeasure;
  996. pNote->bBeat = (BYTE)(lTemp / pSD->dwlnBeat);
  997. lTemp = lTemp % pSD->dwlnBeat;
  998. pNote->bGrid = (BYTE)(lTemp / pSD->dwlnGrid);
  999. //pNote->nOffset = (short)(lTemp % pSD->dwlnGrid);
  1000. pNote->nOffset = (short)(lTemp % pSD->dwlnGrid) + rItem.nOffset;
  1001. }
  1002. pNote->bTimeRange = 0;
  1003. pNote->bDurRange = 0;
  1004. pNote->bVelRange = 0;
  1005. pNote->cTranspose = 0;
  1006. pEvent = (DMUS_PMSG*)pNote;
  1007. }
  1008. }
  1009. else
  1010. {
  1011. // it's a MIDI short that's not a note
  1012. DMUS_MIDI_PMSG* pMidi;
  1013. if( SUCCEEDED( pPerf->AllocPMsg( sizeof(DMUS_MIDI_PMSG),
  1014. (DMUS_PMSG**)&pMidi )))
  1015. {
  1016. pMidi->bStatus = rItem.bStatus & 0xf0;
  1017. pMidi->bByte1 = rItem.bByte1;
  1018. pMidi->bByte2 = rItem.bByte2;
  1019. pMidi->dwType = DMUS_PMSGT_MIDI;
  1020. if (fClockTime)
  1021. {
  1022. pMidi->rtTime = (rItem.mtTime * REF_PER_MIL) + rtOffset;
  1023. pMidi->dwFlags |= DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
  1024. }
  1025. else
  1026. {
  1027. pMidi->mtTime = rItem.mtTime + mtOffset;
  1028. pMidi->dwFlags |= DMUS_PMSGF_MUSICTIME;
  1029. }
  1030. pEvent = (DMUS_PMSG*)pMidi;
  1031. }
  1032. }
  1033. if( pEvent )
  1034. {
  1035. pEvent->dwPChannel = dwMutePChannel;
  1036. pEvent->dwVirtualTrackID = dwVirtualID;
  1037. pEvent->dwGroupID = pSD->dwGroupBits;
  1038. if( pGraph )
  1039. {
  1040. pGraph->StampPMsg( pEvent );
  1041. }
  1042. if(FAILED(pPerf->SendPMsg( pEvent )))
  1043. {
  1044. pPerf->FreePMsg(pEvent);
  1045. }
  1046. }
  1047. }
  1048. }
  1049. }
  1050. }
  1051. if( pGraph )
  1052. {
  1053. pGraph->Release();
  1054. }
  1055. return hr;
  1056. }
  1057. // SendSeekItem() - sends either the pSeq or pCurve, depending on which occurs
  1058. // latest. Sends the item at mtTime + mtOffset.
  1059. void CSeqTrack::SendSeekItem( IDirectMusicPerformance* pPerf,
  1060. IDirectMusicGraph* pGraph,
  1061. IDirectMusicSegmentState* pSegSt,
  1062. SeqStateData* pSD,
  1063. DWORD dwVirtualID,
  1064. MUSIC_TIME mtTime,
  1065. MUSIC_TIME mtOffset,
  1066. REFERENCE_TIME rtOffset,
  1067. TListItem<DMUS_IO_SEQ_ITEM>* pSeq,
  1068. TListItem<DMUS_IO_CURVE_ITEM>* pCurve,
  1069. BOOL fClockTime)
  1070. {
  1071. DWORD dwMutePChannel;
  1072. BOOL fMute;
  1073. if( pSeq )
  1074. {
  1075. DMUS_IO_SEQ_ITEM& rSeq = pSeq->GetItemValue();
  1076. if( pCurve )
  1077. {
  1078. DMUS_IO_CURVE_ITEM& rCurve = pCurve->GetItemValue();
  1079. if( rSeq.mtTime >= rCurve.mtStart + rCurve.mtDuration )
  1080. {
  1081. // the seq item happens after the curve item. Send the
  1082. // seq item and clear the curve item so it doesn't go out.
  1083. pCurve = NULL;
  1084. }
  1085. }
  1086. // if pCurve is NULL or was set to NULL, send out the seq item
  1087. if( NULL == pCurve )
  1088. {
  1089. m_PChMap.GetInfo( rSeq.dwPChannel, rSeq.mtTime, mtOffset, pSD->dwGroupBits,
  1090. pPerf, &fMute, &dwMutePChannel, fClockTime );
  1091. if( !fMute )
  1092. {
  1093. DMUS_MIDI_PMSG* pMidi;
  1094. if( SUCCEEDED( pPerf->AllocPMsg( sizeof(DMUS_MIDI_PMSG),
  1095. (DMUS_PMSG**)&pMidi )))
  1096. {
  1097. pMidi->bStatus = rSeq.bStatus & 0xf0;
  1098. pMidi->bByte1 = rSeq.bByte1;
  1099. pMidi->bByte2 = rSeq.bByte2;
  1100. pMidi->dwType = DMUS_PMSGT_MIDI;
  1101. ASSERT( mtTime > rSeq.mtTime ); // this is true for back-seeking
  1102. if (fClockTime)
  1103. {
  1104. pMidi->rtTime = (mtTime * REF_PER_MIL) + rtOffset;
  1105. pMidi->dwFlags |= DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
  1106. }
  1107. else
  1108. {
  1109. pMidi->mtTime = mtTime + mtOffset;
  1110. pMidi->dwFlags |= DMUS_PMSGF_MUSICTIME;
  1111. }
  1112. pMidi->dwPChannel = dwMutePChannel;
  1113. pMidi->dwVirtualTrackID = dwVirtualID;
  1114. pMidi->dwGroupID = pSD->dwGroupBits;
  1115. if( pGraph )
  1116. {
  1117. pGraph->StampPMsg( (DMUS_PMSG*)pMidi );
  1118. }
  1119. if(FAILED(pPerf->SendPMsg( (DMUS_PMSG*)pMidi )))
  1120. {
  1121. pPerf->FreePMsg((DMUS_PMSG*)pMidi);
  1122. }
  1123. }
  1124. }
  1125. }
  1126. }
  1127. if( pCurve )
  1128. {
  1129. DMUS_IO_CURVE_ITEM& rCurve = pCurve->GetItemValue();
  1130. m_PChMap.GetInfo( rCurve.dwPChannel, rCurve.mtStart, mtOffset, pSD->dwGroupBits,
  1131. pPerf, &fMute, &dwMutePChannel, fClockTime );
  1132. if( !fMute )
  1133. {
  1134. DMUS_CURVE_PMSG* pCurvePmsg;
  1135. if( SUCCEEDED( pPerf->AllocPMsg( sizeof(DMUS_CURVE_PMSG),
  1136. (DMUS_PMSG**)&pCurvePmsg )))
  1137. {
  1138. if (fClockTime) // If clock time, don't fill in time signature info, it's useless.
  1139. {
  1140. pCurvePmsg->wMeasure = 0;
  1141. pCurvePmsg->bBeat = 0;
  1142. pCurvePmsg->bGrid = 0;
  1143. pCurvePmsg->nOffset = 0;
  1144. pCurvePmsg->rtTime = ((mtTime + rCurve.nOffset) * REF_PER_MIL) + rtOffset;
  1145. pCurvePmsg->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
  1146. }
  1147. else
  1148. {
  1149. UpdateTimeSig( pSegSt, pSD, rCurve.mtStart);
  1150. long lTemp = (rCurve.mtStart - pSD->mtCurTimeSig);
  1151. pCurvePmsg->wMeasure = (WORD)((lTemp / pSD->dwlnMeasure) + pSD->dwMeasure);
  1152. lTemp = lTemp % pSD->dwlnMeasure;
  1153. pCurvePmsg->bBeat = (BYTE)(lTemp / pSD->dwlnBeat);
  1154. lTemp = lTemp % pSD->dwlnBeat;
  1155. pCurvePmsg->bGrid = (BYTE)(lTemp / pSD->dwlnGrid);
  1156. pCurvePmsg->nOffset = (short)(lTemp % pSD->dwlnGrid) + rCurve.nOffset;
  1157. pCurvePmsg->dwFlags = DMUS_PMSGF_MUSICTIME;
  1158. ASSERT( mtTime > rCurve.mtStart );// this is true for back-seeking
  1159. // in any case, play curve at mtTime + mtOffset + pCurvePmsg->nOffset
  1160. pCurvePmsg->mtTime = mtTime + mtOffset + rCurve.nOffset;
  1161. pCurvePmsg->dwFlags = DMUS_PMSGF_MUSICTIME;
  1162. }
  1163. pCurvePmsg->dwPChannel = dwMutePChannel;
  1164. pCurvePmsg->dwVirtualTrackID = dwVirtualID;
  1165. pCurvePmsg->dwType = DMUS_PMSGT_CURVE;
  1166. pCurvePmsg->bType = rCurve.bType;
  1167. pCurvePmsg->bCCData = rCurve.bCCData;
  1168. pCurvePmsg->bFlags = rCurve.bFlags;
  1169. pCurvePmsg->dwGroupID = pSD->dwGroupBits;
  1170. pCurvePmsg->nStartValue = rCurve.nStartValue;
  1171. pCurvePmsg->nEndValue = rCurve.nEndValue;
  1172. pCurvePmsg->nResetValue = rCurve.nResetValue;
  1173. if( mtTime >= rCurve.mtStart + rCurve.mtDuration )
  1174. {
  1175. // playing at a time past the curve's duration. Just play
  1176. // an instant curve at that time instead. Instant curves
  1177. // play at their endvalue. Duration is irrelavant.
  1178. pCurvePmsg->bCurveShape = DMUS_CURVES_INSTANT;
  1179. if( pCurvePmsg->bFlags & DMUS_CURVE_RESET )
  1180. {
  1181. if( mtTime >= rCurve.mtStart + rCurve.mtDuration +
  1182. rCurve.mtResetDuration + rCurve.nOffset )
  1183. {
  1184. // don't need the curve reset any more
  1185. pCurvePmsg->bFlags &= ~DMUS_CURVE_RESET;
  1186. }
  1187. else
  1188. {
  1189. // otherwise make sure the reset event happens at the same time
  1190. // it would have if we weren't seeking back.
  1191. pCurvePmsg->mtResetDuration = rCurve.mtStart + rCurve.mtDuration +
  1192. rCurve.mtResetDuration + rCurve.nOffset - mtTime;
  1193. }
  1194. }
  1195. }
  1196. else
  1197. {
  1198. // playing at a time in the middle of a curve.
  1199. pCurvePmsg->bCurveShape = rCurve.bCurveShape;
  1200. if (fClockTime)
  1201. {
  1202. pCurvePmsg->mtOriginalStart = mtTime - (rCurve.mtStart + mtOffset + rCurve.nOffset);
  1203. }
  1204. else
  1205. {
  1206. pCurvePmsg->mtOriginalStart = rCurve.mtStart + mtOffset + rCurve.nOffset;
  1207. }
  1208. if( pCurvePmsg->bCurveShape != DMUS_CURVES_INSTANT )
  1209. {
  1210. pCurvePmsg->mtDuration = rCurve.mtStart + rCurve.mtDuration - mtTime;
  1211. }
  1212. pCurvePmsg->mtResetDuration = rCurve.mtResetDuration;
  1213. }
  1214. if( pGraph )
  1215. {
  1216. pGraph->StampPMsg( (DMUS_PMSG*)pCurvePmsg );
  1217. }
  1218. if(FAILED(pPerf->SendPMsg( (DMUS_PMSG*)pCurve )))
  1219. {
  1220. pPerf->FreePMsg((DMUS_PMSG*)pCurve);
  1221. }
  1222. }
  1223. }
  1224. }
  1225. }
  1226. // Seek() - set all pSD's pointers to the correct location. If fGetPrevious is set,
  1227. // also send control change, pitch bend, curves, etc. that are in the past so the
  1228. // state at mtTime is as if we played from the beginning of the track.
  1229. HRESULT CSeqTrack::Seek( IDirectMusicSegmentState* pSegSt,
  1230. IDirectMusicPerformance* pPerf, DWORD dwVirtualID,
  1231. SeqStateData* pSD, MUSIC_TIME mtTime, BOOL fGetPrevious,
  1232. MUSIC_TIME mtOffset, REFERENCE_TIME rtOffset, BOOL fClockTime)
  1233. {
  1234. DWORD dwIndex;
  1235. TListItem<SEQ_PART>* pPart;
  1236. TListItem<DMUS_IO_SEQ_ITEM>* pSeqItem;
  1237. TListItem<DMUS_IO_CURVE_ITEM>* pCurveItem;
  1238. // in the case of mtTime == 0 and fGetPrevious (which means DMUS_SEGF_START was
  1239. // set in Play() ) we want to reset all lists to the beginning regardless of time.
  1240. if( fGetPrevious && ( mtTime == 0 ) )
  1241. {
  1242. pPart = m_SeqPartList.GetHead();
  1243. for( dwIndex = 0; dwIndex < m_dwPChannelsUsed; dwIndex++ )
  1244. {
  1245. if( pPart )
  1246. {
  1247. pSeqItem = pPart->GetItemValue().seqList.GetHead();
  1248. if( pSeqItem && pSD->apCurrentSeq )
  1249. {
  1250. pSD->apCurrentSeq[dwIndex] = pSeqItem;
  1251. }
  1252. pCurveItem = pPart->GetItemValue().curveList.GetHead();
  1253. if( pCurveItem && pSD->apCurrentCurve )
  1254. {
  1255. pSD->apCurrentCurve[dwIndex] = pCurveItem;
  1256. }
  1257. pPart = pPart->GetNext();
  1258. }
  1259. else
  1260. {
  1261. break;
  1262. }
  1263. }
  1264. return S_OK;
  1265. }
  1266. #define CC_1 96
  1267. // CC_1 is the limit of the CC#'s we pay attention to. CC#96 through #101
  1268. // are registered and non-registered parameter #'s, and data increment and
  1269. // decrement, which we are choosing to ignore.
  1270. TListItem<DMUS_IO_SEQ_ITEM>* apSeqItemCC[ CC_1 ];
  1271. TListItem<DMUS_IO_CURVE_ITEM>* apCurveItemCC[ CC_1 ];
  1272. TListItem<DMUS_IO_SEQ_ITEM>* pSeqItemMonoAT;
  1273. TListItem<DMUS_IO_CURVE_ITEM>* pCurveItemMonoAT;
  1274. TListItem<DMUS_IO_SEQ_ITEM>* pSeqItemPBend;
  1275. TListItem<DMUS_IO_CURVE_ITEM>* pCurveItemPBend;
  1276. IDirectMusicGraph* pGraph;
  1277. if( FAILED( pSegSt->QueryInterface( IID_IDirectMusicGraph,
  1278. (void**)&pGraph )))
  1279. {
  1280. pGraph = NULL;
  1281. }
  1282. pPart = m_SeqPartList.GetHead();
  1283. for( dwIndex = 0; dwIndex < m_dwPChannelsUsed; dwIndex++ )
  1284. {
  1285. if( pPart )
  1286. {
  1287. memset(apSeqItemCC, 0, sizeof(TListItem<DMUS_IO_SEQ_ITEM>*) * CC_1);
  1288. memset(apCurveItemCC, 0, sizeof(TListItem<DMUS_IO_CURVE_ITEM>*) * CC_1);
  1289. pSeqItemMonoAT = NULL;
  1290. pCurveItemMonoAT = NULL;
  1291. pSeqItemPBend = NULL;
  1292. pCurveItemPBend = NULL;
  1293. // scan the seq event list in this part, storing any CC, MonoAT, and PBend
  1294. // events we come across.
  1295. for( pSeqItem = pPart->GetItemValue().seqList.GetHead(); pSeqItem; pSeqItem = pSeqItem->GetNext() )
  1296. {
  1297. DMUS_IO_SEQ_ITEM& rSeqItem = pSeqItem->GetItemValue();
  1298. if( rSeqItem.mtTime >= mtTime )
  1299. {
  1300. break;
  1301. }
  1302. if( !fGetPrevious )
  1303. {
  1304. // if we don't care about previous events, just continue
  1305. continue;
  1306. }
  1307. switch( rSeqItem.bStatus & 0xf0 )
  1308. {
  1309. case MIDI_CCHANGE:
  1310. // ignore Registered and Non-registered Parameters,
  1311. // Data increment, Data decrement, and Data entry MSB and LSB.
  1312. if( ( rSeqItem.bByte1 < CC_1 ) && ( rSeqItem.bByte1 != 6 ) &&
  1313. ( rSeqItem.bByte1 != 38 ) )
  1314. {
  1315. apSeqItemCC[ rSeqItem.bByte1 ] = pSeqItem;
  1316. }
  1317. break;
  1318. case MIDI_MTOUCH:
  1319. pSeqItemMonoAT = pSeqItem;
  1320. break;
  1321. case MIDI_PBEND:
  1322. pSeqItemPBend = pSeqItem;
  1323. break;
  1324. default:
  1325. break;
  1326. }
  1327. }
  1328. if( pSD->apCurrentSeq )
  1329. {
  1330. pSD->apCurrentSeq[dwIndex] = pSeqItem;
  1331. }
  1332. // scan the curve event list in this part, storing any CC, MonoAT, and PBend
  1333. // events we come across
  1334. for( pCurveItem = pPart->GetItemValue().curveList.GetHead(); pCurveItem; pCurveItem = pCurveItem->GetNext() )
  1335. {
  1336. DMUS_IO_CURVE_ITEM& rCurveItem = pCurveItem->GetItemValue();
  1337. if( rCurveItem.mtStart >= mtTime )
  1338. {
  1339. break;
  1340. }
  1341. if( !fGetPrevious )
  1342. {
  1343. // if we don't care about previous events, just continue
  1344. continue;
  1345. }
  1346. switch( rCurveItem.bType )
  1347. {
  1348. case DMUS_CURVET_CCCURVE:
  1349. if( ( rCurveItem.bCCData < CC_1 ) && ( rCurveItem.bCCData != 6 ) &&
  1350. ( rCurveItem.bCCData != 38 ) )
  1351. {
  1352. if( apCurveItemCC[ rCurveItem.bCCData ] )
  1353. {
  1354. DMUS_IO_CURVE_ITEM& rTemp = apCurveItemCC[ rCurveItem.bCCData ]->GetItemValue();
  1355. if( rCurveItem.mtStart + rCurveItem.mtDuration + rCurveItem.nOffset >
  1356. rTemp.mtStart + rTemp.mtDuration + rTemp.nOffset )
  1357. {
  1358. apCurveItemCC[ rCurveItem.bCCData ] = pCurveItem;
  1359. }
  1360. }
  1361. else
  1362. {
  1363. apCurveItemCC[ rCurveItem.bCCData ] = pCurveItem;
  1364. }
  1365. }
  1366. break;
  1367. case DMUS_CURVET_MATCURVE:
  1368. if( pCurveItemMonoAT )
  1369. {
  1370. DMUS_IO_CURVE_ITEM& rTemp = pCurveItemMonoAT->GetItemValue();
  1371. if( rCurveItem.mtStart + rCurveItem.mtDuration + rCurveItem.nOffset >
  1372. rTemp.mtStart + rTemp.mtDuration + rTemp.nOffset )
  1373. {
  1374. pCurveItemMonoAT = pCurveItem;
  1375. }
  1376. }
  1377. else
  1378. {
  1379. pCurveItemMonoAT = pCurveItem;
  1380. }
  1381. break;
  1382. case DMUS_CURVET_PBCURVE:
  1383. if( pCurveItemPBend )
  1384. {
  1385. DMUS_IO_CURVE_ITEM& rTemp = pCurveItemPBend->GetItemValue();
  1386. if( rCurveItem.mtStart + rCurveItem.mtDuration + rCurveItem.nOffset >
  1387. rTemp.mtStart + rTemp.mtDuration + rTemp.nOffset )
  1388. {
  1389. pCurveItemPBend = pCurveItem;
  1390. }
  1391. }
  1392. else
  1393. {
  1394. pCurveItemPBend = pCurveItem;
  1395. }
  1396. break;
  1397. default:
  1398. break;
  1399. }
  1400. }
  1401. if( pSD->apCurrentCurve )
  1402. {
  1403. pSD->apCurrentCurve[dwIndex] = pCurveItem;
  1404. }
  1405. if( fGetPrevious )
  1406. {
  1407. DWORD dwCC;
  1408. // create and send past events appropriately
  1409. SendSeekItem( pPerf, pGraph, pSegSt, pSD, dwVirtualID, mtTime, mtOffset, rtOffset, pSeqItemPBend, pCurveItemPBend, fClockTime );
  1410. SendSeekItem( pPerf, pGraph, pSegSt, pSD, dwVirtualID, mtTime, mtOffset, rtOffset, pSeqItemMonoAT, pCurveItemMonoAT, fClockTime );
  1411. for( dwCC = 0; dwCC < CC_1; dwCC++ )
  1412. {
  1413. SendSeekItem( pPerf, pGraph, pSegSt, pSD, dwVirtualID, mtTime, mtOffset, rtOffset, apSeqItemCC[dwCC], apCurveItemCC[dwCC], fClockTime );
  1414. }
  1415. }
  1416. pPart = pPart->GetNext();
  1417. }
  1418. }
  1419. if( pGraph )
  1420. {
  1421. pGraph->Release();
  1422. }
  1423. return S_OK;
  1424. }
  1425. /*
  1426. @method HRESULT | IDirectMusicTrack | GetParam |
  1427. Retrieves data from a Track.
  1428. @rvalue S_OK | Got the data ok.
  1429. @rvalue E_NOTIMPL | Not implemented.
  1430. */
  1431. STDMETHODIMP CSeqTrack::GetParam(
  1432. REFGUID rguidType, // @parm The type of data to obtain.
  1433. MUSIC_TIME mtTime, // @parm The time, in Track time, to obtain the data.
  1434. MUSIC_TIME* pmtNext,// @parm Returns the Track time until which the data is valid. <p pmtNext>
  1435. // may be NULL. If this returns a value of 0, it means that this
  1436. // data will either be always valid, or it is unknown when it will
  1437. // become invalid.
  1438. void *pData) // @parm The struture in which to return the data. Each
  1439. // <p pGuidType> identifies a particular structure of a
  1440. // particular size. It is important that this field contain
  1441. // the correct structure of the correct size. Otherwise,
  1442. // fatal results can occur.
  1443. {
  1444. return E_NOTIMPL;
  1445. }
  1446. /*
  1447. @method HRESULT | IDirectMusicTrack | SetParam |
  1448. Sets data on a Track.
  1449. @rvalue S_OK | Set the data ok.
  1450. @rvalue E_NOTIMPL | Not implemented.
  1451. */
  1452. STDMETHODIMP CSeqTrack::SetParam(
  1453. REFGUID rguidType, // @parm The type of data to set.
  1454. MUSIC_TIME mtTime, // @parm The time, in Track time, to set the data.
  1455. void *pData) // @parm The struture containing the data to set. Each
  1456. // <p pGuidType> identifies a particular structure of a
  1457. // particular size. It is important that this field contain
  1458. // the correct structure of the correct size. Otherwise,
  1459. // fatal results can occur.
  1460. {
  1461. return E_NOTIMPL;
  1462. }
  1463. STDMETHODIMP CSeqTrack::GetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime,
  1464. REFERENCE_TIME* prtNext,void* pParam,void * pStateData, DWORD dwFlags)
  1465. {
  1466. return E_NOTIMPL;
  1467. }
  1468. STDMETHODIMP CSeqTrack::SetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime,
  1469. void* pParam, void * pStateData, DWORD dwFlags)
  1470. {
  1471. return E_NOTIMPL;
  1472. }
  1473. /*
  1474. @method HRESULT | IDirectMusicTrack | AddNotificationType |
  1475. Similar to and called from <om IDirectMusicSegment.AddNotificationType>. This
  1476. gives the track a chance to respond to notifications.
  1477. @rvalue E_NOTIMPL | The track doesn't support notifications.
  1478. @rvalue S_OK | Success.
  1479. @rvalue S_FALSE | The track doesn't support the requested notification type.
  1480. */
  1481. HRESULT STDMETHODCALLTYPE CSeqTrack::AddNotificationType(
  1482. REFGUID rguidNotification) // @parm The notification guid to add.
  1483. {
  1484. return E_NOTIMPL;
  1485. }
  1486. /*
  1487. @method HRESULT | IDirectMusicTrack | RemoveNotificationType |
  1488. Similar to and called from <om IDirectMusicSegment.RemoveNotificationType>. This
  1489. gives the track a chance to remove notifications.
  1490. @rvalue E_NOTIMPL | The track doesn't support notifications.
  1491. @rvalue S_OK | Success.
  1492. @rvalue S_FALSE | The track doesn't support the requested notification type.
  1493. */
  1494. HRESULT STDMETHODCALLTYPE CSeqTrack::RemoveNotificationType(
  1495. REFGUID rguidNotification) // @parm The notification guid to remove.
  1496. {
  1497. return E_NOTIMPL;
  1498. }
  1499. /*
  1500. @method HRESULT | IDirectMusicTrack | Clone |
  1501. Creates a copy of the Track.
  1502. @rvalue S_OK | Success.
  1503. @rvalue E_OUTOFMEMORY | Out of memory.
  1504. @rvalue E_POINTER | <p ppTrack> is NULL or invalid.
  1505. @xref <om IDirectMusicSegment.Clone>
  1506. */
  1507. HRESULT STDMETHODCALLTYPE CSeqTrack::Clone(
  1508. MUSIC_TIME mtStart, // @parm The start of the part to clone. It should be 0 or greater,
  1509. // and less than the length of the Track.
  1510. MUSIC_TIME mtEnd, // @parm The end of the part to clone. It should be greater than
  1511. // <p mtStart> and less than the length of the Track.
  1512. IDirectMusicTrack** ppTrack) // @parm Returns the cloned Track.
  1513. {
  1514. V_INAME(IDirectMusicTrack::Clone);
  1515. V_PTRPTR_WRITE(ppTrack);
  1516. HRESULT hr = S_OK;
  1517. if(mtStart < 0 )
  1518. {
  1519. Trace(1,"Error: Invalid clone parameters to Sequence Track, start time is %ld.\n",mtStart);
  1520. return E_INVALIDARG;
  1521. }
  1522. if(mtStart > mtEnd)
  1523. {
  1524. Trace(1,"Error: Invalid clone parameters to Sequence Track, start time %ld is greater than end %ld.\n",mtStart,mtEnd);
  1525. return E_INVALIDARG;
  1526. }
  1527. EnterCriticalSection(&m_CrSec);
  1528. CSeqTrack *pDM;
  1529. try
  1530. {
  1531. pDM = new CSeqTrack(*this, mtStart, mtEnd);
  1532. }
  1533. catch( ... )
  1534. {
  1535. pDM = NULL;
  1536. }
  1537. LeaveCriticalSection(&m_CrSec);
  1538. if (pDM == NULL) {
  1539. return E_OUTOFMEMORY;
  1540. }
  1541. hr = pDM->QueryInterface(IID_IDirectMusicTrack, (void**)ppTrack);
  1542. pDM->Release();
  1543. return hr;
  1544. }
  1545. STDMETHODIMP CSeqTrack::Compose(
  1546. IUnknown* pContext,
  1547. DWORD dwTrackGroup,
  1548. IDirectMusicTrack** ppResultTrack)
  1549. {
  1550. return E_NOTIMPL;
  1551. }
  1552. STDMETHODIMP CSeqTrack::Join(
  1553. IDirectMusicTrack* pNewTrack,
  1554. MUSIC_TIME mtJoin,
  1555. IUnknown* pContext,
  1556. DWORD dwTrackGroup,
  1557. IDirectMusicTrack** ppResultTrack)
  1558. {
  1559. return E_NOTIMPL;
  1560. }