|
|
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.