annotate Source/Display/KeyboardTesterDisplay.cpp @ 18:c8387e4f119f

Improvements to tester GUI
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Wed, 27 Nov 2013 00:37:43 +0000
parents 73d2ec21de9a
children 85577160a0d4
rev   line source
andrewm@17 1 /*
andrewm@17 2 TouchKeys: multi-touch musical keyboard control software
andrewm@17 3 Copyright (c) 2013 Andrew McPherson
andrewm@17 4
andrewm@17 5 This program is free software: you can redistribute it and/or modify
andrewm@17 6 it under the terms of the GNU General Public License as published by
andrewm@17 7 the Free Software Foundation, either version 3 of the License, or
andrewm@17 8 (at your option) any later version.
andrewm@17 9
andrewm@17 10 This program is distributed in the hope that it will be useful,
andrewm@17 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
andrewm@17 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
andrewm@17 13 GNU General Public License for more details.
andrewm@17 14
andrewm@17 15 You should have received a copy of the GNU General Public License
andrewm@17 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
andrewm@17 17
andrewm@17 18 =====================================================================
andrewm@17 19
andrewm@17 20 KeyboardTesterDisplay.cpp: A keyboard display for raw data that can be used
andrewm@17 21 for testing the functionality of individual TouchKeys sensors
andrewm@17 22 */
andrewm@17 23
andrewm@17 24 #ifdef ENABLE_TOUCHKEYS_SENSOR_TEST
andrewm@17 25
andrewm@17 26 #include "KeyboardTesterDisplay.h"
andrewm@17 27 #include "../MainApplicationController.h"
andrewm@17 28 #include "../TouchKeys/PianoKeyboard.h"
andrewm@17 29
andrewm@17 30 const int KeyboardTesterDisplay::kNumSensorsPerKey = 26;
andrewm@17 31 const int KeyboardTesterDisplay::kDefaultSensorThreshold = 16;
andrewm@17 32
andrewm@17 33 // Constructor
andrewm@17 34 KeyboardTesterDisplay::KeyboardTesterDisplay(MainApplicationController& controller, PianoKeyboard& keyboard)
andrewm@17 35 : controller_(controller), keyboard_(keyboard),
andrewm@17 36 currentlyActiveKey_(-1), sensorThreshold_(kDefaultSensorThreshold) {
andrewm@17 37 for(int i = 0; i < 128; i++)
andrewm@17 38 resetSensorState(i);
andrewm@17 39
andrewm@17 40 setOscController(&keyboard_);
andrewm@17 41 addOscListener("/touchkeys/rawbytes");
andrewm@17 42 }
andrewm@17 43
andrewm@17 44 // Render the display starting with the underlying keyboard and
andrewm@17 45 // then adding our own display info on top
andrewm@17 46 void KeyboardTesterDisplay::render() {
andrewm@17 47 if(lowestMidiNote_ == highestMidiNote_)
andrewm@17 48 return;
andrewm@17 49
andrewm@17 50 // Start with a light gray background
andrewm@17 51 glClearColor(0.8, 0.8, 0.8, 1.0);
andrewm@17 52 glClear(GL_COLOR_BUFFER_BIT);
andrewm@17 53 glLoadIdentity();
andrewm@17 54
andrewm@17 55 float invAspectRatio = totalDisplayWidth_ / totalDisplayHeight_;
andrewm@17 56 float scaleValue = 2.0 / totalDisplayWidth_;
andrewm@17 57
andrewm@17 58 glScalef(scaleValue, scaleValue * invAspectRatio, scaleValue);
andrewm@17 59 glTranslatef(-1.0 / scaleValue, -totalDisplayHeight_ / 2.0, 0);
andrewm@17 60 glTranslatef(kDisplaySideMargin, kDisplayBottomMargin, 0.0);
andrewm@17 61
andrewm@17 62 glPushMatrix();
andrewm@17 63
andrewm@17 64 // Draw the keys themselves first, with analog values if present, then draw the touches
andrewm@17 65 for(int key = lowestMidiNote_; key <= highestMidiNote_; key++) {
andrewm@17 66 if(keyShape(key) >= 0) {
andrewm@17 67 // White keys: draw and move the frame over for the next key
andrewm@17 68 drawWhiteKey(0, 0, keyShape(key), key == lowestMidiNote_,
andrewm@17 69 key == highestMidiNote_, (key == currentlyActiveKey_) || (key == currentHighlightedKey_));
andrewm@17 70 // Draw sensor state for this key
andrewm@18 71 drawSensorState(key, 0, 0, kWhiteKeyBackWidths[keyShape(key)], kWhiteKeyFrontLength + kWhiteKeyBackLength,
andrewm@18 72 true, kWhiteKeyBackOffsets[keyShape(key)]);
andrewm@17 73 glTranslatef(kWhiteKeyFrontWidth + kInterKeySpacing, 0, 0);
andrewm@17 74 }
andrewm@17 75 else {
andrewm@17 76 // Black keys: draw and leave the frame in place
andrewm@17 77 int previousWhiteKeyShape = keyShape(key - 1);
andrewm@17 78 float offsetH = -1.0 + kWhiteKeyBackOffsets[previousWhiteKeyShape] + kWhiteKeyBackWidths[previousWhiteKeyShape];
andrewm@17 79 float offsetV = kWhiteKeyFrontLength + kWhiteKeyBackLength - kBlackKeyLength;
andrewm@17 80
andrewm@17 81 glTranslatef(offsetH, offsetV, 0.0);
andrewm@17 82 drawBlackKey(0, 0, (key == currentlyActiveKey_) || (key == currentHighlightedKey_));
andrewm@17 83 // Draw sensor state for this key
andrewm@18 84 drawSensorState(key, 0, 0, kBlackKeyWidth, kBlackKeyLength, false, 0);
andrewm@17 85 glTranslatef(-offsetH, -offsetV, 0.0);
andrewm@17 86 }
andrewm@17 87 }
andrewm@17 88
andrewm@17 89 // Restore to the original location we used when drawing the keys
andrewm@17 90 glPopMatrix();
andrewm@17 91
andrewm@17 92 needsUpdate_ = false;
andrewm@17 93 glFlush();
andrewm@17 94 }
andrewm@17 95
andrewm@18 96 // Called when a given key is clicked by mouse
andrewm@18 97 void KeyboardTesterDisplay::keyClicked(int key) {
andrewm@18 98 controller_.touchkeySensorTestSetKey(key);
andrewm@18 99 }
andrewm@18 100
andrewm@17 101 // Set the threshold level at which a sensor is considered active
andrewm@17 102 void KeyboardTesterDisplay::setSensorThreshold(int threshold) {
andrewm@17 103 sensorThreshold_ = threshold;
andrewm@17 104 }
andrewm@17 105
andrewm@17 106 // Set the state of a given sensor on a given key to be on or off,
andrewm@17 107 // based on some externally-computed threshold. Sensors that are on
andrewm@17 108 // will flip the "good" flag to true, which remains set until cleared
andrewm@17 109 // externally.
andrewm@17 110 void KeyboardTesterDisplay::setSensorState(int key, int sensor, bool active) {
andrewm@17 111 if(key < 0 || key > 127)
andrewm@17 112 return;
andrewm@17 113 if(sensor < 0 || sensor >= kNumSensorsPerKey)
andrewm@17 114 return;
andrewm@17 115 if(active) {
andrewm@17 116 keySensorActive_[key] |= (1 << sensor);
andrewm@17 117 keySensorGood_[key] |= (1 << sensor);
andrewm@17 118 }
andrewm@17 119 else
andrewm@17 120 keySensorActive_[key] &= ~(1 << sensor);
andrewm@17 121 currentlyActiveKey_ = key;
andrewm@17 122 needsUpdate_ = true;
andrewm@17 123
andrewm@17 124 if(allSensorsGood(currentlyActiveKey_)) {
andrewm@17 125 controller_.touchkeySensorTestSetKey(key + 1);
andrewm@17 126 }
andrewm@17 127 }
andrewm@17 128
andrewm@17 129 // Indicate whether all sensors have shown an active value on this key
andrewm@17 130 bool KeyboardTesterDisplay::allSensorsGood(int key) {
andrewm@17 131 if(key < 0 || key > 127)
andrewm@17 132 return false;
andrewm@17 133 unsigned int mask = (1 << kNumSensorsPerKey) - 1;
andrewm@17 134
andrewm@17 135 return ((keySensorGood_[key] & mask) == mask);
andrewm@17 136 }
andrewm@17 137
andrewm@17 138 // Reset the sensor state to all off
andrewm@17 139 void KeyboardTesterDisplay::resetSensorState(int key) {
andrewm@17 140 if(key < 0 || key > 127)
andrewm@17 141 return;
andrewm@17 142 keySensorGood_[key] = 0;
andrewm@17 143 keySensorActive_[key] = 0;
andrewm@17 144 if(currentlyActiveKey_ == key)
andrewm@17 145 currentlyActiveKey_ = -1;
andrewm@17 146 }
andrewm@17 147
andrewm@17 148 // Draw the given key sensors as being active, good, or inactive
andrewm@18 149 void KeyboardTesterDisplay::drawSensorState(int key, float x, float y, float width, float height, bool white, float whiteOffset) {
andrewm@17 150 float heightInset = height / ((float)kNumSensorsPerKey * 10);
andrewm@17 151
andrewm@17 152 glPushMatrix();
andrewm@17 153 glTranslatef(x, y, 0);
andrewm@17 154
andrewm@18 155 if(white) {
andrewm@18 156 float hSensorWidth = kWhiteKeyFrontWidth * 0.25;
andrewm@17 157
andrewm@18 158 // Draw the first four sensors horizontally for the white keys
andrewm@18 159 glPushMatrix();
andrewm@17 160
andrewm@18 161 for(int i = 0; i < 4; i++) {
andrewm@18 162 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
andrewm@18 163 if(keySensorActive_[key] & (1 << i) && key == currentlyActiveKey_)
andrewm@18 164 glColor3f(1.0, 1.0, 0.0); // Sensor active right now = yellow
andrewm@18 165 else if(keySensorGood_[key] & (1 << i))
andrewm@18 166 glColor3f(0.0, 1.0, 0.0); // Sensor has been active (good) = green
andrewm@18 167 else
andrewm@18 168 glColor3f(1.0, 0.0, 0.0); // Sensor has not yet been active = red
andrewm@18 169
andrewm@18 170 glBegin(GL_POLYGON);
andrewm@18 171 glVertex2f(hSensorWidth * 0.1, heightInset);
andrewm@18 172 glVertex2f(hSensorWidth * 0.1, 4.0*height / (float)kNumSensorsPerKey - heightInset);
andrewm@18 173 glVertex2f(hSensorWidth * 0.9, 4.0*height / (float)kNumSensorsPerKey - heightInset);
andrewm@18 174 glVertex2f(hSensorWidth * 0.9, heightInset);
andrewm@18 175 glEnd();
andrewm@18 176 glTranslatef(hSensorWidth, 0, 0);
andrewm@18 177 }
andrewm@18 178
andrewm@18 179 glPopMatrix();
andrewm@18 180 glTranslatef(whiteOffset, 4.0*height / (float)kNumSensorsPerKey, 0);
andrewm@18 181
andrewm@18 182 for(int i = 4; i < kNumSensorsPerKey; i++) {
andrewm@18 183 // Draw each sensor in sequence: red = never activated; yellow = active; green = previously
andrewm@18 184 // activated (good)
andrewm@18 185 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
andrewm@18 186 if(keySensorActive_[key] & (1 << i) && key == currentlyActiveKey_)
andrewm@18 187 glColor3f(1.0, 1.0, 0.0); // Sensor active right now = yellow
andrewm@18 188 else if(keySensorGood_[key] & (1 << i))
andrewm@18 189 glColor3f(0.0, 1.0, 0.0); // Sensor has been active (good) = green
andrewm@18 190 else
andrewm@18 191 glColor3f(1.0, 0.0, 0.0); // Sensor has not yet been active = red
andrewm@18 192
andrewm@18 193 glBegin(GL_POLYGON);
andrewm@18 194 glVertex2f(width * 0.1, heightInset);
andrewm@18 195 glVertex2f(width * 0.1, height / (float)kNumSensorsPerKey - heightInset);
andrewm@18 196 glVertex2f(width * 0.9, height / (float)kNumSensorsPerKey - heightInset);
andrewm@18 197 glVertex2f(width * 0.9, heightInset);
andrewm@18 198 glEnd();
andrewm@18 199
andrewm@18 200 glTranslatef(0, height / (float)kNumSensorsPerKey, 0);
andrewm@18 201 }
andrewm@17 202 }
andrewm@18 203 else { // Black
andrewm@18 204 // Draw in two rows
andrewm@18 205 glPushMatrix();
andrewm@18 206 for(int i = 0; i < kNumSensorsPerKey / 2; i++) {
andrewm@18 207 // Draw each sensor in sequence: red = never activated; yellow = active; green = previously
andrewm@18 208 // activated (good)
andrewm@18 209 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
andrewm@18 210 if(keySensorActive_[key] & (1 << i) && key == currentlyActiveKey_)
andrewm@18 211 glColor3f(1.0, 1.0, 0.0); // Sensor active right now = yellow
andrewm@18 212 else if(keySensorGood_[key] & (1 << i))
andrewm@18 213 glColor3f(0.0, 1.0, 0.0); // Sensor has been active (good) = green
andrewm@18 214 else
andrewm@18 215 glColor3f(1.0, 0.0, 0.0); // Sensor has not yet been active = red
andrewm@18 216
andrewm@18 217 glBegin(GL_POLYGON);
andrewm@18 218 glVertex2f(width * 0.55, heightInset);
andrewm@18 219 glVertex2f(width * 0.55, 2.0*height / (float)kNumSensorsPerKey - heightInset);
andrewm@18 220 glVertex2f(width * 0.9, 2.0*height / (float)kNumSensorsPerKey - heightInset);
andrewm@18 221 glVertex2f(width * 0.9, heightInset);
andrewm@18 222 glEnd();
andrewm@18 223
andrewm@18 224 glTranslatef(0, 2.0*height / (float)kNumSensorsPerKey, 0);
andrewm@18 225 }
andrewm@18 226 glPopMatrix();
andrewm@18 227 glPushMatrix();
andrewm@18 228 for(int i = kNumSensorsPerKey / 2; i < kNumSensorsPerKey; i++) {
andrewm@18 229 // Draw each sensor in sequence: red = never activated; yellow = active; green = previously
andrewm@18 230 // activated (good)
andrewm@18 231 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
andrewm@18 232 if(keySensorActive_[key] & (1 << i) && key == currentlyActiveKey_)
andrewm@18 233 glColor3f(1.0, 1.0, 0.0); // Sensor active right now = yellow
andrewm@18 234 else if(keySensorGood_[key] & (1 << i))
andrewm@18 235 glColor3f(0.0, 1.0, 0.0); // Sensor has been active (good) = green
andrewm@18 236 else
andrewm@18 237 glColor3f(1.0, 0.0, 0.0); // Sensor has not yet been active = red
andrewm@18 238
andrewm@18 239 glBegin(GL_POLYGON);
andrewm@18 240 glVertex2f(width * 0.1, heightInset);
andrewm@18 241 glVertex2f(width * 0.1, 2.0*height / (float)kNumSensorsPerKey - heightInset);
andrewm@18 242 glVertex2f(width * 0.45, 2.0*height / (float)kNumSensorsPerKey - heightInset);
andrewm@18 243 glVertex2f(width * 0.45, heightInset);
andrewm@18 244 glEnd();
andrewm@18 245
andrewm@18 246 glTranslatef(0, 2.0*height / (float)kNumSensorsPerKey, 0);
andrewm@18 247 }
andrewm@18 248 glPopMatrix();
andrewm@18 249 }
andrewm@17 250 glPopMatrix();
andrewm@17 251 }
andrewm@17 252
andrewm@17 253 // OSC callback method, for when data comes in
andrewm@17 254 bool KeyboardTesterDisplay::oscHandlerMethod(const char *path, const char *types, int numValues, lo_arg **values, void *data) {
andrewm@17 255 // Look for a blob in value 0 holding the raw data
andrewm@17 256 if(numValues < 1)
andrewm@17 257 return false;
andrewm@17 258 if(types[0] != 'b')
andrewm@17 259 return false;
andrewm@17 260
andrewm@17 261 // Get OSC blob which holds raw data
andrewm@17 262 lo_blob blob = values[0];
andrewm@17 263 int bufferSize = lo_blob_datasize(blob);
andrewm@17 264 const unsigned char *buffer = (const unsigned char *)lo_blob_dataptr(blob);
andrewm@17 265
andrewm@17 266 // buffer[0] holds the key number from which this data came. Make sure it's sane.
andrewm@17 267 if(bufferSize == 0)
andrewm@17 268 return false;
andrewm@17 269 if(buffer[0] > 127)
andrewm@17 270 return false;
andrewm@17 271
andrewm@17 272 // The remainder is raw data, with each single byte corresponding to a sensor.
andrewm@17 273 for(int i = 1; i < bufferSize; i++) {
andrewm@17 274 bool active = (buffer[i] >= sensorThreshold_);
andrewm@17 275 setSensorState(buffer[0], i - 1, active);
andrewm@17 276 }
andrewm@17 277
andrewm@17 278 return true;
andrewm@17 279 }
andrewm@17 280
andrewm@17 281 #endif // ENABLE_TOUCHKEYS_SENSOR_TEST