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.

993 lines
31 KiB

  1. /*******************************************************************************
  2. *
  3. * Module Name: midistrm.c
  4. *
  5. * MIDI Streams implementation
  6. *
  7. * Created: 9 Feb 1995 SteveDav
  8. *
  9. * Copyright (c) 1995-1999 Microsoft Corporation
  10. *
  11. \******************************************************************************/
  12. #include "winmmi.h"
  13. /*
  14. * MIDI Streaming API Port: For the time being, the assumption
  15. * is that the devices are static. This code was designed to
  16. * be PnP friendly, with devices coming and going. The
  17. * validation of devices will be commented out for now, but in
  18. * the future when NT is a more dynamic OS, the validation will
  19. * need to be added back.
  20. *
  21. */
  22. extern BOOL CreatehwndNotify(VOID);
  23. CRITICAL_SECTION midiStrmHdrCritSec;
  24. WINMMAPI MMRESULT WINAPI midiDisconnect (
  25. HMIDI hmi,
  26. HMIDIOUT hmo,
  27. LPVOID lpv)
  28. {
  29. dprintf2(("midiDisconnect(%08X,%08X,%08X)", hmi, hmo, lpv));
  30. return midiInSetThru (hmi, hmo, FALSE);
  31. }
  32. WINMMAPI MMRESULT WINAPI midiConnect (
  33. HMIDI hmi,
  34. HMIDIOUT hmo,
  35. LPVOID lpv)
  36. {
  37. dprintf2(("midiConnect(%08X,%08X,%08X)", hmi, hmo, lpv));
  38. return midiInSetThru (hmi, hmo, TRUE);
  39. }
  40. /*+ midiInSetThru
  41. *
  42. * Establish a thruing midiOut handle for a midiIn device. This is
  43. * done by first calling the driver to let the driver do the thruing,
  44. * if the driver returns UNSUPPORTED a single thruing handle can
  45. * be established by simulation in DriverCallback
  46. *
  47. *-====================================================================*/
  48. MMRESULT midiInSetThru (
  49. HMIDI hmi,
  50. HMIDIOUT hmo,
  51. BOOL bAdd)
  52. {
  53. MMRESULT mmr = MMSYSERR_ERROR; // this value should never get returned....
  54. UINT uType;
  55. dprintf2(("midiInSetThru(%X,%X,%d)", hmi, hmo, bAdd));
  56. AcquireHandleListResourceShared();
  57. // allow first handle to be either midi in or midi out
  58. // (so that we can send DRVM_ADD_THRU messages to dummy
  59. // output drivers.)
  60. //
  61. // we simulate thruing only for input handles though...
  62. //
  63. if (BAD_HANDLE(hmi, TYPE_MIDIIN) && BAD_HANDLE(hmi, TYPE_MIDIOUT))
  64. {
  65. ReleaseHandleListResource();
  66. return MMSYSERR_INVALHANDLE;
  67. }
  68. uType = GetHandleType(hmi);
  69. if (bAdd)
  70. {
  71. if (BAD_HANDLE(hmo, TYPE_MIDIOUT))
  72. {
  73. ReleaseHandleListResource();
  74. return (MMSYSERR_INVALHANDLE);
  75. }
  76. // !!! Devices are static on NT for now.
  77. //
  78. //if (!mregQueryValidHandle(HtoPT(PMIDIDEV, hmo)->hmd))
  79. // return MMSYSERR_NODRIVER;
  80. mmr = (MMRESULT)midiMessage ((HMIDI)hmi, DRVM_ADD_THRU, (DWORD_PTR)(UINT_PTR)hmo, 0l);
  81. if (mmr == MMSYSERR_NOTSUPPORTED && uType == TYPE_MIDIIN)
  82. {
  83. // dont allow more than one handle to be added
  84. //
  85. if (HtoPT(PMIDIDEV, hmi)->pmThru)
  86. mmr = MIDIERR_NOTREADY;
  87. else
  88. {
  89. // add the handle.
  90. //
  91. HtoPT(PMIDIDEV, hmi)->pmThru = HtoPT(PMIDIDEV, hmo);
  92. mmr = MMSYSERR_NOERROR;
  93. }
  94. }
  95. }
  96. else
  97. {
  98. mmr = (MMRESULT)midiMessage ((HMIDI)hmi, DRVM_REMOVE_THRU, (DWORD_PTR)(UINT_PTR)hmo, 0l);
  99. if (mmr == MMSYSERR_NOTSUPPORTED && uType == TYPE_MIDIIN)
  100. mmr = MMSYSERR_NOERROR;
  101. if ( ! hmo || (PMIDIDEV)hmo == HtoPT(PMIDIDEV, hmi)->pmThru)
  102. HtoPT(PMIDIDEV, hmi)->pmThru = NULL;
  103. else
  104. mmr = MMSYSERR_INVALPARAM;
  105. }
  106. return mmr;
  107. }
  108. WINMMAPI MMRESULT WINAPI midiStreamOpen(
  109. LPHMIDISTRM phms,
  110. LPUINT puDeviceID,
  111. DWORD cMidi,
  112. DWORD_PTR dwCallback,
  113. DWORD_PTR dwInstance,
  114. DWORD fdwOpen)
  115. {
  116. PMIDISTRM pms = NULL;
  117. PMIDISTRMID pmsi;
  118. PMIDISTRMID pmsiSave;
  119. MIDIOPENDESC* pmod = NULL;
  120. DWORD cbHandle;
  121. DWORD idx;
  122. MIDIOUTCAPS moc;
  123. MMRESULT mmrc = MMSYSERR_NOERROR;
  124. MMRESULT mmrc2;
  125. UINT msg;
  126. V_WPOINTER((LPVOID)phms, sizeof(HMIDISTRM), MMSYSERR_INVALPARAM);
  127. V_DCALLBACK(dwCallback, HIWORD(fdwOpen), MMSYSERR_INVALPARAM);
  128. *phms = NULL;
  129. // Allocate both the handle and the OPENDESC structure.
  130. //
  131. // NOTE: Using cMidi-1 because rgIds is defined as having 1 element
  132. //
  133. cbHandle = sizeof(MIDISTRM) + cMidi * ELESIZE(MIDISTRM, rgIds[0]);
  134. if ((0 == cMidi) || (cbHandle >= 0x00010000L))
  135. return MMSYSERR_INVALPARAM;
  136. pms = HtoPT(PMIDISTRM, NewHandle(TYPE_MIDISTRM, NULL, (UINT)cbHandle));
  137. if (NULL == pms)
  138. {
  139. dprintf1(("mSO: NewHandle() failed!"));
  140. return MMSYSERR_NOMEM;
  141. }
  142. // Implicitly acquired with NewHandle()...
  143. ReleaseHandleListResource();
  144. pmod = (MIDIOPENDESC*)LocalAlloc(LPTR,
  145. (UINT)(sizeof(MIDIOPENDESC) + (cMidi-1) * ELESIZE(MIDIOPENDESC, rgIds[0])));
  146. if (NULL == pmod)
  147. {
  148. dprintf1(("mSO: !LocalAlloc(MIDIOPENDESC)"));
  149. mmrc = MMSYSERR_NOMEM;
  150. goto midiStreamOpen_Cleanup;
  151. }
  152. pms->fdwOpen = fdwOpen;
  153. pms->dwCallback = dwCallback;
  154. pms->dwInstance = dwInstance;
  155. pms->cIds = cMidi;
  156. // Scan through the given device ID's. Determine if the underlying
  157. // driver supports stream directly. If so, then get it's HMD and uDeviceID,
  158. // etc. Else flag this as an emulator ID.
  159. //
  160. pmsi = pms->rgIds;
  161. for (idx = 0; idx < cMidi; idx++, pmsi++)
  162. {
  163. dprintf1(("mSO: pmsi->fdwId %08lX", (DWORD)pmsi->fdwId));
  164. mmrc = midiOutGetDevCaps(puDeviceID[idx], &moc, sizeof(moc));
  165. if (MMSYSERR_NOERROR != mmrc)
  166. {
  167. puDeviceID[idx] = (UINT)MIDISTRM_ERROR;
  168. goto midiStreamOpen_Cleanup;
  169. }
  170. if (moc.dwSupport & MIDICAPS_STREAM)
  171. {
  172. // Find the driver supporting the device ID. Note that mregFindDevice implicitly
  173. // adds a referance (usage) to the driver (i.e. the hmd).
  174. dprintf1(("mSO: Dev %u MIDICAPS_STREAM! dwSupport %08lX", (UINT)idx, moc.dwSupport));
  175. mmrc = mregFindDevice(puDeviceID[idx], TYPE_MIDIOUT, &pmsi->hmd, &pmsi->uDevice);
  176. if (MMSYSERR_NOERROR != mmrc)
  177. {
  178. dprintf(("mregFindDevice barfed %u", (UINT)mmrc));
  179. puDeviceID[idx] = (UINT)MIDISTRM_ERROR;
  180. goto midiStreamOpen_Cleanup;
  181. }
  182. else
  183. {
  184. dprintf1(("mregFindDevice: hmd %04X", (UINT_PTR)pmsi->hmd));
  185. }
  186. }
  187. else
  188. {
  189. dprintf1(("mSO: Dev %u emulated.", (UINT)idx));
  190. pmsi->fdwId |= MSI_F_EMULATOR;
  191. pmsi->hmd = NULL;
  192. pmsi->uDevice = puDeviceID[idx];
  193. }
  194. }
  195. // At this point, the puDeviceID array's elements contain either device |
  196. // IDs or the error value MIDISTRM_ERROR. Also the pmsi array elements
  197. // corresponding to device IDs supporting MIDICAPS_STREAM will have a
  198. // non-NULL pmsi->hmd with a reference count (usage) on it. pmsi->uDevice
  199. // will be a driver-relative device ID. Other pmsi elements will have a
  200. // NULL pmsi->hmd and pmsi->fdwId will have MSI_F_EMULATOR set.
  201. // pmsi->uDevice will be a midiOut device ID (not a driver relative ID).
  202. // Scan through the list again, but this time actually open the devices.
  203. //
  204. pmod->hMidi = PTtoH(HMIDI, pms);
  205. pmod->dwCallback = (DWORD_PTR)midiOutStreamCallback;
  206. pmod->dwInstance = 0;
  207. msg = MODM_OPEN;
  208. pms->cDrvrs = 0;
  209. for(;;)
  210. {
  211. //
  212. // Set pmsiSave to identify the first unopened device. Break loop
  213. // if all are opened.
  214. //
  215. pmsiSave = NULL;
  216. pmsi = pms->rgIds;
  217. for (idx = 0; idx < cMidi; idx++, pmsi++)
  218. {
  219. if (!(pmsi->fdwId & MSI_F_OPENED))
  220. {
  221. pmsiSave = pmsi;
  222. break;
  223. }
  224. }
  225. if (NULL == pmsiSave)
  226. break;
  227. //
  228. // Group together all IDs implemented by the same driver
  229. //
  230. pmod->cIds = 0;
  231. for(; idx < cMidi; idx++, pmsi++)
  232. {
  233. if (pmsi->hmd == pmsiSave->hmd)
  234. {
  235. pmod->rgIds[pmod->cIds].uDeviceID = pmsi->uDevice;
  236. pmod->rgIds[pmod->cIds++].dwStreamID = idx;
  237. }
  238. }
  239. pmsiSave->fdwId |= MSI_F_FIRST;
  240. //
  241. // Open the driver
  242. //
  243. if (!(pmsiSave->fdwId & MSI_F_EMULATOR))
  244. {
  245. pmsiSave->drvMessage = HtoPT(PMMDRV, pmsiSave->hmd)->drvMessage;
  246. // pmsiSave->dnDevNode = pmod->dnDevNode = mregQueryDevNode(pmsiSave->hmd);
  247. mmrc = (MMRESULT)((*pmsiSave->drvMessage)(
  248. 0,
  249. msg,
  250. (DWORD_PTR)(LPDWORD)&pmsiSave->dwDrvUser,
  251. (DWORD_PTR)(LPMIDIOPENDESC)pmod,
  252. CALLBACK_FUNCTION|MIDI_IO_COOKED));
  253. if (MMSYSERR_NOERROR == mmrc)
  254. {
  255. mregIncUsage(pmsiSave->hmd);
  256. }
  257. }
  258. else
  259. {
  260. mmrc = (MMRESULT)mseMessage(msg,
  261. (DWORD_PTR)(LPDWORD)&pmsiSave->dwDrvUser,
  262. (DWORD_PTR)(LPMIDIOPENDESC)pmod,
  263. CALLBACK_FUNCTION);
  264. }
  265. if (MMSYSERR_NOERROR != mmrc)
  266. {
  267. idx = (DWORD)(pmsiSave - pms->rgIds);
  268. puDeviceID[idx] = (UINT)MIDISTRM_ERROR;
  269. goto midiStreamOpen_Cleanup;
  270. }
  271. //
  272. // Now flag all IDs implemented by the same driver as MSI_F_OPENED
  273. //
  274. ++pms->cDrvrs;
  275. pmsi = pms->rgIds;
  276. for (idx = 0; idx < cMidi; idx++, pmsi++)
  277. {
  278. if (pmsi->hmd == pmsiSave->hmd)
  279. {
  280. pmsi->fdwId |= MSI_F_OPENED;
  281. if (!(pmsiSave->fdwId & MSI_F_EMULATOR))
  282. {
  283. if (mmInitializeCriticalSection(&pmsi->CritSec))
  284. {
  285. pmsi->fdwId |= MSI_F_INITIALIZEDCRITICALSECTION;
  286. } else {
  287. mmrc = MMSYSERR_NOMEM;
  288. }
  289. }
  290. }
  291. }
  292. }
  293. if (MMSYSERR_NOERROR == mmrc && !CreatehwndNotify())
  294. {
  295. dprintf(("Cannot create hwndNotify for async messages!"));
  296. mmrc = MMSYSERR_ERROR;
  297. }
  298. dprintf2(("midiStreamOpen: HMIDISTRM %04X", (WORD)pms));
  299. midiStreamOpen_Cleanup:
  300. if (NULL != pmod) LocalFree((HLOCAL)pmod);
  301. //
  302. // If there was an error, close any drivers we opened and free resources
  303. // associated with them. Note do not free pms yet here, as we need it in
  304. // additional cleanup further below.
  305. //
  306. if (MMSYSERR_NOERROR != mmrc)
  307. {
  308. if (NULL != pms)
  309. {
  310. msg = MODM_CLOSE;
  311. pmsi = pms->rgIds;
  312. for (idx = 0; idx < pms->cIds; idx++, pmsi++)
  313. {
  314. if ((pmsi->fdwId & (MSI_F_OPENED|MSI_F_FIRST)) == (MSI_F_OPENED|MSI_F_FIRST))
  315. {
  316. mmrc2 = (MMRESULT)midiStreamMessage(pmsi, msg, 0L, 0L);
  317. if (MMSYSERR_NOERROR == mmrc2 &&
  318. !(pmsi->fdwId & MSI_F_EMULATOR))
  319. {
  320. if (pmsi->fdwId & MSI_F_INITIALIZEDCRITICALSECTION) {
  321. DeleteCriticalSection(&pmsi->CritSec);
  322. pmsi->fdwId &= ~MSI_F_INITIALIZEDCRITICALSECTION;
  323. }
  324. mregDecUsage(pmsi->hmd);
  325. }
  326. else
  327. {
  328. dprintf1(("midiStreamOpen_Cleanup: Close returned %u", mmrc2));
  329. }
  330. }
  331. }
  332. }
  333. }
  334. else
  335. {
  336. *phms = PTtoH(HMIDISTRM, pms);
  337. msg = MM_MOM_OPEN;
  338. DriverCallback(pms->dwCallback,
  339. HIWORD(pms->fdwOpen),
  340. (HDRVR)PTtoH(HMIDISTRM, pms),
  341. msg,
  342. pms->dwInstance,
  343. 0,
  344. 0);
  345. }
  346. //
  347. // Now release driver references added by mregFindDevice. Those that are
  348. // actually still in use have had an extra reference added and thus will
  349. // still have a reference count on them even after the release done here.
  350. //
  351. if (pms)
  352. {
  353. pmsi = pms->rgIds;
  354. for (pmsi = pms->rgIds, idx = 0;
  355. idx < pms->cIds;
  356. idx++, pmsi++)
  357. {
  358. if (pmsi->hmd) mregDecUsage(pmsi->hmd);
  359. }
  360. }
  361. //
  362. // Free pms if there was an error
  363. //
  364. if ((MMSYSERR_NOERROR != mmrc) && (pms)) FreeHandle((PTtoH(HMIDI, pms)));
  365. return mmrc;
  366. }
  367. WINMMAPI MMRESULT WINAPI midiStreamClose(
  368. HMIDISTRM hms)
  369. {
  370. PMIDISTRM pms;
  371. PMIDISTRMID pmsi;
  372. DWORD idx;
  373. MMRESULT mmrc;
  374. V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE);
  375. dprintf1(("midiStreamClose(%04X)", (WORD)hms));
  376. pms = HtoPT(PMIDISTRM, hms);
  377. pmsi = pms->rgIds;
  378. for (idx = 0; idx < pms->cIds; idx++, pmsi++)
  379. {
  380. if ((pmsi->fdwId & (MSI_F_OPENED|MSI_F_FIRST)) == (MSI_F_OPENED|MSI_F_FIRST))
  381. {
  382. mmrc = (MMRESULT)midiStreamMessage(pmsi, MODM_CLOSE, 0L, 0L);
  383. if (MMSYSERR_NOERROR == mmrc &&
  384. !(pmsi->fdwId & MSI_F_EMULATOR))
  385. {
  386. WinAssert(pmsi->fdwId & MSI_F_INITIALIZEDCRITICALSECTION);
  387. DeleteCriticalSection(&pmsi->CritSec);
  388. pmsi->fdwId &= ~MSI_F_INITIALIZEDCRITICALSECTION;
  389. mregDecUsage(pmsi->hmd);
  390. }
  391. else
  392. {
  393. dprintf1(("midiStreamClose: Close returned %u", mmrc));
  394. }
  395. }
  396. }
  397. dprintf1(("DriverCallback(%04X)", (WORD)hms));
  398. DriverCallback(pms->dwCallback,
  399. HIWORD(pms->fdwOpen),
  400. (HDRVR)hms,
  401. MM_MOM_CLOSE,
  402. pms->dwInstance,
  403. 0,
  404. 0);
  405. dprintf1(("FreeHandle(%04X)", (WORD)hms));
  406. FreeHandle(hms);
  407. return MMSYSERR_NOERROR;
  408. }
  409. /****************************************************************************
  410. * @doc EXTERNAL MIDI M5
  411. *
  412. * @func MMRESULT | midiStreamProperty | Sets or retrieves properties
  413. * of a MIDI data stream associated with a MIDI input or output device.
  414. *
  415. * @parm HMIDI | hm | Specifies the handle of the MIDI device that the
  416. * property is associated with.
  417. *
  418. * @parm LPBYTE | lppropdata | Specifies a pointer to the property data.
  419. *
  420. * @parm DWORD | dwProperty | Contains flags that specify the action
  421. * to perform and identify the appropriate property of the MIDI data stream.
  422. * <f midiStreamProperty> requires setting two flags in each use. One flag
  423. * (either MIDIPROP_GET or MIDIPROP_SET) specifies an action. The other
  424. * identifies a specific property to examine or edit.
  425. *
  426. * @flag MIDIPROP_SET | Set the given property.
  427. * @flag MIDIPROP_GET | Retrieve the current setting of the given property.
  428. * @flag MIDIPROP_TIMEDIV | Time division property.
  429. * This property is valid for both input and output devices. <p lppropdata>
  430. * points to a <t MIDIPROPTIMEDIV> structure. This property can be set only
  431. * when the device is stopped.
  432. *
  433. * @flag MIDIPROP_TEMPO | Tempo property.
  434. * This property is valid for both input and output devices. <p lppropdata>
  435. * points to a <t MIDIPROPTEMPO> structure. The current tempo value can be
  436. * retrieved at any time. This function can set the tempo for input devices.
  437. * Output devices set the tempo by inserting PMSG_TEMPO events into the
  438. * MIDI data.
  439. *
  440. * @flag MIDIPROP_CBTIMEOUT | Timeout value property.
  441. * This property specifies the timeout value for loading buffers when a
  442. * MIDI device is in MIDI_IO_COOKED and MIDI_IO_RAW modes. The current
  443. * timeout value sets the maximum number of milliseconds that a buffer will
  444. * be held once any data is placed in it. If this timeout expires, the
  445. * buffer will be returned to the application even though it might not be
  446. * completely full. <p lppropdata> points to a <t MIDIPROPCBTIMEOUT> structure.
  447. *
  448. * @comm These properties are the default properties defined by MMSYSTEM.
  449. * Driver writers may implement and document their own properties.
  450. *
  451. * @rdesc The return value is one of the following values:
  452. * @flag MMSYSERR_INVALPARAM | The given handle or flags are invalid.
  453. * @flag MIDIERR_BADOPENMODE | The given handle is not open in MIDI_IO_COOKED
  454. * or MIDI_IO_RAW mode.
  455. *
  456. ***************************************************************************/
  457. MMRESULT WINAPI midiStreamProperty(
  458. HMIDISTRM hms,
  459. LPBYTE lppropdata,
  460. DWORD dwProperty)
  461. {
  462. MMRESULT mmrc;
  463. V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE);
  464. if ((!(dwProperty&MIDIPROP_SET)) && (!(dwProperty&MIDIPROP_GET)))
  465. return MMSYSERR_INVALPARAM;
  466. V_RPOINTER(lppropdata, sizeof(DWORD), MMSYSERR_INVALPARAM);
  467. if (dwProperty&MIDIPROP_SET)
  468. {
  469. V_RPOINTER(lppropdata, (UINT)(*(LPDWORD)(lppropdata)), MMSYSERR_INVALPARAM);
  470. }
  471. else
  472. {
  473. V_WPOINTER(lppropdata, (UINT)(*(LPDWORD)(lppropdata)), MMSYSERR_INVALPARAM);
  474. }
  475. mmrc = (MMRESULT)midiStreamBroadcast(HtoPT(PMIDISTRM, hms),
  476. MODM_PROPERTIES,
  477. (DWORD_PTR)lppropdata,
  478. dwProperty);
  479. return mmrc;
  480. }
  481. /*****************************************************************************
  482. * @doc EXTERNAL MIDI
  483. *
  484. * @api MMRESULT | midiOutGetPosition | Retrieves the current
  485. * playback position of the specified MIDI output device.
  486. *
  487. * @parm HMIDIOUT | hmo | Specifies a handle to the MIDI output device.
  488. *
  489. * @parm LPMMTIME | pmmt | Specifies a far pointer to an <t MMTIME>
  490. * structure.
  491. *
  492. * @parm UINT | cbmmt | Specifies the size of the <t MMTIME> structure.
  493. *
  494. * @rdesc Returns zero if the function is successful. Otherwise, it returns
  495. * an error number. Possible error values are:
  496. * @flag MMSYSERR_INVALHANDLE | Specified device handle is invalid.
  497. *
  498. * @comm Before calling <f midiOutGetPosition>, set the <e MMTIME.wType> field
  499. * of <t MMTIME> to indicate the time format that you desire. After
  500. * calling <f midiOutGetPosition>, check the <e MMTIME.wType> field
  501. * to determine if the desired time format is supported. If the desired
  502. * format is not supported, <e MMTIME.wType> will specify an alternative
  503. * format.
  504. *
  505. * The position is set to zero when the device is opened, reset, or
  506. * stopped.
  507. ****************************************************************************/
  508. MMRESULT WINAPI midiStreamPosition(
  509. HMIDISTRM hms,
  510. LPMMTIME pmmt,
  511. UINT cbmmt)
  512. {
  513. MMRESULT mmrc;
  514. V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE);
  515. V_WPOINTER(pmmt, cbmmt, MMSYSERR_INVALPARAM);
  516. mmrc = (MMRESULT)midiStreamMessage(HtoPT(PMIDISTRM, hms)->rgIds,
  517. MODM_GETPOS,
  518. (DWORD_PTR)pmmt,
  519. (DWORD)cbmmt);
  520. return mmrc;
  521. }
  522. /*****************************************************************************
  523. * @doc EXTERNAL MIDI
  524. *
  525. * @api MMRESULT | midiStreamStop | Turns off all notes on all MIDI
  526. * channels for the specified MIDI output device. Any pending
  527. * system-exclusive or polymessage output buffers are marked as done and
  528. * returned to the application. While <f midiOutReset> turns off all notes,
  529. * <f midiStreamStop> turns off only those notes that have been turned on
  530. * by a MIDI note-on message.
  531. *
  532. * @parm HMIDIOUT | hMidiOut | Specifies a handle to the MIDI output
  533. * device.
  534. *
  535. * @rdesc Returns zero if the function is successful. Otherwise, it returns
  536. * an error number. Possible error values are:
  537. * @flag MMSYSERR_INVALHANDLE | Specified device handle is invalid.
  538. * @flag MIDIERR_BADOPENMODE | Specified device handle is not opened in
  539. * MIDI_IO_COOKED mode.
  540. *
  541. * @comm To turn off all notes, a note-off message for each note for each
  542. * channel is sent. In addition, the sustain controller is turned off for
  543. * each channel.
  544. *
  545. * @xref midiOutLongMsg midiOutClose midiOutReset
  546. ****************************************************************************/
  547. MMRESULT WINAPI midiStreamStop(HMIDISTRM hms)
  548. {
  549. PMIDISTRM pms;
  550. MMRESULT mmrc;
  551. V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE);
  552. pms = HtoPT(PMIDISTRM, hms);
  553. mmrc = (MMRESULT)midiStreamBroadcast(pms, MODM_STOP, 0, 0);
  554. return mmrc;
  555. }
  556. /*****************************************************************************
  557. * @doc EXTERNAL MIDI
  558. *
  559. * @api MMRESULT | midiStreamPause | Pauses playback on a specified
  560. * MIDI output device. The current playback position is saved. Use
  561. * <f midiStreamRestart> to resume playback from the current playback position.
  562. * This call is only valid for handles opened in MIDI_IO_COOKED mode.
  563. *
  564. * @parm HMIDIOUT | hmo | Specifies a handle to the MIDI output
  565. * device.
  566. *
  567. * @rdesc Returns zero if the function is successful. Otherwise, it returns
  568. * an error number. Possible error values are:
  569. * @flag MMSYSERR_INVALHANDLE | Specified device handle is invalid.
  570. * @flag MMSYSERR_INVALPARAM | Specified device was not opened with
  571. * the MIDI_IO_COOKED flag.
  572. *
  573. * @comm Calling this function when the output is already paused has no
  574. * effect, and the function returns zero.
  575. *
  576. * @xref midiStreamRestart
  577. ****************************************************************************/
  578. MMRESULT WINAPI midiStreamPause(
  579. HMIDISTRM hms)
  580. {
  581. MMRESULT mmrc;
  582. V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE);
  583. mmrc = (MMRESULT)midiStreamBroadcast(HtoPT(PMIDISTRM, hms), MODM_PAUSE, 0, 0);
  584. return mmrc;
  585. }
  586. /*****************************************************************************
  587. * @doc EXTERNAL MIDI
  588. *
  589. * @api MMRESULT | midiStreamRestart | Restarts a paused MIDI
  590. * output device.
  591. *
  592. * @parm HMIDIOUT | hmo | Specifies a handle to the MIDI output
  593. * device.
  594. *
  595. * @rdesc Returns zero if the function is successful. Otherwise, it returns
  596. * an error number. Possible error values are:
  597. * @flag MMSYSERR_INVALHANDLE | Specified device handle is invalid.
  598. * @flag MMSYSERR_INVALPARAM | Specified device was not opened with
  599. * the MIDI_IO_COOKED flag.
  600. *
  601. * @comm Calling this function when the output is not paused has no
  602. * effect, and the function returns zero.
  603. *
  604. * @xref midiOutPause
  605. ****************************************************************************/
  606. MMRESULT WINAPI midiStreamRestart(
  607. HMIDISTRM hms)
  608. {
  609. MMRESULT mmrc;
  610. MMTIME mmt;
  611. DWORD tkTime;
  612. DWORD msTime;
  613. PMIDISTRM pms;
  614. PMIDISTRMID pmsi;
  615. DWORD idx;
  616. V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE);
  617. tkTime = 0;
  618. pms = HtoPT(PMIDISTRM, hms);
  619. for (idx = 0, pmsi = pms->rgIds; idx < pms->cIds; idx++, pmsi++)
  620. if (pmsi->fdwId & MSI_F_FIRST)
  621. {
  622. mmt.wType = TIME_TICKS;
  623. mmrc = (MMRESULT)midiStreamMessage(pmsi,
  624. MODM_GETPOS,
  625. (DWORD_PTR)&mmt,
  626. sizeof(mmt));
  627. if (mmrc)
  628. {
  629. dprintf(("midiOutRestart: Device %u returned %u", idx, mmrc));
  630. return mmrc;
  631. }
  632. if (mmt.wType == TIME_TICKS)
  633. {
  634. if (mmt.u.ticks > tkTime)
  635. tkTime = mmt.u.ticks;
  636. }
  637. else
  638. {
  639. dprintf(("midiOutRestart: Device %u does not support ticks", idx));
  640. return MIDIERR_NOTREADY;
  641. }
  642. }
  643. // Fudge time to allow device setup
  644. //
  645. msTime = timeGetTime();
  646. dprintf(("midiOutRestart: Tick %lu timeGetTime %lu", tkTime, msTime));
  647. mmrc = (MMRESULT)midiStreamBroadcast(pms,
  648. MODM_RESTART,
  649. msTime,
  650. tkTime);
  651. return mmrc;
  652. }
  653. MMRESULT WINAPI midiStreamOut(
  654. HMIDISTRM hMidiStrm,
  655. LPMIDIHDR lpMidiHdr,
  656. UINT cbMidiHdr)
  657. {
  658. PMIDISTRMID pmsi;
  659. PMIDISTRM pms;
  660. UINT idx;
  661. UINT cSent;
  662. LPMIDIHDR lpmhWork;
  663. BOOL fCallback;
  664. MMRESULT mmrc;
  665. dprintf2(( "midiStreamOut(%04X, %08lX, %08lX)", (UINT_PTR)hMidiStrm, (DWORD_PTR)lpMidiHdr, lpMidiHdr->dwBytesRecorded));
  666. V_HANDLE(hMidiStrm, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE);
  667. V_HEADER(lpMidiHdr, cbMidiHdr, TYPE_MIDIOUT, MMSYSERR_INVALPARAM);
  668. pms = HtoPT(PMIDISTRM, hMidiStrm);
  669. for (pmsi = pms->rgIds, idx = 0; idx < pms->cIds; idx++, pmsi++)
  670. if ( (!(pmsi->fdwId & MSI_F_EMULATOR)) && (!(pmsi->hmd)) )
  671. return MMSYSERR_NODRIVER;
  672. if (!(lpMidiHdr->dwFlags&MHDR_PREPARED))
  673. {
  674. dprintf1(( "midiOutPolyMsg: !MHDR_PREPARED"));
  675. return MIDIERR_UNPREPARED;
  676. }
  677. if (lpMidiHdr->dwFlags&MHDR_INQUEUE)
  678. {
  679. dprintf1(( "midiOutPolyMsg: Still playing!"));
  680. return MIDIERR_STILLPLAYING;
  681. }
  682. if (lpMidiHdr->dwBytesRecorded > lpMidiHdr->dwBufferLength ||
  683. (lpMidiHdr->dwBytesRecorded & 3))
  684. {
  685. dprintf1(( "Bytes recorded too long or not DWORD aligned."));
  686. return MMSYSERR_INVALPARAM;
  687. }
  688. //
  689. // Polymsg buffers are limited to 64k in order that we (and the driver)
  690. // not have to do huge pointer manipulation.
  691. // Length must also be DWORD aligned.
  692. //
  693. if ((lpMidiHdr->dwBufferLength > 65535L) ||
  694. (lpMidiHdr->dwBufferLength&3))
  695. {
  696. dprintf1(( "midiOutPolyMsg: Buffer > 64k or not DWORD aligned"));
  697. return MMSYSERR_INVALPARAM;
  698. }
  699. EnterCriticalSection(&midiStrmHdrCritSec);
  700. LeaveCriticalSection(&midiStrmHdrCritSec);
  701. lpMidiHdr->dwReserved[MH_REFCNT] = 0;
  702. lpMidiHdr->dwFlags |= (MHDR_SENDING|MHDR_INQUEUE|MHDR_ISSTRM);
  703. lpmhWork = (LPMIDIHDR)lpMidiHdr->dwReserved[MH_SHADOW];
  704. pmsi = pms->rgIds;
  705. for (idx = 0, cSent = 0; idx < pms->cIds; idx++, pmsi++)
  706. {
  707. if (pmsi->fdwId & MSI_F_FIRST)
  708. {
  709. lpmhWork->dwBytesRecorded = lpMidiHdr->dwBytesRecorded;
  710. lpmhWork->dwFlags |= MHDR_ISSTRM;
  711. mmrc = (MMRESULT)midiStreamMessage(pmsi, MODM_STRMDATA, (DWORD_PTR)lpmhWork, sizeof(*lpmhWork));
  712. if (mmrc == MMSYSERR_NOERROR)
  713. ++lpMidiHdr->dwReserved[MH_REFCNT], ++cSent;
  714. lpmhWork++;
  715. }
  716. }
  717. fCallback = FALSE;
  718. EnterCriticalSection(&midiStrmHdrCritSec);
  719. lpMidiHdr->dwFlags &= ~MHDR_SENDING;
  720. if (cSent && 0 == lpMidiHdr->dwReserved[MH_REFCNT])
  721. {
  722. fCallback = TRUE;
  723. }
  724. LeaveCriticalSection(&midiStrmHdrCritSec);
  725. if (fCallback)
  726. {
  727. lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
  728. lpMidiHdr->dwFlags |= MHDR_DONE;
  729. DriverCallback(pms->dwCallback,
  730. HIWORD(pms->fdwOpen),
  731. (HDRVR)hMidiStrm,
  732. MM_MOM_DONE,
  733. pms->dwInstance,
  734. (DWORD_PTR)lpMidiHdr,
  735. 0);
  736. }
  737. if (!cSent)
  738. {
  739. lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
  740. return mmrc;
  741. }
  742. else
  743. return MMSYSERR_NOERROR;
  744. }
  745. DWORD FAR PASCAL midiStreamMessage(PMIDISTRMID pmsi, UINT msg, DWORD_PTR dwP1, DWORD_PTR dwP2)
  746. {
  747. MMRESULT mrc;
  748. if (!(pmsi->fdwId & MSI_F_EMULATOR))
  749. {
  750. EnterCriticalSection(&pmsi->CritSec);
  751. mrc = (*(pmsi->drvMessage))
  752. (0, msg, pmsi->dwDrvUser, dwP1, dwP2);
  753. try
  754. {
  755. LeaveCriticalSection(&pmsi->CritSec);
  756. }
  757. except(EXCEPTION_EXECUTE_HANDLER)
  758. {
  759. }
  760. return mrc;
  761. }
  762. else
  763. {
  764. mrc = mseMessage(msg, pmsi->dwDrvUser, dwP1, dwP2);
  765. }
  766. return mrc;
  767. }
  768. DWORD FAR PASCAL midiStreamBroadcast(
  769. PMIDISTRM pms,
  770. UINT msg,
  771. DWORD_PTR dwP1,
  772. DWORD_PTR dwP2)
  773. {
  774. DWORD idx;
  775. DWORD mmrc;
  776. DWORD mmrcRet;
  777. PMIDISTRMID pmsi;
  778. ENTER_MM_HANDLE((HMIDI)pms);
  779. mmrcRet = MMSYSERR_NOERROR;
  780. pmsi = pms->rgIds;
  781. for (idx = pms->cIds; idx; idx--, pmsi++)
  782. {
  783. if (pmsi->fdwId & MSI_F_FIRST)
  784. {
  785. mmrc = midiStreamMessage(pmsi, msg, dwP1, dwP2);
  786. if (MMSYSERR_NOERROR != mmrc)
  787. mmrcRet = mmrc;
  788. }
  789. }
  790. LEAVE_MM_HANDLE((HMIDI)pms);
  791. return mmrcRet;
  792. }
  793. void CALLBACK midiOutStreamCallback(
  794. HMIDISTRM hMidiOut,
  795. WORD wMsg,
  796. DWORD_PTR dwInstance,
  797. DWORD_PTR dwParam1,
  798. DWORD_PTR dwParam2)
  799. {
  800. PMIDISTRM pms = HtoPT(PMIDISTRM, hMidiOut);
  801. LPMIDIHDR lpmh = (LPMIDIHDR)dwParam1;
  802. if (MM_MOM_POSITIONCB == wMsg)
  803. {
  804. LPMIDIHDR lpmh2 = (LPMIDIHDR)lpmh->dwReserved[MH_PARENT];
  805. lpmh2->dwOffset = lpmh->dwOffset;
  806. DriverCallback(pms->dwCallback,
  807. HIWORD(pms->fdwOpen),
  808. (HDRVR)hMidiOut,
  809. MM_MOM_POSITIONCB,
  810. pms->dwInstance,
  811. (DWORD_PTR)lpmh2,
  812. 0);
  813. return;
  814. }
  815. else if (MM_MOM_DONE != wMsg)
  816. return;
  817. #ifdef DEBUG
  818. {
  819. DWORD dwDelta = timeGetTime() - (DWORD)lpmh->dwReserved[7];
  820. if (dwDelta > 1)
  821. dprintf1(("Took %lu ms to deliver callback!", dwDelta));
  822. }
  823. #endif
  824. lpmh = (LPMIDIHDR)lpmh->dwReserved[MH_PARENT];
  825. dprintf2(("mOSCB PMS %04X HDR %08lX", (UINT_PTR)pms, (DWORD_PTR)lpmh));
  826. EnterCriticalSection(&midiStrmHdrCritSec);
  827. --lpmh->dwReserved[MH_REFCNT];
  828. if (0 == lpmh->dwReserved[MH_REFCNT] && (!(lpmh->dwFlags & MHDR_SENDING)))
  829. {
  830. lpmh->dwFlags &= ~MHDR_INQUEUE;
  831. lpmh->dwFlags |= MHDR_DONE;
  832. LeaveCriticalSection(&midiStrmHdrCritSec);
  833. #ifdef DEBUG
  834. lpmh->dwReserved[7] = timeGetTime();
  835. #endif
  836. DriverCallback(pms->dwCallback,
  837. HIWORD(pms->fdwOpen),
  838. (HDRVR)hMidiOut,
  839. MM_MOM_DONE,
  840. pms->dwInstance,
  841. (DWORD_PTR)lpmh,
  842. 0);
  843. }
  844. else
  845. {
  846. LeaveCriticalSection(&midiStrmHdrCritSec);
  847. }
  848. }