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.

10907 lines
389 KiB

  1. // Copyright (c) 1998-2001 Microsoft Corporation
  2. // dmperf.cpp
  3. #include <windows.h>
  4. #include <mmsystem.h>
  5. #include <time.h> // To seed random number generator
  6. #include <dsoundp.h>
  7. #include "debug.h"
  8. #define ASSERT assert
  9. #include "dmperf.h"
  10. #include "dmime.h"
  11. #include "dmgraph.h"
  12. #include "dmsegobj.h"
  13. #include "song.h"
  14. #include "curve.h"
  15. #include "math.h"
  16. #include "..\shared\Validate.h"
  17. #include "..\dmstyle\dmstylep.h"
  18. #include <ks.h>
  19. #include "dmksctrl.h"
  20. #include <dsound.h>
  21. #include "dmscriptautguids.h"
  22. #include "..\shared\dmusiccp.h"
  23. #include "wavtrack.h"
  24. #include "tempotrk.h"
  25. #include <strsafe.h>
  26. #pragma warning(disable:4296)
  27. #define PORT_CHANNEL 0
  28. // @doc EXTERNAL
  29. #define MIDI_NOTEOFF 0x80
  30. #define MIDI_NOTEON 0x90
  31. #define MIDI_PTOUCH 0xA0
  32. #define MIDI_CCHANGE 0xB0
  33. #define MIDI_PCHANGE 0xC0
  34. #define MIDI_MTOUCH 0xD0
  35. #define MIDI_PBEND 0xE0
  36. #define MIDI_SYSX 0xF0
  37. #define MIDI_MTC 0xF1
  38. #define MIDI_SONGPP 0xF2
  39. #define MIDI_SONGS 0xF3
  40. #define MIDI_EOX 0xF7
  41. #define MIDI_CLOCK 0xF8
  42. #define MIDI_START 0xFA
  43. #define MIDI_CONTINUE 0xFB
  44. #define MIDI_STOP 0xFC
  45. #define MIDI_SENSE 0xFE
  46. #define MIDI_CC_BS_MSB 0x00
  47. #define MIDI_CC_BS_LSB 0x20
  48. #define MIDI_CC_DATAENTRYMSB 0x06
  49. #define MIDI_CC_DATAENTRYLSB 0x26
  50. #define MIDI_CC_NRPN_LSB 0x62
  51. #define MIDI_CC_NRPN_MSB 0x63
  52. #define MIDI_CC_RPN_LSB 0x64
  53. #define MIDI_CC_RPN_MSB 0x65
  54. #define MIDI_CC_MOD_WHEEL 0x01
  55. #define MIDI_CC_VOLUME 0x07
  56. #define MIDI_CC_PAN 0x0A
  57. #define MIDI_CC_EXPRESSION 0x0B
  58. #define MIDI_CC_FILTER 0x4A
  59. #define MIDI_CC_REVERB 0x5B
  60. #define MIDI_CC_CHORUS 0x5D
  61. #define MIDI_CC_RESETALL 0x79
  62. #define MIDI_CC_ALLSOUNDSOFF 0x78
  63. #define CLEARTOOLGRAPH(x) { \
  64. if( (x)->pTool ) \
  65. { \
  66. (x)->pTool->Release(); \
  67. (x)->pTool = NULL; \
  68. } \
  69. if( (x)->pGraph ) \
  70. { \
  71. (x)->pGraph->Release(); \
  72. (x)->pGraph = NULL; }}
  73. #define GetLatencyWithPrePlay() ( GetLatency() + m_rtBumperLength )
  74. void CChannelBlockList::Clear()
  75. {
  76. CChannelBlock* pCB;
  77. while( pCB = RemoveHead() )
  78. {
  79. delete pCB;
  80. }
  81. }
  82. void CChannelMap::Clear()
  83. {
  84. Reset(TRUE); // Clear all MIDI controllers
  85. m_TransposeMerger.Clear(0); // No transpose.
  86. nTranspose = 0;
  87. wFlags = CMAP_FREE;
  88. }
  89. void CChannelMap::Reset(BOOL fVolumeAndPanToo)
  90. {
  91. if (fVolumeAndPanToo)
  92. {
  93. m_PanMerger.Clear(0); // Panned to center.
  94. m_VolumeMerger.Clear(-415); // Equivalent to MIDI value 100.
  95. }
  96. m_PitchbendMerger.Clear(0); // No pitch bend.
  97. m_ExpressionMerger.Clear(0);// Full volume for expression (MIDI 127.)
  98. m_FilterMerger.Clear(0); // No filter change.
  99. m_ReverbMerger.Clear(-87); // Start at default level (MIDI 40).
  100. m_ChorusMerger.Clear(-127); // Start with no chorus.
  101. m_ModWheelMerger.Clear(-127); // Start with no mod wheel.
  102. }
  103. void CParamMerger::Clear(long lInitValue )
  104. {
  105. CMergeParam *pParam;
  106. while (pParam = RemoveHead())
  107. {
  108. delete pParam;
  109. }
  110. m_lZeroIndexData = lInitValue;
  111. m_lMergeTotal = 0;
  112. }
  113. long CParamMerger::m_lMIDIToDB[128] = { // Global array used to convert MIDI to dB.
  114. -9600, -8415, -7211, -6506, -6006, -5619, -5302, -5034,
  115. -4802, -4598, -4415, -4249, -4098, -3959, -3830, -3710,
  116. -3598, -3493, -3394, -3300, -3211, -3126, -3045, -2968,
  117. -2894, -2823, -2755, -2689, -2626, -2565, -2506, -2449,
  118. -2394, -2341, -2289, -2238, -2190, -2142, -2096, -2050,
  119. -2006, -1964, -1922, -1881, -1841, -1802, -1764, -1726,
  120. -1690, -1654, -1619, -1584, -1551, -1518, -1485, -1453,
  121. -1422, -1391, -1361, -1331, -1302, -1273, -1245, -1217,
  122. -1190, -1163, -1137, -1110, -1085, -1059, -1034, -1010,
  123. -985, -961, -938, -914, -891, -869, -846, -824,
  124. -802, -781, -759, -738, -718, -697, -677, -657,
  125. -637, -617, -598, -579, -560, -541, -522, -504,
  126. -486, -468, -450, -432, -415, -397, -380, -363,
  127. -347, -330, -313, -297, -281, -265, -249, -233,
  128. -218, -202, -187, -172, -157, -142, -127, -113,
  129. -98, -84, -69, -55, -41, -27, -13, 0
  130. };
  131. long CParamMerger::m_lDBToMIDI[97] = { // Global array used to convert db to MIDI.
  132. 127, 119, 113, 106, 100, 95, 89, 84, 80, 75,
  133. 71, 67, 63, 60, 56, 53, 50, 47, 45, 42,
  134. 40, 37, 35, 33, 31, 30, 28, 26, 25, 23,
  135. 22, 21, 20, 19, 17, 16, 15, 15, 14, 13,
  136. 12, 11, 11, 10, 10, 9, 8, 8, 8, 7,
  137. 7, 6, 6, 6, 5, 5, 5, 4, 4, 4,
  138. 4, 3, 3, 3, 3, 3, 2, 2, 2, 2,
  139. 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
  140. 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
  141. 0, 0, 0, 0, 0, 0, 0
  142. };
  143. CParamMerger::CParamMerger()
  144. {
  145. m_lMergeTotal = 0;
  146. m_lZeroIndexData = 0;
  147. }
  148. BYTE CParamMerger::VolumeToMidi(long lVolume)
  149. {
  150. if (lVolume < -9600) lVolume = -9600;
  151. if (lVolume > 0) lVolume = 0;
  152. lVolume = -lVolume;
  153. long lFraction = lVolume % 100;
  154. lVolume = lVolume / 100;
  155. long lResult = m_lDBToMIDI[lVolume];
  156. lResult += ((m_lDBToMIDI[lVolume + 1] - lResult) * lFraction) / 100;
  157. return (BYTE) lResult;
  158. }
  159. /* MergeMidiVolume() takes an incoming volume and updates the matching
  160. MergeParam structure (determined by index.) If there is no such matching
  161. structure, it creates one. Also, the volumes are totaled to create a new
  162. total volume, which is converted back to MIDI volume and returned.
  163. This mechanism allows us to introduce additional volume controllers
  164. that are summed.
  165. */
  166. BYTE CParamMerger::MergeMidiVolume(DWORD dwIndex, BYTE bMIDIVolume)
  167. {
  168. long lVolume = MergeData(dwIndex,m_lMIDIToDB[bMIDIVolume]);
  169. if (m_lMergeTotal || dwIndex) // Optimization for simplest and most frequent case - there are no additional indexes.
  170. {
  171. return (BYTE) VolumeToMidi(lVolume);
  172. }
  173. return bMIDIVolume;
  174. }
  175. BYTE CParamMerger::GetVolumeStart(DWORD dwIndex)
  176. {
  177. if (dwIndex == 0)
  178. {
  179. return VolumeToMidi(m_lZeroIndexData);
  180. }
  181. return VolumeToMidi(GetIndexedValue(dwIndex));
  182. }
  183. /* MergeValue is used for all data types that have a plus and minus range
  184. around a center bias. These include pitch bend, pan and filter.
  185. MergeValue takes an incoming data value, adds the bias (in lRange),
  186. calls MergeData to combine it with the other merged inputs,
  187. adds the bias back in and checks for over or underflow.
  188. */
  189. long CParamMerger::MergeValue(DWORD dwIndex, long lData, long lCenter, long lRange)
  190. {
  191. lData = MergeData(dwIndex,lData - lCenter) + lCenter;
  192. if (lData < 0) lData = 0;
  193. if (lData > lRange) lData = lRange;
  194. return lData;
  195. }
  196. short CParamMerger::MergeTranspose(DWORD dwIndex, short nTranspose)
  197. {
  198. return (short) MergeData(dwIndex,nTranspose);
  199. }
  200. long CParamMerger::MergeData(DWORD dwIndex, long lData)
  201. {
  202. if (dwIndex)
  203. {
  204. // If this has an index, scan the indexes. Look
  205. // for the matching index. If it is found, update it
  206. // with the new data. Meanwhile, add up all the data fields.
  207. // If it is not found, add an entry for it.
  208. m_lMergeTotal = 0; // Recalculate
  209. BOOL fNoEntry = TRUE;
  210. CMergeParam *pParam = GetHead();
  211. for (;pParam;pParam = pParam->GetNext())
  212. {
  213. if (pParam->m_dwIndex == dwIndex)
  214. {
  215. // Found the index. Store the new value.
  216. pParam->m_lData = lData;
  217. fNoEntry = FALSE;
  218. }
  219. // Sum all values to create the merged total.
  220. m_lMergeTotal += pParam->m_lData;
  221. }
  222. if (fNoEntry)
  223. {
  224. // Didn't find the index. Create one and store the value.
  225. pParam = new CMergeParam;
  226. if (pParam)
  227. {
  228. pParam->m_dwIndex = dwIndex;
  229. pParam->m_lData = lData;
  230. m_lMergeTotal += lData;
  231. AddHead(pParam);
  232. }
  233. }
  234. // Add the initial value for merge index 0.
  235. lData = m_lMergeTotal + m_lZeroIndexData;
  236. }
  237. else
  238. {
  239. m_lZeroIndexData = lData;
  240. lData += m_lMergeTotal;
  241. }
  242. return lData;
  243. }
  244. long CParamMerger::GetIndexedValue(DWORD dwIndex)
  245. {
  246. if (dwIndex)
  247. {
  248. // If this has an index, scan the indexes. Look
  249. // for the matching index. If it is found, return its data.
  250. // If not, return the default 0.
  251. BOOL fNoEntry = TRUE;
  252. CMergeParam *pParam = GetHead();
  253. for (;pParam;pParam = pParam->GetNext())
  254. {
  255. if (pParam->m_dwIndex == dwIndex)
  256. {
  257. return pParam->m_lData;
  258. }
  259. }
  260. return 0;
  261. }
  262. return m_lZeroIndexData;
  263. }
  264. void CChannelBlock::Init(DWORD dwPChannelStart,
  265. DWORD dwPortIndex, DWORD dwGroup,
  266. WORD wFlags)
  267. {
  268. DWORD dwIndex;
  269. m_dwPortIndex = dwPortIndex;
  270. m_dwPChannelStart = ( dwPChannelStart / PCHANNEL_BLOCKSIZE ) * PCHANNEL_BLOCKSIZE;
  271. for( dwIndex = 0; dwIndex < PCHANNEL_BLOCKSIZE; dwIndex++ )
  272. {
  273. m_aChannelMap[dwIndex].Clear();
  274. m_aChannelMap[dwIndex].dwPortIndex = dwPortIndex;
  275. m_aChannelMap[dwIndex].dwGroup = dwGroup;
  276. m_aChannelMap[dwIndex].dwMChannel = dwIndex;
  277. m_aChannelMap[dwIndex].nTranspose = 0;
  278. m_aChannelMap[dwIndex].wFlags = wFlags;
  279. }
  280. if (wFlags == CMAP_FREE) m_dwFreeChannels = 16;
  281. else m_dwFreeChannels = 0;
  282. }
  283. /////////////////////////////////////////////////////////////////////////////
  284. // CPerformance
  285. // Flags for which critical sections have been initialized
  286. //
  287. #define PERF_ICS_SEGMENT 0x0001
  288. #define PERF_ICS_PIPELINE 0x0002
  289. #define PERF_ICS_PCHANNEL 0x0004
  290. #define PERF_ICS_GLOBAL 0x0010
  291. #define PERF_ICS_REALTIME 0x0020
  292. #define PERF_ICS_PORTTABLE 0x0040
  293. #define PERF_ICS_MAIN 0x0100
  294. #define PERF_ICS_PMSGCACHE 0x0200
  295. CPerformance::CPerformance()
  296. {
  297. m_pGraph = NULL;
  298. m_dwPrepareTime = 1000;
  299. m_dwBumperLength = 50;
  300. m_rtBumperLength = m_dwBumperLength * REF_PER_MIL;
  301. m_pGlobalData = NULL;
  302. m_fInTrackPlay = FALSE;
  303. m_fPlaying = FALSE;
  304. m_wRollOverCount = 0;
  305. m_mtTransported = 0;
  306. m_mtTempoCursor = 0;
  307. m_pParamHook = NULL;
  308. m_hNotification = 0;
  309. m_rtNotificationDiscard = 20000000;
  310. m_rtStart = 0;
  311. m_rtAdjust = 0;
  312. m_mtPlayTo = 0;
  313. m_cRef = 1;
  314. m_pUnkDispatch = NULL;
  315. m_dwVersion = 6;
  316. m_dwNumPorts = 0;
  317. m_pPortTable = NULL;
  318. m_fKillThread = 0;
  319. m_fKillRealtimeThread = 0;
  320. m_fInTransportThread = 0;
  321. m_dwTransportThreadID = 0;
  322. m_pDirectMusic = NULL;
  323. m_pDirectSound = NULL;
  324. m_pClock = NULL;
  325. m_fReleasedInTransport = false;
  326. m_fReleasedInRealtime = false;
  327. InterlockedIncrement(&g_cComponent);
  328. TraceI(3,"CPerformance %lx\n", this);
  329. m_dwInitCS = 0;
  330. InitializeCriticalSection(&m_SegmentCrSec); m_dwInitCS |= PERF_ICS_SEGMENT;
  331. InitializeCriticalSection(&m_PipelineCrSec); m_dwInitCS |= PERF_ICS_PIPELINE;
  332. InitializeCriticalSection(&m_PChannelInfoCrSec); m_dwInitCS |= PERF_ICS_PCHANNEL;
  333. InitializeCriticalSection(&m_GlobalDataCrSec); m_dwInitCS |= PERF_ICS_GLOBAL;
  334. InitializeCriticalSection(&m_RealtimeCrSec); m_dwInitCS |= PERF_ICS_REALTIME;
  335. InitializeCriticalSection(&m_PMsgCacheCrSec); m_dwInitCS |= PERF_ICS_PMSGCACHE;
  336. InitializeCriticalSection(&m_MainCrSec); m_dwInitCS |= PERF_ICS_MAIN;
  337. memset( m_apPMsgCache, 0, sizeof(DMUS_PMSG*) * (PERF_PMSG_CB_MAX - PERF_PMSG_CB_MIN) );
  338. DWORD dwCount;
  339. for (dwCount = 0; dwCount < SQ_COUNT; dwCount++)
  340. {
  341. m_SegStateQueues[dwCount].SetID(dwCount);
  342. }
  343. Init();
  344. }
  345. void CPerformance::Init()
  346. {
  347. m_rtEarliestStartTime = 0;
  348. m_lMasterVolume = 0;
  349. if (m_dwVersion >= 8)
  350. {
  351. m_rtQueuePosition = 0;
  352. m_dwPrepareTime = 1000;
  353. m_dwBumperLength = 50;
  354. m_rtBumperLength = m_dwBumperLength * REF_PER_MIL;
  355. if (m_dwAudioPathMode)
  356. {
  357. CloseDown();
  358. }
  359. }
  360. m_pDefaultAudioPath = NULL;
  361. m_fltRelTempo = 1;
  362. m_pGetParamSegmentState = NULL;
  363. m_dwGetParamFlags = 0;
  364. m_rtHighestPackedNoteOn = 0;
  365. m_dwAudioPathMode = 0;
  366. m_hTransport = 0;
  367. m_hTransportThread = 0;
  368. m_dwRealtimeThreadID = 0;
  369. m_hRealtime = 0;
  370. m_hRealtimeThread = 0;
  371. m_fMusicStopped = TRUE;
  372. BOOL fAuto = FALSE;
  373. SetGlobalParam(GUID_PerfAutoDownload,&fAuto,sizeof(BOOL));
  374. DMUS_TIMESIG_PMSG* pTimeSig;
  375. if (SUCCEEDED(AllocPMsg(sizeof(DMUS_TIMESIG_PMSG),(DMUS_PMSG **) &pTimeSig)))
  376. {
  377. pTimeSig->wGridsPerBeat = 4;
  378. pTimeSig->bBeatsPerMeasure = 4;
  379. pTimeSig->bBeat = 4;
  380. pTimeSig->dwFlags = DMUS_PMSGF_REFTIME;
  381. pTimeSig->dwType = DMUS_PMSGT_TIMESIG;
  382. EnterCriticalSection(&m_PipelineCrSec);
  383. m_TimeSigQueue.Enqueue( DMUS_TO_PRIV(pTimeSig) );
  384. LeaveCriticalSection(&m_PipelineCrSec);
  385. }
  386. }
  387. CPerformance::~CPerformance()
  388. {
  389. TraceI(3,"~CPerformance %lx\n", this);
  390. if (m_pParamHook)
  391. {
  392. m_pParamHook->Release();
  393. }
  394. CloseDown(); // this should have already been called, but just in case...
  395. if (m_pUnkDispatch)
  396. m_pUnkDispatch->Release(); // free IDispatch implementation we may have borrowed
  397. if (m_dwInitCS & PERF_ICS_SEGMENT) DeleteCriticalSection(&m_SegmentCrSec);
  398. if (m_dwInitCS & PERF_ICS_PIPELINE) DeleteCriticalSection(&m_PipelineCrSec);
  399. if (m_dwInitCS & PERF_ICS_PCHANNEL) DeleteCriticalSection(&m_PChannelInfoCrSec);
  400. if (m_dwInitCS & PERF_ICS_GLOBAL) DeleteCriticalSection(&m_GlobalDataCrSec);
  401. if (m_dwInitCS & PERF_ICS_REALTIME) DeleteCriticalSection(&m_RealtimeCrSec);
  402. if (m_dwInitCS & PERF_ICS_PMSGCACHE)DeleteCriticalSection(&m_PMsgCacheCrSec);
  403. if (m_dwInitCS & PERF_ICS_MAIN) DeleteCriticalSection(&m_MainCrSec);
  404. InterlockedDecrement(&g_cComponent);
  405. }
  406. STDMETHODIMP CPerformance::CloseDown(void)
  407. {
  408. V_INAME(CPerformance::CloseDown);
  409. DWORD dwThreadID = GetCurrentThreadId();
  410. if( m_dwAudioPathMode )
  411. {
  412. // kill the transport thread
  413. m_fKillThread = 1;
  414. m_fKillRealtimeThread = 1;
  415. if (dwThreadID != m_dwTransportThreadID)
  416. {
  417. // signal the transport thread so we don't have to wait for it to wake up on its own
  418. if( m_hTransport ) SetEvent( m_hTransport );
  419. // wait until the transport thread quits
  420. WaitForSingleObject(m_hTransportThread, INFINITE);
  421. }
  422. if (dwThreadID != m_dwRealtimeThreadID)
  423. {
  424. // signal the realtime thread so we don't have to wait for it to wake up on its own
  425. if( m_hRealtime ) SetEvent( m_hRealtime );
  426. // wait until the realtime thread quits
  427. WaitForSingleObject(m_hRealtimeThread, INFINITE);
  428. }
  429. }
  430. if (m_pGraph) SetGraph(NULL); // shut down the graph and release it (needs to happen before clearing audio path)
  431. EnterCriticalSection(&m_SegmentCrSec);
  432. EnterCriticalSection(&m_RealtimeCrSec);
  433. m_fPlaying = FALSE; // prevents transport thread from doing anything more
  434. IDirectMusicPerformance* pPerf = NULL;
  435. if (SUCCEEDED(QueryInterface(IID_IDirectMusicPerformance, (void**)&pPerf)))
  436. {
  437. CWavTrack::UnloadAllWaves(pPerf);
  438. pPerf->Release();
  439. }
  440. DequeueAllSegments();
  441. if (m_pDefaultAudioPath)
  442. {
  443. m_pDefaultAudioPath->Release();
  444. m_pDefaultAudioPath = NULL;
  445. }
  446. m_dwAudioPathMode = 0;
  447. m_AudioPathList.Clear();
  448. CNotificationItem* pItem = m_NotificationList.GetHead();
  449. while( pItem )
  450. {
  451. CNotificationItem* pNext = pItem->GetNext();
  452. m_NotificationList.Remove( pItem );
  453. delete pItem;
  454. pItem = pNext;
  455. }
  456. LeaveCriticalSection(&m_RealtimeCrSec);
  457. LeaveCriticalSection(&m_SegmentCrSec);
  458. EnterCriticalSection(&m_PipelineCrSec);
  459. PRIV_PMSG* pPMsg;
  460. while( pPMsg = m_EarlyQueue.Dequeue() )
  461. {
  462. FreePMsg(pPMsg);
  463. }
  464. while( pPMsg = m_NearTimeQueue.Dequeue() )
  465. {
  466. FreePMsg(pPMsg);
  467. }
  468. while( pPMsg = m_OnTimeQueue.Dequeue() )
  469. {
  470. FreePMsg(pPMsg);
  471. }
  472. while( pPMsg = m_TempoMap.Dequeue() )
  473. {
  474. FreePMsg(pPMsg);
  475. }
  476. while( pPMsg = m_OldTempoMap.Dequeue() )
  477. {
  478. FreePMsg(pPMsg);
  479. }
  480. while( pPMsg = m_NotificationQueue.Dequeue() )
  481. {
  482. FreePMsg(pPMsg);
  483. }
  484. while( pPMsg = m_TimeSigQueue.Dequeue() )
  485. {
  486. FreePMsg(pPMsg);
  487. }
  488. LeaveCriticalSection(&m_PipelineCrSec);
  489. EnterCriticalSection(&m_GlobalDataCrSec);
  490. GlobalData* pGD = m_pGlobalData;
  491. while( pGD )
  492. {
  493. m_pGlobalData = pGD->pNext;
  494. delete pGD;
  495. pGD = m_pGlobalData;
  496. }
  497. LeaveCriticalSection(&m_GlobalDataCrSec);
  498. EnterCriticalSection(&m_PChannelInfoCrSec);
  499. // clear out ports, buffers, and pchannel maps
  500. if( m_pPortTable )
  501. {
  502. DWORD dwIndex;
  503. for( dwIndex = 0; dwIndex < m_dwNumPorts; dwIndex++ )
  504. {
  505. if( m_pPortTable[dwIndex].pPort )
  506. {
  507. m_pPortTable[dwIndex].pPort->Release();
  508. }
  509. if( m_pPortTable[dwIndex].pBuffer )
  510. {
  511. m_pPortTable[dwIndex].pBuffer->Release();
  512. }
  513. if( m_pPortTable[dwIndex].pLatencyClock )
  514. {
  515. m_pPortTable[dwIndex].pLatencyClock->Release();
  516. }
  517. }
  518. delete [] m_pPortTable;
  519. m_pPortTable = NULL;
  520. m_dwNumPorts = 0;
  521. }
  522. m_ChannelBlockList.Clear();
  523. LeaveCriticalSection(&m_PChannelInfoCrSec);
  524. EnterCriticalSection(&m_MainCrSec);
  525. if( m_pClock )
  526. {
  527. m_pClock->Release();
  528. m_pClock = NULL;
  529. }
  530. m_BufferManager.Clear();
  531. if( m_pDirectMusic )
  532. {
  533. m_pDirectMusic->Release();
  534. m_pDirectMusic = NULL;
  535. }
  536. if (m_pDirectSound)
  537. {
  538. m_pDirectSound->Release();
  539. m_pDirectSound = NULL;
  540. }
  541. m_hNotification = NULL;
  542. LeaveCriticalSection(&m_MainCrSec);
  543. EnterCriticalSection(&m_PMsgCacheCrSec);
  544. for( int i = 0; i < (PERF_PMSG_CB_MAX - PERF_PMSG_CB_MIN); i++ )
  545. {
  546. while( m_apPMsgCache[i] )
  547. {
  548. PRIV_PMSG* pPriv = m_apPMsgCache[i];
  549. m_apPMsgCache[i] = pPriv->pNext;
  550. delete [] pPriv;
  551. }
  552. }
  553. LeaveCriticalSection(&m_PMsgCacheCrSec);
  554. DWORD dwExitCode = 0;
  555. if (m_hTransportThread)
  556. {
  557. CloseHandle( m_hTransportThread );
  558. m_hTransportThread = 0;
  559. }
  560. if( m_hTransport )
  561. {
  562. CloseHandle( m_hTransport );
  563. m_hTransport = 0;
  564. }
  565. if (m_hRealtimeThread)
  566. {
  567. CloseHandle( m_hRealtimeThread );
  568. m_hRealtimeThread = 0;
  569. }
  570. if( m_hRealtime )
  571. {
  572. CloseHandle( m_hRealtime );
  573. m_hRealtime = 0;
  574. }
  575. m_mtPlayTo = 0;
  576. return S_OK;
  577. }
  578. // @method:(INTERNAL) HRESULT | IDirectMusicPerformance | QueryInterface | Standard QueryInterface implementation for <i IDirectMusicPerformance>
  579. //
  580. // @rdesc Returns one of the following:
  581. //
  582. // @flag S_OK | If the interface is supported and was returned
  583. // @flag E_NOINTERFACE | If the object does not support the given interface.
  584. // @flag E_POINTER | <p ppv> is NULL or invalid.
  585. //
  586. STDMETHODIMP CPerformance::QueryInterface(
  587. const IID &iid, // @parm Interface to query for
  588. void **ppv) // @parm The requested interface will be returned here
  589. {
  590. V_INAME(CPerformance::QueryInterface);
  591. V_PTRPTR_WRITE(ppv);
  592. V_REFGUID(iid);
  593. *ppv = NULL;
  594. if (iid == IID_IUnknown || iid == IID_IDirectMusicPerformance)
  595. {
  596. *ppv = static_cast<IDirectMusicPerformance*>(this);
  597. } else
  598. if (iid == IID_IDirectMusicPerformance8)
  599. {
  600. m_dwVersion = 8;
  601. *ppv = static_cast<IDirectMusicPerformance8*>(this);
  602. } else
  603. if (iid == IID_IDirectMusicPerformance2)
  604. {
  605. m_dwVersion = 7;
  606. *ppv = static_cast<IDirectMusicPerformance*>(this);
  607. } else
  608. if( iid == IID_IDirectMusicPerformanceStats )
  609. {
  610. *ppv = static_cast<IDirectMusicPerformanceStats*>(this);
  611. } else
  612. if( iid == IID_IDirectMusicSetParamHook )
  613. {
  614. *ppv = static_cast<IDirectMusicSetParamHook*>(this);
  615. } else
  616. if (iid == IID_IDirectMusicTool)
  617. {
  618. *ppv = static_cast<IDirectMusicTool*>(this);
  619. } else
  620. if (iid == IID_CPerformance)
  621. {
  622. *ppv = static_cast<CPerformance*>(this);
  623. }
  624. if (iid == IID_IDirectMusicGraph)
  625. {
  626. *ppv = static_cast<IDirectMusicGraph*>(this);
  627. }
  628. if (iid == IID_IDirectMusicPerformanceP)
  629. {
  630. *ppv = static_cast<IDirectMusicPerformanceP*>(this);
  631. } else
  632. if (iid == IID_IDispatch)
  633. {
  634. // A helper scripting object implements IDispatch, which we expose from the
  635. // Performance object via COM aggregation.
  636. if (!m_pUnkDispatch)
  637. {
  638. // Create the helper object
  639. ::CoCreateInstance(
  640. CLSID_AutDirectMusicPerformance,
  641. static_cast<IDirectMusicPerformance*>(this),
  642. CLSCTX_INPROC_SERVER,
  643. IID_IUnknown,
  644. reinterpret_cast<void**>(&m_pUnkDispatch));
  645. }
  646. if (m_pUnkDispatch)
  647. {
  648. return m_pUnkDispatch->QueryInterface(IID_IDispatch, ppv);
  649. }
  650. }
  651. if (*ppv == NULL)
  652. {
  653. Trace(4,"Warning: Request to query unknown interface on Performance object\n");
  654. return E_NOINTERFACE;
  655. }
  656. reinterpret_cast<IUnknown*>(this)->AddRef();
  657. return S_OK;
  658. }
  659. // @method:(INTERNAL) HRESULT | IDirectMusicPerformance | AddRef | Standard AddRef implementation for <i IDirectMusicPerformance>
  660. //
  661. // @rdesc Returns the new reference count for this object.
  662. //
  663. STDMETHODIMP_(ULONG) CPerformance::AddRef()
  664. {
  665. return InterlockedIncrement(&m_cRef);
  666. }
  667. // @method:(INTERNAL) HRESULT | IDirectMusicPerformance | Release | Standard Release implementation for <i IDirectMusicPerformance>
  668. //
  669. // @rdesc Returns the new reference count for this object.
  670. //
  671. STDMETHODIMP_(ULONG) CPerformance::Release()
  672. {
  673. if (!InterlockedDecrement(&m_cRef))
  674. {
  675. DWORD dwThreadID = GetCurrentThreadId();
  676. m_cRef = 100; // artificial reference count to prevent reentrency due to COM aggregation
  677. if (dwThreadID == m_dwTransportThreadID)
  678. {
  679. m_fReleasedInTransport = true;
  680. m_fKillThread = TRUE;
  681. }
  682. else if (dwThreadID == m_dwRealtimeThreadID)
  683. {
  684. m_fReleasedInRealtime = true;
  685. m_fKillRealtimeThread = TRUE;
  686. }
  687. else
  688. {
  689. delete this;
  690. }
  691. return 0;
  692. }
  693. return m_cRef;
  694. }
  695. // call this only from within a m_SegmentCrSec critical section
  696. // if fSendNotify, then send segment end notifications for segments that were
  697. // playing
  698. void CPerformance::DequeueAllSegments()
  699. {
  700. CSegState *pNode;
  701. DWORD dwCount;
  702. for( dwCount = 0; dwCount < SQ_COUNT; dwCount++ )
  703. {
  704. while( pNode = m_SegStateQueues[dwCount].RemoveHead())
  705. {
  706. pNode->ShutDown();
  707. }
  708. }
  709. while( pNode = m_ShutDownQueue.RemoveHead())
  710. {
  711. pNode->ShutDown();
  712. }
  713. }
  714. // IDirectMusicPerformanceStats
  715. STDMETHODIMP CPerformance::TraceAllSegments()
  716. {
  717. CSegState *pNode;
  718. DWORD dwCount;
  719. for( dwCount = 0; dwCount < SQ_COUNT; dwCount++ )
  720. {
  721. EnterCriticalSection(&m_SegmentCrSec);
  722. for ( pNode = m_SegStateQueues[dwCount].GetHead();pNode;pNode=pNode->GetNext())
  723. {
  724. TraceI(0,"%x %ld: Playing: %ld, Start: %ld, Seek: %ld, LastPlayed: %ld\n",
  725. pNode,dwCount,pNode->m_fStartedPlay, pNode->m_mtResolvedStart,
  726. pNode->m_mtSeek, pNode->m_mtLastPlayed);
  727. }
  728. LeaveCriticalSection(&m_SegmentCrSec);
  729. }
  730. return S_OK;
  731. }
  732. STDMETHODIMP CPerformance::CreateSegstateList(DMUS_SEGSTATEDATA ** ppList)
  733. {
  734. if (!ppList) return E_POINTER;
  735. CSegState *pNode;
  736. DWORD dwCount;
  737. for( dwCount = 0; dwCount < SQ_COUNT; dwCount++ )
  738. {
  739. EnterCriticalSection(&m_SegmentCrSec);
  740. for ( pNode = m_SegStateQueues[dwCount].GetHead();pNode;pNode=pNode->GetNext())
  741. {
  742. DMUS_SEGSTATEDATA *pData = new DMUS_SEGSTATEDATA;
  743. if (pData)
  744. {
  745. CSegment *pSegment = pNode->m_pSegment;
  746. if (pSegment && (pSegment->m_dwValidData & DMUS_OBJ_NAME))
  747. {
  748. StringCchCopyW(pData->wszName, DMUS_MAX_NAME, pSegment->m_wszName);
  749. }
  750. else
  751. {
  752. pData->wszName[0] = 0;
  753. }
  754. pData->dwQueue = dwCount;
  755. pData->pSegState = (IDirectMusicSegmentState *) pNode;
  756. pNode->AddRef();
  757. pData->pNext = *ppList;
  758. pData->mtLoopEnd = pNode->m_mtLoopEnd;
  759. pData->mtLoopStart = pNode->m_mtLoopStart;
  760. pData->dwRepeats = pNode->m_dwRepeats;
  761. pData->dwPlayFlags = pNode->m_dwPlaySegFlags;
  762. pData->mtLength = pNode->m_mtLength;
  763. pData->rtGivenStart = pNode->m_rtGivenStart;
  764. pData->mtResolvedStart = pNode->m_mtResolvedStart;
  765. pData->mtOffset = pNode->m_mtOffset;
  766. pData->mtLastPlayed = pNode->m_mtLastPlayed;
  767. pData->mtPlayTo = pNode->m_mtStopTime;
  768. pData->mtSeek = pNode->m_mtSeek;
  769. pData->mtStartPoint = pNode->m_mtStartPoint;
  770. pData->dwRepeatsLeft = pNode->m_dwRepeatsLeft;
  771. pData->fStartedPlay = pNode->m_fStartedPlay;
  772. *ppList = pData;
  773. }
  774. }
  775. LeaveCriticalSection(&m_SegmentCrSec);
  776. }
  777. return S_OK;
  778. }
  779. STDMETHODIMP CPerformance::FreeSegstateList(DMUS_SEGSTATEDATA * pList)
  780. {
  781. DMUS_SEGSTATEDATA *pState;
  782. while (pList)
  783. {
  784. pState = pList;
  785. pList = pList->pNext;
  786. pState->pSegState->Release();
  787. delete pState;
  788. }
  789. return S_OK;
  790. }
  791. void CPerformance::SendBuffers()
  792. {
  793. DWORD dwIndex;
  794. PortTable* pPortTable;
  795. #ifdef DBG_PROFILE
  796. DWORD dwDebugTime;
  797. dwDebugTime = timeGetTime();
  798. #endif
  799. EnterCriticalSection(&m_PChannelInfoCrSec);
  800. for( dwIndex = 0; dwIndex < m_dwNumPorts; dwIndex++ )
  801. {
  802. if( m_pPortTable[dwIndex].fBufferFilled && m_pPortTable[dwIndex].pBuffer )
  803. {
  804. pPortTable = &m_pPortTable[dwIndex];
  805. pPortTable->fBufferFilled = FALSE;
  806. ASSERT( pPortTable->pBuffer );
  807. if( pPortTable->pPort )
  808. {
  809. pPortTable->pPort->PlayBuffer( pPortTable->pBuffer );
  810. // TraceI(5, "SENT BUFFERS time=%ld latency=%ld\n", (long)(GetTime() / 10000),(long)(GetLatency()/10000));
  811. }
  812. pPortTable->pBuffer->Flush();
  813. }
  814. }
  815. LeaveCriticalSection(&m_PChannelInfoCrSec);
  816. #ifdef DBG_PROFILE
  817. dwDebugTime = timeGetTime() - dwDebugTime;
  818. if( dwDebugTime > 1 )
  819. {
  820. TraceI(5, "Hall, debugtime SendBuffers %u\n", dwDebugTime);
  821. }
  822. #endif
  823. }
  824. static DWORD WINAPI _Realtime(LPVOID lpParam)
  825. {
  826. if (SUCCEEDED(::CoInitialize(NULL)))
  827. {
  828. ((CPerformance *)lpParam)->Realtime();
  829. ::CoUninitialize();
  830. }
  831. return 0;
  832. }
  833. void CPerformance::Realtime()
  834. {
  835. while (!m_fKillRealtimeThread)
  836. {
  837. EnterCriticalSection(&m_RealtimeCrSec);
  838. PRIV_PMSG *pEvent;
  839. HRESULT hr;
  840. REFERENCE_TIME rtFirst = 0;
  841. REFERENCE_TIME rtEnter = GetLatencyWithPrePlay();
  842. DWORD dwTestTime;
  843. DWORD dwBeginTime = timeGetTime();
  844. DWORD dwLimitLoop = 0;
  845. if( rtEnter > m_rtQueuePosition )
  846. {
  847. m_rtQueuePosition = rtEnter;
  848. }
  849. while (1)
  850. {
  851. // rtFirst equals the time that the first event was packed into a buffer.
  852. // Once this time is greater than the latency clock (minus a delay) we need
  853. // to queue the buffers so the events get down in time to be rendered.
  854. // If rtFirst is 0 it means it hasn't been initialized yet.
  855. dwTestTime = timeGetTime();
  856. if( dwTestTime - dwBeginTime > REALTIME_RES )
  857. {
  858. if( ++dwLimitLoop > 10 )
  859. {
  860. TraceI(1,"Error! We've been in the realtime thread too long!!! Breaking out without completing.\n");
  861. break;
  862. }
  863. SendBuffers();
  864. dwBeginTime = dwTestTime;
  865. }
  866. pEvent = GetNextPMsg();
  867. if( NULL == pEvent )
  868. {
  869. break;
  870. }
  871. ASSERT( pEvent->pNext == NULL );
  872. if( !pEvent->pTool )
  873. {
  874. // this event doesn't have a Tool pointer, so stamp it with the
  875. // final output Tool.
  876. pEvent->pTool = (IDirectMusicTool*)this;
  877. AddRef();
  878. }
  879. // before processing the event, set rtLast to the event's current time
  880. pEvent->rtLast = pEvent->rtTime;
  881. hr = pEvent->pTool->ProcessPMsg( this, PRIV_TO_DMUS(pEvent) );
  882. if( hr != S_OK ) // S_OK means do nothing
  883. {
  884. if( hr == DMUS_S_REQUEUE )
  885. {
  886. if(FAILED(SendPMsg( PRIV_TO_DMUS(pEvent) )))
  887. {
  888. FreePMsg(pEvent);
  889. }
  890. }
  891. else // e.g. DMUS_S_FREE or error code
  892. {
  893. FreePMsg( pEvent );
  894. }
  895. }
  896. }
  897. SendBuffers();
  898. LeaveCriticalSection(&m_RealtimeCrSec);
  899. if( m_hRealtime )
  900. {
  901. WaitForSingleObject( m_hRealtime, REALTIME_RES );
  902. }
  903. else
  904. {
  905. Sleep(REALTIME_RES);
  906. }
  907. }
  908. m_fKillRealtimeThread = FALSE;
  909. TraceI(2, "dmperf: LEAVE realtime\n");
  910. if (m_fReleasedInRealtime)
  911. {
  912. delete this;
  913. }
  914. }
  915. void CPerformance::GenerateNotification( DWORD dwNotification, MUSIC_TIME mtTime,
  916. IDirectMusicSegmentState* pSegSt)
  917. {
  918. GUID guid;
  919. guid = GUID_NOTIFICATION_PERFORMANCE;
  920. if( FindNotification( guid ))
  921. {
  922. DMUS_NOTIFICATION_PMSG* pEvent = NULL;
  923. if( SUCCEEDED( AllocPMsg( sizeof(DMUS_NOTIFICATION_PMSG),
  924. (DMUS_PMSG**)&pEvent )))
  925. {
  926. pEvent->dwField1 = 0;
  927. pEvent->dwField2 = 0;
  928. pEvent->guidNotificationType = GUID_NOTIFICATION_PERFORMANCE;
  929. pEvent->dwType = DMUS_PMSGT_NOTIFICATION;
  930. pEvent->mtTime = mtTime;
  931. pEvent->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_ATTIME;
  932. pEvent->dwGroupID = 0xffffffff;
  933. pEvent->dwPChannel = 0;
  934. pEvent->dwNotificationOption = dwNotification;
  935. if( pSegSt )
  936. {
  937. pSegSt->QueryInterface(IID_IUnknown, (void**)&pEvent->punkUser);
  938. }
  939. StampPMsg((DMUS_PMSG*)pEvent);
  940. if(FAILED(SendPMsg( (DMUS_PMSG*)pEvent )))
  941. {
  942. FreePMsg((DMUS_PMSG*)pEvent);
  943. }
  944. }
  945. }
  946. }
  947. void CPerformance::PrepSegToPlay(CSegState *pSegState, bool fQueue)
  948. /* Called when a segment is first queued, once the start time of the segment is known.
  949. This calculates various fields that need to be initialized and also regenerates the
  950. tempo map if the new segment has an active tempo map in it.
  951. */
  952. {
  953. if (!pSegState->m_fPrepped)
  954. {
  955. pSegState->m_fPrepped = TRUE;
  956. pSegState->m_mtLastPlayed = pSegState->m_mtResolvedStart;
  957. // if this is queued to play after the current segment ends, no need to recalc the tempo map;
  958. // it will be updated as necessary by the transport thread.
  959. if (!fQueue)
  960. {
  961. RecalcTempoMap(pSegState, pSegState->m_mtResolvedStart);
  962. }
  963. MusicToReferenceTime(pSegState->m_mtLastPlayed,&pSegState->m_rtLastPlayed);
  964. // Calculate the total duration of the segment and store in m_mtEndTime.
  965. pSegState->m_mtEndTime = pSegState->GetEndTime(pSegState->m_mtResolvedStart);
  966. }
  967. }
  968. /*
  969. void | CPerformance | PerformSegStNode |
  970. Perform a Segment State contained in the CSegState.
  971. Note that this ppSegStNode may be dequeued, so don't depend on it
  972. staying around!
  973. */
  974. void CPerformance::PerformSegStNode(
  975. DWORD dwList, // The list the segmentstate comes from.
  976. CSegState* pSegStNode) // The segmentstate node.
  977. {
  978. MUSIC_TIME mtMargin; // tracks how much of a segment to play
  979. HRESULT hr;
  980. CSegStateList *pList = &m_SegStateQueues[dwList];
  981. CSegState *pNext;
  982. if( !m_fPlaying || m_fInTrackPlay )
  983. {
  984. return;
  985. }
  986. if( pSegStNode )
  987. {
  988. m_fInTransportThread = TRUE; // Disable realtime processing of early queue messages.
  989. hr = S_OK;
  990. //Trace(0,"%ld: Performing %lx, Active: %ld, Start Time: %ld, End Time: %ld\n",m_mtPlayTo,
  991. // pSegStNode->m_pSegment,pSegStNode->m_fStartedPlay,pSegStNode->m_mtResolvedStart,pSegStNode->m_mtEndTime);
  992. if( !pSegStNode->m_fStartedPlay )
  993. {
  994. // check to see if this SegState should start playing.
  995. ASSERT( !(pSegStNode->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ));
  996. if( pSegStNode->m_mtResolvedStart < m_mtPlayTo )
  997. {
  998. pSegStNode->m_fStartedPlay = TRUE;
  999. PrepSegToPlay(pSegStNode);
  1000. // send a MUSICSTARTED notification if needed
  1001. if(m_fMusicStopped)
  1002. {
  1003. m_fMusicStopped = FALSE;
  1004. GenerateNotification( DMUS_NOTIFICATION_MUSICSTARTED, pSegStNode->m_mtResolvedStart, NULL );
  1005. }
  1006. // We don't want the music to start with a big BLURP in track
  1007. // order, so we send a little dribble out on each track.
  1008. mtMargin = m_mtPlayTo - pSegStNode->m_mtLastPlayed;
  1009. if( mtMargin >= 50 )
  1010. {
  1011. hr = pSegStNode->Play( 50 );
  1012. ProcessEarlyPMsgs();
  1013. // Once done processing all the early messages, make sure that the realtime
  1014. // thread wakes up and does whatever it needs to do. This ensures that the starting
  1015. // notes in a sequence get to the output port immediately.
  1016. if( m_hRealtime ) SetEvent( m_hRealtime );
  1017. mtMargin = m_mtPlayTo - pSegStNode->m_mtLastPlayed;
  1018. // Then, we send a larger chunk out on each track to catch up a little more...
  1019. if ((hr == S_OK) && ( mtMargin >= 200 ))
  1020. {
  1021. hr = pSegStNode->Play( 200 );
  1022. ProcessEarlyPMsgs();
  1023. }
  1024. }
  1025. }
  1026. else
  1027. {
  1028. MusicToReferenceTime(pSegStNode->m_mtLastPlayed,&pSegStNode->m_rtLastPlayed);
  1029. }
  1030. }
  1031. if( pSegStNode->m_fStartedPlay )
  1032. {
  1033. if( pSegStNode->m_mtStopTime && ( pSegStNode->m_mtStopTime < m_mtPlayTo ) )
  1034. {
  1035. mtMargin = pSegStNode->m_mtStopTime - pSegStNode->m_mtLastPlayed;
  1036. }
  1037. else
  1038. {
  1039. mtMargin = m_mtPlayTo - pSegStNode->m_mtLastPlayed;
  1040. }
  1041. while ((hr == S_OK) && (mtMargin > 0))
  1042. {
  1043. // Do not allow more than a quarter note's worth to be done at once.
  1044. MUSIC_TIME mtRange = mtMargin;
  1045. if (mtRange > DMUS_PPQ)
  1046. {
  1047. mtRange = DMUS_PPQ;
  1048. mtMargin -= mtRange;
  1049. }
  1050. else
  1051. {
  1052. mtMargin = 0;
  1053. }
  1054. hr = pSegStNode->Play( mtRange );
  1055. ProcessEarlyPMsgs();
  1056. }
  1057. }
  1058. if( (hr == DMUS_S_END) || ( pSegStNode->m_mtStopTime &&
  1059. ( pSegStNode->m_mtStopTime <= pSegStNode->m_mtLastPlayed ) ) )
  1060. {
  1061. if( pSegStNode->m_mtStopTime && (pSegStNode->m_mtStopTime == pSegStNode->m_mtLastPlayed) )
  1062. {
  1063. pSegStNode->AbortPlay(pSegStNode->m_mtStopTime - 1, FALSE);
  1064. }
  1065. MUSIC_TIME mtEnd = pSegStNode->m_mtLastPlayed;
  1066. if( pList == &m_SegStateQueues[SQ_PRI_PLAY] )
  1067. {
  1068. // move primary segments to PriPastList
  1069. pList->Remove(pSegStNode);
  1070. m_SegStateQueues[SQ_PRI_DONE].Insert(pSegStNode);
  1071. pNext = pList->GetHead();
  1072. if( pNext )
  1073. {
  1074. if (!( pNext->m_dwPlaySegFlags & DMUS_SEGF_NOINVALIDATE ))
  1075. {
  1076. if (IsConQueue(dwList))
  1077. {
  1078. Invalidate( pNext->m_mtResolvedStart, 0 );
  1079. }
  1080. }
  1081. }
  1082. else // No more primary segments, send DMUS_NOTIFICATION_MUSICALMOSTEND
  1083. {
  1084. if (m_dwVersion >= 8)
  1085. {
  1086. MUSIC_TIME mtNow;
  1087. GetTime( NULL, &mtNow );
  1088. GenerateNotification( DMUS_NOTIFICATION_MUSICALMOSTEND, mtNow, pSegStNode );
  1089. }
  1090. }
  1091. ManageControllingTracks();
  1092. }
  1093. else if ( pList == &m_SegStateQueues[SQ_CON_PLAY] )
  1094. {
  1095. pList->Remove(pSegStNode );
  1096. if (pSegStNode->m_mtStopTime == pSegStNode->m_mtLastPlayed)
  1097. {
  1098. m_ShutDownQueue.Insert(pSegStNode);
  1099. }
  1100. else
  1101. {
  1102. m_SegStateQueues[SQ_CON_DONE].Insert(pSegStNode);
  1103. }
  1104. }
  1105. else
  1106. {
  1107. // move 2ndary segments to SecPastList
  1108. pList->Remove(pSegStNode);
  1109. m_SegStateQueues[SQ_SEC_DONE].Insert(pSegStNode);
  1110. }
  1111. // if there aren't any more segments to play, send a Music Stopped
  1112. // notification
  1113. if( (m_SegStateQueues[SQ_PRI_PLAY].IsEmpty() && m_SegStateQueues[SQ_SEC_PLAY].IsEmpty() &&
  1114. m_SegStateQueues[SQ_PRI_WAIT].IsEmpty() && m_SegStateQueues[SQ_SEC_WAIT].IsEmpty() &&
  1115. m_SegStateQueues[SQ_CON_PLAY].IsEmpty() && m_SegStateQueues[SQ_CON_WAIT].IsEmpty()))
  1116. {
  1117. m_fMusicStopped = TRUE;
  1118. GenerateNotification( DMUS_NOTIFICATION_MUSICSTOPPED, mtEnd, NULL );
  1119. }
  1120. }
  1121. m_fInTransportThread = FALSE;
  1122. }
  1123. }
  1124. static DWORD WINAPI _Transport(LPVOID lpParam)
  1125. {
  1126. if (SUCCEEDED(::CoInitialize(NULL)))
  1127. {
  1128. ((CPerformance *)lpParam)->Transport();
  1129. ::CoUninitialize();
  1130. }
  1131. return 0;
  1132. }
  1133. // call Segment's play code on a periodic basis. This routine is in its
  1134. // own thread.
  1135. void CPerformance::Transport()
  1136. {
  1137. srand((unsigned int)time(NULL));
  1138. while (!m_fKillThread)
  1139. {
  1140. DWORD dwCount;
  1141. CSegState* pNode;
  1142. CSegState* pNext;
  1143. CSegState* pTempQueue = NULL;
  1144. REFERENCE_TIME rtNow = GetTime();
  1145. EnterCriticalSection(&m_SegmentCrSec);
  1146. // Compute the time we should play all the segments to.
  1147. REFERENCE_TIME rtPlayTo = rtNow + PREPARE_TIME;
  1148. MUSIC_TIME mtAmount, mtResult, mtPlayTo;
  1149. mtPlayTo = 0;
  1150. ReferenceToMusicTime( rtPlayTo, &mtPlayTo );
  1151. if (m_fTempoChanged)
  1152. {
  1153. // If there has been a tempo change to slower, any clock time tracks could
  1154. // be delayed to long as the transport holds off sending out events. That's
  1155. // okay for music time tracks, but bad news for clock time tracks. This
  1156. // makes sure that the clock time tracks get a chance to spew.
  1157. if (m_mtPlayTo >= mtPlayTo)
  1158. {
  1159. mtPlayTo = m_mtPlayTo + 10;
  1160. }
  1161. m_fTempoChanged = FALSE;
  1162. }
  1163. IncrementTempoMap();
  1164. while (m_mtPlayTo < mtPlayTo)
  1165. {
  1166. BOOL fDirty = FALSE; // see below
  1167. m_mtPlayTo = mtPlayTo; // Start out optimistic
  1168. // We need to set play boundaries at the end of control segments.
  1169. // The beginnings of control segments are handled inside the segment state code.
  1170. pNode = m_SegStateQueues[SQ_PRI_PLAY].GetHead();
  1171. if( pNode && pNode->m_fStartedPlay )
  1172. {
  1173. mtAmount = m_mtPlayTo - pNode->m_mtLastPlayed;
  1174. pNode->CheckPlay( mtAmount, &mtResult );
  1175. if( mtResult < mtAmount )
  1176. {
  1177. m_mtPlayTo -= ( mtAmount - mtResult );
  1178. // don't need dirty flag when primary segment loops or ends normally (bug 30829)
  1179. // fDirty = TRUE; // see below
  1180. }
  1181. }
  1182. // if a control segment ended prematurely, mtPlayTo will have a value besides 0
  1183. // check for upcoming endings to control segments
  1184. for( pNode = m_SegStateQueues[SQ_CON_PLAY].GetHead(); pNode; pNode = pNode->GetNext() )
  1185. {
  1186. if( pNode->m_fStartedPlay )
  1187. {
  1188. if( pNode->m_mtStopTime && (m_mtPlayTo > pNode->m_mtStopTime) )
  1189. {
  1190. m_mtPlayTo = pNode->m_mtStopTime;
  1191. fDirty = TRUE; // see below
  1192. }
  1193. else
  1194. {
  1195. mtAmount = m_mtPlayTo - pNode->m_mtLastPlayed;
  1196. pNode->CheckPlay( mtAmount, &mtResult );
  1197. if( mtResult < mtAmount )
  1198. {
  1199. m_mtPlayTo -= ( mtAmount - mtResult );
  1200. fDirty = TRUE; // see below
  1201. }
  1202. }
  1203. }
  1204. }
  1205. // play the primary segment
  1206. PerformSegStNode( SQ_PRI_PLAY,m_SegStateQueues[SQ_PRI_PLAY].GetHead() );
  1207. // check to see if the next primary segment in the queue is ready to play
  1208. while( (pNode = m_SegStateQueues[SQ_PRI_PLAY].GetHead()) &&
  1209. (pNext = pNode->GetNext()) &&
  1210. ( pNext->m_mtResolvedStart <= pNode->m_mtLastPlayed ) )
  1211. {
  1212. // the next primary segment is indeed ready to begin playing.
  1213. // save the old one in the primary past list so Tools can reference
  1214. // it if they're looking for chord progressions and such.
  1215. pNode->AbortPlay(pNext->m_mtResolvedStart-1,TRUE && (pNext->m_dwPlaySegFlags & DMUS_SEGF_NOINVALIDATE));
  1216. m_SegStateQueues[SQ_PRI_DONE].Insert(m_SegStateQueues[SQ_PRI_PLAY].RemoveHead());
  1217. ManageControllingTracks();
  1218. // we need to flush primary events after the new start time
  1219. if(!( m_SegStateQueues[SQ_PRI_PLAY].GetHead()->m_dwPlaySegFlags & (DMUS_SEGF_NOINVALIDATE | DMUS_SEGF_INVALIDATE_PRI) ))
  1220. {
  1221. Invalidate( m_SegStateQueues[SQ_PRI_PLAY].GetHead()->m_mtResolvedStart, 0 );
  1222. }
  1223. // and play the new segment
  1224. PerformSegStNode( SQ_PRI_PLAY,m_SegStateQueues[SQ_PRI_PLAY].GetHead());
  1225. }
  1226. // play the controlling segments
  1227. pNode = m_SegStateQueues[SQ_CON_PLAY].GetHead();
  1228. pNext = NULL;
  1229. for(; pNode != NULL; pNode = pNext)
  1230. {
  1231. pNext = pNode->GetNext();
  1232. PerformSegStNode(SQ_CON_PLAY,pNode );
  1233. }
  1234. // play the secondary segments
  1235. pNode = m_SegStateQueues[SQ_SEC_PLAY].GetHead();
  1236. pNext = NULL;
  1237. for(; pNode != NULL; pNode = pNext)
  1238. {
  1239. pNext = pNode->GetNext();
  1240. PerformSegStNode( SQ_SEC_PLAY,pNode );
  1241. }
  1242. // if we set fDirty above, it means that we truncated the playback of a control
  1243. // segment because of a loop or end condition. Therefore, we want all segments
  1244. // to set the DMUS_TRACKF_DIRTY flag on the next play cycle.
  1245. if( fDirty )
  1246. {
  1247. for (dwCount = SQ_PRI_PLAY; dwCount <= SQ_SEC_PLAY; dwCount++)
  1248. {
  1249. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext() )
  1250. {
  1251. if( pNode->m_fStartedPlay )
  1252. {
  1253. pNode->m_dwPlayTrackFlags |= DMUS_TRACKF_DIRTY;
  1254. }
  1255. }
  1256. }
  1257. ManageControllingTracks();
  1258. }
  1259. m_mtTransported = m_mtPlayTo;
  1260. }
  1261. // check segments queued in ref-time to see if it's time for them to
  1262. // play. Add some extra time just in case. We'll bet that a tempo pmsg won't come
  1263. // in in the intervening 200 ms.
  1264. REFERENCE_TIME rtLatency = GetLatencyWithPrePlay();
  1265. for (dwCount = SQ_PRI_WAIT;dwCount <= SQ_SEC_WAIT; dwCount++)
  1266. {
  1267. while( m_SegStateQueues[dwCount].GetHead() )
  1268. {
  1269. if( m_SegStateQueues[dwCount].GetHead()->m_rtGivenStart > rtLatency + PREPARE_TIME + (200 * REF_PER_MIL) )
  1270. {
  1271. // it's not yet time to handle this one
  1272. break;
  1273. }
  1274. if (dwCount == SQ_PRI_WAIT)
  1275. {
  1276. QueuePrimarySegment( m_SegStateQueues[SQ_PRI_WAIT].RemoveHead());
  1277. }
  1278. else
  1279. {
  1280. QueueSecondarySegment( m_SegStateQueues[dwCount].RemoveHead());
  1281. }
  1282. }
  1283. }
  1284. // Check to see if Segments in the done queues
  1285. // can be released. They can be released if their
  1286. // final play times are older than the current time.
  1287. for (dwCount = SQ_PRI_DONE;dwCount <= SQ_SEC_DONE; dwCount++)
  1288. {
  1289. for (pNode = m_SegStateQueues[dwCount].GetHead();pNode;pNode = pNext)
  1290. {
  1291. pNext = pNode->GetNext();
  1292. if( pNode->m_rtLastPlayed < rtNow - 1000 * REF_PER_MIL ) // Let it last an additional second
  1293. {
  1294. m_SegStateQueues[dwCount].Remove(pNode);
  1295. pNode->ShutDown();
  1296. }
  1297. }
  1298. }
  1299. for (pNode = m_ShutDownQueue.GetHead();pNode;pNode = pNext)
  1300. {
  1301. pNext = pNode->GetNext();
  1302. if( pNode->m_rtLastPlayed < rtNow - 1000 * REF_PER_MIL ) // Let it last an additional second
  1303. {
  1304. m_ShutDownQueue.Remove(pNode);
  1305. pNode->ShutDown();
  1306. }
  1307. }
  1308. LeaveCriticalSection(&m_SegmentCrSec);
  1309. // check to see if there are old notifications that haven't been
  1310. // retrieved by the application and need to be removed.
  1311. EnterCriticalSection(&m_PipelineCrSec);
  1312. while( m_NotificationQueue.GetHead() )
  1313. {
  1314. if( m_NotificationQueue.GetHead()->rtTime <
  1315. (rtNow - m_rtNotificationDiscard) )
  1316. {
  1317. FreePMsg(m_NotificationQueue.Dequeue());
  1318. }
  1319. else
  1320. {
  1321. break;
  1322. }
  1323. }
  1324. LeaveCriticalSection(&m_PipelineCrSec);
  1325. if( m_hTransport )
  1326. {
  1327. WaitForSingleObject( m_hTransport, TRANSPORT_RES );
  1328. }
  1329. else
  1330. {
  1331. Sleep(TRANSPORT_RES);
  1332. }
  1333. }
  1334. m_fKillThread = FALSE;
  1335. if (m_fReleasedInTransport)
  1336. {
  1337. delete this;
  1338. }
  1339. }
  1340. //////////////////////////////////////////////////////////////////////
  1341. // CPerformance::GetNextPMsg
  1342. /*
  1343. HRESULT | CPerformance | GetNextPMsg |
  1344. Returns messages from the queues in priority order. Any message in the
  1345. OnTime queue that is scheduled to be played at the current time is
  1346. returned above any other. Secondly, any message in the NearTime queue
  1347. that is scheduled to be played within the next NEARTIME ms is returned.
  1348. Lastly, any message in the Early queue is returned.
  1349. rvalue PRIV_PMSG* | The message, or NULL if there are no messages.
  1350. */
  1351. inline PRIV_PMSG *CPerformance::GetNextPMsg()
  1352. {
  1353. #ifdef DBG_PROFILE
  1354. DWORD dwDebugTime;
  1355. dwDebugTime = timeGetTime();
  1356. #endif
  1357. PRIV_PMSG* pEvent = NULL;
  1358. EnterCriticalSection(&m_PipelineCrSec);
  1359. if (m_OnTimeQueue.GetHead())
  1360. {
  1361. ASSERT( m_OnTimeQueue.GetHead()->dwFlags & DMUS_PMSGF_REFTIME );
  1362. if ( m_OnTimeQueue.GetHead()->rtTime - GetTime() <= 0 )
  1363. {
  1364. pEvent = m_OnTimeQueue.Dequeue();
  1365. }
  1366. }
  1367. if( !pEvent )
  1368. {
  1369. if (m_NearTimeQueue.GetHead())
  1370. {
  1371. ASSERT( m_NearTimeQueue.GetHead()->dwFlags & DMUS_PMSGF_REFTIME );
  1372. if ( m_NearTimeQueue.GetHead()->rtTime < (m_rtQueuePosition + (m_rtBumperLength >> 1)))
  1373. {
  1374. pEvent = m_NearTimeQueue.Dequeue();
  1375. }
  1376. }
  1377. if( !pEvent && !m_fInTransportThread)
  1378. {
  1379. if (m_EarlyQueue.GetHead())
  1380. {
  1381. pEvent = m_EarlyQueue.Dequeue();
  1382. }
  1383. }
  1384. }
  1385. LeaveCriticalSection(&m_PipelineCrSec);
  1386. #ifdef DBG_PROFILE
  1387. dwDebugTime = timeGetTime() - dwDebugTime;
  1388. if( dwDebugTime > 1 )
  1389. {
  1390. TraceI(5, "Hall, debugtime GetNextPMsg %u\n", dwDebugTime);
  1391. }
  1392. #endif
  1393. return pEvent;
  1394. }
  1395. /* This next function is used just by the transport thread
  1396. which can process messages in the early queue, but not
  1397. the other types. This allows all the tools that process
  1398. events right after they are generated by tracks to process
  1399. the events right after they were generated, and in sequential
  1400. order. This allows them to take a little longer, since it's
  1401. not as time critical, and it's much more likely to ensure
  1402. that they are in sequential order. If the realtime thread were
  1403. allowed to process these, it would preempt and process them
  1404. as soon as generated, so they would be processed in the order
  1405. of the tracks. The m_fInTransportThread is set by the
  1406. transport thread when it is generating and processing events
  1407. and this disallows the realtime thread from processing
  1408. early events (but not others.) At other times, the realtime
  1409. thread is welcome to process early events.
  1410. */
  1411. void CPerformance::ProcessEarlyPMsgs()
  1412. {
  1413. PRIV_PMSG* pEvent;
  1414. // Exit if the thread is exiting. If we don't test here
  1415. // we can actually loop forever because tools and queue more
  1416. // early PMSGs (the Echo tool does this)
  1417. while (!m_fKillThread)
  1418. {
  1419. EnterCriticalSection(&m_PipelineCrSec);
  1420. pEvent = m_EarlyQueue.Dequeue();
  1421. LeaveCriticalSection(&m_PipelineCrSec);
  1422. if (!pEvent) break; // Done?
  1423. ASSERT( pEvent->pNext == NULL );
  1424. if( !pEvent->pTool )
  1425. {
  1426. // this event doesn't have a Tool pointer, so stamp it with the
  1427. // final output Tool.
  1428. pEvent->pTool = (IDirectMusicTool*)this;
  1429. AddRef();
  1430. // Don't process it. Instead, send to neartime queue so
  1431. // realtime thread will deal with it.
  1432. pEvent->dwFlags &= ~(DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME);
  1433. pEvent->dwFlags |= DMUS_PMSGF_TOOL_QUEUE;
  1434. SendPMsg( PRIV_TO_DMUS(pEvent) );
  1435. }
  1436. else
  1437. {
  1438. // before processing the event, set rtLast to the event's current time
  1439. pEvent->rtLast = pEvent->rtTime;
  1440. HRESULT hr = pEvent->pTool->ProcessPMsg( this, PRIV_TO_DMUS(pEvent) );
  1441. if( hr != S_OK ) // S_OK means do nothing
  1442. {
  1443. if( hr == DMUS_S_REQUEUE )
  1444. {
  1445. if(FAILED(SendPMsg( PRIV_TO_DMUS(pEvent) )))
  1446. {
  1447. FreePMsg(pEvent);
  1448. }
  1449. }
  1450. else // e.g. DMUS_S_FREE or error code
  1451. {
  1452. FreePMsg( pEvent );
  1453. }
  1454. }
  1455. }
  1456. }
  1457. }
  1458. REFERENCE_TIME CPerformance::GetTime()
  1459. {
  1460. REFERENCE_TIME rtTime;
  1461. REFERENCE_TIME rtCurrent = 0;
  1462. WORD w;
  1463. HRESULT hr = S_OK;
  1464. EnterCriticalSection(&m_MainCrSec);
  1465. if (m_pClock) hr = m_pClock->GetTime( &rtCurrent );
  1466. if( !m_pClock || FAILED( hr ) || rtCurrent == 0 )
  1467. {
  1468. // this only gets called with machines that don't support m_pClock
  1469. rtTime = timeGetTime();
  1470. rtCurrent = rtTime * REF_PER_MIL; // 100 ns increments
  1471. // take care of timeGetTime rolling over every 49 days
  1472. if( rtCurrent < 0 )
  1473. {
  1474. m_wRollOverCount++;
  1475. }
  1476. for( w = 0; w < m_wRollOverCount; w++ )
  1477. {
  1478. rtCurrent += 4294967296;
  1479. }
  1480. // if rtCurrent is negative, it means we've rolled over rtCurrent. Ignore
  1481. // this case for now, as it will be quite uncommon.
  1482. }
  1483. LeaveCriticalSection(&m_MainCrSec);
  1484. return rtCurrent;
  1485. }
  1486. REFERENCE_TIME CPerformance::GetLatency(void)
  1487. {
  1488. DWORD dwIndex;
  1489. REFERENCE_TIME rtLatency = 0;
  1490. REFERENCE_TIME rtTemp;
  1491. #ifdef DBG_PROFILE
  1492. DWORD dwDebugTime;
  1493. dwDebugTime = timeGetTime();
  1494. #endif
  1495. EnterCriticalSection(&m_PChannelInfoCrSec);
  1496. if( m_pPortTable )
  1497. {
  1498. for( dwIndex = 0; dwIndex < m_dwNumPorts; dwIndex++ )
  1499. {
  1500. if( m_pPortTable[dwIndex].pLatencyClock )
  1501. {
  1502. if( SUCCEEDED( m_pPortTable[dwIndex].pLatencyClock->GetTime( &rtTemp )))
  1503. {
  1504. if( rtTemp > rtLatency )
  1505. rtLatency = rtTemp;
  1506. }
  1507. }
  1508. else if( m_pPortTable[dwIndex].pPort )
  1509. {
  1510. if( SUCCEEDED( m_pPortTable[dwIndex].pPort->GetLatencyClock( &m_pPortTable[dwIndex].pLatencyClock )))
  1511. {
  1512. if( SUCCEEDED( m_pPortTable[dwIndex].pLatencyClock->GetTime( &rtTemp )))
  1513. {
  1514. if( rtTemp > rtLatency )
  1515. rtLatency = rtTemp;
  1516. }
  1517. }
  1518. }
  1519. }
  1520. }
  1521. LeaveCriticalSection(&m_PChannelInfoCrSec);
  1522. if( 0 == rtLatency )
  1523. {
  1524. rtLatency = GetTime();
  1525. }
  1526. #ifdef DBG_PROFILE
  1527. dwDebugTime = timeGetTime() - dwDebugTime;
  1528. if( dwDebugTime > 1 )
  1529. {
  1530. TraceI(5, "Hall, debugtime GetLatency %u\n", dwDebugTime);
  1531. }
  1532. #endif
  1533. if (m_rtEarliestStartTime > rtLatency)
  1534. {
  1535. rtLatency = m_rtEarliestStartTime;
  1536. }
  1537. return rtLatency;
  1538. }
  1539. // return the most desireable Segment latency, based on which ports this
  1540. // segment plays on.
  1541. REFERENCE_TIME CPerformance::GetBestSegLatency( CSegState* pSeg )
  1542. {
  1543. // If we're using audiopaths, the code below doesn't work because it doesn't
  1544. // take converting pchannels into account. So, just use the worse case
  1545. // latency. 99% of the time, there is only one port, so this results
  1546. // in just a performance enhancement.
  1547. if (m_dwAudioPathMode == 2)
  1548. {
  1549. return GetLatency();
  1550. }
  1551. DWORD dwIndex;
  1552. REFERENCE_TIME rtLatency = 0;
  1553. REFERENCE_TIME rtTemp;
  1554. BOOL* pafIndexUsed = NULL;
  1555. DWORD dwCount;
  1556. if( m_dwNumPorts == 1 )
  1557. {
  1558. return GetLatency();
  1559. }
  1560. pafIndexUsed = new BOOL[m_dwNumPorts];
  1561. if( NULL == pafIndexUsed )
  1562. {
  1563. return GetLatency();
  1564. }
  1565. for( dwCount = 0; dwCount < m_dwNumPorts; dwCount++ )
  1566. {
  1567. pafIndexUsed[dwCount] = FALSE;
  1568. }
  1569. DWORD dwNumPChannels, dwGroup, dwMChannel;
  1570. DWORD* paPChannels;
  1571. pSeg->m_pSegment->GetPChannels( &dwNumPChannels, &paPChannels );
  1572. for( dwCount = 0; dwCount < dwNumPChannels; dwCount++ )
  1573. {
  1574. if( SUCCEEDED( PChannelIndex( paPChannels[dwCount],
  1575. &dwIndex, &dwGroup, &dwMChannel )))
  1576. {
  1577. pafIndexUsed[dwIndex] = TRUE;
  1578. }
  1579. }
  1580. for( dwCount = 0; dwCount < m_dwNumPorts; dwCount++ )
  1581. {
  1582. if( pafIndexUsed[dwCount] )
  1583. break;
  1584. }
  1585. if( dwCount >= m_dwNumPorts )
  1586. {
  1587. delete [] pafIndexUsed;
  1588. return GetLatency();
  1589. }
  1590. EnterCriticalSection(&m_PChannelInfoCrSec);
  1591. for( dwIndex = 0; dwIndex < m_dwNumPorts; dwIndex++ )
  1592. {
  1593. if( pafIndexUsed[dwIndex] )
  1594. {
  1595. if( m_pPortTable[dwIndex].pLatencyClock )
  1596. {
  1597. if( SUCCEEDED( m_pPortTable[dwIndex].pLatencyClock->GetTime( &rtTemp )))
  1598. {
  1599. if( rtTemp > rtLatency )
  1600. rtLatency = rtTemp;
  1601. }
  1602. }
  1603. else if( m_pPortTable[dwIndex].pPort )
  1604. {
  1605. if( SUCCEEDED( m_pPortTable[dwIndex].pPort->GetLatencyClock( &m_pPortTable[dwIndex].pLatencyClock )))
  1606. {
  1607. if( SUCCEEDED( m_pPortTable[dwIndex].pLatencyClock->GetTime( &rtTemp )))
  1608. {
  1609. if( rtTemp > rtLatency )
  1610. rtLatency = rtTemp;
  1611. }
  1612. }
  1613. }
  1614. }
  1615. }
  1616. LeaveCriticalSection(&m_PChannelInfoCrSec);
  1617. if( 0 == rtLatency )
  1618. {
  1619. rtLatency = GetLatency();
  1620. }
  1621. delete [] pafIndexUsed;
  1622. return rtLatency;
  1623. }
  1624. /* Called from either QueuePrimarySegment or QueueSecondarySegment,
  1625. this calculates the appropriate boundary time to start the segment
  1626. playback. Most of the logic takes care of the new DMUS_SEGF_ALIGN
  1627. capabilities.
  1628. */
  1629. void CPerformance::CalculateSegmentStartTime( CSegState* pSeg )
  1630. {
  1631. BOOL fNoValidStart = TRUE;
  1632. if (pSeg->m_dwPlaySegFlags & DMUS_SEGF_ALIGN)
  1633. {
  1634. // If the ALIGN flag is set, see if we can align with the requested resolution,
  1635. // but switch to the new segment at an earlier point, as defined by
  1636. // a "valid start" point in the new segment.
  1637. DMUS_VALID_START_PARAM ValidStart; // Used to read start parameter from segment.
  1638. MUSIC_TIME mtIntervalSize = 0; // Quantization value.
  1639. MUSIC_TIME mtTimeNow = (MUSIC_TIME)pSeg->m_rtGivenStart; // The earliest time this can start.
  1640. // Call resolve time to get the last quantized interval that precedes mtTimeNow.
  1641. MUSIC_TIME mtStartTime = ResolveTime( mtTimeNow, pSeg->m_dwPlaySegFlags, &mtIntervalSize );
  1642. // StartTime actually shows the next time after now, so subtract the interval time to get the previous position.
  1643. mtStartTime -= mtIntervalSize;
  1644. // If the segment was supposed to start after the very beginning, quantize it.
  1645. if (mtIntervalSize && pSeg->m_mtStartPoint)
  1646. {
  1647. pSeg->m_mtStartPoint = ((pSeg->m_mtStartPoint + (mtIntervalSize >> 1))
  1648. / mtIntervalSize) * mtIntervalSize;
  1649. // If this ends up being longer than the segment, do we need to drop back?
  1650. }
  1651. // Now, get the next start point after the point in the segment that
  1652. // corresponds with mtTimeNow, adjusted for the startpoint.
  1653. if (SUCCEEDED(pSeg->m_pSegment->GetParam( GUID_Valid_Start_Time,-1,0,
  1654. pSeg->m_mtStartPoint + mtTimeNow - mtStartTime,NULL,(void *) &ValidStart)))
  1655. {
  1656. // If the valid start point is within the range, we can cut in at the start point.
  1657. if ((mtTimeNow - mtStartTime + ValidStart.mtTime) < (mtIntervalSize + pSeg->m_mtStartPoint))
  1658. {
  1659. pSeg->m_mtResolvedStart = mtTimeNow + ValidStart.mtTime;
  1660. pSeg->m_mtStartPoint += mtTimeNow - mtStartTime + ValidStart.mtTime;
  1661. fNoValidStart = FALSE;
  1662. }
  1663. }
  1664. if (fNoValidStart)
  1665. {
  1666. // Couldn't find a valid start point. Was DMUS_SEGF_VALID_START_XXX set so we can override?
  1667. if (pSeg->m_dwPlaySegFlags &
  1668. (DMUS_SEGF_VALID_START_MEASURE | DMUS_SEGF_VALID_START_BEAT | DMUS_SEGF_VALID_START_GRID | DMUS_SEGF_VALID_START_TICK))
  1669. {
  1670. MUSIC_TIME mtOverrideTime;
  1671. // Depending on the flag, we need to get the appropriate interval resolution.
  1672. if (pSeg->m_dwPlaySegFlags & DMUS_SEGF_VALID_START_MEASURE)
  1673. {
  1674. mtOverrideTime = ResolveTime( mtTimeNow, DMUS_SEGF_MEASURE, 0 );
  1675. }
  1676. else if (pSeg->m_dwPlaySegFlags & DMUS_SEGF_VALID_START_BEAT)
  1677. {
  1678. mtOverrideTime = ResolveTime( mtTimeNow, DMUS_SEGF_BEAT, 0 );
  1679. }
  1680. else if (pSeg->m_dwPlaySegFlags & DMUS_SEGF_VALID_START_GRID)
  1681. {
  1682. mtOverrideTime = ResolveTime( mtTimeNow, DMUS_SEGF_GRID, 0 );
  1683. }
  1684. else
  1685. {
  1686. mtOverrideTime = mtTimeNow;
  1687. }
  1688. // If the valid start point is within the range, we can cut in at the start point.
  1689. if ((mtOverrideTime - mtTimeNow) < (mtIntervalSize + pSeg->m_mtStartPoint))
  1690. {
  1691. pSeg->m_mtResolvedStart = mtOverrideTime;
  1692. if ((mtOverrideTime - mtStartTime) >= mtIntervalSize)
  1693. {
  1694. mtOverrideTime -= mtIntervalSize;
  1695. }
  1696. /*Trace(0,"Startpoint %ld plus OverrideTime %ld - StartTime %ld = %ld\n",
  1697. pSeg->m_mtStartPoint, mtOverrideTime - mtSegmentTime, mtStartTime - mtSegmentTime,
  1698. pSeg->m_mtStartPoint + mtOverrideTime - mtStartTime);*/
  1699. pSeg->m_mtStartPoint += mtOverrideTime - mtStartTime;
  1700. fNoValidStart = FALSE;
  1701. }
  1702. }
  1703. }
  1704. }
  1705. if (fNoValidStart)
  1706. {
  1707. pSeg->m_mtResolvedStart = ResolveTime( (MUSIC_TIME)pSeg->m_rtGivenStart,
  1708. pSeg->m_dwPlaySegFlags, NULL );
  1709. }
  1710. else
  1711. {
  1712. // If we succeeded in finding a place to switch over, make sure it isn't deep inside
  1713. // a loop. This is specifically a problem when syncing to segment and switching inside
  1714. // or after a loop.
  1715. while (pSeg->m_dwRepeats && (pSeg->m_mtStartPoint >= pSeg->m_mtLoopEnd))
  1716. {
  1717. pSeg->m_dwRepeats--;
  1718. pSeg->m_mtStartPoint -= (pSeg->m_mtLoopEnd - pSeg->m_mtLoopStart);
  1719. }
  1720. // Since we were decrementing the repeats, we need to also decrement the repeats left.
  1721. pSeg->m_dwRepeatsLeft = pSeg->m_dwRepeats;
  1722. // Finally, if the startpoint is after the end of the segment, cut it back to the end of the
  1723. // segment. This will cause it to play for time 0 and, if this is a transition segment, whatever
  1724. // should play after will play immediately.
  1725. if (pSeg->m_mtStartPoint > pSeg->m_mtLength)
  1726. {
  1727. pSeg->m_mtStartPoint = pSeg->m_mtLength;
  1728. }
  1729. }
  1730. pSeg->m_mtOffset = pSeg->m_mtResolvedStart;
  1731. pSeg->m_mtLastPlayed = pSeg->m_mtResolvedStart;
  1732. }
  1733. // this function should only be called from within a SegmentCrSec
  1734. // critical section!
  1735. void CPerformance::QueuePrimarySegment( CSegState* pSeg )
  1736. {
  1737. CSegState* pTemp;
  1738. BOOL fInCrSec = TRUE;
  1739. BOOL fNotDone = TRUE;
  1740. EnterCriticalSection(&m_PipelineCrSec);
  1741. pSeg->m_dwPlayTrackFlags |= DMUS_TRACKF_DIRTY;
  1742. if( pSeg->m_dwPlaySegFlags & DMUS_SEGF_QUEUE )
  1743. {
  1744. MUSIC_TIME mtStart = 0;
  1745. pTemp = m_SegStateQueues[SQ_PRI_PLAY].GetTail();
  1746. if( pTemp )
  1747. {
  1748. mtStart = pTemp->GetEndTime( pTemp->m_mtResolvedStart );
  1749. }
  1750. else
  1751. {
  1752. pTemp = m_SegStateQueues[SQ_PRI_DONE].GetTail();
  1753. if( pTemp )
  1754. {
  1755. mtStart = pTemp->m_mtLastPlayed;
  1756. }
  1757. }
  1758. pSeg->m_dwPlaySegFlags &= ~DMUS_SEGF_QUEUE;
  1759. if( NULL == pTemp )
  1760. {
  1761. // if there's nothing in the queue, this means play it now
  1762. if( pSeg->m_dwPlaySegFlags & DMUS_SEGF_AFTERPREPARETIME )
  1763. {
  1764. // we want to queue this at the last transported time,
  1765. // so we don't need to do an invalidate
  1766. if( pSeg->m_dwPlaySegFlags & DMUS_SEGF_REFTIME )
  1767. {
  1768. REFERENCE_TIME rtTrans;
  1769. MusicToReferenceTime( m_mtTransported, &rtTrans );
  1770. if( pSeg->m_rtGivenStart < rtTrans )
  1771. {
  1772. pSeg->m_dwPlaySegFlags &= ~DMUS_SEGF_REFTIME;
  1773. pSeg->m_rtGivenStart = m_mtTransported;
  1774. }
  1775. }
  1776. else
  1777. {
  1778. if( pSeg->m_rtGivenStart < m_mtTransported )
  1779. {
  1780. pSeg->m_rtGivenStart = m_mtTransported;
  1781. }
  1782. }
  1783. }
  1784. else
  1785. {
  1786. // This will be changed to Queue time below
  1787. pSeg->m_rtGivenStart = 0;
  1788. }
  1789. }
  1790. else
  1791. {
  1792. REFERENCE_TIME rtQueue;
  1793. // otherwise, time stamp it with the time corresponding to
  1794. // the end time of all segments currently in the queue.
  1795. pSeg->m_mtResolvedStart = mtStart;
  1796. // make sure the resolved start time isn't before the latency
  1797. GetQueueTime(&rtQueue);
  1798. ReferenceToMusicTime( rtQueue, &mtStart );
  1799. if( pSeg->m_mtResolvedStart < mtStart )
  1800. {
  1801. pSeg->m_mtResolvedStart = 0; // below code will take care of this case
  1802. }
  1803. else
  1804. {
  1805. pSeg->m_dwPlaySegFlags &= ~DMUS_SEGF_REFTIME;
  1806. pSeg->m_mtOffset = pSeg->m_mtResolvedStart;
  1807. m_SegStateQueues[SQ_PRI_PLAY].Insert(pSeg);
  1808. TraceI(2, "dmperf: queueing primary seg/DMUS_SEGF_QUEUE. Prev time=%ld, this=%ld\n",
  1809. pTemp->m_mtResolvedStart, pSeg->m_mtResolvedStart);
  1810. fNotDone = FALSE;
  1811. PrepSegToPlay(pSeg, true);
  1812. }
  1813. }
  1814. }
  1815. if( fNotDone && (pSeg->m_rtGivenStart == 0) )
  1816. {
  1817. // if the given start time is 0, it means play now.
  1818. MUSIC_TIME mtStart;
  1819. REFERENCE_TIME rtStart;
  1820. GetQueueTime( &rtStart );
  1821. ReferenceToMusicTime( rtStart, &mtStart );
  1822. pSeg->m_dwPlaySegFlags &= ~DMUS_SEGF_REFTIME;
  1823. pSeg->m_rtGivenStart = mtStart;
  1824. // we definitely want to get rid of all segments following
  1825. // the currently playing segment
  1826. if( m_SegStateQueues[SQ_PRI_PLAY].GetHead() )
  1827. {
  1828. while( pTemp = m_SegStateQueues[SQ_PRI_PLAY].GetHead()->GetNext() )
  1829. {
  1830. m_SegStateQueues[SQ_PRI_PLAY].Remove(pTemp);
  1831. pTemp->AbortPlay(mtStart,FALSE);
  1832. m_ShutDownQueue.Insert(pTemp);
  1833. }
  1834. }
  1835. }
  1836. if( fNotDone && pSeg->m_dwPlaySegFlags & DMUS_SEGF_REFTIME )
  1837. {
  1838. // rtStartTime is in RefTime units.
  1839. // We can convert this to Music Time immediately if either there
  1840. // is no currently playing Primary Segment, or the conversion
  1841. // falls within the time that has already played. If the time
  1842. // falls within PREPARE_TIME, we need to get this Segment
  1843. // playing right away.
  1844. REFERENCE_TIME rtNow = m_rtQueuePosition;
  1845. MUSIC_TIME mtTime;
  1846. if( m_SegStateQueues[SQ_PRI_PLAY].IsEmpty() || ( pSeg->m_rtGivenStart <= rtNow ) )
  1847. {
  1848. ReferenceToMusicTime( pSeg->m_rtGivenStart, &mtTime );
  1849. pSeg->m_dwPlaySegFlags &= ~( DMUS_SEGF_REFTIME );
  1850. pSeg->m_rtGivenStart = mtTime;
  1851. // let the block of code below that handles music time
  1852. // deal with it from here on
  1853. }
  1854. else
  1855. {
  1856. // Otherwise, we must wait until rtStartTime
  1857. // has been performed in order to convert to music time, because
  1858. // we require the tempo map at that time to do the conversion.
  1859. // This will be handled by the Transport code.
  1860. m_SegStateQueues[SQ_PRI_WAIT].Insert(pSeg);
  1861. fNotDone = FALSE; // prevents the next block of code from operating on
  1862. // this Segment.
  1863. }
  1864. }
  1865. if( fNotDone ) // music time
  1866. {
  1867. // if we're in music time units, we can queue this segment in the
  1868. // main queue, in time order. If this segment's music time is less
  1869. // than the start time of other segments in the queue, all of those
  1870. // segments are removed and discarded. Also, segments that are in
  1871. // the wait queue as RefTime are discarded.
  1872. ASSERT( !(pSeg->m_dwPlaySegFlags & DMUS_SEGF_REFTIME )); // m_rtGivenStart must be in music time
  1873. CalculateSegmentStartTime( pSeg );
  1874. while( (pTemp = m_SegStateQueues[SQ_PRI_WAIT].RemoveHead()) )
  1875. {
  1876. pTemp->AbortPlay(pSeg->m_mtResolvedStart,FALSE);
  1877. m_ShutDownQueue.Insert(pTemp);
  1878. }
  1879. if( pTemp = m_SegStateQueues[SQ_PRI_PLAY].GetHead() )
  1880. {
  1881. if( pSeg->m_mtResolvedStart > pTemp->m_mtResolvedStart )
  1882. {
  1883. while( pTemp->GetNext() )
  1884. {
  1885. if( pTemp->GetNext()->m_mtResolvedStart >= pSeg->m_mtResolvedStart )
  1886. {
  1887. break;
  1888. }
  1889. pTemp = pTemp->GetNext();
  1890. }
  1891. pSeg->SetNext(pTemp->GetNext());
  1892. pTemp->SetNext(pSeg);
  1893. while( pTemp = pSeg->GetNext() )
  1894. {
  1895. // delete the remaining pSegs after this one
  1896. pSeg->SetNext(pTemp->GetNext());
  1897. pTemp->AbortPlay(pSeg->m_mtResolvedStart,FALSE);
  1898. m_ShutDownQueue.Insert(pTemp);
  1899. }
  1900. }
  1901. else
  1902. {
  1903. if( !pTemp->m_fStartedPlay )
  1904. {
  1905. // blow away the entire queue
  1906. while( m_SegStateQueues[SQ_PRI_PLAY].GetHead() )
  1907. {
  1908. pTemp = m_SegStateQueues[SQ_PRI_PLAY].RemoveHead();
  1909. pTemp->AbortPlay(pSeg->m_mtResolvedStart,FALSE);
  1910. m_ShutDownQueue.Insert(pTemp);
  1911. }
  1912. m_SegStateQueues[SQ_PRI_PLAY].AddHead(pSeg);
  1913. // give this a chance to start performing if it's near
  1914. // enough to time
  1915. if( fInCrSec )
  1916. {
  1917. LeaveCriticalSection(&m_PipelineCrSec);
  1918. fInCrSec = FALSE;
  1919. }
  1920. SyncTimeSig( pSeg );
  1921. ManageControllingTracks();
  1922. PerformSegStNode( SQ_PRI_PLAY,pSeg);
  1923. }
  1924. else
  1925. {
  1926. // else, place this segment after the current one
  1927. // and count on the routine below to take care of dequeing
  1928. // the current one, because in this case m_mtLastPlayed
  1929. // must be greater than m_mtResolvedStart.
  1930. if ( m_SegStateQueues[SQ_PRI_PLAY].GetHead()->m_mtLastPlayed <=
  1931. m_SegStateQueues[SQ_PRI_PLAY].GetHead()->m_mtResolvedStart )
  1932. {
  1933. TraceI(0,"Current Primary segment has not started playing.\n");
  1934. }
  1935. m_SegStateQueues[SQ_PRI_PLAY].AddHead(pSeg);
  1936. MUSIC_TIME mtTime = pSeg->m_mtResolvedStart;
  1937. while( pTemp = pSeg->GetNext() )
  1938. {
  1939. pTemp->AbortPlay( mtTime, TRUE && (pSeg->m_dwPlaySegFlags & DMUS_SEGF_NOINVALIDATE) );
  1940. // delete the remaining pSegs after this one
  1941. pSeg->SetNext(pTemp->GetNext());
  1942. m_ShutDownQueue.Insert(pTemp);
  1943. }
  1944. }
  1945. }
  1946. // m_pPriSegQueue could have become NULL from the PerformSegStNode call above.
  1947. if( m_SegStateQueues[SQ_PRI_PLAY].GetHead() && (pSeg != m_SegStateQueues[SQ_PRI_PLAY].GetHead()) )
  1948. {
  1949. CSegState *pCurrentSeg = m_SegStateQueues[SQ_PRI_PLAY].GetHead();
  1950. if( pCurrentSeg->m_fStartedPlay &&
  1951. ( pSeg->m_mtResolvedStart <= pCurrentSeg->m_mtLastPlayed ))
  1952. {
  1953. // If Playsegment is recursively called by the end of a previous segment in a song, don't abort.
  1954. if (!pCurrentSeg->m_fInPlay || !pCurrentSeg->m_fSongMode)
  1955. {
  1956. // the new segment wants to play on top of stuff that's
  1957. // already been transported by the current primary segment.
  1958. pCurrentSeg->AbortPlay(pSeg->m_mtResolvedStart-1,TRUE && (pSeg->m_dwPlaySegFlags & DMUS_SEGF_NOINVALIDATE));
  1959. m_SegStateQueues[SQ_PRI_DONE].Insert(m_SegStateQueues[SQ_PRI_PLAY].RemoveHead());
  1960. // make sure none of the last played times in the past list
  1961. // are past the resolved start
  1962. for( CSegState* pSegTemp = m_SegStateQueues[SQ_PRI_DONE].GetHead();
  1963. pSegTemp; pSegTemp = pSegTemp->GetNext() )
  1964. {
  1965. if( pSegTemp->m_mtLastPlayed > pSeg->m_mtResolvedStart )
  1966. {
  1967. pSegTemp->m_mtLastPlayed = pSeg->m_mtResolvedStart;
  1968. }
  1969. }
  1970. if( !( pSeg->m_dwPlaySegFlags & (DMUS_SEGF_NOINVALIDATE | DMUS_SEGF_INVALIDATE_PRI) ) )
  1971. {
  1972. // if we set the PREPARE flag it means we specifically
  1973. // don't want to invalidate
  1974. Invalidate( pSeg->m_mtResolvedStart, pSeg->m_dwPlaySegFlags );
  1975. }
  1976. else if ( (pSeg->m_dwPlaySegFlags & DMUS_SEGF_INVALIDATE_PRI) &&
  1977. !(pSeg->m_dwPlaySegFlags & DMUS_SEGF_NOINVALIDATE) )
  1978. {
  1979. pCurrentSeg->Flush(pSeg->m_mtResolvedStart);
  1980. }
  1981. ASSERT( m_SegStateQueues[SQ_PRI_PLAY].GetHead() == pSeg ); // this should be the case
  1982. if( fInCrSec )
  1983. {
  1984. LeaveCriticalSection(&m_PipelineCrSec);
  1985. fInCrSec = FALSE;
  1986. }
  1987. SyncTimeSig( pSeg );
  1988. ManageControllingTracks();
  1989. PerformSegStNode( SQ_PRI_PLAY,m_SegStateQueues[SQ_PRI_PLAY].GetHead() );
  1990. }
  1991. }
  1992. else
  1993. {
  1994. if( !( pSeg->m_dwPlaySegFlags & (DMUS_SEGF_NOINVALIDATE | DMUS_SEGF_INVALIDATE_PRI) ))
  1995. {
  1996. Invalidate( pSeg->m_mtResolvedStart, pSeg->m_dwPlaySegFlags );
  1997. }
  1998. else if ( (pSeg->m_dwPlaySegFlags & DMUS_SEGF_INVALIDATE_PRI) &&
  1999. !(pSeg->m_dwPlaySegFlags & DMUS_SEGF_NOINVALIDATE) )
  2000. {
  2001. pCurrentSeg->Flush(pSeg->m_mtResolvedStart);
  2002. }
  2003. }
  2004. }
  2005. }
  2006. else
  2007. {
  2008. m_SegStateQueues[SQ_PRI_PLAY].AddHead(pSeg);
  2009. // give this a chance to start performing if it's near
  2010. // enough to time
  2011. if( fInCrSec )
  2012. {
  2013. LeaveCriticalSection(&m_PipelineCrSec);
  2014. fInCrSec = FALSE;
  2015. }
  2016. //DWORD dwDebugTime = timeGetTime();
  2017. SyncTimeSig( pSeg );
  2018. //DWORD dwDebugTime2 = timeGetTime();
  2019. //Trace(0, "perf, debugtime SyncTimeSig %u\n", dwDebugTime2 - dwDebugTime);
  2020. ManageControllingTracks();
  2021. //dwDebugTime = timeGetTime();
  2022. //Trace(0, "perf, debugtime ManageControllingTracks %u\n", dwDebugTime - dwDebugTime2);
  2023. PerformSegStNode( SQ_PRI_PLAY,pSeg );
  2024. //dwDebugTime2 = timeGetTime();
  2025. //Trace(0, "perf, debugtime PerformSegStNode %u\n", dwDebugTime2 - dwDebugTime);
  2026. }
  2027. }
  2028. if( fInCrSec )
  2029. {
  2030. LeaveCriticalSection(&m_PipelineCrSec);
  2031. }
  2032. }
  2033. // this function should only be called from within a SegmentCrSec
  2034. // critical section!
  2035. void CPerformance::QueueSecondarySegment( CSegState* pSeg)
  2036. {
  2037. BOOL fInCrSec = FALSE;
  2038. BOOL fNotDone = TRUE;
  2039. if( pSeg->m_dwPlaySegFlags & DMUS_SEGF_CONTROL )
  2040. {
  2041. EnterCriticalSection(&m_PipelineCrSec);
  2042. fInCrSec = TRUE;
  2043. }
  2044. pSeg->m_dwPlaySegFlags &= ~DMUS_SEGF_QUEUE; // not legal for 2ndary segs.
  2045. if( pSeg->m_rtGivenStart == 0 )
  2046. {
  2047. MUSIC_TIME mtStart;
  2048. if( pSeg->m_dwPlaySegFlags & DMUS_SEGF_CONTROL )
  2049. {
  2050. REFERENCE_TIME rtStart;
  2051. GetQueueTime( &rtStart ); // need queue time because control segments cause invalidations
  2052. ReferenceToMusicTime( rtStart, &mtStart );
  2053. }
  2054. else
  2055. {
  2056. ReferenceToMusicTime( GetBestSegLatency(pSeg), &mtStart );
  2057. }
  2058. pSeg->m_dwPlaySegFlags &= ~DMUS_SEGF_REFTIME;
  2059. pSeg->m_rtGivenStart = mtStart;
  2060. }
  2061. if( pSeg->m_dwPlaySegFlags & DMUS_SEGF_REFTIME )
  2062. {
  2063. // rtStartTime is in RefTime units.
  2064. // We can convert this to Music Time immediately if either there
  2065. // is no currently playing Primary Segment, or the conversion
  2066. // falls within the time that has already played. If the time
  2067. // falls within PREPARE_TIME, we need to get this Segment
  2068. // playing right away.
  2069. REFERENCE_TIME rtNow;
  2070. if( pSeg->m_dwPlaySegFlags & DMUS_SEGF_CONTROL )
  2071. {
  2072. GetQueueTime( &rtNow ); // need queue time because control segments cause invalidations
  2073. }
  2074. else
  2075. {
  2076. rtNow = GetBestSegLatency(pSeg);
  2077. }
  2078. MUSIC_TIME mtTime;
  2079. if( pSeg->m_rtGivenStart <= rtNow )
  2080. {
  2081. ReferenceToMusicTime( rtNow, &mtTime );
  2082. pSeg->m_dwPlaySegFlags &= ~( DMUS_SEGF_REFTIME );
  2083. pSeg->m_rtGivenStart = mtTime;
  2084. // let the block of code below that handles music time
  2085. // deal with it from here on
  2086. }
  2087. else if( m_SegStateQueues[SQ_PRI_PLAY].IsEmpty() )
  2088. {
  2089. ReferenceToMusicTime( pSeg->m_rtGivenStart, &mtTime );
  2090. pSeg->m_dwPlaySegFlags &= ~( DMUS_SEGF_REFTIME );
  2091. pSeg->m_rtGivenStart = mtTime;
  2092. }
  2093. else
  2094. {
  2095. // Otherwise, we must wait until rtStartTime
  2096. // has been performed in order to convert to music time, because
  2097. // we require the tempo map at that time to do the conversion.
  2098. // This will be handled by the Transport code.
  2099. m_SegStateQueues[SQ_SEC_WAIT].Insert(pSeg);
  2100. fNotDone = FALSE; // prevents the next block of code from operating on
  2101. // this Segment.
  2102. }
  2103. }
  2104. if( fNotDone ) // music time
  2105. {
  2106. // if we're in music time units, we can queue this segment in the
  2107. // main queue, in time order. If this segment's music time is less
  2108. // than the start time of other segments in the queue, all of those
  2109. // segments are removed and discarded.
  2110. ASSERT( !(pSeg->m_dwPlaySegFlags & DMUS_SEGF_REFTIME )); // m_m_rtGivenStart must be in music time
  2111. CalculateSegmentStartTime( pSeg );
  2112. TraceI(2,"Queuing 2ndary seg time %ld\n",pSeg->m_mtResolvedStart);
  2113. if( pSeg->m_dwPlaySegFlags & DMUS_SEGF_CONTROL)
  2114. {
  2115. m_SegStateQueues[SQ_CON_PLAY].Insert( pSeg );
  2116. // If this is a control segment, we need to do an invalidate.
  2117. if(!(pSeg->m_dwPlaySegFlags & DMUS_SEGF_NOINVALIDATE) )
  2118. {
  2119. ManageControllingTracks();
  2120. Invalidate( pSeg->m_mtResolvedStart, 0 );
  2121. }
  2122. }
  2123. else
  2124. {
  2125. m_SegStateQueues[SQ_SEC_PLAY].Insert( pSeg );
  2126. }
  2127. // give this a chance to start performing if it's near
  2128. // enough to time
  2129. if( fInCrSec )
  2130. {
  2131. LeaveCriticalSection(&m_PipelineCrSec);
  2132. fInCrSec = FALSE;
  2133. }
  2134. // play the secondary segments
  2135. CSegState *pNode = m_SegStateQueues[SQ_SEC_PLAY].GetHead();
  2136. CSegState *pNext;
  2137. for(; pNode != NULL; pNode = pNext)
  2138. {
  2139. pNext = pNode->GetNext();
  2140. PerformSegStNode( SQ_SEC_PLAY,pNode );
  2141. }
  2142. // play the controlling segments
  2143. pNode = m_SegStateQueues[SQ_CON_PLAY].GetHead();
  2144. for(; pNode != NULL; pNode = pNext)
  2145. {
  2146. pNext = pNode->GetNext();
  2147. PerformSegStNode( SQ_CON_PLAY,pNode );
  2148. }
  2149. }
  2150. if( fInCrSec )
  2151. {
  2152. LeaveCriticalSection(&m_PipelineCrSec);
  2153. }
  2154. }
  2155. /* If a segment is controlling, this establishes which tracks in the currently playing
  2156. primary segment are disabled.
  2157. We store temporary information in each playing track's m_dwInternalFlags, which is not used
  2158. otherwise in segmentstates.
  2159. Four scenarios, each for play and notify:
  2160. 1) An officially enabled track is currently enabled and gets disabled.
  2161. 2) An officially enabled track is currently disabled and continues to be disabled.
  2162. 3) An officially enabled track is currently disabled and gets enabled.
  2163. 4) An officially disabled track is left disabled. If none of the CONTROL_ flags are set and the track is disabled,
  2164. set the _WAS_DISABLED flag, which also indicates that this should be left alone.
  2165. This should get called every time a primary or secondary segment starts or stop, so it
  2166. can recalculate the behavior of all tracks in the primary segment.
  2167. */
  2168. void CPerformance::ManageControllingTracks()
  2169. {
  2170. EnterCriticalSection(&m_SegmentCrSec);
  2171. CSegState* pSegNode;
  2172. // First, prepare all tracks in the primary segment, putting them back to normal.
  2173. // so they are ready to be reset by the controlling tracks.
  2174. // To do this, check for WAS_ENABLED or WAS_DISABLED and set the appropriate flags in m_dwFlags.
  2175. // Else, if these weren't set, then it's time to set them, since this is the first pass through this segment.
  2176. for( pSegNode = m_SegStateQueues[SQ_PRI_PLAY].GetHead(); pSegNode; pSegNode = pSegNode->GetNext() )
  2177. {
  2178. EnterCriticalSection(&pSegNode->m_CriticalSection);
  2179. CTrack *pTrack = pSegNode->m_TrackList.GetHead();
  2180. for (;pTrack;pTrack = pTrack->GetNext())
  2181. {
  2182. if (pTrack->m_dwInternalFlags) // This has been touched before.
  2183. {
  2184. // First transfer and reset the is disabled flags.
  2185. if (pTrack->m_dwInternalFlags & CONTROL_PLAY_IS_DISABLED)
  2186. {
  2187. pTrack->m_dwInternalFlags |= CONTROL_PLAY_WAS_DISABLED;
  2188. }
  2189. pTrack->m_dwInternalFlags &= ~(CONTROL_PLAY_IS_DISABLED | CONTROL_NTFY_IS_DISABLED);
  2190. // Then, set the play flags based on the original state.
  2191. if (pTrack->m_dwInternalFlags & CONTROL_PLAY_DEFAULT_ENABLED)
  2192. {
  2193. pTrack->m_dwFlags |= DMUS_TRACKCONFIG_PLAY_ENABLED;
  2194. }
  2195. if (pTrack->m_dwInternalFlags & CONTROL_NTFY_DEFAULT_ENABLED)
  2196. {
  2197. pTrack->m_dwFlags |= DMUS_TRACKCONFIG_NOTIFICATION_ENABLED;
  2198. }
  2199. }
  2200. else
  2201. {
  2202. // Since this has never been touched before, set the flags so we can know what to return to.
  2203. if (pTrack->m_dwFlags & DMUS_TRACKCONFIG_PLAY_ENABLED)
  2204. {
  2205. pTrack->m_dwInternalFlags = CONTROL_PLAY_DEFAULT_ENABLED;
  2206. }
  2207. else
  2208. {
  2209. pTrack->m_dwInternalFlags = CONTROL_PLAY_DEFAULT_DISABLED;
  2210. }
  2211. if (pTrack->m_dwFlags & DMUS_TRACKCONFIG_NOTIFICATION_ENABLED)
  2212. {
  2213. pTrack->m_dwInternalFlags |= CONTROL_NTFY_DEFAULT_ENABLED;
  2214. }
  2215. else
  2216. {
  2217. pTrack->m_dwInternalFlags |= CONTROL_NTFY_DEFAULT_DISABLED;
  2218. }
  2219. }
  2220. }
  2221. LeaveCriticalSection(&pSegNode->m_CriticalSection);
  2222. }
  2223. CSegState* pControlNode;
  2224. // Now, go through all the controlling segments and, for each controlling track that matches
  2225. // a primary segment track, clear the enable flags on the segment track.
  2226. for( pControlNode = m_SegStateQueues[SQ_CON_PLAY].GetHead(); pControlNode; pControlNode = pControlNode->GetNext() )
  2227. {
  2228. EnterCriticalSection(&pControlNode->m_CriticalSection);
  2229. CTrack *pTrack = pControlNode->m_TrackList.GetHead();
  2230. for (;pTrack;pTrack = pTrack->GetNext())
  2231. {
  2232. // If the track has never been overridden, the internal flags for IS_DISABLED should be clear.
  2233. // If the track is currently overridden, the internal flags should be CONTROL_PLAY_IS_DISABLED and/or
  2234. // CONTROL_NTFY_IS_DISABLED
  2235. if (pTrack->m_dwFlags & (DMUS_TRACKCONFIG_CONTROL_PLAY | DMUS_TRACKCONFIG_CONTROL_NOTIFICATION)) // This overrides playback and/or notification.
  2236. {
  2237. for( pSegNode = m_SegStateQueues[SQ_PRI_PLAY].GetHead(); pSegNode; pSegNode = pSegNode->GetNext() )
  2238. {
  2239. EnterCriticalSection(&pSegNode->m_CriticalSection);
  2240. CTrack *pPrimaryTrack = pSegNode->m_TrackList.GetHead();
  2241. for (;pPrimaryTrack;pPrimaryTrack = pPrimaryTrack->GetNext())
  2242. {
  2243. // A track matches if it has the same class id and overlapping group bits.
  2244. if ((pPrimaryTrack->m_guidClassID == pTrack->m_guidClassID) &&
  2245. (pPrimaryTrack->m_dwGroupBits & pTrack->m_dwGroupBits))
  2246. {
  2247. if ((pTrack->m_dwFlags & DMUS_TRACKCONFIG_CONTROL_PLAY) &&
  2248. (pPrimaryTrack->m_dwFlags & DMUS_TRACKCONFIG_PLAY_ENABLED))
  2249. {
  2250. pPrimaryTrack->m_dwFlags &= ~DMUS_TRACKCONFIG_PLAY_ENABLED;
  2251. pPrimaryTrack->m_dwInternalFlags |= CONTROL_PLAY_IS_DISABLED; // Mark so we can turn on later.
  2252. }
  2253. if ((pTrack->m_dwFlags & DMUS_TRACKCONFIG_CONTROL_NOTIFICATION) &&
  2254. (pPrimaryTrack->m_dwFlags & DMUS_TRACKCONFIG_NOTIFICATION_ENABLED))
  2255. {
  2256. pPrimaryTrack->m_dwFlags &= ~DMUS_TRACKCONFIG_NOTIFICATION_ENABLED;
  2257. pPrimaryTrack->m_dwInternalFlags |= CONTROL_NTFY_IS_DISABLED; // Mark so we can turn on later.
  2258. }
  2259. }
  2260. }
  2261. LeaveCriticalSection(&pSegNode->m_CriticalSection);
  2262. }
  2263. }
  2264. }
  2265. LeaveCriticalSection(&pControlNode->m_CriticalSection);
  2266. }
  2267. // Now, go back to the primary segment and find all tracks that have been reenabled
  2268. // and tag them so they will generate refresh data on the next play (by seeking, as if they
  2269. // were starting or looping playback.) We only do this for play, not notify, because no
  2270. // notifications have state.
  2271. for( pSegNode = m_SegStateQueues[SQ_PRI_PLAY].GetHead(); pSegNode; pSegNode = pSegNode->GetNext() )
  2272. {
  2273. EnterCriticalSection(&pSegNode->m_CriticalSection);
  2274. CTrack *pTrack = pSegNode->m_TrackList.GetHead();
  2275. for (;pTrack;pTrack = pTrack->GetNext())
  2276. {
  2277. if ((pTrack->m_dwInternalFlags & CONTROL_PLAY_DEFAULT_ENABLED) &&
  2278. (pTrack->m_dwInternalFlags & CONTROL_PLAY_WAS_DISABLED) &&
  2279. !(pTrack->m_dwInternalFlags & CONTROL_PLAY_IS_DISABLED))
  2280. {
  2281. pTrack->m_dwInternalFlags |= CONTROL_PLAY_REFRESH; // Mark so we can turn on later.
  2282. }
  2283. }
  2284. LeaveCriticalSection(&pSegNode->m_CriticalSection);
  2285. }
  2286. LeaveCriticalSection(&m_SegmentCrSec);
  2287. }
  2288. void CPerformance::GetTimeSig( MUSIC_TIME mtTime, DMUS_TIMESIG_PMSG* pTimeSig )
  2289. {
  2290. EnterCriticalSection(&m_PipelineCrSec);
  2291. PRIV_PMSG* pEvent = m_TimeSigQueue.GetHead();
  2292. for (;pEvent;pEvent = pEvent->pNext)
  2293. {
  2294. // If this is the last time sig, return it. Or, if the next time sig is after mtTime.
  2295. if (!pEvent->pNext || ( pEvent->pNext->mtTime > mtTime ))
  2296. {
  2297. DMUS_TIMESIG_PMSG* pNewTimeSig = (DMUS_TIMESIG_PMSG*)PRIV_TO_DMUS(pEvent);
  2298. memcpy( pTimeSig, pNewTimeSig, sizeof(DMUS_TIMESIG_PMSG) );
  2299. LeaveCriticalSection(&m_PipelineCrSec);
  2300. return;
  2301. }
  2302. }
  2303. // This should only happen if there is no timesig at all. Should only happen before any segments play.
  2304. memset( pTimeSig, 0, sizeof(DMUS_TIMESIG_PMSG ) );
  2305. pTimeSig->wGridsPerBeat = 4;
  2306. pTimeSig->bBeatsPerMeasure = 4;
  2307. pTimeSig->bBeat = 4;
  2308. LeaveCriticalSection(&m_PipelineCrSec);
  2309. }
  2310. void CPerformance::SyncTimeSig( CSegState *pSegState )
  2311. /* If a primary segment is played that does not have a time signature track,
  2312. this forces the current time signature to line up with the start of the
  2313. primary segment.
  2314. */
  2315. {
  2316. // First, test to see if the segment has a time signature.
  2317. // If it doesn't then we need to do this.
  2318. DMUS_TIMESIGNATURE TimeSig;
  2319. if (FAILED(pSegState->GetParam(this,GUID_TimeSignature,-1,0,0,NULL,(void *)&TimeSig)))
  2320. {
  2321. MUSIC_TIME mtTime = pSegState->m_mtResolvedStart;
  2322. EnterCriticalSection(&m_PipelineCrSec);
  2323. PRIV_PMSG* pEvent = m_TimeSigQueue.GetHead();
  2324. // Scan through the time signatures until the most recent one is found.
  2325. for (;pEvent;pEvent = pEvent->pNext)
  2326. {
  2327. // If this is the last time sig, return it. Or, if the next time sig is after mtTime.
  2328. if (!pEvent->pNext || ( pEvent->pNext->mtTime > mtTime ))
  2329. {
  2330. pEvent->mtTime = mtTime;
  2331. MusicToReferenceTime(mtTime,&pEvent->rtTime);
  2332. break;
  2333. }
  2334. }
  2335. // Should never fall through to here without finding a time signature because Init() creates a timesig.
  2336. LeaveCriticalSection(&m_PipelineCrSec);
  2337. }
  2338. }
  2339. // Convert mtTime into the resolved time according to the resolution in
  2340. // dwResolution.
  2341. // This should only be called from within a segment critical section.
  2342. MUSIC_TIME CPerformance::ResolveTime( MUSIC_TIME mtTime, DWORD dwResolution, MUSIC_TIME *pmtIntervalSize )
  2343. {
  2344. if (pmtIntervalSize)
  2345. {
  2346. *pmtIntervalSize = 0;
  2347. }
  2348. if (dwResolution & DMUS_SEGF_MARKER)
  2349. {
  2350. DMUS_PLAY_MARKER_PARAM Marker;
  2351. MUSIC_TIME mtNext;
  2352. // First, get the time of the marker preceding this one.
  2353. if (SUCCEEDED (GetParam(GUID_Play_Marker,-1,0,mtTime,&mtNext,(void *) &Marker)))
  2354. {
  2355. BOOL fIsMarker = FALSE;
  2356. MUSIC_TIME mtInitialTime = mtTime;
  2357. MUSIC_TIME mtFirst = mtTime + Marker.mtTime; // This is the time of the preceding marker.
  2358. MUSIC_TIME mtSecond = mtTime + mtNext; // This might be the time of the next marker.
  2359. // Then, scan forward until a marker is found after or equal to this time.
  2360. // If a loop point or end of segment is encountered, the value in Marker.mtTime will
  2361. // continue to be negative. Once we hit the actual marker, it will become 0, since
  2362. // we are asking for the marker at that specific time.
  2363. while (mtNext)
  2364. {
  2365. mtTime += mtNext;
  2366. if (SUCCEEDED(GetParam(GUID_Play_Marker,-1,0,mtTime,&mtNext,(void *) &Marker)))
  2367. {
  2368. // If the marker time is 0, this means we are sitting right on the marker,
  2369. // so we are done.
  2370. if (fIsMarker = (Marker.mtTime == 0))
  2371. {
  2372. mtSecond = mtTime;
  2373. break;
  2374. }
  2375. // Otherwise, this was a loop boundary or segment end, so we should continue scanning forward.
  2376. }
  2377. else
  2378. {
  2379. // GetParam failed, must be nothing more to search.
  2380. break;
  2381. }
  2382. }
  2383. // If the caller wants the interval size, then we know they are interested in
  2384. // aligning to a previous marker as well as a future one. In that case,
  2385. // if we didn't find a marker in the future, it's okay because it will
  2386. // use the previous marker (mtFirst) anyway.
  2387. // For all other cases, we only return if the upcoming marker is legal.
  2388. // Otherwise, we drop through and try other resolutions.
  2389. if (pmtIntervalSize || fIsMarker)
  2390. {
  2391. if (pmtIntervalSize)
  2392. {
  2393. *pmtIntervalSize = mtSecond - mtFirst;
  2394. }
  2395. return mtSecond;
  2396. }
  2397. mtTime = mtInitialTime;
  2398. }
  2399. // If marker fails, we can drop down to the other types...
  2400. }
  2401. if( dwResolution & DMUS_SEGF_SEGMENTEND )
  2402. {
  2403. // In this mode, we don't actually get the time signature. Instead, we
  2404. // find out the time of the next segment start after the requested time.
  2405. CSegState *pSegNode = GetPrimarySegmentAtTime( mtTime );
  2406. if( pSegNode )
  2407. {
  2408. // First, calculate the end time of the segment.
  2409. // Include any starting offset so we see the full span of the segment.
  2410. mtTime = pSegNode->GetEndTime( pSegNode->m_mtStartPoint );
  2411. if (pmtIntervalSize)
  2412. {
  2413. // Interval would be the length of the primary segment!
  2414. *pmtIntervalSize = mtTime;
  2415. }
  2416. // Return the end of the segment.
  2417. LONGLONG llEnd = mtTime + (LONGLONG)(pSegNode->m_mtResolvedStart - pSegNode->m_mtStartPoint);
  2418. if(llEnd > 0x7fffffff) llEnd = 0x7fffffff;
  2419. mtTime = (MUSIC_TIME) llEnd;
  2420. return mtTime;
  2421. }
  2422. // If there was no segment, we should fail and try the other flags.
  2423. }
  2424. long lQuantize;
  2425. MUSIC_TIME mtNewTime;
  2426. MUSIC_TIME mtStartOfTimeSig = 0;
  2427. DMUS_TIMESIGNATURE timeSig;
  2428. if (!(dwResolution & DMUS_SEGF_TIMESIG_ALWAYS))
  2429. {
  2430. if (!GetPrimarySegmentAtTime(mtTime))
  2431. {
  2432. return mtTime;
  2433. }
  2434. }
  2435. GetParam(GUID_TimeSignature,-1,0,mtTime,NULL,(void *) &timeSig);
  2436. mtStartOfTimeSig = timeSig.mtTime + mtTime;
  2437. mtNewTime = mtTime - mtStartOfTimeSig;
  2438. if (dwResolution & DMUS_SEGF_MEASURE)
  2439. {
  2440. lQuantize = ( DMUS_PPQ * 4 * timeSig.bBeatsPerMeasure ) / timeSig.bBeat;
  2441. }
  2442. else if (dwResolution & DMUS_SEGF_BEAT)
  2443. {
  2444. lQuantize = ( DMUS_PPQ * 4 ) / timeSig.bBeat;
  2445. }
  2446. else if (dwResolution & DMUS_SEGF_GRID)
  2447. {
  2448. lQuantize = ( ( DMUS_PPQ * 4 ) / timeSig.bBeat ) / timeSig.wGridsPerBeat;
  2449. }
  2450. else
  2451. {
  2452. lQuantize = 1;
  2453. }
  2454. if (lQuantize == 0) // Avoid divide by 0 error.
  2455. {
  2456. lQuantize = 1;
  2457. }
  2458. if (pmtIntervalSize)
  2459. {
  2460. *pmtIntervalSize = lQuantize;
  2461. }
  2462. if( mtNewTime ) // if it's 0 it stays 0
  2463. {
  2464. // round up to next boundary
  2465. mtNewTime = ((mtNewTime-1) / lQuantize ) * lQuantize;
  2466. mtNewTime += lQuantize;
  2467. }
  2468. return (mtNewTime + mtStartOfTimeSig);
  2469. }
  2470. // returns:
  2471. // true if the note should be invalidated (any other return code will invalidate)
  2472. // false if the note should not be invalidated
  2473. inline bool GetInvalidationStatus(DMUS_PMSG* pPMsg)
  2474. {
  2475. bool fResult = true; // default: invalidate the note
  2476. if( pPMsg->dwType == DMUS_PMSGT_NOTE )
  2477. {
  2478. DMUS_NOTE_PMSG* pNote = (DMUS_NOTE_PMSG*)pPMsg;
  2479. if (pNote->bFlags & DMUS_NOTEF_NOINVALIDATE)
  2480. {
  2481. fResult = false;
  2482. }
  2483. }
  2484. else if( pPMsg->dwType == DMUS_PMSGT_WAVE )
  2485. {
  2486. DMUS_WAVE_PMSG* pWave = (DMUS_WAVE_PMSG*)pPMsg;
  2487. if(pWave->bFlags & DMUS_WAVEF_NOINVALIDATE)
  2488. {
  2489. fResult = false;
  2490. }
  2491. }
  2492. else if( pPMsg->dwType == DMUS_PMSGT_NOTIFICATION )
  2493. {
  2494. // Don't invalidate segment abort messages
  2495. DMUS_NOTIFICATION_PMSG* pNotification = (DMUS_NOTIFICATION_PMSG*) pPMsg;
  2496. if ((pNotification->guidNotificationType == GUID_NOTIFICATION_SEGMENT) &&
  2497. (pNotification->dwNotificationOption == DMUS_NOTIFICATION_SEGABORT))
  2498. {
  2499. fResult = false;
  2500. }
  2501. }
  2502. return fResult;
  2503. }
  2504. static inline long ComputeCurveTimeSlice(DMUS_CURVE_PMSG* pCurve)
  2505. {
  2506. long lTimeIncrement;
  2507. DWORD dwTotalDistance;
  2508. DWORD dwResolution;
  2509. if ((pCurve->bType == DMUS_CURVET_PBCURVE) ||
  2510. (pCurve->bType == DMUS_CURVET_RPNCURVE) ||
  2511. (pCurve->bType == DMUS_CURVET_NRPNCURVE))
  2512. {
  2513. dwResolution = 100;
  2514. }
  2515. else
  2516. {
  2517. dwResolution = 3;
  2518. }
  2519. if (pCurve->nEndValue > pCurve->nStartValue)
  2520. dwTotalDistance = pCurve->nEndValue - pCurve->nStartValue;
  2521. else
  2522. dwTotalDistance = pCurve->nStartValue - pCurve->nEndValue;
  2523. if (dwTotalDistance == 0) dwTotalDistance = 1;
  2524. lTimeIncrement = (pCurve->mtDuration * dwResolution) / dwTotalDistance;
  2525. // Force to no smaller than 192nd note (10ms at 120 bpm.)
  2526. if( lTimeIncrement < (DMUS_PPQ/48) ) lTimeIncrement = DMUS_PPQ/48;
  2527. return lTimeIncrement;
  2528. }
  2529. static DWORD ComputeCurve( DMUS_CURVE_PMSG* pCurve )
  2530. {
  2531. DWORD dwRet;
  2532. short *panTable;
  2533. MUSIC_TIME mtCurrent;
  2534. long lIndex;
  2535. switch( pCurve->bCurveShape )
  2536. {
  2537. case DMUS_CURVES_INSTANT:
  2538. default:
  2539. if( pCurve->dwFlags & DMUS_PMSGF_TOOL_FLUSH )
  2540. {
  2541. pCurve->rtTime = 0;
  2542. return (DWORD)pCurve->nResetValue;
  2543. }
  2544. if( ( pCurve->bFlags & DMUS_CURVE_RESET ) && ( pCurve->mtResetDuration > 0 ) )
  2545. {
  2546. pCurve->mtTime = pCurve->mtResetDuration + pCurve->mtOriginalStart;
  2547. pCurve->mtDuration = 0;
  2548. pCurve->dwFlags &= ~DMUS_PMSGF_REFTIME;
  2549. }
  2550. else
  2551. {
  2552. pCurve->rtTime = 0; // setting this to 0 will free the event upon return
  2553. }
  2554. return (DWORD)pCurve->nEndValue;
  2555. break;
  2556. case DMUS_CURVES_LINEAR:
  2557. panTable = &ganCT_Linear[ 0 ];
  2558. break;
  2559. case DMUS_CURVES_EXP:
  2560. panTable = &ganCT_Exp[ 0 ];
  2561. break;
  2562. case DMUS_CURVES_LOG:
  2563. panTable = &ganCT_Log[ 0 ];
  2564. break;
  2565. case DMUS_CURVES_SINE:
  2566. panTable = &ganCT_Sine[ 0 ];
  2567. break;
  2568. }
  2569. // compute index into table
  2570. // there are CT_MAX + 1 elements in the table.
  2571. mtCurrent = pCurve->mtTime - pCurve->mtOriginalStart;
  2572. // if we're flushing this event, send the reset value
  2573. if( pCurve->dwFlags & DMUS_PMSGF_TOOL_FLUSH )
  2574. {
  2575. // it will only get here if pCurve->bFlags & 1, because that is checked in
  2576. // the :Flush() routine.
  2577. pCurve->rtTime = 0;
  2578. return pCurve->nResetValue;
  2579. }
  2580. // this should now never happen, as a result of fixing 33987: Transition on a beat boundary invalidates CC's right away (doesn't wait for the beat)
  2581. if( (pCurve->bFlags & DMUS_CURVE_RESET) &&
  2582. (pCurve->mtResetDuration < 0 ) && // this can happen from flushing
  2583. (pCurve->mtTime >= pCurve->mtOriginalStart + pCurve->mtDuration + pCurve->mtResetDuration ))
  2584. {
  2585. pCurve->rtTime = 0;
  2586. return pCurve->nResetValue;
  2587. }
  2588. else if( (pCurve->mtDuration == 0) ||
  2589. (pCurve->mtTime - pCurve->mtOriginalStart >= pCurve->mtDuration ))
  2590. {
  2591. // if we're supposed to send the return value (m_bFlags & 1) then
  2592. // set it up to do so. Otherwise, free the event.
  2593. if( pCurve->bFlags & DMUS_CURVE_RESET )
  2594. {
  2595. pCurve->mtTime = pCurve->mtDuration + pCurve->mtResetDuration +
  2596. pCurve->mtOriginalStart;
  2597. pCurve->dwFlags &= ~DMUS_PMSGF_REFTIME;
  2598. }
  2599. else
  2600. {
  2601. pCurve->rtTime = 0; // time to free the event, we're done
  2602. }
  2603. dwRet = pCurve->nEndValue;
  2604. }
  2605. else
  2606. {
  2607. // Calculate how far into the table we should be.
  2608. lIndex = (mtCurrent * (CT_MAX + 1)) / pCurve->mtDuration;
  2609. // find an amount of time to add to the curve event such that there is at
  2610. // least a change by CT_FACTOR. This will be used as the time stamp
  2611. // for the next iteration of the curve.
  2612. // clamp lIndex
  2613. if( lIndex < 0 )
  2614. {
  2615. lIndex = 0;
  2616. }
  2617. if( lIndex >= CT_MAX )
  2618. {
  2619. lIndex = CT_MAX;
  2620. dwRet = pCurve->nEndValue;
  2621. }
  2622. else
  2623. {
  2624. // Okay, in the curve, so calculate the return value.
  2625. dwRet = ((panTable[lIndex] * (pCurve->nEndValue - pCurve->nStartValue)) /
  2626. CT_DIVFACTOR) + pCurve->nStartValue;
  2627. }
  2628. // this should now never happen, as a result of fixing 33987
  2629. if( (pCurve->bFlags & DMUS_CURVE_RESET) && (pCurve->mtResetDuration < 0) )
  2630. {
  2631. // this can happen as a result of flushing. We want to make sure the next
  2632. // time is the reset flush time.
  2633. pCurve->mtTime = pCurve->mtDuration + pCurve->mtResetDuration +
  2634. pCurve->mtOriginalStart;
  2635. }
  2636. else
  2637. {
  2638. // Within curve, so increment time.
  2639. if (!pCurve->wMeasure) // oops --- better compute this.
  2640. {
  2641. TraceI(2, "Warning: Computing curve time slice...\n");
  2642. pCurve->wMeasure = (WORD) ComputeCurveTimeSlice(pCurve); // Use this to store the time slice interval.
  2643. }
  2644. pCurve->mtTime += pCurve->wMeasure; // We are storing the time increment here.
  2645. }
  2646. if( pCurve->mtTime > pCurve->mtDuration + pCurve->mtOriginalStart )
  2647. {
  2648. pCurve->mtTime = pCurve->mtDuration + pCurve->mtOriginalStart;
  2649. }
  2650. pCurve->dwFlags &= ~DMUS_PMSGF_REFTIME;
  2651. }
  2652. return dwRet;
  2653. }
  2654. static int RecomputeCurveEnd( DMUS_CURVE_PMSG* pCurve, MUSIC_TIME mtCurrent )
  2655. {
  2656. int nRet = 0;
  2657. short *panTable;
  2658. switch( pCurve->bCurveShape )
  2659. {
  2660. case DMUS_CURVES_INSTANT:
  2661. default:
  2662. return pCurve->nEndValue;
  2663. break;
  2664. case DMUS_CURVES_LINEAR:
  2665. panTable = &ganCT_Linear[ 0 ];
  2666. break;
  2667. case DMUS_CURVES_EXP:
  2668. panTable = &ganCT_Exp[ 0 ];
  2669. break;
  2670. case DMUS_CURVES_LOG:
  2671. panTable = &ganCT_Log[ 0 ];
  2672. break;
  2673. case DMUS_CURVES_SINE:
  2674. panTable = &ganCT_Sine[ 0 ];
  2675. break;
  2676. }
  2677. if( (pCurve->mtDuration == 0) || (mtCurrent >= pCurve->mtDuration ))
  2678. {
  2679. return pCurve->nEndValue;
  2680. }
  2681. else
  2682. {
  2683. // Calculate how far into the table we should be.
  2684. long lIndex = (mtCurrent * (CT_MAX + 1)) / pCurve->mtDuration;
  2685. // find an amount of time to add to the curve event such that there is at
  2686. // least a change by CT_FACTOR. This will be used as the time stamp
  2687. // for the next iteration of the curve.
  2688. // clamp lIndex
  2689. if( lIndex < 0 )
  2690. {
  2691. lIndex = 0;
  2692. }
  2693. if( lIndex >= CT_MAX )
  2694. {
  2695. lIndex = CT_MAX;
  2696. nRet = pCurve->nEndValue;
  2697. }
  2698. else
  2699. {
  2700. // Okay, in the curve, so calculate the return value.
  2701. nRet = ((panTable[lIndex] * (pCurve->nEndValue - pCurve->nStartValue)) /
  2702. CT_DIVFACTOR) + pCurve->nStartValue;
  2703. }
  2704. }
  2705. return nRet;
  2706. }
  2707. void CPerformance::FlushEventQueue( DWORD dwId,
  2708. CPMsgQueue *pQueue, // Queue to flush events from.
  2709. REFERENCE_TIME rtFlush, // Time that flush occurs. This may be resolved to a timing resolution.
  2710. REFERENCE_TIME rtFlushUnresolved, // Queue time at time flush was requested. This is not resolved to the timing resolution.
  2711. // Instead, it is the actual time at which that the flush was requested. This is used only by curves.
  2712. BOOL fLeaveNotesOn) // If notes or waves are currently on, do not cut short their durations.
  2713. {
  2714. PRIV_PMSG* pEvent;
  2715. PRIV_PMSG* pNext;
  2716. HRESULT hr = S_OK;
  2717. REFERENCE_TIME rtTemp;
  2718. GetQueueTime(&rtTemp);
  2719. pNext = NULL;
  2720. for(pEvent = pQueue->GetHead(); pEvent; pEvent = pNext )
  2721. {
  2722. pNext = pEvent->pNext;
  2723. // Clear the remove bit. This will be set for each event that should be removed from the queue.
  2724. pEvent->dwPrivFlags &= ~PRIV_FLAG_REMOVE;
  2725. // Also clear the requeue bit, which will be set for each event that needs to be requeued.
  2726. pEvent->dwPrivFlags &= ~PRIV_FLAG_REQUEUE;
  2727. if( ( 0 == dwId ) || ( pEvent->dwVirtualTrackID == dwId ) )
  2728. {
  2729. // First, create the correct mtTime and rtTime for invalidation.
  2730. REFERENCE_TIME rtTime = pEvent->rtTime;
  2731. if( pEvent->dwType == DMUS_PMSGT_NOTE )
  2732. {
  2733. DMUS_NOTE_PMSG* pNote = (DMUS_NOTE_PMSG*)PRIV_TO_DMUS(pEvent);
  2734. if( pNote->bFlags & DMUS_NOTEF_NOTEON )
  2735. {
  2736. // If this is a note on, we want to take the offset into consideration for
  2737. // determining whether or not to invalidate.
  2738. MUSIC_TIME mtNote = pNote->mtTime - pNote->nOffset;
  2739. MusicToReferenceTime( mtNote, &rtTime );
  2740. }
  2741. // If note off and we want to leave notes playing, turn on the noinvalidate flag.
  2742. else if (fLeaveNotesOn)
  2743. {
  2744. pNote->bFlags |= DMUS_NOTEF_NOINVALIDATE;
  2745. }
  2746. }
  2747. else if( pEvent->dwType == DMUS_PMSGT_WAVE )
  2748. {
  2749. DMUS_WAVE_PMSG* pWave = (DMUS_WAVE_PMSG*)PRIV_TO_DMUS(pEvent);
  2750. if( !(pWave->bFlags & DMUS_WAVEF_OFF) )
  2751. {
  2752. if (pWave->dwFlags & DMUS_PMSGF_LOCKTOREFTIME)
  2753. {
  2754. rtTime = pWave->rtTime;
  2755. }
  2756. else
  2757. {
  2758. MusicToReferenceTime(pWave->mtTime, &rtTime);
  2759. }
  2760. }
  2761. // If wave off and we want to leave waves playing, turn on the noinvalidate flag.
  2762. else if (fLeaveNotesOn)
  2763. {
  2764. pWave->bFlags |= DMUS_WAVEF_NOINVALIDATE;
  2765. }
  2766. }
  2767. else if( pEvent->dwType == DMUS_PMSGT_CURVE )
  2768. {
  2769. if (fLeaveNotesOn)
  2770. {
  2771. rtTime = 0;
  2772. }
  2773. else
  2774. {
  2775. DMUS_CURVE_PMSG* pCurve = (DMUS_CURVE_PMSG*)PRIV_TO_DMUS(pEvent);
  2776. MUSIC_TIME mtCurve;
  2777. MUSIC_TIME mtStart;
  2778. mtStart = pCurve->mtOriginalStart ? pCurve->mtOriginalStart : pCurve->mtTime;
  2779. // if rtFlush is before the beginning of the curve minus the offset of
  2780. // the curve, we want to prevent the curve from playing
  2781. mtCurve = mtStart - pCurve->nOffset;
  2782. MusicToReferenceTime( mtCurve, &rtTime );
  2783. if( rtFlush > rtTime ) // if it isn't...
  2784. {
  2785. // if the curve has a reset value and has already begun,
  2786. // we may want to flush right away.
  2787. if( ( pCurve->bFlags & DMUS_CURVE_RESET) &&
  2788. pCurve->mtOriginalStart &&
  2789. rtFlush <= rtFlushUnresolved )
  2790. {
  2791. mtCurve = mtStart + pCurve->mtDuration;
  2792. MusicToReferenceTime( mtCurve, &rtTime );
  2793. if( rtTime >= rtFlush && !(pEvent->dwPrivFlags & PRIV_FLAG_FLUSH) )
  2794. {
  2795. MUSIC_TIME mt = 0;
  2796. ReferenceToMusicTime(rtFlush, &mt);
  2797. pCurve->mtDuration = (mt - mtStart) - 1;
  2798. pCurve->mtResetDuration = 1;
  2799. }
  2800. else
  2801. {
  2802. mtCurve = mtStart + pCurve->mtDuration + pCurve->mtResetDuration;
  2803. MusicToReferenceTime( mtCurve, &rtTime );
  2804. if ( rtTime >= rtFlush && !(pEvent->dwPrivFlags & PRIV_FLAG_FLUSH) )
  2805. {
  2806. MUSIC_TIME mt = 0;
  2807. ReferenceToMusicTime(rtFlush, &mt);
  2808. pCurve->mtResetDuration = mt - (mtStart + pCurve->mtDuration);
  2809. }
  2810. }
  2811. }
  2812. else
  2813. {
  2814. // Otherwise, we may cut the curve short in the code below.
  2815. rtTime = 0;
  2816. }
  2817. }
  2818. }
  2819. }
  2820. // now flush the event if needed
  2821. if( rtTime >= rtFlush )
  2822. {
  2823. if (!(pEvent->dwPrivFlags & PRIV_FLAG_FLUSH))
  2824. {
  2825. if( pEvent->pTool)
  2826. {
  2827. bool fFlush = false;
  2828. if (pEvent->dwType == DMUS_PMSGT_WAVE)
  2829. {
  2830. DMUS_WAVE_PMSG* pWave = (DMUS_WAVE_PMSG*)PRIV_TO_DMUS(pEvent);
  2831. if( !(pWave->bFlags & DMUS_WAVEF_OFF) )
  2832. {
  2833. // this wave on is due to start after the flush time.
  2834. // we never want to hear it.
  2835. fFlush = true;
  2836. }
  2837. else
  2838. {
  2839. // cut the duration short, but don't actually flush here,
  2840. // since it's possible to invalidate the same wave more
  2841. // than once, and the second invalidation might have a
  2842. // time prior to the first one (e.g., first is from a loop,
  2843. // second is from a transition)
  2844. if (GetInvalidationStatus(PRIV_TO_DMUS(pEvent)) &&
  2845. rtFlush < pWave->rtTime)
  2846. {
  2847. pEvent->dwPrivFlags |= PRIV_FLAG_REQUEUE;
  2848. MUSIC_TIME mtFlush = 0;
  2849. ReferenceToMusicTime(rtFlush, &mtFlush);
  2850. pWave->rtTime = rtFlush;
  2851. pWave->mtTime = mtFlush;
  2852. }
  2853. }
  2854. }
  2855. if (fFlush ||
  2856. (pEvent->dwType != DMUS_PMSGT_WAVE &&
  2857. GetInvalidationStatus(PRIV_TO_DMUS(pEvent))) )
  2858. {
  2859. pEvent->dwPrivFlags |= PRIV_FLAG_REMOVE;
  2860. pEvent->dwFlags |= DMUS_PMSGF_TOOL_FLUSH;
  2861. if( rtFlush <= pEvent->rtLast )
  2862. {
  2863. pEvent->pTool->Flush( this, PRIV_TO_DMUS(pEvent), pEvent->rtLast + REF_PER_MIL );
  2864. }
  2865. else
  2866. {
  2867. pEvent->pTool->Flush( this, PRIV_TO_DMUS(pEvent), rtFlush );
  2868. }
  2869. }
  2870. }
  2871. else
  2872. {
  2873. pEvent->dwPrivFlags |= PRIV_FLAG_REMOVE;
  2874. }
  2875. }
  2876. }
  2877. else // cut notes, waves, and curves short if needed
  2878. {
  2879. if( pEvent->dwType == DMUS_PMSGT_NOTE && !fLeaveNotesOn )
  2880. {
  2881. DMUS_NOTE_PMSG* pNote = (DMUS_NOTE_PMSG*)PRIV_TO_DMUS(pEvent);
  2882. if( pNote->bFlags & DMUS_NOTEF_NOTEON )
  2883. {
  2884. if (GetInvalidationStatus(PRIV_TO_DMUS(pEvent)))
  2885. {
  2886. // subtract 2 from the duration to guarantee the note cuts short
  2887. // 1 clock before the flush time.
  2888. MUSIC_TIME mtNoteOff = pNote->mtTime + pNote->mtDuration - 2;
  2889. REFERENCE_TIME rtNoteOff;
  2890. MusicToReferenceTime( mtNoteOff, &rtNoteOff );
  2891. if( rtNoteOff >= rtFlush )
  2892. {
  2893. ReferenceToMusicTime( rtFlush, &mtNoteOff );
  2894. mtNoteOff -= pNote->mtTime;
  2895. // Make any duration < 1 be 0; this will cause the note not to
  2896. // sound. Can happen if the note's logical time is well before
  2897. // its physical time.
  2898. if( mtNoteOff < 1 ) mtNoteOff = 0;
  2899. pNote->mtDuration = mtNoteOff;
  2900. }
  2901. }
  2902. }
  2903. }
  2904. else if( pEvent->dwType == DMUS_PMSGT_WAVE && !fLeaveNotesOn )
  2905. {
  2906. DMUS_WAVE_PMSG* pWave = (DMUS_WAVE_PMSG*)PRIV_TO_DMUS(pEvent);
  2907. if( !(pWave->bFlags & DMUS_WAVEF_OFF) &&
  2908. (GetInvalidationStatus(PRIV_TO_DMUS(pEvent))) )
  2909. {
  2910. if (pWave->dwFlags & DMUS_PMSGF_LOCKTOREFTIME)
  2911. {
  2912. // This is a clock time message.
  2913. // subtract 2 from the duration to guarantee the wave cuts short
  2914. // 1 clock before the flush time.
  2915. if ((rtTime + pWave->rtDuration - 2) >= rtFlush)
  2916. {
  2917. pWave->rtDuration = rtFlush - rtTime;
  2918. }
  2919. }
  2920. else
  2921. {
  2922. MUSIC_TIME mtTime = 0;
  2923. MUSIC_TIME mtFlush = 0;
  2924. ReferenceToMusicTime(rtTime, &mtTime);
  2925. ReferenceToMusicTime(rtFlush, &mtFlush);
  2926. // subtract 2 from the duration to guarantee the wave cuts short
  2927. // 1 clock before the flush time.
  2928. if ((mtTime + (MUSIC_TIME)pWave->rtDuration - 2) >= mtFlush)
  2929. {
  2930. pWave->rtDuration = mtFlush - mtTime;
  2931. }
  2932. }
  2933. if (pWave->rtDuration < 1) // disallow durations less than 1. This should never happen anyway.
  2934. {
  2935. pWave->rtDuration = 1;
  2936. }
  2937. }
  2938. }
  2939. else if( pEvent->dwType == DMUS_PMSGT_CURVE && !fLeaveNotesOn )
  2940. {
  2941. DMUS_CURVE_PMSG* pCurve = (DMUS_CURVE_PMSG*)PRIV_TO_DMUS(pEvent);
  2942. MUSIC_TIME mtEnd;
  2943. MUSIC_TIME mtStart = pCurve->mtOriginalStart ? pCurve->mtOriginalStart : pCurve->mtTime;
  2944. if( pCurve->bFlags & DMUS_CURVE_RESET )
  2945. {
  2946. mtEnd = mtStart + pCurve->mtResetDuration + pCurve->mtDuration;
  2947. }
  2948. else
  2949. {
  2950. mtEnd = mtStart + pCurve->mtDuration;
  2951. }
  2952. REFERENCE_TIME rtEnd;
  2953. MusicToReferenceTime( mtEnd, &rtEnd );
  2954. // Note: as a result of fixing 33987, the curve is no longer given
  2955. // a negative reset duration. Now, the curve's duration is recomputed
  2956. // and its time slice is recalculated.
  2957. if( rtEnd >= rtFlush )
  2958. {
  2959. // reset the curve's duration
  2960. ReferenceToMusicTime( rtFlush, &mtEnd );
  2961. mtEnd -= mtStart;
  2962. // get the curve value at the flush time, and make that the end value
  2963. pCurve->nEndValue = (short) RecomputeCurveEnd(pCurve, mtEnd);
  2964. // subtract 2 from the duration to guarantee the curve cuts short
  2965. // 1 clock before the flush time.
  2966. mtEnd -= 2;
  2967. if ( mtEnd < 1)
  2968. {
  2969. mtEnd = 1;
  2970. }
  2971. else if (pCurve->bFlags & DMUS_CURVE_RESET)
  2972. {
  2973. if (mtEnd > pCurve->mtDuration)
  2974. {
  2975. // curve ends in the reset duration; keep regular duration the
  2976. // same as it was and adjust reset duration
  2977. pEvent->dwPrivFlags |= PRIV_FLAG_FLUSH;
  2978. MUSIC_TIME mt = 0;
  2979. ReferenceToMusicTime(rtFlush, &mt);
  2980. pCurve->mtResetDuration = mt - (mtStart + pCurve->mtDuration);
  2981. mtEnd = pCurve->mtDuration;
  2982. if (pCurve->mtTime > mtEnd + pCurve->mtResetDuration + mtStart)
  2983. {
  2984. pCurve->mtTime = mtEnd + pCurve->mtResetDuration + mtStart;
  2985. MusicToReferenceTime(pCurve->mtTime, &pCurve->rtTime);
  2986. }
  2987. }
  2988. else
  2989. {
  2990. // curve ends in the regular duration; reduce it by 1 and
  2991. // give the reset duration a value of 1
  2992. mtEnd--;
  2993. pCurve->mtResetDuration = 1;
  2994. if (mtEnd < 1)
  2995. {
  2996. // this is unlikely, but the curve really should have
  2997. // a duration...
  2998. mtEnd = 1;
  2999. }
  3000. pEvent->dwPrivFlags |= PRIV_FLAG_FLUSH;
  3001. }
  3002. // If this is an instant curve that's already started, we
  3003. // don't want it to play again, so reset its start time
  3004. if ( pCurve->bCurveShape == DMUS_CURVES_INSTANT &&
  3005. pCurve->mtOriginalStart )
  3006. {
  3007. pCurve->mtTime = pCurve->mtResetDuration + pCurve->mtOriginalStart + mtEnd;
  3008. }
  3009. }
  3010. pCurve->mtDuration = mtEnd;
  3011. }
  3012. }
  3013. }
  3014. }
  3015. }
  3016. // remove (and unmark) all marked PMsgs from the current queue
  3017. for(pEvent = pQueue->GetHead(); pEvent; pEvent = pNext )
  3018. {
  3019. pNext = pEvent->pNext;
  3020. if (pEvent->dwPrivFlags & (PRIV_FLAG_REMOVE | PRIV_FLAG_REQUEUE))
  3021. {
  3022. pEvent->dwPrivFlags &= ~PRIV_FLAG_REMOVE;
  3023. if (pQueue->Dequeue(pEvent))
  3024. {
  3025. if (pEvent->dwPrivFlags & PRIV_FLAG_REQUEUE)
  3026. {
  3027. pEvent->dwPrivFlags &= ~PRIV_FLAG_REQUEUE;
  3028. pQueue->Enqueue(pEvent);
  3029. }
  3030. else
  3031. {
  3032. FreePMsg(pEvent);
  3033. }
  3034. }
  3035. else
  3036. {
  3037. TraceI(0,"Error dequeing event for flushing\n");
  3038. }
  3039. }
  3040. }
  3041. SendBuffers();
  3042. }
  3043. /*
  3044. Flushes all events in all queues from time <p mtFlush> on.
  3045. comm Only call this from withing a PipelineCrSec critical section!
  3046. */
  3047. void CPerformance::FlushMainEventQueues(
  3048. DWORD dwId, // Virtual Track ID to flush, or zero for all.
  3049. MUSIC_TIME mtFlush, // Time to flush (resolved to timing resolution).
  3050. MUSIC_TIME mtFlushUnresolved, // Time to flush (unresolved).
  3051. BOOL fLeaveNotesOn) // If true, notes currently on are left to play through their duration.
  3052. {
  3053. REFERENCE_TIME rt;
  3054. if( mtFlush )
  3055. {
  3056. MusicToReferenceTime( mtFlush, &rt );
  3057. }
  3058. else
  3059. {
  3060. rt = 0;
  3061. }
  3062. REFERENCE_TIME rtUnresolved;
  3063. if( mtFlushUnresolved && mtFlushUnresolved != mtFlush)
  3064. {
  3065. MusicToReferenceTime( mtFlushUnresolved, &rtUnresolved );
  3066. }
  3067. else
  3068. {
  3069. rtUnresolved = rt;
  3070. }
  3071. FlushEventQueue( dwId, &m_OnTimeQueue, rt, rtUnresolved, fLeaveNotesOn );
  3072. FlushEventQueue( dwId, &m_NearTimeQueue, rt, rtUnresolved, fLeaveNotesOn );
  3073. FlushEventQueue( dwId, &m_EarlyQueue, rt, rtUnresolved, fLeaveNotesOn );
  3074. if (dwId == 0)
  3075. {
  3076. MUSIC_TIME mtTime;
  3077. ReferenceToMusicTime(rt,&mtTime);
  3078. FlushEventQueue( dwId, &m_TempoMap, rt, rtUnresolved, fLeaveNotesOn );
  3079. RecalcTempoMap(NULL, mtTime );
  3080. }
  3081. }
  3082. // the only kinds of events we care about are note events.
  3083. void CPerformance::OnChordUpdateEventQueue( DMUS_NOTIFICATION_PMSG* pNotify, CPMsgQueue *pQueue, REFERENCE_TIME rtFlush )
  3084. {
  3085. PRIV_PMSG* pEvent;
  3086. PRIV_PMSG* pNext;
  3087. HRESULT hr = S_OK;
  3088. DWORD dwId = pNotify->dwVirtualTrackID;
  3089. DWORD dwTrackGroup = pNotify->dwGroupID;
  3090. CPMsgQueue UpdateQueue; // List of PMsgs to be inserted into a queue during update.
  3091. REFERENCE_TIME rtTemp;
  3092. GetQueueTime(&rtTemp);
  3093. pNext = NULL;
  3094. for(pEvent = pQueue->GetHead(); pEvent; pEvent = pNext )
  3095. {
  3096. pNext = pEvent->pNext;
  3097. pEvent->dwPrivFlags &= ~PRIV_FLAG_REMOVE;
  3098. DMUS_PMSG* pNew = NULL;
  3099. if( ( 0 == dwId || pEvent->dwVirtualTrackID == dwId ) &&
  3100. (pEvent->dwType == DMUS_PMSGT_NOTE) )
  3101. {
  3102. REFERENCE_TIME rtTime = pEvent->rtTime;
  3103. DMUS_NOTE_PMSG* pNote = (DMUS_NOTE_PMSG*)PRIV_TO_DMUS(pEvent);
  3104. if( pNote->bFlags & DMUS_NOTEF_NOTEON )
  3105. {
  3106. MUSIC_TIME mtNote = pNote->mtTime - pNote->nOffset;
  3107. MusicToReferenceTime( mtNote, &rtTime );
  3108. }
  3109. // now flush the event if needed
  3110. if( rtTime >= rtFlush )
  3111. {
  3112. REFERENCE_TIME rtFlushTime = (rtFlush <= pEvent->rtLast) ? pEvent->rtLast + REF_PER_MIL : rtFlush;
  3113. if( pEvent->pTool &&
  3114. !(pNote->bFlags & DMUS_NOTEF_NOTEON) &&
  3115. S_OK == (hr = GetChordNotificationStatus(pNote, dwTrackGroup, rtFlushTime, &pNew)))
  3116. {
  3117. pEvent->dwPrivFlags |= PRIV_FLAG_REMOVE;
  3118. pEvent->dwFlags |= DMUS_PMSGF_TOOL_FLUSH;
  3119. pEvent->pTool->Flush( this, PRIV_TO_DMUS(pEvent), rtFlushTime );
  3120. }
  3121. if (SUCCEEDED(hr) && pNew) // add to temp queue for later insertion into regular queue
  3122. {
  3123. UpdateQueue.Enqueue( DMUS_TO_PRIV(pNew) );
  3124. }
  3125. }
  3126. else // cut notes short if needed
  3127. {
  3128. if( pNote->bFlags & DMUS_NOTEF_NOTEON )
  3129. {
  3130. if (S_OK == (hr = GetChordNotificationStatus(pNote, dwTrackGroup, rtFlush, &pNew)))
  3131. {
  3132. // subtract 2 from the duration to guarantee the note cuts short
  3133. // 1 clock before the flush time.
  3134. MUSIC_TIME mtNoteOff = pNote->mtTime + pNote->mtDuration - 2;
  3135. REFERENCE_TIME rtNoteOff;
  3136. MusicToReferenceTime( mtNoteOff, &rtNoteOff );
  3137. if( rtNoteOff >= rtFlush )
  3138. {
  3139. ReferenceToMusicTime( rtFlush, &mtNoteOff );
  3140. mtNoteOff -= pNote->mtTime;
  3141. if( mtNoteOff < 1 ) mtNoteOff = 1; // disallow durations less than 1. This should never happen anyway.
  3142. pNote->mtDuration = mtNoteOff;
  3143. }
  3144. }
  3145. if (SUCCEEDED(hr) && pNew) // add to temp queue for later insertion into regular queue
  3146. {
  3147. UpdateQueue.Enqueue( DMUS_TO_PRIV(pNew) );
  3148. }
  3149. }
  3150. }
  3151. }
  3152. }
  3153. // remove (and unmark) all marked PMsgs from the current queue
  3154. for(pEvent = pQueue->GetHead(); pEvent; pEvent = pNext )
  3155. {
  3156. pNext = pEvent->pNext;
  3157. if (pEvent->dwPrivFlags & PRIV_FLAG_REMOVE)
  3158. {
  3159. pEvent->dwPrivFlags &= ~PRIV_FLAG_REMOVE;
  3160. if (pQueue->Dequeue(pEvent))
  3161. {
  3162. FreePMsg(pEvent);
  3163. }
  3164. else
  3165. {
  3166. TraceI(0,"Error dequeing event for flushing\n");
  3167. }
  3168. }
  3169. }
  3170. // empty the Update queue into the current queue
  3171. while( pEvent = UpdateQueue.Dequeue() )
  3172. {
  3173. pQueue->Enqueue(pEvent);
  3174. }
  3175. SendBuffers();
  3176. }
  3177. /*
  3178. Only call this from withing a PipelineCrSec critical section!
  3179. */
  3180. void CPerformance::OnChordUpdateEventQueues(
  3181. DMUS_NOTIFICATION_PMSG* pNotify) // notification PMsg that caused this to be called
  3182. {
  3183. IDirectMusicSegmentState* pSegState = NULL;
  3184. if (!pNotify || !pNotify->punkUser) return;
  3185. REFERENCE_TIME rt = 0;
  3186. if( pNotify->mtTime )
  3187. {
  3188. MusicToReferenceTime( pNotify->mtTime, &rt );
  3189. }
  3190. OnChordUpdateEventQueue( pNotify, &m_OnTimeQueue, rt );
  3191. OnChordUpdateEventQueue( pNotify, &m_NearTimeQueue, rt );
  3192. OnChordUpdateEventQueue( pNotify, &m_EarlyQueue, rt );
  3193. }
  3194. /////////////////////////////////////////////////////////////////////////////
  3195. // IDirectMusicPerformance
  3196. HRESULT CPerformance::CreateThreads()
  3197. {
  3198. // initialize the realtime thread
  3199. m_hRealtimeThread = CreateThread(NULL, 0, _Realtime, this, 0, &m_dwRealtimeThreadID);
  3200. if( m_hRealtimeThread )
  3201. {
  3202. m_hRealtime = CreateEvent(NULL,FALSE,FALSE,NULL);
  3203. SetThreadPriority( m_hRealtimeThread, THREAD_PRIORITY_TIME_CRITICAL );
  3204. }
  3205. else
  3206. {
  3207. TraceI(0, "Major error! Realtime thread not created.\n");
  3208. return E_OUTOFMEMORY;
  3209. }
  3210. // initialize the transport thread
  3211. m_hTransportThread = CreateThread(NULL, 0, _Transport, this, 0, &m_dwTransportThreadID);
  3212. if( m_hTransportThread )
  3213. {
  3214. m_hTransport = CreateEvent(NULL, FALSE, FALSE, NULL);
  3215. SetThreadPriority( m_hTransportThread, THREAD_PRIORITY_ABOVE_NORMAL );
  3216. }
  3217. else
  3218. {
  3219. TraceI(0, "Major error! Transport thread not created.\n");
  3220. m_fKillRealtimeThread = TRUE;
  3221. if( m_hRealtime ) SetEvent( m_hRealtime );
  3222. return E_OUTOFMEMORY;
  3223. }
  3224. m_pDirectMusic->GetMasterClock( NULL, &m_pClock );
  3225. m_rtStart = GetTime();
  3226. m_rtQueuePosition = m_rtStart;
  3227. return S_OK;
  3228. }
  3229. STDMETHODIMP CPerformance::InitAudio(IDirectMusic** ppDirectMusic,
  3230. IDirectSound** ppDirectSound,
  3231. HWND hWnd,
  3232. DWORD dwDefaultPathType,
  3233. DWORD dwPChannelCount,
  3234. DWORD dwFlags,
  3235. DMUS_AUDIOPARAMS *pParams)
  3236. {
  3237. V_INAME(IDirectMusicPerformance::InitAudio);
  3238. V_PTRPTR_WRITE_OPT(ppDirectMusic);
  3239. V_PTRPTR_WRITE_OPT(ppDirectSound);
  3240. V_HWND_OPT(hWnd);
  3241. HRESULT hr = S_OK;
  3242. // Further validate, checking for a pointer to a bad interface pointer...
  3243. if (ppDirectMusic)
  3244. {
  3245. V_INTERFACE_OPT(*ppDirectMusic);
  3246. }
  3247. if (ppDirectSound)
  3248. {
  3249. V_INTERFACE_OPT(*ppDirectSound);
  3250. }
  3251. if( m_dwAudioPathMode )
  3252. {
  3253. Trace(1,"Error: InitAudio called on an already initialized Performance.\n");
  3254. return DMUS_E_ALREADY_INITED;
  3255. }
  3256. if (dwFlags == 0)
  3257. {
  3258. dwFlags = DMUS_AUDIOF_ALL;
  3259. }
  3260. Init();
  3261. m_AudioParams.dwFeatures = dwFlags;
  3262. m_AudioParams.dwSampleRate = 22050;
  3263. m_AudioParams.dwSize = sizeof (m_AudioParams);
  3264. m_AudioParams.dwValidData = DMUS_AUDIOPARAMS_FEATURES | DMUS_AUDIOPARAMS_VOICES | DMUS_AUDIOPARAMS_SAMPLERATE | DMUS_AUDIOPARAMS_DEFAULTSYNTH;
  3265. m_AudioParams.dwVoices = 64;
  3266. m_AudioParams.fInitNow = TRUE;
  3267. m_AudioParams.clsidDefaultSynth = CLSID_DirectMusicSynth;
  3268. if (pParams)
  3269. {
  3270. if (pParams->dwValidData & DMUS_AUDIOPARAMS_FEATURES)
  3271. {
  3272. m_AudioParams.dwFeatures = pParams->dwFeatures;
  3273. }
  3274. if (pParams->dwValidData & DMUS_AUDIOPARAMS_VOICES)
  3275. {
  3276. m_AudioParams.dwVoices = pParams->dwVoices;
  3277. }
  3278. if (pParams->dwValidData & DMUS_AUDIOPARAMS_DEFAULTSYNTH)
  3279. {
  3280. // If they requested the DX7 default synth and yet also asked for audiopath
  3281. // features, force to DX8 default synth.
  3282. if ((pParams->clsidDefaultSynth != GUID_NULL) ||
  3283. !((m_AudioParams.dwValidData & DMUS_AUDIOPARAMS_FEATURES) &&
  3284. (m_AudioParams.dwFeatures & DMUS_AUDIOF_ALL)))
  3285. {
  3286. m_AudioParams.clsidDefaultSynth = pParams->clsidDefaultSynth;
  3287. }
  3288. }
  3289. if (pParams->dwValidData & DMUS_AUDIOPARAMS_SAMPLERATE)
  3290. {
  3291. if (pParams->dwSampleRate > 96000)
  3292. {
  3293. m_AudioParams.dwSampleRate = 96000;
  3294. }
  3295. else if (pParams->dwSampleRate < 11025)
  3296. {
  3297. m_AudioParams.dwSampleRate = 11025;
  3298. }
  3299. else
  3300. {
  3301. m_AudioParams.dwSampleRate = pParams->dwSampleRate;
  3302. }
  3303. }
  3304. }
  3305. m_dwAudioPathMode = 2;
  3306. EnterCriticalSection(&m_MainCrSec);
  3307. if (ppDirectMusic && *ppDirectMusic)
  3308. {
  3309. hr = (*ppDirectMusic)->QueryInterface(IID_IDirectMusic8,(void **) &m_pDirectMusic);
  3310. }
  3311. if (SUCCEEDED(hr))
  3312. {
  3313. if (ppDirectSound && *ppDirectSound)
  3314. {
  3315. hr = (*ppDirectSound)->QueryInterface(IID_IDirectSound8,(void **) &m_pDirectSound);
  3316. }
  3317. if (SUCCEEDED(hr))
  3318. {
  3319. if (!m_pDirectSound)
  3320. {
  3321. hr = DirectSoundCreate8(NULL,&m_pDirectSound,NULL);
  3322. if (SUCCEEDED(hr))
  3323. {
  3324. if (!hWnd)
  3325. {
  3326. hWnd = GetForegroundWindow();
  3327. if (!hWnd)
  3328. {
  3329. hWnd = GetDesktopWindow();
  3330. }
  3331. }
  3332. m_pDirectSound->SetCooperativeLevel(hWnd, DSSCL_PRIORITY);
  3333. }
  3334. }
  3335. if (SUCCEEDED(hr))
  3336. {
  3337. if (!m_pDirectMusic)
  3338. {
  3339. hr = CoCreateInstance(CLSID_DirectMusic,
  3340. NULL,
  3341. CLSCTX_INPROC,
  3342. IID_IDirectMusic8,
  3343. (LPVOID*)&m_pDirectMusic);
  3344. if (SUCCEEDED(hr))
  3345. {
  3346. hr = m_pDirectMusic->SetDirectSound(m_pDirectSound,hWnd);
  3347. }
  3348. }
  3349. }
  3350. }
  3351. }
  3352. if (SUCCEEDED(hr))
  3353. {
  3354. hr = m_BufferManager.Init(this,&m_AudioParams);
  3355. if (SUCCEEDED(hr))
  3356. {
  3357. // If we are going to be connecting the synth to Buffers,
  3358. // force the use of the dsound clock.
  3359. if (m_AudioParams.dwFeatures & DMUS_AUDIOF_BUFFERS)
  3360. {
  3361. DMUS_CLOCKINFO ClockInfo;
  3362. ClockInfo.dwSize = sizeof(ClockInfo);
  3363. DWORD dwIndex;
  3364. GUID guidMasterClock = GUID_NULL;
  3365. for (dwIndex = 0; ;dwIndex++)
  3366. {
  3367. if (S_OK == m_pDirectMusic->EnumMasterClock(dwIndex, &ClockInfo))
  3368. {
  3369. if (!wcscmp(ClockInfo.wszDescription, L"DirectSound Clock"))
  3370. {
  3371. guidMasterClock = ClockInfo.guidClock;
  3372. break;
  3373. }
  3374. }
  3375. else
  3376. {
  3377. break;
  3378. }
  3379. }
  3380. m_pDirectMusic->SetMasterClock(guidMasterClock);
  3381. }
  3382. hr = CreateThreads();
  3383. if (SUCCEEDED(hr))
  3384. {
  3385. if (dwDefaultPathType)
  3386. {
  3387. IDirectMusicAudioPath *pPath;
  3388. hr = CreateStandardAudioPath(dwDefaultPathType,dwPChannelCount,m_AudioParams.fInitNow,&pPath);
  3389. if (SUCCEEDED(hr))
  3390. {
  3391. hr = SetDefaultAudioPath(pPath);
  3392. pPath->Release();
  3393. }
  3394. }
  3395. }
  3396. }
  3397. }
  3398. if (SUCCEEDED(hr))
  3399. {
  3400. if (m_pDirectMusic && ppDirectMusic && !*ppDirectMusic)
  3401. {
  3402. *ppDirectMusic = m_pDirectMusic;
  3403. m_pDirectMusic->AddRef();
  3404. }
  3405. if (m_pDirectSound && ppDirectSound && !*ppDirectSound)
  3406. {
  3407. *ppDirectSound = m_pDirectSound;
  3408. m_pDirectSound->AddRef();
  3409. }
  3410. if (pParams && pParams->fInitNow)
  3411. {
  3412. if (pParams->clsidDefaultSynth != m_AudioParams.clsidDefaultSynth)
  3413. {
  3414. pParams->clsidDefaultSynth = m_AudioParams.clsidDefaultSynth;
  3415. if (pParams->dwValidData & DMUS_AUDIOPARAMS_DEFAULTSYNTH)
  3416. {
  3417. Trace(2,"Warning: Default synth choice has been changed.\n");
  3418. hr = S_FALSE;
  3419. }
  3420. }
  3421. if (pParams->dwFeatures != m_AudioParams.dwFeatures)
  3422. {
  3423. pParams->dwFeatures = m_AudioParams.dwFeatures;
  3424. if (pParams->dwValidData & DMUS_AUDIOPARAMS_FEATURES)
  3425. {
  3426. Trace(2,"Warning: Features flags has been changed to %lx.\n",pParams->dwFeatures);
  3427. hr = S_FALSE;
  3428. }
  3429. }
  3430. if (pParams->dwSampleRate != m_AudioParams.dwSampleRate)
  3431. {
  3432. pParams->dwSampleRate = m_AudioParams.dwSampleRate;
  3433. if (pParams->dwValidData & DMUS_AUDIOPARAMS_SAMPLERATE)
  3434. {
  3435. Trace(2,"Warning: Sample rate has been changed to %ld.\n",pParams->dwSampleRate);
  3436. hr = S_FALSE;
  3437. }
  3438. }
  3439. if (pParams->dwVoices != m_AudioParams.dwVoices)
  3440. {
  3441. pParams->dwVoices = m_AudioParams.dwVoices;
  3442. if (pParams->dwValidData & DMUS_AUDIOPARAMS_VOICES)
  3443. {
  3444. Trace(2,"Warning: Number of requested voices has been changed to %ld.\n",pParams->dwVoices);
  3445. hr = S_FALSE;
  3446. }
  3447. }
  3448. pParams->dwValidData = m_AudioParams.dwValidData;
  3449. }
  3450. LeaveCriticalSection(&m_MainCrSec);
  3451. }
  3452. else
  3453. {
  3454. LeaveCriticalSection(&m_MainCrSec);
  3455. CloseDown();
  3456. }
  3457. return hr;
  3458. }
  3459. HRESULT STDMETHODCALLTYPE CPerformance::Init(
  3460. IDirectMusic** ppDirectMusic, LPDIRECTSOUND pDirectSound,HWND hWnd)
  3461. {
  3462. V_INAME(IDirectMusicPerformance::Init);
  3463. V_PTRPTR_WRITE_OPT(ppDirectMusic);
  3464. V_INTERFACE_OPT(pDirectSound);
  3465. V_HWND_OPT(hWnd);
  3466. HRESULT hr = S_OK;
  3467. // Further validate, checking for a pointer to a bad interface pointer...
  3468. if (ppDirectMusic)
  3469. {
  3470. V_INTERFACE_OPT(*ppDirectMusic);
  3471. }
  3472. if( m_dwAudioPathMode )
  3473. {
  3474. Trace(1,"Error: Init called on an already initialized Performance.\n");
  3475. return DMUS_E_ALREADY_INITED;
  3476. }
  3477. Init();
  3478. m_dwAudioPathMode = 1;
  3479. EnterCriticalSection(&m_MainCrSec);
  3480. if(( NULL == ppDirectMusic ) || ( NULL == *ppDirectMusic ))
  3481. {
  3482. // intialize DirectMusic.
  3483. if( FAILED( CoCreateInstance(CLSID_DirectMusic,
  3484. NULL,
  3485. CLSCTX_INPROC,
  3486. IID_IDirectMusic,
  3487. (LPVOID*)&m_pDirectMusic)))
  3488. {
  3489. m_pDirectMusic = NULL;
  3490. LeaveCriticalSection(&m_MainCrSec);
  3491. return E_OUTOFMEMORY;
  3492. }
  3493. // If version2 was requested by the app (in the process of requesting the
  3494. // IDirectMusicPerformance2 interface), do the same for IDirectMusic.
  3495. if (m_dwVersion > 6)
  3496. {
  3497. IDirectMusic *pTemp = NULL;
  3498. if (SUCCEEDED(m_pDirectMusic->QueryInterface(
  3499. IID_IDirectMusic2,
  3500. (LPVOID*)&pTemp)))
  3501. {
  3502. // Succeeded in requesting DX7 and up behavior...
  3503. pTemp->Release();
  3504. }
  3505. }
  3506. hr = m_pDirectMusic->SetDirectSound(pDirectSound, hWnd);
  3507. if( FAILED( hr ) )
  3508. {
  3509. m_pDirectMusic->Release();
  3510. m_pDirectMusic = NULL;
  3511. LeaveCriticalSection(&m_MainCrSec);
  3512. return hr;
  3513. }
  3514. if( ppDirectMusic )
  3515. {
  3516. *ppDirectMusic = m_pDirectMusic;
  3517. m_pDirectMusic->AddRef();
  3518. }
  3519. }
  3520. else
  3521. {
  3522. m_pDirectMusic = (IDirectMusic8 *) *ppDirectMusic;
  3523. m_pDirectMusic->AddRef();
  3524. }
  3525. if (FAILED(hr = CreateThreads()))
  3526. {
  3527. if( m_pDirectMusic )
  3528. {
  3529. m_pDirectMusic->Release();
  3530. m_pDirectMusic = NULL;
  3531. }
  3532. }
  3533. LeaveCriticalSection(&m_MainCrSec);
  3534. return hr;
  3535. }
  3536. CSegState *CPerformance::GetSegmentForTransition(DWORD dwFlags,MUSIC_TIME mtTime, IUnknown *pFrom)
  3537. {
  3538. CSegState *pSegState = NULL;
  3539. // If the source segment was provided, use it.
  3540. if (pFrom)
  3541. {
  3542. if (SUCCEEDED(pFrom->QueryInterface(IID_CSegState,(void **) &pSegState)))
  3543. {
  3544. pSegState->Release();
  3545. }
  3546. }
  3547. // Else, if this is a primary segment, get the current primary segment.
  3548. if (!pSegState && !(dwFlags & DMUS_SEGF_SECONDARY))
  3549. {
  3550. pSegState = GetPrimarySegmentAtTime(mtTime);
  3551. }
  3552. return pSegState;
  3553. }
  3554. void CPerformance::ClearMusicStoppedNotification()
  3555. {
  3556. EnterCriticalSection(&m_PipelineCrSec);
  3557. PRIV_PMSG* pPMsg;
  3558. PRIV_PMSG* pNext;
  3559. DMUS_NOTIFICATION_PMSG* pNotification;
  3560. pPMsg = m_OnTimeQueue.GetHead(); // where notifications live normally
  3561. for (; pPMsg ; pPMsg = pNext)
  3562. {
  3563. pNext = pPMsg->pNext;
  3564. pNotification = (DMUS_NOTIFICATION_PMSG*)PRIV_TO_DMUS(pPMsg);
  3565. if( ( pPMsg->dwType == DMUS_PMSGT_NOTIFICATION ) &&
  3566. ( pNotification->guidNotificationType == GUID_NOTIFICATION_PERFORMANCE ) &&
  3567. ( pNotification->dwNotificationOption == DMUS_NOTIFICATION_MUSICSTOPPED ) )
  3568. {
  3569. pPMsg = m_OnTimeQueue.Dequeue(pPMsg);
  3570. if( pPMsg ) // Should always succeeed
  3571. {
  3572. FreePMsg(pPMsg);
  3573. }
  3574. m_fMusicStopped = FALSE;
  3575. }
  3576. }
  3577. LeaveCriticalSection(&m_PipelineCrSec);
  3578. }
  3579. HRESULT CPerformance::PlayOneSegment(
  3580. CSegment* pSegment,
  3581. DWORD dwFlags,
  3582. __int64 i64StartTime,
  3583. CSegState **ppSegState,
  3584. CAudioPath *pAudioPath)
  3585. {
  3586. HRESULT hr;
  3587. #ifdef DBG_PROFILE
  3588. DWORD dwDebugTime;
  3589. dwDebugTime = timeGetTime();
  3590. #endif
  3591. TraceI(0,"Play Segment %lx (%ls) at time %ld with flags %lx\n",pSegment,pSegment->m_wszName,(long)i64StartTime,dwFlags);
  3592. if( dwFlags & DMUS_SEGF_CONTROL )
  3593. {
  3594. dwFlags |= DMUS_SEGF_SECONDARY;
  3595. }
  3596. if( i64StartTime )
  3597. {
  3598. if(dwFlags & DMUS_SEGF_REFTIME)
  3599. {
  3600. // Give a grace period of 100ms.
  3601. if( i64StartTime < (GetLatency() - (100 * REF_PER_MIL)))
  3602. {
  3603. Trace(1,"Error: Unable to play segment, requested clock time %ld is past current time %ld\n",
  3604. (long)i64StartTime,(long)(GetLatency() - (100 * REF_PER_MIL)));
  3605. return DMUS_E_TIME_PAST;
  3606. }
  3607. }
  3608. else
  3609. {
  3610. MUSIC_TIME mtPrePlay;
  3611. // Give a grace period of 100ms.
  3612. ReferenceToMusicTime( (GetLatency() - (100 * REF_PER_MIL)), &mtPrePlay );
  3613. if( (MUSIC_TIME)i64StartTime < mtPrePlay )
  3614. {
  3615. Trace(1,"Error: Unable to play segment, requested music time %ld is past current time %ld\n",
  3616. (long)i64StartTime,(long)mtPrePlay);
  3617. return DMUS_E_TIME_PAST;
  3618. }
  3619. }
  3620. }
  3621. CSegState *pSegState = NULL;
  3622. hr = pSegment->CreateSegmentState( &pSegState, this, pAudioPath, dwFlags);
  3623. *ppSegState = pSegState;
  3624. if (FAILED(hr))
  3625. {
  3626. Trace(1,"Error: Unable to play segment because of failure creating segment state.\n");
  3627. return DMUS_E_SEGMENT_INIT_FAILED;
  3628. }
  3629. pSegState->m_rtGivenStart = i64StartTime;
  3630. pSegState->m_dwPlaySegFlags = dwFlags;
  3631. // add the pSegState to the appropriate queue
  3632. EnterCriticalSection(&m_SegmentCrSec);
  3633. m_fPlaying = 1; // turn on the transport
  3634. // add all notifications to the segment. First, clear it, in case old notifications
  3635. // are in effect.
  3636. pSegment->RemoveNotificationType(GUID_NULL,TRUE);
  3637. CNotificationItem* pItem;
  3638. pItem = m_NotificationList.GetHead();
  3639. while( pItem )
  3640. {
  3641. pSegment->AddNotificationType( pItem->guidNotificationType, TRUE );
  3642. pItem = pItem->GetNext();
  3643. }
  3644. if( pSegState->m_dwPlaySegFlags & DMUS_SEGF_AFTERPREPARETIME )
  3645. {
  3646. // we want to queue this at the last transported time,
  3647. // so we don't need to do an invalidate
  3648. if( pSegState->m_dwPlaySegFlags & DMUS_SEGF_REFTIME )
  3649. {
  3650. REFERENCE_TIME rtTrans;
  3651. MusicToReferenceTime( m_mtTransported, &rtTrans );
  3652. if( pSegState->m_rtGivenStart < rtTrans )
  3653. {
  3654. pSegState->m_dwPlaySegFlags &= ~DMUS_SEGF_REFTIME;
  3655. pSegState->m_rtGivenStart = m_mtTransported;
  3656. }
  3657. }
  3658. else
  3659. {
  3660. if( pSegState->m_rtGivenStart < m_mtTransported )
  3661. {
  3662. pSegState->m_rtGivenStart = m_mtTransported;
  3663. }
  3664. }
  3665. }
  3666. else if( pSegState->m_dwPlaySegFlags & DMUS_SEGF_AFTERQUEUETIME )
  3667. {
  3668. // we want to queue this at the queue time, as opposed to latency time,
  3669. // which is an option for secondary segments.
  3670. REFERENCE_TIME rtStart;
  3671. GetQueueTime( &rtStart ); // need queue time because control segments cause invalidations
  3672. if( pSegState->m_dwPlaySegFlags & DMUS_SEGF_REFTIME )
  3673. {
  3674. if( pSegState->m_rtGivenStart < rtStart )
  3675. {
  3676. pSegState->m_rtGivenStart = rtStart;
  3677. }
  3678. }
  3679. else
  3680. {
  3681. MUSIC_TIME mtStart;
  3682. ReferenceToMusicTime( rtStart, &mtStart );
  3683. if( pSegState->m_rtGivenStart < mtStart )
  3684. {
  3685. pSegState->m_rtGivenStart = mtStart;
  3686. }
  3687. }
  3688. }
  3689. // need to get rid of any pending musicstopped notifications
  3690. ClearMusicStoppedNotification();
  3691. pSegState->AddRef();
  3692. if( dwFlags & DMUS_SEGF_SECONDARY ) // queue a secondary segment
  3693. {
  3694. QueueSecondarySegment( pSegState );
  3695. }
  3696. else // queue a primary segment
  3697. {
  3698. QueuePrimarySegment( pSegState );
  3699. }
  3700. LeaveCriticalSection(&m_SegmentCrSec);
  3701. #ifdef DBG_PROFILE
  3702. dwDebugTime = timeGetTime() - dwDebugTime;
  3703. TraceI(5, "perf, debugtime PlaySegment %u\n", dwDebugTime);
  3704. #endif
  3705. // signal the transport thread so we don't have to wait for it to wake up on its own
  3706. if( m_hTransport ) SetEvent( m_hTransport );
  3707. return S_OK;
  3708. }
  3709. HRESULT CPerformance::PlaySegmentInternal(
  3710. CSegment* pSegment,
  3711. CSong * pSong,
  3712. WCHAR *pwzSegmentName,
  3713. CSegment* pTransition,
  3714. DWORD dwFlags,
  3715. __int64 i64StartTime,
  3716. IDirectMusicSegmentState** ppSegmentState,
  3717. IUnknown *pFrom,
  3718. CAudioPath *pAudioPath)
  3719. {
  3720. HRESULT hr;
  3721. CAudioPath *pInternalPath = NULL;
  3722. if( m_pClock == NULL )
  3723. {
  3724. Trace(1,"Error: Can not play segment because master clock has not been initialized.\n");
  3725. return DMUS_E_NO_MASTER_CLOCK;
  3726. }
  3727. if (pAudioPath && (pAudioPath->NoPorts()))
  3728. {
  3729. // This audiopath can't be used for playback since it doesn't have any ports.
  3730. Trace(1,"Error: Audiopath can't be used for playback because it doesn't have any ports.\n");
  3731. return DMUS_E_AUDIOPATH_NOPORT;
  3732. }
  3733. // Pointer to segment or song provided audio path config.
  3734. IUnknown *pConfig = NULL;
  3735. /* If this is a song, use the segment name to get the segment.
  3736. Then, it looks like a normal segment except the
  3737. existence of the pSong will let the segstate know
  3738. that it is a member of a song, so it should chain segments.
  3739. */
  3740. if (pSong)
  3741. {
  3742. IDirectMusicSegment *pISegment = NULL;
  3743. hr = pSong->GetSegment(pwzSegmentName,&pISegment);
  3744. if (hr != S_OK)
  3745. {
  3746. return DMUS_E_NOT_FOUND;
  3747. }
  3748. pSegment = (CSegment *) pISegment;
  3749. // If the app wants an audiopath created dynamically from the song, find it and use it.
  3750. if (dwFlags & DMUS_SEGF_USE_AUDIOPATH)
  3751. {
  3752. pSong->GetAudioPathConfig(&pConfig);
  3753. }
  3754. }
  3755. else if (pSegment)
  3756. {
  3757. // Addref so we can release later.
  3758. pSegment->AddRef();
  3759. }
  3760. else
  3761. {
  3762. // No Segment!
  3763. Trace(1,"Error: No segment - nothing to play!\n");
  3764. return E_FAIL;
  3765. }
  3766. if (dwFlags & DMUS_SEGF_DEFAULT )
  3767. {
  3768. DWORD dwResTemp;
  3769. pSegment->GetDefaultResolution( &dwResTemp );
  3770. dwFlags &= ~DMUS_SEGF_DEFAULT;
  3771. dwFlags |= dwResTemp;
  3772. }
  3773. // If the app wants an audiopath created dynamically from the segment, find it and use it.
  3774. // Note that this overrides an audiopath created from the song.
  3775. if (dwFlags & DMUS_SEGF_USE_AUDIOPATH)
  3776. {
  3777. IUnknown *pSegConfig;
  3778. if (SUCCEEDED(pSegment->GetAudioPathConfig(&pSegConfig)))
  3779. {
  3780. if (pConfig)
  3781. {
  3782. pConfig->Release();
  3783. }
  3784. pConfig = pSegConfig;
  3785. }
  3786. }
  3787. // If we got an audiopath config from the segment or song, use it.
  3788. if (pConfig)
  3789. {
  3790. IDirectMusicAudioPath *pNewPath;
  3791. if (SUCCEEDED(CreateAudioPath(pConfig,TRUE,&pNewPath)))
  3792. {
  3793. // Now, get the CAudioPath structure.
  3794. if (SUCCEEDED(pNewPath->QueryInterface(IID_CAudioPath,(void **) &pInternalPath)))
  3795. {
  3796. pAudioPath = pInternalPath;
  3797. }
  3798. pNewPath->Release();
  3799. }
  3800. else
  3801. {
  3802. pConfig->Release();
  3803. Trace(1,"Error: Embedded audiopath failed to create, segment will not play.\n");
  3804. return DMUS_E_NO_AUDIOPATH;
  3805. }
  3806. pConfig->Release();
  3807. }
  3808. if (pTransition)
  3809. {
  3810. pTransition->AddRef();
  3811. }
  3812. if ((dwFlags & DMUS_SEGF_SECONDARY) && (dwFlags & DMUS_SEGF_QUEUE))
  3813. {
  3814. // Can only queue if there's a segment to queue after.
  3815. if (pFrom)
  3816. {
  3817. CSegState *pSegFrom = NULL;
  3818. if (SUCCEEDED(pFrom->QueryInterface(IID_CSegState,(void **) &pSegFrom)))
  3819. {
  3820. // Calculate the time at which the preceding segment will stop.
  3821. MUSIC_TIME mtStartTime = pSegFrom->GetEndTime( pSegFrom->m_mtResolvedStart );
  3822. i64StartTime = mtStartTime;
  3823. dwFlags &= ~DMUS_SEGF_REFTIME;
  3824. pSegFrom->Release();
  3825. }
  3826. }
  3827. }
  3828. // If auto-transition is requested,
  3829. // get the transition template, if it exists,
  3830. // and compose a segment with it.
  3831. CSegment *pPlayAfter = NULL; // This will hold the second segment, if we end up with a transition.
  3832. DWORD dwFlagsAfter = dwFlags & (DMUS_SEGF_SECONDARY | DMUS_SEGF_CONTROL);
  3833. if ( dwFlags & DMUS_SEGF_AUTOTRANSITION )
  3834. {
  3835. // First, calculate the time to start the transition.
  3836. // Note: this will be done again later. We really need to fold this all together.
  3837. REFERENCE_TIME rtTime;
  3838. if (i64StartTime == 0)
  3839. {
  3840. GetQueueTime( &rtTime );
  3841. }
  3842. else if (dwFlags & DMUS_SEGF_REFTIME)
  3843. {
  3844. rtTime = i64StartTime;
  3845. }
  3846. else
  3847. {
  3848. MusicToReferenceTime((MUSIC_TIME) i64StartTime,&rtTime);
  3849. }
  3850. REFERENCE_TIME rtResolved;
  3851. GetResolvedTime(rtTime, &rtResolved,dwFlags);
  3852. MUSIC_TIME mtTime; // Actual time to start transition.
  3853. ReferenceToMusicTime(rtResolved,&mtTime);
  3854. CSegment *pPriorSeg = NULL;
  3855. // Find the segment that is active at transition time.
  3856. CSegState *pPriorState = GetSegmentForTransition(dwFlags,mtTime,pFrom);
  3857. if (pPriorState)
  3858. {
  3859. pPriorSeg = pPriorState->m_pSegment;
  3860. }
  3861. // If this is a song, use the id to get the transition.
  3862. if (pSong && !pTransition)
  3863. {
  3864. DMUS_IO_TRANSITION_DEF Transition;
  3865. // Now, find out what sort of transition is expected.
  3866. if (SUCCEEDED(pSong->GetTransitionSegment(pPriorSeg,pSegment,&Transition)))
  3867. {
  3868. if (Transition.dwTransitionID != DMUS_SONG_NOSEG)
  3869. {
  3870. if (S_OK == pSong->GetPlaySegment(Transition.dwTransitionID,&pTransition))
  3871. {
  3872. dwFlags = Transition.dwPlayFlags;
  3873. }
  3874. }
  3875. else
  3876. {
  3877. dwFlags = Transition.dwPlayFlags;
  3878. }
  3879. }
  3880. }
  3881. if (pTransition)
  3882. {
  3883. IDirectMusicSegment *pITransSegment = NULL;
  3884. if (pPriorState)
  3885. {
  3886. pTransition->Compose(mtTime - pPriorState->m_mtOffset, pPriorSeg, pSegment, &pITransSegment);
  3887. }
  3888. else
  3889. {
  3890. pTransition->Compose(0,pPriorSeg,pSegment,&pITransSegment);
  3891. }
  3892. // Now, if we successfully composed a transition segment, set it up to be the one we
  3893. // will play first. Later, we fill call PlaySegment() with pPlayAfter, to queue it
  3894. // to play after the transition.
  3895. if (pITransSegment)
  3896. {
  3897. pPlayAfter = pSegment;
  3898. pSegment = (CSegment *) pITransSegment;
  3899. }
  3900. }
  3901. }
  3902. if (pSegment)
  3903. {
  3904. CSegState *pSegState;
  3905. if (!pAudioPath)
  3906. {
  3907. pAudioPath = m_pDefaultAudioPath;
  3908. }
  3909. if (pAudioPath && !pAudioPath->IsActive())
  3910. {
  3911. Trace(1,"Error: Can not play segment on inactive audiopath\n");
  3912. hr = DMUS_E_AUDIOPATH_INACTIVE;
  3913. }
  3914. else if ((m_dwAudioPathMode != 1) && !pAudioPath)
  3915. {
  3916. Trace(1,"Error: No audiopath to play segment on.\n");
  3917. hr = DMUS_E_NO_AUDIOPATH;
  3918. }
  3919. else
  3920. {
  3921. if (ppSegmentState)
  3922. {
  3923. *ppSegmentState = NULL;
  3924. }
  3925. hr = PlayOneSegment(
  3926. pSegment,
  3927. dwFlags,
  3928. i64StartTime,
  3929. &pSegState,
  3930. pAudioPath);
  3931. if (SUCCEEDED(hr))
  3932. {
  3933. if (pFrom)
  3934. {
  3935. pSegState->m_fCanStop = FALSE;
  3936. StopEx(pFrom, pSegState->m_mtResolvedStart, 0);
  3937. pSegState->m_fCanStop = TRUE;
  3938. }
  3939. // If this was actually a transition segment, now we need to play the original segment!
  3940. if (pPlayAfter)
  3941. {
  3942. MUSIC_TIME mtStartTime = pSegState->GetEndTime(pSegState->m_mtResolvedStart );
  3943. pSegState->Release();
  3944. hr = PlayOneSegment(pPlayAfter,dwFlagsAfter,mtStartTime,&pSegState,pAudioPath);
  3945. }
  3946. if (SUCCEEDED(hr))
  3947. {
  3948. if (pSong)
  3949. {
  3950. pSegState->m_fSongMode = TRUE;
  3951. }
  3952. if (ppSegmentState)
  3953. {
  3954. *ppSegmentState = pSegState;
  3955. }
  3956. else
  3957. {
  3958. pSegState->Release();
  3959. }
  3960. }
  3961. }
  3962. }
  3963. }
  3964. else
  3965. {
  3966. // There never was a segment to play, not even a transition.
  3967. Trace(1,"Error: No segment to play.\n");
  3968. hr = E_INVALIDARG;
  3969. }
  3970. // Before leaving, reduce the reference counts on variables that have been addref'd.
  3971. if (pSegment)
  3972. {
  3973. pSegment->Release();
  3974. }
  3975. if (pTransition)
  3976. {
  3977. pTransition->Release();
  3978. }
  3979. if (pPlayAfter)
  3980. {
  3981. pPlayAfter->Release();
  3982. }
  3983. if (pInternalPath)
  3984. {
  3985. pInternalPath->Release();
  3986. }
  3987. return hr;
  3988. }
  3989. HRESULT STDMETHODCALLTYPE CPerformance::PlaySegment(
  3990. IDirectMusicSegment *pSegment,
  3991. DWORD dwFlags,
  3992. __int64 i64StartTime,
  3993. IDirectMusicSegmentState **ppSegmentState)
  3994. {
  3995. V_INAME(IDirectMusicPerformance::PlaySegment);
  3996. V_INTERFACE(pSegment);
  3997. V_PTRPTR_WRITE_OPT(ppSegmentState);
  3998. CSegment *pCSourceSegment = NULL;
  3999. if (SUCCEEDED(pSegment->QueryInterface(IID_CSegment,(void **) &pCSourceSegment)))
  4000. {
  4001. pCSourceSegment->Release();
  4002. }
  4003. else
  4004. {
  4005. Trace(1,"Error: Invalid segment object passed to PlaySegment(). Segment must be created using CLSID_DirectMusicSegment object.\n");
  4006. return E_POINTER;
  4007. }
  4008. return PlaySegmentInternal(pCSourceSegment,NULL,0,NULL,dwFlags,i64StartTime,ppSegmentState,NULL,NULL);
  4009. }
  4010. HRESULT STDMETHODCALLTYPE CPerformance::PlaySegmentEx(
  4011. IUnknown* pSource,
  4012. WCHAR *pwzSegmentName,
  4013. IUnknown* pTransition,
  4014. DWORD dwFlags,
  4015. __int64 i64StartTime,
  4016. IDirectMusicSegmentState** ppSegmentState,
  4017. IUnknown *pFrom,
  4018. IUnknown *pAudioPath)
  4019. {
  4020. V_INAME(IDirectMusicPerformance::PlaySegmentEx);
  4021. V_INTERFACE_OPT(pSource);
  4022. V_INTERFACE_OPT(pTransition);
  4023. V_PTRPTR_WRITE_OPT(ppSegmentState);
  4024. V_INTERFACE_OPT(pFrom);
  4025. V_INTERFACE_OPT(pAudioPath);
  4026. CSegment *pCSourceSegment = NULL;
  4027. CSong *pCSourceSong = NULL;
  4028. CSegment *pCTransition = NULL;
  4029. CAudioPath *pCAudioPath = NULL;
  4030. // TraceI(0,"Playing %lx at time %ld, flags %lx, Transition %lx\n",pSource,(long)i64StartTime,dwFlags,pTransition);
  4031. // We may not have a source segment in the special case of transitioning from NULL.
  4032. if (!pSource && !pTransition)
  4033. {
  4034. Trace(1,"Error: Must pass either a segment or transition segment to PlaySegmentEx()\n");
  4035. return E_POINTER;
  4036. }
  4037. if (pSource)
  4038. {
  4039. // Figure out if we have a source song or segment and get the internal representations.
  4040. if (SUCCEEDED(pSource->QueryInterface(IID_CSegment,(void **) &pCSourceSegment)))
  4041. {
  4042. pCSourceSegment->Release();
  4043. }
  4044. else if (SUCCEEDED(pSource->QueryInterface(IID_CSong,(void **) &pCSourceSong)))
  4045. {
  4046. pCSourceSong->Release();
  4047. }
  4048. else
  4049. {
  4050. Trace(1,"Error: Invalid segment or song passed to PlaySegmentEx().\n");
  4051. return E_POINTER;
  4052. }
  4053. }
  4054. // If we have a transition segment, get the CSegment representation.
  4055. if (pTransition)
  4056. {
  4057. if (SUCCEEDED(pTransition->QueryInterface(IID_CSegment,(void **) &pCTransition)))
  4058. {
  4059. pCTransition->Release();
  4060. }
  4061. else
  4062. {
  4063. Trace(1,"Error: Invalid transition passed to PlaySegmentEx().\n");
  4064. return E_POINTER;
  4065. }
  4066. }
  4067. if (pAudioPath)
  4068. {
  4069. if (SUCCEEDED(pAudioPath->QueryInterface(IID_CAudioPath,(void **) &pCAudioPath)))
  4070. {
  4071. pCAudioPath->Release();
  4072. }
  4073. else
  4074. {
  4075. Trace(1,"Error: Invalid audiopath passed to PlaySegmentEx().\n");
  4076. return E_POINTER;
  4077. }
  4078. }
  4079. return PlaySegmentInternal(pCSourceSegment,pCSourceSong,pwzSegmentName,
  4080. pCTransition,dwFlags,i64StartTime,
  4081. ppSegmentState,pFrom,
  4082. pCAudioPath);
  4083. }
  4084. STDMETHODIMP CPerformance::SetDefaultAudioPath(IDirectMusicAudioPath *pAudioPath)
  4085. {
  4086. V_INAME(IDirectMusicPerformance::SetDefaultAudioPath);
  4087. V_INTERFACE_OPT(pAudioPath);
  4088. if (m_dwAudioPathMode == 0)
  4089. {
  4090. Trace(1,"Error: Performance not initialized.\n");
  4091. return DMUS_E_NOT_INIT;
  4092. }
  4093. if (m_dwAudioPathMode == 1)
  4094. {
  4095. Trace(1,"Error: Performance initialized not to support Audiopaths.\n");
  4096. return DMUS_E_AUDIOPATHS_NOT_VALID;
  4097. }
  4098. CAudioPath *pCPath = NULL;
  4099. if (pAudioPath)
  4100. {
  4101. if (SUCCEEDED(pAudioPath->QueryInterface(IID_CAudioPath,(void **) &pCPath)))
  4102. {
  4103. pCPath->Release();
  4104. if (!m_AudioPathList.IsMember(pCPath))
  4105. {
  4106. // This is not a legal audiopath, since it wasn't created by this performance.
  4107. Trace(1,"Error: Invalid audiopath - not created by this Performance.\n");
  4108. return E_INVALIDARG;
  4109. }
  4110. if (pCPath->NoPorts())
  4111. {
  4112. // This is an audiopath that doesn't have any port configurations.
  4113. // For example, it might be environmental reverb.
  4114. Trace(1,"Error: Failure setting default audiopath - does not have any ports, so can not be played on.\n");
  4115. return DMUS_E_AUDIOPATH_NOPORT;
  4116. }
  4117. }
  4118. else
  4119. {
  4120. // This is not a legal audiopath object at all.
  4121. Trace(1,"Error: Invalid audiopath - not created by call to Performance->CreateAudioPath().\n");
  4122. return E_INVALIDARG;
  4123. }
  4124. }
  4125. if (m_pDefaultAudioPath)
  4126. {
  4127. m_pDefaultAudioPath->Release();
  4128. m_pDefaultAudioPath = NULL;
  4129. }
  4130. m_pDefaultAudioPath = pCPath;
  4131. if (pCPath)
  4132. {
  4133. pCPath->AddRef();
  4134. pCPath->Activate(TRUE);
  4135. }
  4136. return S_OK;
  4137. }
  4138. STDMETHODIMP CPerformance::GetDefaultAudioPath(IDirectMusicAudioPath **ppAudioPath)
  4139. {
  4140. V_INAME(IDirectMusicPerformance::GetDefaultAudioPath);
  4141. V_PTRPTR_WRITE(ppAudioPath);
  4142. if (m_dwAudioPathMode == 0)
  4143. {
  4144. Trace(1,"Error: Performance not initialized.\n");
  4145. return DMUS_E_NOT_INIT;
  4146. }
  4147. if (m_dwAudioPathMode == 1)
  4148. {
  4149. Trace(1,"Error: Performance was initialized not to support audiopaths.\n");
  4150. return DMUS_E_AUDIOPATHS_NOT_VALID;
  4151. }
  4152. if (m_pDefaultAudioPath)
  4153. {
  4154. *ppAudioPath = (IDirectMusicAudioPath *) m_pDefaultAudioPath;
  4155. m_pDefaultAudioPath->AddRef();
  4156. return S_OK;
  4157. }
  4158. Trace(3,"Warning: No default audiopath\n");
  4159. return DMUS_E_NOT_FOUND;
  4160. }
  4161. HRESULT STDMETHODCALLTYPE CPerformance::CreateAudioPath( IUnknown *pSourceConfig,
  4162. BOOL fActivate,
  4163. IDirectMusicAudioPath **ppNewPath)
  4164. {
  4165. V_INAME(IDirectMusicPerformance::CreateAudioPath);
  4166. V_INTERFACE(pSourceConfig);
  4167. V_PTRPTR_WRITE_OPT(ppNewPath);
  4168. if (m_dwAudioPathMode == 0)
  4169. {
  4170. Trace(1,"Error: Performance not initialized.\n");
  4171. return DMUS_E_NOT_INIT;
  4172. }
  4173. if (m_dwAudioPathMode == 1)
  4174. {
  4175. Trace(1,"Error: Performance not initialized to support audiopaths (must use InitAudio.)\n");
  4176. return DMUS_E_AUDIOPATHS_NOT_VALID;
  4177. }
  4178. HRESULT hr = E_OUTOFMEMORY;
  4179. CAudioPath *pPath = new CAudioPath;
  4180. if (pPath)
  4181. {
  4182. hr = pPath->Init(pSourceConfig,this);
  4183. if (SUCCEEDED(hr) && fActivate)
  4184. {
  4185. hr = pPath->Activate(TRUE);
  4186. #ifdef DBG
  4187. if (FAILED(hr))
  4188. {
  4189. Trace(1,"Error: Audiopath creation failed because one or more buffers could not be activated.\n");
  4190. }
  4191. #endif
  4192. }
  4193. if (SUCCEEDED(hr))
  4194. {
  4195. hr = pPath->QueryInterface(IID_IDirectMusicAudioPath,(void **) ppNewPath);
  4196. }
  4197. else
  4198. {
  4199. delete pPath;
  4200. }
  4201. }
  4202. return hr;
  4203. }
  4204. STDMETHODIMP CPerformance::CreateStandardAudioPath(DWORD dwType,
  4205. DWORD dwPChannelCount,
  4206. BOOL fActivate,
  4207. IDirectMusicAudioPath **ppNewPath)
  4208. {
  4209. V_INAME(IDirectMusicPerformance::CreateStandardAudioPath);
  4210. V_PTRPTR_WRITE_OPT(ppNewPath);
  4211. HRESULT hr = S_OK;
  4212. if (m_dwAudioPathMode == 2)
  4213. {
  4214. if ((dwType <= DMUS_APATH_DYNAMIC_STEREO) && (dwType >= DMUS_APATH_DYNAMIC_3D)
  4215. || (dwType == DMUS_APATH_SHARED_STEREOPLUSREVERB))
  4216. {
  4217. if (!(m_AudioParams.dwFeatures & DMUS_AUDIOF_BUFFERS))
  4218. {
  4219. Trace(4,"Warning: Creating a standard audiopath without buffers - InitAudio specified no buffer support.\n");
  4220. // If the default synth doesn't support buffers, then create a simple port with no buffers.
  4221. dwType = 0;
  4222. }
  4223. CAudioPathConfig *pConfig = CAudioPathConfig::CreateStandardConfig(dwType,dwPChannelCount,m_AudioParams.dwSampleRate);
  4224. if (pConfig)
  4225. {
  4226. hr = CreateAudioPath((IPersistStream *) pConfig,fActivate,ppNewPath);
  4227. pConfig->Release();
  4228. }
  4229. else
  4230. {
  4231. // CreateStandardConfig only returns NULL if we've run out of memory.
  4232. hr = E_OUTOFMEMORY;
  4233. }
  4234. }
  4235. else
  4236. {
  4237. Trace(1,"Error: %ld is not a valid predefined audiopath.\n",dwType);
  4238. hr = E_INVALIDARG;
  4239. }
  4240. }
  4241. else
  4242. {
  4243. Trace(1,"Error: Performance not initialized to support audiopaths.\n");
  4244. hr = DMUS_E_AUDIOPATHS_NOT_VALID;
  4245. }
  4246. return hr;
  4247. }
  4248. // Stop the segment state at mtTime. If NULL, stop all.
  4249. void CPerformance::DoStop( CSegState* pSegState, MUSIC_TIME mtTime,
  4250. BOOL fInvalidate)
  4251. {
  4252. HRESULT hrAbort = S_OK;
  4253. DWORD dwCount;
  4254. if( NULL == pSegState ) return;
  4255. EnterCriticalSection(&m_SegmentCrSec);
  4256. CSegStateList *pSourceList = NULL;
  4257. CSegStateList *pDestList = NULL;
  4258. CSegState *pNode = NULL;
  4259. // Mark the length of the segstate to be only as far as it played
  4260. // to keep GetParam() from accessing the unplayed portion.
  4261. if (pSegState)
  4262. {
  4263. if (mtTime < pSegState->m_mtEndTime)
  4264. {
  4265. pSegState->m_mtLength = mtTime - pSegState->m_mtResolvedStart +
  4266. pSegState->m_mtStartPoint;
  4267. if (pSegState->m_mtLength < 0)
  4268. {
  4269. pSegState->m_mtLength = 0;
  4270. }
  4271. // Make endtime one greater than mtTime so Abort notification will still happen.
  4272. pSegState->m_mtEndTime = mtTime + 1;
  4273. }
  4274. }
  4275. RecalcTempoMap(pSegState,mtTime);
  4276. // check each play queue
  4277. for (dwCount = SQ_PRI_PLAY; dwCount <= SQ_SEC_PLAY; dwCount++)
  4278. {
  4279. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext())
  4280. {
  4281. if( pNode == pSegState )
  4282. {
  4283. // we want to move this to the approprate done queue
  4284. pDestList = &m_SegStateQueues[SQ_PRI_DONE - SQ_PRI_PLAY + dwCount];
  4285. pSourceList = &m_SegStateQueues[dwCount];
  4286. if ((dwCount == SQ_PRI_PLAY) && (m_SegStateQueues[SQ_PRI_PLAY].GetCount() == 1))
  4287. {
  4288. if (m_dwVersion >= 8)
  4289. {
  4290. MUSIC_TIME mtNow;
  4291. GetTime( NULL, &mtNow );
  4292. GenerateNotification( DMUS_NOTIFICATION_MUSICALMOSTEND, mtNow, pSegState );
  4293. }
  4294. }
  4295. dwCount = SQ_SEC_PLAY; // Force out of outer loop.
  4296. break;
  4297. }
  4298. }
  4299. }
  4300. if (!pNode)
  4301. {
  4302. // check each done queue
  4303. for (dwCount = SQ_PRI_DONE; dwCount <= SQ_SEC_DONE; dwCount++)
  4304. {
  4305. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext())
  4306. {
  4307. if( pNode == pSegState )
  4308. {
  4309. pSourceList = &m_SegStateQueues[dwCount];
  4310. dwCount = SQ_SEC_DONE; // Force out of outer loop.
  4311. break;
  4312. }
  4313. }
  4314. }
  4315. }
  4316. if( pNode && pSourceList)
  4317. {
  4318. REFERENCE_TIME rtTime;
  4319. MusicToReferenceTime(mtTime,&rtTime);
  4320. if( pNode->m_mtLastPlayed >= mtTime )
  4321. {
  4322. pNode->Flush( mtTime );
  4323. pNode->m_mtLastPlayed = mtTime; // must set this to indicate it only played until then
  4324. pNode->m_rtLastPlayed = rtTime;
  4325. }
  4326. if( fInvalidate )
  4327. {
  4328. if( pNode->m_dwPlaySegFlags & DMUS_SEGF_CONTROL )
  4329. {
  4330. Invalidate( mtTime, 0 ); // must call Invalidate before AbortPlay so we don't
  4331. // invalidate the abort notification
  4332. }
  4333. else if ( !(pNode->m_dwPlaySegFlags & DMUS_SEGF_SECONDARY ))
  4334. {
  4335. // If this is a primary segment, kill the tempo map.
  4336. FlushEventQueue( 0, &m_TempoMap, rtTime, rtTime, FALSE );
  4337. }
  4338. }
  4339. hrAbort = pNode->AbortPlay( mtTime, FALSE );
  4340. if( pNode->m_dwPlaySegFlags & DMUS_SEGF_CONTROL )
  4341. {
  4342. pSourceList->Remove(pNode);
  4343. m_ShutDownQueue.Insert(pNode); // we're guaranteed to never need this again
  4344. // set dirty flags on all other segments
  4345. for (dwCount = SQ_PRI_PLAY; dwCount <= SQ_SEC_PLAY; dwCount++)
  4346. {
  4347. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext() )
  4348. {
  4349. if( pNode->m_fStartedPlay )
  4350. {
  4351. pNode->m_dwPlayTrackFlags |= DMUS_TRACKF_DIRTY;
  4352. }
  4353. }
  4354. }
  4355. }
  4356. else if( pDestList )
  4357. {
  4358. pSourceList->Remove(pNode);
  4359. pDestList->Insert(pNode);
  4360. }
  4361. }
  4362. else
  4363. {
  4364. // check the wait lists.
  4365. for (dwCount = SQ_PRI_WAIT; dwCount <= SQ_SEC_WAIT; dwCount++)
  4366. {
  4367. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext() )
  4368. {
  4369. if( pNode == pSegState )
  4370. {
  4371. hrAbort = pNode->AbortPlay( mtTime, FALSE );
  4372. m_SegStateQueues[dwCount].Remove(pNode);
  4373. RecalcTempoMap(pNode, mtTime);
  4374. m_ShutDownQueue.Insert(pNode);
  4375. break;
  4376. }
  4377. }
  4378. }
  4379. }
  4380. // if there aren't any more segments to play, send a Music Stopped
  4381. // notification
  4382. if( m_SegStateQueues[SQ_PRI_PLAY].IsEmpty() && m_SegStateQueues[SQ_SEC_PLAY].IsEmpty() &&
  4383. m_SegStateQueues[SQ_PRI_WAIT].IsEmpty() && m_SegStateQueues[SQ_SEC_WAIT].IsEmpty() &&
  4384. m_SegStateQueues[SQ_CON_PLAY].IsEmpty() && m_SegStateQueues[SQ_CON_WAIT].IsEmpty())
  4385. {
  4386. m_fMusicStopped = TRUE;
  4387. // S_FALSE means we tried to abort this segstate, but it's already been aborted
  4388. if (hrAbort != S_FALSE)
  4389. {
  4390. GenerateNotification( DMUS_NOTIFICATION_MUSICSTOPPED, mtTime, NULL );
  4391. }
  4392. }
  4393. LeaveCriticalSection(&m_SegmentCrSec);
  4394. }
  4395. // Stop all segment states based off of the segment.
  4396. void CPerformance::DoStop( CSegment* pSeg, MUSIC_TIME mtTime, BOOL fInvalidate )
  4397. {
  4398. DWORD dwCount;
  4399. CSegState* pNode;
  4400. CSegState* pNext;
  4401. EnterCriticalSection(&m_SegmentCrSec);
  4402. // find all seg pSegStates based off this segment that have played through time mtTime
  4403. // if pSeg is NULL, go through all of the segment lists. Flush any
  4404. // segment that played through time mtTime. Move any active segments
  4405. // into past lists.
  4406. if( pSeg )
  4407. {
  4408. for (dwCount = 0; dwCount < SQ_COUNT; dwCount++)
  4409. {
  4410. pNode = m_SegStateQueues[dwCount].GetHead();
  4411. while( pNode )
  4412. {
  4413. pNext = pNode->GetNext();
  4414. if( pNode->m_pSegment == pSeg )
  4415. {
  4416. if (IsDoneQueue(dwCount))
  4417. {
  4418. if (pNode->m_mtLastPlayed >= mtTime)
  4419. {
  4420. DoStop( pNode, mtTime, fInvalidate );
  4421. }
  4422. }
  4423. else
  4424. {
  4425. DoStop( pNode, mtTime, fInvalidate );
  4426. }
  4427. }
  4428. pNode = pNext;
  4429. }
  4430. }
  4431. }
  4432. else // pSeg is NULL, stop everything.
  4433. {
  4434. // go ahead and flush the event queues
  4435. EnterCriticalSection(&m_PipelineCrSec);
  4436. FlushMainEventQueues( 0, mtTime, mtTime, FALSE );
  4437. LeaveCriticalSection(&m_PipelineCrSec);
  4438. // clear out the wait lists
  4439. for (dwCount = SQ_PRI_WAIT; dwCount <= SQ_SEC_WAIT; dwCount++)
  4440. {
  4441. while (pNode = m_SegStateQueues[dwCount].GetHead())
  4442. {
  4443. pNode->AbortPlay( mtTime, FALSE );
  4444. m_SegStateQueues[dwCount].RemoveHead();
  4445. m_ShutDownQueue.Insert(pNode);
  4446. }
  4447. }
  4448. // stop any segment that is currently playing.
  4449. for (dwCount = SQ_PRI_DONE; dwCount <= SQ_SEC_DONE; dwCount++)
  4450. {
  4451. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext() )
  4452. {
  4453. if( pNode->m_mtLastPlayed >= mtTime )
  4454. {
  4455. DoStop( pNode, mtTime, fInvalidate );
  4456. }
  4457. }
  4458. }
  4459. for (dwCount = SQ_PRI_PLAY; dwCount <= SQ_SEC_PLAY; dwCount++)
  4460. {
  4461. while( m_SegStateQueues[dwCount].GetHead() )
  4462. {
  4463. DoStop( m_SegStateQueues[dwCount].GetHead(), mtTime, fInvalidate );
  4464. }
  4465. }
  4466. // reset controllers and force all notes off.
  4467. ResetAllControllers( GetLatency() );
  4468. }
  4469. LeaveCriticalSection(&m_SegmentCrSec);
  4470. }
  4471. STDMETHODIMP CPerformance::StopEx(IUnknown *pObjectToStop,__int64 i64StopTime,DWORD dwFlags)
  4472. {
  4473. V_INAME(IDirectMusicPerformance::StopEx);
  4474. V_INTERFACE_OPT(pObjectToStop);
  4475. HRESULT hr = E_INVALIDARG;
  4476. IDirectMusicSegmentState *pState;
  4477. IDirectMusicSegment *pSegment;
  4478. CSong *pSong;
  4479. CAudioPath *pAudioPath;
  4480. if (m_dwAudioPathMode == 0)
  4481. {
  4482. Trace(1,"Error: Performance not initialized.\n");
  4483. return DMUS_E_NOT_INIT;
  4484. }
  4485. TraceI(0,"StopExing %lx at time %ld, flags %lx\n",pObjectToStop,(long)i64StopTime,dwFlags);
  4486. if (pObjectToStop == NULL)
  4487. {
  4488. return Stop(NULL,NULL,(MUSIC_TIME)i64StopTime,dwFlags);
  4489. }
  4490. if (dwFlags & DMUS_SEGF_AUTOTRANSITION)
  4491. {
  4492. // I this is an autotransition, it will only work if the currently playing segment in question
  4493. // is a member of a song. So, check the segstate, segment, song, and audiopath
  4494. // to find the segstate. And, if found, see if it is part of a song. If so,
  4495. // then go ahead and do the transition.
  4496. EnterCriticalSection(&m_SegmentCrSec);
  4497. BOOL fTransition = FALSE;
  4498. dwFlags &= ~DMUS_SEGF_AUTOTRANSITION;
  4499. CSegState *pCState = NULL;
  4500. // First, see if this is a segstate.
  4501. HRESULT hrTemp = pObjectToStop->QueryInterface(IID_CSegState,(void **)&pCState);
  4502. if (FAILED(hrTemp))
  4503. {
  4504. // Segstate failed. Is this a Song? If so, find the first correlating segstate.
  4505. CSong *pCSong = NULL;
  4506. CAudioPath *pCAudioPath = NULL;
  4507. CSegment *pCSegment = NULL;
  4508. hrTemp = pObjectToStop->QueryInterface(IID_CSong,(void **)&pCSong);
  4509. if (FAILED(hrTemp))
  4510. {
  4511. hrTemp = pObjectToStop->QueryInterface(IID_CSegment,(void **)&pCSegment);
  4512. }
  4513. if (FAILED(hrTemp))
  4514. {
  4515. hrTemp = pObjectToStop->QueryInterface(IID_CAudioPath,(void **)&pCAudioPath);
  4516. }
  4517. if (SUCCEEDED(hrTemp))
  4518. {
  4519. CSegState *pNode;
  4520. DWORD dwCount;
  4521. for (dwCount = SQ_PRI_WAIT; dwCount <= SQ_SEC_DONE; dwCount++)
  4522. {
  4523. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext() )
  4524. {
  4525. if (pNode->m_fCanStop)
  4526. {
  4527. // Can only do this if the segstate ultimately points to a song.
  4528. if (pNode->m_pSegment && pNode->m_pSegment->m_pSong)
  4529. {
  4530. if ((pNode->m_pSegment == pCSegment) ||
  4531. (pNode->m_pSegment->m_pSong == pCSong) ||
  4532. (pCAudioPath && (pNode->m_pAudioPath == pCAudioPath)))
  4533. {
  4534. pCState = pNode;
  4535. pCState->AddRef();
  4536. break;
  4537. }
  4538. }
  4539. }
  4540. }
  4541. if (pCState) break;
  4542. }
  4543. }
  4544. if (pCSong) pCSong->Release();
  4545. else if (pCAudioPath) pCAudioPath->Release();
  4546. else if (pCSegment) pCSegment->Release();
  4547. }
  4548. if (pCState)
  4549. {
  4550. CSegment *pPriorSeg = pCState->m_pSegment;
  4551. if (pPriorSeg)
  4552. {
  4553. pSong = pPriorSeg->m_pSong;
  4554. if (pSong)
  4555. {
  4556. // If this is an autotransition, compose a transition segment from the
  4557. // current position in the song and play it.
  4558. // This will, in turn, call stop on the song, so we don't need to do it here.
  4559. // First, calculate the time to start the transition.
  4560. REFERENCE_TIME rtTime;
  4561. if (i64StopTime == 0)
  4562. {
  4563. GetQueueTime( &rtTime );
  4564. }
  4565. else if (dwFlags & DMUS_SEGF_REFTIME)
  4566. {
  4567. rtTime = i64StopTime;
  4568. }
  4569. else
  4570. {
  4571. MusicToReferenceTime((MUSIC_TIME) i64StopTime,&rtTime);
  4572. }
  4573. REFERENCE_TIME rtResolved;
  4574. GetResolvedTime(rtTime, &rtResolved,dwFlags);
  4575. MUSIC_TIME mtTime; // Actual time to start transition.
  4576. ReferenceToMusicTime(rtResolved,&mtTime);
  4577. CSegment *pTransition = NULL;
  4578. // Now, get the transition.
  4579. DMUS_IO_TRANSITION_DEF Transition;
  4580. if (SUCCEEDED(pSong->GetTransitionSegment(pPriorSeg,NULL,&Transition)))
  4581. {
  4582. if (Transition.dwTransitionID != DMUS_SONG_NOSEG)
  4583. {
  4584. if (S_OK == pSong->GetPlaySegment(Transition.dwTransitionID,&pTransition))
  4585. {
  4586. dwFlags = Transition.dwPlayFlags;
  4587. }
  4588. }
  4589. }
  4590. if (pTransition)
  4591. {
  4592. IDirectMusicSegment *pITransSegment = NULL;
  4593. pTransition->Compose(mtTime - pCState->m_mtOffset, pPriorSeg, NULL, &pITransSegment);
  4594. // Now, if we successfully composed a transition segment, set it up to be the one we
  4595. // will play first. Later, we fill call PlaySegment() with pPlayAfter, to queue it
  4596. // to play after the transition.
  4597. if (pITransSegment)
  4598. {
  4599. hr = PlaySegmentEx(pITransSegment,NULL,NULL,dwFlags,i64StopTime,NULL,(IDirectMusicSegmentState *)pCState,NULL);
  4600. pITransSegment->Release();
  4601. fTransition = TRUE;
  4602. }
  4603. pTransition->Release();
  4604. }
  4605. }
  4606. }
  4607. pCState->Release();
  4608. }
  4609. LeaveCriticalSection(&m_SegmentCrSec);
  4610. if (fTransition)
  4611. {
  4612. return hr;
  4613. }
  4614. }
  4615. if (SUCCEEDED(pObjectToStop->QueryInterface(IID_IDirectMusicSegmentState,(void **) &pState)))
  4616. {
  4617. hr = Stop(NULL,pState,(MUSIC_TIME)i64StopTime,dwFlags);
  4618. pState->Release();
  4619. }
  4620. else if (SUCCEEDED(pObjectToStop->QueryInterface(IID_IDirectMusicSegment,(void **) &pSegment)))
  4621. {
  4622. hr = Stop(pSegment,NULL,(MUSIC_TIME)i64StopTime,dwFlags);
  4623. pSegment->Release();
  4624. }
  4625. else if (SUCCEEDED(pObjectToStop->QueryInterface(IID_CAudioPath,(void **) &pAudioPath)))
  4626. {
  4627. pAudioPath->Release();
  4628. EnterCriticalSection(&m_SegmentCrSec);
  4629. CSegState *pNode;
  4630. DWORD dwCount;
  4631. for (dwCount = SQ_PRI_WAIT; dwCount <= SQ_SEC_DONE; dwCount++)
  4632. {
  4633. CSegState *pNext;
  4634. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNext )
  4635. {
  4636. pNext = pNode->GetNext();
  4637. if (pNode->m_fCanStop && (pNode->m_pAudioPath == pAudioPath))
  4638. {
  4639. hr = Stop(NULL,(IDirectMusicSegmentState *)pNode,(MUSIC_TIME)i64StopTime,dwFlags);
  4640. }
  4641. }
  4642. }
  4643. LeaveCriticalSection(&m_SegmentCrSec);
  4644. }
  4645. else if (SUCCEEDED(pObjectToStop->QueryInterface(IID_CSong,(void **) &pSong)))
  4646. {
  4647. pSong->Release();
  4648. EnterCriticalSection(&m_SegmentCrSec);
  4649. CSegState *pNode;
  4650. DWORD dwCount;
  4651. for (dwCount = SQ_PRI_WAIT; dwCount <= SQ_SEC_DONE; dwCount++)
  4652. {
  4653. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext() )
  4654. {
  4655. if (pNode->m_fCanStop && pNode->m_pSegment && (pNode->m_pSegment->m_pSong == pSong))
  4656. {
  4657. hr = Stop(NULL,(IDirectMusicSegmentState *)pNode,(MUSIC_TIME)i64StopTime,dwFlags);
  4658. }
  4659. }
  4660. }
  4661. LeaveCriticalSection(&m_SegmentCrSec);
  4662. }
  4663. return hr;
  4664. }
  4665. HRESULT STDMETHODCALLTYPE CPerformance::Stop(
  4666. IDirectMusicSegment *pISegment, // @parm The Segment to stop playing. All SegmentState's based upon this Segment are
  4667. // stopped playing at time <p mtTime>.
  4668. IDirectMusicSegmentState *pISegmentState, // @parm The SegmentState to stop playing.
  4669. MUSIC_TIME mtTime, // @parm The time at which to stop the Segments, Segment State, or everything. If
  4670. // this time is in the past, stop everything right away. Therefore, a value of
  4671. // 0 indicates stop everything NOW.
  4672. DWORD dwFlags) // @parm Flag that indicates whether we should stop immediately at time <p mtTime>,
  4673. // or on the grid, measure, or beat following <p mtTime>. This is only valid in
  4674. // relation to the currently playing primary segment. (For flag descriptions,
  4675. // see <t DMPLAYSEGFLAGS>.)
  4676. {
  4677. V_INAME(IDirectMusicPerformance::Stop);
  4678. V_INTERFACE_OPT(pISegment);
  4679. V_INTERFACE_OPT(pISegmentState);
  4680. EnterCriticalSection(&m_SegmentCrSec);
  4681. CSegment *pSegment = NULL;
  4682. CSegState *pSegmentState = NULL;
  4683. TraceI(0,"Stopping Segment %lx, SegState %lx at time %ld, flags %lx\n",pISegment,pISegmentState,mtTime,dwFlags);
  4684. if (pISegmentState)
  4685. {
  4686. if (SUCCEEDED(pISegmentState->QueryInterface(IID_CSegState,(void **)&pSegmentState)))
  4687. {
  4688. pISegmentState->Release();
  4689. }
  4690. else
  4691. {
  4692. Trace(1,"Error: Pointer in SegState parameter to Stop() is invalid.\n");
  4693. return E_INVALIDARG;
  4694. }
  4695. }
  4696. if (pISegment)
  4697. {
  4698. if (SUCCEEDED(pISegment->QueryInterface(IID_CSegment,(void **)&pSegment)))
  4699. {
  4700. pISegment->Release();
  4701. }
  4702. else
  4703. {
  4704. Trace(1,"Error: Pointer in Segment parameter to Stop() is invalid.\n");
  4705. return E_INVALIDARG;
  4706. }
  4707. }
  4708. if (pSegmentState)
  4709. {
  4710. // If this is the starting segstate from a playing song, find the
  4711. // current active segstate within that song.
  4712. // The current active segstate keeps a pointer to
  4713. // this segstate.
  4714. if (pSegmentState->m_fSongMode)
  4715. {
  4716. CSegState* pNode;
  4717. DWORD dwCount;
  4718. for (dwCount = 0; dwCount < SQ_COUNT; dwCount++)
  4719. {
  4720. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext() )
  4721. {
  4722. if (pNode->m_pSongSegState == pSegmentState)
  4723. {
  4724. pSegmentState = pNode;
  4725. dwCount = SQ_COUNT;
  4726. break;
  4727. }
  4728. }
  4729. }
  4730. }
  4731. }
  4732. if( dwFlags & DMUS_SEGF_DEFAULT )
  4733. {
  4734. DWORD dwNewRes = 0;
  4735. if( pSegment )
  4736. {
  4737. pSegment->GetDefaultResolution( &dwNewRes );
  4738. }
  4739. else if( pSegmentState )
  4740. {
  4741. IDirectMusicSegment* pSegTemp;
  4742. if( SUCCEEDED( pSegmentState->GetSegment( &pSegTemp ) ) )
  4743. {
  4744. pSegTemp->GetDefaultResolution( &dwNewRes );
  4745. pSegTemp->Release();
  4746. }
  4747. else
  4748. {
  4749. dwNewRes = 0;
  4750. }
  4751. }
  4752. else
  4753. {
  4754. dwNewRes = 0;
  4755. }
  4756. dwFlags |= dwNewRes;
  4757. dwFlags &= ~DMUS_SEGF_DEFAULT;
  4758. }
  4759. // Make sure mtTime is greater or equal to QueueTime, which is the last time notes were
  4760. // queued down (or latency time, whichever is later) so we can stop everything after it.
  4761. MUSIC_TIME mtLatency;
  4762. REFERENCE_TIME rtQueueTime;
  4763. GetQueueTime( &rtQueueTime );
  4764. ReferenceToMusicTime( rtQueueTime, &mtLatency );
  4765. if( mtTime < mtLatency ) mtTime = mtLatency;
  4766. // Resolve the time according to the resolution
  4767. mtTime = ResolveTime( mtTime, dwFlags, NULL );
  4768. // if mtTime is less than the current transported time, we can take
  4769. // care of the Stop now. Otherwise, we need to cue a Stop PMsg and
  4770. // take care of it at QUEUE time.
  4771. if( mtTime <= m_mtTransported )
  4772. {
  4773. if( pSegmentState )
  4774. {
  4775. DoStop( pSegmentState, mtTime, TRUE );
  4776. if( pSegment )
  4777. {
  4778. DoStop( pSegment, mtTime, TRUE );
  4779. }
  4780. }
  4781. else
  4782. {
  4783. DoStop( pSegment, mtTime, TRUE );
  4784. }
  4785. }
  4786. else
  4787. {
  4788. // find and mark the segment and/or segment state to not play beyond
  4789. // the stop point.
  4790. CSegState* pNode;
  4791. DWORD dwCount;
  4792. for (dwCount = SQ_PRI_PLAY; dwCount <= SQ_SEC_PLAY; dwCount++)
  4793. {
  4794. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext() )
  4795. {
  4796. if( (pNode->m_pSegment == pSegment) ||
  4797. (pNode == pSegmentState) )
  4798. {
  4799. if (pNode->m_fCanStop)
  4800. {
  4801. pNode->m_mtStopTime = mtTime;
  4802. // Make sure GetParams ignore the rest of the segment from now on.
  4803. if (mtTime < pNode->m_mtEndTime)
  4804. {
  4805. pNode->m_mtLength = mtTime - pNode->m_mtResolvedStart +
  4806. pNode->m_mtStartPoint;
  4807. if (pNode->m_mtLength < 0)
  4808. {
  4809. pNode->m_mtLength = 0;
  4810. }
  4811. // Make endtime one greater than mtTime so Abort notification will still happen.
  4812. pNode->m_mtEndTime = mtTime + 1;
  4813. }
  4814. // Force the tempo map to be recalculated IF this has a tempo track.
  4815. RecalcTempoMap(pNode,mtTime);
  4816. }
  4817. }
  4818. }
  4819. }
  4820. // create a Stop PMsg and cue it for QUEUE time
  4821. // I've removed this to fix bugs. A stop message at queue time,
  4822. // if in a controlling or primary segment, results in invalidation.
  4823. // This is particularily bad for controlling segments.
  4824. // Can't figure out why we even need the stop message...
  4825. /* DMUS_PMSG* pPMsg;
  4826. if( SUCCEEDED( AllocPMsg( sizeof(DMUS_PMSG), &pPMsg )))
  4827. {
  4828. pPMsg->dwType = DMUS_PMSGT_STOP;
  4829. pPMsg->mtTime = mtTime;
  4830. pPMsg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE;
  4831. if( pSegment )
  4832. {
  4833. pSegment->QueryInterface( IID_IUnknown, (void**)&pPMsg->punkUser );
  4834. if( pSegmentState )
  4835. {
  4836. // if there is also a segment state pointer, we need to create two
  4837. // pmsg's
  4838. DMUS_PMSG* pPMsg2;
  4839. if( SUCCEEDED( AllocPMsg( sizeof(DMUS_PMSG), &pPMsg2 )))
  4840. {
  4841. pPMsg2->dwType = DMUS_PMSGT_STOP;
  4842. pPMsg2->mtTime = mtTime;
  4843. pPMsg2->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE;
  4844. pSegmentState->QueryInterface( IID_IUnknown, (void**)&pPMsg2->punkUser );
  4845. pPMsg2->pTool = this;
  4846. AddRef();
  4847. if(FAILED(SendPMsg( pPMsg2 )))
  4848. {
  4849. FreePMsg(pPMsg2);
  4850. }
  4851. }
  4852. }
  4853. }
  4854. else if( pSegmentState )
  4855. {
  4856. pSegmentState->QueryInterface( IID_IUnknown, (void**)&pPMsg->punkUser );
  4857. }
  4858. pPMsg->pTool = this;
  4859. AddRef();
  4860. if(FAILED(SendPMsg( pPMsg )))
  4861. {
  4862. FreePMsg(pPMsg);
  4863. }
  4864. }*/
  4865. }
  4866. LeaveCriticalSection(&m_SegmentCrSec);
  4867. return S_OK;
  4868. }
  4869. void CPerformance::ResetAllControllers(CChannelMap* pChannelMap, REFERENCE_TIME rtTime, bool fGMReset)
  4870. {
  4871. DWORD dwIndex = pChannelMap->dwPortIndex;
  4872. DWORD dwGroup = pChannelMap->dwGroup;
  4873. DWORD dwMChannel = pChannelMap->dwMChannel;
  4874. EnterCriticalSection(&m_PChannelInfoCrSec);
  4875. IDirectMusicPort* pPort = m_pPortTable[dwIndex].pPort;
  4876. IDirectMusicBuffer* pBuffer = m_pPortTable[dwIndex].pBuffer;
  4877. if( pPort && pBuffer )
  4878. {
  4879. m_pPortTable[dwIndex].fBufferFilled = TRUE;
  4880. if (!rtTime)
  4881. {
  4882. rtTime = m_pPortTable[dwIndex].rtLast + 1;
  4883. }
  4884. else
  4885. {
  4886. m_pPortTable[dwIndex].rtLast = rtTime;
  4887. }
  4888. pChannelMap->Reset(true);
  4889. DWORD dwMsg = dwMChannel | MIDI_CCHANGE | (MIDI_CC_ALLSOUNDSOFF << 8); // 0x78 is all sounds off.
  4890. if( FAILED( pBuffer->PackStructured( rtTime, dwGroup, dwMsg ) ) )
  4891. {
  4892. pPort->PlayBuffer( pBuffer );
  4893. pBuffer->Flush();
  4894. // try one more time
  4895. pBuffer->PackStructured( rtTime, dwGroup, dwMsg );
  4896. }
  4897. dwMsg = dwMChannel | MIDI_CCHANGE | (MIDI_CC_RESETALL << 8) | (1 << 16) ; // 0x79 is reset all controllers. Data byte set to indicate volume and pan too.
  4898. if( FAILED( pBuffer->PackStructured( rtTime + 30 * REF_PER_MIL, dwGroup, dwMsg ) ) )
  4899. {
  4900. pPort->PlayBuffer( pBuffer );
  4901. pBuffer->Flush();
  4902. // try one more time
  4903. pBuffer->PackStructured( rtTime + (30 * REF_PER_MIL), dwGroup, dwMsg );
  4904. }
  4905. // Send one GM Reset per channel group, but only under DX8 (and only if we need to).
  4906. if ((dwMChannel == 0) && (m_dwVersion >= 8) && fGMReset)
  4907. {
  4908. // create a buffer of the right size
  4909. DMUS_BUFFERDESC dmbd;
  4910. IDirectMusicBuffer *pLocalBuffer;
  4911. static BYTE abGMReset[6] = { (BYTE)MIDI_SYSX,0x7E,0x7F,9,1,(BYTE)MIDI_EOX };
  4912. memset( &dmbd, 0, sizeof(DMUS_BUFFERDESC) );
  4913. dmbd.dwSize = sizeof(DMUS_BUFFERDESC);
  4914. dmbd.cbBuffer = 50;
  4915. EnterCriticalSection(&m_MainCrSec);
  4916. if( SUCCEEDED( m_pDirectMusic->CreateMusicBuffer(&dmbd, &pLocalBuffer, NULL)))
  4917. {
  4918. if( SUCCEEDED( pLocalBuffer->PackUnstructured( rtTime + (30 * REF_PER_MIL), dwGroup,
  4919. 6, abGMReset ) ) )
  4920. {
  4921. pPort->PlayBuffer(pLocalBuffer);
  4922. }
  4923. pLocalBuffer->Release();
  4924. }
  4925. LeaveCriticalSection(&m_MainCrSec);
  4926. }
  4927. m_rtEarliestStartTime = rtTime + (60 * REF_PER_MIL); // Give synth chance to stabilize
  4928. // before next start.
  4929. }
  4930. LeaveCriticalSection(&m_PChannelInfoCrSec);
  4931. }
  4932. void CPerformance::ResetAllControllers( REFERENCE_TIME rtTime )
  4933. {
  4934. EnterCriticalSection(&m_PChannelInfoCrSec);
  4935. CChannelBlock* pChannelBlock;
  4936. SendBuffers();
  4937. for( pChannelBlock = m_ChannelBlockList.GetHead(); pChannelBlock; pChannelBlock = pChannelBlock->GetNext() )
  4938. {
  4939. CChannelMap* pChannelMap;
  4940. for( DWORD dwPChannel = pChannelBlock->m_dwPChannelStart;
  4941. dwPChannel < pChannelBlock->m_dwPChannelStart + PCHANNEL_BLOCKSIZE;
  4942. dwPChannel++ )
  4943. {
  4944. pChannelMap = &pChannelBlock->m_aChannelMap[dwPChannel - pChannelBlock->m_dwPChannelStart];
  4945. if( pChannelMap->dwGroup ) // Valid group?
  4946. {
  4947. // Reset controllers and send a GM reset.
  4948. ResetAllControllers(pChannelMap, rtTime, true);
  4949. }
  4950. }
  4951. }
  4952. SendBuffers();
  4953. LeaveCriticalSection(&m_PChannelInfoCrSec);
  4954. }
  4955. // internal: return CSegState* at time mtTime
  4956. // only call this from within a segment critical section
  4957. CSegState* CPerformance::GetPrimarySegmentAtTime( MUSIC_TIME mtTime )
  4958. {
  4959. CSegState* pSegNode;
  4960. CSegState* pSegReturn = NULL;
  4961. BOOL fCheckedPri = FALSE;
  4962. for( pSegNode = m_SegStateQueues[SQ_PRI_DONE].GetHead(); pSegNode; pSegNode = pSegNode->GetNext() )
  4963. {
  4964. // if we're checking the past list, only check up until the last time played.
  4965. if( (mtTime >= pSegNode->m_mtResolvedStart) && (mtTime <= pSegNode->m_mtLastPlayed) )
  4966. {
  4967. pSegReturn = pSegNode;
  4968. break;
  4969. }
  4970. }
  4971. for( pSegNode = m_SegStateQueues[SQ_PRI_PLAY].GetHead(); pSegNode; pSegNode = pSegNode->GetNext() )
  4972. {
  4973. MUSIC_TIME mtTest = mtTime;
  4974. MUSIC_TIME mtOffset;
  4975. DWORD dwRepeat;
  4976. // if we're checking the current list, check the full segment time
  4977. if( S_OK == pSegNode->ConvertToSegTime( &mtTest, &mtOffset, &dwRepeat ))
  4978. {
  4979. pSegReturn = pSegNode;
  4980. break;
  4981. }
  4982. }
  4983. if (!pSegReturn)
  4984. {
  4985. for( pSegNode = m_SegStateQueues[SQ_PRI_WAIT].GetHead(); pSegNode; pSegNode = pSegNode->GetNext() )
  4986. {
  4987. MUSIC_TIME mtTest = mtTime;
  4988. MUSIC_TIME mtOffset;
  4989. DWORD dwRepeat;
  4990. // if we're checking the current list, check the full segment time
  4991. if( S_OK == pSegNode->ConvertToSegTime( &mtTest, &mtOffset, &dwRepeat ))
  4992. {
  4993. pSegReturn = pSegNode;
  4994. break;
  4995. }
  4996. }
  4997. }
  4998. return pSegReturn;
  4999. }
  5000. /*
  5001. @method HRESULT | IDirectMusicPerformance | GetSegmentState |
  5002. Returns the Primary SegmentState at time <p mtTime>.
  5003. @rvalue S_OK | Success.
  5004. @rvalue E_POINTER | ppSegmentState is NULL or invalid.
  5005. @rvalue DMUS_E_NOT_FOUND | There is no currently playing SegmentState or one at <p mtTime>.
  5006. @comm This function is intended for routines that need to access the currently
  5007. playing SegmentState, e.g. to obtain the chord or command track. "Currently
  5008. Playing" in this context means that it is being called into to perform messages.
  5009. I.e., this includes all latencies and doesn't imply that this
  5010. SegmentState is currenty being "heard" through the speakers.
  5011. */
  5012. HRESULT STDMETHODCALLTYPE CPerformance::GetSegmentState(
  5013. IDirectMusicSegmentState **ppSegmentState, // @parm Returns the SegmentState pointer to the one currently playing.
  5014. // The caller is responsible for calling Release on this pointer.
  5015. MUSIC_TIME mtTime ) // @parm Return the SegmentState which played, is playing, or will
  5016. // be playing at mtTime. To get the currently playing segment, pass the
  5017. // mtTime retrieved from <om .GetTime>.
  5018. {
  5019. V_INAME(IDirectMusicPerformance::GetSegmentState);
  5020. V_PTRPTR_WRITE(ppSegmentState);
  5021. CSegState* pSegNode;
  5022. HRESULT hr;
  5023. EnterCriticalSection(&m_SegmentCrSec);
  5024. if( pSegNode = GetPrimarySegmentAtTime( mtTime ))
  5025. {
  5026. *ppSegmentState = pSegNode;
  5027. pSegNode->AddRef();
  5028. hr = S_OK;
  5029. }
  5030. else
  5031. {
  5032. Trace(3,"Unable to find a segment state at time %ld\n",mtTime);
  5033. hr = DMUS_E_NOT_FOUND;
  5034. }
  5035. LeaveCriticalSection(&m_SegmentCrSec);
  5036. return hr;
  5037. }
  5038. /*
  5039. @method HRESULT | IDirectMusicPerformance | SetPrepareTime |
  5040. Sets the prepare time. The prepare time is the amount of time ahead that
  5041. <om IDirectMusicTrack.Play> is called before the messages should actually
  5042. be heard through the loudspeaker. The midi messages from the tracks are placed in
  5043. the early queue, are processed by Tools, and then placed in the near-time
  5044. queue to await being sent to the midi ports.
  5045. @rvalue S_OK | Success.
  5046. @comm The default value is 1000 milliseconds.
  5047. */
  5048. HRESULT STDMETHODCALLTYPE CPerformance::SetPrepareTime(
  5049. DWORD dwMilliSeconds) // @parm The amount of time.
  5050. {
  5051. m_dwPrepareTime = dwMilliSeconds;
  5052. return S_OK;
  5053. }
  5054. /*
  5055. @method HRESULT | IDirectMusicPerformance | GetPrepareTime |
  5056. Gets the prepare time. The prepare time is the amount of time ahead that
  5057. <om IDirectMusicTrack.Play> is called before the messages should actually
  5058. be heard through the loudspeaker. The midi messages from the tracks are placed in
  5059. the early queue, are processed by Tools, and then placed in the near-time
  5060. queue to await being sent to the midi ports.
  5061. @rvalue S_OK | Success.
  5062. @rvalue E_POINTER | pdwMilliSeconds is NULL or invalid.
  5063. @comm The default value is 1000 milliseconds.
  5064. */
  5065. HRESULT STDMETHODCALLTYPE CPerformance::GetPrepareTime(
  5066. DWORD* pdwMilliSeconds) // @parm The amount of time.
  5067. {
  5068. V_INAME(IDirectMusicPerformance::GetPrepareTime);
  5069. V_PTR_WRITE(pdwMilliSeconds,DWORD);
  5070. *pdwMilliSeconds = m_dwPrepareTime;
  5071. return S_OK;
  5072. }
  5073. /*
  5074. @method HRESULT | IDirectMusicPerformance | SetBumperLength |
  5075. Sets the bumper length. The bumper length is the amount of time to buffer ahead
  5076. of the Port's latency for midi messages to be sent to the Port for rendering.
  5077. @rvalue S_OK | Success.
  5078. @comm The default value is 50 milliseconds.
  5079. */
  5080. HRESULT STDMETHODCALLTYPE CPerformance::SetBumperLength(
  5081. DWORD dwMilliSeconds) // @parm The amount of time.
  5082. {
  5083. m_dwBumperLength = dwMilliSeconds;
  5084. m_rtBumperLength = m_dwBumperLength * REF_PER_MIL;
  5085. return S_OK;
  5086. }
  5087. /*
  5088. @method HRESULT | IDirectMusicPerformance | GetBumperLength |
  5089. Gets the bumper length. The bumper length is the amount of time to buffer ahead
  5090. of the Port's latency for midi messages to be sent to the Port for rendering.
  5091. @rvalue S_OK | Success.
  5092. @rvalue E_POINTER | pdwMilliSeconds is NULL or invalid.
  5093. @comm The default value is 50 milliseconds.
  5094. */
  5095. HRESULT STDMETHODCALLTYPE CPerformance::GetBumperLength(
  5096. DWORD* pdwMilliSeconds) // @parm The amount of time.
  5097. {
  5098. V_INAME(IDirectMusicPerformance::GetBumperLength);
  5099. V_PTR_WRITE(pdwMilliSeconds,DWORD);
  5100. *pdwMilliSeconds = m_dwBumperLength;
  5101. return S_OK;
  5102. }
  5103. #define RESOLVE_FLAGS (DMUS_TIME_RESOLVE_AFTERPREPARETIME | \
  5104. DMUS_TIME_RESOLVE_AFTERLATENCYTIME | \
  5105. DMUS_TIME_RESOLVE_AFTERQUEUETIME | \
  5106. DMUS_TIME_RESOLVE_BEAT | \
  5107. DMUS_TIME_RESOLVE_MEASURE | \
  5108. DMUS_TIME_RESOLVE_GRID | \
  5109. DMUS_TIME_RESOLVE_MARKER | \
  5110. DMUS_TIME_RESOLVE_SEGMENTEND)
  5111. HRESULT STDMETHODCALLTYPE CPerformance::SendPMsg(
  5112. DMUS_PMSG *pDMUS_PMSG)
  5113. {
  5114. V_INAME(IDirectMusicPerformance::SendPMsg);
  5115. if( m_dwVersion < 8)
  5116. {
  5117. V_BUFPTR_WRITE(pDMUS_PMSG,sizeof(DMUS_PMSG));
  5118. }
  5119. else
  5120. {
  5121. #ifdef DBG
  5122. V_BUFPTR_WRITE(pDMUS_PMSG,sizeof(DMUS_PMSG));
  5123. #else
  5124. if (!pDMUS_PMSG)
  5125. {
  5126. return E_POINTER;
  5127. }
  5128. #endif
  5129. }
  5130. EnterCriticalSection(&m_MainCrSec);
  5131. if( m_pClock == NULL )
  5132. {
  5133. LeaveCriticalSection(&m_MainCrSec);
  5134. Trace(1,"Error: Unable to Send PMsg because performance not initialized.\n");
  5135. return DMUS_E_NO_MASTER_CLOCK;
  5136. }
  5137. LeaveCriticalSection(&m_MainCrSec);
  5138. if (pDMUS_PMSG->dwSize < sizeof(DMUS_PMSG))
  5139. {
  5140. TraceI(1,"Warning: PMsg size field has been cleared.\n");
  5141. }
  5142. // If this is a PMsg that was marked by STampPMsg as one that should be removed,
  5143. // do so now.
  5144. if (pDMUS_PMSG->dwPChannel == DMUS_PCHANNEL_KILL_ME)
  5145. {
  5146. FreePMsg(pDMUS_PMSG);
  5147. return S_OK;
  5148. }
  5149. EnterCriticalSection(&m_PipelineCrSec);
  5150. PRIV_PMSG* pPrivPMsg = DMUS_TO_PRIV(pDMUS_PMSG);
  5151. if( ( pPrivPMsg->dwPrivFlags & PRIV_FLAG_QUEUED ) ||
  5152. ( ( pPrivPMsg->dwPrivFlags & PRIV_FLAG_ALLOC_MASK ) != PRIV_FLAG_ALLOC ) )
  5153. {
  5154. Trace(1, "Error: Attempt to send an improperly allocated PMsg, or trying to send it after it is already sent.\n" );
  5155. LeaveCriticalSection(&m_PipelineCrSec);
  5156. return DMUS_E_ALREADY_SENT;
  5157. }
  5158. if (m_dwVersion >= 8)
  5159. {
  5160. // If the music and ref times are both 0, set to latency time.
  5161. if ((pDMUS_PMSG->mtTime == 0) && ( pDMUS_PMSG->rtTime == 0 ))
  5162. {
  5163. // If this needs to resolve, use the worse case latency
  5164. // because this needs to sync with other pmsgs.
  5165. if (pDMUS_PMSG->dwFlags & RESOLVE_FLAGS)
  5166. {
  5167. GetLatencyTime(&pDMUS_PMSG->rtTime);
  5168. }
  5169. else
  5170. {
  5171. // Otherwise, we want to play as soon as possible.
  5172. pDMUS_PMSG->rtTime = GetTime();
  5173. }
  5174. pDMUS_PMSG->dwFlags |= DMUS_PMSGF_REFTIME;
  5175. pDMUS_PMSG->dwFlags &= ~DMUS_PMSGF_MUSICTIME;
  5176. }
  5177. }
  5178. // fill in missing time value
  5179. if (!(pDMUS_PMSG->dwFlags & DMUS_PMSGF_MUSICTIME))
  5180. {
  5181. if( !(pDMUS_PMSG->dwFlags & DMUS_PMSGF_REFTIME ) )
  5182. {
  5183. LeaveCriticalSection(&m_PipelineCrSec);
  5184. Trace(1,"Error: Unable to send PMsg because neither clock time (DMUS_PMSGF_REFTIME) nor music time (DMUS_PMSGF_MUSICTIME) has been set.\n");
  5185. return E_INVALIDARG; // one or the other MUST be set
  5186. }
  5187. // quantize to resolution boundaries
  5188. GetResolvedTime( pDMUS_PMSG->rtTime, &pDMUS_PMSG->rtTime, pDMUS_PMSG->dwFlags );
  5189. pDMUS_PMSG->dwFlags &= ~RESOLVE_FLAGS;
  5190. // if time is zero, set it to time now plus latency
  5191. if( pDMUS_PMSG->rtTime == 0 )
  5192. {
  5193. pDMUS_PMSG->rtTime = GetLatency();
  5194. }
  5195. ReferenceToMusicTime(pDMUS_PMSG->rtTime,
  5196. &pDMUS_PMSG->mtTime);
  5197. pDMUS_PMSG->dwFlags |= DMUS_PMSGF_MUSICTIME;
  5198. }
  5199. else if (!(pDMUS_PMSG->dwFlags & DMUS_PMSGF_REFTIME))
  5200. {
  5201. MusicToReferenceTime(pDMUS_PMSG->mtTime,
  5202. &pDMUS_PMSG->rtTime);
  5203. pDMUS_PMSG->dwFlags |= DMUS_PMSGF_REFTIME;
  5204. // quantize to resolution boundaries
  5205. REFERENCE_TIME rtNew;
  5206. GetResolvedTime( pDMUS_PMSG->rtTime, &rtNew, pDMUS_PMSG->dwFlags );
  5207. pDMUS_PMSG->dwFlags &= ~RESOLVE_FLAGS;
  5208. if( rtNew != pDMUS_PMSG->rtTime )
  5209. {
  5210. pDMUS_PMSG->rtTime = rtNew;
  5211. ReferenceToMusicTime( pDMUS_PMSG->rtTime, &pDMUS_PMSG->mtTime );
  5212. }
  5213. }
  5214. // insert into the proper queue by music value
  5215. if (pDMUS_PMSG->dwFlags & DMUS_PMSGF_TOOL_QUEUE)
  5216. {
  5217. m_NearTimeQueue.Enqueue(pPrivPMsg);
  5218. }
  5219. else if (pDMUS_PMSG->dwFlags & DMUS_PMSGF_TOOL_ATTIME)
  5220. {
  5221. m_OnTimeQueue.Enqueue(pPrivPMsg);
  5222. }
  5223. else // (pDMUS_PMSG->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE)
  5224. {
  5225. pDMUS_PMSG->dwFlags |= DMUS_PMSGF_TOOL_IMMEDIATE;
  5226. m_EarlyQueue.Enqueue(pPrivPMsg);
  5227. }
  5228. LeaveCriticalSection(&m_PipelineCrSec);
  5229. return S_OK;
  5230. }
  5231. /*
  5232. Call this only from within a PipelineCrSec.
  5233. */
  5234. void CPerformance::RevalidateRefTimes( CPMsgQueue * pList, MUSIC_TIME mtTime )
  5235. {
  5236. PRIV_PMSG* pCheck;
  5237. BOOL fError = FALSE;
  5238. for( pCheck = pList->GetHead(); pCheck; pCheck = pCheck->pNext )
  5239. {
  5240. if (pCheck->mtTime > mtTime)
  5241. {
  5242. if (pCheck->dwFlags & DMUS_PMSGF_LOCKTOREFTIME)
  5243. {
  5244. ReferenceToMusicTime(pCheck->rtTime,&pCheck->mtTime);
  5245. }
  5246. else // if(pCheck->dwFlags & DMUS_PMSGF_MUSICTIME)
  5247. {
  5248. MusicToReferenceTime(pCheck->mtTime,&pCheck->rtTime);
  5249. }
  5250. }
  5251. }
  5252. // Make sure that we do not end up with out of order RTimes. This can happen with
  5253. // DMUS_PMSGF_LOCKTOREFTIME messages or very abrupt changes in tempo.
  5254. for( pCheck = pList->GetHead(); pCheck; pCheck = pCheck->pNext )
  5255. {
  5256. if (pCheck->pNext && ( pCheck->rtTime > pCheck->pNext->rtTime ))
  5257. {
  5258. fError = TRUE; // Need to sort the list.
  5259. }
  5260. }
  5261. if (fError)
  5262. {
  5263. TraceI(2,"Rearrangement of times in message list due to tempo change, resorting\n");
  5264. pList->Sort();
  5265. }
  5266. }
  5267. void CPerformance::AddToTempoMap( double dblTempo, MUSIC_TIME mtTime, REFERENCE_TIME rtTime )
  5268. {
  5269. DMInternalTempo* pITempo = NULL;
  5270. if( FAILED( AllocPMsg( sizeof(DMInternalTempo), (PRIV_PMSG**)&pITempo )))
  5271. {
  5272. return; // out of memory!
  5273. }
  5274. if( dblTempo > DMUS_TEMPO_MAX ) dblTempo = DMUS_TEMPO_MAX;
  5275. else if( dblTempo < DMUS_TEMPO_MIN ) dblTempo = DMUS_TEMPO_MIN;
  5276. pITempo->tempoPMsg.dblTempo = dblTempo;
  5277. pITempo->tempoPMsg.rtTime = rtTime;
  5278. pITempo->tempoPMsg.mtTime = mtTime;
  5279. pITempo->tempoPMsg.dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_REFTIME;
  5280. pITempo->pNext = NULL;
  5281. // set the relative tempo field
  5282. EnterCriticalSection(&m_GlobalDataCrSec);
  5283. pITempo->fltRelTempo = m_fltRelTempo;
  5284. // add the tempo event to the tempo map and clear the tool and graph pointers
  5285. pITempo->tempoPMsg.pTool = NULL;
  5286. EnterCriticalSection(&m_PipelineCrSec);
  5287. // remove stale tempo events from the tempo map.
  5288. // as long as there is another tempo with a time stamp before the current
  5289. // time, get rid of the first in the list.
  5290. REFERENCE_TIME rtNow = GetTime() - (10000 * 1000); // keep around for a second.
  5291. PRIV_PMSG* pCheck;
  5292. while (pCheck = m_TempoMap.FlushOldest(rtNow))
  5293. {
  5294. m_OldTempoMap.Enqueue(pCheck);
  5295. }
  5296. // add the new tempo event to the queue
  5297. m_TempoMap.Enqueue( (PRIV_PMSG*) pITempo );
  5298. // now that it's been added, scan forward from it and change the relative tempo
  5299. // times of everyone after it
  5300. DMInternalTempo* pChange;
  5301. for( pChange = (DMInternalTempo*)pITempo->pNext; pChange;
  5302. pChange = (DMInternalTempo*)pChange->pNext )
  5303. {
  5304. pChange->fltRelTempo = pITempo->fltRelTempo;
  5305. }
  5306. // remove stale tempo events from the old tempo map.
  5307. // as long as there is another tempo with a time stamp before the current
  5308. // time, get rid of the first in the list.
  5309. rtNow = GetTime() - ((REFERENCE_TIME)10000 * 300000); // keep around for five minutes.
  5310. while (pCheck = m_OldTempoMap.FlushOldest(rtNow))
  5311. {
  5312. FreePMsg(pCheck);
  5313. }
  5314. m_fTempoChanged = TRUE;
  5315. LeaveCriticalSection(&m_PipelineCrSec);
  5316. LeaveCriticalSection(&m_GlobalDataCrSec);
  5317. }
  5318. void CPerformance::AddEventToTempoMap( PRIV_PMSG* pEvent )
  5319. {
  5320. PRIV_TEMPO_PMSG* pTempo = (PRIV_TEMPO_PMSG*)pEvent;
  5321. MUSIC_TIME mtTime = pTempo->tempoPMsg.mtTime;
  5322. AddToTempoMap( pTempo->tempoPMsg.dblTempo, mtTime, pTempo->tempoPMsg.rtTime );
  5323. pEvent->dwPrivFlags = PRIV_FLAG_ALLOC;
  5324. EnterCriticalSection(&m_GlobalDataCrSec);
  5325. EnterCriticalSection(&m_PipelineCrSec);
  5326. // revalidate the ref times of the events in the queues
  5327. RevalidateRefTimes( &m_TempoMap, mtTime );
  5328. RevalidateRefTimes( &m_OnTimeQueue, mtTime );
  5329. RevalidateRefTimes( &m_NearTimeQueue, mtTime );
  5330. RevalidateRefTimes( &m_EarlyQueue, mtTime );
  5331. m_fTempoChanged = TRUE;
  5332. LeaveCriticalSection(&m_PipelineCrSec);
  5333. LeaveCriticalSection(&m_GlobalDataCrSec);
  5334. RecalcTempoMap(NULL, mtTime+1, false);
  5335. }
  5336. #define TEMPO_AHEAD 768 * 4 * 10 // 10 measures ahead is plenty!
  5337. void CPerformance::IncrementTempoMap()
  5338. {
  5339. if (m_mtTempoCursor <= (m_mtTransported + TEMPO_AHEAD))
  5340. {
  5341. UpdateTempoMap(m_mtTempoCursor, false, NULL);
  5342. }
  5343. }
  5344. void CPerformance::RecalcTempoMap(CSegState *pSegState, MUSIC_TIME mtStart, bool fAllDeltas)
  5345. /* Called whenever a primary or controlling segment that has a tempo
  5346. track is played or stopped.
  5347. 1) Convert the music time at transport time to ref time using the old
  5348. map.
  5349. 2) Build a replacement tempo map starting at mtStart, by
  5350. calling GetParam() until there is no next time.
  5351. 3) Install the new map.
  5352. 4) Convert with the new map.
  5353. 5) If the two numbers are not identical, recalculate all message times.
  5354. */
  5355. {
  5356. if( mtStart > 0) // Don't do this for invalid values.
  5357. {
  5358. if (!pSegState || (pSegState->m_pSegment && pSegState->m_pSegment->IsTempoSource()))
  5359. {
  5360. REFERENCE_TIME rtCompareTime;
  5361. REFERENCE_TIME rtAfterTime;
  5362. MUSIC_TIME mtCompareTime = m_mtTransported;
  5363. MusicToReferenceTime(mtCompareTime,&rtCompareTime);
  5364. EnterCriticalSection(&m_PipelineCrSec);
  5365. FlushEventQueue( 0, &m_TempoMap, rtCompareTime, rtCompareTime, FALSE );
  5366. LeaveCriticalSection(&m_PipelineCrSec);
  5367. UpdateTempoMap(mtStart, true, pSegState, fAllDeltas);
  5368. MusicToReferenceTime(mtCompareTime,&rtAfterTime);
  5369. if (rtAfterTime != rtCompareTime)
  5370. {
  5371. EnterCriticalSection(&m_GlobalDataCrSec);
  5372. EnterCriticalSection(&m_PipelineCrSec);
  5373. // revalidate the ref times of the events in the queues
  5374. RevalidateRefTimes( &m_TempoMap, mtStart );
  5375. RevalidateRefTimes( &m_OnTimeQueue, mtStart );
  5376. RevalidateRefTimes( &m_NearTimeQueue, mtStart );
  5377. RevalidateRefTimes( &m_EarlyQueue, mtStart );
  5378. m_fTempoChanged = TRUE;
  5379. LeaveCriticalSection(&m_PipelineCrSec);
  5380. LeaveCriticalSection(&m_GlobalDataCrSec);
  5381. }
  5382. }
  5383. }
  5384. }
  5385. void CPerformance::UpdateTempoMap(MUSIC_TIME mtStart, bool fFirst, CSegState *pSegState, bool fAllDeltas)
  5386. {
  5387. HRESULT hr = S_OK;
  5388. DWORD dwIndex = 0;
  5389. PrivateTempo Tempo;
  5390. TList<PrivateTempo> TempoList;
  5391. TListItem<PrivateTempo>* pScan = NULL;
  5392. MUSIC_TIME mtNext = 0;
  5393. MUSIC_TIME mtTime = mtStart;
  5394. MUSIC_TIME mtCursor = mtStart;
  5395. REFERENCE_TIME rtTime;
  5396. do
  5397. {
  5398. hr = GetParam(GUID_PrivateTempoParam,-1,dwIndex,mtTime,&mtNext,(void *)&Tempo );
  5399. Tempo.mtTime = mtTime;
  5400. if (hr == S_OK && Tempo.mtDelta > 0)
  5401. {
  5402. mtTime += Tempo.mtDelta;
  5403. hr = GetParam(GUID_PrivateTempoParam,-1,dwIndex,mtTime,&mtNext,(void *)&Tempo );
  5404. Tempo.mtTime = mtTime;
  5405. }
  5406. if (hr == S_FALSE && fFirst && !pSegState)
  5407. {
  5408. // If this was the very first try, there might not be any tempo track, and
  5409. // so global tempo is called. If so, S_FALSE is returned. This is okay
  5410. // for the NULL segstate case where we are recomputing the tempo map in response
  5411. // to a change in global tempo, or stop of all segments.
  5412. if (fAllDeltas) // Never do this in response to adding a new event to the tempo map
  5413. {
  5414. MusicToReferenceTime(mtTime,&rtTime);
  5415. // the rtTime in the tempo map needs to be the non-adjusted value (305694)
  5416. AddToTempoMap( Tempo.dblTempo, mtTime, rtTime + m_rtAdjust );
  5417. }
  5418. break;
  5419. }
  5420. if (hr == S_OK)
  5421. {
  5422. TListItem<PrivateTempo>* pNew = new TListItem<PrivateTempo>(Tempo);
  5423. if (pNew)
  5424. {
  5425. // add to TempoList, replacing duplicate times with the most recent mtDelta
  5426. TListItem<PrivateTempo>* pNext = TempoList.GetHead();
  5427. if (!pNext || Tempo.mtTime < pNext->GetItemValue().mtTime)
  5428. {
  5429. TempoList.AddHead(pNew);
  5430. }
  5431. else for (pScan = TempoList.GetHead(); pScan; pScan = pNext)
  5432. {
  5433. pNext = pScan->GetNext();
  5434. if (Tempo.mtTime == pScan->GetItemValue().mtTime)
  5435. {
  5436. if (Tempo.mtDelta > pScan->GetItemValue().mtDelta)
  5437. {
  5438. pScan->GetItemValue() = Tempo;
  5439. }
  5440. delete pNew;
  5441. break;
  5442. }
  5443. else if (!pNext || Tempo.mtTime < pNext->GetItemValue().mtTime)
  5444. {
  5445. pScan->SetNext(pNew);
  5446. pNew->SetNext(pNext);
  5447. break;
  5448. }
  5449. }
  5450. }
  5451. mtTime += mtNext;
  5452. fFirst = false;
  5453. // If this was the last tempo in the track (that we care about),
  5454. // reset the time and bump the track index
  5455. if (Tempo.fLast || mtTime > (m_mtTransported + TEMPO_AHEAD))
  5456. {
  5457. dwIndex++;
  5458. mtCursor = mtTime;
  5459. mtTime = mtStart;
  5460. }
  5461. else if (!mtNext) break; // should never happen but if it does, infinite loop
  5462. }
  5463. else if (Tempo.fLast) // There was an empty tempo track
  5464. {
  5465. dwIndex++;
  5466. hr = S_OK;
  5467. }
  5468. Tempo.fLast = false;
  5469. } while (hr == S_OK);
  5470. if (TempoList.GetHead() && TempoList.GetHead()->GetItemValue().mtTime > mtStart)
  5471. {
  5472. // add a tempo of 120 at time mtStart
  5473. TListItem<PrivateTempo>* pNew = new TListItem<PrivateTempo>();
  5474. if (pNew)
  5475. {
  5476. PrivateTempo& rNew = pNew->GetItemValue();
  5477. rNew.dblTempo = 120.0;
  5478. rNew.mtTime = mtStart;
  5479. TempoList.AddHead(pNew);
  5480. }
  5481. else
  5482. {
  5483. #ifdef DBG
  5484. Trace(1, "Error: Out of memory; Tempo map is incomplete.\n");
  5485. #endif
  5486. TempoList.GetHead()->GetItemValue().mtTime = mtStart;
  5487. }
  5488. }
  5489. for (pScan = TempoList.GetHead(); pScan; pScan = pScan->GetNext())
  5490. {
  5491. PrivateTempo& rTempo = pScan->GetItemValue();
  5492. if (fAllDeltas || rTempo.mtTime + rTempo.mtDelta >= mtStart)
  5493. {
  5494. MusicToReferenceTime(rTempo.mtTime,&rtTime);
  5495. // the rtTime in the tempo map needs to be the non-adjusted value (305694)
  5496. AddToTempoMap( rTempo.dblTempo, rTempo.mtTime, rtTime + m_rtAdjust );
  5497. }
  5498. }
  5499. m_mtTempoCursor = mtCursor;
  5500. }
  5501. HRESULT STDMETHODCALLTYPE CPerformance::MusicToReferenceTime(
  5502. MUSIC_TIME mtTime, // @parm The time in MUSIC_TIME format to convert.
  5503. REFERENCE_TIME *prtTime) // @parm Returns the converted time in REFERENCE_TIME format.
  5504. {
  5505. V_INAME(IDirectMusicPerformance::MusicToReferenceTime);
  5506. V_PTR_WRITE(prtTime,REFERENCE_TIME);
  5507. EnterCriticalSection(&m_MainCrSec);
  5508. if( m_pClock == NULL )
  5509. {
  5510. LeaveCriticalSection(&m_MainCrSec);
  5511. Trace(1,"Error: Unable to convert music to reference time because the performance has not been initialized.\n");
  5512. return DMUS_E_NO_MASTER_CLOCK;
  5513. }
  5514. LeaveCriticalSection(&m_MainCrSec);
  5515. PRIV_PMSG* pEvent;
  5516. double dbl = 120;
  5517. MUSIC_TIME mtTempo = 0;
  5518. REFERENCE_TIME rtTempo = m_rtStart;
  5519. REFERENCE_TIME rtTemp;
  5520. EnterCriticalSection( &m_PipelineCrSec );
  5521. pEvent = m_TempoMap.GetHead();
  5522. if( pEvent )
  5523. {
  5524. if( mtTime >= pEvent->mtTime )
  5525. {
  5526. while( pEvent->pNext )
  5527. {
  5528. if( pEvent->pNext->mtTime > mtTime )
  5529. {
  5530. break;
  5531. }
  5532. pEvent = pEvent->pNext;
  5533. }
  5534. DMInternalTempo* pTempo = (DMInternalTempo*)pEvent;
  5535. dbl = pTempo->tempoPMsg.dblTempo * pTempo->fltRelTempo;
  5536. mtTempo = pTempo->tempoPMsg.mtTime;
  5537. rtTempo = pTempo->tempoPMsg.rtTime;
  5538. }
  5539. else
  5540. {
  5541. // If mtTime is less than everything in the tempo map, look in the old tempo map
  5542. // (which goes five minutes into the past). This keeps the regular tempo map
  5543. // small, but allows us to get a valid tempo in the cases where the regular tempo
  5544. // map no longer contains the tempo we need.
  5545. pEvent = m_OldTempoMap.GetHead();
  5546. if( pEvent )
  5547. {
  5548. if( mtTime >= pEvent->mtTime )
  5549. {
  5550. while( pEvent->pNext )
  5551. {
  5552. if( pEvent->pNext->mtTime > mtTime )
  5553. {
  5554. break;
  5555. }
  5556. pEvent = pEvent->pNext;
  5557. }
  5558. DMInternalTempo* pTempo = (DMInternalTempo*)pEvent;
  5559. dbl = pTempo->tempoPMsg.dblTempo * pTempo->fltRelTempo;
  5560. mtTempo = pTempo->tempoPMsg.mtTime;
  5561. rtTempo = pTempo->tempoPMsg.rtTime;
  5562. }
  5563. }
  5564. }
  5565. }
  5566. LeaveCriticalSection( &m_PipelineCrSec );
  5567. rtTempo -= m_rtAdjust;
  5568. rtTemp = ( mtTime - mtTempo );
  5569. rtTemp *= 600000000;
  5570. rtTemp += (DMUS_PPQ / 2);
  5571. rtTemp /= DMUS_PPQ;
  5572. rtTemp = (REFERENCE_TIME)(rtTemp / dbl);
  5573. *prtTime = rtTempo + rtTemp;
  5574. return S_OK;
  5575. }
  5576. HRESULT STDMETHODCALLTYPE CPerformance::ReferenceToMusicTime(
  5577. REFERENCE_TIME rtTime, // @parm The time in REFERENCE_TIME format to convert.
  5578. MUSIC_TIME *pmtTime) // @parm Returns the converted time in MUSIC_TIME format.
  5579. {
  5580. V_INAME(IDirectMusicPerformance::ReferenceToMusicTime);
  5581. V_PTR_WRITE(pmtTime,MUSIC_TIME);
  5582. EnterCriticalSection(&m_MainCrSec);
  5583. if( m_pClock == NULL )
  5584. {
  5585. LeaveCriticalSection(&m_MainCrSec);
  5586. Trace(1,"Error: Unable to convert reference to music time because the performance has not been initialized.\n");
  5587. return DMUS_E_NO_MASTER_CLOCK;
  5588. }
  5589. LeaveCriticalSection(&m_MainCrSec);
  5590. PRIV_PMSG* pEvent;
  5591. double dbl = 120;
  5592. MUSIC_TIME mtTempo = 0;
  5593. REFERENCE_TIME rtTempo = m_rtStart;
  5594. EnterCriticalSection( &m_PipelineCrSec );
  5595. pEvent = m_TempoMap.GetHead();
  5596. if( pEvent )
  5597. {
  5598. if( rtTime >= pEvent->rtTime )
  5599. {
  5600. while( pEvent->pNext )
  5601. {
  5602. if( pEvent->pNext->rtTime > rtTime )
  5603. {
  5604. break;
  5605. }
  5606. pEvent = pEvent->pNext;
  5607. }
  5608. DMInternalTempo* pTempo = (DMInternalTempo*)pEvent;
  5609. dbl = pTempo->tempoPMsg.dblTempo * pTempo->fltRelTempo;
  5610. mtTempo = pTempo->tempoPMsg.mtTime;
  5611. rtTempo = pTempo->tempoPMsg.rtTime;
  5612. }
  5613. else
  5614. {
  5615. // If mtTime is less than everything in the tempo map, look in the old tempo map
  5616. // (which goes five minutes into the past). This keeps the regular tempo map
  5617. // small, but allows us to get a valid tempo in the cases where the regular tempo
  5618. // map no longer contains the tempo we need.
  5619. pEvent = m_OldTempoMap.GetHead();
  5620. if( pEvent )
  5621. {
  5622. if( rtTime >= pEvent->rtTime )
  5623. {
  5624. while( pEvent->pNext )
  5625. {
  5626. if( pEvent->pNext->rtTime > rtTime )
  5627. {
  5628. break;
  5629. }
  5630. pEvent = pEvent->pNext;
  5631. }
  5632. DMInternalTempo* pTempo = (DMInternalTempo*)pEvent;
  5633. dbl = pTempo->tempoPMsg.dblTempo * pTempo->fltRelTempo;
  5634. mtTempo = pTempo->tempoPMsg.mtTime;
  5635. rtTempo = pTempo->tempoPMsg.rtTime;
  5636. }
  5637. }
  5638. }
  5639. }
  5640. LeaveCriticalSection( &m_PipelineCrSec );
  5641. rtTempo -= m_rtAdjust;
  5642. if( rtTime < rtTempo )
  5643. {
  5644. rtTime = rtTempo;
  5645. }
  5646. rtTime -= rtTempo;
  5647. rtTime *= DMUS_PPQ;
  5648. rtTime = (REFERENCE_TIME)(rtTime * dbl);
  5649. rtTime += 300000000;
  5650. rtTime /= 600000000;
  5651. #ifdef DBG
  5652. if ( rtTime & 0xFFFFFFFF00000000 )
  5653. {
  5654. Trace(1,"Error: Invalid Reference to Music time conversion resulted in overflow.\n");
  5655. }
  5656. #endif
  5657. *pmtTime = (long) (rtTime & 0xFFFFFFFF);
  5658. *pmtTime += mtTempo;
  5659. return S_OK;
  5660. }
  5661. /*
  5662. @method HRESULT | IDirectMusicPerformance | AdjustTime |
  5663. Adjust the internal Performance time forward or backward. This is mostly used to
  5664. compensate for drift when synchronizing to another source, such as SMPTE.
  5665. @rvalue S_OK | Success.
  5666. @rvalue E_INVALIDARG | rtAmount is too large or too small.
  5667. */
  5668. HRESULT STDMETHODCALLTYPE CPerformance::AdjustTime(
  5669. REFERENCE_TIME rtAmount) // @parm The amount of time to adjust. This may be a
  5670. // number from -10000000 to 10000000 (-1 second to +1 second.)
  5671. {
  5672. if( ( rtAmount < -10000000 ) || ( rtAmount > 10000000 ) )
  5673. {
  5674. Trace(1,"Error: Time parameter passed to AdjustTime() is out of range.\n");
  5675. return E_INVALIDARG;
  5676. }
  5677. m_rtAdjust += rtAmount;
  5678. return S_OK;
  5679. }
  5680. /*
  5681. @method HRESULT | IDirectMusicPerformance | GetResolvedTime |
  5682. Quantize a time to a resolution boundary. Given a time, in REFERENCE_TIME,
  5683. return the next time on a given boundary after the time given.
  5684. @rvalue S_OK | Success.
  5685. @rvalue E_POINTER <prtResolved> is not valid.
  5686. */
  5687. HRESULT STDMETHODCALLTYPE CPerformance::GetResolvedTime(
  5688. REFERENCE_TIME rtTime,
  5689. REFERENCE_TIME* prtResolved,
  5690. DWORD dwResolvedTimeFlags)
  5691. {
  5692. V_INAME(IDirectMusicPerformance::GetResolvedTime);
  5693. V_PTR_WRITE(prtResolved,REFERENCE_TIME);
  5694. if (rtTime == 0)
  5695. {
  5696. dwResolvedTimeFlags |= DMUS_TIME_RESOLVE_AFTERQUEUETIME ;
  5697. }
  5698. if( dwResolvedTimeFlags & DMUS_TIME_RESOLVE_AFTERPREPARETIME )
  5699. {
  5700. REFERENCE_TIME rtTrans;
  5701. MusicToReferenceTime( m_mtTransported, &rtTrans );
  5702. if( rtTime < rtTrans ) rtTime = rtTrans;
  5703. }
  5704. else if (dwResolvedTimeFlags & DMUS_TIME_RESOLVE_AFTERLATENCYTIME )
  5705. {
  5706. REFERENCE_TIME rtStart;
  5707. rtStart = GetLatency();
  5708. if( rtTime < rtStart ) rtTime = rtStart;
  5709. }
  5710. else if( dwResolvedTimeFlags & DMUS_TIME_RESOLVE_AFTERQUEUETIME )
  5711. {
  5712. REFERENCE_TIME rtStart;
  5713. GetQueueTime( &rtStart ); // need queue time because control segments cause invalidations
  5714. if( rtTime < rtStart ) rtTime = rtStart;
  5715. }
  5716. if( dwResolvedTimeFlags & ( DMUS_TIME_RESOLVE_BEAT | DMUS_TIME_RESOLVE_MEASURE |
  5717. DMUS_TIME_RESOLVE_GRID | DMUS_TIME_RESOLVE_MARKER | DMUS_TIME_RESOLVE_SEGMENTEND))
  5718. {
  5719. MUSIC_TIME mtTime; //, mtResolved;
  5720. ReferenceToMusicTime( rtTime, &mtTime );
  5721. EnterCriticalSection(&m_SegmentCrSec);
  5722. mtTime = ResolveTime( mtTime, dwResolvedTimeFlags, NULL);
  5723. LeaveCriticalSection(&m_SegmentCrSec);
  5724. MusicToReferenceTime( mtTime, prtResolved );
  5725. }
  5726. else
  5727. {
  5728. *prtResolved = rtTime;
  5729. }
  5730. return S_OK;
  5731. }
  5732. /*
  5733. @method HRESULT | IDirectMusicPerformance | IsPlaying |
  5734. Find out if a particular Segment or SegmentState is currently playing.
  5735. @rvalue E_POINTER | Both pSegment and pSegState are null, or one or both are invalid.
  5736. @rvalue DMUS_E_NO_MASTER_CLOCK | There is no master clock in the performance.
  5737. Make sure to call <om .Init> before calling this method.
  5738. @rvalue S_OK | Yes, it is playing.
  5739. @rvalue S_FALSE | No, it is not playing.
  5740. */
  5741. HRESULT STDMETHODCALLTYPE CPerformance::IsPlaying(
  5742. IDirectMusicSegment *pSegment, // @parm The Segment to check. If NULL, check
  5743. // <p pSegState>.
  5744. IDirectMusicSegmentState *pSegState) // @parm The SegmentState to check. If NULL,
  5745. // check <p pSegment>.
  5746. {
  5747. CSegState* pNode;
  5748. DWORD dwCount;
  5749. V_INAME(IDirectMusicPerformance::IsPlaying);
  5750. V_INTERFACE_OPT(pSegment);
  5751. V_INTERFACE_OPT(pSegState);
  5752. EnterCriticalSection(&m_MainCrSec);
  5753. if( m_pClock == NULL )
  5754. {
  5755. LeaveCriticalSection(&m_MainCrSec);
  5756. Trace(1,"Error: IsPlaying() failed because the performance has not been initialized.\n");
  5757. return DMUS_E_NO_MASTER_CLOCK;
  5758. }
  5759. LeaveCriticalSection(&m_MainCrSec);
  5760. if( !pSegment && !pSegState )
  5761. {
  5762. Trace(1,"Error: IsPlaying() failed because segment and segment state are both NULL pointers.\n");
  5763. return E_POINTER;
  5764. }
  5765. MUSIC_TIME mtNow;
  5766. GetTime(NULL, &mtNow);
  5767. EnterCriticalSection(&m_SegmentCrSec);
  5768. for( dwCount = 0; dwCount < SQ_COUNT; dwCount++ )
  5769. {
  5770. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext() )
  5771. {
  5772. if( !pNode->m_fStartedPlay )
  5773. {
  5774. continue;
  5775. }
  5776. if( mtNow >= pNode->m_mtResolvedStart )
  5777. {
  5778. if( mtNow < pNode->m_mtLastPlayed )
  5779. {
  5780. if(( pNode == (CSegState*) pSegState ) ||
  5781. ( pNode->m_pSegment == (CSegment *) pSegment ))
  5782. {
  5783. LeaveCriticalSection(&m_SegmentCrSec);
  5784. return S_OK;
  5785. }
  5786. }
  5787. }
  5788. else
  5789. {
  5790. // if mtNow is before this pSegState's resolved start, it is before every
  5791. // pSegState after this too, so break now.
  5792. break;
  5793. }
  5794. }
  5795. }
  5796. LeaveCriticalSection(&m_SegmentCrSec);
  5797. return S_FALSE;
  5798. }
  5799. HRESULT STDMETHODCALLTYPE CPerformance::GetTime(
  5800. REFERENCE_TIME *prtNow, // @parm Returns the current time in REFERENCE_TIME
  5801. // format. May be NULL.
  5802. MUSIC_TIME *pmtNow) // @parm Returns the current time in MUSIC_TIME
  5803. // format. May be NULL.
  5804. {
  5805. V_INAME(IDirectMusicPerformance::GetTime);
  5806. V_PTR_WRITE_OPT(prtNow,REFERENCE_TIME);
  5807. V_PTR_WRITE_OPT(pmtNow,MUSIC_TIME);
  5808. EnterCriticalSection(&m_MainCrSec);
  5809. if( m_pClock == NULL )
  5810. {
  5811. LeaveCriticalSection(&m_MainCrSec);
  5812. Trace(1,"Error: GetTime() failed because the performance has not been initialized.\n");
  5813. return DMUS_E_NO_MASTER_CLOCK;
  5814. }
  5815. LeaveCriticalSection(&m_MainCrSec);
  5816. REFERENCE_TIME rtTime = GetTime();
  5817. if( prtNow )
  5818. {
  5819. *prtNow = rtTime;
  5820. }
  5821. if( pmtNow )
  5822. {
  5823. MUSIC_TIME mtTime;
  5824. ReferenceToMusicTime( rtTime, &mtTime );
  5825. *pmtNow = mtTime;
  5826. }
  5827. return S_OK;
  5828. }
  5829. HRESULT STDMETHODCALLTYPE CPerformance::GetLatencyTime(
  5830. REFERENCE_TIME *prtTime) // @parm Returns the current latency time.
  5831. {
  5832. V_INAME(IDirectMusicPerformance::GetLatencyTime);
  5833. V_PTR_WRITE(prtTime,REFERENCE_TIME);
  5834. EnterCriticalSection(&m_MainCrSec);
  5835. if( m_pClock == NULL )
  5836. {
  5837. LeaveCriticalSection(&m_MainCrSec);
  5838. Trace(1,"Error: GetLatencyTime() failed because the performance has not been initialized.\n");
  5839. return DMUS_E_NO_MASTER_CLOCK;
  5840. }
  5841. LeaveCriticalSection(&m_MainCrSec);
  5842. *prtTime = GetLatency();
  5843. return S_OK;
  5844. }
  5845. HRESULT STDMETHODCALLTYPE CPerformance::GetQueueTime(
  5846. REFERENCE_TIME *prtTime) // @parm Returns the current queue time.
  5847. {
  5848. V_INAME(IDirectMusicPerformance::GetQueueTime);
  5849. V_PTR_WRITE(prtTime,REFERENCE_TIME);
  5850. EnterCriticalSection(&m_MainCrSec);
  5851. if( m_pClock == NULL )
  5852. {
  5853. LeaveCriticalSection(&m_MainCrSec);
  5854. Trace(1,"Error: GetQueueTime() failed because the performance has not been initialized.\n");
  5855. return DMUS_E_NO_MASTER_CLOCK;
  5856. }
  5857. LeaveCriticalSection(&m_MainCrSec);
  5858. DWORD dw;
  5859. REFERENCE_TIME rtLatency;
  5860. *prtTime = 0;
  5861. EnterCriticalSection(&m_PChannelInfoCrSec);
  5862. for( dw = 0; dw < m_dwNumPorts; dw++ )
  5863. {
  5864. if( m_pPortTable[dw].rtLast > *prtTime )
  5865. *prtTime = m_pPortTable[dw].rtLast;
  5866. }
  5867. LeaveCriticalSection(&m_PChannelInfoCrSec);
  5868. rtLatency = GetLatency();
  5869. if( *prtTime < rtLatency )
  5870. {
  5871. *prtTime = rtLatency;
  5872. }
  5873. if (m_rtEarliestStartTime > rtLatency)
  5874. {
  5875. rtLatency = m_rtEarliestStartTime;
  5876. }
  5877. return S_OK;
  5878. }
  5879. // private version of AllocPMsg
  5880. HRESULT CPerformance::AllocPMsg(
  5881. ULONG cb,
  5882. PRIV_PMSG** ppPMSG)
  5883. {
  5884. ASSERT( cb >= sizeof(PRIV_PMSG) );
  5885. DMUS_PMSG* pDMUS_PMSG;
  5886. HRESULT hr;
  5887. hr = AllocPMsg( cb - PRIV_PART_SIZE, &pDMUS_PMSG );
  5888. if( SUCCEEDED(hr) )
  5889. {
  5890. *ppPMSG = DMUS_TO_PRIV(pDMUS_PMSG);
  5891. }
  5892. return hr;
  5893. }
  5894. HRESULT STDMETHODCALLTYPE CPerformance::ClonePMsg(DMUS_PMSG* pSourcePMSG,DMUS_PMSG** ppCopyPMSG)
  5895. {
  5896. V_INAME(IDirectMusicPerformance::ClonePMsg);
  5897. #ifdef DBG
  5898. V_PTRPTR_WRITE(ppCopyPMSG);
  5899. V_BUFPTR_READ(pSourcePMSG,sizeof(DMUS_PMSG));
  5900. #else
  5901. if (!ppCopyPMSG || !pSourcePMSG)
  5902. {
  5903. return E_POINTER;
  5904. }
  5905. #endif
  5906. HRESULT hr = AllocPMsg(pSourcePMSG->dwSize,ppCopyPMSG);
  5907. if (SUCCEEDED(hr))
  5908. {
  5909. memcpy(*ppCopyPMSG,pSourcePMSG,pSourcePMSG->dwSize);
  5910. if (pSourcePMSG->punkUser)
  5911. {
  5912. pSourcePMSG->punkUser->AddRef();
  5913. }
  5914. if (pSourcePMSG->pTool)
  5915. {
  5916. pSourcePMSG->pTool->AddRef();
  5917. }
  5918. if (pSourcePMSG->pGraph)
  5919. {
  5920. pSourcePMSG->pGraph->AddRef();
  5921. }
  5922. }
  5923. return hr;
  5924. }
  5925. //////////////////////////////////////////////////////////////////////
  5926. // CPerformance::AllocPMsg
  5927. /*
  5928. @method HRESULT | IDirectMusicPerformance | AllocPMsg |
  5929. Allocate a DMUS_PMSG.
  5930. @rvalue E_OUTOFMEMORY | Out of memory.
  5931. @rvalue S_OK | Success.
  5932. @rvalue E_INVALIDARG | <p cb> is smaller than sizeof(DMUS_PMSG)
  5933. @rvalue E_POINTER | <p ppPMSG> is NULL or invalid.
  5934. */
  5935. HRESULT STDMETHODCALLTYPE CPerformance::AllocPMsg(
  5936. ULONG cb, // @parm Size of the <p ppPMSG>. Must be equal to or greater
  5937. // than sizeof(DMUS_PMSG).
  5938. DMUS_PMSG** ppPMSG // @parm Returns the pointer to the allocated message, which will
  5939. // be of size <p cb>. All fields are initialized to zero,
  5940. // except dwSize which is initialized to <p cb>.
  5941. )
  5942. {
  5943. V_INAME(IDirectMusicPerformance::AllocPMsg);
  5944. if( m_dwVersion < 8)
  5945. {
  5946. V_PTRPTR_WRITE(ppPMSG);
  5947. }
  5948. else
  5949. {
  5950. #ifdef DBG
  5951. V_PTRPTR_WRITE(ppPMSG);
  5952. #else
  5953. if (!ppPMSG)
  5954. {
  5955. return E_POINTER;
  5956. }
  5957. #endif
  5958. }
  5959. PRIV_PMSG* pPrivPMsg;
  5960. if( cb < sizeof(DMUS_PMSG) )
  5961. return E_INVALIDARG;
  5962. EnterCriticalSection(&m_PMsgCacheCrSec);
  5963. // cached pmsg's are stored in an array based on their public size.
  5964. // If a cached pmsg exists, return it. Otherwise, make a new one.
  5965. if( (cb >= PERF_PMSG_CB_MIN) && (cb < PERF_PMSG_CB_MAX) )
  5966. {
  5967. ULONG cbIndex = cb - PERF_PMSG_CB_MIN;
  5968. if( m_apPMsgCache[ cbIndex ] )
  5969. {
  5970. pPrivPMsg = m_apPMsgCache[ cbIndex ];
  5971. m_apPMsgCache[ cbIndex ] = pPrivPMsg->pNext;
  5972. pPrivPMsg->pNext = NULL;
  5973. if (pPrivPMsg->dwPrivFlags != PRIV_FLAG_FREE)
  5974. {
  5975. Trace(0,"Error - previously freed PMsg has been mangled.\n");
  5976. LeaveCriticalSection(&m_PMsgCacheCrSec);
  5977. return E_FAIL;
  5978. }
  5979. pPrivPMsg->dwPrivFlags = PRIV_FLAG_ALLOC;
  5980. if (m_fInTrackPlay) pPrivPMsg->dwPrivFlags |= PRIV_FLAG_TRACK;
  5981. *ppPMSG = PRIV_TO_DMUS(pPrivPMsg);
  5982. LeaveCriticalSection(&m_PMsgCacheCrSec);
  5983. return S_OK;
  5984. }
  5985. }
  5986. HRESULT hr = S_OK;
  5987. // no cached pmsg exists. Return a new one.
  5988. ULONG cbPriv = cb + PRIV_PART_SIZE;
  5989. pPrivPMsg = (PRIV_PMSG*)(new char[cbPriv]);
  5990. if( pPrivPMsg )
  5991. {
  5992. memset( pPrivPMsg, 0, cbPriv );
  5993. pPrivPMsg->dwSize = pPrivPMsg->dwPrivPubSize = cb; // size of public part only
  5994. pPrivPMsg->dwPrivFlags = PRIV_FLAG_ALLOC;
  5995. if (m_fInTrackPlay) pPrivPMsg->dwPrivFlags |= PRIV_FLAG_TRACK;
  5996. *ppPMSG = PRIV_TO_DMUS(pPrivPMsg);
  5997. hr = S_OK;
  5998. }
  5999. else
  6000. {
  6001. hr = E_OUTOFMEMORY;
  6002. }
  6003. LeaveCriticalSection(&m_PMsgCacheCrSec);
  6004. return hr;
  6005. }
  6006. // private version of FreePMsg
  6007. HRESULT CPerformance::FreePMsg(
  6008. PRIV_PMSG* pPMSG)
  6009. {
  6010. return FreePMsg( PRIV_TO_DMUS(pPMSG) );
  6011. }
  6012. HRESULT STDMETHODCALLTYPE CPerformance::FreePMsg(
  6013. DMUS_PMSG* pPMSG // @parm The message to free. This message must have been allocated
  6014. // using <om .AllocPMsg>.
  6015. )
  6016. {
  6017. V_INAME(IDirectMusicPerformance::FreePMsg);
  6018. if( m_dwVersion < 8)
  6019. {
  6020. V_BUFPTR_WRITE(pPMSG,sizeof(DMUS_PMSG));
  6021. }
  6022. else
  6023. {
  6024. #ifdef DBG
  6025. V_BUFPTR_WRITE(pPMSG,sizeof(DMUS_PMSG));
  6026. #else
  6027. if (!pPMSG)
  6028. {
  6029. return E_POINTER;
  6030. }
  6031. #endif
  6032. }
  6033. PRIV_PMSG* pPrivPMsg = DMUS_TO_PRIV(pPMSG);
  6034. if( (pPrivPMsg->dwPrivFlags & PRIV_FLAG_ALLOC_MASK) != PRIV_FLAG_ALLOC )
  6035. {
  6036. Trace(0, "Error --- Attempt to free a PMsg that is not allocated memory.\n");
  6037. // this isn't a msg allocated by AllocPMsg.
  6038. return DMUS_E_CANNOT_FREE;
  6039. }
  6040. if( pPrivPMsg->dwPrivFlags & PRIV_FLAG_QUEUED )
  6041. {
  6042. TraceI(1, "Attempt to free a PMsg that is currently in the Performance queue.\n");
  6043. return DMUS_E_CANNOT_FREE;
  6044. }
  6045. EnterCriticalSection(&m_PMsgCacheCrSec);
  6046. if( pPMSG->pTool )
  6047. {
  6048. pPMSG->pTool->Release();
  6049. }
  6050. if( pPMSG->pGraph )
  6051. {
  6052. pPMSG->pGraph->Release();
  6053. }
  6054. if( pPMSG->punkUser )
  6055. {
  6056. pPMSG->punkUser->Release();
  6057. }
  6058. ULONG cbSize = pPrivPMsg->dwPrivPubSize;
  6059. if( (cbSize >= PERF_PMSG_CB_MIN) && (cbSize < PERF_PMSG_CB_MAX) )
  6060. {
  6061. memset( pPrivPMsg, 0, cbSize + PRIV_PART_SIZE );
  6062. pPrivPMsg->dwPrivFlags = PRIV_FLAG_FREE; // Mark this as in the free queue.
  6063. pPrivPMsg->dwSize = pPrivPMsg->dwPrivPubSize = cbSize;
  6064. pPrivPMsg->pNext = m_apPMsgCache[ cbSize - PERF_PMSG_CB_MIN ];
  6065. m_apPMsgCache[ cbSize - PERF_PMSG_CB_MIN ] = pPrivPMsg;
  6066. }
  6067. else
  6068. {
  6069. delete [] pPrivPMsg;
  6070. }
  6071. LeaveCriticalSection(&m_PMsgCacheCrSec);
  6072. return S_OK;
  6073. }
  6074. HRESULT CPerformance::FlushVirtualTrack(
  6075. DWORD dwId,
  6076. MUSIC_TIME mtTime,
  6077. BOOL fLeaveNotesOn)
  6078. {
  6079. EnterCriticalSection(&m_PipelineCrSec);
  6080. FlushMainEventQueues( dwId, mtTime, mtTime, fLeaveNotesOn );
  6081. LeaveCriticalSection(&m_PipelineCrSec);
  6082. return S_OK;
  6083. }
  6084. /*
  6085. Given a time, mtTime, returns the time of the next control segment in pmtNextSeg.
  6086. Returns S_FALSE if none found, and sets pmtNextSeg to zero.
  6087. */
  6088. HRESULT CPerformance::GetControlSegTime(
  6089. MUSIC_TIME mtTime,
  6090. MUSIC_TIME* pmtNextSeg)
  6091. {
  6092. HRESULT hr = S_FALSE;
  6093. *pmtNextSeg = 0;
  6094. EnterCriticalSection( &m_SegmentCrSec );
  6095. // search the secondary lists for a control segment
  6096. CSegState* pTemp;
  6097. for( pTemp = m_SegStateQueues[SQ_CON_DONE].GetHead(); pTemp; pTemp = pTemp->GetNext() )
  6098. {
  6099. if( pTemp->m_mtResolvedStart >= mtTime )
  6100. {
  6101. *pmtNextSeg = pTemp->m_mtResolvedStart;
  6102. hr = S_OK;
  6103. break;
  6104. }
  6105. }
  6106. if( S_FALSE == hr ) // if this is still zero, check the current queue
  6107. {
  6108. for( pTemp = m_SegStateQueues[SQ_CON_PLAY].GetHead(); pTemp; pTemp = pTemp->GetNext() )
  6109. {
  6110. if( pTemp->m_mtResolvedStart >= mtTime )
  6111. {
  6112. *pmtNextSeg = pTemp->m_mtResolvedStart;
  6113. hr = S_OK;
  6114. break;
  6115. }
  6116. }
  6117. }
  6118. LeaveCriticalSection( &m_SegmentCrSec );
  6119. return hr;
  6120. }
  6121. /*
  6122. Given a time, mtTime, returns the time of the next primary segment in pmtNextSeg.
  6123. Returns S_FALSE if none found, and sets pmtNextSeg to zero.
  6124. */
  6125. HRESULT CPerformance::GetPriSegTime(
  6126. MUSIC_TIME mtTime,
  6127. MUSIC_TIME* pmtNextSeg)
  6128. {
  6129. HRESULT hr = S_FALSE;
  6130. *pmtNextSeg = 0;
  6131. EnterCriticalSection( &m_SegmentCrSec );
  6132. CSegState* pTemp;
  6133. for( pTemp = m_SegStateQueues[SQ_PRI_PLAY].GetHead(); pTemp; pTemp = pTemp->GetNext() )
  6134. {
  6135. if( pTemp->m_mtResolvedStart > mtTime )
  6136. {
  6137. *pmtNextSeg = pTemp->m_mtResolvedStart;
  6138. hr = S_OK;
  6139. break;
  6140. }
  6141. }
  6142. LeaveCriticalSection( &m_SegmentCrSec );
  6143. return hr;
  6144. }
  6145. /*
  6146. @method HRESULT | IDirectMusicPerformance | GetGraph |
  6147. Returns the performance's Tool Graph, AddRef'd.
  6148. @rvalue S_OK | Success.
  6149. @rvalue DMUS_E_NOT_FOUND | There is no graph in the performance, and therefore
  6150. one couldn't be returned.
  6151. @rvalue E_POINTER | <p ppGraph> is NULL or invalid.
  6152. */
  6153. HRESULT STDMETHODCALLTYPE CPerformance::GetGraph(
  6154. IDirectMusicGraph** ppGraph // @parm Returns the tool graph pointer.
  6155. )
  6156. {
  6157. V_INAME(IDirectMusicPerformance::GetGraph);
  6158. V_PTRPTR_WRITE(ppGraph);
  6159. HRESULT hr;
  6160. if ((m_dwVersion >= 8) && (m_dwAudioPathMode == 0))
  6161. {
  6162. Trace(1,"Error: Performance not initialized.\n");
  6163. return DMUS_E_NOT_INIT;
  6164. }
  6165. EnterCriticalSection(&m_MainCrSec);
  6166. if( m_pGraph )
  6167. {
  6168. *ppGraph = m_pGraph;
  6169. m_pGraph->AddRef();
  6170. hr = S_OK;
  6171. }
  6172. else
  6173. {
  6174. Trace(1,"Error: Performance does not currently have a tool graph installed.\n");
  6175. hr = DMUS_E_NOT_FOUND;
  6176. }
  6177. LeaveCriticalSection(&m_MainCrSec);
  6178. return hr;
  6179. }
  6180. HRESULT CPerformance::GetGraphInternal(
  6181. IDirectMusicGraph** ppGraph )
  6182. {
  6183. EnterCriticalSection(&m_MainCrSec);
  6184. if( !m_pGraph )
  6185. {
  6186. m_pGraph = new CGraph;
  6187. }
  6188. LeaveCriticalSection(&m_MainCrSec);
  6189. return GetGraph(ppGraph);
  6190. }
  6191. /*
  6192. @method HRESULT | IDirectMusicPerformance | SetGraph |
  6193. Replaces the performance's Tool Graph. <p pGraph> is AddRef'd inside this
  6194. method. Any messages flowing through Tools in the current Tool Graph are deleted.
  6195. @rvalue S_OK | Success.
  6196. @rvalue E_POINTER | <p pGraph> is invalid.
  6197. */
  6198. HRESULT STDMETHODCALLTYPE CPerformance::SetGraph(
  6199. IDirectMusicGraph* pGraph // @parm The tool graph pointer. May be NULL to clear
  6200. // the current graph out of the performance.
  6201. )
  6202. {
  6203. V_INAME(IDirectMusicPerformance::SetGraph);
  6204. V_INTERFACE_OPT(pGraph);
  6205. if ((m_dwVersion >= 8) && (m_dwAudioPathMode == 0))
  6206. {
  6207. Trace(1,"Error: Performance not initialized.\n");
  6208. return DMUS_E_NOT_INIT;
  6209. }
  6210. EnterCriticalSection(&m_MainCrSec);
  6211. if( m_pGraph )
  6212. {
  6213. m_pGraph->Release();
  6214. }
  6215. m_pGraph = pGraph;
  6216. if( pGraph )
  6217. {
  6218. pGraph->AddRef();
  6219. }
  6220. LeaveCriticalSection(&m_MainCrSec);
  6221. return S_OK;
  6222. }
  6223. HRESULT STDMETHODCALLTYPE CPerformance::SetNotificationHandle(
  6224. HANDLE hNotification, // @parm The event handle created by CreateEvent, or
  6225. // 0 to clear out an existing handle.
  6226. REFERENCE_TIME rtMinimum ) // @parm The minimum amount of time that the
  6227. // performance should hold notify messages before discarding them.
  6228. // 0 means to use the default minimum time of 20000000 reference time units,
  6229. // which is 2 seconds, or the previous value if this API has been called previously.
  6230. // If the application hasn't called <om .GetNotificationPMsg> by this time, the message is
  6231. // discarded to free the memory.
  6232. {
  6233. EnterCriticalSection(&m_MainCrSec);
  6234. m_hNotification = hNotification;
  6235. if( rtMinimum )
  6236. {
  6237. m_rtNotificationDiscard = rtMinimum;
  6238. }
  6239. LeaveCriticalSection(&m_MainCrSec);
  6240. return S_OK;
  6241. }
  6242. HRESULT STDMETHODCALLTYPE CPerformance::GetNotificationPMsg(
  6243. DMUS_NOTIFICATION_PMSG** ppNotificationPMsg )
  6244. {
  6245. V_INAME(IDirectMusicPerformance::GetNotificationPMsg);
  6246. V_PTRPTR_WRITE(ppNotificationPMsg);
  6247. HRESULT hr;
  6248. EnterCriticalSection(&m_PipelineCrSec);
  6249. if( m_NotificationQueue.GetHead() )
  6250. {
  6251. PRIV_PMSG* pPriv = m_NotificationQueue.Dequeue();
  6252. ASSERT(pPriv);
  6253. *ppNotificationPMsg = (DMUS_NOTIFICATION_PMSG*)PRIV_TO_DMUS(pPriv);
  6254. hr = S_OK;
  6255. }
  6256. else
  6257. {
  6258. *ppNotificationPMsg = NULL;
  6259. hr = S_FALSE;
  6260. }
  6261. LeaveCriticalSection(&m_PipelineCrSec);
  6262. return hr;
  6263. }
  6264. void CPerformance::AddNotificationTypeToAllSegments( REFGUID rguidNotification )
  6265. {
  6266. CSegState* pSegSt;
  6267. DWORD dwCount;
  6268. // Note: might be nice to optimize this so the same segment
  6269. // doesn't get called multiple times
  6270. EnterCriticalSection(&m_SegmentCrSec);
  6271. for (dwCount = 0; dwCount < SQ_COUNT; dwCount++)
  6272. {
  6273. for( pSegSt = m_SegStateQueues[dwCount].GetHead(); pSegSt; pSegSt = pSegSt->GetNext() )
  6274. {
  6275. pSegSt->m_pSegment->AddNotificationType( rguidNotification, TRUE );
  6276. }
  6277. }
  6278. LeaveCriticalSection(&m_SegmentCrSec);
  6279. }
  6280. void CPerformance::RemoveNotificationTypeFromAllSegments( REFGUID rguidNotification )
  6281. {
  6282. CSegState* pSegSt;
  6283. DWORD dwCount;
  6284. // Note: might be nice to optimize this so the same segment
  6285. // doesn't get called multiple times
  6286. EnterCriticalSection(&m_SegmentCrSec);
  6287. for (dwCount = 0; dwCount < SQ_COUNT; dwCount++)
  6288. {
  6289. for( pSegSt = m_SegStateQueues[dwCount].GetHead(); pSegSt; pSegSt = pSegSt->GetNext() )
  6290. {
  6291. pSegSt->m_pSegment->RemoveNotificationType( rguidNotification, TRUE );
  6292. }
  6293. }
  6294. LeaveCriticalSection(&m_SegmentCrSec);
  6295. }
  6296. /*
  6297. Check to see if this notification is already being tracked.
  6298. */
  6299. CNotificationItem* CPerformance::FindNotification( REFGUID rguidNotification )
  6300. {
  6301. CNotificationItem* pItem;
  6302. pItem = m_NotificationList.GetHead();
  6303. while(pItem)
  6304. {
  6305. if( rguidNotification == pItem->guidNotificationType )
  6306. {
  6307. break;
  6308. }
  6309. pItem = pItem->GetNext();
  6310. }
  6311. return pItem;
  6312. }
  6313. /*
  6314. @method HRESULT | IDirectMusicPerformance | AddNotificationType |
  6315. Adds a notification type to the performance. Notifications are identified
  6316. by a guid. When a notification is added to the performance, notify messages
  6317. are sent to the application, which provides a message handle on which to
  6318. block through <om IDirectMusicPerformance.SetNotificationHandle>. All segments
  6319. and tracks are automatically updated with the new notification by calling
  6320. their AddNotificationType methods.
  6321. @rvalue S_OK | Success.
  6322. @rvalue S_FALSE | The requested notification is already on the performance.
  6323. @rvalue E_OUTOFMEMORY | Out of memory.
  6324. @xref <om .SetNotificationHandle>, <om .GetNotificationPMsg>, <om .RemoveNotificationType>
  6325. */
  6326. HRESULT STDMETHODCALLTYPE CPerformance::AddNotificationType(
  6327. REFGUID rguidNotification) // @parm The guid of the notification message to add.
  6328. {
  6329. V_INAME(IDirectMusicPerformance::AddNotificationType);
  6330. V_REFGUID(rguidNotification);
  6331. CNotificationItem* pItem;
  6332. HRESULT hr = S_OK;
  6333. EnterCriticalSection(&m_SegmentCrSec);
  6334. if( NULL == FindNotification( rguidNotification ) )
  6335. {
  6336. pItem = new CNotificationItem;
  6337. if( NULL == pItem )
  6338. {
  6339. hr = E_OUTOFMEMORY;
  6340. }
  6341. else
  6342. {
  6343. pItem->guidNotificationType = rguidNotification;
  6344. m_NotificationList.Cat( pItem );
  6345. AddNotificationTypeToAllSegments( rguidNotification );
  6346. }
  6347. }
  6348. else
  6349. {
  6350. hr = S_FALSE;
  6351. }
  6352. LeaveCriticalSection(&m_SegmentCrSec);
  6353. return hr;
  6354. }
  6355. /*
  6356. @method HRESULT | IDirectMusicPerformance | RemoveNotificationType |
  6357. Removes a previously added notification type from the performance. All
  6358. segments and tracks are updated with the removed notification by calling
  6359. their RemoveNotificationType methods.
  6360. @rvalue S_OK | Success.
  6361. @rvalue S_FALSE | The requested notification isn't currently active.
  6362. @xref <om .SetNotificationHandle>, <om .GetNotificationPMsg>, <om .AddNotificationType>
  6363. */
  6364. HRESULT STDMETHODCALLTYPE CPerformance::RemoveNotificationType(
  6365. REFGUID rguidNotification) // @parm The guid of the notification message to remove.
  6366. // If GUID_NULL, remove all notifications.
  6367. {
  6368. V_INAME(IDirectMusicPerformance::RemoveNotificationType);
  6369. V_REFGUID(rguidNotification);
  6370. HRESULT hr = S_OK;
  6371. CNotificationItem* pItem;
  6372. if( GUID_NULL == rguidNotification )
  6373. {
  6374. while (pItem = m_NotificationList.RemoveHead())
  6375. {
  6376. RemoveNotificationTypeFromAllSegments( pItem->guidNotificationType );
  6377. delete pItem;
  6378. }
  6379. }
  6380. else
  6381. {
  6382. if( pItem = FindNotification( rguidNotification ))
  6383. {
  6384. RemoveNotificationTypeFromAllSegments( pItem->guidNotificationType );
  6385. m_NotificationList.Remove( pItem );
  6386. delete pItem;
  6387. }
  6388. else
  6389. {
  6390. Trace(2,"Warning: Unable to remove requested notification because it is not currently installed.\n");
  6391. hr = S_FALSE;
  6392. }
  6393. }
  6394. return hr;
  6395. }
  6396. void CPerformance::RemoveUnusedPorts()
  6397. {
  6398. DWORD dwIndex;
  6399. EnterCriticalSection(&m_PChannelInfoCrSec);
  6400. for( dwIndex = 0; dwIndex < m_dwNumPorts; dwIndex++ )
  6401. {
  6402. if( m_pPortTable[dwIndex].pPort && !m_AudioPathList.UsesPort(m_pPortTable[dwIndex].pPort))
  6403. {
  6404. // release the port and buffer. NULL them in the table. PChannels
  6405. // that map will return an error code.
  6406. ASSERT( m_pPortTable[dwIndex].pBuffer );
  6407. m_pPortTable[dwIndex].pPort->Release();
  6408. m_pPortTable[dwIndex].pBuffer->Release();
  6409. if( m_pPortTable[dwIndex].pLatencyClock )
  6410. {
  6411. m_pPortTable[dwIndex].pLatencyClock->Release();
  6412. }
  6413. memset( &m_pPortTable[dwIndex], 0, sizeof( PortTable ));
  6414. CChannelBlock *pBlock = m_ChannelBlockList.GetHead();
  6415. CChannelBlock *pNext;
  6416. for(;pBlock;pBlock = pNext)
  6417. {
  6418. pNext = pBlock->GetNext();
  6419. if (pBlock->m_dwPortIndex == dwIndex)
  6420. {
  6421. m_ChannelBlockList.Remove(pBlock);
  6422. delete pBlock;
  6423. }
  6424. }
  6425. }
  6426. }
  6427. LeaveCriticalSection(&m_PChannelInfoCrSec);
  6428. }
  6429. HRESULT CPerformance::GetPathPort(CPortConfig *pConfig)
  6430. {
  6431. HRESULT hr = S_OK;
  6432. DWORD dwPort;
  6433. EnterCriticalSection(&m_PChannelInfoCrSec);
  6434. GUID &guidScan = pConfig->m_PortHeader.guidPort;
  6435. // If we are looking for the default synth, get the class id for the default synth.
  6436. BOOL fDefault = (pConfig->m_PortHeader.guidPort == GUID_Synth_Default);
  6437. if (fDefault)
  6438. {
  6439. guidScan = m_AudioParams.clsidDefaultSynth;
  6440. }
  6441. for (dwPort = 0;dwPort < m_dwNumPorts;dwPort++)
  6442. {
  6443. if ((m_pPortTable[dwPort].guidPortID == guidScan) && m_pPortTable[dwPort].pPort)
  6444. {
  6445. pConfig->m_dwPortID = dwPort;
  6446. pConfig->m_pPort = m_pPortTable[dwPort].pPort;
  6447. pConfig->m_PortParams = m_pPortTable[dwPort].PortParams;
  6448. ASSERT(pConfig->m_pPort);
  6449. pConfig->m_pPort->AddRef();
  6450. break;
  6451. }
  6452. }
  6453. LeaveCriticalSection(&m_PChannelInfoCrSec);
  6454. // Failed finding the port, so create it.
  6455. if (dwPort >= m_dwNumPorts)
  6456. {
  6457. BOOL fUseBuffers = FALSE;
  6458. pConfig->m_PortParams.dwSampleRate = m_AudioParams.dwSampleRate;
  6459. if (m_AudioParams.dwFeatures & DMUS_AUDIOF_STREAMING)
  6460. {
  6461. pConfig->m_PortParams.dwFeatures |= DMUS_PORT_FEATURE_STREAMING;
  6462. }
  6463. if (m_AudioParams.dwFeatures & DMUS_AUDIOF_BUFFERS)
  6464. {
  6465. fUseBuffers = TRUE;
  6466. pConfig->m_PortParams.dwFeatures |= DMUS_PORT_FEATURE_AUDIOPATH;
  6467. }
  6468. pConfig->m_PortParams.dwValidParams |= DMUS_PORTPARAMS_SAMPLERATE | DMUS_PORTPARAMS_FEATURES;
  6469. // If this wants a default synth, consult m_AudioParams and create that synth.
  6470. if (fDefault)
  6471. {
  6472. pConfig->m_PortParams.dwAudioChannels = 1;
  6473. pConfig->m_PortParams.dwVoices = m_AudioParams.dwVoices;
  6474. pConfig->m_PortParams.dwValidParams |= DMUS_PORTPARAMS_AUDIOCHANNELS | DMUS_PORTPARAMS_VOICES;
  6475. }
  6476. hr = m_pDirectMusic->CreatePort(guidScan,&pConfig->m_PortParams,&pConfig->m_pPort, NULL);
  6477. if (SUCCEEDED(hr))
  6478. {
  6479. if ((pConfig->m_PortParams.dwValidParams & DMUS_PORTPARAMS_FEATURES) && (pConfig->m_PortParams.dwFeatures & DMUS_PORT_FEATURE_AUDIOPATH))
  6480. {
  6481. IDirectMusicPortP* pPortP = NULL;
  6482. // QI for the private interface.
  6483. if (SUCCEEDED(pConfig->m_pPort->QueryInterface(IID_IDirectMusicPortP,(void **) &pPortP)))
  6484. {
  6485. // Connect the port to the sink.
  6486. hr = pPortP->SetSink(m_BufferManager.m_pSinkConnect);
  6487. pPortP->Release();
  6488. }
  6489. else
  6490. {
  6491. Trace(1,"Error: Attempt to create a port with audiopath buffer support failed because synth does not support buffers.\n");
  6492. hr = E_INVALIDARG;
  6493. }
  6494. }
  6495. else if (fUseBuffers && fDefault)
  6496. {
  6497. Trace(1,"Error: Attempt to create a port with audiopath buffer support failed because default synth does not support buffers.\n");
  6498. hr = E_INVALIDARG;
  6499. }
  6500. }
  6501. if (SUCCEEDED(hr))
  6502. {
  6503. // Now add the port to the performance.
  6504. hr = AddPort(pConfig->m_pPort,&pConfig->m_PortHeader.guidPort,
  6505. &pConfig->m_PortParams,&pConfig->m_dwPortID);
  6506. }
  6507. if (SUCCEEDED(hr))
  6508. {
  6509. // Activate the port.
  6510. hr = pConfig->m_pPort->Activate(TRUE);
  6511. // It's okay if the synth is already active.
  6512. if (hr == DMUS_E_SYNTHACTIVE)
  6513. {
  6514. hr = S_OK;
  6515. }
  6516. }
  6517. if (SUCCEEDED(hr))
  6518. {
  6519. DWORD dwPortID = GetPortID(pConfig->m_pPort);
  6520. // Then create matching channel blocks for all of the channel groups in the port.
  6521. for (DWORD dwGroup = 0;dwGroup < pConfig->m_PortParams.dwChannelGroups; dwGroup++)
  6522. {
  6523. AllocVChannelBlock(dwPortID,dwGroup+1);
  6524. }
  6525. }
  6526. }
  6527. return (hr);
  6528. }
  6529. HRESULT STDMETHODCALLTYPE CPerformance::AddPort(
  6530. IDirectMusicPort* pPort)
  6531. {
  6532. V_INAME(IDirectMusicPerformance::AddPort);
  6533. V_INTERFACE_OPT(pPort);
  6534. if (m_dwAudioPathMode == 2)
  6535. {
  6536. Trace(1,"Error: Can not call AddPort() when using AudioPaths.\n");
  6537. return DMUS_E_AUDIOPATHS_IN_USE;
  6538. }
  6539. m_dwAudioPathMode = 1;
  6540. return AddPort(pPort,NULL,NULL,NULL);
  6541. }
  6542. HRESULT CPerformance::AddPort(
  6543. IDirectMusicPort* pPort,
  6544. GUID *pguidPortID,
  6545. DMUS_PORTPARAMS8 *pParams,
  6546. DWORD *pdwPortID)
  6547. {
  6548. PortTable* pPortTable;
  6549. IDirectMusicBuffer* pBuffer;
  6550. BOOL fSetUpBlock = FALSE;
  6551. BOOL fBuiltNewTable = FALSE;
  6552. HRESULT hr = S_OK;
  6553. GUID guidPortID; // Class ID of port.
  6554. DWORD dwChannelGroups; // Number of channel groups at initialization.
  6555. DWORD dwNewPortIndex = 0; // Index into port array for new port.
  6556. EnterCriticalSection(&m_MainCrSec);
  6557. EnterCriticalSection(&m_PChannelInfoCrSec);
  6558. if( NULL == m_pDirectMusic )
  6559. {
  6560. Trace(1,"Error: Performance is not initialized, ports can not be added.\n");
  6561. hr = DMUS_E_NOT_INIT;
  6562. goto END;
  6563. }
  6564. for (;dwNewPortIndex < m_dwNumPorts; dwNewPortIndex++)
  6565. {
  6566. if (!m_pPortTable[dwNewPortIndex].pPort)
  6567. {
  6568. break;
  6569. }
  6570. }
  6571. if (dwNewPortIndex == m_dwNumPorts)
  6572. {
  6573. pPortTable = new PortTable[m_dwNumPorts + 1];
  6574. if( !pPortTable )
  6575. {
  6576. hr = E_OUTOFMEMORY;
  6577. goto END;
  6578. }
  6579. fBuiltNewTable = TRUE;
  6580. }
  6581. // if pPort is NULL, create a software synth port
  6582. DMUS_PORTPARAMS dmpp;
  6583. if( NULL == pPort )
  6584. {
  6585. pParams = &dmpp;
  6586. memset(&dmpp, 0, sizeof(DMUS_PORTPARAMS) );
  6587. dmpp.dwSize = sizeof(DMUS_PORTPARAMS);
  6588. dmpp.dwChannelGroups = dwChannelGroups = 1;
  6589. dmpp.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS |
  6590. DMUS_PORTPARAMS_AUDIOCHANNELS;
  6591. dmpp.dwAudioChannels = 2;
  6592. guidPortID = GUID_NULL;
  6593. hr = m_pDirectMusic->CreatePort(GUID_NULL, &dmpp, &pPort, NULL);
  6594. if ( SUCCEEDED( hr ) )
  6595. {
  6596. hr = pPort->Activate(TRUE);
  6597. }
  6598. fSetUpBlock = TRUE;
  6599. }
  6600. else
  6601. {
  6602. if (pguidPortID)
  6603. {
  6604. guidPortID = *pguidPortID;
  6605. }
  6606. else
  6607. {
  6608. DMUS_PORTCAPS PortCaps;
  6609. PortCaps.dwSize = sizeof (PortCaps);
  6610. pPort->GetCaps(&PortCaps);
  6611. guidPortID = PortCaps.guidPort;
  6612. }
  6613. pPort->GetNumChannelGroups(&dwChannelGroups);
  6614. pPort->AddRef();
  6615. }
  6616. if( FAILED(hr) || ( pPort == NULL ) )
  6617. {
  6618. if (fBuiltNewTable) delete [] pPortTable;
  6619. Trace(1,"Error: Unable to open requested port.\n");
  6620. hr = DMUS_E_CANNOT_OPEN_PORT;
  6621. goto END;
  6622. }
  6623. // Create a buffer
  6624. DMUS_BUFFERDESC dmbd;
  6625. memset( &dmbd, 0, sizeof(DMUS_BUFFERDESC) );
  6626. dmbd.dwSize = sizeof(DMUS_BUFFERDESC);
  6627. dmbd.cbBuffer = DEFAULT_BUFFER_SIZE;
  6628. if( FAILED( m_pDirectMusic->CreateMusicBuffer(&dmbd, &pBuffer, NULL)))
  6629. {
  6630. if (fBuiltNewTable) delete [] pPortTable;
  6631. pPort->Release();
  6632. Trace(1,"Error: Unable to create MIDI buffer for port.\n");
  6633. hr = DMUS_E_CANNOT_OPEN_PORT;
  6634. goto END;
  6635. }
  6636. if (fBuiltNewTable)
  6637. {
  6638. // if there is an existing port table, copy its contents to the new, bigger, port table
  6639. if( m_pPortTable )
  6640. {
  6641. if( m_dwNumPorts > 0 )
  6642. {
  6643. memcpy( pPortTable, m_pPortTable, sizeof(PortTable) * ( m_dwNumPorts ) );
  6644. }
  6645. delete [] m_pPortTable;
  6646. }
  6647. m_pPortTable = pPortTable;
  6648. }
  6649. if (pdwPortID)
  6650. {
  6651. *pdwPortID = dwNewPortIndex;
  6652. }
  6653. pPortTable = &m_pPortTable[dwNewPortIndex];
  6654. pPortTable->pPort = pPort;
  6655. // If we have a passed params structure, copy it. This will be used for identifying the
  6656. // params as initialized by the synth.
  6657. if (pParams)
  6658. {
  6659. pPortTable->PortParams = *pParams;
  6660. }
  6661. pPortTable->dwGMFlags = 0;
  6662. //set master volume
  6663. IKsControl *pControl;
  6664. if(SUCCEEDED(pPort->QueryInterface(IID_IKsControl, (void**)&pControl)))
  6665. {
  6666. KSPROPERTY ksp;
  6667. ULONG cb;
  6668. memset(&ksp, 0, sizeof(ksp));
  6669. ksp.Set = GUID_DMUS_PROP_Volume;
  6670. ksp.Id = 0;
  6671. ksp.Flags = KSPROPERTY_TYPE_SET;
  6672. pControl->KsProperty(&ksp,
  6673. sizeof(ksp),
  6674. (LPVOID)&m_lMasterVolume,
  6675. sizeof(m_lMasterVolume),
  6676. &cb);
  6677. // Now, find out if it has a gm, gs, or xg sets in rom...
  6678. BOOL bIsSupported = FALSE;
  6679. ksp.Set = GUID_DMUS_PROP_GM_Hardware;
  6680. ksp.Flags = KSPROPERTY_TYPE_GET;
  6681. hr = pControl->KsProperty(&ksp,
  6682. sizeof(ksp),
  6683. (LPVOID)&bIsSupported,
  6684. sizeof(bIsSupported),
  6685. &cb);
  6686. if (SUCCEEDED(hr) && (bIsSupported))
  6687. {
  6688. pPortTable->dwGMFlags |= DM_PORTFLAGS_GM;
  6689. }
  6690. bIsSupported = FALSE;
  6691. ksp.Set = GUID_DMUS_PROP_GS_Hardware;
  6692. ksp.Flags = KSPROPERTY_TYPE_GET;
  6693. hr = pControl->KsProperty(&ksp,
  6694. sizeof(ksp),
  6695. (LPVOID)&bIsSupported,
  6696. sizeof(bIsSupported),
  6697. &cb);
  6698. if (SUCCEEDED(hr) && (bIsSupported))
  6699. {
  6700. pPortTable->dwGMFlags |= DM_PORTFLAGS_GS;
  6701. }
  6702. bIsSupported = FALSE;
  6703. ksp.Set = GUID_DMUS_PROP_XG_Hardware;
  6704. ksp.Flags = KSPROPERTY_TYPE_GET;
  6705. hr = pControl->KsProperty(&ksp,
  6706. sizeof(ksp),
  6707. (LPVOID)&bIsSupported,
  6708. sizeof(bIsSupported),
  6709. &cb);
  6710. if (SUCCEEDED(hr) && (bIsSupported))
  6711. {
  6712. pPortTable->dwGMFlags |= DM_PORTFLAGS_XG;
  6713. }
  6714. pControl->Release();
  6715. }
  6716. if( FAILED( pPort->GetLatencyClock( &pPortTable->pLatencyClock )))
  6717. {
  6718. pPortTable->pLatencyClock = NULL;
  6719. }
  6720. pPortTable->dwChannelGroups = dwChannelGroups;
  6721. pPortTable->guidPortID = guidPortID;
  6722. pPortTable->pBuffer = pBuffer;
  6723. pPortTable->fBufferFilled = FALSE;
  6724. pPortTable->rtLast = 0;
  6725. if (fBuiltNewTable) m_dwNumPorts++; // must do this before calling AssignPChannelBlock
  6726. if( fSetUpBlock && m_ChannelBlockList.IsEmpty() ) // set up default PChannel map if none already set
  6727. {
  6728. AssignPChannelBlock( 0, pPort, 1);
  6729. }
  6730. hr = S_OK;
  6731. END:
  6732. LeaveCriticalSection(&m_PChannelInfoCrSec);
  6733. LeaveCriticalSection(&m_MainCrSec);
  6734. return hr;
  6735. }
  6736. HRESULT STDMETHODCALLTYPE CPerformance::RemovePort(
  6737. IDirectMusicPort* pPort // @parm The port to remove.
  6738. )
  6739. {
  6740. V_INAME(IDirectMusicPerformance::RemovePort);
  6741. V_INTERFACE(pPort);
  6742. DWORD dwIndex;
  6743. HRESULT hr = E_INVALIDARG;
  6744. EnterCriticalSection(&m_PChannelInfoCrSec);
  6745. for( dwIndex = 0; dwIndex < m_dwNumPorts; dwIndex++ )
  6746. {
  6747. if( m_pPortTable[dwIndex].pPort == pPort )
  6748. {
  6749. // release the port and buffer. NULL them in the table. PChannels
  6750. // that map will return an error code.
  6751. ASSERT( m_pPortTable[dwIndex].pBuffer );
  6752. m_pPortTable[dwIndex].pPort->Release();
  6753. m_pPortTable[dwIndex].pBuffer->Release();
  6754. if( m_pPortTable[dwIndex].pLatencyClock )
  6755. {
  6756. m_pPortTable[dwIndex].pLatencyClock->Release();
  6757. }
  6758. memset( &m_pPortTable[dwIndex], 0, sizeof( PortTable ));
  6759. hr = S_OK;
  6760. break;
  6761. }
  6762. }
  6763. #ifdef DBG
  6764. if (hr == E_INVALIDARG)
  6765. {
  6766. Trace(1,"Error: Invalid port passed to RemovePort().\n");
  6767. }
  6768. #endif
  6769. LeaveCriticalSection(&m_PChannelInfoCrSec);
  6770. return hr;
  6771. }
  6772. // this must be called from within a PChannelCrSec critical section.
  6773. HRESULT CPerformance::AssignPChannelBlock(
  6774. DWORD dwBlockNum,
  6775. DWORD dwPortIndex,
  6776. DWORD dwGroup,
  6777. WORD wFlags)
  6778. {
  6779. // see if we've already allocated this block before
  6780. // blocknum is PChannel / 16, so search on that.
  6781. DWORD dwPChannel = dwBlockNum * 16;
  6782. CChannelBlock* pChannelBlock = m_ChannelBlockList.GetHead();
  6783. for( ; pChannelBlock; pChannelBlock = pChannelBlock->GetNext() )
  6784. {
  6785. if( pChannelBlock->m_dwPChannelStart == dwPChannel )
  6786. {
  6787. pChannelBlock->Init(dwPChannel,dwPortIndex,dwGroup,wFlags);
  6788. break;
  6789. }
  6790. }
  6791. if( !pChannelBlock )
  6792. {
  6793. pChannelBlock = new CChannelBlock;
  6794. if( !pChannelBlock )
  6795. {
  6796. return E_OUTOFMEMORY;
  6797. }
  6798. pChannelBlock->Init(dwPChannel,dwPortIndex,dwGroup,wFlags);
  6799. m_ChannelBlockList.AddHead(pChannelBlock);
  6800. pChannelBlock->m_dwPChannelStart = dwPChannel;
  6801. }
  6802. return S_OK;
  6803. }
  6804. // this must be called from within a PChannelCrSec critical section.
  6805. HRESULT CPerformance::AssignPChannel(
  6806. DWORD dwPChannel,
  6807. DWORD dwPortIndex,
  6808. DWORD dwGroup,
  6809. DWORD dwMChannel,
  6810. WORD wFlags)
  6811. {
  6812. DWORD dwIndex;
  6813. CChannelBlock* pChannelBlock = m_ChannelBlockList.GetHead();
  6814. for( ; pChannelBlock; pChannelBlock = pChannelBlock->GetNext() )
  6815. {
  6816. if( pChannelBlock->m_dwPChannelStart <= dwPChannel )
  6817. {
  6818. if( pChannelBlock->m_dwPChannelStart + PCHANNEL_BLOCKSIZE > dwPChannel )
  6819. {
  6820. break;
  6821. }
  6822. }
  6823. }
  6824. if( !pChannelBlock )
  6825. {
  6826. // there is no currently existing block that encompases dwPChannel.
  6827. // Create one.
  6828. pChannelBlock = new CChannelBlock;
  6829. if( !pChannelBlock )
  6830. {
  6831. return E_OUTOFMEMORY;
  6832. }
  6833. pChannelBlock->Init(dwPChannel,0,0,CMAP_FREE);
  6834. m_ChannelBlockList.AddHead(pChannelBlock);
  6835. }
  6836. dwIndex = dwPChannel - pChannelBlock->m_dwPChannelStart;
  6837. ASSERT( dwIndex < PCHANNEL_BLOCKSIZE );
  6838. CChannelMap *pMap = &pChannelBlock->m_aChannelMap[dwIndex];
  6839. pMap->dwPortIndex = dwPortIndex;
  6840. pMap->dwGroup = dwGroup;
  6841. pMap->dwMChannel = dwMChannel;
  6842. pMap->nTranspose = 0;
  6843. if ((pMap->wFlags & CMAP_FREE) && !(wFlags & CMAP_FREE))
  6844. pChannelBlock->m_dwFreeChannels--;
  6845. else if (!(pMap->wFlags & CMAP_FREE) && (wFlags & CMAP_FREE))
  6846. pChannelBlock->m_dwFreeChannels++;
  6847. pMap->wFlags = wFlags;
  6848. return S_OK;
  6849. }
  6850. HRESULT STDMETHODCALLTYPE CPerformance::AssignPChannelBlock(
  6851. DWORD dwBlockNum, // @parm The block number. Should be 0 or greater.
  6852. IDirectMusicPort* pPort, // @parm The port.
  6853. DWORD dwGroup // @parm The group on the port. Should be 1 or greater.
  6854. )
  6855. {
  6856. V_INAME(IDirectMusicPerformance::AssignPChannelBlock);
  6857. V_INTERFACE(pPort);
  6858. if (m_dwAudioPathMode == 2)
  6859. {
  6860. Trace(1,"Error: Can not call AssignPChannelBlock() when using AudioPaths.\n");
  6861. return DMUS_E_AUDIOPATHS_IN_USE;
  6862. }
  6863. m_dwAudioPathMode = 1;
  6864. DWORD dwIndex;
  6865. HRESULT hr = E_INVALIDARG;
  6866. EnterCriticalSection(&m_PChannelInfoCrSec);
  6867. for( dwIndex = 0; dwIndex < m_dwNumPorts; dwIndex++ )
  6868. {
  6869. if( m_pPortTable[dwIndex].pPort == pPort )
  6870. {
  6871. if( SUCCEEDED( hr = AssignPChannelBlock( dwBlockNum, dwIndex, dwGroup, CMAP_STATIC )))
  6872. {
  6873. if (m_pPortTable[dwIndex].dwChannelGroups < dwGroup)
  6874. {
  6875. hr = S_FALSE;
  6876. }
  6877. }
  6878. break;
  6879. }
  6880. }
  6881. #ifdef DBG
  6882. if (hr == E_INVALIDARG)
  6883. {
  6884. Trace(1,"Error: AssignPChannelBlock() called with invalid port.\n");
  6885. }
  6886. #endif
  6887. LeaveCriticalSection(&m_PChannelInfoCrSec);
  6888. return hr;
  6889. }
  6890. HRESULT STDMETHODCALLTYPE CPerformance::AssignPChannel(
  6891. DWORD dwPChannel, // @parm The PChannel.
  6892. IDirectMusicPort* pPort, // @parm The port.
  6893. DWORD dwGroup, // @parm The group on the port.
  6894. DWORD dwMChannel // @parm The channel on the group.
  6895. )
  6896. {
  6897. V_INAME(IDirectMusicPerformance::AssignPChannel);
  6898. V_INTERFACE(pPort);
  6899. if (m_dwAudioPathMode == 2)
  6900. {
  6901. Trace(1,"Error: Can not call AssignPChannel() when using AudioPaths.\n");
  6902. return DMUS_E_AUDIOPATHS_IN_USE;
  6903. }
  6904. m_dwAudioPathMode = 1;
  6905. DWORD dwIndex;
  6906. HRESULT hr = E_INVALIDARG;
  6907. if( (dwMChannel < 0) || (dwMChannel > 15))
  6908. {
  6909. Trace(1,"Error: AssignPChannel() called with invalid MIDI Channel %ld.\n",dwMChannel);
  6910. return E_INVALIDARG;
  6911. }
  6912. EnterCriticalSection(&m_PChannelInfoCrSec);
  6913. for( dwIndex = 0; dwIndex < m_dwNumPorts; dwIndex++ )
  6914. {
  6915. if( m_pPortTable[dwIndex].pPort == pPort )
  6916. {
  6917. if( SUCCEEDED( hr = AssignPChannel( dwPChannel, dwIndex, dwGroup, dwMChannel, CMAP_STATIC )))
  6918. {
  6919. if (m_pPortTable[dwIndex].dwChannelGroups < dwGroup)
  6920. {
  6921. hr = S_FALSE;
  6922. }
  6923. }
  6924. break;
  6925. }
  6926. }
  6927. LeaveCriticalSection(&m_PChannelInfoCrSec);
  6928. return hr;
  6929. }
  6930. /* ReleasePChannel finds the requested PChannel and makes it available
  6931. for reuse.
  6932. It also calls ResetAllControllers(), which sends MIDI CC 121 and 123,
  6933. reset all controllers and all notes off.
  6934. */
  6935. HRESULT CPerformance::ReleasePChannel(DWORD dwPChannel)
  6936. {
  6937. HRESULT hr = E_INVALIDARG;
  6938. EnterCriticalSection(&m_PChannelInfoCrSec);
  6939. CChannelBlock* pChannelBlock = m_ChannelBlockList.GetHead();
  6940. for( ; pChannelBlock; pChannelBlock = pChannelBlock->GetNext() )
  6941. {
  6942. if( pChannelBlock->m_dwPChannelStart <= dwPChannel )
  6943. {
  6944. if( pChannelBlock->m_dwPChannelStart + PCHANNEL_BLOCKSIZE > dwPChannel )
  6945. {
  6946. break;
  6947. }
  6948. }
  6949. }
  6950. if( pChannelBlock )
  6951. {
  6952. // Only release if this is genuinely a virtual pchannel. Otherwise, leave alone.
  6953. CChannelMap *pMap = &pChannelBlock->m_aChannelMap[dwPChannel - pChannelBlock->m_dwPChannelStart];
  6954. if (pMap->wFlags & CMAP_VIRTUAL)
  6955. {
  6956. pChannelBlock->m_dwFreeChannels++;
  6957. // Clear out all the merge lists, etc.
  6958. pMap->Clear();
  6959. // Reset controllers, but don't send a GM reset.
  6960. ResetAllControllers(pMap,0, false);
  6961. }
  6962. hr = S_OK;
  6963. }
  6964. LeaveCriticalSection(&m_PChannelInfoCrSec);
  6965. return hr;
  6966. }
  6967. HRESULT CPerformance::GetPort(DWORD dwPortID, IDirectMusicPort **ppPort)
  6968. {
  6969. HRESULT hr;
  6970. EnterCriticalSection(&m_PChannelInfoCrSec);
  6971. if (dwPortID < m_dwNumPorts)
  6972. {
  6973. *ppPort = m_pPortTable[dwPortID].pPort;
  6974. (*ppPort)->AddRef();
  6975. hr = S_OK;
  6976. }
  6977. else
  6978. {
  6979. Trace(1,"Error: Unable to find requested port.\n");
  6980. hr = E_FAIL;
  6981. }
  6982. LeaveCriticalSection(&m_PChannelInfoCrSec);
  6983. return hr;
  6984. }
  6985. HRESULT CPerformance::AllocVChannel(DWORD dwPortID, DWORD dwDrumFlags, DWORD *pdwPChannel, DWORD *pdwGroup,DWORD *pdwMChannel)
  6986. {
  6987. // dwDrumsFlags:
  6988. // bit 0 determines whether this port separates out drums on channel 10.
  6989. // bit 1 determines whether this request is for a drum.
  6990. // First, figure out if we are scanning for drums on channel 10, melodic instruments
  6991. // on the other channels, or any on all channels.
  6992. static DWORD sdwSearchForDrums[1] = { 9 };
  6993. static DWORD sdwSearchForAll[16] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
  6994. static DWORD sdwSearchForMelodic[15] = { 0,1,2,3,4,5,6,7,8,10,11,12,13,14,15 };
  6995. DWORD *pSearchArray = sdwSearchForAll;
  6996. DWORD dwSearchSize = 16;
  6997. if (dwDrumFlags & 1) // Do we handle drums as a special case for channel 10?
  6998. {
  6999. if (dwDrumFlags & 2) // And are we looking for drums on channel 10?
  7000. {
  7001. pSearchArray = sdwSearchForDrums;
  7002. dwSearchSize = 1;
  7003. }
  7004. else
  7005. {
  7006. pSearchArray = sdwSearchForMelodic;
  7007. dwSearchSize = 15;
  7008. }
  7009. }
  7010. HRESULT hr = E_INVALIDARG; // Return this if the vChannel is out of range.
  7011. EnterCriticalSection(&m_PChannelInfoCrSec);
  7012. CChannelBlock* pChannelBlock = m_ChannelBlockList.GetHead();
  7013. BOOL fNotFound = TRUE; // Use to indicate when we finally find a match.
  7014. DWORD dwHighestPChannel = 0; // Keep track of the highest PCHannel in use, this will be
  7015. // used to create a new PChannel block, if needed.
  7016. DWORD dwChannel;
  7017. for (;fNotFound && pChannelBlock;pChannelBlock = pChannelBlock->GetNext() )
  7018. {
  7019. if (dwHighestPChannel < pChannelBlock->m_dwPChannelStart)
  7020. {
  7021. dwHighestPChannel = pChannelBlock->m_dwPChannelStart;
  7022. }
  7023. if ((pChannelBlock->m_dwPortIndex == dwPortID) && (pChannelBlock->m_dwFreeChannels))
  7024. {
  7025. DWORD dwIndex;
  7026. for (dwIndex = 0; dwIndex < dwSearchSize; dwIndex++)
  7027. {
  7028. dwChannel = pSearchArray[dwIndex];
  7029. if (pChannelBlock->m_aChannelMap[dwChannel].wFlags & CMAP_FREE)
  7030. {
  7031. *pdwPChannel = pChannelBlock->m_dwPChannelStart + dwChannel;
  7032. pChannelBlock->m_dwFreeChannels--;
  7033. pChannelBlock->m_aChannelMap[dwChannel].wFlags = CMAP_VIRTUAL;
  7034. *pdwGroup = pChannelBlock->m_aChannelMap[dwChannel].dwGroup;
  7035. *pdwMChannel = pChannelBlock->m_aChannelMap[dwChannel].dwMChannel;
  7036. fNotFound = FALSE;
  7037. hr = S_OK;
  7038. break;
  7039. }
  7040. }
  7041. }
  7042. }
  7043. if( fNotFound )
  7044. {
  7045. // there is no currently existing block that has a free channel.
  7046. // Create one.
  7047. IDirectMusicPort *pPort = m_pPortTable[dwPortID].pPort;
  7048. DWORD dwChannelGroupCount;
  7049. pPort->GetNumChannelGroups(&dwChannelGroupCount);
  7050. dwChannelGroupCount++;
  7051. hr = pPort->SetNumChannelGroups(dwChannelGroupCount);
  7052. if (SUCCEEDED(hr))
  7053. {
  7054. m_pPortTable[dwPortID].dwChannelGroups = dwChannelGroupCount;
  7055. hr = E_OUTOFMEMORY;
  7056. dwHighestPChannel += PCHANNEL_BLOCKSIZE;
  7057. pChannelBlock = new CChannelBlock;
  7058. if (pChannelBlock)
  7059. {
  7060. pChannelBlock->Init(dwHighestPChannel,dwPortID,dwChannelGroupCount,CMAP_FREE);
  7061. m_ChannelBlockList.AddTail(pChannelBlock);
  7062. dwChannel = pSearchArray[0]; // Which channel should we use?
  7063. CChannelMap *pMap = &pChannelBlock->m_aChannelMap[dwChannel];
  7064. pMap->dwMChannel = dwChannel;
  7065. pMap->wFlags = CMAP_VIRTUAL;
  7066. pChannelBlock->m_dwFreeChannels--;
  7067. *pdwPChannel = dwChannel + dwHighestPChannel;
  7068. *pdwGroup = pMap->dwGroup;
  7069. *pdwMChannel = dwChannel;
  7070. hr = S_OK;
  7071. }
  7072. }
  7073. }
  7074. #ifdef DBG
  7075. if (hr == E_INVALIDARG)
  7076. {
  7077. Trace(1,"Error: Unable to allocated dynamic PChannel.\n");
  7078. }
  7079. #endif
  7080. LeaveCriticalSection(&m_PChannelInfoCrSec);
  7081. return hr;
  7082. }
  7083. HRESULT CPerformance::AllocVChannelBlock(DWORD dwPortID,DWORD dwGroup)
  7084. {
  7085. EnterCriticalSection(&m_PChannelInfoCrSec);
  7086. CChannelBlock* pChannelBlock = m_ChannelBlockList.GetHead();
  7087. long lHighestPChannel = -PCHANNEL_BLOCKSIZE;
  7088. for (;pChannelBlock;pChannelBlock = pChannelBlock->GetNext() )
  7089. {
  7090. if (lHighestPChannel < (long) pChannelBlock->m_dwPChannelStart)
  7091. {
  7092. lHighestPChannel = pChannelBlock->m_dwPChannelStart;
  7093. }
  7094. }
  7095. HRESULT hr = E_OUTOFMEMORY;
  7096. lHighestPChannel += PCHANNEL_BLOCKSIZE;
  7097. pChannelBlock = new CChannelBlock;
  7098. if (pChannelBlock)
  7099. {
  7100. pChannelBlock->Init((DWORD) lHighestPChannel,dwPortID,dwGroup,CMAP_FREE);
  7101. m_ChannelBlockList.AddTail(pChannelBlock);
  7102. hr = S_OK;
  7103. }
  7104. LeaveCriticalSection(&m_PChannelInfoCrSec);
  7105. return hr;
  7106. }
  7107. #ifdef DBG
  7108. void CPerformance::TraceAllChannelMaps()
  7109. {
  7110. EnterCriticalSection(&m_PChannelInfoCrSec);
  7111. CChannelBlock* pChannelBlock = m_ChannelBlockList.GetHead();
  7112. for( ; pChannelBlock; pChannelBlock = pChannelBlock->GetNext() )
  7113. {
  7114. TraceI(0,"ChannelBlock %lx, Free %ld\n",pChannelBlock->m_dwPChannelStart,pChannelBlock->m_dwFreeChannels);
  7115. DWORD dwIndex;
  7116. for (dwIndex = 0; dwIndex < PCHANNEL_BLOCKSIZE; dwIndex++)
  7117. {
  7118. CChannelMap *pMap = &pChannelBlock->m_aChannelMap[dwIndex];
  7119. TraceI(0,"\tPort %ld, Group: %ld, MIDI: %ld, Transpose: %ld, Flags: %ld\n",
  7120. pMap->dwPortIndex, pMap->dwGroup, pMap->dwMChannel, (long) pMap->nTranspose, (long) pMap->wFlags);
  7121. }
  7122. }
  7123. LeaveCriticalSection(&m_PChannelInfoCrSec);
  7124. }
  7125. #endif
  7126. /* Note that the following must be called from within a m_PChannelInfoCrSec
  7127. critical section and stay within that critical section for the duration
  7128. of using the returned CChannelMap.
  7129. */
  7130. CChannelMap * CPerformance::GetPChannelMap( DWORD dwPChannel )
  7131. {
  7132. CChannelBlock* pChannelBlock = m_ChannelBlockList.GetHead();
  7133. for( ; pChannelBlock; pChannelBlock = pChannelBlock->GetNext() )
  7134. {
  7135. if( ( dwPChannel >= pChannelBlock->m_dwPChannelStart ) &&
  7136. ( dwPChannel < pChannelBlock->m_dwPChannelStart + PCHANNEL_BLOCKSIZE ) )
  7137. {
  7138. CChannelMap* pChannelMap;
  7139. pChannelMap = &pChannelBlock->m_aChannelMap[ dwPChannel - pChannelBlock->m_dwPChannelStart ];
  7140. if( pChannelMap->dwGroup == 0 )
  7141. {
  7142. // this PChannel isn't on a valid group, therefore it hasn't
  7143. // been set.
  7144. // return NULL;
  7145. }
  7146. return pChannelMap;
  7147. }
  7148. }
  7149. return NULL;
  7150. }
  7151. /*
  7152. internal version
  7153. */
  7154. HRESULT CPerformance::PChannelIndex( DWORD dwPChannel, DWORD* pdwIndex,
  7155. DWORD* pdwGroup, DWORD* pdwMChannel, short* pnTranspose )
  7156. {
  7157. if (m_dwAudioPathMode == 0)
  7158. {
  7159. Trace(1,"Error: Performance not initialized.\n");
  7160. return DMUS_E_NOT_INIT;
  7161. }
  7162. HRESULT hr;
  7163. EnterCriticalSection(&m_PChannelInfoCrSec);
  7164. CChannelMap *pChannelMap = GetPChannelMap(dwPChannel);
  7165. if (pChannelMap)
  7166. {
  7167. ASSERT( pdwIndex && pdwGroup && pdwMChannel );
  7168. *pdwIndex = pChannelMap->dwPortIndex;
  7169. *pdwGroup = pChannelMap->dwGroup;
  7170. *pdwMChannel = pChannelMap->dwMChannel;
  7171. if( pnTranspose )
  7172. {
  7173. *pnTranspose = pChannelMap->nTranspose;
  7174. }
  7175. hr = S_OK;
  7176. }
  7177. else
  7178. {
  7179. Trace(1,"Error: PChannel %ld has not been assigned to a port.\n",dwPChannel);
  7180. if (m_dwVersion < 8)
  7181. {
  7182. hr = E_INVALIDARG;
  7183. }
  7184. else
  7185. {
  7186. hr = DMUS_E_AUDIOPATH_NOPORT;
  7187. }
  7188. }
  7189. LeaveCriticalSection(&m_PChannelInfoCrSec);
  7190. return hr;
  7191. }
  7192. DWORD CPerformance::GetPortID(IDirectMusicPort * pPort)
  7193. {
  7194. EnterCriticalSection(&m_PChannelInfoCrSec);
  7195. DWORD dwID = 0;
  7196. for (;dwID < m_dwNumPorts; dwID++)
  7197. {
  7198. if (pPort == m_pPortTable[dwID].pPort)
  7199. {
  7200. break;
  7201. }
  7202. }
  7203. LeaveCriticalSection(&m_PChannelInfoCrSec);
  7204. if (dwID == m_dwNumPorts) dwID = 0;
  7205. return dwID;
  7206. }
  7207. STDMETHODIMP CPerformance::GetPortAndFlags(DWORD dwPChannel,IDirectMusicPort **ppPort,DWORD * pdwFlags)
  7208. {
  7209. EnterCriticalSection(&m_PChannelInfoCrSec);
  7210. DWORD dwIndex;
  7211. DWORD dwGroup;
  7212. DWORD dwMChannel;
  7213. HRESULT hr = PChannelIndex( dwPChannel, &dwIndex, &dwGroup, &dwMChannel, NULL );
  7214. if (SUCCEEDED(hr))
  7215. {
  7216. *ppPort = m_pPortTable[dwIndex].pPort;
  7217. if( *ppPort )
  7218. {
  7219. m_pPortTable[dwIndex].pPort->AddRef();
  7220. }
  7221. else
  7222. {
  7223. Trace(1,"Error: Performance does not have a port assigned to PChannel %ld.\n",dwPChannel);
  7224. hr = DMUS_E_NOT_INIT;
  7225. }
  7226. *pdwFlags = m_pPortTable[dwIndex].dwGMFlags;
  7227. }
  7228. LeaveCriticalSection(&m_PChannelInfoCrSec);
  7229. return hr;
  7230. }
  7231. STDMETHODIMP CPerformance::PChannelInfo(
  7232. DWORD dwPChannel, // @parm The PChannel to convert.
  7233. IDirectMusicPort** ppPort, // @parm Returns the port. May be NULL.
  7234. DWORD* pdwGroup, // @parm Returns the group on the port. May be NULL.
  7235. DWORD* pdwMChannel // @parm Returns the channel on the group. May be NULL.
  7236. )
  7237. {
  7238. V_INAME(IDirectMusicPerformance::PChannelInfo);
  7239. V_PTRPTR_WRITE_OPT(ppPort);
  7240. V_PTR_WRITE_OPT(pdwGroup,DWORD);
  7241. V_PTR_WRITE_OPT(pdwMChannel,DWORD);
  7242. DWORD dwIndex, dwGroup, dwMChannel;
  7243. HRESULT hr;
  7244. if (m_dwAudioPathMode == 0)
  7245. {
  7246. Trace(1,"Error: Performance not initialized.\n");
  7247. return DMUS_E_NOT_INIT;
  7248. }
  7249. EnterCriticalSection(&m_PChannelInfoCrSec);
  7250. if( SUCCEEDED( PChannelIndex( dwPChannel, &dwIndex, &dwGroup, &dwMChannel )))
  7251. {
  7252. if( ppPort )
  7253. {
  7254. *ppPort = m_pPortTable[dwIndex].pPort;
  7255. if( *ppPort )
  7256. {
  7257. m_pPortTable[dwIndex].pPort->AddRef();
  7258. }
  7259. }
  7260. if( pdwGroup )
  7261. {
  7262. *pdwGroup = dwGroup;
  7263. }
  7264. if( pdwMChannel )
  7265. {
  7266. *pdwMChannel = dwMChannel;
  7267. }
  7268. hr = S_OK;
  7269. }
  7270. else
  7271. {
  7272. // No need to print an error message because PChannelIndex() does it.
  7273. hr = E_INVALIDARG;
  7274. }
  7275. LeaveCriticalSection(&m_PChannelInfoCrSec);
  7276. return hr;
  7277. }
  7278. /*
  7279. @method HRESULT | IDirectMusicPerformance | DownloadInstrument |
  7280. Downloads an IDirectMusicInstrument to the IDirectMusicPort specified by
  7281. the selected PChannel.
  7282. @rvalue E_INVALIDARG | The PChannel isn't assigned to a Port, or the Port failed
  7283. to download the instrument. No return parameter is valid.
  7284. @rvalue S_OK | Success.
  7285. @rvalue E_POINTER | One of the pointers is invalid.
  7286. */
  7287. HRESULT STDMETHODCALLTYPE CPerformance::DownloadInstrument(
  7288. IDirectMusicInstrument* pInst, // @parm The instrument to download.
  7289. DWORD dwPChannel, // @parm The PChannel to assign the instrument.
  7290. IDirectMusicDownloadedInstrument** ppDownInst, // @parm Returns the downloaded instrument.
  7291. DMUS_NOTERANGE* pNoteRanges, // @parm A pointer to an array of DMUS_NOTERANGE structures
  7292. DWORD dwNumNoteRanges, // @parm Number of DMUS_NOTERANGE structures in array pointed to by pNoteRanges
  7293. IDirectMusicPort** ppPort, // @parm Returns the port to which the instrument was downloaded.
  7294. DWORD* pdwGroup, // @parm Returns the group to which the instrument was assigned.
  7295. DWORD* pdwMChannel // @parm Returns the MChannel to which the instrument was assigned.
  7296. )
  7297. {
  7298. V_INAME(IDirectMusicPerformance::DownloadInstrument);
  7299. V_INTERFACE(pInst);
  7300. V_PTRPTR_WRITE(ppDownInst);
  7301. V_BUFPTR_READ_OPT(pNoteRanges, (sizeof(DMUS_NOTERANGE) * dwNumNoteRanges));
  7302. V_PTRPTR_WRITE(ppPort);
  7303. V_PTR_WRITE(pdwGroup,DWORD);
  7304. V_PTR_WRITE(pdwMChannel,DWORD);
  7305. DWORD dwIndex, dwGroup, dwMChannel;
  7306. IDirectMusicPort* pPort = NULL;
  7307. HRESULT hr = E_INVALIDARG;
  7308. if (m_dwAudioPathMode == 0)
  7309. {
  7310. Trace(1,"Error: Performance not initialized.\n");
  7311. return DMUS_E_NOT_INIT;
  7312. }
  7313. EnterCriticalSection(&m_PChannelInfoCrSec);
  7314. if( SUCCEEDED( PChannelIndex( dwPChannel, &dwIndex, &dwGroup, &dwMChannel )))
  7315. {
  7316. pPort = m_pPortTable[dwIndex].pPort;
  7317. if( pPort )
  7318. {
  7319. hr = pPort->DownloadInstrument( pInst, ppDownInst, pNoteRanges, dwNumNoteRanges );
  7320. pPort->AddRef();
  7321. }
  7322. }
  7323. else
  7324. {
  7325. Trace(1,"Error: Download attempted on unassigned PChannel %ld\n",dwPChannel);
  7326. }
  7327. LeaveCriticalSection(&m_PChannelInfoCrSec);
  7328. if( SUCCEEDED(hr) )
  7329. {
  7330. *ppPort = pPort;
  7331. pPort->AddRef();
  7332. *pdwGroup = dwGroup;
  7333. *pdwMChannel = dwMChannel;
  7334. }
  7335. if( pPort )
  7336. {
  7337. pPort->Release();
  7338. }
  7339. return hr;
  7340. }
  7341. /*
  7342. @method HRESULT | IDirectMusicPerformance | Invalidate |
  7343. Flushes all methods from <p mtTime> forward, and seeks all Segments back
  7344. to <p mtTime>, thereby calling all Tracks to resend their data.
  7345. @rvalue S_OK | Success.
  7346. @rvalue DMUS_E_NO_MASTER_CLOCK | There is no master clock in the performance.
  7347. Make sure to call <om .Init> before calling this method.
  7348. @comm If <p mtTime> is so long ago that it is impossible to invalidate that time,
  7349. the earliest possible time will be used.
  7350. */
  7351. HRESULT STDMETHODCALLTYPE CPerformance::Invalidate(
  7352. MUSIC_TIME mtTime, // @parm The time to invalidate, adjusted by <p dwFlags>. 0 means now.
  7353. DWORD dwFlags) // @parm Adjusts <p mtTime> to align to measures, beats, etc. See
  7354. // <t DMPLAYSEGFLAGS>.
  7355. {
  7356. EnterCriticalSection(&m_MainCrSec);
  7357. if( m_pClock == NULL )
  7358. {
  7359. LeaveCriticalSection(&m_MainCrSec);
  7360. Trace(1,"Error: Invalidate() failed because the performance has not been initialized.\n");
  7361. return DMUS_E_NO_MASTER_CLOCK;
  7362. }
  7363. LeaveCriticalSection(&m_MainCrSec);
  7364. EnterCriticalSection( &m_SegmentCrSec );
  7365. EnterCriticalSection( &m_PipelineCrSec );
  7366. SendBuffers();
  7367. // make sure mtTime is greater than the current queue time
  7368. REFERENCE_TIME rtQueue;
  7369. MUSIC_TIME mtQueue;
  7370. MUSIC_TIME mtBumperLength;
  7371. GetQueueTime( &rtQueue );
  7372. ReferenceToMusicTime( rtQueue, &mtQueue );
  7373. ReferenceToMusicTime( m_rtBumperLength, &mtBumperLength );
  7374. if( mtTime < mtQueue + mtBumperLength )
  7375. {
  7376. mtTime = mtQueue + mtBumperLength;
  7377. }
  7378. // resolve mtTime to the boundary of dwFlags
  7379. mtTime = ResolveTime( mtTime, dwFlags, NULL );
  7380. // flush messages
  7381. FlushMainEventQueues( 0, mtTime, mtQueue, FALSE );
  7382. // move any segments in the past list that are affected into the current list
  7383. CSegState *pSegSt;
  7384. CSegState *pNext;
  7385. for (pSegSt = m_SegStateQueues[SQ_SEC_DONE].GetHead();pSegSt;pSegSt = pNext)
  7386. {
  7387. pNext = pSegSt->GetNext();
  7388. if( pSegSt->m_mtLastPlayed > mtTime )
  7389. {
  7390. m_SegStateQueues[SQ_SEC_DONE].Remove(pSegSt);
  7391. m_SegStateQueues[SQ_SEC_PLAY].Insert( pSegSt );
  7392. }
  7393. }
  7394. for (pSegSt = m_SegStateQueues[SQ_CON_DONE].GetHead();pSegSt;pSegSt = pNext)
  7395. {
  7396. pNext = pSegSt->GetNext();
  7397. if( pSegSt->m_mtLastPlayed > mtTime )
  7398. {
  7399. m_SegStateQueues[SQ_CON_DONE].Remove(pSegSt);
  7400. m_SegStateQueues[SQ_CON_PLAY].Insert( pSegSt );
  7401. }
  7402. }
  7403. pSegSt = m_SegStateQueues[SQ_PRI_DONE].GetTail();
  7404. if(pSegSt)
  7405. {
  7406. // only check the last one in this list
  7407. if( pSegSt->m_mtLastPlayed > mtTime )
  7408. {
  7409. m_SegStateQueues[SQ_PRI_DONE].Remove(pSegSt);
  7410. m_SegStateQueues[SQ_PRI_PLAY].Insert( pSegSt );
  7411. }
  7412. }
  7413. // seek back any affected segmentstates that were playing
  7414. DWORD dwCount;
  7415. for( dwCount = SQ_PRI_PLAY; dwCount <= SQ_SEC_PLAY; dwCount++ )
  7416. {
  7417. for( pSegSt = m_SegStateQueues[dwCount].GetHead(); pSegSt; pSegSt = pSegSt->GetNext() )
  7418. {
  7419. if( pSegSt->m_fStartedPlay )
  7420. {
  7421. if (SQ_PRI_PLAY == dwCount && pSegSt->m_mtResolvedStart >= mtTime)
  7422. {
  7423. // resend the segment start notification
  7424. pSegSt->GenerateNotification( DMUS_NOTIFICATION_SEGSTART, pSegSt->m_mtResolvedStart );
  7425. // if this is a primary or controlling segment, resend a DMUS_PMSGT_DIRTY message
  7426. if( !(pSegSt->m_dwPlaySegFlags & DMUS_SEGF_SECONDARY) || (pSegSt->m_dwPlaySegFlags & DMUS_SEGF_CONTROL) )
  7427. {
  7428. TraceI(4, "ReSend Dirty PMsg [3] %d (%d)\n", pSegSt->m_mtSeek, pSegSt->m_mtOffset + pSegSt->m_mtSeek);
  7429. pSegSt->SendDirtyPMsg( pSegSt->m_mtOffset + pSegSt->m_mtSeek );
  7430. }
  7431. }
  7432. if( pSegSt->m_mtLastPlayed > mtTime )
  7433. {
  7434. // if mtTime is after the actual start time of the segment,
  7435. // set it so the segment has never been played before and
  7436. // seek the segment to the beginning
  7437. if( pSegSt->m_mtResolvedStart > mtTime )
  7438. {
  7439. pSegSt->m_mtLastPlayed = pSegSt->m_mtResolvedStart;
  7440. pSegSt->m_fStartedPlay = FALSE;
  7441. }
  7442. else
  7443. {
  7444. pSegSt->m_mtLastPlayed = mtTime;
  7445. }
  7446. pSegSt->SetInvalidate( pSegSt->m_mtLastPlayed );
  7447. }
  7448. }
  7449. }
  7450. }
  7451. LeaveCriticalSection( &m_PipelineCrSec );
  7452. LeaveCriticalSection( &m_SegmentCrSec );
  7453. // signal the transport thread so we don't have to wait for it to wake up on its own
  7454. if( m_hTransport ) SetEvent( m_hTransport );
  7455. return S_OK;
  7456. }
  7457. STDMETHODIMP CPerformance::SetParamHook(IDirectMusicParamHook *pIHook)
  7458. { V_INAME(IDirectMusicPerformance::SetParamHook);
  7459. V_INTERFACE_OPT(pIHook);
  7460. EnterCriticalSection(&m_MainCrSec);
  7461. if (m_pParamHook)
  7462. {
  7463. m_pParamHook->Release();
  7464. }
  7465. m_pParamHook = pIHook;
  7466. if (pIHook)
  7467. {
  7468. pIHook->AddRef();
  7469. }
  7470. LeaveCriticalSection(&m_MainCrSec);
  7471. return S_OK;
  7472. }
  7473. HRESULT STDMETHODCALLTYPE CPerformance::GetParamEx(
  7474. REFGUID rguidType,
  7475. DWORD dwTrackID,
  7476. DWORD dwGroupBits,
  7477. DWORD dwIndex,
  7478. MUSIC_TIME mtTime,
  7479. MUSIC_TIME* pmtNext,
  7480. void* pData)
  7481. {
  7482. V_INAME(IDirectMusicPerformance::GetParamEx);
  7483. V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME);
  7484. V_PTR_WRITE_OPT(pData,1);
  7485. V_REFGUID(rguidType);
  7486. static DWORD dwSearchOrder[SQ_COUNT] = { SQ_PRI_PLAY, SQ_SEC_PLAY,
  7487. SQ_PRI_DONE, SQ_SEC_DONE,
  7488. SQ_PRI_WAIT, SQ_SEC_WAIT,
  7489. SQ_CON_PLAY, SQ_CON_DONE,
  7490. SQ_CON_WAIT };
  7491. DWORD dwIX;
  7492. HRESULT hr;
  7493. CSegState *pSegNode;
  7494. if (dwTrackID)
  7495. {
  7496. EnterCriticalSection(&m_SegmentCrSec);
  7497. for (dwIX = 0; dwIX < SQ_COUNT; dwIX++)
  7498. {
  7499. pSegNode = m_SegStateQueues[dwSearchOrder[dwIX]].GetHead();
  7500. for (;pSegNode;pSegNode = pSegNode->GetNext())
  7501. {
  7502. if ((pSegNode->m_dwFirstTrackID <= dwTrackID) &&
  7503. (pSegNode->m_dwLastTrackID >= dwTrackID))
  7504. {
  7505. CTrack* pCTrack;
  7506. for (pCTrack = pSegNode->m_TrackList.GetHead();pCTrack;pCTrack = pCTrack->GetNext())
  7507. {
  7508. if (pCTrack->m_dwVirtualID == dwTrackID)
  7509. {
  7510. m_dwGetParamFlags = pCTrack->m_dwFlags;
  7511. m_pGetParamSegmentState = pSegNode;
  7512. break;
  7513. }
  7514. }
  7515. break;
  7516. }
  7517. }
  7518. }
  7519. LeaveCriticalSection(&m_SegmentCrSec);
  7520. }
  7521. else
  7522. {
  7523. m_pGetParamSegmentState = NULL;
  7524. m_dwGetParamFlags = 0;
  7525. }
  7526. hr = GetParam(rguidType,dwGroupBits,dwIndex,mtTime,pmtNext,pData);
  7527. m_pGetParamSegmentState = NULL;
  7528. m_dwGetParamFlags = 0;
  7529. return hr;
  7530. }
  7531. HRESULT STDMETHODCALLTYPE CPerformance::GetParam(
  7532. REFGUID rguidType,
  7533. DWORD dwGroupBits,
  7534. DWORD dwIndex,
  7535. MUSIC_TIME mtTime,
  7536. MUSIC_TIME* pmtNext,
  7537. void* pData)
  7538. {
  7539. V_INAME(IDirectMusicPerformance::GetParam);
  7540. V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME);
  7541. V_PTR_WRITE_OPT(pData,1);
  7542. V_REFGUID(rguidType);
  7543. EnterCriticalSection(&m_MainCrSec);
  7544. if( m_pClock == NULL )
  7545. {
  7546. LeaveCriticalSection(&m_MainCrSec);
  7547. Trace(1,"Error: GetParam() failed because the performance has not been initialized.\n");
  7548. return DMUS_E_NO_MASTER_CLOCK;
  7549. }
  7550. LeaveCriticalSection(&m_MainCrSec);
  7551. if( pmtNext )
  7552. {
  7553. *pmtNext = 0; // this will be replaced by calls to IDMSegment::GetParam
  7554. }
  7555. CSegState* pSegNode;
  7556. CSegState* pSegSource = (CSegState *) m_pGetParamSegmentState;
  7557. DWORD dwOverrideFlags;
  7558. HRESULT hr = DMUS_E_NOT_FOUND;
  7559. BOOL fCheckedPast = FALSE;
  7560. MUSIC_TIME mtOffset;
  7561. DWORD dwRepeat = 0;
  7562. MUSIC_TIME mtSegTime = 0;
  7563. MUSIC_TIME mtSegEnd = 0;
  7564. MUSIC_TIME mtLoopEnd = 0;
  7565. DWORD dwRepeatsLeft = 0;
  7566. if (pSegSource)
  7567. {
  7568. dwOverrideFlags = m_dwGetParamFlags & (DMUS_TRACKCONFIG_OVERRIDE_ALL | DMUS_TRACKCONFIG_OVERRIDE_PRIMARY | DMUS_TRACKCONFIG_FALLBACK);
  7569. }
  7570. else
  7571. {
  7572. dwOverrideFlags = 0;
  7573. }
  7574. if (dwOverrideFlags & DMUS_TRACKCONFIG_OVERRIDE_ALL)
  7575. {
  7576. // The calling track wants the controlling param to come from the segment itself
  7577. mtSegTime = mtTime;
  7578. if( S_OK == pSegSource->ConvertToSegTime( &mtSegTime, &mtOffset, &dwRepeat ) )
  7579. {
  7580. hr = pSegSource->GetParam( this, rguidType, dwGroupBits, dwIndex,
  7581. mtSegTime, pmtNext, pData );
  7582. if( SUCCEEDED(hr) )
  7583. {
  7584. dwRepeatsLeft = pSegSource->m_dwRepeats;
  7585. mtLoopEnd = pSegSource->m_mtLoopEnd;
  7586. mtSegEnd = pSegSource->m_mtLength;
  7587. dwRepeatsLeft -= dwRepeat;
  7588. }
  7589. }
  7590. }
  7591. if (FAILED(hr))
  7592. {
  7593. EnterCriticalSection(&m_SegmentCrSec);
  7594. // we only care about control segments
  7595. if( m_SegStateQueues[SQ_CON_DONE].GetHead() )
  7596. {
  7597. pSegNode = m_SegStateQueues[SQ_CON_DONE].GetHead();
  7598. }
  7599. else
  7600. {
  7601. pSegNode = m_SegStateQueues[SQ_CON_PLAY].GetHead();
  7602. fCheckedPast = TRUE;
  7603. }
  7604. while( pSegNode )
  7605. {
  7606. mtSegTime = mtTime;
  7607. if( S_OK == pSegNode->ConvertToSegTime( &mtSegTime, &mtOffset, &dwRepeat ) )
  7608. {
  7609. hr = pSegNode->GetParam( this, rguidType, dwGroupBits, dwIndex,
  7610. mtSegTime, pmtNext, pData );
  7611. if( SUCCEEDED(hr) )
  7612. {
  7613. dwRepeatsLeft = pSegNode->m_dwRepeats;
  7614. mtLoopEnd = pSegNode->m_mtLoopEnd;
  7615. mtSegEnd = pSegNode->m_mtLength;
  7616. dwRepeatsLeft -= dwRepeat;
  7617. break; // got the param we want. We're outta this loop with a success.
  7618. }
  7619. }
  7620. // we didn't find the param, so try the next segment.
  7621. pSegNode = pSegNode->GetNext();
  7622. // if we're the last segnode in the done queue, we need to
  7623. // check against the time of the first segnode in the control play queue
  7624. if (!pSegNode && !fCheckedPast )
  7625. {
  7626. pSegNode = m_SegStateQueues[SQ_CON_PLAY].GetHead();
  7627. fCheckedPast = TRUE;
  7628. }
  7629. }
  7630. LeaveCriticalSection(&m_SegmentCrSec);
  7631. }
  7632. if( FAILED(hr) && (dwOverrideFlags & DMUS_TRACKCONFIG_OVERRIDE_PRIMARY))
  7633. {
  7634. // The calling track wants the controlling param to come from the segment
  7635. // itself if there was no controlling segment.
  7636. mtSegTime = mtTime;
  7637. if( S_OK == pSegSource->ConvertToSegTime( &mtSegTime, &mtOffset, &dwRepeat ) )
  7638. {
  7639. hr = pSegSource->GetParam( this, rguidType, dwGroupBits, dwIndex,
  7640. mtSegTime, pmtNext, pData );
  7641. if( SUCCEEDED(hr) )
  7642. {
  7643. dwRepeatsLeft = pSegSource->m_dwRepeats;
  7644. mtLoopEnd = pSegSource->m_mtLoopEnd;
  7645. mtSegEnd = pSegSource->m_mtLength;
  7646. dwRepeatsLeft -= dwRepeat;
  7647. }
  7648. }
  7649. }
  7650. if( FAILED(hr) ) // didn't find one in the previous, so check for a primary segment
  7651. {
  7652. IDirectMusicSegment* pSegment = NULL;
  7653. mtSegTime = mtTime;
  7654. EnterCriticalSection(&m_SegmentCrSec);
  7655. pSegNode = GetPrimarySegmentAtTime( mtTime );
  7656. if( pSegNode )
  7657. {
  7658. pSegment = pSegNode->m_pSegment;
  7659. pSegment->AddRef();
  7660. pSegNode->ConvertToSegTime( &mtSegTime, &mtOffset, &dwRepeat );
  7661. dwRepeatsLeft = pSegNode->m_dwRepeats;
  7662. mtLoopEnd = pSegNode->m_mtLoopEnd;
  7663. mtSegEnd = pSegNode->m_mtLength;
  7664. dwRepeatsLeft -= dwRepeat;
  7665. }
  7666. else
  7667. {
  7668. Trace(4, "Couldn't find SegState in GetParam call.\n");
  7669. }
  7670. LeaveCriticalSection(&m_SegmentCrSec);
  7671. if( pSegment )
  7672. {
  7673. hr = pSegNode->GetParam( this, rguidType, dwGroupBits, dwIndex,
  7674. mtSegTime, pmtNext, pData );
  7675. pSegment->Release();
  7676. }
  7677. }
  7678. if( FAILED(hr) && (dwOverrideFlags & DMUS_TRACKCONFIG_FALLBACK))
  7679. {
  7680. // The calling track wants the controlling param to come from the segment itself
  7681. mtSegTime = mtTime;
  7682. if( S_OK == pSegSource->ConvertToSegTime( &mtSegTime, &mtOffset, &dwRepeat ) )
  7683. {
  7684. hr = pSegSource->GetParam( this, rguidType, dwGroupBits, dwIndex,
  7685. mtSegTime, pmtNext, pData );
  7686. if( SUCCEEDED(hr) )
  7687. {
  7688. dwRepeatsLeft = pSegSource->m_dwRepeats;
  7689. mtLoopEnd = pSegSource->m_mtLoopEnd;
  7690. mtSegEnd = pSegSource->m_mtLength;
  7691. dwRepeatsLeft -= dwRepeat;
  7692. }
  7693. }
  7694. }
  7695. if( FAILED(hr) )
  7696. { // If we failed, fill in the end time of loop or segment anyway.
  7697. if (pmtNext)
  7698. { // Check to see if the loop end is earlier than end of segment.
  7699. if (dwRepeatsLeft && (mtLoopEnd > mtSegTime))
  7700. {
  7701. *pmtNext = mtLoopEnd - mtSegTime;
  7702. }
  7703. else // Or, mark end of segment.
  7704. {
  7705. *pmtNext = mtSegEnd - mtSegTime;
  7706. }
  7707. }
  7708. // if we're looking for timesig, and didn't find it anywhere,
  7709. // return the Performance timesig
  7710. if( rguidType == GUID_TimeSignature )
  7711. {
  7712. if( NULL == pData )
  7713. {
  7714. Trace(1,"Error: Null pointer for time signature passed to GetParam().\n");
  7715. hr = E_POINTER;
  7716. }
  7717. else
  7718. {
  7719. DMUS_TIMESIGNATURE* pTSigData = (DMUS_TIMESIGNATURE*)pData;
  7720. DMUS_TIMESIG_PMSG timeSig;
  7721. GetTimeSig( mtTime, &timeSig );
  7722. pTSigData->bBeatsPerMeasure = timeSig.bBeatsPerMeasure;
  7723. pTSigData->bBeat = timeSig.bBeat;
  7724. pTSigData->wGridsPerBeat = timeSig.wGridsPerBeat;
  7725. pTSigData->mtTime = timeSig.mtTime - mtTime;
  7726. hr = S_OK;
  7727. }
  7728. }
  7729. // Likewise, if there was no tempo in a segment, we need to read directly from the tempo list.
  7730. else if ( rguidType == GUID_TempoParam || rguidType == GUID_PrivateTempoParam)
  7731. {
  7732. if( NULL == pData )
  7733. {
  7734. Trace(1,"Error: Null pointer for tempo passed to GetParam().\n");
  7735. hr = E_POINTER;
  7736. }
  7737. else
  7738. {
  7739. DMInternalTempo* pInternalTempo;
  7740. EnterCriticalSection( &m_PipelineCrSec );
  7741. pInternalTempo = (DMInternalTempo*)m_TempoMap.GetHead();
  7742. DMInternalTempo* pNextTempo = NULL;
  7743. for ( ;pInternalTempo;pInternalTempo = pNextTempo )
  7744. {
  7745. pNextTempo = (DMInternalTempo *) pInternalTempo->pNext;
  7746. if (pNextTempo && (pNextTempo->tempoPMsg.mtTime <= mtTime))
  7747. {
  7748. continue;
  7749. }
  7750. if (rguidType == GUID_TempoParam)
  7751. {
  7752. DMUS_TEMPO_PARAM* pTempoData = (DMUS_TEMPO_PARAM*)pData;
  7753. pTempoData->mtTime = pInternalTempo->tempoPMsg.mtTime - mtTime;
  7754. pTempoData->dblTempo = pInternalTempo->tempoPMsg.dblTempo;
  7755. }
  7756. else // rguidType == GUID_PrivateTempoParam
  7757. {
  7758. PrivateTempo* pTempoData = (PrivateTempo*)pData;
  7759. pTempoData->mtTime = pInternalTempo->tempoPMsg.mtTime - mtTime;
  7760. pTempoData->dblTempo = pInternalTempo->tempoPMsg.dblTempo;
  7761. }
  7762. if( pmtNext )
  7763. {
  7764. *pmtNext = 0;
  7765. }
  7766. break;
  7767. }
  7768. LeaveCriticalSection( &m_PipelineCrSec );
  7769. if (pInternalTempo)
  7770. {
  7771. hr = S_FALSE;
  7772. }
  7773. }
  7774. }
  7775. }
  7776. else // GetParam from a segment succeeded, so we need to clean up the next time parameter to account
  7777. // for loops and end of segment.
  7778. {
  7779. if (pmtNext) // Check to see if the loop end is earlier than *pmtNext.
  7780. {
  7781. if (dwRepeatsLeft && (*pmtNext > (mtLoopEnd - mtSegTime)))
  7782. {
  7783. if (mtLoopEnd >= mtSegTime) // This should always be true, but test anyway.
  7784. {
  7785. *pmtNext = mtLoopEnd - mtSegTime;
  7786. }
  7787. }
  7788. }
  7789. }
  7790. EnterCriticalSection(&m_MainCrSec);
  7791. if (m_pParamHook && SUCCEEDED(hr))
  7792. {
  7793. hr = m_pParamHook->GetParam(rguidType,dwGroupBits,dwIndex,mtTime,pmtNext,pData,
  7794. pSegSource,m_dwGetParamFlags,hr);
  7795. }
  7796. LeaveCriticalSection(&m_MainCrSec);
  7797. return hr;
  7798. }
  7799. /*
  7800. @method HRESULT | IDirectMusicPerformance | SetParam |
  7801. Sets data on a Track inside a Primary Segment in this Performance.
  7802. @rvalue S_OK | Success.
  7803. @rvalue DMUS_E_NO_MASTER_CLOCK | There is no master clock in the performance.
  7804. Make sure to call <om .Init> before calling this method.
  7805. */
  7806. HRESULT STDMETHODCALLTYPE CPerformance::SetParam(
  7807. REFGUID rguidType, // @parm The type of data to set.
  7808. DWORD dwGroupBits, // @parm The group the desired track is in. Use 0xffffffff
  7809. // for all groups.
  7810. DWORD dwIndex, // @parm Identifies which track, by index, in the group
  7811. // identified by <p dwGroupBits> to set the data.
  7812. MUSIC_TIME mtTime, // @parm The time at which to set the data. Unlike
  7813. // <om IDirectMusicSegment.SetParam>, this time is in
  7814. // performance time. The start time of the segment is
  7815. // subtracted from this time, and <om IDirectMusicSegment.SetParam>
  7816. // is called.
  7817. void* pData) // @parm The struture containing the data to set. Each
  7818. // <p pGuidType> identifies a particular structure of a
  7819. // particular size. It is important that this field contain
  7820. // the correct structure of the correct size. Otherwise,
  7821. // fatal results can occur.
  7822. {
  7823. V_INAME(IDirectMusicPerformance::SetParam);
  7824. V_PTR_WRITE_OPT(pData,1);
  7825. V_REFGUID(rguidType);
  7826. EnterCriticalSection(&m_MainCrSec);
  7827. if( m_pClock == NULL )
  7828. {
  7829. LeaveCriticalSection(&m_MainCrSec);
  7830. Trace(1,"Error: SetParam() failed because the performance has not been initialized.\n");
  7831. return DMUS_E_NO_MASTER_CLOCK;
  7832. }
  7833. LeaveCriticalSection(&m_MainCrSec);
  7834. CSegState* pSegNode;
  7835. IDirectMusicSegment* pSegment = NULL;
  7836. HRESULT hr;
  7837. EnterCriticalSection(&m_SegmentCrSec);
  7838. pSegNode = GetPrimarySegmentAtTime( mtTime );
  7839. MUSIC_TIME mtOffset;
  7840. DWORD dwRepeat;
  7841. if( pSegNode )
  7842. {
  7843. pSegment = pSegNode->m_pSegment;
  7844. pSegment->AddRef();
  7845. pSegNode->ConvertToSegTime( &mtTime, &mtOffset, &dwRepeat );
  7846. }
  7847. LeaveCriticalSection(&m_SegmentCrSec);
  7848. if( pSegment )
  7849. {
  7850. hr = pSegment->SetParam( rguidType, dwGroupBits, dwIndex,
  7851. mtTime, pData );
  7852. pSegment->Release();
  7853. }
  7854. else
  7855. {
  7856. Trace(1,"Error: SetParam failed because there is no segment at requested time.\n");
  7857. hr = DMUS_E_NOT_FOUND;
  7858. }
  7859. return hr;
  7860. }
  7861. /*
  7862. @method HRESULT | IDirectMusicPerformance | GetGlobalParam |
  7863. Gets global values from the Performance.
  7864. @rvalue S_OK | Success.
  7865. @rvalue E_INVALIDARG | <p pGuidType> isn't in the list of global data being handled by this
  7866. Performance. Make sure to call <om IDirectMusicPerformance.SetGlobalParam> first. Or,
  7867. the value of <p pData> doesn't point to valid memory. Or, <p dwSize> isn't the size
  7868. originally given in <om .SetGlobalParam>
  7869. @rvalue E_POINTER | <p pData> is NULL or invalid.
  7870. @xref <om .SetGlobalParam>
  7871. */
  7872. HRESULT STDMETHODCALLTYPE CPerformance::GetGlobalParam(
  7873. REFGUID rguidType, // @parm Identifies the type of data.
  7874. void* pData, // @parm Allocated memory to receive a copy of the data. This must be
  7875. // the correct size, which is constant for each <p pGuidType> type of
  7876. // data, and was also passed in to <om .SetGlobalParam>.
  7877. DWORD dwSize // @parm The size of the data in <p pData>. This should be constant for each
  7878. // <p pGuidType>. This parameter is needed because the Performance doesn't
  7879. // know about all types of data, allowing new ones to be created as needed.
  7880. )
  7881. {
  7882. V_INAME(IDirectMusicPerformance::GetGlobalParam);
  7883. V_REFGUID(rguidType);
  7884. if( dwSize )
  7885. {
  7886. V_BUFPTR_WRITE( pData, dwSize );
  7887. }
  7888. GlobalData* pGD;
  7889. HRESULT hr = S_OK;
  7890. EnterCriticalSection(&m_GlobalDataCrSec);
  7891. for( pGD = m_pGlobalData; pGD; pGD = pGD->pNext )
  7892. {
  7893. if( pGD->guidType == rguidType )
  7894. {
  7895. break;
  7896. }
  7897. }
  7898. if( pGD && ( dwSize == pGD->dwSize ) )
  7899. {
  7900. memcpy( pData, pGD->pData, pGD->dwSize );
  7901. }
  7902. else
  7903. {
  7904. #ifdef DBG
  7905. if (pGD && ( dwSize != pGD->dwSize ))
  7906. {
  7907. Trace(1,"Error: GetGlobalParam() failed because the passed data size %ld was inconsistent with %ld, set previously.\n",
  7908. dwSize, pGD->dwSize);
  7909. }
  7910. else
  7911. {
  7912. Trace(4,"Warning: GetGlobalParam() failed because the parameter had never been set.\n");
  7913. }
  7914. #endif
  7915. hr = E_INVALIDARG;
  7916. }
  7917. LeaveCriticalSection(&m_GlobalDataCrSec);
  7918. return hr;
  7919. }
  7920. /*
  7921. @method HRESULT | IDirectMusicPerformance | SetGlobalParam |
  7922. Set global values on the Performance.
  7923. @rvalue S_OK | Success.
  7924. @rvalue E_POINTER | <p pData> is NULL or invalid.
  7925. @rvalue E_OUTOFMEMORY | Ran out of memory.
  7926. @rvalue E_INVALIDARG | Other failure. pData or dwSize not correct?
  7927. @xref <om .GetGlobalParam>
  7928. */
  7929. HRESULT STDMETHODCALLTYPE CPerformance::SetGlobalParam(
  7930. REFGUID rguidType, // @parm Identifies the type of data.
  7931. void* pData, // @parm The data itself, which will be copied and stored by the Performance.
  7932. DWORD dwSize // @parm The size of the data in <p pData>. This should be constant for each
  7933. // <p pGuidType>. This parameter is needed because the Performance doesn't
  7934. // know about all types of data, allowing new ones to be created as needed.
  7935. )
  7936. {
  7937. V_INAME(IDirectMusicPerformance::SetGlobalParam);
  7938. V_REFGUID(rguidType);
  7939. if( dwSize )
  7940. {
  7941. V_BUFPTR_READ( pData, dwSize );
  7942. }
  7943. GlobalData* pGD;
  7944. // see if this is one of our special Performance globals
  7945. if( rguidType == GUID_PerfMasterTempo )
  7946. {
  7947. if( dwSize == sizeof(float) )
  7948. {
  7949. float flt;
  7950. memcpy( &flt, pData, sizeof(float) );
  7951. if( (flt >= DMUS_MASTERTEMPO_MIN) && (flt <= DMUS_MASTERTEMPO_MAX) )
  7952. {
  7953. if( m_fltRelTempo != flt )
  7954. {
  7955. m_fltRelTempo = flt;
  7956. // It's only necessary to recalc the tempo map if something is playing
  7957. EnterCriticalSection(&m_SegmentCrSec);
  7958. if (GetPrimarySegmentAtTime(m_mtTransported))
  7959. {
  7960. RecalcTempoMap(NULL,m_mtTransported);
  7961. }
  7962. LeaveCriticalSection(&m_SegmentCrSec);
  7963. }
  7964. }
  7965. }
  7966. else
  7967. {
  7968. Trace(1,"Error: Attempt to set global tempo failed because dwSize is not size of float.\n");
  7969. return E_INVALIDARG;
  7970. }
  7971. }
  7972. else if( rguidType == GUID_PerfMasterVolume )
  7973. {
  7974. // master volume
  7975. if( dwSize == sizeof(long) )
  7976. {
  7977. memcpy( &m_lMasterVolume, pData, sizeof(long) );
  7978. }
  7979. else
  7980. {
  7981. Trace(1,"Error: Attempt to set global volume failed because dwSize is not size of long.\n");
  7982. return E_INVALIDARG;
  7983. }
  7984. // Go through all Ports and set the master volume.
  7985. // This is also done upon adding a Port.
  7986. IDirectMusicPort* pPort;
  7987. DWORD dw;
  7988. EnterCriticalSection(&m_PChannelInfoCrSec);
  7989. for( dw = 0; dw < m_dwNumPorts; dw++ )
  7990. {
  7991. pPort = m_pPortTable[dw].pPort;
  7992. if( pPort )
  7993. {
  7994. IKsControl *pControl;
  7995. if(SUCCEEDED(pPort->QueryInterface(IID_IKsControl, (void**)&pControl)))
  7996. {
  7997. KSPROPERTY ksp;
  7998. ULONG cb;
  7999. memset(&ksp, 0, sizeof(ksp));
  8000. ksp.Set = GUID_DMUS_PROP_Volume;
  8001. ksp.Id = 0;
  8002. ksp.Flags = KSPROPERTY_TYPE_SET;
  8003. pControl->KsProperty(&ksp,
  8004. sizeof(ksp),
  8005. (LPVOID)&m_lMasterVolume,
  8006. sizeof(m_lMasterVolume),
  8007. &cb);
  8008. pControl->Release();
  8009. }
  8010. }
  8011. }
  8012. LeaveCriticalSection(&m_PChannelInfoCrSec);
  8013. }
  8014. // see if this type is already there. If so, use it.
  8015. EnterCriticalSection(&m_GlobalDataCrSec);
  8016. for( pGD = m_pGlobalData; pGD; pGD = pGD->pNext )
  8017. {
  8018. if( pGD->guidType == rguidType )
  8019. {
  8020. break;
  8021. }
  8022. }
  8023. LeaveCriticalSection(&m_GlobalDataCrSec);
  8024. // if it already exists, just copy the new data into the
  8025. // existing memory block and return
  8026. if( pGD )
  8027. {
  8028. if( pGD->dwSize != dwSize )
  8029. {
  8030. Trace(1,"Error: Attempt to set global parameter failed because dwSize is not consistent with previous SetGlobalParam() call.\n");
  8031. return E_INVALIDARG;
  8032. }
  8033. if( dwSize )
  8034. {
  8035. memcpy( pGD->pData, pData, dwSize );
  8036. }
  8037. return S_OK;
  8038. }
  8039. // otherwise, create new memory
  8040. pGD = new GlobalData;
  8041. if( NULL == pGD )
  8042. {
  8043. return E_OUTOFMEMORY;
  8044. }
  8045. pGD->dwSize = dwSize;
  8046. if( dwSize )
  8047. {
  8048. pGD->pData = (void*)(new char[dwSize]);
  8049. if( NULL == pGD->pData )
  8050. {
  8051. delete pGD;
  8052. return E_OUTOFMEMORY;
  8053. }
  8054. memcpy( pGD->pData, pData, dwSize );
  8055. }
  8056. else
  8057. {
  8058. pGD->pData = NULL;
  8059. }
  8060. pGD->guidType = rguidType;
  8061. EnterCriticalSection(&m_GlobalDataCrSec); // just using this one since it's available and not used much
  8062. pGD->pNext = m_pGlobalData;
  8063. m_pGlobalData = pGD;
  8064. LeaveCriticalSection(&m_GlobalDataCrSec);
  8065. return S_OK;
  8066. }
  8067. // IDirectMusicTool
  8068. /*
  8069. @method HRESULT | IDirectMusicTool | Init |
  8070. Called when the Tool is inserted into the Graph, providing the Tool the opportunity
  8071. to initialize itself.
  8072. @rvalue S_OK | Success.
  8073. @rvalue E_NOTIMPL | Not implemented is a valid return for the method.
  8074. */
  8075. HRESULT STDMETHODCALLTYPE CPerformance::Init(
  8076. IDirectMusicGraph* pGraph // @parm The calling graph.
  8077. )
  8078. {
  8079. return E_NOTIMPL;
  8080. }
  8081. inline bool CPerformance::SendShortMsg( IDirectMusicBuffer* pBuffer,
  8082. IDirectMusicPort* pPort,DWORD dwMsg,
  8083. REFERENCE_TIME rt, DWORD dwGroup)
  8084. {
  8085. if( FAILED( pBuffer->PackStructured( rt, dwGroup, dwMsg ) ) )
  8086. {
  8087. // ran out of room in the buffer
  8088. TraceI(2, "RAN OUT OF ROOM IN THE BUFFER!\n");
  8089. pPort->PlayBuffer( pBuffer );
  8090. pBuffer->Flush();
  8091. // try one more time
  8092. if( FAILED( pBuffer->PackStructured( rt, dwGroup, dwMsg ) ) )
  8093. {
  8094. TraceI(1, "MAJOR BUFFER PACKING FAILURE!\n");
  8095. // if it didn't work this time, free the event because something
  8096. // bad has happened.
  8097. return false;
  8098. }
  8099. }
  8100. return true;
  8101. }
  8102. //////////////////////////////////////////////////////////////////////
  8103. // CPerformance::PackNote
  8104. /*
  8105. HRESULT | CPerformance | PackNote |
  8106. Converts the message into a midiShortMsg, midiLongMsg, or user message
  8107. and packs it into the appropriate IDirectMusicBuffer in the PortTable,
  8108. setting the m_fBufferFilled flag.
  8109. DMUS_PMSG* | pPMsg |
  8110. [in] The message to pack into the buffer.
  8111. REFERENCE_TIME | mt |
  8112. [in] The time (in the Buffer's clock coordinates) at which to queue the message.
  8113. E_INVALIDARG | Either pPMsg or pBuffer is NULL.
  8114. E_OUTOFMEMORY | Failed to pack the buffer.
  8115. DMUS_S_REQUEUE | Tells the Pipeline to requeue this message.
  8116. DMUS_S_FREE | Tells the Pipeline to free this message.
  8117. */
  8118. HRESULT CPerformance::PackNote(
  8119. DMUS_PMSG* pEvent,
  8120. REFERENCE_TIME rt )
  8121. {
  8122. DMUS_NOTE_PMSG* pNote = (DMUS_NOTE_PMSG*)pEvent;
  8123. PRIV_PMSG* pPriv = DMUS_TO_PRIV(pEvent);
  8124. REFERENCE_TIME rtLogical; // the time the note occurs in logical music time (subtract offset)
  8125. IDirectMusicBuffer* pBuffer = NULL;
  8126. IDirectMusicPort* pPort = NULL;
  8127. DWORD dwMsg;
  8128. DWORD dwGroup, dwMChannel, dwPortTableIndex;
  8129. short nTranspose = 0;
  8130. short nValue;
  8131. HRESULT hr = DMUS_S_FREE;
  8132. if( NULL == pEvent )
  8133. return E_INVALIDARG;
  8134. if( FAILED( PChannelIndex( pNote->dwPChannel, &dwPortTableIndex, &dwGroup, &dwMChannel,
  8135. &nTranspose )))
  8136. {
  8137. Trace(1,"Play note failed on unassigned PChannel %ld\n",pNote->dwPChannel);
  8138. return DMUS_S_FREE; // the PChannel map wasn't found. Just free the event.
  8139. }
  8140. EnterCriticalSection(&m_PChannelInfoCrSec);
  8141. if( dwPortTableIndex > m_dwNumPorts )
  8142. {
  8143. pPort = NULL; // the PChannel map is out of range of the number of ports
  8144. // so return outta here! (see after the LeaveCriticalSection)
  8145. }
  8146. else
  8147. {
  8148. pPort = m_pPortTable[dwPortTableIndex].pPort;
  8149. if( pPort ) pPort->AddRef();
  8150. pBuffer = m_pPortTable[dwPortTableIndex].pBuffer;
  8151. if( pBuffer ) pBuffer->AddRef();
  8152. }
  8153. LeaveCriticalSection(&m_PChannelInfoCrSec);
  8154. if(pPort && pBuffer )
  8155. {
  8156. dwMsg = 0;
  8157. if( pNote->bFlags & DMUS_NOTEF_NOTEON )
  8158. {
  8159. // transpose the note's bMidiValue, and store it in the note so the note off
  8160. // plays the correct pitch.
  8161. nValue = pNote->bMidiValue + nTranspose;
  8162. if( ( nValue > 127 ) || ( nValue < 0 )
  8163. || pNote->mtDuration <= 0 )
  8164. {
  8165. // don't play this out-of-range or 0-duration note
  8166. pPort->Release();
  8167. pBuffer->Release();
  8168. return DMUS_S_FREE;
  8169. }
  8170. pNote->bMidiValue = (BYTE)nValue;
  8171. dwMsg |= pNote->bVelocity << 16;
  8172. }
  8173. else if( rt < pPriv->rtLast )
  8174. {
  8175. // the note off will play before the note on. Bad.
  8176. rt = pPriv->rtLast + REF_PER_MIL;
  8177. }
  8178. dwMsg |= pNote->bMidiValue << 8; // set note value
  8179. dwMsg |= dwMChannel; // MIDI Channel
  8180. if( pNote->bFlags & DMUS_NOTEF_NOTEON )
  8181. {
  8182. dwMsg |= MIDI_NOTEON;
  8183. }
  8184. else
  8185. {
  8186. dwMsg |= MIDI_NOTEOFF;
  8187. }
  8188. if (SendShortMsg(pBuffer,pPort,dwMsg,rt-2,dwGroup))
  8189. {
  8190. EnterCriticalSection(&m_PipelineCrSec); // to prevent deadlock in MusicToReferenceTime
  8191. EnterCriticalSection(&m_PChannelInfoCrSec);
  8192. m_pPortTable[dwPortTableIndex].fBufferFilled = TRUE; // so we send this in SendBuffers
  8193. rtLogical = rt;
  8194. // subtract the offset if needed, but only for a note on.
  8195. if( pNote->nOffset && (pNote->bFlags & DMUS_NOTEF_NOTEON))
  8196. {
  8197. MUSIC_TIME mtTemp = pNote->mtTime - pNote->nOffset + 1;
  8198. REFERENCE_TIME rtTemp;
  8199. MusicToReferenceTime( mtTemp, &rtTemp );
  8200. if( rtTemp > rtLogical )
  8201. {
  8202. rtLogical = rtTemp;
  8203. }
  8204. }
  8205. if( m_pPortTable[dwPortTableIndex].rtLast < rtLogical )
  8206. {
  8207. m_pPortTable[dwPortTableIndex].rtLast = rtLogical;
  8208. }
  8209. LeaveCriticalSection(&m_PChannelInfoCrSec);
  8210. LeaveCriticalSection(&m_PipelineCrSec);
  8211. if( pNote->bFlags & DMUS_NOTEF_NOTEON )
  8212. {
  8213. pPriv->rtLast = rt;
  8214. m_rtHighestPackedNoteOn = rt;
  8215. if (pNote->dwFlags & DMUS_PMSGF_LOCKTOREFTIME)
  8216. {
  8217. // This is a clock time message.
  8218. rt = pNote->rtTime;
  8219. pNote->rtTime += (pNote->mtDuration * REF_PER_MIL);
  8220. if (pNote->mtDuration > 1)
  8221. {
  8222. pNote->rtTime -= REF_PER_MIL;
  8223. }
  8224. // subtract 1 to guarantee that a note off at the same time as a note on doesn't
  8225. // stop the note on short. It's possible that rt == pNote->rtTime, if the duration
  8226. // was zero, so be sure to check that.
  8227. if( pNote->rtTime < rt + 1 )
  8228. {
  8229. pNote->rtTime = rt + 1;
  8230. }
  8231. pNote->bFlags &= ~DMUS_NOTEF_NOTEON; // make this a note off now
  8232. pNote->dwFlags &= ~DMUS_PMSGF_MUSICTIME;
  8233. hr = DMUS_S_REQUEUE;
  8234. }
  8235. else
  8236. {
  8237. pNote->mtTime += pNote->mtDuration;
  8238. if (pNote->mtDuration > 1)
  8239. {
  8240. pNote->mtTime--;
  8241. }
  8242. MusicToReferenceTime( pNote->mtTime, &rt );
  8243. // subtract 1 to guarantee that a note off at the same time as a note on doesn't
  8244. // stop the note on short. It's possible that rt == pNote->rtTime, if the duration
  8245. // was zero, so be sure to check that.
  8246. if( rt < pNote->rtTime + 2 )
  8247. {
  8248. rt = pNote->rtTime + 2;
  8249. }
  8250. pNote->rtTime = rt - 1;
  8251. }
  8252. pNote->bFlags &= ~DMUS_NOTEF_NOTEON; // make this a note off now
  8253. hr = DMUS_S_REQUEUE;
  8254. }
  8255. }
  8256. }
  8257. if( pPort ) pPort->Release();
  8258. if( pBuffer ) pBuffer->Release();
  8259. return hr;
  8260. }
  8261. //////////////////////////////////////////////////////////////////////
  8262. // CPerformance::PackCurve
  8263. HRESULT CPerformance::PackCurve(
  8264. DMUS_PMSG* pEvent,
  8265. REFERENCE_TIME rt )
  8266. {
  8267. DMUS_CURVE_PMSG* pCurve = (DMUS_CURVE_PMSG*)pEvent;
  8268. IDirectMusicBuffer* pBuffer = NULL;
  8269. IDirectMusicPort* pPort = NULL;
  8270. DWORD dwMsg;
  8271. HRESULT hr = DMUS_S_FREE;
  8272. BOOL fCalcStartValue = FALSE;
  8273. CChannelMap *pChannelMap = NULL;
  8274. if( NULL == pEvent )
  8275. return E_INVALIDARG;
  8276. // store the original start time so we know how far into the curve we are
  8277. if( pCurve->mtOriginalStart == 0 )
  8278. {
  8279. // if we're flushing and have never played this curve at all, just free
  8280. // it.
  8281. if( pCurve->dwFlags & DMUS_PMSGF_TOOL_FLUSH )
  8282. {
  8283. return DMUS_S_FREE;
  8284. }
  8285. if (pCurve->dwFlags & DMUS_PMSGF_LOCKTOREFTIME)
  8286. {
  8287. // This is a clock time message. Convert the duration into music time. It will act as
  8288. // a music time message from now on. This does have the downside that if a dramatic tempo
  8289. // change occurs in the middle of a lengthy curve, the end time can be distorted.
  8290. // But, given the purpose of curves, this is really an unlikely issue.
  8291. MUSIC_TIME mtTemp;
  8292. ReferenceToMusicTime(pCurve->rtTime + (pCurve->mtDuration * REF_PER_MIL),&mtTemp);
  8293. mtTemp -= pCurve->mtTime;
  8294. pCurve->mtDuration = mtTemp;
  8295. ReferenceToMusicTime(pCurve->rtTime + (pCurve->mtResetDuration * REF_PER_MIL),&mtTemp);
  8296. mtTemp -= pCurve->mtTime;
  8297. pCurve->mtResetDuration = mtTemp;
  8298. pCurve->dwFlags &= ~DMUS_PMSGF_LOCKTOREFTIME;
  8299. }
  8300. pCurve->mtOriginalStart = pCurve->mtTime;
  8301. // check the latency clock. Adjust pCurve->mtTime if needed. This can happen
  8302. // if the curve is time-stamped for the past. We only need do this for non-instant
  8303. // curve types.
  8304. if( pCurve->bCurveShape != DMUS_CURVES_INSTANT )
  8305. {
  8306. REFERENCE_TIME rtLatency = GetLatency();
  8307. MUSIC_TIME mtLatency;
  8308. ReferenceToMusicTime( rtLatency, &mtLatency );
  8309. if( pCurve->mtTime < mtLatency )
  8310. {
  8311. if( pCurve->mtTime + pCurve->mtDuration < mtLatency )
  8312. {
  8313. // If it is far enough in the past,
  8314. // we only need to send out the final value.
  8315. pCurve->mtTime += pCurve->mtDuration;
  8316. }
  8317. else
  8318. {
  8319. pCurve->mtTime = mtLatency;
  8320. }
  8321. }
  8322. // If this is the start of a curve and we are supposed to start with the current playing value...
  8323. if (pCurve->bFlags & DMUS_CURVE_START_FROM_CURRENT)
  8324. {
  8325. fCalcStartValue = TRUE;
  8326. }
  8327. else
  8328. {
  8329. pCurve->wMeasure = (WORD) ComputeCurveTimeSlice(pCurve); // Use this to store the time slice interval.
  8330. }
  8331. }
  8332. }
  8333. // it is necessary to check reset duration >= 0 because it could have been set
  8334. // to be negative by the flushing, and we don't want to toss it in that case.
  8335. // (should no longer be necessary to check, as a result of fixing 33987)
  8336. if( ( pCurve->bFlags & DMUS_CURVE_RESET ) && (pCurve->mtResetDuration >= 0) && ( pCurve->mtTime ==
  8337. pCurve->mtDuration + pCurve->mtResetDuration + pCurve->mtOriginalStart ))
  8338. {
  8339. if( !( pCurve->dwFlags & DMUS_PMSGF_TOOL_FLUSH ) )
  8340. {
  8341. PRIV_PMSG* pPrivPMsg = DMUS_TO_PRIV(pEvent);
  8342. if ( (pPrivPMsg->dwPrivFlags & PRIV_FLAG_FLUSH) )
  8343. {
  8344. pPrivPMsg->dwPrivFlags &= ~PRIV_FLAG_FLUSH;
  8345. pCurve->dwFlags |= DMUS_PMSGF_TOOL_FLUSH;
  8346. MUSIC_TIME mt = 0;
  8347. if( rt <= pPrivPMsg->rtLast )
  8348. {
  8349. return PackCurve( pEvent, pPrivPMsg->rtLast + REF_PER_MIL );
  8350. }
  8351. else
  8352. {
  8353. return PackCurve( pEvent, rt );
  8354. }
  8355. }
  8356. else
  8357. {
  8358. // the reset duration has expired, and we're not flushing, so expire the event.
  8359. return DMUS_S_FREE;
  8360. }
  8361. }
  8362. }
  8363. EnterCriticalSection(&m_PChannelInfoCrSec);
  8364. pChannelMap = GetPChannelMap(pCurve->dwPChannel);
  8365. if (!pChannelMap)
  8366. {
  8367. Trace(1,"Play curve failed on unassigned PChannel %ld\n",pCurve->dwPChannel);
  8368. LeaveCriticalSection(&m_PChannelInfoCrSec);
  8369. return DMUS_S_FREE; // the PChannel map wasn't found. Just free the event.
  8370. }
  8371. if( pChannelMap->dwPortIndex > m_dwNumPorts )
  8372. {
  8373. pPort = NULL; // the PChannel map is out of range of the number of ports
  8374. // so return outta here! (see after the LeaveCriticalSection)
  8375. }
  8376. else
  8377. {
  8378. pPort = m_pPortTable[pChannelMap->dwPortIndex].pPort;
  8379. if( pPort ) pPort->AddRef();
  8380. pBuffer = m_pPortTable[pChannelMap->dwPortIndex].pBuffer;
  8381. if( pBuffer ) pBuffer->AddRef();
  8382. }
  8383. if( pPort && pBuffer)
  8384. {
  8385. DWORD dwCurve;
  8386. DWORD dwMergeIndex = 0;
  8387. dwMsg = 0;
  8388. if (pCurve->dwFlags & DMUS_PMSGF_DX8)
  8389. {
  8390. dwMergeIndex = pCurve->wMergeIndex;
  8391. }
  8392. switch( pCurve->bType )
  8393. {
  8394. case DMUS_CURVET_PBCURVE:
  8395. if (fCalcStartValue)
  8396. {
  8397. pCurve->nStartValue =
  8398. (short) pChannelMap->m_PitchbendMerger.GetIndexedValue(dwMergeIndex) + 0x2000;
  8399. }
  8400. dwCurve = ComputeCurve( pCurve );
  8401. dwCurve = pChannelMap->m_PitchbendMerger.MergeValue(dwMergeIndex,(long)dwCurve,0x2000,0x3FFF);
  8402. dwMsg = MIDI_PBEND;
  8403. dwMsg |= ( (dwCurve & 0x7F) << 8);
  8404. dwCurve = dwCurve >> 7;
  8405. dwMsg |= ( (dwCurve & 0x7F) << 16);
  8406. break;
  8407. case DMUS_CURVET_CCCURVE:
  8408. switch (pCurve->bCCData)
  8409. {
  8410. case MIDI_CC_MOD_WHEEL:
  8411. if (fCalcStartValue)
  8412. {
  8413. pCurve->nStartValue =
  8414. (short) pChannelMap->m_ModWheelMerger.GetIndexedValue(dwMergeIndex) + 0x7F;
  8415. }
  8416. dwCurve = ComputeCurve( pCurve );
  8417. dwCurve = pChannelMap->m_ModWheelMerger.MergeValue(dwMergeIndex,(long)dwCurve,0x7F,0x7F);
  8418. break;
  8419. case MIDI_CC_VOLUME:
  8420. if (fCalcStartValue)
  8421. {
  8422. pCurve->nStartValue =
  8423. (short) pChannelMap->m_VolumeMerger.GetVolumeStart(dwMergeIndex);
  8424. }
  8425. dwCurve = ComputeCurve( pCurve );
  8426. dwCurve = pChannelMap->m_VolumeMerger.MergeMidiVolume(dwMergeIndex,(BYTE) dwCurve);
  8427. break;
  8428. case MIDI_CC_PAN:
  8429. if (fCalcStartValue)
  8430. {
  8431. pCurve->nStartValue =
  8432. (short) pChannelMap->m_PanMerger.GetIndexedValue(dwMergeIndex) + 0x40;
  8433. }
  8434. dwCurve = ComputeCurve( pCurve );
  8435. dwCurve = pChannelMap->m_PanMerger.MergeValue(dwMergeIndex,(long)dwCurve,0x40,0x7F);
  8436. break;
  8437. case MIDI_CC_EXPRESSION:
  8438. if (fCalcStartValue)
  8439. {
  8440. pCurve->nStartValue =
  8441. (short) pChannelMap->m_ExpressionMerger.GetVolumeStart(dwMergeIndex);
  8442. }
  8443. dwCurve = ComputeCurve( pCurve );
  8444. dwCurve = pChannelMap->m_ExpressionMerger.MergeMidiVolume(dwMergeIndex,(BYTE) dwCurve);
  8445. break;
  8446. case MIDI_CC_FILTER:
  8447. if (fCalcStartValue)
  8448. {
  8449. pCurve->nStartValue =
  8450. (short) pChannelMap->m_FilterMerger.GetIndexedValue(dwMergeIndex) + 0x40;
  8451. }
  8452. dwCurve = ComputeCurve( pCurve );
  8453. dwCurve = pChannelMap->m_FilterMerger.MergeValue(dwMergeIndex,(long)dwCurve,0x40,0x7F);
  8454. break;
  8455. case MIDI_CC_REVERB:
  8456. if (fCalcStartValue)
  8457. {
  8458. pCurve->nStartValue =
  8459. (short) pChannelMap->m_ReverbMerger.GetIndexedValue(dwMergeIndex) + 0x7F;
  8460. }
  8461. dwCurve = ComputeCurve( pCurve );
  8462. dwCurve = pChannelMap->m_ReverbMerger.MergeValue(dwMergeIndex,(long)dwCurve,0x7F,0x7F);
  8463. break;
  8464. case MIDI_CC_CHORUS:
  8465. if (fCalcStartValue)
  8466. {
  8467. pCurve->nStartValue =
  8468. (short) pChannelMap->m_ChorusMerger.GetIndexedValue(dwMergeIndex) + 0x7F;
  8469. }
  8470. dwCurve = ComputeCurve( pCurve );
  8471. dwCurve = pChannelMap->m_ChorusMerger.MergeValue(dwMergeIndex,(long)dwCurve,0x7F,0x7F);
  8472. break;
  8473. case MIDI_CC_RESETALL:
  8474. dwCurve = ComputeCurve( pCurve );
  8475. pChannelMap->Reset(pCurve->nEndValue);
  8476. break;
  8477. default:
  8478. dwCurve = ComputeCurve( pCurve );
  8479. break;
  8480. }
  8481. dwMsg = MIDI_CCHANGE;
  8482. dwMsg |= (pCurve->bCCData << 8);
  8483. dwMsg |= (dwCurve << 16);
  8484. break;
  8485. case DMUS_CURVET_MATCURVE:
  8486. dwCurve = ComputeCurve( pCurve );
  8487. dwMsg = MIDI_MTOUCH;
  8488. dwMsg |= (dwCurve << 8);
  8489. break;
  8490. case DMUS_CURVET_PATCURVE:
  8491. dwCurve = ComputeCurve( pCurve );
  8492. dwMsg = MIDI_PTOUCH;
  8493. dwMsg |= (pCurve->bCCData << 8);
  8494. dwMsg |= (dwCurve << 16);
  8495. break;
  8496. case DMUS_CURVET_RPNCURVE:
  8497. case DMUS_CURVET_NRPNCURVE:
  8498. if (pCurve->dwFlags & DMUS_PMSGF_DX8)
  8499. {
  8500. dwCurve = ComputeCurve( pCurve );
  8501. DWORD dwMsg2 = MIDI_CCHANGE;
  8502. dwMsg = MIDI_CCHANGE;
  8503. // First, send the two CC commands to select which RPN or NRPN event.
  8504. if (pCurve->bType == DMUS_CURVET_RPNCURVE)
  8505. {
  8506. dwMsg |= (MIDI_CC_RPN_MSB << 8);
  8507. dwMsg2 |= (MIDI_CC_RPN_LSB << 8);
  8508. }
  8509. else
  8510. {
  8511. dwMsg |= (MIDI_CC_NRPN_MSB << 8);
  8512. dwMsg2 |= (MIDI_CC_NRPN_LSB << 8);
  8513. }
  8514. dwMsg |= (pCurve->wParamType & 0x3F80) << 9; // Upper 8 bits of command #
  8515. dwMsg2 |= (pCurve->wParamType & 0x7F) << 16; // Lower 8 bits.
  8516. dwMsg |= pChannelMap->dwMChannel; // MIDI Channel
  8517. dwMsg2 |= pChannelMap->dwMChannel; // MIDI Channel
  8518. SendShortMsg(pBuffer,pPort,dwMsg,rt-3,pChannelMap->dwGroup); // Too bad if it fails!
  8519. SendShortMsg(pBuffer,pPort,dwMsg2,rt-2,pChannelMap->dwGroup);
  8520. // Then, send the two data CC commands.
  8521. dwMsg = MIDI_CCHANGE | (MIDI_CC_DATAENTRYMSB << 8);
  8522. dwMsg |= (dwCurve & 0x3F80) << 9; // Upper 8 bits of data
  8523. dwMsg |= pChannelMap->dwMChannel; // MIDI Channel
  8524. SendShortMsg(pBuffer,pPort,dwMsg,rt-1,pChannelMap->dwGroup);
  8525. dwMsg = MIDI_CCHANGE | (MIDI_CC_DATAENTRYLSB << 8);
  8526. dwMsg |= (dwCurve & 0x7F) << 16; // Lower 8 bits of data
  8527. }
  8528. }
  8529. if (dwMsg) // Make sure we successfully created a message.
  8530. {
  8531. dwMsg |= pChannelMap->dwMChannel; // MIDI Channel
  8532. if (SendShortMsg(pBuffer,pPort,dwMsg,rt,pChannelMap->dwGroup))
  8533. {
  8534. m_pPortTable[pChannelMap->dwPortIndex].fBufferFilled = TRUE; // so we send this in SendBuffers
  8535. m_pPortTable[pChannelMap->dwPortIndex].rtLast = rt;
  8536. // ComputeCurve() will set this to 0 if it's time to free the event. Otherwise, it
  8537. // will set it to the next time this event should be performed.
  8538. if( pCurve->rtTime )
  8539. {
  8540. // If we didn't calculate the time slice because we didn't know
  8541. // what the start value was, do it now.
  8542. if (fCalcStartValue)
  8543. {
  8544. pCurve->wMeasure = (WORD) ComputeCurveTimeSlice(pCurve); // Use this to store the time slice interval.
  8545. }
  8546. hr = DMUS_S_REQUEUE;
  8547. }
  8548. }
  8549. }
  8550. }
  8551. LeaveCriticalSection(&m_PChannelInfoCrSec);
  8552. if( pPort ) pPort->Release();
  8553. if( pBuffer ) pBuffer->Release();
  8554. return hr;
  8555. }
  8556. //////////////////////////////////////////////////////////////////////
  8557. // CPerformance::PackMidi
  8558. HRESULT CPerformance::PackMidi(
  8559. DMUS_PMSG* pEvent,
  8560. REFERENCE_TIME rt )
  8561. {
  8562. DMUS_MIDI_PMSG* pMidi = (DMUS_MIDI_PMSG*)pEvent;
  8563. IDirectMusicBuffer* pBuffer = NULL;
  8564. IDirectMusicPort* pPort = NULL;
  8565. DWORD dwMsg;
  8566. // DWORD dwGroup, dwMChannel, dwPortTableIndex;
  8567. HRESULT hr = DMUS_S_FREE;
  8568. CChannelMap *pChannelMap = NULL;
  8569. if( NULL == pMidi )
  8570. return E_INVALIDARG;
  8571. EnterCriticalSection(&m_PChannelInfoCrSec);
  8572. pChannelMap = GetPChannelMap(pMidi->dwPChannel);
  8573. if (!pChannelMap)
  8574. {
  8575. Trace(1,"Play MIDI failed on unassigned PChannel %ld\n",pMidi->dwPChannel);
  8576. LeaveCriticalSection(&m_PChannelInfoCrSec);
  8577. return DMUS_S_FREE; // the PChannel map wasn't found. Just free the event.
  8578. }
  8579. if( pChannelMap->dwPortIndex > m_dwNumPorts )
  8580. {
  8581. pPort = NULL; // the PChannel map is out of range of the number of ports
  8582. // so return outta here! (see after the LeaveCriticalSection)
  8583. }
  8584. else
  8585. {
  8586. pPort = m_pPortTable[pChannelMap->dwPortIndex].pPort;
  8587. if( pPort ) pPort->AddRef();
  8588. pBuffer = m_pPortTable[pChannelMap->dwPortIndex].pBuffer;
  8589. if( pBuffer ) pBuffer->AddRef();
  8590. }
  8591. if(pPort && pBuffer )
  8592. {
  8593. pMidi->bStatus &= 0xF0;
  8594. if (pMidi->bStatus == MIDI_CCHANGE)
  8595. {
  8596. switch (pMidi->bByte1)
  8597. {
  8598. case MIDI_CC_MOD_WHEEL:
  8599. pMidi->bByte2 = (BYTE) pChannelMap->m_ModWheelMerger.MergeValue(0,pMidi->bByte2,0x7F,0x7F);
  8600. break;
  8601. case MIDI_CC_VOLUME:
  8602. pMidi->bByte2 = pChannelMap->m_VolumeMerger.MergeMidiVolume(0,pMidi->bByte2);
  8603. break;
  8604. case MIDI_CC_PAN:
  8605. pMidi->bByte2 = (BYTE) pChannelMap->m_PanMerger.MergeValue(0,pMidi->bByte2,0x40,0x7F);
  8606. break;
  8607. case MIDI_CC_EXPRESSION:
  8608. pMidi->bByte2 = pChannelMap->m_ExpressionMerger.MergeMidiVolume(0,pMidi->bByte2);
  8609. break;
  8610. case MIDI_CC_FILTER:
  8611. pMidi->bByte2 = (BYTE) pChannelMap->m_FilterMerger.MergeValue(0,pMidi->bByte2,0x40,0x7F);
  8612. break;
  8613. case MIDI_CC_REVERB:
  8614. pMidi->bByte2 = (BYTE) pChannelMap->m_ReverbMerger.MergeValue(0,pMidi->bByte2,0x7F,0x7F);
  8615. break;
  8616. case MIDI_CC_CHORUS:
  8617. pMidi->bByte2 = (BYTE) pChannelMap->m_ChorusMerger.MergeValue(0,pMidi->bByte2,0x7F,0x7F);
  8618. break;
  8619. case MIDI_CC_RESETALL:
  8620. pChannelMap->Reset(pMidi->bByte2);
  8621. break;
  8622. }
  8623. }
  8624. else if (pMidi->bStatus == MIDI_PBEND)
  8625. {
  8626. WORD wBend = pMidi->bByte1 | (pMidi->bByte2 << 7);
  8627. wBend = (WORD) pChannelMap->m_PitchbendMerger.MergeValue(0,wBend,0x2000,0x3FFF);
  8628. pMidi->bByte1 = wBend & 0x7F;
  8629. pMidi->bByte2 = (wBend >> 7) & 0x7F;
  8630. }
  8631. dwMsg = pMidi->bByte1 << 8;
  8632. dwMsg |= pMidi->bByte2 << 16;
  8633. dwMsg |= pMidi->bStatus;
  8634. dwMsg |= pChannelMap->dwMChannel;
  8635. if (SendShortMsg(pBuffer,pPort,dwMsg,rt,pChannelMap->dwGroup))
  8636. {
  8637. m_pPortTable[pChannelMap->dwPortIndex].fBufferFilled = TRUE; // so we send this in SendBuffers
  8638. m_pPortTable[pChannelMap->dwPortIndex].rtLast = rt;
  8639. }
  8640. }
  8641. LeaveCriticalSection(&m_PChannelInfoCrSec);
  8642. if( pPort ) pPort->Release();
  8643. if( pBuffer ) pBuffer->Release();
  8644. return hr;
  8645. }
  8646. //////////////////////////////////////////////////////////////////////
  8647. // CPerformance::PackSysEx
  8648. HRESULT CPerformance::PackSysEx(
  8649. DMUS_PMSG* pEvent,
  8650. REFERENCE_TIME rt )
  8651. {
  8652. DMUS_SYSEX_PMSG* pSysEx = (DMUS_SYSEX_PMSG*)pEvent;
  8653. IDirectMusicBuffer* pBuffer = NULL;
  8654. IDirectMusicPort* pPort = NULL;
  8655. DWORD dwGroup, dwMChannel, dwPortTableIndex;
  8656. HRESULT hr = DMUS_S_FREE;
  8657. if( NULL == pEvent )
  8658. return E_INVALIDARG;
  8659. if( NULL == m_pDirectMusic )
  8660. return DMUS_E_NOT_INIT;
  8661. if( FAILED( PChannelIndex( pSysEx->dwPChannel, &dwPortTableIndex, &dwGroup, &dwMChannel)))
  8662. {
  8663. Trace(1,"Play SysEx failed on unassigned PChannel %ld\n",pSysEx->dwPChannel);
  8664. return DMUS_S_FREE; // the PChannel map wasn't found. Just free the event.
  8665. }
  8666. EnterCriticalSection(&m_PChannelInfoCrSec);
  8667. if( dwPortTableIndex > m_dwNumPorts )
  8668. {
  8669. pPort = NULL; // the PChannel map is out of range of the number of ports
  8670. // so return outta here! (see after the LeaveCriticalSection)
  8671. }
  8672. else
  8673. {
  8674. pPort = m_pPortTable[dwPortTableIndex].pPort;
  8675. if( pPort ) pPort->AddRef();
  8676. }
  8677. LeaveCriticalSection(&m_PChannelInfoCrSec);
  8678. if( pPort )
  8679. {
  8680. // create a buffer of the right size
  8681. DMUS_BUFFERDESC dmbd;
  8682. memset( &dmbd, 0, sizeof(DMUS_BUFFERDESC) );
  8683. dmbd.dwSize = sizeof(DMUS_BUFFERDESC);
  8684. dmbd.cbBuffer = pSysEx->dwLen + 48;
  8685. EnterCriticalSection(&m_MainCrSec);
  8686. if( SUCCEEDED( m_pDirectMusic->CreateMusicBuffer(&dmbd, &pBuffer, NULL)))
  8687. {
  8688. if( SUCCEEDED( pBuffer->PackUnstructured( rt - 4, dwGroup, pSysEx->dwLen, pSysEx->abData ) ) )
  8689. {
  8690. pPort->PlayBuffer(pBuffer);
  8691. }
  8692. pBuffer->Release();
  8693. }
  8694. LeaveCriticalSection(&m_MainCrSec);
  8695. }
  8696. if( pPort ) pPort->Release();
  8697. return hr;
  8698. }
  8699. //////////////////////////////////////////////////////////////////////
  8700. // CPerformance::PackPatch
  8701. HRESULT CPerformance::PackPatch(
  8702. DMUS_PMSG* pEvent,
  8703. REFERENCE_TIME rt )
  8704. {
  8705. DMUS_PATCH_PMSG* pPatch = (DMUS_PATCH_PMSG*)pEvent;
  8706. IDirectMusicBuffer* pBuffer = NULL;
  8707. IDirectMusicPort* pPort = NULL;
  8708. DWORD dwGroup, dwMChannel, dwPortTableIndex;
  8709. DWORD dwMsg;
  8710. HRESULT hr = DMUS_S_FREE;
  8711. if( NULL == pEvent )
  8712. return E_INVALIDARG;
  8713. if( FAILED( PChannelIndex( pPatch->dwPChannel, &dwPortTableIndex, &dwGroup, &dwMChannel)))
  8714. {
  8715. Trace(1,"Play Patch failed on unassigned PChannel %ld\n",pPatch->dwPChannel);
  8716. return DMUS_S_FREE; // the PChannel map wasn't found. Just free the event.
  8717. }
  8718. EnterCriticalSection(&m_PChannelInfoCrSec);
  8719. if( dwPortTableIndex > m_dwNumPorts )
  8720. {
  8721. pPort = NULL; // the PChannel map is out of range of the number of ports
  8722. // so return outta here! (see after the LeaveCriticalSection)
  8723. }
  8724. else
  8725. {
  8726. pPort = m_pPortTable[dwPortTableIndex].pPort;
  8727. if( pPort ) pPort->AddRef();
  8728. pBuffer = m_pPortTable[dwPortTableIndex].pBuffer;
  8729. if( pBuffer ) pBuffer->AddRef();
  8730. }
  8731. if( pPort && pBuffer)
  8732. {
  8733. // subtract 10 from rt to guarantee that patch events always go out earlier than
  8734. // notes with the same time stamp.
  8735. rt -= 10;
  8736. // send the bank select lsb
  8737. dwMsg = MIDI_CCHANGE;
  8738. dwMsg |= ( MIDI_CC_BS_LSB << 8 );
  8739. dwMsg |= (pPatch->byLSB << 16);
  8740. ASSERT( dwMChannel < 16 );
  8741. dwMsg |= dwMChannel;
  8742. SendShortMsg(pBuffer,pPort,dwMsg,rt-2,dwGroup);
  8743. // send the bank select msb
  8744. dwMsg = MIDI_CCHANGE;
  8745. dwMsg |= ( MIDI_CC_BS_MSB << 8 );
  8746. dwMsg |= (pPatch->byMSB << 16);
  8747. dwMsg |= dwMChannel;
  8748. SendShortMsg(pBuffer,pPort,dwMsg,rt-1,dwGroup);
  8749. // send the program change
  8750. dwMsg = MIDI_PCHANGE;
  8751. dwMsg |= (pPatch->byInstrument << 8);
  8752. dwMsg |= dwMChannel;
  8753. if (SendShortMsg(pBuffer,pPort,dwMsg,rt,dwGroup))
  8754. {
  8755. m_pPortTable[dwPortTableIndex].fBufferFilled = TRUE; // so we send this in SendBuffers
  8756. m_pPortTable[dwPortTableIndex].rtLast = rt;
  8757. }
  8758. }
  8759. LeaveCriticalSection(&m_PChannelInfoCrSec);
  8760. if( pPort ) pPort->Release();
  8761. if( pBuffer ) pBuffer->Release();
  8762. return hr;
  8763. }
  8764. HRESULT CPerformance::PackWave(DMUS_PMSG* pPMsg, REFERENCE_TIME rtTime)
  8765. {
  8766. DMUS_WAVE_PMSG* pWave = (DMUS_WAVE_PMSG*)pPMsg;
  8767. HRESULT hr = DMUS_S_FREE;
  8768. IDirectMusicVoiceP *pVoice = (IDirectMusicVoiceP *) pWave->punkUser;
  8769. if (pVoice)
  8770. {
  8771. if (pWave->bFlags & DMUS_WAVEF_OFF)
  8772. {
  8773. pVoice->Stop(rtTime);
  8774. EnterCriticalSection(&m_SegmentCrSec);
  8775. for (DWORD dwCount = 0; dwCount < SQ_COUNT; dwCount++)
  8776. {
  8777. for( CSegState* pSegSt = m_SegStateQueues[dwCount].GetHead(); pSegSt; pSegSt = pSegSt->GetNext() )
  8778. {
  8779. CTrack* pTrack = pSegSt->m_TrackList.GetHead();
  8780. while( pTrack )
  8781. {
  8782. if (pTrack->m_guidClassID == CLSID_DirectMusicWaveTrack)
  8783. {
  8784. IPrivateWaveTrack* pWaveTrack = NULL;
  8785. if (pTrack->m_pTrack &&
  8786. SUCCEEDED(pTrack->m_pTrack->QueryInterface(IID_IPrivateWaveTrack, (void**)&pWaveTrack)))
  8787. {
  8788. pWaveTrack->OnVoiceEnd(pVoice, pTrack->m_pTrackState);
  8789. pWaveTrack->Release();
  8790. }
  8791. }
  8792. pTrack = pTrack->GetNext();
  8793. }
  8794. }
  8795. }
  8796. LeaveCriticalSection(&m_SegmentCrSec);
  8797. }
  8798. else
  8799. {
  8800. if (SUCCEEDED(pVoice->Play(rtTime, pWave->lPitch, pWave->lVolume)))
  8801. {
  8802. if (pWave->dwFlags & DMUS_PMSGF_LOCKTOREFTIME)
  8803. {
  8804. // This is a clock time message.
  8805. pWave->rtTime += pWave->rtDuration ;
  8806. pWave->dwFlags &= ~DMUS_PMSGF_MUSICTIME;
  8807. }
  8808. else
  8809. {
  8810. pWave->mtTime += (MUSIC_TIME) pWave->rtDuration;
  8811. pWave->dwFlags &= ~DMUS_PMSGF_REFTIME;
  8812. }
  8813. pWave->bFlags |= DMUS_WAVEF_OFF; // Queue this back up as a wave off.
  8814. hr = DMUS_S_REQUEUE;
  8815. }
  8816. }
  8817. }
  8818. return hr;
  8819. }
  8820. HRESULT STDMETHODCALLTYPE CPerformance::ProcessPMsg(
  8821. IDirectMusicPerformance* pPerf, // @parm The performance pointer.
  8822. DMUS_PMSG* pPMsg // @parm The message to process.
  8823. )
  8824. {
  8825. V_INAME(IDirectMusicTool::ProcessPMsg);
  8826. V_INTERFACE(pPerf);
  8827. V_BUFPTR_WRITE(pPMsg,sizeof(DMUS_PMSG));
  8828. if (m_rtQueuePosition > pPMsg->rtTime + 50000000)
  8829. {
  8830. // pMSg is more than 5 seconds in the past; get rid of it unless it's signalling the
  8831. // end of something that's already been started.
  8832. if (pPMsg->dwType == DMUS_PMSGT_NOTIFICATION)
  8833. {
  8834. DMUS_NOTIFICATION_PMSG* pNotify = (DMUS_NOTIFICATION_PMSG*)pPMsg;
  8835. if ( (pNotify->guidNotificationType == GUID_NOTIFICATION_PERFORMANCE &&
  8836. pNotify->dwNotificationOption != DMUS_NOTIFICATION_MUSICSTOPPED) ||
  8837. (pNotify->guidNotificationType == GUID_NOTIFICATION_SEGMENT &&
  8838. pNotify->dwNotificationOption != DMUS_NOTIFICATION_SEGEND) )
  8839. {
  8840. return DMUS_S_FREE;
  8841. }
  8842. }
  8843. else if (pPMsg->dwType == DMUS_PMSGT_NOTE)
  8844. {
  8845. DMUS_NOTE_PMSG* pNote = (DMUS_NOTE_PMSG*)pPMsg;
  8846. if (pNote->bFlags & DMUS_NOTEF_NOTEON)
  8847. {
  8848. return DMUS_S_FREE;
  8849. }
  8850. }
  8851. else if (pPMsg->dwType == DMUS_PMSGT_WAVE)
  8852. {
  8853. DMUS_WAVE_PMSG* pWave = (DMUS_WAVE_PMSG*)pPMsg;
  8854. if (!(pWave->bFlags & DMUS_WAVEF_OFF))
  8855. {
  8856. return DMUS_S_FREE;
  8857. }
  8858. }
  8859. else
  8860. {
  8861. return DMUS_S_FREE;
  8862. }
  8863. }
  8864. HRESULT hr = DMUS_S_FREE;
  8865. ASSERT( pPerf == this );
  8866. if( pPMsg->dwType == DMUS_PMSGT_TEMPO )
  8867. {
  8868. PRIV_PMSG* pPrivPMsg = DMUS_TO_PRIV(pPMsg);
  8869. // If the pmsg was generated by a track, discard it
  8870. // because it was already placed in the tempo map.
  8871. if( pPrivPMsg->dwPrivFlags & PRIV_FLAG_TRACK )
  8872. {
  8873. return DMUS_S_FREE;
  8874. }
  8875. // Otherwise, this was generated by the application, so it's not already
  8876. // in the tempo map and we need to add it.
  8877. AddEventToTempoMap( DMUS_TO_PRIV(pPMsg));
  8878. return DMUS_S_FREE; // OK to free this event; not requeued
  8879. }
  8880. if ((pPMsg->dwPChannel == DMUS_PCHANNEL_BROADCAST_GROUPS) ||
  8881. (pPMsg->dwPChannel == DMUS_PCHANNEL_BROADCAST_PERFORMANCE))
  8882. {
  8883. // Scan through all the pchannels and make copies of the message for each pchannel.
  8884. // Then, release this one.
  8885. DWORD dwMax = PCHANNEL_BLOCKSIZE;
  8886. // If one per channel group (for sysex, for example,) do only one per block.
  8887. if (pPMsg->dwPChannel == DMUS_PCHANNEL_BROADCAST_GROUPS) dwMax = 1;
  8888. EnterCriticalSection(&m_PipelineCrSec); // Make sure we are in this so we don't deadlock in SendPMsg().
  8889. EnterCriticalSection(&m_PChannelInfoCrSec);
  8890. CChannelBlock* pChannelBlock = m_ChannelBlockList.GetHead();
  8891. for( ; pChannelBlock; pChannelBlock = pChannelBlock->GetNext() )
  8892. {
  8893. DWORD dwIndex;
  8894. for (dwIndex = 0; dwIndex < dwMax; dwIndex++)
  8895. {
  8896. CChannelMap* pChannelMap = &pChannelBlock->m_aChannelMap[ dwIndex ];
  8897. if( pChannelMap->dwGroup &&
  8898. (pChannelMap->wFlags & (CMAP_STATIC | CMAP_VIRTUAL)))
  8899. {
  8900. DWORD dwPChannel = dwIndex + pChannelBlock->m_dwPChannelStart;
  8901. // If this is a transpose on the drum channel, don't send it.
  8902. if ((pPMsg->dwType != DMUS_PMSGT_TRANSPOSE) || ((dwPChannel & 0xF) != 9))
  8903. {
  8904. DMUS_PMSG *pNewMsg;
  8905. if (SUCCEEDED(ClonePMsg(pPMsg,&pNewMsg)))
  8906. {
  8907. pNewMsg->dwPChannel = dwIndex + pChannelBlock->m_dwPChannelStart;
  8908. SendPMsg(pNewMsg);
  8909. }
  8910. }
  8911. }
  8912. }
  8913. }
  8914. LeaveCriticalSection(&m_PChannelInfoCrSec);
  8915. LeaveCriticalSection(&m_PipelineCrSec);
  8916. return DMUS_S_FREE;
  8917. }
  8918. if(pPMsg->dwType == DMUS_PMSGT_TRANSPOSE)
  8919. {
  8920. if( !( pPMsg->dwFlags & DMUS_PMSGF_TOOL_QUEUE ))
  8921. {
  8922. // requeue any tranpose event to be queue time
  8923. pPMsg->dwFlags |= DMUS_PMSGF_TOOL_QUEUE;
  8924. pPMsg->dwFlags &= ~( DMUS_PMSGF_TOOL_ATTIME | DMUS_PMSGF_TOOL_IMMEDIATE );
  8925. return DMUS_S_REQUEUE;
  8926. }
  8927. else
  8928. {
  8929. DMUS_TRANSPOSE_PMSG* pTrans = (DMUS_TRANSPOSE_PMSG*)pPMsg;
  8930. // set the PChannel for this transpose message
  8931. EnterCriticalSection(&m_PChannelInfoCrSec);
  8932. CChannelMap * pChannelMap = GetPChannelMap(pPMsg->dwPChannel);
  8933. if (pChannelMap)
  8934. {
  8935. WORD wMergeIndex = 0;
  8936. if (pPMsg->dwFlags & DMUS_PMSGF_DX8)
  8937. {
  8938. wMergeIndex = pTrans->wMergeIndex;
  8939. }
  8940. pChannelMap->nTranspose = pChannelMap->m_TransposeMerger.MergeTranspose(
  8941. wMergeIndex,pTrans->nTranspose);
  8942. }
  8943. LeaveCriticalSection(&m_PChannelInfoCrSec);
  8944. return DMUS_S_FREE;
  8945. }
  8946. }
  8947. if(pPMsg->dwType == DMUS_PMSGT_NOTIFICATION )
  8948. {
  8949. DMUS_NOTIFICATION_PMSG* pNotify = (DMUS_NOTIFICATION_PMSG*)pPMsg;
  8950. if (pNotify->guidNotificationType == GUID_NOTIFICATION_PRIVATE_CHORD)
  8951. {
  8952. // if we've got a GUID_NOTIFICATION_PRIVATE_CHORD,
  8953. // invalidate/regenerate queued note events as necessary
  8954. EnterCriticalSection(&m_PipelineCrSec);
  8955. OnChordUpdateEventQueues(pNotify);
  8956. LeaveCriticalSection(&m_PipelineCrSec);
  8957. return DMUS_S_FREE;
  8958. }
  8959. else if( !( pPMsg->dwFlags & DMUS_PMSGF_TOOL_ATTIME ))
  8960. {
  8961. // requeue any notification event to be ontime
  8962. pPMsg->dwFlags |= DMUS_PMSGF_TOOL_ATTIME;
  8963. pPMsg->dwFlags &= ~( DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_IMMEDIATE );
  8964. return DMUS_S_REQUEUE;
  8965. }
  8966. else
  8967. {
  8968. // otherwise, fire the notification
  8969. // first, move the event into the notification queue.
  8970. // The app then calls GetNotificationPMsg to get the event.
  8971. CLEARTOOLGRAPH(pPMsg);
  8972. EnterCriticalSection(&m_PipelineCrSec);
  8973. m_NotificationQueue.Enqueue( DMUS_TO_PRIV(pPMsg) );
  8974. LeaveCriticalSection(&m_PipelineCrSec);
  8975. EnterCriticalSection(&m_MainCrSec);
  8976. if( m_hNotification )
  8977. {
  8978. SetEvent(m_hNotification);
  8979. }
  8980. LeaveCriticalSection(&m_MainCrSec);
  8981. return S_OK; // don't free since we've placed the event into the
  8982. // notification queue
  8983. }
  8984. }
  8985. // add time signature changes to the time sig queue
  8986. if(pPMsg->dwType == DMUS_PMSGT_TIMESIG )
  8987. {
  8988. CLEARTOOLGRAPH(pPMsg);
  8989. DMUS_TIMESIG_PMSG* pTimeSig = (DMUS_TIMESIG_PMSG*)pPMsg;
  8990. // check for a legal time signature, which may not have any
  8991. // members equal to 0, and bBeat must be evenly divisible by 2.
  8992. if( pTimeSig->wGridsPerBeat &&
  8993. pTimeSig->bBeatsPerMeasure &&
  8994. pTimeSig->bBeat &&
  8995. ( 0 == ( pTimeSig->bBeat % 2 )))
  8996. {
  8997. EnterCriticalSection(&m_PipelineCrSec);
  8998. REFERENCE_TIME rtNow = GetTime() - (10000 * 1000); // keep around for a second.
  8999. PRIV_PMSG* pCheck;
  9000. while (pCheck = m_TimeSigQueue.FlushOldest(rtNow))
  9001. {
  9002. FreePMsg(pCheck);
  9003. }
  9004. m_TimeSigQueue.Enqueue( DMUS_TO_PRIV(pPMsg) );
  9005. LeaveCriticalSection(&m_PipelineCrSec);
  9006. return S_OK;
  9007. }
  9008. else
  9009. {
  9010. return DMUS_S_FREE;
  9011. }
  9012. }
  9013. // requeue anything else that's early to be neartime
  9014. if (pPMsg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE)
  9015. {
  9016. // if this is a stop command, make sure the segment state doesn't keep going
  9017. if( pPMsg->dwType == DMUS_PMSGT_STOP )
  9018. {
  9019. IDirectMusicSegment* pSeg = NULL;
  9020. IDirectMusicSegmentState* pSegState = NULL;
  9021. if( pPMsg->punkUser )
  9022. {
  9023. if( FAILED( pPMsg->punkUser->QueryInterface( IID_IDirectMusicSegment,
  9024. (void**)&pSeg )))
  9025. {
  9026. pSeg = NULL;
  9027. }
  9028. else if( FAILED( pPMsg->punkUser->QueryInterface( IID_IDirectMusicSegmentState,
  9029. (void**)&pSegState )))
  9030. {
  9031. pSegState = NULL;
  9032. }
  9033. }
  9034. if( pSeg || pSegState )
  9035. {
  9036. EnterCriticalSection(&m_SegmentCrSec);
  9037. if( pPMsg->mtTime > m_mtTransported )
  9038. {
  9039. // find and mark the segment and/or segment state to not play beyond
  9040. // the stop point.
  9041. CSegState* pNode;
  9042. DWORD dwCount;
  9043. for (dwCount = 0; dwCount < SQ_COUNT; dwCount++)
  9044. {
  9045. for( pNode = m_SegStateQueues[dwCount].GetHead(); pNode; pNode = pNode->GetNext() )
  9046. {
  9047. if( (pNode->m_pSegment == pSeg) ||
  9048. (pNode == pSegState) )
  9049. {
  9050. pNode->m_mtStopTime = pPMsg->mtTime;
  9051. }
  9052. }
  9053. }
  9054. }
  9055. LeaveCriticalSection(&m_SegmentCrSec);
  9056. if( pSeg )
  9057. {
  9058. pSeg->Release();
  9059. }
  9060. if( pSegState )
  9061. {
  9062. pSegState->Release();
  9063. }
  9064. }
  9065. }
  9066. pPMsg->dwFlags |= DMUS_PMSGF_TOOL_QUEUE;
  9067. pPMsg->dwFlags &= ~( DMUS_PMSGF_TOOL_ATTIME | DMUS_PMSGF_TOOL_IMMEDIATE );
  9068. return DMUS_S_REQUEUE;
  9069. }
  9070. switch( pPMsg->dwType )
  9071. {
  9072. case DMUS_PMSGT_NOTE:
  9073. {
  9074. hr = PackNote( pPMsg, pPMsg->rtTime );
  9075. }
  9076. break;
  9077. case DMUS_PMSGT_CURVE:
  9078. {
  9079. hr = PackCurve( pPMsg, pPMsg->rtTime );
  9080. }
  9081. break;
  9082. case DMUS_PMSGT_SYSEX:
  9083. {
  9084. hr = PackSysEx( pPMsg, pPMsg->rtTime );
  9085. }
  9086. break;
  9087. case DMUS_PMSGT_MIDI:
  9088. {
  9089. hr = PackMidi( pPMsg, pPMsg->rtTime );
  9090. }
  9091. break;
  9092. case DMUS_PMSGT_PATCH:
  9093. {
  9094. hr = PackPatch( pPMsg, pPMsg->rtTime );
  9095. }
  9096. break;
  9097. case DMUS_PMSGT_CHANNEL_PRIORITY:
  9098. {
  9099. DMUS_CHANNEL_PRIORITY_PMSG* pPriPMsg = (DMUS_CHANNEL_PRIORITY_PMSG*)pPMsg;
  9100. DWORD dwPortTableIndex, dwGroup, dwMChannel;
  9101. IDirectMusicPort* pPort;
  9102. hr = DMUS_S_FREE;
  9103. if( SUCCEEDED( PChannelIndex( pPriPMsg->dwPChannel, &dwPortTableIndex, &dwGroup,
  9104. &dwMChannel )))
  9105. {
  9106. EnterCriticalSection(&m_PChannelInfoCrSec);
  9107. if( dwPortTableIndex <= m_dwNumPorts )
  9108. {
  9109. pPort = m_pPortTable[dwPortTableIndex].pPort;
  9110. if( pPort )
  9111. {
  9112. pPort->SetChannelPriority( dwGroup, dwMChannel,
  9113. pPriPMsg->dwChannelPriority );
  9114. }
  9115. }
  9116. LeaveCriticalSection(&m_PChannelInfoCrSec);
  9117. }
  9118. }
  9119. break;
  9120. case DMUS_PMSGT_WAVE:
  9121. {
  9122. hr = PackWave( pPMsg, pPMsg->rtTime );
  9123. }
  9124. default:
  9125. break;
  9126. }
  9127. return hr;
  9128. }
  9129. HRESULT STDMETHODCALLTYPE CPerformance::Flush(
  9130. IDirectMusicPerformance* pPerf, // @parm The Performance pointer.
  9131. DMUS_PMSG* pPMsg, // @parm The event to flush.
  9132. REFERENCE_TIME rtTime // @parm The time at which to flush.
  9133. )
  9134. {
  9135. V_INAME(IDirectMusicTool::Flush);
  9136. V_INTERFACE(pPerf);
  9137. V_BUFPTR_WRITE(pPMsg,sizeof(DMUS_PMSG));
  9138. HRESULT hr = S_OK;
  9139. ASSERT( pPerf == this );
  9140. switch( pPMsg->dwType )
  9141. {
  9142. case DMUS_PMSGT_NOTE:
  9143. {
  9144. DMUS_NOTE_PMSG* pNote = (DMUS_NOTE_PMSG*)pPMsg;
  9145. if( !(pNote->bFlags & DMUS_NOTEF_NOTEON) )
  9146. {
  9147. PackNote( pPMsg, rtTime );
  9148. }
  9149. }
  9150. break;
  9151. case DMUS_PMSGT_CURVE:
  9152. {
  9153. DMUS_CURVE_PMSG* pCurve = (DMUS_CURVE_PMSG*)pPMsg;
  9154. if( pCurve->bFlags & DMUS_CURVE_RESET )
  9155. {
  9156. PackCurve( pPMsg, rtTime );
  9157. }
  9158. }
  9159. break;
  9160. case DMUS_PMSGT_WAVE:
  9161. {
  9162. DMUS_WAVE_PMSG* pWave = (DMUS_WAVE_PMSG*)pPMsg;
  9163. if (pWave->bFlags & DMUS_WAVEF_OFF)
  9164. {
  9165. PackWave( pPMsg, rtTime );
  9166. }
  9167. }
  9168. default:
  9169. break;
  9170. }
  9171. return hr;
  9172. }
  9173. HRESULT STDMETHODCALLTYPE CPerformance::GetMsgDeliveryType(
  9174. DWORD* pdwDeliveryType) // @parm Should return either DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGF_TOOL_QUEUE, or DMUS_PMSGF_TOOL_ATTIME.
  9175. // An illegal return value will be treated as DMUS_PMSGF_TOOL_IMMEDIATE by the <i IDirectMusicGraph>.
  9176. {
  9177. V_INAME(IDirectMusicTool::GetMsgDeliveryType);
  9178. V_PTR_WRITE(pdwDeliveryType,DWORD);
  9179. *pdwDeliveryType = DMUS_PMSGF_TOOL_IMMEDIATE;
  9180. return S_OK;
  9181. }
  9182. HRESULT STDMETHODCALLTYPE CPerformance::GetMediaTypeArraySize(
  9183. DWORD* pdwNumElements) // @parm Returns the number of media types, with 0 meaning all.
  9184. {
  9185. V_INAME(IDirectMusicTool::GetMediaTypeArraySize);
  9186. V_PTR_WRITE(pdwNumElements,DWORD);
  9187. *pdwNumElements = 0;
  9188. return S_OK;
  9189. }
  9190. HRESULT STDMETHODCALLTYPE CPerformance::GetMediaTypes(
  9191. DWORD** padwMediaTypes, // @parm This should be a DWORD array of size <p dwNumElements>.
  9192. // Upon return, the elements will be filled with the media types
  9193. // this Tool supports.
  9194. DWORD dwNumElements) // @parm Contains the number of elements, i.e. the size, of the
  9195. // array <p padwMediaTypes>. <p dwNumElements> should be equal
  9196. // to the number returned in
  9197. // <om IDirectMusicTool.GetMediaTypeArraySize>. If dwNumElements
  9198. // is less than this number, this method can't return all of the
  9199. // message types that are supported. If it is greater than this
  9200. // number, the element fields in the array will be set to zero.
  9201. {
  9202. return E_NOTIMPL;
  9203. }
  9204. // IDirectMusicGraph
  9205. HRESULT STDMETHODCALLTYPE CPerformance::Shutdown()
  9206. {
  9207. return E_NOTIMPL;
  9208. }
  9209. HRESULT STDMETHODCALLTYPE CPerformance::InsertTool(
  9210. IDirectMusicTool *pTool,
  9211. DWORD *pdwPChannels,
  9212. DWORD cPChannels,
  9213. LONG lIndex)
  9214. {
  9215. return E_NOTIMPL;
  9216. }
  9217. HRESULT STDMETHODCALLTYPE CPerformance::GetTool(
  9218. DWORD dwIndex,
  9219. IDirectMusicTool** ppTool)
  9220. {
  9221. return E_NOTIMPL;
  9222. }
  9223. HRESULT STDMETHODCALLTYPE CPerformance::RemoveTool(
  9224. IDirectMusicTool* pTool)
  9225. {
  9226. return E_NOTIMPL;
  9227. }
  9228. HRESULT STDMETHODCALLTYPE CPerformance::StampPMsg( DMUS_PMSG* pPMsg )
  9229. {
  9230. V_INAME(IDirectMusicGraph::StampPMsg);
  9231. if( m_dwVersion < 8)
  9232. {
  9233. V_PTR_WRITE(pPMsg,sizeof(DMUS_PMSG));
  9234. }
  9235. else
  9236. {
  9237. #ifdef DBG
  9238. V_PTR_WRITE(pPMsg,sizeof(DMUS_PMSG));
  9239. #else
  9240. if (!pPMsg)
  9241. {
  9242. return E_POINTER;
  9243. }
  9244. #endif
  9245. }
  9246. EnterCriticalSection(&m_MainCrSec);
  9247. if( m_pGraph && ( S_OK == m_pGraph->StampPMsg( pPMsg )))
  9248. {
  9249. if (pPMsg->pGraph != this)
  9250. {
  9251. if( pPMsg->pGraph )
  9252. {
  9253. pPMsg->pGraph->Release();
  9254. pPMsg->pGraph = NULL;
  9255. }
  9256. pPMsg->pGraph = this;
  9257. pPMsg->pGraph->AddRef();
  9258. }
  9259. LeaveCriticalSection(&m_MainCrSec);
  9260. return S_OK;
  9261. }
  9262. LeaveCriticalSection(&m_MainCrSec);
  9263. if( pPMsg->pGraph )
  9264. {
  9265. pPMsg->pGraph->Release();
  9266. pPMsg->pGraph = NULL;
  9267. }
  9268. if( pPMsg->pTool )
  9269. {
  9270. pPMsg->pTool->Release();
  9271. pPMsg->pTool = NULL;
  9272. }
  9273. //otherwise there is no graph: set it to the internal Performance Tool
  9274. pPMsg->dwFlags &= ~(DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME);
  9275. pPMsg->dwFlags |= DMUS_PMSGF_TOOL_QUEUE;
  9276. pPMsg->pTool = this;
  9277. pPMsg->pTool->AddRef();
  9278. return S_OK;
  9279. }
  9280. // default scale is C Major
  9281. const DWORD DEFAULT_SCALE_PATTERN = 0xab5ab5;
  9282. inline DWORD BitCount(DWORD dwPattern)
  9283. {
  9284. DWORD dwCount = 0;
  9285. while (dwPattern)
  9286. {
  9287. dwPattern &= (dwPattern - 1);
  9288. dwCount++;
  9289. }
  9290. return dwCount;
  9291. }
  9292. inline bool InScale(BYTE bMIDI, BYTE bRoot, DWORD dwScale)
  9293. {
  9294. TraceI(3, "note: %d root: %d scale: %x\n", bMIDI, bRoot, dwScale);
  9295. // shift the note by the scale root, and put it in a one-octave range
  9296. bMIDI = ((bMIDI + 12) - (bRoot % 12)) % 12;
  9297. // merge two octaves of scale into one
  9298. dwScale = (dwScale & 0x0fff) | ((dwScale >> 12) & 0x0fff);
  9299. // note n is in scale if there's a bit in position n
  9300. TraceI(3, "shifted note: %d shifted scale: %x\n", bMIDI, dwScale);
  9301. return ((1 << bMIDI) & dwScale) ? true : false;
  9302. }
  9303. inline DWORD CleanupScale(DWORD dwPattern)
  9304. // Force scale to be exactly two octaves
  9305. {
  9306. dwPattern &= 0x0FFF; // Clear upper octave.
  9307. dwPattern |= (dwPattern << 12); // Copy lower octave to top.
  9308. return dwPattern;
  9309. }
  9310. inline DWORD PatternMatch(DWORD dwA, DWORD dwB)
  9311. {
  9312. DWORD dwHit = 0;
  9313. DWORD dwIndex = 0;
  9314. for (;dwIndex < 24; dwIndex++)
  9315. {
  9316. if ((dwA & (1 << dwIndex)) == (dwB & (1 << dwIndex)))
  9317. {
  9318. dwHit++;
  9319. }
  9320. }
  9321. return dwHit;
  9322. }
  9323. static DWORD dwFallbackScales[12] =
  9324. {
  9325. 0xab5ab5,0x6ad6ad,
  9326. 0x5ab5ab,0xad5ad5,
  9327. 0x6b56b5,0x5ad5ad,
  9328. 0x56b56b,0xd5ad5a,
  9329. 0xb56b56,0xd6ad6a,
  9330. 0xb5ab5a,0xad6ad6,
  9331. };
  9332. inline DWORD FixScale(DWORD dwScale)
  9333. {
  9334. if (BitCount(dwScale & 0xFFF) > 4)
  9335. {
  9336. return dwScale;
  9337. }
  9338. DWORD dwBest = 0;
  9339. DWORD dwBestPattern = DEFAULT_SCALE_PATTERN;
  9340. DWORD dwX;
  9341. for (dwX = 0;dwX < 12; dwX++)
  9342. {
  9343. DWORD dwTest = PatternMatch(dwScale,dwFallbackScales[dwX]);
  9344. if (dwTest > dwBest)
  9345. {
  9346. dwBestPattern = dwFallbackScales[dwX];
  9347. dwBest = dwTest;
  9348. }
  9349. }
  9350. return dwBestPattern;
  9351. }
  9352. inline DWORD ThreeOctave(DWORD dwScale)
  9353. {
  9354. DWORD dwResult = dwScale;
  9355. // don't change third octave if there's something there
  9356. if ( !(0xFFF000000 & dwScale) )
  9357. {
  9358. // copy second octave to third octave
  9359. dwResult |= (dwScale & 0xFFF000) << 12;
  9360. }
  9361. return dwResult;
  9362. }
  9363. inline DWORD AddRootToScale(BYTE bScaleRoot, DWORD dwScalePattern)
  9364. {
  9365. dwScalePattern = CleanupScale(dwScalePattern);
  9366. dwScalePattern >>= (12 - (bScaleRoot % 12));
  9367. dwScalePattern = CleanupScale(dwScalePattern);
  9368. return dwScalePattern;
  9369. }
  9370. inline DWORD SubtractRootFromScale(BYTE bScaleRoot, DWORD dwScalePattern)
  9371. {
  9372. dwScalePattern = CleanupScale(dwScalePattern);
  9373. dwScalePattern >>= (bScaleRoot % 12);
  9374. dwScalePattern = CleanupScale(dwScalePattern);
  9375. return dwScalePattern;
  9376. }
  9377. static DWORD ChordFromScale(BYTE bRoot, DWORD dwScalePattern)
  9378. {
  9379. DWORD dwChordPattern = CleanupScale(dwScalePattern >> (bRoot % 12));
  9380. DWORD dwX;
  9381. DWORD dwBitCount = 0;
  9382. for (dwX = 0; dwX < 24; dwX++)
  9383. {
  9384. DWORD dwBit = 1 << dwX;
  9385. if (dwChordPattern & dwBit)
  9386. {
  9387. if ((dwBitCount & 1) || (dwBitCount > 7))
  9388. {
  9389. dwChordPattern &= ~dwBit;
  9390. }
  9391. dwBitCount++;
  9392. }
  9393. }
  9394. return dwChordPattern;
  9395. }
  9396. static DWORD InvertChord(BYTE bKey, BYTE bChordRoot, DWORD dwChordPattern, bool& rfBelowRoot)
  9397. {
  9398. // rotate the chord by the difference between the key and chord root
  9399. rfBelowRoot = false;
  9400. bKey %= 12;
  9401. bChordRoot %= 12;
  9402. if (bKey < bChordRoot) bKey += 12;
  9403. BYTE bRotate = bKey - bChordRoot;
  9404. // first check if the whole chord fits into one octave
  9405. if ( !(dwChordPattern & 0xFFF000) )
  9406. {
  9407. dwChordPattern = ThreeOctave(CleanupScale(dwChordPattern));
  9408. dwChordPattern >>= bRotate;
  9409. dwChordPattern &= 0xFFF;
  9410. if (!(dwChordPattern & 0x7) && ((dwChordPattern & 0xc00)) ||
  9411. !(dwChordPattern & 0x3) && ((dwChordPattern & 0x800)))
  9412. {
  9413. dwChordPattern |= (dwChordPattern << 12);
  9414. dwChordPattern &= 0x3FFC00;
  9415. rfBelowRoot = true;
  9416. }
  9417. }
  9418. else
  9419. {
  9420. dwChordPattern &= 0xFFFFFF; // make sure there are only notes in the two-octave range
  9421. // do a circular shift in the closest direction
  9422. BYTE bRotate2 = (bChordRoot + 12) - bKey;
  9423. if (bRotate <= bRotate2)
  9424. {
  9425. dwChordPattern = (dwChordPattern << (24 - bRotate)) | (dwChordPattern >> bRotate);
  9426. }
  9427. else
  9428. {
  9429. dwChordPattern = (dwChordPattern >> (24 - bRotate2)) | (dwChordPattern << bRotate2);
  9430. }
  9431. dwChordPattern &= 0xFFFFFF;
  9432. if (!(dwChordPattern & 0x7) &&
  9433. (!(dwChordPattern & 0x7000) && ((dwChordPattern & 0xc00000)) ||
  9434. !(dwChordPattern & 0x3000) && ((dwChordPattern & 0x800000)) ||
  9435. !(dwChordPattern & 0x1000) && ((dwChordPattern & 0x1000000)) ||
  9436. !(dwChordPattern & 0x7) && (dwChordPattern & 0x7000) && ((dwChordPattern & 0xc00000))) )
  9437. {
  9438. dwChordPattern = (dwChordPattern << 12) | (dwChordPattern >> 12);
  9439. dwChordPattern &= 0xFFFFFF;
  9440. }
  9441. if (!(dwChordPattern & 0x7) && ((dwChordPattern & 0xc00)) ||
  9442. !(dwChordPattern & 0x3) && ((dwChordPattern & 0x800)) ||
  9443. !(dwChordPattern & 0x1) && ((dwChordPattern & 0x1000)) )
  9444. {
  9445. // put everything up to the G in the first octave two octaves up;
  9446. // put G# and A one octave up
  9447. dwChordPattern |= (((dwChordPattern & 0xFF) << 24) | ((dwChordPattern & 0x300) << 12));
  9448. // get rid of everything below A# in the first octave
  9449. dwChordPattern &= 0xFFFFFC00;
  9450. // If there are no notes lower than C2, shift everything back down an octave
  9451. if (!(dwChordPattern & 0xFFF))
  9452. {
  9453. dwChordPattern >>= 12;
  9454. }
  9455. else
  9456. {
  9457. rfBelowRoot = true;
  9458. }
  9459. }
  9460. }
  9461. return dwChordPattern;
  9462. }
  9463. /* This is SuperJAM! code */
  9464. static unsigned char OldMusicValueToNote(
  9465. unsigned short value, // Music value to convert.
  9466. char scalevalue, // Scale value if chord failes.
  9467. long keypattern, // Description of key as interval pattern.
  9468. char keyroot, // Root note of key.
  9469. long chordpattern, // Description of chord as interval pattern.
  9470. char chordroot) // Root note of chord.
  9471. {
  9472. unsigned char result ;
  9473. char octpart = (char)(value >> 12) ;
  9474. char chordpart = (char)((value >> 8) & 0xF) ;
  9475. char keypart = (char)((value >> 4) & 0xF) ;
  9476. char accpart = (char)(value & 0xF) ;
  9477. result = unsigned char(12 * octpart) ;
  9478. result += chordroot ;
  9479. if( accpart > 8 )
  9480. accpart -= 16 ;
  9481. for( ; chordpattern ; result++ ) {
  9482. if( chordpattern & 1L ) {
  9483. if( !chordpart )
  9484. break ;
  9485. chordpart-- ;
  9486. }
  9487. chordpattern = chordpattern >> 1L ;
  9488. if( !chordpattern ) {
  9489. if( !scalevalue )
  9490. return( 0 ) ;
  9491. result = unsigned char(12 * octpart) ;
  9492. result += chordroot ;
  9493. keypart = char(scalevalue >> 4) ;
  9494. accpart = char(scalevalue & 0x0F) ;
  9495. break ;
  9496. }
  9497. }
  9498. if( keypart ) {
  9499. keypattern = CleanupScale(keypattern) ;
  9500. keypattern = keypattern >> (LONG)((result - keyroot) % 12) ;
  9501. for( ; keypattern ; result++ ) {
  9502. if( keypattern & 1L ) {
  9503. if( !keypart )
  9504. break ;
  9505. keypart-- ;
  9506. }
  9507. keypattern = keypattern >> 1L ;
  9508. }
  9509. }
  9510. result += unsigned char(accpart) ;
  9511. return( result ) ;
  9512. }
  9513. /* This is SuperJAM! code */
  9514. static unsigned short OldNoteToMusicValue(
  9515. unsigned char note, // MIDI note to convert.
  9516. long keypattern, // Description of key as interval pattern.
  9517. char keyroot, // Root note of key.
  9518. long chordpattern, // Description of chord as interval pattern.
  9519. char chordroot) // Root note of chord.
  9520. {
  9521. unsigned char octpart = 0 ;
  9522. unsigned char chordpart = 0;
  9523. unsigned char keypart = (BYTE)-1 ;
  9524. unsigned char accpart = 0 ;
  9525. unsigned char scan, test, base, last ; // was char
  9526. long pattern ;
  9527. short testa, testb ;
  9528. scan = chordroot ;
  9529. // If we're trying to play a note below the bottom of our chord, forget it
  9530. if( note < scan)
  9531. {
  9532. return 0;
  9533. }
  9534. while( scan < (note - 24) )
  9535. {
  9536. scan += 12 ;
  9537. octpart++ ;
  9538. }
  9539. base = last = scan ;
  9540. for( ; base<=note ; base+=12 )
  9541. {
  9542. chordpart = (unsigned char)-1 ;
  9543. pattern = chordpattern ;
  9544. scan = last = base ;
  9545. if( scan == note )
  9546. {
  9547. accpart = 0;
  9548. while (!(pattern & 1) && pattern)
  9549. {
  9550. accpart--;
  9551. pattern >>= 1;
  9552. }
  9553. return( (unsigned short) (octpart << 12) + (accpart & 0xF)) ; // if octave, return.
  9554. }
  9555. for( ; pattern ; pattern=pattern >> 1 )
  9556. {
  9557. if( pattern & 1 ) // chord interval?
  9558. {
  9559. if( scan == note ) // note in chord?
  9560. {
  9561. chordpart++ ;
  9562. return((unsigned short) ((octpart << 12) | (chordpart << 8))) ; // yes, return.
  9563. }
  9564. else if (scan > note) // above note?
  9565. {
  9566. test = scan ;
  9567. break ; // go on to key.
  9568. }
  9569. chordpart++ ;
  9570. last = scan ;
  9571. }
  9572. scan++ ;
  9573. }
  9574. if( !pattern ) // end of chord.
  9575. {
  9576. test = unsigned char(base + 12) ; // set to next note.
  9577. }
  9578. octpart++ ;
  9579. if( test > note )
  9580. {
  9581. break ; // above our note?
  9582. }
  9583. }
  9584. octpart-- ;
  9585. // To get here, the note is not in the chord. Scan should show the last
  9586. // note in the chord. octpart and chordpart have their final values.
  9587. // Now, increment up the key to find the match.
  9588. scan = last ;
  9589. pattern = CleanupScale(keypattern);
  9590. pattern = pattern >> ((scan - keyroot) % 12) ;
  9591. for( ; pattern ; pattern=pattern >> 1 )
  9592. {
  9593. if( 1 & pattern )
  9594. {
  9595. keypart++ ;
  9596. accpart = 0 ;
  9597. }
  9598. else
  9599. {
  9600. accpart++ ;
  9601. }
  9602. if( scan == note )
  9603. break ;
  9604. scan++;
  9605. }
  9606. if( accpart && keypart )
  9607. {
  9608. testa = short((octpart << 12) + (chordpart << 8) + (keypart << 4) + accpart + 1);
  9609. testb = short((octpart << 12) + ((chordpart + 1) << 8) + 0);
  9610. testa = OldMusicValueToNote( testa, 0, keypattern, keyroot,
  9611. chordpattern, chordroot );
  9612. testb = OldMusicValueToNote( testb, 0, keypattern, keyroot,
  9613. chordpattern, chordroot );
  9614. if( testa == testb )
  9615. {
  9616. chordpart++ ;
  9617. keypart = 0 ;
  9618. accpart = -1 ;
  9619. }
  9620. }
  9621. // If the conversion didn't find an exact match, fudge accpart to make it work
  9622. testa = short((octpart << 12) + (chordpart << 8) + (keypart << 4) + (accpart & 0xF));
  9623. testa = OldMusicValueToNote( testa, 0, keypattern, keyroot,
  9624. chordpattern, chordroot );
  9625. if( testa != note )
  9626. {
  9627. accpart += note - testa;
  9628. }
  9629. return unsigned short((octpart << 12) + (chordpart << 8) + (keypart << 4) + (accpart & 0xF));
  9630. }
  9631. inline short MusicValueOctave(WORD wMusicValue)
  9632. { return short((wMusicValue >> 12) & 0xf) * 12; }
  9633. inline short MusicValueAccidentals(WORD wMusicValue)
  9634. {
  9635. short acc = short(wMusicValue & 0xf);
  9636. return (acc > 8) ? acc - 16 : acc;
  9637. }
  9638. inline short BitsInChord(DWORD dwChordPattern)
  9639. {
  9640. for (short nResult = 0; dwChordPattern != 0; dwChordPattern >>= 1)
  9641. if (dwChordPattern & 1) nResult++;
  9642. return nResult;
  9643. }
  9644. #define S_OVER_CHORD 0x1000 // Success code to indicate the musicval could not be
  9645. // converted because the note is above the top of the chord.
  9646. short MusicValueIntervals(WORD wMusicValue, BYTE bPlayModes, DMUS_SUBCHORD *pSubChord, BYTE bRoot)
  9647. {
  9648. if ((bPlayModes & DMUS_PLAYMODE_CHORD_INTERVALS) || (bPlayModes & DMUS_PLAYMODE_SCALE_INTERVALS))
  9649. {
  9650. DWORD dwDefaultScale =
  9651. (pSubChord->dwScalePattern) ? (pSubChord->dwScalePattern) : DEFAULT_SCALE_PATTERN;
  9652. dwDefaultScale = AddRootToScale(pSubChord->bScaleRoot, dwDefaultScale);
  9653. dwDefaultScale = ThreeOctave(FixScale(dwDefaultScale));
  9654. DWORD dwChordPattern = pSubChord->dwChordPattern;
  9655. if (!dwChordPattern) dwChordPattern = 1;
  9656. bool fBelowRoot = false;
  9657. if ((bPlayModes & DMUS_PLAYMODE_KEY_ROOT) && bPlayModes != DMUS_PLAYMODE_PEDALPOINT)
  9658. {
  9659. dwChordPattern = InvertChord(bRoot, pSubChord->bChordRoot, dwChordPattern, fBelowRoot);
  9660. }
  9661. const short nChordPosition = (wMusicValue >> 8) & 0xf;
  9662. // const short nScalePosition = (wMusicValue >> 4) & 0xf;
  9663. // ensure that scale position is < 8
  9664. const short nScalePosition = (wMusicValue >> 4) & 0x7;
  9665. const short nChordBits = BitsInChord(dwChordPattern);
  9666. short nSemitones = 0;
  9667. // If the chord doesn't have a root or second, but does have a seventh, it's been inverted and
  9668. // we need to start below the root
  9669. short nTransposetones;
  9670. DWORD dwPattern;
  9671. short nPosition;
  9672. BYTE bOctRoot = bRoot % 12; // root in one octave
  9673. // if using chord intervals and the note is in the chord
  9674. if ((bPlayModes & DMUS_PLAYMODE_CHORD_INTERVALS) &&
  9675. !nScalePosition &&
  9676. (nChordPosition < nChordBits) )
  9677. {
  9678. nTransposetones = bRoot + MusicValueAccidentals(wMusicValue);
  9679. dwPattern = dwChordPattern;
  9680. nPosition = nChordPosition;
  9681. }
  9682. // if using chord intervals and note is inside the chord (including 6ths)
  9683. else if ((bPlayModes & DMUS_PLAYMODE_CHORD_INTERVALS) &&
  9684. (nChordPosition < nChordBits) )
  9685. {
  9686. dwPattern = dwChordPattern;
  9687. nPosition = nChordPosition;
  9688. if (dwPattern)
  9689. {
  9690. // skip to the first note in the chord
  9691. while (!(dwPattern & 1))
  9692. {
  9693. dwPattern >>= 1;
  9694. nSemitones++;
  9695. }
  9696. }
  9697. if (nPosition > 0)
  9698. {
  9699. do
  9700. {
  9701. dwPattern >>= 1; // this will ignore the first note in the chord
  9702. nSemitones++;
  9703. if (dwPattern & 1)
  9704. {
  9705. nPosition--;
  9706. }
  9707. if (!dwPattern)
  9708. {
  9709. nSemitones += nPosition;
  9710. // assert (0); // This shouldn't happen...
  9711. break;
  9712. }
  9713. } while (nPosition > 0);
  9714. }
  9715. nSemitones += bOctRoot;
  9716. nTransposetones = MusicValueAccidentals(wMusicValue) + bRoot - bOctRoot;
  9717. dwPattern = dwDefaultScale >> (nSemitones % 12); // start comparing partway through the pattern
  9718. nPosition = nScalePosition;
  9719. }
  9720. // if using scale intervals
  9721. else if (bPlayModes & DMUS_PLAYMODE_SCALE_INTERVALS)
  9722. {
  9723. fBelowRoot = false; // forget about chord inversions
  9724. nSemitones = bOctRoot;
  9725. nTransposetones = MusicValueAccidentals(wMusicValue) + bRoot - bOctRoot;
  9726. dwPattern = dwDefaultScale >> bOctRoot; // start comparing partway through the pattern
  9727. nPosition = nChordPosition * 2 + nScalePosition;
  9728. }
  9729. else
  9730. {
  9731. return S_OVER_CHORD; //
  9732. }
  9733. nPosition++; // Now nPosition corresponds to actual scale positions
  9734. for (; nPosition > 0; dwPattern >>= 1)
  9735. {
  9736. nSemitones++;
  9737. if (dwPattern & 1)
  9738. {
  9739. nPosition--;
  9740. }
  9741. if (!dwPattern)
  9742. {
  9743. nSemitones += nPosition;
  9744. // assert (0); // This shouldn't happen...
  9745. break;
  9746. }
  9747. }
  9748. nSemitones--; // the loop counts one too many semitones...
  9749. if (fBelowRoot)
  9750. {
  9751. nSemitones -=12;
  9752. }
  9753. return nSemitones + nTransposetones;
  9754. }
  9755. else
  9756. {
  9757. // should be impossible for 2.5 format
  9758. return bRoot + wMusicValue;
  9759. }
  9760. }
  9761. inline short MusicValueChord(WORD wMusicValue, BYTE bPlayModes, DMUS_SUBCHORD *pSubChord, BYTE bKey)
  9762. {
  9763. // first, get the root for transposition.
  9764. BYTE bRoot = 0;
  9765. if (bPlayModes & DMUS_PLAYMODE_CHORD_ROOT)
  9766. {
  9767. bRoot = pSubChord->bChordRoot;
  9768. }
  9769. else if (bPlayModes & DMUS_PLAYMODE_KEY_ROOT)
  9770. bRoot = bKey;
  9771. // Next, get an interval and combine it with the root.
  9772. return MusicValueIntervals(wMusicValue, bPlayModes, pSubChord, bRoot);
  9773. }
  9774. inline short MusicValueConvert(WORD wMV, BYTE bPlayModes, DMUS_SUBCHORD *pSubChord, BYTE bKey)
  9775. {
  9776. short nResult = 0;
  9777. // First, make sure the octave is not negative.
  9778. short nOffset = 0;
  9779. while (wMV >= 0xE000)
  9780. {
  9781. wMV += 0x1000;
  9782. nOffset -= 12;
  9783. }
  9784. // If the music value has a negative scale offset, convert to an equivalent
  9785. // music value with a positive offset (up an octave) and shift the whole thing
  9786. // down an octave
  9787. WORD wTemp = (wMV & 0x00f0) + 0x0070;
  9788. if (wTemp & 0x0f00)
  9789. {
  9790. wMV = (wMV & 0xff0f) | (wTemp & 0x00f0);
  9791. nOffset = -12;
  9792. }
  9793. short nChordValue = MusicValueChord(wMV, bPlayModes, pSubChord, bKey);
  9794. if (nChordValue != S_OVER_CHORD)
  9795. {
  9796. nChordValue += nOffset;
  9797. // If the chord root is < 12, take the result down an octave.
  9798. if ((bPlayModes & DMUS_PLAYMODE_CHORD_ROOT))
  9799. nResult = MusicValueOctave(wMV) + nChordValue - 12;
  9800. else
  9801. nResult = MusicValueOctave(wMV) + nChordValue;
  9802. }
  9803. else
  9804. nResult = S_OVER_CHORD;
  9805. return nResult;
  9806. }
  9807. HRESULT STDMETHODCALLTYPE CPerformance::MIDIToMusic(
  9808. BYTE bMIDIValue,
  9809. DMUS_CHORD_KEY* pChord,
  9810. BYTE bPlayMode,
  9811. BYTE bChordLevel,
  9812. WORD *pwMusicValue
  9813. )
  9814. {
  9815. V_INAME(IDirectMusicPerformance::MIDIToMusic);
  9816. V_BUFPTR_READ( pChord, sizeof(DMUS_CHORD_KEY) );
  9817. V_PTR_WRITE(pwMusicValue,WORD);
  9818. long lMusicValue;
  9819. HRESULT hr = S_OK;
  9820. #ifdef DBG
  9821. long lMIDIInTraceValue = bMIDIValue;
  9822. #endif
  9823. if ((bPlayMode & DMUS_PLAYMODE_NONE ) || (bMIDIValue & 0x80))
  9824. {
  9825. Trace(1,"Error: MIDIToMusic conversion failed either because there is no playmode or MIDI value %ld is out of range.\n",(long)bMIDIValue);
  9826. return E_INVALIDARG;
  9827. }
  9828. else if( bPlayMode == DMUS_PLAYMODE_FIXED )
  9829. {
  9830. *pwMusicValue = bMIDIValue & 0x7F;
  9831. return S_OK;
  9832. }
  9833. else if (bPlayMode == DMUS_PLAYMODE_FIXEDTOKEY) // fixed to key
  9834. {
  9835. lMusicValue = bMIDIValue - pChord->bKey;
  9836. while (lMusicValue < 0)
  9837. {
  9838. lMusicValue += 12;
  9839. Trace(2,"Warning: MIDIToMusic had to bump the music value up an octave for DMUS_PLAYMODE_FIXEDTOKEY note.\n");
  9840. hr = DMUS_S_UP_OCTAVE;
  9841. }
  9842. while (lMusicValue > 127)
  9843. {
  9844. lMusicValue -= 12;
  9845. Trace(2,"Warning: MIDIToMusic had to bump the music value up an octave for DMUS_PLAYMODE_FIXEDTOKEY note.\n");
  9846. hr = DMUS_S_DOWN_OCTAVE;
  9847. }
  9848. *pwMusicValue = (WORD) lMusicValue;
  9849. return hr;
  9850. }
  9851. else
  9852. {
  9853. DMUS_SUBCHORD *pSubChord;
  9854. DWORD dwLevel = 1 << bChordLevel;
  9855. bool fFoundLevel = false;
  9856. for (int i = 0; i < pChord->bSubChordCount; i++)
  9857. {
  9858. if (dwLevel & pChord->SubChordList[i].dwLevels)
  9859. {
  9860. pSubChord = &pChord->SubChordList[i];
  9861. fFoundLevel = true;
  9862. break;
  9863. }
  9864. }
  9865. if (!fFoundLevel) // No luck? Use first chord.
  9866. {
  9867. pSubChord = &pChord->SubChordList[0];
  9868. }
  9869. if (bPlayMode == DMUS_PLAYMODE_FIXEDTOCHORD) // fixed to chord
  9870. {
  9871. lMusicValue = bMIDIValue - (pSubChord->bChordRoot % 24);
  9872. while (lMusicValue < 0)
  9873. {
  9874. lMusicValue += 12;
  9875. Trace(2,"Warning: MIDIToMusic had to bump the music value up an octave for DMUS_PLAYMODE_FIXEDTOCHORD note.\n");
  9876. hr = DMUS_S_UP_OCTAVE;
  9877. }
  9878. while (lMusicValue > 127)
  9879. {
  9880. lMusicValue -= 12;
  9881. Trace(2,"Warning: MIDIToMusic had to bump the music value down an octave for DMUS_PLAYMODE_FIXEDTOCHORD note.\n");
  9882. hr = DMUS_S_DOWN_OCTAVE;
  9883. }
  9884. *pwMusicValue = (WORD) lMusicValue;
  9885. return hr;
  9886. }
  9887. bool fBelowRoot = false;
  9888. DWORD dwScalePattern = AddRootToScale(pSubChord->bScaleRoot, pSubChord->dwScalePattern);
  9889. DWORD dwChordPattern = pSubChord->dwChordPattern;
  9890. BYTE bKeyRoot = pChord->bKey;
  9891. BYTE bChordRoot = pSubChord->bChordRoot;
  9892. dwScalePattern = FixScale(dwScalePattern);
  9893. bPlayMode &= 0xF; // We only know about the bottom four flags, at this point.
  9894. // if (bPlayMode == DMUS_PLAYMODE_PEDALPOINT)
  9895. // Do this for any non-fixed key root mode (Pedalpoint, PedalpointChord, PedalpointAlways)
  9896. if (bPlayMode & DMUS_PLAYMODE_KEY_ROOT)
  9897. {
  9898. while (bKeyRoot > bMIDIValue)
  9899. {
  9900. hr = DMUS_S_UP_OCTAVE;
  9901. Trace(2,"Warning: MIDIToMusic had to bump the music value up an octave for DMUS_PLAYMODE_KEY_ROOT note.\n");
  9902. bMIDIValue += 12;
  9903. }
  9904. dwScalePattern = SubtractRootFromScale(bKeyRoot,dwScalePattern);
  9905. if (bPlayMode == DMUS_PLAYMODE_PEDALPOINT || !dwChordPattern)
  9906. {
  9907. bChordRoot = bKeyRoot;
  9908. dwChordPattern = ChordFromScale(0,dwScalePattern);
  9909. }
  9910. else
  9911. {
  9912. dwChordPattern = InvertChord(bKeyRoot, bChordRoot, dwChordPattern, fBelowRoot);
  9913. BYTE bNewChordRoot = 0;
  9914. if (dwChordPattern)
  9915. {
  9916. for (; !(dwChordPattern & (1 << bNewChordRoot)); bNewChordRoot++);
  9917. }
  9918. bChordRoot = bNewChordRoot + bKeyRoot;
  9919. dwChordPattern >>= bNewChordRoot;
  9920. }
  9921. }
  9922. else if (bPlayMode == DMUS_PLAYMODE_MELODIC)
  9923. {
  9924. bKeyRoot = 0;
  9925. dwChordPattern = ChordFromScale(bChordRoot,dwScalePattern);
  9926. }
  9927. else
  9928. {
  9929. bKeyRoot = 0;
  9930. if (!dwChordPattern)
  9931. {
  9932. dwChordPattern = ChordFromScale(bChordRoot,dwScalePattern);
  9933. }
  9934. }
  9935. BOOL fDropOctave = FALSE;
  9936. if (bMIDIValue < 24)
  9937. {
  9938. fDropOctave = TRUE;
  9939. bMIDIValue += 24;
  9940. }
  9941. WORD wNewMusicValue = OldNoteToMusicValue( bMIDIValue,
  9942. dwScalePattern,
  9943. bKeyRoot,
  9944. dwChordPattern,
  9945. bChordRoot );
  9946. if (fDropOctave)
  9947. {
  9948. wNewMusicValue -= 0x2000;
  9949. bMIDIValue -= 24;
  9950. }
  9951. // If DMUS_PLAYMODE_CHORD_ROOT is set, take the result up an octave.
  9952. // // also take the result up for the new pedalpoint chord modes.
  9953. if( (bPlayMode & DMUS_PLAYMODE_CHORD_ROOT) ||
  9954. fBelowRoot)
  9955. //((bPlayMode & DMUS_PLAYMODE_KEY_ROOT) && bPlayMode != DMUS_PLAYMODE_PEDALPOINT) )
  9956. {
  9957. wNewMusicValue += 0x1000;
  9958. }
  9959. short nTest =
  9960. MusicValueConvert(wNewMusicValue, bPlayMode,
  9961. pSubChord, pChord->bKey);
  9962. if (nTest == (short) bMIDIValue)
  9963. {
  9964. *pwMusicValue = wNewMusicValue;
  9965. }
  9966. else
  9967. {
  9968. if (nTest == S_OVER_CHORD)
  9969. {
  9970. if (BitCount(pSubChord->dwChordPattern) < 4)
  9971. {
  9972. DWORD dwOldChordPattern = pSubChord->dwChordPattern;
  9973. pSubChord->dwChordPattern = ChordFromScale(bChordRoot,dwScalePattern);
  9974. nTest =
  9975. MusicValueConvert(wNewMusicValue, bPlayMode,
  9976. pSubChord, pChord->bKey);
  9977. pSubChord->dwChordPattern = dwOldChordPattern;
  9978. if (nTest == (short) bMIDIValue)
  9979. {
  9980. *pwMusicValue = wNewMusicValue;
  9981. return hr;
  9982. }
  9983. }
  9984. }
  9985. *pwMusicValue = wNewMusicValue;
  9986. #ifdef DBG // Put in brackets just in case the compiler is using something different than DBG for turning on Trace.
  9987. Trace(1,"Error: Unable to convert MIDI value %ld to Music value. This usually means the DMUS_CHORD_KEY structure has an invalid chord or scale pattern.\n",
  9988. lMIDIInTraceValue);
  9989. #endif
  9990. return DMUS_E_CANNOT_CONVERT;
  9991. }
  9992. }
  9993. return hr;
  9994. }
  9995. HRESULT STDMETHODCALLTYPE CPerformance::MusicToMIDI(
  9996. WORD wMusicValue,
  9997. DMUS_CHORD_KEY* pChord,
  9998. BYTE bPlayMode,
  9999. BYTE bChordLevel,
  10000. BYTE *pbMIDIValue
  10001. )
  10002. {
  10003. V_INAME(IDirectMusicPerformance::MusicToMIDI);
  10004. V_BUFPTR_READ( pChord, sizeof(DMUS_CHORD_KEY) );
  10005. V_PTR_WRITE(pbMIDIValue,BYTE);
  10006. long lReturnVal = wMusicValue;
  10007. HRESULT hr = S_OK;
  10008. if (bPlayMode != DMUS_PLAYMODE_FIXED)
  10009. {
  10010. DMUS_SUBCHORD *pSubChord;
  10011. DWORD dwLevel = 1 << bChordLevel;
  10012. bool fFoundLevel = false;
  10013. for (int i = 0; i < pChord->bSubChordCount; i++)
  10014. {
  10015. if (dwLevel & pChord->SubChordList[i].dwLevels)
  10016. {
  10017. pSubChord = &pChord->SubChordList[i];
  10018. fFoundLevel = true;
  10019. break;
  10020. }
  10021. }
  10022. if (!fFoundLevel) // No luck? Use first chord.
  10023. {
  10024. pSubChord = &pChord->SubChordList[0];
  10025. }
  10026. if (bPlayMode & DMUS_PLAYMODE_NONE )
  10027. {
  10028. *pbMIDIValue = 0;
  10029. Trace(1,"Error: Unable to convert Music value to MIDI because the playmode is DMUS_PLAYMODE_NONE.\n");
  10030. return E_INVALIDARG;
  10031. }
  10032. if (bPlayMode == DMUS_PLAYMODE_FIXEDTOCHORD) // fixed to chord
  10033. {
  10034. lReturnVal += (pSubChord->bChordRoot % 24);
  10035. }
  10036. else if (bPlayMode == DMUS_PLAYMODE_FIXEDTOKEY) // fixed to scale
  10037. {
  10038. lReturnVal += pChord->bKey;
  10039. }
  10040. else
  10041. {
  10042. lReturnVal =
  10043. MusicValueConvert((WORD)lReturnVal, bPlayMode, pSubChord, pChord->bKey);
  10044. }
  10045. }
  10046. if (lReturnVal == S_OVER_CHORD)
  10047. {
  10048. Trace(5,"Warning: MIDIToMusic unable to convert because note out of chord range.\n");
  10049. return DMUS_S_OVER_CHORD;
  10050. }
  10051. while (lReturnVal < 0)
  10052. {
  10053. lReturnVal += 12;
  10054. Trace(2,"Warning: MusicToMIDI had to bump the music value up an octave to stay in MIDI range.\n");
  10055. hr = DMUS_S_UP_OCTAVE;
  10056. }
  10057. while (lReturnVal > 127)
  10058. {
  10059. lReturnVal -= 12;
  10060. Trace(2,"Warning: MusicToMIDI had to bump the music value down an octave to stay in MIDI range.\n");
  10061. hr = DMUS_S_DOWN_OCTAVE;
  10062. }
  10063. *pbMIDIValue = (BYTE) lReturnVal;
  10064. return hr;
  10065. }
  10066. // returns:
  10067. // S_OK if the note should be invalidated (any other return code will not invalidate)
  10068. // S_FALSE if processing otherwise succeeded, but the note should not be invalidated
  10069. // E_OUTOFMEMORY if allocation of a new note failed
  10070. HRESULT CPerformance::GetChordNotificationStatus(DMUS_NOTE_PMSG* pNote,
  10071. //IDirectMusicSegment* pSegment,
  10072. DWORD dwTrackGroup,
  10073. REFERENCE_TIME rtTime,
  10074. DMUS_PMSG** ppNew)
  10075. {
  10076. HRESULT hr = S_FALSE; // default: succeed, but don't invalidate the note
  10077. DMUS_CHORD_PARAM CurrentChord;
  10078. MUSIC_TIME mtTime;
  10079. ReferenceToMusicTime(rtTime, &mtTime);
  10080. if (pNote->bFlags & (DMUS_NOTEF_NOINVALIDATE_INSCALE | DMUS_NOTEF_NOINVALIDATE_INCHORD))
  10081. {
  10082. // If the note is inconsistent with the current scale/chord, invalidate it
  10083. if (SUCCEEDED(GetParam(GUID_ChordParam, dwTrackGroup, DMUS_SEG_ANYTRACK,
  10084. mtTime, NULL, (void*) &CurrentChord)))
  10085. {
  10086. if (CurrentChord.bSubChordCount > 0)
  10087. {
  10088. BYTE bRoot = CurrentChord.SubChordList[0].bChordRoot;
  10089. DWORD dwScale = CurrentChord.SubChordList[0].dwScalePattern;
  10090. if (pNote->bFlags & DMUS_NOTEF_NOINVALIDATE_INCHORD)
  10091. {
  10092. dwScale = CurrentChord.SubChordList[0].dwChordPattern;
  10093. }
  10094. else
  10095. {
  10096. dwScale = FixScale(SubtractRootFromScale(bRoot, dwScale));
  10097. }
  10098. if (!InScale(pNote->bMidiValue, bRoot, dwScale))
  10099. {
  10100. hr = S_OK;
  10101. }
  10102. }
  10103. }
  10104. }
  10105. else if (pNote->bFlags & DMUS_NOTEF_REGENERATE)
  10106. {
  10107. // this always causes an invalidation, and in addition generates a new note event,
  10108. // based on the Music Value of the current one, that starts at rtTime
  10109. // and continues until pNote->mtTime + pNote->Duration
  10110. // EXCEPTION: the newly generated note is the same as the currently playing one
  10111. if (SUCCEEDED(GetParam(GUID_ChordParam, dwTrackGroup, DMUS_SEG_ANYTRACK,
  10112. mtTime, NULL, (void*) &CurrentChord)))
  10113. {
  10114. BYTE bNewMidiValue = 0;
  10115. if (SUCCEEDED(MusicToMIDI(pNote->wMusicValue, &CurrentChord, pNote->bPlayModeFlags,
  10116. pNote->bSubChordLevel, &bNewMidiValue)) &&
  10117. bNewMidiValue != pNote->bMidiValue)
  10118. {
  10119. MUSIC_TIME mtDuration = (pNote->bFlags & DMUS_NOTEF_NOTEON) ? pNote->mtDuration - (mtTime - pNote->mtTime) : pNote->mtTime - mtTime;
  10120. // Make any duration < 1 be 0; this will cause the note not to
  10121. // sound. Can happen if the note's logical time is well before
  10122. // its physical time.
  10123. if( mtDuration < 1 ) mtDuration = 0;
  10124. DMUS_PMSG* pNewPMsg = NULL;
  10125. if( SUCCEEDED( AllocPMsg( sizeof(DMUS_NOTE_PMSG), &pNewPMsg )))
  10126. {
  10127. DMUS_NOTE_PMSG* pNewNote = (DMUS_NOTE_PMSG*)pNewPMsg;
  10128. // start by copying the current note into the new one
  10129. pNewNote->dwFlags = pNote->dwFlags;
  10130. pNewNote->dwPChannel = pNote->dwPChannel;
  10131. pNewNote->dwVirtualTrackID = pNote->dwVirtualTrackID;
  10132. pNewNote->pTool = pNote->pTool;
  10133. if (pNewNote->pTool) pNewNote->pTool->AddRef();
  10134. pNewNote->pGraph = pNote->pGraph;
  10135. if (pNewNote->pGraph) pNewNote->pGraph->AddRef();
  10136. pNewNote->dwType = pNote->dwType;
  10137. pNewNote->dwVoiceID = pNote->dwVoiceID;
  10138. pNewNote->dwGroupID = pNote->dwGroupID;
  10139. pNewNote->punkUser = pNote->punkUser;
  10140. if (pNewNote->punkUser) pNewNote->punkUser->AddRef();
  10141. pNewNote->wMusicValue = pNote->wMusicValue;
  10142. pNewNote->wMeasure = pNote->wMeasure;
  10143. pNewNote->nOffset = pNote->nOffset;
  10144. pNewNote->bBeat = pNote->bBeat;
  10145. pNewNote->bGrid = pNote->bGrid;
  10146. pNewNote->bVelocity = pNote->bVelocity;
  10147. pNewNote->bTimeRange = pNote->bTimeRange;
  10148. pNewNote->bDurRange = pNote->bDurRange;
  10149. pNewNote->bVelRange = pNote->bVelRange;
  10150. pNewNote->bPlayModeFlags = pNote->bPlayModeFlags;
  10151. pNewNote->bSubChordLevel = pNote->bSubChordLevel;
  10152. pNewNote->cTranspose = pNote->cTranspose;
  10153. // only things that need to change are flags, MIDI value, start time, and duration
  10154. pNewNote->mtTime = mtTime;
  10155. MusicToReferenceTime(pNewNote->mtTime, &pNewNote->rtTime);
  10156. pNewNote->mtDuration = mtDuration;
  10157. pNewNote->bMidiValue = bNewMidiValue;
  10158. pNewNote->bFlags = DMUS_NOTEF_NOTEON | DMUS_NOTEF_REGENERATE;
  10159. PackNote(pNewPMsg, rtTime + 1); // play the note on
  10160. *ppNew = pNewPMsg; // PackNote modifies event to be note-off; queue this
  10161. // invalidate the current note
  10162. hr = S_OK;
  10163. }
  10164. else hr = E_OUTOFMEMORY;
  10165. }
  10166. }
  10167. }
  10168. return hr;
  10169. }
  10170. HRESULT STDMETHODCALLTYPE CPerformance::TimeToRhythm(
  10171. MUSIC_TIME mtTime,
  10172. DMUS_TIMESIGNATURE *pTimeSig,
  10173. WORD *pwMeasure,
  10174. BYTE *pbBeat,
  10175. BYTE *pbGrid,
  10176. short *pnOffset
  10177. )
  10178. {
  10179. V_INAME(IDirectMusicPerformance::TimeToRhythm);
  10180. V_BUFPTR_READ( pTimeSig, sizeof(DMUS_TIMESIGNATURE) );
  10181. V_PTR_WRITE(pwMeasure,WORD);
  10182. V_PTR_WRITE(pbBeat,BYTE);
  10183. V_PTR_WRITE(pbGrid,BYTE);
  10184. V_PTR_WRITE(pnOffset,short);
  10185. long lMeasureLength;
  10186. long lBeatLength = DMUS_PPQ;
  10187. long lGridLength;
  10188. if( pTimeSig->bBeat )
  10189. {
  10190. lBeatLength = DMUS_PPQ * 4 / pTimeSig->bBeat;
  10191. }
  10192. lMeasureLength = lBeatLength * pTimeSig->bBeatsPerMeasure;
  10193. if( pTimeSig->wGridsPerBeat )
  10194. {
  10195. lGridLength = lBeatLength / pTimeSig->wGridsPerBeat;
  10196. }
  10197. else
  10198. {
  10199. lGridLength = lBeatLength / 256;
  10200. }
  10201. long lTemp = mtTime - pTimeSig->mtTime;
  10202. *pwMeasure = (WORD)((lTemp / lMeasureLength));
  10203. lTemp = lTemp % lMeasureLength;
  10204. *pbBeat = (BYTE)(lTemp / lBeatLength);
  10205. lTemp = lTemp % lBeatLength;
  10206. *pbGrid = (BYTE)(lTemp / lGridLength);
  10207. *pnOffset = (short)(lTemp % lGridLength);
  10208. if (*pnOffset > (lGridLength >> 1))
  10209. {
  10210. *pnOffset -= (short) lGridLength;
  10211. (*pbGrid)++;
  10212. if (*pbGrid == pTimeSig->wGridsPerBeat)
  10213. {
  10214. *pbGrid = 0;
  10215. (*pbBeat)++;
  10216. if (*pbBeat == pTimeSig->bBeatsPerMeasure)
  10217. {
  10218. *pbBeat = 0;
  10219. (*pwMeasure)++;
  10220. }
  10221. }
  10222. }
  10223. return S_OK;
  10224. }
  10225. HRESULT STDMETHODCALLTYPE CPerformance::RhythmToTime(
  10226. WORD wMeasure,
  10227. BYTE bBeat,
  10228. BYTE bGrid,
  10229. short nOffset,
  10230. DMUS_TIMESIGNATURE *pTimeSig,
  10231. MUSIC_TIME *pmtTime
  10232. )
  10233. {
  10234. V_INAME(IDirectMusicPerformance::RhythmToTime);
  10235. V_BUFPTR_READ( pTimeSig, sizeof(DMUS_TIMESIGNATURE) );
  10236. V_PTR_WRITE(pmtTime,MUSIC_TIME);
  10237. long lMeasureLength;
  10238. long lBeatLength = DMUS_PPQ;
  10239. long lGridLength;
  10240. if( pTimeSig->bBeat )
  10241. {
  10242. lBeatLength = DMUS_PPQ * 4 / pTimeSig->bBeat;
  10243. }
  10244. lMeasureLength = lBeatLength * pTimeSig->bBeatsPerMeasure;
  10245. if( pTimeSig->wGridsPerBeat )
  10246. {
  10247. lGridLength = lBeatLength / pTimeSig->wGridsPerBeat;
  10248. }
  10249. else
  10250. {
  10251. lGridLength = lBeatLength / 256;
  10252. }
  10253. long lTemp = nOffset + pTimeSig->mtTime;
  10254. lTemp += wMeasure * lMeasureLength;
  10255. lTemp += bBeat * lBeatLength;
  10256. lTemp += bGrid * lGridLength;
  10257. *pmtTime = lTemp;
  10258. return S_OK;
  10259. }