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.

988 lines
26 KiB

  1. /**********************************************************************
  2. Copyright (c) 1992-1999 Microsoft Corporation
  3. modfix.c
  4. DESCRIPTION:
  5. Fixed code for doing output mapping. KEEP THE SIZE OF THIS CODE
  6. TO A MINIMUM!
  7. HISTORY:
  8. 02/22/94 [jimge] created.
  9. *********************************************************************/
  10. #pragma warning(disable:4704)
  11. #include "preclude.h"
  12. #include <windows.h>
  13. #include <windowsx.h>
  14. #include <mmsystem.h>
  15. #include <mmddk.h>
  16. #include "idf.h"
  17. #include "midimap.h"
  18. #include "debug.h"
  19. extern HANDLE hMutexRefCnt; // Located in DRVPROC.C
  20. extern HANDLE hMutexConfig; // Located in DRVPROC.C
  21. #define MSG_UNKNOWN 0
  22. #define MSG_SHORT 1
  23. #define MSG_LONG 2
  24. INT FNLOCAL MapEvent (
  25. BYTE * pStatus,
  26. DWORD dwBuffSize,
  27. DWORD * pSkipBytes,
  28. DWORD * pShortMsg);
  29. DWORD FNLOCAL modMapLongMsg (
  30. PINSTANCE pinstance,
  31. LPMIDIHDR lpmh);
  32. /***************************************************************************
  33. @doc internal
  34. @api int | modMessage | Exported entry point for MIDI out messages.
  35. This function conforms to the definition in the MM DDK.
  36. @parm UINT | uid | Device ID within driver to open. For mapper, this
  37. should always be zero.
  38. @parm UINT | umsg | Message to process. This should be one of the
  39. #define'd MODM_xxx messages.
  40. @parm DWORD | dwUser | Points to a DWORD where the driver (us) can
  41. save instance data. This will store the near pointer to our
  42. instance. On every other message, this will contain the instance
  43. data.
  44. @parm DWORD | dwParam1 | Message specific parameters.
  45. @parm DWORD | dwParam2 | Message specific parameters.
  46. @comm This function MUST be in a fixed segment since short messages
  47. are allowed to be sent at interrupt time.
  48. @rdesc | MMSYSERR_xxx.
  49. ***************************************************************************/
  50. DWORD FNEXPORT modMessage(
  51. UINT uid,
  52. UINT umsg,
  53. DWORD_PTR dwUser,
  54. DWORD_PTR dwParam1,
  55. DWORD_PTR dwParam2)
  56. {
  57. BYTE bs;
  58. PINSTANCE pinstance;
  59. // UINT uDeviceID;
  60. PPORT pport;
  61. MMRESULT mmrc;
  62. MMRESULT mmrc2;
  63. DWORD dwResult;
  64. if (0 != uid)
  65. {
  66. DPF(1, TEXT ("Mapper called with non-zero uid!"));
  67. return MMSYSERR_BADDEVICEID;
  68. }
  69. pinstance = (PINSTANCE)(UINT_PTR)(dwUser);
  70. switch(umsg)
  71. {
  72. case MODM_GETDEVCAPS:
  73. return modGetDevCaps((LPMIDIOUTCAPS)dwParam1,
  74. (DWORD)dwParam2);
  75. case MODM_OPEN:
  76. return modOpen((PDWORD_PTR)dwUser,
  77. (LPMIDIOPENDESC)dwParam1,
  78. (DWORD)dwParam2);
  79. case MODM_CLOSE:
  80. return modClose((PINSTANCE)dwUser);
  81. case MODM_DATA:
  82. assert(NULL != pinstance);
  83. // In cooked mode, don't allow non-status short messages.
  84. // Otherwise (packed mode) maintain running status.
  85. //
  86. // TESTTEST -- Make sure running status works properly in
  87. // MIDI_IO_PACKED - essential for backwards compatibility!!!
  88. //
  89. bs = MSG_STATUS(dwParam1);
  90. if (pinstance->fdwOpen & MIDI_IO_COOKED)
  91. {
  92. bs = MSG_STATUS(dwParam1);
  93. if (!IS_STATUS(bs))
  94. {
  95. DPF(1, TEXT ("Non-status short msg while opened in MIDI_IO_COOKED!"));
  96. return MMSYSERR_INVALPARAM;
  97. }
  98. }
  99. else
  100. {
  101. // Track running status
  102. //
  103. if (IS_STATUS(bs))
  104. {
  105. // Do not use real-time messages as the status
  106. // byte of the next message.
  107. if (!IS_REAL_TIME(bs))
  108. {
  109. pinstance->bRunningStatus = bs;
  110. }
  111. }
  112. else
  113. dwParam1 = (dwParam1 << 8) | (pinstance->bRunningStatus);
  114. }
  115. return MapSingleEvent((PINSTANCE)dwUser,
  116. (DWORD)dwParam1,
  117. MSE_F_SENDEVENT,
  118. NULL);
  119. case MODM_LONGDATA:
  120. assert(NULL != pinstance);
  121. // return modLongMsg(pinstance, (LPMIDIHDR)dwParam1);
  122. return modMapLongMsg (pinstance, (LPMIDIHDR)dwParam1);
  123. case MODM_PREPARE:
  124. assert(NULL != pinstance);
  125. return modPrepare((LPMIDIHDR)dwParam1);
  126. case MODM_UNPREPARE:
  127. assert(NULL != pinstance);
  128. return modUnprepare((LPMIDIHDR)dwParam1);
  129. case MODM_GETVOLUME:
  130. if (!IS_ALLOWVOLUME)
  131. return MMSYSERR_NOTSUPPORTED;
  132. *(LPDWORD)dwParam1 = gdwVolume;
  133. return MMSYSERR_NOERROR;
  134. case MODM_SETVOLUME:
  135. if (!IS_ALLOWVOLUME)
  136. return MMSYSERR_NOTSUPPORTED;
  137. gdwVolume = (DWORD)dwParam1;
  138. if (ghMidiStrm)
  139. return midiOutSetVolume((HMIDIOUT)ghMidiStrm, (DWORD)dwParam1);
  140. return modSetVolume((DWORD)dwParam1);
  141. case MODM_PROPERTIES:
  142. assert(NULL != pinstance);
  143. return midiStreamProperty(ghMidiStrm, (LPVOID)dwParam1, (DWORD)dwParam2);
  144. case MODM_STRMDATA:
  145. assert(NULL != pinstance);
  146. return MapCookedBuffer(pinstance, (LPMIDIHDR)dwParam1);
  147. case MODM_RESET:
  148. assert(NULL != pinstance);
  149. if (ghMidiStrm)
  150. return midiOutReset((HMIDIOUT)ghMidiStrm);
  151. mmrc = MMSYSERR_NOERROR;
  152. for (pport = gpportList; pport; pport=pport->pNext)
  153. if (MMSYSERR_NOERROR != (mmrc2 =
  154. midiOutReset(pport->hmidi)))
  155. mmrc = mmrc2;
  156. return mmrc;
  157. case MODM_GETPOS:
  158. assert(NULL != pinstance);
  159. return modGetPosition((PINSTANCE)pinstance,
  160. (LPMMTIME)dwParam1,
  161. (DWORD)dwParam2 /* cbmmtime */);
  162. case MODM_PAUSE:
  163. assert(NULL != pinstance);
  164. return midiStreamPause(ghMidiStrm);
  165. case MODM_RESTART:
  166. assert(NULL != pinstance);
  167. return midiStreamRestart(ghMidiStrm);
  168. case MODM_STOP:
  169. assert(NULL != pinstance);
  170. return midiStreamStop(ghMidiStrm);
  171. case MODM_CACHEPATCHES:
  172. assert(NULL != pinstance);
  173. if (!IS_ALLOWCACHE)
  174. return MMSYSERR_NOTSUPPORTED;
  175. if (ghMidiStrm)
  176. return midiOutCachePatches(
  177. (HMIDIOUT)ghMidiStrm, // hmidi
  178. HIWORD(dwParam2), // wBank
  179. (WORD FAR *)dwParam1, // lpPatchArray
  180. LOWORD(dwParam2)); // wFlags
  181. mmrc = MMSYSERR_NOERROR;
  182. for (pport = gpportList; pport; pport=pport->pNext)
  183. if (MMSYSERR_NOERROR != (mmrc2 =
  184. midiOutCachePatches(
  185. pport->hmidi, // hmidi
  186. HIWORD(dwParam2), // wBank
  187. (WORD FAR *)dwParam1, // lpPatchArray
  188. LOWORD(dwParam2))) && // wFlags
  189. MMSYSERR_NOTSUPPORTED != mmrc2)
  190. mmrc = mmrc2;
  191. return mmrc;
  192. case MODM_CACHEDRUMPATCHES:
  193. assert(NULL != pinstance);
  194. if (!IS_ALLOWCACHE)
  195. return MMSYSERR_NOTSUPPORTED;
  196. if (ghMidiStrm)
  197. return midiOutCacheDrumPatches(
  198. (HMIDIOUT)ghMidiStrm, // hmidi
  199. HIWORD(dwParam2), // wBank
  200. (WORD FAR *)dwParam1, // lpKeyArray
  201. LOWORD(dwParam2)); // wFlags
  202. mmrc = MMSYSERR_NOERROR;
  203. for (pport = gpportList; pport; pport=pport->pNext)
  204. if (MMSYSERR_NOERROR != (mmrc2 =
  205. midiOutCacheDrumPatches(
  206. pport->hmidi, // hmidi
  207. HIWORD(dwParam2), // wBank
  208. (WORD FAR *)dwParam1, // lpKeyArray
  209. LOWORD(dwParam2))) && // wFlags
  210. MMSYSERR_NOTSUPPORTED != mmrc2)
  211. mmrc = mmrc2;
  212. return mmrc;
  213. case DRVM_MAPPER_RECONFIGURE:
  214. DPF(2, TEXT ("DRV_RECONFIGURE"));
  215. // Prevent Synchronization problems during Configuration
  216. if (NULL != hMutexConfig) WaitForSingleObject (hMutexConfig, INFINITE);
  217. dwResult = UpdateInstruments(TRUE, (DWORD)dwParam2);
  218. if (NULL != hMutexConfig) ReleaseMutex (hMutexConfig);
  219. return dwResult;
  220. }
  221. return MMSYSERR_NOTSUPPORTED;
  222. }
  223. /***************************************************************************
  224. @doc internal
  225. @api void | modmCallback | Callback for completion of sending of
  226. long messages. This function conforms to the definition in the SDK.
  227. @parm HMIDIOUT | hmo | The MMSYSTEM handle of the device which
  228. complete sending.
  229. @parm WORD | wmsg | Contains a MOM_xxx code signifying what event
  230. occurred. We only care about MOM_DONE.
  231. @parm DWORD | dwInstance | DWORD of instance data given at open time;
  232. this contains the PPORT which owns the handle.
  233. @parm DWORD | dwParam1 | Message specific parameters. For MOM_DONE,
  234. this contains a far pointer to the header which completed.
  235. @parm DWORD | dwParam2 | Message specific parameters. Contains
  236. nothinf for MOM_DONE.
  237. @comm This function MUST be in a fixed segment since the driver
  238. may call it at interrupt time.
  239. ***************************************************************************/
  240. void CALLBACK _loadds modmCallback(
  241. HMIDIOUT hmo,
  242. WORD wmsg,
  243. DWORD_PTR dwInstance,
  244. DWORD_PTR dwParam1,
  245. DWORD_PTR dwParam2)
  246. {
  247. LPMIDIHDR lpmhShadow;
  248. LPMIDIHDR lpmhUser;
  249. PINSTANCE pinstance;
  250. PSHADOWBLOCK psb;
  251. LPMIDIHDR31 lpmh31;
  252. BOOL fNeedCB = FALSE;
  253. lpmhShadow = (LPMIDIHDR)dwParam1;
  254. if (wmsg == MOM_DONE && lpmhShadow)
  255. {
  256. DPF(1, TEXT ("Callback: MOM_DONE"));
  257. pinstance = (PINSTANCE)(UINT_PTR)lpmhShadow->dwReserved[MH_MAPINST];
  258. lpmhUser = (LPMIDIHDR)lpmhShadow->dwReserved[MH_SHADOWEE];
  259. if (ghMidiStrm)
  260. fNeedCB = TRUE;
  261. else
  262. {
  263. lpmh31 = (LPMIDIHDR31)lpmhUser;
  264. psb = (PSHADOWBLOCK)(UINT_PTR)lpmh31->reserved;
  265. if (0 == --psb->cRefCnt && !(lpmh31->dwFlags & MHDR_SENDING))
  266. fNeedCB = TRUE;
  267. }
  268. if (fNeedCB)
  269. {
  270. DPF(1, TEXT ("Callback: Propogating"));
  271. lpmhUser->dwFlags |= MHDR_DONE;
  272. lpmhUser->dwFlags &= ~MHDR_INQUEUE;
  273. DriverCallback(
  274. pinstance->dwCallback,
  275. HIWORD(pinstance->fdwOpen),
  276. (HANDLE)pinstance->hmidi,
  277. MM_MOM_DONE,
  278. pinstance->dwInstance,
  279. (DWORD_PTR)lpmhUser,
  280. 0L);
  281. }
  282. }
  283. else if (wmsg == MOM_POSITIONCB && lpmhShadow)
  284. {
  285. pinstance = (PINSTANCE)(UINT_PTR)lpmhShadow->dwReserved[MH_MAPINST];
  286. lpmhUser = (LPMIDIHDR)lpmhShadow->dwReserved[MH_SHADOWEE];
  287. if (!ghMidiStrm)
  288. {
  289. DPF(0, TEXT ("Got MOM_POSITIONCB on non-stream handle?"));
  290. return;
  291. }
  292. lpmhUser->dwOffset = lpmhShadow->dwOffset;
  293. DriverCallback(
  294. pinstance->dwCallback,
  295. HIWORD(pinstance->fdwOpen),
  296. (HANDLE)pinstance->hmidi,
  297. MM_MOM_POSITIONCB,
  298. pinstance->dwInstance,
  299. (DWORD_PTR)lpmhUser,
  300. 0L);
  301. }
  302. }
  303. /***************************************************************************
  304. @doc internal
  305. @api DWORD | MapSingleEvent | Map and possibly send a short message.
  306. @parm PINSTANCE | pinstance | Pointer to an open instance.
  307. @parm DWORD | dwData | Contains the short message to transmit.
  308. @parm DWORD | fdwFlags | One of the the following values:
  309. @flag MSE_F_SENDEVENT | Send the event to the physical channel
  310. @flag MSE_F_RETURNEVENT | Return the event to be re-packed into
  311. a buffer.
  312. @comm Running status should be taken care of before we get
  313. called.
  314. @rdesc | Some MMSYSERR_xxx code if MSE_F_SENDEVENT; otherwise the
  315. mapped event if no error, 0L on error.
  316. ***************************************************************************/
  317. DWORD FNGLOBAL MapSingleEvent(
  318. PINSTANCE pinstance,
  319. DWORD dwData,
  320. DWORD fdwFlags,
  321. DWORD BSTACK * pdwStreamID)
  322. {
  323. BYTE bMsg;
  324. BYTE bChan;
  325. BYTE b1;
  326. BYTE b2;
  327. PCHANNEL pchannel;
  328. MMRESULT mmr;
  329. BOOL frtm; // isrealtimemessage.
  330. // Extract message type and channel number.
  331. //
  332. bMsg = MSG_STATUS(dwData);
  333. frtm = IS_REAL_TIME(bMsg);
  334. bChan = MSG_CHAN(bMsg);
  335. bMsg = MSG_EVENT(bMsg);
  336. // Ignore sysex messages.
  337. // (MIDI_SYSEX == bMsg) will also eliminate real time
  338. // messages. Therefore real-time messages are special cased
  339. //
  340. if (MIDI_SYSEX == bMsg && !frtm)
  341. return !(fdwFlags & MSE_F_RETURNEVENT) ? MMSYSERR_NOERROR : (((DWORD)MEVT_NOP)<<24);
  342. if (NULL == (pchannel = gapChannel[bChan]))
  343. return !(fdwFlags & MSE_F_RETURNEVENT) ? MMSYSERR_NOERROR : (((DWORD)MEVT_NOP)<<24);
  344. bChan = (BYTE)pchannel->uChannel;
  345. if (pdwStreamID)
  346. *pdwStreamID = pchannel->dwStreamID;
  347. switch(bMsg)
  348. {
  349. case MIDI_NOTEOFF:
  350. case MIDI_NOTEON:
  351. b1 = MSG_PARM1(dwData);
  352. b2 = MSG_PARM2(dwData);
  353. if (NULL != pchannel->pbKeyMap)
  354. b1 = pchannel->pbKeyMap[b1];
  355. dwData = MSG_PACK2(bMsg|bChan, b1, b2);
  356. break;
  357. case MIDI_POLYPRESSURE:
  358. case MIDI_CONTROLCHANGE:
  359. case MIDI_PITCHBEND:
  360. b1 = MSG_PARM1(dwData);
  361. b2 = MSG_PARM2(dwData);
  362. dwData = MSG_PACK2(bMsg|bChan, b1, b2);
  363. break;
  364. case MIDI_PROGRAMCHANGE:
  365. b1 = MSG_PARM1(dwData);
  366. if (NULL != pchannel->pbPatchMap)
  367. b1 = pchannel->pbPatchMap[b1];
  368. dwData = MSG_PACK1(bMsg|bChan, b1);
  369. break;
  370. }
  371. if (!(fdwFlags & MSE_F_RETURNEVENT))
  372. {
  373. if (dwData)
  374. {
  375. if (ghMidiStrm)
  376. mmr = midiOutShortMsg((HMIDIOUT)ghMidiStrm, dwData);
  377. else
  378. mmr = midiOutShortMsg(pchannel->pport->hmidi, dwData);
  379. if (MMSYSERR_NOERROR != mmr)
  380. {
  381. DPF(1, TEXT ("midiOutShortMsg(%04X, %08lX) -> %u"), (WORD)(pchannel->pport->hmidi), dwData, (UINT)mmr);
  382. }
  383. }
  384. return mmr;
  385. }
  386. else
  387. return dwData;
  388. }
  389. /***************************************************************************
  390. @doc internal
  391. @api DWORD | modLongMsg | Handle MODM_LONGDATA in compatibility mode.
  392. @parm LPMIDIHDR | lpmh | The header to broadcast.
  393. @comm Propogate the header across all drivers. <f modmCallback> handles
  394. counting the returning callbacks and making sure the caller only gets
  395. one.
  396. @rdesc | Some MMSYSERR_xxx code if MSE_F_SENDEVENT; otherwise the
  397. mapped event if no error, 0L on error.
  398. ***************************************************************************/
  399. DWORD FNLOCAL modLongMsg(
  400. PINSTANCE pinstance,
  401. LPMIDIHDR lpmh)
  402. {
  403. WORD wIntStat;
  404. LPMIDIHDR lpmhWork;
  405. PPORT pport;
  406. MMRESULT mmrc = MMSYSERR_NOERROR;
  407. BOOL fNeedCB = FALSE;
  408. LPMIDIHDR31 lpmh31 = (LPMIDIHDR31)lpmh;
  409. PSHADOWBLOCK psb;
  410. if (ghMidiStrm)
  411. psb = (PSHADOWBLOCK)(UINT_PTR)lpmh->dwReserved[MH_SHADOW];
  412. else
  413. psb = (PSHADOWBLOCK)(UINT_PTR)lpmh31->reserved;
  414. lpmhWork = psb->lpmhShadow;
  415. lpmhWork->dwReserved[MH_MAPINST] = (DWORD_PTR)pinstance;
  416. if (ghMidiStrm)
  417. {
  418. lpmhWork->dwBufferLength = lpmh->dwBufferLength;
  419. return midiOutLongMsg((HMIDIOUT)ghMidiStrm,
  420. lpmhWork,
  421. sizeof(*lpmhWork));
  422. }
  423. lpmh->dwFlags |= MHDR_SENDING;
  424. psb->cRefCnt = 0;
  425. DPF(1, TEXT ("LongMsg: User hdr %p Shadow %p"), lpmh, lpmhWork);
  426. for (pport = gpportList; pport; pport=pport->pNext, lpmhWork++)
  427. {
  428. lpmhWork->dwBufferLength = lpmh->dwBufferLength;
  429. mmrc = midiOutLongMsg(pport->hmidi, lpmhWork, sizeof(*lpmhWork));
  430. if (MMSYSERR_NOERROR != mmrc)
  431. {
  432. // Don't turn off MHDR_SENDING; this will prevent any callbacks
  433. // from being propogated to the user.
  434. return mmrc;
  435. }
  436. ++psb->cRefCnt;
  437. }
  438. // Wait for synchronization object
  439. WaitForSingleObject (hMutexRefCnt, INFINITE);
  440. // Do we need to do callback
  441. if (0 == psb->cRefCnt)
  442. fNeedCB = TRUE;
  443. // Release synchronization object
  444. ReleaseMutex (hMutexRefCnt);
  445. if (fNeedCB)
  446. {
  447. lpmh->dwFlags |= MHDR_DONE;
  448. DriverCallback(
  449. pinstance->dwCallback,
  450. HIWORD(pinstance->fdwOpen),
  451. (HANDLE)pinstance->hmidi,
  452. MM_MOM_DONE,
  453. pinstance->dwInstance,
  454. (DWORD_PTR)lpmh,
  455. 0L);
  456. }
  457. return MMSYSERR_NOERROR;
  458. }
  459. /***************************************************************************
  460. @doc internal
  461. @api DWORD | modMapLongMsg | Handle MODM_LONGDATA in compatibility mode.
  462. @parm LPMIDIHDR | lpmh | The header to broadcast.
  463. @comm if a SYSEXE event Propogate the header across all drivers.
  464. <f modmCallback> handles counting the returning callbacks and making sure the caller only gets
  465. one. Otherwise, parse the Long Message into a bunch of short messages
  466. and Map each one individually.
  467. @rdesc | Some MMSYSERR_xxx code if MSE_F_SENDEVENT; otherwise the
  468. mapped event if no error, 0L on error.
  469. ***************************************************************************/
  470. DWORD FNLOCAL modMapLongMsg (
  471. PINSTANCE pinstance,
  472. LPMIDIHDR lpmh)
  473. {
  474. WORD wIntStat;
  475. LPMIDIHDR lpmhWork;
  476. PPORT pport;
  477. MMRESULT mmrc = MMSYSERR_NOERROR;
  478. BOOL fNeedCB = FALSE;
  479. LPMIDIHDR31 lpmh31 = (LPMIDIHDR31)lpmh;
  480. PSHADOWBLOCK psb;
  481. LPBYTE pbData; // Pointer to Data
  482. BYTE bMsg;
  483. UINT uMessageLength;
  484. LPBYTE pbTrans; // Pointer to Translation Buffer
  485. DWORD dwCurr;
  486. DWORD dwLength;
  487. DWORD dwMsg;
  488. DWORD dwBuffLen;
  489. INT rMsg;
  490. // Get Shadow Block
  491. if (ghMidiStrm)
  492. psb = (PSHADOWBLOCK)(UINT_PTR)lpmh->dwReserved[MH_SHADOW];
  493. else
  494. psb = (PSHADOWBLOCK)(UINT_PTR)lpmh31->reserved;
  495. lpmhWork = psb->lpmhShadow;
  496. lpmhWork->dwReserved[MH_MAPINST] = (DWORD_PTR)pinstance;
  497. // Check for MIDI streaming
  498. if (ghMidiStrm)
  499. {
  500. lpmhWork->dwBufferLength = lpmh->dwBufferLength;
  501. return midiOutLongMsg((HMIDIOUT)ghMidiStrm,
  502. lpmhWork,
  503. sizeof(*lpmhWork));
  504. }
  505. lpmh->dwFlags |= MHDR_SENDING;
  506. psb->cRefCnt = 0;
  507. DPF(1, TEXT ("MapLongMsg: User hdr %p Shadow %p"), lpmh, lpmhWork);
  508. pbData = lpmhWork->lpData;
  509. bMsg = MSG_EVENT(*pbData);
  510. if (MIDI_SYSEX == bMsg)
  511. {
  512. // Broadcast SYSEX message to all active ports
  513. for (pport = gpportList; pport; pport=pport->pNext, lpmhWork++)
  514. {
  515. lpmhWork->dwBufferLength = lpmh->dwBufferLength;
  516. mmrc = midiOutLongMsg(pport->hmidi, lpmhWork, sizeof(*lpmhWork));
  517. if (MMSYSERR_NOERROR != mmrc)
  518. {
  519. // Don't turn off MHDR_SENDING; this will prevent any callbacks
  520. // from being propogated to the user.
  521. return mmrc;
  522. }
  523. ++psb->cRefCnt;
  524. }
  525. }
  526. else
  527. {
  528. // Parse and Translate list of Short messages
  529. dwBuffLen = lpmh->dwBufferLength;
  530. // Grow Translation buffer to at least this size
  531. if (!GrowTransBuffer (pinstance, dwBuffLen))
  532. {
  533. // That didn't work !!!
  534. // Default to Broadcast messages to all active ports
  535. for (pport = gpportList; pport; pport=pport->pNext, lpmhWork++)
  536. {
  537. lpmhWork->dwBufferLength = lpmh->dwBufferLength;
  538. mmrc = midiOutLongMsg(pport->hmidi, lpmhWork, sizeof(*lpmhWork));
  539. if (MMSYSERR_NOERROR != mmrc)
  540. {
  541. // Don't turn off MHDR_SENDING; this will prevent any callbacks
  542. // from being propogated to the user.
  543. return mmrc;
  544. }
  545. ++psb->cRefCnt;
  546. }
  547. }
  548. else
  549. {
  550. // Copy buffer to translation buffer
  551. pbTrans = AccessTransBuffer (pinstance);
  552. CopyMemory (pbTrans, pbData, dwBuffLen);
  553. // Parse translation buffer
  554. dwCurr = 0L;
  555. while (dwBuffLen)
  556. {
  557. // Map Event
  558. rMsg = MapEvent (&pbTrans[dwCurr], dwBuffLen, &dwLength, &dwMsg);
  559. switch (rMsg)
  560. {
  561. case MSG_SHORT:
  562. // Send Short Message
  563. MapSingleEvent(pinstance,
  564. dwMsg,
  565. MSE_F_SENDEVENT,
  566. NULL);
  567. dwCurr += dwLength;
  568. break;
  569. case MSG_LONG:
  570. //
  571. // Note: For completeness, we should probably broadcast
  572. // this, but for now assume that there are no embedded
  573. // SYSEX messages in the buffer and skip any we encounter
  574. //
  575. dwCurr += dwLength;
  576. break;
  577. default:
  578. dwCurr += dwLength;
  579. break;
  580. }
  581. dwBuffLen -= dwLength;
  582. } // End While
  583. // Release Translation Buffer
  584. ReleaseTransBuffer (pinstance);
  585. }
  586. }
  587. // Wait for synchronization object
  588. WaitForSingleObject (hMutexRefCnt, INFINITE);
  589. // Do we need to do callback
  590. if (0 == psb->cRefCnt)
  591. fNeedCB = TRUE;
  592. // Release synchronization object
  593. ReleaseMutex (hMutexRefCnt);
  594. if (fNeedCB)
  595. {
  596. lpmh->dwFlags |= MHDR_DONE;
  597. DriverCallback(
  598. pinstance->dwCallback,
  599. HIWORD(pinstance->fdwOpen),
  600. (HANDLE)pinstance->hmidi,
  601. MM_MOM_DONE,
  602. pinstance->dwInstance,
  603. (DWORD_PTR)lpmh,
  604. 0L);
  605. }
  606. return MMSYSERR_NOERROR;
  607. } // End modMapLongMsg
  608. // returns length of various MIDI messages in bytes
  609. INT FNLOCAL MapEvent (
  610. BYTE * pStatus,
  611. DWORD dwBuffSize,
  612. DWORD * pSkipBytes,
  613. DWORD * pShortMsg)
  614. {
  615. INT fResult = MSG_SHORT;
  616. BYTE bMsg = 0;
  617. BYTE bParam1 = 0;
  618. BYTE bParam2 = 0;
  619. bMsg = *pStatus;
  620. *pSkipBytes = 0;
  621. // Mask Off Channel bits
  622. switch (bMsg & 0xF0)
  623. {
  624. case MIDI_NOTEOFF:
  625. case MIDI_NOTEON:
  626. case MIDI_POLYPRESSURE:
  627. case MIDI_CONTROLCHANGE:
  628. bParam1 = *(pStatus+1);
  629. bParam2 = *(pStatus+2);
  630. *pShortMsg = MSG_PACK2(bMsg,bParam1,bParam2);
  631. *pSkipBytes = 3;
  632. break;
  633. case MIDI_PROGRAMCHANGE:
  634. case MIDI_CHANPRESSURE:
  635. bParam1 = *(pStatus+1);
  636. *pShortMsg = MSG_PACK1(bMsg,bParam1);
  637. *pSkipBytes = 2;
  638. break;
  639. case MIDI_PITCHBEND:
  640. bParam1 = *(pStatus+1);
  641. bParam2 = *(pStatus+2);
  642. *pShortMsg = MSG_PACK2(bMsg,bParam1,bParam2);
  643. *pSkipBytes = 3;
  644. break;
  645. case MIDI_SYSEX:
  646. // It's a system message
  647. // Keep counting system messages until
  648. // We don't find any more
  649. fResult = MSG_LONG;
  650. *pSkipBytes = 0;
  651. while (((bMsg & 0xF0) == 0xF0) &&
  652. (*pSkipBytes < dwBuffSize))
  653. {
  654. switch (bMsg)
  655. {
  656. case MIDI_SYSEX:
  657. // Find end of SysEx message
  658. *pSkipBytes ++;
  659. while ((*pSkipBytes < dwBuffSize) &&
  660. (pStatus[*pSkipBytes] != MIDI_SYSEXEND))
  661. {
  662. *pSkipBytes++;
  663. }
  664. break;
  665. case MIDI_QFRAME:
  666. *pSkipBytes += 2;
  667. break;
  668. case MIDI_SONGPOINTER:
  669. *pSkipBytes += 3;
  670. break;
  671. case MIDI_SONGSELECT:
  672. *pSkipBytes += 2;
  673. break;
  674. case MIDI_F4: // Undefined message
  675. case MIDI_F5: // Undefined message
  676. case MIDI_TUNEREQUEST:
  677. case MIDI_SYSEXEND: // Not really a message, but skip it
  678. case MIDI_TIMINGCLOCK:
  679. case MIDI_F9: // Undefined Message
  680. case MIDI_START:
  681. case MIDI_CONTINUE:
  682. case MIDI_STOP:
  683. case MIDI_FD: // Undefined Message
  684. case MIDI_ACTIVESENSING:
  685. case MIDI_META: // Is this how handle this message ?!?
  686. *pSkipBytes += 1;
  687. break;
  688. } // End Switch
  689. if (*pSkipBytes < dwBuffSize)
  690. bMsg = pStatus[*pSkipBytes];
  691. } // End While
  692. break;
  693. default:
  694. // Unknown just increment skip count
  695. fResult = MSG_UNKNOWN;
  696. *pSkipBytes = 1;
  697. break;
  698. } // End switch
  699. // Truncate to end of buffer
  700. if (*pSkipBytes > dwBuffSize)
  701. *pSkipBytes = dwBuffSize;
  702. return fResult;
  703. } // End MapEvent
  704. // Create Translation buffer
  705. BOOL FNGLOBAL InitTransBuffer (PINSTANCE pinstance)
  706. {
  707. if (!pinstance)
  708. return FALSE;
  709. InitializeCriticalSection (& (pinstance->csTrans));
  710. EnterCriticalSection (&(pinstance->csTrans));
  711. pinstance->pTranslate = NULL;
  712. pinstance->cbTransSize = 0;
  713. LeaveCriticalSection (&(pinstance->csTrans));
  714. return TRUE;
  715. } // End InitTransBuffer
  716. // Cleanup Translation Buffer
  717. BOOL FNGLOBAL CleanupTransBuffer (PINSTANCE pinstance)
  718. {
  719. if (!pinstance)
  720. return FALSE;
  721. EnterCriticalSection (&(pinstance->csTrans));
  722. if (pinstance->pTranslate)
  723. {
  724. LocalFree((HLOCAL)(pinstance->pTranslate));
  725. pinstance->pTranslate = NULL;
  726. pinstance->cbTransSize = 0L;
  727. }
  728. LeaveCriticalSection (&(pinstance->csTrans));
  729. DeleteCriticalSection (&(pinstance->csTrans));
  730. return TRUE;
  731. } // End CleanupTransBuffer
  732. // Get Pointer to translation buffer
  733. LPBYTE AccessTransBuffer (PINSTANCE pinstance)
  734. {
  735. if (!pinstance)
  736. return NULL;
  737. EnterCriticalSection (&(pinstance->csTrans));
  738. return pinstance->pTranslate;
  739. } // End AccessTransBuffer
  740. // Release pointer to translation buffer
  741. void FNGLOBAL ReleaseTransBuffer (PINSTANCE pinstance)
  742. {
  743. if (!pinstance)
  744. return;
  745. LeaveCriticalSection (&(pinstance->csTrans));
  746. } // End ReleaseTransBuffer
  747. // Resize Translation buffer
  748. BOOL FNGLOBAL GrowTransBuffer (PINSTANCE pinstance, DWORD cbNewSize)
  749. {
  750. LPBYTE pNew;
  751. if (!pinstance)
  752. return FALSE;
  753. EnterCriticalSection (&(pinstance->csTrans));
  754. // Do we even need to grow buffer
  755. if (cbNewSize > pinstance->cbTransSize)
  756. {
  757. pNew = (LPBYTE)LocalAlloc(LPTR, cbNewSize);
  758. if (!pNew)
  759. {
  760. LeaveCriticalSection (&(pinstance->csTrans));
  761. return FALSE;
  762. }
  763. // Remove old translation buffer, if any
  764. if (pinstance->pTranslate)
  765. LocalFree ((HLOCAL)(pinstance->pTranslate));
  766. // Assign new buffer
  767. pinstance->pTranslate = pNew;
  768. pinstance->cbTransSize = cbNewSize;
  769. }
  770. LeaveCriticalSection (&(pinstance->csTrans));
  771. return TRUE;
  772. } // End GrowTransBuffer