最近想做一个模拟钢琴的小软件,想做个既有键盘接口又有鼠标接口的小软件。所涉及到的知识点如下:

1.有关键盘的知识

2.有关鼠标的知识

3.GDI的知识

4.媒体播放函数的有关知识

因为这几天也正好在看这些内容,所以理论结合实际是最好的学习方式

首先要找钢琴音阶,但是上网找了一圈也没有发现,最后在4399里面找到一个差不多类型的模拟钢琴游戏,而且音色不错,索性用Adobe Audition的录音功能将每个音阶都录下来做一些编辑,自己制作了20来个音阶,用mp3格式保存。

然后用的是mcisendstring函数实现mp3格式音乐的播放。

第一步首先做键盘接口,其实这一步还是比较简单的,在窗口过程里面处理对应的虚拟键码的消息就行了,这里遇到了一个小问题,找不到 <, 和 >. 这两个按键对应的虚拟键码了,找了一下虚拟键码表上面好像也没有写啊?先暂时搁下

成果做出来以后,发现一个比较严重的问题,就是使用mcisendstring这个宏的时候,不能同时打开某个音乐文件,这样比如我的la这个音阶有2秒的话,必须要等到2秒结束以后才能再次播放,这样显然影响了连续的效果,目前也没有什么好的解决方案。

第二步就要涉及到GDI和鼠标接口了,这一部分正在看,等到成品做出来以后在附上源码吧。

昨天晚上做梦的时候突然想到怎么实现可以几乎同时按一个键发出声音了,话说梦真是个神奇的东西啊。

既然不能同时打开同一个音乐文件,那么我在每次打开前关闭这个音乐文件不就实现这个效果了吗?想到了确实觉得挺简单的,但是昨天怎么就没想到呢?呵呵,下面先附上我键盘接口的代码:

/*------------------------------------------------------------ a piano program ------------------------------------------------------------*/ #include <windows.h> #include <mmsystem.h> #pragma comment(lib, "WINMM.LIB") LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("HelloWin") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name TEXT ("The Hello Program"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps ; RECT rect; switch (message) { case WM_CREATE: return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps); GetClientRect (hwnd, &rect); DrawText (hdc, TEXT ("an intersting piano game^_^ just use keyboard" ), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint (hwnd, &ps) ; return 0 ; case WM_KEYDOWN: switch(wParam) { case 0x31: mciSendString(TEXT("close c://1.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://1.mp3"), NULL, 0, NULL); //0-9键盘接口 break; case 0x32: mciSendString(TEXT("close c://2.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://2.mp3"), NULL, 0, NULL); break; case 0x33: mciSendString(TEXT("close c://3.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://3.mp3"), NULL, 0, NULL); break; case 0x34: mciSendString(TEXT("close c://4.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://4.mp3"), NULL, 0, NULL); break; case 0x35: mciSendString(TEXT("close c://5.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://5.mp3"), NULL, 0, NULL); break; case 0x36: mciSendString(TEXT("close c://6.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://6.mp3"), NULL, 0, NULL); break; case 0x37: mciSendString(TEXT("close c://7.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://7.mp3"), NULL, 0, NULL); break; case 0x38: mciSendString(TEXT("close c://8.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://8.mp3"), NULL, 0, NULL); break; case 0x39: mciSendString(TEXT("close c://9.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://9.mp3"), NULL, 0, NULL); break; case 0x30: mciSendString(TEXT("close c://10.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://10.mp3"), NULL, 0, NULL); break; case 65: //字母键盘接口 mciSendString(TEXT("close c://1.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://1.mp3"), NULL, 0, NULL); break; case 90: mciSendString(TEXT("close c://2.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://2.mp3"), NULL, 0, NULL); break; case 83: mciSendString(TEXT("close c://3.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://3.mp3"), NULL, 0, NULL); break; case 88: mciSendString(TEXT("close c://4.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://4.mp3"), NULL, 0, NULL); break; case 68: mciSendString(TEXT("close c://5.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://5.mp3"), NULL, 0, NULL); break; case 67: mciSendString(TEXT("close c://6.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://6.mp3"), NULL, 0, NULL); break; case 70: mciSendString(TEXT("close c://7.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://7.mp3"), NULL, 0, NULL); break; case 86: mciSendString(TEXT("close c://8.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://8.mp3"), NULL, 0, NULL); break; case 71: mciSendString(TEXT("close c://9.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://9.mp3"), NULL, 0, NULL); break; case 66: mciSendString(TEXT("close c://10.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://10.mp3"), NULL, 0, NULL); break; case 72: mciSendString(TEXT("close c://11.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://11.mp3"), NULL, 0, NULL); break; case 78: mciSendString(TEXT("close c://12.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://12.mp3"), NULL, 0, NULL); break; case 74: mciSendString(TEXT("close c://13.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://13.mp3"), NULL, 0, NULL); break; case 77: mciSendString(TEXT("close c://14.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://14.mp3"), NULL, 0, NULL); break; case 75: mciSendString(TEXT("close c://15.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://15.mp3"), NULL, 0, NULL); break; case VK_OEM_8: mciSendString(TEXT("close c://16.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://16.mp3"), NULL, 0, NULL); break; case 76: mciSendString(TEXT("close c://17.mp3"), NULL, 0, NULL); mciSendString(TEXT("play c://17.mp3"), NULL, 0, NULL); break; } return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }

