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