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.

828 lines
30 KiB

  1. // Copyright (c) 1999 Microsoft Corporation. All rights reserved.
  2. //
  3. // Declaration of CParamControlTrack.
  4. //
  5. #include "dmime.h"
  6. #include "ParamTrk.h"
  7. #include "..\shared\Validate.h"
  8. #include "miscutil.h"
  9. #include "limits.h"
  10. #include "math.h"
  11. STDMETHODIMP
  12. CParamControlTrack::QueryInterface(const IID &iid, void **ppv)
  13. {
  14. V_INAME(CParamControlTrack::QueryInterface);
  15. V_PTRPTR_WRITE(ppv);
  16. V_REFGUID(iid);
  17. if (iid == IID_IUnknown || iid == IID_IDirectMusicTrack || iid == IID_IDirectMusicTrack8)
  18. *ppv = static_cast<IDirectMusicTrack8*>(this);
  19. else if (iid == IID_IPersistStream)
  20. *ppv = static_cast<IPersistStream*>(this);
  21. else if (iid == IID_IPersist)
  22. *ppv = static_cast<IPersist*>(this);
  23. else if (iid == IID_CParamControlTrack)
  24. {
  25. *ppv = static_cast<CParamControlTrack*>(this);
  26. }
  27. else
  28. {
  29. *ppv = NULL;
  30. Trace(4,"Warning: Request to query unknown interface on Track\n");
  31. return E_NOINTERFACE;
  32. }
  33. reinterpret_cast<IUnknown*>(this)->AddRef();
  34. return S_OK;
  35. }
  36. STDMETHODIMP
  37. CParamControlTrack::Init(IDirectMusicSegment *pSegment)
  38. {
  39. V_INAME(CBasicTrack::Init);
  40. V_INTERFACE(pSegment);
  41. return S_OK;
  42. }
  43. STDMETHODIMP
  44. CParamControlTrack::Load(IStream* pIStream)
  45. {
  46. V_INAME(CPlayingTrack::Load);
  47. V_INTERFACE(pIStream);
  48. HRESULT hr = S_OK;
  49. SmartRef::CritSec CS(&m_CriticalSection);
  50. // Increment counter so the next play will update state data with the new list.
  51. ++m_dwValidate;
  52. // Clear the objects/params/curves in case we're being reloaded.
  53. m_listObjects.CleanUp();
  54. m_cObjects = 0;
  55. m_cParams = 0;
  56. SmartRef::RiffIter ri(pIStream);
  57. if (!ri)
  58. return ri.hr();
  59. // find <prmt>
  60. hr = ri.FindRequired(SmartRef::RiffIter::List, DMUS_FOURCC_PARAMCONTROLTRACK_TRACK_LIST, DMUS_E_INVALID_PARAMCONTROLTRACK);
  61. if (FAILED(hr))
  62. {
  63. #ifdef DBG
  64. if (hr == DMUS_E_INVALID_PARAMCONTROLTRACK)
  65. {
  66. Trace(1, "Error: Unable to load parameter control track: List 'prmt' not found.\n");
  67. }
  68. #endif
  69. return hr;
  70. }
  71. SmartRef::RiffIter riTrack = ri.Descend();
  72. // for each <prol>
  73. while (riTrack && riTrack.Find(SmartRef::RiffIter::List, DMUS_FOURCC_PARAMCONTROLTRACK_OBJECT_LIST))
  74. {
  75. hr = this->LoadObject(riTrack.Descend());
  76. if (FAILED(hr))
  77. return hr;
  78. ++riTrack;
  79. }
  80. hr = riTrack.hr();
  81. return hr;
  82. }
  83. STDMETHODIMP
  84. CParamControlTrack::InitPlay(
  85. IDirectMusicSegmentState *pSegmentState,
  86. IDirectMusicPerformance *pPerformance,
  87. void **ppStateData,
  88. DWORD dwTrackID,
  89. DWORD dwFlags)
  90. {
  91. V_INAME(CParamControlTrack::InitPlay);
  92. V_PTRPTR_WRITE(ppStateData);
  93. V_INTERFACE(pSegmentState);
  94. V_INTERFACE(pPerformance);
  95. SmartRef::CritSec CS(&m_CriticalSection);
  96. // Set up state data
  97. StateData *pStateData = new StateData;
  98. if (!pStateData)
  99. return E_OUTOFMEMORY;
  100. HRESULT hr = InitStateData(pStateData, pSegmentState);
  101. if (FAILED(hr))
  102. {
  103. delete pStateData;
  104. }
  105. else
  106. {
  107. pStateData->dwValidate = m_dwValidate;
  108. *ppStateData = pStateData;
  109. }
  110. return hr;
  111. }
  112. STDMETHODIMP
  113. CParamControlTrack::EndPlay(void *pStateData)
  114. {
  115. V_INAME(CParamControlTrack::EndPlay);
  116. V_BUFPTR_WRITE(pStateData, sizeof(StateData));
  117. SmartRef::CritSec CS(&m_CriticalSection);
  118. StateData *pSD = static_cast<StateData *>(pStateData);
  119. if (!pSD->fFlushInAbort)
  120. {
  121. // For each object, flush all curves on each parameter up to the start time of the last one we sent.
  122. // (This allows the DMO being controlled to free up memory associated with any previous curves
  123. // while still keeping the last one around so that the next thing played picks up that parameter
  124. // value how it was left.)
  125. // Then release the object's params interface.
  126. int iObj = 0;
  127. for (TListItem<ObjectInfo> *pObject = m_listObjects.GetHead();
  128. pObject && iObj < m_cObjects;
  129. pObject = pObject->GetNext(), ++iObj)
  130. {
  131. IMediaParams *pIMediaParams = pSD->prgpIMediaParams[iObj];
  132. if (pIMediaParams)
  133. {
  134. ObjectInfo &obj = pObject->GetItemValue();
  135. int iParam = 0;
  136. for (TListItem<ParamInfo> *pParam = obj.listParams.GetHead();
  137. pParam && iParam < m_cParams;
  138. pParam = pParam->GetNext(), ++iParam)
  139. {
  140. ParamInfo &param = pParam->GetItemValue();
  141. ParamState &paramstate = pSD->prgParam[iParam];
  142. if (paramstate.fLast)
  143. {
  144. HRESULT hrFlush = pIMediaParams->FlushEnvelope(param.header.dwIndex, _I64_MIN, paramstate.rtStartPointOfLastCurve);
  145. if (FAILED(hrFlush))
  146. {
  147. assert(false);
  148. TraceI(1, "Unable to flush envelope information from an audio path object in parameter control track, HRESULT 0x%08x.\n", hrFlush);
  149. }
  150. }
  151. }
  152. }
  153. SafeRelease(pIMediaParams);
  154. }
  155. }
  156. delete[] pSD->prgpIMediaParams;
  157. delete[] pSD->prgParam;
  158. delete pSD;
  159. return S_OK;
  160. }
  161. HRESULT CParamControlTrack::OnSegmentEnd(REFERENCE_TIME rtEnd, void *pStateData)
  162. {
  163. SmartRef::CritSec CS(&m_CriticalSection);
  164. StateData *pSD = static_cast<StateData *>(pStateData);
  165. // For each object, flush all curves on each parameter up to the start time of the last one we sent
  166. // (if that started before segment end) or flush everything up to the last one to start before
  167. // segment end, and flush everything after segment end (if the start time was after segment end).
  168. // (This allows the DMO being controlled to free up memory associated with any previous curves
  169. // while still keeping the last one around so that the next thing played picks up that parameter
  170. // value how it was left.)
  171. // Then release the object's params interface.
  172. int iObj = 0;
  173. for (TListItem<ObjectInfo> *pObject = m_listObjects.GetHead();
  174. pObject && iObj < m_cObjects;
  175. pObject = pObject->GetNext(), ++iObj)
  176. {
  177. IMediaParams *pIMediaParams = pSD->prgpIMediaParams[iObj];
  178. if (pIMediaParams)
  179. {
  180. ObjectInfo &obj = pObject->GetItemValue();
  181. int iParam = 0;
  182. for (TListItem<ParamInfo> *pParam = obj.listParams.GetHead();
  183. pParam && iParam < m_cParams;
  184. pParam = pParam->GetNext(), ++iParam)
  185. {
  186. ParamInfo &param = pParam->GetItemValue();
  187. ParamState &paramstate = pSD->prgParam[iParam];
  188. if (paramstate.fLast)
  189. {
  190. HRESULT hrFlush = S_OK;
  191. if (paramstate.rtStartPointOfLastCurve < rtEnd)
  192. {
  193. hrFlush = pIMediaParams->FlushEnvelope(param.header.dwIndex, _I64_MIN, paramstate.rtStartPointOfLastCurve);
  194. }
  195. else
  196. {
  197. // first, look for the largest start time less than rtEnd and
  198. // flush up to there. The loop assumes the list is ordered largest to smallest.
  199. TListItem<REFERENCE_TIME>* pStartTime = paramstate.listStartTimes.GetHead();
  200. for (; pStartTime; pStartTime = pStartTime->GetNext())
  201. {
  202. if (pStartTime->GetItemValue() < rtEnd)
  203. {
  204. hrFlush = pIMediaParams->FlushEnvelope(param.header.dwIndex, _I64_MIN, pStartTime->GetItemValue());
  205. break;
  206. }
  207. }
  208. // Then, flush from rtEnd on.
  209. if (SUCCEEDED(hrFlush))
  210. {
  211. hrFlush = pIMediaParams->FlushEnvelope(param.header.dwIndex, rtEnd, _I64_MAX);
  212. }
  213. }
  214. if (FAILED(hrFlush))
  215. {
  216. assert(false);
  217. TraceI(1, "Unable to flush envelope information from an audio path object in parameter control track, HRESULT 0x%08x.\n", hrFlush);
  218. }
  219. }
  220. }
  221. }
  222. SafeRelease(pIMediaParams);
  223. pSD->prgpIMediaParams[iObj] = NULL;
  224. }
  225. pSD->fFlushInAbort = true;
  226. return S_OK;
  227. }
  228. STDMETHODIMP
  229. CParamControlTrack::Clone(MUSIC_TIME mtStart, MUSIC_TIME mtEnd, IDirectMusicTrack** ppTrack)
  230. {
  231. // �� Test more thoroughly when we have multiple working params/objects.
  232. V_INAME(CParamControlTrack::Clone);
  233. V_PTRPTR_WRITE(ppTrack);
  234. SmartRef::CritSec CS(&m_CriticalSection);
  235. HRESULT hr = S_OK;
  236. SmartRef::ComPtr<CParamControlTrack> scomTrack = new CParamControlTrack(&hr);
  237. if (FAILED(hr))
  238. return hr;
  239. if (!scomTrack)
  240. return E_OUTOFMEMORY;
  241. scomTrack->AddRef();
  242. // Copy each object
  243. for (TListItem<ObjectInfo> *pObject = m_listObjects.GetHead();
  244. pObject;
  245. pObject = pObject->GetNext())
  246. {
  247. ObjectInfo &obj = pObject->GetItemValue();
  248. TListItem<ObjectInfo> *pNewObject = new TListItem<ObjectInfo>;
  249. if (!pNewObject)
  250. return E_OUTOFMEMORY;
  251. ObjectInfo &newobj = pNewObject->GetItemValue();
  252. newobj.header = obj.header;
  253. // Copy each parameter
  254. for (TListItem<ParamInfo> *pParam = obj.listParams.GetHead();
  255. pParam;
  256. pParam = pParam->GetNext())
  257. {
  258. ParamInfo &param = pParam->GetItemValue();
  259. TListItem<ParamInfo> *pNewParam = new TListItem<ParamInfo>;
  260. if (!pNewParam)
  261. return E_OUTOFMEMORY;
  262. ParamInfo &newparam = pNewParam->GetItemValue();
  263. newparam.header = param.header;
  264. // Copy the curves from mtStart to mtEnd
  265. // These should include curves that overlap the start and end, though this
  266. // leave some issues we still need to work out (what happens with overlapping curves?)
  267. // So, first find the first curve whose end time is at or after mtStart...
  268. for (DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *pCurveStart = param.curves;
  269. (pCurveStart < param.curvesEnd) && (pCurveStart->mtEndTime < mtStart);
  270. ++pCurveStart)
  271. {}
  272. // Then, find the curve whose start time is after mtEnd.
  273. for (DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *pCurveEnd = pCurveStart;
  274. (pCurveEnd < param.curvesEnd) && (pCurveEnd->mtStartTime < mtEnd);
  275. ++pCurveEnd)
  276. {}
  277. int cCurves = (int)(pCurveEnd - pCurveStart);
  278. newparam.curves = new DMUS_IO_PARAMCONTROLTRACK_CURVEINFO[cCurves];
  279. if (!newparam.curves)
  280. return E_OUTOFMEMORY;
  281. memcpy(newparam.curves, pCurveStart, cCurves * sizeof(DMUS_IO_PARAMCONTROLTRACK_CURVEINFO));
  282. newparam.curvesEnd = newparam.curves + cCurves;
  283. // Now, scan through the new curve array and adjust the times by subtracting mtStart from everything.
  284. for (pCurveStart = newparam.curves; pCurveStart < newparam.curvesEnd; pCurveStart++)
  285. {
  286. pCurveStart->mtStartTime -= mtStart;
  287. pCurveStart->mtEndTime -= mtStart;
  288. }
  289. newobj.listParams.AddHead(pNewParam);
  290. }
  291. newobj.listParams.Reverse(); // Technically, the order shouldn't matter. But this ensures that the cloned track will send curves to different parameters in the exact same order just in case.
  292. scomTrack->m_listObjects.AddHead(pNewObject);
  293. }
  294. scomTrack->m_listObjects.Reverse(); // Technically, the order shouldn't matter. But this ensures that the cloned track will send curves to different objects in the exact same order just in case.
  295. ++scomTrack->m_dwValidate;
  296. scomTrack->m_cObjects = m_cObjects;
  297. scomTrack->m_cParams = m_cParams;
  298. *ppTrack = scomTrack.disown();
  299. return hr;
  300. }
  301. HRESULT
  302. CParamControlTrack::PlayMusicOrClock(
  303. void *pStateData,
  304. MUSIC_TIME mtStart,
  305. MUSIC_TIME mtEnd,
  306. MUSIC_TIME mtOffset,
  307. REFERENCE_TIME rtOffset,
  308. DWORD dwFlags,
  309. IDirectMusicPerformance* pPerf,
  310. IDirectMusicSegmentState* pSegSt,
  311. DWORD dwVirtualID,
  312. bool fClockTime)
  313. {
  314. V_INAME(CParamControlTrack::Play);
  315. V_BUFPTR_WRITE( pStateData, sizeof(StateData));
  316. V_INTERFACE(pPerf);
  317. V_INTERFACE(pSegSt);
  318. if (dwFlags & DMUS_TRACKF_PLAY_OFF)
  319. return S_OK;
  320. SmartRef::CritSec CS(&m_CriticalSection);
  321. StateData *pSD = static_cast<StateData *>(pStateData);
  322. if (m_dwValidate != pSD->dwValidate)
  323. {
  324. HRESULT hr = InitStateData(pSD, pSegSt);
  325. if (FAILED(hr))
  326. {
  327. return hr;
  328. }
  329. }
  330. // envelope structure we'll fill for sending each envelope segment.
  331. MP_ENVELOPE_SEGMENT envCurve;
  332. Zero(&envCurve);
  333. MP_ENVELOPE_SEGMENT *const penvCurve = &envCurve;
  334. bool fMoreCurves = false; // set to true by any parameter that has more curves to play
  335. // for each parameter...
  336. int iParam = 0;
  337. int iObject = 0;
  338. for (TListItem<ObjectInfo> *pObject = m_listObjects.GetHead();
  339. pObject && iObject < m_cObjects;
  340. pObject = pObject->GetNext(), ++iObject)
  341. {
  342. ObjectInfo &obj = pObject->GetItemValue();
  343. IMediaParams *pIMediaParams = pSD->prgpIMediaParams[iObject];
  344. bool fObjClockTime = !!(obj.header.guidTimeFormat == GUID_TIME_REFERENCE);
  345. if (!fObjClockTime && obj.header.guidTimeFormat != GUID_TIME_MUSIC)
  346. {
  347. // track can only handle music and clock time
  348. assert(false);
  349. // Only log this once at warning level one. Rest go to warning level three to avoid tons of identical trace messages during playback).
  350. TraceI(
  351. obj.fAlreadyTracedPlaybackError ? 3 : 1,
  352. "Parameter control track unable to control object -- unknown time format (must be GUID_TIME_MUSIC or GUID_TIME_REFERENCE).\n");
  353. obj.fAlreadyTracedPlaybackError = true;
  354. continue;
  355. }
  356. for (TListItem<ParamInfo> *pParam = obj.listParams.GetHead();
  357. pParam && iParam < m_cParams;
  358. pParam = pParam->GetNext(), ++iParam)
  359. {
  360. ParamInfo &param = pParam->GetItemValue();
  361. ParamState &paramstate = pSD->prgParam[iParam];
  362. DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *&pCurrentCurve = paramstate.pCurrentCurve;
  363. // We're going to seek through the event list to find the proper next control curve for each parameter if
  364. // the track's data has been reloaded or if playback has made a jump to a different position in the track.
  365. if (m_dwValidate != pSD->dwValidate || dwFlags & (DMUS_TRACKF_SEEK | DMUS_TRACKF_LOOP | DMUS_TRACKF_FLUSH | DMUS_TRACKF_START))
  366. {
  367. assert(m_dwValidate != pSD->dwValidate || dwFlags & DMUS_TRACKF_SEEK); // by contract SEEK should be set whenever the other dwFlags are
  368. // find first curve that begins at or after the start time we're currently playing
  369. for (pCurrentCurve = param.curves; pCurrentCurve < param.curvesEnd && pCurrentCurve->mtStartTime < mtStart; ++pCurrentCurve)
  370. {}
  371. if (pIMediaParams && pCurrentCurve > param.curves)
  372. {
  373. // check the previous curve to see if we ended up in the middle of it
  374. DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *pPrevCurve = pCurrentCurve - 1;
  375. // Send a curve chopped off at the start time we're currently playing.
  376. // We can't send the whole curve because it would take effect too early.
  377. HRESULT hrEnv = this->PlayTruncatedEnvelope(mtStart, pIMediaParams, penvCurve, pPrevCurve, obj, param, paramstate, mtOffset, rtOffset, pPerf, fClockTime, fObjClockTime, dwFlags);
  378. if (FAILED(hrEnv))
  379. {
  380. // Can't fail from Play. Just assert and print trace information.
  381. assert(false);
  382. // Only log this once at warning level one. Rest go to warning level three to avoid tons of identical trace messages during playback).
  383. TraceI(
  384. param.fAlreadyTracedPlaybackError ? 3 : 1,
  385. "Unable to send envelope information to an audio path object in parameter control track, HRESULT 0x%08x.\n", hrEnv);
  386. param.fAlreadyTracedPlaybackError = true;
  387. }
  388. }
  389. }
  390. // Send curves until the next curve is after mtEnd
  391. for ( ; pCurrentCurve < param.curvesEnd; ++pCurrentCurve )
  392. {
  393. if (pCurrentCurve->mtStartTime < mtStart) // this can happen if DMUS_TRACKF_PLAY_OFF was set and the seek pointer remains at events from the past
  394. continue;
  395. if (pCurrentCurve->mtStartTime >= mtEnd)
  396. break;
  397. // send this curve
  398. if (pIMediaParams)
  399. {
  400. HRESULT hrEnv = this->PlayEnvelope(pIMediaParams, penvCurve, pCurrentCurve, obj, param, paramstate, mtOffset, rtOffset, pPerf, fClockTime, fObjClockTime);
  401. if (FAILED(hrEnv))
  402. {
  403. // Can't fail from Play. Just assert and print trace information.
  404. assert(false);
  405. // Only log this once at warning level one. Rest go to warning level three to avoid tons of identical trace messages during playback).
  406. TraceI(
  407. param.fAlreadyTracedPlaybackError ? 3 : 1,
  408. "Unable to send envelope information to an audio path object in parameter control track, HRESULT 0x%08x.\n", hrEnv);
  409. param.fAlreadyTracedPlaybackError = true;
  410. }
  411. }
  412. }
  413. if (pCurrentCurve < param.curvesEnd)
  414. fMoreCurves = true;
  415. }
  416. assert(!pParam); // we should have gotten all the way through this param list
  417. }
  418. assert(!pObject && iParam == m_cParams && iObject == m_cObjects); // we should have gotten all the way through the object list and done the expected number of objects and parameters
  419. pSD->dwValidate = m_dwValidate; // if we weren't in sync with new track data before, we are now
  420. return fMoreCurves ? S_OK : DMUS_S_END;
  421. }
  422. HRESULT CParamControlTrack::LoadObject(SmartRef::RiffIter ri)
  423. {
  424. if (!ri)
  425. return ri.hr();
  426. HRESULT hr = S_OK;
  427. SmartRef::Ptr<TListItem<ObjectInfo> > spItem = new TListItem<ObjectInfo>;
  428. if (!spItem)
  429. return E_OUTOFMEMORY;
  430. ObjectInfo &ritem = spItem->GetItemValue();
  431. // find <proh>
  432. hr = ri.FindRequired(SmartRef::RiffIter::Chunk, DMUS_FOURCC_PARAMCONTROLTRACK_OBJECT_CHUNK, DMUS_E_INVALID_PARAMCONTROLTRACK);
  433. if (FAILED(hr))
  434. {
  435. #ifdef DBG
  436. if (hr == DMUS_E_INVALID_PARAMCONTROLTRACK)
  437. {
  438. Trace(1, "Error: Unable to load parameter control track: Chunk 'proh' not found.\n");
  439. }
  440. #endif
  441. return hr;
  442. }
  443. hr = SmartRef::RiffIterReadChunk(ri, &ritem.header);
  444. if (FAILED(hr))
  445. return hr;
  446. if (!(ritem.header.guidTimeFormat == GUID_TIME_MUSIC || ritem.header.guidTimeFormat == GUID_TIME_REFERENCE))
  447. {
  448. Trace(1, "Error: Unable to load parameter control track: guidTimeFormat in chunk 'proh' must be either GUID_TIME_MUSIC or GUID_TIME_REFERENCE.\n");
  449. return DMUS_E_INVALID_PARAMCONTROLTRACK;
  450. }
  451. // for each <prpl>
  452. while (ri && ri.Find(SmartRef::RiffIter::List, DMUS_FOURCC_PARAMCONTROLTRACK_PARAM_LIST))
  453. {
  454. hr = this->LoadParam(ri.Descend(), ritem.listParams);
  455. if (FAILED(hr))
  456. return hr;
  457. ++ri;
  458. }
  459. hr = ri.hr();
  460. if (SUCCEEDED(hr))
  461. {
  462. m_listObjects.AddHead(spItem.disown());
  463. ++m_cObjects;
  464. }
  465. return hr;
  466. }
  467. HRESULT CParamControlTrack::LoadParam(SmartRef::RiffIter ri, TList<ParamInfo> &listParams)
  468. {
  469. if (!ri)
  470. return ri.hr();
  471. HRESULT hr = S_OK;
  472. SmartRef::Ptr<TListItem<ParamInfo> > spItem = new TListItem<ParamInfo>;
  473. if (!spItem)
  474. return E_OUTOFMEMORY;
  475. ParamInfo &ritem = spItem->GetItemValue();
  476. // find <prph>
  477. hr = ri.FindRequired(SmartRef::RiffIter::Chunk, DMUS_FOURCC_PARAMCONTROLTRACK_PARAM_CHUNK, DMUS_E_INVALID_PARAMCONTROLTRACK);
  478. if (FAILED(hr))
  479. {
  480. #ifdef DBG
  481. if (hr == DMUS_E_INVALID_PARAMCONTROLTRACK)
  482. {
  483. Trace(1, "Error: Unable to load parameter control track: Chunk 'prph' not found.\n");
  484. }
  485. #endif
  486. return hr;
  487. }
  488. hr = SmartRef::RiffIterReadChunk(ri, &ritem.header);
  489. if (FAILED(hr))
  490. return hr;
  491. // find <prcc>
  492. if (!ri.Find(SmartRef::RiffIter::Chunk, DMUS_FOURCC_PARAMCONTROLTRACK_CURVES_CHUNK))
  493. {
  494. // It is OK if we read to the end without finding the chunk--we succeed without finding any curves.
  495. // Or it could be a failure because there was a problem reading from the stream.
  496. // The RiffIter's hr method reflects this.
  497. return ri.hr();
  498. }
  499. // read the array of control curves
  500. int cRecords;
  501. hr = SmartRef::RiffIterReadArrayChunk(ri, &ritem.curves, &cRecords);
  502. if (FAILED(hr))
  503. return hr;
  504. ritem.curvesEnd = ritem.curves + cRecords;
  505. listParams.AddHead(spItem.disown());
  506. ++m_cParams;
  507. return hr;
  508. }
  509. HRESULT CParamControlTrack::TrackToObjectTime(
  510. MUSIC_TIME mtOffset,
  511. REFERENCE_TIME rtOffset,
  512. IDirectMusicPerformance* pPerf,
  513. bool fTrkClockTime,
  514. bool fObjClockTime,
  515. MUSIC_TIME mt,
  516. REFERENCE_TIME *rt)
  517. {
  518. HRESULT hr = S_OK;
  519. // set the time (reference time variable is used to hold either music or reference time in different contexts)
  520. REFERENCE_TIME rtEnv = mt;
  521. // add the correct offset and if necessary convert from millisecond time
  522. rtEnv = fTrkClockTime
  523. ? rtEnv * gc_RefPerMil + rtOffset
  524. : rtEnv = rtEnv + mtOffset;
  525. if (fTrkClockTime != fObjClockTime)
  526. {
  527. // need to convert between out track's time format and the audio object's time format
  528. if (fObjClockTime)
  529. {
  530. MUSIC_TIME mtEnv = static_cast<MUSIC_TIME>(rtEnv);
  531. hr = pPerf->MusicToReferenceTime(mtEnv, &rtEnv);
  532. if (FAILED(hr))
  533. return hr;
  534. }
  535. else
  536. {
  537. MUSIC_TIME mtEnv = 0;
  538. hr = pPerf->ReferenceToMusicTime(rtEnv, &mtEnv);
  539. rtEnv = mtEnv;
  540. if (FAILED(hr))
  541. return hr;
  542. }
  543. }
  544. *rt = rtEnv;
  545. return hr;
  546. }
  547. HRESULT
  548. CParamControlTrack::PlayEnvelope(
  549. IMediaParams *pIMediaParams,
  550. MP_ENVELOPE_SEGMENT *pEnv,
  551. DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *pPt,
  552. const ObjectInfo &obj,
  553. const ParamInfo &param,
  554. ParamState &paramstate,
  555. MUSIC_TIME mtOffset,
  556. REFERENCE_TIME rtOffset,
  557. IDirectMusicPerformance* pPerf,
  558. bool fTrkClockTime,
  559. bool fObjClockTime)
  560. {
  561. HRESULT hr = S_OK;
  562. // set the curve type and flags
  563. pEnv->iCurve = static_cast<MP_CURVE_TYPE>(pPt->dwCurveType);
  564. pEnv->flags = pPt->dwFlags;
  565. pEnv->valEnd = pPt->fltEndValue;
  566. pEnv->valStart = pPt->fltStartValue;
  567. // set the time (used to hold either music or reference time in different contexts)
  568. REFERENCE_TIME &rtEnvStart = pEnv->rtStart;
  569. hr = this->TrackToObjectTime(mtOffset, rtOffset, pPerf, fTrkClockTime, fObjClockTime, pPt->mtStartTime, &rtEnvStart);
  570. if (FAILED(hr))
  571. return hr;
  572. REFERENCE_TIME &rtEnvEnd = pEnv->rtEnd;
  573. hr = this->TrackToObjectTime(mtOffset, rtOffset, pPerf, fTrkClockTime, fObjClockTime, pPt->mtEndTime, &rtEnvEnd);
  574. if (FAILED(hr))
  575. return hr;
  576. hr = pIMediaParams->AddEnvelope(param.header.dwIndex, 1, pEnv);
  577. if (SUCCEEDED(hr))
  578. {
  579. paramstate.rtStartPointOfLastCurve = rtEnvStart;
  580. TListItem<REFERENCE_TIME>* pStartTime = new TListItem<REFERENCE_TIME>;
  581. if (pStartTime)
  582. {
  583. pStartTime->GetItemValue() = rtEnvStart;
  584. // Adding to the head maintains a largest-to-smallest ordering.
  585. paramstate.listStartTimes.AddHead(pStartTime);
  586. }
  587. paramstate.fLast = true;
  588. }
  589. return hr;
  590. }
  591. HRESULT
  592. CParamControlTrack::PlayTruncatedEnvelope(
  593. MUSIC_TIME mtTruncStart,
  594. IMediaParams *pIMediaParams,
  595. MP_ENVELOPE_SEGMENT *pEnv,
  596. DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *pPt,
  597. const ObjectInfo &obj,
  598. const ParamInfo &param,
  599. ParamState &paramstate,
  600. MUSIC_TIME mtOffset,
  601. REFERENCE_TIME rtOffset,
  602. IDirectMusicPerformance* pPerf,
  603. bool fTrkClockTime,
  604. bool fObjClockTime,
  605. DWORD dwFlags)
  606. {
  607. // Copy info from the curve
  608. DMUS_IO_PARAMCONTROLTRACK_CURVEINFO curveinfo = *pPt;
  609. // Cut the start to the designated time
  610. curveinfo.mtStartTime = mtTruncStart;
  611. bool fSkip = false;
  612. if (mtTruncStart >= curveinfo.mtEndTime)
  613. {
  614. // Curve happened in the past. Send a jump curve right at the current (truncate) time picking up with
  615. // that value.
  616. // if we're looping and we passed the end of this curve, just skip it.
  617. if ( (dwFlags & DMUS_TRACKF_LOOP) )
  618. {
  619. fSkip = true;
  620. }
  621. else
  622. {
  623. curveinfo.mtEndTime = mtTruncStart;
  624. curveinfo.dwCurveType = MP_CURVE_JUMP;
  625. }
  626. }
  627. else if (pPt->dwCurveType != MP_CURVE_JUMP)
  628. {
  629. // Find the point at that time and pick up with a linear curve from there.
  630. // (For the nonlinear curves, there's no way to pick them up part-way along.)
  631. curveinfo.dwCurveType = MP_CURVE_LINEAR;
  632. MUSIC_TIME mtTimeChange = pPt->mtEndTime - pPt->mtStartTime;
  633. MUSIC_TIME mtTimeIntermediate = mtTruncStart - pPt->mtStartTime;
  634. float fltScalingX = static_cast<float>(mtTimeIntermediate) / mtTimeChange; // horizontal distance along curve between 0 and 1
  635. float fltScalingY; // height of curve at that point between 0 and 1 based on curve function
  636. switch (pPt->dwCurveType)
  637. {
  638. case MP_CURVE_SQUARE:
  639. fltScalingY = fltScalingX * fltScalingX;
  640. break;
  641. case MP_CURVE_INVSQUARE:
  642. fltScalingY = (float) sqrt(fltScalingX);
  643. break;
  644. case MP_CURVE_SINE:
  645. // �� Maybe we should have a lookup table here?
  646. fltScalingY = (float) (sin(fltScalingX * 3.1415926535 - (3.1415926535/2)) + 1) / 2;
  647. break;
  648. case MP_CURVE_LINEAR:
  649. default:
  650. fltScalingY = fltScalingX;
  651. }
  652. // Apply that scaling to the range of the actual points
  653. curveinfo.fltStartValue = (pPt->fltEndValue - pPt->fltStartValue) * fltScalingY + pPt->fltStartValue;
  654. }
  655. if (fSkip) return S_OK;
  656. return this->PlayEnvelope(pIMediaParams, pEnv, &curveinfo, obj, param, paramstate, mtOffset, rtOffset, pPerf, fTrkClockTime, fObjClockTime);
  657. }
  658. HRESULT CParamControlTrack::InitStateData(StateData *pStateData,
  659. IDirectMusicSegmentState *pSegmentState)
  660. {
  661. if (pStateData->prgpIMediaParams)
  662. {
  663. delete [] pStateData->prgpIMediaParams;
  664. pStateData->prgpIMediaParams = NULL;
  665. }
  666. if (pStateData->prgParam)
  667. {
  668. delete [] pStateData->prgParam;
  669. pStateData->prgParam = NULL;
  670. }
  671. pStateData->prgpIMediaParams = new IMediaParams *[m_cObjects];
  672. if (!pStateData->prgpIMediaParams)
  673. {
  674. return E_OUTOFMEMORY;
  675. }
  676. pStateData->prgParam = new ParamState[m_cParams];
  677. if (!pStateData->prgParam)
  678. {
  679. delete [] pStateData->prgpIMediaParams;
  680. return E_OUTOFMEMORY;
  681. }
  682. // Get the IMediaParams interface for each object
  683. SmartRef::ComPtr<IDirectMusicSegmentState8> scomSegSt8;
  684. HRESULT hr = pSegmentState->QueryInterface(IID_IDirectMusicSegmentState8, reinterpret_cast<void**>(&scomSegSt8));
  685. if (FAILED(hr))
  686. {
  687. delete [] pStateData->prgParam;
  688. delete [] pStateData->prgpIMediaParams;
  689. return hr;
  690. }
  691. int iObject = 0;
  692. for (TListItem<ObjectInfo> *pObject = m_listObjects.GetHead();
  693. pObject;
  694. pObject = pObject->GetNext(), ++iObject)
  695. {
  696. IMediaParams *pIMediaParams = NULL;
  697. ObjectInfo &rinfo = pObject->GetItemValue();
  698. HRESULT hrObject = scomSegSt8->GetObjectInPath(
  699. rinfo.header.dwPChannel,
  700. rinfo.header.dwStage,
  701. rinfo.header.dwBuffer,
  702. rinfo.header.guidObject,
  703. rinfo.header.dwIndex,
  704. IID_IMediaParams,
  705. reinterpret_cast<void**>(&pIMediaParams));
  706. if (FAILED(hrObject))
  707. {
  708. // Can't fail from InitPlay (and this is called from there).
  709. // Just print trace information.
  710. TraceI(1, "Parameter control track was unable to find audio path object, HRESULT 0x%08x.\n", hrObject);
  711. }
  712. else
  713. {
  714. hrObject = pIMediaParams->SetTimeFormat(rinfo.header.guidTimeFormat, rinfo.header.guidTimeFormat == GUID_TIME_MUSIC ? 768 : 0);
  715. }
  716. if (FAILED(hrObject))
  717. {
  718. // Can't fail from InitPlay (and this is called from there).
  719. // Just print trace information.
  720. Trace(1, "Unable to set time format of object in parameter control track, HRESULT 0x%08x.\n", hrObject);
  721. }
  722. if (FAILED(hrObject))
  723. {
  724. SafeRelease(pIMediaParams);
  725. }
  726. pStateData->prgpIMediaParams[iObject] = pIMediaParams;
  727. }
  728. return S_OK;
  729. }