Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

964 lines
26 KiB

  1. //
  2. // Copyright (c) 1996-2000 Microsoft Corporation. All rights reserved.
  3. // CSynth.cpp
  4. //
  5. #include "common.h"
  6. #include "fltsafe.h"
  7. #define STR_MODULENAME "DDKSynth.sys:CSynth: "
  8. #pragma code_seg()
  9. /*****************************************************************************
  10. * CSynth::CSynth()
  11. *****************************************************************************
  12. * Contructor for CSynth object. Initialize the voice list, the stereo mode,
  13. * sample rate, performance statistics, etc.
  14. */
  15. CSynth::CSynth()
  16. {
  17. FLOATSAFE fs;
  18. DWORD nIndex;
  19. CVoice *pVoice;
  20. m_fCSInitialized = FALSE;
  21. ::InitializeCriticalSection(&m_CriticalSection);
  22. m_fCSInitialized = TRUE;
  23. for (nIndex = 0;nIndex < MAX_NUM_VOICES;nIndex++)
  24. {
  25. pVoice = new CVoice;
  26. if (pVoice != NULL)
  27. {
  28. m_VoicesFree.AddHead(pVoice);
  29. }
  30. }
  31. for (nIndex = 0;nIndex < NUM_EXTRA_VOICES;nIndex++)
  32. {
  33. pVoice = new CVoice;
  34. if (pVoice != NULL)
  35. {
  36. m_VoicesExtra.AddHead(pVoice);
  37. }
  38. }
  39. m_ppControl = NULL;
  40. m_dwControlCount = 0;
  41. m_nMaxVoices = MAX_NUM_VOICES;
  42. m_nExtraVoices = NUM_EXTRA_VOICES;
  43. m_stLastStats = 0;
  44. m_fAllowPanWhilePlayingNote = TRUE;
  45. m_fAllowVolumeChangeWhilePlayingNote = TRUE;
  46. ResetPerformanceStats();
  47. m_dwSampleRate = 22050;
  48. m_dwStereo = 1;
  49. m_stLastTime = 0;
  50. SetSampleRate(SAMPLE_RATE_22);
  51. SetStereoMode(2);
  52. SetGainAdjust(600);
  53. }
  54. /*****************************************************************************
  55. * CSynth::~CSynth()
  56. *****************************************************************************
  57. * Destructor for CSynth object. Delete the voices in the lists.
  58. */
  59. CSynth::~CSynth()
  60. {
  61. CVoice *pVoice;
  62. if (m_fCSInitialized)
  63. {
  64. // If CS never initialized, nothing else will have been set up
  65. //
  66. Close();
  67. while (pVoice = m_VoicesInUse.RemoveHead())
  68. {
  69. delete pVoice;
  70. }
  71. while (pVoice = m_VoicesFree.RemoveHead())
  72. {
  73. delete pVoice;
  74. }
  75. while (pVoice = m_VoicesExtra.RemoveHead())
  76. {
  77. delete pVoice;
  78. }
  79. DeleteCriticalSection(&m_CriticalSection);
  80. }
  81. }
  82. /*****************************************************************************
  83. * ChangeVoiceCount()
  84. *****************************************************************************
  85. * Change the number of voices in a given voice list.
  86. */
  87. static short ChangeVoiceCount(CVoiceList *pList,short nOld,short nCount)
  88. {
  89. if (nCount > nOld)
  90. {
  91. short nNew = nCount - nOld;
  92. for (;nNew != 0; nNew--)
  93. {
  94. CVoice *pVoice = new CVoice;
  95. if (pVoice != NULL)
  96. {
  97. pList->AddHead(pVoice);
  98. }
  99. }
  100. }
  101. else
  102. {
  103. short nNew = nOld - nCount;
  104. for (;nNew > 0; nNew--)
  105. {
  106. CVoice *pVoice = pList->RemoveHead();
  107. if (pVoice != NULL)
  108. {
  109. delete pVoice;
  110. }
  111. else
  112. {
  113. nCount += nNew;
  114. break;
  115. }
  116. }
  117. }
  118. return nCount;
  119. }
  120. /*****************************************************************************
  121. * CSynth::SetMaxVoices()
  122. *****************************************************************************
  123. * Set the maximum number of voices available.
  124. */
  125. HRESULT CSynth::SetMaxVoices(short nVoices,short nTempVoices)
  126. {
  127. if (nVoices < 1)
  128. {
  129. nVoices = 1;
  130. }
  131. if (nTempVoices < 1)
  132. {
  133. nTempVoices = 1;
  134. }
  135. ::EnterCriticalSection(&m_CriticalSection);
  136. m_nMaxVoices = ChangeVoiceCount(&m_VoicesFree,m_nMaxVoices,nVoices);
  137. m_nExtraVoices = ChangeVoiceCount(&m_VoicesExtra,m_nExtraVoices,nTempVoices);
  138. ::LeaveCriticalSection(&m_CriticalSection);
  139. return S_OK;
  140. }
  141. /*****************************************************************************
  142. * CSynth::SetNumChannelGroups()
  143. *****************************************************************************
  144. * Set the number of channel groups (virtual MIDI cables). For each channel
  145. * group, there is a separate CControlLogic object.
  146. */
  147. HRESULT CSynth::SetNumChannelGroups(DWORD dwCableCount)
  148. {
  149. HRESULT hr = S_OK;
  150. CControlLogic **ppControl;
  151. if ((dwCableCount < 1) || (dwCableCount > MAX_CHANNEL_GROUPS))
  152. {
  153. return E_INVALIDARG;
  154. }
  155. ::EnterCriticalSection(&m_CriticalSection);
  156. if (m_dwControlCount != dwCableCount)
  157. {
  158. ppControl = new(NonPagedPool,'PSmD') CControlLogic *[dwCableCount]; // DmSP
  159. if (ppControl)
  160. {
  161. DWORD dwX;
  162. for (dwX = 0; dwX < dwCableCount; dwX++)
  163. {
  164. ppControl[dwX] = NULL;
  165. }
  166. if (m_dwControlCount < dwCableCount)
  167. {
  168. for (dwX = 0; dwX < m_dwControlCount; dwX++)
  169. {
  170. ppControl[dwX] = m_ppControl[dwX];
  171. }
  172. for (;dwX < dwCableCount; dwX++)
  173. {
  174. ppControl[dwX] = new(NonPagedPool,'CSmD') CControlLogic; // DmSC
  175. if (ppControl[dwX])
  176. {
  177. hr = ppControl[dwX]->Init(&m_Instruments, this);
  178. if (FAILED(hr))
  179. {
  180. delete ppControl[dwX];
  181. ppControl[dwX] = NULL;
  182. dwCableCount = dwX;
  183. break;
  184. }
  185. ppControl[dwX]->SetGainAdjust(m_vrGainAdjust);
  186. }
  187. else
  188. {
  189. dwCableCount = dwX;
  190. break;
  191. }
  192. }
  193. }
  194. else
  195. {
  196. AllNotesOff();
  197. for (dwX = 0; dwX < dwCableCount; dwX++)
  198. {
  199. ppControl[dwX] = m_ppControl[dwX];
  200. }
  201. for (; dwX < m_dwControlCount; dwX++)
  202. {
  203. if (m_ppControl[dwX])
  204. {
  205. delete m_ppControl[dwX];
  206. }
  207. }
  208. }
  209. if (m_ppControl)
  210. {
  211. delete m_ppControl;
  212. }
  213. m_ppControl = ppControl;
  214. m_dwControlCount = dwCableCount;
  215. }
  216. else
  217. {
  218. hr = E_OUTOFMEMORY;
  219. }
  220. }
  221. ::LeaveCriticalSection(&m_CriticalSection);
  222. return hr;
  223. }
  224. /*****************************************************************************
  225. * CSynth::SetGainAdjust()
  226. *****************************************************************************
  227. * Set the gain for the overall synth. Set gain on each CControlLogic object.
  228. */
  229. void CSynth::SetGainAdjust(VREL vrGainAdjust)
  230. {
  231. DWORD idx;
  232. m_vrGainAdjust = vrGainAdjust;
  233. ::EnterCriticalSection(&m_CriticalSection);
  234. for (idx = 0; idx < m_dwControlCount; idx++)
  235. {
  236. m_ppControl[idx]->SetGainAdjust(m_vrGainAdjust);
  237. }
  238. ::LeaveCriticalSection(&m_CriticalSection);
  239. }
  240. /*****************************************************************************
  241. * CSynth::Open()
  242. *****************************************************************************
  243. * Open the synth with the given number of channel groups.
  244. */
  245. HRESULT CSynth::Open(DWORD dwCableCount, DWORD dwVoices)
  246. {
  247. HRESULT hr = S_OK;
  248. if ((dwCableCount < 1) || (dwCableCount > MAX_CHANNEL_GROUPS))
  249. {
  250. return E_INVALIDARG;
  251. }
  252. ::EnterCriticalSection(&m_CriticalSection);
  253. hr = SetNumChannelGroups(dwCableCount);
  254. if (SUCCEEDED(hr))
  255. {
  256. short nTemp = (short) dwVoices / 4;
  257. if (nTemp < 4) nTemp = 4;
  258. SetMaxVoices((short) dwVoices, nTemp);
  259. }
  260. m_vrGainAdjust = 0;
  261. ::LeaveCriticalSection(&m_CriticalSection);
  262. return hr;
  263. }
  264. /*****************************************************************************
  265. * CSynth::Close()
  266. *****************************************************************************
  267. * Close down the synth:, silence it, delete the list of CControlLogic objects.
  268. */
  269. HRESULT CSynth::Close()
  270. {
  271. ::EnterCriticalSection(&m_CriticalSection);
  272. AllNotesOff();
  273. DWORD dwX;
  274. for (dwX = 0; dwX < m_dwControlCount; dwX++)
  275. {
  276. if (m_ppControl[dwX])
  277. {
  278. delete m_ppControl[dwX];
  279. }
  280. }
  281. m_dwControlCount = 0;
  282. if (m_ppControl)
  283. {
  284. delete [] m_ppControl;
  285. m_ppControl = NULL;
  286. }
  287. m_stLastStats = 0;
  288. m_stLastTime = 0;
  289. ::LeaveCriticalSection(&m_CriticalSection);
  290. return S_OK;
  291. }
  292. /*****************************************************************************
  293. * CSynth::GetMaxVoices()
  294. *****************************************************************************
  295. * Returns the maximum number of voices available.
  296. */
  297. HRESULT CSynth::GetMaxVoices(
  298. short * pnMaxVoices, // Returns maximum number of allowed voices for continuous play.
  299. short * pnTempVoices ) // Returns number of extra voices for voice overflow.
  300. {
  301. if (pnMaxVoices != NULL)
  302. {
  303. *pnMaxVoices = m_nMaxVoices;
  304. }
  305. if (pnTempVoices != NULL)
  306. {
  307. *pnTempVoices = m_nExtraVoices;
  308. }
  309. return S_OK;
  310. }
  311. /*****************************************************************************
  312. * CSynth::SetSampleRate()
  313. *****************************************************************************
  314. * Set the sample rate of the synth. This silences the synth. The SR is
  315. * forwarded to the instrument manager.
  316. */
  317. HRESULT CSynth::SetSampleRate(DWORD dwSampleRate)
  318. {
  319. HRESULT hr = S_OK;
  320. ::EnterCriticalSection(&m_CriticalSection);
  321. AllNotesOff();
  322. m_stLastTime *= dwSampleRate;
  323. m_stLastTime /= m_dwSampleRate;
  324. // m_stLastTime = MulDiv(m_stLastTime,dwSampleRate,m_dwSampleRate);
  325. m_stLastStats = 0;
  326. m_dwSampleRate = dwSampleRate;
  327. m_stMinSpan = dwSampleRate / 100; // 10 ms.
  328. m_stMaxSpan = (dwSampleRate + 19) / 20; // 50 ms.
  329. ::LeaveCriticalSection(&m_CriticalSection);
  330. m_Instruments.SetSampleRate(dwSampleRate);
  331. return hr;
  332. }
  333. /*****************************************************************************
  334. * CSynth::Activate()
  335. *****************************************************************************
  336. * Make the synth active.
  337. */
  338. HRESULT CSynth::Activate(DWORD dwSampleRate, DWORD dwChannels )
  339. {
  340. m_stLastTime = 0;
  341. SetSampleRate(dwSampleRate);
  342. SetStereoMode(dwChannels);
  343. ResetPerformanceStats();
  344. return S_OK;
  345. }
  346. /*****************************************************************************
  347. * CSynth::Deactivate()
  348. *****************************************************************************
  349. * Gag the synth.
  350. */
  351. HRESULT CSynth::Deactivate()
  352. {
  353. AllNotesOff();
  354. return S_OK;
  355. }
  356. /*****************************************************************************
  357. * CSynth::GetPerformanceStats()
  358. *****************************************************************************
  359. * Get the latest perf statistics.
  360. */
  361. HRESULT CSynth::GetPerformanceStats(PerfStats *pStats)
  362. {
  363. if (pStats == NULL)
  364. {
  365. return E_POINTER;
  366. }
  367. *pStats = m_CopyStats;
  368. return (S_OK);
  369. }
  370. /*****************************************************************************
  371. * CSynth::Mix()
  372. *****************************************************************************
  373. * Mix into the given buffer. This is called by Render in the software
  374. * synth case, or this could be called by a request from hardware.
  375. */
  376. void CSynth::Mix(short *pBuffer,DWORD dwLength,LONGLONG llPosition)
  377. {
  378. PAGED_CODE();
  379. FLOATSAFE fs;
  380. STIME stEndTime;
  381. CVoice *pVoice;
  382. CVoice *pNextVoice;
  383. long lNumVoices = 0;
  384. ::EnterCriticalSection(&m_CriticalSection);
  385. LONG lTime = - (LONG)::GetTheCurrentTime();
  386. stEndTime = llPosition + dwLength;
  387. StealNotes(stEndTime);
  388. DWORD dwX;
  389. for (dwX = 0; dwX < m_dwControlCount; dwX++)
  390. {
  391. m_ppControl[dwX]->QueueNotes(stEndTime);
  392. }
  393. pVoice = m_VoicesInUse.GetHead();
  394. for (;pVoice != NULL;pVoice = pNextVoice)
  395. {
  396. pNextVoice = pVoice->GetNext();
  397. pVoice->Mix(pBuffer,dwLength,llPosition,stEndTime);
  398. lNumVoices++;
  399. if (pVoice->m_fInUse == FALSE)
  400. {
  401. m_VoicesInUse.Remove(pVoice);
  402. m_VoicesFree.AddHead(pVoice);
  403. // m_BuildStats.dwTotalSamples += (pVoice->m_stStopTime - pVoice->m_stStartTime);
  404. if (pVoice->m_stStartTime < m_stLastStats)
  405. {
  406. m_BuildStats.dwTotalSamples += (long) (pVoice->m_stStopTime - m_stLastStats);
  407. }
  408. else
  409. {
  410. m_BuildStats.dwTotalSamples += (long) (pVoice->m_stStopTime - pVoice->m_stStartTime);
  411. }
  412. }
  413. }
  414. for (dwX = 0; dwX < m_dwControlCount; dwX++)
  415. {
  416. m_ppControl[dwX]->ClearMIDI(stEndTime);
  417. }
  418. FinishMix(pBuffer,dwLength);
  419. if (stEndTime > m_stLastTime)
  420. {
  421. m_stLastTime = stEndTime;
  422. }
  423. lTime += ::GetTheCurrentTime();
  424. m_BuildStats.dwTotalTime += lTime;
  425. if ((m_stLastStats + m_dwSampleRate) <= m_stLastTime)
  426. {
  427. DWORD dwElapsed = (DWORD) (m_stLastTime - m_stLastStats);
  428. pVoice = m_VoicesInUse.GetHead();
  429. for (;pVoice != NULL;pVoice = pVoice->GetNext())
  430. {
  431. if (pVoice->m_stStartTime < m_stLastStats)
  432. {
  433. m_BuildStats.dwTotalSamples += dwElapsed;
  434. }
  435. else
  436. {
  437. m_BuildStats.dwTotalSamples += (long) (m_stLastTime - pVoice->m_stStartTime);
  438. }
  439. }
  440. if (dwElapsed == 0) dwElapsed = 1;
  441. if (m_BuildStats.dwTotalSamples == 0) m_BuildStats.dwTotalSamples = 1;
  442. m_BuildStats.dwVoices =
  443. (m_BuildStats.dwTotalSamples + (dwElapsed >> 1)) / dwElapsed;
  444. {
  445. m_BuildStats.dwCPU = MulDiv(m_BuildStats.dwTotalTime,
  446. m_dwSampleRate, dwElapsed);
  447. }
  448. m_CopyStats = m_BuildStats;
  449. RtlZeroMemory(&m_BuildStats, sizeof(m_BuildStats));
  450. m_stLastStats = m_stLastTime;
  451. }
  452. ::LeaveCriticalSection(&m_CriticalSection);
  453. }
  454. /*****************************************************************************
  455. * CSynth::OldestVoice()
  456. *****************************************************************************
  457. * Get the most likely candidate to be shut down, to support voice stealing.
  458. * Priority is looked at first, then age.
  459. */
  460. CVoice *CSynth::OldestVoice()
  461. {
  462. CVoice *pVoice;
  463. CVoice *pBest = NULL;
  464. pVoice = m_VoicesInUse.GetHead();
  465. pBest = pVoice;
  466. if (pBest)
  467. {
  468. pVoice = pVoice->GetNext();
  469. for (;pVoice;pVoice = pVoice->GetNext())
  470. {
  471. if (!pVoice->m_fTag)
  472. {
  473. if (pBest->m_fTag)
  474. {
  475. pBest = pVoice;
  476. }
  477. else
  478. {
  479. if (pVoice->m_dwPriority <= pBest->m_dwPriority)
  480. {
  481. if (pVoice->m_fNoteOn)
  482. {
  483. if (pBest->m_fNoteOn)
  484. {
  485. if (pBest->m_stStartTime > pVoice->m_stStartTime)
  486. {
  487. pBest = pVoice;
  488. }
  489. }
  490. }
  491. else
  492. {
  493. if (pBest->m_fNoteOn ||
  494. (pBest->m_vrVolume > pVoice->m_vrVolume))
  495. {
  496. pBest = pVoice;
  497. }
  498. }
  499. }
  500. }
  501. }
  502. }
  503. if (pBest->m_fTag)
  504. {
  505. pBest = NULL;
  506. }
  507. }
  508. return pBest;
  509. }
  510. /*****************************************************************************
  511. * CSynth::StealVoice()
  512. *****************************************************************************
  513. * Steal a voice, if possible. If none are at or below this priority, then
  514. * return NULL, and this voice will go unheard. If there IS a voice to be
  515. * stolen, silence it first.
  516. */
  517. CVoice *CSynth::StealVoice(DWORD dwPriority)
  518. {
  519. CVoice *pVoice;
  520. CVoice *pBest = NULL;
  521. pVoice = m_VoicesInUse.GetHead();
  522. for (;pVoice != NULL;pVoice = pVoice->GetNext())
  523. {
  524. if (pVoice->m_dwPriority <= dwPriority)
  525. {
  526. if (!pBest)
  527. {
  528. pBest = pVoice;
  529. }
  530. else
  531. {
  532. if (pVoice->m_fNoteOn == FALSE)
  533. {
  534. if ((pBest->m_fNoteOn == TRUE) ||
  535. (pBest->m_vrVolume > pVoice->m_vrVolume))
  536. {
  537. pBest = pVoice;
  538. }
  539. }
  540. else
  541. {
  542. if (pBest->m_stStartTime > pVoice->m_stStartTime)
  543. {
  544. pBest = pVoice;
  545. }
  546. }
  547. }
  548. }
  549. }
  550. if (pBest != NULL)
  551. {
  552. pBest->ClearVoice();
  553. pBest->m_fInUse = FALSE;
  554. m_VoicesInUse.Remove(pBest);
  555. pBest->SetNext(NULL);
  556. }
  557. return pBest;
  558. }
  559. /*****************************************************************************
  560. * CSynth::QueueVoice()
  561. *****************************************************************************
  562. * This method queues a voice in the list of currently
  563. * synthesizing voices. It places them in the queue so that
  564. * the higher priority voices are later in the queue. This
  565. * allows the note stealing algorithm to take off the top of
  566. * the queue.
  567. * And, we want older playing notes to be later in the queue
  568. * so the note ons and offs overlap properly. So, the queue is
  569. * sorted in priority order with older notes later within one
  570. * priority level.
  571. */
  572. void CSynth::QueueVoice(CVoice *pVoice)
  573. {
  574. CVoice *pScan = m_VoicesInUse.GetHead();
  575. CVoice *pNext = NULL;
  576. if (!pScan) // Empty list?
  577. {
  578. m_VoicesInUse.AddHead(pVoice);
  579. return;
  580. }
  581. if (pScan->m_dwPriority > pVoice->m_dwPriority)
  582. { // Are we lower priority than the head of the list?
  583. m_VoicesInUse.AddHead(pVoice);
  584. return;
  585. }
  586. pNext = pScan->GetNext();
  587. for (;pNext;)
  588. {
  589. if (pNext->m_dwPriority > pVoice->m_dwPriority)
  590. {
  591. // Lower priority than next in the list.
  592. pScan->SetNext(pVoice);
  593. pVoice->SetNext(pNext);
  594. return;
  595. }
  596. pScan = pNext;
  597. pNext = pNext->GetNext();
  598. }
  599. // Reached the end of the list.
  600. pScan->SetNext(pVoice);
  601. pVoice->SetNext(NULL);
  602. }
  603. /*****************************************************************************
  604. * CSynth::StealNotes()
  605. *****************************************************************************
  606. * Clear out notes at a given time.
  607. */
  608. void CSynth::StealNotes(STIME stTime)
  609. {
  610. CVoice *pVoice;
  611. long lToMove = m_nExtraVoices - m_VoicesExtra.GetCount();
  612. if (lToMove > 0)
  613. {
  614. for (;lToMove > 0;)
  615. {
  616. pVoice = m_VoicesFree.RemoveHead();
  617. if (pVoice != NULL)
  618. {
  619. m_VoicesExtra.AddHead(pVoice);
  620. lToMove--;
  621. }
  622. else break;
  623. }
  624. if (lToMove > 0)
  625. {
  626. pVoice = m_VoicesInUse.GetHead();
  627. for (;pVoice;pVoice = pVoice->GetNext())
  628. {
  629. if (pVoice->m_fTag) // Voice is already slated to be returned.
  630. {
  631. lToMove--;
  632. }
  633. }
  634. for (;lToMove > 0;lToMove--)
  635. {
  636. pVoice = OldestVoice();
  637. if (pVoice != NULL)
  638. {
  639. pVoice->QuickStopVoice(stTime);
  640. m_BuildStats.dwNotesLost++;
  641. }
  642. else break;
  643. }
  644. }
  645. }
  646. }
  647. /*****************************************************************************
  648. * CSynth::FinishMix()
  649. *****************************************************************************
  650. * Cleanup after the mix.
  651. */
  652. void CSynth::FinishMix(short *pBuffer,DWORD dwLength)
  653. {
  654. DWORD dwIndex;
  655. long lMax = (long) m_BuildStats.dwMaxAmplitude;
  656. long lTemp;
  657. for (dwIndex = 0; dwIndex < (dwLength << m_dwStereo); dwIndex++)
  658. {
  659. lTemp = pBuffer[dwIndex];
  660. lTemp <<= 1;
  661. if (lTemp < -32767) lTemp = -32767;
  662. if (lTemp > 32767) lTemp = 32767;
  663. pBuffer[dwIndex] = (short) lTemp;
  664. if (lTemp > lMax)
  665. {
  666. lMax = lTemp;
  667. }
  668. }
  669. m_BuildStats.dwMaxAmplitude = lMax;
  670. }
  671. /*****************************************************************************
  672. * CSynth::Unload()
  673. *****************************************************************************
  674. * Unload a previous download. Forward the request to the instrument manager.
  675. */
  676. HRESULT CSynth::Unload(HANDLE hDownload,
  677. HRESULT ( CALLBACK *lpFreeMemory)(HANDLE,HANDLE),
  678. HANDLE hUserData)
  679. {
  680. return m_Instruments.Unload( hDownload, lpFreeMemory, hUserData);
  681. }
  682. /*****************************************************************************
  683. * CSynth::Download()
  684. *****************************************************************************
  685. * Handle a download. Forward the request to the instrument manager.
  686. */
  687. HRESULT CSynth::Download(LPHANDLE phDownload, void * pdwData, LPBOOL bpFree)
  688. {
  689. FLOATSAFE fs;
  690. return m_Instruments.Download( phDownload, (DWORD *) pdwData, bpFree);
  691. }
  692. /*****************************************************************************
  693. * CSynth::PlayBuffer()
  694. *****************************************************************************
  695. * This receives one MIDI message in the form of a buffer of data and
  696. * ulCable, which indicates which Channel Group the message is addressed
  697. * to. Each channel group is implemented with an instance of a CControlLogic
  698. * object, so this chooses which CControlLogic object to send the message
  699. * to. If ulCable is 0, this is a broadcast message and should be sent to all
  700. * CControlLogics.
  701. *
  702. * PlayBuffer() analyzes the message and, depending on the size, either
  703. * sends to CControlLogic::RecordMIDI() or CControlLogic::RecordSysEx().
  704. *
  705. * In order to properly associate the time stamp of the MIDI
  706. * message in the buffer, the synth needs to convert from the
  707. * REFERENCE_TIME format to its internal sample based time. Since
  708. * the wave out stream is actually managed by IDirectMusicSynthSink,
  709. * the synth calls IDirectMusicSynthSink::RefTimeToSample
  710. * for each MIDI message to convert its time stamp into sample time.
  711. *
  712. * So, typically, the synthesizer pulls each MIDI message from the
  713. * buffer, stamps it in sample time, then places it in its own
  714. * internal queue. The queue is emptied later by the rendering
  715. * process, which is managed by CDmSynthStream::Render and
  716. * called by IDirectMusicSynthSink.
  717. */
  718. HRESULT CSynth::PlayBuffer(IDirectMusicSynthSink *pSynthSink,REFERENCE_TIME rt,
  719. LPBYTE lpBuffer, DWORD cbBuffer, ULONG ulCable)
  720. {
  721. STIME stTime;
  722. ::EnterCriticalSection(&m_CriticalSection);
  723. if ( rt == 0 ) // Special case of time == 0.
  724. {
  725. stTime = m_stLastTime;
  726. }
  727. else
  728. {
  729. pSynthSink->RefTimeToSample(rt, &stTime);
  730. }
  731. if (cbBuffer <= sizeof(DWORD))
  732. {
  733. if (ulCable <= m_dwControlCount)
  734. {
  735. if (ulCable == 0) // Play all groups if 0.
  736. {
  737. for (; ulCable < m_dwControlCount; ulCable++)
  738. {
  739. m_ppControl[ulCable]->RecordMIDI(stTime,lpBuffer[0],
  740. lpBuffer[1], lpBuffer[2]);
  741. }
  742. }
  743. else
  744. {
  745. m_ppControl[ulCable - 1]->RecordMIDI(stTime,lpBuffer[0],
  746. lpBuffer[1], lpBuffer[2]);
  747. }
  748. }
  749. else
  750. {
  751. Trace(1,"MIDI event on channel group %ld is beyond range of %ld opened channel groups\n",
  752. ulCable, m_dwControlCount);
  753. }
  754. }
  755. else
  756. {
  757. if (ulCable <= m_dwControlCount)
  758. {
  759. if (ulCable == 0)
  760. {
  761. for (; ulCable < m_dwControlCount; ulCable++)
  762. {
  763. m_ppControl[ulCable]->RecordSysEx(cbBuffer,
  764. &lpBuffer[0], stTime);
  765. }
  766. }
  767. else
  768. {
  769. m_ppControl[ulCable-1]->RecordSysEx(cbBuffer,
  770. &lpBuffer[0], stTime);
  771. }
  772. }
  773. }
  774. ::LeaveCriticalSection(&m_CriticalSection);
  775. return S_OK;
  776. }
  777. /*****************************************************************************
  778. * CSynth::SetStereoMode()
  779. *****************************************************************************
  780. * Set the stereo/mono mode for this synth.
  781. */
  782. HRESULT CSynth::SetStereoMode(DWORD dwChannels) // 1 for Mono, 2 for Stereo.
  783. {
  784. HRESULT hr = S_OK;
  785. if ((m_dwStereo + 1) != dwChannels)
  786. {
  787. DWORD dwStereo;
  788. if (dwChannels > 1) dwStereo = 1;
  789. else dwStereo = 0;
  790. if (dwStereo != m_dwStereo)
  791. {
  792. m_dwStereo = dwStereo;
  793. }
  794. }
  795. return hr;
  796. }
  797. /*****************************************************************************
  798. * CSynth::ResetPerformanceStats()
  799. *****************************************************************************
  800. * Reset the running performance statistics.
  801. */
  802. void CSynth::ResetPerformanceStats()
  803. {
  804. m_BuildStats.dwNotesLost = 0;
  805. m_BuildStats.dwTotalTime = 0;
  806. m_BuildStats.dwVoices = 0;
  807. m_BuildStats.dwTotalSamples = 0;
  808. m_BuildStats.dwCPU = 0;
  809. m_BuildStats.dwMaxAmplitude = 0;
  810. m_CopyStats = m_BuildStats;
  811. }
  812. /*****************************************************************************
  813. * CSynth::AllNotesOff()
  814. *****************************************************************************
  815. * Stop all voices.
  816. */
  817. HRESULT CSynth::AllNotesOff()
  818. {
  819. CVoice *pVoice;
  820. ::EnterCriticalSection(&m_CriticalSection);
  821. while (pVoice = m_VoicesInUse.RemoveHead())
  822. {
  823. pVoice->ClearVoice();
  824. pVoice->m_fInUse = FALSE;
  825. m_VoicesFree.AddHead(pVoice);
  826. long lSamples;
  827. if (pVoice->m_stStartTime < m_stLastStats)
  828. {
  829. lSamples = (long) (pVoice->m_stStopTime - m_stLastStats);
  830. }
  831. else
  832. {
  833. lSamples = (long) (pVoice->m_stStopTime - pVoice->m_stStartTime);
  834. }
  835. if (lSamples < 0)
  836. {
  837. lSamples = 0;
  838. }
  839. m_BuildStats.dwTotalSamples += lSamples;
  840. }
  841. ::LeaveCriticalSection(&m_CriticalSection);
  842. return (S_OK);
  843. }
  844. /*****************************************************************************
  845. * CSynth::SetChannelPriority()
  846. *****************************************************************************
  847. * Set the priority for a given channel, to be used in voice stealing.
  848. */
  849. HRESULT CSynth::SetChannelPriority(DWORD dwChannelGroup,DWORD dwChannel,
  850. DWORD dwPriority)
  851. {
  852. HRESULT hr = S_OK;
  853. ::EnterCriticalSection(&m_CriticalSection);
  854. dwChannelGroup--;
  855. if ((dwChannelGroup >= m_dwControlCount) || (dwChannel > 15))
  856. {
  857. hr = E_INVALIDARG;
  858. }
  859. else
  860. {
  861. if (m_ppControl)
  862. {
  863. hr = m_ppControl[dwChannelGroup]->SetChannelPriority(dwChannel,dwPriority);
  864. }
  865. }
  866. ::LeaveCriticalSection(&m_CriticalSection);
  867. return hr;
  868. }
  869. /*****************************************************************************
  870. * CSynth::GetChannelPriority()
  871. *****************************************************************************
  872. * Retrieve the priority of a given channel/channel group, to be used to
  873. * facilitate correct voice stealing.
  874. */
  875. HRESULT CSynth::GetChannelPriority(DWORD dwChannelGroup, DWORD dwChannel,
  876. LPDWORD pdwPriority)
  877. {
  878. HRESULT hr = S_OK;
  879. ::EnterCriticalSection(&m_CriticalSection);
  880. dwChannelGroup--;
  881. if ((dwChannelGroup >= m_dwControlCount) || (dwChannel > 15))
  882. {
  883. hr = E_INVALIDARG;
  884. }
  885. else
  886. {
  887. if (m_ppControl)
  888. {
  889. hr = m_ppControl[dwChannelGroup]->GetChannelPriority(dwChannel,pdwPriority);
  890. }
  891. }
  892. ::LeaveCriticalSection(&m_CriticalSection);
  893. return hr;
  894. }