annotate Source/Display/KeyboardDisplay.cpp @ 56:b4a2d2ae43cf tip

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents 78b9808a2c65
children
rev   line source
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 }