comparison Source/Display/KeyboardTesterDisplay.cpp @ 17:73d2ec21de9a

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