中间有很多东西都是重复的,只是简单地完成了一个音乐播放的功能,而且也没有加上资源,加上GDI等东西。

这几天偶尔在书上看到了类似的一个程序,集合了菜单,资源等很多功能,是用MIDI来控制音乐播放的,所以有很多选项可以选择,下面附上代码:

/*--------------------------------------- KBMIDI.C -- Keyboard MIDI Player (c) Charles Petzold, 1998 ---------------------------------------*/ #include <windows.h> // Defines for Menu IDs // -------------------- #define IDM_OPEN 0x100 #define IDM_CLOSE 0x101 #define IDM_DEVICE 0x200 #define IDM_CHANNEL 0x300 #define IDM_VOICE 0x400 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); TCHAR szAppName [] = TEXT ("KBMidi") ; HMIDIOUT hMidiOut ; int iDevice = MIDIMAPPER, iChannel = 0, iVoice = 0, iVelocity = 64 ; int cxCaps, cyChar, xOffset, yOffset ; // Structures and data for showing families and instruments on menu // ---------------------------------------------------------------- typedef struct { TCHAR * szInst ; int iVoice ; } INSTRUMENT ; typedef struct { TCHAR * szFam ; INSTRUMENT inst [8] ; } FAMILY ; FAMILY fam [16] = { TEXT ("Piano"), TEXT ("Acoustic Grand Piano"), 0, TEXT ("Bright Acoustic Piano"), 1, TEXT ("Electric Grand Piano"), 2, TEXT ("Honky-tonk Piano"), 3, TEXT ("Rhodes Piano"), 4, TEXT ("Chorused Piano"), 5, TEXT ("Harpsichord"), 6, TEXT ("Clavinet"), 7, TEXT ("Chromatic Percussion"), TEXT ("Celesta"), 8, TEXT ("Glockenspiel"), 9, TEXT ("Music Box"), 10, TEXT ("Vibraphone"), 11, TEXT ("Marimba"), 12, TEXT ("Xylophone"), 13, TEXT ("Tubular Bells"), 14, TEXT ("Dulcimer"), 15, TEXT ("Organ"), TEXT ("Hammond Organ"), 16, TEXT ("Percussive Organ"), 17, TEXT ("Rock Organ"), 18, TEXT ("Church Organ"), 19, TEXT ("Reed Organ"), 20, TEXT ("Accordian"), 21, TEXT ("Harmonica"), 22, TEXT ("Tango Accordian"), 23, TEXT ("Guitar"), TEXT ("Acoustic Guitar (nylon)"), 24, TEXT ("Acoustic Guitar (steel)"), 25, TEXT ("Electric Guitar (jazz)"), 26, TEXT ("Electric Guitar (clean)"), 27, TEXT ("Electric Guitar (muted)"), 28, TEXT ("Overdriven Guitar"), 29, TEXT ("Distortion Guitar"), 30, TEXT ("Guitar Harmonics"), 31, TEXT ("Bass"), TEXT ("Acoustic Bass"), 32, TEXT ("Electric Bass (finger)"), 33, TEXT ("Electric Bass (pick)"), 34, TEXT ("Fretless Bass"), 35, TEXT ("Slap Bass 1"), 36, TEXT ("Slap Bass 2"), 37, TEXT ("Synth Bass 1"), 38, TEXT ("Synth Bass 2"), 39, TEXT ("Strings"), TEXT ("Violin"), 40, TEXT ("Viola"), 41, TEXT ("Cello"), 42, TEXT ("Contrabass"), 43, TEXT ("Tremolo Strings"), 44, TEXT ("Pizzicato Strings"), 45, TEXT ("Orchestral Harp"), 46, TEXT ("Timpani"), 47, TEXT ("Ensemble"), TEXT ("String Ensemble 1"), 48, TEXT ("String Ensemble 2"), 49, TEXT ("Synth Strings 1"), 50, TEXT ("Synth Strings 2"), 51, TEXT ("Choir Aahs"), 52, TEXT ("Voice Oohs"), 53, TEXT ("Synth Voice"), 54, TEXT ("Orchestra Hit"), 55, TEXT ("Brass"), TEXT ("Trumpet"), 56, TEXT ("Trombone"), 57, TEXT ("Tuba"), 58, TEXT ("Muted Trumpet"), 59, TEXT ("French Horn"), 60, TEXT ("Brass Section"), 61, TEXT ("Synth Brass 1"), 62, TEXT ("Synth Brass 2"), 63, TEXT ("Reed"), TEXT ("Soprano Sax"), 64, TEXT ("Alto Sax"), 65, TEXT ("Tenor Sax"), 66, TEXT ("Baritone Sax"), 67, TEXT ("Oboe"), 68, TEXT ("English Horn"), 69, TEXT ("Bassoon"), 70, TEXT ("Clarinet"), 71, TEXT ("Pipe"), TEXT ("Piccolo"), 72, TEXT ("Flute"), 73, TEXT ("Recorder"), 74, TEXT ("Pan Flute"), 75, TEXT ("Bottle Blow"), 76, TEXT ("Shakuhachi"), 77, TEXT ("Whistle"), 78, TEXT ("Ocarina"), 79, TEXT ("Synth Lead"), TEXT ("Lead 1 (square)"), 80, TEXT ("Lead 2 (sawtooth)"), 81, TEXT ("Lead 3 (caliope lead)"), 82, TEXT ("Lead 4 (chiff lead)"), 83, TEXT ("Lead 5 (charang)"), 84, TEXT ("Lead 6 (voice)"), 85, TEXT ("Lead 7 (fifths)"), 86, TEXT ("Lead 8 (brass + lead)"), 87, TEXT ("Synth Pad"), TEXT ("Pad 1 (new age)"), 88, TEXT ("Pad 2 (warm)"), 89, TEXT ("Pad 3 (polysynth)"), 90, TEXT ("Pad 4 (choir)"), 91, TEXT ("Pad 5 (bowed)"), 92, TEXT ("Pad 6 (metallic)"), 93, TEXT ("Pad 7 (halo)"), 94, TEXT ("Pad 8 (sweep)"), 95, TEXT ("Synth Effects"), TEXT ("FX 1 (rain)"), 96, TEXT ("FX 2 (soundtrack)"), 97, TEXT ("FX 3 (crystal)"), 98, TEXT ("FX 4 (atmosphere)"), 99, TEXT ("FX 5 (brightness)"), 100, TEXT ("FX 6 (goblins)"), 101, TEXT ("FX 7 (echoes)"), 102, TEXT ("FX 8 (sci-fi)"), 103, TEXT ("Ethnic"), TEXT ("Sitar"), 104, TEXT ("Banjo"), 105, TEXT ("Shamisen"), 106, TEXT ("Koto"), 107, TEXT ("Kalimba"), 108, TEXT ("Bagpipe"), 109, TEXT ("Fiddle"), 110, TEXT ("Shanai"), 111, TEXT ("Percussive"), TEXT ("Tinkle Bell"), 112, TEXT ("Agogo"), 113, TEXT ("Steel Drums"), 114, TEXT ("Woodblock"), 115, TEXT ("Taiko Drum"), 116, TEXT ("Melodic Tom"), 117, TEXT ("Synth Drum"), 118, TEXT ("Reverse Cymbal"), 119, TEXT ("Sound Effects"), TEXT ("Guitar Fret Noise"), 120, TEXT ("Breath Noise"), 121, TEXT ("Seashore"), 122, TEXT ("Bird Tweet"), 123, TEXT ("Telephone Ring"), 124, TEXT ("Helicopter"), 125, TEXT ("Applause"), 126, TEXT ("Gunshot"), 127 } ; // Data for translating scan codes to octaves and notes // ---------------------------------------------------- #define NUMSCANS (sizeof key / sizeof key[0]) struct { int iOctave ; int iNote ; int yPos ; int xPos ; TCHAR * szKey ; } key [] = { // Scan Char Oct Note // ---- ---- --- ---- -1, -1, -1, -1, NULL, // 0 None -1, -1, -1, -1, NULL, // 1 Esc -1, -1, 0, 0, TEXT (""), // 2 1 5, 1, 0, 2, TEXT ("C#"), // 3 2 5 C# 5, 3, 0, 4, TEXT ("D#"), // 4 3 5 D# -1, -1, 0, 6, TEXT (""), // 5 4 5, 6, 0, 8, TEXT ("F#"), // 6 5 5 F# 5, 8, 0, 10, TEXT ("G#"), // 7 6 5 G# 5, 10, 0, 12, TEXT ("A#"), // 8 7 5 A# -1, -1, 0, 14, TEXT (""), // 9 8 6, 1, 0, 16, TEXT ("C#"), // 10 9 6 C# 6, 3, 0, 18, TEXT ("D#"), // 11 0 6 D# -1, -1, 0, 20, TEXT (""), // 12 - 6, 6, 0, 22, TEXT ("F#"), // 13 = 6 F# -1, -1, -1, -1, NULL, // 14 Back -1, -1, -1, -1, NULL, // 15 Tab 5, 0, 1, 1, TEXT ("C"), // 16 q 5 C 5, 2, 1, 3, TEXT ("D"), // 17 w 5 D 5, 4, 1, 5, TEXT ("E"), // 18 e 5 E 5, 5, 1, 7, TEXT ("F"), // 19 r 5 F 5, 7, 1, 9, TEXT ("G"), // 20 t 5 G 5, 9, 1, 11, TEXT ("A"), // 21 y 5 A 5, 11, 1, 13, TEXT ("B"), // 22 u 5 B 6, 0, 1, 15, TEXT ("C"), // 23 i 6 C 6, 2, 1, 17, TEXT ("D"), // 24 o 6 D 6, 4, 1, 19, TEXT ("E"), // 25 p 6 E 6, 5, 1, 21, TEXT ("F"), // 26 [ 6 F 6, 7, 1, 23, TEXT ("G"), // 27 ] 6 G -1, -1, -1, -1, NULL, // 28 Ent -1, -1, -1, -1, NULL, // 29 Ctrl 3, 8, 2, 2, TEXT ("G#"), // 30 a 3 G# 3, 10, 2, 4, TEXT ("A#"), // 31 s 3 A# -1, -1, 2, 6, TEXT (""), // 32 d 4, 1, 2, 8, TEXT ("C#"), // 33 f 4 C# 4, 3, 2, 10, TEXT ("D#"), // 34 g 4 D# -1, -1, 2, 12, TEXT (""), // 35 h 4, 6, 2, 14, TEXT ("F#"), // 36 j 4 F# 4, 8, 2, 16, TEXT ("G#"), // 37 k 4 G# 4, 10, 2, 18, TEXT ("A#"), // 38 l 4 A# -1, -1, 2, 20, TEXT (""), // 39 ; 5, 1, 2, 22, TEXT ("C#"), // 40 ' 5 C# -1, -1, -1, -1, NULL, // 41 ` -1, -1, -1, -1, NULL, // 42 Shift -1, -1, -1, -1, NULL, // 43 \ (not line continuation) 3, 9, 3, 3, TEXT ("A"), // 44 z 3 A 3, 11, 3, 5, TEXT ("B"), // 45 x 3 B 4, 0, 3, 7, TEXT ("C"), // 46 c 4 C 4, 2, 3, 9, TEXT ("D"), // 47 v 4 D 4, 4, 3, 11, TEXT ("E"), // 48 b 4 E 4, 5, 3, 13, TEXT ("F"), // 49 n 4 F 4, 7, 3, 15, TEXT ("G"), // 50 m 4 G 4, 9, 3, 17, TEXT ("A"), // 51 , 4 A 4, 11, 3, 19, TEXT ("B"), // 52 . 4 B 5, 0, 3, 21, TEXT ("C") // 53 / 5 C } ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MSG msg; HWND hwnd ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Keyboard MIDI Player"), WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; if (!hwnd) return 0 ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd); while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } // Create the program's menu (called from WndProc, WM_CREATE) // ---------------------------------------------------------- HMENU CreateTheMenu (int iNumDevs) { TCHAR szBuffer [32] ; HMENU hMenu, hMenuPopup, hMenuSubPopup ; int i, iFam, iIns ; MIDIOUTCAPS moc ; hMenu = CreateMenu () ; // Create "On/Off" popup menu hMenuPopup = CreateMenu () ; AppendMenu (hMenuPopup, MF_STRING, IDM_OPEN, TEXT ("&Open")) ; AppendMenu (hMenuPopup, MF_STRING | MF_CHECKED, IDM_CLOSE, TEXT ("&Closed")) ; AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup, TEXT ("&Status")) ; // Create "Device" popup menu hMenuPopup = CreateMenu () ; // Put MIDI Mapper on menu if it's installed if (!midiOutGetDevCaps (MIDIMAPPER, &moc, sizeof (moc))) AppendMenu (hMenuPopup, MF_STRING, IDM_DEVICE + (int) MIDIMAPPER, moc.szPname) ; else iDevice = 0 ; // Add the rest of the MIDI devices for (i = 0 ; i < iNumDevs ; i++) { midiOutGetDevCaps (i, &moc, sizeof (moc)) ; AppendMenu (hMenuPopup, MF_STRING, IDM_DEVICE + i, moc.szPname) ; } CheckMenuItem (hMenuPopup, 0, MF_BYPOSITION | MF_CHECKED) ; AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup, TEXT ("&Device")) ; // Create "Channel" popup menu hMenuPopup = CreateMenu () ; for (i = 0 ; i < 16 ; i++) { wsprintf (szBuffer, TEXT ("%d"), i + 1) ; AppendMenu (hMenuPopup, MF_STRING | (i ? MF_UNCHECKED : MF_CHECKED), IDM_CHANNEL + i, szBuffer) ; } AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup, TEXT ("&Channel")) ; // Create "Voice" popup menu hMenuPopup = CreateMenu () ; for (iFam = 0 ; iFam < 16 ; iFam++) { hMenuSubPopup = CreateMenu () ; for (iIns = 0 ; iIns < 8 ; iIns++) { wsprintf (szBuffer, TEXT ("&%d.\t%s"), iIns + 1, fam[iFam].inst[iIns].szInst) ; AppendMenu (hMenuSubPopup, MF_STRING | (fam[iFam].inst[iIns].iVoice ? MF_UNCHECKED : MF_CHECKED), fam[iFam].inst[iIns].iVoice + IDM_VOICE, szBuffer) ; } wsprintf (szBuffer, TEXT ("&%c.\t%s"), 'A' + iFam, fam[iFam].szFam) ; AppendMenu (hMenuPopup, MF_STRING | MF_POPUP, (UINT) hMenuSubPopup, szBuffer) ; } AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup, TEXT ("&Voice")) ; return hMenu ; } // Routines for simplifying MIDI output // ------------------------------------ DWORD MidiOutMessage (HMIDIOUT hMidi, int iStatus, int iChannel, int iData1, int iData2) { DWORD dwMessage ; dwMessage = iStatus | iChannel | (iData1 << 8) | (iData2 << 16) ; return midiOutShortMsg (hMidi, dwMessage) ; } DWORD MidiNoteOff (HMIDIOUT hMidi, int iChannel, int iOct, int iNote, int iVel) { return MidiOutMessage (hMidi, 0x080, iChannel, 12 * iOct + iNote, iVel) ; } DWORD MidiNoteOn (HMIDIOUT hMidi, int iChannel, int iOct, int iNote, int iVel) { return MidiOutMessage (hMidi, 0x090, iChannel, 12 * iOct + iNote, iVel) ; } DWORD MidiSetPatch (HMIDIOUT hMidi, int iChannel, int iVoice) { return MidiOutMessage (hMidi, 0x0C0, iChannel, iVoice, 0) ; } DWORD MidiPitchBend (HMIDIOUT hMidi, int iChannel, int iBend) { return MidiOutMessage (hMidi, 0x0E0, iChannel, iBend & 0x7F, iBend >> 7) ; } // Draw a single key on window // --------------------------- VOID DrawKey (HDC hdc, int iScanCode, BOOL fInvert) { RECT rc ; rc.left = 3 * cxCaps * key[iScanCode].xPos / 2 + xOffset ; rc.top = 3 * cyChar * key[iScanCode].yPos / 2 + yOffset ; rc.right = rc.left + 3 * cxCaps ; rc.bottom = rc.top + 3 * cyChar / 2 ; SetTextColor (hdc, fInvert ? 0x00FFFFFFul : 0x00000000ul) ; SetBkColor (hdc, fInvert ? 0x00000000ul : 0x00FFFFFFul) ; FillRect (hdc, &rc, GetStockObject (fInvert ? BLACK_BRUSH : WHITE_BRUSH)) ; DrawText (hdc, key[iScanCode].szKey, -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; FrameRect (hdc, &rc, GetStockObject (BLACK_BRUSH)) ; } // Process a Key Up or Key Down message // ------------------------------------ VOID ProcessKey (HDC hdc, UINT message, LPARAM lParam) { int iScanCode, iOctave, iNote ; iScanCode = 0x0FF & HIWORD (lParam) ; if (iScanCode >= NUMSCANS) // No scan codes over 53 return ; if ((iOctave = key[iScanCode].iOctave) == -1) // Non-music key return ; if (GetKeyState (VK_SHIFT) < 0) iOctave += 0x20000000 & lParam ? 2 : 1 ; if (GetKeyState (VK_CONTROL) < 0) iOctave -= 0x20000000 & lParam ? 2 : 1 ; iNote = key[iScanCode].iNote ; if (message == WM_KEYUP) // For key up { MidiNoteOff (hMidiOut, iChannel, iOctave, iNote, 0) ; // Note off DrawKey (hdc, iScanCode, FALSE) ; return ; } if (0x40000000 & lParam) // ignore typematics return ; MidiNoteOn (hMidiOut, iChannel, iOctave, iNote, iVelocity) ; // Note on DrawKey (hdc, iScanCode, TRUE) ; // Draw the inverted key } // Window Procedure // ---------------- LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static BOOL bOpened = FALSE ; HDC hdc ; HMENU hMenu ; int i, iNumDevs, iPitchBend, cxClient, cyClient ; MIDIOUTCAPS moc ; PAINTSTRUCT ps ; SIZE size ; TCHAR szBuffer [16] ; switch (message) { case WM_CREATE: // Get size of capital letters in system font hdc = GetDC (hwnd) ; GetTextExtentPoint (hdc, TEXT ("M"), 1, &size) ; cxCaps = size.cx ; cyChar = size.cy ; ReleaseDC (hwnd, hdc) ; // Initialize "Volume" scroll bar SetScrollRange (hwnd, SB_HORZ, 1, 127, FALSE) ; SetScrollPos (hwnd, SB_HORZ, iVelocity, TRUE) ; // Initialize "Pitch Bend" scroll bar SetScrollRange (hwnd, SB_VERT, 0, 16383, FALSE) ; SetScrollPos (hwnd, SB_VERT, 8192, TRUE) ; // Get number of MIDI output devices and set up menu if (0 == (iNumDevs = midiOutGetNumDevs ())) { MessageBeep (MB_ICONSTOP) ; MessageBox (hwnd, TEXT ("No MIDI output devices!"), szAppName, MB_OK | MB_ICONSTOP) ; return -1 ; } SetMenu (hwnd, CreateTheMenu (iNumDevs)) ; return 0 ; case WM_SIZE: cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; xOffset = (cxClient - 25 * 3 * cxCaps / 2) / 2 ; yOffset = (cyClient - 11 * cyChar) / 2 + 5 * cyChar ; return 0 ; case WM_COMMAND: hMenu = GetMenu (hwnd) ; // "Open" menu command if (LOWORD (wParam) == IDM_OPEN && !bOpened) { if (midiOutOpen (&hMidiOut, iDevice, 0, 0, 0)) { MessageBeep (MB_ICONEXCLAMATION) ; MessageBox (hwnd, TEXT ("Cannot open MIDI device"), szAppName, MB_OK | MB_ICONEXCLAMATION) ; } else { CheckMenuItem (hMenu, IDM_OPEN, MF_CHECKED) ; CheckMenuItem (hMenu, IDM_CLOSE, MF_UNCHECKED) ; MidiSetPatch (hMidiOut, iChannel, iVoice) ; bOpened = TRUE ; } } // "Close" menu command else if (LOWORD (wParam) == IDM_CLOSE && bOpened) { CheckMenuItem (hMenu, IDM_OPEN, MF_UNCHECKED) ; CheckMenuItem (hMenu, IDM_CLOSE, MF_CHECKED) ; // Turn all keys off and close device for (i = 0 ; i < 16 ; i++) MidiOutMessage (hMidiOut, 0xB0, i, 123, 0) ; midiOutClose (hMidiOut) ; bOpened = FALSE ; } // Change MIDI "Device" menu command else if (LOWORD (wParam) >= IDM_DEVICE - 1 && LOWORD (wParam) < IDM_CHANNEL) { CheckMenuItem (hMenu, IDM_DEVICE + iDevice, MF_UNCHECKED) ; iDevice = LOWORD (wParam) - IDM_DEVICE ; CheckMenuItem (hMenu, IDM_DEVICE + iDevice, MF_CHECKED) ; // Close and reopen MIDI device if (bOpened) { SendMessage (hwnd, WM_COMMAND, IDM_CLOSE, 0L) ; SendMessage (hwnd, WM_COMMAND, IDM_OPEN, 0L) ; } } // Change MIDI "Channel" menu command else if (LOWORD (wParam) >= IDM_CHANNEL && LOWORD (wParam) < IDM_VOICE) { CheckMenuItem (hMenu, IDM_CHANNEL + iChannel, MF_UNCHECKED); iChannel = LOWORD (wParam) - IDM_CHANNEL ; CheckMenuItem (hMenu, IDM_CHANNEL + iChannel, MF_CHECKED) ; if (bOpened) MidiSetPatch (hMidiOut, iChannel, iVoice) ; } // Change MIDI "Voice" menu command else if (LOWORD (wParam) >= IDM_VOICE) { CheckMenuItem (hMenu, IDM_VOICE + iVoice, MF_UNCHECKED) ; iVoice = LOWORD (wParam) - IDM_VOICE ; CheckMenuItem (hMenu, IDM_VOICE + iVoice, MF_CHECKED) ; if (bOpened) MidiSetPatch (hMidiOut, iChannel, iVoice) ; } InvalidateRect (hwnd, NULL, TRUE) ; return 0 ; // Process a Key Up or Key Down message case WM_KEYUP: case WM_KEYDOWN: hdc = GetDC (hwnd) ; if (bOpened) ProcessKey (hdc, message, lParam) ; ReleaseDC (hwnd, hdc) ; return 0 ; // For Escape, turn off all notes and repaint case WM_CHAR: if (bOpened && wParam == 27) { for (i = 0 ; i < 16 ; i++) MidiOutMessage (hMidiOut, 0xB0, i, 123, 0) ; InvalidateRect (hwnd, NULL, TRUE) ; } return 0 ; // Horizontal scroll: Velocity case WM_HSCROLL: switch (LOWORD (wParam)) { case SB_LINEUP: iVelocity -= 1 ; break ; case SB_LINEDOWN: iVelocity += 1 ; break ; case SB_PAGEUP: iVelocity -= 8 ; break ; case SB_PAGEDOWN: iVelocity += 8 ; break ; case SB_THUMBPOSITION: iVelocity = HIWORD (wParam) ; break ; default: return 0 ; } iVelocity = max (1, min (iVelocity, 127)) ; SetScrollPos (hwnd, SB_HORZ, iVelocity, TRUE) ; return 0 ; // Vertical scroll: Pitch Bend case WM_VSCROLL: switch (LOWORD (wParam)) { case SB_THUMBTRACK: iPitchBend = 16383 - HIWORD (wParam) ; break ; case SB_THUMBPOSITION: iPitchBend = 8191 ; break ; default: return 0 ; } iPitchBend = max (0, min (iPitchBend, 16383)) ; SetScrollPos (hwnd, SB_VERT, 16383 - iPitchBend, TRUE) ; if (bOpened) MidiPitchBend (hMidiOut, iChannel, iPitchBend) ; return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; for (i = 0 ; i < NUMSCANS ; i++) if (key[i].xPos != -1) DrawKey (hdc, i, FALSE) ; midiOutGetDevCaps (iDevice, &moc, sizeof (MIDIOUTCAPS)) ; wsprintf (szBuffer, TEXT ("Channel %i"), iChannel + 1) ; TextOut (hdc, cxCaps, 1 * cyChar, bOpened ? TEXT ("Open") : TEXT ("Closed"), bOpened ? 4 : 6) ; TextOut (hdc, cxCaps, 2 * cyChar, moc.szPname, lstrlen (moc.szPname)) ; TextOut (hdc, cxCaps, 3 * cyChar, szBuffer, lstrlen (szBuffer)) ; TextOut (hdc, cxCaps, 4 * cyChar, fam[iVoice / 8].inst[iVoice % 8].szInst, lstrlen (fam[iVoice / 8].inst[iVoice % 8].szInst)) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : SendMessage (hwnd, WM_COMMAND, IDM_CLOSE, 0L) ; PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
程序虽然有点长,自己现在也没有学得很好,所以还不能完全看懂。希望以后能写一下关于这个程序的解释。

