mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
853 lines
29 KiB
853 lines
29 KiB
/* 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);
|
|
}
|