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