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(¤tTime, 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*)×tamp, 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*)×tamp, 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(¤tTime, 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 } |