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