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.

449 lines
11 KiB

  1. // Copyright (c) 1999 Microsoft Corporation. All rights reserved.
  2. //
  3. // Declaration of CDirectMusicScriptTrack.
  4. //
  5. #include "stdinc.h"
  6. #include "dll.h"
  7. #include "track.h"
  8. #include "dmusicf.h"
  9. #include "dmusicp.h"
  10. //////////////////////////////////////////////////////////////////////
  11. // Types
  12. CScriptTrackEvent::~CScriptTrackEvent()
  13. {
  14. if (m_pSegSt) m_pSegSt->Release();
  15. if (m_pEvent) delete m_pEvent;
  16. }
  17. HRESULT CScriptTrackEvent::Init(
  18. const EventInfo &item,
  19. IDirectMusicSegmentState* pSegSt)
  20. {
  21. HRESULT hr = S_OK;
  22. m_pEvent = new EventInfo;
  23. if (!m_pEvent)
  24. {
  25. return E_OUTOFMEMORY;
  26. }
  27. hr = m_pEvent->Clone(item, 0);
  28. if (FAILED(hr))
  29. {
  30. delete m_pEvent;
  31. return E_OUTOFMEMORY;
  32. }
  33. m_pSegSt = pSegSt;
  34. m_pSegSt->AddRef();
  35. return S_OK;
  36. }
  37. void CScriptTrackEvent::Call(DWORD dwVirtualTrackID, bool fErrorPMsgsEnabled)
  38. {
  39. #ifdef DBG
  40. // �� Probably will want better logging.
  41. DebugTrace(g_ScriptCallTraceLevel, "Script event %S\n", m_pEvent->pwszRoutineName);
  42. #endif
  43. HRESULT hrCall = m_pEvent->pIDMScriptPrivate->ScriptTrackCallRoutine(
  44. m_pEvent->pwszRoutineName,
  45. m_pSegSt,
  46. dwVirtualTrackID,
  47. fErrorPMsgsEnabled,
  48. m_i64IntendedStartTime,
  49. m_dwIntendedStartTimeFlags);
  50. #ifdef DBG
  51. if (FAILED(hrCall))
  52. {
  53. DebugTrace(g_ScriptCallTraceLevel, "Call failed 0x%08X\n", hrCall);
  54. }
  55. #endif
  56. }
  57. STDMETHODIMP CScriptTrackEvent::QueryInterface(
  58. const IID &iid, // @parm Interface to query for
  59. void **ppv) // @parm The requested interface will be returned here
  60. {
  61. if (iid == IID_IUnknown || iid == IID_CScriptTrackEvent)
  62. {
  63. *ppv = static_cast<CScriptTrackEvent*>(this);
  64. }
  65. else
  66. {
  67. *ppv = NULL;
  68. return E_NOINTERFACE;
  69. }
  70. reinterpret_cast<IUnknown*>(this)->AddRef();
  71. return S_OK;
  72. }
  73. STDMETHODIMP_(ULONG) CScriptTrackEvent::AddRef()
  74. {
  75. return InterlockedIncrement(&m_cRef);
  76. }
  77. STDMETHODIMP_(ULONG) CScriptTrackEvent::Release()
  78. {
  79. if (!InterlockedDecrement(&m_cRef))
  80. {
  81. delete this;
  82. return 0;
  83. }
  84. return m_cRef;
  85. }
  86. //////////////////////////////////////////////////////////////////////
  87. // Creation
  88. // When the script track plays one of its items, sends a PMsg to itself. When it receives the PMsg, it calls the specified
  89. // routine. If an invalidation occurs, the PMsg isn't retracted. (Perhaps because it sends the PMsgs directly to itself
  90. // without calling StampPMsg.) Then the track is played again (with the FLUSH bit set). This was causing it to trigger the
  91. // routine a second time. To fix this, the last parameter to the CSegTriggerTrackBase is false, which instructs it not to call play
  92. // a second time when the FLUSH bit is set.
  93. CDirectMusicScriptTrack::CDirectMusicScriptTrack(HRESULT *pHr)
  94. : CDirectMusicScriptTrackBase(GetModuleLockCounter(), CLSID_DirectMusicScriptTrack, true, false),
  95. m_fErrorPMsgsEnabled(false)
  96. {
  97. }
  98. //////////////////////////////////////////////////////////////////////
  99. // Load
  100. HRESULT
  101. CDirectMusicScriptTrack::LoadRiff(SmartRef::RiffIter &ri, IDirectMusicLoader *pIDMLoader)
  102. {
  103. struct LocalFunction
  104. {
  105. // Helper used by the LoadRiff function when we expected to find something
  106. // but a RiffIter becomes false. In this case, if it has a success HR
  107. // indicating there were no more items then we return DMUS_E_INVALID_SCRIPTTRACK
  108. // because the stream didn't contain the data we expected. If it has a
  109. // failure hr, it was unable to read from the stream and we return its HR.
  110. static HRESULT HrFailOK(const SmartRef::RiffIter &ri)
  111. {
  112. HRESULT hr = ri.hr();
  113. return SUCCEEDED(hr) ? DMUS_E_INVALID_SCRIPTTRACK : hr;
  114. }
  115. };
  116. // find <scrt>
  117. if (!ri.Find(SmartRef::RiffIter::List, DMUS_FOURCC_SCRIPTTRACK_LIST))
  118. {
  119. #ifdef DBG
  120. if (SUCCEEDED(ri.hr()))
  121. {
  122. Trace(1, "Error: Unable to load script track: List 'scrt' not found.\n");
  123. }
  124. #endif
  125. return LocalFunction::HrFailOK(ri);
  126. }
  127. // find <scrl>
  128. SmartRef::RiffIter riEventsList = ri.Descend();
  129. if (!riEventsList)
  130. return riEventsList.hr();
  131. if (!riEventsList.Find(SmartRef::RiffIter::List, DMUS_FOURCC_SCRIPTTRACKEVENTS_LIST))
  132. {
  133. #ifdef DBG
  134. if (SUCCEEDED(ri.hr()))
  135. {
  136. Trace(1, "Error: Unable to load script track: List 'scrl' not found.\n");
  137. }
  138. #endif
  139. return LocalFunction::HrFailOK(riEventsList);
  140. }
  141. // process each event <scre>
  142. SmartRef::RiffIter riEvent = riEventsList.Descend();
  143. if (!riEvent)
  144. return riEvent.hr();
  145. for ( ; riEvent; ++riEvent)
  146. {
  147. if (riEvent.id() == DMUS_FOURCC_SCRIPTTRACKEVENT_LIST)
  148. {
  149. HRESULT hr = this->LoadEvent(riEvent.Descend(), pIDMLoader);
  150. if (FAILED(hr))
  151. return hr;
  152. }
  153. }
  154. return riEvent.hr();
  155. }
  156. //////////////////////////////////////////////////////////////////////
  157. // IDirectMusicTrack
  158. STDMETHODIMP
  159. CDirectMusicScriptTrack::InitPlay(
  160. IDirectMusicSegmentState *pSegmentState,
  161. IDirectMusicPerformance *pPerformance,
  162. void **ppStateData,
  163. DWORD dwTrackID,
  164. DWORD dwFlags)
  165. {
  166. SmartRef::CritSec CS(&m_CriticalSection);
  167. HRESULT hr = CDirectMusicScriptTrackBase::InitPlay(pSegmentState, pPerformance, ppStateData, dwTrackID, dwFlags);
  168. if (FAILED(hr))
  169. return hr;
  170. // Init each script in the event list with this performance.
  171. for (TListItem<EventInfo> *li = m_EventList.GetHead(); li; li = li->GetNext())
  172. {
  173. EventInfo &rinfo = li->GetItemValue();
  174. if (!rinfo.pIDMScript)
  175. {
  176. assert(false);
  177. continue;
  178. }
  179. DMUS_SCRIPT_ERRORINFO ErrorInfo;
  180. if (m_fErrorPMsgsEnabled)
  181. ZeroAndSize(&ErrorInfo);
  182. hr = rinfo.pIDMScript->Init(pPerformance, &ErrorInfo);
  183. if (m_fErrorPMsgsEnabled && hr == DMUS_E_SCRIPT_ERROR_IN_SCRIPT)
  184. FireScriptTrackErrorPMsg(pPerformance, pSegmentState, dwTrackID, &ErrorInfo);
  185. }
  186. return S_OK;
  187. }
  188. //////////////////////////////////////////////////////////////////////
  189. // IDirectMusicTool
  190. STDMETHODIMP
  191. CDirectMusicScriptTrack::ProcessPMsg(
  192. IDirectMusicPerformance* pPerf,
  193. DMUS_PMSG* pPMSG)
  194. {
  195. if (!pPMSG || !pPMSG->punkUser) return E_POINTER;
  196. CScriptTrackEvent *pScriptEvent = NULL;
  197. if (SUCCEEDED(pPMSG->punkUser->QueryInterface(IID_CScriptTrackEvent, (void**)&pScriptEvent)))
  198. {
  199. pScriptEvent->Call(pPMSG->dwVirtualTrackID, m_fErrorPMsgsEnabled);
  200. pScriptEvent->Release();
  201. }
  202. return DMUS_S_FREE;
  203. }
  204. //////////////////////////////////////////////////////////////////////
  205. // IDirectMusicTrack methods
  206. STDMETHODIMP
  207. CDirectMusicScriptTrack::IsParamSupported(REFGUID rguid)
  208. {
  209. return rguid == GUID_EnableScriptTrackError ? S_OK : DMUS_E_TYPE_UNSUPPORTED;
  210. }
  211. STDMETHODIMP
  212. CDirectMusicScriptTrack::SetParam(REFGUID rguid,MUSIC_TIME mtTime,void *pData)
  213. {
  214. if (rguid == GUID_EnableScriptTrackError)
  215. {
  216. m_fErrorPMsgsEnabled = true;
  217. return S_OK;
  218. }
  219. else
  220. {
  221. return DMUS_E_SET_UNSUPPORTED;
  222. }
  223. }
  224. //////////////////////////////////////////////////////////////////////
  225. // other methods
  226. HRESULT
  227. CDirectMusicScriptTrack::LoadEvent(
  228. SmartRef::RiffIter ri,
  229. IDirectMusicLoader *pIDMLoader)
  230. {
  231. HRESULT hr = S_OK;
  232. if (!ri)
  233. return ri.hr();
  234. // Create an event
  235. // TListItem<EventInfo> is the item we're going to insert into out event list.
  236. // SmartRef::Ptr is used instead of a regular pointer because it will automatically
  237. // call delete to free the allocated list item if we bail out before the item is
  238. // successfully inserted into the event list.
  239. // See class Ptr in smartref.h for the definition of SmartRef::Ptr.
  240. SmartRef::Ptr<TListItem<EventInfo> > spItem = new TListItem<EventInfo>;
  241. if (!spItem)
  242. return E_OUTOFMEMORY;
  243. EventInfo &rinfo = spItem->GetItemValue();
  244. bool fFoundEventHeader = false;
  245. for ( ; ri; ++ri)
  246. {
  247. switch(ri.id())
  248. {
  249. case DMUS_FOURCC_SCRIPTTRACKEVENTHEADER_CHUNK:
  250. // Read an event chunk
  251. DMUS_IO_SCRIPTTRACK_EVENTHEADER ioItem;
  252. hr = SmartRef::RiffIterReadChunk(ri, &ioItem);
  253. if (FAILED(hr))
  254. return hr;
  255. fFoundEventHeader = true;
  256. rinfo.dwFlags = ioItem.dwFlags;
  257. rinfo.lTriggerTime = ioItem.lTimeLogical;
  258. rinfo.lTimePhysical = ioItem.lTimePhysical;
  259. break;
  260. case DMUS_FOURCC_REF_LIST:
  261. hr = ri.LoadReference(pIDMLoader, IID_IDirectMusicScript, reinterpret_cast<void**>(&rinfo.pIDMScript));
  262. if (FAILED(hr))
  263. return hr;
  264. hr = rinfo.pIDMScript->QueryInterface(IID_IDirectMusicScriptPrivate, reinterpret_cast<void**>(&rinfo.pIDMScriptPrivate));
  265. if (FAILED(hr))
  266. return hr;
  267. break;
  268. case DMUS_FOURCC_SCRIPTTRACKEVENTNAME_CHUNK:
  269. {
  270. hr = ri.ReadText(&rinfo.pwszRoutineName);
  271. if (FAILED(hr))
  272. {
  273. #ifdef DBG
  274. if (hr == E_FAIL)
  275. {
  276. Trace(1, "Error: Unable to load script track: Problem reading 'scrn' chunk.\n");
  277. }
  278. #endif
  279. return hr == E_FAIL ? DMUS_E_INVALID_SCRIPTTRACK : hr;
  280. }
  281. }
  282. break;
  283. default:
  284. break;
  285. }
  286. }
  287. hr = ri.hr();
  288. if (SUCCEEDED(hr) && (!fFoundEventHeader || !rinfo.pIDMScript || !rinfo.pwszRoutineName))
  289. {
  290. #ifdef DBG
  291. if (!fFoundEventHeader)
  292. {
  293. Trace(1, "Error: Unable to load script track: Chunk 'scrh' not found.\n");
  294. }
  295. else if (!rinfo.pIDMScript)
  296. {
  297. Trace(1, "Error: Unable to load script track: List 'DMRF' not found.\n");
  298. }
  299. else
  300. {
  301. Trace(1, "Error: Unable to load script track: Chunk 'scrn' not found.\n");
  302. }
  303. #endif
  304. hr = DMUS_E_INVALID_SCRIPTTRACK;
  305. }
  306. if (SUCCEEDED(hr))
  307. m_EventList.AddHead(spItem.disown()); // disown releases SmartRef::Ptr from its obligation to delete the item since that is now handled by the list
  308. return hr;
  309. }
  310. HRESULT CDirectMusicScriptTrack::PlayItem(
  311. const EventInfo &item,
  312. statedata &state,
  313. IDirectMusicPerformance *pPerf,
  314. IDirectMusicSegmentState* pSegSt,
  315. DWORD dwVirtualID,
  316. MUSIC_TIME mtOffset,
  317. REFERENCE_TIME rtOffset,
  318. bool fClockTime)
  319. {
  320. DWORD dwTimingFlags = 0;
  321. DMUS_PMSG *pMsg;
  322. HRESULT hr = pPerf->AllocPMsg(sizeof(DMUS_PMSG), &pMsg);
  323. if (FAILED(hr))
  324. return hr;
  325. ZeroAndSize(pMsg);
  326. CScriptTrackEvent *pScriptEvent = new CScriptTrackEvent;
  327. if (!pScriptEvent)
  328. {
  329. hr = E_OUTOFMEMORY;
  330. goto End;
  331. }
  332. hr = pScriptEvent->Init(item, pSegSt);
  333. if (FAILED(hr))
  334. {
  335. goto End;
  336. }
  337. if (item.dwFlags & DMUS_IO_SCRIPTTRACKF_PREPARE)
  338. dwTimingFlags = DMUS_PMSGF_TOOL_IMMEDIATE;
  339. else if (item.dwFlags & DMUS_IO_SCRIPTTRACKF_QUEUE)
  340. dwTimingFlags = DMUS_PMSGF_TOOL_QUEUE;
  341. else if (item.dwFlags & DMUS_IO_SCRIPTTRACKF_ATTIME)
  342. dwTimingFlags = DMUS_PMSGF_TOOL_ATTIME;
  343. else
  344. dwTimingFlags = DMUS_IO_SCRIPTTRACKF_QUEUE; // default
  345. if (fClockTime)
  346. {
  347. pMsg->rtTime = item.lTimePhysical * gc_RefPerMil + rtOffset;
  348. pMsg->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME | dwTimingFlags;
  349. if (!(item.dwFlags & DMUS_IO_SCRIPTTRACKF_ATTIME)) // with at time, it may already be too late to play at the designated time so Play calls will just use time zero (ASAP)
  350. {
  351. pScriptEvent->SetTime(pMsg->rtTime, DMUS_SEGF_REFTIME);
  352. }
  353. }
  354. else
  355. {
  356. pMsg->mtTime = item.lTimePhysical + mtOffset;
  357. pMsg->dwFlags = DMUS_PMSGF_MUSICTIME | dwTimingFlags;
  358. if (!(item.dwFlags & DMUS_IO_SCRIPTTRACKF_ATTIME)) // with at time, it may already be too late to play at the designated time so Play calls will just use time zero (ASAP)
  359. {
  360. pScriptEvent->SetTime(pMsg->mtTime, 0);
  361. }
  362. }
  363. pMsg->dwVirtualTrackID = dwVirtualID;
  364. pMsg->punkUser = pScriptEvent;
  365. pMsg->pTool = this;
  366. this->AddRef(); // will be released when message is sent
  367. pMsg->dwType = DMUS_PMSGT_USER;
  368. hr = pPerf->SendPMsg(reinterpret_cast<DMUS_PMSG*>(pMsg));
  369. if (FAILED(hr))
  370. {
  371. this->Release(); // balance AddRef that now won't be counteracted
  372. goto End;
  373. }
  374. return hr;
  375. End:
  376. if (pScriptEvent)
  377. {
  378. delete pScriptEvent;
  379. }
  380. pMsg->punkUser = NULL;
  381. pPerf->FreePMsg(pMsg);
  382. return hr;
  383. }