windows SDK模拟游戏钢琴的实现(一)相关推荐

  1. windows SDK模拟游戏钢琴的实现(二)

    昨天晚上失眠了,于是一直在想实现GDI的一个效果:首先绘制10个相连的矩形,当鼠标移到某个矩形的时候,自动填充那个矩形的背景颜色为灰色,移开的时候恢复原来的白色.当只有一个矩形的时候,当然不是一件难事 ...

  2. [译]Kinect for Windows SDK开发入门(八):骨骼追踪进阶 上

    前7篇文件我们介绍了Kinect SDK中各种传感器的各种基本知识,我们用实验的方式演示了这些基本对象和方法的如何使用,这些都是Kinect开发最基本的知识.了解了这些基本知识后,就可以开发出一个基于 ...

  3. Kinect for Windows SDK开发入门

    Kinect for Windows SDK开发入门(一):开发环境配置 首先来看一下Kinect设备: 1. Kinect设备 黑色的Kinect设备如下图:基座和感应器之间有一个电动的马达,通过程 ...

  4. C++模拟游戏中鼠标点击和键盘按键

    游戏中模拟键盘输入,有时回被系统屏蔽,Java等语言都试过很多方法,好像都没用,所以下面给出一种C++实现方法 #include <iostream> #include <windo ...

  5. Kinect for Windows SDK发布

    转载请注明出处为KlayGE游戏引擎,本文地址为http://www.klayge.org/2011/06/17/kinect-for-windows-sdk%e5%8f%91%e5%b8%83/ 前 ...

  6. 深度相机(八)--OpenNI及与Kinect for windows SDK的比较

     OpenNI(开放自然交互)是一个多语言,跨平台的框架,它定义了编写应用程序,并利用其自然交互的API.OpenNI API由一组可用来编写通用自然交互应用的接口组成.OpenNI的主要目的是要 ...

  7. 【计算机视觉】深度相机(八)--OpenNI及与Kinect for windows SDK的比较

    OpenNI(开放自然交互)是一个多语言,跨平台的框架,它定义了编写应用程序,并利用其自然交互的API.OpenNI API由一组可用来编写通用自然交互应用的接口组成.OpenNI的主要目的是要形成一 ...

  8. Kinect for Windows SDK v2.0 开发笔记 (十三) 高清面部帧(4) 面部模型构建器

     (转载请注明出处) 使用SDK: Kinect for Windows SDK v2.0 public preview1409 同前面,因为SDK未完成,不附上函数/方法/接口的超链接. 这次让 ...

  9. 大漠找图算法_GitHub - jozhn/op: op插件(类似大漠插件),Windows消息模拟,gdi,dx,opengl截图,找图,找字(OCR),其他实用算法...

    Overview OP(operator & open)是一个开源插件(类似大漠插件).主要特点:Windows消息模拟,后台截图,找图,字符识别(OCR),以及其他实用功能...使用c++编 ...

