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.

914 lines
26 KiB

  1. /* Copyright (c) 1998-1999 Microsoft Corporation */
  2. /*
  3. * @Doc DMusic16
  4. *
  5. * @Module MIDIIn.c - Legacy MIDI capture emulation for DirectMusic |
  6. */
  7. #pragma warning(disable:4704) /* Inline assembly */
  8. #include <windows.h>
  9. #include <mmsystem.h>
  10. #include <stddef.h>
  11. #include "dmusic16.h"
  12. #include "debug.h"
  13. #define IS_STATUS_BYTE(x) ((x) & 0x80)
  14. #define IS_CHANNEL_MSG(x) (((x) & 0xF0) != 0xF0)
  15. #define IS_SYSEX(x) ((x) == 0xF0)
  16. #define SYSEX_SIZE 4096
  17. /* (65535 - sizeof(MIDIHDR) - sizeof(EVENT) - sizeof(SEGHDR)) */
  18. #define SYSEX_BUFFERS 8 /* Keep 2 buffers outstanding */
  19. static unsigned cbChanMsg[16] =
  20. {
  21. 0, 0, 0, 0, 0, 0, 0, 0, /* Running status */
  22. 3, 3, 3, 3, 2, 2, 3, 0
  23. };
  24. static unsigned cbSysCommData[16] =
  25. {
  26. 1, 2, 3, 2, 1, 1, 1, 1,
  27. 1, 1, 1, 1, 1, 1, 1, 1
  28. };
  29. VOID CALLBACK _loadds midiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
  30. STATIC BOOL NEAR PASCAL RecordShortEvent(NPOPENHANDLE poh, DWORD dwMessage, DWORD dwTime);
  31. STATIC BOOL NEAR PASCAL RecordSysExEvent(NPOPENHANDLE poh, LPMIDIHDR lpmh, DWORD dwTime);
  32. STATIC VOID NEAR PASCAL NotifyClientList(LPOPENHANDLE poh);
  33. STATIC VOID NEAR PASCAL ThruClientList(LPOPENHANDLE poh, DWORD dwMessage);
  34. STATIC VOID NEAR PASCAL RefillFreeEventList(NPOPENHANDLE poh);
  35. STATIC VOID NEAR PASCAL MidiInFlushQueues(NPOPENHANDLE poh);
  36. #pragma alloc_text(INIT_TEXT, MidiOutOnLoad)
  37. #pragma alloc_text(FIX_IN_TEXT, midiInProc)
  38. #pragma alloc_text(FIX_IN_TEXT, RecordShortEvent)
  39. #pragma alloc_text(FIX_IN_TEXT, RecordSysExEvent)
  40. #pragma alloc_text(FIX_IN_TEXT, NotifyClientList)
  41. #pragma alloc_text(FIX_IN_TEXT, ThruClientList)
  42. /* @func Called at DLL <f LibInit>
  43. *
  44. * @comm
  45. *
  46. * Currently does nothing.
  47. *
  48. */
  49. VOID PASCAL
  50. MidiInOnLoad(VOID)
  51. {
  52. }
  53. /* @func Called at DLL <f LibExit>
  54. *
  55. * @comm
  56. *
  57. * Currently does nothing
  58. */
  59. VOID PASCAL
  60. MidiInOnExit()
  61. {
  62. }
  63. /* @func Open a MIDI in device
  64. *
  65. * @rdesc Returns one of the following:
  66. * @flag MMSYSERR_NOERROR | on success
  67. * @flag MMSYSERR_NOMEM | on out of memory
  68. *
  69. * @comm
  70. *
  71. * Makes sure only one client is opening the device.
  72. *
  73. * Opens the device and starts MIDI input on it, noting the time of the start for timestamp calculations.
  74. */
  75. MMRESULT PASCAL
  76. MidiInOnOpen(
  77. NPOPENHANDLEINSTANCE pohi) /* @parm The open handle instance to fulfill */
  78. {
  79. NPOPENHANDLE poh = pohi->pHandle;
  80. int iChannel;
  81. MMRESULT mmr;
  82. /* Protect here against more than one client opening an input device.
  83. */
  84. if (poh->uReferenceCount > 1)
  85. {
  86. return MMSYSERR_ALLOCATED;
  87. }
  88. /* Per client initialize thruing to NULL.
  89. */
  90. pohi->pThru = (NPTHRUCHANNEL)LocalAlloc(LPTR, MIDI_CHANNELS * sizeof(THRUCHANNEL));
  91. if (pohi->pThru == NULL)
  92. {
  93. return MMSYSERR_NOMEM;
  94. }
  95. DPF(2, "MidiInOnOpen: pohi %04X pThru %04X", pohi, pohi->pThru);
  96. for (iChannel = 0; iChannel < MIDI_CHANNELS; iChannel++)
  97. {
  98. pohi->pThru[iChannel].pohi = (HANDLE)NULL;
  99. }
  100. return MMSYSERR_NOERROR;
  101. }
  102. /* @func Close a MIDI in device
  103. *
  104. * @comm
  105. *
  106. * Close the device using the <f midiInClose> API.
  107. */
  108. VOID PASCAL
  109. MidiInOnClose(
  110. NPOPENHANDLEINSTANCE pohi) /* @parm The open handle instance to close */
  111. {
  112. }
  113. /* @func Activate a MIDI in device
  114. *
  115. * @rdesc Returns one of the following:
  116. * @flag MMSYSERR_NOERROR | on success
  117. * @flag MMSYSERR_ALLOCATED | if the device is already in use
  118. *
  119. * May also return any of the possible return codes from the <f midiInOpen> API call.
  120. *
  121. * @comm
  122. *
  123. * Opens the device and starts MIDI input on it, noting the time of the start for timestamp calculations.
  124. */
  125. MMRESULT PASCAL
  126. MidiInOnActivate(
  127. NPOPENHANDLEINSTANCE pohi)
  128. {
  129. NPOPENHANDLE poh = pohi->pHandle;
  130. MMRESULT mmr;
  131. if (1 == poh->uActiveCount)
  132. {
  133. poh->wFlags &= ~OH_F_CLOSING;
  134. mmr = midiInOpen(&poh->hmi,
  135. poh->id,
  136. (DWORD)midiInProc,
  137. (DWORD)(LPOPENHANDLE)poh,
  138. CALLBACK_FUNCTION);
  139. if (mmr)
  140. {
  141. return mmr;
  142. }
  143. mmr = midiInStart(poh->hmi);
  144. poh->msStartTime = timeGetTime();
  145. if (mmr)
  146. {
  147. midiInClose(poh->hmi);
  148. }
  149. /* NOTE: poh memory is guaranteed zeroed by allocator, so we have
  150. * no event count and NULL pointers right now.
  151. */
  152. RefillFreeEventList(poh);
  153. }
  154. return MMSYSERR_NOERROR;
  155. }
  156. /* @func Deactivate a MIDI in device
  157. *
  158. * @comm
  159. *
  160. * Close the device using the <f midiInClose> API.
  161. */
  162. MMRESULT PASCAL
  163. MidiInOnDeactivate(
  164. NPOPENHANDLEINSTANCE pohi)
  165. {
  166. NPOPENHANDLE poh = pohi->pHandle;
  167. MMRESULT mmr;
  168. if (0 == poh->uActiveCount)
  169. {
  170. poh->wFlags |= OH_F_CLOSING;
  171. mmr = midiInStop(poh->hmi);
  172. if (mmr)
  173. {
  174. return mmr;
  175. }
  176. if (MMSYSERR_NOERROR == midiInReset(poh->hmi))
  177. {
  178. while (poh->wPostedSysExBuffers)
  179. {
  180. }
  181. }
  182. midiInClose(poh->hmi);
  183. MidiInFlushQueues(poh);
  184. }
  185. return MMSYSERR_NOERROR;
  186. }
  187. /* @func Set the event handle to signal
  188. *
  189. * @rdesc Always returns MMSYSERR_NOERROR.
  190. *
  191. * @comm
  192. *
  193. * This function is exported through the thunk layer to DMusic32.DLL
  194. *
  195. * This handle is already the VxD handle that can be passed to VWin32 via MMDEVLDR using
  196. * <f SetWin32Event>.
  197. *
  198. * Input notification is delivered to the Win32 application using events. The application creates
  199. * an event using the <f CreateEvent> API and gives it to the DirectMusic port. The port code
  200. * for legacy emulation calls the undocumented Win9x kernel API <f OpenVxDHandle> to retrieve
  201. * an equivalent event handle that is valid in any kernel context. That handle is passed to
  202. * this function.
  203. *
  204. * The event handle is stored in our per-client data (<c OPENHANDLEINSTANCE>). When MIDI data
  205. * arrives, the event will be set. This is done using MMDEVLDR, which already has semantics
  206. * in place to do the same sort of notification for WinMM event callbacks.
  207. *
  208. */
  209. MMRESULT WINAPI
  210. MidiInSetEventHandle(
  211. HANDLE hMidiIn, /* @parm The handle of the input device which desires notification */
  212. DWORD dwEvent) /* @parm The VxD handle of the event to set when new data arrives */
  213. {
  214. NPOPENHANDLEINSTANCE pohi;
  215. if (!IsValidHandle(hMidiIn, VA_F_INPUT, &pohi))
  216. {
  217. return MMSYSERR_INVALHANDLE;
  218. }
  219. pohi->dwVxDEventHandle = dwEvent;
  220. return MMSYSERR_NOERROR;
  221. }
  222. /* @func Read MIDI input data into a buffer
  223. *
  224. * @rdesc Returns one of the following
  225. *
  226. * @comm
  227. *
  228. * This function is thunked to the 32-bit DLL
  229. *
  230. * Take as much data from the given event list as will fit and put it into the buffer.
  231. */
  232. MMRESULT WINAPI
  233. MidiInRead(
  234. HANDLE hMidiIn, /* @parm The handle of the input device to read */
  235. LPBYTE lpBuffer, /* @parm A pointer to memory to pack, in DMEVENT format */
  236. LPDWORD pcbData, /* @parm On input, the max size of <p lpBuffer> in bytes.
  237. On return, will contain the number of bytes of data packed into the buffer */
  238. LPDWORD pmsTime) /* @parm On return, will contain the starting time of the buffer */
  239. {
  240. NPOPENHANDLEINSTANCE pohi;
  241. NPOPENHANDLE poh;
  242. WORD wCSID;
  243. LPEVENT pEvent;
  244. LPEVENT pEventRemoved;
  245. LPBYTE pbEventData;
  246. DWORD cbLength;
  247. DWORD cbPaddedLength;
  248. DWORD cbLeft;
  249. LPBYTE lpNextEvent;
  250. LPDMEVENT pdm;
  251. DWORD msFirst;
  252. MMRESULT mmr;
  253. LPMIDIHDR lpmh;
  254. if (!IsValidHandle(hMidiIn, VA_F_INPUT, &pohi))
  255. {
  256. return MMSYSERR_INVALHANDLE;
  257. }
  258. poh = pohi->pHandle;
  259. lpNextEvent = lpBuffer;
  260. cbLeft = *pcbData;
  261. wCSID = EnterCriticalSection(&poh->wCritSect, CS_BLOCKING);
  262. assert(wCSID);
  263. msFirst = 0;
  264. while (NULL != (pEvent = QueuePeek(&poh->qDone)))
  265. {
  266. lpmh = NULL;
  267. if (cbLeft < sizeof(DMEVENT))
  268. {
  269. break;
  270. }
  271. if (pEvent->wFlags & EVENT_F_MIDIHDR)
  272. {
  273. /* This event is a SysEx message starting with a MIDIHDR, which contains
  274. * the recorded length of the message.
  275. */
  276. lpmh = (LPMIDIHDR)(&pEvent->abEvent[0]);
  277. cbLength = lpmh->dwBytesRecorded - lpmh->dwOffset;
  278. pbEventData = lpmh->lpData + lpmh->dwOffset;
  279. cbPaddedLength = DMEVENT_SIZE(cbLength);
  280. /* For SysEx, split out as much as will fit if the whole message can't.
  281. */
  282. if (cbPaddedLength > cbLeft)
  283. {
  284. cbLength = DMEVENT_DATASIZE(cbLeft);
  285. cbPaddedLength = DMEVENT_SIZE(cbLength);
  286. }
  287. }
  288. else
  289. {
  290. /* The data for this event is directly contained in the event.
  291. */
  292. cbLength = pEvent->cbEvent;
  293. pbEventData = &pEvent->abEvent[0];
  294. cbPaddedLength = DMEVENT_SIZE(cbLength);
  295. if (cbPaddedLength > cbLeft)
  296. {
  297. break;
  298. }
  299. }
  300. assert(cbPaddedLength <= cbLeft);
  301. pdm = (LPDMEVENT)lpNextEvent;
  302. pdm->cbEvent = cbLength;
  303. pdm->dwChannelGroup = 1;
  304. pdm->dwFlags = 0;
  305. if (msFirst)
  306. {
  307. QuadwordMul( pEvent->msTime - msFirst,
  308. REFTIME_TO_MS,
  309. &pdm->rtDelta);
  310. }
  311. else
  312. {
  313. *pmsTime = pEvent->msTime;
  314. msFirst = pEvent->msTime;
  315. pdm->rtDelta.dwLow = 0;
  316. pdm->rtDelta.dwHigh = 0;
  317. }
  318. hmemcpy(pdm->abEvent, pbEventData, cbLength);
  319. lpNextEvent += cbPaddedLength;
  320. cbLeft -= cbPaddedLength;
  321. if (lpmh)
  322. {
  323. lpmh->dwOffset += cbLength;
  324. assert(lpmh->dwOffset <= lpmh->dwBytesRecorded);
  325. if (lpmh->dwOffset == lpmh->dwBytesRecorded)
  326. {
  327. pEventRemoved = QueueRemoveFromFront(&poh->qDone);
  328. assert(pEventRemoved == pEvent);
  329. InterlockedIncrement(&poh->wPostedSysExBuffers);
  330. lpmh->dwOffset = 0;
  331. mmr = midiInAddBuffer(poh->hmi, (LPMIDIHDR)(&pEvent->abEvent[0]), sizeof(MIDIHDR));
  332. if (mmr)
  333. {
  334. InterlockedDecrement(&poh->wPostedSysExBuffers);
  335. DPF(0, "midiInAddBuffer failed with mmr=%d", mmr);
  336. mmr = midiInUnprepareHeader(poh->hmi, (LPMIDIHDR)(&pEvent->abEvent[0]), sizeof(MIDIHDR));
  337. if (mmr)
  338. {
  339. DPF(0, "...midiInUnprepareHeader failed too %d, memory leak!", mmr);
  340. }
  341. else
  342. {
  343. FreeEvent(pEvent);
  344. }
  345. }
  346. }
  347. }
  348. else
  349. {
  350. pEventRemoved = QueueRemoveFromFront(&poh->qDone);
  351. assert(pEventRemoved == pEvent);
  352. QueueAppend(&poh->qFree, pEvent);
  353. }
  354. }
  355. *pcbData = lpNextEvent - lpBuffer;
  356. DPF(1, "MidiInRead: Returning %ld bytes", (DWORD)*pcbData);
  357. LeaveCriticalSection(&poh->wCritSect);
  358. return MMSYSERR_NOERROR;
  359. }
  360. /* @func Enable thruing to a MIDI output port
  361. *
  362. * @comm For the given channel group and channel, enable (or disable, if the
  363. * output handle is NULL) thruing to the given output handle, channel group, and
  364. * channel.
  365. */
  366. MMRESULT WINAPI
  367. MidiInThru(
  368. HANDLE hMidiIn, /* @parm The handle of the input device to thru */
  369. DWORD dwFrom, /* @parm The channel of the input stream to thru */
  370. DWORD dwTo, /* @parm Desination channel */
  371. HANDLE hMidiOut) /* The output handle to receive the thru'ed data. */
  372. {
  373. NPOPENHANDLEINSTANCE pohiInput;
  374. NPOPENHANDLEINSTANCE pohiOutput;
  375. if (!IsValidHandle(hMidiIn, VA_F_INPUT, &pohiInput) ||
  376. ((hMidiOut != NULL) && !IsValidHandle(hMidiOut, VA_F_OUTPUT, &pohiOutput)))
  377. {
  378. return MMSYSERR_INVALHANDLE;
  379. }
  380. /* Note that since only 1 channel group is supported on legacy drivers,
  381. * we don't need any channel group information.
  382. */
  383. if (dwFrom > 15 || dwTo > 15)
  384. {
  385. return MMSYSERR_INVALPARAM;
  386. }
  387. DPF(1, "Thru: Sending <%04X,%u> to <%04X,%u>",
  388. (WORD)hMidiIn, (UINT)dwFrom, (WORD)hMidiOut, (UINT)dwTo);
  389. pohiInput->pThru[(WORD)dwFrom].wChannel = (WORD)dwTo;
  390. pohiInput->pThru[(WORD)dwFrom].pohi = hMidiOut ? pohiOutput : NULL;
  391. return MMSYSERR_NOERROR;
  392. }
  393. /* @func MIDI in data callback
  394. *
  395. * @comm
  396. *
  397. * This is a standard MIDI input callback from MMSYSYTEM. It calls the correct record routine
  398. * and notifies the client that data has arrived.
  399. *
  400. * For a description of event notification of clients, see <f MidiInSetEventHandle>.
  401. */
  402. VOID CALLBACK _loadds
  403. midiInProc(
  404. HMIDIIN hMidiIn, /* @parm The MMSYSTEM handle of the device which received data */
  405. UINT wMsg, /* @parm The type of callback */
  406. DWORD dwInstance, /* @parm Instance data; in our case, a pointer to an <c OPENHANDLE> matching <p hMidiIn> */
  407. DWORD dwParam1, /* @parm Message-specific parameters */
  408. DWORD dwParam2) /* @parm Message-specific parameters */
  409. {
  410. NPOPENHANDLE poh = (NPOPENHANDLE)(WORD)dwInstance;
  411. BOOL bIsNewData = FALSE;
  412. WORD wCSID;
  413. /* If we can get the critical section we can do all sorts of fun stuff like
  414. * transfer the lists over.
  415. */
  416. wCSID = EnterCriticalSection(&poh->wCritSect, CS_NONBLOCKING);
  417. if (wCSID)
  418. {
  419. /* We now have exclusive access to all the queues.
  420. *
  421. * Move any new free events into our internal free list.
  422. */
  423. QueueCat(&poh->qFreeCB, &poh->qFree);
  424. }
  425. switch(wMsg)
  426. {
  427. case MIM_DATA:
  428. DPF(1, "MIM_DATA %08lX %08lX", dwParam1, dwParam2);
  429. bIsNewData = RecordShortEvent(poh, dwParam1, dwParam2);
  430. break;
  431. case MIM_LONGDATA:
  432. DPF(1, "MIM_LONGDATA %08lX %08lX", dwParam1, dwParam2);
  433. bIsNewData = RecordSysExEvent(poh, (LPMIDIHDR)dwParam1, dwParam2);
  434. break;
  435. default:
  436. break;
  437. }
  438. if (wCSID)
  439. {
  440. /* It's safe to move events over to the shared list.
  441. */
  442. QueueCat(&poh->qDone, &poh->qDoneCB);
  443. LeaveCriticalSection(&poh->wCritSect);
  444. }
  445. /* Let clients know there is new data
  446. */
  447. if (bIsNewData && (!(poh->wFlags & OH_F_CLOSING)))
  448. {
  449. NotifyClientList(poh);
  450. }
  451. }
  452. /* @func Record a short message (channel messsage or system message).
  453. *
  454. * @comm
  455. *
  456. * Queue the incoming data as quickly as possible.
  457. *
  458. * For a description of the queues used for incoming data, see the <c OPENHANDLE> struct.
  459. *
  460. * @rdesc
  461. * Returns TRUE if the data was successfully recorded; FALSE otherwise.
  462. */
  463. STATIC BOOL NEAR PASCAL
  464. RecordShortEvent(
  465. NPOPENHANDLE poh, /* @parm The handle to record this data to */
  466. DWORD dwMessage, /* @parm The short message to record */
  467. DWORD dwTime) /* @parm The time stamp of the message */
  468. {
  469. LPEVENT pEvent;
  470. LPBYTE pb;
  471. BYTE b;
  472. pEvent = QueueRemoveFromFront(&poh->qFreeCB);
  473. if (pEvent == NULL)
  474. {
  475. DPF(0, "midiInProc: Missed a short event!!!");
  476. return FALSE;
  477. }
  478. pEvent->msTime = poh->msStartTime + dwTime;
  479. pEvent->wFlags = 0;
  480. /* Now we have to parse and rebuild the channel message.
  481. *
  482. * NOTE: Endian specific code ahead
  483. */
  484. pb = (LPBYTE)&dwMessage;
  485. assert(!IS_SYSEX(*pb)); /* This should *always* be in MIM_LONGDATA */
  486. assert(IS_STATUS_BYTE(*pb)); /* API guarantees no running status */
  487. /* Copying over all the bytes is harmless (we have a DWORD in both
  488. * source and dest) and is faster than checking to see if we have to.
  489. */
  490. b = pEvent->abEvent[0] = *pb++;
  491. pEvent->abEvent[1] = *pb++;
  492. pEvent->abEvent[2] = *pb++;
  493. if (IS_CHANNEL_MSG(b))
  494. {
  495. /* 8x, 9x, Ax, Bx, Cx, Dx, Ex */
  496. /* 0x..7x invalid, that would need running status */
  497. /* Fx handled below */
  498. pEvent->cbEvent = cbChanMsg[(b >> 4) & 0x0F];
  499. /* This is also our criteria for thruing
  500. */
  501. ThruClientList(poh, dwMessage);
  502. }
  503. else
  504. {
  505. /* F1..FF */
  506. /* F0 is sysex, should never see it here */
  507. pEvent->cbEvent = cbSysCommData[b & 0x0F];
  508. }
  509. /* Now we have something to save
  510. */
  511. QueueAppend(&poh->qDoneCB, pEvent);
  512. return TRUE;
  513. }
  514. /* @func Record a SysEx message.
  515. *
  516. * @comm
  517. *
  518. * Queue the incoming data as quickly as possible.
  519. *
  520. * For a description of the queues used for incoming data, see the <c OPENHANDLE> struct.
  521. *
  522. * @rdesc
  523. * Returns TRUE if the data was successfully recorded; FALSE otherwise.
  524. */
  525. STATIC BOOL NEAR PASCAL
  526. RecordSysExEvent(
  527. NPOPENHANDLE poh, /* @parm The handle to record this data to */
  528. LPMIDIHDR lpmh, /* @parm The SysEx message to record */
  529. DWORD dwTime) /* @parm The time stamp of the message */
  530. {
  531. LPEVENT pEvent;
  532. /* Get back the event header for this MIDIHDR. While buffers are in MMSYSTEM, they are not
  533. * in any queue.
  534. */
  535. InterlockedDecrement(&poh->wPostedSysExBuffers);
  536. /* dwOffset in the MIDIHDR is used to indicate the start of data to send
  537. * up to Win32. It is incremented by MidiInRead until the buffer has been
  538. * emptied, at which time it will be put back into the pool.
  539. */
  540. lpmh->dwOffset = 0;
  541. pEvent = (LPEVENT)(lpmh->dwUser);
  542. pEvent->msTime = poh->msStartTime + dwTime;
  543. QueueAppend(&poh->qDoneCB, pEvent);
  544. return TRUE;
  545. }
  546. /* @func Notify all clients of a device that data has arrived.
  547. *
  548. * @comm
  549. *
  550. * Walks the list of clients for the device and sets the notification event for each one.
  551. *
  552. * This function is now overkill since we no longer support multiple input clients per device.
  553. */
  554. STATIC VOID NEAR PASCAL
  555. NotifyClientList(
  556. LPOPENHANDLE poh) /* @parm The handle of the device that has received data */
  557. {
  558. NPLINKNODE plink;
  559. NPOPENHANDLEINSTANCE pohi;
  560. for (plink = poh->pInstanceList; plink; plink = plink->pNext)
  561. {
  562. pohi = (NPOPENHANDLEINSTANCE)(((PBYTE)plink) - offsetof(OPENHANDLEINSTANCE, linkHandleList));
  563. if (!pohi->dwVxDEventHandle)
  564. {
  565. /* No notification event registered for this handle yet.
  566. */
  567. continue;
  568. }
  569. SetWin32Event(pohi->dwVxDEventHandle);
  570. }
  571. }
  572. /* @func Thru this message based on the settings of all clients of a device.
  573. *
  574. * @comm
  575. *
  576. * Walks the list of clients for the device and looks at the thru settings of each one.
  577. *
  578. * This function is now overkill since we no longer support multiple input clients per device.
  579. */
  580. STATIC VOID NEAR PASCAL
  581. ThruClientList(
  582. LPOPENHANDLE poh,
  583. DWORD dwMessage)
  584. {
  585. NPLINKNODE plink;
  586. NPOPENHANDLEINSTANCE pohi;
  587. NPOPENHANDLEINSTANCE pohiDest;
  588. int iChannel;
  589. iChannel = (int)(dwMessage & 0x0000000Fl);
  590. dwMessage &= 0xFFFFFFF0l;
  591. for (plink = poh->pInstanceList; plink; plink = plink->pNext)
  592. {
  593. pohi = (NPOPENHANDLEINSTANCE)(((PBYTE)plink) - offsetof(OPENHANDLEINSTANCE, linkHandleList));
  594. pohiDest = pohi->pThru[iChannel].pohi;
  595. if (pohiDest == NULL || !pohiDest->fActive)
  596. {
  597. continue;
  598. }
  599. MidiOutThru(pohiDest,
  600. dwMessage & 0xFFFFFFF0l | pohi->pThru[iChannel].wChannel);
  601. }
  602. }
  603. /* @func Refill the free lists
  604. *
  605. * @comm
  606. *
  607. * This function is called periodically from user mode to ensure that there are enough free
  608. * events available for the input callback.
  609. */
  610. VOID PASCAL
  611. MidiInRefillFreeLists(VOID)
  612. {
  613. NPLINKNODE plink;
  614. NPOPENHANDLE poh;
  615. for (plink = gOpenHandleList;
  616. (poh = (NPOPENHANDLE)plink) != NULL;
  617. plink = plink->pNext)
  618. {
  619. /* Only refill MIDI in devices which are not in the process of closing
  620. */
  621. if ((poh->wFlags & (OH_F_MIDIIN | OH_F_CLOSING)) != OH_F_MIDIIN)
  622. {
  623. continue;
  624. }
  625. RefillFreeEventList(poh);
  626. }
  627. }
  628. /* @func Terminate thruing to this output handle
  629. *
  630. * @comm
  631. *
  632. * This function is called before the given output handle is closed.
  633. */
  634. VOID PASCAL
  635. MidiInUnthruToInstance(
  636. NPOPENHANDLEINSTANCE pohiClosing) /* @parm NPOPENHANDLE | pohClosing |
  637. The handle which is closing. */
  638. {
  639. NPLINKNODE plink;
  640. NPOPENHANDLE poh;
  641. NPLINKNODE plinkInstance;
  642. NPOPENHANDLEINSTANCE pohiInstance;
  643. int iChannel;
  644. for (plink = gOpenHandleList; (poh = (NPOPENHANDLE)plink) != NULL; plink = plink->pNext)
  645. {
  646. DPF(2, "Unthru: poh <%04X>", (WORD)poh);
  647. if (!(poh->wFlags & OH_F_MIDIIN))
  648. {
  649. DPF(2, "...not input");
  650. continue;
  651. }
  652. for (plinkInstance = poh->pInstanceList; plinkInstance; plinkInstance = plinkInstance->pNext)
  653. {
  654. pohiInstance = (NPOPENHANDLEINSTANCE)(((PBYTE)plinkInstance) - offsetof(OPENHANDLEINSTANCE, linkHandleList));
  655. DPF(2, "pohiInstance <%04X>", (WORD)pohiInstance);
  656. for (iChannel = 0; iChannel < MIDI_CHANNELS; iChannel++)
  657. {
  658. DPF(2, "Channel 0 @ <%04X>", (WORD)&pohiInstance->pThru[iChannel]);
  659. if (pohiInstance->pThru[iChannel].pohi == pohiClosing)
  660. {
  661. DPF(1, "Thru: Closing output handle %04X which is in use!", (WORD)pohiClosing);
  662. pohiInstance->pThru[iChannel].pohi = NULL;
  663. }
  664. }
  665. }
  666. }
  667. }
  668. /* @func Allocate enough free events to refill the pool to CAP_HIGHWATERMARK
  669. *
  670. * @comm
  671. *
  672. * BUGBUG call this on a window timer callback
  673. *
  674. */
  675. STATIC VOID NEAR PASCAL
  676. RefillFreeEventList(
  677. NPOPENHANDLE poh) /* @parm The device to refill the free list of */
  678. {
  679. int idx;
  680. LPEVENT pEvent;
  681. UINT cFree;
  682. WORD wCSID;
  683. QUADWORD rt = {0, 0};
  684. int cNewBuffers;
  685. LPMIDIHDR lpmh;
  686. MMRESULT mmr;
  687. WORD wIntStat;
  688. wCSID = EnterCriticalSection(&poh->wCritSect, CS_BLOCKING);
  689. assert(wCSID);
  690. /* NOTE: Technically not allowed to access qFreeCB here, but this is an approximation
  691. */
  692. cFree = poh->qFree.cEle + poh->qFreeCB.cEle;
  693. if (cFree < CAP_HIGHWATERMARK)
  694. {
  695. DPF(1, "RefillFreeEventList poh %.4x free %u highwater %u",
  696. (WORD)poh,
  697. (UINT)cFree,
  698. (UINT)CAP_HIGHWATERMARK);
  699. for (idx = CAP_HIGHWATERMARK - cFree; idx; --idx)
  700. {
  701. pEvent = AllocEvent(0, rt, sizeof(DWORD));
  702. if (NULL == pEvent)
  703. {
  704. DPF(0, "AllocEvent returned NULL in RefillFreeEventList");
  705. break;
  706. }
  707. QueueAppend(&poh->qFree, pEvent);
  708. }
  709. }
  710. LeaveCriticalSection(&poh->wCritSect);
  711. if (poh->wPostedSysExBuffers < SYSEX_BUFFERS)
  712. {
  713. for (idx = SYSEX_BUFFERS - cFree; idx; --idx)
  714. {
  715. pEvent = AllocEvent(0, rt, sizeof(MIDIHDR) + SYSEX_SIZE);
  716. if (NULL == pEvent)
  717. {
  718. break;
  719. }
  720. pEvent->wFlags |= EVENT_F_MIDIHDR;
  721. lpmh = (LPMIDIHDR)(&pEvent->abEvent[0]);
  722. lpmh->lpData = (LPSTR)(lpmh + 1);
  723. lpmh->dwBufferLength = SYSEX_SIZE;
  724. lpmh->dwUser = (DWORD)pEvent;
  725. mmr = midiInPrepareHeader(poh->hmi, lpmh, sizeof(MIDIHDR));
  726. if (mmr)
  727. {
  728. DPF(0, "midiInPrepareHeader: %u\n", mmr);
  729. FreeEvent(pEvent);
  730. break;
  731. }
  732. InterlockedIncrement(&poh->wPostedSysExBuffers);
  733. mmr = midiInAddBuffer(poh->hmi, lpmh, sizeof(MIDIHDR));
  734. if (mmr)
  735. {
  736. InterlockedDecrement(&poh->wPostedSysExBuffers);
  737. DPF(0, "midiInAddBuffer: %u\n", mmr);
  738. midiInUnprepareHeader(poh->hmi, lpmh, sizeof(MIDIHDR));
  739. FreeEvent(pEvent);
  740. break;
  741. }
  742. }
  743. }
  744. }
  745. /* @func Return all memory from all queues to the free event list.
  746. *
  747. * @comm
  748. *
  749. */
  750. STATIC VOID NEAR PASCAL
  751. MidiInFlushQueues(
  752. NPOPENHANDLE poh)
  753. {
  754. WORD wCSID;
  755. wCSID = EnterCriticalSection(&poh->wCritSect, CS_BLOCKING);
  756. assert(wCSID);
  757. FreeAllQueueEvents(&poh->qDone);
  758. FreeAllQueueEvents(&poh->qDoneCB);
  759. FreeAllQueueEvents(&poh->qFree);
  760. FreeAllQueueEvents(&poh->qFreeCB);
  761. LeaveCriticalSection(&poh->wCritSect);
  762. }
  763. /* @func Free all events in the given event queue.
  764. *
  765. * @comm
  766. *
  767. * Assumes that the queue's critical section has already been taken by the caller.
  768. *
  769. */
  770. VOID PASCAL
  771. FreeAllQueueEvents(
  772. NPEVENTQUEUE peq)
  773. {
  774. LPEVENT lpCurr;
  775. LPEVENT lpNext;
  776. lpCurr = peq->pHead;
  777. while (lpCurr)
  778. {
  779. lpNext = lpCurr->lpNext;
  780. FreeEvent(lpCurr);
  781. lpCurr = lpNext;
  782. }
  783. peq->pHead = peq->pTail = NULL;
  784. peq->cEle = 0;
  785. }