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.

2324 lines
86 KiB

  1. //
  2. // Copyright (c) 1998-2001 Microsoft Corporation
  3. // song.cpp : Implementation of CSong
  4. //
  5. #include "dmime.h"
  6. #include "song.h"
  7. #include "..\shared\validp.h"
  8. #include "..\shared\dmstrm.h"
  9. #include "..\shared\Validate.h"
  10. #include "debug.h"
  11. CTrack::CTrack()
  12. {
  13. m_pTrack = NULL;
  14. m_pTrack8 = NULL;
  15. m_pTrackState = NULL;
  16. m_bDone = FALSE;
  17. m_dwPriority = 0;
  18. m_dwPosition = 0;
  19. m_dwFlags = DMUS_TRACKCONFIG_DEFAULT;
  20. m_dwInternalFlags = 0;
  21. m_dwGroupBits = 0xFFFFFFFF;
  22. m_dwVirtualID = 0;
  23. m_guidClassID = GUID_NULL;
  24. }
  25. CTrack::~CTrack()
  26. {
  27. assert( !( m_pTrackState && !m_pTrack ) ); // if we have state but no track, something's wrong
  28. if( m_pTrack )
  29. {
  30. if( m_pTrackState )
  31. {
  32. m_pTrack->EndPlay( m_pTrackState ); // allow the track to delete its state data
  33. }
  34. m_pTrack->Release();
  35. }
  36. if ( m_pTrack8 )
  37. {
  38. m_pTrack8->Release();
  39. }
  40. }
  41. HRESULT CTrackList::CreateCopyWithBlankState(CTrackList* pTrackList)
  42. {
  43. if( pTrackList )
  44. {
  45. CTrack* pTrack;
  46. CTrack* pCopy;
  47. pTrackList->Clear();
  48. pTrack = (CTrack*)m_pHead;
  49. while( pTrack )
  50. {
  51. pCopy = new CTrack;
  52. if( pCopy )
  53. {
  54. // copy the IDirectMusicTrack pointer, but leave
  55. // the track state blank.
  56. *pCopy = *pTrack;
  57. pCopy->SetNext(NULL);
  58. pCopy->m_pTrackState = NULL;
  59. assert( pCopy->m_pTrack );
  60. pCopy->m_pTrack->AddRef();
  61. if (pCopy->m_pTrack8)
  62. {
  63. pCopy->m_pTrack8->AddRef();
  64. }
  65. pTrackList->Cat( pCopy );
  66. }
  67. else
  68. {
  69. assert(FALSE); // out of memory
  70. return E_OUTOFMEMORY;
  71. }
  72. pTrack = pTrack->GetNext();
  73. }
  74. }
  75. else
  76. {
  77. assert(FALSE); // out of memory
  78. return E_OUTOFMEMORY;
  79. }
  80. return S_OK;
  81. }
  82. CVirtualSegment::CVirtualSegment()
  83. {
  84. m_wszName[0] = 0;
  85. m_pSourceSegment = NULL;
  86. m_pPlaySegment = NULL;
  87. m_pGraph = NULL;
  88. m_dwFlags = 0;
  89. m_dwID = 0;
  90. m_dwNextPlayID = DMUS_SONG_NOSEG;
  91. m_dwNextPlayFlags = 0;
  92. m_mtTime = 0;
  93. m_dwTransitionCount = 0;
  94. m_pTransitions = NULL;
  95. m_SegHeader.rtLength = 0;
  96. m_SegHeader.dwFlags = 0;
  97. m_SegHeader.dwRepeats = 0; /* Number of repeats. By default, 0. */
  98. m_SegHeader.mtLength = 0xC00; /* Length, in music time. */
  99. m_SegHeader.mtPlayStart = 0; /* Start of playback. By default, 0. */
  100. m_SegHeader.mtLoopStart = 0; /* Start of looping portion. By default, 0. */
  101. m_SegHeader.mtLoopEnd = 0; /* End of loop. Must be greater than dwPlayStart. By default equal to length. */
  102. m_SegHeader.dwResolution = 0; /* Default resolution. */
  103. }
  104. CVirtualSegment::~CVirtualSegment()
  105. {
  106. if (m_pSourceSegment)
  107. {
  108. m_pSourceSegment->Release();
  109. }
  110. if (m_pPlaySegment)
  111. {
  112. m_pPlaySegment->Release();
  113. }
  114. if (m_pGraph)
  115. {
  116. m_pGraph->Release();
  117. }
  118. if (m_pTransitions)
  119. {
  120. delete [] m_pTransitions;
  121. }
  122. m_TrackList.Clear();
  123. }
  124. CTrack * CVirtualSegment::GetTrackByParam( CTrack * pCTrack,
  125. REFGUID rguidType,DWORD dwGroupBits,DWORD dwIndex)
  126. {
  127. // If the caller was already part way through the list, it passes the current
  128. // track. Otherwise, NULL to indicate start at the top.
  129. if (pCTrack)
  130. {
  131. pCTrack = pCTrack->GetNext();
  132. }
  133. else
  134. {
  135. pCTrack = m_TrackList.GetHead();
  136. }
  137. while( pCTrack )
  138. {
  139. ASSERT(pCTrack->m_pTrack);
  140. if( (pCTrack->m_dwGroupBits & dwGroupBits ) &&
  141. (pCTrack->m_dwFlags & DMUS_TRACKCONFIG_CONTROL_ENABLED))
  142. {
  143. if( (GUID_NULL == rguidType) || (pCTrack->m_pTrack->IsParamSupported( rguidType ) == S_OK ))
  144. {
  145. if( 0 == dwIndex )
  146. {
  147. return pCTrack;
  148. }
  149. dwIndex--;
  150. }
  151. }
  152. pCTrack = pCTrack->GetNext();
  153. }
  154. return NULL;
  155. }
  156. void CVirtualSegmentList::Clear()
  157. {
  158. CVirtualSegment *pVirtualSegment;
  159. while (pVirtualSegment = RemoveHead())
  160. {
  161. delete pVirtualSegment;
  162. }
  163. }
  164. CSongSegment::CSongSegment()
  165. {
  166. m_pSegment = NULL;
  167. m_dwLoadID = 0;
  168. }
  169. CSongSegment::~CSongSegment()
  170. {
  171. if (m_pSegment)
  172. {
  173. m_pSegment->Release();
  174. }
  175. }
  176. HRESULT CSongSegmentList::AddSegment(CSegment *pSegment, DWORD dwLoadID)
  177. {
  178. CSongSegment *pSeg = new CSongSegment;
  179. if (pSeg)
  180. {
  181. pSeg->m_dwLoadID = dwLoadID;
  182. pSeg->m_pSegment = pSegment;
  183. pSegment->AddRef();
  184. AddTail(pSeg);
  185. return S_OK;
  186. }
  187. return E_OUTOFMEMORY;
  188. }
  189. void CSongSegmentList::Clear()
  190. {
  191. CSongSegment *pSongSegment;
  192. while (pSongSegment = RemoveHead())
  193. {
  194. delete pSongSegment;
  195. }
  196. }
  197. CSong::CSong()
  198. {
  199. InitializeCriticalSection(&m_CriticalSection);
  200. m_dwStartSegID = DMUS_SONG_NOSEG;
  201. m_pAudioPathConfig = NULL;
  202. m_fPartialLoad = FALSE;
  203. m_cRef = 1;
  204. m_dwFlags = 0;
  205. m_dwValidData = DMUS_OBJ_CLASS; // upon creation, only this data is valid
  206. memset(&m_guidObject,0,sizeof(m_guidObject));
  207. memset(&m_ftDate, 0,sizeof(m_ftDate));
  208. memset(&m_vVersion, 0,sizeof(m_vVersion));
  209. m_pUnkDispatch = NULL;
  210. InterlockedIncrement(&g_cComponent);
  211. m_fZombie = false;
  212. TraceI(2, "Song %lx created\n", this );
  213. }
  214. CSong::~CSong()
  215. {
  216. Clear();
  217. if (m_pUnkDispatch)
  218. {
  219. m_pUnkDispatch->Release(); // free IDispatch implementation we may have borrowed
  220. }
  221. DeleteCriticalSection(&m_CriticalSection);
  222. InterlockedDecrement(&g_cComponent);
  223. TraceI(2, "Song %lx destroyed\n", this );
  224. }
  225. void CSong::Clear()
  226. {
  227. if (m_pAudioPathConfig)
  228. {
  229. m_pAudioPathConfig->Release();
  230. m_pAudioPathConfig = NULL;
  231. }
  232. m_GraphList.Clear();
  233. m_PlayList.Clear();
  234. m_SegmentList.Clear();
  235. m_VirtualSegmentList.Clear();
  236. m_dwStartSegID = DMUS_SONG_NOSEG;
  237. m_fPartialLoad = FALSE;
  238. m_dwFlags = 0;
  239. m_dwValidData = DMUS_OBJ_CLASS; // upon creation, only this data is valid
  240. }
  241. STDMETHODIMP_(void) CSong::Zombie()
  242. {
  243. Clear();
  244. m_fZombie = true;
  245. }
  246. STDMETHODIMP CSong::QueryInterface(
  247. const IID &iid, // @parm Interface to query for
  248. void **ppv) // @parm The requested interface will be returned here
  249. {
  250. V_INAME(CSong::QueryInterface);
  251. V_PTRPTR_WRITE(ppv);
  252. V_REFGUID(iid);
  253. *ppv = NULL;
  254. if (iid == IID_IUnknown || iid == IID_IDirectMusicSong)
  255. {
  256. *ppv = static_cast<IDirectMusicSong*>(this);
  257. }
  258. else if (iid == IID_CSong)
  259. {
  260. *ppv = static_cast<CSong*>(this);
  261. }
  262. else if (iid == IID_IPersistStream)
  263. {
  264. *ppv = static_cast<IPersistStream*>(this);
  265. }
  266. else if(iid == IID_IDirectMusicObject)
  267. {
  268. *ppv = static_cast<IDirectMusicObject*>(this);
  269. }
  270. else if (iid == IID_IDirectMusicObjectP)
  271. {
  272. *ppv = static_cast<IDirectMusicObjectP*>(this);
  273. }
  274. else if(iid == IID_IDispatch)
  275. {
  276. // A helper scripting object implements IDispatch, which we expose via COM aggregation.
  277. if (!m_pUnkDispatch)
  278. {
  279. // Create the helper object
  280. ::CoCreateInstance(
  281. CLSID_AutDirectMusicSong,
  282. static_cast<IDirectMusicSong*>(this),
  283. CLSCTX_INPROC_SERVER,
  284. IID_IUnknown,
  285. reinterpret_cast<void**>(&m_pUnkDispatch));
  286. }
  287. if (m_pUnkDispatch)
  288. {
  289. return m_pUnkDispatch->QueryInterface(IID_IDispatch, ppv);
  290. }
  291. }
  292. if (*ppv == NULL)
  293. {
  294. Trace(4,"Warning: Request to query unknown interface on Song object\n");
  295. return E_NOINTERFACE;
  296. }
  297. reinterpret_cast<IUnknown*>(this)->AddRef();
  298. return S_OK;
  299. }
  300. STDMETHODIMP_(ULONG) CSong::AddRef()
  301. {
  302. return InterlockedIncrement(&m_cRef);
  303. }
  304. STDMETHODIMP_(ULONG) CSong::Release()
  305. {
  306. if (!InterlockedDecrement(&m_cRef))
  307. {
  308. m_cRef = 100; // artificial reference count to prevent reentrency due to COM aggregation
  309. delete this;
  310. return 0;
  311. }
  312. return m_cRef;
  313. }
  314. STDMETHODIMP CSong::Compose( )
  315. {
  316. if (m_fZombie)
  317. {
  318. Trace(1, "Error: Call of IDirectMusicSong::Compose after the song has been garbage collected. "
  319. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  320. "and then calling CollectGarbage or Release on the loader.");
  321. return DMUS_S_GARBAGE_COLLECTED;
  322. }
  323. HRESULT hr = S_OK;
  324. EnterCriticalSection(&m_CriticalSection);
  325. // Go through the seg ref list and create master composition tracks for each composing track.
  326. TList<ComposingTrack> MasterTrackList;
  327. CVirtualSegment* pVirtualSegment = m_VirtualSegmentList.GetHead();
  328. for (; pVirtualSegment; pVirtualSegment = pVirtualSegment->GetNext())
  329. {
  330. if (!pVirtualSegment->m_pPlaySegment)
  331. {
  332. Trace(1,"Error: Corrupt song, one or more virtual segments do not resolve to real segments. Unable to compose.\n");
  333. hr = E_POINTER;
  334. break;
  335. }
  336. CSegment *pSegment = pVirtualSegment->m_pPlaySegment;
  337. CTrack* pTrack = pSegment->m_TrackList.GetHead();
  338. for (; pTrack; pTrack = pTrack->GetNext())
  339. {
  340. if (pTrack->m_dwFlags & DMUS_TRACKCONFIG_COMPOSING)
  341. {
  342. DWORD dwTrackGroup = pTrack->m_dwGroupBits;
  343. // filter out any group bits already covered by other master tracks of same type
  344. TListItem<ComposingTrack>* pMaster = MasterTrackList.GetHead();
  345. for (; pMaster; pMaster = pMaster->GetNext())
  346. {
  347. ComposingTrack& rMaster = pMaster->GetItemValue();
  348. if (rMaster.GetTrackID() == pTrack->m_guidClassID)
  349. {
  350. DWORD dwMaster = rMaster.GetTrackGroup();
  351. if (dwMaster == dwTrackGroup)
  352. {
  353. // Exact match: put the track here.
  354. hr = rMaster.AddTrack(pVirtualSegment, pTrack);
  355. dwTrackGroup = 0;
  356. break;
  357. }
  358. DWORD dwIntersection = dwMaster & dwTrackGroup;
  359. if (dwIntersection)
  360. {
  361. dwTrackGroup |= ~dwIntersection;
  362. }
  363. }
  364. }
  365. // If we've still got any group bits left, add a new composing track
  366. if (dwTrackGroup)
  367. {
  368. TListItem<ComposingTrack>* pTrackItem = new TListItem<ComposingTrack>;
  369. if (!pTrackItem)
  370. {
  371. hr = E_OUTOFMEMORY;
  372. }
  373. else
  374. {
  375. ComposingTrack& rTrack = pTrackItem->GetItemValue();
  376. rTrack.SetTrackGroup(dwTrackGroup);
  377. rTrack.SetTrackID(pTrack->m_guidClassID);
  378. rTrack.SetPriority(pTrack->m_dwPriority);
  379. // Add tracks in priority order (higher priority first)
  380. pMaster = MasterTrackList.GetHead();
  381. TListItem<ComposingTrack>* pPrevious = NULL;
  382. for (; pMaster; pMaster = pMaster->GetNext())
  383. {
  384. ComposingTrack& rMaster = pMaster->GetItemValue();
  385. if (pTrack->m_dwPriority > rMaster.GetPriority()) break;
  386. pPrevious = pMaster;
  387. }
  388. if (!pPrevious) // this has higher priority than anything in the list
  389. {
  390. MasterTrackList.AddHead(pTrackItem);
  391. }
  392. else // lower priority than pPrevious, higher than pMaster
  393. {
  394. pTrackItem->SetNext(pMaster);
  395. pPrevious->SetNext(pTrackItem);
  396. }
  397. hr = pTrackItem->GetItemValue().AddTrack(pVirtualSegment, pTrack);
  398. }
  399. }
  400. }
  401. if (FAILED(hr)) break;
  402. }
  403. if (FAILED(hr)) break;
  404. }
  405. // Call compose on each master composition track
  406. if (SUCCEEDED(hr))
  407. {
  408. TListItem<ComposingTrack>* pMaster = MasterTrackList.GetHead();
  409. if (pMaster)
  410. {
  411. for (; pMaster; pMaster = pMaster->GetNext())
  412. {
  413. hr = pMaster->GetItemValue().Compose(this);
  414. }
  415. }
  416. else hr = S_FALSE;
  417. }
  418. LeaveCriticalSection(&m_CriticalSection);
  419. return hr;
  420. }
  421. STDMETHODIMP CSong::Download(IUnknown *pAudioPath)
  422. {
  423. V_INAME(IDirectMusicSong::Download);
  424. V_INTERFACE(pAudioPath);
  425. if (m_fZombie)
  426. {
  427. Trace(1, "Error: Call of IDirectMusicSong::Download after the song has been garbage collected. "
  428. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  429. "and then calling CollectGarbage or Release on the loader.");
  430. return DMUS_S_GARBAGE_COLLECTED;
  431. }
  432. DWORD dwSuccess = 0;
  433. HRESULT hr = S_OK;
  434. HRESULT hrFail = S_OK;
  435. EnterCriticalSection(&m_CriticalSection);
  436. CSegment *pSegment = m_PlayList.GetHead();
  437. for (;pSegment;pSegment = pSegment->GetNext())
  438. {
  439. if (SUCCEEDED(hr = pSegment->Download(pAudioPath)))
  440. {
  441. // count partial successes, so that S_FALSE will be returned if we have, e.g.,
  442. // one partial success followed by one failure
  443. dwSuccess++;
  444. }
  445. if (hr != S_OK)
  446. {
  447. // keep track of partial successes so that they always percolate up
  448. hrFail = hr;
  449. }
  450. }
  451. LeaveCriticalSection(&m_CriticalSection);
  452. if (hrFail != S_OK && dwSuccess)
  453. {
  454. Trace(1,"Warning: Only %ld of the total %ld segments successfully downloaded.\n",
  455. dwSuccess,m_PlayList.GetCount());
  456. hr = S_FALSE;
  457. }
  458. return hr;
  459. }
  460. STDMETHODIMP CSong::Unload(IUnknown *pAudioPath)
  461. {
  462. V_INAME(IDirectMusicSong::Unload);
  463. V_INTERFACE(pAudioPath);
  464. if (m_fZombie)
  465. {
  466. Trace(1, "Error: Call of IDirectMusicSong::Unload after the song has been garbage collected. "
  467. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  468. "and then calling CollectGarbage or Release on the loader.");
  469. return DMUS_S_GARBAGE_COLLECTED;
  470. }
  471. DWORD dwSuccess = 0;
  472. HRESULT hr = S_OK;
  473. HRESULT hrFail = S_OK;
  474. EnterCriticalSection(&m_CriticalSection);
  475. CSegment *pSegment = m_PlayList.GetHead();
  476. for (;pSegment;pSegment = pSegment->GetNext())
  477. {
  478. if (SUCCEEDED(hr = pSegment->Unload(pAudioPath)))
  479. {
  480. dwSuccess++;
  481. }
  482. else
  483. {
  484. hrFail = hr;
  485. }
  486. }
  487. LeaveCriticalSection(&m_CriticalSection);
  488. if (FAILED(hrFail) && dwSuccess)
  489. {
  490. Trace(1,"Warning: Only %ld of the total %ld segments successfully unloaded.\n",
  491. dwSuccess,m_PlayList.GetCount());
  492. hr = S_FALSE;
  493. }
  494. return hr;
  495. }
  496. /*STDMETHODIMP CSong::Clone(IDirectMusicSong **ppSong)
  497. {
  498. V_INAME(IDirectMusicSong::Clone);
  499. V_PTRPTR_WRITE_OPT(ppSong);
  500. HRESULT hr = E_OUTOFMEMORY;
  501. CSong *pSong = new CSong();
  502. if (*ppSong)
  503. {
  504. *ppSong = pSong;
  505. EnterCriticalSection(&m_CriticalSection);
  506. CSegment *pSegment = m_PlayList.GetHead();
  507. for (;pSegment;pSegment = pSegment->GetNext())
  508. {
  509. IDirectMusicSegment *pISeg;
  510. hr = pSegment->Clone(0,pSegment->m_mtLength,&pISeg);
  511. if (SUCCEEDED(hr))
  512. {
  513. CSegment *pCopy = (CSegment *) pISeg;
  514. pSong->m_PlayList.AddTail(pCopy);
  515. pCopy->m_pSong = pSong;
  516. }
  517. }
  518. pSong->m_dwValidData = m_dwValidData;
  519. pSong->m_guidObject = m_guidObject;
  520. pSong->m_ftDate = m_ftDate;
  521. pSong->m_vVersion = m_vVersion;
  522. wcscpy(pSong->m_wszName,m_wszName);
  523. wcscpy(pSong->m_wszCategory,m_wszCategory);
  524. wcscpy(pSong->m_wszFileName,m_wszFileName);
  525. pSong->m_dwVersion = m_dwVersion;
  526. pSong->m_dwFlags = m_dwFlags;
  527. pSong->m_pAudioPathConfig = m_pAudioPathConfig;
  528. if (m_pAudioPathConfig)
  529. m_pAudioPathConfig->AddRef();
  530. LeaveCriticalSection(&m_CriticalSection);
  531. }
  532. return hr;
  533. }
  534. */
  535. STDMETHODIMP CSong::GetParam( REFGUID rguidType,
  536. DWORD dwGroupBits,
  537. DWORD dwIndex,
  538. MUSIC_TIME mtTime,
  539. MUSIC_TIME* pmtNext,
  540. void* pParam)
  541. {
  542. V_INAME(IDirectMusiCSong::GetParam);
  543. V_REFGUID(rguidType);
  544. V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME);
  545. if (m_fZombie)
  546. {
  547. Trace(1, "Error: Call of IDirectMusicSong::GetParam after the song has been garbage collected. "
  548. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  549. "and then calling CollectGarbage or Release on the loader.");
  550. return DMUS_S_GARBAGE_COLLECTED;
  551. }
  552. HRESULT hr = DMUS_E_TRACK_NOT_FOUND;
  553. /* BOOL fMultipleTry = FALSE;
  554. if (dwIndex == DMUS_SEG_ANYTRACK)
  555. {
  556. dwIndex = 0;
  557. fMultipleTry = TRUE;
  558. }*/
  559. EnterCriticalSection(&m_CriticalSection);
  560. /*CSegment *pSegment = m_PlayList.GetHead();
  561. for (;pSegment;pSegment = pSegment->GetNext())
  562. {
  563. if (pSegment->m_mtStart <= mtTime &&
  564. mtTime < pSegment->m_mtStart + pSegment->m_mtLength)
  565. {
  566. hr = pSegment->GetParam(rguidType, dwGroupBits, dwIndex, mtTime - pSegment->m_mtStart, pmtNext, pParam);
  567. if (SUCCEEDED(hr)) break;
  568. }
  569. }*/
  570. CVirtualSegment *pVirtualSegment = m_VirtualSegmentList.GetHead();
  571. for (;pVirtualSegment;pVirtualSegment = pVirtualSegment->GetNext())
  572. {
  573. if (pVirtualSegment->m_mtTime <= mtTime &&
  574. pVirtualSegment->m_pPlaySegment &&
  575. mtTime < pVirtualSegment->m_mtTime + pVirtualSegment->m_pPlaySegment->m_mtLength)
  576. {
  577. hr = pVirtualSegment->m_pPlaySegment->GetParam(rguidType, dwGroupBits, dwIndex, mtTime - pVirtualSegment->m_mtTime, pmtNext, pParam);
  578. if (SUCCEEDED(hr)) break;
  579. }
  580. }
  581. /* for (;pVirtualSegment;pVirtualSegment = pVirtualSegment->GetNext())
  582. {
  583. if (pVirtualSegment->m_mtTime <= mtTime)
  584. {
  585. CTrack* pCTrack;
  586. pCTrack = pVirtualSegment->GetTrackByParam(NULL, rguidType,dwGroupBits, dwIndex);
  587. while (pCTrack)
  588. {
  589. if (pCTrack->m_pTrack8)
  590. {
  591. REFERENCE_TIME rtNext, *prtNext;
  592. // We need to store the next time in a 64 bit pointer. But, don't
  593. // make 'em fill it in unless the caller requested it.
  594. if (pmtNext)
  595. {
  596. prtNext = &rtNext;
  597. }
  598. else
  599. {
  600. prtNext = NULL;
  601. }
  602. hr = pCTrack->m_pTrack8->GetParamEx( rguidType, mtTime - pVirtualSegment->m_mtTime, prtNext, pParam,
  603. NULL, 0 );
  604. if (pmtNext)
  605. {
  606. *pmtNext = (MUSIC_TIME) rtNext;
  607. }
  608. }
  609. else
  610. {
  611. hr = pCTrack->m_pTrack->GetParam( rguidType, mtTime - pVirtualSegment->m_mtTime, pmtNext, pParam );
  612. / * if( pmtNext && (( *pmtNext == 0 ) || (*pmtNext > (m_mtLength - mtTime))))
  613. {
  614. *pmtNext = m_mtLength - mtTime;
  615. }* /
  616. }
  617. // If nothing was found and dwIndex was DMUS_SEG_ANYTRACK, try again...
  618. if (fMultipleTry && (hr == DMUS_E_NOT_FOUND))
  619. {
  620. pCTrack = pVirtualSegment->GetTrackByParam(pCTrack, rguidType,dwGroupBits, dwIndex);
  621. }
  622. else
  623. {
  624. pCTrack = NULL;
  625. }
  626. }
  627. }
  628. }*/
  629. if (FAILED(hr) && pmtNext)
  630. {
  631. // return the time of the first segment after mtTime (or 0 if there is no such segment)
  632. pVirtualSegment = m_VirtualSegmentList.GetHead();
  633. for (;pVirtualSegment;pVirtualSegment = pVirtualSegment->GetNext())
  634. {
  635. if (pVirtualSegment->m_mtTime > mtTime)
  636. {
  637. *pmtNext = pVirtualSegment->m_mtTime;
  638. break;
  639. }
  640. }
  641. }
  642. LeaveCriticalSection(&m_CriticalSection);
  643. return hr;
  644. }
  645. HRESULT CSong::Instantiate()
  646. {
  647. V_INAME(IDirectMusicSong::Instantiate);
  648. EnterCriticalSection(&m_CriticalSection);
  649. CVirtualSegment *pRef = m_VirtualSegmentList.GetHead();
  650. m_PlayList.Clear();
  651. for (;pRef;pRef = pRef->GetNext())
  652. {
  653. // the constructor below does an AddRef.
  654. CSegment *pSegment = new CSegment(&pRef->m_SegHeader,pRef->m_pSourceSegment);
  655. if (pSegment)
  656. {
  657. if (pRef->m_wszName[0])
  658. {
  659. wcscpy(pSegment->m_wszName,pRef->m_wszName);
  660. pSegment->m_dwValidData |= DMUS_OBJ_NAME;
  661. }
  662. CTrack *pTrack;
  663. for (pTrack = pRef->m_TrackList.GetHead();pTrack;pTrack = pTrack->GetNext())
  664. {
  665. CTrack *pCopy = new CTrack;
  666. if( pCopy )
  667. {
  668. *pCopy = *pTrack;
  669. pCopy->SetNext(NULL);
  670. pCopy->m_pTrackState = NULL;
  671. pCopy->m_pTrack->AddRef();
  672. if (pCopy->m_pTrack8)
  673. {
  674. pCopy->m_pTrack8->AddRef();
  675. }
  676. // The tracks were in backwards order. This puts them back in order, and ahead of the segment tracks.
  677. pSegment->m_TrackList.AddHead( pCopy );
  678. }
  679. }
  680. pSegment->m_pSong = this;
  681. pSegment->m_dwPlayID = pRef->m_dwID;
  682. //Trace(0,"Intantiating PlaySegment %ls with ID %ld.\n",pRef->m_wszName,pRef->m_dwID);
  683. pSegment->m_dwNextPlayFlags = pRef->m_dwNextPlayFlags;
  684. pSegment->m_dwNextPlayID = pRef->m_dwNextPlayID;
  685. m_PlayList.AddTail(pSegment);
  686. if (pRef->m_pPlaySegment) pRef->m_pPlaySegment->Release();
  687. pRef->m_pPlaySegment = pSegment;
  688. pRef->m_pPlaySegment->AddRef();
  689. }
  690. }
  691. LeaveCriticalSection(&m_CriticalSection);
  692. return S_OK;
  693. }
  694. HRESULT CSong::EnumSegment( DWORD dwIndex,IDirectMusicSegment **ppSegment)
  695. {
  696. V_INAME(IDirectMusicSong::EnumSegment);
  697. V_PTRPTR_WRITE (ppSegment);
  698. if (m_fZombie)
  699. {
  700. Trace(1, "Error: Call of IDirectMusicSong::EnumSegment after the song has been garbage collected. "
  701. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  702. "and then calling CollectGarbage or Release on the loader.");
  703. return DMUS_S_GARBAGE_COLLECTED;
  704. }
  705. HRESULT hr = S_FALSE;
  706. EnterCriticalSection(&m_CriticalSection);
  707. CSegment *pSegment = m_PlayList.GetHead();
  708. for (;pSegment && dwIndex;pSegment = pSegment->GetNext()) dwIndex--;
  709. if (pSegment)
  710. {
  711. *ppSegment = static_cast<IDirectMusicSegment*>(pSegment);
  712. pSegment->AddRef();
  713. hr = S_OK;
  714. }
  715. LeaveCriticalSection(&m_CriticalSection);
  716. return hr;
  717. }
  718. HRESULT CSong::GetPlaySegment( DWORD dwIndex,CSegment **ppSegment)
  719. {
  720. HRESULT hr = S_FALSE;
  721. EnterCriticalSection(&m_CriticalSection);
  722. CSegment *pSegment = m_PlayList.GetHead();
  723. for (;pSegment;pSegment = pSegment->GetNext())
  724. {
  725. if (pSegment->m_dwPlayID == dwIndex)
  726. {
  727. *ppSegment = pSegment;
  728. pSegment->AddRef();
  729. hr = S_OK;
  730. break;
  731. }
  732. }
  733. LeaveCriticalSection(&m_CriticalSection);
  734. return hr;
  735. }
  736. STDMETHODIMP CSong::GetSegment(WCHAR *wszName, IDirectMusicSegment **ppSegment)
  737. {
  738. V_INAME(IDirectMusicSong::GetSegment);
  739. V_PTRPTR_WRITE(ppSegment);
  740. if (m_fZombie)
  741. {
  742. Trace(1, "Error: Call of IDirectMusicSong::GetSegment after the song has been garbage collected. "
  743. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  744. "and then calling CollectGarbage or Release on the loader.");
  745. return DMUS_S_GARBAGE_COLLECTED;
  746. }
  747. HRESULT hr = S_FALSE;
  748. CSegment *pSegment;
  749. if (wszName)
  750. {
  751. V_BUFPTR_READ(wszName,2);
  752. EnterCriticalSection(&m_CriticalSection);
  753. pSegment = m_PlayList.GetHead();
  754. for (;pSegment;pSegment = pSegment->GetNext())
  755. {
  756. if (_wcsicmp(pSegment->m_wszName, wszName) == 0)
  757. {
  758. pSegment->AddRef();
  759. hr = S_OK;
  760. break;
  761. }
  762. }
  763. LeaveCriticalSection(&m_CriticalSection);
  764. }
  765. else
  766. {
  767. hr = GetPlaySegment( m_dwStartSegID,&pSegment);
  768. }
  769. if (hr == S_OK)
  770. {
  771. *ppSegment = static_cast<IDirectMusicSegment*>(pSegment);
  772. }
  773. else
  774. {
  775. #ifdef DBG
  776. if (wszName)
  777. {
  778. Trace(1,"Error: Unable to find segment %ls in song.\n",wszName);
  779. }
  780. else
  781. {
  782. Trace(1,"Error: Unable to find starting segment in the song.\n");
  783. }
  784. #endif
  785. }
  786. return hr;
  787. }
  788. STDMETHODIMP CSong::GetAudioPathConfig(IUnknown ** ppAudioPathConfig)
  789. {
  790. V_INAME(IDirectMusicSegment::GetAudioPathConfig);
  791. V_PTRPTR_WRITE(ppAudioPathConfig);
  792. if (m_fZombie)
  793. {
  794. Trace(1, "Error: Call of IDirectMusicSong::GetAudioPathConfig after the song has been garbage collected. "
  795. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  796. "and then calling CollectGarbage or Release on the loader.");
  797. return DMUS_S_GARBAGE_COLLECTED;
  798. }
  799. HRESULT hr;
  800. EnterCriticalSection(&m_CriticalSection);
  801. if (m_pAudioPathConfig)
  802. {
  803. hr = m_pAudioPathConfig->QueryInterface(IID_IUnknown,(void **)ppAudioPathConfig);
  804. }
  805. else
  806. {
  807. Trace(2,"Warning: No embedded audiopath configuration in the song.\n");
  808. hr = DMUS_E_NO_AUDIOPATH_CONFIG;
  809. }
  810. LeaveCriticalSection(&m_CriticalSection);
  811. return hr;
  812. }
  813. /////////////////////////////////////////////////////////////////////////////
  814. // IPersist
  815. HRESULT CSong::GetClassID( CLSID* pClassID )
  816. {
  817. V_INAME(CSong::GetClassID);
  818. V_PTR_WRITE(pClassID, CLSID);
  819. if (m_fZombie)
  820. {
  821. Trace(1, "Error: Call of IDirectMusicSong::GetClassID after the song has been garbage collected. "
  822. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  823. "and then calling CollectGarbage or Release on the loader.");
  824. return DMUS_S_GARBAGE_COLLECTED;
  825. }
  826. *pClassID = CLSID_DirectMusicSong;
  827. return S_OK;
  828. }
  829. /////////////////////////////////////////////////////////////////////////////
  830. // IPersistStream functions
  831. HRESULT CSong::IsDirty()
  832. {
  833. return E_NOTIMPL;
  834. }
  835. HRESULT CSong::Load( IStream* pIStream )
  836. {
  837. V_INAME(CSong::Load);
  838. V_INTERFACE(pIStream);
  839. // Song format temporarily turned off for DX8 release.
  840. return E_NOTIMPL;
  841. /*
  842. if (m_fZombie)
  843. {
  844. Trace(1, "Error: Call of IDirectMusicSong::Load after the song has been garbage collected. "
  845. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  846. "and then calling CollectGarbage or Release on the loader.");
  847. return DMUS_S_GARBAGE_COLLECTED;
  848. }
  849. // Create RIFF parser.
  850. CRiffParser Parser(pIStream);
  851. RIFFIO ckMain;
  852. HRESULT hr = S_OK;
  853. // First, clear the song in case it is being read into a second time.
  854. Clear();
  855. Parser.EnterList(&ckMain);
  856. if (Parser.NextChunk(&hr))
  857. {
  858. if (ckMain.fccType == DMUS_FOURCC_SONG_FORM)
  859. {
  860. EnterCriticalSection(&m_CriticalSection);
  861. RIFFIO ckNext;
  862. RIFFIO ckChild;
  863. IDirectMusicContainer *pContainer = NULL; // For handling embedded container with linked objects.
  864. Parser.EnterList(&ckNext);
  865. while(Parser.NextChunk(&hr))
  866. {
  867. switch(ckNext.ckid)
  868. {
  869. case DMUS_FOURCC_SONG_CHUNK:
  870. DMUS_IO_SONG_HEADER ioSongHdr;
  871. ioSongHdr.dwFlags = 0;
  872. hr = Parser.Read(&ioSongHdr, sizeof(DMUS_IO_SONG_HEADER));
  873. if(SUCCEEDED(hr))
  874. {
  875. m_dwFlags = ioSongHdr.dwFlags;
  876. m_dwStartSegID = ioSongHdr.dwStartSegID;
  877. }
  878. break;
  879. case DMUS_FOURCC_GUID_CHUNK:
  880. if( ckNext.cksize == sizeof(GUID) )
  881. {
  882. hr = Parser.Read(&m_guidObject, sizeof(GUID));
  883. if( SUCCEEDED(hr) )
  884. {
  885. m_dwValidData |= DMUS_OBJ_OBJECT;
  886. }
  887. }
  888. break;
  889. case DMUS_FOURCC_VERSION_CHUNK:
  890. hr = Parser.Read( &m_vVersion, sizeof(DMUS_VERSION) );
  891. if( SUCCEEDED(hr) )
  892. {
  893. m_dwValidData |= DMUS_OBJ_VERSION;
  894. }
  895. break;
  896. case DMUS_FOURCC_CATEGORY_CHUNK:
  897. hr = Parser.Read( m_wszCategory, sizeof(WCHAR)*DMUS_MAX_CATEGORY );
  898. if( SUCCEEDED(hr) )
  899. {
  900. m_dwValidData |= DMUS_OBJ_CATEGORY;
  901. }
  902. break;
  903. case DMUS_FOURCC_DATE_CHUNK:
  904. if( sizeof(FILETIME) == ckNext.cksize )
  905. {
  906. hr = Parser.Read( &m_ftDate, sizeof(FILETIME) );
  907. if( SUCCEEDED(hr) )
  908. {
  909. m_dwValidData |= DMUS_OBJ_DATE;
  910. }
  911. }
  912. break;
  913. case FOURCC_LIST:
  914. case FOURCC_RIFF:
  915. switch(ckNext.fccType)
  916. {
  917. case DMUS_FOURCC_UNFO_LIST:
  918. Parser.EnterList(&ckChild);
  919. while(Parser.NextChunk(&hr))
  920. {
  921. switch( ckChild.ckid )
  922. {
  923. case DMUS_FOURCC_UNAM_CHUNK:
  924. {
  925. hr = Parser.Read(&m_wszName, sizeof(m_wszName));
  926. if(SUCCEEDED(hr) )
  927. {
  928. m_dwValidData |= DMUS_OBJ_NAME;
  929. }
  930. break;
  931. }
  932. default:
  933. break;
  934. }
  935. }
  936. Parser.LeaveList();
  937. break;
  938. case DMUS_FOURCC_CONTAINER_FORM:
  939. // An embedded container RIFF chunk which includes a bunch
  940. // of objects referenced by the song. This should precede the
  941. // segments and gets loaded prior to them. Loading this
  942. // causes all of its objects to get SetObject'd in the loader,
  943. // so they later get pulled in as requested by the tracks in the segments.
  944. // After the tracks are loaded, the loader references are
  945. // released by a call to release the IDirectMusicContainer.
  946. {
  947. DMUS_OBJECTDESC Desc;
  948. IDirectMusicLoader *pLoader;
  949. IDirectMusicGetLoader *pGetLoader;
  950. HRESULT hr = pIStream->QueryInterface(IID_IDirectMusicGetLoader,(void **) &pGetLoader);
  951. if (SUCCEEDED(hr))
  952. {
  953. if (SUCCEEDED(pGetLoader->GetLoader(&pLoader)))
  954. {
  955. // Move back stream's current position
  956. Parser.SeekBack();
  957. Desc.dwSize = sizeof(Desc);
  958. Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_STREAM;
  959. Desc.guidClass = CLSID_DirectMusicContainer;
  960. Desc.pStream = pIStream;
  961. pLoader->GetObject(&Desc,IID_IDirectMusicContainer,(void **) &pContainer);
  962. if (pContainer)
  963. {
  964. // Don't cache the container object! We want it and the
  965. // objects it references to go away when the segment is done loading.
  966. IDirectMusicObject *pObject = NULL;
  967. pContainer->QueryInterface(IID_IDirectMusicObject,(void **)&pObject);
  968. if (pObject)
  969. {
  970. pLoader->ReleaseObject(pObject);
  971. pObject->Release();
  972. }
  973. }
  974. // Now, seek to the end of this chunk.
  975. Parser.SeekForward();
  976. pLoader->Release();
  977. }
  978. pGetLoader->Release();
  979. }
  980. }
  981. break;
  982. case DMUS_FOURCC_SONGSEGMENTS_LIST:
  983. hr = LoadSegmentList(&Parser); //pIStream, pIDirectMusicStream, ckNext);
  984. break;
  985. case DMUS_FOURCC_SEGREFS_LIST:
  986. hr = LoadVirtualSegmentList(&Parser);
  987. break;
  988. case DMUS_FOURCC_AUDIOPATH_FORM:
  989. // Move back to start of this chunk.
  990. Parser.SeekBack();
  991. hr = LoadAudioPath(pIStream);
  992. // Now, seek to the end of this chunk.
  993. Parser.SeekForward();
  994. break;
  995. default:
  996. break;
  997. }
  998. break;
  999. default:
  1000. break;
  1001. }
  1002. }
  1003. Parser.LeaveList();
  1004. LeaveCriticalSection(&m_CriticalSection);
  1005. if (pContainer)
  1006. {
  1007. pContainer->Release();
  1008. }
  1009. if( SUCCEEDED(hr) )
  1010. {
  1011. if( m_fPartialLoad & PARTIALLOAD_E_FAIL )
  1012. {
  1013. if( m_fPartialLoad & PARTIALLOAD_S_OK )
  1014. {
  1015. Trace(1,"Error: Song load was incomplete, some components failed loading.\n");
  1016. hr = DMUS_S_PARTIALLOAD;
  1017. }
  1018. else
  1019. {
  1020. Trace(1,"Error: Song load failed because all components failed loading.\n");
  1021. hr = DMUS_E_ALL_TRACKS_FAILED;
  1022. }
  1023. }
  1024. }
  1025. }
  1026. else
  1027. {
  1028. // Couldn't find the chunk header for a song.
  1029. // But, maybe this is actually a segment, in which case see if
  1030. // the segment object will load it.
  1031. CSegment *pSegment = new CSegment;
  1032. if (pSegment)
  1033. {
  1034. pSegment->AddRef(); // Segment::Load (and possibly others) may need the refcount
  1035. // Force the version so audiopath functionality will be supported.
  1036. pSegment->m_dwVersion = 8;
  1037. Parser.SeekBack();
  1038. hr = pSegment->Load(pIStream);
  1039. if (SUCCEEDED(hr))
  1040. {
  1041. DMUS_OBJECTDESC Desc;
  1042. Desc.dwSize = sizeof (Desc);
  1043. pSegment->GetDescriptor(&Desc);
  1044. Desc.guidClass = CLSID_DirectMusicSong;
  1045. SetDescriptor(&Desc);
  1046. // AddSegment addref's by one.
  1047. m_SegmentList.AddSegment(pSegment,0);
  1048. pSegment->GetAudioPathConfig((IUnknown **) &m_pAudioPathConfig);
  1049. m_dwStartSegID = 0; // Points to this segment.
  1050. CVirtualSegment *pVirtual = new CVirtualSegment;
  1051. if (pVirtual)
  1052. {
  1053. pVirtual->m_pSourceSegment = pSegment;
  1054. pSegment->AddRef();
  1055. pVirtual->m_SegHeader.dwRepeats = pSegment->m_dwRepeats;
  1056. pVirtual->m_SegHeader.dwResolution = pSegment->m_dwResolution;
  1057. pVirtual->m_SegHeader.mtLength = pSegment->m_mtLength;
  1058. pVirtual->m_SegHeader.mtLoopEnd = pSegment->m_mtLoopEnd;
  1059. pVirtual->m_SegHeader.mtLoopStart = pSegment->m_mtLoopStart;
  1060. pVirtual->m_SegHeader.mtPlayStart = pSegment->m_mtStart;
  1061. pVirtual->m_SegHeader.rtLength = pSegment->m_rtLength;
  1062. pVirtual->m_SegHeader.dwFlags = pSegment->m_dwSegFlags;
  1063. if (pSegment->m_dwValidData & DMUS_OBJ_NAME)
  1064. {
  1065. wcscpy(pVirtual->m_wszName,pSegment->m_wszName);
  1066. }
  1067. m_VirtualSegmentList.AddHead(pVirtual);
  1068. }
  1069. else
  1070. {
  1071. hr = E_OUTOFMEMORY;
  1072. }
  1073. pSegment->Release(); // release the initial AddRef
  1074. }
  1075. if (FAILED(hr))
  1076. {
  1077. delete pSegment;
  1078. }
  1079. }
  1080. else
  1081. {
  1082. hr = E_OUTOFMEMORY;
  1083. }
  1084. }
  1085. }
  1086. // If there are no virtual segments, clear the song and fail the load
  1087. if ( !m_VirtualSegmentList.GetHead() )
  1088. {
  1089. Clear();
  1090. hr = DMUS_E_NOT_INIT;
  1091. }
  1092. if (SUCCEEDED(hr)) Instantiate();
  1093. return hr;*/
  1094. }
  1095. HRESULT CSong::LoadAudioPath(IStream *pStream)
  1096. {
  1097. assert(pStream);
  1098. CAudioPathConfig *pPath = new CAudioPathConfig;
  1099. if (pPath == NULL) {
  1100. return E_OUTOFMEMORY;
  1101. }
  1102. HRESULT hr = pPath->Load(pStream);
  1103. EnterCriticalSection(&m_CriticalSection);
  1104. if(m_pAudioPathConfig)
  1105. {
  1106. m_pAudioPathConfig->Release();
  1107. }
  1108. m_pAudioPathConfig = pPath;
  1109. LeaveCriticalSection(&m_CriticalSection);
  1110. return hr;
  1111. }
  1112. HRESULT CSong::LoadReferencedSegment(CSegment **ppSegment, CRiffParser *pParser)
  1113. {
  1114. IDirectMusicLoader* pLoader = NULL;
  1115. IDirectMusicGetLoader *pIGetLoader;
  1116. HRESULT hr = pParser->GetStream()->QueryInterface( IID_IDirectMusicGetLoader,(void **) &pIGetLoader );
  1117. if (FAILED(hr)) return hr;
  1118. hr = pIGetLoader->GetLoader(&pLoader);
  1119. pIGetLoader->Release();
  1120. if (FAILED(hr)) return hr;
  1121. DMUS_OBJECTDESC desc;
  1122. ZeroMemory(&desc, sizeof(desc));
  1123. RIFFIO ckNext;
  1124. pParser->EnterList(&ckNext);
  1125. while(pParser->NextChunk(&hr))
  1126. {
  1127. switch(ckNext.ckid)
  1128. {
  1129. case DMUS_FOURCC_REF_CHUNK:
  1130. DMUS_IO_REFERENCE ioDMRef;
  1131. hr = pParser->Read(&ioDMRef, sizeof(DMUS_IO_REFERENCE));
  1132. if(SUCCEEDED(hr))
  1133. {
  1134. if (ioDMRef.guidClassID != CLSID_DirectMusicSegment)
  1135. {
  1136. Trace(1,"Error: Invalid segment reference in song.\n");
  1137. hr = DMUS_E_CANNOTREAD;
  1138. }
  1139. else
  1140. {
  1141. desc.guidClass = ioDMRef.guidClassID;
  1142. desc.dwValidData |= ioDMRef.dwValidData;
  1143. desc.dwValidData |= DMUS_OBJ_CLASS;
  1144. }
  1145. }
  1146. break;
  1147. case DMUS_FOURCC_GUID_CHUNK:
  1148. hr = pParser->Read(&(desc.guidObject), sizeof(GUID));
  1149. if(SUCCEEDED(hr))
  1150. {
  1151. desc.dwValidData |= DMUS_OBJ_OBJECT;
  1152. }
  1153. break;
  1154. case DMUS_FOURCC_NAME_CHUNK:
  1155. hr = pParser->Read(desc.wszName, sizeof(desc.wszName));
  1156. if(SUCCEEDED(hr))
  1157. {
  1158. desc.dwValidData |= DMUS_OBJ_NAME;
  1159. }
  1160. break;
  1161. case DMUS_FOURCC_FILE_CHUNK:
  1162. hr = pParser->Read(desc.wszFileName, sizeof(desc.wszFileName));
  1163. if(SUCCEEDED(hr))
  1164. {
  1165. desc.dwValidData |= DMUS_OBJ_FILENAME;
  1166. }
  1167. break;
  1168. case DMUS_FOURCC_CATEGORY_CHUNK:
  1169. hr = pParser->Read(desc.wszCategory, sizeof(desc.wszCategory));
  1170. if(SUCCEEDED(hr))
  1171. {
  1172. desc.dwValidData |= DMUS_OBJ_CATEGORY;
  1173. }
  1174. break;
  1175. default:
  1176. break;
  1177. }
  1178. }
  1179. pParser->LeaveList();
  1180. if(SUCCEEDED(hr))
  1181. {
  1182. desc.dwSize = sizeof(DMUS_OBJECTDESC);
  1183. hr = pLoader->GetObject(&desc, IID_CSegment, (void**)ppSegment);
  1184. // Once we get the object, we need to ensure that the same object is never
  1185. // connected up to any other songs (or this one, too.)
  1186. // So, we ensure that the loader doesn't keep it around.
  1187. if (SUCCEEDED(hr))
  1188. {
  1189. IDirectMusicObject *pObject;
  1190. if (SUCCEEDED((*ppSegment)->QueryInterface(IID_IDirectMusicObject,(void **)&pObject)))
  1191. {
  1192. pLoader->ReleaseObject(pObject);
  1193. pObject->Release();
  1194. }
  1195. // If the segment has a next pointer, it still must be in another song. This
  1196. // should never happen, but being paranoid...
  1197. if ((*ppSegment)->GetNext())
  1198. {
  1199. *ppSegment = NULL;
  1200. hr = E_FAIL;
  1201. TraceI(0,"Error: Attempt to load song segment that is already referenced by another song. \n");
  1202. }
  1203. }
  1204. }
  1205. if (pLoader)
  1206. {
  1207. pLoader->Release();
  1208. }
  1209. return hr;
  1210. }
  1211. HRESULT CSong::LoadSegmentList(CRiffParser *pParser)
  1212. {
  1213. assert(pParser);
  1214. RIFFIO ckNext, ckChild;
  1215. DWORD dwSegmentCount = 0;
  1216. HRESULT hr = S_OK;
  1217. pParser->EnterList(&ckNext);
  1218. while(pParser->NextChunk(&hr))
  1219. {
  1220. switch(ckNext.ckid)
  1221. {
  1222. case FOURCC_LIST:
  1223. if (ckNext.fccType == DMUS_FOURCC_SONGSEGMENT_LIST)
  1224. {
  1225. pParser->EnterList(&ckChild);
  1226. while (pParser->NextChunk(&hr))
  1227. {
  1228. switch(ckChild.ckid)
  1229. {
  1230. case FOURCC_RIFF:
  1231. case FOURCC_LIST:
  1232. if ((ckChild.fccType == DMUS_FOURCC_SEGMENT_FORM) ||
  1233. (ckChild.fccType == DMUS_FOURCC_REF_LIST))
  1234. {
  1235. CSegment *pSegment = NULL;
  1236. if (ckChild.fccType == DMUS_FOURCC_SEGMENT_FORM)
  1237. {
  1238. pSegment = new CSegment;
  1239. if (pSegment)
  1240. {
  1241. pSegment->AddRef(); // Segment::Load may need a refcount
  1242. // Force the version so audiopath functionality will be supported.
  1243. pSegment->m_dwVersion = 8;
  1244. // Move back to start of this chunk.
  1245. pParser->SeekBack();
  1246. hr = pSegment->Load(pParser->GetStream());
  1247. pParser->SeekForward();
  1248. }
  1249. else
  1250. {
  1251. return E_OUTOFMEMORY;
  1252. }
  1253. }
  1254. else
  1255. {
  1256. // This will increment the refcount for the segment
  1257. hr = LoadReferencedSegment( &pSegment, pParser );
  1258. }
  1259. if (SUCCEEDED(hr))
  1260. {
  1261. // This increments the refcount.
  1262. m_SegmentList.AddSegment(pSegment,dwSegmentCount);
  1263. }
  1264. pSegment->Release(); // Release the extra AddRef
  1265. dwSegmentCount++;
  1266. if(SUCCEEDED(hr) && hr != DMUS_S_PARTIALLOAD)
  1267. {
  1268. m_fPartialLoad |= PARTIALLOAD_S_OK;
  1269. }
  1270. else
  1271. {
  1272. m_fPartialLoad |= PARTIALLOAD_E_FAIL;
  1273. hr = S_OK;
  1274. }
  1275. }
  1276. break;
  1277. }
  1278. }
  1279. pParser->LeaveList();
  1280. }
  1281. default:
  1282. break;
  1283. }
  1284. }
  1285. pParser->LeaveList();
  1286. return hr;
  1287. }
  1288. HRESULT CSong::LoadGraphList(CRiffParser *pParser)
  1289. {
  1290. RIFFIO ckNext;
  1291. DWORD dwGraphCount = 0;
  1292. HRESULT hr = S_OK;
  1293. pParser->EnterList(&ckNext);
  1294. while(pParser->NextChunk(&hr))
  1295. {
  1296. switch(ckNext.ckid)
  1297. {
  1298. case FOURCC_RIFF:
  1299. switch(ckNext.fccType)
  1300. {
  1301. CGraph *pGraph;
  1302. case DMUS_FOURCC_TOOLGRAPH_FORM :
  1303. // Move back to start of this chunk.
  1304. pParser->SeekBack();
  1305. pGraph = new CGraph;
  1306. if (pGraph)
  1307. {
  1308. hr = pGraph->Load(pParser->GetStream());
  1309. dwGraphCount++;
  1310. if (SUCCEEDED(hr))
  1311. {
  1312. m_GraphList.AddTail(pGraph);
  1313. pGraph->m_dwLoadID = dwGraphCount;
  1314. }
  1315. if(SUCCEEDED(hr) && hr != DMUS_S_PARTIALLOAD)
  1316. {
  1317. m_fPartialLoad |= PARTIALLOAD_S_OK;
  1318. }
  1319. else
  1320. {
  1321. m_fPartialLoad |= PARTIALLOAD_E_FAIL;
  1322. hr = S_OK;
  1323. }
  1324. }
  1325. else
  1326. {
  1327. return E_OUTOFMEMORY;
  1328. }
  1329. pParser->SeekForward();
  1330. break;
  1331. default:
  1332. break;
  1333. }
  1334. break;
  1335. default:
  1336. break;
  1337. }
  1338. }
  1339. pParser->LeaveList();
  1340. return hr;
  1341. }
  1342. HRESULT CSong::GetTransitionSegment(CSegment *pSource, CSegment *pDestination,
  1343. DMUS_IO_TRANSITION_DEF *pTransDef)
  1344. {
  1345. HRESULT hr = DMUS_E_NOT_FOUND;
  1346. // if (pSource) Trace(0,"Transitioning from %ls ",pSource->m_wszName);
  1347. // if (pDestination) Trace(0,"to %ls",pDestination->m_wszName);
  1348. // Trace(0,"\n");
  1349. EnterCriticalSection(&m_CriticalSection);
  1350. // Default values for other fields, in case we don't find a match.
  1351. pTransDef->dwPlayFlags = 0;
  1352. pTransDef->dwTransitionID = DMUS_SONG_NOSEG;
  1353. pTransDef->dwSegmentID = DMUS_SONG_NOSEG;
  1354. CVirtualSegment *pVSource = NULL;
  1355. // If there is a source segment, look to see if it's in this song
  1356. // and pull the matchin virtual segment.
  1357. if (pSource)
  1358. {
  1359. pVSource = m_VirtualSegmentList.GetHead();
  1360. for (;pVSource;pVSource = pVSource->GetNext())
  1361. {
  1362. if (pVSource->m_pPlaySegment == pSource)
  1363. {
  1364. // Trace(0,"Found match for source segment %ls in song\n",pSource->m_wszName);
  1365. break;
  1366. }
  1367. }
  1368. }
  1369. CVirtualSegment *pVDestination = NULL;
  1370. // If there is a destination segment, look to see if it's in this song
  1371. // and pull the matching virtual segment.
  1372. if (pDestination)
  1373. {
  1374. pVDestination = m_VirtualSegmentList.GetHead();
  1375. for (;pVDestination;pVDestination = pVDestination->GetNext())
  1376. {
  1377. if (pVDestination->m_pPlaySegment == pDestination)
  1378. {
  1379. // Trace(0,"Found match for destination segment %ls in song\n",pDestination->m_wszName);
  1380. break;
  1381. }
  1382. }
  1383. }
  1384. if (pVSource)
  1385. {
  1386. if (pVDestination)
  1387. {
  1388. pTransDef->dwSegmentID = pVDestination->m_dwID;
  1389. }
  1390. else
  1391. {
  1392. // If there is no destination, mark this to transition to nothing.
  1393. pTransDef->dwSegmentID = DMUS_SONG_NOSEG;
  1394. }
  1395. if (pVSource->m_dwTransitionCount)
  1396. {
  1397. ASSERT(pVSource->m_pTransitions);
  1398. DWORD dwIndex;
  1399. DWORD dwMatchCount = 0;
  1400. // First, find out how many transitions match the requirement.
  1401. // We'll randomly select from the matching ones.
  1402. for (dwIndex = 0; dwIndex < pVSource->m_dwTransitionCount; dwIndex++)
  1403. {
  1404. if (pVSource->m_pTransitions[dwIndex].dwSegmentID == pTransDef->dwSegmentID)
  1405. {
  1406. dwMatchCount++;
  1407. }
  1408. }
  1409. DWORD dwChoice;
  1410. if (dwMatchCount)
  1411. {
  1412. dwChoice = rand() % dwMatchCount;
  1413. }
  1414. for (dwIndex = 0; dwIndex < pVSource->m_dwTransitionCount; dwIndex++)
  1415. {
  1416. if (pVSource->m_pTransitions[dwIndex].dwSegmentID == pTransDef->dwSegmentID)
  1417. {
  1418. if (!dwChoice)
  1419. {
  1420. //Trace(0,"Chose transition from %lx with Transition %lx, flags %lx\n",pVSource->m_pTransitions[dwIndex].dwSegmentID,
  1421. // pVSource->m_pTransitions[dwIndex].dwTransitionID,pVSource->m_pTransitions[dwIndex].dwPlayFlags);
  1422. pTransDef->dwPlayFlags = pVSource->m_pTransitions[dwIndex].dwPlayFlags;
  1423. pTransDef->dwTransitionID = pVSource->m_pTransitions[dwIndex].dwTransitionID;
  1424. hr = S_OK;
  1425. break;
  1426. }
  1427. dwChoice--;
  1428. }
  1429. else if ((pVSource->m_pTransitions[dwIndex].dwSegmentID == DMUS_SONG_ANYSEG) && !dwMatchCount)
  1430. {
  1431. // Mark the segment and flags, but don't break because we might still have the matched segment in the list.
  1432. pTransDef->dwPlayFlags = pVSource->m_pTransitions[dwIndex].dwPlayFlags;
  1433. pTransDef->dwTransitionID = pVSource->m_pTransitions[dwIndex].dwTransitionID;
  1434. //Trace(0,"Found default transition from %lx with Transition %lx, flags %lx\n",pVSource->m_pTransitions[dwIndex].dwSegmentID,
  1435. // pVSource->m_pTransitions[dwIndex].dwTransitionID,pVSource->m_pTransitions[dwIndex].dwPlayFlags);
  1436. hr = S_OK;
  1437. break;
  1438. }
  1439. }
  1440. }
  1441. }
  1442. else if (pVDestination)
  1443. {
  1444. // This is the special case where there is no source segment, perhaps because we are starting
  1445. // playback or we are starting from a different song. In this case, look for a transition in the destination
  1446. // segment for the special case of DMUS_SONG_NOFROMSEG. Typically, this represents a transition
  1447. // segment that is an intro.
  1448. if (pVDestination->m_dwTransitionCount)
  1449. {
  1450. ASSERT(pVDestination->m_pTransitions);
  1451. DWORD dwIndex;
  1452. DWORD dwMatchCount = 0;
  1453. // First, find out how many transitions match the requirement.
  1454. // We'll randomly select from the matching ones.
  1455. for (dwIndex = 0; dwIndex < pVDestination->m_dwTransitionCount; dwIndex++)
  1456. {
  1457. if (pVDestination->m_pTransitions[dwIndex].dwSegmentID == DMUS_SONG_NOFROMSEG)
  1458. {
  1459. dwMatchCount++;
  1460. }
  1461. }
  1462. DWORD dwChoice;
  1463. if (dwMatchCount)
  1464. {
  1465. dwChoice = rand() % dwMatchCount;
  1466. }
  1467. for (dwIndex = 0; dwIndex < pVDestination->m_dwTransitionCount; dwIndex++)
  1468. {
  1469. if (pVDestination->m_pTransitions[dwIndex].dwSegmentID == DMUS_SONG_NOFROMSEG)
  1470. {
  1471. if (!dwChoice)
  1472. {
  1473. //Trace(0,"Chose transition from NONE with Transition %lx, flags %lx\n",
  1474. // pVDestination->m_pTransitions[dwIndex].dwTransitionID,pVDestination->m_pTransitions[dwIndex].dwPlayFlags);
  1475. pTransDef->dwPlayFlags = pVDestination->m_pTransitions[dwIndex].dwPlayFlags;
  1476. pTransDef->dwTransitionID = pVDestination->m_pTransitions[dwIndex].dwTransitionID;
  1477. hr = S_OK;
  1478. break;
  1479. }
  1480. dwChoice--;
  1481. }
  1482. }
  1483. }
  1484. }
  1485. LeaveCriticalSection(&m_CriticalSection);
  1486. #ifdef DBG
  1487. if (hr == DMUS_E_NOT_FOUND)
  1488. {
  1489. Trace(2,"Warning: No transition segment was found in song.\n");
  1490. }
  1491. #endif
  1492. return hr;
  1493. }
  1494. void CSong::GetSourceSegment(CSegment **ppSegment,DWORD dwSegmentID)
  1495. {
  1496. CSongSegment *pSongSegment = m_SegmentList.GetHead();
  1497. while (pSongSegment)
  1498. {
  1499. if (pSongSegment->m_dwLoadID == dwSegmentID)
  1500. {
  1501. if (pSongSegment->m_pSegment)
  1502. {
  1503. pSongSegment->m_pSegment->AddRef();
  1504. *ppSegment = pSongSegment->m_pSegment;
  1505. return;
  1506. }
  1507. }
  1508. pSongSegment = pSongSegment->GetNext();
  1509. }
  1510. }
  1511. void CSong::GetGraph(CGraph **ppGraph,DWORD dwGraphID)
  1512. {
  1513. CGraph *pGraph = m_GraphList.GetHead();
  1514. while (pGraph)
  1515. {
  1516. if (pGraph->m_dwLoadID == dwGraphID)
  1517. {
  1518. pGraph->AddRef();
  1519. *ppGraph = pGraph;
  1520. return;
  1521. }
  1522. pGraph = pGraph->GetNext();
  1523. }
  1524. }
  1525. BOOL CSong::GetSegmentTrack(IDirectMusicTrack **ppTrack,DWORD dwSegmentID,DWORD dwGroupBits,DWORD dwIndex,REFGUID guidClassID)
  1526. {
  1527. CSongSegment *pSongSegment = m_SegmentList.GetHead();
  1528. while (pSongSegment)
  1529. {
  1530. if (pSongSegment->m_dwLoadID == dwSegmentID)
  1531. {
  1532. if (pSongSegment->m_pSegment)
  1533. {
  1534. return (pSongSegment->m_pSegment->GetTrack(guidClassID,dwGroupBits,dwIndex,ppTrack) == S_OK);
  1535. }
  1536. }
  1537. pSongSegment = pSongSegment->GetNext();
  1538. }
  1539. return FALSE;
  1540. }
  1541. HRESULT CSong::LoadVirtualSegmentList(CRiffParser *pParser)
  1542. {
  1543. RIFFIO ckNext;
  1544. RIFFIO ckChild;
  1545. RIFFIO ckUNFO;
  1546. DWORD dwSegmentCount = 0;
  1547. CVirtualSegment *pVirtualSegment;
  1548. MUSIC_TIME mtTime = 0;
  1549. HRESULT hr = S_OK;
  1550. pParser->EnterList(&ckNext);
  1551. while(pParser->NextChunk(&hr))
  1552. {
  1553. switch(ckNext.ckid)
  1554. {
  1555. case FOURCC_RIFF:
  1556. case FOURCC_LIST:
  1557. switch(ckNext.fccType)
  1558. {
  1559. case DMUS_FOURCC_SEGREF_LIST:
  1560. pVirtualSegment = new CVirtualSegment;
  1561. if (pVirtualSegment)
  1562. {
  1563. BOOL fGotHeader = FALSE;
  1564. BOOL fGotSegmentHeader = FALSE;
  1565. pVirtualSegment->m_mtTime = mtTime; // Give the start time, an accumulation of all preceding segments.
  1566. pParser->EnterList(&ckChild);
  1567. while(pParser->NextChunk(&hr))
  1568. {
  1569. switch( ckChild.ckid )
  1570. {
  1571. case FOURCC_RIFF:
  1572. case FOURCC_LIST:
  1573. switch(ckChild.fccType)
  1574. {
  1575. case DMUS_FOURCC_TRACKREFS_LIST:
  1576. hr = LoadTrackRefList(pParser, pVirtualSegment);
  1577. break;
  1578. case DMUS_FOURCC_UNFO_LIST:
  1579. pParser->EnterList(&ckUNFO);
  1580. while(pParser->NextChunk(&hr))
  1581. {
  1582. switch( ckUNFO.ckid )
  1583. {
  1584. case DMUS_FOURCC_UNAM_CHUNK:
  1585. {
  1586. hr = pParser->Read(pVirtualSegment->m_wszName, sizeof(pVirtualSegment->m_wszName));
  1587. break;
  1588. }
  1589. default:
  1590. break;
  1591. }
  1592. }
  1593. pParser->LeaveList();
  1594. }
  1595. break;
  1596. case DMUS_FOURCC_SEGREF_CHUNK:
  1597. {
  1598. DMUS_IO_SEGREF_HEADER ioVirtualSegment;
  1599. hr = pParser->Read(&ioVirtualSegment,sizeof(ioVirtualSegment));
  1600. if(SUCCEEDED(hr) )
  1601. {
  1602. pVirtualSegment->m_dwFlags = ioVirtualSegment.dwFlags;
  1603. pVirtualSegment->m_dwID = ioVirtualSegment.dwID;
  1604. pVirtualSegment->m_dwNextPlayID = ioVirtualSegment.dwNextPlayID;
  1605. if (ioVirtualSegment.dwSegmentID != DMUS_SONG_NOSEG)
  1606. {
  1607. GetSourceSegment(&pVirtualSegment->m_pSourceSegment,ioVirtualSegment.dwSegmentID);
  1608. }
  1609. if (ioVirtualSegment.dwToolGraphID != DMUS_SONG_NOSEG)
  1610. {
  1611. GetGraph(&pVirtualSegment->m_pGraph,ioVirtualSegment.dwToolGraphID);
  1612. }
  1613. fGotHeader = TRUE;
  1614. }
  1615. break;
  1616. }
  1617. case DMUS_FOURCC_SEGTRANS_CHUNK:
  1618. {
  1619. DWORD dwTransCount;
  1620. dwTransCount = ckChild.cksize / sizeof(DMUS_IO_TRANSITION_DEF);
  1621. if (dwTransCount > 0)
  1622. {
  1623. pVirtualSegment->m_pTransitions = new DMUS_IO_TRANSITION_DEF[dwTransCount];
  1624. if (pVirtualSegment->m_pTransitions)
  1625. {
  1626. pVirtualSegment->m_dwTransitionCount = dwTransCount;
  1627. hr = pParser->Read(pVirtualSegment->m_pTransitions,sizeof(DMUS_IO_TRANSITION_DEF)*dwTransCount);
  1628. }
  1629. else
  1630. {
  1631. return E_OUTOFMEMORY;
  1632. }
  1633. }
  1634. }
  1635. break;
  1636. case DMUS_FOURCC_SEGMENT_CHUNK:
  1637. fGotSegmentHeader = TRUE;
  1638. hr = pParser->Read(&pVirtualSegment->m_SegHeader, sizeof(DMUS_IO_SEGMENT_HEADER));
  1639. mtTime += (pVirtualSegment->m_SegHeader.dwRepeats * (pVirtualSegment->m_SegHeader.mtLoopEnd - pVirtualSegment->m_SegHeader.mtLoopStart)) +
  1640. pVirtualSegment->m_SegHeader.mtLength - pVirtualSegment->m_SegHeader.mtPlayStart;
  1641. default:
  1642. break;
  1643. }
  1644. }
  1645. pParser->LeaveList();
  1646. if (fGotHeader && fGotSegmentHeader)
  1647. {
  1648. //Trace(0,"Adding VSegment %ls with ID %ld to song.\n",pVirtualSegment->m_wszName,pVirtualSegment->m_dwID);
  1649. m_VirtualSegmentList.AddTail(pVirtualSegment);
  1650. }
  1651. else
  1652. {
  1653. delete pVirtualSegment;
  1654. }
  1655. break;
  1656. }
  1657. else
  1658. {
  1659. return E_OUTOFMEMORY;
  1660. }
  1661. break;
  1662. default:
  1663. break;
  1664. }
  1665. break;
  1666. default:
  1667. break;
  1668. }
  1669. }
  1670. pParser->LeaveList();
  1671. return hr;
  1672. }
  1673. struct ClassGuidCounts
  1674. {
  1675. GUID guidClass;
  1676. DWORD dwCount;
  1677. };
  1678. HRESULT CSong::LoadTrackRefList(CRiffParser *pParser,CVirtualSegment *pVirtualSegment)
  1679. {
  1680. RIFFIO ckNext;
  1681. RIFFIO ckChild;
  1682. HRESULT hr = S_OK;
  1683. TList<ClassGuidCounts> GuidCountList;
  1684. pParser->EnterList(&ckNext);
  1685. while(pParser->NextChunk(&hr))
  1686. {
  1687. switch(ckNext.ckid)
  1688. {
  1689. case FOURCC_LIST:
  1690. switch(ckNext.fccType)
  1691. {
  1692. CTrack *pTrack;
  1693. case DMUS_FOURCC_TRACKREF_LIST :
  1694. pTrack = new CTrack;
  1695. if (pTrack)
  1696. {
  1697. TListItem<ClassGuidCounts>* pCountItem = NULL;
  1698. DMUS_IO_TRACKREF_HEADER ioTrackRef;
  1699. DMUS_IO_TRACK_HEADER ioTrackHdr;
  1700. DMUS_IO_TRACK_EXTRAS_HEADER ioTrackExtrasHdr;
  1701. ioTrackExtrasHdr.dwPriority = 0;
  1702. ioTrackExtrasHdr.dwFlags = DMUS_TRACKCONFIG_DEFAULT;
  1703. ioTrackHdr.dwPosition = 0;
  1704. BOOL fGotHeader = FALSE;
  1705. BOOL fGotRef = FALSE;
  1706. pParser->EnterList(&ckChild);
  1707. while(pParser->NextChunk(&hr))
  1708. {
  1709. switch( ckChild.ckid )
  1710. {
  1711. case DMUS_FOURCC_TRACKREF_CHUNK:
  1712. {
  1713. hr = pParser->Read(&ioTrackRef, sizeof(ioTrackRef));
  1714. fGotRef = SUCCEEDED(hr);
  1715. break;
  1716. }
  1717. case DMUS_FOURCC_TRACK_CHUNK:
  1718. {
  1719. hr = pParser->Read(&ioTrackHdr, sizeof(ioTrackHdr));
  1720. fGotHeader = SUCCEEDED(hr);
  1721. pTrack->m_guidClassID = ioTrackHdr.guidClassID;
  1722. pTrack->m_dwGroupBits = ioTrackHdr.dwGroup;
  1723. pTrack->m_dwPosition = ioTrackHdr.dwPosition;
  1724. break;
  1725. }
  1726. case DMUS_FOURCC_TRACK_EXTRAS_CHUNK:
  1727. {
  1728. hr = pParser->Read(&ioTrackExtrasHdr, sizeof(ioTrackExtrasHdr));
  1729. pTrack->m_dwPriority = ioTrackExtrasHdr.dwPriority;
  1730. pTrack->m_dwFlags = ioTrackExtrasHdr.dwFlags;
  1731. break;
  1732. }
  1733. default:
  1734. break;
  1735. }
  1736. }
  1737. pParser->LeaveList();
  1738. if (fGotHeader && fGotRef)
  1739. {
  1740. if (ioTrackRef.dwSegmentID != DMUS_SONG_NOSEG)
  1741. {
  1742. DWORD dwID = 0;
  1743. for (pCountItem = GuidCountList.GetHead(); pCountItem; pCountItem = pCountItem->GetNext())
  1744. {
  1745. if (pCountItem->GetItemValue().guidClass == pTrack->m_guidClassID)
  1746. {
  1747. break;
  1748. }
  1749. }
  1750. if (pCountItem)
  1751. {
  1752. dwID = pCountItem->GetItemValue().dwCount;
  1753. }
  1754. fGotHeader = GetSegmentTrack(&pTrack->m_pTrack,ioTrackRef.dwSegmentID,pTrack->m_dwGroupBits,dwID,pTrack->m_guidClassID);
  1755. }
  1756. }
  1757. if (fGotHeader && pTrack->m_pTrack)
  1758. {
  1759. pTrack->m_pTrack->QueryInterface(IID_IDirectMusicTrack8,(void **) &pTrack->m_pTrack8);
  1760. // Add the track based on position.
  1761. CTrack* pScan = pVirtualSegment->m_TrackList.GetHead();
  1762. CTrack* pPrevTrack = NULL;
  1763. for (; pScan; pScan = pScan->GetNext())
  1764. {
  1765. if (pTrack->Less(pScan))
  1766. {
  1767. break;
  1768. }
  1769. pPrevTrack = pScan;
  1770. }
  1771. if (pPrevTrack)
  1772. {
  1773. pPrevTrack->SetNext(pTrack);
  1774. pTrack->SetNext(pScan);
  1775. }
  1776. else
  1777. {
  1778. pVirtualSegment->m_TrackList.AddHead( pTrack );
  1779. }
  1780. if (pCountItem)
  1781. {
  1782. pCountItem->GetItemValue().dwCount++;
  1783. }
  1784. else
  1785. {
  1786. TListItem<ClassGuidCounts>* pNew = new TListItem<ClassGuidCounts>;
  1787. if (pNew)
  1788. {
  1789. pNew->GetItemValue().dwCount = 1;
  1790. pNew->GetItemValue().guidClass = pTrack->m_guidClassID;
  1791. GuidCountList.AddHead(pNew);
  1792. }
  1793. else return E_OUTOFMEMORY;
  1794. }
  1795. }
  1796. else
  1797. {
  1798. delete pTrack;
  1799. }
  1800. break;
  1801. }
  1802. else
  1803. {
  1804. return E_OUTOFMEMORY;
  1805. }
  1806. break;
  1807. default:
  1808. break;
  1809. }
  1810. break;
  1811. default:
  1812. break;
  1813. }
  1814. }
  1815. pParser->LeaveList();
  1816. return hr;
  1817. }
  1818. HRESULT CSong::Save( IStream* pIStream, BOOL fClearDirty )
  1819. {
  1820. return E_NOTIMPL;
  1821. }
  1822. HRESULT CSong::GetSizeMax( ULARGE_INTEGER FAR* pcbSize )
  1823. {
  1824. return E_NOTIMPL;
  1825. }
  1826. /////////////////////////////////////////////////////////////////////////////
  1827. // IDirectMusicObject
  1828. STDMETHODIMP CSong::GetDescriptor(LPDMUS_OBJECTDESC pDesc)
  1829. {
  1830. // Argument validation
  1831. V_INAME(CSong::GetDescriptor);
  1832. V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC);
  1833. if (m_fZombie)
  1834. {
  1835. Trace(1, "Error: Call of IDirectMusicSong::GetDescriptor after the song has been garbage collected. "
  1836. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  1837. "and then calling CollectGarbage or Release on the loader.");
  1838. return DMUS_S_GARBAGE_COLLECTED;
  1839. }
  1840. memset( pDesc, 0, sizeof(DMUS_OBJECTDESC));
  1841. pDesc->dwSize = sizeof(DMUS_OBJECTDESC);
  1842. pDesc->guidClass = CLSID_DirectMusicSong;
  1843. pDesc->guidObject = m_guidObject;
  1844. pDesc->ftDate = m_ftDate;
  1845. pDesc->vVersion = m_vVersion;
  1846. memcpy( pDesc->wszName, m_wszName, sizeof(m_wszName) );
  1847. memcpy( pDesc->wszCategory, m_wszCategory, sizeof(m_wszCategory) );
  1848. memcpy( pDesc->wszFileName, m_wszFileName, sizeof(m_wszFileName) );
  1849. pDesc->dwValidData = ( m_dwValidData | DMUS_OBJ_CLASS );
  1850. return S_OK;
  1851. }
  1852. STDMETHODIMP CSong::SetDescriptor(LPDMUS_OBJECTDESC pDesc)
  1853. {
  1854. // Argument validation
  1855. V_INAME(CSong::SetDescriptor);
  1856. V_STRUCTPTR_READ(pDesc, DMUS_OBJECTDESC);
  1857. if (m_fZombie)
  1858. {
  1859. Trace(1, "Error: Call of IDirectMusicSong::SetDescriptor after the song has been garbage collected. "
  1860. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  1861. "and then calling CollectGarbage or Release on the loader.");
  1862. return DMUS_S_GARBAGE_COLLECTED;
  1863. }
  1864. HRESULT hr = E_INVALIDARG;
  1865. DWORD dw = 0;
  1866. if( pDesc->dwSize >= sizeof(DMUS_OBJECTDESC) )
  1867. {
  1868. if( pDesc->dwValidData & DMUS_OBJ_OBJECT )
  1869. {
  1870. m_guidObject = pDesc->guidObject;
  1871. dw |= DMUS_OBJ_OBJECT;
  1872. }
  1873. if( pDesc->dwValidData & DMUS_OBJ_NAME )
  1874. {
  1875. memcpy( m_wszName, pDesc->wszName, sizeof(WCHAR)*DMUS_MAX_NAME );
  1876. dw |= DMUS_OBJ_NAME;
  1877. }
  1878. if( pDesc->dwValidData & DMUS_OBJ_CATEGORY )
  1879. {
  1880. memcpy( m_wszCategory, pDesc->wszCategory, sizeof(WCHAR)*DMUS_MAX_CATEGORY );
  1881. dw |= DMUS_OBJ_CATEGORY;
  1882. }
  1883. if( ( pDesc->dwValidData & DMUS_OBJ_FILENAME ) ||
  1884. ( pDesc->dwValidData & DMUS_OBJ_FULLPATH ) )
  1885. {
  1886. memcpy( m_wszFileName, pDesc->wszFileName, sizeof(WCHAR)*DMUS_MAX_FILENAME );
  1887. dw |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH));
  1888. }
  1889. if( pDesc->dwValidData & DMUS_OBJ_VERSION )
  1890. {
  1891. m_vVersion = pDesc->vVersion;
  1892. dw |= DMUS_OBJ_VERSION;
  1893. }
  1894. if( pDesc->dwValidData & DMUS_OBJ_DATE )
  1895. {
  1896. m_ftDate = pDesc->ftDate;
  1897. dw |= DMUS_OBJ_DATE;
  1898. }
  1899. m_dwValidData |= dw;
  1900. if( pDesc->dwValidData & (~dw) )
  1901. {
  1902. Trace(2,"Warning: Song::SetDescriptor was not able to handle all passed fields, dwValidData bits %lx.\n",pDesc->dwValidData & (~dw));
  1903. hr = S_FALSE; // there were extra fields we didn't parse;
  1904. pDesc->dwValidData = dw;
  1905. }
  1906. else
  1907. {
  1908. hr = S_OK;
  1909. }
  1910. }
  1911. return hr;
  1912. }
  1913. STDMETHODIMP CSong::ParseDescriptor(LPSTREAM pIStream, LPDMUS_OBJECTDESC pDesc)
  1914. {
  1915. V_INAME(CSong::ParseDescriptor);
  1916. V_INTERFACE(pIStream);
  1917. V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC);
  1918. if (m_fZombie)
  1919. {
  1920. Trace(1, "Error: Call of IDirectMusicSong::ParseDescriptor after the song has been garbage collected. "
  1921. "It is invalid to continue using a song after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  1922. "and then calling CollectGarbage or Release on the loader.");
  1923. return DMUS_S_GARBAGE_COLLECTED;
  1924. }
  1925. CRiffParser Parser(pIStream);
  1926. RIFFIO ckMain;
  1927. RIFFIO ckNext;
  1928. RIFFIO ckUNFO;
  1929. HRESULT hr = S_OK;
  1930. Parser.EnterList(&ckMain);
  1931. if (Parser.NextChunk(&hr) && (ckMain.fccType == DMUS_FOURCC_SONG_FORM))
  1932. {
  1933. pDesc->dwValidData = DMUS_OBJ_CLASS;
  1934. pDesc->guidClass = CLSID_DirectMusicSong;
  1935. Parser.EnterList(&ckNext);
  1936. while(Parser.NextChunk(&hr))
  1937. {
  1938. switch(ckNext.ckid)
  1939. {
  1940. case DMUS_FOURCC_GUID_CHUNK:
  1941. hr = Parser.Read( &pDesc->guidObject, sizeof(GUID) );
  1942. if( SUCCEEDED(hr) )
  1943. {
  1944. pDesc->dwValidData |= DMUS_OBJ_OBJECT;
  1945. }
  1946. break;
  1947. case DMUS_FOURCC_VERSION_CHUNK:
  1948. hr = Parser.Read( &pDesc->vVersion, sizeof(DMUS_VERSION) );
  1949. if( SUCCEEDED(hr) )
  1950. {
  1951. pDesc->dwValidData |= DMUS_OBJ_VERSION;
  1952. }
  1953. break;
  1954. case DMUS_FOURCC_CATEGORY_CHUNK:
  1955. hr = Parser.Read( &pDesc->wszCategory, sizeof(pDesc->wszCategory) );
  1956. if( SUCCEEDED(hr) )
  1957. {
  1958. pDesc->dwValidData |= DMUS_OBJ_CATEGORY;
  1959. }
  1960. break;
  1961. case DMUS_FOURCC_DATE_CHUNK:
  1962. hr = Parser.Read( &pDesc->ftDate, sizeof(FILETIME) );
  1963. if( SUCCEEDED(hr))
  1964. {
  1965. pDesc->dwValidData |= DMUS_OBJ_DATE;
  1966. }
  1967. break;
  1968. case FOURCC_LIST:
  1969. switch(ckNext.fccType)
  1970. {
  1971. case DMUS_FOURCC_UNFO_LIST:
  1972. Parser.EnterList(&ckUNFO);
  1973. while (Parser.NextChunk(&hr))
  1974. {
  1975. switch( ckUNFO.ckid )
  1976. {
  1977. case DMUS_FOURCC_UNAM_CHUNK:
  1978. {
  1979. hr = Parser.Read(&pDesc->wszName, sizeof(pDesc->wszName));
  1980. if(SUCCEEDED(hr) )
  1981. {
  1982. pDesc->dwValidData |= DMUS_OBJ_NAME;
  1983. }
  1984. break;
  1985. }
  1986. default:
  1987. break;
  1988. }
  1989. }
  1990. Parser.LeaveList();
  1991. break;
  1992. }
  1993. break;
  1994. default:
  1995. break;
  1996. }
  1997. }
  1998. Parser.LeaveList();
  1999. }
  2000. else
  2001. {
  2002. // Couldn't find the chunk header for a song.
  2003. // But, maybe this is actually a segment, in which case see if
  2004. // the segment object will parse it.
  2005. CSegment *pSegment = new CSegment;
  2006. if (pSegment)
  2007. {
  2008. pSegment->AddRef(); // just to be safe...
  2009. // Force the version so audiopath functionality will be supported.
  2010. pSegment->m_dwVersion = 8;
  2011. Parser.SeekBack();
  2012. hr = pSegment->ParseDescriptor(pIStream,pDesc);
  2013. pDesc->guidClass = CLSID_DirectMusicSong;
  2014. // Done with the segment, say bye bye.
  2015. delete pSegment;
  2016. }
  2017. else
  2018. {
  2019. hr = E_OUTOFMEMORY;
  2020. }
  2021. }
  2022. return hr;
  2023. }
  2024. ComposingTrack::ComposingTrack() : m_dwTrackGroup(0), m_dwPriority(0)
  2025. {
  2026. memset((void*) &m_guidClassID, 0, sizeof(m_guidClassID));
  2027. }
  2028. ComposingTrack::~ComposingTrack()
  2029. {
  2030. TListItem<CompositionComponent>* pComponent = m_Components.GetHead();
  2031. for (; pComponent; pComponent = pComponent->GetNext())
  2032. {
  2033. CompositionComponent& rComponent = pComponent->GetItemValue();
  2034. if (rComponent.pVirtualSegment && rComponent.pVirtualSegment->m_pPlaySegment)
  2035. {
  2036. rComponent.pVirtualSegment->m_pPlaySegment->Release();
  2037. }
  2038. if (rComponent.pComposingTrack && rComponent.pComposingTrack->m_pTrack8)
  2039. {
  2040. rComponent.pComposingTrack->m_pTrack8->Release();
  2041. }
  2042. }
  2043. }
  2044. HRESULT ComposingTrack::AddTrack(CVirtualSegment* pVirtualSegment, CTrack* pTrack)
  2045. {
  2046. HRESULT hr = S_OK;
  2047. if (!pVirtualSegment || !pVirtualSegment->m_pPlaySegment || !pTrack || !pTrack->m_pTrack8)
  2048. {
  2049. Trace(1,"Error: Unable to compose song because of a required segment or track is missing.\n");
  2050. return E_INVALIDARG;
  2051. }
  2052. TListItem<CompositionComponent>* pComponent = new TListItem<CompositionComponent>;
  2053. if (!pComponent)
  2054. {
  2055. hr = E_OUTOFMEMORY;
  2056. }
  2057. else
  2058. {
  2059. pVirtualSegment->m_pPlaySegment->AddRef();
  2060. pTrack->m_pTrack8->AddRef();
  2061. CompositionComponent& rComponent = pComponent->GetItemValue();
  2062. rComponent.pVirtualSegment = pVirtualSegment;
  2063. rComponent.pComposingTrack = pTrack;
  2064. rComponent.mtTime = pVirtualSegment->m_mtTime;
  2065. m_Components.AddHead(pComponent);
  2066. }
  2067. return hr;
  2068. }
  2069. BOOL Less(CompositionComponent& Comp1, CompositionComponent& Comp2)
  2070. {
  2071. return Comp1.mtTime < Comp2.mtTime;
  2072. }
  2073. // Compose does the joining, composing, successive splitting, and adding to segments
  2074. HRESULT ComposingTrack::Compose(IDirectMusicSong* pSong)
  2075. {
  2076. HRESULT hr = S_OK;
  2077. IDirectMusicTrack8* pMasterTrack = NULL;
  2078. IDirectMusicTrack8* pComposedTrack = NULL;
  2079. m_Components.MergeSort(Less);
  2080. // Join the tracks together according to the ordering of their associated segments.
  2081. TListItem<CompositionComponent>* pComponent = m_Components.GetHead();
  2082. for (; pComponent; pComponent = pComponent->GetNext())
  2083. {
  2084. CompositionComponent& rComponent = pComponent->GetItemValue();
  2085. if (!pMasterTrack)
  2086. {
  2087. //MUSIC_TIME mtEnd = 0;
  2088. //if (pComponent->GetNext())
  2089. //{
  2090. // mtEnd = pComponent->GetNext()->GetItemValue().mtTime;
  2091. //}
  2092. //else
  2093. //{
  2094. // rComponent.pVirtualSegment->m_pPlaySegment->GetLength(&mtEnd);
  2095. //}
  2096. //hr = rComponent.pComposingTrack->m_pTrack8->Clone(0, mtEnd, (IDirectMusicTrack**)&pMasterTrack);
  2097. hr = rComponent.pComposingTrack->m_pTrack8->Clone(0, 0, (IDirectMusicTrack**)&pMasterTrack);
  2098. }
  2099. //else
  2100. if (SUCCEEDED(hr))
  2101. {
  2102. hr = pMasterTrack->Join(rComponent.pComposingTrack->m_pTrack8, rComponent.mtTime, pSong, m_dwTrackGroup, NULL);
  2103. }
  2104. if (FAILED(hr)) break;
  2105. }
  2106. // Call Compose on the joined track.
  2107. if (SUCCEEDED(hr))
  2108. {
  2109. hr = pMasterTrack->Compose(pSong, m_dwTrackGroup, (IDirectMusicTrack**)&pComposedTrack);
  2110. }
  2111. // Split the composed result according to the original segments.
  2112. if (SUCCEEDED(hr))
  2113. {
  2114. MUSIC_TIME mtStart = 0;
  2115. MUSIC_TIME mtEnd = 0;
  2116. pComponent = m_Components.GetHead();
  2117. for (; pComponent; pComponent = pComponent->GetNext())
  2118. {
  2119. CompositionComponent& rComponent = pComponent->GetItemValue();
  2120. mtStart = rComponent.mtTime;
  2121. // only split off a composed track if the original segment contained a composing track
  2122. IDirectMusicTrack* pOldTrack = NULL;
  2123. IPersistStream* pPersist = NULL;
  2124. GUID guidClassId;
  2125. memset(&guidClassId, 0, sizeof(guidClassId));
  2126. if (SUCCEEDED(pMasterTrack->QueryInterface(IID_IPersistStream, (void**)&pPersist)) &&
  2127. SUCCEEDED(pPersist->GetClassID(&guidClassId)) &&
  2128. SUCCEEDED( rComponent.pVirtualSegment->m_pPlaySegment->GetTrack( guidClassId, m_dwTrackGroup, 0, &pOldTrack ) ) )
  2129. {
  2130. pPersist->Release();
  2131. pOldTrack->Release();
  2132. if (pComponent->GetNext())
  2133. {
  2134. mtEnd = pComponent->GetNext()->GetItemValue().mtTime;
  2135. }
  2136. else
  2137. {
  2138. MUSIC_TIME mtLength = 0;
  2139. rComponent.pVirtualSegment->m_pPlaySegment->GetLength(&mtLength);
  2140. mtEnd = mtStart + mtLength;
  2141. }
  2142. IDirectMusicTrack8* pComposedFragment = NULL;
  2143. hr = pComposedTrack->Clone(mtStart, mtEnd, (IDirectMusicTrack**)&pComposedFragment);
  2144. if (SUCCEEDED(hr))
  2145. {
  2146. // Remove any tracks of this type (in the same group) from the segment.
  2147. pOldTrack = NULL;
  2148. pPersist = NULL;
  2149. memset(&guidClassId, 0, sizeof(guidClassId));
  2150. if (SUCCEEDED(pComposedFragment->QueryInterface(IID_IPersistStream, (void**)&pPersist)) )
  2151. {
  2152. if (SUCCEEDED(pPersist->GetClassID(&guidClassId)) &&
  2153. SUCCEEDED( rComponent.pVirtualSegment->m_pPlaySegment->GetTrack( guidClassId, m_dwTrackGroup, 0, &pOldTrack ) ) )
  2154. {
  2155. rComponent.pVirtualSegment->m_pPlaySegment->RemoveTrack( pOldTrack );
  2156. pOldTrack->Release();
  2157. }
  2158. pPersist->Release();
  2159. }
  2160. hr = rComponent.pVirtualSegment->m_pPlaySegment->InsertTrack(pComposedFragment, m_dwTrackGroup);
  2161. pComposedFragment->Release(); // release from the Clone
  2162. }
  2163. if (FAILED(hr)) break;
  2164. }
  2165. else // the QI to pPersist might have succeeded, so clean it up
  2166. {
  2167. if (pPersist) pPersist->Release();
  2168. }
  2169. }
  2170. if (pComposedTrack) pComposedTrack->Release();
  2171. }
  2172. if (pMasterTrack) pMasterTrack->Release();
  2173. return hr;
  2174. }