1 module test1.virtmidikeyb;
2 
3 import pixelperfectengine.concrete.window; 
4 import pixelperfectengine.system.input.types;
5 import pixelperfectengine.system.etc : hashCalc;
6 import midi2.types.structs;
7 import midi2.types.enums;
8 import test1.app;
9 import std.conv : to;
10 
11 public class VirtualMidiKeyboard : Window {
12 	SmallButton[]	keys;
13 	TextBox			textBox_NoteOffset;
14 	TextBox			textBox_Channel;
15 	TextBox			textBox_PrgChange;
16 	ubyte			noteOffset = 36;
17 	ubyte			channel;
18 	bool			typematicBlock;
19 	AudioDevKit		app;
20 	public this(AudioDevKit app) {
21 		this.app = app;
22 		super(Box.bySize(0,0,200,85), "Virtual MIDI keyboard");
23 		for (int i ; i < 20 ; i++) {
24 			Box b;
25 			const int note = i % 12, oct = i / 12;
26 			switch (note) {
27 				case 1, 3, 6, 8, 10:
28 					b.top = 20;
29 					b.bottom = 20 + 15;
30 					break;
31 				default:
32 					b.top = 20 + 16;
33 					b.bottom = 20 + 16 + 15;
34 					break;
35 			}
36 			switch (note) {
37 				default:	//c
38 					b.left = 4 + (oct * 112);
39 					b.right = 4 + 15 + (oct * 112);
40 					break;
41 				case 1:		//c#
42 					b.left = 4 + (oct * 112) + 8;
43 					b.right = 4 + 15 + (oct * 112) + 8;
44 					break;
45 				case 2:		//d
46 					b.left = 4 + (oct * 112) + (16 * 1);
47 					b.right = 4 + 15 + (oct * 112) + (16 * 1);
48 					break;
49 				case 3:		//d#
50 					b.left = 4 + (oct * 112) + 8 + (16 * 1);
51 					b.right = 4 + 15 + (oct * 112) + 8 + (16 * 1);
52 					break;
53 				case 4:		//e
54 					b.left = 4 + (oct * 112) + (16 * 2);
55 					b.right = 4 + 15 + (oct * 112) + (16 * 2);
56 					break;
57 				case 5:		//f
58 					b.left = 4 + (oct * 112) + (16 * 3);
59 					b.right = 4 + 15 + (oct * 112) + (16 * 3);
60 					break;
61 				case 6:		//f#
62 					b.left = 4 + (oct * 112) + 8 + (16 * 3);
63 					b.right = 4 + 15 + (oct * 112) + 8 + (16 * 3);
64 					break;
65 				case 7:		//g
66 					b.left = 4 + (oct * 112) + (16 * 4);
67 					b.right = 4 + 15 + (oct * 112) + (16 * 4);
68 					break;
69 				case 8:		//g#
70 					b.left = 4 + (oct * 112) + 8 + (16 * 4);
71 					b.right = 4 + 15 + (oct * 112) + 8 + (16 * 4);
72 					break;
73 				case 9:		//a
74 					b.left = 4 + (oct * 112) + (16 * 5);
75 					b.right = 4 + 15 + (oct * 112) + (16 * 5);
76 					break;
77 				case 10:	//a#
78 					b.left = 4 + (oct * 112) + 8 + (16 * 5);
79 					b.right = 4 + 15 + (oct * 112) + 8 + (16 * 5);
80 					break;
81 				case 11:	//b
82 					b.left = 4 + (oct * 112) + (16 * 6);
83 					b.right = 4 + 15 + (oct * 112) + (16 * 6);
84 					break;
85 			}
86 			SmallButton sb = new SmallButton("closeButtonB", "closeButtonA", [cast(char)i], b);
87 			sb.mousePressEvent = true;
88 			sb.onMouseLClick = &onKey!(0xFFFF_0000);
89 			sb.onMouseMClick = &onKey!(0x7FFF_0000);
90 			sb.onMouseRClick = &onKey!(0x3FFF_0000);
91 			addElement(sb);
92 			keys ~= sb;
93 		}
94 		textBox_NoteOffset = new TextBox("36", "textBox_NoteOffset", Box(5, 25+16+16 , 45, 25+16+16+20));
95 		textBox_Channel = new TextBox("0", "textBox_Channel", Box(50, 25+16+16 , 95, 25+16+16+20));
96 		textBox_PrgChange = new TextBox("PrgCh", "textBox_PrgChange", Box(100, 25+16+16 , 170, 25+16+16+20));
97 		addElement(textBox_NoteOffset);
98 		addElement(textBox_Channel);
99 		addElement(textBox_PrgChange);
100 		textBox_NoteOffset.onTextInput = &textBox_NoteOffset_onTextInput;
101 		textBox_Channel.onTextInput = &textBox_Channel_onTextInput;
102 		textBox_PrgChange.onTextInput = &textBox_PrgChange_onTextInput;
103 		onClose = &app.onVirtMIDIKeybClose;
104 	}
105 	public int keyEventReceive(uint id, BindingCode code, uint timestamp, bool isPressed) {
106 		if (!active)
107 			return 0;
108 		if (isPressed) {
109 			if (typematicBlock) {
110 				return 1;
111 			}
112 			typematicBlock = true;
113 		} else {
114 			typematicBlock = false;
115 		}
116 		ubyte note;
117 		switch (id) {
118 			case hashCalc("VirtMIDIKB-C-0"):
119 				note = 0;
120 				break;
121 			case hashCalc("VirtMIDIKB-C#0"):
122 				note = 1;
123 				break;
124 			case hashCalc("VirtMIDIKB-D-0"):
125 				note = 2;
126 				break;
127 			case hashCalc("VirtMIDIKB-D#0"):
128 				note = 3;
129 				break;
130 			case hashCalc("VirtMIDIKB-E-0"):
131 				note = 4;
132 				break;
133 			case hashCalc("VirtMIDIKB-F-0"):
134 				note = 5;
135 				break;
136 			case hashCalc("VirtMIDIKB-F#0"):
137 				note = 6;
138 				break;
139 			case hashCalc("VirtMIDIKB-G-0"):
140 				note = 7;
141 				break;
142 			case hashCalc("VirtMIDIKB-G#0"):
143 				note = 8;
144 				break;
145 			case hashCalc("VirtMIDIKB-A-0"):
146 				note = 9;
147 				break;
148 			case hashCalc("VirtMIDIKB-A#0"):
149 				note = 10;
150 				break;
151 			case hashCalc("VirtMIDIKB-B-0"):
152 				note = 11;
153 				break;
154 			case hashCalc("VirtMIDIKB-C-1"):
155 				note = 12;
156 				break;
157 			case hashCalc("VirtMIDIKB-C#1"):
158 				note = 13;
159 				break;
160 			case hashCalc("VirtMIDIKB-D-1"):
161 				note = 14;
162 				break;
163 			case hashCalc("VirtMIDIKB-D#1"):
164 				note = 15;
165 				break;
166 			case hashCalc("VirtMIDIKB-E-1"):
167 				note = 16;
168 				break;
169 			case hashCalc("VirtMIDIKB-F-1"):
170 				note = 17;
171 				break;
172 			case hashCalc("VirtMIDIKB-F#1"):
173 				note = 18;
174 				break;
175 			case hashCalc("VirtMIDIKB-G-1"):
176 				note = 19;
177 				break;
178 			case hashCalc("VirtMIDIKB-oct+"):
179 				if (!isPressed) {
180 					noteOffset += 12;
181 					textBox_NoteOffset.setText(to!dstring(noteOffset));
182 				}
183 				return 1;
184 			case hashCalc("VirtMIDIKB-oct-"):
185 				if (!isPressed) {
186 					noteOffset -= 12;
187 					textBox_NoteOffset.setText(to!dstring(noteOffset));
188 				}
189 				return 1;
190 			case hashCalc("VirtMIDIKB-note+"):
191 				if (!isPressed) {
192 					noteOffset++;
193 					textBox_NoteOffset.setText(to!dstring(noteOffset));
194 				}
195 				return 1;
196 			case hashCalc("VirtMIDIKB-note-"):
197 				if (!isPressed) {
198 					noteOffset--;
199 					textBox_NoteOffset.setText(to!dstring(noteOffset));
200 				}
201 				return 1;
202 			default:
203 				return 0;
204 		}
205 		UMP midiPacket = UMP(MessageType.MIDI2, cast(ubyte)(channel>>4), isPressed ? MIDI2_0Cmd.NoteOn : MIDI2_0Cmd.NoteOff, 
206 				cast(ubyte)(channel&0x0F), cast(ubyte)(note + noteOffset), 0x00);
207 		uint velo = 0xFFFF_0000;
208 		if (app.selectedModule !is null) {
209 			app.selectedModule.midiReceive(midiPacket, velo);
210 		}
211 		return 1;
212 	}
213 	protected void onKey(uint velo)(Event ev) {
214 		SmallButton sender = cast(SmallButton)ev.sender;
215 		ubyte note = cast(ubyte)(noteOffset + sender.getSource[0]);
216 		UMP midiPacket = UMP(MessageType.MIDI2, cast(ubyte)(channel>>4), sender.isPressed ? MIDI2_0Cmd.NoteOn : 
217 				MIDI2_0Cmd.NoteOff, cast(ubyte)(channel&0x0F), note, 0x00);
218 		//uint velo = 0xFFFF_0000;
219 		if (app.selectedModule !is null) {
220 			app.selectedModule.midiReceive(midiPacket, velo);
221 		}
222 	}
223 	protected void textBox_PrgChange_onTextInput(Event ev) {
224 		dstring text = textBox_PrgChange.getText().toDString();
225 		if (text.length & 1 || text.length >= 7) {
226 			textBox_PrgChange.setText("Error!");
227 			return;
228 		}
229 		try {
230 			uint bankMSB, bankLSB, prg;
231 			if (text.length == 6) {
232 				bankMSB = to!uint(text[0..2], 16);
233 				text = text[2..$];
234 			}
235 			if (text.length == 4) {
236 				bankLSB = to!uint(text[0..2], 16);
237 				text = text[2..$];
238 			}
239 			prg = to!uint(text, 16);
240 			UMP midiPacket = UMP(MessageType.MIDI2, cast(ubyte)(channel>>4), MIDI2_0Cmd.PrgCh, 
241 					cast(ubyte)(channel&0x0F), 0x00, 0x00);
242 			uint velo = (prg<<24) | (bankMSB<<8) | bankLSB;
243 			if (app.selectedModule !is null) {
244 				app.selectedModule.midiReceive(midiPacket, velo);
245 			}
246 		} catch (Exception e) {
247 			textBox_PrgChange.setText("Error!");
248 		}
249 	}
250 	protected void textBox_NoteOffset_onTextInput(Event ev) {
251 		try {
252 			noteOffset = to!ubyte(textBox_NoteOffset.getText().toDString());
253 		} catch (Exception e) {
254 			textBox_NoteOffset.setText("Error!");
255 		}
256 	}
257 	protected void textBox_Channel_onTextInput(Event ev) {
258 		try {
259 			channel = to!ubyte(textBox_Channel.getText().toDString());
260 		} catch (Exception e) {
261 			textBox_Channel.setText("Error!");
262 		}
263 	}
264 }