andrewm@0
|
1 /*
|
andrewm@0
|
2 TouchKeys: multi-touch musical keyboard control software
|
andrewm@0
|
3 Copyright (c) 2013 Andrew McPherson
|
andrewm@0
|
4
|
andrewm@0
|
5 This program is free software: you can redistribute it and/or modify
|
andrewm@0
|
6 it under the terms of the GNU General Public License as published by
|
andrewm@0
|
7 the Free Software Foundation, either version 3 of the License, or
|
andrewm@0
|
8 (at your option) any later version.
|
andrewm@0
|
9
|
andrewm@0
|
10 This program is distributed in the hope that it will be useful,
|
andrewm@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
andrewm@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
andrewm@0
|
13 GNU General Public License for more details.
|
andrewm@0
|
14
|
andrewm@0
|
15 You should have received a copy of the GNU General Public License
|
andrewm@0
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
andrewm@0
|
17
|
andrewm@0
|
18 =====================================================================
|
andrewm@0
|
19
|
andrewm@0
|
20 KeyboardDisplay.cpp: displays the keyboard state, including active MIDI
|
andrewm@0
|
21 notes and current touch position and size.
|
andrewm@0
|
22 */
|
andrewm@0
|
23
|
andrewm@0
|
24 #include "KeyboardDisplay.h"
|
andrewm@27
|
25 #include "OpenGLJuceCanvas.h"
|
andrewm@0
|
26 #include <iostream>
|
andrewm@0
|
27 #include <cmath>
|
andrewm@0
|
28
|
andrewm@0
|
29 // Class constants
|
andrewm@0
|
30
|
andrewm@0
|
31 const float KeyboardDisplay::kWhiteKeyFrontWidth = 1.0;
|
andrewm@0
|
32 const float KeyboardDisplay::kBlackKeyWidth = 0.5;
|
andrewm@0
|
33 const float KeyboardDisplay::kWhiteKeyFrontLength = 2.3;
|
andrewm@0
|
34 const float KeyboardDisplay::kWhiteKeyBackLength = 4.1;
|
andrewm@0
|
35 const float KeyboardDisplay::kBlackKeyLength = 4.0;
|
andrewm@0
|
36 const float KeyboardDisplay::kInterKeySpacing = 0.1;
|
andrewm@0
|
37 const float KeyboardDisplay::kAnalogSliderVerticalSpacing = 0.2;
|
andrewm@0
|
38 const float KeyboardDisplay::kAnalogSliderLength = 3.0;
|
andrewm@0
|
39 const float KeyboardDisplay::kAnalogSliderWidth = 0.4;
|
andrewm@0
|
40 const float KeyboardDisplay::kAnalogSliderMinimumValue = -0.2;
|
andrewm@0
|
41 const float KeyboardDisplay::kAnalogSliderMaximumValue = 1.2;
|
andrewm@0
|
42 const float KeyboardDisplay::kAnalogSliderZeroLocation = kAnalogSliderLength * (0.0 - kAnalogSliderMinimumValue) / (kAnalogSliderMaximumValue - kAnalogSliderMinimumValue);
|
andrewm@0
|
43 const float KeyboardDisplay::kAnalogSliderOneLocation = kAnalogSliderLength * (1.0 - kAnalogSliderMinimumValue) / (kAnalogSliderMaximumValue - kAnalogSliderMinimumValue);
|
andrewm@0
|
44
|
andrewm@0
|
45 // Individual geometry for C, D, E, F, G, A, B, c'
|
andrewm@0
|
46
|
andrewm@0
|
47 const float KeyboardDisplay::kWhiteKeyBackOffsets[9] = {0, 0.22, 0.42, 0, 0.14, 0.3, 0.44, 0.22, 0};
|
andrewm@0
|
48 const float KeyboardDisplay::kWhiteKeyBackWidths[9] = {0.6, 0.58, 0.58, 0.56, 0.56, 0.56, 0.56, 0.58, 1.0};
|
andrewm@0
|
49
|
andrewm@0
|
50 // Display margins
|
andrewm@0
|
51
|
andrewm@0
|
52 const float KeyboardDisplay::kDisplaySideMargin = 0.4;
|
andrewm@0
|
53 const float KeyboardDisplay::kDisplayBottomMargin = 0.8;
|
andrewm@0
|
54 const float KeyboardDisplay::kDisplayTopMargin = 0.8;
|
andrewm@0
|
55
|
andrewm@0
|
56 // Key shape constants
|
andrewm@0
|
57
|
andrewm@0
|
58 const int KeyboardDisplay::kShapeForNote[12] = {0, -1, 1, -1, 2, 3, -1, 4, -1, 5, -1, 6};
|
andrewm@0
|
59 const int KeyboardDisplay::kWhiteToChromatic[7] = {0, 2, 4, 5, 7, 9, 11};
|
andrewm@0
|
60 const float KeyboardDisplay::kWhiteKeyFrontBackCutoff = (6.5 / 19.0);
|
andrewm@0
|
61
|
andrewm@0
|
62 // Touch constants
|
andrewm@0
|
63 const float KeyboardDisplay::kDisplayMinTouchSize = 0.1;
|
andrewm@0
|
64 const float KeyboardDisplay::kDisplayTouchSizeScaler = 0.5;
|
andrewm@0
|
65
|
andrewm@27
|
66 KeyboardDisplay::KeyboardDisplay() : canvas_(0), lowestMidiNote_(0), highestMidiNote_(0),
|
andrewm@0
|
67 totalDisplayWidth_(1.0), totalDisplayHeight_(1.0), displayPixelWidth_(1.0), displayPixelHeight_(1.0),
|
andrewm@27
|
68 currentHighlightedKey_(-1), touchSensingEnabled_(false), analogSensorsPresent_(false) {
|
andrewm@0
|
69 // Initialize OpenGL settings: 2D only
|
andrewm@0
|
70
|
andrewm@0
|
71 //glMatrixMode(GL_PROJECTION);
|
andrewm@0
|
72 //glDisable(GL_DEPTH_TEST);
|
andrewm@0
|
73
|
andrewm@0
|
74 clearAllTouches();
|
andrewm@0
|
75 for(int i = 0; i < 128; i++)
|
andrewm@0
|
76 midiActiveForKey_[i] = false;
|
andrewm@44
|
77
|
andrewm@44
|
78 recalculateKeyDivisions();
|
andrewm@0
|
79 }
|
andrewm@0
|
80
|
andrewm@27
|
81 // Tell the underlying canvas to repaint itself
|
andrewm@27
|
82 void KeyboardDisplay::tellCanvasToRepaint() {
|
andrewm@27
|
83 if(canvas_ != 0)
|
andrewm@27
|
84 canvas_->triggerRepaint();
|
andrewm@27
|
85 }
|
andrewm@27
|
86
|
andrewm@0
|
87 void KeyboardDisplay::setKeyboardRange(int lowest, int highest) {
|
andrewm@0
|
88 if(lowest < 0 || highest < 0)
|
andrewm@0
|
89 return;
|
andrewm@0
|
90
|
andrewm@0
|
91 ScopedLock sl(displayMutex_);
|
andrewm@0
|
92
|
andrewm@0
|
93 lowestMidiNote_ = lowest;
|
andrewm@0
|
94 if(keyShape(lowest) < 0) // Lowest key must always be a white key for display to
|
andrewm@0
|
95 lowest++; // render properly
|
andrewm@0
|
96
|
andrewm@0
|
97 highestMidiNote_ = highest;
|
andrewm@0
|
98
|
andrewm@0
|
99 // Recalculate relevant display parameters
|
andrewm@0
|
100 // Display size is based on the number of white keys
|
andrewm@0
|
101
|
andrewm@0
|
102 int numKeys = 0;
|
andrewm@0
|
103 for(int i = lowestMidiNote_; i <= highestMidiNote_; i++) {
|
andrewm@0
|
104 if(keyShape(i) >= 0)
|
andrewm@0
|
105 numKeys++;
|
andrewm@0
|
106 if(i >= 0 && i < 128) {
|
andrewm@0
|
107 analogValueForKey_[i] = 0.0;
|
andrewm@0
|
108 analogValueIsCalibratedForKey_[i] = false;
|
andrewm@0
|
109 }
|
andrewm@0
|
110 }
|
andrewm@0
|
111
|
andrewm@0
|
112 if(numKeys == 0) {
|
andrewm@0
|
113 return;
|
andrewm@0
|
114 }
|
andrewm@0
|
115
|
andrewm@0
|
116 // Width: N keys, N-1 interkey spaces, 2 side margins
|
andrewm@0
|
117 totalDisplayWidth_ = (float)numKeys * (kWhiteKeyFrontWidth + kInterKeySpacing)
|
andrewm@0
|
118 - kInterKeySpacing + 2.0 * kDisplaySideMargin;
|
andrewm@0
|
119
|
andrewm@0
|
120 // Height: white key height plus top and bottom margins
|
andrewm@0
|
121 if(analogSensorsPresent_)
|
andrewm@0
|
122 totalDisplayHeight_ = kDisplayTopMargin + kDisplayBottomMargin + kWhiteKeyFrontLength + kWhiteKeyBackLength
|
andrewm@0
|
123 + kAnalogSliderVerticalSpacing + kAnalogSliderLength;
|
andrewm@0
|
124 else
|
andrewm@0
|
125 totalDisplayHeight_ = kDisplayTopMargin + kDisplayBottomMargin + kWhiteKeyFrontLength + kWhiteKeyBackLength;
|
andrewm@0
|
126 }
|
andrewm@0
|
127
|
andrewm@0
|
128 void KeyboardDisplay::setDisplaySize(float width, float height) {
|
andrewm@0
|
129 ScopedLock sl(displayMutex_);
|
andrewm@0
|
130
|
andrewm@0
|
131 displayPixelWidth_ = width;
|
andrewm@0
|
132 displayPixelHeight_ = height;
|
andrewm@0
|
133 refreshViewport();
|
andrewm@0
|
134 }
|
andrewm@0
|
135
|
andrewm@0
|
136
|
andrewm@0
|
137 // Render the keyboard display
|
andrewm@0
|
138
|
andrewm@0
|
139 void KeyboardDisplay::render() {
|
andrewm@0
|
140 if(lowestMidiNote_ == highestMidiNote_)
|
andrewm@0
|
141 return;
|
andrewm@0
|
142
|
andrewm@0
|
143 // Start with a light gray background
|
andrewm@0
|
144 glClearColor(0.8, 0.8, 0.8, 1.0);
|
andrewm@0
|
145 glClear(GL_COLOR_BUFFER_BIT);
|
andrewm@0
|
146 glLoadIdentity();
|
andrewm@0
|
147
|
andrewm@0
|
148 float invAspectRatio = totalDisplayWidth_ / totalDisplayHeight_; //displayPixelWidth_ / displayPixelHeight_;
|
andrewm@0
|
149 float scaleValue = 2.0 / totalDisplayWidth_;
|
andrewm@0
|
150
|
andrewm@0
|
151 glScalef(scaleValue, scaleValue * invAspectRatio, scaleValue);
|
andrewm@0
|
152 glTranslatef(-1.0 / scaleValue, -totalDisplayHeight_ / 2.0, 0);
|
andrewm@0
|
153 glTranslatef(kDisplaySideMargin, kDisplayBottomMargin, 0.0);
|
andrewm@0
|
154
|
andrewm@0
|
155 //ScopedLock sl(displayMutex_);
|
andrewm@0
|
156
|
andrewm@0
|
157 glPushMatrix();
|
andrewm@0
|
158
|
andrewm@0
|
159 // Draw the keys themselves first, with analog values if present, then draw the touches
|
andrewm@0
|
160 for(int key = lowestMidiNote_; key <= highestMidiNote_; key++) {
|
andrewm@0
|
161 if(keyShape(key) >= 0) {
|
andrewm@46
|
162 if(key < 0 || key > 127) {
|
andrewm@46
|
163 // Safety check
|
andrewm@46
|
164 drawWhiteKey(0, 0, keyShape(key), key == lowestMidiNote_, key == highestMidiNote_,
|
andrewm@46
|
165 false, 1);
|
andrewm@46
|
166 // Analog slider should be centered with respect to the back of the white key
|
andrewm@46
|
167 if(analogSensorsPresent_ && keyShape(key) >= 0) {
|
andrewm@46
|
168 float sliderOffset = kWhiteKeyBackOffsets[keyShape(key)] + (kWhiteKeyBackWidths[keyShape(key)] - kAnalogSliderWidth) * 0.5;
|
andrewm@46
|
169 drawAnalogSlider(sliderOffset, kWhiteKeyFrontLength + kWhiteKeyBackLength + kAnalogSliderVerticalSpacing,
|
andrewm@46
|
170 false, true, 0);
|
andrewm@46
|
171 }
|
andrewm@46
|
172 }
|
andrewm@46
|
173 else {
|
andrewm@46
|
174 // White keys: draw and move the frame over for the next key
|
andrewm@46
|
175 drawWhiteKey(0, 0, keyShape(key), key == lowestMidiNote_, key == highestMidiNote_,
|
andrewm@46
|
176 /*(key == currentHighlightedKey_) ||*/ midiActiveForKey_[key], keyDivisionsForNote_[key]);
|
andrewm@46
|
177 // Analog slider should be centered with respect to the back of the white key
|
andrewm@46
|
178 if(analogSensorsPresent_ && keyShape(key) >= 0) {
|
andrewm@46
|
179 float sliderOffset = kWhiteKeyBackOffsets[keyShape(key)] + (kWhiteKeyBackWidths[keyShape(key)] - kAnalogSliderWidth) * 0.5;
|
andrewm@46
|
180 drawAnalogSlider(sliderOffset, kWhiteKeyFrontLength + kWhiteKeyBackLength + kAnalogSliderVerticalSpacing,
|
andrewm@46
|
181 analogValueIsCalibratedForKey_[key], true, analogValueForKey_[key]);
|
andrewm@46
|
182 }
|
andrewm@0
|
183 }
|
andrewm@0
|
184 glTranslatef(kWhiteKeyFrontWidth + kInterKeySpacing, 0, 0);
|
andrewm@0
|
185 }
|
andrewm@0
|
186 else {
|
andrewm@0
|
187 // Black keys: draw and leave the frame in place
|
andrewm@0
|
188 int previousWhiteKeyShape = keyShape(key - 1);
|
andrewm@0
|
189 float offsetH = -1.0 + kWhiteKeyBackOffsets[previousWhiteKeyShape] + kWhiteKeyBackWidths[previousWhiteKeyShape];
|
andrewm@0
|
190 float offsetV = kWhiteKeyFrontLength + kWhiteKeyBackLength - kBlackKeyLength;
|
andrewm@0
|
191
|
andrewm@0
|
192 glTranslatef(offsetH, offsetV, 0.0);
|
andrewm@46
|
193
|
andrewm@46
|
194 if(key < 0 || key > 127) {
|
andrewm@46
|
195 // Safety check
|
andrewm@46
|
196 drawBlackKey(0, 0, false, 1);
|
andrewm@46
|
197 if(analogSensorsPresent_) {
|
andrewm@46
|
198 drawAnalogSlider((kBlackKeyWidth - kAnalogSliderWidth) * 0.5, kBlackKeyLength + kAnalogSliderVerticalSpacing,
|
andrewm@46
|
199 false, false, 0);
|
andrewm@46
|
200 }
|
andrewm@46
|
201 }
|
andrewm@46
|
202 else {
|
andrewm@46
|
203 drawBlackKey(0, 0, /*(key == currentHighlightedKey_) ||*/ midiActiveForKey_[key], keyDivisionsForNote_[key]);
|
andrewm@46
|
204 if(analogSensorsPresent_) {
|
andrewm@46
|
205 drawAnalogSlider((kBlackKeyWidth - kAnalogSliderWidth) * 0.5, kBlackKeyLength + kAnalogSliderVerticalSpacing,
|
andrewm@46
|
206 analogValueIsCalibratedForKey_[key], false, analogValueForKey_[key]);
|
andrewm@46
|
207 }
|
andrewm@0
|
208 }
|
andrewm@0
|
209 glTranslatef(-offsetH, -offsetV, 0.0);
|
andrewm@0
|
210 }
|
andrewm@0
|
211 }
|
andrewm@0
|
212
|
andrewm@0
|
213 // Restore to the original location we used when drawing the keys
|
andrewm@0
|
214 glPopMatrix();
|
andrewm@0
|
215
|
andrewm@0
|
216 // Transfer the touch display data from storage buffer to a local copy we can use for display.
|
andrewm@0
|
217 // This avoids OpenGL calls happening while the mutex is locked, which stalls the data producer thread
|
andrewm@0
|
218 displayMutex_.enter();
|
andrewm@0
|
219 memcpy(currentTouchesMirror_, currentTouches_, 128*sizeof(TouchInfo));
|
andrewm@0
|
220 displayMutex_.exit();
|
andrewm@0
|
221
|
andrewm@0
|
222 // Draw touches
|
andrewm@0
|
223 for(int key = lowestMidiNote_; key <= highestMidiNote_; key++) {
|
andrewm@0
|
224 if(keyShape(key) >= 0) {
|
andrewm@0
|
225 // Check whether there are any current touches for this key
|
andrewm@0
|
226 //if(currentTouches_.count(key) > 0) {
|
andrewm@0
|
227 if(currentTouchesMirror_[key].active) {
|
andrewm@0
|
228 TouchInfo& t = currentTouchesMirror_[key];
|
andrewm@0
|
229
|
andrewm@0
|
230 if(t.locV1 >= 0)
|
andrewm@0
|
231 drawWhiteTouch(0, 0, keyShape(key), t.locH, t.locV1, t.size1);
|
andrewm@0
|
232 if(t.locV2 >= 0)
|
andrewm@0
|
233 drawWhiteTouch(0, 0, keyShape(key), t.locH, t.locV2, t.size2);
|
andrewm@0
|
234 if(t.locV3 >= 0)
|
andrewm@0
|
235 drawWhiteTouch(0, 0, keyShape(key), t.locH, t.locV3, t.size3);
|
andrewm@0
|
236 }
|
andrewm@0
|
237
|
andrewm@0
|
238 glTranslatef(kWhiteKeyFrontWidth + kInterKeySpacing, 0, 0);
|
andrewm@0
|
239 }
|
andrewm@0
|
240 else {
|
andrewm@0
|
241 // Black keys: draw and leave the frame in place
|
andrewm@0
|
242 int previousWhiteKeyShape = keyShape(key - 1);
|
andrewm@0
|
243 float offsetH = -1.0 + kWhiteKeyBackOffsets[previousWhiteKeyShape] + kWhiteKeyBackWidths[previousWhiteKeyShape];
|
andrewm@0
|
244 float offsetV = kWhiteKeyFrontLength + kWhiteKeyBackLength - kBlackKeyLength;
|
andrewm@0
|
245
|
andrewm@0
|
246 glTranslatef(offsetH, offsetV, 0.0);
|
andrewm@0
|
247
|
andrewm@0
|
248 // Check whether there are any current touches for this key
|
andrewm@0
|
249 //if(currentTouches_.count(key) > 0) {
|
andrewm@0
|
250 if(currentTouchesMirror_[key].active) {
|
andrewm@0
|
251 TouchInfo& t = currentTouchesMirror_[key];
|
andrewm@0
|
252
|
andrewm@0
|
253 if(t.locV1 >= 0)
|
andrewm@0
|
254 drawBlackTouch(0, 0, t.locH, t.locV1, t.size1);
|
andrewm@0
|
255 if(t.locV2 >= 0)
|
andrewm@0
|
256 drawBlackTouch(0, 0, t.locH, t.locV2, t.size2);
|
andrewm@0
|
257 if(t.locV3 >= 0)
|
andrewm@0
|
258 drawBlackTouch(0, 0, t.locH, t.locV3, t.size3);
|
andrewm@0
|
259 }
|
andrewm@0
|
260
|
andrewm@0
|
261 glTranslatef(-offsetH, -offsetV, 0.0);
|
andrewm@0
|
262 }
|
andrewm@0
|
263 }
|
andrewm@0
|
264
|
andrewm@0
|
265 glFlush();
|
andrewm@0
|
266 }
|
andrewm@0
|
267
|
andrewm@0
|
268 // Mouse interaction methods
|
andrewm@0
|
269
|
andrewm@0
|
270 void KeyboardDisplay::mouseDown(float x, float y) {
|
andrewm@0
|
271 Point mousePoint = {x, y};
|
andrewm@0
|
272 Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
273
|
andrewm@0
|
274 currentHighlightedKey_ = keyForLocation(scaledPoint);
|
andrewm@27
|
275 tellCanvasToRepaint();
|
andrewm@0
|
276 }
|
andrewm@0
|
277
|
andrewm@0
|
278 void KeyboardDisplay::mouseDragged(float x, float y) {
|
andrewm@0
|
279 Point mousePoint = {x, y};
|
andrewm@0
|
280 Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
281
|
andrewm@0
|
282 currentHighlightedKey_ = keyForLocation(scaledPoint);
|
andrewm@27
|
283 tellCanvasToRepaint();
|
andrewm@0
|
284 }
|
andrewm@0
|
285
|
andrewm@0
|
286 void KeyboardDisplay::mouseUp(float x, float y) {
|
andrewm@0
|
287 //Point mousePoint = {x, y};
|
andrewm@0
|
288 //Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
289
|
andrewm@0
|
290 // When the mouse is released, see if it was over a key. If so, take any action
|
andrewm@0
|
291 // associated with clicking that key.
|
andrewm@0
|
292
|
andrewm@0
|
293 if(currentHighlightedKey_ != -1)
|
andrewm@0
|
294 keyClicked(currentHighlightedKey_);
|
andrewm@0
|
295
|
andrewm@18
|
296 currentHighlightedKey_ = -1;
|
andrewm@27
|
297 tellCanvasToRepaint();
|
andrewm@0
|
298 }
|
andrewm@0
|
299
|
andrewm@0
|
300 void KeyboardDisplay::rightMouseDown(float x, float y) {
|
andrewm@0
|
301 Point mousePoint = {x, y};
|
andrewm@0
|
302 Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
303
|
andrewm@0
|
304 int key = keyForLocation(scaledPoint);
|
andrewm@0
|
305 if(key != -1)
|
andrewm@0
|
306 keyRightClicked(key);
|
andrewm@0
|
307
|
andrewm@27
|
308 tellCanvasToRepaint();
|
andrewm@0
|
309 }
|
andrewm@0
|
310
|
andrewm@0
|
311 void KeyboardDisplay::rightMouseDragged(float x, float y) {
|
andrewm@0
|
312 //Point mousePoint = {x, y};
|
andrewm@0
|
313 //Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
314 }
|
andrewm@0
|
315
|
andrewm@0
|
316 void KeyboardDisplay::rightMouseUp(float x, float y) {
|
andrewm@0
|
317 //Point mousePoint = {x, y};
|
andrewm@0
|
318 //Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
319 }
|
andrewm@0
|
320
|
andrewm@0
|
321 void KeyboardDisplay::keyClicked(int key) {
|
andrewm@0
|
322
|
andrewm@0
|
323 }
|
andrewm@0
|
324
|
andrewm@0
|
325 void KeyboardDisplay::keyRightClicked(int key) {
|
andrewm@0
|
326
|
andrewm@0
|
327 }
|
andrewm@0
|
328
|
andrewm@0
|
329 // Insert new touch information for the given key and request a display update.
|
andrewm@0
|
330
|
andrewm@0
|
331 void KeyboardDisplay::setTouchForKey(int key, const KeyTouchFrame& touch) {
|
andrewm@0
|
332 if(key < lowestMidiNote_ || key > highestMidiNote_)
|
andrewm@0
|
333 return;
|
andrewm@0
|
334
|
andrewm@0
|
335 ScopedLock sl(displayMutex_);
|
andrewm@0
|
336
|
andrewm@0
|
337 //TouchInfo t = {touch.locH, touch.locs[0], touch.locs[1], touch.locs[2], touch.sizes[0], touch.sizes[1], touch.sizes[2]};
|
andrewm@0
|
338 //currentTouches_[key] = t;
|
andrewm@20
|
339 //currentTouches_[key] = {true, touch.locH, touch.locs[0], touch.locs[1], touch.locs[2], touch.sizes[0], touch.sizes[1], touch.sizes[2]};
|
andrewm@20
|
340 currentTouches_[key].active = true;
|
andrewm@20
|
341 currentTouches_[key].locH = touch.locH;
|
andrewm@20
|
342 currentTouches_[key].locV1 = touch.locs[0];
|
andrewm@20
|
343 currentTouches_[key].locV2 = touch.locs[1];
|
andrewm@20
|
344 currentTouches_[key].locV3 = touch.locs[2];
|
andrewm@20
|
345 currentTouches_[key].size1 = touch.sizes[0];
|
andrewm@20
|
346 currentTouches_[key].size2 = touch.sizes[1];
|
andrewm@20
|
347 currentTouches_[key].size3 = touch.sizes[2];
|
andrewm@20
|
348
|
andrewm@27
|
349 tellCanvasToRepaint();
|
andrewm@0
|
350 }
|
andrewm@0
|
351
|
andrewm@0
|
352 // Clear touch information for this key
|
andrewm@0
|
353
|
andrewm@0
|
354 void KeyboardDisplay::clearTouchForKey(int key) {
|
andrewm@0
|
355 ScopedLock sl(displayMutex_);
|
andrewm@0
|
356
|
andrewm@0
|
357 //currentTouches_.erase(key);
|
andrewm@0
|
358 currentTouches_[key].active = 0;
|
andrewm@0
|
359
|
andrewm@27
|
360 tellCanvasToRepaint();
|
andrewm@0
|
361 }
|
andrewm@0
|
362
|
andrewm@0
|
363 // Clear all current touch information
|
andrewm@0
|
364
|
andrewm@0
|
365 void KeyboardDisplay::clearAllTouches() {
|
andrewm@0
|
366 ScopedLock sl(displayMutex_);
|
andrewm@0
|
367
|
andrewm@0
|
368 //currentTouches_.clear();
|
andrewm@0
|
369 for(int i = 0; i < 128; i++)
|
andrewm@0
|
370 currentTouches_[i].active = false;
|
andrewm@0
|
371
|
andrewm@27
|
372 tellCanvasToRepaint();
|
andrewm@0
|
373 }
|
andrewm@0
|
374
|
andrewm@0
|
375 // Indicate whether the given key is calibrated or not
|
andrewm@0
|
376
|
andrewm@0
|
377 void KeyboardDisplay::setAnalogCalibrationStatusForKey(int key, bool isCalibrated) {
|
andrewm@0
|
378 if(key < 0 || key > 127)
|
andrewm@0
|
379 return;
|
andrewm@0
|
380 analogValueIsCalibratedForKey_[key] = isCalibrated;
|
andrewm@27
|
381 tellCanvasToRepaint();
|
andrewm@0
|
382 }
|
andrewm@0
|
383
|
andrewm@0
|
384 // Set the current value of the analog sensor for the given key.
|
andrewm@0
|
385 // Whether calibrated or not, the data should be in the range 0.0-1.0
|
andrewm@0
|
386 // with a bit of room for deviation outside that range (i.e. for extra key
|
andrewm@0
|
387 // pressure > 1.0, or for resting key states slightly miscalibrated < 0.0).
|
andrewm@0
|
388
|
andrewm@0
|
389 void KeyboardDisplay::setAnalogValueForKey(int key, float value) {
|
andrewm@0
|
390 if(key < 0 || key > 127)
|
andrewm@0
|
391 return;
|
andrewm@0
|
392 analogValueForKey_[key] = value;
|
andrewm@27
|
393 tellCanvasToRepaint();
|
andrewm@0
|
394 }
|
andrewm@0
|
395
|
andrewm@0
|
396 // Clear all the analog data for all keys
|
andrewm@0
|
397 void KeyboardDisplay::clearAnalogData() {
|
andrewm@0
|
398 for(int key = 0; key < 128; key++) {
|
andrewm@0
|
399 analogValueForKey_[key] = 0.0;
|
andrewm@0
|
400 }
|
andrewm@27
|
401 tellCanvasToRepaint();
|
andrewm@0
|
402 }
|
andrewm@0
|
403
|
andrewm@0
|
404 void KeyboardDisplay::setMidiActive(int key, bool active) {
|
andrewm@0
|
405 if(key < 0 || key > 127)
|
andrewm@0
|
406 return;
|
andrewm@0
|
407 midiActiveForKey_[key] = active;
|
andrewm@27
|
408 tellCanvasToRepaint();
|
andrewm@0
|
409 }
|
andrewm@0
|
410
|
andrewm@0
|
411 void KeyboardDisplay::clearMidiData() {
|
andrewm@0
|
412 for(int key = 0; key < 128; key++)
|
andrewm@0
|
413 midiActiveForKey_[key] = false;
|
andrewm@27
|
414 tellCanvasToRepaint();
|
andrewm@0
|
415 }
|
andrewm@0
|
416
|
andrewm@0
|
417 // Indicate whether a given key has touch sensing capability
|
andrewm@0
|
418
|
andrewm@0
|
419 void KeyboardDisplay::setTouchSensorPresentForKey(int key, bool present) {
|
andrewm@0
|
420 if(key < 0 || key > 127 || !touchSensingEnabled_)
|
andrewm@0
|
421 return;
|
andrewm@0
|
422 touchSensingPresentOnKey_[key] = present;
|
andrewm@0
|
423 }
|
andrewm@0
|
424
|
andrewm@0
|
425 // Indicate whether touch sensing is active at all on the keyboard.
|
andrewm@0
|
426 // Clear all key-specific information on whether a touch-sensing key is connected
|
andrewm@0
|
427
|
andrewm@0
|
428 void KeyboardDisplay::setTouchSensingEnabled(bool enabled) {
|
andrewm@0
|
429 touchSensingEnabled_ = enabled;
|
andrewm@0
|
430
|
andrewm@0
|
431 for(int i = 0; i < 128; i++)
|
andrewm@0
|
432 touchSensingPresentOnKey_[i] = false;
|
andrewm@0
|
433 }
|
andrewm@0
|
434
|
andrewm@44
|
435 // Key division methods: indicate that certain keys are divided into more than
|
andrewm@44
|
436 // one segment on the display. Useful for certain mappings.
|
andrewm@44
|
437
|
andrewm@44
|
438 void KeyboardDisplay::addKeyDivision(void *who, int noteLow, int noteHigh, int divisions) {
|
andrewm@44
|
439 KeyDivision div;
|
andrewm@44
|
440
|
andrewm@44
|
441 div.noteLow = noteLow;
|
andrewm@44
|
442 div.noteHigh = noteHigh;
|
andrewm@44
|
443 div.divisions = divisions;
|
andrewm@44
|
444
|
andrewm@44
|
445 keyDivisions_[who] = div;
|
andrewm@44
|
446
|
andrewm@44
|
447 recalculateKeyDivisions();
|
andrewm@44
|
448 tellCanvasToRepaint();
|
andrewm@44
|
449 }
|
andrewm@44
|
450
|
andrewm@44
|
451 void KeyboardDisplay::removeKeyDivision(void *who) {
|
andrewm@44
|
452 if(keyDivisions_.count(who) == 0)
|
andrewm@44
|
453 return;
|
andrewm@44
|
454 keyDivisions_.erase(who);
|
andrewm@44
|
455
|
andrewm@44
|
456 recalculateKeyDivisions();
|
andrewm@44
|
457 tellCanvasToRepaint();
|
andrewm@44
|
458 }
|
andrewm@44
|
459
|
andrewm@0
|
460 // Draw the outline of a white key. Shape ranges from 0-7, giving the type of white key to draw
|
andrewm@0
|
461 // Coordinates give the lower-left corner of the key
|
andrewm@0
|
462
|
andrewm@44
|
463 void KeyboardDisplay::drawWhiteKey(float x, float y, int shape, bool first, bool last, bool highlighted, int divisions) {
|
andrewm@0
|
464 // First and last keys will have special geometry since there is no black key below
|
andrewm@0
|
465 // Figure out the precise geometry in this case...
|
andrewm@0
|
466
|
andrewm@0
|
467 float backOffset, backWidth;
|
andrewm@0
|
468
|
andrewm@0
|
469 if(first) {
|
andrewm@0
|
470 backOffset = 0.0;
|
andrewm@0
|
471 backWidth = kWhiteKeyBackOffsets[shape] + kWhiteKeyBackWidths[shape];
|
andrewm@0
|
472 }
|
andrewm@0
|
473 else if(last) {
|
andrewm@0
|
474 backOffset = kWhiteKeyBackOffsets[shape];
|
andrewm@0
|
475 backWidth = 1.0 - kWhiteKeyBackOffsets[shape];
|
andrewm@0
|
476 }
|
andrewm@0
|
477 else {
|
andrewm@0
|
478 backOffset = kWhiteKeyBackOffsets[shape];
|
andrewm@0
|
479 backWidth = kWhiteKeyBackWidths[shape];
|
andrewm@0
|
480 }
|
andrewm@0
|
481
|
andrewm@0
|
482 // First draw white fill as two squares
|
andrewm@0
|
483 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
andrewm@0
|
484 if(highlighted)
|
andrewm@0
|
485 glColor3f(1.0, 0.8, 0.8);
|
andrewm@0
|
486 else
|
andrewm@0
|
487 glColor3f(1.0, 1.0, 1.0);
|
andrewm@0
|
488 glBegin(GL_QUADS);
|
andrewm@0
|
489
|
andrewm@0
|
490 glVertex2f(x, y);
|
andrewm@0
|
491 glVertex2f(x, y + kWhiteKeyFrontLength);
|
andrewm@0
|
492 glVertex2f(x + kWhiteKeyFrontWidth, y + kWhiteKeyFrontLength);
|
andrewm@0
|
493 glVertex2f(x + kWhiteKeyFrontWidth, y);
|
andrewm@0
|
494
|
andrewm@0
|
495 glVertex2f(x + backOffset, y + kWhiteKeyFrontLength);
|
andrewm@0
|
496 glVertex2f(x + backOffset, y + kWhiteKeyFrontLength + kWhiteKeyBackLength);
|
andrewm@0
|
497 glVertex2f(x + backOffset + backWidth, y + kWhiteKeyFrontLength + kWhiteKeyBackLength);
|
andrewm@0
|
498 glVertex2f(x + backOffset + backWidth, y + kWhiteKeyFrontLength);
|
andrewm@0
|
499
|
andrewm@0
|
500 glEnd();
|
andrewm@0
|
501
|
andrewm@0
|
502 // Now draw the outline as black line segments
|
andrewm@0
|
503 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
andrewm@0
|
504 glColor3f(0.0, 0.0, 0.0);
|
andrewm@0
|
505 glBegin(GL_POLYGON);
|
andrewm@0
|
506
|
andrewm@0
|
507 glVertex2f(x, y);
|
andrewm@0
|
508 glVertex2f(x, y + kWhiteKeyFrontLength);
|
andrewm@0
|
509 glVertex2f(x + backOffset, y + kWhiteKeyFrontLength);
|
andrewm@0
|
510 glVertex2f(x + backOffset, y + kWhiteKeyFrontLength + kWhiteKeyBackLength);
|
andrewm@0
|
511 glVertex2f(x + backOffset + backWidth, y + kWhiteKeyFrontLength + kWhiteKeyBackLength);
|
andrewm@0
|
512 glVertex2f(x + backOffset + backWidth, y + kWhiteKeyFrontLength);
|
andrewm@0
|
513 glVertex2f(x + kWhiteKeyFrontWidth, y + kWhiteKeyFrontLength);
|
andrewm@0
|
514 glVertex2f(x + kWhiteKeyFrontWidth, y);
|
andrewm@0
|
515
|
andrewm@0
|
516 glEnd();
|
andrewm@44
|
517
|
andrewm@44
|
518 if(divisions > 1) {
|
andrewm@44
|
519 glColor3f(0.0, 0.0, 0.0);
|
andrewm@44
|
520
|
andrewm@44
|
521 for(int i = 1; i < divisions; i++) {
|
andrewm@44
|
522 float ratio = (float)i / (float)divisions;
|
andrewm@44
|
523 if(ratio > kWhiteFrontBackCutoff) {
|
andrewm@44
|
524 glBegin(GL_LINES);
|
andrewm@44
|
525 glVertex2d(x + backOffset, y + (kWhiteKeyFrontLength + kWhiteKeyBackLength) * ratio);
|
andrewm@44
|
526 glVertex2d(x + backOffset + backWidth, y + (kWhiteKeyFrontLength + kWhiteKeyBackLength) * ratio);
|
andrewm@44
|
527 glEnd();
|
andrewm@44
|
528 }
|
andrewm@44
|
529 else {
|
andrewm@44
|
530 glBegin(GL_LINES);
|
andrewm@44
|
531 glVertex2d(x, y + (kWhiteKeyFrontLength + kWhiteKeyBackLength) * ratio);
|
andrewm@44
|
532 glVertex2d(x + kWhiteKeyFrontWidth, y + (kWhiteKeyFrontLength + kWhiteKeyBackLength) * ratio);
|
andrewm@44
|
533 glEnd();
|
andrewm@44
|
534 }
|
andrewm@44
|
535 }
|
andrewm@44
|
536 }
|
andrewm@0
|
537 }
|
andrewm@0
|
538
|
andrewm@0
|
539 // Draw the outline of a black key, given its lower-left corner
|
andrewm@0
|
540
|
andrewm@44
|
541 void KeyboardDisplay::drawBlackKey(float x, float y, bool highlighted, int divisions) {
|
andrewm@0
|
542 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
andrewm@0
|
543 if(highlighted)
|
andrewm@0
|
544 glColor3f(0.7, 0.0, 0.0);
|
andrewm@0
|
545 else
|
andrewm@0
|
546 glColor3f(0.0, 0.0, 0.0); // Display color black
|
andrewm@0
|
547 glBegin(GL_POLYGON);
|
andrewm@0
|
548
|
andrewm@0
|
549 glVertex2f(x, y);
|
andrewm@0
|
550 glVertex2f(x, y + kBlackKeyLength);
|
andrewm@0
|
551 glVertex2f(x + kBlackKeyWidth, y + kBlackKeyLength);
|
andrewm@0
|
552 glVertex2f(x + kBlackKeyWidth, y);
|
andrewm@0
|
553
|
andrewm@0
|
554 glEnd();
|
andrewm@44
|
555
|
andrewm@44
|
556 if(divisions > 1) {
|
andrewm@44
|
557 glColor3f(1.0, 1.0, 1.0);
|
andrewm@44
|
558 for(int i = 1; i < divisions; i++) {
|
andrewm@44
|
559 glBegin(GL_LINES);
|
andrewm@44
|
560 glVertex2d(x, y + kBlackKeyLength * (float)i / (float)divisions);
|
andrewm@44
|
561 glVertex2d(x + kBlackKeyWidth, y + kBlackKeyLength * (float)i / (float)divisions);
|
andrewm@44
|
562 glEnd();
|
andrewm@44
|
563 }
|
andrewm@44
|
564 }
|
andrewm@0
|
565 }
|
andrewm@0
|
566
|
andrewm@0
|
567 // Draw a circle indicating a touch on the white key surface
|
andrewm@0
|
568
|
andrewm@0
|
569 void KeyboardDisplay::drawWhiteTouch(float x, float y, int shape, float touchLocH, float touchLocV, float touchSize) {
|
andrewm@0
|
570 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
andrewm@0
|
571 glColor3f(1.0, 0.0, 1.0);
|
andrewm@0
|
572
|
andrewm@0
|
573 glBegin(GL_POLYGON);
|
andrewm@0
|
574 if(/*touchLocV < kWhiteKeyFrontBackCutoff && */touchLocH >= 0.0) { // FIXME: find a more permanent solution
|
andrewm@0
|
575 // Here, the touch is in a location that has both horizontal and vertical information.
|
andrewm@0
|
576 for(int i = 0; i < 360; i += 5) {
|
andrewm@0
|
577 glVertex2f(x + cosf((float)i*3.14159/180.0)*(kDisplayMinTouchSize + touchSize*kDisplayTouchSizeScaler)
|
andrewm@0
|
578 + touchLocH*kWhiteKeyFrontWidth,
|
andrewm@0
|
579 y + sinf((float)i*3.14159/180.0)*(kDisplayMinTouchSize + touchSize*kDisplayTouchSizeScaler)
|
andrewm@0
|
580 + kWhiteKeyFrontLength*(touchLocV/kWhiteKeyFrontBackCutoff));
|
andrewm@0
|
581 }
|
andrewm@0
|
582 }
|
andrewm@0
|
583 else {
|
andrewm@0
|
584 // The touch is in the back part of the key, or for some reason lacks horizontal information
|
andrewm@0
|
585 for(int i = 0; i < 360; i += 5) {
|
andrewm@0
|
586 glVertex2f(x + cosf((float)i*3.14159/180.0)*(kDisplayMinTouchSize + touchSize*kDisplayTouchSizeScaler)
|
andrewm@0
|
587 + kWhiteKeyBackOffsets[shape] + kWhiteKeyBackWidths[shape]/2,
|
andrewm@0
|
588 y + sinf((float)i*3.14159/180.0)*(kDisplayMinTouchSize + touchSize*kDisplayTouchSizeScaler)
|
andrewm@0
|
589 + kWhiteKeyFrontLength + (kWhiteKeyBackLength*
|
andrewm@0
|
590 ((touchLocV-kWhiteKeyFrontBackCutoff)/(1.0-kWhiteKeyFrontBackCutoff))));
|
andrewm@0
|
591 }
|
andrewm@0
|
592 }
|
andrewm@0
|
593 glEnd();
|
andrewm@0
|
594 }
|
andrewm@0
|
595
|
andrewm@0
|
596 // Draw a circle indicating a touch on the black key surface
|
andrewm@0
|
597
|
andrewm@0
|
598 void KeyboardDisplay::drawBlackTouch(float x, float y, float touchLocH, float touchLocV, float touchSize) {
|
andrewm@0
|
599 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
andrewm@0
|
600 glColor3f(0.0, 1.0, 0.0);
|
andrewm@0
|
601
|
andrewm@0
|
602 glBegin(GL_POLYGON);
|
andrewm@0
|
603
|
andrewm@0
|
604 if(touchLocH < 0.0)
|
andrewm@0
|
605 touchLocH = 0.5;
|
andrewm@0
|
606
|
andrewm@0
|
607 for(int i = 0; i < 360; i += 5) {
|
andrewm@0
|
608 glVertex2f(x + cosf((float)i*3.14159/180.0)*(kDisplayMinTouchSize + touchSize*kDisplayTouchSizeScaler)
|
andrewm@0
|
609 + touchLocH * kBlackKeyWidth,
|
andrewm@0
|
610 y + sinf((float)i*3.14159/180.0)*(kDisplayMinTouchSize + touchSize*kDisplayTouchSizeScaler) + kBlackKeyLength*touchLocV);
|
andrewm@0
|
611 }
|
andrewm@0
|
612
|
andrewm@0
|
613 glEnd();
|
andrewm@0
|
614 }
|
andrewm@0
|
615
|
andrewm@0
|
616 // Draw a slider bar indicating the current key analog position
|
andrewm@0
|
617
|
andrewm@0
|
618 void KeyboardDisplay::drawAnalogSlider(float x, float y, bool calibrated, bool whiteKey, float value) {
|
andrewm@0
|
619 // First some gray lines indicating the 0.0 and 1.0 marks
|
andrewm@0
|
620 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
andrewm@0
|
621 glColor3f(0.5, 0.5, 0.5);
|
andrewm@0
|
622
|
andrewm@0
|
623 glBegin(GL_POLYGON);
|
andrewm@0
|
624 glVertex2f(x, y + kAnalogSliderZeroLocation);
|
andrewm@0
|
625 glVertex2f(x, y + kAnalogSliderOneLocation);
|
andrewm@0
|
626 glVertex2f(x + kAnalogSliderWidth, y + kAnalogSliderOneLocation);
|
andrewm@0
|
627 glVertex2f(x + kAnalogSliderWidth, y + kAnalogSliderZeroLocation);
|
andrewm@0
|
628 glEnd();
|
andrewm@0
|
629
|
andrewm@0
|
630 // Draw a red box at the top for uncalibrated values
|
andrewm@0
|
631 if(!calibrated) {
|
andrewm@0
|
632 glColor3f(1.0, 0.0, 0.0);
|
andrewm@0
|
633 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
andrewm@0
|
634 glBegin(GL_POLYGON);
|
andrewm@0
|
635 glVertex2f(x, y + kAnalogSliderOneLocation);
|
andrewm@0
|
636 glVertex2f(x, y + kAnalogSliderLength);
|
andrewm@0
|
637 glVertex2f(x + kAnalogSliderWidth, y + kAnalogSliderLength);
|
andrewm@0
|
638 glVertex2f(x + kAnalogSliderWidth, y + kAnalogSliderOneLocation);
|
andrewm@0
|
639 glEnd();
|
andrewm@0
|
640 }
|
andrewm@0
|
641
|
andrewm@0
|
642 // Next the filled part indicating the specific value (same color as touches), then the outline
|
andrewm@0
|
643 if(whiteKey)
|
andrewm@0
|
644 glColor3f(1.0, 0.0, 1.0);
|
andrewm@0
|
645 else
|
andrewm@0
|
646 glColor3f(0.0, 1.0, 0.0);
|
andrewm@0
|
647 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
andrewm@0
|
648 glBegin(GL_POLYGON);
|
andrewm@0
|
649
|
andrewm@0
|
650 float locationForValue = kAnalogSliderLength * (value - kAnalogSliderMinimumValue) / (kAnalogSliderMaximumValue - kAnalogSliderMinimumValue);
|
andrewm@0
|
651 if(locationForValue < 0.0)
|
andrewm@0
|
652 locationForValue = 0.0;
|
andrewm@0
|
653 if(locationForValue > kAnalogSliderLength)
|
andrewm@0
|
654 locationForValue = kAnalogSliderLength;
|
andrewm@0
|
655
|
andrewm@0
|
656 // Draw solid box from 0.0 to current value
|
andrewm@0
|
657 glVertex2f(x, y + kAnalogSliderZeroLocation);
|
andrewm@0
|
658 glVertex2f(x, y + locationForValue);
|
andrewm@0
|
659 glVertex2f(x + kAnalogSliderWidth, y + locationForValue);
|
andrewm@0
|
660 glVertex2f(x + kAnalogSliderWidth, y + kAnalogSliderZeroLocation);
|
andrewm@0
|
661 glEnd();
|
andrewm@0
|
662
|
andrewm@0
|
663 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
andrewm@0
|
664 glColor3f(0.0, 0.0, 0.0);
|
andrewm@0
|
665
|
andrewm@0
|
666 glBegin(GL_POLYGON);
|
andrewm@0
|
667 glVertex2f(x, y);
|
andrewm@0
|
668 glVertex2f(x, y + kAnalogSliderLength);
|
andrewm@0
|
669 glVertex2f(x + kAnalogSliderWidth, y + kAnalogSliderLength);
|
andrewm@0
|
670 glVertex2f(x + kAnalogSliderWidth, y);
|
andrewm@0
|
671 glEnd();
|
andrewm@0
|
672 }
|
andrewm@0
|
673
|
andrewm@0
|
674 void KeyboardDisplay::refreshViewport() {
|
andrewm@0
|
675 //glViewport(0, 0, displayPixelWidth_, displayPixelHeight_);
|
andrewm@0
|
676 }
|
andrewm@0
|
677
|
andrewm@0
|
678 // Conversion from internal coordinate space to external pixel values and back
|
andrewm@0
|
679
|
andrewm@0
|
680 // Pixel values go from 0,0 (lower left) to displayPixelWidth_, displayPixelHeight_ (upper right)
|
andrewm@0
|
681 // Internal values go from -totalDisplayWidth_/2, -totalDisplayHeight_/2 (lower left)
|
andrewm@0
|
682 // to totalDisplayWidth_/2, totalDisplayHeight_/2 (upper right)
|
andrewm@0
|
683
|
andrewm@0
|
684 // Pixel value in --> OpenGL value out
|
andrewm@0
|
685 KeyboardDisplay::Point KeyboardDisplay::screenToInternal(Point& inPoint) {
|
andrewm@0
|
686 Point out;
|
andrewm@0
|
687
|
andrewm@0
|
688 out.x = -totalDisplayWidth_*0.5 + (inPoint.x/displayPixelWidth_) * totalDisplayWidth_;
|
andrewm@0
|
689 out.y = -totalDisplayHeight_*0.5 + (inPoint.y/displayPixelHeight_) * totalDisplayHeight_;
|
andrewm@0
|
690
|
andrewm@0
|
691 return out;
|
andrewm@0
|
692 }
|
andrewm@0
|
693
|
andrewm@0
|
694 // OpenGL value in --> Pixel value out
|
andrewm@0
|
695 KeyboardDisplay::Point KeyboardDisplay::internalToScreen(Point& inPoint) {
|
andrewm@0
|
696 Point out;
|
andrewm@0
|
697
|
andrewm@0
|
698 out.x = ((inPoint.x + totalDisplayWidth_*0.5)/totalDisplayWidth_) * displayPixelWidth_;
|
andrewm@0
|
699 out.y = ((inPoint.y + totalDisplayHeight_*0.5)/totalDisplayHeight_) * displayPixelHeight_;
|
andrewm@0
|
700
|
andrewm@0
|
701 return out;
|
andrewm@0
|
702 }
|
andrewm@0
|
703
|
andrewm@0
|
704 // Given an internal-coordinate representation, return the number of the key that it belongs
|
andrewm@0
|
705 // in, otherwise return -1 if no key matches.
|
andrewm@0
|
706
|
andrewm@0
|
707 int KeyboardDisplay::keyForLocation(Point& internalPoint) {
|
andrewm@27
|
708 // std::cout << "(" << internalPoint.x << "," << internalPoint.y << ")\n";
|
andrewm@18
|
709
|
andrewm@0
|
710 // First, check that the point is within the overall bounding box of the keyboard
|
andrewm@0
|
711 if(internalPoint.y < -totalDisplayHeight_*0.5 + kDisplayBottomMargin ||
|
andrewm@0
|
712 internalPoint.y > totalDisplayHeight_*0.5 - kDisplayTopMargin)
|
andrewm@0
|
713 return -1;
|
andrewm@0
|
714 if(internalPoint.x < -totalDisplayWidth_*0.5 + kDisplaySideMargin ||
|
andrewm@0
|
715 internalPoint.x > totalDisplayWidth_*0.5 - kDisplaySideMargin)
|
andrewm@0
|
716 return -1;
|
andrewm@0
|
717
|
andrewm@0
|
718 // Now, look for the key region corresponding to this horizontal location
|
andrewm@0
|
719 // hLoc indicates the relative distance from the beginning of the first key
|
andrewm@0
|
720
|
andrewm@0
|
721 float hLoc = internalPoint.x + totalDisplayWidth_*0.5 - kDisplaySideMargin;
|
andrewm@0
|
722
|
andrewm@0
|
723 if(hLoc < 0.0)
|
andrewm@0
|
724 return -1;
|
andrewm@0
|
725
|
andrewm@0
|
726 // normalizedHLoc indicates the index of the white key this touch is near.
|
andrewm@0
|
727 float normalizedHLoc = hLoc / (kWhiteKeyFrontWidth + kInterKeySpacing);
|
andrewm@0
|
728
|
andrewm@0
|
729 // Two relevant regions: front of the white keys, back of the white keys with black keys
|
andrewm@0
|
730 // Distinguish them by vertical position.
|
andrewm@0
|
731
|
andrewm@0
|
732 int shapeOfBottomKey = keyShape(lowestMidiNote_); // White key index of lowest key
|
andrewm@0
|
733 int lowestC = (lowestMidiNote_ / 12) * 12; // C below lowest key
|
andrewm@0
|
734 int whiteKeyNumber = floorf(normalizedHLoc); // Number of white key
|
andrewm@0
|
735 int whiteOctaveNumber = (whiteKeyNumber + shapeOfBottomKey) / 7; // Octave the key is in
|
andrewm@0
|
736 int chromaticKeyNumber = 12 * whiteOctaveNumber + kWhiteToChromatic[(whiteKeyNumber + shapeOfBottomKey) % 7];
|
andrewm@0
|
737
|
andrewm@0
|
738 // Check if we're on the front area of the white keys, and if so, ignore points located in the gaps
|
andrewm@0
|
739 // between the keys
|
andrewm@18
|
740
|
andrewm@27
|
741 // std::cout << "norm " << (-internalPoint.y + totalDisplayHeight_*0.5) << std::endl;
|
andrewm@0
|
742
|
andrewm@18
|
743 if(-internalPoint.y + totalDisplayHeight_*0.5 - kDisplayBottomMargin <= kWhiteKeyFrontLength) {
|
andrewm@0
|
744 if(normalizedHLoc - floorf(normalizedHLoc) > kWhiteKeyFrontWidth / (kWhiteKeyFrontWidth + kInterKeySpacing))
|
andrewm@0
|
745 return -1;
|
andrewm@0
|
746 return lowestC + chromaticKeyNumber;
|
andrewm@0
|
747 }
|
andrewm@0
|
748 else {
|
andrewm@0
|
749 // Back of white keys, or black keys
|
andrewm@0
|
750
|
andrewm@0
|
751 int whiteKeyShape = keyShape(chromaticKeyNumber);
|
andrewm@0
|
752 if(whiteKeyShape < 0) // Shouldn't happen
|
andrewm@0
|
753 return -1;
|
andrewm@0
|
754
|
andrewm@0
|
755 float locRelativeToLeft = (normalizedHLoc - floorf(normalizedHLoc)) * (kWhiteKeyFrontWidth + kInterKeySpacing);
|
andrewm@0
|
756
|
andrewm@0
|
757 // Check if we are in the back region of the white key. Handle the lowest and highest notes specially since
|
andrewm@0
|
758 // the white keys are generally wider on account of no adjacent black key.
|
andrewm@0
|
759 if(lowestC + chromaticKeyNumber == lowestMidiNote_) {
|
andrewm@0
|
760 if(locRelativeToLeft <= kWhiteKeyBackOffsets[whiteKeyShape] + kWhiteKeyBackWidths[whiteKeyShape])
|
andrewm@0
|
761 return lowestC + chromaticKeyNumber;
|
andrewm@0
|
762 }
|
andrewm@0
|
763 else if(lowestC + chromaticKeyNumber == highestMidiNote_) {
|
andrewm@0
|
764 if(locRelativeToLeft >= kWhiteKeyBackOffsets[whiteKeyShape])
|
andrewm@0
|
765 return lowestC + chromaticKeyNumber;
|
andrewm@0
|
766 }
|
andrewm@0
|
767 else if(locRelativeToLeft >= kWhiteKeyBackOffsets[whiteKeyShape] &&
|
andrewm@0
|
768 locRelativeToLeft <= kWhiteKeyBackOffsets[whiteKeyShape] + kWhiteKeyBackWidths[whiteKeyShape]) {
|
andrewm@0
|
769 return lowestC + chromaticKeyNumber;
|
andrewm@0
|
770 }
|
andrewm@0
|
771
|
andrewm@0
|
772 // By now, we've established that we're not on top of a white key. See if we align to a black key.
|
andrewm@0
|
773 // Watch the vertical gap between white and black keys
|
andrewm@18
|
774 if(-internalPoint.y + totalDisplayHeight_*0.5 - kDisplayBottomMargin <=
|
andrewm@0
|
775 kWhiteKeyFrontLength + kWhiteKeyBackLength - kBlackKeyLength)
|
andrewm@0
|
776 return -1;
|
andrewm@0
|
777
|
andrewm@0
|
778 // Is there a black key below this white key?
|
andrewm@0
|
779 if(keyShape(chromaticKeyNumber - 1) < 0) {
|
andrewm@0
|
780 if(locRelativeToLeft <= kWhiteKeyBackOffsets[whiteKeyShape] - kInterKeySpacing &&
|
andrewm@0
|
781 lowestC + chromaticKeyNumber > lowestMidiNote_)
|
andrewm@0
|
782 return lowestC + chromaticKeyNumber - 1;
|
andrewm@0
|
783 }
|
andrewm@0
|
784 // Or is there a black key above this white key?
|
andrewm@0
|
785 if(keyShape(chromaticKeyNumber + 1) < 0) {
|
andrewm@0
|
786 if(locRelativeToLeft >= kWhiteKeyBackOffsets[whiteKeyShape] + kWhiteKeyBackWidths[whiteKeyShape] + kInterKeySpacing
|
andrewm@0
|
787 && lowestC + chromaticKeyNumber < highestMidiNote_)
|
andrewm@0
|
788 return lowestC + chromaticKeyNumber + 1;
|
andrewm@0
|
789 }
|
andrewm@0
|
790 }
|
andrewm@0
|
791
|
andrewm@0
|
792 // If all else fails, assume we're not on any key
|
andrewm@0
|
793 return -1;
|
andrewm@44
|
794 }
|
andrewm@44
|
795
|
andrewm@44
|
796 // Convert the map of keyboard segments with divisions into an array ordered by
|
andrewm@44
|
797 // note number, to save time during display
|
andrewm@44
|
798
|
andrewm@44
|
799 void KeyboardDisplay::recalculateKeyDivisions() {
|
andrewm@44
|
800 // By default, 1 division per key
|
andrewm@44
|
801 for(int i = 0; i < 128; i++)
|
andrewm@44
|
802 keyDivisionsForNote_[i] = 1;
|
andrewm@44
|
803
|
andrewm@44
|
804 // Increase divisions wherever we find a relevant mapping
|
andrewm@44
|
805 std::map<void*, KeyDivision>::iterator it;
|
andrewm@44
|
806 for(it = keyDivisions_.begin(); it != keyDivisions_.end(); ++it) {
|
andrewm@44
|
807 int start = it->second.noteLow;
|
andrewm@44
|
808 int end = it->second.noteHigh;
|
andrewm@44
|
809 int div = it->second.divisions;
|
andrewm@44
|
810
|
andrewm@44
|
811 if(start < 0)
|
andrewm@44
|
812 start = 0;
|
andrewm@44
|
813 if(end > 127)
|
andrewm@44
|
814 end = 127;
|
andrewm@44
|
815 for(int i = start; i <= end; i++) {
|
andrewm@44
|
816 if(div > keyDivisionsForNote_[i])
|
andrewm@44
|
817 keyDivisionsForNote_[i] = div;
|
andrewm@44
|
818 }
|
andrewm@44
|
819 }
|
andrewm@0
|
820 } |