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@44
|
69 key == highestMidiNote_, (key == currentlyActiveKey_) || (key == currentHighlightedKey_), 1);
|
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@44
|
82 drawBlackKey(0, 0, (key == currentlyActiveKey_) || (key == currentHighlightedKey_), 1);
|
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 glFlush();
|
andrewm@17
|
92 }
|
andrewm@17
|
93
|
andrewm@18
|
94 // Called when a given key is clicked by mouse
|
andrewm@18
|
95 void KeyboardTesterDisplay::keyClicked(int key) {
|
andrewm@18
|
96 controller_.touchkeySensorTestSetKey(key);
|
andrewm@18
|
97 }
|
andrewm@18
|
98
|
andrewm@17
|
99 // Set the threshold level at which a sensor is considered active
|
andrewm@17
|
100 void KeyboardTesterDisplay::setSensorThreshold(int threshold) {
|
andrewm@17
|
101 sensorThreshold_ = threshold;
|
andrewm@17
|
102 }
|
andrewm@17
|
103
|
andrewm@17
|
104 // Set the state of a given sensor on a given key to be on or off,
|
andrewm@17
|
105 // based on some externally-computed threshold. Sensors that are on
|
andrewm@17
|
106 // will flip the "good" flag to true, which remains set until cleared
|
andrewm@17
|
107 // externally.
|
andrewm@17
|
108 void KeyboardTesterDisplay::setSensorState(int key, int sensor, bool active) {
|
andrewm@17
|
109 if(key < 0 || key > 127)
|
andrewm@17
|
110 return;
|
andrewm@17
|
111 if(sensor < 0 || sensor >= kNumSensorsPerKey)
|
andrewm@17
|
112 return;
|
andrewm@17
|
113 if(active) {
|
andrewm@17
|
114 keySensorActive_[key] |= (1 << sensor);
|
andrewm@17
|
115 keySensorGood_[key] |= (1 << sensor);
|
andrewm@17
|
116 }
|
andrewm@17
|
117 else
|
andrewm@17
|
118 keySensorActive_[key] &= ~(1 << sensor);
|
andrewm@17
|
119 currentlyActiveKey_ = key;
|
andrewm@41
|
120 tellCanvasToRepaint();
|
andrewm@17
|
121
|
andrewm@17
|
122 if(allSensorsGood(currentlyActiveKey_)) {
|
andrewm@17
|
123 controller_.touchkeySensorTestSetKey(key + 1);
|
andrewm@17
|
124 }
|
andrewm@17
|
125 }
|
andrewm@17
|
126
|
andrewm@17
|
127 // Indicate whether all sensors have shown an active value on this key
|
andrewm@17
|
128 bool KeyboardTesterDisplay::allSensorsGood(int key) {
|
andrewm@17
|
129 if(key < 0 || key > 127)
|
andrewm@17
|
130 return false;
|
andrewm@17
|
131 unsigned int mask = (1 << kNumSensorsPerKey) - 1;
|
andrewm@17
|
132
|
andrewm@17
|
133 return ((keySensorGood_[key] & mask) == mask);
|
andrewm@17
|
134 }
|
andrewm@17
|
135
|
andrewm@17
|
136 // Reset the sensor state to all off
|
andrewm@17
|
137 void KeyboardTesterDisplay::resetSensorState(int key) {
|
andrewm@17
|
138 if(key < 0 || key > 127)
|
andrewm@17
|
139 return;
|
andrewm@17
|
140 keySensorGood_[key] = 0;
|
andrewm@17
|
141 keySensorActive_[key] = 0;
|
andrewm@17
|
142 if(currentlyActiveKey_ == key)
|
andrewm@17
|
143 currentlyActiveKey_ = -1;
|
andrewm@17
|
144 }
|
andrewm@17
|
145
|
andrewm@17
|
146 // Draw the given key sensors as being active, good, or inactive
|
andrewm@18
|
147 void KeyboardTesterDisplay::drawSensorState(int key, float x, float y, float width, float height, bool white, float whiteOffset) {
|
andrewm@17
|
148 float heightInset = height / ((float)kNumSensorsPerKey * 10);
|
andrewm@17
|
149
|
andrewm@17
|
150 glPushMatrix();
|
andrewm@17
|
151 glTranslatef(x, y, 0);
|
andrewm@17
|
152
|
andrewm@18
|
153 if(white) {
|
andrewm@18
|
154 float hSensorWidth = kWhiteKeyFrontWidth * 0.25;
|
andrewm@17
|
155
|
andrewm@18
|
156 // Draw the first four sensors horizontally for the white keys
|
andrewm@18
|
157 glPushMatrix();
|
andrewm@17
|
158
|
andrewm@18
|
159 for(int i = 0; i < 4; i++) {
|
andrewm@18
|
160 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
andrewm@18
|
161 if(keySensorActive_[key] & (1 << i) && key == currentlyActiveKey_)
|
andrewm@18
|
162 glColor3f(1.0, 1.0, 0.0); // Sensor active right now = yellow
|
andrewm@18
|
163 else if(keySensorGood_[key] & (1 << i))
|
andrewm@18
|
164 glColor3f(0.0, 1.0, 0.0); // Sensor has been active (good) = green
|
andrewm@18
|
165 else
|
andrewm@18
|
166 glColor3f(1.0, 0.0, 0.0); // Sensor has not yet been active = red
|
andrewm@18
|
167
|
andrewm@18
|
168 glBegin(GL_POLYGON);
|
andrewm@18
|
169 glVertex2f(hSensorWidth * 0.1, heightInset);
|
andrewm@18
|
170 glVertex2f(hSensorWidth * 0.1, 4.0*height / (float)kNumSensorsPerKey - heightInset);
|
andrewm@18
|
171 glVertex2f(hSensorWidth * 0.9, 4.0*height / (float)kNumSensorsPerKey - heightInset);
|
andrewm@18
|
172 glVertex2f(hSensorWidth * 0.9, heightInset);
|
andrewm@18
|
173 glEnd();
|
andrewm@18
|
174 glTranslatef(hSensorWidth, 0, 0);
|
andrewm@18
|
175 }
|
andrewm@18
|
176
|
andrewm@18
|
177 glPopMatrix();
|
andrewm@18
|
178 glTranslatef(whiteOffset, 4.0*height / (float)kNumSensorsPerKey, 0);
|
andrewm@18
|
179
|
andrewm@18
|
180 for(int i = 4; i < kNumSensorsPerKey; i++) {
|
andrewm@18
|
181 // Draw each sensor in sequence: red = never activated; yellow = active; green = previously
|
andrewm@18
|
182 // activated (good)
|
andrewm@18
|
183 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
andrewm@18
|
184 if(keySensorActive_[key] & (1 << i) && key == currentlyActiveKey_)
|
andrewm@18
|
185 glColor3f(1.0, 1.0, 0.0); // Sensor active right now = yellow
|
andrewm@18
|
186 else if(keySensorGood_[key] & (1 << i))
|
andrewm@18
|
187 glColor3f(0.0, 1.0, 0.0); // Sensor has been active (good) = green
|
andrewm@18
|
188 else
|
andrewm@18
|
189 glColor3f(1.0, 0.0, 0.0); // Sensor has not yet been active = red
|
andrewm@18
|
190
|
andrewm@18
|
191 glBegin(GL_POLYGON);
|
andrewm@18
|
192 glVertex2f(width * 0.1, heightInset);
|
andrewm@18
|
193 glVertex2f(width * 0.1, height / (float)kNumSensorsPerKey - heightInset);
|
andrewm@18
|
194 glVertex2f(width * 0.9, height / (float)kNumSensorsPerKey - heightInset);
|
andrewm@18
|
195 glVertex2f(width * 0.9, heightInset);
|
andrewm@18
|
196 glEnd();
|
andrewm@18
|
197
|
andrewm@18
|
198 glTranslatef(0, height / (float)kNumSensorsPerKey, 0);
|
andrewm@18
|
199 }
|
andrewm@17
|
200 }
|
andrewm@18
|
201 else { // Black
|
andrewm@18
|
202 // Draw in two rows
|
andrewm@18
|
203 glPushMatrix();
|
andrewm@18
|
204 for(int i = 0; i < kNumSensorsPerKey / 2; i++) {
|
andrewm@18
|
205 // Draw each sensor in sequence: red = never activated; yellow = active; green = previously
|
andrewm@18
|
206 // activated (good)
|
andrewm@18
|
207 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
andrewm@18
|
208 if(keySensorActive_[key] & (1 << i) && key == currentlyActiveKey_)
|
andrewm@18
|
209 glColor3f(1.0, 1.0, 0.0); // Sensor active right now = yellow
|
andrewm@18
|
210 else if(keySensorGood_[key] & (1 << i))
|
andrewm@18
|
211 glColor3f(0.0, 1.0, 0.0); // Sensor has been active (good) = green
|
andrewm@18
|
212 else
|
andrewm@18
|
213 glColor3f(1.0, 0.0, 0.0); // Sensor has not yet been active = red
|
andrewm@18
|
214
|
andrewm@18
|
215 glBegin(GL_POLYGON);
|
andrewm@18
|
216 glVertex2f(width * 0.55, heightInset);
|
andrewm@18
|
217 glVertex2f(width * 0.55, 2.0*height / (float)kNumSensorsPerKey - heightInset);
|
andrewm@18
|
218 glVertex2f(width * 0.9, 2.0*height / (float)kNumSensorsPerKey - heightInset);
|
andrewm@18
|
219 glVertex2f(width * 0.9, heightInset);
|
andrewm@18
|
220 glEnd();
|
andrewm@18
|
221
|
andrewm@18
|
222 glTranslatef(0, 2.0*height / (float)kNumSensorsPerKey, 0);
|
andrewm@18
|
223 }
|
andrewm@18
|
224 glPopMatrix();
|
andrewm@18
|
225 glPushMatrix();
|
andrewm@18
|
226 for(int i = kNumSensorsPerKey / 2; i < kNumSensorsPerKey; i++) {
|
andrewm@18
|
227 // Draw each sensor in sequence: red = never activated; yellow = active; green = previously
|
andrewm@18
|
228 // activated (good)
|
andrewm@18
|
229 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
andrewm@18
|
230 if(keySensorActive_[key] & (1 << i) && key == currentlyActiveKey_)
|
andrewm@18
|
231 glColor3f(1.0, 1.0, 0.0); // Sensor active right now = yellow
|
andrewm@18
|
232 else if(keySensorGood_[key] & (1 << i))
|
andrewm@18
|
233 glColor3f(0.0, 1.0, 0.0); // Sensor has been active (good) = green
|
andrewm@18
|
234 else
|
andrewm@18
|
235 glColor3f(1.0, 0.0, 0.0); // Sensor has not yet been active = red
|
andrewm@18
|
236
|
andrewm@18
|
237 glBegin(GL_POLYGON);
|
andrewm@18
|
238 glVertex2f(width * 0.1, heightInset);
|
andrewm@18
|
239 glVertex2f(width * 0.1, 2.0*height / (float)kNumSensorsPerKey - heightInset);
|
andrewm@18
|
240 glVertex2f(width * 0.45, 2.0*height / (float)kNumSensorsPerKey - heightInset);
|
andrewm@18
|
241 glVertex2f(width * 0.45, heightInset);
|
andrewm@18
|
242 glEnd();
|
andrewm@18
|
243
|
andrewm@18
|
244 glTranslatef(0, 2.0*height / (float)kNumSensorsPerKey, 0);
|
andrewm@18
|
245 }
|
andrewm@18
|
246 glPopMatrix();
|
andrewm@18
|
247 }
|
andrewm@17
|
248 glPopMatrix();
|
andrewm@17
|
249 }
|
andrewm@17
|
250
|
andrewm@17
|
251 // OSC callback method, for when data comes in
|
andrewm@17
|
252 bool KeyboardTesterDisplay::oscHandlerMethod(const char *path, const char *types, int numValues, lo_arg **values, void *data) {
|
andrewm@17
|
253 // Look for a blob in value 0 holding the raw data
|
andrewm@17
|
254 if(numValues < 1)
|
andrewm@17
|
255 return false;
|
andrewm@17
|
256 if(types[0] != 'b')
|
andrewm@17
|
257 return false;
|
andrewm@17
|
258
|
andrewm@17
|
259 // Get OSC blob which holds raw data
|
andrewm@17
|
260 lo_blob blob = values[0];
|
andrewm@17
|
261 int bufferSize = lo_blob_datasize(blob);
|
andrewm@17
|
262 const unsigned char *buffer = (const unsigned char *)lo_blob_dataptr(blob);
|
andrewm@17
|
263
|
andrewm@17
|
264 // buffer[0] holds the key number from which this data came. Make sure it's sane.
|
andrewm@17
|
265 if(bufferSize == 0)
|
andrewm@17
|
266 return false;
|
andrewm@17
|
267 if(buffer[0] > 127)
|
andrewm@17
|
268 return false;
|
andrewm@17
|
269
|
andrewm@17
|
270 // The remainder is raw data, with each single byte corresponding to a sensor.
|
andrewm@17
|
271 for(int i = 1; i < bufferSize; i++) {
|
andrewm@17
|
272 bool active = (buffer[i] >= sensorThreshold_);
|
andrewm@17
|
273 setSensorState(buffer[0], i - 1, active);
|
andrewm@17
|
274 }
|
andrewm@17
|
275
|
andrewm@17
|
276 return true;
|
andrewm@17
|
277 }
|
andrewm@17
|
278
|
andrewm@17
|
279 #endif // ENABLE_TOUCHKEYS_SENSOR_TEST |