Windows NT 4.0 source code leak
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.

1602 lines
50 KiB

4 years ago
  1. /****************************************************************************
  2. *
  3. * mididd.c
  4. *
  5. * Multimedia kernel driver support component (mmdrv)
  6. *
  7. * Copyright (c) 1991 - 1995 Microsoft Corporation. All Rights Reserved.
  8. *
  9. * Driver for midi input and output devices
  10. *
  11. * -- Midi driver entry points (modMessage, midMessage)
  12. * -- Auxiliary task (necessary for receiving Apcs and generating
  13. * callbacks ASYNCRHONOUSLY)
  14. * -- Interface to kernel driver (NtDeviceIoControlFile)
  15. * -- Midi parsing code (ported from Windows 3.1).
  16. *
  17. * History
  18. * 01-Feb-1992 - Robin Speed (RobinSp) wrote it
  19. *
  20. ***************************************************************************/
  21. #include <drvlib.h>
  22. #include <ntddmidi.h>
  23. /*****************************************************************************
  24. internal declarations
  25. ****************************************************************************/
  26. //
  27. // Stack size for our auxiliary task
  28. //
  29. #define MIDI_STACK_SIZE 300
  30. #define SYSEX_ERROR 0xFF // internal error for sysex's on input
  31. //
  32. // Functions for auxiliary thread to perform
  33. //
  34. typedef enum {
  35. MidiThreadInvalid,
  36. MidiThreadAddBuffer,
  37. MidiThreadSetState,
  38. MidiThreadSetData,
  39. MidiThreadClose,
  40. MidiThreadTerminate
  41. } MIDITHREADFUNCTION;
  42. //
  43. // Our local buffers for interfacing to midi input
  44. //
  45. #define LOCAL_MIDI_DATA_SIZE 20
  46. typedef struct _LOCALMIDIHDR {
  47. OVERLAPPED Ovl;
  48. DWORD BytesReturned;
  49. struct _LOCALMIDIHDR *lpNext; // Queueing (really debug only)
  50. BOOL Done; // Driver completed buffer
  51. PVOID pClient; // Our instance data for Apcs
  52. MIDI_DD_INPUT_DATA MidiData; // What the driver wants to process
  53. BYTE ExtraData[LOCAL_MIDI_DATA_SIZE - sizeof(ULONG)];
  54. // The rest of our input buffer
  55. } LOCALMIDIHDR, *PLOCALMIDIHDR;
  56. //
  57. // Midi input data
  58. //
  59. #define NUMBER_OF_LOCAL_MIDI_BUFFERS 8
  60. typedef struct {
  61. //
  62. // Static data for managing midi input
  63. //
  64. BOOL fMidiInStarted; // Do we think midi in is running ?
  65. DWORD dwMsg; // Current short msg
  66. DWORD dwCurData; // Position in long message
  67. BYTE status; // Running status byte
  68. BOOLEAN fSysex; // Processing extended message
  69. BOOLEAN Bad; // Input not working properly
  70. BYTE bBytesLeft; // Bytes left in short message
  71. BYTE bBytePos; // Position in short message
  72. DWORD dwCurTime; // Latest time from driver
  73. DWORD dwMsgTime; // Time to insert into message
  74. // in milliseconds since device
  75. // was opened
  76. PLOCALMIDIHDR DeviceQueue; // Keep track of what the device
  77. // has (debugging only)
  78. LOCALMIDIHDR // Driver interface buffers
  79. Bufs[NUMBER_OF_LOCAL_MIDI_BUFFERS];// When input is active these
  80. // are queued on the device
  81. // except while data is being
  82. // processed from them
  83. } LOCALMIDIDATA, *PLOCALMIDIDATA;
  84. //
  85. // per allocation structure for Midi
  86. //
  87. typedef struct tag_MIDIALLOC {
  88. struct tag_MIDIALLOC *Next; // Chain of devices
  89. UINT DeviceNumber; // Number of device
  90. UINT DeviceType; // MidiInput or MidiOutput
  91. DWORD dwCallback; // client's callback
  92. DWORD dwInstance; // client's instance data
  93. HMIDI hMidi; // handle for stream
  94. HANDLE hDev; // Midi device handle
  95. LPMIDIHDR lpMIQueue; // Buffers sent to device
  96. // This is only required so that
  97. // CLOSE knows when things have
  98. // really finished.
  99. // notify. This is only accessed
  100. // on the device thread and its
  101. // apcs so does not need any
  102. // synchronized access.
  103. HANDLE Event; // Event for driver syncrhonization
  104. // and notification of auxiliary
  105. // task operation completion.
  106. MIDITHREADFUNCTION AuxFunction; // Function for thread to perform
  107. union {
  108. LPMIDIHDR pHdr; // Buffer to pass in aux task
  109. ULONG State; // State to set
  110. struct {
  111. ULONG Function; // IOCTL to use
  112. PBYTE pData; // Data to set or get
  113. ULONG DataLen; // Length of data
  114. } GetSetData;
  115. } AuxParam;
  116. // 0 means terminate task.
  117. HANDLE ThreadHandle; // Handle for termination ONLY
  118. HANDLE AuxEvent1; // Aux thread waits on this
  119. HANDLE AuxEvent2; // Aux thread caller waits on this
  120. DWORD AuxReturnCode; // Return code from Aux task
  121. DWORD dwFlags; // Open flags
  122. PLOCALMIDIDATA Mid; // Extra midi input structures
  123. int l; // Helper global for modMidiLength
  124. } MIDIALLOC, *PMIDIALLOC;
  125. PMIDIALLOC MidiHandleList; // Our chain of wave handles
  126. /*****************************************************************************
  127. internal function prototypes
  128. ****************************************************************************/
  129. STATIC DWORD midiThread(LPVOID lpParameter);
  130. STATIC void midiCleanUp(PMIDIALLOC pClient);
  131. STATIC DWORD midiThreadCall(MIDITHREADFUNCTION Function, PMIDIALLOC pClient);
  132. STATIC DWORD midiSetState(PMIDIALLOC pClient, ULONG State);
  133. STATIC void midiInOvl(DWORD dwRet, DWORD dwBytes, LPOVERLAPPED pOverlap);
  134. STATIC DWORD midiInWrite(LPMIDIHDR pHdr, PMIDIALLOC pClient);
  135. STATIC DWORD midiOutWrite(PBYTE pData, ULONG Len, PMIDIALLOC pClient);
  136. STATIC void midiBlockFinished(LPMIDIHDR lpHdr, DWORD MsgId);
  137. STATIC void midiCallback(PMIDIALLOC pMidi, DWORD msg, DWORD dw1, DWORD dw2);
  138. STATIC int modMIDIlength(PMIDIALLOC pClient, BYTE b);
  139. STATIC void midByteRec(PMIDIALLOC pClient, BYTE byte);
  140. STATIC void midSendPartBuffer(PMIDIALLOC pClient);
  141. STATIC void midFreeQ(PMIDIALLOC pClient);
  142. STATIC void midiFlush(PMIDIALLOC pClient);
  143. /****************************************************************************
  144. * @doc INTERNAL
  145. *
  146. * @api VOID | TerminateMidi | Free all midi resources for mmdrv.dll
  147. *
  148. * @rdesc None
  149. ***************************************************************************/
  150. VOID TerminateMidi(VOID)
  151. {
  152. //
  153. // Don't do any cleanup - Midi input resources cleaned up on Close.
  154. //
  155. }
  156. /****************************************************************************
  157. * @doc INTERNAL
  158. *
  159. * @api void | midiGetDevCaps | Get the device capabilities.
  160. *
  161. * @parm DWORD | id | Device id
  162. *
  163. * @parm UINT | DeviceType | type of device
  164. *
  165. * @parm LPBYTE | lpCaps | Far pointer to a MIDIOUTCAPS structure to
  166. * receive the information.
  167. *
  168. * @parm DWORD | dwSize | Size of the MIDIOUTCAPS structure.
  169. *
  170. * @rdesc There is no return value.
  171. ***************************************************************************/
  172. STATIC DWORD midiGetDevCaps(DWORD id, UINT DeviceType,
  173. LPBYTE lpCaps, DWORD dwSize)
  174. {
  175. MMRESULT mrc;
  176. if (DeviceType != MIDI_IN) {
  177. MIDIOUTCAPSW mc;
  178. mrc = sndGetData(DeviceType, id, sizeof(mc), (LPBYTE)&mc,
  179. IOCTL_MIDI_GET_CAPABILITIES);
  180. if (mrc != MMSYSERR_NOERROR) {
  181. return mrc;
  182. }
  183. InternalLoadString((UINT)*(LPDWORD)mc.szPname, mc.szPname,
  184. sizeof(mc.szPname) / sizeof(WCHAR));
  185. CopyMemory(lpCaps, &mc, min(sizeof(mc), dwSize));
  186. } else {
  187. MIDIINCAPSW mc;
  188. mrc = sndGetData(DeviceType, id, sizeof(mc), (LPBYTE)&mc,
  189. IOCTL_MIDI_GET_CAPABILITIES);
  190. if (mrc != MMSYSERR_NOERROR) {
  191. return mrc;
  192. }
  193. InternalLoadString((UINT)*(LPDWORD)mc.szPname, mc.szPname,
  194. sizeof(mc.szPname) / sizeof(WCHAR));
  195. CopyMemory(lpCaps, &mc, min(sizeof(mc), dwSize));
  196. }
  197. return MMSYSERR_NOERROR;
  198. }
  199. /****************************************************************************
  200. * @doc INTERNAL
  201. *
  202. * @api DWORD | midiOpen | Open midi device and set up logical device data
  203. * and auxilary task for issuing requests and servicing Apc's
  204. *
  205. * @parm MIDIDEVTYPE | DeviceType | Whether it's a midi input or output device
  206. *
  207. * @parm DWORD | id | The device logical id
  208. *
  209. * @parm DWORD | msg | Input parameter to modMessage
  210. *
  211. * @parm DWORD | dwUser | Input parameter to modMessage - pointer to
  212. * application's handle (generated by this routine)
  213. *
  214. * @parm DWORD | dwParam1 | Input parameter to modMessage
  215. *
  216. * @parm DWORD | dwParam2 | Input parameter to modMessage
  217. *
  218. * @rdesc modMessage return code.
  219. ***************************************************************************/
  220. STATIC DWORD midiOpen(UINT DeviceType,
  221. DWORD id,
  222. DWORD dwUser,
  223. DWORD dwParam1,
  224. DWORD dwParam2)
  225. {
  226. PMIDIALLOC pClient; // pointer to client information structure
  227. MMRESULT mRet;
  228. // dwParam1 contains a pointer to a MIDIOPENDESC
  229. // dwParam2 contains midi driver specific flags in the LOWORD
  230. // and generic driver flags in the HIWORD
  231. //
  232. // allocate my per-client structure
  233. //
  234. if (DeviceType == MIDI_OUT) {
  235. pClient = (PMIDIALLOC)HeapAlloc(hHeap, 0, sizeof(MIDIALLOC));
  236. if (pClient != NULL) {
  237. memset(pClient, 0, sizeof(MIDIALLOC));
  238. }
  239. } else {
  240. WinAssert(DeviceType == MIDI_IN);
  241. pClient = (PMIDIALLOC)HeapAlloc(hHeap, 0,
  242. sizeof(struct _xx {MIDIALLOC S1; LOCALMIDIDATA S2;}));
  243. if (pClient != NULL) {
  244. memset(pClient, 0, sizeof(struct _xx {MIDIALLOC S1; LOCALMIDIDATA S2;}));
  245. }
  246. }
  247. if (pClient == NULL) {
  248. return MMSYSERR_NOMEM;
  249. }
  250. if (DeviceType == MIDI_IN) {
  251. PLOCALMIDIDATA pMid;
  252. int i;
  253. pMid = pClient->Mid = (PLOCALMIDIDATA)(pClient + 1);
  254. for (i = 0 ;i < NUMBER_OF_LOCAL_MIDI_BUFFERS ; i++) {
  255. pMid->Bufs[i].pClient = pClient;
  256. }
  257. }
  258. //
  259. // and fill it with info
  260. //
  261. // (note that setting everything to 0 correctly initialized our
  262. // midi input processing static data).
  263. pClient->DeviceType = DeviceType;
  264. pClient->dwCallback = ((LPMIDIOPENDESC)dwParam1)->dwCallback;
  265. pClient->dwInstance = ((LPMIDIOPENDESC)dwParam1)->dwInstance;
  266. pClient->hMidi = ((LPMIDIOPENDESC)dwParam1)->hMidi;
  267. pClient->dwFlags = dwParam2;
  268. //
  269. // See if we can open our device
  270. // If it's only a query be sure only to open for read, otherwise
  271. // we could get STATUS_BUSY if someone else is writing to the
  272. // device.
  273. //
  274. mRet = sndOpenDev(DeviceType,
  275. id,
  276. &pClient->hDev,
  277. (GENERIC_READ | GENERIC_WRITE));
  278. if (mRet != MMSYSERR_NOERROR) {
  279. midiCleanUp(pClient);
  280. return mRet;
  281. }
  282. //
  283. // Create our event for syncrhonization with the device driver
  284. //
  285. pClient->Event = CreateEvent(NULL, FALSE, FALSE, NULL);
  286. if (pClient->Event == NULL) {
  287. midiCleanUp(pClient);
  288. return MMSYSERR_NOMEM;
  289. }
  290. if (DeviceType == MIDI_IN) {
  291. //
  292. // Create our event pair for synchronization with the auxiliary
  293. // thread
  294. //
  295. pClient->AuxEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);
  296. if (pClient->AuxEvent1 == NULL) {
  297. midiCleanUp(pClient);
  298. return MMSYSERR_NOMEM;
  299. }
  300. pClient->AuxEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
  301. if (pClient->AuxEvent2 == NULL) {
  302. midiCleanUp(pClient);
  303. return MMSYSERR_NOMEM;
  304. }
  305. //
  306. // Create our auxiliary thread for sending buffers to the driver
  307. // and collecting Apcs
  308. //
  309. mRet = mmTaskCreate((LPTASKCALLBACK)midiThread,
  310. &pClient->ThreadHandle,
  311. (DWORD)pClient);
  312. if (mRet != MMSYSERR_NOERROR) {
  313. midiCleanUp(pClient);
  314. return MMSYSERR_NOMEM;
  315. }
  316. //
  317. // Make sure the thread has really started
  318. //
  319. WaitForSingleObject(pClient->AuxEvent2, INFINITE);
  320. }
  321. //
  322. // give the client my driver dw
  323. //
  324. {
  325. PMIDIALLOC *pUserHandle;
  326. pUserHandle = (PMIDIALLOC *)dwUser;
  327. *pUserHandle = pClient;
  328. }
  329. //
  330. // sent client his OPEN callback message
  331. //
  332. midiCallback(pClient, DeviceType == MIDI_OUT ? MOM_OPEN : MIM_OPEN,
  333. 0L, 0L);
  334. return MMSYSERR_NOERROR;
  335. }
  336. /****************************************************************************
  337. * @doc INTERNAL
  338. *
  339. * @api void | midiCleanUp | Free resources for a midi device
  340. *
  341. * @parm PMIDIALLOC | pClient | Pointer to a MIDIALLOC structure describing
  342. * resources to be freed.
  343. *
  344. * @rdesc There is no return value.
  345. *
  346. * @comm If the pointer to the resource is NULL then the resource has not
  347. * been allocated.
  348. ***************************************************************************/
  349. STATIC void midiCleanUp(PMIDIALLOC pClient)
  350. {
  351. if (pClient->hDev != INVALID_HANDLE_VALUE) {
  352. CloseHandle(pClient->hDev);
  353. }
  354. if (pClient->AuxEvent1) {
  355. CloseHandle(pClient->AuxEvent1);
  356. }
  357. if (pClient->AuxEvent2) {
  358. CloseHandle(pClient->AuxEvent2);
  359. }
  360. if (pClient->Event) {
  361. CloseHandle(pClient->Event);
  362. }
  363. HeapFree(hHeap, 0, (LPSTR)pClient);
  364. }
  365. /****************************************************************************
  366. * @doc INTERNAL
  367. *
  368. * @api DWORD | midiOutWrite | Synchronously process a midi output
  369. * buffer.
  370. *
  371. * @parm LPMIDIHDR | pHdr | Pointer to a midi buffer
  372. *
  373. * @parm PMIDIALLOC | pClient | The data associated with the logical midi
  374. * device.
  375. *
  376. * @rdesc A MMSYS... type return code for the application.
  377. ***************************************************************************/
  378. STATIC DWORD midiOutWrite(PBYTE pData, ULONG Len, PMIDIALLOC pClient)
  379. {
  380. DWORD BytesReturned;
  381. //
  382. // Try passing the request to our driver
  383. // We operate synchronously but allow for the driver to operate
  384. // asynchronously by waiting on an event.
  385. //
  386. if (!DeviceIoControl(
  387. pClient->hDev,
  388. IOCTL_MIDI_PLAY,
  389. (PVOID)pData, // Input buffer
  390. Len, // Input buffer size
  391. NULL, // Output buffer
  392. 0, // Output buffer size
  393. &BytesReturned,
  394. NULL)) {
  395. return sndTranslateStatus();
  396. }
  397. return MMSYSERR_NOERROR;
  398. }
  399. /****************************************************************************
  400. * @doc INTERNAL
  401. *
  402. * @api DWORD | midiInPutBuffer | Pass a buffer to receive midi input
  403. *
  404. * @parm LPMIDIHDR | pHdr | Pointer to a midi buffer
  405. *
  406. * @parm PMIDIALLOC | pClient | The data associated with the logical midi
  407. * device.
  408. *
  409. * @rdesc A MMSYS... type return code for the application.
  410. ***************************************************************************/
  411. STATIC MMRESULT midiInPutBuffer(PLOCALMIDIHDR pHdr, PMIDIALLOC pClient)
  412. {
  413. DWORD BytesReturned;
  414. BOOL Result;
  415. WinAssert(!pHdr->Done); // Flag should be clear ready for setting by Apc
  416. //
  417. // BUGBUG - nice to have a semaphore for some of this !
  418. //
  419. //
  420. // Try passing the request to our driver
  421. // We operate synchronously but allow for the driver to operate
  422. // asynchronously by waiting on an event.
  423. //
  424. Result = ReadFileEx(
  425. pClient->hDev,
  426. (LPVOID)&pHdr->MidiData,
  427. sizeof(pHdr->ExtraData) +
  428. sizeof(MIDI_DD_INPUT_DATA),
  429. &pHdr->Ovl,
  430. midiInOvl);
  431. //
  432. // Put the buffer in our queue
  433. //
  434. if (Result || GetLastError() == ERROR_IO_PENDING) {
  435. PLOCALMIDIHDR *ppHdr;
  436. pHdr->lpNext = NULL;
  437. ppHdr = &pClient->Mid->DeviceQueue;
  438. while (*ppHdr) {
  439. ppHdr = &(*ppHdr)->lpNext;
  440. }
  441. *ppHdr = pHdr;
  442. return MMSYSERR_NOERROR;
  443. }
  444. return sndTranslateStatus();
  445. }
  446. /****************************************************************************
  447. * @doc INTERNAL
  448. *
  449. * @api DWORD | midiInWrite | Pass a new buffer to the Auxiliary thread for
  450. * a midi device.
  451. *
  452. * @parm LPMIDIHDR | pHdr | Pointer to a midit buffer
  453. *
  454. * @parm PMIDIALLOC | pClient | The data associated with the logical midi
  455. * device.
  456. *
  457. * @rdesc A MMSYS... type return code for the application.
  458. *
  459. * @comm The buffer flags are set and the buffer is passed to the auxiliary
  460. * device task for processing.
  461. ***************************************************************************/
  462. STATIC DWORD midiInWrite(LPMIDIHDR pHdr, PMIDIALLOC pClient)
  463. {
  464. //
  465. // Put the request at the end of our queue.
  466. //
  467. pHdr->dwFlags |= MHDR_INQUEUE;
  468. pHdr->dwFlags &= ~MHDR_DONE;
  469. pClient->AuxParam.pHdr = pHdr;
  470. return midiThreadCall(MidiThreadAddBuffer, pClient);
  471. }
  472. /****************************************************************************
  473. * @doc INTERNAL
  474. *
  475. * @api DWORD | midiSetState | Set a midi device to a given state
  476. *
  477. * @parm PMIDIALLOC | pClient | The data associated with the logical midi
  478. * output device.
  479. *
  480. * @parm ULONG | State | The new state
  481. *
  482. * @rdesc A MMSYS... type return code for the application.
  483. ***************************************************************************/
  484. STATIC DWORD midiSetState(PMIDIALLOC pClient, ULONG State)
  485. {
  486. MMRESULT mRc;
  487. mRc = sndSetHandleData(pClient->hDev,
  488. sizeof(State),
  489. &State,
  490. IOCTL_MIDI_SET_STATE,
  491. pClient->Event);
  492. midiFlush(pClient);
  493. return mRc;
  494. }
  495. /****************************************************************************
  496. * @doc INTERNAL
  497. *
  498. * @api DWORD | midiThreadCall | Set the function for the thread to perform
  499. * and 'call' the thread using the event pair mechanism.
  500. *
  501. * @parm MIDITHREADFUNCTION | Function | The function to perform
  502. *
  503. * @parm PMIDIALLOC | Our logical device data
  504. *
  505. * @rdesc An MMSYS... type return value suitable for returning to the
  506. * application
  507. *
  508. * @comm The AuxParam field in the device data is the 'input' to
  509. * the function processing loop in MidiThread().
  510. ***************************************************************************/
  511. STATIC DWORD midiThreadCall(MIDITHREADFUNCTION Function, PMIDIALLOC pClient)
  512. {
  513. //
  514. // Set the function code
  515. //
  516. pClient->AuxFunction = Function;
  517. //
  518. // Kick off the thread
  519. //
  520. SetEvent(pClient->AuxEvent1);
  521. //
  522. // Wait for it to complete
  523. //
  524. WaitForSingleObject(pClient->AuxEvent2, INFINITE);
  525. //
  526. // Return the return code that our task set.
  527. //
  528. return pClient->AuxReturnCode;
  529. }
  530. /****************************************************************************
  531. * @doc INTERNAL
  532. *
  533. * @api void | midiInApc | Apc routine. Called when a kernel sound driver
  534. * completes processing of a midi buffer.
  535. *
  536. * @parm PVOID | ApcContext | The Apc parameter. In our case this is a
  537. * pointer to our midi device data.
  538. *
  539. * @parm PIO_STATUS_BLOCK | pIosb | Pointer to the Io status block
  540. * used for the request.
  541. *
  542. * @rdesc There is no return code.
  543. ***************************************************************************/
  544. STATIC void midiInOvl(DWORD dwRet, DWORD dwBytesReturned, LPOVERLAPPED pOverlap)
  545. {
  546. PLOCALMIDIHDR pHdr;
  547. pHdr = ((PLOCALMIDIHDR)pOverlap);
  548. WinAssert(((PMIDIALLOC)pHdr->pClient)->DeviceType == MIDI_IN);
  549. //
  550. // Note that the buffer is complete. We don't do anything else here
  551. // because funny things happen if we call the client's callback
  552. // routine from within an Apc.
  553. //
  554. pHdr->BytesReturned = dwBytesReturned;
  555. pHdr->Done = TRUE;
  556. }
  557. /****************************************************************************
  558. * @doc INTERNAL
  559. *
  560. * @api void | midiFlush | Buffer completion routine. This completes
  561. * the work of the Apc routine at below Apc priority. This gets
  562. * round the nasty situations arising when the user's callback
  563. * causes more apcs to run (I strongly suspect this is a kernel
  564. * but).
  565. *
  566. * @parm PMIDIALLOC | pClient | The client's handle data
  567. *
  568. * @rdesc There is no return code.
  569. ***************************************************************************/
  570. STATIC void midiFlush(PMIDIALLOC pClient)
  571. {
  572. //
  573. // Process any completed buffers - the Apc routine
  574. // set the 'Done' flag in any completed requests.
  575. // Note that the call to the user's callback can
  576. // cause more requests to become complete
  577. //
  578. if (pClient->DeviceType == MIDI_IN) { // Output is synchronous
  579. while (pClient->Mid->DeviceQueue &&
  580. pClient->Mid->DeviceQueue->Done) {
  581. PLOCALMIDIHDR pHdr;
  582. pHdr = pClient->Mid->DeviceQueue;
  583. //
  584. // Clear our flag ready for next time
  585. //
  586. pHdr->Done = FALSE;
  587. //
  588. // Take buffer off the device queue
  589. //
  590. pClient->Mid->DeviceQueue = pHdr->lpNext;
  591. //
  592. // Grab the latest time estimate - convert from 100ns units
  593. // to milliseconds
  594. //
  595. pClient->Mid->dwCurTime =
  596. (DWORD)(pHdr->MidiData.Time.QuadPart / 10000);
  597. //
  598. // Complete our buffer
  599. //
  600. if (!pClient->Mid->Bad) {
  601. int i;
  602. for (i = 0;
  603. i + sizeof(LARGE_INTEGER) < pHdr->BytesReturned;
  604. i++) {
  605. midByteRec(pClient, pHdr->MidiData.Data[i]);
  606. }
  607. //
  608. // Requeue our buffer if we're still recording
  609. //
  610. if (pClient->Mid->fMidiInStarted) {
  611. if (midiInPutBuffer(pHdr, pClient) != MMSYSERR_NOERROR) {
  612. pClient->Mid->Bad = TRUE;
  613. }
  614. }
  615. }
  616. } // End of processing completed buffers
  617. }
  618. }
  619. /****************************************************************************
  620. * @doc INTERNAL
  621. *
  622. * @api DWORD | midiThread | Midi device auxiliary thread.
  623. *
  624. * @parm LPVOID | lpParameter | The thread parameter. In our case this is a
  625. * pointer to our midi device data.
  626. *
  627. * @rdesc Thread return code.
  628. ***************************************************************************/
  629. STATIC DWORD midiThread(LPVOID lpParameter)
  630. {
  631. PMIDIALLOC pClient;
  632. BOOL Close;
  633. Close = FALSE;
  634. pClient = (PMIDIALLOC)lpParameter;
  635. //
  636. // Set our thread to high priority so we don't fail to pass
  637. // new buffers to the device when we get them back. Also
  638. // we don't want any gaps if callbacks are meant to play
  639. // notes just received.
  640. //
  641. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  642. //
  643. // We start notifying our creator we have started and
  644. // waiting for something to do.
  645. //
  646. SetEvent(pClient->AuxEvent2);
  647. WaitForSingleObject(pClient->AuxEvent1, INFINITE);
  648. //
  649. // Now we're going
  650. //
  651. for(;;) {
  652. //
  653. // Initialize our return code
  654. //
  655. pClient->AuxReturnCode = MMSYSERR_NOERROR;
  656. //
  657. // Decode function number to perform
  658. //
  659. switch (pClient->AuxFunction) {
  660. case MidiThreadAddBuffer:
  661. //
  662. // Add the buffer to our list to be processed
  663. //
  664. {
  665. LPMIDIHDR *pHdrSearch;
  666. pClient->AuxParam.pHdr->lpNext = NULL;
  667. pHdrSearch = &pClient->lpMIQueue;
  668. while (*pHdrSearch) {
  669. pHdrSearch = &(*pHdrSearch)->lpNext;
  670. }
  671. *pHdrSearch = pClient->AuxParam.pHdr;
  672. }
  673. break;
  674. case MidiThreadSetState:
  675. switch (pClient->AuxParam.State) {
  676. case MIDI_DD_RECORD:
  677. //
  678. // Start means we must add our buffers to the driver's list
  679. //
  680. if (!pClient->Mid->fMidiInStarted && !pClient->Mid->Bad) {
  681. int i;
  682. for (i = 0; i < NUMBER_OF_LOCAL_MIDI_BUFFERS; i++) {
  683. pClient->AuxReturnCode =
  684. midiInPutBuffer(&pClient->Mid->Bufs[i], pClient);
  685. if (pClient->AuxReturnCode != MMSYSERR_NOERROR) {
  686. //
  687. // Failed to add our buffer so give up and
  688. // get our buffers back !
  689. //
  690. pClient->Mid->Bad = TRUE;
  691. break;
  692. }
  693. }
  694. //
  695. // Set Device state. By issuing state changes on THIS
  696. // thread the calling thread can be sure that all Apc's
  697. // generated by buffer completions will complete
  698. // BEFORE this function completes.
  699. //
  700. pClient->AuxReturnCode =
  701. midiSetState(pClient, pClient->AuxParam.State);
  702. //
  703. // If this failed then get our buffers back,
  704. // otherwise set our new state
  705. //
  706. if (pClient->AuxReturnCode != MMSYSERR_NOERROR) {
  707. pClient->Mid->Bad = TRUE;
  708. } else {
  709. pClient->Mid->fMidiInStarted = TRUE;
  710. }
  711. } else {
  712. //
  713. // Already started or bad
  714. //
  715. }
  716. break;
  717. case MIDI_DD_STOP:
  718. //
  719. // Set Device state. By issuing state changes on THIS
  720. // thread the calling thread can be sure that all Apc's
  721. // generated by buffer completions will complete
  722. // BEFORE this function completes.
  723. //
  724. if (pClient->Mid->fMidiInStarted) {
  725. pClient->Mid->fMidiInStarted = FALSE;
  726. //
  727. // RESET so we get our buffers back
  728. //
  729. pClient->AuxReturnCode =
  730. midiSetState(pClient, MIDI_DD_RESET);
  731. WinAssert(!pClient->Mid->DeviceQueue);
  732. if (pClient->AuxReturnCode == MMSYSERR_NOERROR) {
  733. midSendPartBuffer(pClient);
  734. }
  735. }
  736. break;
  737. case MIDI_DD_RESET:
  738. //
  739. // Set Device state. By issuing state changes on THIS
  740. // thread the calling thread can be sure that all Apc's
  741. // generated by buffer completions will complete
  742. // BEFORE this function completes.
  743. //
  744. if (pClient->Mid->fMidiInStarted) {
  745. pClient->Mid->fMidiInStarted = FALSE;
  746. pClient->AuxReturnCode =
  747. midiSetState(pClient, pClient->AuxParam.State);
  748. WinAssert(!pClient->Mid->DeviceQueue);
  749. if (pClient->AuxReturnCode == MMSYSERR_NOERROR) {
  750. pClient->Mid->Bad = FALSE; // Recovered !!
  751. midSendPartBuffer(pClient);
  752. }
  753. }
  754. //
  755. // We zero the input queue anyway - compatibility with
  756. // windows 3.1
  757. //
  758. midFreeQ(pClient);
  759. break;
  760. }
  761. break;
  762. case MidiThreadSetData:
  763. {
  764. pClient->AuxReturnCode =
  765. sndSetHandleData(pClient->hDev,
  766. pClient->AuxParam.GetSetData.DataLen,
  767. pClient->AuxParam.GetSetData.pData,
  768. pClient->AuxParam.GetSetData.Function,
  769. pClient->Event);
  770. }
  771. break;
  772. case MidiThreadClose:
  773. //
  774. // Try to complete.
  775. // If we're completed all our buffers then we can.
  776. // otherwise we can't
  777. //
  778. if (pClient->lpMIQueue == NULL) {
  779. pClient->AuxReturnCode = MMSYSERR_NOERROR;
  780. Close = TRUE;
  781. } else {
  782. pClient->AuxReturnCode = MIDIERR_STILLPLAYING;
  783. }
  784. break;
  785. default:
  786. WinAssert(FALSE); // Invalid call
  787. break;
  788. }
  789. //
  790. // Trap invalid callers
  791. //
  792. pClient->AuxFunction = MidiThreadInvalid;
  793. //
  794. // See if apcs completed
  795. //
  796. midiFlush(pClient);
  797. //
  798. // Release the caller
  799. //
  800. SetEvent(pClient->AuxEvent2);
  801. //
  802. // Complete ?
  803. //
  804. if (Close) {
  805. break;
  806. }
  807. //
  808. // Wait for more !
  809. //
  810. while (WaitForSingleObjectEx(pClient->AuxEvent1, INFINITE, TRUE) ==
  811. WAIT_IO_COMPLETION) {
  812. //
  813. // Complete buffers whose Apcs ran
  814. //
  815. midiFlush(pClient);
  816. }
  817. }
  818. //
  819. // We've been asked to terminte
  820. //
  821. return 1;
  822. }
  823. /****************************************************************************
  824. * @doc INTERNAL
  825. *
  826. * @api void | midiCallback | This calls DriverCallback for a MIDIHDR.
  827. *
  828. * @parm PMIDIALLOC | pMidi | Pointer to midi device.
  829. *
  830. * @parm DWORD | msg | The message.
  831. *
  832. * @parm DWORD | dw1 | message DWORD (dw2 is always set to 0).
  833. *
  834. * @rdesc There is no return value.
  835. ***************************************************************************/
  836. void midiCallback(PMIDIALLOC pMidi, DWORD msg, DWORD dw1, DWORD dw2)
  837. {
  838. // invoke the callback function, if it exists. dwFlags contains
  839. // midi driver specific flags in the LOWORD and generic driver
  840. // flags in the HIWORD
  841. if (pMidi->dwCallback)
  842. DriverCallback(pMidi->dwCallback, // user's callback DWORD
  843. HIWORD(pMidi->dwFlags), // callback flags
  844. (HDRVR)pMidi->hMidi, // handle to the midi device
  845. msg, // the message
  846. pMidi->dwInstance, // user's instance data
  847. dw1, // first DWORD
  848. dw2); // second DWORD
  849. }
  850. /****************************************************************************
  851. This function conforms to the standard Midi input driver message proc
  852. (midMessage), which is documented in mmddk.d.
  853. ****************************************************************************/
  854. DWORD APIENTRY midMessage(DWORD id, DWORD msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
  855. {
  856. PMIDIALLOC pInClient;
  857. switch (msg) {
  858. case MIDM_GETNUMDEVS:
  859. D2(("MIDM_GETNUMDEVS"));
  860. return sndGetNumDevs(MIDI_IN);
  861. case MIDM_GETDEVCAPS:
  862. D2(("MIDM_GETDEVCAPS"));
  863. return midiGetDevCaps(id, MIDI_IN, (LPBYTE)dwParam1,
  864. (DWORD)dwParam2);
  865. case MIDM_OPEN:
  866. D2(("MIDM_OPEN"));
  867. return midiOpen(MIDI_IN, id, dwUser, dwParam1, dwParam2);
  868. case MIDM_CLOSE:
  869. D2(("MIDM_CLOSE"));
  870. pInClient = (PMIDIALLOC)dwUser;
  871. //
  872. // Call our task to see if it's ready to complete
  873. //
  874. if (midiThreadCall(MidiThreadClose, pInClient) != 0L) {
  875. return MIDIERR_STILLPLAYING;
  876. }
  877. //
  878. // Wait for our thread to terminate and close our device
  879. //
  880. WaitForSingleObject(pInClient->ThreadHandle, INFINITE);
  881. CloseHandle(pInClient->ThreadHandle);
  882. //
  883. // Tell the caller we're done
  884. //
  885. midiCallback(pInClient, MIM_CLOSE, 0L, 0L);
  886. midiCleanUp(pInClient);
  887. return MMSYSERR_NOERROR;
  888. case MIDM_ADDBUFFER:
  889. D2(("MIDM_ADDBUFFER"));
  890. // check if it's been prepared
  891. if (!(((LPMIDIHDR)dwParam1)->dwFlags & MHDR_PREPARED))
  892. return MIDIERR_UNPREPARED;
  893. WinAssert(!(((LPMIDIHDR)dwParam1)->dwFlags & MHDR_INQUEUE));
  894. // if it is already in our Q, then we cannot do this
  895. if ( ((LPMIDIHDR)dwParam1)->dwFlags & MHDR_INQUEUE )
  896. return ( MIDIERR_STILLPLAYING );
  897. // store the pointer to my MIDIALLOC structure in the midihdr
  898. pInClient = (PMIDIALLOC)dwUser;
  899. ((LPMIDIHDR)dwParam1)->reserved = (DWORD)(LPSTR)pInClient;
  900. return midiInWrite((LPMIDIHDR)dwParam1, pInClient);
  901. case MIDM_STOP:
  902. D2(("MIDM_PAUSE"));
  903. pInClient = (PMIDIALLOC)dwUser;
  904. pInClient->AuxParam.State = MIDI_DD_STOP;
  905. return midiThreadCall(MidiThreadSetState, pInClient);
  906. case MIDM_START:
  907. D2(("MIDM_RESTART"));
  908. pInClient = (PMIDIALLOC)dwUser;
  909. pInClient->AuxParam.State = MIDI_DD_RECORD;
  910. return midiThreadCall(MidiThreadSetState, pInClient);
  911. case MIDM_RESET:
  912. D2(("MIDM_RESET"));
  913. pInClient = (PMIDIALLOC)dwUser;
  914. pInClient->AuxParam.State = MIDI_DD_RESET;
  915. return midiThreadCall(MidiThreadSetState, pInClient);
  916. default:
  917. return MMSYSERR_NOTSUPPORTED;
  918. }
  919. //
  920. // Should not get here
  921. //
  922. WinAssert(0);
  923. return MMSYSERR_NOTSUPPORTED;
  924. }
  925. /****************************************************************************
  926. This function conforms to the standard Midi output driver message proc
  927. (modMessage), which is documented in mmddk.d.
  928. ****************************************************************************/
  929. DWORD APIENTRY modMessage(DWORD id, DWORD msg, DWORD dwUser, DWORD dwParam1,
  930. DWORD dwParam2)
  931. {
  932. PMIDIALLOC pOutClient;
  933. switch (msg) {
  934. case MODM_GETNUMDEVS:
  935. D2(("MODM_GETNUMDEVS"));
  936. return sndGetNumDevs(MIDI_OUT);
  937. case MODM_GETDEVCAPS:
  938. D2(("MODM_GETDEVCAPS"));
  939. return midiGetDevCaps(id, MIDI_OUT, (LPBYTE)dwParam1,
  940. (DWORD)dwParam2);
  941. case MODM_OPEN:
  942. D2(("MODM_OPEN"));
  943. return midiOpen(MIDI_OUT, id, dwUser, dwParam1, dwParam2);
  944. case MODM_CLOSE:
  945. D2(("MODM_CLOSE"));
  946. pOutClient = (PMIDIALLOC)dwUser;
  947. midiCallback(pOutClient, MOM_CLOSE, 0L, 0L);
  948. //
  949. // Close our device
  950. //
  951. midiCleanUp(pOutClient);
  952. return MMSYSERR_NOERROR;
  953. case MODM_DATA:
  954. D2(("MODM_DATA"));
  955. {
  956. int i;
  957. BYTE b[4];
  958. for (i = 0; i < 4; i ++) {
  959. b[i] = (BYTE)(dwParam1 % 256);
  960. dwParam1 /= 256;
  961. }
  962. return midiOutWrite(b, modMIDIlength((PMIDIALLOC)dwUser, b[0]),
  963. (PMIDIALLOC)dwUser);
  964. }
  965. case MODM_LONGDATA:
  966. D2(("MODM_LONGDATA"));
  967. pOutClient = (PMIDIALLOC)dwUser;
  968. {
  969. LPMIDIHDR lpHdr;
  970. MMRESULT mRet;
  971. //
  972. // check if it's been prepared
  973. //
  974. lpHdr = (LPMIDIHDR)dwParam1;
  975. if (!(lpHdr->dwFlags & MHDR_PREPARED)) {
  976. return MIDIERR_UNPREPARED;
  977. }
  978. //
  979. //
  980. //
  981. mRet = midiOutWrite((LPBYTE)lpHdr->lpData, lpHdr->dwBufferLength,
  982. pOutClient);
  983. // note that clearing the done bit or setting the inqueue bit
  984. // isn't necessary here since this function is synchronous -
  985. // the client will not get control back until it's done.
  986. lpHdr->dwFlags |= MHDR_DONE;
  987. // notify client
  988. if (mRet == MMSYSERR_NOERROR) {
  989. midiCallback(pOutClient, MOM_DONE, (DWORD)lpHdr, 0L);
  990. }
  991. return mRet;
  992. }
  993. case MODM_RESET:
  994. D2(("MODM_RESET"));
  995. return midiSetState((PMIDIALLOC)dwUser, MIDI_DD_RESET);
  996. case MODM_SETVOLUME:
  997. D2(("MODM_SETVOLUME"));
  998. //pOutClient = (PMIDIALLOC)dwUser;
  999. //pOutClient->AuxParam.GetSetData.pData = *(PBYTE *)&dwParam1;
  1000. //pOutClient->AuxParam.GetSetData.DataLen = sizeof(DWORD);
  1001. //pOutClient->AuxParam.GetSetData.Function = IOCTL_MIDI_SET_VOLUME;
  1002. //return midiThreadCall(MidiThreadSetData, pOutClient);
  1003. return sndSetData(MIDI_OUT, id, sizeof(DWORD),
  1004. (PBYTE)&dwParam1, IOCTL_MIDI_SET_VOLUME);
  1005. case MODM_GETVOLUME:
  1006. D2(("MODM_GETVOLUME"));
  1007. //pOutClient = (PMIDIALLOC)dwUser;
  1008. //pOutClient->AuxParam.GetSetData.pData = *(PBYTE *)&dwParam1;
  1009. //pOutClient->AuxParam.GetSetData.DataLen = sizeof(DWORD);
  1010. //pOutClient->AuxParam.GetSetData.Function = IOCTL_MIDI_GET_VOLUME;
  1011. //return midiThreadCall(MidiThreadGetData, pOutClient);
  1012. return sndGetData(MIDI_OUT, id, sizeof(DWORD),
  1013. (PBYTE)dwParam1, IOCTL_MIDI_GET_VOLUME);
  1014. case MODM_CACHEPATCHES:
  1015. D2(("MODM_CACHEPATCHES"));
  1016. pOutClient = (PMIDIALLOC)dwUser;
  1017. {
  1018. MIDI_DD_CACHE_PATCHES AppData;
  1019. DWORD BytesReturned;
  1020. AppData.Bank = HIWORD(dwParam2);
  1021. AppData.Flags = LOWORD(dwParam2);
  1022. memcpy(AppData.Patches, (PVOID)dwParam1, sizeof(AppData.Patches));
  1023. return DeviceIoControl(
  1024. pOutClient->hDev,
  1025. IOCTL_MIDI_CACHE_PATCHES,
  1026. (PVOID)&AppData,
  1027. sizeof(AppData),
  1028. NULL,
  1029. 0,
  1030. &BytesReturned,
  1031. NULL) ?
  1032. MMSYSERR_NOERROR :
  1033. sndTranslateStatus();
  1034. }
  1035. case MODM_CACHEDRUMPATCHES:
  1036. D2(("MODM_CACHEDRUMPATCHES"));
  1037. pOutClient = (PMIDIALLOC)dwUser;
  1038. {
  1039. MIDI_DD_CACHE_DRUM_PATCHES AppData;
  1040. DWORD BytesReturned;
  1041. AppData.Patch = HIWORD(dwParam2);
  1042. AppData.Flags = LOWORD(dwParam2);
  1043. memcpy(AppData.DrumPatches, (PVOID)dwParam1,
  1044. sizeof(AppData.DrumPatches));
  1045. return DeviceIoControl(
  1046. pOutClient->hDev,
  1047. IOCTL_MIDI_CACHE_DRUM_PATCHES,
  1048. (PVOID)&AppData,
  1049. sizeof(AppData),
  1050. NULL,
  1051. 0,
  1052. &BytesReturned,
  1053. NULL) ?
  1054. MMSYSERR_NOERROR :
  1055. sndTranslateStatus();
  1056. }
  1057. default:
  1058. return MMSYSERR_NOTSUPPORTED;
  1059. }
  1060. //
  1061. // Should not get here
  1062. //
  1063. WinAssert(0);
  1064. return MMSYSERR_NOTSUPPORTED;
  1065. }
  1066. /***********************************************************************
  1067. UTILITY ROUTINES PORTED DIRECTLY FROM WIN 3.1
  1068. ***********************************************************************/
  1069. /****************************************************************************
  1070. * @doc INTERNAL
  1071. *
  1072. * @api int | modMIDIlength | Get the length of a short midi message.
  1073. *
  1074. * @parm DWORD | dwMessage | The message.
  1075. *
  1076. * @rdesc Returns the length of the message.
  1077. ***************************************************************************/
  1078. STATIC int modMIDIlength(PMIDIALLOC pClient, BYTE b)
  1079. {
  1080. if (b >= 0xF8) { // system realtime
  1081. /* for realtime messages, leave running status untouched */
  1082. return 1; // write one byte
  1083. }
  1084. switch (b) {
  1085. case 0xF0: case 0xF4: case 0xF5: case 0xF6: case 0xF7:
  1086. pClient->l = 1;
  1087. return pClient->l;
  1088. case 0xF1: case 0xF3:
  1089. pClient->l = 2;
  1090. return pClient->l;
  1091. case 0xF2:
  1092. pClient->l = 3;
  1093. return pClient->l;
  1094. }
  1095. switch (b & 0xF0) {
  1096. case 0x80: case 0x90: case 0xA0: case 0xB0: case 0xE0:
  1097. pClient->l = 3;
  1098. return pClient->l;
  1099. case 0xC0: case 0xD0:
  1100. pClient->l = 2;
  1101. return pClient->l;
  1102. }
  1103. return (pClient->l - 1); // uses previous value if data byte (running status)
  1104. }
  1105. /****************************************************************************
  1106. * @doc INTERNAL
  1107. *
  1108. * @api void | midBufferWrite | This function writes a byte into the long
  1109. * message buffer. If the buffer is full or a SYSEX_ERROR or
  1110. * end-of-sysex byte is received, the buffer is marked as 'done' and
  1111. * it's owner is called back.
  1112. *
  1113. * @parm BYTE | byte | The byte received.
  1114. *
  1115. * @rdesc There is no return value
  1116. ***************************************************************************/
  1117. STATIC void midBufferWrite(PMIDIALLOC pClient, BYTE byte)
  1118. {
  1119. LPMIDIHDR lpmh;
  1120. UINT msg;
  1121. // if no buffers, nothing happens
  1122. if (pClient->lpMIQueue == NULL)
  1123. return;
  1124. lpmh = pClient->lpMIQueue;
  1125. if (byte == SYSEX_ERROR) {
  1126. D2(("sysexerror"));
  1127. msg = MIM_LONGERROR;
  1128. }
  1129. else {
  1130. D2(("bufferwrite"));
  1131. msg = MIM_LONGDATA;
  1132. *((LPSTR)(lpmh->lpData) + pClient->Mid->dwCurData++) = byte;
  1133. }
  1134. // if end of sysex, buffer full or error, send them back the buffer
  1135. if ((byte == SYSEX_ERROR) || (byte == 0xF7) || (pClient->Mid->dwCurData >= lpmh->dwBufferLength)) {
  1136. D2(("bufferdone"));
  1137. pClient->lpMIQueue = pClient->lpMIQueue->lpNext;
  1138. lpmh->dwBytesRecorded = pClient->Mid->dwCurData;
  1139. pClient->Mid->dwCurData = 0L;
  1140. lpmh->dwFlags |= MHDR_DONE;
  1141. lpmh->dwFlags &= ~MHDR_INQUEUE;
  1142. midiCallback(pClient, msg, (DWORD)lpmh, pClient->Mid->dwMsgTime);
  1143. }
  1144. return;
  1145. }
  1146. /****************************************************************************
  1147. * @doc INTERNAL
  1148. *
  1149. * @api void | midByteRec | This function constructs the complete midi
  1150. * messages from the individual bytes received and passes the message
  1151. * to the client via his callback.
  1152. *
  1153. * @parm WORD | word | The byte received is in the low order byte.
  1154. *
  1155. * @rdesc There is no return value
  1156. *
  1157. * @comm Note that currently running status isn't turned off on errors.
  1158. ***************************************************************************/
  1159. STATIC void midByteRec(PMIDIALLOC pClient, BYTE byte)
  1160. {
  1161. if (!pClient->Mid->fMidiInStarted)
  1162. return;
  1163. // if it's a system realtime message, send it
  1164. // this does not affect running status or any current message
  1165. if (byte >= 0xF8) {
  1166. D2((" rt"));
  1167. midiCallback(pClient, MIM_DATA, (DWORD)byte, pClient->Mid->dwCurTime);
  1168. }
  1169. // else if it's a system common message
  1170. else if (byte >= 0xF0) {
  1171. if (pClient->Mid->fSysex) { // if we're in a sysex
  1172. pClient->Mid->fSysex = FALSE; // status byte during sysex ends it
  1173. if (byte == 0xF7)
  1174. {
  1175. midBufferWrite(pClient, 0xF7); // write in long message buffer
  1176. return;
  1177. }
  1178. else
  1179. midBufferWrite(pClient, SYSEX_ERROR); // secret code indicating error
  1180. }
  1181. if (pClient->Mid->dwMsg) { // throw away any incomplete short data
  1182. midiCallback(pClient, MIM_ERROR, pClient->Mid->dwMsg, pClient->Mid->dwMsgTime);
  1183. pClient->Mid->dwMsg = 0L;
  1184. }
  1185. pClient->Mid->status = 0; // kill running status
  1186. pClient->Mid->dwMsgTime = pClient->Mid->dwCurTime; // save timestamp
  1187. switch(byte) {
  1188. case 0xF0:
  1189. D2((" F0"));
  1190. pClient->Mid->fSysex = TRUE;
  1191. midBufferWrite(pClient, 0xF0);
  1192. break;
  1193. case 0xF7:
  1194. D2((" F7"));
  1195. if (!pClient->Mid->fSysex)
  1196. midiCallback(pClient, MIM_ERROR, (DWORD)byte, pClient->Mid->dwMsgTime);
  1197. // else already took care of it above
  1198. break;
  1199. case 0xF4: // system common, no data bytes
  1200. case 0xF5:
  1201. case 0xF6:
  1202. D2((" status0"));
  1203. midiCallback(pClient, MIM_DATA, (DWORD)byte, pClient->Mid->dwMsgTime);
  1204. pClient->Mid->bBytePos = 0;
  1205. break;
  1206. case 0xF1: // system common, one data byte
  1207. case 0xF3:
  1208. D2((" status1"));
  1209. pClient->Mid->dwMsg |= byte;
  1210. pClient->Mid->bBytesLeft = 1;
  1211. pClient->Mid->bBytePos = 1;
  1212. break;
  1213. case 0xF2: // system common, two data bytes
  1214. D2((" status2"));
  1215. pClient->Mid->dwMsg |= byte;
  1216. pClient->Mid->bBytesLeft = 2;
  1217. pClient->Mid->bBytePos = 1;
  1218. break;
  1219. }
  1220. }
  1221. // else if it's a channel message
  1222. else if (byte >= 0x80) {
  1223. if (pClient->Mid->fSysex) { // if we're in a sysex
  1224. pClient->Mid->fSysex = FALSE; // status byte during sysex ends it
  1225. midBufferWrite(pClient, SYSEX_ERROR); // secret code indicating error
  1226. }
  1227. if (pClient->Mid->dwMsg) { // throw away any incomplete data
  1228. midiCallback(pClient, MIM_ERROR, pClient->Mid->dwMsg, pClient->Mid->dwMsgTime);
  1229. pClient->Mid->dwMsg = 0L;
  1230. }
  1231. pClient->Mid->status = byte; // save for running status
  1232. pClient->Mid->dwMsgTime = pClient->Mid->dwCurTime; // save timestamp
  1233. pClient->Mid->dwMsg |= byte;
  1234. pClient->Mid->bBytePos = 1;
  1235. switch(byte & 0xF0) {
  1236. case 0xC0: // channel message, one data byte
  1237. case 0xD0:
  1238. D2((" status1"));
  1239. pClient->Mid->bBytesLeft = 1;
  1240. break;
  1241. case 0x80: // channel message, two data bytes
  1242. case 0x90:
  1243. case 0xA0:
  1244. case 0xB0:
  1245. case 0xE0:
  1246. D2((" status2"));
  1247. pClient->Mid->bBytesLeft = 2;
  1248. break;
  1249. }
  1250. }
  1251. // else if it's an expected data byte for a long message
  1252. else if (pClient->Mid->fSysex) {
  1253. D2((" sxdata"));
  1254. midBufferWrite(pClient, byte); // write in long message buffer
  1255. }
  1256. // else if it's an expected data byte for a short message
  1257. else if (pClient->Mid->bBytePos != 0) {
  1258. D2((" data"));
  1259. if ((pClient->Mid->status) && (pClient->Mid->bBytePos == 1)) { // if running status
  1260. pClient->Mid->dwMsg |= pClient->Mid->status;
  1261. pClient->Mid->dwMsgTime = pClient->Mid->dwCurTime; // save timestamp
  1262. }
  1263. pClient->Mid->dwMsg += (DWORD)byte << ((pClient->Mid->bBytePos++) * 8);
  1264. if (--pClient->Mid->bBytesLeft == 0) {
  1265. midiCallback(pClient, MIM_DATA, pClient->Mid->dwMsg, pClient->Mid->dwMsgTime);
  1266. pClient->Mid->dwMsg = 0L;
  1267. if (pClient->Mid->status) {
  1268. pClient->Mid->bBytesLeft = pClient->Mid->bBytePos - (BYTE)1;
  1269. pClient->Mid->bBytePos = 1;
  1270. }
  1271. else {
  1272. pClient->Mid->bBytePos = 0;
  1273. }
  1274. }
  1275. }
  1276. // else if it's an unexpected data byte
  1277. else {
  1278. D2((" baddata"));
  1279. midiCallback(pClient, MIM_ERROR, (DWORD)byte, pClient->Mid->dwMsgTime);
  1280. }
  1281. return;
  1282. }
  1283. /****************************************************************************
  1284. * @doc INTERNAL
  1285. *
  1286. * @api void | midFreeQ | Free all buffers in the MIQueue.
  1287. *
  1288. * @comm Currently this is only called after sending off any partially filled
  1289. * buffers, so all buffers here are empty. The timestamp value is 0 in
  1290. * this case.
  1291. *
  1292. * @rdesc There is no return value.
  1293. ***************************************************************************/
  1294. STATIC void midFreeQ(PMIDIALLOC pClient)
  1295. {
  1296. LPMIDIHDR lpH, lpN;
  1297. lpH = pClient->lpMIQueue; // point to top of the queue
  1298. pClient->lpMIQueue = NULL; // mark the queue as empty
  1299. pClient->Mid->dwCurData = 0L;
  1300. while (lpH) {
  1301. lpN = lpH->lpNext;
  1302. lpH->dwFlags |= MHDR_DONE;
  1303. lpH->dwFlags &= ~MHDR_INQUEUE;
  1304. lpH->dwBytesRecorded = 0;
  1305. midiCallback(pClient, MIM_LONGDATA, (DWORD)lpH,
  1306. pClient->Mid->dwCurTime);
  1307. lpH = lpN;
  1308. }
  1309. }
  1310. /****************************************************************************
  1311. * @doc INTERNAL
  1312. *
  1313. * @api void | midSendPartBuffer | This function is called from midStop().
  1314. * It looks at the buffer at the head of the queue and, if it contains
  1315. * any data, marks it as done as sends it back to the client.
  1316. *
  1317. * @rdesc The return value is the number of bytes transfered. A value of zero
  1318. * indicates that there was no more data in the input queue.
  1319. ***************************************************************************/
  1320. STATIC void midSendPartBuffer(PMIDIALLOC pClient)
  1321. {
  1322. LPMIDIHDR lpH;
  1323. if (pClient->lpMIQueue && pClient->Mid->dwCurData) {
  1324. lpH = pClient->lpMIQueue;
  1325. pClient->lpMIQueue = pClient->lpMIQueue->lpNext;
  1326. lpH->dwFlags |= MHDR_DONE;
  1327. lpH->dwFlags &= ~MHDR_INQUEUE;
  1328. pClient->Mid->dwCurData = 0L;
  1329. midiCallback(pClient, MIM_LONGERROR, (DWORD)lpH,
  1330. pClient->Mid->dwMsgTime);
  1331. }
  1332. }