Mercurial > hg > touchkeys
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Display/KeyboardTesterDisplay.cpp Tue Nov 26 21:42:45 2013 +0000 @@ -0,0 +1,201 @@ +/* + TouchKeys: multi-touch musical keyboard control software + Copyright (c) 2013 Andrew McPherson + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ===================================================================== + + KeyboardTesterDisplay.cpp: A keyboard display for raw data that can be used + for testing the functionality of individual TouchKeys sensors +*/ + +#ifdef ENABLE_TOUCHKEYS_SENSOR_TEST + +#include "KeyboardTesterDisplay.h" +#include "../MainApplicationController.h" +#include "../TouchKeys/PianoKeyboard.h" + +const int KeyboardTesterDisplay::kNumSensorsPerKey = 26; +const int KeyboardTesterDisplay::kDefaultSensorThreshold = 16; + +// Constructor +KeyboardTesterDisplay::KeyboardTesterDisplay(MainApplicationController& controller, PianoKeyboard& keyboard) +: controller_(controller), keyboard_(keyboard), + currentlyActiveKey_(-1), sensorThreshold_(kDefaultSensorThreshold) { + for(int i = 0; i < 128; i++) + resetSensorState(i); + + setOscController(&keyboard_); + addOscListener("/touchkeys/rawbytes"); +} + +// Render the display starting with the underlying keyboard and +// then adding our own display info on top +void KeyboardTesterDisplay::render() { + if(lowestMidiNote_ == highestMidiNote_) + return; + + // Start with a light gray background + glClearColor(0.8, 0.8, 0.8, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + glLoadIdentity(); + + float invAspectRatio = totalDisplayWidth_ / totalDisplayHeight_; + float scaleValue = 2.0 / totalDisplayWidth_; + + glScalef(scaleValue, scaleValue * invAspectRatio, scaleValue); + glTranslatef(-1.0 / scaleValue, -totalDisplayHeight_ / 2.0, 0); + glTranslatef(kDisplaySideMargin, kDisplayBottomMargin, 0.0); + + glPushMatrix(); + + // Draw the keys themselves first, with analog values if present, then draw the touches + for(int key = lowestMidiNote_; key <= highestMidiNote_; key++) { + if(keyShape(key) >= 0) { + // White keys: draw and move the frame over for the next key + drawWhiteKey(0, 0, keyShape(key), key == lowestMidiNote_, + key == highestMidiNote_, (key == currentlyActiveKey_) || (key == currentHighlightedKey_)); + // Draw sensor state for this key + drawSensorState(key, kWhiteKeyBackOffsets[keyShape(key)], 0, kWhiteKeyBackWidths[keyShape(key)], kWhiteKeyFrontLength + kWhiteKeyBackLength); + glTranslatef(kWhiteKeyFrontWidth + kInterKeySpacing, 0, 0); + } + else { + // Black keys: draw and leave the frame in place + int previousWhiteKeyShape = keyShape(key - 1); + float offsetH = -1.0 + kWhiteKeyBackOffsets[previousWhiteKeyShape] + kWhiteKeyBackWidths[previousWhiteKeyShape]; + float offsetV = kWhiteKeyFrontLength + kWhiteKeyBackLength - kBlackKeyLength; + + glTranslatef(offsetH, offsetV, 0.0); + drawBlackKey(0, 0, (key == currentlyActiveKey_) || (key == currentHighlightedKey_)); + // Draw sensor state for this key + drawSensorState(key, 0, 0, kBlackKeyWidth, kBlackKeyLength); + glTranslatef(-offsetH, -offsetV, 0.0); + } + } + + // Restore to the original location we used when drawing the keys + glPopMatrix(); + + needsUpdate_ = false; + glFlush(); +} + +// Set the threshold level at which a sensor is considered active +void KeyboardTesterDisplay::setSensorThreshold(int threshold) { + sensorThreshold_ = threshold; +} + +// Set the state of a given sensor on a given key to be on or off, +// based on some externally-computed threshold. Sensors that are on +// will flip the "good" flag to true, which remains set until cleared +// externally. +void KeyboardTesterDisplay::setSensorState(int key, int sensor, bool active) { + if(key < 0 || key > 127) + return; + if(sensor < 0 || sensor >= kNumSensorsPerKey) + return; + if(active) { + keySensorActive_[key] |= (1 << sensor); + keySensorGood_[key] |= (1 << sensor); + } + else + keySensorActive_[key] &= ~(1 << sensor); + currentlyActiveKey_ = key; + needsUpdate_ = true; + + if(allSensorsGood(currentlyActiveKey_)) { + controller_.touchkeySensorTestSetKey(key + 1); + } +} + +// Indicate whether all sensors have shown an active value on this key +bool KeyboardTesterDisplay::allSensorsGood(int key) { + if(key < 0 || key > 127) + return false; + unsigned int mask = (1 << kNumSensorsPerKey) - 1; + + return ((keySensorGood_[key] & mask) == mask); +} + +// Reset the sensor state to all off +void KeyboardTesterDisplay::resetSensorState(int key) { + if(key < 0 || key > 127) + return; + keySensorGood_[key] = 0; + keySensorActive_[key] = 0; + if(currentlyActiveKey_ == key) + currentlyActiveKey_ = -1; +} + +// Draw the given key sensors as being active, good, or inactive +void KeyboardTesterDisplay::drawSensorState(int key, float x, float y, float width, float height) { + float heightInset = height / ((float)kNumSensorsPerKey * 10); + + glPushMatrix(); + glTranslatef(x, y, 0); + + for(int i = 0; i < kNumSensorsPerKey; i++) { + // Draw each sensor in sequence: red = never activated; yellow = active; green = previously + // activated (good) + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + if(keySensorActive_[key] & (1 << i) && key == currentlyActiveKey_) + glColor3f(1.0, 1.0, 0.0); // Sensor active right now = yellow + else if(keySensorGood_[key] & (1 << i)) + glColor3f(0.0, 1.0, 0.0); // Sensor has been active (good) = green + else + glColor3f(1.0, 0.0, 0.0); // Sensor has not yet been active = red + + glBegin(GL_POLYGON); + glVertex2f(width * 0.1, heightInset); + glVertex2f(width * 0.1, height / (float)kNumSensorsPerKey - heightInset); + glVertex2f(width * 0.9, height / (float)kNumSensorsPerKey - heightInset); + glVertex2f(width * 0.9, heightInset); + glEnd(); + + glTranslatef(0, height / (float)kNumSensorsPerKey, 0); + } + + glPopMatrix(); +} + +// OSC callback method, for when data comes in +bool KeyboardTesterDisplay::oscHandlerMethod(const char *path, const char *types, int numValues, lo_arg **values, void *data) { + // Look for a blob in value 0 holding the raw data + if(numValues < 1) + return false; + if(types[0] != 'b') + return false; + + // Get OSC blob which holds raw data + lo_blob blob = values[0]; + int bufferSize = lo_blob_datasize(blob); + const unsigned char *buffer = (const unsigned char *)lo_blob_dataptr(blob); + + // buffer[0] holds the key number from which this data came. Make sure it's sane. + if(bufferSize == 0) + return false; + if(buffer[0] > 127) + return false; + + // The remainder is raw data, with each single byte corresponding to a sensor. + for(int i = 1; i < bufferSize; i++) { + bool active = (buffer[i] >= sensorThreshold_); + setSensorState(buffer[0], i - 1, active); + } + + return true; +} + +#endif // ENABLE_TOUCHKEYS_SENSOR_TEST \ No newline at end of file