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.

683 lines
17 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. // SysExTrk.cpp : Implementation of CSysExTrk
  21. #include "dmime.h"
  22. #include "SysExTrk.h"
  23. #include "dmusici.h"
  24. #include "dmusicf.h"
  25. #include "dmperf.h"
  26. #include "debug.h"
  27. #include "..\shared\Validate.h"
  28. #include "debug.h"
  29. #define ASSERT assert
  30. /////////////////////////////////////////////////////////////////////////////
  31. // CSysExTrack
  32. void CSysExTrack::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. CSysExTrack::CSysExTrack()
  42. {
  43. Construct();
  44. }
  45. CSysExTrack::CSysExTrack(
  46. CSysExTrack& rTrack, MUSIC_TIME mtStart, MUSIC_TIME mtEnd)
  47. {
  48. Construct();
  49. SysExListItem* pScan = rTrack.m_SysExEventList.GetHead();
  50. for(; pScan; pScan = pScan->GetNext())
  51. {
  52. FullSysexEvent* pItem = pScan->m_pItem;
  53. if( NULL == pItem )
  54. {
  55. continue;
  56. }
  57. else if( pItem->mtTime < mtStart )
  58. {
  59. continue;
  60. }
  61. else if (pItem->mtTime < mtEnd)
  62. {
  63. SysExListItem* pNew = new SysExListItem;
  64. if (pNew)
  65. {
  66. FullSysexEvent item;
  67. item.mtTime = pItem->mtTime - mtStart;
  68. item.dwSysExLength = pItem->dwSysExLength;
  69. if (item.dwSysExLength && (item.pbSysExData = new BYTE[item.dwSysExLength]))
  70. {
  71. memcpy( item.pbSysExData, pItem->pbSysExData, item.dwSysExLength );
  72. pNew->SetItem(item);
  73. m_SysExEventList.AddTail(pNew);
  74. }
  75. else
  76. {
  77. delete pNew;
  78. }
  79. }
  80. }
  81. else break;
  82. }
  83. }
  84. CSysExTrack::~CSysExTrack()
  85. {
  86. if (m_fCSInitialized)
  87. {
  88. DeleteCriticalSection(&m_CrSec);
  89. }
  90. InterlockedDecrement(&g_cComponent);
  91. }
  92. // method:(EXTERNAL) HRESULT | IDirectMusicSysExTrack | QueryInterface | Standard QueryInterface implementation for <i IDirectMusicSysExTrack>
  93. //
  94. // parm const IID & | iid | Interface to query for
  95. // parm void ** | ppv | The requested interface will be returned here
  96. //
  97. // rdesc Returns one of the following:
  98. //
  99. // flag S_OK | If the interface is supported and was returned
  100. // flag E_NOINTERFACE | If the object does not support the given interface.
  101. //
  102. // mfunc:(INTERNAL)
  103. //
  104. //
  105. STDMETHODIMP CSysExTrack::QueryInterface(
  106. const IID &iid, // @parm Interface to query for
  107. void **ppv) // @parm The requested interface will be returned here
  108. {
  109. V_INAME(CSysExTrack::QueryInterface);
  110. V_PTRPTR_WRITE(ppv);
  111. V_REFGUID(iid);
  112. if (iid == IID_IUnknown || iid == IID_IDirectMusicTrack || iid == IID_IDirectMusicTrack8)
  113. {
  114. *ppv = static_cast<IDirectMusicTrack*>(this);
  115. } else
  116. if (iid == IID_IPersistStream)
  117. {
  118. *ppv = static_cast<IPersistStream*>(this);
  119. } else
  120. {
  121. *ppv = NULL;
  122. Trace(4,"Warning: Request to query unknown interface on Sysex Track\n");
  123. return E_NOINTERFACE;
  124. }
  125. reinterpret_cast<IUnknown*>(this)->AddRef();
  126. return S_OK;
  127. }
  128. // method:(EXTERNAL) HRESULT | IDirectMusicSysExTrack | AddRef | Standard AddRef implementation for <i IDirectMusicSysExTrack>
  129. //
  130. // rdesc Returns the new reference count for this object.
  131. //
  132. // mfunc:(INTERNAL)
  133. //
  134. //
  135. STDMETHODIMP_(ULONG) CSysExTrack::AddRef()
  136. {
  137. return InterlockedIncrement(&m_cRef);
  138. }
  139. // method:(EXTERNAL) HRESULT | IDirectMusicSysExTrack | Release | Standard Release implementation for <i IDirectMusicSysExTrack>
  140. //
  141. // rdesc Returns the new reference count for this object.
  142. //
  143. // mfunc:(INTERNAL)
  144. //
  145. //
  146. STDMETHODIMP_(ULONG) CSysExTrack::Release()
  147. {
  148. if (!InterlockedDecrement(&m_cRef))
  149. {
  150. delete this;
  151. return 0;
  152. }
  153. return m_cRef;
  154. }
  155. /////////////////////////////////////////////////////////////////////////////
  156. // IPersist
  157. HRESULT CSysExTrack::GetClassID( CLSID* pClassID )
  158. {
  159. V_INAME(CSysExTrack::GetClassID);
  160. V_PTR_WRITE(pClassID, CLSID);
  161. *pClassID = CLSID_DirectMusicSysExTrack;
  162. return S_OK;
  163. }
  164. /////////////////////////////////////////////////////////////////////////////
  165. // IPersistStream functions
  166. HRESULT CSysExTrack::IsDirty()
  167. {
  168. return S_FALSE;
  169. }
  170. /*
  171. method HRESULT | ISeqTrack | Load |
  172. Call this with an IStream filled with SysExEvents, sorted in time order.
  173. parm IStream* | pIStream |
  174. A stream of SysExEvents, sorted in time order. The seek pointer should point
  175. to the first event. The stream should contain only SysExEvents and nothing more.
  176. rvalue E_POINTER | If pIStream == NULL or invalid.
  177. rvalue S_OK
  178. comm The <p pIStream> will be AddRef'd inside this function and held
  179. until the SysExTrack is released.
  180. */
  181. HRESULT CSysExTrack::Load( IStream* pIStream )
  182. {
  183. V_INAME(CSysExTrack::Load);
  184. V_INTERFACE(pIStream);
  185. EnterCriticalSection(&m_CrSec);
  186. HRESULT hr = S_OK;
  187. m_dwValidate++;
  188. if( m_SysExEventList.GetHead() )
  189. {
  190. m_SysExEventList.DeleteAll();
  191. }
  192. // copy contents of the stream into the list.
  193. //DMUS_IO_SYSEX_ITEM sysexEvent;
  194. FullSysexEvent sysexEvent;
  195. // read in the chunk id
  196. DWORD dwChunk;
  197. long lSize;
  198. pIStream->Read( &dwChunk, sizeof(DWORD), NULL);
  199. if( dwChunk != DMUS_FOURCC_SYSEX_TRACK )
  200. {
  201. Trace(1,"Error: Invalid data in sysex track.\n");
  202. LeaveCriticalSection(&m_CrSec);
  203. return DMUS_E_CHUNKNOTFOUND;
  204. }
  205. // read in the overall size
  206. if( FAILED( pIStream->Read( &lSize, sizeof(long), NULL )))
  207. {
  208. Trace(1,"Error: Unable to read sysex track.\n");
  209. LeaveCriticalSection(&m_CrSec);
  210. return DMUS_E_CANNOTREAD;
  211. }
  212. DMUS_IO_SYSEX_ITEM SysexItem;
  213. BYTE* pbSysExData;
  214. while( lSize > 0 )
  215. {
  216. if( FAILED( pIStream->Read( &SysexItem, sizeof(DMUS_IO_SYSEX_ITEM), NULL )))
  217. {
  218. Trace(1,"Error: Unable to read sysex track.\n");
  219. hr = DMUS_E_CANNOTREAD;
  220. break;
  221. }
  222. lSize -= sizeof(DMUS_IO_SYSEX_ITEM);
  223. pbSysExData = new BYTE[SysexItem.dwSysExLength];
  224. if( NULL == pbSysExData )
  225. {
  226. hr = E_OUTOFMEMORY;
  227. break;
  228. }
  229. if( FAILED( pIStream->Read( pbSysExData, SysexItem.dwSysExLength, NULL )))
  230. {
  231. Trace(1,"Error: Unable to read sysex track.\n");
  232. hr = DMUS_E_CANNOTREAD;
  233. break;
  234. }
  235. lSize -= SysexItem.dwSysExLength;
  236. sysexEvent.mtTime = SysexItem.mtTime;
  237. sysexEvent.dwPChannel = SysexItem.dwPChannel;
  238. sysexEvent.dwSysExLength = SysexItem.dwSysExLength;
  239. sysexEvent.pbSysExData = pbSysExData;
  240. SysExListItem* pNew = new SysExListItem;
  241. if (pNew)
  242. {
  243. if( FAILED( pNew->SetItem(sysexEvent)))
  244. {
  245. delete [] pbSysExData;
  246. hr = E_OUTOFMEMORY;
  247. break;
  248. }
  249. m_SysExEventList.AddTail(pNew);
  250. }
  251. else
  252. {
  253. delete [] pbSysExData;
  254. hr = E_OUTOFMEMORY;
  255. break;
  256. }
  257. }
  258. LeaveCriticalSection(&m_CrSec);
  259. return hr;
  260. }
  261. HRESULT CSysExTrack::Save( IStream* pIStream, BOOL fClearDirty )
  262. {
  263. return E_NOTIMPL;
  264. }
  265. HRESULT CSysExTrack::GetSizeMax( ULARGE_INTEGER FAR* pcbSize )
  266. {
  267. return E_NOTIMPL;
  268. }
  269. // IDirectMusicTrack
  270. HRESULT STDMETHODCALLTYPE CSysExTrack::IsParamSupported(
  271. /* [in] */ REFGUID rguid)
  272. {
  273. return E_NOTIMPL;
  274. }
  275. //////////////////////////////////////////////////////////////////////
  276. // IDirectMusicTrack::Init
  277. /*
  278. method HRESULT | IDirectMusicTrack | Init |
  279. When a track is first added to a Segment, it's Init() routine is called
  280. by that Segment.
  281. parm IDirectMusicSegment* | pSegment |
  282. [in] Pointer to the Segment to which this Track belongs.
  283. rvalue S_OK
  284. */
  285. HRESULT CSysExTrack::Init(
  286. /* [in] */ IDirectMusicSegment *pSegment)
  287. {
  288. return S_OK;
  289. }
  290. HRESULT CSysExTrack::InitPlay(
  291. /* [in] */ IDirectMusicSegmentState *pSegmentState,
  292. /* [in] */ IDirectMusicPerformance *pPerformance,
  293. /* [out] */ void **ppStateData,
  294. /* [in] */ DWORD dwTrackID,
  295. /* [in] */ DWORD dwFlags)
  296. {
  297. V_INAME(IDirectMusicTrack::InitPlay);
  298. V_PTRPTR_WRITE(ppStateData);
  299. V_INTERFACE(pSegmentState);
  300. V_INTERFACE(pPerformance);
  301. SysExStateData* pStateData;
  302. pStateData = new SysExStateData;
  303. if( NULL == pStateData )
  304. return E_OUTOFMEMORY;
  305. *ppStateData = pStateData;
  306. pStateData->dwVirtualTrackID = dwTrackID;
  307. pStateData->pPerformance = pPerformance; // weak reference, no addref.
  308. pStateData->pSegState = pSegmentState; // weak reference, no addref.
  309. pStateData->pCurrentSysEx = m_SysExEventList.GetHead();
  310. pStateData->dwValidate = m_dwValidate;
  311. return S_OK;
  312. }
  313. HRESULT CSysExTrack::EndPlay(
  314. /* [in] */ void *pStateData)
  315. {
  316. ASSERT( pStateData );
  317. if( pStateData )
  318. {
  319. V_INAME(IDirectMusicTrack::EndPlay);
  320. V_BUFPTR_WRITE(pStateData, sizeof(SysExStateData));
  321. SysExStateData* pSD = (SysExStateData*)pStateData;
  322. delete pSD;
  323. }
  324. return S_OK;
  325. }
  326. STDMETHODIMP CSysExTrack::PlayEx(void* pStateData,REFERENCE_TIME rtStart,
  327. REFERENCE_TIME rtEnd,REFERENCE_TIME rtOffset,
  328. DWORD dwFlags,IDirectMusicPerformance* pPerf,
  329. IDirectMusicSegmentState* pSegSt,DWORD dwVirtualID)
  330. {
  331. V_INAME(IDirectMusicTrack::PlayEx);
  332. V_BUFPTR_WRITE( pStateData, sizeof(SysExStateData));
  333. V_INTERFACE(pPerf);
  334. V_INTERFACE(pSegSt);
  335. HRESULT hr;
  336. EnterCriticalSection(&m_CrSec);
  337. if (dwFlags & DMUS_TRACKF_CLOCK)
  338. {
  339. // Convert all reference times to millisecond times. Then, just use same MUSIC_TIME
  340. // variables.
  341. hr = Play(pStateData,(MUSIC_TIME)(rtStart / REF_PER_MIL),(MUSIC_TIME)(rtEnd / REF_PER_MIL),
  342. (MUSIC_TIME)(rtOffset / REF_PER_MIL),rtOffset,dwFlags,pPerf,pSegSt,dwVirtualID,TRUE);
  343. }
  344. else
  345. {
  346. hr = Play(pStateData,(MUSIC_TIME)rtStart,(MUSIC_TIME)rtEnd,
  347. (MUSIC_TIME)rtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE);
  348. }
  349. LeaveCriticalSection(&m_CrSec);
  350. return hr;
  351. }
  352. /*
  353. method HRESULT | CSysExTrack | Play |
  354. Play method.
  355. rvalue S_FALSE | If there has been no stream loaded into the Track.
  356. rvalue S_OK
  357. */
  358. HRESULT CSysExTrack::Play(
  359. /* [in] */ void *pStateData,
  360. /* [in] */ MUSIC_TIME mtStart,
  361. /* [in] */ MUSIC_TIME mtEnd,
  362. /* [in] */ MUSIC_TIME mtOffset,
  363. DWORD dwFlags,
  364. IDirectMusicPerformance* pPerf,
  365. IDirectMusicSegmentState* pSegSt,
  366. DWORD dwVirtualID
  367. )
  368. {
  369. V_INAME(IDirectMusicTrack::Play);
  370. V_BUFPTR_WRITE( pStateData, sizeof(SysExStateData));
  371. V_INTERFACE(pPerf);
  372. V_INTERFACE(pSegSt);
  373. EnterCriticalSection(&m_CrSec);
  374. HRESULT hr = Play(pStateData,mtStart,mtEnd,mtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE);
  375. LeaveCriticalSection(&m_CrSec);
  376. return hr;
  377. }
  378. /* The Play method handles both music time and clock time versions, as determined by
  379. fClockTime. If running in clock time, rtOffset is used to identify the start time
  380. of the segment. Otherwise, mtOffset. The mtStart and mtEnd parameters are in MUSIC_TIME units
  381. or milliseconds, depending on which mode.
  382. */
  383. HRESULT CSysExTrack::Play(
  384. void *pStateData,
  385. MUSIC_TIME mtStart,
  386. MUSIC_TIME mtEnd,
  387. MUSIC_TIME mtOffset,
  388. REFERENCE_TIME rtOffset,
  389. DWORD dwFlags,
  390. IDirectMusicPerformance* pPerf,
  391. IDirectMusicSegmentState* pSegSt,
  392. DWORD dwVirtualID,
  393. BOOL fClockTime)
  394. {
  395. if (dwFlags & DMUS_TRACKF_PLAY_OFF)
  396. {
  397. return S_OK;
  398. }
  399. IDirectMusicGraph* pGraph = NULL;
  400. DMUS_SYSEX_PMSG* pSysEx;
  401. SysExStateData* pSD = (SysExStateData*)pStateData;
  402. HRESULT hr = S_OK;
  403. BOOL fSeek = (dwFlags & DMUS_TRACKF_SEEK) ? TRUE : FALSE;
  404. // if mtStart is 0 and dwFlags contains DMUS_TRACKF_START, we want to be sure to
  405. // send out any negative time events. So, we'll set mtStart to -768.
  406. if( (mtStart == 0) && ( dwFlags & DMUS_TRACKF_START ))
  407. {
  408. mtStart = -768;
  409. }
  410. // if pSD->pCurrentSysEx is NULL, and we're in a normal Play call (dwFlags is 0)
  411. // this means that we either have no events, or we got to the end of the event
  412. // list previously. So, it's safe to just return.
  413. if( (pSD->pCurrentSysEx == NULL) && (dwFlags == 0) )
  414. {
  415. return S_FALSE;
  416. }
  417. if( pSD->dwValidate != m_dwValidate )
  418. {
  419. pSD->dwValidate = m_dwValidate;
  420. pSD->pCurrentSysEx = NULL;
  421. }
  422. if( NULL == m_SysExEventList.GetHead() )
  423. {
  424. return DMUS_S_END;
  425. }
  426. // if the previous end time isn't the same as the current start time,
  427. // we need to seek to the right position.
  428. if( fSeek || ( pSD->mtPrevEnd != mtStart ))
  429. {
  430. Seek( pStateData, mtStart );
  431. }
  432. else if( NULL == pSD->pCurrentSysEx )
  433. {
  434. pSD->pCurrentSysEx = m_SysExEventList.GetHead();
  435. }
  436. pSD->mtPrevEnd = mtEnd;
  437. if( FAILED( pSD->pSegState->QueryInterface( IID_IDirectMusicGraph,
  438. (void**)&pGraph )))
  439. {
  440. pGraph = NULL;
  441. }
  442. for( ; pSD->pCurrentSysEx; pSD->pCurrentSysEx = pSD->pCurrentSysEx->GetNext() )
  443. {
  444. FullSysexEvent* pItem = pSD->pCurrentSysEx->m_pItem;
  445. if( NULL == pItem )
  446. {
  447. continue;
  448. }
  449. if( pItem->mtTime >= mtEnd )
  450. {
  451. // this time is in the future. Return now to retain the same
  452. // seek pointers for next time.
  453. hr = S_OK;
  454. break;
  455. }
  456. if( (pItem->mtTime < mtStart) && !fSeek )
  457. {
  458. break;
  459. }
  460. // allocate a DMUS_SYSEX_PMSG of the approriate size and read
  461. // the sysex data into it
  462. if( SUCCEEDED( hr = pSD->pPerformance->AllocPMsg(
  463. sizeof(DMUS_SYSEX_PMSG) + pItem->dwSysExLength, (DMUS_PMSG**)&pSysEx ) ) )
  464. {
  465. memcpy( pSysEx->abData, pItem->pbSysExData, pItem->dwSysExLength );
  466. if (fClockTime)
  467. {
  468. pSysEx->rtTime = (pItem->mtTime * REF_PER_MIL) + rtOffset;
  469. pSysEx->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
  470. }
  471. else
  472. {
  473. pSysEx->mtTime = pItem->mtTime + mtOffset;
  474. pSysEx->dwFlags = DMUS_PMSGF_MUSICTIME;
  475. }
  476. pSysEx->dwLen = pItem->dwSysExLength;
  477. pSysEx->dwPChannel = 0;
  478. pSysEx->dwVirtualTrackID = pSD->dwVirtualTrackID;
  479. pSysEx->dwType = DMUS_PMSGT_SYSEX;
  480. pSysEx->dwGroupID = 0xffffffff;
  481. if( pGraph )
  482. {
  483. pGraph->StampPMsg( (DMUS_PMSG*)pSysEx );
  484. }
  485. if(FAILED(pSD->pPerformance->SendPMsg( (DMUS_PMSG*)pSysEx )))
  486. {
  487. pSD->pPerformance->FreePMsg( (DMUS_PMSG*)pSysEx );
  488. }
  489. }
  490. else
  491. {
  492. hr = DMUS_S_END;
  493. break;
  494. }
  495. }
  496. if( pGraph )
  497. {
  498. pGraph->Release();
  499. }
  500. return hr;
  501. }
  502. HRESULT CSysExTrack::Seek(
  503. /* [in] */ void *pStateData,
  504. /* [in] */ MUSIC_TIME mtTime)
  505. {
  506. SysExStateData* pSD = (SysExStateData*)pStateData;
  507. if( NULL == m_SysExEventList.GetHead() )
  508. {
  509. return S_FALSE;
  510. }
  511. if( NULL == pSD->pCurrentSysEx )
  512. {
  513. pSD->pCurrentSysEx = m_SysExEventList.GetHead();
  514. }
  515. // if the current event's time is on or past mtTime, we need to rewind to the beginning
  516. FullSysexEvent* pItem = pSD->pCurrentSysEx->m_pItem;
  517. if( pItem->mtTime >= mtTime )
  518. {
  519. pSD->pCurrentSysEx = m_SysExEventList.GetHead();
  520. }
  521. // now start seeking until we find an event with time on or past mtTime
  522. for( ; pSD->pCurrentSysEx; pSD->pCurrentSysEx = pSD->pCurrentSysEx->GetNext() )
  523. {
  524. pItem = pSD->pCurrentSysEx->m_pItem;
  525. if( pItem->mtTime >= mtTime )
  526. {
  527. break;
  528. }
  529. }
  530. return S_OK;
  531. }
  532. STDMETHODIMP CSysExTrack::GetParam(
  533. REFGUID rguid,
  534. MUSIC_TIME mtTime,
  535. MUSIC_TIME* pmtNext,
  536. void *pData)
  537. {
  538. return E_NOTIMPL;
  539. }
  540. STDMETHODIMP CSysExTrack::SetParam(
  541. REFGUID rguid,
  542. MUSIC_TIME mtTime,
  543. void *pData)
  544. {
  545. return E_NOTIMPL;
  546. }
  547. STDMETHODIMP CSysExTrack::GetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime,
  548. REFERENCE_TIME* prtNext,void* pParam,void * pStateData, DWORD dwFlags)
  549. {
  550. return E_NOTIMPL;
  551. }
  552. STDMETHODIMP CSysExTrack::SetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime,
  553. void* pParam, void * pStateData, DWORD dwFlags)
  554. {
  555. return E_NOTIMPL;
  556. }
  557. HRESULT STDMETHODCALLTYPE CSysExTrack::AddNotificationType(
  558. /* [in] */ REFGUID rguidNotification)
  559. {
  560. return E_NOTIMPL;
  561. }
  562. HRESULT STDMETHODCALLTYPE CSysExTrack::RemoveNotificationType(
  563. /* [in] */ REFGUID rguidNotification)
  564. {
  565. return E_NOTIMPL;
  566. }
  567. HRESULT STDMETHODCALLTYPE CSysExTrack::Clone(
  568. MUSIC_TIME mtStart,
  569. MUSIC_TIME mtEnd,
  570. IDirectMusicTrack** ppTrack)
  571. {
  572. V_INAME(IDirectMusicTrack::Clone);
  573. V_PTRPTR_WRITE(ppTrack);
  574. HRESULT hr = S_OK;
  575. if((mtStart < 0 ) || (mtStart > mtEnd))
  576. {
  577. Trace(1,"Error: Unable to clone sysex track, invalid start parameter.\n",mtStart);
  578. return E_INVALIDARG;
  579. }
  580. EnterCriticalSection(&m_CrSec);
  581. CSysExTrack *pDM;
  582. try
  583. {
  584. pDM = new CSysExTrack(*this, mtStart, mtEnd);
  585. }
  586. catch( ... )
  587. {
  588. pDM = NULL;
  589. }
  590. LeaveCriticalSection(&m_CrSec);
  591. if (pDM == NULL) {
  592. return E_OUTOFMEMORY;
  593. }
  594. hr = pDM->QueryInterface(IID_IDirectMusicTrack, (void**)ppTrack);
  595. pDM->Release();
  596. return hr;
  597. }
  598. STDMETHODIMP CSysExTrack::Compose(
  599. IUnknown* pContext,
  600. DWORD dwTrackGroup,
  601. IDirectMusicTrack** ppResultTrack)
  602. {
  603. return E_NOTIMPL;
  604. }
  605. STDMETHODIMP CSysExTrack::Join(
  606. IDirectMusicTrack* pNewTrack,
  607. MUSIC_TIME mtJoin,
  608. IUnknown* pContext,
  609. DWORD dwTrackGroup,
  610. IDirectMusicTrack** ppResultTrack)
  611. {
  612. return E_NOTIMPL;
  613. }