Source code of Windows XP (NT5)
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.

368 lines
8.9 KiB

  1. /****************************************************************************
  2. *
  3. * keyb.c
  4. *
  5. * Copyright (c) 1991 Microsoft Corporation. All Rights Reserved.
  6. *
  7. ***************************************************************************/
  8. #include <windows.h>
  9. #include <mmsystem.h>
  10. #include "wincom.h"
  11. #include "sbtest.h"
  12. // make these multiples of 12
  13. #define NUMKEYS 36
  14. #define FIRSTKEY 48 // 60 is middle C (includes semi-tones)
  15. /****************************************************************************
  16. *
  17. * public data
  18. *
  19. ***************************************************************************/
  20. HWND hKeyWnd; // keyboard window
  21. /****************************************************************************
  22. *
  23. * local data
  24. *
  25. ***************************************************************************/
  26. typedef struct _key {
  27. RECT rc; // the key's area
  28. BYTE midinote; // which midi key number
  29. BYTE note; // which (alphabetical) note
  30. BYTE noteon; // is the note on?
  31. } KEY, *PKEY;
  32. static UINT wWidth; // client area width
  33. static UINT wHeight; // client area height
  34. static int iWhiteKeys; // number of white keys
  35. static BYTE bLastNote;
  36. static char keyadd = 0;
  37. static KEY keys[128];
  38. static TEXTMETRIC tm;
  39. /****************************************************************************
  40. *
  41. * internal function prototypes
  42. *
  43. ***************************************************************************/
  44. static char WhiteKey(int key);
  45. static void MidiNoteOn(BYTE note);
  46. static void MidiNoteOff(BYTE note);
  47. static BYTE GetNote(LONG pos);
  48. static void noteChanged(HWND hWnd, BYTE note);
  49. static void Paint(HWND hWnd, HDC hDC);
  50. static void Size(HWND hWnd);
  51. static char WhiteKey(int key)
  52. {
  53. switch (key % 12) {
  54. case 0:
  55. return 'C';
  56. case 2:
  57. return 'D';
  58. case 4:
  59. return 'E';
  60. case 5:
  61. return 'F';
  62. case 7:
  63. return 'G';
  64. case 9:
  65. return 'A';
  66. case 11:
  67. return 'B';
  68. default:
  69. return 0;
  70. }
  71. }
  72. void CreateKeyboard(HWND hParent)
  73. {
  74. UINT x, y, dx, dy;
  75. int i;
  76. RECT rc;
  77. // initialize key array
  78. for (i = 0; i < 128; i++) {
  79. keys[i].noteon = FALSE;
  80. keys[i].note = WhiteKey(i);
  81. keys[i].midinote = (BYTE)i;
  82. }
  83. // initial size and location of keyboard
  84. dx = 600;
  85. dy = 150;
  86. x = 20;
  87. GetWindowRect(hParent, &rc);
  88. y = rc.bottom - GetSystemMetrics(SM_CYFRAME) - dy;
  89. hKeyWnd = CreateWindow("SYNTHKEYS",
  90. "",
  91. WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_THICKFRAME,
  92. x, y, dx, dy,
  93. hParent,
  94. NULL,
  95. ghInst,
  96. (LPSTR)NULL);
  97. }
  98. static void MidiNoteOn(BYTE note)
  99. {
  100. SHORTMSG sm;
  101. if (hMidiOut) {
  102. sm.b[0] = (BYTE) 0x90 + bChannel;
  103. sm.b[1] = note;
  104. sm.b[2] = NOTEVELOCITY;
  105. midiOutShortMsg(hMidiOut, sm.dw);
  106. bLastNote = note;
  107. keys[note].noteon = TRUE;
  108. if (ISBITSET(fDebug, DEBUG_NOTE))
  109. sprintf(ach, "\nNote On %d Channel %d", note, bChannel);
  110. dbgOut;
  111. }
  112. }
  113. static void MidiNoteOff(BYTE note)
  114. {
  115. SHORTMSG sm;
  116. if (hMidiOut) {
  117. sm.b[0] = (BYTE) 0x80 + bChannel;
  118. sm.b[1] = note;
  119. sm.b[2] = 0;
  120. midiOutShortMsg(hMidiOut, sm.dw);
  121. keys[note].noteon = FALSE;
  122. if (ISBITSET(fDebug, DEBUG_NOTE))
  123. sprintf(ach, "\nNote Off %d Channel %d", note, bChannel);
  124. dbgOut;
  125. }
  126. }
  127. static BYTE GetNote(LONG pos)
  128. {
  129. POINT pt;
  130. BYTE i;
  131. pt.x = LOWORD(pos);
  132. pt.y = HIWORD(pos);
  133. for (i = FIRSTKEY; i < FIRSTKEY + NUMKEYS; i++) {
  134. if (!keys[i].note) { // black notes are 'over' white ones
  135. if (PtInRect(&(keys[i].rc), pt))
  136. return (i + keyadd);
  137. }
  138. }
  139. for (i = FIRSTKEY; i < FIRSTKEY + NUMKEYS; i++) {
  140. if (keys[i].note) { // look at white notes
  141. if (PtInRect(&(keys[i].rc), pt))
  142. return (i + keyadd);
  143. }
  144. }
  145. // shouldn't get here...
  146. return 0;
  147. }
  148. static void noteChanged(HWND hWnd, BYTE note)
  149. {
  150. RECT onRC;
  151. CopyRect(&onRC, &(keys[note].rc));
  152. onRC.top = onRC.bottom - tm.tmHeight * 2;
  153. // set top of ON rect to not overlap black keys
  154. if (WhiteKey(note))
  155. onRC.top = max(onRC.top, wHeight * 2/3);
  156. InflateRect(&onRC, -2, -2);
  157. InvalidateRect(hWnd, &onRC, WhiteKey(note));
  158. }
  159. static void Paint(HWND hWnd, HDC hDC)
  160. {
  161. int i;
  162. RECT onRC;
  163. HBRUSH hBlackBrush, hOldBrush;
  164. char buf[4];
  165. GetTextMetrics(hDC, &tm);
  166. hBlackBrush = GetStockObject(BLACK_BRUSH);
  167. for (i = FIRSTKEY; i < (FIRSTKEY + NUMKEYS); i++) {
  168. CopyRect(&onRC, &(keys[i].rc));
  169. onRC.top = onRC.bottom - 2 * tm.tmHeight;
  170. if (keys[i].note) { // paint white keys
  171. MoveToEx(hDC, keys[i].rc.right, keys[i].rc.top, NULL);
  172. LineTo(hDC, keys[i].rc.right, keys[i].rc.bottom);
  173. wsprintf(buf, "%u", i + keyadd);
  174. TextOut(hDC,
  175. keys[i].rc.left + (keys[i].rc.right -
  176. keys[i].rc.left - tm.tmAveCharWidth*lstrlen(buf)) / 2,
  177. wHeight - 2 * tm.tmHeight,
  178. (LPSTR)buf,
  179. lstrlen(buf));
  180. TextOut(hDC,
  181. keys[i].rc.right - (keys[i].rc.right -
  182. keys[i].rc.left) / 2 - tm.tmAveCharWidth / 2,
  183. wHeight - tm.tmHeight,
  184. (LPSTR)&(keys[i].note),
  185. 1);
  186. // set top of ON rect to not overlap black keys
  187. onRC.top = max(onRC.top, wHeight * 2/3);
  188. }
  189. else { // paint black keys
  190. hOldBrush = SelectObject(hDC, hBlackBrush);
  191. Rectangle(hDC,
  192. keys[i].rc.left,
  193. keys[i].rc.top,
  194. keys[i].rc.right,
  195. keys[i].rc.bottom);
  196. SelectObject(hDC, hOldBrush);
  197. }
  198. if (keys[i+keyadd].noteon) {
  199. InflateRect(&onRC, -2, -2);
  200. InvertRect(hDC, &onRC);
  201. }
  202. }
  203. }
  204. static void Size(HWND hWnd)
  205. {
  206. int i, x, dx;
  207. // count the number of white keys visible
  208. iWhiteKeys = 0;
  209. for (i = FIRSTKEY; i < FIRSTKEY + NUMKEYS; i++) {
  210. if (WhiteKey(i))
  211. iWhiteKeys++;
  212. }
  213. // x is the right side of the white, dx is the width of a white key.
  214. x = dx = wWidth / iWhiteKeys;
  215. for (i = FIRSTKEY; i < FIRSTKEY + NUMKEYS; i++) {
  216. if (keys[i].note) { // it's a white key
  217. SetRect(&(keys[i-NUMKEYS].rc), x - dx, 0, x, wHeight);
  218. SetRect(&(keys[i].rc), x - dx, 0, x, wHeight);
  219. SetRect(&(keys[i+NUMKEYS].rc), x - dx, 0, x, wHeight);
  220. x += dx;
  221. }
  222. else { // it's a black key
  223. SetRect(&(keys[i-NUMKEYS].rc), x-dx/3-dx, 0, x+dx/3-dx, wHeight*2/3);
  224. SetRect(&(keys[i].rc), x-dx/3-dx, 0, x+dx/3-dx, wHeight*2/3);
  225. SetRect(&(keys[i+NUMKEYS].rc), x-dx/3-dx, 0, x+dx/3-dx, wHeight*2/3);
  226. }
  227. }
  228. }
  229. long FAR PASCAL KeyWndProc(HWND hWnd, unsigned message, UINT wParam, LONG lParam)
  230. {
  231. PAINTSTRUCT ps; // paint structure
  232. HMENU hMenu;
  233. BYTE note;
  234. RECT rc;
  235. // process any messages we want
  236. switch(message) {
  237. case WM_CREATE:
  238. hMenu = GetMenu(hMainWnd);
  239. CheckMenuItem(hMenu, IDM_KEYBOARD, MF_CHECKED);
  240. break;
  241. case WM_SIZE:
  242. wWidth = LOWORD(lParam);
  243. wHeight = HIWORD(lParam);
  244. Size(hWnd);
  245. break;
  246. case WM_LBUTTONDOWN:
  247. note = GetNote(lParam);
  248. if (note) {
  249. MidiNoteOn(note);
  250. noteChanged(hWnd, note);
  251. }
  252. break;
  253. case WM_LBUTTONUP:
  254. note = GetNote(lParam);
  255. if (note) {
  256. MidiNoteOff(note);
  257. noteChanged(hWnd, note);
  258. }
  259. break;
  260. case WM_KEYDOWN:
  261. GetWindowRect(hWnd, &rc);
  262. rc.right = rc.right - rc.left;
  263. rc.left = 0;
  264. rc.top = wHeight - 2 * tm.tmHeight;
  265. rc.bottom = wHeight - tm.tmHeight;
  266. if (wParam == VK_CONTROL) {
  267. keyadd = -NUMKEYS;
  268. if (!(lParam & 0x40000000))
  269. InvalidateRect(hWnd, &rc, TRUE);
  270. }
  271. else if (wParam == VK_SHIFT) {
  272. keyadd = NUMKEYS;
  273. if (!(lParam & 0x40000000))
  274. InvalidateRect(hWnd, &rc, TRUE);
  275. }
  276. break;
  277. case WM_KEYUP:
  278. GetWindowRect(hWnd, &rc);
  279. rc.right = rc.right - rc.left;
  280. rc.left = 0;
  281. rc.top = wHeight - 2 * tm.tmHeight;
  282. rc.bottom = wHeight - tm.tmHeight;
  283. keyadd = 0;
  284. InvalidateRect(hWnd, &rc, TRUE);
  285. break;
  286. case WM_MOUSEMOVE:
  287. if (wParam & MK_LBUTTON) {
  288. note = GetNote(lParam);
  289. if (note && (note != bLastNote)) {
  290. MidiNoteOff(bLastNote);
  291. noteChanged(hWnd, bLastNote);
  292. MidiNoteOn(note);
  293. noteChanged(hWnd, note);
  294. }
  295. }
  296. break;
  297. case WM_PAINT:
  298. BeginPaint(hWnd, &ps);
  299. Paint(hWnd, ps.hdc);
  300. EndPaint(hWnd, &ps);
  301. break;
  302. case WM_DESTROY:
  303. hMenu = GetMenu(hMainWnd);
  304. CheckMenuItem(hMenu, IDM_KEYBOARD, MF_UNCHECKED);
  305. hKeyWnd = NULL;
  306. break;
  307. default:
  308. return DefWindowProc(hWnd, message, wParam, lParam);
  309. break;
  310. }
  311. return NULL;
  312. }