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.

704 lines
19 KiB

  1. /**********************************************************************
  2. Copyright (c) 1992-1999 Microsoft Corporation
  3. modmsg.c
  4. DESCRIPTION:
  5. Non-fixed code for doing output mapping. Keep out of the reach
  6. of children. This prescription may be refilled twice. May cause
  7. temporary distortion of reality in your vicinity.
  8. HISTORY:
  9. 02/21/94 [jimge] created.
  10. *********************************************************************/
  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 <mmreg.h>
  18. #include <memory.h>
  19. #include "midimap.h"
  20. #include "res.h"
  21. #include "debug.h"
  22. //=========================== Globals ======================================
  23. //
  24. extern HANDLE hMutexConfig; // Located in DRVPROC.C
  25. BOOL gfReconfigured = FALSE;
  26. //=========================== Prototypes ===================================
  27. //
  28. PRIVATE MMRESULT FNLOCAL SendChannelInitString(
  29. HMIDIOUT hmidi,
  30. PBYTE pbinit,
  31. DWORD cbinit);
  32. /***************************************************************************
  33. @doc internal
  34. @api DWORD | modGetDevCaps | Handles the MODM_GETDEVCAPS message.
  35. @parm LPMIDIOUTCAPS | pmoc | Pointer to a caps structure to fill in.
  36. @parm DWORD | cbmoc | How big the caller thinks the structure is.
  37. @rdesc Some MMSYSERR_xxx code.
  38. ***************************************************************************/
  39. DWORD FNGLOBAL modGetDevCaps(
  40. LPMIDIOUTCAPS pmoc,
  41. DWORD cbmoc)
  42. {
  43. MIDIOUTCAPS moc;
  44. MIDIOUTCAPS mocWork;
  45. DWORD cbCopy;
  46. WORD wMask;
  47. UINT idx;
  48. MMRESULT mmr;
  49. PPORT pport;
  50. moc.wMid = MM_MICROSOFT;
  51. moc.wPid = MM_MIDI_MAPPER;
  52. moc.vDriverVersion = 0x0500;
  53. LoadString(ghinst, IDS_MIDIMAPPER, moc.szPname, sizeof(moc.szPname)/sizeof(moc.szPname[0]));
  54. moc.wTechnology = MOD_MAPPER;
  55. moc.wVoices = 0;
  56. moc.wNotes = 0;
  57. moc.wChannelMask = 0;
  58. wMask = 1;
  59. for (idx = 0; idx < MAX_CHANNELS; idx++)
  60. {
  61. if (gapChannel[idx])
  62. moc.wChannelMask |= wMask;
  63. wMask <<= 1;
  64. }
  65. // If any underlying device supports cache patches, we must support it.
  66. // Only support volume or lrvolume, however, if ALL devices support it.
  67. //
  68. do
  69. {
  70. gfReconfigured = FALSE;
  71. moc.dwSupport = MIDICAPS_STREAM|MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
  72. for (pport = gpportList; pport; pport=pport->pNext)
  73. {
  74. mmr = midiOutGetDevCaps(pport->uDeviceID, &mocWork, sizeof(mocWork));
  75. // This prevents a corrupt gpportList in the event of a pnp event
  76. // during a midiOutGetDevCaps call...
  77. if (gfReconfigured)
  78. break;
  79. if (MMSYSERR_NOERROR != mmr)
  80. continue;
  81. if (!(mocWork.dwSupport & MIDICAPS_LRVOLUME))
  82. moc.dwSupport &= ~MIDICAPS_LRVOLUME;
  83. if (!(mocWork.dwSupport & MIDICAPS_VOLUME))
  84. moc.dwSupport &= ~(MIDICAPS_VOLUME|MIDICAPS_LRVOLUME);
  85. moc.dwSupport |= (mocWork.dwSupport & MIDICAPS_CACHE);
  86. }
  87. }
  88. while (gfReconfigured);
  89. CLR_ALLOWVOLUME;
  90. if (moc.dwSupport & MIDICAPS_VOLUME)
  91. SET_ALLOWVOLUME;
  92. CLR_ALLOWCACHE;
  93. if (moc.dwSupport & MIDICAPS_CACHE)
  94. SET_ALLOWCACHE;
  95. cbCopy = min(cbmoc, sizeof(moc));
  96. hmemcpy((LPSTR)pmoc, (LPSTR)&moc, cbCopy);
  97. return MMSYSERR_NOERROR;
  98. }
  99. /***************************************************************************
  100. @doc internal
  101. @api DWORD | modOpen | Handles the MODM_OPEN message.
  102. @parm LPDWORD | lpdwInstance | Points to a DWORD where we can store
  103. our instance data. We save our PINSTANCE here.
  104. @parm LPMIDIOPENDESC | lpmidiopendesc | Points to parameters from
  105. MMSYSTEM describing the caller's callback, etc.
  106. @parm DWORD | fdwOpen | Flags describing the callback type.
  107. @rdesc Some MMSYSERR_xxx code.
  108. ***************************************************************************/
  109. DWORD FNGLOBAL modOpen(
  110. PDWORD_PTR lpdwInstance,
  111. LPMIDIOPENDESC lpmidiopendesc,
  112. DWORD fdwOpen)
  113. {
  114. PINSTANCE pinstance = NULL;
  115. PPORT pport;
  116. MMRESULT mmrc = MMSYSERR_NOERROR;
  117. UINT idx;
  118. UINT idx2;
  119. UINT auDeviceID[MAX_CHANNELS];
  120. // Open in MIDI_IO_CONTROL -- only allows reconfigure message. This MUST
  121. // ALWAYS succeed so we have a chance of recovery on reconfigure no matter
  122. // how badly messed up the old config was.
  123. //
  124. if (!(fdwOpen & MIDI_IO_CONTROL))
  125. {
  126. if (IS_DEVSOPENED)
  127. return MMSYSERR_ALLOCATED;
  128. // Mapper is single instance now
  129. //
  130. assert(NULL == gpinstanceList);
  131. }
  132. else
  133. {
  134. if (NULL != gpIoctlInstance)
  135. return MMSYSERR_ALLOCATED;
  136. }
  137. #ifdef DEBUG
  138. if (fdwOpen & MIDI_IO_COOKED)
  139. DPF(2, TEXT ("Mapper opened in polymsg mode!!!"));
  140. #endif
  141. // Alloc this zero-init so all the initial channel mappings in
  142. // rgpChannel are NULL.
  143. //
  144. if (NULL == (pinstance = (PINSTANCE)LocalAlloc(LPTR, sizeof(INSTANCE))))
  145. return MMSYSERR_NOMEM;
  146. pinstance->hmidi = lpmidiopendesc->hMidi;
  147. pinstance->dwCallback = lpmidiopendesc->dwCallback;
  148. pinstance->dwInstance = lpmidiopendesc->dwInstance;
  149. pinstance->fdwOpen = fdwOpen;
  150. // Create Translation buffer
  151. if (! InitTransBuffer (pinstance))
  152. {
  153. LocalFree ((HGLOBAL)pinstance);
  154. return MMSYSERR_NOMEM;
  155. }
  156. if (fdwOpen & MIDI_IO_CONTROL)
  157. {
  158. *lpdwInstance = (DWORD_PTR)pinstance;
  159. gpIoctlInstance = pinstance;
  160. DriverCallback(
  161. pinstance->dwCallback,
  162. HIWORD(pinstance->fdwOpen),
  163. (HDRVR)(pinstance->hmidi),
  164. MM_MOM_OPEN,
  165. pinstance->dwInstance,
  166. 0L,
  167. 0L);
  168. return MMSYSERR_NOERROR;
  169. }
  170. DPF(2, TEXT ("modOpen pinstance %04X"), (WORD)pinstance);
  171. *lpdwInstance = 0; // Assume failure
  172. if (IS_CONFIGERR)
  173. {
  174. DPF(1, TEXT ("Open failed because configuration invalid"));
  175. mmrc = MIDIERR_NOMAP;
  176. goto midi_Out_Open_Cleanup;
  177. }
  178. if (fdwOpen & MIDI_IO_COOKED)
  179. {
  180. // Build list of device ID's (stream id's emuerate ports) and
  181. // assign stream id's to channels.
  182. //
  183. for (idx = 0, pport = gpportList; pport; idx++,pport=pport->pNext)
  184. {
  185. auDeviceID[idx] = pport->uDeviceID;
  186. for (idx2 = 0; idx2 < MAX_CHANNELS; idx2++)
  187. if (gapChannel[idx2] && gapChannel[idx2]->pport == pport)
  188. gapChannel[idx2]->dwStreamID = (DWORD)idx;
  189. }
  190. // Attempt to open.
  191. //
  192. mmrc = midiStreamOpen(&ghMidiStrm, auDeviceID, idx, (DWORD_PTR)modmCallback, 0L, CALLBACK_FUNCTION);
  193. // Fall through to cleanup code
  194. //
  195. }
  196. else
  197. {
  198. // Run through the port list and try to open all referenced ports
  199. //
  200. for (pport = gpportList; pport; pport=pport->pNext)
  201. {
  202. if (NULL == pport->hmidi)
  203. {
  204. mmrc = midiOutOpen(&pport->hmidi,
  205. pport->uDeviceID,
  206. (DWORD_PTR)modmCallback,
  207. (DWORD_PTR)pport,
  208. CALLBACK_FUNCTION|MIDI_IO_SHARED);
  209. if (MMSYSERR_NOERROR != mmrc)
  210. {
  211. DPF(1, TEXT ("Could not open pport %04X device %u"), (WORD)pport, pport->uDeviceID);
  212. // Just in case....
  213. //
  214. pport->hmidi = NULL;
  215. for (pport = gpportList; pport; pport=pport->pNext)
  216. if (NULL != pport->hmidi)
  217. {
  218. midiOutClose(pport->hmidi);
  219. pport->hmidi = NULL;
  220. }
  221. // Return whatever caused the underlying open to fail
  222. //
  223. break;
  224. }
  225. }
  226. }
  227. }
  228. midi_Out_Open_Cleanup:
  229. if (MMSYSERR_NOERROR != mmrc)
  230. {
  231. // Cleanup
  232. CleanupTransBuffer (pinstance);
  233. if (pinstance) LocalFree((HLOCAL)pinstance);
  234. return mmrc;
  235. }
  236. gdwVolume = 0xFFFFFFFFL;
  237. SET_DEVSOPENED;
  238. // We've succeeded; put the instance into the global instance list
  239. // and return it as our instance data.
  240. //
  241. pinstance->pNext = gpinstanceList;
  242. gpinstanceList = pinstance;
  243. *lpdwInstance = (DWORD_PTR)pinstance;
  244. // Lock the segments we need. If we're doing packed mode mapping,
  245. // we don't need the cooked mode segment in memory. However, the
  246. // cooked mode mapper DOES call the packed routines, so we need to
  247. // lock both in that case.
  248. //
  249. if (fdwOpen & MIDI_IO_COOKED)
  250. {
  251. LockMapperData();
  252. LockPackedMapper();
  253. LockCookedMapper();
  254. }
  255. else
  256. {
  257. LockMapperData();
  258. LockPackedMapper();
  259. }
  260. // Do the (useless) callback
  261. //
  262. DriverCallback(
  263. pinstance->dwCallback,
  264. HIWORD(pinstance->fdwOpen),
  265. (HDRVR)(pinstance->hmidi),
  266. MM_MOM_OPEN,
  267. pinstance->dwInstance,
  268. 0L,
  269. 0L);
  270. return MMSYSERR_NOERROR;
  271. }
  272. /***************************************************************************
  273. @doc internal
  274. @api DWORD | modPrepare | Handles the MODM_PREPARE message.
  275. @parm LPMIDIHDR | lpmh | The user header to prepare.
  276. @rdesc Some MMSYSERR_xxx code.
  277. @comm
  278. Create some shadow headers.
  279. For the case of mapper opened for stream
  280. we only require one shadow header that we can pass along to our
  281. mapped-to stream. We need this because the mapped-to stream and
  282. the mapped-from stream will both want to use the dwReserved[]
  283. fields in the MIDIHDR.
  284. For the case of mapper opened not-for-stream
  285. This must be a long message header that we want to propogate to all
  286. ports. Therefore we have gcPorts shadow headers and each one is
  287. prepared on one node of the global port list.
  288. In either case, we return MMSYSERR_NOTSUPPORTED on success so that
  289. MMSYSTEM will take its default action and page lock the user MIDIHDR
  290. for us.
  291. ***************************************************************************/
  292. DWORD FNGLOBAL modPrepare(
  293. LPMIDIHDR lpmh)
  294. {
  295. LPMIDIHDR lpmhNew;
  296. LPMIDIHDR lpmhWork;
  297. MMRESULT mmrcRet = MMSYSERR_NOERROR;
  298. PPORT pport;
  299. PPORT pportWork;
  300. PSHADOWBLOCK psb = NULL;
  301. psb = (PSHADOWBLOCK)LocalAlloc(LPTR, sizeof(*psb));
  302. if (NULL == psb)
  303. {
  304. mmrcRet = MMSYSERR_NOMEM;
  305. goto modPrepare_Cleanup;
  306. }
  307. psb->cRefCnt = 0;
  308. psb->dwBufferLength = lpmh->dwBufferLength;
  309. if (ghMidiStrm)
  310. {
  311. psb->lpmhShadow = (LPMIDIHDR)GlobalAllocPtr(
  312. GMEM_MOVEABLE|GMEM_SHARE,
  313. sizeof(*lpmhNew));
  314. if (NULL == psb->lpmhShadow)
  315. {
  316. mmrcRet = MMSYSERR_NOMEM;
  317. goto modPrepare_Cleanup;
  318. }
  319. lpmhNew = psb->lpmhShadow;
  320. *lpmhNew = *lpmh;
  321. lpmhNew->dwReserved[MH_SHADOWEE] = (DWORD_PTR)lpmh;
  322. lpmh->dwReserved[MH_SHADOW] = (DWORD_PTR)psb;
  323. lpmhNew->dwFlags |= MHDR_SHADOWHDR;
  324. mmrcRet = midiOutPrepareHeader((HMIDIOUT)ghMidiStrm,
  325. lpmhNew,
  326. sizeof(*lpmhNew));
  327. if (MMSYSERR_NOERROR != mmrcRet)
  328. lpmh->dwReserved[MH_SHADOW] = 0;
  329. }
  330. else
  331. {
  332. LPMIDIHDR31 lpmh31 = (LPMIDIHDR31)lpmh;
  333. // Prepare shadow headers for sending to multiple non-stream
  334. // drivers
  335. //
  336. // NOTE: The parent header is a 3.1 style header; the children
  337. // are 4.0 and thus are longer.
  338. //
  339. psb->lpmhShadow = (LPMIDIHDR)GlobalAllocPtr(
  340. GMEM_MOVEABLE|GMEM_SHARE|GMEM_ZEROINIT,
  341. sizeof(*lpmhNew)*gcPorts);
  342. if (NULL == psb->lpmhShadow)
  343. {
  344. mmrcRet = MMSYSERR_NOMEM;
  345. goto modPrepare_Cleanup;
  346. }
  347. lpmhNew = psb->lpmhShadow;
  348. lpmhWork = lpmhNew;
  349. for (pport = gpportList; pport; pport = pport->pNext, lpmhWork++)
  350. {
  351. *(LPMIDIHDR31)lpmhWork = *lpmh31;
  352. lpmhWork->dwFlags |= MHDR_SHADOWHDR;
  353. mmrcRet = midiOutPrepareHeader(pport->hmidi,
  354. lpmhWork,
  355. sizeof(*lpmhWork));
  356. if (MMSYSERR_NOERROR != mmrcRet)
  357. {
  358. lpmhWork = lpmhNew;
  359. for (pportWork = gpportList; pportWork != pport; pportWork = pportWork->pNext, lpmhWork++)
  360. midiOutUnprepareHeader(pport->hmidi, lpmhWork, sizeof(*lpmhWork));
  361. goto modPrepare_Cleanup;
  362. }
  363. lpmhWork->dwReserved[MH_SHADOWEE] = (DWORD_PTR)lpmh31;
  364. }
  365. DPF(1, TEXT ("Prepare: User header %p Shadow %p"), lpmh, lpmhNew);
  366. lpmh31->reserved = (DWORD_PTR)psb;
  367. }
  368. // This will force MMSYSTEM to do default prepare on the parent header --
  369. // i.e. page lock it for us.
  370. //
  371. modPrepare_Cleanup:
  372. if (MMSYSERR_NOERROR != mmrcRet)
  373. {
  374. if (psb)
  375. {
  376. if (psb->lpmhShadow) GlobalFreePtr(psb->lpmhShadow);
  377. LocalFree((HLOCAL)psb);
  378. }
  379. }
  380. return (MMSYSERR_NOERROR != mmrcRet) ? mmrcRet : MMSYSERR_NOTSUPPORTED;
  381. }
  382. /***************************************************************************
  383. @doc internal
  384. @api DWORD | modUnprepare | Handles the MODM_UNPREPARE message.
  385. @parm LPMIDIHDR | lpmh | The user header to unprepare.
  386. @rdesc Some MMSYSERR_xxx code.
  387. @comm
  388. Fully undo the effects of the modPrepare call.
  389. Unprepare and free all shadow headers.
  390. Return MMSYSERR_NOTSUPPORTED so MMSYSTEM will correctly handle the
  391. final unprepare of the user header.
  392. ***************************************************************************/
  393. DWORD FNGLOBAL modUnprepare(
  394. LPMIDIHDR lpmh)
  395. {
  396. LPMIDIHDR lpmhNew;
  397. MMRESULT mmrc;
  398. PPORT pport;
  399. PSHADOWBLOCK psb;
  400. if (ghMidiStrm)
  401. {
  402. psb = (PSHADOWBLOCK)(UINT_PTR)lpmh->dwReserved[MH_SHADOW];
  403. lpmhNew = psb->lpmhShadow;
  404. lpmhNew->dwBufferLength = psb->dwBufferLength;
  405. mmrc = midiOutUnprepareHeader((HMIDIOUT)ghMidiStrm,
  406. lpmhNew,
  407. sizeof(*lpmhNew));
  408. if (MMSYSERR_NOERROR != mmrc)
  409. return mmrc;
  410. LocalFree((HLOCAL)psb);
  411. GlobalFreePtr(lpmhNew);
  412. lpmh->dwReserved[MH_SHADOW] = 0;
  413. }
  414. else
  415. {
  416. LPMIDIHDR31 lpmh31 = (LPMIDIHDR31)lpmh;
  417. psb = (PSHADOWBLOCK)(UINT_PTR)lpmh31->reserved;
  418. lpmhNew = psb->lpmhShadow;
  419. for (pport = gpportList; pport; pport = pport->pNext, ++lpmhNew)
  420. {
  421. lpmhNew->dwBufferLength = psb->dwBufferLength;
  422. midiOutUnprepareHeader(pport->hmidi, lpmhNew, sizeof(*lpmhNew));
  423. }
  424. GlobalFreePtr(psb->lpmhShadow);
  425. LocalFree((HLOCAL)psb);
  426. lpmh31->reserved = 0;
  427. }
  428. // Need the default action of unprepare in MMSYSTEM here
  429. //
  430. return MMSYSERR_NOTSUPPORTED;
  431. }
  432. /***************************************************************************
  433. @doc internal
  434. @api DWORD | modClose | Handles the MODM_CLOSE message.
  435. @parm PINSTANCE | pinstance | Pointer to an open instance to close.
  436. @rdesc Some MMSYSERR_xxx code.
  437. ***************************************************************************/
  438. DWORD FNGLOBAL modClose(
  439. PINSTANCE pinstance)
  440. {
  441. PPORT pport;
  442. DPF(1, TEXT ("Mapper close"));
  443. // Close underlying streams on the query can-close (which is first)
  444. // and just succeed the actual close
  445. //
  446. assert(pinstance);
  447. if (pinstance->fdwOpen & MIDI_IO_CONTROL)
  448. {
  449. assert(pinstance == gpIoctlInstance);
  450. gpIoctlInstance = NULL;
  451. goto modClose_Cleanup;
  452. }
  453. // Assert that we're the only thing in the instance list
  454. //
  455. assert(gpinstanceList == pinstance);
  456. assert(pinstance->pNext == NULL);
  457. gpinstanceList = NULL;
  458. if (pinstance->fdwOpen & MIDI_IO_COOKED)
  459. UnlockCookedMapper();
  460. UnlockMapperData();
  461. UnlockPackedMapper();
  462. if (ghMidiStrm)
  463. {
  464. midiStreamClose(ghMidiStrm);
  465. ghMidiStrm = NULL;
  466. }
  467. else
  468. {
  469. for (pport = gpportList; pport; pport = pport->pNext)
  470. {
  471. if (NULL != pport->hmidi)
  472. {
  473. midiOutClose(pport->hmidi);
  474. pport->hmidi = NULL;
  475. }
  476. }
  477. }
  478. CLR_DEVSOPENED;
  479. // If reconfigure due, do it!
  480. //
  481. if (IS_RECONFIGURE)
  482. {
  483. // Prevent Synchronization problems
  484. // During Configuration
  485. if (NULL != hMutexConfig)
  486. WaitForSingleObject (hMutexConfig, INFINITE);
  487. DPF(1, TEXT ("Delayed reconfigure now being done"));
  488. UpdateInstruments(FALSE, 0);
  489. if (NULL != hMutexConfig)
  490. ReleaseMutex (hMutexConfig);
  491. CLR_RECONFIGURE;
  492. }
  493. modClose_Cleanup:
  494. DriverCallback(
  495. pinstance->dwCallback,
  496. HIWORD(pinstance->fdwOpen),
  497. (HDRVR)(pinstance->hmidi),
  498. MM_MOM_CLOSE,
  499. pinstance->dwInstance,
  500. 0L,
  501. 0L);
  502. // Free up instance memory.
  503. //
  504. CleanupTransBuffer (pinstance);
  505. LocalFree((HLOCAL)pinstance);
  506. return MMSYSERR_NOERROR;
  507. }
  508. /***************************************************************************
  509. @doc internal
  510. @api DWORD | modGetPosition | Get the current position in the MIDI stream.
  511. @parm LPINSTANCE | pinstance | Stream we want the position in.
  512. @parm LPMMTIME | lpmmt | Pointer to a standard MMTIME struct to fill in.
  513. @parm DWORD | cbmmt | Size of the MMTIME structure passed.
  514. @comment
  515. Pass the structure along to the first open subsidiary stream.
  516. This will be considered the de facto timebase until there's a
  517. way to set it.
  518. @rdesc MMSYSERR_xxx
  519. ***************************************************************************/
  520. DWORD FNGLOBAL modGetPosition(
  521. PINSTANCE pinstance,
  522. LPMMTIME lpmmt,
  523. DWORD cbmmt)
  524. {
  525. return midiStreamPosition(ghMidiStrm,
  526. lpmmt,
  527. (UINT)cbmmt);
  528. }
  529. DWORD FNGLOBAL modSetVolume(
  530. DWORD dwVolume)
  531. {
  532. PPORT pport;
  533. MMRESULT mmrc;
  534. MMRESULT mmrc2;
  535. // Walk the port list and send the volume change to everyone
  536. //
  537. mmrc2 = MMSYSERR_NOERROR;
  538. for (pport = gpportList; pport; pport = pport->pNext)
  539. {
  540. mmrc = midiOutSetVolume(pport->hmidi, dwVolume);
  541. if (MMSYSERR_NOERROR != mmrc)
  542. mmrc2 = mmrc;
  543. }
  544. return mmrc2;
  545. }