annotate Source/TouchKeys/TouchkeyDevice.cpp @ 56:b4a2d2ae43cf tip

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents ff5d65c69e73
children
rev   line source
andrewm@0 1 /*
andrewm@0 2 TouchKeys: multi-touch musical keyboard control software
andrewm@0 3 Copyright (c) 2013 Andrew McPherson
andrewm@0 4
andrewm@0 5 This program is free software: you can redistribute it and/or modify
andrewm@0 6 it under the terms of the GNU General Public License as published by
andrewm@0 7 the Free Software Foundation, either version 3 of the License, or
andrewm@0 8 (at your option) any later version.
andrewm@0 9
andrewm@0 10 This program is distributed in the hope that it will be useful,
andrewm@0 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
andrewm@0 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
andrewm@0 13 GNU General Public License for more details.
andrewm@0 14
andrewm@0 15 You should have received a copy of the GNU General Public License
andrewm@0 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
andrewm@0 17
andrewm@0 18 =====================================================================
andrewm@0 19
andrewm@0 20 TouchkeyDevice.cpp: handles communication with the TouchKeys hardware
andrewm@0 21 */
andrewm@0 22
andrewm@0 23 #include <iomanip>
andrewm@0 24 #include <stdio.h>
andrewm@0 25 #include <stdlib.h>
andrewm@0 26 #include "TouchkeyDevice.h"
andrewm@0 27
andrewm@0 28 const char* kKeyNames[13] = {"C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B ", "c "};
andrewm@0 29
andrewm@0 30 // Constructor
andrewm@0 31
andrewm@0 32 TouchkeyDevice::TouchkeyDevice(PianoKeyboard& keyboard)
andrewm@23 33 : keyboard_(keyboard),
andrewm@23 34 #ifdef _MSC_VER
andrewm@23 35 serialHandle_(INVALID_HANDLE_VALUE),
andrewm@23 36 #else
andrewm@23 37 device_(-1),
andrewm@23 38 #endif
andrewm@0 39 ioThread_(boost::bind(&TouchkeyDevice::runLoop, this, _1), "TouchKeyDevice::ioThread"),
andrewm@0 40 rawDataThread_(boost::bind(&TouchkeyDevice::rawDataRunLoop, this, _1), "TouchKeyDevice::rawDataThread"),
andrewm@0 41 autoGathering_(false), shouldStop_(false), sendRawOscMessages_(false),
andrewm@28 42 verbose_(0), numOctaves_(0), lowestMidiNote_(48), lowestKeyPresentMidiNote_(48),
andrewm@48 43 updatedLowestMidiNote_(48), lowestNotePerOctave_(0),
andrewm@48 44 deviceSoftwareVersion_(-1), deviceHardwareVersion_(-1),
andrewm@0 45 expectedLengthWhite_(kTransmissionLengthWhiteNewHardware),
andrewm@0 46 expectedLengthBlack_(kTransmissionLengthBlackNewHardware), deviceHasRGBLEDs_(false),
andrewm@0 47 ledThread_(boost::bind(&TouchkeyDevice::ledUpdateLoop, this, _1), "TouchKeyDevice::ledThread"),
andrewm@0 48 isCalibrated_(false), calibrationInProgress_(false),
andrewm@0 49 keyCalibrators_(0), keyCalibratorsLength_(0), sensorDisplay_(0)
andrewm@0 50 {
andrewm@0 51 // Tell the piano keyboard class how to call us back
andrewm@0 52 keyboard_.setTouchkeyDevice(this);
andrewm@0 53
andrewm@0 54 // Initialize the frame -> timestamp synchronization. Frame interval is nominally 1ms,
andrewm@0 55 // but this class helps us find the actual rate which might drift slightly, and it keeps
andrewm@0 56 // the time stamps of each data point in sync with other streams.
andrewm@0 57 timestampSynchronizer_.initialize(Time::getMillisecondCounterHiRes(), keyboard_.schedulerCurrentTimestamp());
andrewm@0 58 timestampSynchronizer_.setNominalSampleInterval(.001);
andrewm@0 59 timestampSynchronizer_.setFrameModulus(65536);
andrewm@0 60
andrewm@0 61 for(int i = 0; i < 4; i++)
andrewm@0 62 analogLastFrame_[i] = 0;
andrewm@0 63
andrewm@0 64 logFileCreated_ = false;
andrewm@0 65 loggingActive_ = false;
andrewm@0 66 }
andrewm@0 67
andrewm@0 68
andrewm@0 69 // ------------------------------------------------------
andrewm@0 70 // create a new MIDI log file, ready to have data written to it
andrewm@0 71 void TouchkeyDevice::createLogFiles(string keyTouchLogFilename, string analogLogFilename, string path)
andrewm@0 72 {
andrewm@0 73 if (path.compare("") != 0)
andrewm@0 74 {
andrewm@0 75 path = path + "/";
andrewm@0 76 }
andrewm@0 77
andrewm@0 78 keyTouchLogFilename = path + keyTouchLogFilename;
andrewm@0 79 analogLogFilename = path + analogLogFilename;
andrewm@0 80
andrewm@0 81 char *fileName = (char*)keyTouchLogFilename.c_str();
andrewm@0 82
andrewm@0 83 // create output file for key touch
andrewm@0 84 keyTouchLog_.open (fileName, ios::out | ios::binary);
andrewm@0 85 keyTouchLog_.seekp(0);
andrewm@0 86
andrewm@0 87 fileName = (char*)analogLogFilename.c_str();
andrewm@0 88
andrewm@0 89 // create output file for analog data
andrewm@0 90 analogLog_.open (fileName, ios::out | ios::binary);
andrewm@0 91 analogLog_.seekp(0);
andrewm@0 92
andrewm@0 93 // indicate that we have created a log file (so we can close it later)
andrewm@0 94 logFileCreated_ = true;
andrewm@0 95 }
andrewm@0 96
andrewm@0 97 // ------------------------------------------------------
andrewm@0 98 // close the log file
andrewm@0 99 void TouchkeyDevice::closeLogFile()
andrewm@0 100 {
andrewm@0 101 if (logFileCreated_)
andrewm@0 102 {
andrewm@0 103 keyTouchLog_.close();
andrewm@0 104 analogLog_.close();
andrewm@0 105 logFileCreated_ = false;
andrewm@0 106 }
andrewm@0 107 loggingActive_ = false;
andrewm@0 108 }
andrewm@0 109
andrewm@0 110 // ------------------------------------------------------
andrewm@0 111 // start logging midi data
andrewm@0 112 void TouchkeyDevice::startLogging()
andrewm@0 113 {
andrewm@0 114 loggingActive_ = true;
andrewm@0 115 }
andrewm@0 116
andrewm@0 117 // ------------------------------------------------------
andrewm@0 118 // stop logging midi data
andrewm@0 119 void TouchkeyDevice::stopLogging()
andrewm@0 120 {
andrewm@0 121 loggingActive_ = false;
andrewm@0 122 }
andrewm@0 123
andrewm@0 124 // Open the touchkey device (a USB CDC device). Returns true on success.
andrewm@0 125
andrewm@0 126 bool TouchkeyDevice::openDevice(const char * inputDevicePath) {
andrewm@0 127 // If the device is already open, close it
andrewm@23 128 if(isOpen())
andrewm@0 129 closeDevice();
andrewm@0 130
andrewm@0 131 // Open the device
andrewm@20 132 #ifdef _MSC_VER
andrewm@23 133 // Open the serial port
andrewm@28 134 serialHandle_ = CreateFile(inputDevicePath, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
andrewm@28 135 if(serialHandle_ == INVALID_HANDLE_VALUE) {
andrewm@28 136 Logger::writeToLog("Unable to open serial port " + String(inputDevicePath));
andrewm@28 137 return false;
andrewm@28 138 }
andrewm@28 139
andrewm@28 140 // Set some serial parameters, though they don't actually affect the operation
andrewm@28 141 // of the port since it is all native USB
andrewm@28 142 DCB serialParams = { 0 };
andrewm@28 143 serialParams.DCBlength = sizeof(serialParams);
andrewm@28 144
andrewm@28 145 if(!BuildCommDCBA("baud=1000000 data=8 parity=N stop=1 dtr=on rts=on", &serialParams)) {
andrewm@28 146 Logger::writeToLog("Unable to create port settings\n");
andrewm@28 147 CloseHandle(serialHandle_);
andrewm@28 148 serialHandle_ = INVALID_HANDLE_VALUE;
andrewm@28 149 return false;
andrewm@28 150 }
andrewm@28 151
andrewm@28 152 if(!SetCommState(serialHandle_, &serialParams)) {
andrewm@28 153 Logger::writeToLog("Unable to set comm state\n");
andrewm@28 154 CloseHandle(serialHandle_);
andrewm@28 155 serialHandle_ = INVALID_HANDLE_VALUE;
andrewm@28 156 return false;
andrewm@28 157 }
andrewm@28 158
andrewm@28 159 // Set timeouts
andrewm@28 160 COMMTIMEOUTS timeout = { 0 };
andrewm@28 161 timeout.ReadIntervalTimeout = MAXDWORD;
andrewm@28 162 timeout.ReadTotalTimeoutConstant = 0;
andrewm@28 163 timeout.ReadTotalTimeoutMultiplier = 0;
andrewm@28 164 timeout.WriteTotalTimeoutConstant = 0;
andrewm@28 165 timeout.WriteTotalTimeoutMultiplier = 0;
andrewm@28 166
andrewm@23 167 if(!SetCommTimeouts(serialHandle_, &timeout)) {
andrewm@23 168 Logger::writeToLog("Unable to set timeouts\n");
andrewm@23 169 CloseHandle(serialHandle_);
andrewm@23 170 serialHandle_ = INVALID_HANDLE_VALUE;
andrewm@23 171 return false;
andrewm@23 172 }
andrewm@20 173 #else
andrewm@0 174 device_ = open(inputDevicePath, O_RDWR | O_NOCTTY | O_NDELAY);
andrewm@20 175
andrewm@21 176 if(device_ < 0) {
andrewm@0 177 return false;
andrewm@21 178 }
andrewm@20 179 #endif
andrewm@0 180 return true;
andrewm@0 181 }
andrewm@0 182
andrewm@0 183 // Close the touchkey serial device
andrewm@0 184 void TouchkeyDevice::closeDevice() {
andrewm@23 185 if(!isOpen())
andrewm@0 186 return;
andrewm@0 187
andrewm@0 188 stopAutoGathering();
andrewm@0 189 keysPresent_.clear();
andrewm@20 190
andrewm@20 191 #ifdef _MSC_VER
andrewm@23 192 CloseHandle(serialHandle_);
andrewm@23 193 serialHandle_ = INVALID_HANDLE_VALUE;
andrewm@20 194 #else
andrewm@0 195 close(device_);
andrewm@0 196 device_ = -1;
andrewm@20 197 #endif
andrewm@0 198 }
andrewm@0 199
andrewm@23 200 bool TouchkeyDevice::isOpen() {
andrewm@23 201 #ifdef _MSC_VER
andrewm@23 202 return serialHandle_ != INVALID_HANDLE_VALUE;
andrewm@23 203 #else
andrewm@23 204 return device_ >= 0;
andrewm@23 205 #endif
andrewm@23 206 }
andrewm@23 207
andrewm@0 208 // Check if the device is present and ready to respond. If status is not null, store the current
andrewm@0 209 // controller status information.
andrewm@0 210
andrewm@0 211 bool TouchkeyDevice::checkIfDevicePresent(int millisecondsToWait) {
andrewm@0 212 //struct timeval startTime, currentTime;
andrewm@0 213 double startTime, currentTime;
andrewm@0 214 unsigned char ch;
andrewm@0 215 bool controlSeq = false, startingFrame = false;
andrewm@0 216
andrewm@23 217 if(!isOpen())
andrewm@0 218 return false;
andrewm@22 219 deviceFlush(false);
andrewm@22 220
andrewm@22 221 if(deviceWrite((char*)kCommandStatus, 5) < 0) { // Write status command
andrewm@7 222 if(verbose_ >= 1)
andrewm@7 223 cout << "ERROR: unable to write status command. errno = " << errno << endl;
andrewm@0 224 return false;
andrewm@0 225 }
andrewm@22 226
andrewm@20 227
andrewm@0 228 // Wait the specified amount of time for a response before giving up
andrewm@0 229 startTime = Time::getMillisecondCounterHiRes();
andrewm@0 230 currentTime = startTime;
andrewm@0 231
andrewm@0 232 while(currentTime - startTime < (double)millisecondsToWait) {
andrewm@22 233 long count = deviceRead((char *)&ch, 1);
andrewm@0 234
andrewm@0 235 if(count < 0) { // Check if an error occurred on read
andrewm@0 236 if(errno != EAGAIN) {
andrewm@7 237 if(verbose_ >= 1)
andrewm@7 238 cout << "Unable to read from device (error " << errno << "). Aborting.\n";
andrewm@0 239 return false;
andrewm@0 240 }
andrewm@0 241 }
andrewm@0 242 else if(count > 0) { // Data received
andrewm@0 243 // Wait for a frame back that is of type status. We don't even care what the
andrewm@0 244 // status is at this point, just that we got something.
andrewm@0 245
andrewm@0 246 if(controlSeq) {
andrewm@0 247 controlSeq = false;
andrewm@0 248 if(ch == kControlCharacterFrameBegin)
andrewm@0 249 startingFrame = true;
andrewm@0 250 else
andrewm@0 251 startingFrame = false;
andrewm@0 252 }
andrewm@0 253 else {
andrewm@0 254 if(ch == ESCAPE_CHARACTER) {
andrewm@0 255 controlSeq = true;
andrewm@0 256 }
andrewm@0 257 else if(startingFrame) {
andrewm@0 258 if(ch == kFrameTypeStatus) {
andrewm@0 259 ControllerStatus status;
andrewm@0 260 unsigned char statusBuf[TOUCHKEY_MAX_FRAME_LENGTH];
andrewm@0 261 int statusBufLength = 0;
andrewm@0 262 bool frameError = false;
andrewm@0 263
andrewm@0 264 // Gather and parse the status frame
andrewm@0 265
andrewm@0 266 while(currentTime - startTime < millisecondsToWait) {
andrewm@22 267 count = deviceRead((char *)&ch, 1);
andrewm@22 268
andrewm@0 269 if(count == 0)
andrewm@0 270 continue;
andrewm@0 271 if(count < 0) {
andrewm@0 272 if(errno != EAGAIN && verbose_ >= 1) { // EAGAIN just means no data was available
andrewm@7 273 if(verbose_ >= 1)
andrewm@7 274 cout << "Unable to read from device (error " << errno << "). Aborting.\n";
andrewm@0 275 return false;
andrewm@0 276 }
andrewm@0 277
andrewm@0 278 continue;
andrewm@0 279 }
andrewm@0 280
andrewm@0 281 if(controlSeq) {
andrewm@0 282 controlSeq = false;
andrewm@0 283 if(ch == kControlCharacterFrameEnd) { // frame finished?
andrewm@0 284 break;
andrewm@0 285 }
andrewm@0 286 else if(ch == kControlCharacterFrameError) // device telling us about an internal comm error
andrewm@0 287 frameError = true;
andrewm@0 288 else if(ch == ESCAPE_CHARACTER) { // double-escape means a literal escape character
andrewm@0 289 statusBuf[statusBufLength++] = (unsigned char)ch;
andrewm@0 290 if(statusBufLength >= TOUCHKEY_MAX_FRAME_LENGTH) {
andrewm@0 291 frameError = true;
andrewm@0 292 break;
andrewm@0 293 }
andrewm@0 294 }
andrewm@0 295 else if(ch == kControlCharacterNak && verbose_ >= 1) {
andrewm@0 296 cout << "Warning: received NAK\n";
andrewm@0 297 }
andrewm@0 298 }
andrewm@0 299 else {
andrewm@0 300 if(ch == ESCAPE_CHARACTER)
andrewm@0 301 controlSeq = true;
andrewm@0 302 else {
andrewm@0 303 statusBuf[statusBufLength++] = (unsigned char)ch;
andrewm@0 304 if(statusBufLength >= TOUCHKEY_MAX_FRAME_LENGTH) {
andrewm@0 305 frameError = true;
andrewm@0 306 break;
andrewm@0 307 }
andrewm@0 308 }
andrewm@0 309 }
andrewm@0 310
andrewm@0 311 currentTime = Time::getMillisecondCounterHiRes();
andrewm@0 312 }
andrewm@0 313
andrewm@0 314 if(frameError) {
andrewm@0 315 if(verbose_ >= 1)
andrewm@0 316 cout << "Warning: device present, but frame error received trying to get status.\n";
andrewm@0 317 }
andrewm@0 318 else if(processStatusFrame(statusBuf, statusBufLength, &status)) {
andrewm@0 319 // Clear keys present in preparation to read new list of keys
andrewm@0 320 keysPresent_.clear();
andrewm@0 321
andrewm@0 322 numOctaves_ = status.octaves;
andrewm@0 323 deviceSoftwareVersion_ = status.softwareVersionMajor;
andrewm@0 324 deviceHardwareVersion_ = status.hardwareVersion;
andrewm@0 325 deviceHasRGBLEDs_ = status.hasRGBLEDs;
andrewm@0 326 lowestKeyPresentMidiNote_ = 127;
andrewm@0 327
andrewm@0 328 if(verbose_ >= 1) {
andrewm@0 329 cout << endl << "Found Device: Hardware Version " << status.hardwareVersion;
andrewm@0 330 cout << " Software Version " << status.softwareVersionMajor << "." << status.softwareVersionMinor;
andrewm@0 331 cout << endl << " " << status.octaves << " octaves connected" << endl;
andrewm@0 332 }
andrewm@7 333
andrewm@0 334 for(int i = 0; i < status.octaves; i++) {
andrewm@0 335 bool foundKey = false;
andrewm@0 336
andrewm@0 337 if(verbose_ >= 1) cout << " Octave " << i << ": ";
andrewm@0 338 for(int j = 0; j < 13; j++) {
andrewm@0 339 if(status.connectedKeys[i] & (1<<j)) {
andrewm@0 340 if(verbose_ >= 1) cout << kKeyNames[j] << " ";
andrewm@0 341 keysPresent_.insert(octaveNoteToIndex(i, j));
andrewm@0 342 foundKey = true;
andrewm@0 343 if(octaveKeyToMidi(i, j) < lowestKeyPresentMidiNote_)
andrewm@0 344 lowestKeyPresentMidiNote_ = octaveKeyToMidi(i, j);
andrewm@0 345 }
andrewm@0 346 else {
andrewm@0 347 if(verbose_ >= 1) cout << "- ";
andrewm@0 348 }
andrewm@0 349
andrewm@0 350 }
andrewm@0 351
andrewm@7 352 if(verbose_ >= 1) cout << endl;
andrewm@0 353 }
andrewm@0 354
andrewm@0 355 // Hardware version determines whether all keys have XY or not
andrewm@0 356 if(status.hardwareVersion >= 2) {
andrewm@0 357 expectedLengthWhite_ = kTransmissionLengthWhiteNewHardware;
andrewm@0 358 expectedLengthBlack_ = kTransmissionLengthBlackNewHardware;
andrewm@0 359 whiteMaxX_ = kWhiteMaxXValueNewHardware;
andrewm@0 360 whiteMaxY_ = kWhiteMaxYValueNewHardware;
andrewm@0 361 blackMaxX_ = kBlackMaxXValueNewHardware;
andrewm@0 362 blackMaxY_ = kBlackMaxYValueNewHardware;
andrewm@0 363 }
andrewm@0 364 else {
andrewm@0 365 expectedLengthWhite_ = kTransmissionLengthWhiteOldHardware;
andrewm@0 366 expectedLengthBlack_ = kTransmissionLengthBlackOldHardware;
andrewm@0 367 whiteMaxX_ = kWhiteMaxXValueOldHardware;
andrewm@0 368 whiteMaxY_ = kWhiteMaxYValueOldHardware;
andrewm@0 369 blackMaxX_ = 1.0; // irrelevant -- no X data
andrewm@0 370 blackMaxY_ = kBlackMaxYValueOldHardware;
andrewm@0 371 }
andrewm@0 372
andrewm@0 373 // Software version indicates what information is available. On version
andrewm@0 374 // 2 and greater, can indicate which is lowest sensor available. Might
andrewm@0 375 // be different from lowest connected key.
andrewm@0 376 if(status.softwareVersionMajor >= 2) {
andrewm@0 377 lowestKeyPresentMidiNote_ = octaveKeyToMidi(0, status.lowestHardwareNote);
andrewm@48 378
andrewm@48 379 if(status.softwareVersionMinor == 1) {
andrewm@48 380 // Version 2.1 uses the lowest MIDI note to handle keyboards which don't
andrewm@48 381 // begin and end at C, e.g. E-E or F-F keyboards.
andrewm@48 382 lowestNotePerOctave_ = status.lowestHardwareNote;
andrewm@48 383 }
andrewm@48 384 else {
andrewm@48 385 lowestNotePerOctave_ = 0;
andrewm@48 386 }
andrewm@0 387 }
andrewm@0 388 else if(lowestKeyPresentMidiNote_ == 127) // No keys found and old device software
andrewm@0 389 lowestKeyPresentMidiNote_ = lowestMidiNote_;
andrewm@0 390
andrewm@48 391 keyboard_.setKeyboardGUIRange(lowestKeyPresentMidiNote_, lowestMidiNote_ + 12*numOctaves_ + lowestNotePerOctave_);
andrewm@0 392 calibrationInit(12*numOctaves_ + 1); // One more for the top C
andrewm@0 393 }
andrewm@0 394 else {
andrewm@0 395 if(verbose_ >= 1) cout << "Warning: device present, but received invalid status frame.\n";
andrewm@22 396 deviceFlush(true);
andrewm@0 397 return false; // Yes... found the device
andrewm@0 398 }
andrewm@0 399
andrewm@22 400 deviceFlush(true);
andrewm@0 401 return true; // Yes... found the device
andrewm@0 402 }
andrewm@0 403 }
andrewm@0 404
andrewm@0 405 startingFrame = false;
andrewm@0 406 }
andrewm@0 407 }
andrewm@0 408
andrewm@0 409 currentTime = Time::getMillisecondCounterHiRes();
andrewm@0 410 }
andrewm@0 411
andrewm@0 412 return false;
andrewm@0 413 }
andrewm@0 414
andrewm@0 415 // Start a run loop thread to receive centroid data. Returns true
andrewm@0 416 // on success.
andrewm@0 417
andrewm@0 418 bool TouchkeyDevice::startAutoGathering() {
andrewm@0 419 // Can only start if the device is open
andrewm@0 420 if(!isOpen())
andrewm@0 421 return false;
andrewm@0 422 // Already running?
andrewm@0 423 if(autoGathering_)
andrewm@0 424 return true;
andrewm@0 425 shouldStop_ = false;
andrewm@0 426 ledShouldStop_ = false;
andrewm@0 427
andrewm@0 428 if(verbose_ >= 1)
andrewm@0 429 cout << "Starting auto centroid collection\n";
andrewm@0 430
andrewm@0 431 // Start the data input and LED threads
andrewm@0 432 ioThread_.startThread();
andrewm@0 433 ledThread_.startThread();
andrewm@0 434 autoGathering_ = true;
andrewm@0 435
andrewm@0 436 // Tell the device to start scanning for new data
andrewm@22 437 if(deviceWrite((char*)kCommandStartScanning, 5) < 0) {
andrewm@7 438 if(verbose_ >= 1)
andrewm@7 439 cout << "ERROR: unable to write startAutoGather command. errno = " << errno << endl;
andrewm@0 440 }
andrewm@20 441
andrewm@0 442 keyboard_.sendMessage("/touchkeys/allnotesoff", "", LO_ARGS_END);
andrewm@0 443 if(keyboard_.gui() != 0) {
andrewm@0 444 // Update display: touch sensing enabled, which keys connected, no current touches
andrewm@0 445 keyboard_.gui()->setTouchSensingEnabled(true);
andrewm@0 446 for(set<int>::iterator it = keysPresent_.begin(); it != keysPresent_.end(); ++it) {
andrewm@0 447 keyboard_.gui()->setTouchSensorPresentForKey(octaveKeyToMidi(indexToOctave(*it), indexToNote(*it)), true);
andrewm@0 448 }
andrewm@0 449 keyboard_.gui()->clearAllTouches();
andrewm@0 450 keyboard_.gui()->clearAnalogData();
andrewm@0 451 }
andrewm@0 452
andrewm@0 453 return true;
andrewm@0 454 }
andrewm@0 455
andrewm@0 456 // Stop the run loop if applicable
andrewm@28 457 void TouchkeyDevice::stopAutoGathering(bool writeStopCommandToDevice) {
andrewm@0 458 // Check if actually running
andrewm@0 459 if(!autoGathering_ || !isOpen())
andrewm@0 460 return;
andrewm@0 461 // Stop any calibration in progress
andrewm@0 462 calibrationAbort();
andrewm@0 463
andrewm@28 464 if(writeStopCommandToDevice) {
andrewm@28 465 // Tell device to stop scanning
andrewm@28 466 if(deviceWrite((char*)kCommandStopScanning, 5) < 0) {
andrewm@28 467 if(verbose_ >= 1)
andrewm@28 468 cout << "ERROR: unable to write stopAutoGather command. errno = " << errno << endl;
andrewm@28 469 }
andrewm@28 470 }
andrewm@0 471
andrewm@0 472 // Setting this to true tells the run loop to exit what it's doing
andrewm@0 473 shouldStop_ = true;
andrewm@0 474 ledShouldStop_ = true;
andrewm@0 475
andrewm@0 476 if(verbose_ >= 1)
andrewm@0 477 cout << "Stopping auto centroid collection\n";
andrewm@0 478
andrewm@0 479 // Wait for run loop thread to finish. Set a timeout in case there's
andrewm@0 480 // some sort of device hangup
andrewm@28 481 if(ioThread_.getThreadId() != Thread::getCurrentThreadId())
andrewm@28 482 if(ioThread_.isThreadRunning())
andrewm@28 483 ioThread_.stopThread(3000);
andrewm@28 484 if(ledThread_.getThreadId() != Thread::getCurrentThreadId())
andrewm@28 485 if(ledThread_.isThreadRunning())
andrewm@28 486 ledThread_.stopThread(3000);
andrewm@28 487 if(rawDataThread_.getThreadId() != Thread::getCurrentThreadId())
andrewm@28 488 if(rawDataThread_.isThreadRunning())
andrewm@28 489 rawDataThread_.stopThread(3000);
andrewm@0 490
andrewm@0 491 // Stop any currently playing notes
andrewm@0 492 keyboard_.sendMessage("/touchkeys/allnotesoff", "", LO_ARGS_END);
andrewm@0 493
andrewm@0 494 // Clear touch for all keys
andrewm@0 495 //std::pair<int, int> keyboardRange = keyboard_.keyboardRange();
andrewm@0 496 //for(int i = keyboardRange.first; i <= keyboardRange.second; i++)
andrewm@0 497 for(int i = 0; i <= 127; i++)
andrewm@0 498 if(keyboard_.key(i) != 0)
andrewm@0 499 keyboard_.key(i)->touchOff(lastTimestamp_);
andrewm@0 500
andrewm@0 501 if(keyboard_.gui() != 0) {
andrewm@0 502 // Update display: touch sensing disabled
andrewm@0 503 keyboard_.gui()->clearAllTouches();
andrewm@0 504 keyboard_.gui()->setTouchSensingEnabled(false);
andrewm@0 505 keyboard_.gui()->clearAnalogData();
andrewm@0 506 }
andrewm@0 507
andrewm@0 508 if(verbose_ >= 2)
andrewm@0 509 cout << "...done.\n";
andrewm@0 510
andrewm@0 511 autoGathering_ = false;
andrewm@0 512 }
andrewm@0 513
andrewm@0 514 // Begin raw data collection from a given single key
andrewm@0 515
andrewm@0 516 bool TouchkeyDevice::startRawDataCollection(int octave, int key, int mode, int scaler) {
andrewm@0 517 if(!isOpen())
andrewm@0 518 return false;
andrewm@0 519
andrewm@0 520 stopAutoGathering(); // Stop the thread if it's running
andrewm@0 521
andrewm@17 522 // Account for the high C which is ID 12 on the top octave
andrewm@17 523 if(octave == numOctaves_ && key == 0) {
andrewm@17 524 octave--;
andrewm@17 525 key = 12;
andrewm@17 526 }
andrewm@0 527
andrewm@0 528 rawDataCurrentOctave_ = octave;
andrewm@0 529 rawDataCurrentKey_ = key;
andrewm@17 530 rawDataCurrentMode_ = mode;
andrewm@17 531 rawDataCurrentScaler_ = scaler;
andrewm@17 532 rawDataShouldChangeMode_ = true;
andrewm@0 533
andrewm@0 534 shouldStop_ = false;
andrewm@0 535 rawDataThread_.startThread();
andrewm@0 536
andrewm@0 537 if(verbose_ >= 1)
andrewm@0 538 cout << "Starting raw data collection from octave " << octave << ", key " << key << endl;
andrewm@0 539
andrewm@0 540 autoGathering_ = true;
andrewm@0 541
andrewm@0 542 return true;
andrewm@0 543 }
andrewm@0 544
andrewm@17 545 void TouchkeyDevice::rawDataChangeKeyAndMode(int octave, int key, int mode, int scaler) {
andrewm@17 546 // Account for the high C which is ID 12 on the top octave
andrewm@17 547 if(octave == numOctaves_ && key == 0) {
andrewm@17 548 octave--;
andrewm@17 549 key = 12;
andrewm@17 550 }
andrewm@17 551
andrewm@17 552 rawDataCurrentOctave_ = octave;
andrewm@17 553 rawDataCurrentKey_ = key;
andrewm@17 554 rawDataCurrentMode_ = mode;
andrewm@17 555 rawDataCurrentScaler_ = scaler;
andrewm@17 556 rawDataShouldChangeMode_ = true;
andrewm@17 557 }
andrewm@17 558
andrewm@0 559 // Set the scan interval in milliseconds. Returns true on success.
andrewm@0 560
andrewm@0 561 bool TouchkeyDevice::setScanInterval(int intervalMilliseconds) {
andrewm@0 562 if(!isOpen())
andrewm@0 563 return false;
andrewm@0 564 if(intervalMilliseconds <= 0 || intervalMilliseconds > 255)
andrewm@0 565 return false;
andrewm@0 566
andrewm@0 567 unsigned char command[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin,
andrewm@0 568 kFrameTypeScanRate, (unsigned char)(intervalMilliseconds & 0xFF), ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@0 569
andrewm@0 570 // Send command
andrewm@22 571 if(deviceWrite((char*)command, 6) < 0) {
andrewm@7 572 if(verbose_ >= 1)
andrewm@7 573 cout << "ERROR: unable to write startRawDataCollection command. errno = " << errno << endl;
andrewm@0 574 }
andrewm@0 575
andrewm@0 576 if(verbose_ >= 2)
andrewm@0 577 cout << "Setting scan interval to " << intervalMilliseconds << endl;
andrewm@0 578
andrewm@0 579 // Return value depends on ACK or NAK received
andrewm@0 580 return checkForAck(250);
andrewm@0 581 }
andrewm@0 582
andrewm@0 583 // Key parameters. Setting octave or key to -1 means all octaves or all keys, respectively.
andrewm@0 584 // This controls the sensitivity of the capacitive touch sensing system on each key.
andrewm@0 585 // It is a balance between achieving the best range of data and not saturating the sensors
andrewm@0 586 // for the largest touches.
andrewm@0 587 bool TouchkeyDevice::setKeySensitivity(int octave, int key, int value) {
andrewm@0 588 unsigned char chOctave, chKey, chVal;
andrewm@0 589
andrewm@0 590 if(!isOpen())
andrewm@0 591 return false;
andrewm@0 592 if(octave > 255)
andrewm@0 593 return false;
andrewm@0 594 if(key > 12)
andrewm@0 595 return false;
andrewm@0 596 if(value > 255 || value < 0)
andrewm@0 597 return false;
andrewm@0 598 if(octave < 0)
andrewm@0 599 chOctave = 0xFF;
andrewm@0 600 else
andrewm@0 601 chOctave = (unsigned char)(octave & 0xFF);
andrewm@0 602 if(key < 0)
andrewm@0 603 chKey = 0xFF;
andrewm@0 604 else
andrewm@0 605 chKey = (unsigned char)(key & 0xFF);
andrewm@0 606 chVal = (unsigned char)value;
andrewm@0 607
andrewm@0 608 unsigned char command[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin, kFrameTypeSensitivity,
andrewm@0 609 chOctave, chKey, chVal, ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@0 610
andrewm@0 611 // Send command
andrewm@22 612 if(deviceWrite((char*)command, 8) < 0) {
andrewm@7 613 if(verbose_ >= 1)
andrewm@7 614 cout << "ERROR: unable to write setKeySensitivity command. errno = " << errno << endl;
andrewm@0 615 }
andrewm@20 616
andrewm@0 617 if(verbose_ >= 2)
andrewm@0 618 cout << "Setting sensitivity to " << value << endl;
andrewm@0 619
andrewm@0 620 // Return value depends on ACK or NAK received
andrewm@0 621 return checkForAck(250);
andrewm@0 622 }
andrewm@0 623
andrewm@0 624 // Change how the calculated centroids are scaled to fit in a single byte. They
andrewm@0 625 // will be right-shifted by the indicated number of bits before being transmitted.
andrewm@0 626 bool TouchkeyDevice::setKeyCentroidScaler(int octave, int key, int value) {
andrewm@0 627 unsigned char chOctave, chKey, chVal;
andrewm@0 628
andrewm@0 629 if(!isOpen())
andrewm@0 630 return false;
andrewm@0 631 if(octave > 255)
andrewm@0 632 return false;
andrewm@0 633 if(key > 12)
andrewm@0 634 return false;
andrewm@0 635 if(value > 7 || value < 0)
andrewm@0 636 return false;
andrewm@0 637 if(octave < 0)
andrewm@0 638 chOctave = 0xFF;
andrewm@0 639 else
andrewm@0 640 chOctave = (unsigned char)(octave & 0xFF);
andrewm@0 641 if(key < 0)
andrewm@0 642 chKey = 0xFF;
andrewm@0 643 else
andrewm@0 644 chKey = (unsigned char)(key & 0xFF);
andrewm@0 645 chVal = (unsigned char)value;
andrewm@0 646
andrewm@0 647 unsigned char command[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin, kFrameTypeSizeScaler,
andrewm@0 648 chOctave, chKey, chVal, ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@0 649
andrewm@0 650 // Send command
andrewm@22 651 if(deviceWrite((char*)command, 8) < 0) {
andrewm@7 652 if(verbose_ >= 1)
andrewm@7 653 cout << "ERROR: unable to write setKeyCentroidScaler command. errno = " << errno << endl;
andrewm@0 654 }
andrewm@22 655
andrewm@0 656 if(verbose_ >= 2)
andrewm@0 657 cout << "Setting size scaler to " << value << endl;
andrewm@0 658
andrewm@0 659 // Return value depends on ACK or NAK received
andrewm@0 660 return checkForAck(250);
andrewm@0 661 }
andrewm@0 662
andrewm@0 663 // Set the minimum size of a centroid calculated on the key which is considered
andrewm@0 664 // "real" and not noise.
andrewm@0 665 bool TouchkeyDevice::setKeyMinimumCentroidSize(int octave, int key, int value) {
andrewm@0 666 unsigned char chOctave, chKey, chValHi, chValLo;
andrewm@0 667
andrewm@0 668 if(!isOpen())
andrewm@0 669 return false;
andrewm@0 670 if(octave > 255)
andrewm@0 671 return false;
andrewm@0 672 if(key > 12)
andrewm@0 673 return false;
andrewm@0 674 if(value > 0xFFFF || value < 0)
andrewm@0 675 return false;
andrewm@0 676 if(octave < 0)
andrewm@0 677 chOctave = 0xFF;
andrewm@0 678 else
andrewm@0 679 chOctave = (unsigned char)(octave & 0xFF);
andrewm@0 680 if(key < 0)
andrewm@0 681 chKey = 0xFF;
andrewm@0 682 else
andrewm@0 683 chKey = (unsigned char)(key & 0xFF);
andrewm@0 684 chValHi = (unsigned char)((value >> 8) & 0xFF);
andrewm@0 685 chValLo = (unsigned char)(value & 0xFF);
andrewm@0 686
andrewm@0 687 unsigned char command[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin, kFrameTypeMinimumSize,
andrewm@0 688 chOctave, chKey, chValHi, chValLo, ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@0 689
andrewm@0 690 // Send command
andrewm@22 691 if(deviceWrite((char*)command, 9) < 0) {
andrewm@7 692 if(verbose_ >= 1)
andrewm@7 693 cout << "ERROR: unable to write setKeyMinimumCentroidSize command. errno = " << errno << endl;
andrewm@0 694 }
andrewm@20 695
andrewm@0 696 if(verbose_ >= 2)
andrewm@0 697 cout << "Setting minimum centroid size to " << value << endl;
andrewm@0 698
andrewm@0 699 // Return value depends on ACK or NAK received
andrewm@0 700 return checkForAck(250);
andrewm@0 701 }
andrewm@0 702
andrewm@0 703 // Set the noise threshold for individual sensor pads: the reading must exceed
andrewm@0 704 // the background value by this amount to be considered an actual touch.
andrewm@0 705 bool TouchkeyDevice::setKeyNoiseThreshold(int octave, int key, int value) {
andrewm@0 706 unsigned char chOctave, chKey, chVal;
andrewm@0 707
andrewm@0 708 if(octave > 255)
andrewm@0 709 return false;
andrewm@0 710 if(key > 12)
andrewm@0 711 return false;
andrewm@0 712 if(value > 255 || value < 0)
andrewm@0 713 return false;
andrewm@0 714 if(octave < 0)
andrewm@0 715 chOctave = 0xFF;
andrewm@0 716 else
andrewm@0 717 chOctave = (unsigned char)(octave & 0xFF);
andrewm@0 718 if(key < 0)
andrewm@0 719 chKey = 0xFF;
andrewm@0 720 else
andrewm@0 721 chKey = (unsigned char)(key & 0xFF);
andrewm@0 722 chVal = (unsigned char)value;
andrewm@0 723
andrewm@0 724 unsigned char command[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin, kFrameTypeNoiseThreshold,
andrewm@0 725 chOctave, chKey, chVal, ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@0 726
andrewm@0 727 // Send command
andrewm@22 728 if(deviceWrite((char*)command, 8) < 0) {
andrewm@7 729 if(verbose_ >= 1)
andrewm@7 730 cout << "ERROR: unable to write setKeyNoiseThreshold command. errno = " << errno << endl;
andrewm@0 731 }
andrewm@20 732
andrewm@0 733 if(verbose_ >= 2)
andrewm@0 734 cout << "Setting noise threshold to " << value << endl;
andrewm@0 735
andrewm@0 736 // Return value depends on ACK or NAK received
andrewm@0 737 return checkForAck(250);
andrewm@0 738 }
andrewm@0 739
andrewm@19 740 // Update the baseline sensor values on the given key
andrewm@19 741 bool TouchkeyDevice::setKeyUpdateBaseline(int octave, int key) {
andrewm@19 742 unsigned char baselineCommand[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin,
andrewm@19 743 kFrameTypeSendI2CCommand, (unsigned char)octave, (unsigned char)key,
andrewm@19 744 2 /* xmit */, 0 /* response */, 0 /* command offset */, 6 /* baseline update */,
andrewm@19 745 ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@19 746
andrewm@19 747 // Send command
andrewm@22 748 if(deviceWrite((char*)baselineCommand, 11) < 0) {
andrewm@19 749 if(verbose_ >= 1)
andrewm@19 750 cout << "ERROR: unable to write baseline update command. errno = " << errno << endl;
andrewm@19 751 }
andrewm@20 752
andrewm@19 753 if(verbose_ >= 2)
andrewm@19 754 cout << "Updating baseline on octave " << octave << " key " << key << endl;
andrewm@19 755
andrewm@19 756 checkForAck(100);
andrewm@19 757
andrewm@19 758 unsigned char commandPrepareRead[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin,
andrewm@19 759 kFrameTypeSendI2CCommand, (unsigned char)octave, (unsigned char)key,
andrewm@19 760 1 /* xmit */, 0 /* response */, 6 /* data offset */,
andrewm@19 761 ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@19 762
andrewm@22 763 if(deviceWrite((char*)commandPrepareRead, 10) < 0) {
andrewm@19 764 if(verbose_ >= 1)
andrewm@19 765 cout << "ERROR: unable to write prepareRead command. errno = " << errno << endl;
andrewm@19 766 }
andrewm@20 767
andrewm@19 768 // Return value depends on ACK or NAK received
andrewm@19 769 return checkForAck(100);
andrewm@19 770 }
andrewm@19 771
andrewm@0 772 // Jump to the built-in bootloader of the TouchKeys device
andrewm@0 773 void TouchkeyDevice::jumpToBootloader() {
andrewm@53 774 // The command includes a 4-byte magic number to avoid a corrupt packet accidentally triggering the jump
andrewm@0 775 unsigned char command[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin, kFrameTypeEnterSelfProgramMode,
andrewm@53 776 0xA1, 0xB2, 0xC3, 0xD4, ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@0 777
andrewm@0 778 // Send command
andrewm@53 779 if(deviceWrite((char*)command, 9) < 0) {
andrewm@7 780 if(verbose_ >= 1)
andrewm@7 781 cout << "ERROR: unable to write jumpToBootloader command. errno = " << errno << endl;
andrewm@0 782 }
andrewm@0 783 }
andrewm@0 784
andrewm@0 785 // Set the LED color for the given MIDI note (if RGB LEDs are present). This method
andrewm@0 786 // does not directly communicate with the device, but it schedules an update to take
andrewm@0 787 // place in the relevant thread.
andrewm@0 788 void TouchkeyDevice::rgbledSetColor(const int midiNote, const float red, const float green, const float blue) {
andrewm@0 789 RGBLEDUpdate updateStructure;
andrewm@0 790
andrewm@0 791 updateStructure.allLedsOff = false;
andrewm@0 792 updateStructure.midiNote = midiNote;
andrewm@0 793
andrewm@0 794 // Convert 0-1 floating point range to 0-255
andrewm@0 795 updateStructure.red = (int)(red * 4095.0);
andrewm@0 796 updateStructure.green = (int)(green * 4095.0);
andrewm@0 797 updateStructure.blue = (int)(blue * 4095.0);
andrewm@0 798
andrewm@0 799 ledUpdateQueue_.push_front(updateStructure);
andrewm@0 800 }
andrewm@0 801
andrewm@0 802 // Same as rgbledSetColor() but uses HSV format color instead of RGB
andrewm@0 803 void TouchkeyDevice::rgbledSetColorHSV(const int midiNote, const float hue, const float saturation, const float value) {
andrewm@0 804 float red = 0, green = 0, blue = 0;
andrewm@0 805 float chroma = value * saturation;
andrewm@0 806
andrewm@0 807 // Hue will lie on one of 6 segments from 0 to 1; convert this from 0 to 6.
andrewm@0 808 float hueSegment = hue * 6.0;
andrewm@0 809 float x = chroma * (1.0 - fabsf(fmodf(hueSegment, 2.0) - 1.0));
andrewm@0 810
andrewm@0 811 if(hueSegment < 1.0) {
andrewm@0 812 red = chroma;
andrewm@0 813 green = x;
andrewm@0 814 blue = 0;
andrewm@0 815 }
andrewm@0 816 else if(hueSegment < 2.0) {
andrewm@0 817 red = x;
andrewm@0 818 green = chroma;
andrewm@0 819 blue = 0;
andrewm@0 820 }
andrewm@0 821 else if(hueSegment < 3.0) {
andrewm@0 822 red = 0;
andrewm@0 823 green = chroma;
andrewm@0 824 blue = x;
andrewm@0 825 }
andrewm@0 826 else if(hueSegment < 4.0) {
andrewm@0 827 red = 0;
andrewm@0 828 green = x;
andrewm@0 829 blue = chroma;
andrewm@0 830 }
andrewm@0 831 else if(hueSegment < 5.0) {
andrewm@0 832 red = x;
andrewm@0 833 green = 0;
andrewm@0 834 blue = chroma;
andrewm@0 835 }
andrewm@0 836 else {
andrewm@0 837 red = chroma;
andrewm@0 838 green = 0;
andrewm@0 839 blue = x;
andrewm@0 840 }
andrewm@0 841
andrewm@0 842 rgbledSetColor(midiNote, red, green, blue);
andrewm@0 843 }
andrewm@0 844
andrewm@0 845 // Set all RGB LEDs off (if RGB LEDs are present). This method does not
andrewm@0 846 // directly communicate with the device, but it schedules an update to take
andrewm@0 847 // place in the relevant thread.
andrewm@0 848 void TouchkeyDevice::rgbledAllOff() {
andrewm@0 849 RGBLEDUpdate updateStructure;
andrewm@0 850
andrewm@0 851 updateStructure.allLedsOff = true;
andrewm@0 852 updateStructure.midiNote = 0;
andrewm@0 853 updateStructure.red = 0;
andrewm@0 854 updateStructure.green = 0;
andrewm@0 855 updateStructure.blue = 0;
andrewm@0 856
andrewm@0 857 ledUpdateQueue_.push_front(updateStructure);
andrewm@0 858 }
andrewm@0 859
andrewm@0 860 // Set the color of a given RGB LED (piano scanner boards only). LEDs are numbered from 0-24
andrewm@0 861 // starting at left. Boards are numbered 0-3 starting at left.
andrewm@0 862 bool TouchkeyDevice::internalRGBLEDSetColor(const int device, const int led, const int red, const int green, const int blue) {
andrewm@0 863 if(!isOpen())
andrewm@0 864 return false;
andrewm@0 865 if(!deviceHasRGBLEDs_)
andrewm@0 866 return false;
andrewm@0 867 if(device < 0 || device > 3)
andrewm@0 868 return false;
andrewm@0 869 if(led < 0 || led > 24)
andrewm@0 870 return false;
andrewm@0 871 if(red < 0 || red > 4095)
andrewm@0 872 return false;
andrewm@0 873 if(green < 0 || green > 4095)
andrewm@0 874 return false;
andrewm@0 875 if(blue < 0 || blue > 4095)
andrewm@0 876 return false;
andrewm@0 877
andrewm@0 878 unsigned char command[17]; // 11 bytes + possibly 6 doubled characters
andrewm@0 879
andrewm@0 880 // There's a chance that one of the bytes will come out to ESCAPE_CHARACTER (0xFE) depending
andrewm@0 881 // on LED color. We need to double up any bytes that come in that way.
andrewm@0 882
andrewm@0 883 command[0] = ESCAPE_CHARACTER;
andrewm@0 884 command[1] = kControlCharacterFrameBegin;
andrewm@0 885 command[2] = kFrameTypeRGBLEDSetColors;
andrewm@0 886
andrewm@0 887 int byte, location = 3;
andrewm@0 888
andrewm@0 889 byte = (((unsigned char)device & 0xFF) << 6) | (unsigned char)led;
andrewm@0 890 command[location++] = byte;
andrewm@0 891 if(byte == ESCAPE_CHARACTER)
andrewm@0 892 command[location++] = byte;
andrewm@0 893 byte = (red >> 4) & 0xFF;
andrewm@0 894 command[location++] = byte;
andrewm@0 895 if(byte == ESCAPE_CHARACTER)
andrewm@0 896 command[location++] = byte;
andrewm@0 897 byte = ((red << 4) & 0xF0) | ((green >> 8) & 0x0F);
andrewm@0 898 command[location++] = byte;
andrewm@0 899 if(byte == ESCAPE_CHARACTER)
andrewm@0 900 command[location++] = byte;
andrewm@0 901 byte = (green & 0xFF);
andrewm@0 902 command[location++] = byte;
andrewm@0 903 if(byte == ESCAPE_CHARACTER)
andrewm@0 904 command[location++] = byte;
andrewm@0 905 byte = (blue >> 4) & 0xFF;
andrewm@0 906 command[location++] = byte;
andrewm@0 907 if(byte == ESCAPE_CHARACTER)
andrewm@0 908 command[location++] = byte;
andrewm@0 909 byte = (blue << 4) & 0xF0;
andrewm@0 910 command[location++] = byte;
andrewm@0 911 if(byte == ESCAPE_CHARACTER)
andrewm@0 912 command[location++] = byte;
andrewm@0 913 command[location++] = ESCAPE_CHARACTER;
andrewm@0 914 command[location++] = kControlCharacterFrameEnd;
andrewm@0 915
andrewm@0 916 // Send command
andrewm@22 917 if(deviceWrite((char*)command, location) < 0) {
andrewm@7 918 if(verbose_ >= 1)
andrewm@7 919 cout << "ERROR: unable to write setRGBLEDColor command. errno = " << errno << endl;
andrewm@0 920 }
andrewm@0 921
andrewm@0 922 if(verbose_ >= 3)
andrewm@0 923 cout << "Setting RGB LED color for device " << device << ", led " << led << endl;
andrewm@0 924
andrewm@0 925 // Return value depends on ACK or NAK received
andrewm@0 926 return true; //checkForAck(20);
andrewm@0 927 }
andrewm@0 928
andrewm@0 929 // Turn off all RGB LEDs on a given board
andrewm@0 930 bool TouchkeyDevice::internalRGBLEDAllOff() {
andrewm@0 931 if(!isOpen())
andrewm@0 932 return false;
andrewm@0 933 if(!deviceHasRGBLEDs_)
andrewm@0 934 return false;
andrewm@0 935
andrewm@0 936 unsigned char command[5];
andrewm@0 937
andrewm@0 938 command[0] = ESCAPE_CHARACTER;
andrewm@0 939 command[1] = kControlCharacterFrameBegin;
andrewm@0 940 command[2] = kFrameTypeRGBLEDAllOff;
andrewm@0 941 command[3] = ESCAPE_CHARACTER;
andrewm@0 942 command[4] = kControlCharacterFrameEnd;
andrewm@0 943
andrewm@0 944 // Send command
andrewm@22 945 if(deviceWrite((char*)command, 5) < 0) {
andrewm@7 946 if(verbose_ >= 1)
andrewm@7 947 cout << "ERROR: unable to write setRGBLEDAllOff command. errno = " << errno << endl;
andrewm@0 948 }
andrewm@22 949
andrewm@0 950 if(verbose_ >= 3)
andrewm@0 951 cout << "Turning off all RGB LEDs" << endl;
andrewm@0 952
andrewm@0 953 // Return value depends on ACK or NAK received
andrewm@0 954 return true; //checkForAck(20);
andrewm@0 955 }
andrewm@0 956
andrewm@0 957 // Get board number for MIDI note
andrewm@0 958 int TouchkeyDevice::internalRGBLEDMIDIToBoardNumber(const int midiNote) {
andrewm@0 959 // lowestMidiNote_ holds the very bottom LED on the bottom board. The boards
andrewm@0 960 // go up by two-octave sets from there. The top board has one extra LED (high C).
andrewm@0 961
andrewm@0 962 if(midiNote > lowestMidiNote_ + 96)
andrewm@0 963 return -1;
andrewm@0 964 if(midiNote >= lowestMidiNote_ + 72)
andrewm@0 965 return 3;
andrewm@0 966 else if(midiNote >= lowestMidiNote_ + 48)
andrewm@0 967 return 2;
andrewm@0 968 else if(midiNote >= lowestMidiNote_ + 24)
andrewm@0 969 return 1;
andrewm@0 970 else if(midiNote >= lowestMidiNote_)
andrewm@0 971 return 0;
andrewm@0 972 return -1;
andrewm@0 973 }
andrewm@0 974
andrewm@0 975 // Get LED number for MIDI note (within a board)
andrewm@0 976 int TouchkeyDevice::internalRGBLEDMIDIToLEDNumber(const int midiNote) {
andrewm@0 977 // Take the note number relative to the lowest note of the whole device.
andrewm@0 978 // Once it's located within a board (2-octaves each), the offset gives us the LED number.
andrewm@0 979 // However, the lowest board works differently as it only has 15 LEDs which start at A,
andrewm@0 980 // not at C.
andrewm@0 981 const int midiNoteOffset = midiNote - lowestMidiNote_;
andrewm@0 982
andrewm@0 983 if(midiNoteOffset < 9) // Below the bottom A, hence invalid
andrewm@0 984 return -1;
andrewm@0 985 if(midiNoteOffset < 24) {
andrewm@0 986 // Within 2 octaves of bottom --> lowest board --> adjust for 15 LEDs on board
andrewm@0 987 return midiNoteOffset - 9;
andrewm@0 988 }
andrewm@0 989 else if(midiNoteOffset < 48) {
andrewm@0 990 // Board 1
andrewm@0 991 return midiNoteOffset - 24;
andrewm@0 992 }
andrewm@0 993 else if(midiNoteOffset < 72) {
andrewm@0 994 // Board 2
andrewm@0 995 return midiNoteOffset - 48;
andrewm@0 996 }
andrewm@0 997 else if(midiNoteOffset < 97) {
andrewm@0 998 // Board 3 includes a top C (index 24)
andrewm@0 999 return midiNoteOffset - 72;
andrewm@0 1000 }
andrewm@0 1001 return -1;
andrewm@0 1002 }
andrewm@0 1003
andrewm@0 1004 // ***** Calibration Methods *****
andrewm@0 1005
andrewm@0 1006 // Start calibrating selected keys and pedals. If argument is NULL, assume it applies to all keys.
andrewm@0 1007 void TouchkeyDevice::calibrationStart(std::vector<int>* keysToCalibrate) {
andrewm@0 1008 if(keysToCalibrate == 0) {
andrewm@0 1009 for(int i = 0; i < keyCalibratorsLength_; i++)
andrewm@0 1010 keyCalibrators_[i]->calibrationStart();
andrewm@0 1011 }
andrewm@0 1012 else {
andrewm@0 1013 std::vector<int>::iterator it;
andrewm@0 1014 for(it = keysToCalibrate->begin(); it != keysToCalibrate->end(); it++) {
andrewm@0 1015 if(*it >= 0 && *it < keyCalibratorsLength_)
andrewm@0 1016 keyCalibrators_[*it]->calibrationStart();
andrewm@0 1017 }
andrewm@0 1018 }
andrewm@0 1019
andrewm@0 1020 calibrationInProgress_ = true;
andrewm@0 1021 }
andrewm@0 1022
andrewm@0 1023 // Finish the current calibration in progress. Pass it on to all Calibrators, and the ones that weren't
andrewm@0 1024 // calibrating will just ignore it.
andrewm@0 1025 void TouchkeyDevice::calibrationFinish() {
andrewm@0 1026 bool calibratedAtLeastOneKey = false;
andrewm@0 1027
andrewm@0 1028 for(int i = 0; i < keyCalibratorsLength_; i++) {
andrewm@0 1029 // Check if calibration was successful
andrewm@0 1030 if(keyCalibrators_[i]->calibrationFinish()) {
andrewm@0 1031 calibratedAtLeastOneKey = true;
andrewm@0 1032 // Update the display if available
andrewm@0 1033 if(keyboard_.gui() != 0) {
andrewm@0 1034 keyboard_.gui()->setAnalogCalibrationStatusForKey(i + lowestMidiNote_, true);
andrewm@0 1035 }
andrewm@0 1036 }
andrewm@0 1037 }
andrewm@0 1038
andrewm@0 1039 calibrationInProgress_ = false;
andrewm@0 1040 isCalibrated_ = calibratedAtLeastOneKey;
andrewm@0 1041 }
andrewm@0 1042
andrewm@0 1043 // Abort a calibration in progress, without saving its results. Pass it on to all Calibrators.
andrewm@0 1044 void TouchkeyDevice::calibrationAbort() {
andrewm@0 1045 for(int i = 0; i < keyCalibratorsLength_; i++)
andrewm@0 1046 keyCalibrators_[i]->calibrationAbort();
andrewm@0 1047
andrewm@0 1048 calibrationInProgress_ = false;
andrewm@0 1049 }
andrewm@0 1050
andrewm@0 1051 // Clear the existing calibration, reverting to an uncalibrated state.
andrewm@0 1052 void TouchkeyDevice::calibrationClear() {
andrewm@0 1053 for(int i = 0; i < keyCalibratorsLength_; i++) {
andrewm@0 1054 keyCalibrators_[i]->calibrationClear();
andrewm@0 1055 if(keyboard_.gui() != 0) {
andrewm@0 1056 keyboard_.gui()->setAnalogCalibrationStatusForKey(i + lowestMidiNote_, false);
andrewm@0 1057 }
andrewm@0 1058 }
andrewm@0 1059
andrewm@0 1060 calibrationInProgress_ = false;
andrewm@0 1061 isCalibrated_ = false;
andrewm@0 1062 }
andrewm@0 1063
andrewm@0 1064 // Save calibration data to a file
andrewm@0 1065 bool TouchkeyDevice::calibrationSaveToFile(std::string const& filename) {
andrewm@0 1066 int i;
andrewm@0 1067
andrewm@0 1068 if(!isCalibrated()) {
andrewm@0 1069 std::cerr << "TouchKeys not calibrated, so can't save calibration data.\n";
andrewm@0 1070 return false;
andrewm@0 1071 }
andrewm@0 1072
andrewm@0 1073 // Create an XML structure and save it to file.
andrewm@0 1074 try {
andrewm@0 1075 XmlElement baseElement("TouchkeyDeviceCalibration");
andrewm@0 1076 bool savedValidData = false;
andrewm@0 1077
andrewm@0 1078 for(i = 0; i < keyCalibratorsLength_; i++) {
andrewm@0 1079 XmlElement *calibrationElement = baseElement.createNewChildElement("Key");
andrewm@0 1080 if(calibrationElement == 0)
andrewm@0 1081 continue;
andrewm@0 1082
andrewm@0 1083 calibrationElement->setAttribute("id", i);
andrewm@0 1084
andrewm@0 1085 // Tell each individual calibrator to add its data to the XML tree
andrewm@0 1086 if(keyCalibrators_[i]->saveToXml(*calibrationElement)) {
andrewm@0 1087 savedValidData = true;
andrewm@0 1088 }
andrewm@0 1089 }
andrewm@0 1090
andrewm@0 1091 if(!savedValidData) {
andrewm@0 1092 std::cerr << "TouchkeyDevice: unable to find valid calibration data to save.\n";
andrewm@0 1093 throw 1;
andrewm@0 1094 }
andrewm@0 1095
andrewm@0 1096 // Now save the generated tree to a file
andrewm@0 1097
andrewm@0 1098 if(!baseElement.writeToFile(File(filename.c_str()), "")) {
andrewm@0 1099 std::cerr << "TouchkeyDevice: could not write calibration file " << filename << "\n";
andrewm@0 1100 throw 1;
andrewm@0 1101 }
andrewm@0 1102
andrewm@0 1103 //lastCalibrationFile_ = filename;
andrewm@0 1104 }
andrewm@0 1105 catch(...) {
andrewm@0 1106 return false;
andrewm@0 1107 }
andrewm@0 1108
andrewm@0 1109 return true;
andrewm@0 1110 }
andrewm@0 1111
andrewm@0 1112 // Load calibration from a file
andrewm@0 1113 bool TouchkeyDevice::calibrationLoadFromFile(std::string const& filename) {
andrewm@0 1114 //int i, j;
andrewm@0 1115
andrewm@0 1116 calibrationClear();
andrewm@0 1117
andrewm@0 1118 // Open the file and read the new values
andrewm@0 1119 try {
andrewm@0 1120 XmlDocument doc(File(filename.c_str()));
andrewm@0 1121 XmlElement *baseElement = doc.getDocumentElement();
andrewm@0 1122 XmlElement *deviceCalibrationElement, *calibratorElement;
andrewm@0 1123
andrewm@0 1124 if(baseElement == 0) {
andrewm@0 1125 std::cerr << "TouchkeyDevice: unable to load patch table file: \"" << filename << "\". Error was:\n";
andrewm@0 1126 std::cerr << doc.getLastParseError() << std::endl;
andrewm@0 1127 throw 1;
andrewm@0 1128 }
andrewm@0 1129
andrewm@0 1130 // All calibration data is encapsulated within the root element <PianoBarCalibration>
andrewm@0 1131 deviceCalibrationElement = baseElement->getChildByName("TouchkeyDeviceCalibration");
andrewm@0 1132 if(deviceCalibrationElement == 0) {
andrewm@0 1133 std::cerr << "TouchkeyDevice: malformed calibration file, aborting.\n";
andrewm@0 1134 delete baseElement;
andrewm@0 1135 throw 1;
andrewm@0 1136 }
andrewm@0 1137
andrewm@0 1138 // Go through and find each key's calibration information
andrewm@0 1139 calibratorElement = deviceCalibrationElement->getChildByName("Key");
andrewm@0 1140 if(calibratorElement == 0) {
andrewm@0 1141 std::cerr << "TouchkeyDevice: warning: no keys found\n";
andrewm@0 1142 }
andrewm@0 1143 else {
andrewm@0 1144 while(calibratorElement != 0) {
andrewm@0 1145 int keyId;
andrewm@0 1146
andrewm@0 1147 if(calibratorElement->hasAttribute("id")) {
andrewm@0 1148 keyId = calibratorElement->getIntAttribute("id");
andrewm@0 1149 if(keyId >= 0 && keyId < keyCalibratorsLength_)
andrewm@0 1150 keyCalibrators_[keyId]->loadFromXml(*calibratorElement);
andrewm@0 1151 }
andrewm@0 1152
andrewm@0 1153 calibratorElement = calibratorElement->getNextElementWithTagName("Key");
andrewm@0 1154 }
andrewm@0 1155 }
andrewm@0 1156
andrewm@0 1157 calibrationInProgress_ = false;
andrewm@0 1158 isCalibrated_ = true;
andrewm@0 1159 if(keyboard_.gui() != 0) {
andrewm@0 1160 for(int i = lowestMidiNote_; i < lowestMidiNote_ + 12*numOctaves_; i++) {
andrewm@0 1161 keyboard_.gui()->setAnalogCalibrationStatusForKey(i, true);
andrewm@0 1162 }
andrewm@0 1163 }
andrewm@0 1164 //lastCalibrationFile_ = filename;
andrewm@0 1165
andrewm@0 1166 delete baseElement;
andrewm@0 1167 }
andrewm@0 1168 catch(...) {
andrewm@0 1169 return false;
andrewm@0 1170 }
andrewm@0 1171
andrewm@0 1172 // TODO: reset key states?
andrewm@0 1173
andrewm@0 1174 return true;
andrewm@0 1175 }
andrewm@0 1176
andrewm@0 1177 // Initialize the calibrators
andrewm@0 1178 void TouchkeyDevice::calibrationInit(int numberOfCalibrators) {
andrewm@0 1179 if(keyCalibrators_ != 0)
andrewm@0 1180 calibrationDeinit();
andrewm@0 1181 if(numberOfCalibrators <= 0)
andrewm@0 1182 return;
andrewm@0 1183 keyCalibratorsLength_ = numberOfCalibrators;
andrewm@0 1184
andrewm@0 1185 // Initialize the calibrator array
andrewm@0 1186 keyCalibrators_ = (PianoKeyCalibrator **)malloc(keyCalibratorsLength_ * sizeof(PianoKeyCalibrator*));
andrewm@0 1187
andrewm@0 1188 for(int i = 0; i < keyCalibratorsLength_; i++) {
andrewm@0 1189 keyCalibrators_[i] = new PianoKeyCalibrator(true, 0);
andrewm@0 1190 }
andrewm@0 1191
andrewm@0 1192 calibrationClear();
andrewm@0 1193 }
andrewm@0 1194
andrewm@0 1195 // Free the initialized calibrators
andrewm@0 1196 void TouchkeyDevice::calibrationDeinit() {
andrewm@0 1197 if(keyCalibrators_ == 0)
andrewm@0 1198 return;
andrewm@0 1199
andrewm@0 1200 for(int i = 0; i < keyCalibratorsLength_; i++) {
andrewm@0 1201 if(keyCalibrators_[i] != 0)
andrewm@0 1202 delete keyCalibrators_[i];
andrewm@0 1203 keyCalibrators_[i] = 0;
andrewm@0 1204 }
andrewm@0 1205 free(keyCalibrators_);
andrewm@0 1206
andrewm@0 1207 keyCalibratorsLength_ = 0;
andrewm@0 1208 isCalibrated_ = calibrationInProgress_ = false;
andrewm@0 1209 }
andrewm@0 1210
andrewm@0 1211 // Update the lowest MIDI note of the TouchKeys device
andrewm@0 1212 void TouchkeyDevice::setLowestMidiNote(int note) {
andrewm@0 1213 // If running, save the value in a temporary holding place until
andrewm@0 1214 // the data gathering thread makes the update. Otherwise update right away.
andrewm@0 1215 // This avoids things changing during data processing and other threading problems.
andrewm@0 1216 if(isAutoGathering())
andrewm@0 1217 updatedLowestMidiNote_ = note;
andrewm@0 1218 else {
andrewm@0 1219 lowestKeyPresentMidiNote_ += (note - lowestMidiNote_);
andrewm@0 1220 lowestMidiNote_ = updatedLowestMidiNote_ = note;
andrewm@0 1221 if(isOpen())
andrewm@48 1222 keyboard_.setKeyboardGUIRange(lowestKeyPresentMidiNote_, lowestMidiNote_ + 12*numOctaves_ + lowestNotePerOctave_);
andrewm@0 1223 }
andrewm@0 1224 }
andrewm@0 1225
andrewm@48 1226 // Convert an octave and key designation into a MIDI note
andrewm@48 1227 int TouchkeyDevice::octaveKeyToMidi(int octave, int key) {
andrewm@48 1228 int midi = lowestMidiNote_ + octave*12 + key;
andrewm@48 1229
andrewm@48 1230 if(lowestNotePerOctave_ == 0)
andrewm@48 1231 return midi;
andrewm@48 1232
andrewm@48 1233 // For keyboards which do not change octaves at C (e.g. E-E and F-F keyboards),
andrewm@48 1234 // the lowest note numbers are actually one octave higher (e.g. an octave might start
andrewm@48 1235 // at E, meaning C-Eb are part of the next higher octave).
andrewm@48 1236 // Also, the "top C" (key 12) has a special designation as being the top of
andrewm@48 1237 // whichever note the keyboard began at.
andrewm@48 1238
andrewm@48 1239 if(key == 12)
andrewm@48 1240 midi = lowestMidiNote_ + octave*12 + key + lowestNotePerOctave_;
andrewm@48 1241 else if(key < lowestNotePerOctave_)
andrewm@48 1242 midi += 12;
andrewm@48 1243
andrewm@48 1244 return midi;
andrewm@48 1245 }
andrewm@48 1246
andrewm@0 1247 // Loop for sending LED updates to the device, which must happen
andrewm@0 1248 // in a separate thread from data collection so the device's capacity
andrewm@0 1249 // to process incoming data doesn't gate its transmission of sensor data
andrewm@0 1250 void TouchkeyDevice::ledUpdateLoop(DeviceThread *thread) {
andrewm@0 1251
andrewm@0 1252 // Run until told to stop, looking for updates to send to the board
andrewm@0 1253 while(!shouldStop_ && !ledShouldStop_ && !thread->threadShouldExit()) {
andrewm@0 1254 while(!ledUpdateQueue_.empty()) {
andrewm@0 1255 // Get the update
andrewm@0 1256 RGBLEDUpdate& updateStructure = ledUpdateQueue_.back();
andrewm@0 1257
andrewm@0 1258 if(updateStructure.allLedsOff) {
andrewm@0 1259 internalRGBLEDAllOff();
andrewm@0 1260 }
andrewm@0 1261 else {
andrewm@0 1262 // Convert MIDI note number to board/LED pair. If valid, send to device.
andrewm@0 1263 int board = internalRGBLEDMIDIToBoardNumber(updateStructure.midiNote);
andrewm@0 1264 int led = internalRGBLEDMIDIToLEDNumber(updateStructure.midiNote);
andrewm@0 1265
andrewm@0 1266 if(board >= 0 && board <= 3 && led >= 0)
andrewm@0 1267 internalRGBLEDSetColor(board, led, updateStructure.red, updateStructure.green, updateStructure.blue);
andrewm@0 1268 }
andrewm@0 1269
andrewm@0 1270 // Remove the update we just transmitted
andrewm@0 1271 ledUpdateQueue_.pop_back();
andrewm@0 1272 }
andrewm@0 1273
andrewm@22 1274 Thread::sleep(20);
andrewm@0 1275 }
andrewm@0 1276 }
andrewm@0 1277
andrewm@0 1278 // Main run loop, which runs in its own thread
andrewm@0 1279 void TouchkeyDevice::runLoop(DeviceThread *thread) {
andrewm@0 1280 unsigned char buffer[1024]; // Raw data from device
andrewm@0 1281 unsigned char frame[TOUCHKEY_MAX_FRAME_LENGTH]; // Accumulated frame of data
andrewm@0 1282 int frameLength;
andrewm@0 1283 bool controlSeq = false, inFrame = false, frameError = false;
andrewm@0 1284
andrewm@0 1285 /* struct timeval currentTime;
andrewm@0 1286 unsigned long long currentTicks = 0, lastTicks = 0;
andrewm@0 1287 int currentNote = 21;*/
andrewm@0 1288
andrewm@0 1289 // Continuously read from the input device. Read as much data as is available, up to
andrewm@0 1290 // 1024 bytes at a time. If no data is available, wait 0.5ms before trying again. USB
andrewm@0 1291 // data comes in every 1ms, so this guarantees no more than a 1ms wait for data, and often less.
andrewm@0 1292
andrewm@0 1293 while(!shouldStop_ && !thread->threadShouldExit()) {
andrewm@0 1294
andrewm@0 1295 /*
andrewm@0 1296 // This code for RGBLED testing
andrewm@0 1297 gettimeofday(&currentTime, 0);
andrewm@0 1298
andrewm@0 1299 currentTicks = currentTime.tv_sec * 1000000ULL + currentTime.tv_usec;
andrewm@0 1300 if(currentTicks - lastTicks > 50000ULL) {
andrewm@0 1301 lastTicks = currentTicks;
andrewm@0 1302 rgbledSetColor(currentNote, 0, 0, 0);
andrewm@0 1303 currentNote++;
andrewm@0 1304 if(currentNote > highestMidiNote()) {
andrewm@0 1305 rgbledAllOff();
andrewm@0 1306 currentNote = 21;
andrewm@0 1307 }
andrewm@0 1308 rgbledSetColorHSV(currentNote, (float)(currentNote - 21)/(float)(highestMidiNote() - 21), 1.0, 1.0);
andrewm@0 1309 }
andrewm@0 1310 */
andrewm@22 1311 long count = deviceRead((char *)buffer, 1024);
andrewm@20 1312
andrewm@0 1313 if(count == 0) {
andrewm@20 1314 #ifdef _MSC_VER
andrewm@22 1315 Thread::sleep(1);
andrewm@20 1316 #else
andrewm@0 1317 usleep(500);
andrewm@20 1318 #endif
andrewm@0 1319 continue;
andrewm@0 1320 }
andrewm@0 1321 if(count < 0) {
andrewm@0 1322 if(errno != EAGAIN) { // EAGAIN just means no data was available
andrewm@7 1323 if(verbose_ >= 1)
andrewm@7 1324 cout << "Unable to read from device (error " << errno << "). Aborting.\n";
andrewm@28 1325 stopAutoGathering(false);
andrewm@28 1326 //shouldStop_ = true;
andrewm@0 1327 }
andrewm@0 1328
andrewm@20 1329 #ifdef _MSC_VER
andrewm@22 1330 Thread::sleep(1);
andrewm@20 1331 #else
andrewm@0 1332 usleep(500);
andrewm@20 1333 #endif
andrewm@0 1334 continue;
andrewm@0 1335 }
andrewm@0 1336
andrewm@0 1337 // Process the received data
andrewm@0 1338
andrewm@0 1339 for(int i = 0; i < count; i++) {
andrewm@0 1340 unsigned char ch = buffer[i];
andrewm@0 1341
andrewm@0 1342 if(inFrame) {
andrewm@0 1343 // Receiving a frame
andrewm@0 1344
andrewm@0 1345 if(controlSeq) {
andrewm@0 1346 controlSeq = false;
andrewm@0 1347 if(ch == kControlCharacterFrameEnd) { // frame finished?
andrewm@0 1348 inFrame = false;
andrewm@0 1349 processFrame(frame, frameLength);
andrewm@0 1350 }
andrewm@0 1351 else if(ch == kControlCharacterFrameError) { // device telling us about an internal comm error
andrewm@0 1352 if(verbose_ >= 1)
andrewm@0 1353 cout << "Warning: received frame error, continuing anyway.\n";
andrewm@0 1354 frameError = true;
andrewm@0 1355 }
andrewm@0 1356 else if(ch == ESCAPE_CHARACTER) { // double-escape means a literal escape character
andrewm@0 1357 frame[frameLength++] = ch;
andrewm@0 1358 if(frameLength >= TOUCHKEY_MAX_FRAME_LENGTH) {
andrewm@0 1359 inFrame = false;
andrewm@0 1360 if(verbose_ >= 1)
andrewm@0 1361 cout << "Warning: ignoring frame exceeding length limit " << (int)TOUCHKEY_MAX_FRAME_LENGTH << endl;
andrewm@0 1362 }
andrewm@0 1363 }
andrewm@0 1364 else if(ch == kControlCharacterNak && verbose_ >= 1) {
andrewm@0 1365 // TODO: pass this on to a checkForAck() call
andrewm@0 1366 cout << "Warning: received NAK (while receiving frame)\n";
andrewm@0 1367 }
andrewm@0 1368 }
andrewm@0 1369 else {
andrewm@0 1370 if(ch == ESCAPE_CHARACTER)
andrewm@0 1371 controlSeq = true;
andrewm@0 1372 else {
andrewm@0 1373 frame[frameLength++] = ch;
andrewm@0 1374 if(frameLength >= TOUCHKEY_MAX_FRAME_LENGTH) {
andrewm@0 1375 inFrame = false;
andrewm@0 1376 if(verbose_ >= 1)
andrewm@0 1377 cout << "Warning: ignoring frame exceeding length limit " << (int)TOUCHKEY_MAX_FRAME_LENGTH << endl;
andrewm@0 1378 }
andrewm@0 1379 }
andrewm@0 1380 }
andrewm@0 1381 }
andrewm@0 1382 else {
andrewm@0 1383 // Waiting for a frame beginning control sequence
andrewm@0 1384
andrewm@0 1385 if(controlSeq) {
andrewm@0 1386 controlSeq = false;
andrewm@0 1387 if(ch == kControlCharacterFrameBegin) {
andrewm@0 1388 inFrame = true;
andrewm@0 1389 frameLength = 0;
andrewm@0 1390 frameError = false;
andrewm@0 1391 }
andrewm@0 1392 else if(ch == kControlCharacterNak && verbose_ >= 1) {
andrewm@0 1393 // TODO: pass this on to a checkForAck() call
andrewm@0 1394 cout << "Warning: received NAK (while waiting for frame)\n";
andrewm@0 1395 }
andrewm@0 1396 }
andrewm@0 1397 else {
andrewm@0 1398 if(ch == ESCAPE_CHARACTER)
andrewm@0 1399 controlSeq = true;
andrewm@0 1400 }
andrewm@0 1401 }
andrewm@0 1402 }
andrewm@0 1403 }
andrewm@0 1404 }
andrewm@0 1405
andrewm@0 1406 // Main run loop for gathering raw data from a particular key, used for debugging
andrewm@0 1407 // and testing purposes
andrewm@0 1408 void TouchkeyDevice::rawDataRunLoop(DeviceThread *thread) {
andrewm@0 1409 unsigned char buffer[1024]; // Raw data from device
andrewm@0 1410 unsigned char frame[TOUCHKEY_MAX_FRAME_LENGTH]; // Accumulated frame of data
andrewm@0 1411 int frameLength;
andrewm@0 1412 bool controlSeq = false, inFrame = false, frameError = false;
andrewm@0 1413
andrewm@0 1414 unsigned char gatherDataCommand[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin,
andrewm@0 1415 kFrameTypeSendI2CCommand, (unsigned char)rawDataCurrentOctave_, (unsigned char)rawDataCurrentKey_,
andrewm@0 1416 0 /* xmit */, 26 /* response */,
andrewm@0 1417 ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@0 1418
andrewm@0 1419 //struct timeval currentTime;
andrewm@0 1420 double currentTime = 0, lastTime = 0;
andrewm@0 1421 //unsigned long long currentTicks = 0, lastTicks = 0;
andrewm@0 1422
andrewm@0 1423 // Continuously read from the input device. Read as much data as is available, up to
andrewm@0 1424 // 1024 bytes at a time. If no data is available, wait 0.5ms before trying again. USB
andrewm@0 1425 // data comes in every 1ms, so this guarantees no more than a 1ms wait for data, and often less.
andrewm@0 1426
andrewm@0 1427 while(!shouldStop_ && !thread->threadShouldExit()) {
andrewm@0 1428 // Every 100ms, request raw data from the active key
andrewm@0 1429 currentTime = Time::getMillisecondCounterHiRes();
andrewm@0 1430
andrewm@17 1431 if(currentTime - lastTime > 50.0) {
andrewm@0 1432 lastTime = currentTime;
andrewm@17 1433
andrewm@17 1434 // Check if we need to choose a new key or mode
andrewm@17 1435 if(rawDataShouldChangeMode_) {
andrewm@17 1436 // Prepare the key and update the command
andrewm@17 1437 rawDataPrepareCollection(rawDataCurrentOctave_, rawDataCurrentKey_, rawDataCurrentMode_, rawDataCurrentScaler_);
andrewm@17 1438 gatherDataCommand[3] = rawDataCurrentOctave_;
andrewm@17 1439 gatherDataCommand[4] = rawDataCurrentKey_;
andrewm@17 1440 }
andrewm@17 1441
andrewm@0 1442 // Request data
andrewm@22 1443 if(deviceWrite((char*)gatherDataCommand, 9) < 0) {
andrewm@7 1444 if(verbose_ >= 1)
andrewm@17 1445 cout << "ERROR: unable to write gather data command. errno = " << errno << endl;
andrewm@0 1446 }
andrewm@0 1447 }
andrewm@20 1448
andrewm@22 1449 long count = deviceRead((char *)buffer, 1024);
andrewm@20 1450
andrewm@0 1451 if(count == 0) {
andrewm@20 1452 #ifdef _MSC_VER
andrewm@22 1453 Thread::sleep(1);
andrewm@20 1454 #else
andrewm@0 1455 usleep(500);
andrewm@20 1456 #endif
andrewm@0 1457 continue;
andrewm@0 1458 }
andrewm@0 1459 if(count < 0) {
andrewm@0 1460 if(errno != EAGAIN) { // EAGAIN just means no data was available
andrewm@7 1461 if(verbose_ >= 1)
andrewm@7 1462 cout << "Unable to read from device (error " << errno << "). Aborting.\n";
andrewm@28 1463 stopAutoGathering(false);
andrewm@28 1464 //shouldStop_ = true;
andrewm@0 1465 }
andrewm@0 1466
andrewm@20 1467 #ifdef _MSC_VER
andrewm@22 1468 Thread::sleep(1);
andrewm@20 1469 #else
andrewm@0 1470 usleep(500);
andrewm@20 1471 #endif
andrewm@0 1472 continue;
andrewm@0 1473 }
andrewm@0 1474
andrewm@0 1475 // Process the received data
andrewm@0 1476
andrewm@0 1477 for(int i = 0; i < count; i++) {
andrewm@0 1478 unsigned char ch = buffer[i];
andrewm@0 1479
andrewm@0 1480 if(inFrame) {
andrewm@0 1481 // Receiving a frame
andrewm@0 1482
andrewm@0 1483 if(controlSeq) {
andrewm@0 1484 controlSeq = false;
andrewm@0 1485 if(ch == kControlCharacterFrameEnd) { // frame finished?
andrewm@0 1486 inFrame = false;
andrewm@0 1487 processFrame(frame, frameLength);
andrewm@0 1488 }
andrewm@0 1489 else if(ch == kControlCharacterFrameError) { // device telling us about an internal comm error
andrewm@0 1490 if(verbose_ >= 1)
andrewm@0 1491 cout << "Warning: received frame error, continuing anyway.\n";
andrewm@0 1492 frameError = true;
andrewm@0 1493 }
andrewm@0 1494 else if(ch == ESCAPE_CHARACTER) { // double-escape means a literal escape character
andrewm@0 1495 frame[frameLength++] = ch;
andrewm@0 1496 if(frameLength >= TOUCHKEY_MAX_FRAME_LENGTH) {
andrewm@0 1497 inFrame = false;
andrewm@0 1498 if(verbose_ >= 1)
andrewm@0 1499 cout << "Warning: ignoring frame exceeding length limit " << (int)TOUCHKEY_MAX_FRAME_LENGTH << endl;
andrewm@0 1500 }
andrewm@0 1501 }
andrewm@0 1502 else if(ch == kControlCharacterNak && verbose_ >= 1) {
andrewm@0 1503 // TODO: pass this on to a checkForAck() call
andrewm@0 1504 cout << "Warning: received NAK (while receiving frame)\n";
andrewm@0 1505 }
andrewm@0 1506 }
andrewm@0 1507 else {
andrewm@0 1508 if(ch == ESCAPE_CHARACTER)
andrewm@0 1509 controlSeq = true;
andrewm@0 1510 else {
andrewm@0 1511 frame[frameLength++] = ch;
andrewm@0 1512 if(frameLength >= TOUCHKEY_MAX_FRAME_LENGTH) {
andrewm@0 1513 inFrame = false;
andrewm@0 1514 if(verbose_ >= 1)
andrewm@0 1515 cout << "Warning: ignoring frame exceeding length limit " << (int)TOUCHKEY_MAX_FRAME_LENGTH << endl;
andrewm@0 1516 }
andrewm@0 1517 }
andrewm@0 1518 }
andrewm@0 1519 }
andrewm@0 1520 else {
andrewm@0 1521 // Waiting for a frame beginning control sequence
andrewm@0 1522
andrewm@0 1523 if(controlSeq) {
andrewm@0 1524 controlSeq = false;
andrewm@0 1525 if(ch == kControlCharacterFrameBegin) {
andrewm@0 1526 inFrame = true;
andrewm@0 1527 frameLength = 0;
andrewm@0 1528 frameError = false;
andrewm@0 1529 }
andrewm@0 1530 else if(ch == kControlCharacterNak && verbose_ >= 1) {
andrewm@0 1531 // TODO: pass this on to a checkForAck() call
andrewm@0 1532 cout << "Warning: received NAK (while waiting for frame)\n";
andrewm@0 1533 }
andrewm@0 1534 }
andrewm@0 1535 else {
andrewm@0 1536 if(ch == ESCAPE_CHARACTER)
andrewm@0 1537 controlSeq = true;
andrewm@0 1538 }
andrewm@0 1539 }
andrewm@0 1540 }
andrewm@0 1541 }
andrewm@0 1542 }
andrewm@0 1543
andrewm@0 1544 // Process the contents of a frame that has been received from the device
andrewm@0 1545 void TouchkeyDevice::processFrame(unsigned char * const frame, int length) {
andrewm@0 1546 if(length == 0) // Empty frame --> nothing to do here
andrewm@0 1547 return;
andrewm@0 1548
andrewm@0 1549 switch(frame[0]) { // First character gives frame type
andrewm@0 1550 case kFrameTypeCentroid:
andrewm@0 1551 if(verbose_ >= 3)
andrewm@0 1552 cout << "Received centroid data\n";
andrewm@0 1553 processCentroidFrame(&frame[1], length - 1);
andrewm@0 1554 break;
andrewm@0 1555 case kFrameTypeRawKeyData:
andrewm@0 1556 if(verbose_ >= 3)
andrewm@0 1557 cout << "Received raw key data\n";
andrewm@0 1558 processRawDataFrame(&frame[1], length - 1);
andrewm@0 1559 break;
andrewm@0 1560 case kFrameTypeAnalog:
andrewm@0 1561 if(verbose_ >= 3)
andrewm@0 1562 cout << "Received analog data\n";
andrewm@0 1563 processAnalogFrame(&frame[1], length - 1);
andrewm@0 1564 break;
andrewm@0 1565 case kFrameTypeErrorMessage:
andrewm@0 1566 if(verbose_ >= 3)
andrewm@0 1567 cout << "Received error data\n";
andrewm@0 1568 processErrorMessageFrame(&frame[1], length-1);
andrewm@0 1569 break;
andrewm@0 1570 case kFrameTypeI2CResponse:
andrewm@0 1571 if(verbose_ >= 3)
andrewm@0 1572 cout << "Received I2C response\n";
andrewm@0 1573 processI2CResponseFrame(&frame[1], length - 1);
andrewm@0 1574 break;
andrewm@0 1575 case kFrameTypeStatus:
andrewm@0 1576 default:
andrewm@0 1577 if(verbose_ >= 3)
andrewm@0 1578 cout << "Received frame type " << (int)frame[0] << endl;
andrewm@0 1579 break;
andrewm@0 1580 }
andrewm@0 1581 }
andrewm@0 1582
andrewm@0 1583 // Process a frame of data containing centroid values (the default mode of scanning)
andrewm@0 1584 void TouchkeyDevice::processCentroidFrame(unsigned char * const buffer, const int bufferLength) {
andrewm@0 1585 int frame, octave, bufferIndex;
andrewm@0 1586
andrewm@0 1587 // Old and new generation devices structure the frame differently.
andrewm@0 1588 if((deviceSoftwareVersion_ <= 0 && bufferLength < 3) || (deviceSoftwareVersion_ > 0 && bufferLength < 5)) {
andrewm@0 1589 if(verbose_ >= 1)
andrewm@0 1590 cout << "Warning: ignoring malformed centroid frame of " << bufferLength << " bytes, less than minimum 3\n";
andrewm@0 1591 if(verbose_ >= 2) {
andrewm@0 1592 cout << " Contents: ";
andrewm@0 1593 hexDump(cout, buffer, bufferLength);
andrewm@0 1594 cout << endl;
andrewm@0 1595 }
andrewm@0 1596 return;
andrewm@0 1597 }
andrewm@0 1598
andrewm@0 1599 if(verbose_ >= 4) {
andrewm@0 1600 cout << "Centroid frame contents: ";
andrewm@0 1601 hexDump(cout, buffer, bufferLength);
andrewm@0 1602 cout << endl;
andrewm@0 1603 }
andrewm@0 1604
andrewm@0 1605 // Parse the octave and timestamp differently depending on hardware version
andrewm@0 1606 if(deviceSoftwareVersion_ > 0) {
andrewm@0 1607 octave = buffer[0]; // First byte is octave
andrewm@0 1608
andrewm@0 1609 // Frame is stored as 32-bit little endian value
andrewm@0 1610 frame = buffer[1] + ((int)buffer[2] << 8) + ((int)buffer[3] << 16) + ((int)buffer[4] << 24);
andrewm@0 1611 bufferIndex = 5;
andrewm@0 1612
andrewm@0 1613 if(verbose_ >= 3)
andrewm@0 1614 cout << "Centroid frame octave " << octave << " timestamp " << frame << endl;
andrewm@0 1615 }
andrewm@0 1616 else {
andrewm@0 1617 frame = (buffer[0] << 8) + buffer[1]; // First two bytes give us the timestamp in milliseconds (mod 2^16)
andrewm@0 1618 octave = buffer[2]; // Third byte tells us which octave of keys is being addressed
andrewm@0 1619 bufferIndex = 3;
andrewm@0 1620 }
andrewm@0 1621
andrewm@0 1622 // Convert from device frame number (expressed in USB 1ms SOF intervals) to a system
andrewm@0 1623 // timestamp that can be synchronized with other data streams
andrewm@0 1624 lastTimestamp_ = timestampSynchronizer_.synchronizedTimestamp(frame);
andrewm@0 1625
andrewm@0 1626 //ioMutex_.enter();
andrewm@0 1627
andrewm@0 1628 while(bufferIndex < bufferLength) {
andrewm@0 1629 // First byte tells us the number of the key (0-12); next bytes hold the data frame
andrewm@0 1630 int key = (int)buffer[bufferIndex++];
andrewm@0 1631 int bytesParsed = processKeyCentroid(frame,octave, key, lastTimestamp_, &buffer[bufferIndex], bufferLength - bufferIndex);
andrewm@0 1632
andrewm@0 1633 if(bytesParsed < 0) {
andrewm@0 1634 if(verbose_ >= 1)
andrewm@0 1635 cout << "Warning: malformed data frame (parsing key " << key << " at byte " << bufferIndex << ")\n";
andrewm@0 1636
andrewm@0 1637 if(verbose_ >= 2) {
andrewm@0 1638 cout << "--> Data: ";
andrewm@0 1639 hexDump(cout, buffer, bufferLength);
andrewm@0 1640 cout << endl;
andrewm@0 1641 }
andrewm@0 1642
andrewm@0 1643 break;
andrewm@0 1644 }
andrewm@0 1645
andrewm@0 1646 bufferIndex += bytesParsed;
andrewm@0 1647 }
andrewm@0 1648
andrewm@0 1649 if(updatedLowestMidiNote_ != lowestMidiNote_) {
andrewm@0 1650 int keyPresentDifference = (lowestKeyPresentMidiNote_ - lowestMidiNote_);
andrewm@0 1651
andrewm@0 1652 lowestMidiNote_ = updatedLowestMidiNote_;
andrewm@0 1653 lowestKeyPresentMidiNote_ = lowestMidiNote_ + keyPresentDifference;
andrewm@0 1654
andrewm@0 1655 // Turn off all existing touches before changing the octave
andrewm@0 1656 // so we don't end up with orphan touches when the "off" message is
andrewm@0 1657 // sent to a different octave than the "on"
andrewm@0 1658 for(int i = 0; i <= 127; i++)
andrewm@0 1659 if(keyboard_.key(i) != 0)
andrewm@0 1660 if(keyboard_.key(i)->touchIsActive())
andrewm@0 1661 keyboard_.key(i)->touchOff(lastTimestamp_);
andrewm@0 1662
andrewm@48 1663 keyboard_.setKeyboardGUIRange(lowestKeyPresentMidiNote_, lowestMidiNote_ + 12*numOctaves_ + lowestNotePerOctave_);
andrewm@0 1664 }
andrewm@0 1665
andrewm@0 1666 //ioMutex_.exit();
andrewm@0 1667 }
andrewm@0 1668
andrewm@0 1669 // Process a frame containing raw key data, whose configuration was set with startRawDataCollection()
andrewm@0 1670 // First byte holds the octave that the data came from.
andrewm@0 1671
andrewm@0 1672 void TouchkeyDevice::processRawDataFrame(unsigned char * const buffer, const int bufferLength) {
andrewm@0 1673 int octave = buffer[0];
andrewm@0 1674
andrewm@0 1675 if(verbose_ >= 3)
andrewm@0 1676 cout << "Raw data frame from octave " << octave << " contains " << bufferLength - 1 << " samples\n";
andrewm@0 1677
andrewm@0 1678 if(verbose_ >= 4) {
andrewm@0 1679 cout << " ";
andrewm@0 1680 hexDump(cout, &buffer[1], bufferLength - 1);
andrewm@0 1681 cout << endl;
andrewm@0 1682 }
andrewm@17 1683
andrewm@17 1684 // Change the first byte to contain the note number this data is expected to have come
andrewm@17 1685 // from (based on which key we are presently querying)
andrewm@17 1686 buffer[0] = lowestMidiNote_ + (rawDataCurrentOctave_ * 12 + rawDataCurrentKey_);
andrewm@0 1687
andrewm@0 1688 // Send raw data as an OSC blob
andrewm@0 1689 lo_blob b = lo_blob_new(bufferLength, buffer);
andrewm@0 1690 keyboard_.sendMessage("/touchkeys/rawbytes", "b", b, LO_ARGS_END);
andrewm@0 1691 lo_blob_free(b);
andrewm@0 1692 }
andrewm@0 1693
andrewm@0 1694 // Extract the floating-point centroid data for a key from packed character input.
andrewm@0 1695 // Send OSC features as appropriate
andrewm@0 1696
andrewm@17 1697 int TouchkeyDevice::processKeyCentroid(int frame, int octave, int key, timestamp_type timestamp, unsigned char * buffer, int maxLength) {
andrewm@0 1698 int touchCount = 0;
andrewm@0 1699
andrewm@0 1700 float sliderPosition[3];
andrewm@0 1701 float sliderPositionH;
andrewm@0 1702 float sliderSize[3];
andrewm@0 1703
andrewm@0 1704 int bytesParsed;
andrewm@0 1705
andrewm@0 1706 if(key < 0 || key > 12 || maxLength < 1)
andrewm@0 1707 return -1;
andrewm@0 1708
andrewm@0 1709 int white = (kKeyColor[key] == kKeyColorWhite);
andrewm@0 1710 int midiNote = octaveKeyToMidi(octave, key);
andrewm@0 1711
andrewm@0 1712 // Check that the received data is actually valid and not left over from a previous scan (which
andrewm@0 1713 // can happen when the scan rate is too high). 0x88 is a special "warning" marker for this case
andrewm@0 1714 // since it will never be part of a valid centroid.
andrewm@0 1715
andrewm@0 1716 if(buffer[0] == 0x88) {
andrewm@7 1717 if(verbose_ >= 1)
andrewm@7 1718 cout << "Warning: octave " << octave << " key " << key << " data is not ready. Check scan rate.\n";
andrewm@0 1719 if(deviceSoftwareVersion_ >= 1)
andrewm@0 1720 return white ? expectedLengthWhite_ : expectedLengthBlack_;
andrewm@0 1721 else
andrewm@0 1722 return 1;
andrewm@0 1723 }
andrewm@0 1724
andrewm@0 1725 // A value of 0xFF means that no touch is active, and no further data will be present on this key.
andrewm@0 1726
andrewm@0 1727 if(buffer[0] == 0xFF && deviceSoftwareVersion_ <= 0) {
andrewm@0 1728 bytesParsed = 1;
andrewm@0 1729 sliderPosition[0] = sliderPosition[1] = sliderPosition[2] = -1.0;
andrewm@0 1730 sliderSize[0] = sliderSize[1] = sliderSize[2] = 0.0;
andrewm@0 1731 sliderPositionH = -1.0;
andrewm@0 1732
andrewm@0 1733 if(verbose_ >= 4) {
andrewm@0 1734 cout << "Octave " << octave << " Key " << key << " (TS " << timestamp << "): ff\n";
andrewm@0 1735 }
andrewm@0 1736 }
andrewm@0 1737 else {
andrewm@0 1738 bytesParsed = white ? expectedLengthWhite_ : expectedLengthBlack_;
andrewm@0 1739
andrewm@0 1740 if(bytesParsed > maxLength) // Make sure there's enough buffer left to process this key
andrewm@0 1741 return -1;
andrewm@0 1742
andrewm@0 1743 int rawSliderPosition[3];
andrewm@0 1744 int rawSliderPositionH;
andrewm@0 1745
andrewm@0 1746 rawSliderPosition[0] = (((buffer[0] & 0xF0) << 4) + buffer[1]);
andrewm@0 1747 rawSliderPosition[1] = (((buffer[0] & 0x0F) << 8) + buffer[2]);
andrewm@0 1748 rawSliderPosition[2] = (((buffer[3] & 0xF0) << 4) + buffer[4]);
andrewm@0 1749
andrewm@0 1750 if(deviceHardwareVersion_ >= 2)
andrewm@0 1751 {
andrewm@0 1752 // Always an H value with version 2 sensor hardware
andrewm@0 1753 rawSliderPositionH = (((buffer[3] & 0x0F) << 8) + buffer[5]);
andrewm@0 1754
andrewm@0 1755 if(white) {
andrewm@0 1756 for(int i = 0; i < 3; i++) {
andrewm@0 1757 if(rawSliderPosition[i] != 0x0FFF) { // 0x0FFF means no touch
andrewm@0 1758 sliderPosition[i] = (float)rawSliderPosition[i] / whiteMaxY_;
andrewm@0 1759 sliderSize[i] = (float)buffer[i + 6] / kSizeMaxValue;
andrewm@0 1760 touchCount++;
andrewm@0 1761 }
andrewm@0 1762 else {
andrewm@0 1763 sliderPosition[i] = -1.0;
andrewm@0 1764 sliderSize[i] = 0.0;
andrewm@0 1765 }
andrewm@0 1766 }
andrewm@0 1767 }
andrewm@0 1768 else {
andrewm@0 1769 for(int i = 0; i < 3; i++) {
andrewm@0 1770 if(rawSliderPosition[i] != 0x0FFF) { // 0x0FFF means no touch
andrewm@0 1771 sliderPosition[i] = (float)rawSliderPosition[i] / blackMaxY_;
andrewm@0 1772 sliderSize[i] = (float)buffer[i + 6] / kSizeMaxValue;
andrewm@0 1773 touchCount++;
andrewm@0 1774 }
andrewm@0 1775 else {
andrewm@0 1776 sliderPosition[i] = -1.0;
andrewm@0 1777 sliderSize[i] = 0.0;
andrewm@0 1778 }
andrewm@0 1779 }
andrewm@0 1780 }
andrewm@0 1781 }
andrewm@0 1782 else
andrewm@0 1783 {
andrewm@0 1784 // H value only on white keys with version 0-1 sensor hardware
andrewm@0 1785
andrewm@0 1786 if(white) {
andrewm@0 1787 rawSliderPositionH = (((buffer[3] & 0x0F) << 8) + buffer[5]);
andrewm@0 1788
andrewm@0 1789 for(int i = 0; i < 3; i++) {
andrewm@0 1790 if(rawSliderPosition[i] != 0x0FFF) { // 0x0FFF means no touch
andrewm@0 1791 sliderPosition[i] = (float)rawSliderPosition[i] / whiteMaxY_;
andrewm@0 1792 sliderSize[i] = (float)buffer[i + 6] / kSizeMaxValue;
andrewm@0 1793 touchCount++;
andrewm@0 1794 }
andrewm@0 1795 else {
andrewm@0 1796 sliderPosition[i] = -1.0;
andrewm@0 1797 sliderSize[i] = 0.0;
andrewm@0 1798 }
andrewm@0 1799 }
andrewm@0 1800 }
andrewm@0 1801 else {
andrewm@0 1802 rawSliderPositionH = 0x0FFF;
andrewm@0 1803
andrewm@0 1804 for(int i = 0; i < 3; i++) {
andrewm@0 1805 if(rawSliderPosition[i] != 0x0FFF) { // 0x0FFF means no touch
andrewm@0 1806 sliderPosition[i] = (float)rawSliderPosition[i] / blackMaxY_;
andrewm@0 1807 sliderSize[i] = (float)buffer[i + 5] / kSizeMaxValue;
andrewm@0 1808 touchCount++;
andrewm@0 1809 }
andrewm@0 1810 else {
andrewm@0 1811 sliderPosition[i] = -1.0;
andrewm@0 1812 sliderSize[i] = 0.0;
andrewm@0 1813 }
andrewm@0 1814 }
andrewm@0 1815 }
andrewm@0 1816 }
andrewm@0 1817
andrewm@0 1818 if(rawSliderPositionH != 0x0FFF) {
andrewm@0 1819 sliderPositionH = (float)rawSliderPositionH / whiteMaxX_ ;
andrewm@0 1820 }
andrewm@0 1821 else
andrewm@0 1822 sliderPositionH = -1.0;
andrewm@0 1823
andrewm@0 1824 if(verbose_ >= 4) {
andrewm@0 1825 cout << "Octave " << octave << " Key " << key << ": ";
andrewm@0 1826 hexDump(cout, buffer, white ? expectedLengthWhite_ : expectedLengthBlack_);
andrewm@0 1827 cout << endl;
andrewm@0 1828 }
andrewm@0 1829 }
andrewm@0 1830
andrewm@0 1831 // Sanity check: do we have the PianoKey structure available to receive this data?
andrewm@0 1832 // If not, no need to proceed further.
andrewm@0 1833 if(keyboard_.key(midiNote) == 0) {
andrewm@7 1834 if(verbose_ >= 1)
andrewm@7 1835 cout << "Warning: No PianoKey available for touchkey MIDI note " << midiNote << endl;
andrewm@0 1836 return bytesParsed;
andrewm@0 1837 }
andrewm@0 1838
andrewm@0 1839 // From here on out, grab the performance data mutex so no MIDI events can show up in the middle
andrewm@0 1840 ScopedLock ksl(keyboard_.performanceDataMutex_);
andrewm@0 1841
andrewm@0 1842 // Turn off touch activity on this key if there's no active touches
andrewm@0 1843 if(touchCount == 0) {
andrewm@0 1844 if(keyboard_.key(midiNote)->touchIsActive())
andrewm@0 1845 {
andrewm@0 1846 keyboard_.key(midiNote)->touchOff(timestamp);
andrewm@0 1847 KeyTouchFrame newFrame(0, sliderPosition, sliderSize, sliderPositionH, white);
andrewm@0 1848
andrewm@0 1849 if (loggingActive_)
andrewm@0 1850 {
andrewm@0 1851 ////////////////////////////////////////////////////////
andrewm@0 1852 ////////////////////////////////////////////////////////
andrewm@0 1853 //////////////////// BEGIN LOGGING /////////////////////
andrewm@0 1854
andrewm@0 1855 keyTouchLog_.write((char*)&timestamp, sizeof(timestamp_type));
andrewm@0 1856 keyTouchLog_.write((char*)&frame, sizeof(int));
andrewm@0 1857 keyTouchLog_.write((char*)&midiNote, sizeof(int));
andrewm@0 1858 keyTouchLog_.write((char*)&newFrame, sizeof(KeyTouchFrame));
andrewm@0 1859
andrewm@0 1860 ///////////////////// END LOGGING //////////////////////
andrewm@0 1861 ////////////////////////////////////////////////////////
andrewm@0 1862 ////////////////////////////////////////////////////////
andrewm@0 1863 }
andrewm@0 1864
andrewm@0 1865 // Send raw OSC message if enabled
andrewm@0 1866 if(sendRawOscMessages_) {
andrewm@0 1867 keyboard_.sendMessage("/touchkeys/raw-off", "iii",
andrewm@0 1868 octave, key, frame,
andrewm@0 1869 LO_ARGS_END );
andrewm@0 1870 }
andrewm@0 1871
andrewm@0 1872 }
andrewm@0 1873
andrewm@0 1874 return bytesParsed;
andrewm@0 1875 }
andrewm@0 1876
andrewm@0 1877 // At this point, construct a new frame with this data and pass it to the PianoKey for
andrewm@0 1878 // further processing. Leave the ID fields empty; these are state-dependent and will
andrewm@0 1879 // be worked out based on the previous frames.
andrewm@0 1880
andrewm@0 1881 KeyTouchFrame newFrame(touchCount, sliderPosition, sliderSize, sliderPositionH, white);
andrewm@0 1882
andrewm@0 1883 keyboard_.key(midiNote)->touchInsertFrame(newFrame, timestamp);
andrewm@0 1884
andrewm@0 1885
andrewm@0 1886 if (loggingActive_)
andrewm@0 1887 {
andrewm@0 1888 ////////////////////////////////////////////////////////
andrewm@0 1889 ////////////////////////////////////////////////////////
andrewm@0 1890 //////////////////// BEGIN LOGGING /////////////////////
andrewm@0 1891
andrewm@0 1892 keyTouchLog_.write((char*)&timestamp, sizeof(timestamp_type));
andrewm@0 1893 keyTouchLog_.write((char*)&frame, sizeof(int));
andrewm@0 1894 keyTouchLog_.write((char*)&midiNote, sizeof(int));
andrewm@0 1895 keyTouchLog_.write((char*)&newFrame, sizeof(KeyTouchFrame));
andrewm@0 1896
andrewm@0 1897 ///////////////////// END LOGGING //////////////////////
andrewm@0 1898 ////////////////////////////////////////////////////////
andrewm@0 1899 ////////////////////////////////////////////////////////
andrewm@0 1900 }
andrewm@0 1901
andrewm@0 1902 // Send raw OSC message if enabled
andrewm@0 1903 if(sendRawOscMessages_) {
andrewm@0 1904 keyboard_.sendMessage("/touchkeys/raw", "iiifffffff",
andrewm@0 1905 octave, key, frame,
andrewm@0 1906 sliderPosition[0],
andrewm@0 1907 sliderSize[0],
andrewm@0 1908 sliderPosition[1],
andrewm@0 1909 sliderSize[1],
andrewm@0 1910 sliderPosition[2],
andrewm@0 1911 sliderSize[2],
andrewm@0 1912 sliderPositionH,
andrewm@0 1913 LO_ARGS_END );
andrewm@0 1914 }
andrewm@0 1915
andrewm@0 1916 // Verbose logging of key info
andrewm@0 1917 if(verbose_ >= 3) {
andrewm@0 1918 cout << "Octave " << octave << " Key " << key << " (TS " << timestamp << "): ";
andrewm@0 1919 cout << sliderPositionH << " ";
andrewm@0 1920 cout << sliderPosition[0] << " " << sliderPosition[1] << " " << sliderPosition[2] << " ";
andrewm@0 1921 cout << sliderSize[0] << " " << sliderSize[1] << " " << sliderSize[2] << endl;
andrewm@0 1922 }
andrewm@0 1923
andrewm@0 1924 return bytesParsed;
andrewm@0 1925 }
andrewm@0 1926
andrewm@0 1927 // Process a frame of data containing analog values (i.e. key angle, Z-axis). These
andrewm@0 1928 // always come as a group for a whole board, and should be parsed apart into individual keys
andrewm@0 1929 void TouchkeyDevice::processAnalogFrame(unsigned char * const buffer, const int bufferLength) {
andrewm@0 1930 // Format: [Octave] [TS0] [TS1] [TS2] [TS3] [Key0L] [Key0H] [Key1L] [Key1H] ... [Key24L] [Key24H]
andrewm@0 1931 // ... (more frames)
andrewm@0 1932 // [TS0] [TS1] [TS2] [TS3] [Key0L] [Key0H] [Key1L] [Key1H] ... [Key24L] [Key24H]
andrewm@0 1933
andrewm@0 1934 if(bufferLength < 1) {
andrewm@0 1935 if(verbose_ >= 1)
andrewm@0 1936 cout << "Warning: ignoring malformed analog frame of " << bufferLength << " bytes, less than minimum 1\n";
andrewm@0 1937 return;
andrewm@0 1938 }
andrewm@0 1939
andrewm@0 1940 int octave = buffer[0];
andrewm@0 1941 int board = octave / 2;
andrewm@0 1942 int frame;
andrewm@0 1943 int bufferIndex = 1;
andrewm@0 1944 int midiNote, value;
andrewm@0 1945
andrewm@0 1946 // Parse the buffer one frame at a time
andrewm@0 1947 while(bufferIndex < bufferLength) {
andrewm@0 1948 if(bufferLength - bufferIndex < 54) {
andrewm@0 1949 // This condition indicates a malformed analog frame (not enough data)
andrewm@0 1950 if(verbose_ >= 1)
andrewm@0 1951 cout << "Warning: ignoring extra analog data of " << bufferLength - bufferIndex << " bytes, less than full frame 54 (total " << bufferLength << ")\n";
andrewm@0 1952 break;
andrewm@0 1953 }
andrewm@0 1954
andrewm@0 1955 // Find the timestamp (i.e. frame ID generated by the device). 32-bit little-endian.
andrewm@0 1956 frame = buffer[bufferIndex] + ((int)buffer[bufferIndex+1] << 8) +
andrewm@0 1957 ((int)buffer[bufferIndex+2] << 16) + ((int)buffer[bufferIndex+3] << 24);
andrewm@0 1958
andrewm@0 1959 // Check the timestamp against the last frame from this board to see if any frames have been dropped
andrewm@0 1960 if(frame > analogLastFrame_[board] + 1) {
andrewm@7 1961 if(verbose_ >= 1)
andrewm@7 1962 cout << "WARNING: dropped frame(s) on board " << board << " at " << frame << " (last was " << analogLastFrame_[board] << ")" << endl;
andrewm@0 1963 }
andrewm@0 1964 else if(frame < analogLastFrame_[board] + 1) {
andrewm@7 1965 if(verbose_ >= 1)
andrewm@7 1966 cout << "WARNING: repeat frame(s) on board " << board << " at " << frame << " (last was " << analogLastFrame_[board] << ")" << endl;
andrewm@0 1967 }
andrewm@0 1968 analogLastFrame_[board] = frame;
andrewm@0 1969
andrewm@0 1970 // TESTING
andrewm@0 1971 /*if(verbose_ >= 3 || (frame % 500 == 0))
andrewm@0 1972 cout << "Analog frame octave " << octave << " timestamp " << frame << endl;
andrewm@0 1973 if(verbose_ >= 4 || (frame % 500 == 0)) {
andrewm@0 1974 cout << "Values: ";
andrewm@0 1975 for(int i = 0; i < 25; i++) {
andrewm@0 1976 cout << std::setw(5) << (((signed char)buffer[i*2 + 6])*256 + buffer[i*2 + 5]) << " ";
andrewm@0 1977 }
andrewm@0 1978 cout << endl;
andrewm@0 1979 }*/
andrewm@0 1980
andrewm@0 1981 // Process key values individually and add them to the keyboard data structure
andrewm@0 1982 for(int key = 0; key < 25; key++) {
andrewm@0 1983 // Every analog frame contains 25 values, however only the top board actually uses all 25
andrewm@0 1984 // sensors. There are several "high C" values in the lower boards (i.e. key == 24) which
andrewm@0 1985 // do not correspond to real sensors. These should be ignored.
andrewm@0 1986 if(key == 24 && octave != numberOfOctaves() - 2)
andrewm@0 1987 continue;
andrewm@0 1988
andrewm@0 1989 midiNote = octaveKeyToMidi(octave, key);
andrewm@0 1990
andrewm@0 1991 // Check that this note is in range to the available calibrators and keys.
andrewm@0 1992 if(keyboard_.key(midiNote) == 0 || (octave*12 + key) >= keyCalibratorsLength_ || midiNote < 21)
andrewm@0 1993 continue;
andrewm@0 1994
andrewm@0 1995 // Pull the value out from the packed buffer (little endian 16 bit)
andrewm@0 1996 value = (((signed char)buffer[key*2 + 6])*256 + buffer[key*2 + 5]);
andrewm@0 1997
andrewm@0 1998 // Calibrate the value, assuming the calibrator is ready and running
andrewm@0 1999 key_position calibratedPosition = keyCalibrators_[octave*12 + key]->evaluate(value);
andrewm@0 2000 if(!missing_value<key_position>::isMissing(calibratedPosition)) {
andrewm@0 2001 timestamp_type timestamp = timestampSynchronizer_.synchronizedTimestamp(frame);
andrewm@0 2002 keyboard_.key(midiNote)->insertSample(calibratedPosition, timestamp);
andrewm@0 2003 }
andrewm@0 2004 else if(keyboard_.gui() != 0){
andrewm@0 2005
andrewm@0 2006 //keyboard_.key(midiNote)->insertSample((float)value / 4096.0, timestampSynchronizer_.synchronizedTimestamp(frame));
andrewm@0 2007
andrewm@0 2008 // Update the GUI but don't actually save the value since it's uncalibrated
andrewm@0 2009 keyboard_.gui()->setAnalogValueForKey(midiNote, (float)value / kTouchkeyAnalogValueMax);
andrewm@0 2010
andrewm@7 2011 if(keyCalibrators_[octave*12 + key]->calibrationStatus() == kPianoKeyCalibrated) {
andrewm@7 2012 if(verbose_ >= 1)
andrewm@7 2013 cout << "key " << midiNote << " calibrated but missing (raw value " << value << ")\n";
andrewm@7 2014 }
andrewm@0 2015 }
andrewm@0 2016 }
andrewm@0 2017
andrewm@0 2018 if(loggingActive_) {
andrewm@0 2019 analogLog_.write((char*)&buffer[0], 1); // Octave number
andrewm@0 2020 analogLog_.write((char*)&buffer[bufferIndex], 54);
andrewm@0 2021 }
andrewm@0 2022
andrewm@0 2023 // Skip to next frame
andrewm@0 2024 bufferIndex += 54;
andrewm@0 2025 }
andrewm@0 2026 }
andrewm@0 2027
andrewm@0 2028 // Process a frame containing a human-readable (and machine-coded) error message generated
andrewm@0 2029 // internally by the device
andrewm@0 2030 void TouchkeyDevice::processErrorMessageFrame(unsigned char * const buffer, const int bufferLength) {
andrewm@0 2031 char msg[256];
andrewm@0 2032 int len = bufferLength - 5;
andrewm@0 2033
andrewm@0 2034 // Error on error message frame!
andrewm@0 2035 if(bufferLength < 5) {
andrewm@7 2036 if(verbose_ >= 1)
andrewm@7 2037 cout << "Warning: received error message frame of " << bufferLength << " bytes, less than minimum 5\n";
andrewm@0 2038 return;
andrewm@0 2039 }
andrewm@0 2040
andrewm@0 2041 // Limit length of string for safety reasons
andrewm@0 2042 if(len > 256)
andrewm@0 2043 len = 256;
andrewm@0 2044 memcpy(msg, &buffer[5], len * sizeof(char));
andrewm@0 2045 msg[len - 1] = '\0';
andrewm@0 2046
andrewm@0 2047 // Print the error
andrewm@7 2048 if(verbose_ >= 1)
andrewm@7 2049 cout << "Error frame received: " << msg << endl;
andrewm@0 2050
andrewm@0 2051 // Dump the buffer containing error coding information
andrewm@0 2052 if(verbose_ >= 2) {
andrewm@0 2053 cout << "Contents: ";
andrewm@0 2054 hexDump(cout, buffer, 5);
andrewm@0 2055 cout << endl;
andrewm@0 2056 }
andrewm@0 2057 }
andrewm@0 2058
andrewm@0 2059 // Process a frame containing a response to an I2C command. We can use this to gather
andrewm@0 2060 // raw information from the key.
andrewm@0 2061 void TouchkeyDevice::processI2CResponseFrame(unsigned char * const buffer, const int bufferLength) {
andrewm@0 2062 // Format: [octave] [key] [length] <data>
andrewm@0 2063
andrewm@0 2064 if(bufferLength < 3) {
andrewm@7 2065 if(verbose_ >= 1)
andrewm@7 2066 cout << "Warning: received I2C response frame of " << bufferLength << " bytes, less than minimum 3\n";
andrewm@0 2067 return;
andrewm@0 2068 }
andrewm@0 2069
andrewm@0 2070 int octave = buffer[0];
andrewm@0 2071 int key = buffer[1];
andrewm@0 2072 int responseLength = buffer[2];
andrewm@0 2073
andrewm@0 2074 if(bufferLength < responseLength + 3) {
andrewm@7 2075 if(verbose_ >= 1) {
andrewm@7 2076 cout << "Warning: received malformed I2C response (octave " << octave << ", key " << key << ", length " << responseLength;
andrewm@7 2077 cout << ") but only " << bufferLength - 3 << " bytes of data\n";
andrewm@7 2078 }
andrewm@17 2079 if(verbose_ >= 4) {
andrewm@17 2080 cout << " ";
andrewm@17 2081 hexDump(cout, &buffer[3], bufferLength - 3);
andrewm@17 2082 cout << endl;
andrewm@17 2083 }
andrewm@0 2084
andrewm@0 2085 responseLength = bufferLength - 3;
andrewm@0 2086 }
andrewm@0 2087 else {
andrewm@0 2088 if(verbose_ >= 3) {
andrewm@0 2089 cout << "I2C response from octave " << octave << ", key " << key << ", length " << responseLength << endl;
andrewm@0 2090 }
andrewm@17 2091 if(verbose_ >= 4) {
andrewm@17 2092 cout << " ";
andrewm@17 2093 hexDump(cout, &buffer[3], responseLength);
andrewm@17 2094 cout << endl;
andrewm@17 2095 }
andrewm@0 2096 }
andrewm@0 2097
andrewm@0 2098 if(sensorDisplay_ != 0) {
andrewm@0 2099 // Copy response data to display
andrewm@0 2100 vector<int> data;
andrewm@0 2101
andrewm@0 2102 for(int i = 3; i < responseLength + 3; i++) {
andrewm@0 2103 data.push_back(buffer[i]);
andrewm@0 2104 }
andrewm@0 2105 sensorDisplay_->setDisplayData(data);
andrewm@0 2106 }
andrewm@17 2107
andrewm@17 2108 // Change the first byte to contain the note number this data is expected to have come
andrewm@17 2109 // from (based on which key we are presently querying)
andrewm@17 2110 buffer[2] = lowestMidiNote_ + (octave * 12 + key);
andrewm@17 2111
andrewm@17 2112 // Send raw data as an OSC blob
andrewm@17 2113 lo_blob b = lo_blob_new(responseLength + 1, &buffer[2]);
andrewm@17 2114 keyboard_.sendMessage("/touchkeys/rawbytes", "b", b, LO_ARGS_END);
andrewm@17 2115 lo_blob_free(b);
andrewm@0 2116 }
andrewm@0 2117
andrewm@0 2118 // Parse raw data from a status request. Buffer should start immediately after the
andrewm@0 2119 // frame type byte, and processing will finish either at the end of the expected buffer,
andrewm@0 2120 // or at the given length, whichever comes first. Returns true if a status buffer was
andrewm@0 2121 // successfully received.
andrewm@0 2122
andrewm@0 2123 bool TouchkeyDevice::processStatusFrame(unsigned char * buffer, int maxLength, TouchkeyDevice::ControllerStatus *status) {
andrewm@0 2124 if((status == 0 || maxLength < 5) && verbose_ >= 1) {
andrewm@0 2125 cout << "Invalid status frame: ";
andrewm@0 2126 hexDump(cout, buffer, maxLength);
andrewm@0 2127 cout << endl;
andrewm@0 2128 return false;
andrewm@0 2129 }
andrewm@0 2130
andrewm@0 2131 status->hardwareVersion = buffer[0];
andrewm@0 2132 status->softwareVersionMajor = buffer[1];
andrewm@0 2133 status->softwareVersionMinor = buffer[2];
andrewm@0 2134 status->running = ((buffer[3] & kStatusFlagRunning) != 0);
andrewm@0 2135 status->octaves = buffer[4];
andrewm@0 2136 status->connectedKeys = (unsigned int *)malloc(2*status->octaves*sizeof(unsigned int));
andrewm@0 2137
andrewm@0 2138 int i, oct = 0; // Get connected key information
andrewm@0 2139 if(status->softwareVersionMajor >= 2) {
andrewm@0 2140 // One extra byte holds lowest physical sensor
andrewm@0 2141 status->lowestHardwareNote = buffer[5];
andrewm@0 2142 status->hasTouchSensors = ((buffer[3] & kStatusFlagHasI2C) != 0);
andrewm@0 2143 status->hasAnalogSensors = ((buffer[3] & kStatusFlagHasAnalog) != 0);
andrewm@0 2144 status->hasRGBLEDs = ((buffer[3] & kStatusFlagHasRGBLED) != 0);
andrewm@0 2145 i = 6;
andrewm@0 2146 }
andrewm@0 2147 else {
andrewm@0 2148 status->lowestHardwareNote = 0;
andrewm@0 2149 status->hasTouchSensors = true;
andrewm@0 2150 status->hasAnalogSensors = true;
andrewm@0 2151 status->hasRGBLEDs = true;
andrewm@0 2152 i = 5;
andrewm@0 2153 }
andrewm@0 2154
andrewm@0 2155 while(i+1 < maxLength) {
andrewm@0 2156 status->connectedKeys[oct] = 256*buffer[i] + buffer[i+1];
andrewm@0 2157 i += 2;
andrewm@0 2158 oct++;
andrewm@0 2159 }
andrewm@0 2160
andrewm@0 2161 if(oct < status->octaves && verbose_ >= 1) {
andrewm@0 2162 cout << "Invalid status frame: ";
andrewm@0 2163 hexDump(cout, buffer, maxLength);
andrewm@0 2164 cout << endl;
andrewm@0 2165 return false;
andrewm@0 2166 }
andrewm@0 2167
andrewm@0 2168 return true;
andrewm@0 2169 }
andrewm@0 2170
andrewm@17 2171 // Prepare the indicated key for raw data collection
andrewm@17 2172 void TouchkeyDevice::rawDataPrepareCollection(int octave, int key, int mode, int scaler) {
andrewm@22 2173 Thread::sleep(10);
andrewm@17 2174
andrewm@17 2175 // Command to set the mode of the key
andrewm@17 2176 unsigned char commandSetMode[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin,
andrewm@17 2177 kFrameTypeSendI2CCommand, (unsigned char)octave, (unsigned char)key,
andrewm@17 2178 3 /* xmit */, 0 /* response */, 0 /* command offset */, 1 /* mode */, (unsigned char)mode,
andrewm@17 2179 ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@17 2180
andrewm@22 2181 if(deviceWrite((char*)commandSetMode, 12) < 0) {
andrewm@17 2182 if(verbose_ >= 1)
andrewm@17 2183 cout << "ERROR: unable to write setMode command. errno = " << errno << endl;
andrewm@17 2184 }
andrewm@20 2185
andrewm@22 2186 Thread::sleep(10);
andrewm@17 2187
andrewm@17 2188 // Command to set the scaler of the key
andrewm@17 2189 unsigned char commandSetScaler[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin,
andrewm@17 2190 kFrameTypeSendI2CCommand, (unsigned char)octave, (unsigned char)key,
andrewm@17 2191 3 /* xmit */, 0 /* response */, 0 /* command offset */, 3 /* raw scaler */, (unsigned char)scaler,
andrewm@17 2192 ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@17 2193
andrewm@22 2194 if(deviceWrite((char*)commandSetScaler, 12) < 0) {
andrewm@17 2195 if(verbose_ >= 1)
andrewm@17 2196 cout << "ERROR: unable to write setMode command. errno = " << errno << endl;
andrewm@17 2197 }
andrewm@19 2198
andrewm@22 2199 Thread::sleep(10);
andrewm@17 2200
andrewm@17 2201 unsigned char commandPrepareRead[] = {ESCAPE_CHARACTER, kControlCharacterFrameBegin,
andrewm@17 2202 kFrameTypeSendI2CCommand, (unsigned char)octave, (unsigned char)key,
andrewm@17 2203 1 /* xmit */, 0 /* response */, 6 /* data offset */,
andrewm@17 2204 ESCAPE_CHARACTER, kControlCharacterFrameEnd};
andrewm@17 2205
andrewm@22 2206 if(deviceWrite((char*)commandPrepareRead, 10) < 0) {
andrewm@17 2207 if(verbose_ >= 1)
andrewm@17 2208 cout << "ERROR: unable to write prepareRead command. errno = " << errno << endl;
andrewm@17 2209 }
andrewm@20 2210
andrewm@22 2211 Thread::sleep(10);
andrewm@17 2212
andrewm@17 2213 rawDataShouldChangeMode_ = false;
andrewm@17 2214 }
andrewm@17 2215
andrewm@0 2216 // Check for an ACK response from the device. Returns true if found. Returns
andrewm@0 2217 // false if NAK received, or if a timeout occurs.
andrewm@0 2218 // TODO: this implementation needs to change to not chew up other data coming in.
andrewm@0 2219
andrewm@0 2220 bool TouchkeyDevice::checkForAck(int timeoutMilliseconds) {
andrewm@0 2221 //struct timeval startTime, currentTime;
andrewm@0 2222 bool controlSeq = false;
andrewm@0 2223 unsigned char ch;
andrewm@0 2224
andrewm@0 2225 double startTime = Time::getMillisecondCounterHiRes();
andrewm@0 2226 double currentTime = startTime;
andrewm@0 2227
andrewm@0 2228 //gettimeofday(&startTime, 0);
andrewm@0 2229 //gettimeofday(&currentTime, 0);
andrewm@0 2230
andrewm@0 2231 while(currentTime - startTime < (double)timeoutMilliseconds) {
andrewm@22 2232 long count = deviceRead((char *)&ch, 1);
andrewm@20 2233
andrewm@0 2234 if(count < 0) { // Check if an error occurred on read
andrewm@0 2235 if(errno != EAGAIN) {
andrewm@7 2236 if(verbose_ >= 1)
andrewm@7 2237 cout << "Unable to read from device while waiting for ACK (error " << errno << "). Aborting.\n";
andrewm@0 2238 return false;
andrewm@0 2239 }
andrewm@0 2240 }
andrewm@0 2241 else if(count > 0) { // Data received
andrewm@0 2242 // Wait for a sequence {ESCAPE_CHARACTER, ACK} or {ESCAPE_CHARACTER, NAK}
andrewm@0 2243 if(controlSeq) {
andrewm@0 2244 controlSeq = false;
andrewm@0 2245 if(ch == kControlCharacterAck) {
andrewm@0 2246 if(verbose_ >= 2)
andrewm@0 2247 cout << "Received ACK\n";
andrewm@0 2248 return true;
andrewm@0 2249 }
andrewm@0 2250 else if(ch == kControlCharacterNak) {
andrewm@0 2251 if(verbose_ >= 1)
andrewm@0 2252 cout << "Warning: received NAK\n";
andrewm@0 2253 return false;
andrewm@0 2254 }
andrewm@0 2255 }
andrewm@0 2256 else if(ch == ESCAPE_CHARACTER)
andrewm@0 2257 controlSeq = true;
andrewm@0 2258 }
andrewm@0 2259
andrewm@0 2260 currentTime = Time::getMillisecondCounterHiRes();
andrewm@0 2261 }
andrewm@0 2262
andrewm@7 2263 if(verbose_ >= 1)
andrewm@7 2264 cout << "Error: timeout waiting for ACK\n";
andrewm@0 2265 return false;
andrewm@0 2266 }
andrewm@0 2267
andrewm@0 2268 // Convenience method to dump hexadecimal output
andrewm@0 2269 void TouchkeyDevice::hexDump(ostream& str, unsigned char * buffer, int length) {
andrewm@0 2270 if(length <= 0)
andrewm@0 2271 return;
andrewm@0 2272 str << std::hex << (int)buffer[0];
andrewm@0 2273 for(int i = 1; i < length; i++) {
andrewm@0 2274 str << " " << (int)buffer[i];
andrewm@0 2275 }
andrewm@0 2276 str << std::dec;
andrewm@0 2277 }
andrewm@0 2278
andrewm@22 2279 // Read from the TouchKeys device
andrewm@22 2280 long TouchkeyDevice::deviceRead(char *buffer, unsigned int count) {
andrewm@22 2281 #ifdef _MSC_VER
andrewm@23 2282 int n;
andrewm@23 2283
andrewm@23 2284 if(!ReadFile(serialHandle_, buffer, count, (LPDWORD)((void *)&n), NULL))
andrewm@23 2285 return -1;
andrewm@23 2286 return n;
andrewm@22 2287 #else
andrewm@22 2288 return read(device_, buffer, count);
andrewm@22 2289 #endif
andrewm@22 2290 }
andrewm@22 2291
andrewm@22 2292 // Write to the TouchKeys device
andrewm@22 2293 int TouchkeyDevice::deviceWrite(char *buffer, unsigned int count) {
andrewm@23 2294 int result;
andrewm@22 2295
andrewm@22 2296 #ifdef _MSC_VER
andrewm@23 2297 if(!WriteFile(serialHandle_, buffer, count, (LPDWORD)((void *)&result), NULL))
andrewm@23 2298 return -1;
andrewm@22 2299 #else
andrewm@22 2300 result = write(device_, buffer, count);
andrewm@22 2301 #endif
andrewm@22 2302 deviceDrainOutput();
andrewm@22 2303 return result;
andrewm@22 2304 }
andrewm@22 2305
andrewm@23 2306 // Flush (discard) the TouchKeys device input
andrewm@22 2307 void TouchkeyDevice::deviceFlush(bool bothDirections) {
andrewm@22 2308 #ifdef _MSC_VER
andrewm@23 2309 // WINDOWS_TODO (?)
andrewm@22 2310 #else
andrewm@22 2311 if(bothDirections)
andrewm@22 2312 tcflush(device_, TCIOFLUSH);
andrewm@22 2313 else
andrewm@22 2314 tcflush(device_, TCIFLUSH); // Flush device input
andrewm@22 2315 #endif
andrewm@22 2316 }
andrewm@22 2317
andrewm@23 2318 // Flush the TouchKeys device output
andrewm@22 2319 void TouchkeyDevice::deviceDrainOutput() {
andrewm@22 2320 #ifdef _MSC_VER
andrewm@23 2321 FlushFileBuffers(serialHandle_);
andrewm@22 2322 #else
andrewm@22 2323 tcdrain(device_);
andrewm@22 2324 #endif
andrewm@22 2325 }
andrewm@22 2326
andrewm@22 2327
andrewm@0 2328 TouchkeyDevice::~TouchkeyDevice() {
andrewm@0 2329 if (logFileCreated_)
andrewm@0 2330 {
andrewm@0 2331 keyTouchLog_.close();
andrewm@0 2332 analogLog_.close();
andrewm@0 2333 }
andrewm@0 2334
andrewm@0 2335 closeDevice();
andrewm@0 2336 calibrationDeinit();
andrewm@0 2337 }