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.

1638 lines
57 KiB

  1. // Copyright (c) 1998-2001 Microsoft Corporation
  2. // dmsstobj.cpp : Implementation of CSegState
  3. #include "dmime.h"
  4. #include "DMSStObj.h"
  5. #include "dmsegobj.h"
  6. #include "song.h"
  7. #include "dmgraph.h"
  8. #include "dmperf.h"
  9. #include "dmusici.h"
  10. #include "..\shared\Validate.h"
  11. #include "debug.h"
  12. #include "dmscriptautguids.h"
  13. #include "paramtrk.h"
  14. #define ASSERT assert
  15. CSegState::CSegState()
  16. {
  17. InitializeCriticalSection(&m_CriticalSection);
  18. InterlockedIncrement(&g_cComponent);
  19. m_fDelayShutDown = false;
  20. m_fInPlay = false;
  21. m_cRef = 1;
  22. m_dwPlayTrackFlags = DMUS_TRACKF_START | DMUS_TRACKF_SEEK;
  23. m_dwFirstTrackID = 0;
  24. m_dwLastTrackID = 0;
  25. m_mtEndTime = 0;
  26. m_mtAbortTime = 0;
  27. m_mtOffset = 0;
  28. m_rtOffset = 0;
  29. m_rtEndTime = 0;
  30. m_mtStartPoint = 0;
  31. m_rtStartPoint = 0;
  32. m_mtSeek = 0;
  33. m_rtSeek = 0;
  34. m_rtFirstLoopStart = 0;
  35. m_rtCurLoopStart = 0;
  36. m_rtCurLoopEnd = 0;
  37. m_mtLength = 0;
  38. m_rtLength = 0;
  39. m_mtLoopStart = 0;
  40. m_mtLoopEnd = 0;
  41. m_dwRepeatsLeft = 0;
  42. m_dwRepeats = 0;
  43. m_dwVersion = 0; // Init to 6.1 behavior.
  44. m_fPrepped = FALSE;
  45. m_fCanStop = TRUE;
  46. m_rtGivenStart = -1;
  47. m_mtResolvedStart = -1;
  48. m_mtLastPlayed = 0;
  49. m_rtLastPlayed = 0;
  50. m_mtStopTime = 0;
  51. m_dwPlaySegFlags = 0;
  52. m_dwSegFlags = 0;
  53. m_fStartedPlay = FALSE;
  54. m_pUnkDispatch = NULL;
  55. m_pSegment = NULL;
  56. m_pPerformance = NULL;
  57. m_pAudioPath = NULL;
  58. m_pGraph = NULL;
  59. m_fSongMode = FALSE;
  60. m_pSongSegState = NULL;
  61. TraceI(2, "SegmentState %lx created\n", this );
  62. }
  63. CSegState::~CSegState()
  64. {
  65. if (m_pUnkDispatch)
  66. m_pUnkDispatch->Release(); // free IDispatch implementation we may have borrowed
  67. if (m_pAudioPath) m_pAudioPath->Release();
  68. if (m_pGraph) m_pGraph->Release();
  69. if (m_pSongSegState) m_pSongSegState->Release();
  70. InterlockedDecrement(&g_cComponent);
  71. DeleteCriticalSection(&m_CriticalSection);
  72. TraceI(2, "SegmentState %lx destroyed with %ld releases outstanding\n", this, m_cRef );
  73. }
  74. STDMETHODIMP CSegState::QueryInterface(
  75. const IID &iid, // @parm Interface to query for
  76. void **ppv) // @parm The requested interface will be returned here
  77. {
  78. V_INAME(CSegState::QueryInterface);
  79. V_PTRPTR_WRITE(ppv);
  80. V_REFGUID(iid);
  81. *ppv = NULL;
  82. if (iid == IID_IUnknown || iid == IID_IDirectMusicSegmentState ||
  83. iid == IID_IDirectMusicSegmentState8)
  84. {
  85. *ppv = static_cast<IDirectMusicSegmentState*>(this);
  86. } else
  87. if (iid == IID_CSegState)
  88. {
  89. *ppv = static_cast<CSegState*>(this);
  90. } else
  91. if (iid == IID_IDirectMusicGraph)
  92. {
  93. *ppv = static_cast<IDirectMusicGraph*>(this);
  94. } else
  95. if (iid == IID_IDispatch)
  96. {
  97. // A helper scripting object implements IDispatch, which we expose from the
  98. // Performance object via COM aggregation.
  99. if (!m_pUnkDispatch)
  100. {
  101. // Create the helper object
  102. ::CoCreateInstance(
  103. CLSID_AutDirectMusicSegmentState,
  104. static_cast<IDirectMusicSegmentState*>(this),
  105. CLSCTX_INPROC_SERVER,
  106. IID_IUnknown,
  107. reinterpret_cast<void**>(&m_pUnkDispatch));
  108. }
  109. if (m_pUnkDispatch)
  110. {
  111. return m_pUnkDispatch->QueryInterface(IID_IDispatch, ppv);
  112. }
  113. }
  114. if (*ppv == NULL)
  115. {
  116. Trace(4,"Warning: Request to query unknown interface on SegmentState object\n");
  117. return E_NOINTERFACE;
  118. }
  119. reinterpret_cast<IUnknown*>(this)->AddRef();
  120. return S_OK;
  121. }
  122. STDMETHODIMP_(ULONG) CSegState::AddRef()
  123. {
  124. return InterlockedIncrement(&m_cRef);
  125. }
  126. STDMETHODIMP_(ULONG) CSegState::Release()
  127. {
  128. if (!InterlockedDecrement(&m_cRef))
  129. {
  130. m_cRef = 100; // artificial reference count to prevent reentrency due to COM aggregation
  131. delete this;
  132. return 0;
  133. }
  134. return m_cRef;
  135. }
  136. /*
  137. Private initialization function called by IDirectMusicSegment to set this
  138. state object's parent segment and performance. Addref's the parent segment
  139. but only retains a weak reference to the performance.
  140. */
  141. HRESULT CSegState::PrivateInit(
  142. CSegment *pParentSegment,
  143. CPerformance *pPerformance)
  144. {
  145. HRESULT hr = S_OK;
  146. ASSERT(pParentSegment);
  147. ASSERT(pPerformance);
  148. m_pSegment = pParentSegment;
  149. pParentSegment->AddRef();
  150. m_pPerformance = pPerformance; // retain only a weak reference
  151. m_rtLength = pParentSegment->m_rtLength;
  152. if (m_rtLength) // It's a ref time segment, so convert the length to music time
  153. {
  154. pParentSegment->ReferenceToMusicTime(m_rtLength, &m_mtLength);
  155. }
  156. else
  157. {
  158. m_mtLength = pParentSegment->m_mtLength;
  159. }
  160. m_mtStartPoint = pParentSegment->m_mtStart;
  161. pParentSegment->MusicToReferenceTime(m_mtStartPoint, &m_rtStartPoint);
  162. m_mtLoopStart = pParentSegment->m_mtLoopStart;
  163. m_mtLoopEnd = pParentSegment->m_mtLoopEnd;
  164. m_dwSegFlags = pParentSegment->m_dwSegFlags;
  165. m_dwRepeats = pParentSegment->m_dwRepeats;
  166. // Don't allow repeat count to overflow and cause mathematical errors.
  167. // Make it so it can't create a segment length larger than 0x3FFFFFFF,
  168. // which would last for 8 days at 120 bpm!
  169. if (m_dwRepeats)
  170. {
  171. if ((m_mtLoopEnd == 0) && (m_mtLoopStart == 0))
  172. {
  173. // This happens when loading waves and MIDI files.
  174. m_mtLoopEnd = m_mtLength;
  175. }
  176. // Make sure the loop is real.
  177. if (m_mtLoopEnd > m_mtLoopStart)
  178. {
  179. // Take the maximum length, subtract out the full length, then divide by the loop size.
  180. DWORD dwMax = (0x3FFFFFFF - m_mtLength) / (m_mtLoopEnd - m_mtLoopStart);
  181. // dwMax is the maximum number of loops that can be done without overflowing the time.
  182. if (m_dwRepeats > dwMax)
  183. {
  184. m_dwRepeats = dwMax;
  185. }
  186. }
  187. else
  188. {
  189. m_dwRepeats = 0;
  190. }
  191. }
  192. m_dwRepeatsLeft = m_dwRepeats;
  193. if( m_mtLoopEnd == 0 )
  194. {
  195. m_mtLoopEnd = m_mtLength;
  196. }
  197. if( m_mtStartPoint >= m_mtLoopEnd )
  198. {
  199. // in this case, we're not doing any looping.
  200. m_mtLoopEnd = m_mtLoopStart = 0;
  201. m_dwRepeats = m_dwRepeatsLeft = 0;
  202. }
  203. return hr;
  204. }
  205. HRESULT CSegState::InitRoute(IDirectMusicAudioPath *pAudioPath)
  206. {
  207. HRESULT hr = E_INVALIDARG;
  208. EnterCriticalSection(&m_CriticalSection);
  209. if (pAudioPath)
  210. {
  211. if (m_dwVersion < 8) m_dwVersion = 8;
  212. m_pAudioPath = (CAudioPath *) pAudioPath;
  213. pAudioPath->AddRef();
  214. hr = S_OK;
  215. }
  216. LeaveCriticalSection(&m_CriticalSection);
  217. return hr;
  218. }
  219. /*
  220. This is called from the performance when it wants to release a
  221. segmentstate. This ensures that the segstate is
  222. no longer valid once outside the Performance.
  223. */
  224. HRESULT CSegState::ShutDown(void)
  225. {
  226. if (this)
  227. {
  228. if (m_fInPlay)
  229. {
  230. m_fDelayShutDown = true;
  231. return S_OK;
  232. }
  233. EnterCriticalSection(&m_CriticalSection);
  234. m_TrackList.Clear();
  235. if( m_pSegment )
  236. {
  237. m_pSegment->Release();
  238. m_pSegment = NULL;
  239. }
  240. if( m_pAudioPath)
  241. {
  242. m_pAudioPath->Release();
  243. m_pAudioPath = NULL;
  244. }
  245. if (m_pSongSegState)
  246. {
  247. m_pSongSegState->Release();
  248. m_pSongSegState = NULL;
  249. }
  250. m_pPerformance = NULL;
  251. LeaveCriticalSection(&m_CriticalSection);
  252. if( int nCount = Release() )
  253. {
  254. TraceI( 2, "Warning! SegmentState %lx still referenced %d times after Performance has released it.\n", this, nCount );
  255. }
  256. return S_OK;
  257. }
  258. TraceI(0,"Attempting to delete a NULL SegmentState!\n");
  259. return E_FAIL;
  260. }
  261. /*
  262. Retrieve the internal track list. Used by IDirectMusicSegment.
  263. */
  264. HRESULT CSegState::GetTrackList(
  265. void** ppTrackList)
  266. {
  267. ASSERT(ppTrackList);
  268. *ppTrackList = (void*)&m_TrackList;
  269. return S_OK;
  270. }
  271. /*
  272. Computes the length of the segmentstate using the internal length, loop points,
  273. and repeat count. This is the length of the segstate that will actually play,
  274. not necessarily the length if it played from the beginning.
  275. */
  276. MUSIC_TIME CSegState::GetEndTime(MUSIC_TIME mtStartTime)
  277. {
  278. EnterCriticalSection(&m_CriticalSection);
  279. if (m_rtLength && m_pPerformance)
  280. {
  281. // If there is a reference time length, convert it into Music Time.
  282. // ALSO: convert m_mtLength and re-adjust loop points.
  283. MUSIC_TIME mtOffset = m_mtResolvedStart;
  284. REFERENCE_TIME rtOffset = 0;
  285. m_pPerformance->MusicToReferenceTime(mtOffset, &rtOffset);
  286. REFERENCE_TIME rtEndTime = (m_rtLength - m_rtStartPoint) + rtOffset; // Convert from length to actual end time.
  287. m_pPerformance->ReferenceToMusicTime(rtEndTime, &m_mtEndTime);
  288. MUSIC_TIME mtOldLength = m_mtLength;
  289. m_mtLength = m_mtEndTime - mtOffset + m_mtStartPoint;
  290. if (m_mtLoopEnd >= mtOldLength) // keep loop end equal to length
  291. {
  292. m_mtLoopEnd = m_mtLength;
  293. }
  294. if( m_mtLoopEnd > m_mtLength ) // shrink loop end to equal length
  295. {
  296. m_mtLoopEnd = m_mtLength;
  297. if( m_mtStartPoint >= m_mtLoopEnd )
  298. {
  299. // in this case, we're not doing any looping.
  300. m_mtLoopEnd = m_mtLoopStart = 0;
  301. m_dwRepeats = m_dwRepeatsLeft = 0;
  302. }
  303. }
  304. }
  305. LeaveCriticalSection(&m_CriticalSection);
  306. LONGLONG length;
  307. length = m_mtLength + ((m_mtLoopEnd - m_mtLoopStart) * m_dwRepeats);
  308. length -= m_mtStartPoint;
  309. length += mtStartTime;
  310. if(length > 0x7fffffff) length = 0x7fffffff;
  311. return (MUSIC_TIME)length;
  312. }
  313. /*
  314. Converts an absolute Performance time to the index into the SegmentState, using
  315. the SegmentState's offset, internal length, loop points, and repeat count.
  316. Also returns the offset and repeat count for that time.
  317. */
  318. HRESULT CSegState::ConvertToSegTime(
  319. MUSIC_TIME* pmtTime, MUSIC_TIME* pmtOffset, DWORD* pdwRepeat )
  320. {
  321. ASSERT( pmtTime );
  322. ASSERT( pmtOffset );
  323. ASSERT( pdwRepeat );
  324. MUSIC_TIME mtPos = *pmtTime - m_mtResolvedStart + m_mtStartPoint;
  325. MUSIC_TIME mtLoopLength = m_mtLoopEnd - m_mtLoopStart;
  326. DWORD dwRepeat = 0;
  327. DWORD mtOffset = m_mtResolvedStart - m_mtStartPoint;
  328. while( mtPos >= m_mtLoopEnd )
  329. {
  330. if( dwRepeat >= m_dwRepeats ) break;
  331. mtPos -= mtLoopLength;
  332. mtOffset += mtLoopLength;
  333. dwRepeat++;
  334. }
  335. *pmtTime = mtPos;
  336. *pmtOffset = mtOffset;
  337. *pdwRepeat = dwRepeat;
  338. if( (mtPos >= 0) && (mtPos < m_mtLength) )
  339. {
  340. return S_OK; // time is in range of the Segment
  341. }
  342. else
  343. {
  344. return S_FALSE; // time is out of range of the Segment
  345. }
  346. }
  347. void CSegState::GenerateNotification( DWORD dwNotification, MUSIC_TIME mtTime )
  348. {
  349. GUID guid;
  350. HRESULT hr;
  351. guid = GUID_NOTIFICATION_SEGMENT;
  352. hr = m_pSegment->CheckNotification( guid );
  353. if( S_FALSE != hr )
  354. {
  355. DMUS_NOTIFICATION_PMSG* pEvent = NULL;
  356. if( SUCCEEDED( m_pPerformance->AllocPMsg( sizeof(DMUS_NOTIFICATION_PMSG),
  357. (DMUS_PMSG**)&pEvent )))
  358. {
  359. pEvent->dwField1 = 0;
  360. pEvent->dwField2 = 0;
  361. pEvent->guidNotificationType = GUID_NOTIFICATION_SEGMENT;
  362. pEvent->dwType = DMUS_PMSGT_NOTIFICATION;
  363. pEvent->mtTime = mtTime;
  364. pEvent->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_ATTIME;
  365. pEvent->dwPChannel = 0;
  366. pEvent->dwNotificationOption = dwNotification;
  367. pEvent->dwGroupID = 0xffffffff;
  368. pEvent->punkUser = (IUnknown*)(IDirectMusicSegmentState*)this;
  369. AddRef();
  370. StampPMsg((DMUS_PMSG*)pEvent);
  371. if(FAILED(m_pPerformance->SendPMsg( (DMUS_PMSG*)pEvent )))
  372. {
  373. m_pPerformance->FreePMsg((DMUS_PMSG*) pEvent );
  374. }
  375. }
  376. }
  377. }
  378. /*
  379. Called to send the tools in the tool graph a dirty pmsg so they update any
  380. cached GetParam() info.
  381. */
  382. void CSegState::SendDirtyPMsg( MUSIC_TIME mtTime )
  383. {
  384. DMUS_PMSG* pEvent = NULL;
  385. if (m_pPerformance)
  386. {
  387. if( SUCCEEDED( m_pPerformance->AllocPMsg( sizeof(DMUS_PMSG),
  388. (DMUS_PMSG**)&pEvent )))
  389. {
  390. pEvent->mtTime = mtTime;
  391. pEvent->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE;
  392. pEvent->dwGroupID = 0xffffffff;
  393. pEvent->dwType = DMUS_PMSGT_DIRTY;
  394. StampPMsg((DMUS_PMSG*)pEvent);
  395. if( FAILED( m_pPerformance->SendPMsg( pEvent )))
  396. {
  397. m_pPerformance->FreePMsg( pEvent );
  398. }
  399. }
  400. }
  401. }
  402. /*
  403. Called when the SegState is stopped prematurely, so we can send a SEGABORT
  404. Notification.
  405. Also, flushes all events that were sent after the stop time.
  406. */
  407. HRESULT CSegState::AbortPlay( MUSIC_TIME mtTime, BOOL fLeaveNotesOn )
  408. {
  409. EnterCriticalSection(&m_CriticalSection);
  410. if (m_pPerformance)
  411. {
  412. if( m_mtLastPlayed > mtTime )
  413. {
  414. // If we've played past the abort time, we need to flush messages.
  415. // Note that if we were aborted by playing another segment that had
  416. // the DMUS_SEGF_NOINVALIDATE flag set, don't truncate notes
  417. // that are currently on.
  418. CTrack* pTrack;
  419. pTrack = m_TrackList.GetHead();
  420. while( pTrack )
  421. {
  422. m_pPerformance->FlushVirtualTrack( pTrack->m_dwVirtualID, mtTime, fLeaveNotesOn );
  423. pTrack = pTrack->GetNext();
  424. }
  425. m_mtLastPlayed = mtTime;
  426. }
  427. // Always fill in the updated value for lastplayed so the ShutDown or Done queue will flush this
  428. // at the right time.
  429. m_pPerformance->MusicToReferenceTime(mtTime,&m_rtLastPlayed);
  430. }
  431. LeaveCriticalSection(&m_CriticalSection);
  432. // Always generate an abort for a segment that has not started playing yet.
  433. if (m_fStartedPlay && (m_mtEndTime <= mtTime))
  434. {
  435. return S_FALSE; // Abort was too late to matter.
  436. }
  437. if (m_mtAbortTime) // Previous abort.
  438. {
  439. if (m_mtAbortTime <= mtTime) // Is this earlier?
  440. {
  441. return S_FALSE; // No, don't send abort message.
  442. }
  443. }
  444. m_mtAbortTime = mtTime;
  445. // Find all the parameter control tracks and invalidate any parameter envelopes
  446. // that need invalidation.
  447. CTrack* pTrack = m_TrackList.GetHead();
  448. while( pTrack )
  449. {
  450. if (pTrack->m_guidClassID == CLSID_DirectMusicParamControlTrack)
  451. {
  452. CParamControlTrack* pParamTrack = NULL;
  453. if (pTrack->m_pTrack &&
  454. SUCCEEDED(pTrack->m_pTrack->QueryInterface(IID_CParamControlTrack, (void**)&pParamTrack)))
  455. {
  456. pParamTrack->OnSegmentEnd(m_rtLastPlayed, pTrack->m_pTrackState);
  457. pParamTrack->Release();
  458. }
  459. }
  460. pTrack = pTrack->GetNext();
  461. }
  462. GenerateNotification( DMUS_NOTIFICATION_SEGABORT, mtTime );
  463. // if this is a primary or controlling segment, send a DMUS_PMSGT_DIRTY message
  464. if( !(m_dwPlaySegFlags & DMUS_SEGF_SECONDARY) || (m_dwPlaySegFlags & DMUS_SEGF_CONTROL) )
  465. {
  466. TraceI(4, "Send Dirty PMsg [4] %d (%d)\n", m_mtSeek, m_mtOffset + m_mtSeek);
  467. SendDirtyPMsg( m_mtOffset + m_mtSeek );
  468. }
  469. return S_OK;
  470. }
  471. /////////////////////////////////////////////////////////////////////////////
  472. // IDirectMusicSegmentState
  473. //////////////////////////////////////////////////////////////////////
  474. // IDirectMusicSegmentState::GetRepeats
  475. /*
  476. @method HRESULT | IDirectMusicSegmentState | GetRepeats |
  477. Returns the number of times the SegmentState is set to repeat. A value of zero indicates
  478. to play through only once (no repeats.) This value remains constant throughout the life
  479. of the SegmentState.
  480. @rvalue E_POINTER | if <p pdwRepeats> is NULL or invalid.
  481. @rvalue S_OK | Success.
  482. */
  483. HRESULT STDMETHODCALLTYPE CSegState::GetRepeats(
  484. DWORD *pdwRepeats) // @parm Returns the repeat count.
  485. {
  486. V_INAME(IDirectMusicSegmentState::GetRepeats);
  487. V_PTR_WRITE(pdwRepeats,DWORD);
  488. *pdwRepeats = m_dwRepeats;
  489. return S_OK;
  490. }
  491. ///////////////////////////////////////////////////////////////////////
  492. // IDirectMusicSegmentState::GetSegment
  493. /*
  494. @method HRESULT | IDirectMusicSegmentState | GetSegment |
  495. Returns a pointer to the Segment that owns this SegmentState.
  496. @rvalue E_POINTER | if ppSegment is NULL or invalid.
  497. @rvalue S_OK | Success.
  498. */
  499. HRESULT STDMETHODCALLTYPE CSegState::GetSegment(
  500. IDirectMusicSegment **ppSegment) // @parm The Segment interface pointer to this
  501. // SegmentState. Call Release() on this pointer when
  502. // through.
  503. {
  504. V_INAME(IDirectMusicSegmentState::GetSegment);
  505. V_PTRPTR_WRITE(ppSegment);
  506. *ppSegment = (IDirectMusicSegment *) m_pSegment;
  507. if( m_pSegment )
  508. {
  509. m_pSegment->AddRef();
  510. }
  511. else
  512. {
  513. Trace(1,"Error: Segmentstate doesn't have an associated segment.\n");
  514. return DMUS_E_NOT_FOUND;
  515. }
  516. return S_OK;
  517. }
  518. ///////////////////////////////////////////////////////////////////////
  519. // IDirectMusicSegmentState::Play
  520. /*
  521. method (INTERNAL) HRESULT | IDirectMusicSegmentState | Play |
  522. <om IDirectMusicSegmentState.Play> is called regularly by the Performance object,
  523. usually every 200 ms or so, at a time ahead of playback that is set by
  524. <om IDirectMusicPerformance.SetPerformTime>
  525. .
  526. parm MUSIC_TIME | mtAmount |
  527. [in] The length of time to play, starting at the current Seek time.
  528. The SegmentState updates its Seek time to be the current Seek time
  529. plus mtAmount. Therefore, the SegmentState should play from the current
  530. Seek time to Seek time plus mtAmount, not including the last clock.
  531. comm
  532. Play calls each Track's Play method in priority order, instructing the Track to
  533. create events from the current Seek time up to, but not including the current Seek
  534. time plus <p mtAmount.>
  535. Since the Segment started at the point designated by m_mtOffset (set by
  536. <im IDirectMusicSegmentState.SetOffset>
  537. m_mtOffset sets the starting offset to add to the times of all events.
  538. rvalue E_INVALIDARG | mtAmount <= 0
  539. rvalue S_OK | Success.
  540. */
  541. HRESULT STDMETHODCALLTYPE CSegState::Play(
  542. /* [in] */ MUSIC_TIME mtAmount, MUSIC_TIME* pmtPlayed )
  543. {
  544. return E_FAIL; // We don't want to support this publicly!
  545. }
  546. HRESULT CSegState::Play( MUSIC_TIME mtAmount )
  547. {
  548. CTrack* pCTrack;
  549. MUSIC_TIME mtMyAmount = mtAmount;
  550. REFERENCE_TIME rtMyAmount;
  551. HRESULT hr = DMUS_S_END;
  552. BOOL fUseClockTime = FALSE;
  553. if( mtAmount <= 0 )
  554. return E_INVALIDARG;
  555. EnterCriticalSection(&m_CriticalSection);
  556. if (m_fInPlay)
  557. {
  558. LeaveCriticalSection(&m_CriticalSection);
  559. return S_OK;
  560. }
  561. m_fInPlay = true;
  562. m_pPerformance->m_pGetParamSegmentState = (IDirectMusicSegmentState *) this;
  563. // if this is the first call to play, we need to send a SegStart notification.
  564. // We also need to check to see if we are supposed to start at the beginning,
  565. // or at an offset.
  566. if( m_dwPlayTrackFlags & DMUS_TRACKF_START )
  567. {
  568. // send a segment start notification
  569. GenerateNotification( DMUS_NOTIFICATION_SEGSTART, m_mtOffset );
  570. // if this is a primary or controlling segment, send a DMUS_PMSGT_DIRTY message
  571. if( !(m_dwPlaySegFlags & DMUS_SEGF_SECONDARY) || (m_dwPlaySegFlags & DMUS_SEGF_CONTROL) )
  572. {
  573. TraceI(4, "Send Dirty PMsg [1] %d (%d)\n", m_mtSeek, m_mtOffset + m_mtSeek);
  574. SendDirtyPMsg( m_mtOffset + m_mtSeek );
  575. }
  576. // set the current seek to the start point
  577. m_mtSeek = m_mtStartPoint;
  578. // convert current offset to ref time
  579. m_pPerformance->MusicToReferenceTime(m_mtOffset,&m_rtOffset);
  580. m_rtEndTime = m_rtOffset + m_rtLength;
  581. // subtract the start points from the offsets
  582. m_mtOffset -= m_mtStartPoint;
  583. m_rtOffset -= m_rtStartPoint;
  584. m_rtEndTime -= m_rtStartPoint;
  585. m_rtSeek = m_rtLastPlayed - m_rtOffset;
  586. m_rtFirstLoopStart = 0;
  587. }
  588. if (m_rtLength)
  589. {
  590. // If there is a reference time length, convert it into mtTime.
  591. // Because there's always the danger of a tempo change, we do this every
  592. // time. It doesn't require the tight precision that song time
  593. // requires, so that's okay.
  594. // ALSO: convert m_mtLength and re-adjust loop points. (RSW)
  595. m_pPerformance->ReferenceToMusicTime(m_rtEndTime, &m_mtEndTime);
  596. MUSIC_TIME mtOldLength = m_mtLength;
  597. m_mtLength = m_mtEndTime - m_mtOffset;
  598. if (m_mtLoopEnd >= mtOldLength) // keep loop end equal to length
  599. {
  600. m_mtLoopEnd = m_mtLength;
  601. }
  602. if( m_mtLoopEnd > m_mtLength )
  603. {
  604. m_mtLoopEnd = m_mtLength;
  605. if( m_mtStartPoint >= m_mtLoopEnd )
  606. {
  607. // in this case, we're not doing any looping.
  608. m_mtLoopEnd = m_mtLoopStart = 0;
  609. m_dwRepeats = m_dwRepeatsLeft = 0;
  610. }
  611. }
  612. //m_mtEndTime += (m_mtLoopEnd - m_mtLoopStart) * m_dwRepeats;
  613. fUseClockTime = TRUE;
  614. }
  615. // if we need to do a loop or the end is near, restrict mtMyAmount
  616. // ASSERT( m_mtLength ); // length is 0, this segment won't do anything
  617. if( m_dwRepeatsLeft )
  618. {
  619. if( mtMyAmount > m_mtLoopEnd - m_mtSeek )
  620. {
  621. mtMyAmount = m_mtLoopEnd - m_mtSeek;
  622. }
  623. }
  624. else
  625. {
  626. if (fUseClockTime)
  627. {
  628. if (mtMyAmount > (m_mtEndTime - (m_mtOffset + m_mtSeek)))
  629. {
  630. mtMyAmount = m_mtEndTime - (m_mtOffset + m_mtSeek);
  631. }
  632. }
  633. else if( mtMyAmount > m_mtLength - m_mtSeek )
  634. {
  635. mtMyAmount = m_mtLength - m_mtSeek;
  636. }
  637. }
  638. if (mtMyAmount <= 0)
  639. {
  640. hr = DMUS_S_END;
  641. }
  642. else
  643. {
  644. // check the primary segment queue for a segment that might begin
  645. // before mtMyAmount is up
  646. MUSIC_TIME mtNextPri;
  647. if (S_OK == m_pPerformance->GetPriSegTime( m_mtOffset + m_mtSeek, &mtNextPri ))
  648. {
  649. if( m_mtOffset + m_mtSeek + mtMyAmount > mtNextPri )
  650. {
  651. mtMyAmount = mtNextPri - m_mtOffset - m_mtSeek;
  652. }
  653. }
  654. TraceI(3, "SegState %ld Play from %ld to %ld at %ld = %ld - %ld\n", this, m_mtSeek, m_mtSeek + mtMyAmount, m_mtOffset, m_mtSeek + m_mtOffset, m_mtSeek + mtMyAmount + m_mtOffset );
  655. // find out if there's a control segment interrupting this period of time.
  656. MUSIC_TIME mtControlSeg;
  657. if( S_OK == m_pPerformance->GetControlSegTime( m_mtOffset + m_mtSeek, &mtControlSeg ))
  658. {
  659. if( m_mtOffset + m_mtSeek == mtControlSeg )
  660. {
  661. // we're at the beginning of a new control seg, so tell the tracks
  662. m_dwPlayTrackFlags |= DMUS_TRACKF_DIRTY;
  663. }
  664. else if( m_mtOffset + m_mtSeek + mtMyAmount > mtControlSeg )
  665. {
  666. mtMyAmount = mtControlSeg - m_mtOffset - m_mtSeek;
  667. }
  668. }
  669. // Now that mtMyAmount is calculated for how far to play in music time,
  670. // create the equivalent value in reference time.
  671. m_pPerformance->MusicToReferenceTime(m_mtLastPlayed + mtMyAmount,&rtMyAmount);
  672. rtMyAmount -= m_rtLastPlayed;
  673. pCTrack = m_TrackList.GetHead();
  674. while( pCTrack )
  675. {
  676. if( mtMyAmount )
  677. {
  678. m_pPerformance->m_fInTrackPlay = TRUE; // This causes the Pmsgs to be stamped with PRIV_FLAG_TRACK.
  679. ASSERT( pCTrack->m_pTrack );
  680. // If either notification or play are enabled, we need to call the play method and set the behavior
  681. // with the DMUS_TRACKF_NOTIFY_OFF and DMUS_TRACKF_PLAY_OFF flags.
  682. if (pCTrack->m_dwFlags & (DMUS_TRACKCONFIG_PLAY_ENABLED | DMUS_TRACKCONFIG_NOTIFICATION_ENABLED))
  683. {
  684. DWORD dwAdditionalFlags = 0;
  685. if (!(pCTrack->m_dwFlags & DMUS_TRACKCONFIG_NOTIFICATION_ENABLED))
  686. {
  687. dwAdditionalFlags = DMUS_TRACKF_NOTIFY_OFF;
  688. }
  689. if (!(pCTrack->m_dwFlags & DMUS_TRACKCONFIG_PLAY_ENABLED))
  690. {
  691. dwAdditionalFlags |= DMUS_TRACKF_PLAY_OFF;
  692. }
  693. // If the track was authored to generate new data on start or loop, let it know.
  694. if ( ((m_dwPlayTrackFlags & DMUS_TRACKF_START) && (pCTrack->m_dwFlags & DMUS_TRACKCONFIG_PLAY_COMPOSE)) ||
  695. ((m_dwPlayTrackFlags & DMUS_TRACKF_LOOP) && (pCTrack->m_dwFlags & DMUS_TRACKCONFIG_LOOP_COMPOSE)) )
  696. {
  697. dwAdditionalFlags |= DMUS_TRACKF_RECOMPOSE;
  698. }
  699. if (pCTrack->m_dwInternalFlags & CONTROL_PLAY_REFRESH)
  700. {
  701. dwAdditionalFlags |= DMUS_TRACKF_START;
  702. pCTrack->m_dwInternalFlags &= ~CONTROL_PLAY_REFRESH;
  703. }
  704. // Let performance know what the priority should be in ensuing GetParam() calls from the track.
  705. m_pPerformance->m_dwGetParamFlags = pCTrack->m_dwFlags;
  706. // If track has DX8 interface, use it.
  707. if (pCTrack->m_pTrack8)
  708. {
  709. // The track can call GetParam on the segment which locks the segment so
  710. // we have to lock the segment before calling PlayEx or we'll deadlock
  711. // with a thread that's calling PlayOneSegment which locks the segment
  712. // before playing the tracks.
  713. if (m_pSegment) {
  714. EnterCriticalSection(&m_pSegment->m_CriticalSection);
  715. }
  716. // If track plays in clock time, set time variables appropriately.
  717. if (pCTrack->m_dwFlags & DMUS_TRACKCONFIG_PLAY_CLOCKTIME)
  718. {
  719. if( ( S_OK == (pCTrack->m_pTrack8->PlayEx(pCTrack->m_pTrackState,
  720. m_rtSeek,m_rtSeek + rtMyAmount, m_rtOffset, m_dwPlayTrackFlags | dwAdditionalFlags | DMUS_TRACKF_CLOCK,
  721. m_pPerformance, this, pCTrack->m_dwVirtualID ))))
  722. {
  723. hr = S_OK; // if even one track isn't done playing,
  724. // keep going
  725. }
  726. else
  727. {
  728. pCTrack->m_bDone = TRUE;
  729. }
  730. }
  731. else
  732. {
  733. if( ( S_OK == (pCTrack->m_pTrack8->PlayEx(pCTrack->m_pTrackState,
  734. m_mtSeek,m_mtSeek + mtMyAmount, m_mtOffset, m_dwPlayTrackFlags | dwAdditionalFlags,
  735. m_pPerformance, this, pCTrack->m_dwVirtualID ))))
  736. {
  737. hr = S_OK; // if even one track isn't done playing,
  738. // keep going
  739. }
  740. else
  741. {
  742. pCTrack->m_bDone = TRUE;
  743. }
  744. }
  745. if (m_pSegment) {
  746. LeaveCriticalSection(&m_pSegment->m_CriticalSection);
  747. }
  748. }
  749. else
  750. {
  751. if( ( S_OK == ( pCTrack->m_pTrack->Play( pCTrack->m_pTrackState,
  752. m_mtSeek, m_mtSeek + mtMyAmount, m_mtOffset, m_dwPlayTrackFlags | dwAdditionalFlags,
  753. m_pPerformance, this, pCTrack->m_dwVirtualID ))))
  754. {
  755. hr = S_OK; // if even one track isn't done playing,
  756. // keep going
  757. }
  758. else
  759. {
  760. pCTrack->m_bDone = TRUE;
  761. }
  762. }
  763. }
  764. m_pPerformance->m_fInTrackPlay = FALSE;
  765. }
  766. pCTrack = pCTrack->GetNext();
  767. if( pCTrack == NULL )
  768. {
  769. // none of the play flags are persistent
  770. m_dwPlayTrackFlags = 0;
  771. m_mtLastPlayed += mtMyAmount; // increment play pointer
  772. m_rtLastPlayed += rtMyAmount; // same in ref time
  773. m_mtSeek += mtMyAmount; // increment seek pointer
  774. m_rtSeek += rtMyAmount;
  775. hr = S_OK;
  776. // If we're looping....
  777. // And if this is the first repeat
  778. if(m_dwRepeats > 0 && m_dwRepeats == m_dwRepeatsLeft)
  779. {
  780. // If we're playing the loop start remember it's reftime value
  781. if(m_mtSeek >= m_mtLoopStart && m_rtFirstLoopStart == 0)
  782. {
  783. m_pPerformance->MusicToReferenceTime(m_mtLoopStart + m_mtOffset + m_mtStartPoint, &m_rtFirstLoopStart);
  784. m_rtFirstLoopStart -= m_rtStartPoint;
  785. m_rtCurLoopStart = m_rtFirstLoopStart;
  786. }
  787. }
  788. // take into account repeats if necessary
  789. if( m_mtSeek >= m_mtLoopEnd )
  790. {
  791. // Remember the current loop end
  792. m_pPerformance->MusicToReferenceTime(m_mtLoopEnd + m_mtOffset + m_mtStartPoint, &m_rtCurLoopEnd);
  793. m_rtCurLoopEnd -= m_rtStartPoint;
  794. if(m_dwRepeatsLeft)
  795. {
  796. m_dwPlayTrackFlags |= DMUS_TRACKF_LOOP | DMUS_TRACKF_SEEK;
  797. m_dwRepeatsLeft--;
  798. pCTrack = m_TrackList.GetHead();
  799. while( pCTrack )
  800. {
  801. pCTrack->m_bDone = FALSE;
  802. pCTrack = pCTrack->GetNext();
  803. }
  804. m_mtSeek = m_mtLoopStart;
  805. m_mtOffset += ( m_mtLoopEnd - m_mtLoopStart);
  806. m_rtOffset += (m_rtCurLoopEnd - m_rtCurLoopStart);
  807. m_rtFirstLoopStart += (m_rtCurLoopEnd - m_rtCurLoopStart);
  808. m_rtSeek = m_rtFirstLoopStart - m_rtOffset;
  809. m_rtEndTime += (m_rtCurLoopEnd - m_rtCurLoopStart);
  810. m_rtCurLoopStart = m_rtCurLoopEnd;
  811. if( mtMyAmount < mtAmount )
  812. {
  813. pCTrack = m_TrackList.GetHead(); // cause outer while loop to start over
  814. mtMyAmount = mtAmount - mtMyAmount;
  815. mtAmount = mtMyAmount;
  816. // if we need to do a loop or the end is near, restrict mtMyAmount
  817. if( m_dwRepeatsLeft )
  818. {
  819. if( mtMyAmount > m_mtLoopEnd - m_mtSeek )
  820. {
  821. mtMyAmount = m_mtLoopEnd - m_mtSeek;
  822. }
  823. }
  824. else
  825. {
  826. if (fUseClockTime)
  827. {
  828. if (mtMyAmount > (m_mtEndTime - (m_mtOffset + m_mtSeek)))
  829. {
  830. mtMyAmount = m_mtEndTime - (m_mtOffset + m_mtSeek);
  831. }
  832. }
  833. else if( mtMyAmount > m_mtLength - m_mtSeek )
  834. {
  835. mtMyAmount = m_mtLength - m_mtSeek;
  836. }
  837. }
  838. }
  839. // send a segment looped notification
  840. GenerateNotification( DMUS_NOTIFICATION_SEGLOOP, m_mtOffset + m_mtSeek );
  841. // find out if there's a control segment interrupting this period of time
  842. if( S_OK == m_pPerformance->GetControlSegTime( m_mtOffset + m_mtSeek, &mtControlSeg ))
  843. {
  844. if( m_mtOffset + m_mtSeek == mtControlSeg )
  845. {
  846. // we're at the beginning of a new control seg, so tell the tracks
  847. m_dwPlayTrackFlags |= DMUS_TRACKF_DIRTY;
  848. }
  849. else if( m_mtOffset + m_mtSeek + mtMyAmount < mtControlSeg )
  850. {
  851. mtMyAmount = mtControlSeg - m_mtOffset - m_mtSeek;
  852. }
  853. }
  854. m_pPerformance->MusicToReferenceTime(m_mtLastPlayed + mtMyAmount,&rtMyAmount);
  855. rtMyAmount -= m_rtLastPlayed;
  856. }
  857. else if( m_mtSeek == m_mtLength )
  858. {
  859. // no more repeats.
  860. hr = DMUS_S_END;
  861. }
  862. }
  863. }
  864. }
  865. }
  866. if (hr == DMUS_S_END)
  867. {
  868. // send a segment end notification
  869. GenerateNotification( DMUS_NOTIFICATION_SEGEND, m_mtOffset + m_mtSeek );
  870. // also queue the almost ended for now
  871. MUSIC_TIME mtNow;
  872. m_pPerformance->GetTime( NULL, &mtNow );
  873. GenerateNotification( DMUS_NOTIFICATION_SEGALMOSTEND, mtNow );
  874. // if this is a primary or controlling segment, send a DMUS_PMSGT_DIRTY message
  875. if( !(m_dwPlaySegFlags & DMUS_SEGF_SECONDARY) || (m_dwPlaySegFlags & DMUS_SEGF_CONTROL) )
  876. {
  877. TraceI(4, "Send Dirty PMsg [2] %d (%d)\n", m_mtSeek, m_mtOffset + m_mtSeek);
  878. SendDirtyPMsg( m_mtOffset + m_mtSeek );
  879. }
  880. // If this is part of a song, we need to queue the next segment.
  881. if (m_fSongMode)
  882. {
  883. if (m_pSegment)
  884. {
  885. CSong *pSong = m_pSegment->m_pSong;
  886. if (pSong)
  887. {
  888. // Get the next segment from the song.
  889. CSegment *pSegment;
  890. if (S_OK == pSong->GetPlaySegment(m_pSegment->m_dwNextPlayID,&pSegment))
  891. {
  892. // Now, play it.
  893. // Unless DMUS_SEGF_USE_AUDIOPATH is set, play it on the same audiopath.
  894. // And, make sure that it plays at the same level (control, secondary, or primary.)
  895. CSegState *pCSegState = NULL;
  896. CAudioPath *pPath = m_pAudioPath;
  897. CAudioPath *pInternalPath = NULL;
  898. DWORD dwFlags = m_dwPlaySegFlags & (DMUS_SEGF_CONTROL | DMUS_SEGF_SECONDARY);
  899. dwFlags &= ~DMUS_SEGF_REFTIME;
  900. if (dwFlags & DMUS_SEGF_USE_AUDIOPATH)
  901. {
  902. IUnknown *pConfig;
  903. if (SUCCEEDED(pSegment->GetAudioPathConfig(&pConfig)))
  904. {
  905. IDirectMusicAudioPath *pNewPath;
  906. if (SUCCEEDED(m_pPerformance->CreateAudioPath(pConfig,TRUE,&pNewPath)))
  907. {
  908. // Now, get the CAudioPath structure.
  909. pConfig->QueryInterface(IID_CAudioPath,(void **) &pInternalPath);
  910. pPath = pInternalPath;
  911. }
  912. pConfig->Release();
  913. }
  914. }
  915. if (SUCCEEDED(m_pPerformance->PlayOneSegment((CSegment *)pSegment,dwFlags,m_mtEndTime,&pCSegState,pPath)))
  916. {
  917. if (m_pSongSegState)
  918. {
  919. // This is not the first, so transfer the segstate pointer.
  920. pCSegState->m_pSongSegState = m_pSongSegState;
  921. m_pSongSegState = NULL;
  922. }
  923. else
  924. {
  925. // This is the first, so have the next segstate point to this.
  926. pCSegState->m_pSongSegState = this;
  927. AddRef();
  928. }
  929. pCSegState->m_fSongMode = TRUE;
  930. pCSegState->Release();
  931. }
  932. if (pInternalPath)
  933. {
  934. pInternalPath->Release();
  935. }
  936. pSegment->Release();
  937. }
  938. }
  939. }
  940. }
  941. }
  942. m_dwPlayTrackFlags &= ~DMUS_TRACKF_DIRTY;
  943. m_pPerformance->m_dwGetParamFlags = 0;
  944. m_pPerformance->m_pGetParamSegmentState = NULL;
  945. m_fInPlay = false;
  946. if (m_fDelayShutDown)
  947. {
  948. Shutdown();
  949. m_fDelayShutDown = false;
  950. }
  951. LeaveCriticalSection(&m_CriticalSection);
  952. return hr;
  953. }
  954. CTrack * CSegState::GetTrackByParam( CTrack * pCTrack,
  955. REFGUID rguidType,DWORD dwGroupBits,DWORD dwIndex)
  956. {
  957. // If the caller was already part way through the list, it passes the current
  958. // track. Otherwise, NULL to indicate start at the top.
  959. if (pCTrack)
  960. {
  961. pCTrack = pCTrack->GetNext();
  962. }
  963. else
  964. {
  965. pCTrack = m_TrackList.GetHead();
  966. }
  967. while( pCTrack )
  968. {
  969. ASSERT(pCTrack->m_pTrack);
  970. if( (pCTrack->m_dwGroupBits & dwGroupBits ) &&
  971. (pCTrack->m_dwFlags & DMUS_TRACKCONFIG_CONTROL_ENABLED))
  972. {
  973. if( (GUID_NULL == rguidType) || (pCTrack->m_pTrack->IsParamSupported( rguidType ) == S_OK ))
  974. {
  975. if( 0 == dwIndex )
  976. {
  977. return pCTrack;
  978. }
  979. dwIndex--;
  980. }
  981. }
  982. pCTrack = pCTrack->GetNext();
  983. }
  984. return NULL;
  985. }
  986. /* GetParam() is called by the performance in response to a GetParam() call
  987. on the performance. This needs the performance pointer so it can handle
  988. clock time to music time conversion and back, in case the source track is a
  989. clock time track.
  990. */
  991. HRESULT CSegState::GetParam(
  992. CPerformance *pPerf,
  993. REFGUID rguidType,
  994. DWORD dwGroupBits,
  995. DWORD dwIndex,
  996. MUSIC_TIME mtTime,
  997. MUSIC_TIME* pmtNext,
  998. void* pParam)
  999. {
  1000. HRESULT hr = DMUS_E_TRACK_NOT_FOUND;
  1001. BOOL fMultipleTry = FALSE;
  1002. if (dwIndex == DMUS_SEG_ANYTRACK)
  1003. {
  1004. dwIndex = 0;
  1005. // Even though DX7 didn't support this, this is always safe because an index this high could never happen.
  1006. fMultipleTry = TRUE;
  1007. }
  1008. CTrack * pTrack = GetTrackByParam( NULL, rguidType, dwGroupBits, dwIndex);
  1009. while (pTrack)
  1010. {
  1011. if (pTrack->m_pTrack8)
  1012. {
  1013. if (pTrack->m_dwFlags & DMUS_TRACKCONFIG_PLAY_CLOCKTIME)
  1014. {
  1015. REFERENCE_TIME rtTime, rtNext;
  1016. // Convert mtTime into reference time units:
  1017. pPerf->MusicToReferenceTime(m_mtOffset + mtTime,&rtTime);
  1018. rtTime -= m_rtOffset;
  1019. hr = pTrack->m_pTrack8->GetParamEx( rguidType, rtTime, &rtNext,
  1020. pParam, pTrack->m_pTrackState, DMUS_TRACK_PARAMF_CLOCK );
  1021. if (pmtNext)
  1022. {
  1023. if (rtNext == 0) *pmtNext = 0;
  1024. else
  1025. {
  1026. rtNext += m_rtOffset;
  1027. pPerf->ReferenceToMusicTime(rtNext,pmtNext);
  1028. *pmtNext -= m_mtOffset;
  1029. }
  1030. }
  1031. }
  1032. else
  1033. {
  1034. REFERENCE_TIME rtNext, *prtNext;
  1035. // We need to store the next time in a 64 bit pointer. But, don't
  1036. // make 'em fill it in unless the caller requested it.
  1037. if (pmtNext)
  1038. {
  1039. prtNext = &rtNext;
  1040. }
  1041. else
  1042. {
  1043. prtNext = NULL;
  1044. }
  1045. hr = pTrack->m_pTrack8->GetParamEx( rguidType, mtTime, prtNext, pParam,
  1046. pTrack->m_pTrackState, 0 );
  1047. if (pmtNext)
  1048. {
  1049. *pmtNext = (MUSIC_TIME) rtNext;
  1050. }
  1051. }
  1052. }
  1053. else
  1054. {
  1055. // This is a pre DX8 track...
  1056. hr = pTrack->m_pTrack->GetParam( rguidType, mtTime, pmtNext, pParam );
  1057. }
  1058. if (SUCCEEDED(hr))
  1059. {
  1060. if( pmtNext )
  1061. {
  1062. if(( *pmtNext == 0 ) || (*pmtNext > (m_mtLength - mtTime)))
  1063. {
  1064. // If no next was found OR it's greater than the end of the segment, set
  1065. // it to the end of the segment.
  1066. *pmtNext = m_mtLength - mtTime;
  1067. }
  1068. }
  1069. pTrack = NULL;
  1070. }
  1071. // If nothing was found and dwIndex was DMUS_SEG_ANYTRACK, try again...
  1072. else if (fMultipleTry && (hr == DMUS_E_NOT_FOUND))
  1073. {
  1074. pTrack = GetTrackByParam( pTrack, rguidType, dwGroupBits, 0);
  1075. }
  1076. else
  1077. {
  1078. pTrack = NULL;
  1079. }
  1080. }
  1081. #ifdef DBG
  1082. if (hr == DMUS_E_TRACK_NOT_FOUND)
  1083. {
  1084. Trace(4,"Warning: Segmentstate::GetParam failed, unable to find a track that supports the requested param.\n");
  1085. }
  1086. #endif
  1087. return hr;
  1088. }
  1089. CTrack *CSegState::GetTrack(
  1090. REFCLSID rType,
  1091. DWORD dwGroupBits,
  1092. DWORD dwIndex)
  1093. {
  1094. CTrack* pCTrack;
  1095. pCTrack = m_TrackList.GetHead();
  1096. while( pCTrack )
  1097. {
  1098. ASSERT(pCTrack->m_pTrack);
  1099. if( pCTrack->m_dwGroupBits & dwGroupBits )
  1100. {
  1101. if( (GUID_All_Objects == rType) || (pCTrack->m_guidClassID == rType))
  1102. {
  1103. if( 0 == dwIndex )
  1104. {
  1105. break;
  1106. }
  1107. dwIndex--;
  1108. }
  1109. }
  1110. pCTrack = pCTrack->GetNext();
  1111. }
  1112. return pCTrack;
  1113. }
  1114. STDMETHODIMP CSegState::SetTrackConfig(REFGUID rguidTrackClassID,
  1115. DWORD dwGroup, DWORD dwIndex,
  1116. DWORD dwFlagsOn, DWORD dwFlagsOff)
  1117. {
  1118. V_INAME(IDirectMusicSegment::SetTrackConfig);
  1119. V_REFGUID(rguidTrackClassID);
  1120. if (rguidTrackClassID == GUID_NULL)
  1121. {
  1122. return E_INVALIDARG;
  1123. }
  1124. HRESULT hr = DMUS_E_TRACK_NOT_FOUND;
  1125. CTrack* pCTrack;
  1126. DWORD dwCounter = dwIndex;
  1127. DWORD dwMax = dwIndex;
  1128. if (dwIndex == DMUS_SEG_ALLTRACKS)
  1129. {
  1130. dwCounter = 0;
  1131. dwMax = DMUS_SEG_ALLTRACKS;
  1132. }
  1133. EnterCriticalSection(&m_CriticalSection);
  1134. while (pCTrack = GetTrack(rguidTrackClassID,dwGroup,dwIndex))
  1135. {
  1136. pCTrack->m_dwFlags &= ~dwFlagsOff;
  1137. pCTrack->m_dwFlags |= dwFlagsOn;
  1138. hr = S_OK;
  1139. dwCounter++;
  1140. if (dwCounter > dwMax) break;
  1141. }
  1142. LeaveCriticalSection(&m_CriticalSection);
  1143. #ifdef DBG
  1144. if (hr == DMUS_E_TRACK_NOT_FOUND)
  1145. {
  1146. Trace(1,"Error: Segmentstate::SetTrackConfig failed, unable to find the requested track.\n");
  1147. }
  1148. #endif
  1149. return hr;
  1150. }
  1151. HRESULT CSegState::CheckPlay(
  1152. MUSIC_TIME mtAmount, MUSIC_TIME* pmtResult )
  1153. {
  1154. MUSIC_TIME mtMyAmount = mtAmount;
  1155. MUSIC_TIME mtSeek = m_mtSeek;
  1156. MUSIC_TIME mtOffset = m_mtOffset;
  1157. ASSERT(pmtResult);
  1158. // if this is the first call to play,
  1159. // We also need to check to see if we are supposed to start at the beginning,
  1160. // or at an offset.
  1161. if( m_dwPlayTrackFlags & DMUS_TRACKF_START )
  1162. {
  1163. // set the current seek to the start point
  1164. mtSeek = m_mtStartPoint;
  1165. }
  1166. // if we need to do a loop or the end is near, restrict mtMyAmount
  1167. ASSERT( m_mtLength ); // length is 0, this segment won't do anything
  1168. if( m_dwRepeatsLeft )
  1169. {
  1170. if( mtMyAmount > m_mtLoopEnd - mtSeek )
  1171. {
  1172. mtMyAmount = m_mtLoopEnd - mtSeek;
  1173. }
  1174. }
  1175. else if( mtMyAmount > m_mtLength - mtSeek )
  1176. {
  1177. mtMyAmount = m_mtLength - mtSeek;
  1178. }
  1179. // take into account repeats if necessary
  1180. *pmtResult = mtMyAmount;
  1181. return S_OK;
  1182. }
  1183. ///////////////////////////////////////////////////////////////////////
  1184. // IDirectMusicSegmentState::GetStartTime
  1185. /*
  1186. @method HRESULT | IDirectMusicSegmentState | GetStartTime |
  1187. Gets the music time this SegmentState started playing.
  1188. @rvalue E_POINTER | <p pmtStart> is NULL or invalid.
  1189. @rvalue S_OK | Success.
  1190. @xref <om IDirectMusicPerformance.PlaySegment>
  1191. */
  1192. HRESULT STDMETHODCALLTYPE CSegState::GetStartTime(
  1193. MUSIC_TIME *pmtStart) // @parm Returns the music time of the start of this SegmentState.
  1194. // This is the music time, in Performance time, that the SegmentState
  1195. // started or will start playing.
  1196. {
  1197. V_INAME(IDirectMusicSegmentState::GetStartTime);
  1198. V_PTR_WRITE(pmtStart,MUSIC_TIME);
  1199. *pmtStart = m_mtResolvedStart;
  1200. return S_OK;
  1201. }
  1202. ///////////////////////////////////////////////////////////////////////
  1203. // IDirectMusicSegmentState::GetStartPoint
  1204. /*
  1205. @method HRESULT | IDirectMusicSegmentState | GetStartPoint |
  1206. Calling <om IDirectMusicSegment.SetStartPoint> causes the SegmentState to begin
  1207. playing from the middle instead of from the beginning. <om .GetStartPoint>
  1208. returns the amount of time from the beginning of the SegmentState that it
  1209. plays.
  1210. @rvalue E_POINTER | <p pmtStart> is NULL or invalid.
  1211. @rvalue S_OK | Success.
  1212. @xref <om IDirectMusicSegment.SetStartPoint>,
  1213. <om IDirectMusicPerformance.PlaySegment>
  1214. */
  1215. HRESULT STDMETHODCALLTYPE CSegState::GetStartPoint(
  1216. MUSIC_TIME *pmtStart) // @parm Returns the music time offset from the start of the
  1217. // SegmentState at which the SegmentState initially plays.
  1218. {
  1219. V_INAME(IDirectMusicSegmentState::GetStartPoint);
  1220. V_PTR_WRITE(pmtStart,MUSIC_TIME);
  1221. *pmtStart = m_mtStartPoint;
  1222. return S_OK;
  1223. }
  1224. ///////////////////////////////////////////////////////////////////////
  1225. // IDirectMusicSegmentState::SetSeek
  1226. /*
  1227. method (INTERNAL) HRESULT | IDirectMusicSegmentState | SetSeek |
  1228. Sets the music time Seek maintained by this SegmentState.
  1229. parm MUSIC_TIME | mtSeek |
  1230. [in] The music time Seek to store in this SegmentState.
  1231. comm The SegmentState passes this Seek value to <im IDirectMusicTrack.Play>
  1232. Note that newly created SegmentState's start with a Seek time of 0.
  1233. rvalue S_OK | Success.
  1234. */
  1235. HRESULT CSegState::SetSeek(
  1236. MUSIC_TIME mtSeek, DWORD dwPlayFlags)
  1237. {
  1238. m_mtSeek = mtSeek;
  1239. m_dwPlayTrackFlags |= dwPlayFlags | DMUS_TRACKF_SEEK;
  1240. return S_OK;
  1241. }
  1242. /*
  1243. Called from IDirectMusicPerformance::Invalidate, this routine helps set
  1244. the current seek pointer. Done here instead of directly inside Performance
  1245. because it's easier to compute the repeats, etc. here.
  1246. */
  1247. HRESULT CSegState::SetInvalidate(
  1248. MUSIC_TIME mtTime) // mtTime is in Performance time
  1249. {
  1250. MUSIC_TIME mtOffset;
  1251. DWORD dwRepeat;
  1252. DWORD dwFlags = DMUS_TRACKF_FLUSH | DMUS_TRACKF_SEEK;
  1253. HRESULT hr = ConvertToSegTime( &mtTime, &mtOffset, &dwRepeat );
  1254. if( hr != S_OK )
  1255. {
  1256. mtTime = 0;
  1257. m_dwRepeatsLeft = m_dwRepeats;
  1258. m_mtOffset = m_mtResolvedStart;
  1259. dwFlags |= DMUS_TRACKF_START;
  1260. }
  1261. else
  1262. {
  1263. m_dwRepeatsLeft = m_dwRepeats - dwRepeat;
  1264. m_mtOffset = mtOffset;
  1265. }
  1266. EnterCriticalSection(&m_CriticalSection);
  1267. CTrack* pCTrack = m_TrackList.GetHead();
  1268. while( pCTrack )
  1269. {
  1270. pCTrack->m_bDone = FALSE;
  1271. pCTrack = pCTrack->GetNext();
  1272. }
  1273. LeaveCriticalSection(&m_CriticalSection);
  1274. return SetSeek( mtTime, dwFlags );
  1275. }
  1276. ///////////////////////////////////////////////////////////////////////
  1277. // IDirectMusicSegmentState::GetSeek
  1278. HRESULT STDMETHODCALLTYPE CSegState::GetSeek(
  1279. MUSIC_TIME *pmtSeek) // @parm Returns the current seek pointer, which indicates
  1280. // the next time that will be called inside <om IDirectMusicTrack.Play>.
  1281. {
  1282. V_INAME(IDirectMusicSegmentState::GetSeek);
  1283. V_PTR_WRITE(pmtSeek, MUSIC_TIME);
  1284. *pmtSeek = m_mtSeek;
  1285. return S_OK;
  1286. }
  1287. HRESULT STDMETHODCALLTYPE CSegState::Flush(MUSIC_TIME mtTime) // The time on and after which to flush.
  1288. {
  1289. CTrack* pTrack;
  1290. EnterCriticalSection(&m_CriticalSection);
  1291. pTrack = m_TrackList.GetHead();
  1292. while( pTrack )
  1293. {
  1294. m_pPerformance->FlushVirtualTrack( pTrack->m_dwVirtualID, mtTime, FALSE );
  1295. pTrack = pTrack->GetNext();
  1296. }
  1297. LeaveCriticalSection(&m_CriticalSection);
  1298. return S_OK;
  1299. }
  1300. HRESULT STDMETHODCALLTYPE CSegState::Shutdown()
  1301. {
  1302. return E_NOTIMPL;
  1303. }
  1304. HRESULT STDMETHODCALLTYPE CSegState::InsertTool(
  1305. IDirectMusicTool *pTool,
  1306. DWORD *pdwPChannels,
  1307. DWORD cPChannels,
  1308. LONG lIndex)
  1309. {
  1310. return E_NOTIMPL;
  1311. }
  1312. HRESULT STDMETHODCALLTYPE CSegState::GetTool(
  1313. DWORD dwIndex,
  1314. IDirectMusicTool** ppTool)
  1315. {
  1316. return E_NOTIMPL;
  1317. }
  1318. HRESULT STDMETHODCALLTYPE CSegState::RemoveTool(
  1319. IDirectMusicTool* pTool)
  1320. {
  1321. return E_NOTIMPL;
  1322. }
  1323. HRESULT STDMETHODCALLTYPE CSegState::StampPMsg(
  1324. /* [in */ DMUS_PMSG* pPMsg)
  1325. {
  1326. V_INAME(IDirectMusicSegmentState::StampPMsg);
  1327. if( m_dwVersion < 8)
  1328. {
  1329. V_BUFPTR_WRITE(pPMsg,sizeof(DMUS_PMSG));
  1330. }
  1331. else
  1332. {
  1333. #ifdef DBG
  1334. V_BUFPTR_WRITE(pPMsg,sizeof(DMUS_PMSG));
  1335. #else
  1336. if (!pPMsg)
  1337. {
  1338. return E_POINTER;
  1339. }
  1340. #endif
  1341. }
  1342. HRESULT hr = E_FAIL;
  1343. EnterCriticalSection(&m_CriticalSection);
  1344. if (m_pPerformance)
  1345. {
  1346. // First, check if the segmentstate has its own graph.
  1347. if (m_pGraph)
  1348. {
  1349. // Could return DMUS_S_LAST_TOOL, indicating end of graph.
  1350. // If so, we'll treat that as a failure and drop on through to the next graph...
  1351. if( S_OK == ( hr = m_pGraph->StampPMsg( pPMsg )))
  1352. {
  1353. if( pPMsg->pGraph != this ) // Make sure this is set to point to the segstate embedded graph so it will come here again.
  1354. {
  1355. if( pPMsg->pGraph )
  1356. {
  1357. pPMsg->pGraph->Release();
  1358. pPMsg->pGraph = NULL;
  1359. }
  1360. pPMsg->pGraph = this;
  1361. AddRef();
  1362. }
  1363. }
  1364. }
  1365. // If done with the graph, send to the audio path, if it exists,
  1366. // else the performance. Also, check for the special case of
  1367. // DMUS_PCHANNEL_BROADCAST_SEGMENT. If so, duplicate the pMsg
  1368. // and send all the copies with the appropriate pchannel values.
  1369. if( FAILED(hr) || (m_dwVersion && (hr == DMUS_S_LAST_TOOL)))
  1370. {
  1371. if (pPMsg->dwPChannel == DMUS_PCHANNEL_BROADCAST_SEGMENT)
  1372. {
  1373. CSegment *pSegment = m_pSegment;
  1374. EnterCriticalSection(&pSegment->m_CriticalSection);
  1375. DWORD dwIndex;
  1376. // Create new messages with new pchannels for all but one, which will
  1377. // be assigned to this message.
  1378. for (dwIndex = 1;dwIndex < pSegment->m_dwNumPChannels;dwIndex++)
  1379. {
  1380. DWORD dwNewChannel = pSegment->m_paPChannels[dwIndex];
  1381. // Don't broadcast any broadcast messages!
  1382. // And, if this is a transpose on the drum channel, don't send it.
  1383. if ((dwNewChannel < DMUS_PCHANNEL_BROADCAST_GROUPS) &&
  1384. ((pPMsg->dwType != DMUS_PMSGT_TRANSPOSE) || ((dwNewChannel & 0xF) != 9)))
  1385. {
  1386. DMUS_PMSG *pNewMsg;
  1387. if (SUCCEEDED(m_pPerformance->ClonePMsg(pPMsg,&pNewMsg)))
  1388. {
  1389. HRESULT hrTemp;
  1390. pNewMsg->dwPChannel = dwNewChannel;
  1391. if (m_pAudioPath)
  1392. {
  1393. hrTemp = m_pAudioPath->StampPMsg(pNewMsg);
  1394. }
  1395. else
  1396. {
  1397. hrTemp = m_pPerformance->StampPMsg(pNewMsg);
  1398. }
  1399. if (SUCCEEDED(hrTemp))
  1400. {
  1401. m_pPerformance->SendPMsg(pNewMsg);
  1402. }
  1403. else
  1404. {
  1405. m_pPerformance->FreePMsg(pNewMsg);
  1406. }
  1407. }
  1408. }
  1409. }
  1410. // Now, set the pchannel for this one. First check that there are any
  1411. // pchannels. If none, mark the PMsg to be deleted by the SendPMsg routine.
  1412. // Also, mark it this way if the PMsg is a broadcast PMsg.
  1413. pPMsg->dwPChannel = DMUS_PCHANNEL_KILL_ME;
  1414. if (pSegment->m_dwNumPChannels)
  1415. {
  1416. if (pSegment->m_paPChannels[0] < DMUS_PCHANNEL_BROADCAST_GROUPS)
  1417. {
  1418. pPMsg->dwPChannel = pSegment->m_paPChannels[0];
  1419. }
  1420. }
  1421. LeaveCriticalSection(&pSegment->m_CriticalSection);
  1422. }
  1423. if (m_pAudioPath)
  1424. {
  1425. hr = m_pAudioPath->StampPMsg(pPMsg);
  1426. }
  1427. else
  1428. {
  1429. hr = m_pPerformance->StampPMsg(pPMsg);
  1430. }
  1431. }
  1432. }
  1433. else
  1434. {
  1435. hr = DMUS_E_NOT_INIT;
  1436. Trace(1,"Error: Segmentstate::StampPMsg failed because the segmentstate is not properly initialized.\n");
  1437. }
  1438. LeaveCriticalSection(&m_CriticalSection);
  1439. return hr;
  1440. }
  1441. STDMETHODIMP CSegState::GetObjectInPath( DWORD dwPChannel,DWORD dwStage,DWORD dwBuffer, REFGUID guidObject,
  1442. DWORD dwIndex,REFGUID iidInterface, void ** ppObject)
  1443. {
  1444. V_INAME(IDirectMusicSegmentState::GetObjectInPath);
  1445. V_PTRPTR_WRITE(ppObject);
  1446. *ppObject = NULL;
  1447. if (dwBuffer && ((dwStage < DMUS_PATH_BUFFER) || (dwStage >= DMUS_PATH_PRIMARY_BUFFER)))
  1448. {
  1449. return DMUS_E_NOT_FOUND;
  1450. }
  1451. HRESULT hr = DMUS_E_NOT_FOUND;
  1452. EnterCriticalSection(&m_CriticalSection);
  1453. switch (dwStage)
  1454. {
  1455. case DMUS_PATH_SEGMENT:
  1456. if (m_pSegment && (dwIndex == 0) && (dwPChannel == 0))
  1457. {
  1458. hr = m_pSegment->QueryInterface(iidInterface,ppObject);
  1459. }
  1460. break;
  1461. case DMUS_PATH_SEGMENT_TRACK:
  1462. if (dwPChannel == 0)
  1463. {
  1464. CTrack * pCTrack = GetTrack(guidObject,-1,dwIndex);
  1465. if (pCTrack)
  1466. {
  1467. if (pCTrack->m_pTrack)
  1468. {
  1469. hr = pCTrack->m_pTrack->QueryInterface(iidInterface,ppObject);
  1470. }
  1471. }
  1472. }
  1473. break;
  1474. case DMUS_PATH_SEGMENT_GRAPH:
  1475. if ((dwIndex == 0) && (dwPChannel == 0))
  1476. {
  1477. if (!m_pGraph)
  1478. {
  1479. m_pGraph = new CGraph;
  1480. }
  1481. if (m_pGraph)
  1482. {
  1483. hr = m_pGraph->QueryInterface(iidInterface,ppObject);
  1484. }
  1485. else
  1486. {
  1487. hr = E_OUTOFMEMORY;
  1488. }
  1489. }
  1490. break;
  1491. case DMUS_PATH_SEGMENT_TOOL:
  1492. if (!m_pGraph)
  1493. {
  1494. m_pGraph = new CGraph;
  1495. }
  1496. if (m_pGraph)
  1497. {
  1498. hr = m_pGraph->GetObjectInPath(dwPChannel,guidObject,dwIndex,iidInterface,ppObject);
  1499. }
  1500. else
  1501. {
  1502. hr = E_OUTOFMEMORY;
  1503. }
  1504. break;
  1505. case DMUS_PATH_PERFORMANCE:
  1506. if (m_pPerformance && (dwIndex == 0) && (dwPChannel == 0))
  1507. {
  1508. hr = m_pPerformance->QueryInterface(iidInterface,ppObject);
  1509. }
  1510. break;
  1511. case DMUS_PATH_PERFORMANCE_GRAPH:
  1512. if (m_pPerformance && (dwIndex == 0) && (dwPChannel == 0))
  1513. {
  1514. IDirectMusicGraph *pGraph;
  1515. if (SUCCEEDED(hr = m_pPerformance->GetGraphInternal(&pGraph)))
  1516. {
  1517. hr = pGraph->QueryInterface(iidInterface,ppObject);
  1518. pGraph->Release();
  1519. }
  1520. }
  1521. break;
  1522. case DMUS_PATH_PERFORMANCE_TOOL:
  1523. if (m_pPerformance)
  1524. {
  1525. IDirectMusicGraph *pGraph;
  1526. if (SUCCEEDED(hr = m_pPerformance->GetGraphInternal(&pGraph)))
  1527. {
  1528. CGraph *pCGraph = (CGraph *) pGraph;
  1529. hr = pCGraph->GetObjectInPath(dwPChannel,guidObject,dwIndex,iidInterface,ppObject);
  1530. pGraph->Release();
  1531. }
  1532. }
  1533. break;
  1534. default:
  1535. if (m_pAudioPath)
  1536. {
  1537. hr = m_pAudioPath->GetObjectInPath(dwPChannel,dwStage,dwBuffer,guidObject,dwIndex,iidInterface,ppObject);
  1538. }
  1539. else
  1540. {
  1541. Trace(1,"Error: Unable to access audiopath components of segmentstate.\n");
  1542. }
  1543. }
  1544. LeaveCriticalSection(&m_CriticalSection);
  1545. return hr;
  1546. }