home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c480 / 18.ddi / SAMPLES / MIDIMON / MIDIMON.C_ / MIDIMON.C
Encoding:
C/C++ Source or Header  |  1993-02-08  |  29.5 KB  |  826 lines

  1. /* midimon.c - WinMain() and WndProc() functions for MIDIMon, along
  2.  *      with some initialization and error reporting functions.
  3.  *
  4.  * MIDIMon is a Windows with Multimedia application that records and displays 
  5.  *  incoming MIDI information.  It uses a low-level callback function to 
  6.  *  get timestamped MIDI input.  The callback function puts the incoming
  7.  *  MIDI event information (source device, timestamp, and raw MIDI
  8.  *  data) in a circular input buffer and notifies the application by posting 
  9.  *  a MM_MIDIINPUT message.  When the application processes the MM_MIDIINPUT
  10.  *  message, it removes the MIDI event from the input buffer and puts it in
  11.  *  a display buffer.  Information in the display buffer is converted to
  12.  *  text and displayed in a scrollable window.  Incoming MIDI data can be sent
  13.  *  to the MIDI Mapper if the user chooses.  Filtering is provided for the
  14.  *  display buffer, but not for data sent to the Mapper.
  15.  *
  16.  *    (C) Copyright Microsoft Corp. 1991.  All rights reserved.
  17.  *
  18.  *    You have a royalty-free right to use, modify, reproduce and 
  19.  *    distribute the Sample Files (and/or any modified version) in 
  20.  *    any way you find useful, provided that you agree that 
  21.  *    Microsoft has no warranty obligations or liability for any 
  22.  *    Sample Application Files which are modified. 
  23.  *
  24.  */
  25.  
  26. #include <windows.h>
  27. #include <mmsystem.h>
  28. #include <stdio.h>
  29. #include "midimon.h"
  30. #include "about.h"
  31. #include "circbuf.h"
  32. #include "display.h"
  33. #include "prefer.h"
  34. #include "instdata.h"
  35. #include "callback.h"
  36. #include "filter.h"
  37.  
  38. HANDLE hInst;                           // Instance handle for application
  39. char szAppName[20];                     // Application name
  40. HWND hMainWnd;                          // Main window handle
  41. HMIDIOUT hMapper = 0;                   // Handle to MIDI Mapper
  42. WORD wNumDevices = 0;                   // Number of MIDI input devices opened
  43. BOOL bRecordingEnabled = 1;             // Enable/disable recording flag
  44. short nNumBufferLines = 0;              // Number of lines in display buffer
  45. RECT rectScrollClip;                    // Clipping rectangle for scrolling
  46.  
  47. LPCIRCULARBUFFER lpInputBuffer;         // Input buffer structure
  48. LPDISPLAYBUFFER lpDisplayBuffer;        // Display buffer structure
  49. PREFERENCES preferences;                // User preferences structure
  50. EVENT incomingEvent;                    // Incoming MIDI event structure
  51.  
  52. MIDIINCAPS midiInCaps[MAX_NUM_DEVICES]; // Device capabilities structures
  53. HMIDIIN hMidiIn[MAX_NUM_DEVICES];       // MIDI input device handles
  54.  
  55. // Callback instance data pointers
  56. LPCALLBACKINSTANCEDATA lpCallbackInstanceData[MAX_NUM_DEVICES];
  57.  
  58. // Display filter structure
  59. FILTER filter = {
  60.                     0, 0, 0, 0, 0, 0, 0, 0, 
  61.                     0, 0, 0, 0, 0, 0, 0, 0, 
  62.                     0, 0, 0, 0, 0, 0, 0, 0, 
  63.                     0, 0, 0, 0, 0, 0, 0, 0
  64.                 };
  65.  
  66. // Virtual key to scroll message translation structure
  67. KEYTOSCROLL keyToScroll [] = { 
  68.                                 VK_HOME,  WM_VSCROLL, SB_TOP,
  69.                                 VK_END,   WM_VSCROLL, SB_BOTTOM,
  70.                                 VK_PRIOR, WM_VSCROLL, SB_PAGEUP,
  71.                                 VK_NEXT,  WM_VSCROLL, SB_PAGEDOWN,
  72.                                 VK_UP,    WM_VSCROLL, SB_LINEUP,
  73.                                 VK_DOWN,  WM_VSCROLL, SB_LINEDOWN,
  74.                                 VK_LEFT,  WM_HSCROLL, SB_LINEUP,
  75.                                 VK_RIGHT, WM_HSCROLL, SB_LINEDOWN 
  76.                              };
  77.  
  78. #define NUMKEYS (sizeof (keyToScroll) / sizeof (keyToScroll[0]))
  79.  
  80.  
  81.                                                                         //
  82. /* WinMain - Entry point for MIDIMon.
  83.  */
  84. int PASCAL WinMain(hInstance,hPrevInstance,lpszCmdLine,cmdShow)
  85. HINSTANCE hInstance,hPrevInstance;
  86. LPSTR lpszCmdLine;
  87. int cmdShow;
  88. {
  89.     MSG msg;
  90.     WORD wRtn;
  91.     PREFERENCES preferences;
  92.     char szErrorText[80];
  93.     unsigned int i;
  94.  
  95.     hInst = hInstance;
  96.  
  97.     /* Get preferred user setup.
  98.      */
  99.     getPreferences(&preferences);
  100.     
  101.     /* Initialize application.
  102.      */
  103.     LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName)); 
  104.     if (hPrevInstance || !InitFirstInstance(hInstance))
  105.         return 0;
  106.  
  107.     /* Create a display window.
  108.      */
  109.     hMainWnd = CreateWindow(szAppName,
  110.                         szAppName,
  111.                         WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
  112.                         preferences.iInitialX,
  113.                         preferences.iInitialY,
  114.                         preferences.iInitialW,
  115.                         preferences.iInitialH,
  116.                         (HWND)NULL,
  117.                         (HMENU)NULL,
  118.                         hInstance,
  119.                         (LPSTR)NULL);
  120.  
  121.     if (!hMainWnd)
  122.         return 1;
  123.  
  124.     /* Hide scroll bars for now.
  125.      */
  126.     SetScrollRange(hMainWnd, SB_VERT, 0, 0, FALSE);
  127.     SetScrollRange(hMainWnd, SB_HORZ, 0, 0, FALSE);
  128.     
  129.     /* Show the display window.
  130.      */
  131.     ShowWindow(hMainWnd, cmdShow);
  132.     UpdateWindow(hMainWnd);
  133.     
  134.     /* Get the number of MIDI input devices.  Then get the capabilities of
  135.      * each device.  We don't use the capabilities information right now,
  136.      * but we could use it to report the name of the device that received
  137.      * each MIDI event.
  138.      */
  139.     wNumDevices = midiInGetNumDevs();
  140.     if (!wNumDevices) {
  141.         Error("There are no MIDI input devices.");
  142.         PostQuitMessage(0);
  143.     }
  144.     for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) {
  145.         wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &midiInCaps[i],
  146.                                 sizeof(MIDIINCAPS));
  147.         if(wRtn) {
  148.             midiInGetErrorText(wRtn, (LPSTR)szErrorText, 
  149.                                sizeof(szErrorText));
  150.             Error(szErrorText);
  151.         }
  152.     }
  153.  
  154.     /* Allocate a circular buffer for low-level MIDI input.  This buffer
  155.      * is filled by the low-level callback function and emptied by the
  156.      * application when it receives MM_MIDIINPUT messages.
  157.      */
  158.     lpInputBuffer = AllocCircularBuffer(
  159.                         (DWORD)(INPUT_BUFFER_SIZE * sizeof(EVENT)));
  160.     if (lpInputBuffer == NULL) {
  161.         Error("Not enough memory available for input buffer.");
  162.         return 1;
  163.     }
  164.  
  165.     /* Allocate a display buffer.  Incoming events from the circular input
  166.      * buffer are put into this buffer for display.
  167.      */
  168.     lpDisplayBuffer = AllocDisplayBuffer((DWORD)(DISPLAY_BUFFER_SIZE));
  169.     if (lpDisplayBuffer == NULL) {
  170.         Error("Not enough memory available for display buffer.");
  171.         FreeCircularBuffer(lpInputBuffer);
  172.         return 1;
  173.     }
  174.  
  175.     /* Open all MIDI input devices after allocating and setting up
  176.      * instance data for each device.  The instance data is used to
  177.      * pass buffer management information between the application and
  178.      * the low-level callback function.  It also includes a device ID,
  179.      * a handle to the MIDI Mapper, and a handle to the application's
  180.      * display window, so the callback can notify the window when input
  181.      * data is available.  A single callback function is used to service
  182.      * all opened input devices.
  183.      */
  184.     for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++)
  185.     {
  186.         if ((lpCallbackInstanceData[i] = AllocCallbackInstanceData()) == NULL)
  187.         {
  188.             Error("Not enough memory available.");
  189.             FreeCircularBuffer(lpInputBuffer);
  190.             FreeDisplayBuffer(lpDisplayBuffer);
  191.             return 1;
  192.         }
  193.         lpCallbackInstanceData[i]->hWnd = hMainWnd;         
  194.         lpCallbackInstanceData[i]->dwDevice = i;
  195.         lpCallbackInstanceData[i]->lpBuf = lpInputBuffer;
  196.         lpCallbackInstanceData[i]->hMapper = hMapper;
  197.         
  198.         wRtn = midiInOpen((LPHMIDIIN)&hMidiIn[i],
  199.                           i,
  200.                           (DWORD)midiInputHandler,
  201.                           (DWORD)lpCallbackInstanceData[i],
  202.                           CALLBACK_FUNCTION);
  203.         if(wRtn)
  204.         {
  205.             FreeCallbackInstanceData(lpCallbackInstanceData[i]);
  206.             midiInGetErrorText(wRtn, (LPSTR)szErrorText, sizeof(szErrorText));
  207.             Error(szErrorText);
  208.         }
  209.     }
  210.  
  211.     /* Start MIDI input.
  212.      */
  213.     for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) {
  214.         if (hMidiIn[i])
  215.             midiInStart(hMidiIn[i]);
  216.     }
  217.  
  218.     /* Standard Windows message processing loop.  We don't drop out of
  219.      * this loop until the user quits the application.
  220.      */
  221.     while (GetMessage(&msg, NULL, 0, 0)) {
  222.          TranslateMessage(&msg);
  223.          DispatchMessage(&msg);
  224.     }
  225.  
  226.     /* Stop, reset, close MIDI input.  Free callback instance data.
  227.      */
  228.     for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) {
  229.         if (hMidiIn[i]) {
  230.             midiInStop(hMidiIn[i]);
  231.             midiInReset(hMidiIn[i]);
  232.             midiInClose(hMidiIn[i]);
  233.             FreeCallbackInstanceData(lpCallbackInstanceData[i]);
  234.         }
  235.     }
  236.  
  237.     /* Close the MIDI Mapper, if it's open.
  238.      */
  239.     if (hMapper)
  240.         midiOutClose(hMapper);
  241.     
  242.     /* Free input and display buffers.
  243.      */
  244.     FreeCircularBuffer(lpInputBuffer);
  245.     FreeDisplayBuffer(lpDisplayBuffer);
  246.  
  247.     return (msg.wParam);
  248. }
  249.  
  250.                                                                         //
  251. /* WndProc - Main window procedure function.
  252.  */
  253. long FAR PASCAL __export WndProc(hWnd, message, wParam, lParam)
  254. HWND hWnd;
  255. UINT message;
  256. WPARAM wParam;
  257. LPARAM lParam;
  258. {
  259.     PAINTSTRUCT ps;
  260.     HFONT hFont;
  261.     HBRUSH hBrush;
  262. //!!    HPEN hPen;
  263.     HDC hDC;
  264.     TEXTMETRIC tm;
  265.     static BOOL bWindowCreated = 0;
  266.     static short wChar, hChar;
  267.     static short maxClientWidth;
  268.     static short wClient, hClient;
  269.     static short nVscrollMax = 0;
  270.     static short nHscrollMax = 0;
  271.     static short nVscrollPos = 0;
  272.     static short nHscrollPos = 0;
  273.     static short nNumCharsPerLine = 0;
  274.     static short nNumDisplayLines = 0;
  275.     static short nNumDisplayChars = 0;
  276.     short nVscrollInc, nHscrollInc;
  277.     short nPaintBeg, nPaintEnd;
  278.     int i;
  279.         
  280.     char szDisplayTextBuffer[120];
  281.  
  282.     switch(message)
  283.     {
  284.         case WM_CREATE:
  285.             hDC = GetDC(hWnd);
  286.  
  287.             /* Set the font we want to use.
  288.              */
  289.             hFont = GetStockObject(ANSI_FIXED_FONT);
  290.             SelectObject(hDC, hFont);
  291.             
  292.             /* Get text metrics and calculate the number of characters
  293.              * per line and the maximum width required for the client area.
  294.              */
  295.             GetTextMetrics(hDC, &tm);
  296.             wChar = tm.tmAveCharWidth;
  297.             hChar = tm.tmHeight + tm.tmExternalLeading;
  298.             nNumCharsPerLine = sizeof(LABEL) - 1;
  299.             maxClientWidth = LOWORD(GetTextExtent(hDC, szDisplayTextBuffer,
  300.                                         sprintf(szDisplayTextBuffer, LABEL)));
  301.             ReleaseDC(hWnd, hDC);
  302.             
  303.             bWindowCreated = 1;
  304.             break;
  305.  
  306.         case WM_SIZE:
  307.             hClient = HIWORD(lParam);
  308.             wClient = LOWORD(lParam);
  309.  
  310.             /* Get new client area and adjust scroll clip rectangle.
  311.              */
  312.             GetClientRect(hWnd, (LPRECT)&rectScrollClip);
  313.             rectScrollClip.top += hChar;
  314.             
  315.             /* Calculate new display metrics.  We subtract 1 from
  316.              * nNumDisplayLines to allow room for the label line.
  317.              */
  318.             nNumDisplayLines = hClient / hChar - 1;
  319.             nNumDisplayChars = wClient / wChar;
  320.  
  321.             /* Calculate and set new scroll bar calibrations.
  322.              */
  323.             nVscrollMax = max(0, nNumBufferLines - nNumDisplayLines);
  324.             nVscrollPos = min(nVscrollPos, nVscrollMax);
  325.             nHscrollMax = max(0, nNumCharsPerLine - nNumDisplayChars);
  326.             nHscrollPos = min(nHscrollPos, nHscrollMax);
  327.             SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
  328.             SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
  329.             SetScrollRange(hWnd, SB_HORZ, 0, nHscrollMax, FALSE);
  330.             SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
  331.             break;
  332.  
  333.         case WM_GETMINMAXINFO:
  334.             /* Limit the maximum width of the window.
  335.              */
  336.             if(bWindowCreated)
  337.                 *((LPWORD)lParam + 8) = maxClientWidth + 
  338.                                         (2 * GetSystemMetrics(SM_CXFRAME)) +
  339.                                         (GetSystemMetrics(SM_CXVSCROLL));
  340.             break;
  341.             
  342.         case WM_COMMAND:
  343.             /* Process menu messages. 
  344.              */
  345.             CommandMsg(hWnd, wParam, lParam); 
  346.             break;
  347.  
  348.         case WM_VSCROLL:
  349.             /* Determine how much to scroll vertically.
  350.              */
  351.             switch (wParam)
  352.             {
  353.                 case SB_TOP:
  354.                     nVscrollInc = -nVscrollPos;
  355.                     break;
  356.                     
  357.                 case SB_BOTTOM:
  358.                     nVscrollInc = nVscrollMax - nVscrollPos;
  359.                     break;
  360.  
  361.                 case SB_LINEUP:
  362.                     nVscrollInc = -1;
  363.                     break;
  364.  
  365.                 case SB_LINEDOWN:
  366.                     nVscrollInc = 1;
  367.                     break;
  368.  
  369.                 case SB_PAGEUP:
  370.                     nVscrollInc = min (-1, -nNumDisplayLines);
  371.                     break;
  372.  
  373.                 case SB_PAGEDOWN:
  374.                     nVscrollInc = max(1, nNumDisplayLines);
  375.                     break;
  376.  
  377.                 case SB_THUMBTRACK:
  378.                     nVscrollInc = LOWORD(lParam) - nVscrollPos;
  379.                     break;
  380.  
  381.                 default:
  382.                     nVscrollInc = 0;
  383.             
  384.             }
  385.             
  386.             /* Limit the scroll range and do the scroll.  We use the
  387.              * rectScrollClip rectangle because we don't want to scroll
  388.              * the entire window, only the part below the display label line.
  389.              */
  390.             if(nVscrollInc = max(-nVscrollPos, 
  391.                                  min(nVscrollInc, nVscrollMax - nVscrollPos)))
  392.             {
  393.                 nVscrollPos += nVscrollInc;
  394.                 ScrollWindow(hWnd, 0, -hChar * nVscrollInc,
  395.                               (LPRECT)&rectScrollClip,
  396.                               (LPRECT)&rectScrollClip);
  397.                 UpdateWindow(hWnd);
  398.                 SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
  399.             }
  400.             break;
  401.  
  402.         case WM_HSCROLL:
  403.             /* Determine how much to scroll horizontally.
  404.              */
  405.             switch (wParam)
  406.             {
  407.                 case SB_LINEUP:
  408.                     nHscrollInc = -1;
  409.                     break;
  410.  
  411.                 case SB_LINEDOWN:
  412.                     nHscrollInc = 1;
  413.                     break;
  414.  
  415.                 case SB_PAGEUP:
  416.                     nHscrollInc = min (-1, -nNumDisplayChars);
  417.                     break;
  418.  
  419.                 case SB_PAGEDOWN:
  420.                     nHscrollInc = max(1, nNumDisplayChars);
  421.                     break;
  422.  
  423.                 case SB_THUMBTRACK:
  424.                     nHscrollInc = LOWORD(lParam) - nHscrollPos;
  425.                     break;
  426.  
  427.                 default:
  428.                     nHscrollInc = 0;
  429.             }
  430.             
  431.             /* Limit the scroll range and to the scroll.
  432.              */
  433.             if(nHscrollInc = max(-nHscrollPos,
  434.                                  min(nHscrollInc, nHscrollMax - nHscrollPos)))
  435.             {
  436.                 nHscrollPos += nHscrollInc;
  437.                 ScrollWindow(hWnd, -wChar * nHscrollInc, 0, NULL, NULL);
  438.                 UpdateWindow(hWnd);
  439.                 SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
  440.             }
  441.             break;
  442.  
  443.         case WM_KEYDOWN:
  444.             /* Translate keystrokes to scroll message.
  445.              */
  446.             for (i = 0; i < NUMKEYS; i++)
  447.                 if (wParam == keyToScroll[i].wVirtKey)
  448.                 {
  449.                     SendMessage(hWnd, keyToScroll[i].iMessage,
  450.                                 keyToScroll[i].wRequest, 0L);
  451.                     break;
  452.                 }
  453.             break;
  454.  
  455.         case WM_PAINT:
  456.             BeginPaint(hWnd, &ps);
  457.  
  458.             hBrush = CreateSolidBrush(GetSysColor(COLOR_APPWORKSPACE));
  459.             FillRect(ps.hdc, &ps.rcPaint, hBrush);
  460.             DeleteObject(hBrush);
  461.  
  462.             /* Set up text attributes.
  463.              */
  464.             hFont = GetStockObject(ANSI_FIXED_FONT);
  465.             SelectObject(ps.hdc, hFont);
  466.             SetBkMode(ps.hdc, TRANSPARENT);
  467.  
  468.             /* Put up the display label if we're asked to repaint the
  469.              * top line of the screen.
  470.              */
  471.             if(ps.rcPaint.top < hChar)
  472.             {
  473.                 TextOut(ps.hdc, wChar * (0 - nHscrollPos),
  474.                         0, szDisplayTextBuffer,
  475.                         sprintf(szDisplayTextBuffer, LABEL));
  476.                 MoveTo(ps.hdc, wChar * (0 - nHscrollPos), hChar - 1);
  477.                 LineTo(ps.hdc, wClient, hChar - 1);
  478.  
  479.                 ps.rcPaint.top = hChar;
  480.             }
  481.                 
  482.             /* Calculate the beginning and ending line numbers that we need
  483.              * to paint.  These line numbers refer to lines in the display
  484.              * buffer, not to lines in the display window.
  485.              */
  486.             nPaintBeg = max (0, nVscrollPos + ps.rcPaint.top / hChar - 1);
  487.             nPaintEnd = min(nNumBufferLines,
  488.                               nVscrollPos + ps.rcPaint.bottom / hChar + 1);
  489.  
  490.             /* Get the appropriate events from the display buffer, convert
  491.              * to a text string and paint the text on the display.
  492.              */
  493.             for (i = nPaintBeg; i < nPaintEnd; i++)
  494.             {
  495.                 GetDisplayEvent(lpDisplayBuffer, (LPEVENT)&incomingEvent, i);
  496.                 TextOut(ps.hdc, 
  497.                         wChar * (0 - nHscrollPos),
  498.                         hChar * (1 - nVscrollPos + i),
  499.                         szDisplayTextBuffer, 
  500.                         GetDisplayText(szDisplayTextBuffer,
  501.                                        (LPEVENT)&incomingEvent));
  502.             }
  503.                 
  504.             EndPaint(hWnd, &ps);
  505.             break;
  506.  
  507.         case WM_DESTROY:
  508.             PostQuitMessage(0);
  509.             break;
  510.  
  511.         case MM_MIDIINPUT:
  512.             /* This is a custom message sent by the low level callback
  513.              * function telling us that there is at least one MIDI event
  514.              * in the input buffer.  We empty the input buffer, and put
  515.              * each event in the display buffer, if it's not filtered.
  516.              * If the input buffer is being filled as fast we can empty
  517.              * it, then we'll stay in this loop and not process any other
  518.              * Windows messages, or yield to other applications.  We need
  519.              * something to restrict the amount of time we spend here...
  520.              */
  521.             while(GetEvent(lpInputBuffer, (LPEVENT)&incomingEvent))
  522.             {
  523.                 if(!bRecordingEnabled)
  524.                     continue;
  525.  
  526.                 if(!CheckEventFilter((LPEVENT)&incomingEvent,
  527.                                     (LPFILTER)&filter))
  528.                 {
  529.                     AddDisplayEvent(lpDisplayBuffer, 
  530.                                     (LPEVENT)&incomingEvent);
  531.                     ++nNumBufferLines;
  532.                     nNumBufferLines = min(nNumBufferLines,
  533.                                           DISPLAY_BUFFER_SIZE - 1);
  534.                 }
  535.             }
  536.             
  537.             /* Recalculate vertical scroll bar range, and force
  538.              * the display to be updated.
  539.              */
  540.             nVscrollMax = max(0, nNumBufferLines - nNumDisplayLines);
  541.             nVscrollPos = nVscrollMax;
  542.             SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
  543.             SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
  544.             InvalidateRect(hMainWnd, (LPRECT)&rectScrollClip, 0);
  545.             UpdateWindow(hMainWnd);
  546.             
  547.             break;
  548.  
  549.         default:
  550.             return DefWindowProc(hWnd, message, wParam, lParam);
  551.             break;
  552.     }
  553.     return NULL;
  554. }
  555.  
  556.                                                                         //
  557. /* CommandMsg - Processes WM_COMMAND messages.
  558.  *
  559.  * Params:  hWnd - Handle to the window receiving the message.
  560.  *          wParam - The WORD parameter of the WM_COMMAND message. 
  561.  *          lParam - The DWORD parameter of the WM_COMMAND message. 
  562.  *
  563.  * Return:  void
  564.  */
  565. void CommandMsg(hWnd, wParam, lParam)
  566. HWND hWnd;
  567. WORD wParam;
  568. LONG lParam;
  569. {
  570.     PREFERENCES preferences;
  571.     RECT rectWindow;
  572.     WORD wRtn;
  573.     HMENU hMenu;
  574.     unsigned int i;
  575.     char szErrorText[80];
  576.     
  577.     /* Process any WM_COMMAND messages we want */
  578.     switch (wParam) {
  579.         case IDM_ABOUT:
  580.             About(hInst, hWnd);
  581.             break;
  582.  
  583.         case IDM_EXIT:
  584.             PostMessage(hWnd, WM_CLOSE, 0, 0l);
  585.             break;
  586.  
  587.         case IDM_SENDTOMAPPER:
  588.             /* We use hMapper as a toggle between sending events to the
  589.              * Mapper and not sending events.
  590.              */
  591.             if(hMapper) {
  592.                 /* Close the Mapper and reset hMapper.  Uncheck the menu item.
  593.                  * Clear Mapper handle in the instance data for each device.
  594.                  */
  595.                 wRtn = midiOutClose(hMapper);
  596.                 if(wRtn != 0)
  597.                 {
  598.                     midiOutGetErrorText(wRtn, (LPSTR)szErrorText, 
  599.                                         sizeof(szErrorText));
  600.                     Error(szErrorText);
  601.                 }
  602.                 hMapper = 0;
  603.  
  604.                 for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++)
  605.                     lpCallbackInstanceData[i]->hMapper = hMapper;
  606.  
  607.                 DoMenuItemCheck(hWnd, wParam, FALSE);
  608.             }
  609.             
  610.             else {
  611.                 /* Open the MIDI Mapper, put the Mapper handle in the instance
  612.                  * data for each device and check the menu item.
  613.                  */
  614.         wRtn = midiOutOpen((LPHMIDIOUT) &hMapper, (UINT)MIDIMAPPER,
  615.                                     0L, 0L, 0L);
  616.                                 
  617.                 if(wRtn != 0) {             // error opening Mapper
  618.                     midiOutGetErrorText(wRtn, (LPSTR)szErrorText, 
  619.                                         sizeof(szErrorText));
  620.                     Error(szErrorText);
  621.                     hMapper = 0;
  622.                 }
  623.  
  624.                 else {                      // Mapper opened successfully
  625.                     for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++)
  626.                         lpCallbackInstanceData[i]->hMapper = hMapper;
  627.  
  628.                     DoMenuItemCheck(hWnd, wParam, TRUE);
  629.                 }
  630.             }
  631.             
  632.             break;
  633.             
  634.         case IDM_SAVESETUP:
  635.             /* Save the current location and size of the display window
  636.              * in the MIDIMON.INI file.
  637.              */
  638.             GetWindowRect(hMainWnd, (LPRECT)&rectWindow);
  639.             preferences.iInitialX = rectWindow.left;
  640.             preferences.iInitialY = rectWindow.top;
  641.             preferences.iInitialW = rectWindow.right - rectWindow.left;
  642.             preferences.iInitialH = rectWindow.bottom - rectWindow.top;
  643.  
  644.             setPreferences((LPPREFERENCES) &preferences);
  645.             break;
  646.  
  647.         case IDM_STARTSTOP:
  648.             /* Toggle between recording into the display buffer and not
  649.              * recording.  Toggle the menu item between "Start" to "Stop"
  650.              * accordingly.
  651.              */
  652.             hMenu = GetMenu(hWnd);
  653.             if(bRecordingEnabled)
  654.             {
  655.                 ModifyMenu(hMenu, IDM_STARTSTOP, MF_BYCOMMAND, IDM_STARTSTOP,
  656.                            "&Start");
  657.                 bRecordingEnabled = 0;
  658.             }
  659.             else
  660.             {
  661.                 ModifyMenu(hMenu, IDM_STARTSTOP, MF_BYCOMMAND, IDM_STARTSTOP,
  662.                            "&Stop");
  663.                 bRecordingEnabled = 1;
  664.             }
  665.             DrawMenuBar(hWnd);
  666.             break;
  667.  
  668.         case IDM_CLEAR:
  669.             /* Reset the display buffer, recalibrate the scroll bars,
  670.              * and force an update of the display.
  671.              */
  672.             ResetDisplayBuffer(lpDisplayBuffer);
  673.             nNumBufferLines = 0;
  674.             SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE);
  675.  
  676.             InvalidateRect(hWnd, (LPRECT)&rectScrollClip, 0);
  677.             UpdateWindow(hWnd);
  678.  
  679.             break;
  680.             
  681.         /* Set up filter structure for MIDI channel filtering.
  682.          */
  683.         case IDM_FILTCHAN0:
  684.         case IDM_FILTCHAN1:
  685.         case IDM_FILTCHAN2:
  686.         case IDM_FILTCHAN3:
  687.         case IDM_FILTCHAN4:
  688.         case IDM_FILTCHAN5:
  689.         case IDM_FILTCHAN6:
  690.         case IDM_FILTCHAN7:
  691.         case IDM_FILTCHAN8:
  692.         case IDM_FILTCHAN9:
  693.         case IDM_FILTCHAN10:
  694.         case IDM_FILTCHAN11:
  695.         case IDM_FILTCHAN12:
  696.         case IDM_FILTCHAN13:
  697.         case IDM_FILTCHAN14:
  698.         case IDM_FILTCHAN15:
  699.             filter.channel[wParam - IDM_FILTCHAN0] = 
  700.                 !filter.channel[wParam - IDM_FILTCHAN0];
  701.             DoMenuItemCheck(hWnd, wParam, 
  702.                 filter.channel[wParam - IDM_FILTCHAN0]);
  703.             break;
  704.             
  705.         /* Setup filter structure for MIDI event filtering.
  706.          */
  707.         case IDM_NOTEOFF:
  708.             filter.event.noteOff = !filter.event.noteOff;
  709.             DoMenuItemCheck(hWnd, wParam, filter.event.noteOff);
  710.             break;
  711.         case IDM_NOTEON:
  712.             filter.event.noteOn = !filter.event.noteOn;
  713.             DoMenuItemCheck(hWnd, wParam, filter.event.noteOn);
  714.             break;
  715.         case IDM_POLYAFTERTOUCH:
  716.             filter.event.keyAftertouch = !filter.event.keyAftertouch;
  717.             DoMenuItemCheck(hWnd, wParam, filter.event.keyAftertouch);
  718.             break;
  719.         case IDM_CONTROLCHANGE:
  720.             filter.event.controller = !filter.event.controller;
  721.             DoMenuItemCheck(hWnd, wParam, filter.event.controller);
  722.             break;
  723.         case IDM_PROGRAMCHANGE:
  724.             filter.event.progChange = !filter.event.progChange;
  725.             DoMenuItemCheck(hWnd, wParam, filter.event.progChange);
  726.             break;
  727.         case IDM_CHANNELAFTERTOUCH:
  728.             filter.event.chanAftertouch = !filter.event.chanAftertouch;
  729.             DoMenuItemCheck(hWnd, wParam, filter.event.chanAftertouch);
  730.             break;
  731.         case IDM_PITCHBEND:
  732.             filter.event.pitchBend = !filter.event.pitchBend;
  733.             DoMenuItemCheck(hWnd, wParam, filter.event.pitchBend);
  734.             break;
  735.         case IDM_CHANNELMODE:
  736.             filter.event.channelMode = !filter.event.channelMode;
  737.             DoMenuItemCheck(hWnd, wParam, filter.event.channelMode);
  738.             break;
  739.         case IDM_SYSTEMEXCLUSIVE:
  740.             filter.event.sysEx = !filter.event.sysEx;
  741.             DoMenuItemCheck(hWnd, wParam, filter.event.sysEx);
  742.             break;
  743.         case IDM_SYSTEMCOMMON:
  744.             filter.event.sysCommon = !filter.event.sysCommon;
  745.             DoMenuItemCheck(hWnd, wParam, filter.event.sysCommon);
  746.             break;
  747.         case IDM_SYSTEMREALTIME:
  748.             filter.event.sysRealTime = !filter.event.sysRealTime;
  749.             DoMenuItemCheck(hWnd, wParam, filter.event.sysRealTime);
  750.             break;
  751.         case IDM_ACTIVESENSE:
  752.             filter.event.activeSense = !filter.event.activeSense;
  753.             DoMenuItemCheck(hWnd, wParam, filter.event.activeSense);
  754.             break;
  755.  
  756.         default:
  757.             break;
  758.     }
  759. }
  760.  
  761.                                                                         //
  762. /* InitFirstInstance - Performs initializaion for the first instance 
  763.  *      of the application.
  764.  *
  765.  * Params:  hInstance - Instance handle.
  766.  *
  767.  * Return:  Returns 1 if there were no errors.  Otherwise, returns 0.
  768.  */
  769. BOOL InitFirstInstance(hInstance)
  770. HANDLE hInstance;
  771. {
  772.     WNDCLASS wc;
  773.     
  774.     /* Define the class of window we want to register.
  775.      */
  776.     wc.lpszClassName    = szAppName;
  777.     wc.style            = CS_HREDRAW | CS_VREDRAW;
  778.     wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
  779.     wc.hIcon            = LoadIcon(hInstance,"Icon");
  780.     wc.lpszMenuName     = "Menu";
  781.     wc.hbrBackground    = (HBRUSH)(COLOR_APPWORKSPACE + 1);
  782.     wc.hInstance        = hInstance;
  783.     wc.lpfnWndProc      = WndProc;
  784.     wc.cbClsExtra       = 0;
  785.     wc.cbWndExtra       = 0;
  786.     
  787.     if(!RegisterClass(&wc))
  788.         return FALSE;
  789.     
  790.     return TRUE;
  791. }
  792.  
  793.                                                                         //
  794. /* DoMenuItemCheck - Checks and unchecks menu items.
  795.  *
  796.  * Params:  hWnd - Window handle for window associated with menu items.
  797.  *          wMenuItem - The menu ID for the menu item.
  798.  *          newState - The new checked/unchecked state of the menu item.
  799.  *
  800.  * Return:  void
  801. */
  802. void DoMenuItemCheck(HWND hWnd, WORD wMenuItem, BOOL newState)
  803. {
  804.     HMENU hMenu;
  805.     
  806.     hMenu = GetMenu(hWnd);
  807.     CheckMenuItem(hMenu, wMenuItem, (newState ? MF_CHECKED: MF_UNCHECKED));
  808. }
  809.  
  810.  
  811. /* Error - Beeps and shows an error message.
  812.  *
  813.  * Params:  szMsg - Points to a NULL-terminated string containing the
  814.  *              error message.
  815.  *
  816.  * Return:  Returns the return value from the MessageBox() call.
  817.  *          Since this message box has only a single button, the
  818.  *          return value isn't too meaningful.
  819.  */
  820. int Error(szMsg)
  821. LPSTR szMsg;
  822. {
  823.     MessageBeep(0);
  824.     return MessageBox(hMainWnd, szMsg, szAppName, MB_OK);
  825. }
  826.