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.

1868 lines
46 KiB

  1. // Copyright (c) 1998-1999 Microsoft Corporation
  2. // dmeport.cpp
  3. //
  4. // CDirectMusicEmulatePort
  5. // Implements the MMSYSTEM API version of IDirectMusicPort.
  6. //
  7. #define INITGUID
  8. #include <objbase.h>
  9. #include <ks.h>
  10. #include <ksproxy.h>
  11. #include <assert.h>
  12. #include <mmsystem.h>
  13. #include <dsoundp.h>
  14. #include "dmusicc.h"
  15. #include "..\dmusic\dmusicp.h"
  16. #include "debug.h"
  17. #include "dmusic32.h"
  18. #include "dm32p.h"
  19. #include "dmthunk.h"
  20. #include "..\shared\validate.h"
  21. #include <ks.h> // KSDATAFORMAT_SUBTYPE_MIDI
  22. #pragma warning(disable:4530)
  23. #define CLOCK_UPDATE_INTERVAL 100 // milliseconds
  24. #define MS_TO_REFERENCE_TIME (10 * 1000)
  25. static HRESULT MMRESULTToHRESULT(
  26. MMRESULT mmr);
  27. static DWORD InputWorker(LPVOID lpv);
  28. // @func API call into DLL to get a new port
  29. //
  30. HRESULT
  31. CreateCDirectMusicEmulatePort(
  32. PORTENTRY *pPE,
  33. CDirectMusic *pDM,
  34. LPDMUS_PORTPARAMS pPortParams,
  35. CDirectMusicEmulatePort **pPort)
  36. {
  37. HRESULT hr;
  38. *pPort = new CDirectMusicEmulatePort(pPE, pDM);
  39. if (NULL == *pPort)
  40. {
  41. return E_OUTOFMEMORY;
  42. }
  43. hr = (*pPort)->Init(pPortParams);
  44. if (!SUCCEEDED(hr))
  45. {
  46. delete *pPort;
  47. *pPort = NULL;
  48. return hr;
  49. }
  50. return hr;
  51. }
  52. // @mfunc Constructor for CDirectMusicEmulatePort
  53. //
  54. CDirectMusicEmulatePort::CDirectMusicEmulatePort(
  55. PORTENTRY *pPE, // @parm The portentry of this device
  56. CDirectMusic *pDM):// @parm The CDirectMusic implementation which created this port
  57. m_cRef(1),
  58. m_id(pPE->idxDevice),
  59. m_pDM(pDM),
  60. m_hKillThreads(NULL),
  61. m_hDataReady(NULL),
  62. m_hAppEvent(NULL),
  63. m_dwWorkBufferTileInfo(0),
  64. m_pThruBuffer(NULL),
  65. m_pThruMap(NULL),
  66. m_lActivated(0),
  67. m_hCaptureThread(NULL),
  68. m_pMasterClock(NULL),
  69. m_fCSInitialized(FALSE)
  70. {
  71. m_fIsOutput = (pPE->pc.dwClass == DMUS_PC_OUTPUTCLASS) ? TRUE : FALSE;
  72. m_hDevice = NULL;
  73. m_pLatencyClock = NULL;
  74. dmpc = pPE->pc;
  75. }
  76. // @mfunc Destructor for CDirectMusicEmulatePort
  77. //
  78. CDirectMusicEmulatePort::~CDirectMusicEmulatePort()
  79. {
  80. Close();
  81. }
  82. // @mfunc Initialization of CDirectMusicEmulatePort
  83. //
  84. // @comm Call through the thunk layer to open the requested device.
  85. //
  86. // Flags we recognize
  87. //
  88. #define DMUS_ALL_FLAGS (DMUS_PORTPARAMS_VOICES | \
  89. DMUS_PORTPARAMS_CHANNELGROUPS | \
  90. DMUS_PORTPARAMS_AUDIOCHANNELS | \
  91. DMUS_PORTPARAMS_SAMPLERATE | \
  92. DMUS_PORTPARAMS_EFFECTS | \
  93. DMUS_PORTPARAMS_SHARE)
  94. // Of those, which do we actually look at?
  95. //
  96. #define DMUS_SUP_FLAGS (DMUS_PORTPARAMS_CHANNELGROUPS | \
  97. DMUS_PORTPARAMS_SHARE)
  98. HRESULT
  99. CDirectMusicEmulatePort::Init(
  100. LPDMUS_PORTPARAMS pPortParams)
  101. {
  102. MMRESULT mmr;
  103. HRESULT hr;
  104. BOOL fChangedParms;
  105. // Get, but don't hold onto, the notification interface
  106. //
  107. hr = m_pDM->QueryInterface(IID_IDirectMusicPortNotify, (void**)&m_pNotify);
  108. if (FAILED(hr))
  109. {
  110. return hr;
  111. }
  112. m_pNotify->Release();
  113. // Munge the portparams to match what we support.
  114. //
  115. fChangedParms = FALSE;
  116. if (pPortParams->dwValidParams & ~DMUS_ALL_FLAGS)
  117. {
  118. Trace(0, "Undefined flags in port parameters: %08X\n", pPortParams->dwValidParams & ~DMUS_ALL_FLAGS);
  119. // Flags set we don't recognize.
  120. //
  121. pPortParams->dwValidParams &= DMUS_ALL_FLAGS;
  122. fChangedParms = TRUE;
  123. }
  124. // We recognize these flags but don't support them.
  125. //
  126. if (pPortParams->dwValidParams & ~DMUS_SUP_FLAGS)
  127. {
  128. pPortParams->dwValidParams &= DMUS_SUP_FLAGS;
  129. fChangedParms = TRUE;
  130. }
  131. // Channel groups better be one.
  132. //
  133. if (pPortParams->dwValidParams & DMUS_PORTPARAMS_CHANNELGROUPS)
  134. {
  135. if (pPortParams->dwChannelGroups != 1)
  136. {
  137. pPortParams->dwChannelGroups = 1;
  138. fChangedParms = TRUE;
  139. }
  140. }
  141. else
  142. {
  143. pPortParams->dwValidParams |= DMUS_PORTPARAMS_CHANNELGROUPS;
  144. pPortParams->dwChannelGroups = 1;
  145. }
  146. BOOL fShare = FALSE;
  147. if (pPortParams->dwValidParams & DMUS_PORTPARAMS_SHARE)
  148. {
  149. if (m_fIsOutput)
  150. {
  151. fShare = pPortParams->fShare;
  152. }
  153. else
  154. {
  155. pPortParams->fShare = FALSE;
  156. fChangedParms = TRUE;
  157. }
  158. }
  159. else
  160. {
  161. pPortParams->dwValidParams |= DMUS_PORTPARAMS_SHARE;
  162. pPortParams->fShare = fShare;
  163. }
  164. mmr = OpenLegacyDevice(m_id, m_fIsOutput, fShare, &m_hDevice);
  165. if (mmr)
  166. {
  167. return MMRESULTToHRESULT(mmr);
  168. }
  169. // Set up the master clock and our latency clock
  170. //
  171. hr = InitializeClock();
  172. if (FAILED(hr))
  173. {
  174. return hr;
  175. }
  176. // If an input port, initialize capture specific stuff like thruing
  177. //
  178. if (!m_fIsOutput)
  179. {
  180. hr = InitializeCapture();
  181. if (FAILED(hr))
  182. {
  183. return hr;
  184. }
  185. }
  186. return fChangedParms ? S_FALSE : S_OK;
  187. }
  188. HRESULT CDirectMusicEmulatePort::InitializeClock()
  189. {
  190. HRESULT hr;
  191. GUID guidMasterClock;
  192. DWORD dwThreadID;
  193. REFERENCE_TIME rtMasterClock;
  194. REFERENCE_TIME rtSlaveClock;
  195. hr = m_pDM->GetMasterClock(&guidMasterClock, &m_pMasterClock);
  196. if (FAILED(hr))
  197. {
  198. return hr;
  199. }
  200. m_pLatencyClock = new CEmulateLatencyClock(m_pMasterClock);
  201. if (NULL == m_pLatencyClock)
  202. {
  203. return E_OUTOFMEMORY;
  204. }
  205. #if 0
  206. if (guidMasterClock == GUID_SysClock)
  207. {
  208. m_fSyncToMaster = FALSE;
  209. return S_OK;
  210. }
  211. #endif
  212. m_fSyncToMaster = TRUE;
  213. // Read both clocks
  214. //
  215. hr = m_pMasterClock->GetTime(&rtMasterClock);
  216. rtSlaveClock = MS_TO_REFERENCE_TIME * ((ULONGLONG)timeGetTime());
  217. if (FAILED(hr))
  218. {
  219. return hr;
  220. }
  221. m_lTimeOffset = rtMasterClock - rtSlaveClock;
  222. return S_OK;
  223. }
  224. HRESULT CDirectMusicEmulatePort::InitializeCapture()
  225. {
  226. HRESULT hr;
  227. MMRESULT mmr;
  228. DWORD dwThreadID;
  229. // Allocate thru map for 16 channels, since we only have one channel group
  230. // Initialize to no thruing (destination port is NULL).
  231. //
  232. m_pThruMap = new DMUS_THRU_CHANNEL[MIDI_CHANNELS];
  233. ZeroMemory(m_pThruMap, MIDI_CHANNELS * sizeof(DMUS_THRU_CHANNEL));
  234. // Create thruing buffer
  235. //
  236. // XXX Defer this until the first call to thru?
  237. //
  238. // Note: guaranteed by dmusic16 this is the biggest event ever to be returned
  239. // (thunk api asking?)
  240. //
  241. DMUS_BUFFERDESC dmbd;
  242. ZeroMemory(&dmbd, sizeof(dmbd));
  243. dmbd.dwSize = sizeof(dmbd);
  244. dmbd.cbBuffer = 4096; // XXX Where should we get this???
  245. hr = m_pDM->CreateMusicBuffer(&dmbd, &m_pThruBuffer, NULL);
  246. if (FAILED(hr))
  247. {
  248. Trace(0, "Failed to create thruing buffer\n");
  249. return hr;
  250. }
  251. // Create events
  252. //
  253. m_hDataReady = CreateEvent(NULL, // Event attributes
  254. FALSE, // Manual reset
  255. FALSE, // Not signalled
  256. NULL); // Name
  257. m_hKillThreads = CreateEvent(NULL, // Event attributes
  258. FALSE, // Manual reset
  259. FALSE, // Not signalled
  260. NULL); // Name
  261. if (m_hDataReady == (HANDLE)NULL || m_hKillThreads == (HANDLE)NULL)
  262. {
  263. return E_OUTOFMEMORY;
  264. }
  265. // Set our data ready event for dmusic16
  266. //
  267. m_hVxDEvent = OpenVxDHandle(m_hDataReady);
  268. Trace(2, "Setting event handle; hDevice %08x hEvent=%08X hVxDEvent=%08X\n",
  269. (DWORD)m_hDevice,
  270. (DWORD)m_hDataReady,
  271. (DWORD)m_hVxDEvent);
  272. mmr = MidiInSetEventHandle(m_hDevice, m_hVxDEvent);
  273. if (mmr)
  274. {
  275. Trace(0, "MidiInSetEventHandle returned [%d]\n", mmr);
  276. return MMRESULTToHRESULT(mmr);
  277. }
  278. // Create a tiling for our work buffer so we only need to do it once
  279. //
  280. m_dwWorkBufferTileInfo = dmTileBuffer((DWORD)m_WorkBuffer, sizeof(m_WorkBuffer));
  281. m_p1616WorkBuffer = TILE_P1616(m_dwWorkBufferTileInfo);
  282. if (m_p1616WorkBuffer == NULL)
  283. {
  284. Trace(0, "Could not tile work buffer\n");
  285. return E_OUTOFMEMORY;
  286. }
  287. // Initialize cs to protect event queues.
  288. //
  289. // Unfortunately this can throw an exception if out of memory.
  290. //
  291. _try
  292. {
  293. InitializeCriticalSection(&m_csEventQueues);
  294. }
  295. _except (EXCEPTION_EXECUTE_HANDLER)
  296. {
  297. return E_OUTOFMEMORY;
  298. }
  299. m_fCSInitialized = TRUE;
  300. m_hCaptureThread = CreateThread(NULL, // Thread attributes
  301. 0, // Stack size
  302. ::InputWorker,
  303. this,
  304. 0, // Flags
  305. &dwThreadID);
  306. if (m_hCaptureThread == NULL)
  307. {
  308. Trace(0, "CreateThread failed with error %d\n", GetLastError());
  309. return E_OUTOFMEMORY;
  310. }
  311. return S_OK;
  312. }
  313. static DWORD WINAPI InputWorker(LPVOID lpv)
  314. {
  315. CDirectMusicEmulatePort *pPort = (CDirectMusicEmulatePort*)lpv;
  316. return pPort->InputWorker();
  317. }
  318. // @mfunc
  319. //
  320. // @comm Standard QueryInterface
  321. //
  322. STDMETHODIMP
  323. CDirectMusicEmulatePort::QueryInterface(const IID &iid,
  324. void **ppv)
  325. {
  326. if (iid == IID_IUnknown || iid == IID_IDirectMusicPort)
  327. {
  328. *ppv = static_cast<IDirectMusicPort*>(this);
  329. }
  330. else if (iid == IID_IDirectMusicPortP)
  331. {
  332. *ppv = static_cast<IDirectMusicPortP*>(this);
  333. }
  334. else if (iid == IID_IDirectMusicPortPrivate)
  335. {
  336. *ppv = static_cast<IDirectMusicPortPrivate*>(this);
  337. }
  338. else if (iid == IID_IKsControl)
  339. {
  340. *ppv = static_cast<IKsControl*>(this);
  341. }
  342. else if (iid == IID_IDirectMusicThru)
  343. {
  344. *ppv = static_cast<IDirectMusicThru*>(this);
  345. }
  346. else
  347. {
  348. *ppv = NULL;
  349. return E_NOINTERFACE;
  350. }
  351. reinterpret_cast<IUnknown*>(*ppv)->AddRef();
  352. return S_OK;
  353. }
  354. // CDirectMusicEmulatePort::AddRef
  355. //
  356. STDMETHODIMP_(ULONG)
  357. CDirectMusicEmulatePort::AddRef()
  358. {
  359. return InterlockedIncrement(&m_cRef);
  360. }
  361. // CDirectMusicEmulatePort::Release
  362. //
  363. STDMETHODIMP_(ULONG)
  364. CDirectMusicEmulatePort::Release()
  365. {
  366. if (!InterlockedDecrement(&m_cRef)) {
  367. if (m_pNotify)
  368. {
  369. m_pNotify->NotifyFinalRelease(static_cast<IDirectMusicPort*>(this));
  370. }
  371. delete this;
  372. return 0;
  373. }
  374. return m_cRef;
  375. }
  376. //////////////////////////////////////////////////////////////////////
  377. // CDirectMusicEmulatePort::Compact
  378. STDMETHODIMP
  379. CDirectMusicEmulatePort::Compact()
  380. {
  381. return E_NOTIMPL;
  382. }
  383. //////////////////////////////////////////////////////////////////////
  384. // CDirectMusicEmulatePort::GetCaps
  385. STDMETHODIMP
  386. CDirectMusicEmulatePort::GetCaps(
  387. LPDMUS_PORTCAPS pPortCaps)
  388. {
  389. V_INAME(IDirectMusicPort::GetCaps);
  390. V_STRUCTPTR_WRITE(pPortCaps, DMUS_PORTCAPS);
  391. if (!m_pDM)
  392. {
  393. return DMUS_E_DMUSIC_RELEASED;
  394. }
  395. CopyMemory(pPortCaps, &dmpc, sizeof(DMUS_PORTCAPS));
  396. return S_OK;
  397. }
  398. //////////////////////////////////////////////////////////////////////
  399. // CDirectMusicEmulatePort::DeviceIoControl
  400. STDMETHODIMP
  401. CDirectMusicEmulatePort::DeviceIoControl(
  402. DWORD dwIoControlCode,
  403. LPVOID lpInBuffer,
  404. DWORD nInBufferSize,
  405. LPVOID lpOutBuffer,
  406. DWORD nOutBufferSize,
  407. LPDWORD lpBytesReturned,
  408. LPOVERLAPPED lpOverlapped)
  409. {
  410. return E_NOTIMPL;
  411. }
  412. STDMETHODIMP
  413. CDirectMusicEmulatePort::SetNumChannelGroups(
  414. DWORD dwNumChannelGroups)
  415. {
  416. if (!m_pDM)
  417. {
  418. return DMUS_E_DMUSIC_RELEASED;
  419. }
  420. if (dwNumChannelGroups != 1)
  421. {
  422. return E_INVALIDARG;
  423. }
  424. return S_OK;
  425. }
  426. STDMETHODIMP
  427. CDirectMusicEmulatePort::GetNumChannelGroups(
  428. LPDWORD pdwChannelGroups)
  429. {
  430. V_INAME(IDirectMusicPort::GetNumChannelGroups);
  431. V_PTR_WRITE(pdwChannelGroups, DWORD);
  432. if (!m_pDM)
  433. {
  434. return DMUS_E_DMUSIC_RELEASED;
  435. }
  436. *pdwChannelGroups = 1;
  437. return S_OK;
  438. }
  439. // @mfunc Queue a buffer for playback
  440. //
  441. #define REFTIME_TO_MS (10L*1000L)
  442. STDMETHODIMP
  443. CDirectMusicEmulatePort::PlayBuffer(
  444. IDirectMusicBuffer *pIBuffer)
  445. {
  446. CDirectMusicBuffer *pBuffer = reinterpret_cast<CDirectMusicBuffer *>(pIBuffer);
  447. REFERENCE_TIME rt;
  448. LPBYTE pbData;
  449. DWORD cbData;
  450. DWORD dwTileInfo;
  451. LONGLONG msTime;
  452. MMRESULT mmr;
  453. V_INAME(IDirectMusicPort::PlayBuffer);
  454. V_INTERFACE(pIBuffer);
  455. if (!m_pDM)
  456. {
  457. return DMUS_E_DMUSIC_RELEASED;
  458. }
  459. if (!m_fIsOutput)
  460. {
  461. return E_NOTIMPL;
  462. }
  463. if (!m_lActivated)
  464. {
  465. return DMUS_E_SYNTHINACTIVE;
  466. }
  467. // Make sure the object doesn't disappear out from under us while we're in Win16
  468. //
  469. pBuffer->AddRef();
  470. pBuffer->GetUsedBytes(&cbData);
  471. if (cbData == 0)
  472. {
  473. pBuffer->Release();
  474. return S_OK;
  475. }
  476. pBuffer->GetRawBufferPtr(&pbData);
  477. assert(pbData);
  478. pBuffer->GetStartTime(&rt);
  479. // Adjust timebase if we are not using the timeGetTime clock
  480. //
  481. Trace(2, "Buffer base time %I64d timeGetTime %u\n", rt, timeGetTime());
  482. SyncClocks();
  483. MasterToSlave(&rt);
  484. Trace(2, "Buffer adjusted base time %I64d\n", rt);
  485. msTime = rt / REFTIME_TO_MS;
  486. // Send it through the thunk
  487. //
  488. dwTileInfo = dmTileBuffer((DWORD)pbData, cbData);
  489. mmr = MidiOutSubmitPlaybackBuffer(m_hDevice,
  490. TILE_P1616(dwTileInfo),
  491. cbData,
  492. (DWORD)msTime,
  493. (DWORD)(rt & 0xFFFFFFFF), // RefTime low
  494. (DWORD)((rt >> 32) & 0xFFFFFFFF)); // RefTime high
  495. dmUntileBuffer(dwTileInfo);
  496. pBuffer->Release();
  497. return MMRESULTToHRESULT(mmr);
  498. }
  499. STDMETHODIMP
  500. CDirectMusicEmulatePort::Read(
  501. IDirectMusicBuffer *pIBuffer)
  502. {
  503. HRESULT hr;
  504. V_INAME(IDirectMusicPort::Read);
  505. V_INTERFACE(pIBuffer);
  506. if (!m_pDM)
  507. {
  508. return DMUS_E_DMUSIC_RELEASED;
  509. }
  510. if (m_fIsOutput)
  511. {
  512. return E_NOTIMPL;
  513. }
  514. LPBYTE pbBuffer;
  515. hr = pIBuffer->GetRawBufferPtr(&pbBuffer);
  516. if (FAILED(hr))
  517. {
  518. return hr;
  519. }
  520. DWORD cbBuffer;
  521. hr = pIBuffer->GetMaxBytes(&cbBuffer);
  522. if (FAILED(hr))
  523. {
  524. return hr;
  525. }
  526. Trace(1, "Read: buffer size %u\n", cbBuffer);
  527. LPBYTE pbData = pbBuffer;
  528. // Since events are now buffered, we read them out of the local queue
  529. //
  530. //
  531. EnterCriticalSection(&m_csEventQueues);
  532. REFERENCE_TIME rtStart;
  533. if (m_ReadEvents.pFront)
  534. {
  535. rtStart = m_ReadEvents.pFront->e.rtDelta;
  536. }
  537. else
  538. {
  539. Trace(2, "Read: No events queued\n");
  540. }
  541. while (m_ReadEvents.pFront)
  542. {
  543. QUEUED_EVENT *pQueuedEvent = m_ReadEvents.pFront;
  544. DWORD cbQueuedEvent = DMUS_EVENT_SIZE(pQueuedEvent->e.cbEvent);
  545. Trace(2, "Read: cbEvent %u cbQueuedEvent %u\n",
  546. pQueuedEvent->e.cbEvent,
  547. cbQueuedEvent);
  548. if (cbQueuedEvent > cbBuffer)
  549. {
  550. Trace(2, "Read: No more room for events in buffer.\n");
  551. break;
  552. }
  553. Trace(2, "Read: Got an event!\n");
  554. pQueuedEvent->e.rtDelta -= rtStart;
  555. CopyMemory(pbData,
  556. &pQueuedEvent->e,
  557. sizeof(DMEVENT) - sizeof(DWORD) + pQueuedEvent->e.cbEvent);
  558. pbData += cbQueuedEvent;
  559. cbBuffer -= cbQueuedEvent;
  560. m_ReadEvents.pFront = pQueuedEvent->pNext;
  561. if (pQueuedEvent->e.cbEvent <= sizeof(DWORD))
  562. {
  563. // This event came out of the pool
  564. //
  565. m_FreeEvents.Free(pQueuedEvent);
  566. }
  567. else
  568. {
  569. // This event was allocated via new char[]
  570. //
  571. char *pOriginalMemory = (char*)pQueuedEvent;
  572. delete[] pOriginalMemory;
  573. }
  574. }
  575. if (m_ReadEvents.pFront == NULL)
  576. {
  577. m_ReadEvents.pRear = NULL;
  578. }
  579. LeaveCriticalSection(&m_csEventQueues);
  580. // Update the buffer header information to match the events just packed
  581. //
  582. Trace(2, "Read: Leaving with %u bytes in buffer\n", (unsigned)(pbData - pbBuffer));
  583. pIBuffer->SetStartTime(rtStart);
  584. pIBuffer->SetUsedBytes(pbData - pbBuffer);
  585. return (pbData == pbBuffer) ? S_FALSE : S_OK;
  586. }
  587. STDMETHODIMP
  588. CDirectMusicEmulatePort::SetReadNotificationHandle(
  589. HANDLE hEvent)
  590. {
  591. if (!m_pDM)
  592. {
  593. return DMUS_E_DMUSIC_RELEASED;
  594. }
  595. if (m_fIsOutput)
  596. {
  597. return E_NOTIMPL;
  598. }
  599. m_hAppEvent = hEvent;
  600. return S_OK;
  601. }
  602. STDMETHODIMP
  603. CDirectMusicEmulatePort::DownloadInstrument(
  604. IDirectMusicInstrument *pInstrument,
  605. IDirectMusicDownloadedInstrument **pDownloadedInstrument,
  606. DMUS_NOTERANGE *pRange,
  607. DWORD dw)
  608. {
  609. return E_NOTIMPL;
  610. }
  611. STDMETHODIMP
  612. CDirectMusicEmulatePort::UnloadInstrument(
  613. IDirectMusicDownloadedInstrument *pDownloadedInstrument)
  614. {
  615. V_INAME(IDirectMusicPort::UnloadInstrument);
  616. V_INTERFACE(pDownloadedInstrument);
  617. return E_NOTIMPL;
  618. }
  619. STDMETHODIMP
  620. CDirectMusicEmulatePort::GetLatencyClock(
  621. IReferenceClock **ppClock)
  622. {
  623. V_INAME(IDirectMusicPort::GetLatencyClock);
  624. V_PTRPTR_WRITE(ppClock);
  625. if (!m_pDM)
  626. {
  627. return DMUS_E_DMUSIC_RELEASED;
  628. }
  629. m_pLatencyClock->AddRef();
  630. *ppClock = m_pLatencyClock;
  631. return S_OK;
  632. }
  633. STDMETHODIMP
  634. CDirectMusicEmulatePort::GetRunningStats(
  635. LPDMUS_SYNTHSTATS pStats)
  636. {
  637. V_INAME(IDirectMusicPort::GetRunningStats);
  638. V_STRUCTPTR_WRITE(pStats, DMUS_SYNTHSTATS);
  639. return E_NOTIMPL;
  640. }
  641. STDMETHODIMP
  642. CDirectMusicEmulatePort::Activate(
  643. BOOL fActivate)
  644. {
  645. MMRESULT mmr;
  646. V_INAME(IDirectMusicPort::Activate);
  647. if (!m_pDM)
  648. {
  649. return DMUS_E_DMUSIC_RELEASED;
  650. }
  651. if (fActivate)
  652. {
  653. if (InterlockedExchange(&m_lActivated, 1))
  654. {
  655. Trace(0, "Activate: Already active\n");
  656. // Already activated
  657. //
  658. return S_FALSE;
  659. }
  660. mmr = ActivateLegacyDevice(m_hDevice, TRUE);
  661. if (mmr)
  662. {
  663. Trace(0, "Activate: Activate mmr %d\n", mmr);
  664. m_lActivated = 0;
  665. }
  666. }
  667. else
  668. {
  669. if (InterlockedExchange(&m_lActivated, 0) == 0)
  670. {
  671. Trace(0, "Activate: Already inactive\n");
  672. // Already deactivated
  673. //
  674. return S_FALSE;
  675. }
  676. mmr = ActivateLegacyDevice(m_hDevice, FALSE);
  677. if (mmr)
  678. {
  679. Trace(0, "Activate: Deactivate mmr %d\n", mmr);
  680. m_lActivated = 1;
  681. }
  682. }
  683. return MMRESULTToHRESULT(mmr);
  684. }
  685. STDMETHODIMP
  686. CDirectMusicEmulatePort::SetChannelPriority(
  687. DWORD dwChannelGroup,
  688. DWORD dwChannel,
  689. DWORD dwPriority)
  690. {
  691. return E_NOTIMPL;
  692. }
  693. STDMETHODIMP
  694. CDirectMusicEmulatePort::GetChannelPriority(
  695. DWORD dwChannelGroup,
  696. DWORD dwChannel,
  697. LPDWORD pdwPriority)
  698. {
  699. return E_NOTIMPL;
  700. }
  701. STDMETHODIMP
  702. CDirectMusicEmulatePort::Close()
  703. {
  704. if (m_hCaptureThread)
  705. {
  706. SetEvent(m_hKillThreads);
  707. if (WaitForSingleObject(m_hCaptureThread, THREAD_KILL_TIMEOUT) == WAIT_TIMEOUT)
  708. {
  709. Trace(0, "Warning: Input thread timed out; exit anyway.\n");
  710. }
  711. m_hCaptureThread = NULL;
  712. }
  713. if (m_pThruMap)
  714. {
  715. for (int iChannel = 0; iChannel < 16; iChannel++)
  716. {
  717. if (m_pThruMap[iChannel].pDestinationPort == NULL)
  718. {
  719. continue;
  720. }
  721. if (m_pThruMap[iChannel].fThruInWin16)
  722. {
  723. MMRESULT mmr = MidiInThru(m_hDevice,
  724. (DWORD)iChannel,
  725. 0,
  726. NULL);
  727. }
  728. m_pThruMap[iChannel].pDestinationPort->Release();
  729. }
  730. delete[] m_pThruMap;
  731. m_pThruMap = NULL;
  732. }
  733. if (m_pThruBuffer)
  734. {
  735. m_pThruBuffer->Release();
  736. m_pThruBuffer = NULL;
  737. }
  738. if (m_hDataReady)
  739. {
  740. CloseHandle(m_hDataReady);
  741. m_hDataReady = NULL;
  742. }
  743. if (m_hKillThreads)
  744. {
  745. CloseHandle(m_hKillThreads);
  746. m_hKillThreads = NULL;
  747. }
  748. if (m_hAppEvent)
  749. {
  750. m_hAppEvent = NULL;
  751. }
  752. if (m_dwWorkBufferTileInfo)
  753. {
  754. dmUntileBuffer(m_dwWorkBufferTileInfo);
  755. m_dwWorkBufferTileInfo = 0;
  756. m_p1616WorkBuffer = NULL;
  757. }
  758. if (m_hVxDEvent)
  759. {
  760. CloseVxDHandle(m_hVxDEvent);
  761. m_hVxDEvent = NULL;
  762. }
  763. if (m_hDevice)
  764. {
  765. CloseLegacyDevice(m_hDevice);
  766. m_hDevice = NULL;
  767. }
  768. if (m_pMasterClock)
  769. {
  770. m_pMasterClock->Release();
  771. m_pMasterClock = NULL;
  772. }
  773. if (m_pLatencyClock)
  774. {
  775. m_pLatencyClock->Close();
  776. m_pLatencyClock->Release();
  777. m_pLatencyClock = NULL;
  778. }
  779. if (m_fCSInitialized)
  780. {
  781. DeleteCriticalSection(&m_csEventQueues);
  782. }
  783. m_pDM = NULL;
  784. m_pNotify = NULL;
  785. return S_OK;
  786. }
  787. STDMETHODIMP
  788. CDirectMusicEmulatePort::Report()
  789. {
  790. return S_OK;
  791. }
  792. // StartVoice and StopVoice don't work on legacy devices
  793. //
  794. STDMETHODIMP CDirectMusicEmulatePort::StartVoice(
  795. DWORD dwVoiceId,
  796. DWORD dwChannel,
  797. DWORD dwChannelGroup,
  798. REFERENCE_TIME rtStart,
  799. DWORD dwDLId,
  800. LONG prPitch,
  801. LONG veVolume,
  802. SAMPLE_TIME stVoiceStart,
  803. SAMPLE_TIME stLoopStart,
  804. SAMPLE_TIME stLoopEnd)
  805. {
  806. return E_NOTIMPL;
  807. }
  808. STDMETHODIMP CDirectMusicEmulatePort::StopVoice(
  809. DWORD dwVoiceID,
  810. REFERENCE_TIME rtStop)
  811. {
  812. return E_NOTIMPL;
  813. }
  814. STDMETHODIMP CDirectMusicEmulatePort::GetVoiceState(
  815. DWORD dwVoice[],
  816. DWORD cbVoice,
  817. DMUS_VOICE_STATE VoiceState[])
  818. {
  819. return E_NOTIMPL;
  820. }
  821. STDMETHODIMP CDirectMusicEmulatePort::Refresh(
  822. DWORD dwDownloadID,
  823. DWORD dwFlags)
  824. {
  825. return E_NOTIMPL;
  826. }
  827. // CDirectMusicEmulatePort::ThruChannel
  828. //
  829. STDMETHODIMP
  830. CDirectMusicEmulatePort::ThruChannel(
  831. DWORD dwSourceChannelGroup,
  832. DWORD dwSourceChannel,
  833. DWORD dwDestinationChannelGroup,
  834. DWORD dwDestinationChannel,
  835. LPDIRECTMUSICPORT pDestinationPort)
  836. {
  837. V_INAME(IDirectMusicPort::Thru);
  838. V_INTERFACE_OPT(pDestinationPort);
  839. if (m_fIsOutput)
  840. {
  841. return E_NOTIMPL;
  842. }
  843. // Channel group must not be zero (broadcast) but in range 1..NumChannelGroups]
  844. // (which for legacy is always 1)
  845. //
  846. if (dwSourceChannelGroup != 1 ||
  847. dwSourceChannel > 15)
  848. {
  849. return E_INVALIDARG;
  850. }
  851. // Given a port means enable thruing for this channel; NULL means
  852. // disable.
  853. //
  854. if (pDestinationPort)
  855. {
  856. // Enabling thruing on this channel. First look at the destination port.
  857. //
  858. DMUS_PORTCAPS dmpc;
  859. dmpc.dwSize = sizeof(dmpc);
  860. HRESULT hr = pDestinationPort->GetCaps(&dmpc);
  861. if (FAILED(hr))
  862. {
  863. Trace(0, "ThruChannel: Destination port failed portcaps [%08X]\n", hr);
  864. return hr;
  865. }
  866. // Port must be an output port
  867. //
  868. if (dmpc.dwClass != DMUS_PC_OUTPUTCLASS)
  869. {
  870. return DMUS_E_PORT_NOT_RENDER;
  871. }
  872. // Channel group and channel must be in range.
  873. //
  874. if (dwDestinationChannel > 15 ||
  875. dwDestinationChannelGroup > dmpc.dwMaxChannelGroups)
  876. {
  877. return E_INVALIDARG;
  878. }
  879. // Release existing port
  880. //
  881. if (m_pThruMap[dwSourceChannel].pDestinationPort)
  882. {
  883. // Reference to another port type, release it.
  884. // (NOTE: No need to turn off native dmusic16 thruing at this point,
  885. // that's handled in dmusic16).
  886. //
  887. m_pThruMap[dwSourceChannel].pDestinationPort->Release();
  888. }
  889. m_pThruMap[dwSourceChannel].dwDestinationChannel = dwDestinationChannel;
  890. m_pThruMap[dwSourceChannel].dwDestinationChannelGroup = dwDestinationChannelGroup;
  891. m_pThruMap[dwSourceChannel].pDestinationPort = pDestinationPort;
  892. m_pThruMap[dwSourceChannel].fThruInWin16 = FALSE;
  893. // Is the destination also a legacy port?
  894. //
  895. if (dmpc.dwType == DMUS_PORT_WINMM_DRIVER)
  896. {
  897. // Woohoo! We can do native thruing in Win16!
  898. //
  899. m_pThruMap[dwSourceChannel].fThruInWin16 = TRUE;
  900. Trace(2, "32: Thruing <%d> -> <%d> in Win16\n",
  901. dwSourceChannel,
  902. dwDestinationChannel);
  903. MMRESULT mmr = MidiInThru(m_hDevice,
  904. dwSourceChannel,
  905. dwDestinationChannel,
  906. ((CDirectMusicEmulatePort*)pDestinationPort)->m_hDevice);
  907. if (mmr)
  908. {
  909. Trace(0, "ThruChannel: MidiInThru returned %d\n", mmr);
  910. return MMRESULTToHRESULT(mmr);
  911. }
  912. }
  913. else
  914. {
  915. Trace(2, "ThruChannel: From (%u,%u) -> (%u,%u,%p)\n",
  916. dwSourceChannelGroup,
  917. dwSourceChannel,
  918. dwDestinationChannelGroup,
  919. dwDestinationChannel,
  920. pDestinationPort);
  921. }
  922. pDestinationPort->AddRef();
  923. }
  924. else
  925. {
  926. // Disabling thruing on this channel
  927. //
  928. if (m_pThruMap[dwSourceChannel].pDestinationPort)
  929. {
  930. if (m_pThruMap[dwSourceChannel].fThruInWin16)
  931. {
  932. MMRESULT mmr = MidiInThru(m_hDevice,
  933. dwSourceChannel,
  934. 0,
  935. (HANDLE)NULL);
  936. if (mmr)
  937. {
  938. Trace(0, "ThruChannel: MidiInThru returned %d\n", mmr);
  939. return MMRESULTToHRESULT(mmr);
  940. }
  941. }
  942. m_pThruMap[dwSourceChannel].pDestinationPort->Release();
  943. m_pThruMap[dwSourceChannel].pDestinationPort = NULL;
  944. }
  945. }
  946. return S_OK;
  947. }
  948. STDMETHODIMP
  949. CDirectMusicEmulatePort::SetDirectSound(
  950. LPDIRECTSOUND pDirectSound,
  951. LPDIRECTSOUNDBUFFER pDirectSoundBuffer)
  952. {
  953. return E_NOTIMPL;
  954. }
  955. STDMETHODIMP
  956. CDirectMusicEmulatePort::GetFormat(
  957. LPWAVEFORMATEX pWaveFormatEx,
  958. LPDWORD pdwWaveFormatExSize,
  959. LPDWORD pdwBufferSize)
  960. {
  961. return E_NOTIMPL;
  962. }
  963. // CDirectMusicEmulatePort::DownloadWave
  964. //
  965. STDMETHODIMP
  966. CDirectMusicEmulatePort::DownloadWave(
  967. IDirectSoundWave *pWave,
  968. IDirectSoundDownloadedWaveP **ppWave,
  969. REFERENCE_TIME rtStartHint)
  970. {
  971. V_INAME(IDirectMusicPort::DownloadWave);
  972. V_INTERFACE(pWave);
  973. V_PTRPTR_WRITE(ppWave);
  974. return E_NOTIMPL;
  975. }
  976. // CDirectMusicEmulatePort::UnloadWave
  977. //
  978. STDMETHODIMP
  979. CDirectMusicEmulatePort::UnloadWave(
  980. IDirectSoundDownloadedWaveP *pDownloadedWave)
  981. {
  982. V_INAME(IDirectMusicPort::UnloadWave);
  983. V_INTERFACE(pDownloadedWave);
  984. return E_NOTIMPL;
  985. }
  986. // CDirectMusicEmulatePort::AllocVoice
  987. //
  988. STDMETHODIMP
  989. CDirectMusicEmulatePort::AllocVoice(
  990. IDirectSoundDownloadedWaveP *pWave,
  991. DWORD dwChannel,
  992. DWORD dwChannelGroup,
  993. REFERENCE_TIME rtStart,
  994. SAMPLE_TIME stLoopStart,
  995. SAMPLE_TIME stLoopEnd,
  996. IDirectMusicVoiceP **ppVoice)
  997. {
  998. V_INAME(IDirectMusicPort::AllocVoice);
  999. V_INTERFACE(pWave);
  1000. V_PTRPTR_WRITE(ppVoice);
  1001. return E_NOTIMPL;
  1002. }
  1003. // CDirectMusicEmulatePort::AssignChannelToBuses
  1004. //
  1005. STDMETHODIMP
  1006. CDirectMusicEmulatePort::AssignChannelToBuses(
  1007. DWORD dwChannelGroup,
  1008. DWORD dwChannel,
  1009. LPDWORD pdwBuses,
  1010. DWORD cBusCount)
  1011. {
  1012. return E_NOTIMPL;
  1013. }
  1014. STDMETHODIMP
  1015. CDirectMusicEmulatePort::SetSink(
  1016. IDirectSoundConnect *pSinkConnect)
  1017. {
  1018. return E_NOTIMPL;
  1019. }
  1020. STDMETHODIMP
  1021. CDirectMusicEmulatePort::GetSink(
  1022. IDirectSoundConnect **ppSinkConnect)
  1023. {
  1024. return E_NOTIMPL;
  1025. }
  1026. GENERICPROPERTY CDirectMusicEmulatePort::m_aProperty[] =
  1027. {
  1028. { &GUID_DMUS_PROP_LegacyCaps, // Set
  1029. 0, // Item
  1030. KSPROPERTY_SUPPORT_GET, // KS support flags
  1031. GENPROP_F_FNHANDLER, // GENPROP flags
  1032. NULL, 0, // static data and size
  1033. CDirectMusicEmulatePort::LegacyCaps // Handler
  1034. }
  1035. };
  1036. const int CDirectMusicEmulatePort::m_nProperty = sizeof(m_aProperty) / sizeof(m_aProperty[0]);
  1037. HRESULT CDirectMusicEmulatePort::LegacyCaps(
  1038. ULONG ulId,
  1039. BOOL fSet,
  1040. LPVOID pbBuffer,
  1041. PULONG pcbBuffer)
  1042. {
  1043. if (fSet == KSPROPERTY_SUPPORT_SET)
  1044. {
  1045. return DMUS_E_SET_UNSUPPORTED;
  1046. }
  1047. MIDIINCAPS mic;
  1048. MIDIOUTCAPS moc;
  1049. LPBYTE pbData;
  1050. ULONG cbData;
  1051. if (m_fIsOutput)
  1052. {
  1053. MMRESULT mmr = midiOutGetDevCaps(m_id, &moc, sizeof(moc));
  1054. if (mmr)
  1055. {
  1056. Trace(0, "midiOutGetDevCaps failed!\n");
  1057. return MMRESULTToHRESULT(mmr);
  1058. }
  1059. pbData = (LPBYTE)&moc;
  1060. cbData = sizeof(moc);
  1061. }
  1062. else
  1063. {
  1064. MMRESULT mmr = midiInGetDevCaps(m_id, &mic, sizeof(mic));
  1065. if (mmr)
  1066. {
  1067. Trace(0, "midiInGetDevCaps failed!\n");
  1068. return MMRESULTToHRESULT(mmr);
  1069. }
  1070. pbData = (LPBYTE)&mic;
  1071. cbData = sizeof(mic);
  1072. }
  1073. ULONG cbToCopy = min(*pcbBuffer, cbData);
  1074. CopyMemory(pbBuffer, pbData, cbToCopy);
  1075. *pcbBuffer = cbToCopy;
  1076. return S_OK;
  1077. }
  1078. //
  1079. // CDirectMusicEmulatePort::FindPropertyItem
  1080. //
  1081. // Given a GUID and an item ID, find the associated property item in the synth's
  1082. // table of SYNPROPERTY's.
  1083. //
  1084. // Returns a pointer to the entry or NULL if the item was not found.
  1085. //
  1086. GENERICPROPERTY *CDirectMusicEmulatePort::FindPropertyItem(REFGUID rguid, ULONG ulId)
  1087. {
  1088. GENERICPROPERTY *pPropertyItem = &m_aProperty[0];
  1089. GENERICPROPERTY *pEndOfItems = pPropertyItem + m_nProperty;
  1090. for (; pPropertyItem != pEndOfItems; pPropertyItem++)
  1091. {
  1092. if (*pPropertyItem->pguidPropertySet == rguid &&
  1093. pPropertyItem->ulId == ulId)
  1094. {
  1095. return pPropertyItem;
  1096. }
  1097. }
  1098. return NULL;
  1099. }
  1100. #define KS_VALID_FLAGS (KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET| KSPROPERTY_TYPE_BASICSUPPORT)
  1101. STDMETHODIMP CDirectMusicEmulatePort::KsProperty(
  1102. PKSPROPERTY pPropertyIn, ULONG ulPropertyLength,
  1103. LPVOID pvPropertyData, ULONG ulDataLength,
  1104. PULONG pulBytesReturned)
  1105. {
  1106. V_INAME(DirectMusicSynthPort::IKsContol::KsProperty);
  1107. V_BUFPTR_WRITE(pPropertyIn, ulPropertyLength);
  1108. V_BUFPTR_WRITE_OPT(pvPropertyData, ulDataLength);
  1109. V_PTR_WRITE(pulBytesReturned, ULONG);
  1110. DWORD dwFlags = pPropertyIn->Flags & KS_VALID_FLAGS;
  1111. if ((dwFlags == 0) || (dwFlags == (KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET)))
  1112. {
  1113. }
  1114. GENERICPROPERTY *pProperty = FindPropertyItem(pPropertyIn->Set, pPropertyIn->Id);
  1115. if (pProperty == NULL)
  1116. {
  1117. return DMUS_E_UNKNOWN_PROPERTY;
  1118. }
  1119. switch (dwFlags)
  1120. {
  1121. case KSPROPERTY_TYPE_GET:
  1122. if (!(pProperty->ulSupported & KSPROPERTY_SUPPORT_GET))
  1123. {
  1124. return DMUS_E_GET_UNSUPPORTED;
  1125. }
  1126. if (pProperty->ulFlags & GENPROP_F_FNHANDLER)
  1127. {
  1128. GENPROPHANDLER pfn = pProperty->pfnHandler;
  1129. *pulBytesReturned = ulDataLength;
  1130. return (this->*pfn)(pPropertyIn->Id, KSPROPERTY_SUPPORT_GET, pvPropertyData, pulBytesReturned);
  1131. }
  1132. if (ulDataLength > pProperty->cbPropertyData)
  1133. {
  1134. ulDataLength = pProperty->cbPropertyData;
  1135. }
  1136. CopyMemory(pvPropertyData, pProperty->pPropertyData, ulDataLength);
  1137. *pulBytesReturned = ulDataLength;
  1138. return S_OK;
  1139. case KSPROPERTY_TYPE_SET:
  1140. if (!(pProperty->ulSupported & KSPROPERTY_SUPPORT_SET))
  1141. {
  1142. return DMUS_E_SET_UNSUPPORTED;
  1143. }
  1144. if (pProperty->ulFlags & GENPROP_F_FNHANDLER)
  1145. {
  1146. GENPROPHANDLER pfn = pProperty->pfnHandler;
  1147. return (this->*pfn)(pPropertyIn->Id, KSPROPERTY_SUPPORT_SET, pvPropertyData, &ulDataLength);
  1148. }
  1149. if (ulDataLength > pProperty->cbPropertyData)
  1150. {
  1151. ulDataLength = pProperty->cbPropertyData;
  1152. }
  1153. CopyMemory(pProperty->pPropertyData, pvPropertyData, ulDataLength);
  1154. return S_OK;
  1155. case KSPROPERTY_TYPE_BASICSUPPORT:
  1156. if (pProperty == NULL)
  1157. {
  1158. return DMUS_E_UNKNOWN_PROPERTY;
  1159. }
  1160. // XXX Find out what convention is for this!!
  1161. //
  1162. if (ulDataLength < sizeof(DWORD))
  1163. {
  1164. return E_INVALIDARG;
  1165. }
  1166. *(LPDWORD)pvPropertyData = pProperty->ulSupported;
  1167. *pulBytesReturned = sizeof(DWORD);
  1168. return S_OK;
  1169. }
  1170. Trace(-1, "%s: Flags must contain one of\n"
  1171. "\tKSPROPERTY_TYPE_SET, KSPROPERTY_TYPE_GET, or KSPROPERTY_TYPE_BASICSUPPORT\n");
  1172. return E_INVALIDARG;
  1173. }
  1174. STDMETHODIMP CDirectMusicEmulatePort::KsMethod(
  1175. PKSMETHOD pMethod, ULONG ulMethodLength,
  1176. LPVOID pvMethodData, ULONG ulDataLength,
  1177. PULONG pulBytesReturned)
  1178. {
  1179. V_INAME(DirectMusicSynth::IKsContol::KsMethod);
  1180. V_BUFPTR_WRITE(pMethod, ulMethodLength);
  1181. V_BUFPTR_WRITE_OPT(pvMethodData, ulDataLength);
  1182. V_PTR_WRITE(pulBytesReturned, ULONG);
  1183. return DMUS_E_UNKNOWN_PROPERTY;
  1184. }
  1185. STDMETHODIMP CDirectMusicEmulatePort::KsEvent(
  1186. PKSEVENT pEvent, ULONG ulEventLength,
  1187. LPVOID pvEventData, ULONG ulDataLength,
  1188. PULONG pulBytesReturned)
  1189. {
  1190. V_INAME(DirectMusicSynthPort::IKsContol::KsEvent);
  1191. V_BUFPTR_WRITE(pEvent, ulEventLength);
  1192. V_BUFPTR_WRITE_OPT(pvEventData, ulDataLength);
  1193. V_PTR_WRITE(pulBytesReturned, ULONG);
  1194. return DMUS_E_UNKNOWN_PROPERTY;
  1195. }
  1196. #define OFFSET_DATA_READY 0
  1197. #define OFFSET_KILL_THREAD 1
  1198. DWORD CDirectMusicEmulatePort::InputWorker()
  1199. {
  1200. HANDLE h[2];
  1201. h[OFFSET_DATA_READY] = m_hDataReady;
  1202. h[OFFSET_KILL_THREAD] = m_hKillThreads;
  1203. UINT uWait;
  1204. for(;;)
  1205. {
  1206. uWait = WaitForMultipleObjects(2, h, FALSE, INFINITE);
  1207. switch(uWait)
  1208. {
  1209. case WAIT_OBJECT_0 + OFFSET_DATA_READY:
  1210. // m_hDataReady set
  1211. //
  1212. InputWorkerDataReady();
  1213. if (m_hAppEvent)
  1214. {
  1215. try
  1216. {
  1217. SetEvent(m_hAppEvent);
  1218. }
  1219. catch (...)
  1220. {
  1221. Trace(0, "Capture: Application notify event handle prematurely free'd!\n");
  1222. }
  1223. }
  1224. break;
  1225. case WAIT_OBJECT_0 + OFFSET_KILL_THREAD:
  1226. // m_hKillThread set
  1227. //
  1228. Trace(0, "CDirectMusicEmulateWorker::InputWorker thread exit\n");
  1229. return 0;
  1230. case WAIT_FAILED:
  1231. Trace(0, "WaitForMultipleObjects failed %d killing thread\n", GetLastError());
  1232. return 0;
  1233. default:
  1234. break;
  1235. }
  1236. }
  1237. return 0;
  1238. }
  1239. // CDirectMusicEmulatePort::InputWorkerDataReady()
  1240. //
  1241. // The input worker thread has been notified that there is data available.
  1242. // Read any pending events from the 16-bit DLL, perform needed thruing, and
  1243. // save the data in a queue so we can repackage it on the read request
  1244. // from the client.
  1245. //
  1246. void CDirectMusicEmulatePort::InputWorkerDataReady()
  1247. {
  1248. MMRESULT mmr;
  1249. DWORD cbData;
  1250. DWORD msTime;
  1251. LPBYTE pbData;
  1252. DMEVENT *pEvent;
  1253. DWORD cbRounded;
  1254. REFERENCE_TIME rtStart;
  1255. HRESULT hr;
  1256. REFERENCE_TIME rtMasterClock;
  1257. Trace(0, "Enter InputWorkerDataReady()\n");
  1258. for(;;)
  1259. {
  1260. // Fill temporary buffer
  1261. //
  1262. cbData = sizeof(m_WorkBuffer);
  1263. mmr = MidiInRead(m_hDevice,
  1264. m_p1616WorkBuffer,
  1265. &cbData,
  1266. &msTime);
  1267. rtStart = ((ULONGLONG)msTime) * REFTIME_TO_MS;
  1268. SyncClocks();
  1269. SlaveToMaster(&rtStart);
  1270. hr = m_pMasterClock->GetTime(&rtMasterClock);
  1271. if (mmr)
  1272. {
  1273. Trace(2, "InputWorkerDataReady: MidiInRead returned %d\n", mmr);
  1274. return;
  1275. }
  1276. if (cbData == 0)
  1277. {
  1278. Trace(2, "MidiInRead returned no data\n");
  1279. return;
  1280. }
  1281. // Copy temporary buffer as events into queue
  1282. //
  1283. pbData = m_WorkBuffer;
  1284. while (cbData)
  1285. {
  1286. pEvent = (DMEVENT*)pbData;
  1287. cbRounded = DMUS_EVENT_SIZE(pEvent->cbEvent);
  1288. Trace(2, "cbData %u cbRounded %u\n", cbData, cbRounded);
  1289. if (cbRounded > cbData)
  1290. {
  1291. Trace(0, "InputWorkerDataReady: Event ran off end of buffer\n");
  1292. break;
  1293. }
  1294. cbData -= cbRounded;
  1295. pbData += cbRounded;
  1296. EnterCriticalSection(&m_csEventQueues);
  1297. QUEUED_EVENT *pQueuedEvent;
  1298. int cbEvent;
  1299. if (pEvent->cbEvent <= sizeof(DWORD))
  1300. {
  1301. // Channel message or other really small event, take from
  1302. // free pool.
  1303. //
  1304. pQueuedEvent = m_FreeEvents.Alloc();
  1305. cbEvent = sizeof(DMEVENT);
  1306. Trace(2, "Queue [%02X %02X %02X %02X]\n",
  1307. pEvent->abEvent[0],
  1308. pEvent->abEvent[1],
  1309. pEvent->abEvent[2],
  1310. pEvent->abEvent[3]);
  1311. }
  1312. else
  1313. {
  1314. // SysEx or other long event, just allocate it
  1315. //
  1316. cbEvent = DMUS_EVENT_SIZE(pEvent->cbEvent);
  1317. pQueuedEvent = (QUEUED_EVENT*)new char[QUEUED_EVENT_SIZE(pEvent->cbEvent)];
  1318. }
  1319. if (pQueuedEvent)
  1320. {
  1321. CopyMemory(&pQueuedEvent->e, pEvent, cbEvent);
  1322. // rtDelta is the absolute time of the event while it's in our queue
  1323. //
  1324. pQueuedEvent->e.rtDelta += rtStart;
  1325. ThruEvent(&pQueuedEvent->e);
  1326. if (m_ReadEvents.pFront)
  1327. {
  1328. m_ReadEvents.pRear->pNext = pQueuedEvent;
  1329. }
  1330. else
  1331. {
  1332. m_ReadEvents.pFront = pQueuedEvent;
  1333. }
  1334. m_ReadEvents.pRear = pQueuedEvent;
  1335. pQueuedEvent->pNext = NULL;
  1336. }
  1337. else
  1338. {
  1339. Trace(1, "InputWorker: Failed to allocate event; dropping\n");
  1340. }
  1341. LeaveCriticalSection(&m_csEventQueues);
  1342. }
  1343. }
  1344. Trace(2, "Leave InputWorkerDataReady()\n");
  1345. }
  1346. void CDirectMusicEmulatePort::ThruEvent(
  1347. DMEVENT *pEvent)
  1348. {
  1349. // Since we know we only have one event and we already have it in the right format,
  1350. // just slam it into the thru buffer. We only have to do this because we might modify
  1351. // it.
  1352. //
  1353. LPBYTE pbData;
  1354. DWORD cbData;
  1355. DWORD cbEvent = DMUS_EVENT_SIZE(pEvent->cbEvent);
  1356. // First see if the event is thruable
  1357. //
  1358. if (pEvent->cbEvent > 3 || ((pEvent->abEvent[0] & 0xF0) == 0xF0))
  1359. {
  1360. // SysEx of some description
  1361. return;
  1362. }
  1363. // Note: legacy driver assures no running status
  1364. //
  1365. DWORD dwSourceChannel = (DWORD)(pEvent->abEvent[0] & 0x0F);
  1366. DMUS_THRU_CHANNEL *pThru = &m_pThruMap[dwSourceChannel];
  1367. if (pThru->pDestinationPort == NULL ||
  1368. pThru->fThruInWin16)
  1369. {
  1370. return;
  1371. }
  1372. if (FAILED(m_pThruBuffer->GetRawBufferPtr(&pbData)))
  1373. {
  1374. Trace(0, "Thru: GetRawBufferPtr\n");
  1375. return;
  1376. }
  1377. if (FAILED(m_pThruBuffer->GetMaxBytes(&cbData)))
  1378. {
  1379. Trace(0, "Thru: GetMaxBytes\n");
  1380. return;
  1381. }
  1382. if (cbEvent > cbData)
  1383. {
  1384. Trace(0, "Thru: cbData %u cbEvent %u\n", cbData, cbEvent);
  1385. return;
  1386. }
  1387. if (FAILED(m_pThruBuffer->SetStartTime(pEvent->rtDelta)) ||
  1388. FAILED(m_pThruBuffer->SetUsedBytes(cbEvent)))
  1389. {
  1390. Trace(0, "Thru: buffer setup failed\n");
  1391. }
  1392. pEvent->rtDelta = 50000;
  1393. CopyMemory(pbData, pEvent, cbEvent);
  1394. pEvent = (DMEVENT*)pbData;
  1395. pEvent->dwChannelGroup = pThru->dwDestinationChannelGroup;
  1396. pEvent->abEvent[0] = (BYTE)((pEvent->abEvent[0] & 0xF0) | pThru->dwDestinationChannel);
  1397. pThru->pDestinationPort->PlayBuffer(m_pThruBuffer);
  1398. }
  1399. void CDirectMusicEmulatePort::MasterToSlave(
  1400. REFERENCE_TIME *prt)
  1401. {
  1402. if (m_fSyncToMaster)
  1403. {
  1404. *prt -= m_lTimeOffset;
  1405. }
  1406. }
  1407. void CDirectMusicEmulatePort::SlaveToMaster(
  1408. REFERENCE_TIME *prt)
  1409. {
  1410. if (m_fSyncToMaster)
  1411. {
  1412. *prt += m_lTimeOffset;
  1413. }
  1414. }
  1415. void CDirectMusicEmulatePort::SyncClocks()
  1416. {
  1417. HRESULT hr;
  1418. REFERENCE_TIME rtMasterClock;
  1419. REFERENCE_TIME rtSlaveClock;
  1420. LONGLONG drift;
  1421. if (m_fSyncToMaster)
  1422. {
  1423. hr = m_pMasterClock->GetTime(&rtMasterClock);
  1424. rtSlaveClock = ((ULONGLONG)timeGetTime()) * MS_TO_REFERENCE_TIME;
  1425. if (FAILED(hr))
  1426. {
  1427. return;
  1428. }
  1429. drift = (rtSlaveClock + m_lTimeOffset) - rtMasterClock;
  1430. // Work-around 46782 for DX8 release:
  1431. // If drift is greater than 10ms, jump to the new offset value instead
  1432. // of drifting there slowly.
  1433. if( drift > 10000 * 10
  1434. || drift < 10000 * -10 )
  1435. {
  1436. m_lTimeOffset -= drift;
  1437. }
  1438. else
  1439. {
  1440. m_lTimeOffset -= drift / 100;
  1441. }
  1442. }
  1443. }
  1444. /////////////////////////////////////////////////////////////////////
  1445. //
  1446. // CEmulateLatencyClock
  1447. //
  1448. // Latency clock for emulated ports, which is just a fixed offset from
  1449. // the DirectMusic master clock
  1450. //
  1451. CEmulateLatencyClock::CEmulateLatencyClock(IReferenceClock *pMasterClock) :
  1452. m_cRef(1),
  1453. m_pMasterClock(pMasterClock)
  1454. {
  1455. pMasterClock->AddRef();
  1456. }
  1457. CEmulateLatencyClock::~CEmulateLatencyClock()
  1458. {
  1459. Close();
  1460. }
  1461. STDMETHODIMP
  1462. CEmulateLatencyClock::QueryInterface(
  1463. const IID &iid,
  1464. void **ppv)
  1465. {
  1466. if (iid == IID_IUnknown || iid == IID_IReferenceClock)
  1467. {
  1468. *ppv = static_cast<IReferenceClock*>(this);
  1469. }
  1470. else
  1471. {
  1472. *ppv = NULL;
  1473. return E_NOINTERFACE;
  1474. }
  1475. reinterpret_cast<IUnknown*>(*ppv)->AddRef();
  1476. return S_OK;
  1477. }
  1478. STDMETHODIMP_(ULONG)
  1479. CEmulateLatencyClock::AddRef()
  1480. {
  1481. return InterlockedIncrement(&m_cRef);
  1482. }
  1483. STDMETHODIMP_(ULONG)
  1484. CEmulateLatencyClock::Release()
  1485. {
  1486. if (!InterlockedDecrement(&m_cRef)) {
  1487. delete this;
  1488. return 0;
  1489. }
  1490. return m_cRef;
  1491. }
  1492. STDMETHODIMP
  1493. CEmulateLatencyClock::GetTime(
  1494. REFERENCE_TIME *pTime)
  1495. {
  1496. REFERENCE_TIME rt;
  1497. V_INAME(IReferenceClock::GetTime);
  1498. V_PTR_WRITE(pTime, REFERENCE_TIME);
  1499. if (!m_pMasterClock)
  1500. {
  1501. return DMUS_E_DMUSIC_RELEASED;
  1502. }
  1503. HRESULT hr = m_pMasterClock->GetTime(&rt);
  1504. rt += FIXED_LEGACY_LATENCY_OFFSET; // Default : 10 ms
  1505. *pTime = rt;
  1506. return hr;
  1507. }
  1508. STDMETHODIMP
  1509. CEmulateLatencyClock::AdviseTime(
  1510. REFERENCE_TIME baseTime,
  1511. REFERENCE_TIME streamTime,
  1512. HANDLE hEvent,
  1513. DWORD * pdwAdviseCookie)
  1514. {
  1515. return DMUS_E_UNKNOWN_PROPERTY;
  1516. }
  1517. STDMETHODIMP
  1518. CEmulateLatencyClock::AdvisePeriodic(
  1519. REFERENCE_TIME startTime,
  1520. REFERENCE_TIME periodTime,
  1521. HANDLE hSemaphore,
  1522. DWORD * pdwAdviseCookie)
  1523. {
  1524. return DMUS_E_UNKNOWN_PROPERTY;
  1525. }
  1526. STDMETHODIMP
  1527. CEmulateLatencyClock::Unadvise(
  1528. DWORD dwAdviseCookie)
  1529. {
  1530. return DMUS_E_UNKNOWN_PROPERTY;
  1531. }
  1532. void
  1533. CEmulateLatencyClock::Close()
  1534. {
  1535. if (m_pMasterClock)
  1536. {
  1537. m_pMasterClock->Release();
  1538. m_pMasterClock = NULL;
  1539. }
  1540. }
  1541. static HRESULT MMRESULTToHRESULT(
  1542. MMRESULT mmr)
  1543. {
  1544. switch (mmr)
  1545. {
  1546. case MMSYSERR_NOERROR:
  1547. return S_OK;
  1548. case MMSYSERR_ALLOCATED:
  1549. return DMUS_E_DEVICE_IN_USE;
  1550. case MIDIERR_BADOPENMODE:
  1551. return DMUS_E_ALREADYOPEN;
  1552. case MMSYSERR_NOMEM:
  1553. return E_OUTOFMEMORY;
  1554. }
  1555. return E_FAIL;
  1556. }