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.

1019 lines
27 KiB

  1. /* Copyright (c) 1998 Microsoft Corporation */
  2. /*
  3. * @Doc DMusic16
  4. *
  5. * @Module MIDIOut.c - Legacy MIDI output emulation for DirectMusic |
  6. *
  7. * @comm
  8. *
  9. * BUGBUG Need to deal with timer wraparound
  10. *
  11. */
  12. #pragma warning(disable:4704) /* Inline assembly */
  13. #include <windows.h>
  14. #include <mmsystem.h>
  15. #include "dmusic16.h"
  16. #include "debug.h"
  17. #define MIDI_CHANMSG_STATUS_CMD_MASK (0xF0)
  18. #define MIDI_NOTE_ON (0x90)
  19. /* How far past the current time do we send events?
  20. */
  21. #define MS_TIMER_SLOP (3)
  22. STATIC TIMECAPS gTimeCaps;
  23. STATIC BOOL gbTimerRunning;
  24. STATIC DWORD gdwTimerDue;
  25. STATIC UINT guTimerID;
  26. STATIC UINT gcActiveOutputDevices;
  27. int PASCAL IsEventDone(LPEVENT pEvent, DWORD dwInstance);
  28. VOID SetNextTimer();
  29. VOID CALLBACK __loadds midiOutProc(HMIDIOUT hMidiIn, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
  30. VOID CALLBACK __loadds RunTimer(UINT uTimerID, UINT wMsg, DWORD dwUser, DWORD dw1, DWORD dw2);
  31. STATIC VOID NEAR PASCAL MidiOutFlushQueues(NPOPENHANDLE poh);
  32. STATIC VOID NEAR PASCAL MidiOutSendAllNow(NPOPENHANDLE poh);
  33. #pragma alloc_text(INIT_TEXT, MidiOutOnLoad)
  34. #pragma alloc_text(FIX_OUT_TEXT, midiOutProc)
  35. #pragma alloc_text(FIX_OUT_TEXT, RunTimer)
  36. /* @func Called at DLL <f LibInit>
  37. *
  38. * @comm
  39. *
  40. * Get the timer caps.
  41. * Initialize globals.
  42. */
  43. VOID PASCAL
  44. MidiOutOnLoad()
  45. {
  46. /* This cannot fail
  47. */
  48. timeGetDevCaps(&gTimeCaps, sizeof(gTimeCaps));
  49. gbTimerRunning = FALSE;
  50. }
  51. /* @func Called at DLL <f LibExit>
  52. *
  53. * @comm
  54. *
  55. * The DLL is unloading, so kill any future timer callback.
  56. */
  57. VOID PASCAL
  58. MidiOutOnExit()
  59. {
  60. WORD wIntStat;
  61. wIntStat = DisableInterrupts();
  62. if (gbTimerRunning)
  63. {
  64. DPF(1, "DLL unloading, killing timer interrupts");
  65. timeKillEvent(guTimerID);
  66. gbTimerRunning = FALSE;
  67. }
  68. RestoreInterrupts(wIntStat);
  69. }
  70. /* @func Open a handle instance
  71. *
  72. * @comm
  73. *
  74. */
  75. MMRESULT PASCAL
  76. MidiOutOnOpen(
  77. NPOPENHANDLEINSTANCE pohi)
  78. {
  79. return MMSYSERR_NOERROR;
  80. }
  81. /* @func Close a MIDI device
  82. *
  83. * @comm
  84. *
  85. */
  86. VOID PASCAL
  87. MidiOutOnClose(
  88. NPOPENHANDLEINSTANCE pohi)
  89. {
  90. /* Give MIDI input a chance to turn off thruing to this handle.
  91. */
  92. MidiInUnthruToInstance(pohi);
  93. }
  94. /* @func Activate a MIDI device
  95. *
  96. * @comm
  97. *
  98. * If this is the first activation of the device, open it using the <f midiOutOpen> legacy API.
  99. */
  100. MMRESULT PASCAL
  101. MidiOutOnActivate(
  102. NPOPENHANDLEINSTANCE pohi)
  103. {
  104. NPOPENHANDLE poh = pohi->pHandle;
  105. MMRESULT mmr;
  106. HINSTANCE hInstance;
  107. WORD sel;
  108. WORD off;
  109. HTASK FAR *lph;
  110. DPF(1, "MidiOutActivate poh %04X device %d refcount %u",
  111. (WORD)poh,
  112. poh->id,
  113. poh->uReferenceCount);
  114. /* Only open on the first activation
  115. */
  116. if (1 == poh->uActiveCount)
  117. {
  118. mmr = midiOutOpen(&poh->hmo,
  119. poh->id,
  120. (DWORD)midiOutProc,
  121. (DWORD)(LPOPENHANDLE)poh,
  122. CALLBACK_FUNCTION);
  123. if (mmr)
  124. {
  125. return mmr;
  126. }
  127. /* Since mapper can't be open shared, and we don't want the first instance that opens
  128. * mapper to take it with it on exit (due to mmsystem appexit), we do really nasty
  129. * stuff here.
  130. *
  131. * The WORD immediately PRECEDING the handle in MMSYSTEM's data segment is the task
  132. * owner of the handle. We nuke it to NULL (which is all MIDI_IO_SHARED does anyway)
  133. * to make AppExit ignore us.
  134. *
  135. * God help us if anyone changes HNDL in mmsysi.h
  136. *
  137. */
  138. hInstance = LoadLibrary("mmsystem.dll");
  139. sel = (WORD)hInstance;
  140. /* hInstance <= 32 means LoadLibrary failed; in this case we just live with it.
  141. */
  142. if (sel > 32)
  143. {
  144. off = ((WORD)poh->hmo) - sizeof(WORD);
  145. lph = (HTASK FAR *)MAKELP(sel, off);
  146. *lph = (HTASK)NULL;
  147. FreeLibrary(hInstance);
  148. }
  149. /* If this is the first output device, bump up timer resolution
  150. */
  151. ++gcActiveOutputDevices;
  152. if (gcActiveOutputDevices == 1)
  153. {
  154. SetOutputTimerRes(TRUE);
  155. }
  156. }
  157. return MMSYSERR_NOERROR;
  158. }
  159. /* @func Deactivate a MIDI device
  160. *
  161. * @comm
  162. *
  163. * If the last client using the device is closing, then close the actual device.
  164. * If closing the last actual device, then shut down the high precision timer
  165. *
  166. */
  167. MMRESULT PASCAL
  168. MidiOutOnDeactivate(
  169. NPOPENHANDLEINSTANCE pohi)
  170. {
  171. NPOPENHANDLE poh = pohi->pHandle;
  172. DPF(1, "MidiOutOnDeactivate poh %04X device %d refcount %u",
  173. (WORD)poh,
  174. poh->id,
  175. poh->uReferenceCount);
  176. if (poh->uActiveCount)
  177. {
  178. /* Still open instances out there
  179. */
  180. return MMSYSERR_NOERROR;
  181. }
  182. MidiOutSendAllNow(poh);
  183. midiOutReset(poh->hmo);
  184. midiOutClose(poh->hmo);
  185. MidiOutFlushQueues(poh);
  186. /* If this was the last output device, shut down precision timer resolution
  187. */
  188. --gcActiveOutputDevices;
  189. if (gcActiveOutputDevices == 0)
  190. {
  191. SetOutputTimerRes(FALSE);
  192. }
  193. return MMSYSERR_NOERROR;
  194. }
  195. /* @func Set the timer resolution
  196. *
  197. * @comm
  198. *
  199. * Set the resolution of the timer callbacks using the <f timeBeginPeriod> and <f timeEndPeriod>
  200. * API's.
  201. *
  202. * If <p fOnOpen> is TRUE, then the timer resolution will be changed to 1 millisecond. Otherwise, it
  203. * will be set to its previous value.
  204. *
  205. */
  206. VOID PASCAL
  207. SetOutputTimerRes(
  208. BOOL fOnOpen) /* @parm TRUE if we are supposed to raise precision */
  209. {
  210. MMRESULT mmr;
  211. if (fOnOpen)
  212. {
  213. mmr = timeBeginPeriod(gTimeCaps.wPeriodMin);
  214. if (MMSYSERR_NOERROR != mmr)
  215. {
  216. DPF(1, "Could not timeBeginPeriod() -> %u", (UINT)mmr);
  217. }
  218. }
  219. else
  220. {
  221. mmr = timeEndPeriod(gTimeCaps.wPeriodMin);
  222. if (MMSYSERR_NOERROR != mmr)
  223. {
  224. DPF(1, "Could not timeEndPeriod() -> %u", (UINT)mmr);
  225. }
  226. }
  227. }
  228. /* @func Submit a buffer to a device for playback
  229. *
  230. * @rdesc Returns one of the following
  231. * @flag MMSYSERR_NOERROR | If the buffer was successfully queued
  232. * @flag MMSYSERR_INVALPARAM | If the buffer is incorrectly packed or the handle is invalid
  233. * @flag MMSYSERR_NOMEM | If there was no memory available to queue the events
  234. *
  235. * @comm
  236. *
  237. * This function is thunked to DMusic32.
  238. *
  239. * The DirectMusic port interface specifies that a submitted buffer not be
  240. * kept by the system past the time of the call which submits it.
  241. *
  242. * This routine parses the buffer into individual events and copies them into
  243. * local event structures, which are then queued onto the handle of the device
  244. * specified by <p h>. The queue for each device is kept in time-increasing order.
  245. * All local event memory is page-locked (see alloc.c) so that it can be accessed
  246. * at interrupt time.
  247. *
  248. * The time stamps in the buffer are millisecond resolution and are relative to
  249. * the absolute time <p msStartTime>.
  250. *
  251. */
  252. MMRESULT WINAPI
  253. MidiOutSubmitPlaybackBuffer(
  254. HANDLE h, /* @parm The handle of the device to queue these events for */
  255. LPBYTE lpBuffer, /* @parm A pointer to the buffer as packed by the IDirectMusicBuffer interface */
  256. DWORD cbBuffer, /* @parm The number of bytes of data in the buffer */
  257. DWORD msStartTime, /* @parm The starting time of the buffer in absolute time */
  258. DWORD rtStartTimeLow, /* @parm Low DWORD of starting reference time */
  259. DWORD rtStartTimeHigh) /* @parm High DWORD of starting reference time */
  260. {
  261. NPOPENHANDLEINSTANCE pohi;
  262. NPOPENHANDLE poh;
  263. LPDMEVENT lpEventHdr;
  264. DWORD cbEvent;
  265. DWORD msTime;
  266. LPEVENT pPrev;
  267. LPEVENT pCurr;
  268. LPEVENT pNew;
  269. WORD wCSID;
  270. MMRESULT mmr;
  271. LPMIDIHDR lpmh;
  272. QUADWORD rtStartTime;
  273. QUADWORD rtTime;
  274. #ifdef DUMP_EVERY_BUFFER
  275. UINT idx;
  276. LPDWORD lpdw;
  277. #endif //DUMP_EVERY_BUFFER
  278. rtStartTime.dwLow = rtStartTimeLow;
  279. rtStartTime.dwHigh = rtStartTimeHigh;
  280. DPF(2, "Buffer @ %08lX msStartTime %lu", (DWORD)lpBuffer, (DWORD)msStartTime);
  281. DPF(2, "At the tone the time will be... %lu <BEEP>", (DWORD)timeGetTime());
  282. #ifdef DUMP_EVERY_BUFFER
  283. cbEvent = cbBuffer & 0xFFFFFFF0;
  284. lpdw = (LPDWORD)lpBuffer;
  285. for (idx = 0; idx < cbEvent; idx += 16) {
  286. DPF(3, "%04X: %08lX %08lX %08lX %08lX",
  287. (UINT)idx,
  288. lpdw[0],
  289. lpdw[1],
  290. lpdw[2],
  291. lpdw[3]);
  292. lpdw += 4;
  293. }
  294. cbEvent = cbBuffer - (cbBuffer & 0xFFFFFFF0);
  295. if (cbEvent >= 12) {
  296. DPF(3, "%04x: %08lX %08lX %08lX",
  297. (UINT)idx, lpdw[0], lpdw[1], lpdw[2]);
  298. } else if (cbEvent >= 8) {
  299. DPF(3, "%04x: %08lX %08lX",
  300. (UINT)idx, lpdw[0], lpdw[1]);
  301. } else if (cbEvent >= 8) {
  302. DPF(3, "%04x: %08lX",
  303. (UINT)idx, lpdw[0]);
  304. }
  305. #endif // DUMP_EVERY_BUFFER
  306. if (!IsValidHandle(h, VA_F_OUTPUT, &pohi))
  307. {
  308. return MMSYSERR_INVALHANDLE;
  309. }
  310. /* Get the handle and lock its list
  311. */
  312. poh = pohi->pHandle;
  313. /* Dequeue and free all completed events on this handle
  314. */
  315. FreeDoneHandleEvents(poh, FALSE);
  316. wCSID = EnterCriticalSection(&poh->wCritSect, CS_BLOCKING);
  317. assert(wCSID);
  318. /* Get the time of the first event and position ourselves in the list
  319. */
  320. if (0 == poh->qPlay.cEle)
  321. {
  322. pPrev = NULL;
  323. pCurr = NULL;
  324. }
  325. else if (!QuadwordLT(rtStartTime, poh->qPlay.pTail->rtTime))
  326. {
  327. pPrev = poh->qPlay.pTail;
  328. pCurr = NULL;
  329. }
  330. else
  331. {
  332. pPrev = NULL;
  333. pCurr = poh->qPlay.pHead;
  334. }
  335. /* Walk the buffer and add the events to the handle's queue
  336. */
  337. while (cbBuffer)
  338. {
  339. if (cbBuffer < sizeof(DMEVENT))
  340. {
  341. return MMSYSERR_INVALPARAM;
  342. }
  343. lpEventHdr = (LPDMEVENT)lpBuffer;
  344. cbEvent = DMEVENT_SIZE(lpEventHdr->cbEvent);
  345. DPF(2, "cbEvent now %u", (UINT)cbEvent);
  346. if (cbEvent > cbBuffer)
  347. {
  348. DPF(0, "Event past end of buffer");
  349. return MMSYSERR_INVALPARAM;
  350. }
  351. lpBuffer += cbEvent;
  352. cbBuffer -= cbEvent;
  353. /* We only play events on channel group 1 (0 is broadcast, so we
  354. * play that as well).
  355. */
  356. if (lpEventHdr->dwChannelGroup > 1)
  357. {
  358. continue;
  359. }
  360. // Time here is in 100ns for queue sorting
  361. //
  362. QuadwordAdd(rtStartTime, lpEventHdr->rtDelta, &rtTime);
  363. // Also need msTime for scheduling
  364. //
  365. msTime = msStartTime + QuadwordDiv(lpEventHdr->rtDelta, REFTIME_TO_MS);
  366. // BUGBUG: >64k??
  367. //
  368. DPF(2, "Schedule event %02X%02X%02X%02X at %lu",
  369. (BYTE)lpEventHdr->abEvent[0],
  370. (BYTE)lpEventHdr->abEvent[1],
  371. (BYTE)lpEventHdr->abEvent[2],
  372. (BYTE)lpEventHdr->abEvent[3],
  373. msTime);
  374. if (lpEventHdr->cbEvent <= sizeof(DWORD))
  375. {
  376. pNew = AllocEvent(msTime, rtTime, (WORD)lpEventHdr->cbEvent);
  377. if (!pNew)
  378. {
  379. return MMSYSERR_NOMEM;
  380. }
  381. hmemcpy(pNew->abEvent, lpEventHdr->abEvent, lpEventHdr->cbEvent);
  382. }
  383. else
  384. {
  385. pNew = AllocEvent(msTime, rtTime, (WORD)(lpEventHdr->cbEvent + sizeof(MIDIHDR)));
  386. if (!pNew)
  387. {
  388. return MMSYSERR_NOMEM;
  389. }
  390. pNew->wFlags |= EVENT_F_MIDIHDR;
  391. lpmh = (LPMIDIHDR)&pNew->abEvent;
  392. lpmh->lpData = (LPSTR)(lpmh + 1);
  393. lpmh->dwBufferLength = lpEventHdr->cbEvent;
  394. lpmh->dwUser = 0; /* Flag if MMSYSTEM owns this buffer */
  395. lpmh->dwFlags = 0;
  396. hmemcpy(lpmh->lpData, lpEventHdr->abEvent, lpEventHdr->cbEvent);
  397. mmr = midiOutPrepareHeader(poh->hmo, lpmh, sizeof(MIDIHDR));
  398. if (mmr)
  399. {
  400. DPF(2, "midiOutPrepareHeader %u", mmr);
  401. FreeEvent(pNew);
  402. return mmr;
  403. }
  404. }
  405. while (pCurr)
  406. {
  407. if (QuadwordLT(rtTime, pCurr->rtTime))
  408. {
  409. break;
  410. }
  411. pPrev = pCurr;
  412. pCurr = pCurr->lpNext;
  413. }
  414. if (pPrev)
  415. {
  416. pPrev->lpNext = pNew;
  417. }
  418. else
  419. {
  420. poh->qPlay.pHead = pNew;
  421. }
  422. pNew->lpNext = pCurr;
  423. if (NULL == pCurr)
  424. {
  425. poh->qPlay.pTail = pNew;
  426. }
  427. pPrev = pNew;
  428. pCurr = pNew->lpNext;
  429. ++poh->qPlay.cEle;
  430. AssertQueueValid(&poh->qPlay);
  431. }
  432. LeaveCriticalSection(&poh->wCritSect);
  433. SetNextTimer();
  434. return MMSYSERR_NOERROR;
  435. }
  436. /* @func VOID PASCAL | FreeDoneHandleEvents | Free events that have already been played, but are still sitting in the done queue
  437. * on this handle.
  438. *
  439. * @comm
  440. *
  441. * If fClosing is TRUE, then the events will be free'd regardless of whether they are marked as completed.
  442. *
  443. */
  444. typedef struct {
  445. NPOPENHANDLE poh;
  446. BOOL fClosing;
  447. } ISEVENTDONEPARMS, FAR *LPISEVENTDONEPARMS;
  448. VOID PASCAL
  449. FreeDoneHandleEvents(
  450. NPOPENHANDLE poh, /* @parm What handle? */
  451. BOOL fClosing) /* @parm TRUE if the device is being closed. */
  452. {
  453. ISEVENTDONEPARMS iedp;
  454. WORD wCSID;
  455. iedp.poh = poh;
  456. iedp.fClosing = fClosing;
  457. wCSID = EnterCriticalSection(&poh->wCritSect, CS_BLOCKING);
  458. assert(wCSID);
  459. QueueFilter(&poh->qDone, (DWORD)(LPVOID)&iedp, IsEventDone);
  460. LeaveCriticalSection(&poh->wCritSect);
  461. }
  462. /* @func
  463. *
  464. * @comm
  465. */
  466. int PASCAL
  467. IsEventDone(
  468. LPEVENT pEvent,
  469. DWORD dwInstance)
  470. {
  471. LPISEVENTDONEPARMS piedp = (LPISEVENTDONEPARMS)dwInstance;
  472. MMRESULT mmr;
  473. if (piedp->fClosing ||
  474. pEvent->cbEvent <= sizeof(DWORD) ||
  475. ((LPMIDIHDR)(&pEvent->abEvent[0]))->dwUser == 0)
  476. {
  477. /* Ok to free this event
  478. */
  479. if (pEvent->cbEvent > sizeof(DWORD))
  480. {
  481. mmr = midiOutUnprepareHeader(piedp->poh->hmo, (LPMIDIHDR)(&pEvent->abEvent[0]), sizeof(MIDIHDR));
  482. if (mmr)
  483. {
  484. DPF(0, "FreeOldEvents: midiOutUnprepareHeader returned %u", (UINT)mmr);
  485. }
  486. }
  487. FreeEvent(pEvent);
  488. return QUEUE_FILTER_REMOVE;
  489. }
  490. return QUEUE_FILTER_KEEP;
  491. }
  492. /* @func Thru the given message on the given output port
  493. *
  494. * @comm
  495. *
  496. */
  497. VOID PASCAL
  498. MidiOutThru(
  499. NPOPENHANDLEINSTANCE pohi,
  500. DWORD dwMessage)
  501. {
  502. NPOPENHANDLE poh = pohi->pHandle;
  503. MMRESULT mmr;
  504. /* !!! Verify that VMM will not interrupt a timer callback with another event
  505. */
  506. mmr = midiOutShortMsg(poh->hmo, dwMessage);
  507. if (mmr)
  508. {
  509. DPF(0, "Thru: midiOutShortMsg() -> %d", mmr);
  510. }
  511. }
  512. /* @func Set the timer to schedule the next pending event
  513. *
  514. * @comm
  515. *
  516. * Walk the list of output handles and look at the first scheduled event on each. Save the time
  517. * of the nearest event. If there is such an event, schedule a timer callback at that time to call
  518. * <f RunTimer>; otherwise, schedule no callback.
  519. *
  520. * Any pending timer callback will be killed before the new callback is scheduled.
  521. */
  522. VOID
  523. SetNextTimer(VOID)
  524. {
  525. WORD wIntStat;
  526. NPLINKNODE npLink;
  527. NPOPENHANDLE poh;
  528. DWORD dwLowTime;
  529. BOOL fNeedTimer;
  530. DWORD dwNow;
  531. LONG lWhen;
  532. UINT uWhen;
  533. /* We actually need to disable interrupts here as opposed to just entering a critical section
  534. * because we don't want the timer callback to fire.
  535. */
  536. wIntStat = DisableInterrupts();
  537. /* BUGBUG: wrap
  538. */
  539. fNeedTimer = FALSE;
  540. dwLowTime = (DWORD)(0xFFFFFFFFL);
  541. for (npLink = gOpenHandleList; npLink; npLink = npLink->pNext)
  542. {
  543. poh = (NPOPENHANDLE)npLink;
  544. if (0 == poh->qPlay.cEle)
  545. {
  546. continue;
  547. }
  548. assert(poh->qPlay.pHead);
  549. if (poh->qPlay.pHead->msTime < dwLowTime)
  550. {
  551. fNeedTimer = TRUE;
  552. dwLowTime = poh->qPlay.pHead->msTime;
  553. }
  554. }
  555. if (fNeedTimer)
  556. {
  557. if ((!gbTimerRunning) || dwLowTime < gdwTimerDue)
  558. {
  559. /* We need to set the timer. Kill it now so there's no chance of it
  560. * firing before being killed
  561. */
  562. if (gbTimerRunning)
  563. {
  564. timeKillEvent(guTimerID);
  565. gbTimerRunning = FALSE;
  566. }
  567. }
  568. else
  569. {
  570. fNeedTimer = FALSE;
  571. }
  572. }
  573. RestoreInterrupts(wIntStat);
  574. if (fNeedTimer)
  575. {
  576. /* Guaranteed that current timer expired or dead. Reschedule.
  577. */
  578. dwNow = timeGetTime();
  579. gbTimerRunning = TRUE;
  580. gdwTimerDue = dwLowTime;
  581. lWhen = gdwTimerDue - dwNow;
  582. if (lWhen < (LONG)gTimeCaps.wPeriodMin)
  583. {
  584. uWhen = gTimeCaps.wPeriodMin;
  585. }
  586. else if (lWhen > (LONG)gTimeCaps.wPeriodMax)
  587. {
  588. uWhen = gTimeCaps.wPeriodMax;
  589. }
  590. else
  591. {
  592. uWhen = (UINT)lWhen;
  593. }
  594. DPF(2, "SetNextTimer: Now %lu, setting timer for %u ms from now. dwLowTime %lu",
  595. (DWORD)dwNow, (UINT)uWhen, (DWORD)dwLowTime);
  596. guTimerID = timeSetEvent(uWhen,
  597. gTimeCaps.wPeriodMin,
  598. RunTimer,
  599. NULL,
  600. TIME_ONESHOT);
  601. if (0 == guTimerID)
  602. {
  603. gbTimerRunning = FALSE;
  604. }
  605. }
  606. else
  607. {
  608. DPF(2, "SetNextTimer: Timer cancelled; no pending events.");
  609. }
  610. }
  611. /* @func Process a high precision timer callback
  612. *
  613. * @comm
  614. *
  615. * This is a standard callback for the <f timeSetEvent> API.
  616. *
  617. * Walk the list of open output handles. For each handle, look at the event queue. Play all
  618. * the events that are due.
  619. *
  620. * Events are pulled from the qPlay queue on each handle. This queue (as well as the qDone queue) are
  621. * protected by the handle's critical section. If we cannot get the critical section, then the events
  622. * that may be due on that handle will not be played.
  623. *
  624. * If we do get the critical section and play events, then the events will be moved to the qDone
  625. * queue, where they will later be returned to the free list.
  626. *
  627. * This intermediate step is needed because we cannot call <f FreeEvent> at interrupt time. We cannot
  628. * just protect the free list with a critical section, because we cannot afford to fail getting the
  629. * critical section. If we did, we would lost the memory for the event we were about to free.
  630. *
  631. */
  632. VOID CALLBACK __loadds
  633. RunTimer(
  634. UINT uTimerID, /* @parm The ID of the timer which fired */
  635. UINT wMsg, /* @parm The type of callback (unused) */
  636. DWORD dwUser, /* @parm User instance data */
  637. DWORD dw1, /* @parm Message specific data (unused) */
  638. DWORD dw2) /* @parm Message specific data (unused) */
  639. {
  640. NPLINKNODE npLink;
  641. NPOPENHANDLE poh;
  642. WORD wCSID;
  643. WORD wIntStat;
  644. DWORD msNow;
  645. DWORD msFence;
  646. LPEVENT pEvent;
  647. DWORD dwEvent;
  648. MMRESULT mmr;
  649. /* Walk the event queues and send out pending events.
  650. */
  651. msNow = timeGetTime();
  652. msFence = msNow + MS_TIMER_SLOP;
  653. for (npLink = gOpenHandleList; npLink; npLink = npLink->pNext)
  654. {
  655. poh = (NPOPENHANDLE)npLink;
  656. /* If we can't get the critical section, don't sweat it - just reschedule
  657. */
  658. wCSID = EnterCriticalSection(&poh->wCritSect, CS_NONBLOCKING);
  659. if (!wCSID)
  660. {
  661. DPF(1, "Timer: Could not get critical section for '%04x'; next time.", (UINT)poh);
  662. continue;
  663. }
  664. /* Now safe against foreground messing with this handle
  665. */
  666. for(;;)
  667. {
  668. pEvent = poh->qPlay.pHead;
  669. if (NULL == pEvent || pEvent->msTime > msFence)
  670. {
  671. break;
  672. }
  673. if (pEvent->msTime > msNow)
  674. {
  675. DPF(2, "Late!");
  676. }
  677. QueueRemoveFromFront(&poh->qPlay);
  678. if (pEvent->cbEvent <= 4)
  679. {
  680. dwEvent = (pEvent->abEvent[0]) |
  681. (((DWORD)pEvent->abEvent[1]) << 8) |
  682. (((DWORD)pEvent->abEvent[2]) << 16);
  683. mmr = midiOutShortMsg(poh->hmo, dwEvent);
  684. if (mmr)
  685. {
  686. DPF(0, "midiOutShortMsg(%04X,%08lX) -> %u",
  687. (UINT)poh->hmo,
  688. dwEvent,
  689. (UINT)mmr);
  690. }
  691. else
  692. {
  693. DPF(2, "midiOutShortMsg(%04X,%08lX) ",
  694. (UINT)poh->hmo,
  695. dwEvent);
  696. }
  697. }
  698. else
  699. {
  700. /* Data contains an already prepared long message.
  701. * DON'T leave interrupts disabled here! Most legacy MIDI drivers
  702. * do this synchronously.
  703. *
  704. */
  705. RestoreInterrupts(wIntStat);
  706. ((LPMIDIHDR)(&pEvent->abEvent[0]))->dwUser = 1;
  707. mmr = midiOutLongMsg(poh->hmo,
  708. (LPMIDIHDR)(&pEvent->abEvent[0]),
  709. sizeof(MIDIHDR));
  710. if (mmr)
  711. {
  712. DPF(0, "midiOutLongMsg(%04X, %08lX, %04X) -> %u\n",
  713. (UINT)poh->hmo,
  714. (DWORD)(LPMIDIHDR)(&pEvent->abEvent[0]),
  715. (UINT)sizeof(MIDIHDR),
  716. (UINT)mmr);
  717. }
  718. DisableInterrupts();
  719. }
  720. /* We're done with this event; back to the free list with ya!
  721. *
  722. * Since we can't protect the free list with a critical section (what
  723. * would we do if getting the critical section failed here?) we keep
  724. * a temporary free list in the handle. Free events are moved from
  725. * the handle to the master free list in user time.
  726. */
  727. QueueAppend(&poh->qDone, pEvent);
  728. }
  729. LeaveCriticalSection(&poh->wCritSect);
  730. }
  731. /* Now reschedule ourselves if needed.
  732. */
  733. gbTimerRunning = FALSE;
  734. SetNextTimer();
  735. }
  736. VOID CALLBACK _loadds
  737. midiOutProc(
  738. HMIDIOUT hMidiIn,
  739. UINT wMsg,
  740. DWORD dwInstance,
  741. DWORD dwParam1,
  742. DWORD dwParam2)
  743. {
  744. LPOPENHANDLE poh = (LPOPENHANDLE)dwInstance;
  745. switch(wMsg)
  746. {
  747. case MOM_DONE:
  748. /* Buffer is already queued for free on the device's queue. dwUser flags if it
  749. * is still in use by MMSYSTEM/driver.
  750. */
  751. ((LPMIDIHDR)dwParam1)->dwUser = 0;
  752. break;
  753. }
  754. }
  755. /* @func Return all memory from all queues to the free event list.
  756. *
  757. * @comm
  758. *
  759. */
  760. STATIC VOID NEAR PASCAL
  761. MidiOutFlushQueues(
  762. NPOPENHANDLE poh)
  763. {
  764. WORD wCSID;
  765. wCSID = EnterCriticalSection(&poh->wCritSect, CS_BLOCKING);
  766. assert(wCSID);
  767. FreeAllQueueEvents(&poh->qPlay);
  768. FreeAllQueueEvents(&poh->qDone);
  769. LeaveCriticalSection(&poh->wCritSect);
  770. }
  771. /* @func Send all pending messages (other than note on) in preperation
  772. * to close the port.
  773. *
  774. * @comm
  775. *
  776. */
  777. STATIC VOID NEAR PASCAL
  778. MidiOutSendAllNow(
  779. NPOPENHANDLE poh)
  780. {
  781. LPEVENT pEvent;
  782. DWORD dwEvent;
  783. MMRESULT mmr;
  784. WORD wCSID;
  785. wCSID = EnterCriticalSection(&poh->wCritSect, CS_BLOCKING);
  786. assert(wCSID);
  787. /* Now safe against foreground messing with this handle
  788. */
  789. for(;;)
  790. {
  791. pEvent = poh->qPlay.pHead;
  792. if (NULL == pEvent)
  793. {
  794. DPF(2,"MidiOutSendAllNow: No queued Messages.");
  795. break;
  796. }
  797. QueueRemoveFromFront(&poh->qPlay);
  798. if (pEvent->cbEvent <= 4)
  799. {
  800. dwEvent = (pEvent->abEvent[0]) |
  801. (((DWORD)pEvent->abEvent[1]) << 8) |
  802. (((DWORD)pEvent->abEvent[2]) << 16);
  803. // We aren't going to process MIDI_NOTE_ON with a
  804. // velocity of zero
  805. //There are two kinds of short messages, Two Byte and
  806. //Three Byte.. They pack differently in MIDI Short message
  807. //If the first bit if the High Byte of the Low Word is SET we are
  808. //looking at a 3 byte message.
  809. //MIDI status messages begin with a
  810. //set bit, and every other part of the same message starts with an
  811. //unset bit.
  812. if (HIBYTE(LOWORD(dwEvent) & 0x80) )
  813. {
  814. //This is a THREE BYTE message
  815. // note on with a non-zero velocity is skipped
  816. if ( (HIBYTE(LOWORD(dwEvent)) & MIDI_NOTE_ON) && (LOBYTE(LOWORD(dwEvent)) != 0 ))
  817. {
  818. QueueAppend(&poh->qDone, pEvent);
  819. continue;
  820. }
  821. }
  822. else
  823. {
  824. //This is a THREE BYTE Message
  825. // Any note-on is skiped
  826. if (LOBYTE(LOWORD(dwEvent)) & MIDI_NOTE_ON)
  827. {
  828. QueueAppend(&poh->qDone, pEvent);
  829. continue;
  830. }
  831. }
  832. mmr = midiOutShortMsg(poh->hmo, dwEvent);
  833. if (mmr)
  834. {
  835. DPF(0, "midiOutShortMsg(%04X,%08lX) -> %u",
  836. (UINT)poh->hmo,
  837. dwEvent,
  838. (UINT)mmr);
  839. }
  840. else
  841. {
  842. DPF(2, "midiOutShortMsg(%04X,%08lX) ",
  843. (UINT)poh->hmo,
  844. dwEvent);
  845. }
  846. }
  847. else
  848. {
  849. /* Data contains an already prepared long message.
  850. * DON'T leave interrupts disabled here! Most legacy MIDI drivers
  851. * do this synchronously.
  852. *
  853. */
  854. ((LPMIDIHDR)(&pEvent->abEvent[0]))->dwUser = 1;
  855. mmr = midiOutLongMsg(poh->hmo,
  856. (LPMIDIHDR)(&pEvent->abEvent[0]),
  857. sizeof(MIDIHDR));
  858. if (mmr)
  859. {
  860. DPF(0, "midiOutLongMsg(%04X, %08lX, %04X) -> %u\n",
  861. (UINT)poh->hmo,
  862. (DWORD)(LPMIDIHDR)(&pEvent->abEvent[0]),
  863. (UINT)sizeof(MIDIHDR),
  864. (UINT)mmr);
  865. }
  866. }
  867. /* We're done with this event; back to the free list with ya!
  868. *
  869. * Since we can't protect the free list with a critical section (what
  870. * would we do if getting the critical section failed here?) we keep
  871. * a temporary free list in the handle. Free events are moved from
  872. * the handle to the master free list in user time.
  873. */
  874. QueueAppend(&poh->qDone, pEvent);
  875. }
  876. LeaveCriticalSection(&poh->wCritSect);
  877. return;
  878. }