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

2219 lines
61 KiB

  1. /*****************************************************************************
  2. midiemu.c
  3. MIDI support -- routines for stream emulation
  4. Copyright (c) 1990-1999 Microsoft Corporation
  5. *****************************************************************************/
  6. #define INCL_WINMM
  7. #include "winmmi.h"
  8. #include "muldiv32.h"
  9. #define NUM_NOTES (128)
  10. #define NUM_CHANNELS (16)
  11. #define MEMU_CB_NOTEON (NUM_CHANNELS*NUM_NOTES/2) // 16 chan * 128 notes (4 bits/note)
  12. #define MAX_NOTES_ON (0xF)
  13. #define TIMER_OFF (0)
  14. PMIDIEMU gpEmuList = NULL;
  15. UINT guMIDIInTimer = 0;
  16. UINT guMIDITimerID = TIMER_OFF;
  17. BOOL gfMinPeriod = FALSE;
  18. UINT guMIDIPeriodMin;
  19. STATIC HMIDI FAR PASCAL mseIDtoHMidi(
  20. PMIDIEMU pme,
  21. DWORD dwStreamID);
  22. MMRESULT FAR PASCAL mseOpen(
  23. PDWORD_PTR lpdwUser,
  24. LPMIDIOPENDESC lpmod,
  25. DWORD fdwOpen);
  26. MMRESULT FAR PASCAL mseClose(
  27. PMIDIEMU pme);
  28. MMRESULT FAR PASCAL mseProperty(
  29. PMIDIEMU pme,
  30. LPBYTE lpbProp,
  31. DWORD fdwProp);
  32. MMRESULT FAR PASCAL mseGetPosition(
  33. PMIDIEMU pme,
  34. LPMMTIME lpmmt);
  35. MMRESULT FAR PASCAL mseGetVolume(
  36. PMIDIEMU pme,
  37. LPDWORD lpdwVolume);
  38. MMRESULT FAR PASCAL mseSetVolume(
  39. PMIDIEMU pme,
  40. DWORD dwVolume);
  41. MMRESULT FAR PASCAL mseOutStop(
  42. PMIDIEMU pme);
  43. MMRESULT FAR PASCAL mseOutReset(
  44. PMIDIEMU pme);
  45. MMRESULT FAR PASCAL mseOutPause(
  46. PMIDIEMU pme);
  47. MMRESULT FAR PASCAL mseOutRestart(
  48. PMIDIEMU pme,
  49. DWORD msTime,
  50. DWORD tkTime);
  51. MMRESULT FAR PASCAL mseOutCachePatches(
  52. PMIDIEMU pme,
  53. UINT uBank,
  54. LPWORD pwpa,
  55. UINT fuCache);
  56. MMRESULT FAR PASCAL mseOutCacheDrumPatches(
  57. PMIDIEMU pme,
  58. UINT uPatch,
  59. LPWORD pwkya,
  60. UINT fuCache);
  61. DWORD FAR PASCAL mseOutBroadcast(
  62. PMIDIEMU pme,
  63. UINT msg,
  64. DWORD_PTR dwParam1,
  65. DWORD_PTR dwParam2);
  66. DWORD FAR PASCAL mseTimebase(
  67. PCLOCK pclock);
  68. #ifndef WIN32
  69. #pragma alloc_text(FIXMIDI, mseIDtoHMidi)
  70. #pragma alloc_text(FIXMIDI, mseMessage)
  71. #pragma alloc_text(FIXMIDI, mseOutReset)
  72. #pragma alloc_text(FIXMIDI, midiOutScheduleNextEvent)
  73. #pragma alloc_text(FIXMIDI, midiOutPlayNextPolyEvent)
  74. #pragma alloc_text(FIXMIDI, midiOutDequeueAndCallback)
  75. #pragma alloc_text(FIXMIDI, midiOutTimerTick)
  76. #pragma alloc_text(FIXMIDI, midiOutCallback)
  77. #pragma alloc_text(FIXMIDI, midiOutSetClockRate)
  78. #pragma alloc_text(INIT,midiEmulatorInit)
  79. #pragma alloc_text(FIXMIDI, mseTimebase)
  80. #endif
  81. /****************************************************************************/
  82. /****************************************************************************/
  83. INLINE LONG PDEVLOCK(PMIDIEMU pdev)
  84. {
  85. LONG lTemp;
  86. lTemp = InterlockedIncrement(&(pdev->lLockCount));
  87. EnterCriticalSection(&(pdev->CritSec));
  88. return lTemp;
  89. }
  90. INLINE LONG PDEVUNLOCK(PMIDIEMU pdev)
  91. {
  92. LONG lTemp;
  93. lTemp = InterlockedDecrement(&(pdev->lLockCount));
  94. LeaveCriticalSection(&(pdev->CritSec));
  95. return lTemp;
  96. }
  97. /****************************************************************************/
  98. /****************************************************************************/
  99. DWORD FAR PASCAL mseMessage(
  100. UINT msg,
  101. DWORD_PTR dwUser,
  102. DWORD_PTR dwParam1,
  103. DWORD_PTR dwParam2)
  104. {
  105. MMRESULT mmr = MMSYSERR_NOERROR;
  106. PMIDIEMU pme = (PMIDIEMU)dwUser;
  107. switch(msg)
  108. {
  109. case MODM_OPEN:
  110. mmr = mseOpen((PDWORD_PTR)dwUser, (LPMIDIOPENDESC)dwParam1, (DWORD)dwParam2);
  111. break;
  112. case MODM_CLOSE:
  113. mmr = mseClose(pme);
  114. break;
  115. case MODM_GETVOLUME:
  116. mmr = mseGetVolume(pme, (LPDWORD)dwParam1);
  117. break;
  118. case MODM_SETVOLUME:
  119. mmr = mseSetVolume(pme, (DWORD)dwParam1);
  120. break;
  121. case MODM_PREPARE:
  122. case MODM_UNPREPARE:
  123. mmr = MMSYSERR_NOTSUPPORTED;
  124. break;
  125. case MODM_DATA:
  126. //#pragma FIXMSG("How to route async short messages to other stream-ids?")
  127. if (!(dwParam1 & 0x80))
  128. mmr = MIDIERR_BADOPENMODE;
  129. else
  130. mmr = midiOutShortMsg((HMIDIOUT)pme->rIds[0].hMidi, (DWORD)dwParam1);
  131. break;
  132. case MODM_RESET:
  133. mmr = mseOutReset(pme);
  134. break;
  135. case MODM_STOP:
  136. mmr = mseOutStop(pme);
  137. break;
  138. case MODM_CACHEPATCHES:
  139. mmr = mseOutCachePatches(pme, HIWORD(dwParam2), (LPWORD)dwParam1, LOWORD(dwParam2));
  140. break;
  141. case MODM_CACHEDRUMPATCHES:
  142. mmr = mseOutCacheDrumPatches(pme, HIWORD(dwParam2), (LPWORD)dwParam1, LOWORD(dwParam2));
  143. break;
  144. case MODM_PAUSE:
  145. mmr = mseOutPause(pme);
  146. break;
  147. case MODM_RESTART:
  148. mmr = mseOutRestart(pme, (DWORD)dwParam1, (DWORD)dwParam2);
  149. break;
  150. case MODM_STRMDATA:
  151. mmr = mseOutSend(pme, (LPMIDIHDR)dwParam1, (UINT)dwParam2);
  152. break;
  153. case MODM_PROPERTIES:
  154. mmr = mseProperty(pme, (LPBYTE)dwParam1, (DWORD)dwParam2);
  155. break;
  156. case MODM_GETPOS:
  157. mmr = mseGetPosition(pme, (LPMMTIME)dwParam1);
  158. break;
  159. default:
  160. if ((msg < DRVM_IOCTL) ||
  161. (msg >= DRVM_IOCTL_LAST) && (msg < DRVM_MAPPER))
  162. {
  163. dprintf1(("Unknown message [%04X] in MIDI emulator", (WORD)msg));
  164. mmr = MMSYSERR_NOTSUPPORTED;
  165. }
  166. else
  167. mmr = mseOutBroadcast(pme, msg, dwParam1, dwParam2);
  168. }
  169. return mmr;
  170. }
  171. MMRESULT FAR PASCAL mseOpen(
  172. PDWORD_PTR lpdwUser,
  173. LPMIDIOPENDESC lpmod,
  174. DWORD fdwOpen)
  175. {
  176. MMRESULT mmrc = MMSYSERR_NOERROR;
  177. DWORD cbHandle;
  178. PMIDIEMU pme = NULL;
  179. UINT idx;
  180. mmrc = MMSYSERR_NOMEM;
  181. cbHandle = sizeof(MIDIEMU) + lpmod->cIds * ELESIZE(MIDIEMU, rIds[0]);
  182. if (cbHandle >= 65536L)
  183. {
  184. dprintf1(("mSEO: cbHandle >= 64K!"));
  185. goto mseOpen_Cleanup;
  186. }
  187. if (NULL == (pme = (PMIDIEMU)winmmAlloc(cbHandle)))
  188. {
  189. dprintf1(("mSEO: !winmmAlloc(cbHandle)"));
  190. goto mseOpen_Cleanup;
  191. }
  192. if (NULL == (pme->rbNoteOn = winmmAlloc(MEMU_CB_NOTEON)))
  193. {
  194. dprintf1(("mSEO: !GlobalAlloc(MEMU_CB_NOTEON"));
  195. goto mseOpen_Cleanup;
  196. }
  197. pme->fdwDev |= MDV_F_LOCKED;
  198. pme->hStream = (HMIDISTRM)lpmod->hMidi;
  199. pme->dwTimeDiv = DEFAULT_TIMEDIV;
  200. pme->dwTempo = DEFAULT_TEMPO;
  201. pme->dwCallback = lpmod->dwCallback;
  202. pme->dwFlags = fdwOpen;
  203. pme->dwInstance = lpmod->dwInstance;
  204. pme->dwPolyMsgState = PM_STATE_PAUSED;
  205. pme->chMidi = (UINT)lpmod->cIds;
  206. pme->dwSavedState = PM_STATE_STOPPED;
  207. pme->tkPlayed = 0;
  208. pme->lLockCount = -1;
  209. pme->dwSignature = MSE_SIGNATURE;
  210. for (idx = 0; idx < pme->chMidi; idx++)
  211. {
  212. pme->rIds[idx].dwStreamID = lpmod->rgIds[idx].dwStreamID;
  213. mmrc = midiOutOpen((LPHMIDIOUT)&pme->rIds[idx].hMidi,
  214. lpmod->rgIds[idx].uDeviceID,
  215. (DWORD_PTR)midiOutCallback,
  216. 0L,
  217. CALLBACK_FUNCTION);
  218. if (MMSYSERR_NOERROR != mmrc)
  219. goto mseOpen_Cleanup;
  220. }
  221. if (!mmInitializeCriticalSection(&pme->CritSec)) {
  222. mmrc = MMSYSERR_NOMEM;
  223. goto mseOpen_Cleanup;
  224. }
  225. clockInit(&pme->clock, 0, 0, mseTimebase);
  226. dprintf2(("midiOutOpen: midiOutSetClockRate()"));
  227. midiOutSetClockRate(pme, 0);
  228. mseOpen_Cleanup:
  229. if (MMSYSERR_NOERROR != mmrc)
  230. {
  231. if (pme)
  232. {
  233. if (pme->rbNoteOn)
  234. {
  235. winmmFree(pme->rbNoteOn);
  236. }
  237. DeleteCriticalSection(&pme->CritSec);
  238. pme->dwSignature = 0L;
  239. for (idx = 0; idx < pme->chMidi; idx++)
  240. if (NULL != pme->rIds[idx].hMidi)
  241. midiOutClose((HMIDIOUT)pme->rIds[idx].hMidi);
  242. winmmFree(pme);
  243. }
  244. }
  245. else
  246. {
  247. pme->pNext = gpEmuList;
  248. gpEmuList = pme;
  249. *lpdwUser = (DWORD_PTR)pme;
  250. }
  251. return mmrc;
  252. }
  253. MMRESULT FAR PASCAL mseClose(
  254. PMIDIEMU pme)
  255. {
  256. UINT idx;
  257. MMRESULT mmrc;
  258. PMIDIEMU pmePrev;
  259. PMIDIEMU pmeCurr;
  260. #ifdef DEBUG
  261. {
  262. dprintf2(("cEvents %lu", pme->cEvents));
  263. for (idx = 0; idx < MEM_MAX_LATENESS; idx++)
  264. dprintf2(("%5u: %u", idx, pme->auLateness[idx]));
  265. }
  266. #endif
  267. if ((PM_STATE_STOPPED != pme->dwPolyMsgState &&
  268. PM_STATE_PAUSED != pme->dwPolyMsgState &&
  269. PM_STATE_EMPTY != pme->dwPolyMsgState))
  270. {
  271. dprintf1(("mseClose: Started playing again since close query!!!"));
  272. mseOutStop(pme);
  273. }
  274. midiOutAllNotesOff(pme);
  275. for (idx = 0; idx < pme->chMidi; idx++)
  276. {
  277. mmrc = midiOutClose((HMIDIOUT)pme->rIds[idx].hMidi);
  278. if (MMSYSERR_NOERROR != mmrc)
  279. {
  280. dprintf1(( "mseClose: HMIDI %04X returned %u for close", pme->rIds[idx].hMidi, mmrc));
  281. }
  282. }
  283. winmmFree(pme->rbNoteOn);
  284. pmePrev = NULL;
  285. pmeCurr = gpEmuList;
  286. while (pmeCurr)
  287. {
  288. if (pmeCurr == pme)
  289. break;
  290. pmePrev = pmeCurr;
  291. pmeCurr = pmeCurr->pNext;
  292. }
  293. if (pmeCurr)
  294. {
  295. if (pmePrev)
  296. pmePrev->pNext = pmeCurr->pNext;
  297. else
  298. gpEmuList = pmeCurr->pNext;
  299. }
  300. //
  301. // Make sure that we don't have the critical section before
  302. // we try to delete it. Otherwise we will leak critical section
  303. // handles in the kernel.
  304. //
  305. while ( pme->lLockCount >= 0 )
  306. {
  307. PDEVUNLOCK( pme );
  308. }
  309. DeleteCriticalSection(&pme->CritSec);
  310. pme->dwSignature = 0L;
  311. winmmFree(pme);
  312. return MMSYSERR_NOERROR;
  313. }
  314. STATIC HMIDI FAR PASCAL mseIDtoHMidi(
  315. PMIDIEMU pme,
  316. DWORD dwStreamID)
  317. {
  318. UINT idx;
  319. PMIDIEMUSID pmesi;
  320. for (idx = 0, pmesi = pme->rIds; idx < pme->chMidi; idx++, pmesi++)
  321. if (pmesi->dwStreamID == dwStreamID)
  322. return pmesi->hMidi;
  323. return NULL;
  324. }
  325. MMRESULT FAR PASCAL mseProperty(
  326. PMIDIEMU pme,
  327. LPBYTE lppropdata,
  328. DWORD fdwProp)
  329. {
  330. PMIDISTRM pms;
  331. pms = (PMIDISTRM)(pme->hStream);
  332. if ((!(fdwProp&MIDIPROP_SET)) && (!(fdwProp&MIDIPROP_GET)))
  333. return MMSYSERR_INVALPARAM;
  334. V_RPOINTER(lppropdata, sizeof(DWORD), MMSYSERR_INVALPARAM);
  335. if (fdwProp & MIDIPROP_SET)
  336. {
  337. V_RPOINTER(lppropdata, (UINT)(*(LPDWORD)(lppropdata)), MMSYSERR_INVALPARAM);
  338. }
  339. else
  340. {
  341. V_WPOINTER(lppropdata, (UINT)(*(LPDWORD)(lppropdata)), MMSYSERR_INVALPARAM);
  342. }
  343. switch(fdwProp & MIDIPROP_PROPVAL)
  344. {
  345. case MIDIPROP_TIMEDIV:
  346. if (((LPMIDIPROPTIMEDIV)lppropdata)->cbStruct < sizeof(MIDIPROPTIMEDIV))
  347. return MMSYSERR_INVALPARAM;
  348. if (fdwProp & MIDIPROP_GET)
  349. {
  350. ((LPMIDIPROPTIMEDIV)lppropdata)->dwTimeDiv = pme->dwTimeDiv;
  351. return MMSYSERR_NOERROR;
  352. }
  353. if (PM_STATE_STOPPED != pme->dwPolyMsgState &&
  354. PM_STATE_PAUSED != pme->dwPolyMsgState)
  355. return MMSYSERR_INVALPARAM;
  356. pme->dwTimeDiv = ((LPMIDIPROPTIMEDIV)lppropdata)->dwTimeDiv;
  357. dprintf1(( "dwTimeDiv %08lX", pme->dwTimeDiv));
  358. midiOutSetClockRate(pme, 0);
  359. return MMSYSERR_NOERROR;
  360. case MIDIPROP_TEMPO:
  361. if (((LPMIDIPROPTEMPO)lppropdata)->cbStruct < sizeof(MIDIPROPTEMPO))
  362. return MMSYSERR_INVALPARAM;
  363. if (fdwProp & MIDIPROP_GET)
  364. {
  365. ((LPMIDIPROPTEMPO)lppropdata)->dwTempo = pme->dwTempo;
  366. return MMSYSERR_NOERROR;
  367. }
  368. pme->dwTempo = ((LPMIDIPROPTEMPO)lppropdata)->dwTempo;
  369. midiOutSetClockRate(pme, pme->tkPlayed);
  370. return MMSYSERR_NOERROR;
  371. default:
  372. return MMSYSERR_INVALPARAM;
  373. }
  374. }
  375. MMRESULT FAR PASCAL mseGetPosition(
  376. PMIDIEMU pme,
  377. LPMMTIME pmmt)
  378. {
  379. DWORD tkTime;
  380. DWORD dw10Min;
  381. DWORD dw10MinCycle;
  382. DWORD dw1Min;
  383. DWORD dwDropMe;
  384. //
  385. // Figure out position in stream based on emulation.
  386. //
  387. //
  388. // Validate wType parameter and change it if needed.
  389. //
  390. if (pmmt->wType != TIME_TICKS && pmmt->wType != TIME_MS)
  391. {
  392. if (pme->dwTimeDiv & IS_SMPTE)
  393. {
  394. if (pmmt->wType != TIME_SMPTE)
  395. {
  396. pmmt->wType = TIME_MS;
  397. }
  398. }
  399. else
  400. {
  401. if (pmmt->wType != TIME_MIDI)
  402. {
  403. pmmt->wType = TIME_MS;
  404. }
  405. }
  406. }
  407. switch(pmmt->wType)
  408. {
  409. case TIME_TICKS:
  410. //
  411. // We interpret samples to be straight MIDI ticks.
  412. //
  413. tkTime = (DWORD)clockTime(&pme->clock);
  414. pmmt->u.ticks = (((TICKS)tkTime) < 0) ? 0 : tkTime;
  415. break;
  416. case TIME_MIDI:
  417. //
  418. // Song position pointer is number of 1/16th notes we've
  419. // played which we can get from number of ticks played and
  420. // number of 1/4 notes per tick.
  421. //
  422. tkTime = (DWORD)clockTime(&pme->clock);
  423. if (((TICKS)tkTime) < 0)
  424. tkTime = 0;
  425. pmmt->u.midi.songptrpos =
  426. muldiv32(
  427. tkTime,
  428. 4,
  429. TICKS_PER_QN(pme->dwTimeDiv));
  430. break;
  431. case TIME_SMPTE:
  432. tkTime = (DWORD)clockTime(&pme->clock);
  433. if (((TICKS)tkTime) < 0)
  434. tkTime = 0;
  435. pmmt->u.smpte.fps = (BYTE)(-SMPTE_FORMAT(pme->dwTimeDiv));
  436. //
  437. // If this has managed to get set to something bizarre, just
  438. // do normal 30 nondrop.
  439. //
  440. if ((pmmt->u.smpte.fps != SMPTE_24) &&
  441. (pmmt->u.smpte.fps != SMPTE_25) &&
  442. (pmmt->u.smpte.fps != SMPTE_30DROP) &&
  443. (pmmt->u.smpte.fps != SMPTE_30))
  444. {
  445. pmmt->u.smpte.fps = SMPTE_30;
  446. }
  447. switch(pmmt->u.smpte.fps)
  448. {
  449. case SMPTE_24:
  450. pmmt->u.smpte.frame = (BYTE)(tkTime%24);
  451. tkTime /= 24;
  452. break;
  453. case SMPTE_25:
  454. pmmt->u.smpte.frame = (BYTE)(tkTime%25);
  455. tkTime /= 25;
  456. break;
  457. case SMPTE_30DROP:
  458. //
  459. // Calculate drop-frame stuff.
  460. //
  461. // We add 2 frames per 1-minute interval except
  462. // on every 10th minute.
  463. //
  464. dw10Min = tkTime/S30D_FRAMES_PER_10MIN;
  465. dw10MinCycle = tkTime%S30D_FRAMES_PER_10MIN;
  466. dw1Min = (dw10MinCycle < 2
  467. ? 0 :
  468. (dw10MinCycle-2)/S30D_FRAMES_PER_MIN);
  469. dwDropMe = 18*dw10Min + 2*dw1Min;
  470. tkTime += dwDropMe;
  471. //
  472. // !!! Falling through to 30-nondrop case !!!
  473. //
  474. case SMPTE_30:
  475. pmmt->u.smpte.frame = (BYTE)(tkTime%30);
  476. tkTime /= 30;
  477. break;
  478. }
  479. pmmt->u.smpte.sec = (BYTE)(tkTime%60);
  480. tkTime /= 60;
  481. pmmt->u.smpte.min = (BYTE)(tkTime%60);
  482. tkTime /= 60;
  483. pmmt->u.smpte.hour = (BYTE)(tkTime);
  484. break;
  485. case TIME_MS:
  486. //
  487. // Use msTotal + ms since time parms last updated; this
  488. // takes starvation/paused time into account.
  489. //
  490. pmmt->u.ms =
  491. clockMsTime(&pme->clock);
  492. break;
  493. default:
  494. dprintf1(( "midiOutGetPosition: unexpected wType!!!"));
  495. return MMSYSERR_INVALPARAM;
  496. }
  497. return MMSYSERR_NOERROR;
  498. }
  499. MMRESULT FAR PASCAL mseGetVolume(
  500. PMIDIEMU pme,
  501. LPDWORD lpdwVolume)
  502. {
  503. MMRESULT mmr = MMSYSERR_NOTSUPPORTED;
  504. UINT idx;
  505. // Walk the device list underneath us until someone knows the volume
  506. //
  507. for (idx = 0; idx < pme->chMidi; ++idx)
  508. if (MMSYSERR_NOERROR ==
  509. (midiOutGetVolume((HMIDIOUT)pme->rIds[idx].hMidi, lpdwVolume)))
  510. {
  511. mmr = MMSYSERR_NOERROR;
  512. break;
  513. }
  514. return mmr;
  515. }
  516. MMRESULT FAR PASCAL mseSetVolume(
  517. PMIDIEMU pme,
  518. DWORD dwVolume)
  519. {
  520. MMRESULT mmr = MMSYSERR_NOERROR;
  521. MMRESULT mmr2;
  522. UINT idx;
  523. // Try to set everyone's volume
  524. //
  525. for (idx = 0; idx < pme->chMidi; ++idx)
  526. if (MMSYSERR_NOERROR !=
  527. (mmr2 = midiOutSetVolume((HMIDIOUT)pme->rIds[idx].hMidi, dwVolume)))
  528. mmr = mmr2;
  529. return mmr;
  530. }
  531. MMRESULT FAR PASCAL mseOutReset(
  532. PMIDIEMU pme)
  533. {
  534. LPMIDIHDR lpmh;
  535. LPMIDIHDR lpmhWork;
  536. UINT idx;
  537. MSG msg;
  538. // If we have anything posted to mmtask to be cleaned up, process
  539. // it first
  540. //
  541. while (pme->cPostedBuffers)
  542. {
  543. Sleep(0);
  544. }
  545. //
  546. // If we're running the timer, interrupt and force a reschedule
  547. // of all remaining channels.
  548. //
  549. if (guMIDITimerID != TIMER_OFF)
  550. {
  551. dprintf2(( "mOR: About to take %u", guMIDITimerID));
  552. if (MMSYSERR_NOERROR != timeKillEvent(guMIDITimerID))
  553. {
  554. dprintf1(( "timeKillEvent() failed in midiOutPolyMsg"));
  555. }
  556. else
  557. {
  558. guMIDITimerID = TIMER_OFF;
  559. }
  560. midiOutTimerTick(
  561. guMIDITimerID, // ID of our timer
  562. 0, // wMsg is unused
  563. timeGetTime(), // dwUser unused
  564. 0L, // dw1 unused
  565. 0L); // dw2 unused
  566. dprintf2(( "mOR: mOTT"));
  567. if (gfMinPeriod)
  568. {
  569. gfMinPeriod = FALSE;
  570. timeEndPeriod(guMIDIPeriodMin);
  571. }
  572. }
  573. //
  574. // Kill anything queued for midiOutPolyMsg. This will ensure that
  575. // sending will stop after the current buffer.
  576. //
  577. PDEVLOCK( pme );
  578. lpmh = pme->lpmhFront;
  579. pme->lpmhFront = NULL;
  580. pme->lpmhRear = NULL;
  581. pme->dwPolyMsgState = PM_STATE_EMPTY;
  582. while (lpmh)
  583. {
  584. lpmh->dwFlags &= ~MHDR_INQUEUE;
  585. lpmh->dwFlags |= MHDR_DONE;
  586. lpmhWork = lpmh->lpNext;
  587. dprintf2(( "mOR: Next buffer to nuke %08lx", lpmhWork));
  588. midiOutNukePMBuffer(pme, lpmh);
  589. lpmh = lpmhWork;
  590. }
  591. //
  592. // Check to see if our pme structure is still valid. Someone
  593. // might have called midiStreamClose in their callback and we
  594. // don't want to touch it after it's closed and freed. This
  595. // is what the MidiPlyr sample application does.
  596. //
  597. try
  598. {
  599. if (MSE_SIGNATURE != pme->dwSignature) // must have been freed
  600. return MMSYSERR_NOERROR;
  601. PDEVUNLOCK( pme ); // keep it in try for extra protection
  602. }
  603. except(EXCEPTION_EXECUTE_HANDLER)
  604. {
  605. return MMSYSERR_NOERROR;
  606. }
  607. //
  608. // We've just reset the stream; restart the tick clock at 0 and invalidate
  609. // the time division to force the time stuff to be reset when the next
  610. // polymsg comes in.
  611. //
  612. dprintf2(( "midiOutReset: clockInit()/ midiOutSetClockRate()"));
  613. clockInit(&pme->clock, 0, 0, mseTimebase);
  614. midiOutSetClockRate(pme, 0);
  615. pme->tkPlayed = 0;
  616. // Have a reset party on all the drivers under us
  617. //
  618. for (idx = 0; idx < pme->chMidi; idx++)
  619. midiOutReset((HMIDIOUT)pme->rIds[idx].hMidi);
  620. pme->dwPolyMsgState = PM_STATE_PAUSED;
  621. return MMSYSERR_NOERROR;
  622. }
  623. MMRESULT FAR PASCAL mseOutStop(
  624. PMIDIEMU pme)
  625. {
  626. LPMIDIHDR lpmh;
  627. LPMIDIHDR lpmhWork;
  628. MSG msg;
  629. BOOL fSetEvent = FALSE;
  630. // If we have anything posted to mmtask to be cleaned up, process
  631. // it first
  632. //
  633. while (pme->cPostedBuffers)
  634. {
  635. Sleep(0);
  636. }
  637. //
  638. // If we're running the timer, interrupt and force a reschedule
  639. // of all remaining channels.
  640. //
  641. if (guMIDITimerID != TIMER_OFF)
  642. {
  643. dprintf2(( "mOS: About to take %u", guMIDITimerID));
  644. if (MMSYSERR_NOERROR != timeKillEvent(guMIDITimerID))
  645. {
  646. dprintf1(( "timeKillEvent() failed in midiOutPolyMsg"));
  647. }
  648. else
  649. {
  650. guMIDITimerID = TIMER_OFF;
  651. }
  652. dprintf2(( "mOS: take -- About to mOTT"));
  653. midiOutTimerTick(
  654. guMIDITimerID, // ID of our timer
  655. 0, // wMsg is unused
  656. timeGetTime(), // dwUser unused
  657. 0L, // dw1 unused
  658. 0L); // dw2 unused
  659. dprintf2(( "mOS: mOTT"));
  660. if (gfMinPeriod)
  661. {
  662. gfMinPeriod = FALSE;
  663. timeEndPeriod(guMIDIPeriodMin);
  664. }
  665. }
  666. //
  667. // Kill anything queued for midiOutPolyMsg. This will ensure that
  668. // sending will stop after the current buffer.
  669. //
  670. PDEVLOCK( pme );
  671. lpmh = pme->lpmhFront;
  672. pme->lpmhFront = NULL;
  673. pme->lpmhRear = NULL;
  674. pme->dwPolyMsgState = PM_STATE_EMPTY;
  675. while (lpmh)
  676. {
  677. lpmh->dwFlags &= ~MHDR_INQUEUE;
  678. lpmh->dwFlags |= MHDR_DONE;
  679. lpmhWork = lpmh->lpNext;
  680. dprintf2(( "mOS: Next buffer to nuke %08lx", lpmhWork));
  681. midiOutNukePMBuffer(pme, lpmh);
  682. lpmh = lpmhWork;
  683. }
  684. //
  685. // Check to see if our pme structure is still valid. Someone
  686. // might have called midiStreamClose in their callback and we
  687. // don't want to touch it after it's closed and freed. This
  688. // is what the MidiPlyr sample application does.
  689. //
  690. try
  691. {
  692. if (MSE_SIGNATURE != pme->dwSignature) // must have been freed
  693. return MMSYSERR_NOERROR;
  694. PDEVUNLOCK( pme ); // keep it in try for extra protection
  695. }
  696. except(EXCEPTION_EXECUTE_HANDLER)
  697. {
  698. return MMSYSERR_NOERROR;
  699. }
  700. //
  701. // We've just reset the stream; restart the tick clock at 0 and invalidate
  702. // the time division to force the time stuff to be reset when the next
  703. // polymsg comes in.
  704. //
  705. dprintf2(( "midiOutStop: clockInit()/ midiOutSetClockRate()"));
  706. clockInit(&pme->clock, 0, 0, mseTimebase);
  707. midiOutSetClockRate(pme, 0);
  708. pme->tkPlayed = 0;
  709. //
  710. // In case someone queues up headers during the stop
  711. // operation we want to make sure that all they have to
  712. // do is restart the stream to get started again.
  713. //
  714. mseOutPause(pme);
  715. //midiOutAllNotesOff(pme);
  716. //pme->dwPolyMsgState = PM_STATE_STOPPED;
  717. return MMSYSERR_NOERROR;
  718. }
  719. MMRESULT FAR PASCAL mseOutPause(
  720. PMIDIEMU pme)
  721. {
  722. //
  723. // Emulating on this handle - do the pause ourselves.
  724. //
  725. if (pme->dwPolyMsgState == PM_STATE_PAUSED)
  726. return MMSYSERR_NOERROR;
  727. pme->dwSavedState = pme->dwPolyMsgState;
  728. pme->dwPolyMsgState = PM_STATE_PAUSED;
  729. clockPause(&pme->clock, CLK_TK_NOW);
  730. midiOutAllNotesOff(pme);
  731. return MMSYSERR_NOERROR;
  732. }
  733. MMRESULT FAR PASCAL mseOutRestart(
  734. PMIDIEMU pme,
  735. DWORD msTime,
  736. DWORD tkTime)
  737. {
  738. //
  739. // Emulating on this handle - do the pause ourselves.
  740. //
  741. if (pme->dwPolyMsgState != PM_STATE_PAUSED)
  742. return MMSYSERR_NOERROR;
  743. pme->dwPolyMsgState = pme->dwSavedState;
  744. clockRestart(&pme->clock, tkTime, msTime);
  745. dprintf2(( "restart: state->%lu", pme->dwPolyMsgState));
  746. midiOutTimerTick(
  747. guMIDITimerID, // ID of our timer
  748. 0, // wMsg is unused
  749. timeGetTime(),
  750. 0L, // dw1 unused
  751. 0L); // dw2 unused
  752. return MMSYSERR_NOERROR;
  753. }
  754. /*****************************************************************************
  755. * @doc INTERNAL MIDI
  756. *
  757. * @api void | midiEmulatorInit | This function is called at init time to
  758. * allow MMSYSTEM to initialize anything it needs to for the polymsg
  759. * emulators. Right now, all we do is find the minimum period of the
  760. * timeGetTime clock.
  761. *
  762. * @rdesc Currently always returns MMSYSERR_NOERROR.
  763. ****************************************************************************/
  764. #ifdef DEBUG
  765. STATIC SZCODE aszInit[] = "midiEmulatorInit: Using clock res of %lums.";
  766. #endif
  767. void NEAR PASCAL midiEmulatorInit
  768. (
  769. void
  770. )
  771. {
  772. TIMECAPS tc;
  773. if (MMSYSERR_NOERROR != timeGetDevCaps(&tc, sizeof(tc)))
  774. {
  775. dprintf1(( "*** MMSYSTEM IS HORKED ***"));
  776. dprintf1(( "*** timeGetDevCaps failed in midiEmulatorInit ***"));
  777. return;
  778. }
  779. //
  780. // Select the larger of the period we would like to have or
  781. // the minimum period the timer supports.
  782. //
  783. guMIDIPeriodMin = max(MIN_PERIOD, tc.wPeriodMin);
  784. // guMIDIPeriodMin = MIN_PERIOD;
  785. #ifdef DEBUG
  786. dprintf2(( aszInit, (DWORD)guMIDIPeriodMin));
  787. #endif
  788. }
  789. /*****************************************************************************
  790. * @doc EXTERNAL MIDI M4
  791. *
  792. * @api UINT | mseOutSend | Plays or queues a buffer of
  793. * MIDI data to a MIDI output device.
  794. *
  795. * @parm PMIDIEMU | pme | Specifies the stream instance the data should
  796. * go to.
  797. *
  798. * @parm LPMIDIHDR | lpMidiOutHdr | Specifies a far pointer to a <t MIDIHDR>
  799. * structure that identifies the MIDI data buffer.
  800. *
  801. * @parm UINT | cbMidiHdr | Specifies the size of the <t MIDIHDR> structure.
  802. *
  803. * @rdesc The return value is zero if the function is successful. Otherwise,
  804. * it returns an error number. Possible error values include the following:
  805. *
  806. * @flag MMSYSERR_INVALHANDLE | The specified device handle is invalid.
  807. * @flag MMSYSERR_INVALPARAM | The value of <p lpMidiOutHdr> is invalid.
  808. * @flag MIDIERR_UNPREPARED | The output buffer header <p lpMidiOutHdr> has
  809. * not been prepared.
  810. * @flag MIDIERR_STILLPLAYING | <p lpMidiOutHdr> is still playing or
  811. * queued from a previous call to <f midiOutPolyMsg>.
  812. *
  813. * @comm The polymessage buffer contains one or more MIDI messages. Entries in the
  814. * buffer can be of the following three types:
  815. *
  816. * @flag Short Message | Is two DWORDs. One contains time data, the other
  817. * contains message content. Time information is the time to wait between the
  818. * previous event and the event being described. Time units are based on the
  819. * time-division header in the MIDI file.
  820. *
  821. * Message content for short messages occupy the 24 least-significant bits of
  822. * the DWORD; the high-order byte contains a zero.
  823. *
  824. * @flag System Message | Is a multiple of two DWORDs. The first DWORD contains
  825. * time information that specifies the amount of time to wait between the
  826. * previous event and the event being described. Time units are based on the
  827. * time-division header in the MIDI file.
  828. *
  829. * The second DWORD contains the length of the system-message data (SysEx) in
  830. * the 24 least-significant bits of the DWORD; the high-order bit contains
  831. * a one.
  832. *
  833. * Remaining DWORDs in the system message contain SysEx data.
  834. *
  835. * @flag End-of-Buffer | Is two DWORDs, each with the value -1. This entry
  836. * indicates the end of data in the poly-message buffer. This message is not passed
  837. * to MIDI devices.
  838. *
  839. * @comm This function cannot be called at interrupt time.
  840. *
  841. * @xref <f midiOutLongMsg> <f midiOutPrepareHeader>
  842. ****************************************************************************/
  843. #define ERROR_EXIT(x) \
  844. { \
  845. uRet = (x); \
  846. goto CLEANUP; \
  847. }
  848. #define SKIP_BYTES(x,s) \
  849. { \
  850. if (dwLength < (x)) \
  851. { \
  852. dprintf1(( "!midiOutPolyMsg: ran off end of polymsg buffer in parse!\r\n%ls\r\nOffset %lu", (LPSTR)(s), (DWORD)(((LPBYTE)lpdwBuffer) - lpMidiHdr->lpData))); \
  853. uRet = MMSYSERR_INVALPARAM; \
  854. goto CLEANUP; \
  855. } \
  856. ((LPBYTE)lpdwBuffer) += (x); \
  857. dwLength -= (x); \
  858. }
  859. MMRESULT FAR PASCAL mseOutSend(
  860. PMIDIEMU pme,
  861. LPMIDIHDR lpMidiHdr,
  862. UINT cbMidiHdr)
  863. {
  864. UINT uRet = MMSYSERR_NOERROR;
  865. UINT idx;
  866. LPDWORD lpdwBuffer;
  867. DWORD dwLength;
  868. LPMIDIHDR lpmhWork;
  869. LPMIDIHDREXT lpExt;
  870. BOOL fQueueWasEmpty;
  871. BYTE bEvent;
  872. DWORD dwParm;
  873. DWORD dwStreamID;
  874. HMIDIOUT hmo;
  875. DWORD_PTR dwBase;
  876. UINT cNewHeaders;
  877. dprintf2(( "mseOutSend pme %04X lpmh %08lX", (UINT_PTR)pme, (DWORD_PTR)lpMidiHdr));
  878. dwBase = lpMidiHdr->reserved;
  879. if ((lpExt = winmmAlloc(sizeof(MIDIHDREXT))) == NULL)
  880. {
  881. dprintf1(( "midiOutPolyMsg: No room for shadow"));
  882. ERROR_EXIT(MMSYSERR_NOMEM);
  883. }
  884. //
  885. // This needs to be done ASAP in case we error out.
  886. //
  887. lpMidiHdr->reserved = (DWORD_PTR)(lpExt);
  888. lpMidiHdr->dwReserved[MH_BUFIDX] = 0;
  889. lpExt->nHeaders = 0;
  890. lpExt->lpmidihdr = (LPMIDIHDR)(lpExt+1);
  891. //
  892. // Parse the poly msg buffer and see if there are any long msgs.
  893. // If there are, allocate MIDIHDR's for them on the end of the
  894. // main MIDIHDR extension and fill them in and prepare them.
  895. //
  896. lpdwBuffer = (LPDWORD)lpMidiHdr->lpData;
  897. dwLength = lpMidiHdr->dwBytesRecorded;
  898. while (dwLength)
  899. {
  900. //
  901. // Skip over the delta time stamp
  902. //
  903. SKIP_BYTES(sizeof(DWORD), "d-time");
  904. dwStreamID = *lpdwBuffer;
  905. SKIP_BYTES(sizeof(DWORD), "stream-id");
  906. //
  907. // Extract the event type and parameter and skip the event DWORD
  908. //
  909. bEvent = MEVT_EVENTTYPE(*lpdwBuffer) & (BYTE)~(MEVT_F_CALLBACK >> 24);
  910. dwParm = MEVT_EVENTPARM(*lpdwBuffer);
  911. SKIP_BYTES(sizeof(DWORD), "event");
  912. if (bEvent == MEVT_LONGMSG)
  913. {
  914. LPMIDIHDREXT lpExtRealloc;
  915. if (dwParm > dwLength)
  916. {
  917. dprintf1(( "parse: I don't like stuff that sucks!"));
  918. ERROR_EXIT(MMSYSERR_INVALPARAM);
  919. }
  920. cNewHeaders = 1;
  921. if (dwStreamID == (DWORD)-1L)
  922. cNewHeaders = pme->chMidi;
  923. lpExt->nHeaders += cNewHeaders;
  924. if ((lpExtRealloc = (LPMIDIHDREXT)HeapReAlloc(hHeap,
  925. HEAP_ZERO_MEMORY, lpExt,
  926. sizeof(MIDIHDREXT)+sizeof(MIDIHDR)*lpExt->nHeaders))
  927. == NULL)
  928. {
  929. lpExt->nHeaders -= cNewHeaders;
  930. ERROR_EXIT(MMSYSERR_NOMEM);
  931. }
  932. lpExt = lpExtRealloc;
  933. lpMidiHdr->reserved = (DWORD_PTR)(lpExt);
  934. lpmhWork = ((LPMIDIHDR)(lpExt+1)) + lpExt->nHeaders - cNewHeaders;
  935. while (cNewHeaders--)
  936. {
  937. lpmhWork->lpData = (LPSTR)lpdwBuffer;
  938. lpmhWork->dwBufferLength = dwParm;
  939. lpmhWork->dwBytesRecorded = 0;
  940. lpmhWork->dwUser = 0;
  941. lpmhWork->dwFlags =
  942. (lpMidiHdr->dwFlags & MHDR_MAPPED) | MHDR_SHADOWHDR;
  943. if (dwStreamID == (DWORD)-1L)
  944. lpmhWork->dwReserved[MH_STREAM] = cNewHeaders;
  945. else
  946. lpmhWork->dwReserved[MH_STREAM] = dwStreamID;
  947. lpmhWork->dwReserved[MH_STRMPME] = (DWORD_PTR)pme;
  948. ++lpmhWork;
  949. }
  950. dwParm = (dwParm+3)&~3;
  951. SKIP_BYTES(dwParm, "longmsg parm");
  952. }
  953. else
  954. {
  955. //
  956. // Skip any additional paramters for other length-class messages
  957. //
  958. if (bEvent & (MEVT_F_LONG >> 24))
  959. {
  960. dwParm = (dwParm+3)&~3;
  961. // dprintf1(( "Length [%lu] rounded [%lu]", dwParm, (dwParm+3)&~3));
  962. SKIP_BYTES(dwParm, "generic long event data");
  963. }
  964. }
  965. }
  966. // Now prepare any headers we allocated
  967. //
  968. lpmhWork = (LPMIDIHDR)(lpExt+1);
  969. for (idx = 0; idx < lpExt->nHeaders; idx++, lpmhWork++)
  970. {
  971. hmo = (HMIDIOUT)mseIDtoHMidi(pme, (DWORD)lpmhWork->dwReserved[MH_STREAM]);
  972. if (NULL != hmo)
  973. {
  974. if ((uRet = midiOutPrepareHeader(hmo,
  975. lpmhWork,
  976. sizeof(MIDIHDR))) != MMSYSERR_NOERROR)
  977. {
  978. dprintf1(( "parse: pre-prepare of embedded long msg failed! (%lu)", (DWORD)uRet));
  979. ERROR_EXIT(uRet);
  980. }
  981. }
  982. }
  983. //
  984. // Reset lpExt->lpmidihdr to the next header to play
  985. //
  986. lpExt->lpmidihdr = (LPMIDIHDR)(lpExt+1);
  987. //
  988. // Prepare to update handle information to contain this header
  989. //
  990. PDEVLOCK( pme );
  991. //
  992. // Shove the block in the queue, noting if it was empty
  993. //
  994. fQueueWasEmpty = FALSE;
  995. if (pme->lpmhRear == NULL)
  996. {
  997. fQueueWasEmpty = TRUE;
  998. pme->lpmhRear = pme->lpmhFront = lpMidiHdr;
  999. }
  1000. else
  1001. {
  1002. pme->lpmhRear->lpNext = lpMidiHdr;
  1003. pme->lpmhRear = lpMidiHdr;
  1004. }
  1005. lpMidiHdr->lpNext = NULL;
  1006. lpMidiHdr->dwFlags |= MHDR_INQUEUE;
  1007. PDEVUNLOCK( pme );
  1008. if (pme->dwPolyMsgState == PM_STATE_PAUSED)
  1009. {
  1010. if (fQueueWasEmpty)
  1011. pme->dwSavedState = PM_STATE_READY;
  1012. }
  1013. else
  1014. {
  1015. if (fQueueWasEmpty)
  1016. {
  1017. // We want to schedule this now. If the there's no timer
  1018. // or we can kill the current one, send. If we can't kill the
  1019. // pending timer, it's in the process of being scheduled anyway
  1020. //
  1021. if (guMIDITimerID == TIMER_OFF ||
  1022. MMSYSERR_NOERROR == timeKillEvent(guMIDITimerID))
  1023. {
  1024. guMIDITimerID = TIMER_OFF;
  1025. pme->dwPolyMsgState = PM_STATE_READY;
  1026. dprintf2(( "mseSend take -- about to mot"));
  1027. midiOutTimerTick(
  1028. guMIDITimerID, // ID of our timer
  1029. 0, // wMsg is unused
  1030. timeGetTime(), // dwUser unused
  1031. 0L, // dw1 unused
  1032. 0L); // dw2 unused
  1033. dprintf2(( "mseSend mot"));
  1034. }
  1035. }
  1036. }
  1037. CLEANUP:
  1038. if (uRet != MMSYSERR_NOERROR)
  1039. {
  1040. if (lpExt != NULL)
  1041. {
  1042. lpMidiHdr = (LPMIDIHDR)(lpExt+1);
  1043. while (lpExt->nHeaders--)
  1044. {
  1045. hmo = (HMIDIOUT)mseIDtoHMidi(pme, (DWORD)lpMidiHdr->dwReserved[MH_STREAM]);
  1046. #ifdef DEBUG
  1047. if (NULL == hmo)
  1048. dprintf1(( "stream-id disappeared during cleanup!!!"));
  1049. #endif
  1050. midiOutUnprepareHeader(hmo, lpMidiHdr++, sizeof(MIDIHDR));
  1051. }
  1052. winmmFree(lpExt);
  1053. }
  1054. }
  1055. return uRet;
  1056. } /* midiOutPolyMsg() */
  1057. /** void FAR PASCAL midiOutSetClockRate(PMIDIEMU pme, TICKS tkWhen)
  1058. *
  1059. * DESCRIPTION:
  1060. *
  1061. * This function is called whenever the clock rate for the stream
  1062. * needs to be changed.
  1063. *
  1064. * ARGUMENTS:
  1065. * (PMIDIEMU pme, TICKS tkWhen)
  1066. *
  1067. * pme indicates the handle to change the clock rate of.
  1068. *
  1069. * tkWhen is the absolute tick time at which the time change occurs.
  1070. *
  1071. ** jfg */
  1072. void FAR PASCAL midiOutSetClockRate(
  1073. PMIDIEMU pme,
  1074. TICKS tkWhen)
  1075. {
  1076. DWORD dwNum;
  1077. DWORD dwDenom;
  1078. if (pme->dwTimeDiv&IS_SMPTE)
  1079. {
  1080. switch(-SMPTE_FORMAT(pme->dwTimeDiv))
  1081. {
  1082. case SMPTE_24:
  1083. dwNum = 24L;
  1084. dwDenom = 1L;
  1085. break;
  1086. case SMPTE_25:
  1087. dwNum = 25L;
  1088. dwDenom = 1L;
  1089. break;
  1090. case SMPTE_30DROP:
  1091. case SMPTE_30:
  1092. //
  1093. // Actual frame rate for 30 fps (color television) is
  1094. // 29.97 fps.
  1095. //
  1096. dwNum = 2997L;
  1097. dwDenom = 100L;
  1098. break;
  1099. default:
  1100. dprintf1(( "Invalid SMPTE frames/sec in midiOutSetClockRate! (using 30)"));
  1101. dwNum = 2997L;
  1102. dwDenom = 100L;
  1103. break;
  1104. }
  1105. dwNum *= (DWORD)TICKS_PER_FRAME(pme->dwTimeDiv);
  1106. dwDenom *= 1000L;
  1107. }
  1108. else
  1109. {
  1110. dwNum = 1000L * TICKS_PER_QN(pme->dwTimeDiv);
  1111. dwDenom = pme->dwTempo;
  1112. }
  1113. clockSetRate(&pme->clock, tkWhen, dwNum, dwDenom);
  1114. }
  1115. /** BOOL NEAR PASCAL midiOutScheduleNextEvent(PMIDIEMU pme)
  1116. *
  1117. * DESCRIPTION:
  1118. *
  1119. * Determine when (in ticks defined for this device) the next event
  1120. * is due.
  1121. *
  1122. * ARGUMENTS:
  1123. * (PMIDIEMU pme)
  1124. *
  1125. * RETURN (BOOL):
  1126. *
  1127. * TRUE if there was an event in this buffer to schedule.
  1128. *
  1129. * NOTES:
  1130. *
  1131. * Just calculate how many ticks till next event and store in the
  1132. * device struct.
  1133. *
  1134. * This function does NOT schedule across buffers; caller must
  1135. * link to next buffer if needed.
  1136. *
  1137. ** jfg */
  1138. BOOL NEAR PASCAL midiOutScheduleNextEvent(
  1139. PMIDIEMU pme)
  1140. {
  1141. LPMIDIHDR lpmhdr;
  1142. LPBYTE lpb;
  1143. DWORD tkDelta;
  1144. if ((lpmhdr = pme->lpmhFront) == NULL ||
  1145. lpmhdr->dwReserved[MH_BUFIDX] == lpmhdr->dwBytesRecorded)
  1146. {
  1147. pme->dwPolyMsgState = PM_STATE_EMPTY;
  1148. return FALSE;
  1149. }
  1150. lpb = (LPBYTE)lpmhdr->lpData;
  1151. tkDelta = *(LPDWORD)(lpb+lpmhdr->dwReserved[MH_BUFIDX]);
  1152. pme->tkNextEventDue = pme->tkPlayed + tkDelta;
  1153. pme->dwPolyMsgState = PM_STATE_READY;
  1154. return TRUE;
  1155. } /* ScheduleNextEvent() */
  1156. /** void NEAR PASCAL midiOutPlayNextPolyEvent(PMIDIEMU pme)
  1157. *
  1158. * DESCRIPTION:
  1159. *
  1160. * Play the next event if there is one. Current buffer must
  1161. * be pointing at an event (*NOT* end-of-buffer).
  1162. *
  1163. * - Plays all events which are due
  1164. *
  1165. * - Schedules next event
  1166. *
  1167. * ARGUMENTS:
  1168. * (PMIDIEMU pme)
  1169. *
  1170. * NOTES:
  1171. *
  1172. * First, play the event. If it's a short msg, just do it.
  1173. * If it's a SysEx, pull the appropriate (already prepared)
  1174. * header from the extension block and send it. Mark the state
  1175. * of the device as blocked so nothing else will be played
  1176. * until the SysEx is done.
  1177. *
  1178. * Update dwReserved[MH_BUFIDX] to point at the next event.
  1179. *
  1180. * Determine the next event and schedule it, crossing to the
  1181. * next buffer if needed. If the next event is already due
  1182. * (i.e. had a delta-time of zero), stick around and send that,
  1183. * too.
  1184. *
  1185. *
  1186. *
  1187. ** jfg */
  1188. void NEAR PASCAL midiOutPlayNextPolyEvent(
  1189. PMIDIEMU pme
  1190. #ifdef DEBUG
  1191. ,DWORD dwStartTime
  1192. #endif
  1193. )
  1194. {
  1195. LPBYTE lpb;
  1196. LPMIDIHDR lpmhdr;
  1197. DWORD dwMsg;
  1198. LPMIDIHDREXT lpExt;
  1199. MMRESULT mmrError;
  1200. DWORD tkDelta;
  1201. BYTE bEvent;
  1202. DWORD dwOffset;
  1203. DWORD dwStreamID;
  1204. HMIDIOUT hmo;
  1205. UINT cToSend;
  1206. #if 0
  1207. if (NULL != pme->lpmhFront)
  1208. {
  1209. lpb = (LPBYTE)(pme->lpmhFront->lpData);
  1210. _asm
  1211. {
  1212. mov ax, word ptr lpb
  1213. mov dx, word ptr lpb+2
  1214. int 3
  1215. }
  1216. }
  1217. #endif
  1218. while (pme->dwPolyMsgState == PM_STATE_READY)
  1219. {
  1220. for(;;)
  1221. {
  1222. lpmhdr = pme->lpmhFront;
  1223. if (!lpmhdr)
  1224. return;
  1225. // Make sure next buffer contains valid data and skip if it
  1226. // doesn't
  1227. //
  1228. if (midiOutScheduleNextEvent(pme))
  1229. break;
  1230. // That buffer is done or empty
  1231. //
  1232. midiOutDequeueAndCallback(pme);
  1233. }
  1234. lpb = lpmhdr->lpData;
  1235. tkDelta = *(LPDWORD)(lpb+lpmhdr->dwReserved[MH_BUFIDX]);
  1236. // dprintf2(( "dwReserved[MH_BUFIDX] %lu tkDelta %lu", lpmhdr->dwReserved[0], tkDelta));
  1237. pme->tkNextEventDue = pme->tkPlayed + tkDelta;
  1238. if (pme->tkNextEventDue > pme->tkTime)
  1239. {
  1240. return;
  1241. }
  1242. //
  1243. // There is an event pending and it's due; send it and update pointers
  1244. //
  1245. dwOffset = (DWORD)lpmhdr->dwReserved[MH_BUFIDX];
  1246. pme->tkPlayed += tkDelta;
  1247. // Skip tkDelta and stream-id
  1248. //
  1249. lpmhdr->dwReserved[MH_BUFIDX] += sizeof(DWORD);
  1250. dwStreamID = *(LPDWORD)(lpb+lpmhdr->dwReserved[MH_BUFIDX]);
  1251. lpmhdr->dwReserved[MH_BUFIDX] += sizeof(DWORD);
  1252. // Will be NULL if dwStreamID == -1 (all IDs)
  1253. //
  1254. hmo = (HMIDIOUT)mseIDtoHMidi(pme, dwStreamID);
  1255. //
  1256. // Extract event type and parms and update past event
  1257. //
  1258. dwMsg = *(LPDWORD)(lpb+lpmhdr->dwReserved[MH_BUFIDX]);
  1259. bEvent = MEVT_EVENTTYPE(dwMsg);
  1260. dwMsg = MEVT_EVENTPARM(dwMsg);
  1261. lpmhdr->dwReserved[MH_BUFIDX] += sizeof(DWORD);
  1262. if (hmo && (bEvent & (MEVT_F_CALLBACK >> 24)))
  1263. {
  1264. lpmhdr->dwOffset = dwOffset;
  1265. DriverCallback(
  1266. pme->dwCallback,
  1267. HIWORD(pme->dwFlags),
  1268. (HDRVR)pme->hStream,
  1269. MM_MOM_POSITIONCB,
  1270. pme->dwInstance,
  1271. (DWORD_PTR)lpmhdr,
  1272. 0L);
  1273. }
  1274. bEvent &= ~(MEVT_F_CALLBACK >> 24);
  1275. switch(bEvent)
  1276. {
  1277. case MEVT_SHORTMSG:
  1278. {
  1279. BYTE bEventType;
  1280. BYTE bNote;
  1281. BYTE bVelocity;
  1282. LPBYTE pbEntry = pme->rbNoteOn;
  1283. if (NULL == hmo)
  1284. {
  1285. dprintf1(( "Event skipped - not ours"));
  1286. break;
  1287. }
  1288. //
  1289. // If we're sending a note on or note off, track note-on
  1290. // count.
  1291. //
  1292. bEventType = (BYTE)(dwMsg&0xFF);
  1293. if (!(bEventType & 0x80))
  1294. {
  1295. bEventType = pme->bRunningStatus;
  1296. bNote = (BYTE)(dwMsg&0xFF);
  1297. bVelocity = (BYTE)((dwMsg >> 8)&0xFF);
  1298. // ALWAYS expand running status - individual dev's can't
  1299. // track running status of entire stream.
  1300. //
  1301. dwMsg = (dwMsg << 8) | (DWORD)(bEventType);
  1302. }
  1303. else
  1304. {
  1305. pme->bRunningStatus = bEventType;
  1306. bNote = (BYTE)((dwMsg >> 8)&0xFF);
  1307. bVelocity = (BYTE)((dwMsg >> 16)&0xFF);
  1308. }
  1309. if ((bEventType&0xF0) == MIDI_NOTEON ||
  1310. (bEventType&0xF0) == MIDI_NOTEOFF)
  1311. {
  1312. BYTE bChannel = (bEventType & 0x0F);
  1313. UINT cbOffset = (bChannel * NUM_NOTES + bNote) / 2;
  1314. //
  1315. // Note-on with a velocity of 0 == note off
  1316. //
  1317. if ((bEventType&0xF0) == MIDI_NOTEOFF || bVelocity == 0)
  1318. {
  1319. if (bNote&0x01) // odd
  1320. {
  1321. if ((*(pbEntry + cbOffset)&0xF0) != 0)
  1322. *(pbEntry + cbOffset) -= 0x10;
  1323. }
  1324. else //even
  1325. {
  1326. if ((*(pbEntry + cbOffset)&0xF) != 0)
  1327. *(pbEntry + cbOffset) -= 0x01;
  1328. }
  1329. }
  1330. else
  1331. {
  1332. if (bNote&0x01) // odd
  1333. {
  1334. if ((*(pbEntry + cbOffset)&0xF0) != 0xF0)
  1335. *(pbEntry + cbOffset) += 0x10;
  1336. }
  1337. else //even
  1338. {
  1339. if ((*(pbEntry + cbOffset)&0xF) != 0xF)
  1340. *(pbEntry + cbOffset) += 0x01;
  1341. }
  1342. }
  1343. }
  1344. mmrError = midiOutShortMsg(hmo, dwMsg);
  1345. if (MMSYSERR_NOERROR != mmrError)
  1346. {
  1347. dprintf(("Short msg returned %08lX!!!", (DWORD)mmrError));
  1348. }
  1349. }
  1350. break;
  1351. case MEVT_TEMPO:
  1352. pme->dwTempo = dwMsg;
  1353. dprintf1(( "dwTempo %lu", pme->dwTempo));
  1354. midiOutSetClockRate((PMIDIEMU)pme, pme->tkPlayed);
  1355. break;
  1356. case MEVT_LONGMSG:
  1357. //
  1358. // Advance lpmhdr past the message; the header is already
  1359. // prepared with the proper address and length, so we set
  1360. // the polymsg header so that it points at the next message
  1361. // when this long msg completes.
  1362. //
  1363. // Keep low 24 bits of dwMsg (SysEx length, byte aligned),
  1364. // round to next DWORD (buffer must be padded to match this),
  1365. // and skip past dwMsg and the SysEx buffer.
  1366. //
  1367. dwMsg = (dwMsg+3)&~3;
  1368. lpmhdr->dwReserved[MH_BUFIDX] += dwMsg;
  1369. cToSend = 1;
  1370. if (dwStreamID == (DWORD)-1L)
  1371. cToSend = pme->chMidi;
  1372. lpExt = (LPMIDIHDREXT)lpmhdr->reserved;
  1373. pme->cSentLongMsgs = 0;
  1374. pme->dwPolyMsgState = PM_STATE_BLOCKED;
  1375. pme->fdwDev |= MDV_F_SENDING;
  1376. while (cToSend--)
  1377. {
  1378. lpmhdr = lpExt->lpmidihdr;
  1379. ++lpExt->lpmidihdr;
  1380. hmo = (HMIDIOUT)mseIDtoHMidi(pme,
  1381. (DWORD)lpmhdr->dwReserved[MH_STREAM]);
  1382. if (hmo)
  1383. mmrError = midiOutLongMsg(hmo, lpmhdr, sizeof(MIDIHDR));
  1384. else
  1385. dprintf1(( "mseIDtoHMidi() failed and returned a NULL" ));
  1386. if ((hmo) && (MMSYSERR_NOERROR == mmrError))
  1387. ++pme->cSentLongMsgs;
  1388. else
  1389. dprintf1(( "MODM_LONGDATA returned %u in emulator!",
  1390. (UINT)mmrError));
  1391. }
  1392. if (0 == pme->cSentLongMsgs)
  1393. pme->dwPolyMsgState = PM_STATE_READY;
  1394. pme->fdwDev &= ~MDV_F_SENDING;
  1395. break;
  1396. default:
  1397. //
  1398. // If we didn't understand a length-class message, skip it.
  1399. //
  1400. if (bEvent&(MEVT_F_LONG >> 24))
  1401. {
  1402. dwMsg = (dwMsg+3)&~3;
  1403. lpmhdr->dwReserved[MH_BUFIDX] += dwMsg;
  1404. }
  1405. break;
  1406. }
  1407. //
  1408. // Find the next schedulable polyMsg
  1409. //
  1410. while (!midiOutScheduleNextEvent(pme))
  1411. {
  1412. midiOutDequeueAndCallback(pme);
  1413. if (pme->lpmhFront == NULL)
  1414. break;
  1415. }
  1416. }
  1417. }
  1418. /** void NEAR PASCAL midiOutDequeueAndCallback(PMIDIEMU pme)
  1419. *
  1420. * DESCRIPTION:
  1421. *
  1422. * The current polymsg buffer has finished. Pull it off the queue
  1423. * and do a callback.
  1424. *
  1425. * ARGUMENTS:
  1426. * (PMIDIEMU pme)
  1427. *
  1428. * NOTES:
  1429. *
  1430. ** jfg */
  1431. void NEAR PASCAL midiOutDequeueAndCallback(
  1432. PMIDIEMU pme)
  1433. {
  1434. LPMIDIHDR lpmidihdr;
  1435. BOOL fPosted;
  1436. dprintf2(( "DQ"));
  1437. //
  1438. // A polymsg buffer has finished. Pull it off the queue and
  1439. // call back the app.
  1440. //
  1441. if ((lpmidihdr = pme->lpmhFront) == NULL)
  1442. return;
  1443. if ((pme->lpmhFront = lpmidihdr->lpNext) == NULL)
  1444. {
  1445. dprintf2(( "DQ/CB -- last buffer"));
  1446. pme->lpmhRear = NULL;
  1447. }
  1448. //
  1449. // Can't be at interrupt callback time to unprepare possible
  1450. // embedded long messages in this thing. The notify window's
  1451. // wndproc will call midiOutNukePMBuffer to clean up.
  1452. //
  1453. dprintf2(( "!DQ/CB %08lX", (DWORD_PTR)lpmidihdr));
  1454. ++pme->cPostedBuffers;
  1455. fPosted = PostMessage(
  1456. hwndNotify,
  1457. MM_POLYMSGBUFRDONE,
  1458. (WPARAM)pme,
  1459. (DWORD_PTR)lpmidihdr);
  1460. WinAssert(fPosted);
  1461. if (!fPosted)
  1462. {
  1463. GetLastError();
  1464. --pme->cPostedBuffers;
  1465. }
  1466. }
  1467. void FAR PASCAL midiOutNukePMBuffer(
  1468. PMIDIEMU pme,
  1469. LPMIDIHDR lpmh)
  1470. {
  1471. LPMIDIHDREXT lpExt;
  1472. LPMIDIHDR lpmhWork;
  1473. MMRESULT mmrc;
  1474. HMIDIOUT hmo;
  1475. dprintf2(( "Nuke %08lX", (DWORD_PTR)lpmh));
  1476. //
  1477. // Unprepare internal stuff and do user callback
  1478. //
  1479. lpExt = (LPMIDIHDREXT)(lpmh->reserved);
  1480. lpmhWork = (LPMIDIHDR)(lpExt+1);
  1481. while (lpExt->nHeaders--)
  1482. {
  1483. if ((lpmhWork->dwFlags&MHDR_PREPARED) &&
  1484. (!(lpmhWork->dwFlags&MHDR_INQUEUE)))
  1485. {
  1486. hmo = (HMIDIOUT)mseIDtoHMidi(pme, (DWORD)lpmhWork->dwReserved[MH_STREAM]);
  1487. mmrc = midiOutUnprepareHeader(hmo, lpmhWork, sizeof(*lpmhWork));
  1488. #ifdef DEBUG
  1489. if (MMSYSERR_NOERROR != mmrc)
  1490. {
  1491. dprintf1(( "midiOutNukePMBuffer: Could not unprepare! (%lu)", (DWORD)mmrc));
  1492. }
  1493. #endif
  1494. }
  1495. else
  1496. {
  1497. dprintf1(( "midiOutNukePMBuffer: Emulation header flags bogus!!!"));
  1498. }
  1499. lpmhWork++;
  1500. }
  1501. winmmFree(lpExt);
  1502. lpmh->reserved = 0L;
  1503. lpmh->dwFlags &= ~MHDR_INQUEUE;
  1504. lpmh->dwFlags |= MHDR_DONE;
  1505. // dprintf2(( "Nuke: callback"));
  1506. DriverCallback(
  1507. pme->dwCallback,
  1508. HIWORD(pme->dwFlags),
  1509. (HDRVR)pme->hStream,
  1510. MM_MOM_DONE,
  1511. pme->dwInstance,
  1512. (DWORD_PTR)lpmh,
  1513. 0L);
  1514. }
  1515. /*****************************************************************************
  1516. *
  1517. * @doc INTERNAL MIDI
  1518. *
  1519. * @api void | midiOutTimerTick |
  1520. * This function handles the timing of polymsg out buffers. One timer instance
  1521. * is shared by all polymsg out streams. When <f midiOutPolyMsg> is called
  1522. * and the timer is not running, or <f midiOutTimerTick> finished processing,
  1523. * the timer is set to go off based on the time until the event with the
  1524. * shortest time remaining of all events. All timers are one-shot timers.
  1525. *
  1526. * @parm UINT | uTimerID |
  1527. * The timer ID of the timer that fired.
  1528. *
  1529. * @parm UINT | wMsg |
  1530. * Unused.
  1531. *
  1532. * @parm DWORD | dwUser |
  1533. * User instance data for the timer callback (unused).
  1534. *
  1535. * @parm DWORD | dwParam1 |
  1536. * Unused.
  1537. *
  1538. * @parm DWORD | dwParam2 |
  1539. * Unused.
  1540. *
  1541. * @comm Determine elapsed microseconds using <f timeGetTime>.
  1542. *
  1543. * Traverse the list of output handles. Update the tick clock for each handle. If there are
  1544. * events to do on that handle, start them.
  1545. *
  1546. * Determine the next event due on any stream. Start another one-shot timer
  1547. * to call <f midiOutTimerTick> when this interval has expired.
  1548. *
  1549. *****************************************************************************/
  1550. STATIC UINT uTimesIn = 0;
  1551. void CALLBACK midiOutTimerTick(
  1552. UINT uTimerID,
  1553. UINT wMsg,
  1554. DWORD_PTR dwUser,
  1555. DWORD_PTR dw1,
  1556. DWORD_PTR dw2)
  1557. {
  1558. PMIDIEMU pme;
  1559. DWORD msNextEventMin = (DWORD)-1L;
  1560. DWORD msNextEvent;
  1561. UINT uDelay;
  1562. #ifdef DEBUG
  1563. DWORD dwNow = timeGetTime();
  1564. #endif
  1565. if (guMIDIInTimer)
  1566. {
  1567. dprintf2(( "midiOutTimerTick() re-entered (%u)", guMIDIInTimer));
  1568. return;
  1569. }
  1570. guMIDIInTimer++;
  1571. #ifdef DEBUG
  1572. {
  1573. DWORD dwDelta = dwNow - (DWORD)dwUser;
  1574. if (dwDelta > 1)
  1575. dprintf2(( "Timer event delivered %lu ms late", dwDelta));
  1576. }
  1577. #endif
  1578. for (pme = gpEmuList; pme; pme = pme->pNext)
  1579. {
  1580. pme->tkTime = clockTime(&pme->clock);
  1581. //
  1582. // Play all events on this pdev that are due
  1583. //
  1584. if (pme->dwPolyMsgState == PM_STATE_READY)
  1585. {
  1586. //
  1587. // Lock starts at -1. When incrementing the lock
  1588. // if we are the only one with the lock the count
  1589. // will be 0, otherwise it will be some non-zero
  1590. // value determined by InterlockedIncrement.
  1591. //
  1592. if (PDEVLOCK( pme ) == 0)
  1593. midiOutPlayNextPolyEvent(pme
  1594. #ifdef DEBUG
  1595. ,dwNow
  1596. #endif
  1597. );
  1598. PDEVUNLOCK( pme );
  1599. }
  1600. //
  1601. // If there's still data to play on this stream, figure out when
  1602. // it'll be due so we can schedule the next nearest event.
  1603. //
  1604. if (pme->dwPolyMsgState != PM_STATE_EMPTY)
  1605. {
  1606. // dprintf1(( "tkNextEventDue %lu pdev->tkTime %lu", pme->tkNextEventDue, pme->tkTime));
  1607. if (pme->tkNextEventDue <= pme->tkTime)
  1608. {
  1609. //
  1610. // This can happen if we send a long embedded SysEx and the
  1611. // next event is scheduled a short time away (comes due before
  1612. // SysEx finishes). In this case, we want the timer to fire
  1613. // again ASAP.
  1614. //
  1615. msNextEvent = 0;
  1616. }
  1617. else
  1618. {
  1619. msNextEvent =
  1620. clockOffsetTo(&pme->clock, pme->tkNextEventDue);
  1621. }
  1622. if (msNextEvent < msNextEventMin)
  1623. {
  1624. msNextEventMin = msNextEvent;
  1625. }
  1626. }
  1627. else
  1628. {
  1629. dprintf1(( "dwPolyMsgState == PM_STATE_EMPTY"));
  1630. }
  1631. }
  1632. if (0 == msNextEventMin)
  1633. {
  1634. dprintf1(( "midiEmu: Next event due now!!!"));
  1635. }
  1636. --guMIDIInTimer;
  1637. //
  1638. // Schedule the next event. In no case schedule an event less than
  1639. // guMIDIPeriodMin away (no point in coming back w/ no time elapsed).
  1640. //
  1641. if (msNextEventMin != (DWORD)-1L)
  1642. {
  1643. uDelay = max(guMIDIPeriodMin, (UINT)msNextEventMin);
  1644. // dprintf1(("PM Resched %u ms (ID=%u)", uDelay, guMIDITimerID));
  1645. if (!gfMinPeriod)
  1646. {
  1647. timeBeginPeriod(guMIDIPeriodMin);
  1648. gfMinPeriod = TRUE;
  1649. }
  1650. #ifdef DEBUG
  1651. guMIDITimerID = timeSetEvent(uDelay, guMIDIPeriodMin, midiOutTimerTick, timeGetTime()+uDelay, TIME_ONESHOT | TIME_KILL_SYNCHRONOUS);
  1652. #else
  1653. guMIDITimerID = timeSetEvent(uDelay, guMIDIPeriodMin, midiOutTimerTick, uDelay, TIME_ONESHOT | TIME_KILL_SYNCHRONOUS);
  1654. #endif
  1655. dprintf2(( "mOTT tse(%u) = %u", guMIDIPeriodMin, guMIDITimerID));
  1656. if (guMIDITimerID == TIMER_OFF)
  1657. dprintf1(( "timeSetEvent(%u) failed in midiOutTimerTick!!!", uDelay));
  1658. }
  1659. else
  1660. {
  1661. dprintf1(( "Stop in the name of all that which does not suck!"));
  1662. guMIDITimerID = TIMER_OFF;
  1663. if (gfMinPeriod)
  1664. {
  1665. dprintf1(( "timeEndPeriod"));
  1666. gfMinPeriod = FALSE;
  1667. timeEndPeriod(guMIDIPeriodMin);
  1668. }
  1669. }
  1670. #ifdef DEBUG
  1671. {
  1672. DWORD dwDelta = timeGetTime() - dwNow;
  1673. if (dwDelta > 1)
  1674. dprintf2(( "Spent %lu ms in midiOutTimerTick", dwDelta));
  1675. }
  1676. #endif
  1677. } /* TimerTick() */
  1678. /*****************************************************************************
  1679. *
  1680. * @doc INTERNAL MIDI
  1681. *
  1682. * @api void | midiOutCallback |
  1683. * This function is called by the midi output driver whenever an event
  1684. * completes. It filters long message completions when we are emulating
  1685. * polymsg out.
  1686. *
  1687. * @parm HMIDIOUT | hMidiOut |
  1688. * Handle of the device which completed something.
  1689. *
  1690. * @parm UINT | wMsg |
  1691. * Specifies the event which completed.
  1692. *
  1693. * @parm DWORD | dwInstance |
  1694. * User instance data for the callback.
  1695. *
  1696. * @parm DWORD | dwParam1 |
  1697. * Message specific parameter.
  1698. *
  1699. * @parm DWORD | dwParam2 |
  1700. * Message specific parameter.
  1701. *
  1702. * @comm
  1703. *
  1704. * If this is a completion for a long message buffer on a stream we are
  1705. * emulating polymsg out for, mark the stream as ready to play.
  1706. *
  1707. *****************************************************************************/
  1708. void CALLBACK midiOutCallback(
  1709. HMIDIOUT hMidiOut,
  1710. WORD wMsg,
  1711. DWORD_PTR dwInstance,
  1712. DWORD_PTR dwParam1,
  1713. DWORD_PTR dwParam2)
  1714. {
  1715. PMIDIEMU pme;
  1716. LPMIDIHDR lpmh;
  1717. if (MM_MOM_DONE != wMsg)
  1718. return;
  1719. lpmh = (LPMIDIHDR)dwParam1;
  1720. pme = (PMIDIEMU)lpmh->dwReserved[MH_STRMPME];
  1721. #ifdef DEBUG
  1722. if (lpmh->dwFlags & MHDR_ISSTRM)
  1723. dprintf1(( "Uh-oh, got stream header back from 3.1 driver???"));
  1724. #endif
  1725. if (MM_MOM_DONE == wMsg)
  1726. {
  1727. if (0 == --pme->cSentLongMsgs &&
  1728. !(pme->fdwDev & MDV_F_SENDING))
  1729. pme->dwPolyMsgState = PM_STATE_READY;
  1730. }
  1731. }
  1732. /*****************************************************************************
  1733. * @doc INTERNAL MIDI
  1734. *
  1735. * @api void | midiOutAllNotesOff | This function turns off all notes
  1736. * by using the map kept in polymsg emulation. It only works if we're
  1737. * opened with MIDI_IO_COOKED and are emulating on that device.
  1738. *
  1739. * @parm PMIDIEMU | pme | The device to turn off notes on.
  1740. *
  1741. * @xref midiOutPause midiOutStop
  1742. ****************************************************************************/
  1743. void NEAR PASCAL midiOutAllNotesOff(
  1744. PMIDIEMU pme)
  1745. {
  1746. UINT uChannel;
  1747. UINT uNote;
  1748. BYTE bCount;
  1749. DWORD dwMsg;
  1750. UINT idx;
  1751. LPBYTE pbEntry = pme->rbNoteOn;
  1752. for (uChannel=0; uChannel < NUM_CHANNELS; uChannel++)
  1753. {
  1754. // Turn off any sustained notes so the note off won't be ignored
  1755. //
  1756. dwMsg = ((DWORD)MIDI_CONTROLCHANGE) |
  1757. ((DWORD)uChannel)|
  1758. (((DWORD)MIDI_SUSTAIN)<<8);
  1759. for (idx = 0; idx < pme->chMidi; idx++)
  1760. midiOutShortMsg((HMIDIOUT)pme->rIds[idx].hMidi, dwMsg);
  1761. for (uNote=0; uNote < NUM_NOTES; uNote++)
  1762. {
  1763. if (uNote&0x01) // odd
  1764. {
  1765. bCount = (*(pbEntry + (uChannel * NUM_NOTES + uNote)/2) & 0xF0)>>4;
  1766. }
  1767. else // even
  1768. {
  1769. bCount = *(pbEntry + (uChannel * NUM_NOTES + uNote)/2) & 0xF;
  1770. }
  1771. if (bCount != 0)
  1772. {
  1773. //
  1774. // Message is Note off on this channel and note
  1775. // with a turn off velocity of 127
  1776. //
  1777. dwMsg =
  1778. ((DWORD)MIDI_NOTEOFF)|
  1779. ((DWORD)uChannel)|
  1780. ((DWORD)(uNote<<8))|
  1781. 0x007F0000L;
  1782. dprintf1(( "mOANO: dwMsg %08lX count %u", dwMsg, (UINT)bCount));
  1783. while (bCount--)
  1784. {
  1785. for (idx = 0; idx < pme->chMidi; idx++)
  1786. midiOutShortMsg((HMIDIOUT)pme->rIds[idx].hMidi, dwMsg);
  1787. }
  1788. }
  1789. }
  1790. }
  1791. }
  1792. MMRESULT FAR PASCAL mseOutCachePatches(
  1793. PMIDIEMU pme,
  1794. UINT uBank,
  1795. LPWORD pwpa,
  1796. UINT fuCache)
  1797. {
  1798. UINT cmesi;
  1799. PMIDIEMUSID pmesi;
  1800. MMRESULT mmrc;
  1801. MMRESULT mmrc2;
  1802. cmesi = pme->chMidi;
  1803. pmesi = pme->rIds;
  1804. mmrc2 = MMSYSERR_NOERROR;
  1805. while (cmesi--)
  1806. {
  1807. mmrc = midiOutCachePatches((HMIDIOUT)pmesi->hMidi, uBank, pwpa, fuCache);
  1808. if (MMSYSERR_NOERROR != mmrc && MMSYSERR_NOTSUPPORTED != mmrc)
  1809. mmrc2 = mmrc;
  1810. }
  1811. return mmrc2;
  1812. }
  1813. MMRESULT FAR PASCAL mseOutCacheDrumPatches(
  1814. PMIDIEMU pme,
  1815. UINT uPatch,
  1816. LPWORD pwkya,
  1817. UINT fuCache)
  1818. {
  1819. UINT cmesi;
  1820. PMIDIEMUSID pmesi;
  1821. MMRESULT mmrc;
  1822. MMRESULT mmrc2;
  1823. cmesi = pme->chMidi;
  1824. pmesi = pme->rIds;
  1825. mmrc2 = MMSYSERR_NOERROR;
  1826. while (cmesi--)
  1827. {
  1828. mmrc = midiOutCacheDrumPatches((HMIDIOUT)pmesi->hMidi, uPatch, pwkya, fuCache);
  1829. if (MMSYSERR_NOERROR != mmrc && MMSYSERR_NOTSUPPORTED != mmrc)
  1830. mmrc2 = mmrc;
  1831. }
  1832. return mmrc2;
  1833. }
  1834. DWORD FAR PASCAL mseOutBroadcast(
  1835. PMIDIEMU pme,
  1836. UINT msg,
  1837. DWORD_PTR dwParam1,
  1838. DWORD_PTR dwParam2)
  1839. {
  1840. UINT idx;
  1841. DWORD dwRet;
  1842. DWORD dwRetImmed;
  1843. dwRet = 0;
  1844. for (idx = 0; idx < pme->chMidi; idx++)
  1845. {
  1846. dwRetImmed = midiOutMessage((HMIDIOUT)pme->rIds[idx].hMidi, msg, dwParam1, dwParam2);
  1847. if (dwRetImmed)
  1848. dwRet = dwRetImmed;
  1849. }
  1850. return dwRet;
  1851. }
  1852. DWORD FAR PASCAL mseTimebase(
  1853. PCLOCK pclock)
  1854. {
  1855. return timeGetTime();
  1856. }