commit be89fa7bf6a1257b0fc63f043a3db015634fabd2 Author: Quantum Date: Sun Jun 2 22:14:25 2013 -0400 Initial Commit. Signed-off-by: Xiaomao Chen diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46885eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.obj +*.exe +*.res +/build +/dist +/test +/old +/*.pdb diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5ccff7e --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +SRCDIR=src +INCDIR=include + +CXX=cl /nologo +LD=link /nologo +CXXFLAGS=/c /O1 /I$(INCDIR) /W4 /Zi /DWIN32_LEAN_AND_MEAN /DWINVER=0x0501 /D_WIN32_WINNT=0x0501 /DUNICODE /D_UNICODE +LDFLAGS=/subsystem:windows /debug /manifest /incremental:no /opt:REF +LDFLAGS=$(LDFLAGS) "/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'" +RC=rc /nologo +RCFLAGS=/i$(INCDIR) +LIBS= + +!IFDEF DEBUG +BUILD=Debug +CXXFLAGS=$(CXXFLAGS) /DDEBUG +LDFLAGS=$(LDFLAGS) +!ELSE +BUILD=Release +LDFLAGS=$(LDFLAGS) +!ENDIF + +OUTDIR=build\$(BUILD) +DISTDIR=dist\$(BUILD) +FILES=$(OUTDIR)\Keyboard.obj \ + $(OUTDIR)\MainWindow.obj \ + $(OUTDIR)\Window.obj \ + $(OUTDIR)\keyboard.res + +all: initdir $(DISTDIR)\Keyboard.exe + +initdir: + if not exist build md build + if not exist $(OUTDIR) md $(OUTDIR) + if not exist build md dist + if not exist $(DISTDIR) md $(DISTDIR) + +$(INCDIR)\MainWindow.hpp: $(INCDIR)\Window.hpp + +$(SRCDIR)\MainWindow.cpp: $(INCDIR)\MainWindow.hpp +$(SRCDIR)\Keyboard.cpp: $(INCDIR)\MainWindow.hpp +$(SRCDIR)\Window.cpp: $(INCDIR)\Window.hpp +keyboard.rc: keyboard.ico keymap.bmp + +$(OUTDIR)\keyboard.res: keyboard.rc + $(RC) $(RCFLAGS) /fo$@ $** + +{$(SRCDIR)}.cpp{$(OUTDIR)}.obj:: + $(CXX) $(CXXFLAGS) /Fo$(OUTDIR)\ /Fd$(OUTDIR)\ $< + +{$(SRCDIR)}.c{$(OUTDIR)}.obj:: + $(CXX) $(CXXFLAGS) /Fo$(OUTDIR)\ /Fd$(OUTDIR)\ $< + +$(DISTDIR)\Keyboard.exe: $(FILES) + $(LD) /out:$@ $(LDFLAGS) $** $(LIBS) + mt.exe -nologo -manifest $@.manifest -outputresource:$@;1 && del $@.manifest || set ERRORLEVEL=0 diff --git a/include/MainWindow.hpp b/include/MainWindow.hpp new file mode 100644 index 0000000..61bb231 --- /dev/null +++ b/include/MainWindow.hpp @@ -0,0 +1,42 @@ +#pragma once +#ifndef id68C60171_0140_4DE1_B7255EFF557A74F9 +#define id68C60171_0140_4DE1_B7255EFF557A74F9 + +#include + +#include +#include +#include + +class MainWindow : public Window { +public: + virtual LPCTSTR ClassName() { return TEXT("MusicKeyboardMain"); } + static MainWindow *Create(LPCTSTR szTitle); +protected: + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + LRESULT OnCreate(); + LRESULT OnDestroy(); + bool Play(WPARAM wCode, LPARAM lParam, bool down); + void OnPaint(); + void PaintContent(PAINTSTRUCT *pps); + BOOL WinRegisterClass(WNDCLASS *pwc); + WORD GetQWERTYKeyCode(WORD wKeyCode); + WORD GetRealKeyCode(WORD wQWERTYCode); + + virtual HICON GetIcon(); + + HWND m_volumeLabel, m_volumeBar; + HWND m_forceLabel, m_forceBar; + HWND m_instruLabel, m_instruSelect; + int m_instrument, m_volume, m_force; + HMIDIOUT m_midi; + bool isQWERTY; + HKL hklQWERTY; +private: + HFONT hFont; + HBRUSH hBrush; + HBITMAP hKeyboardLayout; + HDC hdcKeyboard; +}; + +#endif diff --git a/include/Window.hpp b/include/Window.hpp new file mode 100644 index 0000000..ef96bda --- /dev/null +++ b/include/Window.hpp @@ -0,0 +1,65 @@ +#pragma once +#ifndef idC3714409_EABC_45DE_B5338EC60149AE87 +#define idC3714409_EABC_45DE_B5338EC60149AE87 + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +inline void MakeRect(RECT &rect, int x, int y, int cx, int cy) +{ + rect.left = x; + rect.top = y; + rect.right = x + cx; + rect.bottom = y + cy; +} + +inline void MakeRectRight(RECT &rect, int x, int y, int cx, int cy) +{ + MakeRect(rect, x - cx, y, cx, cy); +} + +inline void MakeRectBottom(RECT &rect, int x, int y, int cx, int cy) +{ + MakeRect(rect, x, y - cy, cx, cy); +} + +inline void MakeRectBottomRight(RECT &rect, int x, int y, int cx, int cy) +{ + MakeRect(rect, x - cx, y - cy, cx, cy); +} + +class Window { +public: + HWND GetHWND() { return m_hwnd; } + static HINSTANCE GetInstance() { + static HINSTANCE instance = (HINSTANCE) GetModuleHandle(NULL); + return instance; // Doesn't change + } +protected: + virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual void PaintContent(PAINTSTRUCT *pps) { UNREFERENCED_PARAMETER(pps); } + virtual LPCTSTR ClassName() = 0; + virtual HICON GetIcon() { return NULL; } + virtual BOOL WinRegisterClass(WNDCLASS *pwc) { + return RegisterClass(pwc); + } + virtual ~Window(); + + HWND WinCreateWindow(DWORD dwExStyle, LPCTSTR pszName, + DWORD dwStyle, int x, int y, int cx, + int cy, HWND hwndParent, HMENU hmenu); +private: + virtual void Register(); + void OnPaint(); + void OnPrintClient(HDC hdc); + + static LRESULT CALLBACK + s_WndProc(HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam); +protected: + HWND m_hwnd; +}; + +#endif // header diff --git a/include/resource.h b/include/resource.h new file mode 100644 index 0000000..3b97a01 --- /dev/null +++ b/include/resource.h @@ -0,0 +1,2 @@ +#define RID_ICON 1 +#define RID_KEYBOARD 2 diff --git a/keyboard.bmp b/keyboard.bmp new file mode 100644 index 0000000..13cfed2 Binary files /dev/null and b/keyboard.bmp differ diff --git a/keyboard.ico b/keyboard.ico new file mode 100644 index 0000000..55bdb3b Binary files /dev/null and b/keyboard.ico differ diff --git a/keyboard.rc b/keyboard.rc new file mode 100644 index 0000000..def68c3 --- /dev/null +++ b/keyboard.rc @@ -0,0 +1,137 @@ +#include +#include + +RID_ICON ICON keyboard.ico +RID_KEYBOARD BITMAP keymap.bmp + +STRINGTABLE LANGUAGE 0x09, 0x01 // en-US +BEGIN + 0xAE00, "Acoustic Grand Piano" + 0xAE01, "Bright Acoustic Piano" + 0xAE02, "Electric Grand Piano" + 0xAE03, "Honky-tonk Piano" + 0xAE04, "Rhodes Piano" + 0xAE05, "Chorused Piano" + 0xAE06, "Harpsichord" + 0xAE07, "Clavichord" + 0xAE08, "Celesta" + 0xAE09, "Glockenspiel" + 0xAE0A, "Music box" + 0xAE0B, "Vibraphone" + 0xAE0C, "Marimba" + 0xAE0D, "Xylophone" + 0xAE0E, "Tubular Bells" + 0xAE0F, "Dulcimer" + 0xAE10, "Hammond Organ" + 0xAE11, "Percussive Organ" + 0xAE12, "Rock Organ" + 0xAE13, "Church Organ" + 0xAE14, "Reed Organ" + 0xAE15, "Accordian" + 0xAE16, "Harmonica" + 0xAE17, "Tango Accordian" + 0xAE18, "Acoustic Guitar (nylon)" + 0xAE19, "Acoustic Guitar (steel)" + 0xAE1A, "Electric Guitar (jazz)" + 0xAE1B, "Electric Guitar (clean)" + 0xAE1C, "Electric Guitar (muted)" + 0xAE1D, "Overdriven Guitar" + 0xAE1E, "Distortion Guitar" + 0xAE1F, "Guitar Harmonics" + 0xAE20, "Acoustic Bass" + 0xAE21, "Electric Bass(finger)" + 0xAE22, "Electric Bass (pick)" + 0xAE23, "Fretless Bass" + 0xAE24, "Slap Bass 1" + 0xAE25, "Slap Bass 2" + 0xAE26, "Synth Bass 1" + 0xAE27, "Synth Bass 2" + 0xAE28, "Violin" + 0xAE29, "Viola" + 0xAE2A, "Cello" + 0xAE2B, "Contrabass" + 0xAE2C, "Tremolo Strings" + 0xAE2D, "Pizzicato Strings" + 0xAE2E, "Orchestral Harp" + 0xAE2F, "Timpani" + 0xAE30, "String Ensemble 1" + 0xAE31, "String Ensemble 2" + 0xAE32, "Synth Strings 1" + 0xAE33, "Synth Strings 2" + 0xAE34, "Choir Aahs" + 0xAE35, "Voice Oohs" + 0xAE36, "Synth Voice" + 0xAE37, "Orchestra Hit" + 0xAE38, "Trumpet" + 0xAE39, "Trombone" + 0xAE3A, "Tuba" + 0xAE3B, "Muted Trumpet" + 0xAE3C, "French Horn" + 0xAE3D, "Brass Section" + 0xAE3E, "Synth Brass 1" + 0xAE3F, "Synth Brass 2" + 0xAE40, "Soprano Sax" + 0xAE41, "Alto Sax" + 0xAE42, "Tenor Sax" + 0xAE43, "Baritone Sax" + 0xAE44, "Oboe" + 0xAE45, "English Horn" + 0xAE46, "Bassoon" + 0xAE47, "Clarinet" + 0xAE48, "Piccolo" + 0xAE49, "Flute" + 0xAE4A, "Recorder" + 0xAE4B, "Pan Flute" + 0xAE4C, "Bottle Blow" + 0xAE4D, "Shakuhachi" + 0xAE4E, "Whistle" + 0xAE4F, "Ocarina" + 0xAE50, "Lead 1 (square)" + 0xAE51, "Lead 2 (sawtooth)" + 0xAE52, "Lead 3 (caliope lead)" + 0xAE53, "Lead 4 (chiff lead)" + 0xAE54, "Lead 5 (charang)" + 0xAE55, "Lead 6 (voice)" + 0xAE56, "Lead 7 (fifths)" + 0xAE57, "87 Lead 8 (bass+lead) " + 0xAE58, "Pad 1 (new age)" + 0xAE59, "Pad 2 (warm)" + 0xAE5A, "Pad 3 (polysynth)" + 0xAE5B, "Pad 4 (choir)" + 0xAE5C, "Pad 5 (bowed)" + 0xAE5D, "Pad 6 (metallic)" + 0xAE5E, "Pad 7 (halo)" + 0xAE5F, "Pad 8 (sweep)" + 0xAE60, "FX 1 (rain)" + 0xAE61, "FX 2 (soundtrack)" + 0xAE62, "FX 3 (crystal)" + 0xAE63, "FX 4 (atmosphere)" + 0xAE64, "FX 5 (brightness)" + 0xAE65, "FX 6 (goblins)" + 0xAE66, "FX 7 (echoes)" + 0xAE67, "FX 8 (sci-fi)" + 0xAE68, "Sitar" + 0xAE69, "Banjo" + 0xAE6A, "Shamisen" + 0xAE6B, "Koto" + 0xAE6C, "Kalimba" + 0xAE6D, "Bagpipe" + 0xAE6E, "Fiddle" + 0xAE6F, "Shanai" + 0xAE70, "Tinkle Bell" + 0xAE71, "Agogo" + 0xAE72, "Steel Drums" + 0xAE73, "Woodblock" + 0xAE74, "Taiko Drum" + 0xAE75, "Melodic Tom" + 0xAE76, "Synth Drum" + 0xAE77, "Reverse Cymbal" + 0xAE78, "Guitar Fret Noise" + 0xAE79, "Breath Noise" + 0xAE7A, "Seashore" + 0xAE7B, "Bird Tweet" + 0xAE7C, "Telephone Ring" + 0xAE7D, "Helicopter" + 0xAE7E, "Applause" + 0xAE7F, "Gunshot" +END diff --git a/keymap.bmp b/keymap.bmp new file mode 100644 index 0000000..567402a Binary files /dev/null and b/keymap.bmp differ diff --git a/src/Keyboard.cpp b/src/Keyboard.cpp new file mode 100644 index 0000000..6caf683 --- /dev/null +++ b/src/Keyboard.cpp @@ -0,0 +1,22 @@ +#include +#include + +#pragma comment(lib, "comctl32.lib") + +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { + UNREFERENCED_PARAMETER(hInstance); + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + InitCommonControls(); + + MainWindow *win = MainWindow::Create(L"Music Keyboard"); + if (win) { + ShowWindow(win->GetHWND(), nCmdShow); + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + return 0; +} diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp new file mode 100644 index 0000000..09499ef --- /dev/null +++ b/src/MainWindow.cpp @@ -0,0 +1,395 @@ +#include + +#include +#include +#include + +#define LEFT(x, y, cx, cy) x, y, cx, cy +#define RIGHT(x, y, cx, cy) (x - cx), y, cx, cy +#define BOTTOM(x, y, cx, cy) x, (y - cy), cx, cy +#define BOTTOMRIGHT(x, y, cx, cy) (x - cx), (y - cy), cx, cy +#define KEYBOARD_IMAGE 0xAA00 +#define KEYBOARD_VOLUME 0xAA01 +#define KEYBOARD_FORCE 0xAA02 +#define KEYBOARD_INSTRUMENT 0xAA03 +#define MIDI_MESSAGE(handle, code, arg1, arg2) \ + midiOutShortMsg(handle, ((arg2 & 0x7F) << 16) |\ + ((arg1 & 0x7F) << 8) | (code & 0xFF)) + +#pragma comment(lib, "winmm.lib") + +DWORD rgbWindowBackground; + +static char keymap[256]; + +BOOL MainWindow::WinRegisterClass(WNDCLASS *pwc) +{ + pwc->style = CS_HREDRAW | CS_VREDRAW; + return __super::WinRegisterClass(pwc); +} + +BOOL EnableCloseButton(const HWND hwnd, const BOOL bState) +{ + HMENU hMenu; + UINT dwExtra; + + if (hwnd == NULL) + return FALSE; + if ((hMenu = GetSystemMenu(hwnd, FALSE)) == NULL) + return FALSE; + dwExtra = bState ? MF_ENABLED : (MF_DISABLED | MF_GRAYED); + return EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | dwExtra) != -1; +} + +LRESULT MainWindow::OnCreate() +{ + NONCLIENTMETRICS ncmMetrics = { sizeof(NONCLIENTMETRICS) }; + RECT client; + + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncmMetrics, 0); + GetClientRect(m_hwnd, &client); + + hFont = CreateFontIndirect(&ncmMetrics.lfMessageFont); + rgbWindowBackground = GetSysColor(COLOR_WINDOW); + hBrush = GetSysColorBrush(COLOR_WINDOW); + hKeyboardLayout = (HBITMAP) LoadImage(GetInstance(), MAKEINTRESOURCE(RID_KEYBOARD), + IMAGE_BITMAP, 0, 0, LR_LOADTRANSPARENT); + hdcKeyboard = CreateCompatibleDC(GetDC(m_hwnd)); + SelectBitmap(hdcKeyboard, hKeyboardLayout); + + // Children + m_volumeLabel = CreateWindow(WC_STATIC, L"Volume:", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 0, 0, 0, 0, + m_hwnd, NULL, GetInstance(), NULL); + m_forceLabel = CreateWindow(WC_STATIC, L"Force:", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 0, 0, 0, 0, + m_hwnd, NULL, GetInstance(), NULL); + m_instruLabel = CreateWindow(WC_STATIC, L"Instrument:", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 0, 0, 0, 0, + m_hwnd, NULL, GetInstance(), NULL); + + m_volumeBar = CreateWindow(TRACKBAR_CLASS, NULL, + WS_CHILD | WS_VISIBLE | TBS_NOTICKS, 0, 0, 0, 0, + m_hwnd, (HMENU) KEYBOARD_VOLUME, GetInstance(), NULL); + m_forceBar = CreateWindow(TRACKBAR_CLASS, NULL, + WS_CHILD | WS_VISIBLE | TBS_NOTICKS, 0, 0, 0, 0, + m_hwnd, (HMENU) KEYBOARD_FORCE, GetInstance(), NULL); + m_instruSelect = CreateWindow(WC_COMBOBOX, NULL, WS_CHILD | WS_VISIBLE | + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL, 0, 0, 0, 0, + m_hwnd, (HMENU) KEYBOARD_INSTRUMENT, GetInstance(), NULL); + + SendMessage(m_volumeBar, TBM_SETRANGEMIN, FALSE, 0x0000); + SendMessage(m_volumeBar, TBM_SETRANGEMAX, FALSE, 0xFFFF); + SendMessage(m_forceBar, TBM_SETRANGE, FALSE, 127 << 16); + SendMessage(m_volumeBar, TBM_SETPOS, FALSE, 0xFFFF); + SendMessage(m_forceBar, TBM_SETPOS, FALSE, 64); + m_force = 64; + m_volume = 0xFFFF; + + WCHAR buf[MAX_PATH]; + int piano; + SendMessage(m_instruSelect, CB_INITSTORAGE, 128, 4096); + for (int i = 0; i < 128; ++i) { + int id; + LoadString(GetInstance(), 0xAE00 | i, buf, MAX_PATH); + id = SendMessage(m_instruSelect, CB_ADDSTRING, 0, (LPARAM) buf); + SendMessage(m_instruSelect, CB_SETITEMDATA, id, i); + } + LoadString(GetInstance(), 0xAE00, buf, MAX_PATH); + piano = SendMessage(m_instruSelect, CB_FINDSTRING, 0, (LPARAM) buf); + SendMessage(m_instruSelect, CB_SETCURSEL, piano, 0); +#define SETFONT(hwnd) PostMessage(hwnd, WM_SETFONT, (WPARAM) hFont, (LPARAM) TRUE) + SETFONT(m_volumeLabel); + SETFONT(m_volumeBar); + SETFONT(m_forceLabel); + SETFONT(m_forceBar); + SETFONT(m_instruLabel); + SETFONT(m_instruSelect); +#undef SETFONT + + if (midiOutOpen(&m_midi, 0, NULL, NULL, CALLBACK_NULL) != MMSYSERR_NOERROR) + MessageBox(m_hwnd, L"Failed to open MIDI device!", L"Fatal Error", MB_ICONERROR); + + keymap[VK_OEM_3] = 54; // `~ key + keymap['Q'] = 55; + keymap[VK_TAB] = 55; + keymap['A'] = 57; + keymap['Z'] = 57; + keymap['W'] = 56; + keymap['E'] = 58; + keymap['S'] = 59; + keymap['X'] = 59; + keymap['D'] = 60; + keymap['C'] = 60; + keymap['R'] = 61; + keymap['F'] = 62; + keymap['V'] = 62; + keymap['T'] = 63; + keymap['G'] = 64; + keymap['B'] = 64; + keymap['H'] = 65; + keymap['N'] = 65; + keymap['U'] = 66; + keymap['J'] = 67; + keymap['M'] = 67; + keymap['I'] = 68; + keymap['K'] = 69; + keymap[VK_OEM_COMMA] = 69; + keymap['O'] = 70; + keymap['L'] = 71; + keymap[VK_OEM_PERIOD] = 71; + keymap[VK_OEM_1] = 72; // ;: + keymap[VK_OEM_2] = 72; // /? + keymap[VK_OEM_7] = 74; // '" + keymap[VK_OEM_4] = 73; // [ + keymap[VK_OEM_6] = 75; // ] + keymap[VK_RETURN] = 76; + keymap[VK_OEM_5] = 77; // \| + keymap[VK_BACK] = 77; + keymap[0x31] = 57; + keymap[0x32] = 59; + keymap[0x33] = 60; + keymap[0x34] = 62; + keymap[0x35] = 64; + keymap[0x36] = 65; + keymap[0x37] = 67; + keymap[0x38] = 69; + keymap[0x39] = 71; + keymap[0x30] = 72; + keymap[VK_OEM_MINUS] = 74; + keymap[VK_OEM_PLUS] = 76; + PostMessage(m_hwnd, WM_INPUTLANGCHANGE, 0, 0); + + SetTimer(m_hwnd, 0xDE00, 1000, NULL); + return 0; +} + +LRESULT MainWindow::OnDestroy() +{ + DestroyWindow(m_volumeLabel); + DestroyWindow(m_volumeBar); + DestroyWindow(m_forceLabel); + DestroyWindow(m_forceBar); + DestroyWindow(m_instruLabel); + DestroyWindow(m_instruSelect); + midiOutClose(m_midi); + return 0; +} + +HICON MainWindow::GetIcon() +{ + return LoadIcon(GetInstance(), MAKEINTRESOURCE(RID_ICON)); +} + +void MainWindow::PaintContent(PAINTSTRUCT *pps) +{ + BITMAP bm; + int cx, cy, tx, ty, x, y; + double ratio; + RECT client; + GetClientRect(m_hwnd, &client); + GetObject(hKeyboardLayout, sizeof(BITMAP), &bm); + cx = client.right - 24, cy = client.bottom - 117; + ratio = ((double) bm.bmWidth) / ((double) bm.bmHeight); + if (cx / ratio > cy) + tx = (int) (cy * ratio), ty = (int) cy; + else + tx = (int) cx, ty = (int) (cx / ratio); + x = 12 + (cx - tx) / 2; + y = 12 + (cy - ty) / 2; + StretchBlt(pps->hdc, x, y, tx, ty, hdcKeyboard, 0, 0, bm.bmWidth, bm.bmHeight, SRCAND); +} + +void MainWindow::OnPaint() +{ + PAINTSTRUCT ps; + BeginPaint(m_hwnd, &ps); + PaintContent(&ps); + EndPaint(m_hwnd, &ps); +} + +WORD MainWindow::GetQWERTYKeyCode(WORD wKeyCode) +{ + if (isQWERTY) + return wKeyCode; + UINT uiScan = MapVirtualKey(wKeyCode, MAPVK_VK_TO_VSC); + if (!uiScan) + return wKeyCode; + UINT uiQWERTY = MapVirtualKeyEx(uiScan, MAPVK_VSC_TO_VK, hklQWERTY); + if (uiScan) + return (WORD) uiQWERTY; + else + return wKeyCode; +} + +WORD MainWindow::GetRealKeyCode(WORD wQWERTYCode) +{ + if (isQWERTY) + return wQWERTYCode; + UINT uiScan = MapVirtualKeyEx(wQWERTYCode, MAPVK_VK_TO_VSC, hklQWERTY); + if (!uiScan) + return wQWERTYCode; + UINT uiKeyCode = MapVirtualKey(uiScan, MAPVK_VSC_TO_VK); + if (uiScan) + return (WORD) uiKeyCode; + else + return wQWERTYCode; +} + +int GetMIDINote(WPARAM wCode) +{ + int state = 0; + int note; + if (GetKeyState(VK_CONTROL) < 0) + state |= 0x001; + if (GetKeyState(VK_SHIFT) < 0) + state |= 0x010; + if (GetKeyState(VK_MENU) < 0) + state |= 0x100; + + note = keymap[wCode]; + switch (state) { + case 0x001: + note -= 24; + break; + case 0x010: + note += 24; + break; + case 0x100: + note += 48; + break; + } + return note; +} + +bool MainWindow::Play(WPARAM wCode, LPARAM lParam, bool down) +{ + int note; + wCode = GetQWERTYKeyCode((WORD) wCode); + if (wCode > 255 || !keymap[wCode] || (down && (lParam & 0x40000000))) + return false; + + note = GetMIDINote(wCode); + if (down) { + int num = note; + while (num > 24) + num -= 24; + while (num < 0x7F) { + if (num != note) + MIDI_MESSAGE(m_midi, 0x90, num, 0); + num += 24; + } + } + if (down) + MIDI_MESSAGE(m_midi, 0x90, note, m_force); + else + MIDI_MESSAGE(m_midi, 0x90, note, 0); + return true; +} + +LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_CREATE: + return OnCreate(); + case WM_DESTROY: + return OnDestroy(); + case WM_NCDESTROY: + PostQuitMessage(0); + break; + case WM_PAINT: + OnPaint(); + return 0; + case WM_SIZE: { + RECT client; + HDWP hdwp; + GetClientRect(m_hwnd, &client); +#define REPOS(hwnd, k) hdwp = DeferWindowPos(hdwp, hwnd, 0, k, SWP_NOACTIVATE|SWP_NOZORDER) + hdwp = BeginDeferWindowPos(14); + REPOS(m_volumeLabel, BOTTOM(12, client.bottom - 12, 70, 25)); + REPOS(m_volumeBar, BOTTOM(82, client.bottom - 12, client.right - 94, 25)); + REPOS(m_forceLabel, BOTTOM(12, client.bottom - 42, 70, 25)); + REPOS(m_forceBar, BOTTOM(82, client.bottom - 42, client.right - 94, 25)); + REPOS(m_instruLabel, BOTTOM(12, client.bottom - 72, 70, 25)); + REPOS(m_instruSelect, BOTTOM(82, client.bottom - 72, client.right - 94, 25)); + EndDeferWindowPos(hdwp); +#undef REPOS + return 0; + } + case WM_COMMAND: + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: + switch (LOWORD(wParam)) { + case KEYBOARD_INSTRUMENT: + m_instrument = SendMessage((HWND) lParam, CB_GETITEMDATA, + SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0), 0); + MIDI_MESSAGE(m_midi, 0xC0, m_instrument, 0); + } + } + break; + case WM_HSCROLL: + switch (LOWORD(wParam)) { + case TB_THUMBPOSITION: + if (lParam == (LPARAM) m_volumeBar) { + m_volume = HIWORD(wParam); + midiOutSetVolume(m_midi, m_volume); + } else if (lParam == (LPARAM) m_forceBar) + m_force = HIWORD(wParam); + break; + } + break; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (Play(wParam, lParam, true)) + return 0; + break; + case WM_KEYUP: + case WM_SYSKEYUP: + if (Play(wParam, lParam, false)) + return 0; + break; + case WM_LBUTTONDOWN: + SetFocus(m_hwnd); + return 0; + case WM_MOUSEWHEEL: + if (GetFocus() != m_forceBar) + SendMessage(m_forceBar, WM_MOUSEWHEEL, + -GET_WHEEL_DELTA_WPARAM(wParam) * 2 << 16 | + GET_KEYSTATE_WPARAM(wParam), lParam); + case WM_INPUTLANGCHANGE: { + TCHAR buf[KL_NAMELENGTH]; + GetKeyboardLayoutName(buf); + isQWERTY = lstrcmpi(buf, L"00000409") == 0; + if (!isQWERTY && !hklQWERTY) + hklQWERTY = LoadKeyboardLayout(L"00000409", KLF_NOTELLSHELL); + } + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + return 0; + case WM_ENTERSIZEMOVE: + SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_COMPOSITED); + return 0; + case WM_EXITSIZEMOVE: + SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) & ~WS_EX_COMPOSITED); + return 0; + } + return __super::HandleMessage(uMsg, wParam, lParam); +} + +MainWindow *MainWindow::Create(LPCTSTR szTitle) +{ + MainWindow *self = new MainWindow(); + RECT client = {0, 0, 622, 286}; + AdjustWindowRect(&client, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, FALSE); + if (self && + self->WinCreateWindow(0, + szTitle, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, + CW_USEDEFAULT, CW_USEDEFAULT, client.right - client.left, + client.bottom - client.top, NULL, NULL)) { + return self; + } + delete self; + return NULL; +} diff --git a/src/Window.cpp b/src/Window.cpp new file mode 100644 index 0000000..17815e5 --- /dev/null +++ b/src/Window.cpp @@ -0,0 +1,88 @@ +#include + +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "gdi32.lib") + +HWND Window::WinCreateWindow(DWORD dwExStyle, LPCTSTR pszName, + DWORD dwStyle, int x, int y, int cx, + int cy, HWND hwndParent, HMENU hmenu) +{ + Register(); + return CreateWindowEx(dwExStyle, ClassName(), pszName, dwStyle, + x, y, cx, cy, hwndParent, hmenu, GetInstance(), + this); +} + +Window::~Window() {} + +void Window::Register() +{ + WNDCLASS wc; + wc.style = 0; + wc.lpfnWndProc = Window::s_WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = GetInstance(); + wc.hIcon = GetIcon(); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = CreateSolidBrush(RGB(0xF0, 0xF0, 0xF0)); + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName(); + + WinRegisterClass(&wc); +} + +LRESULT CALLBACK +Window::s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + Window *self; + if (uMsg == WM_NCCREATE) { + LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam; + self = (Window *)(lpcs->lpCreateParams); + self->m_hwnd = hwnd; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) self); + } else { + self = (Window *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + } + if (self) { + return self->HandleMessage(uMsg, wParam, lParam); + } else { + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } +} + +LRESULT Window::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_NCDESTROY: { + LRESULT lres; + lres = DefWindowProc(m_hwnd, uMsg, wParam, lParam); + SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0); + delete this; + return lres; + } + case WM_PAINT: + OnPaint(); + return 0; + case WM_PRINTCLIENT: + OnPrintClient(reinterpret_cast(wParam)); + return 0; + } + return DefWindowProc(m_hwnd, uMsg, wParam, lParam); +} + +void Window::OnPaint() +{ + PAINTSTRUCT ps; + BeginPaint(m_hwnd, &ps); + PaintContent(&ps); + EndPaint(m_hwnd, &ps); +} + +void Window::OnPrintClient(HDC hdc) +{ + PAINTSTRUCT ps; + ps.hdc = hdc; + GetClientRect(m_hwnd, &ps.rcPaint); + PaintContent(&ps); +}