home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast.iso / pcmag / vol11n15.zip / MIDREC.C < prev    next >
C/C++ Source or Header  |  1992-05-04  |  21KB  |  601 lines

  1. /*---------------------------------------
  2.    MIDREC.C -- MIDI Recorder and Player
  3.                (c) Charles Petzold, 1992
  4.   ---------------------------------------*/
  5.  
  6. #include <windows.h>
  7. #include <windowsx.h>
  8. #include <mmsystem.h>
  9. #include <string.h>
  10. #include "midbuf.h"
  11. #include "midrec.h"
  12.  
  13. #define BUFFER_SIZE 4096      // Should be multiple of 8
  14.  
  15. BOOL FAR PASCAL _export DlgProc (HWND, UINT, UINT, LONG) ;
  16.  
  17. static char szAppName [] = "MidRec" ;
  18.  
  19. int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
  20.                     LPSTR lpszCmdLine, int nCmdShow)
  21.      {
  22.      FARPROC lpDlgProc ;
  23.  
  24.      lpDlgProc = MakeProcInstance ((FARPROC) DlgProc, hInstance) ;
  25.      DialogBox (hInstance, szAppName, NULL, lpDlgProc) ;
  26.      FreeProcInstance (lpDlgProc) ;
  27.  
  28.      return 0 ;
  29.      }
  30.  
  31.           // Functions to allocate and free MIDIHDR structures and buffers
  32.           // -------------------------------------------------------------
  33.  
  34. LPMIDIHDR AllocMidiHeader (HANDLE hMidi, LPMIDIHDR pmhRoot)
  35.      {
  36.      LPMIDIHDR pmhNew, pmhNext ;
  37.  
  38.                // Allocate memory for the new MIDIHDR
  39.  
  40.      pmhNew = (LPMIDIHDR) GlobalAllocPtr (GHND | GMEM_SHARE, sizeof (MIDIHDR));
  41.  
  42.      if (pmhNew == NULL)
  43.           return NULL ;
  44.  
  45.                // Allocate memory for the buffer
  46.  
  47.      pmhNew->lpData = (LPSTR) GlobalAllocPtr (GHND | GMEM_SHARE, BUFFER_SIZE) ;
  48.  
  49.      if (pmhNew->lpData == NULL)
  50.           {
  51.           GlobalFreePtr (pmhNew) ;
  52.           return NULL ;
  53.           }
  54.  
  55.      pmhNew->dwBufferLength = BUFFER_SIZE ;
  56.  
  57.                // Prepare the header
  58.  
  59.      if (midiInPrepareHeader (hMidi, pmhNew, sizeof (MIDIHDR)))
  60.           {
  61.           GlobalFreePtr (pmhNew->lpData) ;
  62.           GlobalFreePtr (pmhNew) ;
  63.           return NULL ;
  64.           }
  65.  
  66.                // Attach new header to end of chain
  67.  
  68.      if (pmhRoot != NULL)
  69.           {
  70.           pmhNext = pmhRoot ;
  71.  
  72.           while (pmhNext->dwUser != NULL)
  73.                pmhNext = (LPMIDIHDR) pmhNext->dwUser ;
  74.  
  75.           pmhNext->dwUser = (DWORD) pmhNew ;
  76.           }
  77.  
  78.      return pmhNew ;
  79.      }
  80.  
  81. LPMIDIHDR CleanUpMidiHeaderChain (HANDLE hMidi, LPMIDIHDR pmhRoot)
  82.      {
  83.      LPMIDIHDR pmhCurr, pmhLast, pmhNext, pmhRetn ;
  84.  
  85.      pmhRetn = pmhRoot ;
  86.      pmhCurr = pmhRoot ;
  87.      pmhLast = NULL ;
  88.  
  89.      while (pmhCurr != NULL)
  90.           {
  91.           pmhNext = (LPMIDIHDR) pmhCurr->dwUser ;
  92.  
  93.           if (pmhCurr->dwBytesRecorded == 0)
  94.                {
  95.                midiInUnprepareHeader (hMidi, pmhCurr, sizeof (MIDIHDR)) ;
  96.  
  97.                GlobalFreePtr (pmhCurr->lpData) ;
  98.                GlobalFreePtr (pmhCurr) ;
  99.  
  100.                if (pmhCurr == pmhRoot)
  101.                     pmhRetn = NULL ;
  102.  
  103.                if (pmhLast != NULL)
  104.                     pmhLast->dwUser = (DWORD) pmhNext ;
  105.  
  106.                pmhCurr = pmhLast ;
  107.                }
  108.  
  109.           else if (pmhCurr->dwBytesRecorded < BUFFER_SIZE)
  110.                {
  111.                midiInUnprepareHeader (hMidi, pmhCurr, sizeof (MIDIHDR)) ;
  112.  
  113.                GlobalReAllocPtr (pmhCurr->lpData,
  114.                                  pmhCurr->dwBytesRecorded, 0) ;
  115.  
  116.                midiInPrepareHeader (hMidi, pmhCurr, sizeof (MIDIHDR)) ;
  117.  
  118.                pmhCurr->dwBufferLength = pmhCurr->dwBytesRecorded ;
  119.                }
  120.  
  121.           pmhLast = pmhCurr ;
  122.           pmhCurr = pmhNext ;
  123.           }
  124.  
  125.      return pmhRetn ;
  126.      }
  127.  
  128. VOID FreeMidiHeaderChain (HANDLE hMidi, LPMIDIHDR pmhRoot)
  129.      {
  130.      LPMIDIHDR pmhNext, pmhTemp ;
  131.  
  132.      pmhNext = pmhRoot ;
  133.  
  134.      while (pmhNext != NULL)
  135.           {
  136.           pmhTemp = (LPMIDIHDR) pmhNext->dwUser ;
  137.  
  138.           midiInUnprepareHeader (hMidi, pmhNext, sizeof (MIDIHDR)) ;
  139.  
  140.           GlobalFreePtr (pmhNext->lpData) ;
  141.           GlobalFreePtr (pmhNext) ;
  142.  
  143.           pmhNext = pmhTemp ;
  144.           }
  145.      }
  146.  
  147.           // Add MIDI device lists to the program's menu
  148.           // -------------------------------------------
  149.  
  150. WORD AddDevicesToMenu (HWND hwnd, int iNumInpDevs, int iNumOutDevs)
  151.      {
  152.      HMENU       hMenu, hMenuInp, hMenuMon, hMenuOut ;
  153.      int         i ;
  154.      MIDIINCAPS  mic ;
  155.      MIDIOUTCAPS moc ;
  156.      WORD        wDefaultOut ;
  157.  
  158.      hMenu = GetMenu (hwnd) ;
  159.  
  160.                // Create "Input" popup menu
  161.  
  162.      hMenuInp = CreateMenu () ;
  163.  
  164.      for (i = 0 ; i < iNumInpDevs ; i++)
  165.           {
  166.           midiInGetDevCaps (i, &mic, sizeof (MIDIINCAPS)) ;
  167.           AppendMenu (hMenuInp, MF_STRING, ID_DEV_INP + i, mic.szPname) ;
  168.           }
  169.  
  170.      CheckMenuItem (hMenuInp, 0, MF_BYPOSITION | MF_CHECKED) ;
  171.      ModifyMenu (hMenu, ID_DEV_INP, MF_POPUP, hMenuInp, "&Input") ;
  172.  
  173.                // Create "Monitor" and "Output" popup menus
  174.  
  175.      hMenuMon = CreateMenu () ;
  176.      hMenuOut = CreateMenu () ;
  177.  
  178.      AppendMenu (hMenuMon, MF_STRING, ID_DEV_MON, "&None") ;
  179.  
  180.      if (!midiOutGetDevCaps (MIDIMAPPER, &moc, sizeof (moc)))
  181.           {
  182.           AppendMenu (hMenuMon, MF_STRING, ID_DEV_MON + 1, moc.szPname) ;
  183.           AppendMenu (hMenuOut, MF_STRING, ID_DEV_OUT    , moc.szPname) ;
  184.  
  185.           wDefaultOut = 0 ;
  186.           }
  187.      else
  188.           wDefaultOut = 1 ;
  189.  
  190.                          // Add the rest of the MIDI devices
  191.  
  192.      for (i = 0 ; i < iNumOutDevs ; i++)
  193.           {
  194.           midiOutGetDevCaps (i, &moc, sizeof (moc)) ;
  195.           AppendMenu (hMenuMon, MF_STRING, ID_DEV_MON + i + 2, moc.szPname) ;
  196.           AppendMenu (hMenuOut, MF_STRING, ID_DEV_OUT + i + 1, moc.szPname) ;
  197.           }
  198.  
  199.      CheckMenuItem (hMenuMon, 0, MF_BYPOSITION | MF_CHECKED) ;
  200.      CheckMenuItem (hMenuOut, 0, MF_BYPOSITION | MF_CHECKED) ;
  201.  
  202.      ModifyMenu (hMenu, ID_DEV_MON, MF_POPUP, hMenuMon, "&Monitor") ;
  203.      ModifyMenu (hMenu, ID_DEV_OUT, MF_POPUP, hMenuOut, "&Output") ;
  204.  
  205.      return wDefaultOut ;
  206.      }
  207.  
  208. BOOL FAR PASCAL _export DlgProc (HWND hwnd, UINT message, UINT wParam,
  209.                                                           LONG lParam)
  210.      {
  211.      static BOOL      bRecording, bPlaying, bEnding, bPaused, bTerminating ;
  212.      static char      szInpError[] = { "Error opening MIDI input port!" } ;
  213.      static char      szOutError[] = { "Error opening MIDI output port!" } ;
  214.      static char      szMonError[] = { "Error opening MIDI output port "
  215.                                        "for monitoring input!  Continuing." } ;
  216.      static char      szMemError[] = { "Error allocating memory!" } ;
  217.      static HMIDIIN   hMidiIn ;
  218.      static HMIDIOUT  hMidiOut ;
  219.      static int       iNumInpDevs, iNumOutDevs ;
  220.      static LPMIDIHDR pMidiHdrRoot, pMidiHdrNext, pMidiHdr ;
  221.      static WORD      wDeviceInp, wDeviceMon, wDeviceOut ;
  222.      HMENU            hMenu ;
  223.      int              i ;
  224.  
  225.      switch (message)
  226.           {
  227.           case WM_INITDIALOG:
  228.  
  229.                if (0 == (iNumInpDevs = midiInGetNumDevs ()))
  230.                     {
  231.                     MessageBox (hwnd, "No MIDI Input Devices!", szAppName,
  232.                                 MB_ICONEXCLAMATION | MB_OK) ;
  233.                     DestroyWindow (hwnd) ;
  234.                     }
  235.  
  236.                if (0 == (iNumOutDevs = midiOutGetNumDevs ()))
  237.                     {
  238.                     MessageBox (hwnd, "No MIDI Output Devices!", szAppName,
  239.                                 MB_ICONEXCLAMATION | MB_OK) ;
  240.                     DestroyWindow (hwnd) ;
  241.                     }
  242.  
  243.                wDeviceOut = AddDevicesToMenu (hwnd, iNumInpDevs, iNumOutDevs) ;
  244.  
  245.                return TRUE ;
  246.  
  247.           case WM_COMMAND:
  248.                hMenu = GetMenu (hwnd) ;
  249.  
  250.                switch (wParam)
  251.                     {
  252.                     case ID_RECORD_BEG:
  253.  
  254.                                    // Open MIDI In port for recording
  255.  
  256.                          if (midiInOpen (&hMidiIn, wDeviceInp, hwnd, 0L,
  257.                                          CALLBACK_WINDOW))
  258.                               {
  259.                               MessageBox (hwnd, szInpError, szAppName,
  260.                                           MB_ICONEXCLAMATION | MB_OK) ;
  261.  
  262.                               return TRUE ;
  263.                               }
  264.  
  265.                                    // Open MIDI Out port for monitoring
  266.                                    //     (continue if unable to open it)
  267.  
  268.                          if (wDeviceMon > 0)
  269.                               {
  270.                               if (midiOutOpen (&hMidiOut, wDeviceMon - 2,
  271.                                                0L, 0L, 0L))
  272.                                    {
  273.                                    hMidiOut = NULL ;
  274.                                    MessageBox (hwnd, szMonError, szAppName,
  275.                                                MB_ICONEXCLAMATION | MB_OK) ;
  276.                                    }
  277.                               }
  278.                          else
  279.                               hMidiOut = NULL ;
  280.  
  281.                          return TRUE ;
  282.  
  283.                     case ID_RECORD_END:
  284.                                         // Reset and close input
  285.  
  286.                          bEnding = TRUE ;
  287.  
  288.                          midiInReset (hMidiIn) ;
  289.                          midiInClose (hMidiIn) ;
  290.  
  291.                          return TRUE ;
  292.  
  293.                     case ID_PLAY_BEG:
  294.                                         // Open MIDI Out port for playing
  295.  
  296.                          if (midiOutOpen (&hMidiOut, wDeviceOut - 1,
  297.                                           hwnd, 0L, CALLBACK_WINDOW))
  298.                               {
  299.                               MessageBox (hwnd, szOutError, szAppName,
  300.                                           MB_ICONEXCLAMATION | MB_OK) ;
  301.                               }
  302.  
  303.                          return TRUE ;
  304.  
  305.                     case ID_PLAY_PAUSE:
  306.                                         // Pause or restart output
  307.  
  308.                          if (!bPaused)
  309.                               {
  310.                               midiOutPause (hMidiOut) ;
  311.  
  312.                                    // All Notes Off message
  313.  
  314.                               for (i = 0 ; i < 16 ; i++)
  315.                                    midiOutShortMsg (hMidiOut, 0x7BB0 + i) ;
  316.  
  317.                               SetDlgItemText (hwnd, ID_PLAY_PAUSE, "Resume") ;
  318.                               bPaused = TRUE ;
  319.                               }
  320.                          else
  321.                               {
  322.                               midiOutRestart (hMidiOut) ;
  323.                               SetDlgItemText (hwnd, ID_PLAY_PAUSE, "Pause") ;
  324.                               bPaused = FALSE ;
  325.                               }
  326.  
  327.                          return TRUE ;
  328.  
  329.                     case ID_PLAY_END:
  330.                                         // Reset the port and close it
  331.  
  332.                          bEnding = TRUE ;
  333.                          midiOutReset (hMidiOut) ;
  334.                          midiOutClose (hMidiOut) ;
  335.                          return TRUE ;
  336.  
  337.                     default:
  338.                          break ;
  339.                     }
  340.  
  341.                if (wParam >= ID_DEV_INP & wParam < ID_DEV_MON)
  342.                     {
  343.                     CheckMenuItem (hMenu, wDeviceInp + ID_DEV_INP,
  344.                                           MF_UNCHECKED) ;
  345.  
  346.                     wDeviceInp = wParam - ID_DEV_INP ;
  347.  
  348.                     CheckMenuItem (hMenu, wDeviceInp + ID_DEV_INP,
  349.                                           MF_CHECKED) ;
  350.                     return 0 ;
  351.                     }
  352.  
  353.                else if (wParam >= ID_DEV_MON & wParam < ID_DEV_OUT)
  354.                     {
  355.                     CheckMenuItem (hMenu, wDeviceMon + ID_DEV_MON,
  356.                                           MF_UNCHECKED) ;
  357.  
  358.                     wDeviceMon = wParam - ID_DEV_MON ;
  359.  
  360.                     CheckMenuItem (hMenu, wDeviceMon + ID_DEV_MON,
  361.                                           MF_CHECKED) ;
  362.                     return 0 ;
  363.                     }
  364.  
  365.                if (wParam >= ID_DEV_OUT)
  366.                     {
  367.                     CheckMenuItem (hMenu, wDeviceOut + ID_DEV_OUT,
  368.                                           MF_UNCHECKED) ;
  369.  
  370.                     wDeviceOut = wParam - ID_DEV_OUT ;
  371.  
  372.                     CheckMenuItem (hMenu, wDeviceOut + ID_DEV_OUT,
  373.                                           MF_CHECKED) ;
  374.                     return 0 ;
  375.                     }
  376.  
  377.                break ;
  378.  
  379.           case MM_MIM_OPEN:
  380.                hMidiIn = wParam ;
  381.  
  382.                          // Free existing headers
  383.  
  384.                FreeMidiHeaderChain (hMidiIn, pMidiHdrRoot) ;
  385.  
  386.                          // Allocate root header
  387.  
  388.                if (NULL == (pMidiHdrRoot = AllocMidiHeader (hMidiIn, NULL)))
  389.                     {
  390.                     midiInClose (hMidiIn) ;
  391.                     MessageBox (hwnd, szMemError, szAppName,
  392.                                 MB_ICONEXCLAMATION | MB_OK) ;
  393.  
  394.                     return TRUE ;
  395.                     }
  396.  
  397.                          // Allocate next header
  398.  
  399.                 if (NULL == (pMidiHdrNext = AllocMidiHeader (hMidiIn,
  400.                                                              pMidiHdrRoot)))
  401.                     {
  402.                     FreeMidiHeaderChain (hMidiIn, pMidiHdrRoot) ;
  403.                     midiInClose (hMidiIn) ;
  404.                     MessageBox (hwnd, szMemError, szAppName,
  405.                                 MB_ICONEXCLAMATION | MB_OK) ;
  406.  
  407.                     return TRUE ;
  408.                     }
  409.                          // Enable and disable buttons
  410.  
  411.                EnableWindow (GetDlgItem (hwnd, ID_RECORD_BEG), FALSE) ;
  412.                EnableWindow (GetDlgItem (hwnd, ID_RECORD_END), TRUE)  ;
  413.                EnableWindow (GetDlgItem (hwnd, ID_PLAY_BEG),   FALSE) ;
  414.                EnableWindow (GetDlgItem (hwnd, ID_PLAY_PAUSE), FALSE) ;
  415.                EnableWindow (GetDlgItem (hwnd, ID_PLAY_END),   FALSE) ;
  416.                SetFocus (GetDlgItem (hwnd, ID_RECORD_END)) ;
  417.  
  418.                          // Submit the buffers for receiving data
  419.  
  420.                midiInShortBuffer (hMidiIn, pMidiHdrRoot, sizeof (MIDIHDR)) ;
  421.                midiInShortBuffer (hMidiIn, pMidiHdrNext, sizeof (MIDIHDR)) ;
  422.  
  423.                               // Begin recording
  424.  
  425.                midiInStart (hMidiIn) ;
  426.                bRecording = TRUE ;
  427.                bEnding = FALSE ;
  428.                return TRUE ;
  429.  
  430.           case MM_MIM_DATA:
  431.                if (hMidiOut)
  432.                     {
  433.                     midiOutShortMsg (hMidiOut, lParam) ;
  434.                     }
  435.  
  436.                return TRUE ;
  437.  
  438.           case MM_MIM_LONGDATA:
  439.  
  440.                if (bEnding)
  441.                     return TRUE ;
  442.  
  443.                pMidiHdrNext = AllocMidiHeader (hMidiIn, pMidiHdrRoot) ;
  444.  
  445.                if (pMidiHdrNext == NULL)
  446.                     {
  447.                     midiInReset (hMidiIn) ;
  448.                     midiInClose (hMidiIn) ;
  449.                     MessageBox (hwnd, szMemError, szAppName,
  450.                                 MB_ICONEXCLAMATION | MB_OK) ;
  451.  
  452.                     return TRUE ;
  453.                     }
  454.  
  455.                midiInShortBuffer (hMidiIn, pMidiHdrNext, sizeof (MIDIHDR));
  456.  
  457.                return TRUE ;
  458.  
  459.           case MM_MIM_CLOSE:
  460.                               // Close the monitoring output port
  461.  
  462.                if (hMidiOut)
  463.                     {
  464.                     midiOutReset (hMidiOut) ;
  465.                     midiOutClose (hMidiOut) ;
  466.                     }
  467.  
  468.                               // Enable and Disable Buttons
  469.  
  470.                EnableWindow (GetDlgItem (hwnd, ID_RECORD_BEG), TRUE) ;
  471.                EnableWindow (GetDlgItem (hwnd, ID_RECORD_END), FALSE) ;
  472.                SetFocus (GetDlgItem (hwnd, ID_RECORD_BEG)) ;
  473.  
  474.                pMidiHdrRoot = CleanUpMidiHeaderChain (hMidiIn, pMidiHdrRoot) ;
  475.  
  476.                if (pMidiHdrRoot != NULL)
  477.                     {
  478.                     EnableWindow (GetDlgItem (hwnd, ID_PLAY_BEG),   TRUE)  ;
  479.                     EnableWindow (GetDlgItem (hwnd, ID_PLAY_PAUSE), FALSE) ;
  480.                     EnableWindow (GetDlgItem (hwnd, ID_PLAY_END),   FALSE) ;
  481.                     SetFocus (GetDlgItem (hwnd, ID_PLAY_BEG)) ;
  482.                     }
  483.  
  484.                bRecording = FALSE ;
  485.  
  486.                if (bTerminating)
  487.                     {
  488.                     FreeMidiHeaderChain (hMidiIn, pMidiHdrRoot) ;
  489.                     SendMessage (hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L) ;
  490.                     }
  491.  
  492.                return TRUE ;
  493.  
  494.           case MM_MOM_OPEN:
  495.                hMidiOut = wParam ;
  496.  
  497.                          // Enable and Disable Buttons
  498.  
  499.                EnableWindow (GetDlgItem (hwnd, ID_RECORD_BEG), FALSE) ;
  500.                EnableWindow (GetDlgItem (hwnd, ID_RECORD_END), FALSE) ;
  501.                EnableWindow (GetDlgItem (hwnd, ID_PLAY_BEG),   FALSE) ;
  502.                EnableWindow (GetDlgItem (hwnd, ID_PLAY_PAUSE), TRUE)  ;
  503.                EnableWindow (GetDlgItem (hwnd, ID_PLAY_END),   TRUE)  ;
  504.                SetFocus (GetDlgItem (hwnd, ID_PLAY_END)) ;
  505.  
  506.                          // Submit the root buffer to begin playing
  507.  
  508.                midiOutShortBuffer (hMidiOut, pMidiHdrRoot, sizeof (MIDIHDR)) ;
  509.  
  510.                          // If there's a second buffer, submit that also
  511.  
  512.                if (NULL != (pMidiHdr = (LPMIDIHDR) pMidiHdrRoot->dwUser))
  513.                     midiOutShortBuffer (hMidiOut, pMidiHdr, sizeof (MIDIHDR)) ;
  514.  
  515.                bEnding = FALSE ;
  516.                bPlaying = TRUE ;
  517.                return TRUE ;
  518.  
  519.           case MM_MOM_DONE:
  520.  
  521.                          // If stopping playback, just return
  522.  
  523.                if (bEnding)
  524.                     return TRUE ;
  525.  
  526.                          // Get header of buffer just finished playing
  527.  
  528.                pMidiHdr = (LPMIDIHDR) lParam ;
  529.  
  530.                          // Get header of next buffer (already submitted)
  531.  
  532.                pMidiHdr = (LPMIDIHDR) pMidiHdr->dwUser ;
  533.  
  534.                          // Get header of next buffer to submit now
  535.  
  536.                if (pMidiHdr != NULL)
  537.                     pMidiHdr = (LPMIDIHDR) pMidiHdr->dwUser ;
  538.  
  539.                if (pMidiHdr != NULL)
  540.                     midiOutShortBuffer (hMidiOut, pMidiHdr, sizeof (MIDIHDR)) ;
  541.                else
  542.                     {
  543.                     midiOutReset (hMidiOut) ;
  544.                     midiOutClose (hMidiOut) ;
  545.                     }
  546.  
  547.                return TRUE ;
  548.  
  549.           case MM_MOM_CLOSE:
  550.  
  551.                          // Enable and Disable Buttons
  552.  
  553.                EnableWindow (GetDlgItem (hwnd, ID_RECORD_BEG), TRUE)  ;
  554.                EnableWindow (GetDlgItem (hwnd, ID_RECORD_END), TRUE)  ;
  555.                EnableWindow (GetDlgItem (hwnd, ID_PLAY_BEG),   TRUE)  ;
  556.                EnableWindow (GetDlgItem (hwnd, ID_PLAY_PAUSE), FALSE) ;
  557.                EnableWindow (GetDlgItem (hwnd, ID_PLAY_END),   FALSE) ;
  558.                SetFocus (GetDlgItem (hwnd, ID_PLAY_BEG)) ;
  559.  
  560.                SetDlgItemText (hwnd, ID_PLAY_PAUSE, "Pause") ;
  561.                bPaused = FALSE ;
  562.                bPlaying = FALSE ;
  563.  
  564.                if (bTerminating)
  565.                     {
  566.                     FreeMidiHeaderChain (hMidiIn, pMidiHdrRoot) ;
  567.                     SendMessage (hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L) ;
  568.                     }
  569.  
  570.                return TRUE ;
  571.  
  572.           case WM_SYSCOMMAND:
  573.                switch (wParam)
  574.                     {
  575.                     case SC_CLOSE:
  576.                          if (bRecording)
  577.                               {
  578.                               bTerminating = TRUE ;
  579.                               bEnding = TRUE ;
  580.                               midiInReset (hMidiIn) ;
  581.                               midiInClose (hMidiIn) ;
  582.                               return TRUE ;
  583.                               }
  584.  
  585.                          if (bPlaying)
  586.                               {
  587.                               bTerminating = TRUE ;
  588.                               bEnding = TRUE ;
  589.                               midiOutReset (hMidiOut) ;
  590.                               midiOutClose (hMidiOut) ;
  591.                               return TRUE ;
  592.                               }
  593.  
  594.                          EndDialog (hwnd, 0) ;
  595.                          return TRUE ;
  596.                     }
  597.                break ;
  598.           }
  599.      return FALSE ;
  600.      }
  601.