|
1 | 1 | #include <windows.h> |
2 | 2 | #include <iostream> |
3 | 3 | #include <conio.h> |
4 | | - |
5 | 4 | #include <stdexcept> |
6 | 5 | #include <locale.h> |
7 | 6 | #include <string.h> |
8 | 7 | #include <sstream> |
9 | | - |
10 | 8 | #include <map> |
| 9 | +#include <functional> |
11 | 10 | #include "UI.h" |
| 11 | +#include "UIExtensions.h" |
12 | 12 | #include "Config.h" |
13 | 13 |
|
14 | 14 | #pragma comment(lib, "winmm.lib") |
15 | 15 |
|
16 | | -bool isConfigured = false; |
17 | | -UI ui = UI("MIDI-device to keyboard mapper"); |
18 | | -Config config; |
19 | | - |
20 | | -char cmkc; // current midi key code |
21 | | -char cmkp; // is current midi key is pressed or released |
22 | | -int currentDevice; // current midi device |
23 | | -MMRESULT result; |
24 | | -HMIDIIN hMidiDevice; |
25 | | - |
26 | | -void CALLBACK MICallback(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { |
27 | | - if (wMsg == MIM_DATA) { |
28 | | - cmkc = (dwParam1 & 0x0000ff00) >> 8; // keycode |
29 | | - if (config.altcmkp == 0) cmkp = (dwParam1 & 0x00ff0000) >> 16; // key released if 0, else pressed |
30 | | - else if (config.altcmkp == 1) cmkp = ((dwParam1 & 0x00ff0000) >> 16) != 64; // key released if 64, else pressed |
31 | | - |
32 | | - if (isConfigured) { |
33 | | - if (cmkp) ui.print("Pressed MIDI #%d", +cmkc); |
34 | | - if (!cmkp) ui.print("Released MIDI #%d", +cmkc); |
35 | | - if(config.keymap[+cmkc] != 0) { |
36 | | - ui.printchars("\t mapped to KB #%c", config.keymap[+cmkc]); |
37 | | - |
38 | | - INPUT input; |
39 | | - input.type = INPUT_KEYBOARD; |
40 | | - input.ki.wScan = 0; |
41 | | - input.ki.time = 0; |
42 | | - input.ki.dwExtraInfo = 0; |
43 | | - input.ki.wVk = config.keymap[+cmkc]; |
44 | | - input.ki.dwFlags = cmkp ? 0 : KEYEVENTF_KEYUP; |
45 | | - |
46 | | - SendInput(1, &input, sizeof(INPUT)); |
47 | | - } else { |
48 | | - std::cout << std::endl; |
49 | | - } |
50 | | - } |
51 | | - } |
52 | | - return; |
53 | | -} |
54 | | - |
55 | | -struct Keymapper { |
56 | | - Keymapper () { |
57 | | - currentDevice = 0; |
58 | | - cmkc = 0; |
59 | | - cmkp = 0; |
60 | | - hMidiDevice = NULL; |
61 | | - } |
62 | | - |
63 | | - ~Keymapper () { |
64 | | - midiInStop(hMidiDevice); |
65 | | - midiInClose(hMidiDevice); |
66 | | - hMidiDevice = NULL; |
67 | | - } |
68 | | - |
69 | | - void selectActiveDevice() { |
70 | | - currentDevice = 0; |
| 16 | +class Keymapper { |
| 17 | + public: |
| 18 | + UI ui = UI("MIDI-device to keyboard mapper"); |
| 19 | + Config config; |
71 | 20 |
|
72 | | - int numDevs = midiInGetNumDevs(); |
73 | | - if (numDevs == 0) { |
74 | | - ui.print("No MIDI-devices detected"); |
75 | | - throw std::invalid_argument("No MIDI-devices detected"); |
| 21 | + int currentDevice; // current midi device |
| 22 | + char cmkc; // current midi key code |
| 23 | + char cmkp; // is current midi key is pressed or released |
| 24 | + HMIDIIN hMidiDevice; |
| 25 | + bool mapping; |
| 26 | + |
| 27 | + Keymapper () { |
| 28 | + currentDevice = -1; |
| 29 | + cmkc = 0; |
| 30 | + cmkp = 0; |
| 31 | + hMidiDevice = NULL; |
| 32 | + mapping = false; |
| 33 | + } |
76 | 34 |
|
77 | | - return; |
| 35 | + ~Keymapper () { |
| 36 | + mapping = false; |
| 37 | + midiInStop(hMidiDevice); |
| 38 | + midiInClose(hMidiDevice); |
| 39 | + hMidiDevice = NULL; |
| 40 | + config.~Config(); |
| 41 | + ui.~UI(); |
78 | 42 | } |
79 | | - |
80 | | - if (numDevs > 1) { |
81 | | - ui.print("Multiple MIDI-devices detected"); |
82 | | - MIDIINCAPS cur; |
83 | | - for (size_t i=0; i<numDevs; i++) { |
84 | | - midiInGetDevCaps(i, &cur, sizeof(MIDIINCAPS)); |
85 | | - ui.print("#%d: %s", i, cur.szPname); |
86 | | - } |
87 | 43 |
|
88 | | - int choice = -1; |
89 | | - int input; |
90 | | - int selectedNumber; |
91 | | - std::stringstream inputStream; |
92 | | - while (choice >= numDevs || choice < 0) { |
93 | | - ui.print("Enter valid device number: "); |
94 | | - choice = -1; |
95 | | - inputStream.clear(); |
96 | | - inputStream.str(""); |
97 | | - while((input = _getch()) != VK_RETURN) { |
98 | | - selectedNumber = input - '0'; |
99 | | - if (selectedNumber < 0 || selectedNumber > 9) continue; |
100 | | - inputStream << std::to_string(selectedNumber); |
101 | | - ui.printchars(std::to_string(selectedNumber).c_str()); |
| 44 | + void selectActiveDevice() { |
| 45 | + currentDevice = -1; |
| 46 | + int numDevs = midiInGetNumDevs(); |
| 47 | + if (numDevs == 0) { |
| 48 | + throw std::invalid_argument("No MIDI-devices detected"); |
| 49 | + } else if (numDevs == 1) { |
| 50 | + currentDevice = 0; |
| 51 | + } else { |
| 52 | + ui.print("Multiple MIDI-devices detected"); |
| 53 | + MIDIINCAPS cur; |
| 54 | + for (size_t i = 0; i < numDevs; i++) { |
| 55 | + midiInGetDevCaps(i, &cur, sizeof(MIDIINCAPS)); |
| 56 | + ui.print("#%d: %s", i, cur.szPname); |
| 57 | + } |
| 58 | + int choice = -1; |
| 59 | + int input; |
| 60 | + int selectedNumber; |
| 61 | + std::stringstream inputStream; |
| 62 | + while (choice >= numDevs || choice < 0) { |
| 63 | + ui.print("Enter valid device number: "); |
| 64 | + choice = -1; |
| 65 | + inputStream.clear(); |
| 66 | + inputStream.str(""); |
| 67 | + while((input = _getch()) != VK_RETURN) { |
| 68 | + selectedNumber = input - '0'; |
| 69 | + if (selectedNumber < 0 || selectedNumber > 9) continue; |
| 70 | + inputStream << std::to_string(selectedNumber); |
| 71 | + ui.printchars(std::to_string(selectedNumber).c_str()); |
| 72 | + } |
| 73 | + inputStream >> choice; |
102 | 74 | } |
103 | | - inputStream >> choice; |
| 75 | + currentDevice = choice; |
104 | 76 | } |
105 | | - |
106 | | - currentDevice = choice; |
| 77 | + MIDIINCAPS cur; |
| 78 | + midiInGetDevCaps(currentDevice, &cur, sizeof(MIDIINCAPS)); |
| 79 | + ui.print("Current device: %s", cur.szPname); |
107 | 80 | } |
108 | 81 |
|
109 | | - MIDIINCAPS cur; |
110 | | - midiInGetDevCaps(currentDevice, &cur, sizeof(MIDIINCAPS)); |
111 | | - ui.print("Current device: %s", cur.szPname); |
112 | | - |
113 | | - return; |
114 | | - } |
115 | | - |
116 | | - void printConfig() { |
117 | | - ui.print("Current keymap:"); |
118 | | - for(size_t i = 0; i < 255; ++i) { |
119 | | - if (config.keymap[i] != 0) { |
120 | | - ui.print("\t MIDI #%d : KB #%d", i, config.keymap[i]); |
| 82 | + void printConfig() { |
| 83 | + ui.print("Current keymap:"); |
| 84 | + for(size_t i = 0; i < 255; ++i) { |
| 85 | + if (config.keymap[i] != 0) { |
| 86 | + ui.print("\t MIDI #%d : KB #%d", i, config.keymap[i]); |
| 87 | + } |
| 88 | + } |
| 89 | + ui.print("Current config:"); |
| 90 | + for(const auto &fieldPair : config.repr()) { |
| 91 | + ui.print("\t%s:\t%s", fieldPair.first.c_str(), fieldPair.second.c_str()); |
121 | 92 | } |
122 | 93 | } |
123 | | - ui.print("Current config:"); |
124 | | - for(const auto &fieldPair : config.getEntries()) { |
125 | | - ui.print("\t%s:\t%s", fieldPair.first.c_str(), fieldPair.second.c_str()); |
| 94 | + |
| 95 | + void changeSettings() { |
| 96 | + config.altcmkp = question(ui, "Use alternative key release detection? (y/n)"); |
| 97 | + config.save(); |
126 | 98 | } |
127 | | - return; |
128 | | - } |
129 | 99 |
|
130 | | - void changeSettings() { |
131 | | - char vkCode; |
132 | | - ui.print("Use alternative key release detection? Type \"Y\" or \"y\" to confirm"); |
133 | | - vkCode = _getch(); |
134 | | - config.altcmkp = (vkCode == 'Y' || vkCode == 'y'); |
135 | | - config.save(); |
136 | | - return; |
137 | | - } |
| 100 | + void reassignKeymap() { |
| 101 | + char midiKey; |
| 102 | + char vkCode; |
| 103 | + ui.print("To stop press Escape"); |
| 104 | + ui.print("Press key to be emulated on the keyboard, then MIDI-key"); |
| 105 | + while (true) { |
| 106 | + vkCode = _getch(); |
| 107 | + if (vkCode == VK_ESCAPE) break; |
| 108 | + else { |
| 109 | + const char* name = setlocale(LC_ALL, ""); |
| 110 | + std::setlocale(LC_ALL, name); |
| 111 | + vkCode = std::toupper(vkCode); |
| 112 | + ui.print("KB: %d \t", vkCode); |
| 113 | + cmkc = 0; |
| 114 | + while (cmkc == 0) { |
| 115 | + Sleep(100); |
| 116 | + } |
| 117 | + midiKey = cmkc; |
| 118 | + ui.printchars("MIDI: %d", midiKey); |
| 119 | + config.keymap[+midiKey] = +vkCode; |
| 120 | + } |
| 121 | + Sleep(100); |
| 122 | + } |
| 123 | + config.save(); |
| 124 | + return; |
| 125 | + } |
138 | 126 |
|
139 | | - void reassignKeymap() { |
140 | | - char midiKey; |
141 | | - char vkCode; |
142 | | - ui.print("To stop press Escape"); |
143 | | - ui.print("Press key to be emulated on the keyboard, then MIDI-key"); |
144 | | - while (true) { |
145 | | - Sleep(100); |
146 | | - vkCode = _getch(); |
147 | | - if (vkCode == VK_ESCAPE) break; |
148 | | - else { |
149 | | - const char* name = setlocale(LC_ALL, ""); |
150 | | - std::setlocale(LC_ALL, name); |
151 | | - vkCode = std::toupper(vkCode); |
152 | | - ui.print("KB: %d \t", vkCode); |
153 | | - cmkc = 0; |
154 | | - while (cmkc == 0) { |
155 | | - Sleep(100); |
| 127 | + static void CALLBACK MICallback(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { |
| 128 | + Keymapper* keymapper = reinterpret_cast <Keymapper*>(dwInstance); |
| 129 | + if (wMsg == MIM_DATA) { |
| 130 | + keymapper->cmkc = (dwParam1 & 0x0000ff00) >> 8; // keycode |
| 131 | + if (keymapper->config.altcmkp == 0) keymapper->cmkp = (dwParam1 & 0x00ff0000) >> 16; // key released if 0, else pressed |
| 132 | + else if (keymapper->config.altcmkp == 1) keymapper->cmkp = ((dwParam1 & 0x00ff0000) >> 16) != 64; // key released if 64, else pressed |
| 133 | + if (keymapper->mapping) { |
| 134 | + if (keymapper->cmkp) keymapper->ui.print("Pressed MIDI #%d", +keymapper->cmkc); |
| 135 | + if (!keymapper->cmkp) keymapper->ui.print("Released MIDI #%d", +keymapper->cmkc); |
| 136 | + if(keymapper->config.keymap[+keymapper->cmkc] != 0) { |
| 137 | + keymapper->ui.printchars("\t mapped to KB #%c", keymapper->config.keymap[+keymapper->cmkc]); |
| 138 | + |
| 139 | + INPUT input; |
| 140 | + input.type = INPUT_KEYBOARD; |
| 141 | + input.ki.wScan = 0; |
| 142 | + input.ki.time = 0; |
| 143 | + input.ki.dwExtraInfo = 0; |
| 144 | + input.ki.wVk = config.keymap[+keymapper->cmkc]; |
| 145 | + input.ki.dwFlags = cmkp ? 0 : KEYEVENTF_KEYUP; |
| 146 | + |
| 147 | + SendInput(1, &input, sizeof(INPUT)); |
| 148 | + } else { |
| 149 | + std::cout << std::endl; |
| 150 | + } |
156 | 151 | } |
157 | | - midiKey = cmkc; |
158 | | - ui.printchars("MIDI: %d", midiKey); |
159 | | - config.keymap[+midiKey] = +vkCode; |
160 | 152 | } |
161 | 153 | } |
162 | | - config.save(); |
163 | | - return; |
164 | | - } |
165 | 154 |
|
166 | | - void listen() { |
167 | | - result = midiInOpen(&hMidiDevice, currentDevice, (DWORD)(void*)MICallback, 0, CALLBACK_FUNCTION); |
168 | | - midiInStart(hMidiDevice); |
169 | | - } |
| 155 | + void listen() { |
| 156 | + MMRESULT result = midiInOpen(&hMidiDevice, currentDevice, (DWORD_PTR)(void*)&Keymapper::MICallback, reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION); |
| 157 | + midiInStart(hMidiDevice); |
| 158 | + } |
170 | 159 | }; |
171 | 160 |
|
172 | 161 | int main() { |
173 | | - config = Config(); |
174 | | - config.load(); |
175 | 162 | Keymapper keymapper = Keymapper(); |
176 | | - keymapper.selectActiveDevice(); |
| 163 | + keymapper.config.use("config.ini", "keymap.cfg"); |
| 164 | + keymapper.config.load(); |
| 165 | + |
| 166 | + while (keymapper.currentDevice == -1) { |
| 167 | + try { |
| 168 | + keymapper.selectActiveDevice(); |
| 169 | + } catch (std::invalid_argument const& e) { |
| 170 | + keymapper.ui.print("No MIDI-devices detected"); |
| 171 | + Sleep(100); |
| 172 | + } |
| 173 | + } |
177 | 174 | keymapper.printConfig(); |
178 | 175 | keymapper.listen(); |
179 | 176 |
|
180 | | - ui.print("Change settings? Type \"Y\" or \"y\" to confirm"); |
181 | | - char changeSettings = _getch(); |
182 | | - if (changeSettings == 'Y' || changeSettings == 'y') { |
183 | | - keymapper.changeSettings(); |
184 | | - } |
185 | | - |
186 | | - ui.print("Reassign mappings? Type \"Y\" or \"y\" to confirm"); |
187 | | - char reassign = _getch(); |
188 | | - if (reassign == 'Y' || reassign == 'y') { |
189 | | - keymapper.reassignKeymap(); |
190 | | - } |
191 | | - |
192 | | - isConfigured = true; |
193 | | - |
194 | | - ui.print("Started mapping..."); |
195 | | - while (true) { |
196 | | - int c = _getch(); |
197 | | - if (c == VK_ESCAPE) break; |
198 | | - } |
| 177 | + if (question(keymapper.ui, "Change settings? (y/n)")) keymapper.changeSettings(); |
| 178 | + if (question(keymapper.ui, "Reassign mappings? (y/n)")) keymapper.reassignKeymap(); |
199 | 179 |
|
| 180 | + keymapper.mapping = true; |
| 181 | + expect(keymapper.ui, "Started mapping... Press ESC to quit.", VK_ESCAPE); |
| 182 | + |
200 | 183 | keymapper.~Keymapper(); |
201 | | - config.~Config(); |
202 | | - ui.~UI(); |
203 | 184 | return 0; |
204 | 185 | } |
0 commit comments