854 lines
30 KiB
C
854 lines
30 KiB
C
/* 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 <windows.h>
|
||
#include <mmsystem.h>
|
||
#include <stdio.h>
|
||
#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; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) {
|
||
wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &midiInCaps[i],
|
||
sizeof(MIDIINCAPS));
|
||
if(wRtn) {
|
||
midiInGetErrorText(wRtn, (LPSTR)szErrorText,
|
||
sizeof(szErrorText));
|
||
Error(szErrorText);
|
||
}
|
||
}
|
||
|
||
/* Allocate a circular buffer for low-level MIDI input. This buffer
|
||
* is filled by the low-level callback function and emptied by the
|
||
* application when it receives MM_MIDIINPUT messages.
|
||
*/
|
||
lpInputBuffer = AllocCircularBuffer(
|
||
(DWORD)(INPUT_BUFFER_SIZE * sizeof(EVENT)));
|
||
if (lpInputBuffer == NULL) {
|
||
Error("Not enough memory available for input buffer.");
|
||
return 1;
|
||
}
|
||
|
||
/* Allocate a display buffer. Incoming events from the circular input
|
||
* buffer are put into this buffer for display.
|
||
*/
|
||
lpDisplayBuffer = AllocDisplayBuffer((DWORD)(DISPLAY_BUFFER_SIZE));
|
||
if (lpDisplayBuffer == NULL) {
|
||
Error("Not enough memory available for display buffer.");
|
||
FreeCircularBuffer(lpInputBuffer);
|
||
return 1;
|
||
}
|
||
|
||
/* Open all MIDI input devices after allocating and setting up
|
||
* instance data for each device. The instance data is used to
|
||
* pass buffer management information between the application and
|
||
* the low-level callback function. It also includes a device ID,
|
||
* a handle to the MIDI Mapper, and a handle to the application's
|
||
* display window, so the callback can notify the window when input
|
||
* data is available. A single callback function is used to service
|
||
* all opened input devices.
|
||
*/
|
||
for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++)
|
||
{
|
||
if ((lpCallbackInstanceData[i] = AllocCallbackInstanceData()) == NULL)
|
||
{
|
||
Error("Not enough memory available.");
|
||
FreeCircularBuffer(lpInputBuffer);
|
||
FreeDisplayBuffer(lpDisplayBuffer);
|
||
return 1;
|
||
}
|
||
lpCallbackInstanceData[i]->hWnd = 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; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) {
|
||
if (hMidiIn[i])
|
||
midiInStart(hMidiIn[i]);
|
||
}
|
||
|
||
/* Standard Windows message processing loop. We don't drop out of
|
||
* this loop until the user quits the application.
|
||
*/
|
||
while (GetMessage(&msg, NULL, 0, 0)) {
|
||
TranslateMessage(&msg);
|
||
DispatchMessage(&msg);
|
||
}
|
||
|
||
/* Stop, reset, close MIDI input. Free callback instance data.
|
||
*/
|
||
for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) {
|
||
if (hMidiIn[i]) {
|
||
midiInStop(hMidiIn[i]);
|
||
midiInReset(hMidiIn[i]);
|
||
midiInClose(hMidiIn[i]);
|
||
FreeCallbackInstanceData(lpCallbackInstanceData[i]);
|
||
}
|
||
}
|
||
|
||
/* Close the MIDI Mapper, if it's open.
|
||
*/
|
||
if (hMapper)
|
||
midiOutClose(hMapper);
|
||
|
||
/* Free input and display buffers.
|
||
*/
|
||
FreeCircularBuffer(lpInputBuffer);
|
||
FreeDisplayBuffer(lpDisplayBuffer);
|
||
|
||
return (msg.wParam);
|
||
}
|
||
|
||
//
|
||
/* WndProc - Main window procedure function.
|
||
*/
|
||
LRESULT FAR PASCAL WndProc(
|
||
HWND hWnd,
|
||
UINT message,
|
||
WPARAM wParam,
|
||
LPARAM lParam)
|
||
{
|
||
PAINTSTRUCT ps;
|
||
HFONT hFont;
|
||
HBRUSH hBrush;
|
||
//!! HPEN hPen;
|
||
HDC hDC;
|
||
TEXTMETRIC tm;
|
||
static BOOL bWindowCreated = 0;
|
||
static LONG wChar, hChar;
|
||
static LONG maxClientWidth;
|
||
static LONG wClient, hClient;
|
||
static LONG nVscrollMax = 0;
|
||
static LONG nHscrollMax = 0;
|
||
static LONG nVscrollPos = 0;
|
||
static LONG nHscrollPos = 0;
|
||
static LONG nNumCharsPerLine = 0;
|
||
static LONG nNumDisplayLines = 0;
|
||
static LONG nNumDisplayChars = 0;
|
||
BOOL bNeedToUpdate = FALSE;
|
||
LONG nVscrollInc, nHscrollInc;
|
||
LONG nPaintBeg, nPaintEnd;
|
||
LONG i;
|
||
SIZE size;
|
||
|
||
char szDisplayTextBuffer[120];
|
||
|
||
switch(message)
|
||
{
|
||
case WM_CREATE:
|
||
hDC = GetDC(hWnd);
|
||
|
||
/* Set the font we want to use.
|
||
*/
|
||
hFont = GetStockObject(ANSI_FIXED_FONT);
|
||
SelectObject(hDC, hFont);
|
||
|
||
/* Get text metrics and calculate the number of characters
|
||
* per line and the maximum width required for the client area.
|
||
*/
|
||
GetTextMetrics(hDC, &tm);
|
||
wChar = (LONG) tm.tmAveCharWidth;
|
||
hChar = (LONG) (tm.tmHeight + tm.tmExternalLeading);
|
||
nNumCharsPerLine = sizeof(LABEL) - 1;
|
||
|
||
GetTextExtentPoint(hDC,
|
||
szDisplayTextBuffer,
|
||
sprintf(szDisplayTextBuffer, LABEL),
|
||
&size);
|
||
maxClientWidth = size.cx;
|
||
|
||
ReleaseDC(hWnd, hDC);
|
||
|
||
bWindowCreated = 1;
|
||
break;
|
||
|
||
case WM_SIZE:
|
||
hClient = (LONG) HIWORD(lParam);
|
||
wClient = (LONG) LOWORD(lParam);
|
||
|
||
/* Get new client area and adjust scroll clip rectangle.
|
||
*/
|
||
GetClientRect(hWnd, (LPRECT)&rectScrollClip);
|
||
rectScrollClip.top += hChar;
|
||
|
||
/* Calculate new display metrics. We subtract 1 from
|
||
* nNumDisplayLines to allow room for the label line.
|
||
*/
|
||
nNumDisplayLines = hClient / hChar - 1;
|
||
nNumDisplayChars = wClient / wChar;
|
||
|
||
/* Calculate and set new scroll bar calibrations.
|
||
*/
|
||
nVscrollMax = (LONG) max(0, nNumBufferLines - nNumDisplayLines);
|
||
nVscrollPos = (LONG) min(nVscrollPos, nVscrollMax);
|
||
nHscrollMax = (LONG) max(0, nNumCharsPerLine - nNumDisplayChars);
|
||
nHscrollPos = (LONG) min(nHscrollPos, nHscrollMax);
|
||
SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
|
||
SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
|
||
SetScrollRange(hWnd, SB_HORZ, 0, nHscrollMax, FALSE);
|
||
SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
|
||
break;
|
||
|
||
case WM_GETMINMAXINFO:
|
||
/* Limit the maximum width of the window.
|
||
*/
|
||
if(bWindowCreated) {
|
||
((LPPOINT)lParam)[4].x = maxClientWidth +
|
||
(2 * GetSystemMetrics(SM_CXFRAME)) +
|
||
(GetSystemMetrics(SM_CXVSCROLL));
|
||
}
|
||
|
||
// *((LPWORD)lParam + 8) = maxClientWidth +
|
||
// (2 * GetSystemMetrics(SM_CXFRAME)) +
|
||
// (GetSystemMetrics(SM_CXVSCROLL));
|
||
break;
|
||
|
||
case WM_COMMAND:
|
||
/* Process menu messages.
|
||
*/
|
||
CommandMsg(hWnd, wParam, lParam);
|
||
break;
|
||
|
||
case WM_VSCROLL:
|
||
/* Determine how much to scroll vertically.
|
||
*/
|
||
switch (LOWORD(wParam))
|
||
{
|
||
case SB_TOP:
|
||
nVscrollInc = -nVscrollPos;
|
||
break;
|
||
|
||
case SB_BOTTOM:
|
||
nVscrollInc = nVscrollMax - nVscrollPos;
|
||
break;
|
||
|
||
case SB_LINEUP:
|
||
nVscrollInc = -1;
|
||
break;
|
||
|
||
case SB_LINEDOWN:
|
||
nVscrollInc = 1;
|
||
break;
|
||
|
||
case SB_PAGEUP:
|
||
nVscrollInc = min (-1, -nNumDisplayLines);
|
||
break;
|
||
|
||
case SB_PAGEDOWN:
|
||
nVscrollInc = (LONG) max(1, nNumDisplayLines);
|
||
break;
|
||
|
||
case SB_THUMBTRACK:
|
||
nVscrollInc = ((LONG)HIWORD(wParam) - nVscrollPos);
|
||
break;
|
||
|
||
default:
|
||
nVscrollInc = 0;
|
||
|
||
}
|
||
|
||
/* Limit the scroll range and do the scroll. We use the
|
||
* rectScrollClip rectangle because we don't want to scroll
|
||
* the entire window, only the part below the display label line.
|
||
*/
|
||
if(nVscrollInc = max(-nVscrollPos,
|
||
min(nVscrollInc,
|
||
nVscrollMax - nVscrollPos)))
|
||
{
|
||
nVscrollPos += nVscrollInc;
|
||
ScrollWindow(hWnd, 0, -hChar * nVscrollInc,
|
||
(LPRECT)&rectScrollClip,
|
||
(LPRECT)&rectScrollClip);
|
||
UpdateWindow(hWnd);
|
||
SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
|
||
}
|
||
break;
|
||
|
||
case WM_HSCROLL:
|
||
/* Determine how much to scroll horizontally.
|
||
*/
|
||
switch (LOWORD(wParam))
|
||
{
|
||
case SB_LINEUP:
|
||
nHscrollInc = -1;
|
||
break;
|
||
|
||
case SB_LINEDOWN:
|
||
nHscrollInc = 1;
|
||
break;
|
||
|
||
case SB_PAGEUP:
|
||
nHscrollInc = (LONG) min(-1, -nNumDisplayChars);
|
||
break;
|
||
|
||
case SB_PAGEDOWN:
|
||
nHscrollInc = (LONG) max(1, nNumDisplayChars);
|
||
break;
|
||
|
||
case SB_THUMBTRACK:
|
||
nHscrollInc = ((LONG)HIWORD(wParam) - nHscrollPos);
|
||
break;
|
||
|
||
default:
|
||
nHscrollInc = 0;
|
||
}
|
||
|
||
/* Limit the scroll range and to the scroll.
|
||
*/
|
||
if(nHscrollInc = max(-nHscrollPos,
|
||
min(nHscrollInc,
|
||
nHscrollMax - nHscrollPos)))
|
||
{
|
||
nHscrollPos += nHscrollInc;
|
||
ScrollWindow(hWnd, -wChar * nHscrollInc, 0, NULL, NULL);
|
||
UpdateWindow(hWnd);
|
||
SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
|
||
}
|
||
break;
|
||
|
||
case WM_KEYDOWN:
|
||
/* Translate keystrokes to scroll message.
|
||
*/
|
||
for (i = 0; i < NUMKEYS; i++)
|
||
if (wParam == keyToScroll[i].wVirtKey)
|
||
{
|
||
SendMessage(hWnd, keyToScroll[i].iMessage,
|
||
keyToScroll[i].wRequest, 0L);
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case WM_PAINT:
|
||
BeginPaint(hWnd, &ps);
|
||
|
||
hBrush = CreateSolidBrush(GetSysColor(COLOR_APPWORKSPACE));
|
||
FillRect(ps.hdc, &ps.rcPaint, hBrush);
|
||
DeleteObject(hBrush);
|
||
|
||
/* Set up text attributes.
|
||
*/
|
||
hFont = GetStockObject(ANSI_FIXED_FONT);
|
||
SelectObject(ps.hdc, hFont);
|
||
SetBkMode(ps.hdc, TRANSPARENT);
|
||
|
||
/* Put up the display label if we're asked to repaint the
|
||
* top line of the screen.
|
||
*/
|
||
if(ps.rcPaint.top < hChar)
|
||
{
|
||
TextOut(ps.hdc, wChar * (0 - nHscrollPos),
|
||
0, szDisplayTextBuffer,
|
||
sprintf(szDisplayTextBuffer, LABEL));
|
||
MoveToEx(ps.hdc, wChar * (0 - nHscrollPos), hChar - 1, NULL);
|
||
LineTo(ps.hdc, wClient, hChar - 1);
|
||
|
||
ps.rcPaint.top = hChar;
|
||
}
|
||
|
||
/* Calculate the beginning and ending line numbers that we need
|
||
* to paint. These line numbers refer to lines in the display
|
||
* buffer, not to lines in the display window.
|
||
*/
|
||
nPaintBeg = max (0, nVscrollPos + ps.rcPaint.top / hChar - 1);
|
||
nPaintEnd = min(nNumBufferLines,
|
||
nVscrollPos + ps.rcPaint.bottom / hChar + 1);
|
||
|
||
/* Get the appropriate events from the display buffer, convert
|
||
* to a text string and paint the text on the display.
|
||
*/
|
||
for (i = nPaintBeg; i < nPaintEnd; i++)
|
||
{
|
||
GetDisplayEvent(lpDisplayBuffer, (LPEVENT)&incomingEvent, i);
|
||
TextOut(ps.hdc,
|
||
wChar * (0 - nHscrollPos),
|
||
hChar * (1 - nVscrollPos + i),
|
||
szDisplayTextBuffer,
|
||
GetDisplayText(szDisplayTextBuffer,
|
||
(LPEVENT)&incomingEvent));
|
||
}
|
||
|
||
EndPaint(hWnd, &ps);
|
||
break;
|
||
|
||
case WM_DESTROY:
|
||
PostQuitMessage(0);
|
||
break;
|
||
|
||
case MM_MIDIINPUT:
|
||
/* This is a custom message sent by the low level callback
|
||
* function telling us that there is at least one MIDI event
|
||
* in the input buffer. We empty the input buffer, and put
|
||
* each event in the display buffer, if it's not filtered.
|
||
* If the input buffer is being filled as fast we can empty
|
||
* it, then we'll stay in this loop and not process any other
|
||
* Windows messages, or yield to other applications. We need
|
||
* something to restrict the amount of time we spend here...
|
||
*/
|
||
while(GetEvent(lpInputBuffer, (LPEVENT)&incomingEvent))
|
||
{
|
||
if(!bRecordingEnabled)
|
||
continue;
|
||
|
||
if(!CheckEventFilter((LPEVENT)&incomingEvent,
|
||
(LPFILTER)&filter))
|
||
{
|
||
AddDisplayEvent(lpDisplayBuffer,
|
||
(LPEVENT)&incomingEvent);
|
||
++nNumBufferLines;
|
||
nNumBufferLines = min(nNumBufferLines,
|
||
DISPLAY_BUFFER_SIZE - 1);
|
||
bNeedToUpdate = TRUE;
|
||
}
|
||
}
|
||
|
||
/* Recalculate vertical scroll bar range, and force
|
||
* the display to be updated.
|
||
*/
|
||
|
||
if (bNeedToUpdate) {
|
||
nVscrollMax = (LONG) max(0, nNumBufferLines - nNumDisplayLines);
|
||
nVscrollPos = nVscrollMax;
|
||
SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
|
||
SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
|
||
InvalidateRect(hMainWnd, (LPRECT)&rectScrollClip, 0);
|
||
UpdateWindow(hMainWnd);
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||
break;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
/* CommandMsg - Processes WM_COMMAND messages.
|
||
*
|
||
* Params: hWnd - Handle to the window receiving the message.
|
||
* wParam - Parameter of the WM_COMMAND message.
|
||
* lParam - Parameter of the WM_COMMAND message.
|
||
*
|
||
* Return: void
|
||
*/
|
||
VOID CommandMsg(
|
||
HWND hWnd,
|
||
WPARAM wParam,
|
||
LPARAM lParam)
|
||
{
|
||
PREFERENCES preferences;
|
||
RECT rectWindow;
|
||
UINT wRtn;
|
||
HMENU hMenu;
|
||
unsigned int i;
|
||
char szErrorText[80];
|
||
WORD wCommand;
|
||
|
||
UNREFERENCED_PARAMETER(lParam);
|
||
|
||
wCommand = LOWORD(wParam);
|
||
|
||
/* Process any WM_COMMAND messages we want */
|
||
switch (wCommand) {
|
||
case IDM_ABOUT:
|
||
About(hInst, hWnd);
|
||
break;
|
||
|
||
case IDM_EXIT:
|
||
PostMessage(hWnd, WM_CLOSE, 0, 0l);
|
||
break;
|
||
|
||
case IDM_SENDTOMAPPER:
|
||
/* We use hMapper as a toggle between sending events to the
|
||
* Mapper and not sending events.
|
||
*/
|
||
if(hMapper) {
|
||
/* Close the Mapper and reset hMapper. Uncheck the menu item.
|
||
* Clear Mapper handle in the instance data for each device.
|
||
*/
|
||
wRtn = midiOutClose(hMapper);
|
||
if(wRtn != 0)
|
||
{
|
||
midiOutGetErrorText(wRtn, (LPSTR)szErrorText,
|
||
sizeof(szErrorText));
|
||
Error(szErrorText);
|
||
}
|
||
hMapper = 0;
|
||
|
||
for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++)
|
||
lpCallbackInstanceData[i]->hMapper = 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; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++)
|
||
lpCallbackInstanceData[i]->hMapper = 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);
|
||
}
|