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.

323 lines
8.9 KiB

  1. // Copyright (c) 1999 Microsoft Corporation. All rights reserved.
  2. //
  3. // Declaration of CSegTriggerTrack.
  4. //
  5. #include "dmime.h"
  6. #include "segtrtrk.h"
  7. #include "..\shared\Validate.h"
  8. #include "dmperf.h"
  9. #include "miscutil.h"
  10. //////////////////////////////////////////////////////////////////////
  11. // SetParam
  12. STDMETHODIMP
  13. CSegTriggerTrack::SetParam(REFGUID rguid, MUSIC_TIME mtTime, void *pData)
  14. {
  15. HRESULT hr = S_OK;
  16. // Allow a certain amount of recursion. If it gets to 10, something is obviously broken.
  17. if (m_dwRecursionCount < 10)
  18. {
  19. m_dwRecursionCount++;
  20. TListItem<TriggerInfo> *li = m_EventList.GetHead();
  21. for (; li; li = li->GetNext())
  22. {
  23. TriggerInfo &rinfo = li->GetItemValue();
  24. rinfo.pIDMSegment->SetParam(rguid, 0xFFFFFFFF, DMUS_SEG_ALLTRACKS, mtTime - rinfo.lTimePhysical, pData);
  25. }
  26. m_dwRecursionCount--;
  27. }
  28. return hr;
  29. }
  30. STDMETHODIMP
  31. CSegTriggerTrack::InitPlay(
  32. IDirectMusicSegmentState *pSegmentState,
  33. IDirectMusicPerformance *pPerformance,
  34. void **ppStateData,
  35. DWORD dwTrackID,
  36. DWORD dwFlags)
  37. {
  38. // Call PlayingTrack base class, which sets up our state data.
  39. HRESULT hr = CSegTriggerTrackBase::InitPlay(pSegmentState, pPerformance, ppStateData, dwTrackID, dwFlags);
  40. if (SUCCEEDED(hr))
  41. {
  42. // Get the audiopath being used by our segment state and save it in our state data.
  43. assert(*ppStateData); // base class should have created state data
  44. assert(pSegmentState); // base class should have returned E_POINTER if it wasn't given a segment state
  45. CSegTriggerTrackState *pState = static_cast<CSegTriggerTrackState *>(*ppStateData);
  46. IDirectMusicSegmentState8 *pSegSt8 = NULL;
  47. hr = pSegmentState->QueryInterface(IID_IDirectMusicSegmentState8, reinterpret_cast<void**>(&pSegSt8));
  48. if (SUCCEEDED(hr))
  49. {
  50. hr = pSegSt8->GetObjectInPath(
  51. 0, // pchannel doesn't apply
  52. DMUS_PATH_AUDIOPATH, // get the audiopath
  53. 0, // buffer index doesn't apply
  54. CLSID_NULL, // clsid doesn't apply
  55. 0, // there should be only one audiopath
  56. IID_IDirectMusicAudioPath,
  57. reinterpret_cast<void**>(&pState->pAudioPath));
  58. pSegSt8->Release();
  59. // If this doesn't find an audiopath that's OK. If we're not playing on an audiopath then
  60. // pAudioPath stays NULL and we'll play our triggered segments on the general performance.
  61. if (hr == DMUS_E_NOT_FOUND)
  62. hr = S_OK;
  63. }
  64. }
  65. return hr;
  66. }
  67. //////////////////////////////////////////////////////////////////////
  68. // Load
  69. // Helper used by the Load functions when we expected to find something
  70. // but a RiffIter becomes false. In this case, if it has a success HR
  71. // indicating there were no more items then we return DMUS_E_INVALID_SEGMENTTRIGGERTRACK
  72. // because the stream didn't contain the data we expected. If it has a
  73. // failure hr, it was unable to read from the stream and we return its HR.
  74. HRESULT LoadHrFailOK(const SmartRef::RiffIter &ri)
  75. {
  76. HRESULT hr = ri.hr();
  77. return SUCCEEDED(hr) ? DMUS_E_INVALID_SEGMENTTRIGGERTRACK : hr;
  78. };
  79. HRESULT
  80. CSegTriggerTrack::LoadRiff(SmartRef::RiffIter &ri, IDirectMusicLoader *pIDMLoader)
  81. {
  82. HRESULT hr = S_OK;
  83. if (!ri.Find(SmartRef::RiffIter::List, DMUS_FOURCC_SEGTRACK_LIST))
  84. {
  85. #ifdef DBG
  86. if (SUCCEEDED(ri.hr()))
  87. {
  88. Trace(1, "Error: Unable to load segment trigger track: List 'segt' not found.\n");
  89. }
  90. #endif
  91. return LoadHrFailOK(ri);
  92. }
  93. SmartRef::RiffIter riTrackForm = ri.Descend();
  94. if (!riTrackForm)
  95. return riTrackForm.hr();
  96. for ( ; riTrackForm; ++riTrackForm)
  97. {
  98. if (riTrackForm.type() == SmartRef::RiffIter::Chunk)
  99. {
  100. if (riTrackForm.id() == DMUS_FOURCC_SEGTRACK_CHUNK)
  101. {
  102. DMUS_IO_SEGMENT_TRACK_HEADER ioItem;
  103. hr = SmartRef::RiffIterReadChunk(riTrackForm, &ioItem);
  104. if (FAILED(hr))
  105. return hr;
  106. m_dwFlags = ioItem.dwFlags;
  107. }
  108. }
  109. else if (riTrackForm.type() == SmartRef::RiffIter::List)
  110. {
  111. if (riTrackForm.id() == DMUS_FOURCC_SEGMENTS_LIST)
  112. {
  113. SmartRef::RiffIter riSegList = riTrackForm.Descend();
  114. while (riSegList && riSegList.Find(SmartRef::RiffIter::List, DMUS_FOURCC_SEGMENT_LIST))
  115. {
  116. hr = LoadTrigger(riSegList.Descend(), pIDMLoader);
  117. if (FAILED(hr))
  118. return hr;
  119. ++riSegList;
  120. }
  121. hr = riSegList.hr();
  122. if (FAILED(hr))
  123. return hr;
  124. }
  125. }
  126. }
  127. return riTrackForm.hr();
  128. }
  129. //////////////////////////////////////////////////////////////////////
  130. // other methods
  131. HRESULT
  132. CSegTriggerTrack::PlayItem(
  133. const TriggerInfo &item,
  134. statedata &state,
  135. IDirectMusicPerformance *pPerf,
  136. IDirectMusicSegmentState* pSegSt,
  137. DWORD dwVirtualID,
  138. MUSIC_TIME mtOffset,
  139. REFERENCE_TIME rtOffset,
  140. bool fClockTime)
  141. {
  142. IDirectMusicPerformance8 *pPerf8 = NULL;
  143. HRESULT hr = pPerf->QueryInterface(IID_IDirectMusicPerformance8, reinterpret_cast<void**>(&pPerf8));
  144. if (FAILED(hr))
  145. return hr;
  146. hr = pPerf8->PlaySegmentEx(
  147. item.pIDMSegment,
  148. NULL, // not a song
  149. NULL, // no transition
  150. item.dwPlayFlags | (fClockTime ? DMUS_SEGF_REFTIME : 0), // flags
  151. fClockTime
  152. ? item.lTimePhysical * REF_PER_MIL + rtOffset
  153. : item.lTimePhysical + mtOffset, // time
  154. NULL, // ignore returned segment state
  155. NULL, // no replacement
  156. state.pAudioPath // audio path to use (may be NULL indicating defualt)
  157. );
  158. pPerf8->Release();
  159. if (FAILED(hr))
  160. {
  161. Trace(0,"Segment Trigger Track failed segment playback\n");
  162. hr = S_OK; // Avoid an assert.
  163. }
  164. return hr;
  165. }
  166. HRESULT
  167. CSegTriggerTrack::LoadTrigger(
  168. SmartRef::RiffIter ri,
  169. IDirectMusicLoader *pIDMLoader)
  170. {
  171. HRESULT hr = S_OK;
  172. if (!ri)
  173. return ri.hr();
  174. // Create an event
  175. TListItem<TriggerInfo> *pItem = new TListItem<TriggerInfo>;
  176. if (!pItem)
  177. return E_OUTOFMEMORY;
  178. TriggerInfo &rinfo = pItem->GetItemValue();
  179. // find the item header (we can't interpret the other chunks until we've found it)
  180. if (!ri.Find(SmartRef::RiffIter::Chunk, DMUS_FOURCC_SEGMENTITEM_CHUNK))
  181. {
  182. delete pItem;
  183. #ifdef DBG
  184. if (SUCCEEDED(ri.hr()))
  185. {
  186. Trace(1, "Error: Unable to load segment trigger track: Chunk 'sgih' not found.\n");
  187. }
  188. #endif
  189. return LoadHrFailOK(ri);
  190. }
  191. // read the header
  192. DMUS_IO_SEGMENT_ITEM_HEADER ioItem;
  193. hr = SmartRef::RiffIterReadChunk(ri, &ioItem);
  194. if (FAILED(hr))
  195. {
  196. delete pItem;
  197. return hr;
  198. }
  199. rinfo.lTriggerTime = ioItem.lTimeLogical;
  200. rinfo.lTimePhysical = ioItem.lTimePhysical;
  201. rinfo.dwPlayFlags = ioItem.dwPlayFlags;
  202. rinfo.dwFlags = ioItem.dwFlags;
  203. ++ri;
  204. if (!ri)
  205. {
  206. // If there's nothing more then this is an empty trigger we should ignore because the user hasn't specified
  207. // the style or segment to play from.
  208. delete pItem;
  209. return ri.hr();
  210. }
  211. if (!(rinfo.dwFlags & DMUS_SEGMENTTRACKF_MOTIF))
  212. {
  213. // find the referenced segment
  214. if (!ri.Find(SmartRef::RiffIter::List, DMUS_FOURCC_REF_LIST))
  215. {
  216. // If there's no DMRF then we should ignore this trigger because the user hasn't specified a segment.
  217. delete pItem;
  218. return ri.hr();
  219. }
  220. hr = ri.LoadReference(pIDMLoader, IID_IDirectMusicSegment, reinterpret_cast<void**>(&rinfo.pIDMSegment));
  221. if (FAILED(hr))
  222. {
  223. delete pItem;
  224. return hr;
  225. }
  226. }
  227. else
  228. {
  229. // find the segment from the referenced style and motif name
  230. SmartRef::ComPtr<IDirectMusicStyle> scomStyle;
  231. SmartRef::Buffer<WCHAR> wbufMotifName;
  232. for ( ; ri; ++ri)
  233. {
  234. if (ri.type() == SmartRef::RiffIter::List)
  235. {
  236. if (ri.id() == DMUS_FOURCC_REF_LIST)
  237. {
  238. hr = ri.LoadReference(pIDMLoader, IID_IDirectMusicStyle, reinterpret_cast<void**>(&scomStyle));
  239. if (FAILED(hr))
  240. {
  241. delete pItem;
  242. return hr;
  243. }
  244. }
  245. }
  246. else if (ri.type() == SmartRef::RiffIter::Chunk)
  247. {
  248. if (ri.id() == DMUS_FOURCC_SEGMENTITEMNAME_CHUNK)
  249. {
  250. hr = ri.ReadText(&wbufMotifName);
  251. if (FAILED(hr))
  252. {
  253. delete pItem;
  254. #ifdef DBG
  255. if (hr == E_FAIL)
  256. {
  257. Trace(1, "Error: Unable to load segment trigger track: Problem reading 'snam' chunk.\n");
  258. }
  259. #endif
  260. return hr == E_FAIL ? DMUS_E_INVALID_SEGMENTTRIGGERTRACK : hr;
  261. }
  262. }
  263. }
  264. }
  265. hr = ri.hr();
  266. if (FAILED(hr))
  267. {
  268. delete pItem;
  269. return hr;
  270. }
  271. if (!(scomStyle && wbufMotifName))
  272. {
  273. // This happens if the track didn't contain a DMRF list or snam chunk. We allow
  274. // this as a means of representing an empty trigger track item or where the
  275. // motif to play hasn't been specified. When loading we'll simply ignore
  276. // this item and continue reading the track.
  277. delete pItem;
  278. return S_OK;
  279. }
  280. hr = scomStyle->GetMotif(wbufMotifName, &rinfo.pIDMSegment);
  281. if (hr == S_FALSE)
  282. {
  283. Trace(1, "Error: The segment trigger track couldn't load because the motif %S was not found in the style.\n", wbufMotifName);
  284. hr = DMUS_E_NOT_FOUND;
  285. }
  286. if (FAILED(hr))
  287. {
  288. delete pItem;
  289. return hr;
  290. }
  291. }
  292. m_EventList.AddHead(pItem);
  293. return hr;
  294. }