最新文章

  1. 一道终身受用的测试题
  2. 关于ExtJS3.0组件连接http://extjs.com/s.gif的问题
  3. 【科技金融】专家评分卡和机器学习评分卡的差异化和必要性
  4. IntelliJ IDEA使用技巧——自动生成 serialVersionUID 的方法
  5. 从没想过会有一个这样的机会|大疆招聘
  6. C 语言 函数调用栈
  7. python系统关键字_Python基础教程 - global关键字及全局变量的用法
  8. 安卓页面去掉顶部标题
  9. 3-2Tensor的基本定义
  10. golang的指针和切片
  11. DNS原因导致的不能访问网站问题一例
  12. objective-C nil,Nil,NULL 和NSNull的小结
  13. Spring 测试代码的写法以及一个c3p0的错误
  14. 安全产品的核心逻辑-IPS/IDS
  15. Create BD link
  16. 第四扩展fs_四个fs
  17. # 北京市职业技能提升管理平台#大数据#
  18. STM8读取AD值偶尔跳变出错的问题
  19. java 图形学_JAVA画图形学-实验报告
  20. bugku web18 秋名山车神

热门文章

  1. 富士康的二手iPhone生意再度遭遇危机
  2. 7-13 盲盒包装流水线
  3. 手机卡服务器密码忘记了怎么修改密码,wifi密码忘记了怎么办找回密码 手机怎么修改自家wifi密码...
  4. 关于frida调试的时候提示Failed to spawn: the ‘argv‘ option is not supported when spawning Android apps错误
  5. 微信公众号网页H5跳转微信小程序
  6. java 手电筒 电池_android 手电筒的实现
  7. 【深度学习】大牛的《深度学习》笔记,Deep Learning速成教程
  8. 1003. 我要通过!
  9. PHP7 搭建简易的学生管理系统(四)
  10. 什么是套利型创业者?