windows-nt/Source/XPSP1/NT/multimedia/media/test/sbtest/keyb.c

369 lines
8.9 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/****************************************************************************
*
* keyb.c
*
* Copyright (c) 1991 Microsoft Corporation. All Rights Reserved.
*
***************************************************************************/
#include <windows.h>
#include <mmsystem.h>
#include "wincom.h"
#include "sbtest.h"
// make these multiples of 12
#define NUMKEYS 36
#define FIRSTKEY 48 // 60 is middle C (includes semi-tones)
/****************************************************************************
*
* public data
*
***************************************************************************/
HWND hKeyWnd; // keyboard window
/****************************************************************************
*
* local data
*
***************************************************************************/
typedef struct _key {
RECT rc; // the key's area
BYTE midinote; // which midi key number
BYTE note; // which (alphabetical) note
BYTE noteon; // is the note on?
} KEY, *PKEY;
static UINT wWidth; // client area width
static UINT wHeight; // client area height
static int iWhiteKeys; // number of white keys
static BYTE bLastNote;
static char keyadd = 0;
static KEY keys[128];
static TEXTMETRIC tm;
/****************************************************************************
*
* internal function prototypes
*
***************************************************************************/
static char WhiteKey(int key);
static void MidiNoteOn(BYTE note);
static void MidiNoteOff(BYTE note);
static BYTE GetNote(LONG pos);
static void noteChanged(HWND hWnd, BYTE note);
static void Paint(HWND hWnd, HDC hDC);
static void Size(HWND hWnd);
static char WhiteKey(int key)
{
switch (key % 12) {
case 0:
return 'C';
case 2:
return 'D';
case 4:
return 'E';
case 5:
return 'F';
case 7:
return 'G';
case 9:
return 'A';
case 11:
return 'B';
default:
return 0;
}
}
void CreateKeyboard(HWND hParent)
{
UINT x, y, dx, dy;
int i;
RECT rc;
// initialize key array
for (i = 0; i < 128; i++) {
keys[i].noteon = FALSE;
keys[i].note = WhiteKey(i);
keys[i].midinote = (BYTE)i;
}
// initial size and location of keyboard
dx = 600;
dy = 150;
x = 20;
GetWindowRect(hParent, &rc);
y = rc.bottom - GetSystemMetrics(SM_CYFRAME) - dy;
hKeyWnd = CreateWindow("SYNTHKEYS",
"",
WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_THICKFRAME,
x, y, dx, dy,
hParent,
NULL,
ghInst,
(LPSTR)NULL);
}
static void MidiNoteOn(BYTE note)
{
SHORTMSG sm;
if (hMidiOut) {
sm.b[0] = (BYTE) 0x90 + bChannel;
sm.b[1] = note;
sm.b[2] = NOTEVELOCITY;
midiOutShortMsg(hMidiOut, sm.dw);
bLastNote = note;
keys[note].noteon = TRUE;
if (ISBITSET(fDebug, DEBUG_NOTE))
sprintf(ach, "\nNote On %d Channel %d", note, bChannel);
dbgOut;
}
}
static void MidiNoteOff(BYTE note)
{
SHORTMSG sm;
if (hMidiOut) {
sm.b[0] = (BYTE) 0x80 + bChannel;
sm.b[1] = note;
sm.b[2] = 0;
midiOutShortMsg(hMidiOut, sm.dw);
keys[note].noteon = FALSE;
if (ISBITSET(fDebug, DEBUG_NOTE))
sprintf(ach, "\nNote Off %d Channel %d", note, bChannel);
dbgOut;
}
}
static BYTE GetNote(LONG pos)
{
POINT pt;
BYTE i;
pt.x = LOWORD(pos);
pt.y = HIWORD(pos);
for (i = FIRSTKEY; i < FIRSTKEY + NUMKEYS; i++) {
if (!keys[i].note) { // black notes are 'over' white ones
if (PtInRect(&(keys[i].rc), pt))
return (i + keyadd);
}
}
for (i = FIRSTKEY; i < FIRSTKEY + NUMKEYS; i++) {
if (keys[i].note) { // look at white notes
if (PtInRect(&(keys[i].rc), pt))
return (i + keyadd);
}
}
// shouldn't get here...
return 0;
}
static void noteChanged(HWND hWnd, BYTE note)
{
RECT onRC;
CopyRect(&onRC, &(keys[note].rc));
onRC.top = onRC.bottom - tm.tmHeight * 2;
// set top of ON rect to not overlap black keys
if (WhiteKey(note))
onRC.top = max(onRC.top, wHeight * 2/3);
InflateRect(&onRC, -2, -2);
InvalidateRect(hWnd, &onRC, WhiteKey(note));
}
static void Paint(HWND hWnd, HDC hDC)
{
int i;
RECT onRC;
HBRUSH hBlackBrush, hOldBrush;
char buf[4];
GetTextMetrics(hDC, &tm);
hBlackBrush = GetStockObject(BLACK_BRUSH);
for (i = FIRSTKEY; i < (FIRSTKEY + NUMKEYS); i++) {
CopyRect(&onRC, &(keys[i].rc));
onRC.top = onRC.bottom - 2 * tm.tmHeight;
if (keys[i].note) { // paint white keys
MoveToEx(hDC, keys[i].rc.right, keys[i].rc.top, NULL);
LineTo(hDC, keys[i].rc.right, keys[i].rc.bottom);
wsprintf(buf, "%u", i + keyadd);
TextOut(hDC,
keys[i].rc.left + (keys[i].rc.right -
keys[i].rc.left - tm.tmAveCharWidth*lstrlen(buf)) / 2,
wHeight - 2 * tm.tmHeight,
(LPSTR)buf,
lstrlen(buf));
TextOut(hDC,
keys[i].rc.right - (keys[i].rc.right -
keys[i].rc.left) / 2 - tm.tmAveCharWidth / 2,
wHeight - tm.tmHeight,
(LPSTR)&(keys[i].note),
1);
// set top of ON rect to not overlap black keys
onRC.top = max(onRC.top, wHeight * 2/3);
}
else { // paint black keys
hOldBrush = SelectObject(hDC, hBlackBrush);
Rectangle(hDC,
keys[i].rc.left,
keys[i].rc.top,
keys[i].rc.right,
keys[i].rc.bottom);
SelectObject(hDC, hOldBrush);
}
if (keys[i+keyadd].noteon) {
InflateRect(&onRC, -2, -2);
InvertRect(hDC, &onRC);
}
}
}
static void Size(HWND hWnd)
{
int i, x, dx;
// count the number of white keys visible
iWhiteKeys = 0;
for (i = FIRSTKEY; i < FIRSTKEY + NUMKEYS; i++) {
if (WhiteKey(i))
iWhiteKeys++;
}
// x is the right side of the white, dx is the width of a white key.
x = dx = wWidth / iWhiteKeys;
for (i = FIRSTKEY; i < FIRSTKEY + NUMKEYS; i++) {
if (keys[i].note) { // it's a white key
SetRect(&(keys[i-NUMKEYS].rc), x - dx, 0, x, wHeight);
SetRect(&(keys[i].rc), x - dx, 0, x, wHeight);
SetRect(&(keys[i+NUMKEYS].rc), x - dx, 0, x, wHeight);
x += dx;
}
else { // it's a black key
SetRect(&(keys[i-NUMKEYS].rc), x-dx/3-dx, 0, x+dx/3-dx, wHeight*2/3);
SetRect(&(keys[i].rc), x-dx/3-dx, 0, x+dx/3-dx, wHeight*2/3);
SetRect(&(keys[i+NUMKEYS].rc), x-dx/3-dx, 0, x+dx/3-dx, wHeight*2/3);
}
}
}
long FAR PASCAL KeyWndProc(HWND hWnd, unsigned message, UINT wParam, LONG lParam)
{
PAINTSTRUCT ps; // paint structure
HMENU hMenu;
BYTE note;
RECT rc;
// process any messages we want
switch(message) {
case WM_CREATE:
hMenu = GetMenu(hMainWnd);
CheckMenuItem(hMenu, IDM_KEYBOARD, MF_CHECKED);
break;
case WM_SIZE:
wWidth = LOWORD(lParam);
wHeight = HIWORD(lParam);
Size(hWnd);
break;
case WM_LBUTTONDOWN:
note = GetNote(lParam);
if (note) {
MidiNoteOn(note);
noteChanged(hWnd, note);
}
break;
case WM_LBUTTONUP:
note = GetNote(lParam);
if (note) {
MidiNoteOff(note);
noteChanged(hWnd, note);
}
break;
case WM_KEYDOWN:
GetWindowRect(hWnd, &rc);
rc.right = rc.right - rc.left;
rc.left = 0;
rc.top = wHeight - 2 * tm.tmHeight;
rc.bottom = wHeight - tm.tmHeight;
if (wParam == VK_CONTROL) {
keyadd = -NUMKEYS;
if (!(lParam & 0x40000000))
InvalidateRect(hWnd, &rc, TRUE);
}
else if (wParam == VK_SHIFT) {
keyadd = NUMKEYS;
if (!(lParam & 0x40000000))
InvalidateRect(hWnd, &rc, TRUE);
}
break;
case WM_KEYUP:
GetWindowRect(hWnd, &rc);
rc.right = rc.right - rc.left;
rc.left = 0;
rc.top = wHeight - 2 * tm.tmHeight;
rc.bottom = wHeight - tm.tmHeight;
keyadd = 0;
InvalidateRect(hWnd, &rc, TRUE);
break;
case WM_MOUSEMOVE:
if (wParam & MK_LBUTTON) {
note = GetNote(lParam);
if (note && (note != bLastNote)) {
MidiNoteOff(bLastNote);
noteChanged(hWnd, bLastNote);
MidiNoteOn(note);
noteChanged(hWnd, note);
}
}
break;
case WM_PAINT:
BeginPaint(hWnd, &ps);
Paint(hWnd, ps.hdc);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
hMenu = GetMenu(hMainWnd);
CheckMenuItem(hMenu, IDM_KEYBOARD, MF_UNCHECKED);
hKeyWnd = NULL;
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return NULL;
}