home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / pcmagazi / 1992 / 14 / midbuf.c < prev    next >
C/C++ Source or Header  |  1992-04-15  |  26KB  |  812 lines

  1. /*-------------------------------------------------------------------
  2.    MIDBUF.C -- Dynamic Link Library for Buffered MIDI Short Messages
  3.                (c) Charles Petzold
  4.   -------------------------------------------------------------------*/
  5.  
  6. #include <windows.h>
  7. #include <mmsystem.h>
  8.  
  9.           // Definitions of handle and structure for buffered MIDI input
  10.           // -----------------------------------------------------------
  11.  
  12. typedef UINT HMIDIBUFIN ;
  13. typedef HMIDIBUFIN FAR * LPHMIDIBUFIN ;
  14.  
  15. typedef struct
  16.      {
  17.      HMIDIBUFIN hMidiBufIn ;       // Used to verify validity of handle
  18.      HMIDIIN    hMidiIn ;          // Real MIDI input handle
  19.      DWORD      dwCallBack ;       // Callback passed in open function
  20.      DWORD      dwUser ;           // Application data passed in open function
  21.      DWORD      dwFlags ;          // Flags passed in open function
  22.      LPMIDIHDR  lpmh ;             // Pointer to first MIDIHDR structure
  23.      DWORD      dwLastTime ;       // Used to calculate delta-times
  24.      DWORD      dwStartTime ;      // Used to time-stamp whole buffers
  25.      }
  26.      MIDIBUFIN ;
  27.  
  28. typedef MIDIBUFIN * PMIDIBUFIN ;
  29.  
  30.           // Definitions of handle and structure for buffered MIDI output
  31.           // ------------------------------------------------------------
  32.  
  33. typedef UINT HMIDIBUFOUT ;
  34. typedef HMIDIBUFOUT FAR * LPHMIDIBUFOUT ;
  35.  
  36. typedef struct
  37.      {
  38.      HMIDIBUFOUT hMidiBufOut ;     // Used to verify validity of handle
  39.      HMIDIOUT    hMidiOut ;        // Real MIDI output handle
  40.      DWORD       dwCallBack ;      // Callback passed in open function
  41.      DWORD       dwUser ;          // Application data passed in open function
  42.      DWORD       dwFlags ;         // Flags passed in open function
  43.      LPMIDIHDR   lpmh ;            // Pointer to first MIDIHDR structure
  44.      DWORD       dwDataPtr ;       // Pointer to current data in buffer
  45.      DWORD       dwTime ;          // Time before next output event
  46.      UINT        iTimerID ;        // ID of multimedia timer
  47.      BOOL        bPaused ;         // Flag if paused playing
  48.      }
  49.      MIDIBUFOUT ;
  50.  
  51. typedef MIDIBUFOUT * PMIDIBUFOUT ;
  52.  
  53.           // Other miscellaneous definitions
  54.           // -------------------------------
  55.  
  56. typedef void (FAR PASCAL * MMCALLBACK) (UINT, UINT, DWORD, DWORD, DWORD) ;
  57.  
  58. #define MIN(a,b)      (((a) < (b)) ? (a) : (b))
  59. #define MAX(a,b)      (((a) > (b)) ? (a) : (b))
  60. #define MINMAX(a,x,b) (MIN (MAX (x, a), b))
  61.  
  62.           // Prevent name-mangling for exported functions
  63.           // --------------------------------------------
  64.  
  65. extern "C"
  66. {
  67. UINT  FAR PASCAL xMidiInAddBuffer (HMIDIBUFIN, LPMIDIHDR, UINT) ;
  68. UINT  FAR PASCAL xMidiInClose (HMIDIBUFIN) ;
  69. UINT  FAR PASCAL xMidiInGetID (HMIDIBUFIN, UINT FAR *) ;
  70. DWORD FAR PASCAL xMidiInMessage (HMIDIBUFIN, UINT, DWORD, DWORD) ;
  71. UINT  FAR PASCAL xMidiInOpen (LPHMIDIBUFIN, UINT, DWORD, DWORD, DWORD) ;
  72. UINT  FAR PASCAL xMidiInPrepareHeader (HMIDIBUFIN, LPMIDIHDR, UINT) ;
  73. UINT  FAR PASCAL xMidiInReset (HMIDIBUFIN) ;
  74. UINT  FAR PASCAL xMidiInShortBuffer (HMIDIBUFIN, LPMIDIHDR, UINT) ;
  75. UINT  FAR PASCAL xMidiInStart (HMIDIBUFIN) ;
  76. UINT  FAR PASCAL xMidiInStop (HMIDIBUFIN) ;
  77. UINT  FAR PASCAL xMidiInUnprepareHeader (HMIDIBUFIN, LPMIDIHDR, UINT) ;
  78.  
  79. UINT  FAR PASCAL xMidiOutCacheDrumPatches (HMIDIBUFOUT, UINT, LPKEYARRAY, UINT);
  80. UINT  FAR PASCAL xMidiOutCachePatches (HMIDIBUFOUT, UINT, LPPATCHARRAY, UINT) ;
  81. UINT  FAR PASCAL xMidiOutClose (HMIDIBUFOUT) ;
  82. UINT  FAR PASCAL xMidiOutGetID (HMIDIBUFOUT, UINT FAR *) ;
  83. UINT  FAR PASCAL xMidiOutLongMsg (HMIDIBUFOUT, LPMIDIHDR, UINT) ;
  84. DWORD FAR PASCAL xMidiOutMessage (HMIDIBUFOUT, UINT, DWORD, DWORD) ;
  85. UINT  FAR PASCAL xMidiOutOpen (LPHMIDIBUFOUT, UINT, DWORD, DWORD, DWORD) ;
  86. UINT  FAR PASCAL xMidiOutPause (HMIDIBUFOUT) ;
  87. UINT  FAR PASCAL xMidiOutPrepareHeader (HMIDIBUFOUT, LPMIDIHDR, UINT) ;
  88. UINT  FAR PASCAL xMidiOutReset (HMIDIBUFOUT) ;
  89. UINT  FAR PASCAL xMidiOutRestart (HMIDIBUFOUT) ;
  90. UINT  FAR PASCAL xMidiOutShortBuffer (HMIDIBUFOUT, LPMIDIHDR, UINT) ;
  91. UINT  FAR PASCAL xMidiOutShortMsg (HMIDIBUFOUT, DWORD) ;
  92. UINT  FAR PASCAL xMidiOutUnprepareHeader (HMIDIBUFOUT, LPMIDIHDR, UINT) ;
  93. }
  94.  
  95.           // The only global variable (set during LibMain)
  96.           // ---------------------------------------------
  97.  
  98. TIMECAPS tc ;
  99.  
  100.           // Initialization routine: Do not unlock data segment!
  101.           // ---------------------------------------------------
  102.  
  103. int FAR PASCAL LibMain (HANDLE hInstance, UINT wDataSeg, UINT wHeapSize,
  104.                         LPSTR lpszCmdLine)
  105.      {
  106.      timeGetDevCaps (&tc, sizeof (TIMECAPS)) ;
  107.  
  108.      return 1 ;
  109.      }
  110.  
  111.           // MidiInCallBack:  Call-back function for MIDI input
  112.           // --------------------------------------------------
  113.  
  114. void FAR PASCAL _export MidiInCallBack (HMIDIIN hMidiIn, UINT wMsg,
  115.                                         DWORD dwUser, DWORD dw1, DWORD dw2)
  116.      {
  117.      DWORD huge * lpData ;
  118.      HMIDIBUFIN   hMidiBufIn ;
  119.      PMIDIBUFIN   pmb ;
  120.      LPMIDIHDR    lpmh ;
  121.  
  122.                // Get some useful information
  123.  
  124.      pmb        = (PMIDIBUFIN) dwUser ;
  125.      hMidiBufIn = pmb->hMidiBufIn ;
  126.  
  127.                // Post message to window or call callback function
  128.  
  129.      if ((pmb->dwFlags & CALLBACK_TYPEMASK) == CALLBACK_WINDOW)
  130.           PostMessage ((HWND) pmb->dwCallBack, wMsg, hMidiBufIn, dw1) ;
  131.  
  132.      if ((pmb->dwFlags & CALLBACK_TYPEMASK) == CALLBACK_FUNCTION)
  133.           ((MMCALLBACK) pmb->dwCallBack) (hMidiBufIn, wMsg, pmb->dwUser,
  134.                                           dw1, dw2) ;
  135.  
  136.                // For DATA messages, try to store them in a buffer
  137.  
  138.      if (wMsg == MIM_DATA)
  139.           {
  140.                     // If no MIDIHDR present, can't store data
  141.  
  142.           if (NULL == (lpmh = pmb->lpmh))
  143.                return ;
  144.  
  145.                     // Do not store Active Sensing messages
  146.  
  147.           if (dw1 == 0x000000FE)
  148.                return ;
  149.  
  150.                     // Store the data -- delta time first then MIDI message
  151.  
  152.           lpData = (DWORD huge *) lpmh->lpData ;
  153.  
  154.           lpData [lpmh->dwBytesRecorded / 4] = dw2 - pmb->dwLastTime ;
  155.           lpmh->dwBytesRecorded += 4 ;
  156.           lpData [lpmh->dwBytesRecorded / 4] = dw1 ;
  157.           lpmh->dwBytesRecorded += 4 ;
  158.  
  159.                     // Save the latest time stamp
  160.  
  161.           pmb->dwLastTime = dw2 ;
  162.  
  163.                     // If at the end of a buffer, get the next one,
  164.                     //    and post a LONGDATA message
  165.  
  166.           if (lpmh->dwBytesRecorded + 8 > lpmh->dwBufferLength)
  167.                {
  168.                pmb->lpmh = lpmh->lpNext ;
  169.                lpmh->dwFlags |= MHDR_DONE ;
  170.  
  171.                if ((pmb->dwFlags & CALLBACK_TYPEMASK) == CALLBACK_WINDOW)
  172.                     PostMessage ((HWND) pmb->dwCallBack, MM_MIM_LONGDATA,
  173.                                  hMidiBufIn, (DWORD) lpmh) ;
  174.  
  175.                if ((pmb->dwFlags & CALLBACK_TYPEMASK) == CALLBACK_FUNCTION)
  176.                     ((MMCALLBACK) pmb->dwCallBack) (hMidiBufIn, MIM_LONGDATA,
  177.                                              pmb->dwUser, (DWORD) lpmh, dw2) ;
  178.                }
  179.           }
  180.      }
  181.  
  182.           // MidiInValidateHandle:  Convert handle to pointer and validate it
  183.           // ----------------------------------------------------------------
  184.  
  185. PMIDIBUFIN MidiInValidateHandle (HMIDIBUFIN hMidiBufIn)
  186.      {
  187.      PMIDIBUFIN pmb ;
  188.  
  189.      pmb = (PMIDIBUFIN) hMidiBufIn ;
  190.  
  191.      if (pmb == NULL || pmb->hMidiBufIn != hMidiBufIn)
  192.           return NULL ;
  193.  
  194.      return pmb ;
  195.      }
  196.  
  197.           // MidiInAddBuffer:  Pass through
  198.           // ------------------------------
  199.  
  200. UINT FAR PASCAL xMidiInAddBuffer (HMIDIBUFIN hMidiBufIn,
  201.                                   LPMIDIHDR lpmh, UINT wSize)
  202.      {
  203.      PMIDIBUFIN pmb ;
  204.  
  205.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  206.           return MMSYSERR_INVALHANDLE ;
  207.  
  208.      return midiInAddBuffer (pmb->hMidiIn, lpmh, wSize) ;
  209.      }
  210.  
  211.           // MidiInClose:  Free the handle
  212.           // -----------------------------
  213.  
  214. UINT FAR PASCAL xMidiInClose (HMIDIBUFIN hMidiBufIn)
  215.      {
  216.      PMIDIBUFIN pmb ;
  217.      UINT       wError ;
  218.  
  219.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  220.           return MMSYSERR_INVALHANDLE ;
  221.  
  222.      if (0L == (wError = midiInClose (pmb->hMidiIn)))
  223.           {
  224.           if (pmb->lpmh != NULL)
  225.                return MIDIERR_STILLPLAYING ;
  226.  
  227.           LocalFree (hMidiBufIn) ;
  228.           }
  229.  
  230.      return wError ;
  231.      }
  232.  
  233.           // MidiInGetID:  Pass through
  234.           // --------------------------
  235.  
  236. UINT FAR PASCAL xMidiInGetID (HMIDIBUFIN hMidiBufIn, UINT FAR * lpiDeviceID)
  237.      {
  238.      PMIDIBUFIN pmb ;
  239.  
  240.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  241.           return MMSYSERR_INVALHANDLE ;
  242.  
  243.      return midiInGetID (pmb->hMidiIn, lpiDeviceID) ;
  244.      }
  245.  
  246.           // MidiInMessage:  Pass through
  247.           // ----------------------------
  248.  
  249. DWORD FAR PASCAL xMidiInMessage (HMIDIBUFIN hMidiBufIn, UINT msg,
  250.                                  DWORD dw1, DWORD dw2)
  251.      {
  252.      PMIDIBUFIN pmb ;
  253.  
  254.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  255.           return MMSYSERR_INVALHANDLE ;
  256.  
  257.      return midiInMessage (pmb->hMidiIn, msg, dw1, dw2) ;
  258.      }
  259.  
  260.           // MidiInOpen:  Allocate handle and initialize stuff
  261.           // -------------------------------------------------
  262.  
  263. UINT FAR PASCAL xMidiInOpen (LPHMIDIBUFIN lphMidiBufIn,
  264.                              UINT  wID,    DWORD dwCallBack,
  265.                              DWORD dwUser, DWORD dwFlags)
  266.      {
  267.      HMIDIBUFIN hMidiBufIn ;
  268.      PMIDIBUFIN pmb ;
  269.      UINT       wError ;
  270.  
  271.      if (NULL == (hMidiBufIn = LocalAlloc (LPTR, sizeof (MIDIBUFIN))))
  272.           MMSYSERR_NOMEM ;
  273.  
  274.      pmb = (PMIDIBUFIN) hMidiBufIn ;
  275.  
  276.      pmb->hMidiBufIn = hMidiBufIn ;
  277.      pmb->dwCallBack = dwCallBack ;
  278.      pmb->dwUser     = dwUser ;
  279.      pmb->dwFlags    = dwFlags ;
  280.  
  281.      wError = midiInOpen (&pmb->hMidiIn, wID, (DWORD) MidiInCallBack,
  282.                           (DWORD) (LPVOID) pmb, CALLBACK_FUNCTION) ;
  283.  
  284.      if (wError == 0)
  285.           *lphMidiBufIn = hMidiBufIn ;
  286.      else
  287.           LocalFree (hMidiBufIn) ;
  288.  
  289.      return wError ;
  290.      }
  291.  
  292.           // MidiInPrepareHeader:  Pass through
  293.           // ----------------------------------
  294.  
  295. UINT FAR PASCAL xMidiInPrepareHeader (HMIDIBUFIN hMidiBufIn,
  296.                                       LPMIDIHDR lpmh, UINT wSize)
  297.      {
  298.      PMIDIBUFIN pmb ;
  299.  
  300.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  301.           return MMSYSERR_INVALHANDLE ;
  302.  
  303.      return midiInPrepareHeader (pmb->hMidiIn, lpmh, wSize) ;
  304.      }
  305.  
  306.           // MidiInReset:  Return pending buffers to application
  307.           // ---------------------------------------------------
  308.  
  309. UINT FAR PASCAL xMidiInReset (HMIDIBUFIN hMidiBufIn)
  310.      {
  311.      PMIDIBUFIN pmb ;
  312.      LPMIDIHDR  lpmh ;
  313.      UINT       wError ;
  314.  
  315.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  316.           return MMSYSERR_INVALHANDLE ;
  317.  
  318.      if (0L == (wError = midiInReset (pmb->hMidiIn)))
  319.           {
  320.           while (pmb->lpmh != NULL)
  321.                {
  322.                lpmh           = pmb->lpmh ;
  323.                pmb->lpmh      = lpmh->lpNext ;
  324.                lpmh->dwFlags |= MHDR_DONE ;
  325.  
  326.                if ((pmb->dwFlags & CALLBACK_TYPEMASK) == CALLBACK_WINDOW)
  327.                     PostMessage ((HWND) pmb->dwCallBack, MM_MIM_LONGDATA,
  328.                                  hMidiBufIn, (DWORD) lpmh) ;
  329.  
  330.                if ((pmb->dwFlags & CALLBACK_TYPEMASK) == CALLBACK_FUNCTION)
  331.                     ((MMCALLBACK) pmb->dwCallBack) (hMidiBufIn, MIM_LONGDATA,
  332.                                         pmb->dwUser, (DWORD) lpmh,
  333.                                         timeGetTime () - pmb->dwStartTime) ;
  334.                }
  335.           }
  336.  
  337.      return wError ;
  338.      }
  339.  
  340.           // MidiInShortBuffer:  Add buffer to chain
  341.           // ---------------------------------------
  342.  
  343. UINT FAR PASCAL xMidiInShortBuffer (HMIDIBUFIN hMidiBufIn,
  344.                                     LPMIDIHDR lpmh, UINT wSize)
  345.      {
  346.      PMIDIBUFIN pmb ;
  347.      LPMIDIHDR  lpmhSearch ;
  348.  
  349.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  350.           return MMSYSERR_INVALHANDLE ;
  351.  
  352.      if (!(lpmh->dwFlags & MHDR_PREPARED))
  353.           return MIDIERR_UNPREPARED ;
  354.  
  355.      lpmh->dwBytesRecorded = 0 ;
  356.      lpmh->lpNext = NULL ;
  357.  
  358.      if (pmb->lpmh == NULL)
  359.           pmb->lpmh = lpmh ;
  360.      else
  361.           {
  362.           lpmhSearch = pmb->lpmh ;
  363.  
  364.           while (lpmhSearch->lpNext != NULL)
  365.                lpmhSearch = lpmhSearch->lpNext ;
  366.  
  367.           lpmhSearch->lpNext = lpmh ;
  368.           }
  369.  
  370.      return 0 ;
  371.      }
  372.  
  373.           // MidiInStart:  Set time stamp to zero
  374.           // ------------------------------------
  375.  
  376. UINT FAR PASCAL xMidiInStart (HMIDIBUFIN hMidiBufIn)
  377.      {
  378.      PMIDIBUFIN pmb ;
  379.      UINT       wError ;
  380.  
  381.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  382.           return MMSYSERR_INVALHANDLE ;
  383.  
  384.      if (0 == (wError = midiInStart (pmb->hMidiIn)))
  385.           pmb->dwStartTime = timeGetTime () ;
  386.  
  387.      return wError ;
  388.      }
  389.  
  390.           // MidiInStop:  Pass through
  391.           // -------------------------
  392.  
  393. UINT FAR PASCAL xMidiInStop (HMIDIBUFIN hMidiBufIn)
  394.      {
  395.      PMIDIBUFIN pmb ;
  396.  
  397.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  398.           return MMSYSERR_INVALHANDLE ;
  399.  
  400.      return midiInStop (pmb->hMidiIn) ;
  401.      }
  402.  
  403.           // MidiInUnprepareHeader:  Pass through
  404.           // ------------------------------------
  405.  
  406. UINT FAR PASCAL xMidiInUnprepareHeader (HMIDIBUFIN hMidiBufIn,
  407.                                         LPMIDIHDR lpmh, UINT wSize)
  408.      {
  409.      PMIDIBUFIN pmb ;
  410.  
  411.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  412.           return MMSYSERR_INVALHANDLE ;
  413.  
  414.      return midiInUnprepareHeader (pmb->hMidiIn, lpmh, wSize) ;
  415.      }
  416.  
  417.           // MidiOutTimerFunc:  Send MIDI short messages from buffer
  418.           // -------------------------------------------------------
  419.  
  420. UINT MidiOutSetEvent (PMIDIBUFOUT) ;
  421.  
  422. void FAR PASCAL _export MidiOutTimerFunc (UINT wID, UINT wMsg,
  423.                                           DWORD dwUser, DWORD dw1, DWORD dw2)
  424.      {
  425.      DWORD        dwMsg ;
  426.      DWORD huge * lpData ;
  427.      PMIDIBUFOUT  pmb ;
  428.      LPMIDIHDR    lpmh ;
  429.  
  430.                // Get pointers to the MIDIBUF and current MIDIHDR structures
  431.  
  432.      pmb = (PMIDIBUFOUT) dwUser ;
  433.      lpmh = pmb->lpmh ;
  434.  
  435.                // Flag the timer event as finished
  436.  
  437.      pmb->iTimerID = 0 ;
  438.  
  439.                // For periods longer than maximum period, set new timer
  440.  
  441.      if (pmb->dwTime > 0)
  442.           {
  443.           pmb->iTimerID = MidiOutSetEvent (pmb) ;
  444.           return ;
  445.           }
  446.  
  447.                // If playback is paused, don't do anything
  448.  
  449.      if (pmb->bPaused)
  450.           return ;
  451.  
  452.                // Send MIDI messages out until next delta time is not zero
  453.                // Use next buffer if at end of buffer
  454.      do
  455.           {
  456.           lpData = (DWORD huge *) lpmh->lpData ;
  457.  
  458.           dwMsg = lpData [pmb->dwDataPtr / 4] ;
  459.           pmb->dwDataPtr += 4 ;
  460.           midiOutShortMsg (pmb->hMidiOut, dwMsg) ;
  461.  
  462.           if (pmb->dwDataPtr + 8 > lpmh->dwBufferLength)
  463.                {
  464.                pmb->lpmh      = lpmh->lpNext ;
  465.                lpmh->dwFlags |= MHDR_DONE ;
  466.  
  467.                if ((pmb->dwFlags & CALLBACK_TYPEMASK) == CALLBACK_WINDOW)
  468.                     PostMessage ((HWND) pmb->dwCallBack, MM_MOM_DONE,
  469.                                  pmb->hMidiBufOut, (DWORD) lpmh) ;
  470.  
  471.                if ((pmb->dwFlags & CALLBACK_TYPEMASK) == CALLBACK_FUNCTION)
  472.                     ((MMCALLBACK) pmb->dwCallBack) (pmb->hMidiBufOut,
  473.                           MM_MOM_DONE, pmb->dwUser, (DWORD) lpmh, 0L) ;
  474.  
  475.                lpmh = pmb->lpmh ;
  476.                pmb->dwDataPtr = 0 ;
  477.                }
  478.  
  479.           if (lpmh == NULL)
  480.                return ;
  481.  
  482.           lpData = (DWORD huge *) lpmh->lpData ;
  483.           pmb->dwTime = lpData [pmb->dwDataPtr / 4] ;
  484.           pmb->dwDataPtr += 4 ;
  485.           }
  486.      while (pmb->dwTime < tc.wPeriodMin) ;
  487.  
  488.                // Set a new timer event
  489.  
  490.      pmb->iTimerID = MidiOutSetEvent (pmb) ;
  491.      return ;
  492.      }
  493.  
  494.           // MidiOutSetEvent:  Simplified timeSetEvent Caller
  495.           // ------------------------------------------------
  496.  
  497. UINT MidiOutSetEvent (PMIDIBUFOUT pmb)
  498.      {
  499.      UINT iPeriod ;
  500.  
  501.      iPeriod = (UINT) MINMAX (tc.wPeriodMin, pmb->dwTime, tc.wPeriodMax) ;
  502.  
  503.      pmb->dwTime -= iPeriod ;
  504.  
  505.      return timeSetEvent (iPeriod, tc.wPeriodMin, MidiOutTimerFunc,
  506.                           (DWORD) (LPVOID) pmb, TIME_ONESHOT) ;
  507.      }
  508.  
  509.           // MidiOutValidateHandle:  Convert handle to pointer and validate it
  510.           // -----------------------------------------------------------------
  511.  
  512. PMIDIBUFOUT MidiOutValidateHandle (HMIDIBUFOUT hMidiBufOut)
  513.      {
  514.      PMIDIBUFOUT pmb ;
  515.  
  516.      pmb = (PMIDIBUFOUT) hMidiBufOut ;
  517.  
  518.      if (pmb == NULL || pmb->hMidiBufOut != hMidiBufOut)
  519.           return NULL ;
  520.  
  521.      return pmb ;
  522.      }
  523.  
  524.           // MidiOutCacheDrumPatches:  Pass through
  525.           // --------------------------------------
  526.  
  527. UINT FAR PASCAL xMidiOutCacheDrumPatches (HMIDIBUFIN hMidiBufIn, UINT wPatch,
  528.                                           LPKEYARRAY lpka, UINT wFlags)
  529.      {
  530.      PMIDIBUFIN pmb ;
  531.  
  532.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  533.           return MMSYSERR_INVALHANDLE ;
  534.  
  535.      return midiOutCacheDrumPatches (pmb->hMidiIn, wPatch, lpka, wFlags) ;
  536.      }
  537.  
  538.           // MidiOutCachePatches:  Pass through
  539.           // ----------------------------------
  540.  
  541. UINT FAR PASCAL xMidiOutCachePatches (HMIDIBUFIN hMidiBufIn, UINT wBank,
  542.                                       LPPATCHARRAY lppa, UINT wFlags)
  543.      {
  544.      PMIDIBUFIN pmb ;
  545.  
  546.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  547.           return MMSYSERR_INVALHANDLE ;
  548.  
  549.      return midiOutCachePatches (pmb->hMidiIn, wBank, lppa, wFlags) ;
  550.      }
  551.  
  552.           // MidiOutClose:  Free handle and call timeKillEvent
  553.           // -------------------------------------------------
  554.  
  555. UINT FAR PASCAL xMidiOutClose (HMIDIBUFOUT hMidiBufOut)
  556.      {
  557.      PMIDIBUFOUT pmb ;
  558.      UINT        wError ;
  559.  
  560.      if (NULL == (pmb = MidiOutValidateHandle (hMidiBufOut)))
  561.           return MMSYSERR_INVALHANDLE ;
  562.  
  563.      if (0L == (wError = midiOutClose (pmb->hMidiOut)))
  564.           {
  565.           if (pmb->lpmh != NULL)
  566.                return MIDIERR_STILLPLAYING ;
  567.  
  568.           if (pmb->iTimerID != 0)
  569.                timeKillEvent (pmb->iTimerID) ;
  570.  
  571.           timeEndPeriod (tc.wPeriodMin) ;
  572.           LocalFree (hMidiBufOut) ;
  573.           }
  574.  
  575.      return wError ;
  576.      }
  577.  
  578.           // MidiOutGetID:  Pass through
  579.           // ---------------------------
  580.  
  581. UINT FAR PASCAL xMidiOutGetID (HMIDIBUFOUT hMidiBufOut, UINT FAR * lpiDeviceID)
  582.      {
  583.      PMIDIBUFOUT pmb ;
  584.  
  585.      if (NULL == (pmb = MidiOutValidateHandle (hMidiBufOut)))
  586.           return MMSYSERR_INVALHANDLE ;
  587.  
  588.      return midiOutGetID (pmb->hMidiOut, lpiDeviceID) ;
  589.      }
  590.  
  591.           // MidiOutLongMsg:  Pass through
  592.           // -----------------------------
  593.  
  594. UINT FAR PASCAL xMidiOutLongMsg (HMIDIBUFOUT hMidiBufOut,
  595.                                  LPMIDIHDR lpmh, UINT wSize)
  596.      {
  597.      PMIDIBUFOUT pmb ;
  598.  
  599.      if (NULL == (pmb = MidiOutValidateHandle (hMidiBufOut)))
  600.           return MMSYSERR_INVALHANDLE ;
  601.  
  602.      return midiOutLongMsg (pmb->hMidiOut, lpmh, wSize) ;
  603.      }
  604.  
  605.           // MidiOutMessage:  Pass through
  606.           // -----------------------------
  607.  
  608. DWORD FAR PASCAL xMidiOutMessage (HMIDIBUFIN hMidiBufIn, UINT msg,
  609.                                   DWORD dw1, DWORD dw2)
  610.      {
  611.      PMIDIBUFIN pmb ;
  612.  
  613.      if (NULL == (pmb = MidiInValidateHandle (hMidiBufIn)))
  614.           return MMSYSERR_INVALHANDLE ;
  615.  
  616.      return midiOutMessage (pmb->hMidiIn, msg, dw1, dw2) ;
  617.      }
  618.  
  619.           // MidiOutOpen:  Allocate handle and call timeBeginPeriod
  620.           // ------------------------------------------------------
  621.  
  622. UINT FAR PASCAL xMidiOutOpen (LPHMIDIBUFOUT lphMidiBufOut,
  623.                               UINT  wID,    DWORD dwCallBack,
  624.                               DWORD dwUser, DWORD dwFlags)
  625.      {
  626.      HMIDIBUFOUT hMidiBufOut ;
  627.      PMIDIBUFOUT pmb ;
  628.      UINT        wError ;
  629.  
  630.      if (NULL == (hMidiBufOut = LocalAlloc (LPTR, sizeof (MIDIBUFOUT))))
  631.           MMSYSERR_NOMEM ;
  632.  
  633.      pmb = (PMIDIBUFOUT) hMidiBufOut ;
  634.  
  635.      pmb->hMidiBufOut = hMidiBufOut ;
  636.      pmb->dwCallBack  = dwCallBack ;
  637.      pmb->dwUser      = dwUser ;
  638.      pmb->dwFlags     = dwFlags ;
  639.  
  640.      wError = midiOutOpen (&pmb->hMidiOut, wID, dwCallBack, dwUser, dwFlags) ;
  641.  
  642.      if (wError == 0)
  643.           {
  644.           * lphMidiBufOut = hMidiBufOut ;
  645.           timeBeginPeriod (tc.wPeriodMin) ;
  646.           }
  647.      else
  648.           LocalFree (hMidiBufOut) ;
  649.  
  650.      return wError ;
  651.      }
  652.  
  653.           // MidiOutPause:  Set pause flag
  654.           // -----------------------------
  655.  
  656. UINT FAR PASCAL xMidiOutPause (HMIDIBUFOUT hMidiBufOut)
  657.      {
  658.      PMIDIBUFOUT pmb ;
  659.  
  660.      if (NULL == (pmb = MidiOutValidateHandle (hMidiBufOut)))
  661.           return MMSYSERR_INVALHANDLE ;
  662.  
  663.      pmb->bPaused = TRUE ;
  664.  
  665.      return 0 ;
  666.      }
  667.  
  668.           // MidiOutPrepareHeader:  Pass through
  669.           // -----------------------------------
  670.  
  671. UINT FAR PASCAL xMidiOutPrepareHeader (HMIDIBUFOUT hMidiBufOut,
  672.                                        LPMIDIHDR lpmh, UINT wSize)
  673.      {
  674.      PMIDIBUFOUT pmb ;
  675.  
  676.      if (NULL == (pmb = MidiOutValidateHandle (hMidiBufOut)))
  677.           return MMSYSERR_INVALHANDLE ;
  678.  
  679.      return midiOutPrepareHeader (pmb->hMidiOut, lpmh, wSize) ;
  680.      }
  681.  
  682.           // MidiOutReset:  Return pending buffers to application
  683.           // ----------------------------------------------------
  684.  
  685. UINT FAR PASCAL xMidiOutReset (HMIDIBUFOUT hMidiBufOut)
  686.      {
  687.      PMIDIBUFOUT pmb ;
  688.      LPMIDIHDR   lpmh ;
  689.      UINT        wError ;
  690.  
  691.      if (NULL == (pmb = MidiOutValidateHandle (hMidiBufOut)))
  692.           return MMSYSERR_INVALHANDLE ;
  693.  
  694.      if (0 == (wError = midiOutReset (pmb->hMidiOut)))
  695.           {
  696.           if (pmb->iTimerID != 0)
  697.                timeKillEvent (pmb->iTimerID) ;
  698.  
  699.           while (pmb->lpmh != NULL)
  700.                {
  701.                lpmh           = pmb->lpmh ;
  702.                pmb->lpmh      = lpmh->lpNext ;
  703.                lpmh->dwFlags |= MHDR_DONE ;
  704.  
  705.                if ((pmb->dwFlags & CALLBACK_TYPEMASK) == CALLBACK_WINDOW)
  706.                     PostMessage ((HWND) pmb->dwCallBack, MM_MOM_DONE,
  707.                                  hMidiBufOut, (DWORD) lpmh) ;
  708.  
  709.                if ((pmb->dwFlags & CALLBACK_TYPEMASK) == CALLBACK_FUNCTION)
  710.                     ((MMCALLBACK) pmb->dwCallBack) (hMidiBufOut, MOM_DONE,
  711.                                              pmb->dwUser, (DWORD) lpmh, 0) ;
  712.                }
  713.           }
  714.  
  715.      return wError ;
  716.      }
  717.  
  718.           // MidiOutRestart:  Reset paused flag and set timer if not set
  719.           // -----------------------------------------------------------
  720.  
  721. UINT FAR PASCAL xMidiOutRestart (HMIDIBUFOUT hMidiBufOut)
  722.      {
  723.      PMIDIBUFOUT pmb ;
  724.  
  725.      if (NULL == (pmb = MidiOutValidateHandle (hMidiBufOut)))
  726.           return MMSYSERR_INVALHANDLE ;
  727.  
  728.      pmb->bPaused = FALSE ;
  729.  
  730.      if (pmb->iTimerID == 0)
  731.           {
  732.           pmb->dwTime = tc.wPeriodMin ;
  733.           pmb->iTimerID = MidiOutSetEvent (pmb) ;
  734.           }
  735.  
  736.      return 0 ;
  737.      }
  738.  
  739.           // MidiOutShortBuffer:  Add buffer to chain and set timer if not set
  740.           // -----------------------------------------------------------------
  741.  
  742. UINT FAR PASCAL xMidiOutShortBuffer (HMIDIBUFOUT hMidiBufOut,
  743.                                      LPMIDIHDR lpmh, UINT wSize)
  744.      {
  745.      DWORD huge * lpData ;
  746.      LPMIDIHDR    lpmhSearch ;
  747.      PMIDIBUFOUT  pmb ;
  748.  
  749.                // Validate handle and check if header is prepared
  750.  
  751.      if (NULL == (pmb = MidiOutValidateHandle (hMidiBufOut)))
  752.           return MMSYSERR_INVALHANDLE ;
  753.  
  754.      if (!(lpmh->dwFlags & MHDR_PREPARED))
  755.           return MIDIERR_UNPREPARED ;
  756.  
  757.                // Add buffer to chain
  758.  
  759.      lpmh->lpNext = NULL ;
  760.  
  761.      if (pmb->lpmh == NULL)
  762.           pmb->lpmh = lpmh ;
  763.      else
  764.           {
  765.           lpmhSearch = pmb->lpmh ;
  766.  
  767.           while (lpmhSearch->lpNext != NULL)
  768.                lpmhSearch = lpmhSearch->lpNext ;
  769.  
  770.           lpmhSearch->lpNext = lpmh ;
  771.           }
  772.  
  773.                // Begin sequence by setting a timer event
  774.  
  775.      if (pmb->iTimerID == 0)
  776.           {
  777.           lpData = (DWORD huge *) lpmh->lpData ;
  778.           pmb->dwTime = lpData [0] ;
  779.           pmb->dwDataPtr += 4 ;
  780.           pmb->iTimerID = MidiOutSetEvent (pmb) ;
  781.           }
  782.  
  783.      return 0 ;
  784.      }
  785.  
  786.           // MidiOutShortMsg:  Pass through
  787.           // ------------------------------
  788.  
  789. UINT FAR PASCAL xMidiOutShortMsg (HMIDIBUFOUT hMidiBufOut, DWORD dwMsg)
  790.      {
  791.      PMIDIBUFOUT pmb ;
  792.  
  793.      if (NULL == (pmb = MidiOutValidateHandle (hMidiBufOut)))
  794.           return MMSYSERR_INVALHANDLE ;
  795.  
  796.      return midiOutShortMsg (pmb->hMidiOut, dwMsg) ;
  797.      }
  798.  
  799.           // MidiOutUnprepareHeader:  Pass through
  800.           // -------------------------------------
  801.  
  802. UINT FAR PASCAL xMidiOutUnprepareHeader (HMIDIBUFOUT hMidiBufOut,
  803.                                          LPMIDIHDR lpmh, UINT wSize)
  804.      {
  805.      PMIDIBUFOUT pmb ;
  806.  
  807.      if (NULL == (pmb = MidiOutValidateHandle (hMidiBufOut)))
  808.           return MMSYSERR_INVALHANDLE ;
  809.  
  810.      return midiOutUnprepareHeader (pmb->hMidiOut, lpmh, wSize) ;
  811.      }
  812.