/* midimon.c - WinMain() and WndProc() functions for MIDIMon, along * with some initialization and error reporting functions. * * MIDIMon is a Windows with Multimedia application that records and displays * incoming MIDI information. It uses a low-level callback function to * get timestamped MIDI input. The callback function puts the incoming * MIDI event information (source device, timestamp, and raw MIDI * data) in a circular input buffer and notifies the application by posting * a MM_MIDIINPUT message. When the application processes the MM_MIDIINPUT * message, it removes the MIDI event from the input buffer and puts it in * a display buffer. Information in the display buffer is converted to * text and displayed in a scrollable window. Incoming MIDI data can be sent * to the MIDI Mapper if the user chooses. Filtering is provided for the * display buffer, but not for data sent to the Mapper. * * (C) Copyright Microsoft Corp. 1991. All rights reserved. * * You have a royalty-free right to use, modify, reproduce and * distribute the Sample Files (and/or any modified version) in * any way you find useful, provided that you agree that * Microsoft has no warranty obligations or liability for any * Sample Application Files which are modified. * */ #include #include #include #include "midimon.h" #include "about.h" #include "circbuf.h" #include "display.h" #include "prefer.h" #include "instdata.h" #include "callback.h" #include "filter.h" HANDLE hInst; // Instance handle for application char szAppName[20]; // Application name HWND hMainWnd; // Main window handle HMIDIOUT hMapper = 0; // Handle to MIDI Mapper UINT wNumDevices = 0; // Number of MIDI input devices opened BOOL bRecordingEnabled = 1; // Enable/disable recording flag LONG nNumBufferLines = 0; // Number of lines in display buffer RECT rectScrollClip; // Clipping rectangle for scrolling LPCIRCULARBUFFER lpInputBuffer; // Input buffer structure LPDISPLAYBUFFER lpDisplayBuffer; // Display buffer structure PREFERENCES preferences; // User preferences structure EVENT incomingEvent; // Incoming MIDI event structure MIDIINCAPS midiInCaps[MAX_NUM_DEVICES]; // Device capabilities structures HMIDIIN hMidiIn[MAX_NUM_DEVICES]; // MIDI input device handles // Callback instance data pointers LPCALLBACKINSTANCEDATA lpCallbackInstanceData[MAX_NUM_DEVICES]; // Display filter structure FILTER filter = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // Virtual key to scroll message translation structure KEYTOSCROLL keyToScroll [] = { VK_HOME, WM_VSCROLL, SB_TOP, VK_END, WM_VSCROLL, SB_BOTTOM, VK_PRIOR, WM_VSCROLL, SB_PAGEUP, VK_NEXT, WM_VSCROLL, SB_PAGEDOWN, VK_UP, WM_VSCROLL, SB_LINEUP, VK_DOWN, WM_VSCROLL, SB_LINEDOWN, VK_LEFT, WM_HSCROLL, SB_LINEUP, VK_RIGHT, WM_HSCROLL, SB_LINEDOWN }; #define NUMKEYS (sizeof (keyToScroll) / sizeof (keyToScroll[0])) // /* WinMain - Entry point for MIDIMon. */ int PASCAL WinMain(hInstance,hPrevInstance,lpszCmdLine,cmdShow) HANDLE hInstance,hPrevInstance; LPSTR lpszCmdLine; int cmdShow; { MSG msg; UINT wRtn; PREFERENCES preferences; char szErrorText[256]; unsigned int i; UNREFERENCED_PARAMETER(lpszCmdLine); hInst = hInstance; /* Get preferred user setup. */ getPreferences(&preferences); /* Initialize application. */ LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName)); if (hPrevInstance || !InitFirstInstance(hInstance)) return 0; /* Create a display window. */ hMainWnd = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, preferences.iInitialX, preferences.iInitialY, preferences.iInitialW, preferences.iInitialH, (HWND)NULL, (HMENU)NULL, hInstance, (LPSTR)NULL); if (!hMainWnd) return 1; /* Hide scroll bars for now. */ SetScrollRange(hMainWnd, SB_VERT, 0, 0, FALSE); SetScrollRange(hMainWnd, SB_HORZ, 0, 0, FALSE); /* Show the display window. */ ShowWindow(hMainWnd, cmdShow); UpdateWindow(hMainWnd); /* Get the number of MIDI input devices. Then get the capabilities of * each device. We don't use the capabilities information right now, * but we could use it to report the name of the device that received * each MIDI event. */ wNumDevices = midiInGetNumDevs(); if (!wNumDevices) { Error("There are no MIDI input devices."); PostQuitMessage(0); } for (i=0; (ihWnd = hMainWnd; lpCallbackInstanceData[i]->dwDevice = i; lpCallbackInstanceData[i]->lpBuf = lpInputBuffer; lpCallbackInstanceData[i]->hMapper = hMapper; wRtn = midiInOpen((LPHMIDIIN)&hMidiIn[i], i, (DWORD)midiInputHandler, (DWORD)lpCallbackInstanceData[i], CALLBACK_FUNCTION); if(wRtn) { FreeCallbackInstanceData(lpCallbackInstanceData[i]); midiInGetErrorText(wRtn, (LPSTR)szErrorText, sizeof(szErrorText)); Error(szErrorText); } } /* Start MIDI input. */ for (i=0; (ihMapper = hMapper; DoMenuItemCheck(hWnd, wCommand, FALSE); } else { /* Open the MIDI Mapper, put the Mapper handle in the instance * data for each device and check the menu item. */ wRtn = midiOutOpen((LPHMIDIOUT) &hMapper, (UINT) MIDIMAPPER, 0L, 0L, 0L); if(wRtn != 0) { // error opening Mapper midiOutGetErrorText(wRtn, (LPSTR)szErrorText, sizeof(szErrorText)); Error(szErrorText); hMapper = 0; } else { // Mapper opened successfully for (i=0; (ihMapper = hMapper; DoMenuItemCheck(hWnd, wCommand, TRUE); } } break; case IDM_SAVESETUP: /* Save the current location and size of the display window * in the MIDIMON.INI file. */ GetWindowRect(hMainWnd, (LPRECT)&rectWindow); preferences.iInitialX = rectWindow.left; preferences.iInitialY = rectWindow.top; preferences.iInitialW = rectWindow.right - rectWindow.left; preferences.iInitialH = rectWindow.bottom - rectWindow.top; setPreferences((LPPREFERENCES) &preferences); break; case IDM_STARTSTOP: /* Toggle between recording into the display buffer and not * recording. Toggle the menu item between "Start" to "Stop" * accordingly. */ hMenu = GetMenu(hWnd); if(bRecordingEnabled) { ModifyMenu(hMenu, IDM_STARTSTOP, MF_BYCOMMAND, IDM_STARTSTOP, "&Start"); bRecordingEnabled = 0; } else { ModifyMenu(hMenu, IDM_STARTSTOP, MF_BYCOMMAND, IDM_STARTSTOP, "&Stop"); bRecordingEnabled = 1; } DrawMenuBar(hWnd); break; case IDM_CLEAR: /* Reset the display buffer, recalibrate the scroll bars, * and force an update of the display. */ ResetDisplayBuffer(lpDisplayBuffer); nNumBufferLines = 0; SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE); InvalidateRect(hWnd, (LPRECT)&rectScrollClip, 0); UpdateWindow(hWnd); break; /* Set up filter structure for MIDI channel filtering. */ case IDM_FILTCHAN0: case IDM_FILTCHAN1: case IDM_FILTCHAN2: case IDM_FILTCHAN3: case IDM_FILTCHAN4: case IDM_FILTCHAN5: case IDM_FILTCHAN6: case IDM_FILTCHAN7: case IDM_FILTCHAN8: case IDM_FILTCHAN9: case IDM_FILTCHAN10: case IDM_FILTCHAN11: case IDM_FILTCHAN12: case IDM_FILTCHAN13: case IDM_FILTCHAN14: case IDM_FILTCHAN15: filter.channel[wCommand - IDM_FILTCHAN0] = !filter.channel[wCommand - IDM_FILTCHAN0]; DoMenuItemCheck(hWnd, wCommand, filter.channel[wCommand - IDM_FILTCHAN0]); break; /* Setup filter structure for MIDI event filtering. */ case IDM_NOTEOFF: filter.event.noteOff = !filter.event.noteOff; DoMenuItemCheck(hWnd, wCommand, filter.event.noteOff); break; case IDM_NOTEON: filter.event.noteOn = !filter.event.noteOn; DoMenuItemCheck(hWnd, wCommand, filter.event.noteOn); break; case IDM_POLYAFTERTOUCH: filter.event.keyAftertouch = !filter.event.keyAftertouch; DoMenuItemCheck(hWnd, wCommand, filter.event.keyAftertouch); break; case IDM_CONTROLCHANGE: filter.event.controller = !filter.event.controller; DoMenuItemCheck(hWnd, wCommand, filter.event.controller); break; case IDM_PROGRAMCHANGE: filter.event.progChange = !filter.event.progChange; DoMenuItemCheck(hWnd, wCommand, filter.event.progChange); break; case IDM_CHANNELAFTERTOUCH: filter.event.chanAftertouch = !filter.event.chanAftertouch; DoMenuItemCheck(hWnd, wCommand, filter.event.chanAftertouch); break; case IDM_PITCHBEND: filter.event.pitchBend = !filter.event.pitchBend; DoMenuItemCheck(hWnd, wCommand, filter.event.pitchBend); break; case IDM_CHANNELMODE: filter.event.channelMode = !filter.event.channelMode; DoMenuItemCheck(hWnd, wCommand, filter.event.channelMode); break; case IDM_SYSTEMEXCLUSIVE: filter.event.sysEx = !filter.event.sysEx; DoMenuItemCheck(hWnd, wCommand, filter.event.sysEx); break; case IDM_SYSTEMCOMMON: filter.event.sysCommon = !filter.event.sysCommon; DoMenuItemCheck(hWnd, wCommand, filter.event.sysCommon); break; case IDM_SYSTEMREALTIME: filter.event.sysRealTime = !filter.event.sysRealTime; DoMenuItemCheck(hWnd, wCommand, filter.event.sysRealTime); break; case IDM_ACTIVESENSE: filter.event.activeSense = !filter.event.activeSense; DoMenuItemCheck(hWnd, wCommand, filter.event.activeSense); break; default: break; } } // /* InitFirstInstance - Performs initializaion for the first instance * of the application. * * Params: hInstance - Instance handle. * * Return: Returns 1 if there were no errors. Otherwise, returns 0. */ BOOL InitFirstInstance(hInstance) HANDLE hInstance; { WNDCLASS wc; /* Define the class of window we want to register. */ wc.lpszClassName = szAppName; wc.style = CS_HREDRAW | CS_VREDRAW; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(hInstance,"Icon"); wc.lpszMenuName = "Menu"; wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1); wc.hInstance = hInstance; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; if(!RegisterClass(&wc)) return FALSE; return TRUE; } // /* DoMenuItemCheck - Checks and unchecks menu items. * * Params: hWnd - Window handle for window associated with menu items. * wMenuItem - The menu ID for the menu item. * newState - The new checked/unchecked state of the menu item. * * Return: void */ void DoMenuItemCheck( HWND hWnd, WORD wMenuItem, BOOL newState) { HMENU hMenu; hMenu = GetMenu(hWnd); CheckMenuItem(hMenu, wMenuItem, (newState ? MF_CHECKED: MF_UNCHECKED)); } /* Error - Beeps and shows an error message. * * Params: szMsg - Points to a NULL-terminated string containing the * error message. * * Return: Returns the return value from the MessageBox() call. * Since this message box has only a single button, the * return value isn't too meaningful. */ int Error(szMsg) LPSTR szMsg; { MessageBeep(0); return MessageBox(hMainWnd, szMsg, szAppName, MB_OK); }