annotate Source/MainApplicationController.cpp @ 56:b4a2d2ae43cf tip

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents 19650b4076ee e468cb91794a
children
rev   line source
andrewm@0 1 /*
andrewm@0 2 TouchKeys: multi-touch musical keyboard control software
andrewm@0 3 Copyright (c) 2013 Andrew McPherson
andrewm@0 4
andrewm@0 5 This program is free software: you can redistribute it and/or modify
andrewm@0 6 it under the terms of the GNU General Public License as published by
andrewm@0 7 the Free Software Foundation, either version 3 of the License, or
andrewm@0 8 (at your option) any later version.
andrewm@0 9
andrewm@0 10 This program is distributed in the hope that it will be useful,
andrewm@0 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
andrewm@0 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
andrewm@0 13 GNU General Public License for more details.
andrewm@0 14
andrewm@0 15 You should have received a copy of the GNU General Public License
andrewm@0 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
andrewm@0 17
andrewm@0 18 =====================================================================
andrewm@0 19
andrewm@0 20 MainApplicationController.cpp: contains the overall glue that holds
andrewm@0 21 together the various parts of the TouchKeys code. It works together
andrewm@0 22 with the user interface to let the user configure the hardware and
andrewm@0 23 manage the mappings, but it is kept separate from any particular user
andrewm@0 24 interface configuration.
andrewm@0 25 */
andrewm@0 26
andrewm@0 27 #include "MainApplicationController.h"
andrewm@17 28 #ifndef TOUCHKEYS_NO_GUI
andrewm@17 29 #include "Display/KeyboardTesterDisplay.h"
andrewm@17 30 #endif
andrewm@0 31 #include <cstdlib>
andrewm@0 32 #include <sstream>
andrewm@0 33
andrewm@0 34 // Strings for pitch classes (two forms for sharps), for static methods
andrewm@0 35 const char* kNoteNames[12] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
andrewm@0 36 const char* kNoteNamesAlternate[12] = {"C", "Db", "D ", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"};
andrewm@0 37
andrewm@0 38 MainApplicationController::MainApplicationController()
andrewm@0 39 : midiInputController_(keyboardController_),
andrewm@9 40 oscReceiver_(0, "/touchkeys"),
andrewm@0 41 touchkeyController_(keyboardController_),
andrewm@9 42 touchkeyEmulator_(keyboardController_, oscReceiver_),
andrewm@53 43 logPlayback_(0),
andrewm@11 44 #ifdef TOUCHKEY_ENTROPY_GENERATOR_ENABLE
andrewm@11 45 touchkeyEntropyGenerator_(keyboardController_),
andrewm@11 46 entropyGeneratorSelected_(false),
andrewm@11 47 #endif
andrewm@0 48 touchkeyErrorOccurred_(false),
andrewm@0 49 touchkeyErrorMessage_(""),
andrewm@0 50 touchkeyAutodetecting_(false),
andrewm@0 51 touchkeyStandaloneModeEnabled_(false),
andrewm@28 52 deviceUpdateCounter_(0),
andrewm@7 53 oscReceiveEnabled_(false),
andrewm@6 54 oscReceivePort_(kDefaultOscReceivePort),
andrewm@0 55 experimentalMappingsEnabled_(false),
andrewm@0 56 #ifndef TOUCHKEYS_NO_GUI
andrewm@0 57 keyboardDisplayWindow_(0),
andrewm@17 58 keyboardTesterDisplay_(0),
andrewm@17 59 keyboardTesterWindow_(0),
andrewm@41 60 preferencesWindow_(0),
andrewm@0 61 #endif
andrewm@0 62 segmentCounter_(0),
andrewm@53 63 loggingActive_(false),
andrewm@53 64 isPlayingLog_(false)
andrewm@0 65 {
andrewm@0 66 // Set our OSC controller
andrewm@0 67 setOscController(&keyboardController_);
andrewm@0 68 oscTransmitter_.setEnabled(false);
andrewm@0 69 //oscTransmitter_.setDebugMessages(true);
andrewm@0 70
andrewm@0 71 // Initialize the links between objects
andrewm@0 72 keyboardController_.setOscTransmitter(&oscTransmitter_);
andrewm@0 73 keyboardController_.setMidiOutputController(&midiOutputController_);
andrewm@0 74 keyboardController_.setGUI(&keyboardDisplay_);
andrewm@0 75 midiInputController_.setMidiOutputController(&midiOutputController_);
andrewm@0 76
andrewm@0 77 // Set up default logging directory
andrewm@0 78 loggingDirectory_ = (File::getSpecialLocation(File::userHomeDirectory).getFullPathName() + "/Desktop").toUTF8();
andrewm@0 79
andrewm@41 80 // Configure application properties
andrewm@41 81 PropertiesFile::Options options;
andrewm@41 82 options.applicationName = "TouchKeys";
andrewm@41 83 options.folderName = "TouchKeys";
andrewm@41 84 options.filenameSuffix = ".properties";
andrewm@41 85 options.osxLibrarySubFolder = "Application Support";
andrewm@41 86 applicationProperties_.setStorageParameters(options);
andrewm@41 87
andrewm@0 88 // Defaults for display, until we get other information
andrewm@0 89 keyboardDisplay_.setKeyboardRange(36, 72);
andrewm@0 90
andrewm@0 91 // Add one keyboard segment at the beginning
andrewm@0 92 midiSegmentAdd();
andrewm@41 93
andrewm@41 94 // Load the current preferences
andrewm@41 95 loadApplicationPreferences();
andrewm@41 96
andrewm@41 97 // Set up an initial OSC transmit host/port if none has been loaded
andrewm@41 98 if(oscTransmitter_.addresses().size() == 0)
andrewm@41 99 oscTransmitter_.addAddress(kDefaultOscTransmitHost, kDefaultOscTransmitPort);
andrewm@49 100
andrewm@49 101 // Listen for control messages by OSC
andrewm@49 102 mainOscController_ = new MainApplicationOSCController(*this, oscReceiver_);
andrewm@0 103 }
andrewm@0 104
andrewm@0 105 MainApplicationController::~MainApplicationController() {
andrewm@17 106 #ifdef ENABLE_TOUCHKEYS_SENSOR_TEST
andrewm@17 107 if(touchkeySensorTestIsRunning())
andrewm@17 108 touchkeySensorTestStop();
andrewm@17 109 #endif
andrewm@53 110 if(logPlayback_ != 0)
andrewm@53 111 delete logPlayback_;
andrewm@42 112 removeAllOscListeners();
andrewm@51 113 midiInputController_.removeAllSegments(); // Remove segments now to avoid deletion-order problems
andrewm@49 114 delete mainOscController_;
andrewm@0 115 }
andrewm@0 116
andrewm@41 117 // Actions here run in the JUCE initialise() method once the application is loaded
andrewm@41 118 void MainApplicationController::initialise() {
andrewm@41 119 // Load a preset if enabled
andrewm@41 120 if(getPrefsStartupPresetLastSaved()) {
andrewm@41 121 if(applicationProperties_.getUserSettings()->containsKey("LastSavedPreset")) {
andrewm@41 122 String presetFile = applicationProperties_.getUserSettings()->getValue("LastSavedPreset");
andrewm@41 123 if(presetFile != "") {
andrewm@41 124 loadPresetFromFile(presetFile.toUTF8());
andrewm@41 125 }
andrewm@41 126 }
andrewm@41 127 }
andrewm@41 128 else if(getPrefsStartupPresetVibratoPitchBend()) {
andrewm@41 129 if(midiInputController_.numSegments() > 0) {
andrewm@41 130 MidiKeyboardSegment *segment = midiInputController_.segment(0);
andrewm@41 131
andrewm@41 132 MappingFactory *factory = new TouchkeyVibratoMappingFactory(keyboardController_, *segment);
andrewm@41 133 if(factory != 0)
andrewm@42 134 segment->addMappingFactory(factory, true);
andrewm@41 135 factory = new TouchkeyPitchBendMappingFactory(keyboardController_, *segment);
andrewm@41 136 if(factory != 0)
andrewm@42 137 segment->addMappingFactory(factory, true);
andrewm@41 138 }
andrewm@41 139 }
andrewm@41 140 else if(!getPrefsStartupPresetNone()) {
andrewm@41 141 String presetFile = getPrefsStartupPreset();
andrewm@41 142 if(presetFile != "") {
andrewm@41 143 loadPresetFromFile(presetFile.toUTF8());
andrewm@41 144 }
andrewm@41 145 }
andrewm@41 146
andrewm@41 147 // Automatically start the TouchKeys if the preferences are enabled
andrewm@41 148 if(getPrefsAutoStartTouchKeys() && applicationProperties_.getUserSettings()->containsKey("TouchKeysDevice")) {
andrewm@41 149 String tkDevicePath = applicationProperties_.getUserSettings()->getValue("TouchKeysDevice");
andrewm@41 150 if(touchkeyDeviceExists(tkDevicePath.toUTF8())) {
andrewm@41 151 // Exists: try to open and run
andrewm@41 152 touchkeyDeviceStartupSequence(tkDevicePath.toUTF8());
andrewm@41 153 }
andrewm@41 154 }
andrewm@41 155 }
andrewm@41 156
andrewm@0 157 bool MainApplicationController::touchkeyDeviceStartupSequence(const char * path) {
andrewm@11 158 #ifdef TOUCHKEY_ENTROPY_GENERATOR_ENABLE
andrewm@23 159 if(!strcmp(path, "/dev/Entropy Generator") || !strcmp(path, "\\\\.\\Entropy Generator")) {
andrewm@11 160 entropyGeneratorSelected_ = true;
andrewm@11 161 touchkeyEntropyGenerator_.start();
andrewm@11 162 }
andrewm@11 163 else {
andrewm@11 164 entropyGeneratorSelected_ = false;
andrewm@11 165 #endif
andrewm@11 166
andrewm@0 167 // Step 1: attempt to open device
andrewm@0 168 if(!openTouchkeyDevice(path)) {
andrewm@0 169 touchkeyErrorMessage_ = "Failed to open";
andrewm@0 170 touchkeyErrorOccurred_ = true;
andrewm@0 171 return false;
andrewm@0 172 }
andrewm@0 173
andrewm@0 174 // Step 2: see if a real TouchKeys device is present at the other end
andrewm@0 175 if(!touchkeyDeviceCheckForPresence()) {
andrewm@0 176 touchkeyErrorMessage_ = "Device not recognized";
andrewm@0 177 touchkeyErrorOccurred_ = true;
andrewm@0 178 return false;
andrewm@0 179 }
andrewm@0 180
andrewm@0 181 // Step 3: update the display
andrewm@0 182 keyboardDisplay_.setKeyboardRange(touchkeyController_.lowestKeyPresentMidiNote(), touchkeyController_.highestMidiNote());
andrewm@0 183 #ifndef TOUCHKEYS_NO_GUI
andrewm@0 184 if(keyboardDisplayWindow_ != 0) {
andrewm@0 185 keyboardDisplayWindow_->getConstrainer()->setFixedAspectRatio(keyboardDisplay_.keyboardAspectRatio());
andrewm@48 186
a@54 187 juce::Rectangle<int> bounds = keyboardDisplayWindow_->getBounds();
andrewm@48 188 if(bounds.getY() < 44)
andrewm@48 189 bounds.setY(44);
andrewm@48 190 keyboardDisplayWindow_->setBoundsConstrained(bounds);
andrewm@0 191 }
andrewm@0 192 #endif
andrewm@0 193
andrewm@0 194 // Step 4: start data collection from the device
andrewm@0 195 if(!startTouchkeyDevice()) {
andrewm@0 196 touchkeyErrorMessage_ = "Failed to start";
andrewm@0 197 touchkeyErrorOccurred_ = true;
andrewm@0 198 }
andrewm@0 199
andrewm@11 200 #ifdef TOUCHKEY_ENTROPY_GENERATOR_ENABLE
andrewm@11 201 }
andrewm@11 202 #endif
andrewm@11 203
andrewm@0 204 // Success!
andrewm@0 205 touchkeyErrorMessage_ = "";
andrewm@0 206 touchkeyErrorOccurred_ = false;
andrewm@41 207
andrewm@50 208 #ifndef TOUCHKEYS_NO_GUI
andrewm@41 209 showKeyboardDisplayWindow();
andrewm@50 210 #endif
andrewm@41 211
andrewm@41 212 // Automatically detect the lowest octave if set
andrewm@41 213 if(getPrefsAutodetectOctave())
andrewm@41 214 touchkeyDeviceAutodetectLowestMidiNote();
andrewm@41 215
andrewm@0 216 return true;
andrewm@0 217 }
andrewm@0 218
andrewm@0 219 std::string MainApplicationController::touchkeyDevicePrefix() {
andrewm@23 220 #ifdef _MSC_VER
andrewm@23 221 return "\\\\.\\";
andrewm@23 222 #else
andrewm@0 223 if(SystemStats::getOperatingSystemType() == SystemStats::Linux) {
andrewm@0 224 return "/dev/serial/by-id/";
andrewm@0 225 }
andrewm@0 226 else {
andrewm@0 227 return "/dev/";
andrewm@0 228 }
andrewm@23 229 #endif
andrewm@0 230 }
andrewm@0 231
andrewm@0 232 // Return a list of available TouchKey devices
andrewm@0 233 std::vector<std::string> MainApplicationController::availableTouchkeyDevices() {
andrewm@0 234 std::vector<std::string> devices;
andrewm@23 235
andrewm@27 236 #ifdef _MSC_VER
andrewm@27 237 for(int i = 1; i <= 128; i++) {
andrewm@27 238 String comPortName("COM");
andrewm@27 239 comPortName += i;
andrewm@27 240
andrewm@27 241 DWORD dwSize = 0;
andrewm@27 242 LPCOMMCONFIG lpCC = (LPCOMMCONFIG) new BYTE[1];
andrewm@27 243 BOOL ret = GetDefaultCommConfig(comPortName.toUTF8(), lpCC, &dwSize);
andrewm@27 244 delete [] lpCC;
andrewm@27 245
andrewm@27 246 if(ret)
andrewm@27 247 devices.push_back(comPortName.toStdString());
andrewm@27 248 else {
andrewm@27 249 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
andrewm@27 250 //Logger::writeToLog(String::formatted("Found " + comPortName));
andrewm@27 251 lpCC = (LPCOMMCONFIG) new BYTE[dwSize];
andrewm@27 252 ret = GetDefaultCommConfig(comPortName.toUTF8(), lpCC, &dwSize);
andrewm@27 253 if(ret)
andrewm@27 254 devices.push_back(comPortName.toStdString());
andrewm@27 255 else {
andrewm@27 256 int error = GetLastError();
andrewm@27 257 //Logger::writeToLog(String("2Didn't find " + comPortName + "; error " + String(error)));
andrewm@27 258 }
andrewm@27 259 delete [] lpCC;
andrewm@27 260 }
andrewm@27 261 else {
andrewm@27 262 int error = GetLastError();
andrewm@27 263 //Logger::writeToLog(String("Didn't find " + comPortName + "; error " + String(error)));
andrewm@27 264 }
andrewm@27 265 }
andrewm@23 266 }
andrewm@23 267 #else
andrewm@0 268 if(SystemStats::getOperatingSystemType() == SystemStats::Linux) {
andrewm@0 269 DirectoryIterator devDirectory(File("/dev/serial/by-id"),false,"*");
andrewm@0 270
andrewm@0 271 while(devDirectory.next()) {
andrewm@0 272 devices.push_back(string(devDirectory.getFile().getFileName().toUTF8()));
andrewm@0 273 }
andrewm@0 274 }
andrewm@0 275 else {
andrewm@0 276 DirectoryIterator devDirectory(File("/dev"),false,"cu.usbmodem*");
andrewm@0 277
andrewm@0 278 while(devDirectory.next()) {
andrewm@0 279 devices.push_back(string(devDirectory.getFile().getFileName().toUTF8()));
andrewm@0 280 }
andrewm@0 281 }
andrewm@23 282 #endif
andrewm@0 283
andrewm@11 284 #ifdef TOUCHKEY_ENTROPY_GENERATOR_ENABLE
andrewm@11 285 devices.push_back("Entropy Generator");
andrewm@11 286 #endif
andrewm@11 287
andrewm@0 288 return devices;
andrewm@0 289 }
andrewm@0 290
andrewm@41 291 void MainApplicationController::touchkeyDeviceClearErrorMessage() {
andrewm@41 292 touchkeyErrorMessage_ = "";
andrewm@41 293 touchkeyErrorOccurred_ = false;
andrewm@41 294 }
andrewm@41 295
andrewm@41 296 // Check whether a given touchkey device exists
andrewm@41 297 bool MainApplicationController::touchkeyDeviceExists(const char * path) {
andrewm@41 298 String pathString(path);
andrewm@41 299 File tkDeviceFile(pathString);
andrewm@41 300 return tkDeviceFile.existsAsFile();
andrewm@41 301 }
andrewm@41 302
andrewm@41 303 // Select a particular touchkey device
andrewm@41 304 bool MainApplicationController::openTouchkeyDevice(const char * path) {
andrewm@41 305 bool success = touchkeyController_.openDevice(path);
andrewm@41 306
andrewm@41 307 if(success)
andrewm@41 308 applicationProperties_.getUserSettings()->setValue("TouchKeysDevice", String(path));
andrewm@41 309 return success;
andrewm@41 310 }
andrewm@41 311
andrewm@11 312 // Close the currently open TouchKeys device
andrewm@11 313 void MainApplicationController::closeTouchkeyDevice() {
andrewm@11 314 #ifdef TOUCHKEY_ENTROPY_GENERATOR_ENABLE
andrewm@11 315 if(entropyGeneratorSelected_)
andrewm@11 316 touchkeyEntropyGenerator_.stop();
andrewm@11 317 else
andrewm@11 318 touchkeyController_.closeDevice();
andrewm@11 319 #else
andrewm@11 320 touchkeyController_.closeDevice();
andrewm@11 321 #endif
andrewm@11 322 }
andrewm@11 323
andrewm@0 324 // Check whether a TouchKey device is present. Returns true if device found.
andrewm@0 325 bool MainApplicationController::touchkeyDeviceCheckForPresence(int waitMilliseconds, int tries) {
andrewm@0 326
andrewm@0 327 int count = 0;
andrewm@0 328 while(1) {
andrewm@0 329 if(touchkeyController_.checkIfDevicePresent(waitMilliseconds))
andrewm@0 330 break;
andrewm@0 331 if(++count >= tries) {
andrewm@0 332 return false;
andrewm@0 333 }
andrewm@0 334 }
andrewm@0 335
andrewm@0 336 return true;
andrewm@0 337 }
andrewm@0 338
andrewm@41 339 // Start/stop the TouchKeys data collection
andrewm@41 340 bool MainApplicationController::startTouchkeyDevice() {
andrewm@41 341 return touchkeyController_.startAutoGathering();
andrewm@41 342 }
andrewm@41 343
andrewm@41 344 void MainApplicationController::stopTouchkeyDevice() {
andrewm@41 345 touchkeyController_.stopAutoGathering();
andrewm@41 346 }
andrewm@41 347
andrewm@41 348 // Status queries on TouchKeys
andrewm@41 349 // Returns true if device has been opened
andrewm@41 350 bool MainApplicationController::touchkeyDeviceIsOpen() {
andrewm@41 351 return touchkeyController_.isOpen();
andrewm@41 352 }
andrewm@41 353
andrewm@11 354 // Return true if device is collecting data
andrewm@11 355 bool MainApplicationController::touchkeyDeviceIsRunning() {
andrewm@11 356 #ifdef TOUCHKEY_ENTROPY_GENERATOR_ENABLE
andrewm@11 357 if(entropyGeneratorSelected_)
andrewm@11 358 return touchkeyEntropyGenerator_.isRunning();
andrewm@11 359 else
andrewm@11 360 return touchkeyController_.isAutoGathering();
andrewm@11 361 #else
andrewm@11 362 return touchkeyController_.isAutoGathering();
andrewm@11 363 #endif
andrewm@11 364 }
andrewm@11 365
andrewm@41 366 // Returns true if an error has occurred
andrewm@41 367 bool MainApplicationController::touchkeyDeviceErrorOccurred() {
andrewm@41 368 return touchkeyErrorOccurred_;
andrewm@41 369 }
andrewm@41 370
andrewm@41 371 // Return the error message if one occurred
andrewm@41 372 std::string MainApplicationController::touchkeyDeviceErrorMessage() {
andrewm@41 373 return touchkeyErrorMessage_;
andrewm@41 374 }
andrewm@41 375
andrewm@41 376 // How many octaves on the current device
andrewm@41 377 int MainApplicationController::touchkeyDeviceNumberOfOctaves() {
andrewm@41 378 return touchkeyController_.numberOfOctaves();
andrewm@41 379 }
andrewm@41 380
andrewm@41 381 // Return the lowest MIDI note
andrewm@41 382 int MainApplicationController::touchkeyDeviceLowestMidiNote() {
andrewm@41 383 return touchkeyController_.lowestMidiNote();
andrewm@41 384 }
andrewm@41 385
andrewm@41 386 // Set the lowest MIDI note for the TouchKeys
andrewm@41 387 void MainApplicationController::touchkeyDeviceSetLowestMidiNote(int note) {
andrewm@41 388 keyboardDisplay_.clearAllTouches();
andrewm@41 389 touchkeyEmulator_.setLowestMidiNote(note);
andrewm@41 390 touchkeyController_.setLowestMidiNote(note);
andrewm@41 391
andrewm@41 392 applicationProperties_.getUserSettings()->setValue("TouchKeysLowestMIDINote", note);
andrewm@41 393 }
andrewm@41 394
andrewm@0 395 // Start an autodetection routine to match touch data to MIDI
andrewm@0 396 void MainApplicationController::touchkeyDeviceAutodetectLowestMidiNote() {
andrewm@0 397 if(touchkeyAutodetecting_)
andrewm@0 398 return;
andrewm@0 399
andrewm@0 400 touchkeyAutodetecting_ = true;
andrewm@0 401 addOscListener("/midi/noteon");
andrewm@0 402 }
andrewm@0 403
andrewm@0 404 // Abort an autodetection routine
andrewm@0 405 void MainApplicationController::touchkeyDeviceStopAutodetecting() {
andrewm@0 406 if(!touchkeyAutodetecting_)
andrewm@0 407 return;
andrewm@0 408
andrewm@0 409 removeOscListener("/midi/noteon");
andrewm@0 410 touchkeyAutodetecting_ = false;
andrewm@0 411 }
andrewm@0 412
andrewm@0 413 bool MainApplicationController::touchkeyDeviceIsAutodetecting() {
andrewm@0 414 return touchkeyAutodetecting_;
andrewm@0 415 }
andrewm@0 416
andrewm@0 417 // Start logging TouchKeys/MIDI data to a file. Filename is autogenerated
andrewm@0 418 // based on current time.
andrewm@0 419 void MainApplicationController::startLogging() {
andrewm@0 420 if(loggingActive_)
andrewm@0 421 stopLogging();
andrewm@0 422
andrewm@0 423 std::stringstream out;
andrewm@0 424 out << time(NULL);
andrewm@0 425 std::string fileId = out.str();
andrewm@0 426
andrewm@0 427
andrewm@0 428 string midiLogFileName = "midiLog_" + fileId + ".bin";
andrewm@0 429 string keyTouchLogFileName = "keyTouchLog_" + fileId + ".bin";
andrewm@0 430 string analogLogFileName = "keyAngleLog_" + fileId + ".bin";
andrewm@0 431
andrewm@0 432 // Create log files with these names
andrewm@0 433 midiInputController_.createLogFile(midiLogFileName, loggingDirectory_);
andrewm@0 434 touchkeyController_.createLogFiles(keyTouchLogFileName, analogLogFileName, loggingDirectory_);
andrewm@0 435
andrewm@0 436 // Enable logging from each controller
andrewm@0 437 midiInputController_.startLogging();
andrewm@0 438 touchkeyController_.startLogging();
andrewm@0 439
andrewm@0 440 loggingActive_ = true;
andrewm@0 441 }
andrewm@0 442
andrewm@0 443 // Stop a currently running log.
andrewm@0 444 void MainApplicationController::stopLogging() {
andrewm@0 445 if(!loggingActive_)
andrewm@0 446 return;
andrewm@0 447
andrewm@0 448 // stop logging data
andrewm@0 449 midiInputController_.stopLogging();
andrewm@0 450 touchkeyController_.stopLogging();
andrewm@0 451
andrewm@0 452 // close the log files
andrewm@0 453 midiInputController_.closeLogFile();
andrewm@0 454 touchkeyController_.closeLogFile();
andrewm@0 455
andrewm@0 456 loggingActive_ = false;
andrewm@0 457 }
andrewm@0 458
andrewm@0 459 void MainApplicationController::setLoggingDirectory(const char *directory) {
andrewm@0 460 loggingDirectory_ = directory;
andrewm@0 461 }
andrewm@0 462
andrewm@53 463 void MainApplicationController::playLogWithDialog() {
andrewm@53 464 if(isPlayingLog_)
andrewm@53 465 return;
andrewm@53 466
andrewm@53 467 FileChooser tkChooser ("Select TouchKeys log...",
andrewm@53 468 File::nonexistent, // File::getSpecialLocation (File::userHomeDirectory),
andrewm@53 469 "*.bin");
andrewm@53 470 if(tkChooser.browseForFileToOpen()) {
andrewm@53 471 FileChooser midiChooser ("Select MIDI log...",
andrewm@53 472 File::nonexistent, // File::getSpecialLocation (File::userHomeDirectory),
andrewm@53 473 "*.bin");
andrewm@53 474 if(midiChooser.browseForFileToOpen()) {
andrewm@53 475 logPlayback_ = new LogPlayback(keyboardController_, midiInputController_);
andrewm@53 476 if(logPlayback_ == 0)
andrewm@53 477 return;
andrewm@53 478
andrewm@53 479 if(logPlayback_->openLogFiles(tkChooser.getResult().getFullPathName().toRawUTF8(), midiChooser.getResult().getFullPathName().toRawUTF8())) {
andrewm@53 480 logPlayback_->startPlayback();
andrewm@53 481 isPlayingLog_ = true;
andrewm@53 482 #ifndef TOUCHKEYS_NO_GUI
andrewm@53 483 // Always show 88 keys for log playback since we won't know which keys were actually recorded
andrewm@53 484 keyboardDisplay_.setKeyboardRange(21, 108);
andrewm@53 485 if(keyboardDisplayWindow_ != 0) {
andrewm@53 486 keyboardDisplayWindow_->getConstrainer()->setFixedAspectRatio(keyboardDisplay_.keyboardAspectRatio());
andrewm@53 487
a@54 488 juce::Rectangle<int> bounds = keyboardDisplayWindow_->getBounds();
andrewm@53 489 if(bounds.getY() < 44)
andrewm@53 490 bounds.setY(44);
andrewm@53 491 keyboardDisplayWindow_->setBoundsConstrained(bounds);
andrewm@53 492 }
andrewm@53 493 showKeyboardDisplayWindow();
andrewm@53 494 #endif
andrewm@53 495 }
andrewm@53 496 }
andrewm@53 497 }
andrewm@53 498 }
andrewm@53 499
andrewm@53 500 void MainApplicationController::stopPlayingLog() {
andrewm@53 501 if(!isPlayingLog_)
andrewm@53 502 return;
andrewm@53 503
andrewm@53 504 if(logPlayback_ != 0) {
andrewm@53 505 logPlayback_->stopPlayback();
andrewm@53 506 logPlayback_->closeLogFiles();
andrewm@53 507 delete logPlayback_;
andrewm@53 508 logPlayback_ = 0;
andrewm@53 509 }
andrewm@53 510
andrewm@53 511 #ifndef TOUCHKEYS_NO_GUI
andrewm@53 512 keyboardDisplay_.clearAllTouches();
andrewm@53 513 #endif
andrewm@53 514 midiInputController_.allNotesOff();
andrewm@53 515 isPlayingLog_ = false;
andrewm@53 516 }
andrewm@53 517
andrewm@0 518 // Add a new MIDI keyboard segment. This method also handles numbering of the segments
andrewm@0 519 MidiKeyboardSegment* MainApplicationController::midiSegmentAdd() {
andrewm@0 520 // For now, the segment counter increments with each new segment. Eventually, we could
andrewm@0 521 // consider renumbering every time a segment is removed so that we always have an index
andrewm@0 522 // 0-N which corresponds to the indexes within MidiInputController (and also the layout
andrewm@0 523 // of the tabs).
andrewm@41 524 MidiKeyboardSegment *newSegment = midiInputController_.addSegment(segmentCounter_, 12, 127);
andrewm@0 525
andrewm@0 526 // Set up defaults
andrewm@0 527 newSegment->setModePassThrough();
andrewm@0 528 newSegment->setPolyphony(8);
andrewm@0 529 newSegment->setVoiceStealingEnabled(false);
andrewm@0 530 newSegment->enableAllChannels();
andrewm@0 531 newSegment->setOutputTransposition(0);
andrewm@5 532 newSegment->setUsesKeyboardPitchWheel(true);
andrewm@0 533
andrewm@41 534 // Enable the MIDI output for this segment if it exists in the preferences
andrewm@41 535 loadMIDIOutputFromApplicationPreferences(segmentCounter_);
andrewm@41 536
andrewm@0 537 // Enable standalone mode on the new segment if generally enabled
andrewm@0 538 if(touchkeyStandaloneModeEnabled_)
andrewm@0 539 newSegment->enableTouchkeyStandaloneMode();
andrewm@0 540
andrewm@41 541 segmentCounter_++;
andrewm@41 542
andrewm@0 543 return newSegment;
andrewm@0 544 }
andrewm@0 545
andrewm@13 546 // Remove a MIDI keyboard segment.
andrewm@13 547 void MainApplicationController::midiSegmentRemove(MidiKeyboardSegment *segment) {
andrewm@13 548 if(segment == 0)
andrewm@13 549 return;
andrewm@13 550 // Check if this segment uses a virtual output port. Right now, we have a unique
andrewm@13 551 // output per segment. If it does, then disable the virtual output port.
andrewm@13 552 int identifier = segment->outputPort();
andrewm@13 553 if(midiOutputController_.enabledPort(identifier) == MidiOutputController::kMidiVirtualOutputPortNumber)
andrewm@13 554 midiOutputController_.disablePort(identifier);
andrewm@13 555 midiInputController_.removeSegment(segment);
andrewm@13 556 }
andrewm@13 557
andrewm@41 558 // Enable one MIDI input port either as primary or auxiliary
andrewm@41 559 void MainApplicationController::enableMIDIInputPort(int portNumber, bool isPrimary) {
andrewm@41 560 midiInputController_.enablePort(portNumber, isPrimary);
andrewm@41 561 if(isPrimary)
andrewm@41 562 applicationProperties_.getUserSettings()->setValue("MIDIInputPrimary",
andrewm@41 563 midiInputController_.deviceName(portNumber));
andrewm@41 564 else
andrewm@41 565 applicationProperties_.getUserSettings()->setValue("MIDIInputAuxiliary",
andrewm@41 566 midiInputController_.deviceName(portNumber));
andrewm@41 567 }
andrewm@41 568
andrewm@41 569 // Enable all available MIDI input ports, with one in particular selected as primary
andrewm@41 570 void MainApplicationController::enableAllMIDIInputPorts(int primaryPortNumber) {
andrewm@41 571 midiInputController_.enableAllPorts(primaryPortNumber);
andrewm@41 572 applicationProperties_.getUserSettings()->setValue("MIDIInputPrimary",
andrewm@41 573 midiInputController_.deviceName(primaryPortNumber));
andrewm@41 574 applicationProperties_.getUserSettings()->setValue("MIDIInputAuxiliary", "__all__");
andrewm@41 575 }
andrewm@41 576
andrewm@41 577 // Disable a particular MIDI input port number
andrewm@41 578 // For now, the preferences for auxiliary ports don't update; could add a complete list of enabled aux ports
andrewm@41 579 void MainApplicationController::disableMIDIInputPort(int portNumber) {
andrewm@41 580 if(portNumber == selectedMIDIPrimaryInputPort())
andrewm@41 581 applicationProperties_.getUserSettings()->setValue("MIDIInputPrimary", "");
andrewm@41 582 midiInputController_.disablePort(portNumber);
andrewm@41 583 }
andrewm@41 584
andrewm@41 585 // Disable the current primary MIDI input port
andrewm@41 586 void MainApplicationController::disablePrimaryMIDIInputPort() {
andrewm@41 587 applicationProperties_.getUserSettings()->setValue("MIDIInputPrimary", "");
andrewm@41 588 midiInputController_.disablePrimaryPort();
andrewm@41 589 }
andrewm@41 590
andrewm@41 591 // Disable either all MIDI input ports or all auxiliary inputs
andrewm@41 592 void MainApplicationController::disableAllMIDIInputPorts(bool auxiliaryOnly) {
andrewm@41 593 applicationProperties_.getUserSettings()->setValue("MIDIInputAuxiliary", "");
andrewm@41 594 if(!auxiliaryOnly)
andrewm@41 595 applicationProperties_.getUserSettings()->setValue("MIDIInputPrimary", "");
andrewm@41 596 midiInputController_.disableAllPorts(auxiliaryOnly);
andrewm@41 597 }
andrewm@41 598
andrewm@41 599 // Enable a particular MIDI output port, associating it with a segment
andrewm@41 600 void MainApplicationController::enableMIDIOutputPort(int identifier, int deviceNumber) {
andrewm@41 601 midiOutputController_.enablePort(identifier, deviceNumber);
andrewm@41 602
andrewm@41 603 String zoneName = "MIDIOutputZone";
andrewm@41 604 zoneName += identifier;
andrewm@41 605 applicationProperties_.getUserSettings()->setValue(zoneName, midiOutputController_.deviceName(deviceNumber));
andrewm@41 606 }
andrewm@41 607
andrewm@41 608 #ifndef JUCE_WINDOWS
andrewm@41 609 // Create a virtual (inter-application) MIDI output port
andrewm@41 610 void MainApplicationController::enableMIDIOutputVirtualPort(int identifier, const char *name) {
andrewm@41 611 midiOutputController_.enableVirtualPort(identifier, name);
andrewm@41 612
andrewm@41 613 String zoneName = "MIDIOutputZone";
andrewm@41 614 zoneName += identifier;
andrewm@41 615 String zoneValue = "__virtual__";
andrewm@41 616 zoneValue += String(name);
andrewm@41 617 applicationProperties_.getUserSettings()->setValue(zoneName, zoneValue);
andrewm@41 618 }
andrewm@41 619 #endif
andrewm@41 620
andrewm@41 621 // Disable a particular MIDI output port
andrewm@41 622 void MainApplicationController::disableMIDIOutputPort(int identifier) {
andrewm@41 623 String zoneName = "MIDIOutputZone";
andrewm@41 624 zoneName += identifier;
andrewm@41 625 applicationProperties_.getUserSettings()->setValue(zoneName, "");
andrewm@41 626
andrewm@41 627 midiOutputController_.disablePort(identifier);
andrewm@41 628 }
andrewm@41 629
andrewm@41 630 // Disable all MIDI output ports
andrewm@41 631 void MainApplicationController::disableAllMIDIOutputPorts() {
andrewm@41 632 std::vector<std::pair<int, int> > enabledPorts = midiOutputController_.enabledPorts();
andrewm@41 633 for(int i = 0; i < enabledPorts.size(); i++) {
andrewm@41 634 // For each active zone, set output port to disabled in preferences
andrewm@41 635 String zoneName = "MIDIOutputZone";
andrewm@41 636 zoneName += enabledPorts[i].first;
andrewm@41 637 applicationProperties_.getUserSettings()->setValue(zoneName, "");
andrewm@41 638 }
andrewm@41 639
andrewm@41 640 midiOutputController_.disableAllPorts();
andrewm@41 641 }
andrewm@13 642
andrewm@0 643 // Enable TouchKeys standalone mode
andrewm@0 644 void MainApplicationController::midiTouchkeysStandaloneModeEnable() {
andrewm@0 645 touchkeyStandaloneModeEnabled_ = true;
andrewm@0 646 // Go through all segments and enable standalone mode
andrewm@0 647 for(int i = 0; i < midiInputController_.numSegments(); i++) {
andrewm@0 648 midiInputController_.segment(i)->enableTouchkeyStandaloneMode();
andrewm@0 649 }
andrewm@41 650
andrewm@41 651 applicationProperties_.getUserSettings()->setValue("MIDIInputPrimary", "__standalone__");
andrewm@0 652 }
andrewm@0 653
andrewm@0 654 void MainApplicationController::midiTouchkeysStandaloneModeDisable() {
andrewm@0 655 touchkeyStandaloneModeEnabled_ = false;
andrewm@0 656 // Go through all segments and disable standalone mode
andrewm@0 657 for(int i = 0; i < midiInputController_.numSegments(); i++) {
andrewm@28 658 midiInputController_.segment(i)->disableTouchkeyStandaloneMode();
andrewm@0 659 }
andrewm@41 660
andrewm@41 661 if(applicationProperties_.getUserSettings()->getValue("MIDIInputPrimary") == "__standalone__")
andrewm@41 662 applicationProperties_.getUserSettings()->setValue("MIDIInputPrimary", "");
andrewm@41 663 }
andrewm@41 664
andrewm@41 665 // *** OSC device methods ***
andrewm@41 666
andrewm@41 667 // Return whether OSC transmission is enabled
andrewm@41 668 bool MainApplicationController::oscTransmitEnabled() {
andrewm@41 669 return oscTransmitter_.enabled();
andrewm@41 670 }
andrewm@41 671
andrewm@41 672 // Set whether OSC transmission is enabled
andrewm@41 673 void MainApplicationController::oscTransmitSetEnabled(bool enable) {
andrewm@41 674 oscTransmitter_.setEnabled(enable);
andrewm@41 675 applicationProperties_.getUserSettings()->setValue("OSCTransmitEnabled", enable);
andrewm@41 676 }
andrewm@41 677
andrewm@41 678 // Return whether raw frame transmission is enabled
andrewm@41 679 bool MainApplicationController::oscTransmitRawDataEnabled() {
andrewm@41 680 return touchkeyController_.transmitRawDataEnabled();
andrewm@41 681 }
andrewm@41 682
andrewm@41 683 // Set whether raw frame transmission is enabled
andrewm@41 684 void MainApplicationController::oscTransmitSetRawDataEnabled(bool enable) {
andrewm@41 685 touchkeyController_.setTransmitRawData(enable);
andrewm@41 686 applicationProperties_.getUserSettings()->setValue("OSCTransmitRawDataEnabled", enable);
andrewm@41 687 }
andrewm@41 688
andrewm@41 689 // Return the addresses to which OSC messages are sent
andrewm@41 690 std::vector<lo_address> MainApplicationController::oscTransmitAddresses() {
andrewm@41 691 return oscTransmitter_.addresses();
andrewm@41 692 }
andrewm@41 693
andrewm@41 694 // Add a new address for sending OSC messages to
andrewm@41 695 int MainApplicationController::oscTransmitAddAddress(const char * host, const char * port, int proto) {
andrewm@41 696 int indexOfNewAddress = oscTransmitter_.addAddress(host, port, proto);
andrewm@41 697
andrewm@41 698 if(indexOfNewAddress >= 0) {
andrewm@41 699 // Successfully added; update preferences
andrewm@41 700 String keyName = "OSCTransmitHost";
andrewm@41 701 keyName += indexOfNewAddress;
andrewm@41 702 applicationProperties_.getUserSettings()->setValue(keyName, String(host));
andrewm@41 703
andrewm@41 704 keyName = "OSCTransmitPort";
andrewm@41 705 keyName += indexOfNewAddress;
andrewm@41 706 applicationProperties_.getUserSettings()->setValue(keyName, String(port));
andrewm@41 707
andrewm@41 708 keyName = "OSCTransmitProtocol";
andrewm@41 709 keyName += indexOfNewAddress;
andrewm@41 710 applicationProperties_.getUserSettings()->setValue(keyName, proto);
andrewm@41 711 }
andrewm@41 712
andrewm@41 713 return indexOfNewAddress;
andrewm@41 714 }
andrewm@41 715
andrewm@41 716 // Remove a particular OSC address from the send list
andrewm@41 717 void MainApplicationController::oscTransmitRemoveAddress(int index) {
andrewm@41 718 oscTransmitter_.removeAddress(index);
andrewm@41 719
andrewm@41 720 // Remove this destination from the preferences, if it exists
andrewm@41 721 String keyName = "OSCTransmitHost";
andrewm@41 722 keyName += index;
andrewm@41 723
andrewm@41 724 if(applicationProperties_.getUserSettings()->containsKey(keyName)) {
andrewm@41 725 applicationProperties_.getUserSettings()->setValue(keyName, "");
andrewm@41 726
andrewm@41 727 keyName = "OSCTransmitPort";
andrewm@41 728 keyName += index;
andrewm@41 729 applicationProperties_.getUserSettings()->setValue(keyName, "");
andrewm@41 730
andrewm@41 731 keyName = "OSCTransmitProtocol";
andrewm@41 732 keyName += index;
andrewm@41 733 applicationProperties_.getUserSettings()->setValue(keyName, (int)0);
andrewm@41 734 }
andrewm@41 735 }
andrewm@41 736
andrewm@41 737 // Remove all OSC addresses from the send list
andrewm@41 738 void MainApplicationController::oscTransmitClearAddresses() {
andrewm@41 739 oscTransmitter_.clearAddresses();
andrewm@41 740
andrewm@41 741 for(int index = 0; index < 16; index++) {
andrewm@41 742 // Go through and clear preferences for recent OSC hosts;
andrewm@41 743 // 16 hosts is a sanity check
andrewm@41 744
andrewm@41 745 String keyName = "OSCTransmitHost";
andrewm@41 746 keyName += index;
andrewm@41 747
andrewm@41 748 if(applicationProperties_.getUserSettings()->containsKey(keyName)) {
andrewm@41 749 applicationProperties_.getUserSettings()->setValue(keyName, "");
andrewm@41 750
andrewm@41 751 keyName = "OSCTransmitPort";
andrewm@41 752 keyName += index;
andrewm@41 753 applicationProperties_.getUserSettings()->setValue(keyName, "");
andrewm@41 754
andrewm@41 755 keyName = "OSCTransmitProtocol";
andrewm@41 756 keyName += index;
andrewm@41 757 applicationProperties_.getUserSettings()->setValue(keyName, (int)0);
andrewm@41 758 }
andrewm@41 759 }
andrewm@41 760
andrewm@41 761 }
andrewm@41 762
andrewm@41 763 // OSC Input (receiver) methods
andrewm@41 764 // Enable or disable on the OSC receive, and report is status
andrewm@41 765 bool MainApplicationController::oscReceiveEnabled() {
andrewm@41 766 return oscReceiveEnabled_;
andrewm@41 767 }
andrewm@41 768
andrewm@41 769 // Enable method returns true on success (false only if it was
andrewm@41 770 // unable to set the port)
andrewm@41 771 bool MainApplicationController::oscReceiveSetEnabled(bool enable) {
andrewm@41 772 applicationProperties_.getUserSettings()->setValue("OSCReceiveEnabled", enable);
andrewm@41 773
andrewm@41 774 if(enable && !oscReceiveEnabled_) {
andrewm@41 775 oscReceiveEnabled_ = true;
andrewm@41 776 return oscReceiver_.setPort(oscReceivePort_);
andrewm@41 777 }
andrewm@41 778 else if(!enable && oscReceiveEnabled_) {
andrewm@41 779 oscReceiveEnabled_ = false;
andrewm@41 780 return oscReceiver_.setPort(0);
andrewm@41 781 }
andrewm@41 782 return true;
andrewm@41 783 }
andrewm@41 784
andrewm@41 785 // Whether the OSC server is running (false means couldn't open port)
andrewm@41 786 bool MainApplicationController::oscReceiveRunning() {
andrewm@41 787 return oscReceiver_.running();
andrewm@41 788 }
andrewm@41 789
andrewm@41 790 // Get the current OSC receive port
andrewm@41 791 int MainApplicationController::oscReceivePort() {
andrewm@41 792 return oscReceivePort_;
andrewm@41 793 }
andrewm@41 794
andrewm@41 795 // Set the current OSC receive port (returns true on success)
andrewm@41 796 bool MainApplicationController::oscReceiveSetPort(int port) {
andrewm@41 797 applicationProperties_.getUserSettings()->setValue("OSCReceivePort", port);
andrewm@41 798
andrewm@41 799 oscReceivePort_ = port;
andrewm@41 800 return oscReceiver_.setPort(port);
andrewm@0 801 }
andrewm@0 802
andrewm@0 803 // OSC handler method
andrewm@0 804 bool MainApplicationController::oscHandlerMethod(const char *path, const char *types, int numValues, lo_arg **values, void *data) {
andrewm@49 805 std::cout << path << endl;
andrewm@49 806
andrewm@49 807 if(!strcmp(path, "/midi/noteon")) {
andrewm@49 808 if(touchkeyAutodetecting_ && numValues > 0) {
andrewm@49 809 // std::cout << "/midi/noteon\n";
andrewm@49 810 // Found a MIDI note. Look for a unique touch on this pitch class to
andrewm@49 811 // determine which octave the keyboard is set to
andrewm@49 812 if(types[0] != 'i')
andrewm@49 813 return false; // Ill-formed message
andrewm@49 814 int midiNote = values[0]->i;
andrewm@49 815 if(midiNote < 0 || midiNote > 127)
andrewm@49 816 return false;
andrewm@49 817
andrewm@49 818 // Go through each octave and see if a touch is present
andrewm@49 819 int midiTestNote = midiNote % 12;
andrewm@49 820 int count = 0;
andrewm@49 821 int lastFoundTouchNote = 0;
andrewm@49 822 while(midiTestNote <= 127) {
andrewm@49 823 if(keyboardController_.key(midiTestNote) != 0) {
andrewm@49 824 if(keyboardController_.key(midiTestNote)->touchIsActive()) {
andrewm@49 825 count++;
andrewm@49 826 lastFoundTouchNote = midiTestNote;
andrewm@49 827 }
andrewm@0 828 }
andrewm@49 829 midiTestNote += 12;
andrewm@41 830 }
andrewm@0 831
andrewm@49 832 // We return success if exactly one note had a touch on this pitch class
andrewm@49 833 if(count == 1) {
andrewm@49 834 int noteDifference = lastFoundTouchNote - midiNote;
andrewm@49 835 int currentMinNote = touchkeyController_.lowestMidiNote();
andrewm@49 836
andrewm@49 837 // std::cout << "Found difference of " << noteDifference << std::endl;
andrewm@49 838
andrewm@49 839 currentMinNote -= noteDifference;
andrewm@49 840 if(currentMinNote >= 0 && currentMinNote <= 127) {
andrewm@49 841 touchkeyController_.setLowestMidiNote(currentMinNote);
andrewm@49 842 applicationProperties_.getUserSettings()->setValue("TouchKeysLowestMIDINote", currentMinNote);
andrewm@49 843 }
andrewm@49 844
andrewm@49 845 touchkeyDeviceStopAutodetecting();
andrewm@49 846 }
andrewm@49 847 return false; // Others may still want to handle this message
andrewm@0 848 }
andrewm@0 849 }
andrewm@0 850
andrewm@0 851 return false;
andrewm@0 852 }
andrewm@0 853
andrewm@33 854 // Save the current settings to an XML file
andrewm@33 855 // Returns true on success
andrewm@33 856 bool MainApplicationController::savePresetToFile(const char *filename) {
andrewm@33 857 File outputFile(filename);
andrewm@33 858
andrewm@33 859 return savePresetHelper(outputFile);
andrewm@33 860 }
andrewm@33 861
andrewm@33 862 // Load settings from a saved XML file
andrewm@33 863 // Returns true on success
andrewm@33 864 bool MainApplicationController::loadPresetFromFile(const char *filename) {
andrewm@33 865 File inputFile(filename);
andrewm@33 866
andrewm@33 867 return loadPresetHelper(inputFile);
andrewm@33 868 }
andrewm@33 869
andrewm@33 870 #ifndef TOUCHKEYS_NO_GUI
andrewm@33 871 // Present the user with a Save dialog and then save the preset
andrewm@33 872 bool MainApplicationController::savePresetWithDialog() {
andrewm@33 873 FileChooser myChooser ("Save preset...",
andrewm@33 874 File::nonexistent, // File::getSpecialLocation (File::userHomeDirectory),
andrewm@33 875 "*.tkpreset");
andrewm@33 876 if(myChooser.browseForFileToSave(true)) {
andrewm@33 877 File outputFile(myChooser.getResult());
andrewm@33 878 return savePresetHelper(outputFile);
andrewm@33 879 }
andrewm@33 880 // User clicked cancel...
andrewm@33 881 return true;
andrewm@33 882 }
andrewm@33 883
andrewm@33 884
andrewm@33 885 // Present the user with a Load dialog and then save the preset
andrewm@33 886 bool MainApplicationController::loadPresetWithDialog() {
andrewm@33 887 FileChooser myChooser ("Select a preset...",
andrewm@33 888 File::nonexistent, // File::getSpecialLocation (File::userHomeDirectory),
andrewm@33 889 "*.tkpreset");
andrewm@33 890 if(myChooser.browseForFileToOpen()) {
andrewm@33 891 return loadPresetHelper(myChooser.getResult());
andrewm@33 892 }
andrewm@33 893 // User clicked cancel...
andrewm@33 894 return true;
andrewm@33 895 }
andrewm@33 896 #endif
andrewm@33 897
andrewm@33 898 bool MainApplicationController::loadPresetHelper(File const& inputFile) {
andrewm@33 899 if(!inputFile.existsAsFile())
andrewm@33 900 return false;
andrewm@33 901
andrewm@33 902 // Load the XML element from the file and check that it is valid
andrewm@33 903 XmlDocument document(inputFile);
andrewm@40 904 ScopedPointer<XmlElement> mainElement(document.getDocumentElement());
andrewm@33 905
andrewm@33 906 if(mainElement == 0)
andrewm@33 907 return false;
andrewm@33 908 if(mainElement->getTagName() != "TouchKeysPreset")
andrewm@33 909 return false;
andrewm@33 910 XmlElement *segmentsElement = mainElement->getChildByName("KeyboardSegments");
andrewm@33 911 if(segmentsElement == 0)
andrewm@33 912 return false;
andrewm@33 913
andrewm@33 914 // Load the preset from this element
andrewm@39 915 bool result = midiInputController_.loadSegmentPreset(segmentsElement);
andrewm@39 916
andrewm@55 917 // Enable any necessary MIDI outputs
andrewm@55 918 for(int i = 0; i < midiInputController_.numSegments(); i++)
andrewm@55 919 loadMIDIOutputFromApplicationPreferences(i);
andrewm@55 920
andrewm@39 921 // Loading a preset won't set standalone mode; so re-enable it when finished
andrewm@39 922 // if needed
andrewm@39 923 if(touchkeyStandaloneModeEnabled_) {
andrewm@39 924 midiTouchkeysStandaloneModeEnable();
andrewm@39 925 }
andrewm@39 926
andrewm@39 927 return result;
andrewm@33 928 }
andrewm@33 929
andrewm@33 930 bool MainApplicationController::savePresetHelper(File& outputFile) {
andrewm@33 931 XmlElement mainElement("TouchKeysPreset");
andrewm@33 932 mainElement.setAttribute("format", "0.1");
andrewm@33 933
andrewm@33 934 XmlElement* segmentsElement = midiInputController_.getSegmentPreset();
andrewm@33 935 mainElement.addChildElement(segmentsElement);
andrewm@33 936
andrewm@41 937 bool result = mainElement.writeToFile(outputFile, "");
andrewm@41 938
andrewm@41 939 if(result) {
andrewm@41 940 applicationProperties_.getUserSettings()->setValue("LastSavedPreset", outputFile.getFullPathName());
andrewm@41 941 }
andrewm@41 942
andrewm@41 943 return result;
andrewm@33 944 }
andrewm@33 945
andrewm@37 946 // Clear the current preset and restore default settings
andrewm@37 947 void MainApplicationController::clearPreset() {
andrewm@37 948 midiInputController_.removeAllSegments();
andrewm@39 949 //midiOutputController_.disableAllPorts();
andrewm@37 950 segmentCounter_ = 0;
andrewm@37 951
andrewm@37 952 // Re-add a new segment, starting at 0
andrewm@37 953 midiSegmentAdd();
andrewm@37 954 }
andrewm@37 955
andrewm@41 956 // Whether to automatically start the TouchKeys on startup
andrewm@41 957 bool MainApplicationController::getPrefsAutoStartTouchKeys() {
andrewm@41 958 if(!applicationProperties_.getUserSettings()->containsKey("StartupStartTouchKeys"))
andrewm@41 959 return false;
andrewm@41 960 return applicationProperties_.getUserSettings()->getBoolValue("StartupStartTouchKeys");
andrewm@41 961 }
andrewm@41 962
andrewm@41 963 void MainApplicationController::setPrefsAutoStartTouchKeys(bool autoStart) {
andrewm@41 964 applicationProperties_.getUserSettings()->setValue("StartupStartTouchKeys", autoStart);
andrewm@41 965 }
andrewm@41 966
andrewm@41 967 // Whether to automatically detect the TouchKeys octave when they start
andrewm@41 968 bool MainApplicationController::getPrefsAutodetectOctave() {
andrewm@41 969 if(!applicationProperties_.getUserSettings()->containsKey("StartupAutodetectTouchKeysOctave"))
andrewm@41 970 return false;
andrewm@41 971 return applicationProperties_.getUserSettings()->getBoolValue("StartupAutodetectTouchKeysOctave");
andrewm@41 972 }
andrewm@41 973
andrewm@41 974 void MainApplicationController::setPrefsAutodetectOctave(bool autoDetect) {
andrewm@41 975 applicationProperties_.getUserSettings()->setValue("StartupAutodetectTouchKeysOctave", autoDetect);
andrewm@41 976 }
andrewm@41 977
andrewm@41 978 // Which preset (if any) to load at startup
andrewm@41 979 void MainApplicationController::setPrefsStartupPresetNone() {
andrewm@41 980 applicationProperties_.getUserSettings()->setValue("StartupPreset", "__none__");
andrewm@41 981 }
andrewm@41 982 bool MainApplicationController::getPrefsStartupPresetNone() {
andrewm@41 983 // By default, no prefs means no preset
andrewm@41 984 if(!applicationProperties_.getUserSettings()->containsKey("StartupPreset"))
andrewm@41 985 return true;
andrewm@41 986 if(applicationProperties_.getUserSettings()->getValue("StartupPreset") == "__none__")
andrewm@41 987 return true;
andrewm@41 988 return false;
andrewm@41 989 }
andrewm@41 990
andrewm@41 991 void MainApplicationController::setPrefsStartupPresetVibratoPitchBend() {
andrewm@41 992 applicationProperties_.getUserSettings()->setValue("StartupPreset", "__vib_pb__");
andrewm@41 993 }
andrewm@41 994 bool MainApplicationController::getPrefsStartupPresetVibratoPitchBend() {
andrewm@41 995 if(!applicationProperties_.getUserSettings()->containsKey("StartupPreset"))
andrewm@41 996 return false;
andrewm@41 997 if(applicationProperties_.getUserSettings()->getValue("StartupPreset") == "__vib_pb__")
andrewm@41 998 return true;
andrewm@41 999 return false;
andrewm@41 1000 }
andrewm@41 1001
andrewm@41 1002 void MainApplicationController::setPrefsStartupPresetLastSaved() {
andrewm@41 1003 applicationProperties_.getUserSettings()->setValue("StartupPreset", "__last__");
andrewm@41 1004 }
andrewm@41 1005 bool MainApplicationController::getPrefsStartupPresetLastSaved() {
andrewm@41 1006 if(!applicationProperties_.getUserSettings()->containsKey("StartupPreset"))
andrewm@41 1007 return false;
andrewm@41 1008 if(applicationProperties_.getUserSettings()->getValue("StartupPreset") == "__last__")
andrewm@41 1009 return true;
andrewm@41 1010 return false;
andrewm@41 1011 }
andrewm@41 1012
andrewm@41 1013 void MainApplicationController::setPrefsStartupPreset(String const& path) {
andrewm@41 1014 applicationProperties_.getUserSettings()->setValue("StartupPreset", path);
andrewm@41 1015 }
andrewm@41 1016 String MainApplicationController::getPrefsStartupPreset() {
andrewm@41 1017 if(!applicationProperties_.getUserSettings()->containsKey("StartupPreset"))
andrewm@41 1018 return "";
andrewm@41 1019 return applicationProperties_.getUserSettings()->getValue("StartupPreset");
andrewm@41 1020 }
andrewm@41 1021
andrewm@41 1022 // Reset application preferences to defaults
andrewm@41 1023 void MainApplicationController::resetPreferences() {
andrewm@41 1024 // TODO: reset settings now, not after restart
andrewm@41 1025 applicationProperties_.getUserSettings()->clear();
andrewm@41 1026
andrewm@41 1027 setPrefsStartupPresetVibratoPitchBend();
andrewm@41 1028 setPrefsAutodetectOctave(true);
andrewm@41 1029 }
andrewm@41 1030
andrewm@41 1031 // Load the current devices from a global preferences file
andrewm@41 1032 void MainApplicationController::loadApplicationPreferences(){
andrewm@41 1033 PropertiesFile *props = applicationProperties_.getUserSettings();
andrewm@41 1034
andrewm@41 1035 if(props == 0)
andrewm@41 1036 return;
andrewm@41 1037
andrewm@41 1038 // A few first-time defaults if the properties file is missing
andrewm@41 1039 if(props->getAllProperties().size() == 0) {
andrewm@41 1040 resetPreferences();
andrewm@41 1041 }
andrewm@41 1042
andrewm@41 1043 // Load TouchKeys settings
andrewm@41 1044 if(props->containsKey("TouchKeysDevice")) {
andrewm@41 1045 // TODO
andrewm@41 1046 }
andrewm@41 1047 if(props->containsKey("TouchKeysLowestMIDINote")) {
andrewm@41 1048 int note = props->getIntValue("TouchKeysLowestMIDINote");
andrewm@41 1049 if(note >= 0 && note <= 127)
andrewm@41 1050 touchkeyDeviceSetLowestMidiNote(note);
andrewm@41 1051 }
andrewm@41 1052
andrewm@41 1053 // Load MIDI input settings
andrewm@41 1054 if(props->containsKey("MIDIInputPrimary")) {
andrewm@41 1055 String deviceName = props->getValue("MIDIInputPrimary");
andrewm@41 1056 if(deviceName == "__standalone__") {
andrewm@41 1057 midiTouchkeysStandaloneModeEnable();
andrewm@41 1058 }
andrewm@41 1059 else {
andrewm@41 1060 int index = midiInputController_.indexOfDeviceNamed(deviceName);
andrewm@41 1061 // cout << "primary input id " << index << " name " << deviceName << endl;
andrewm@41 1062 if(index >= 0)
andrewm@41 1063 enableMIDIInputPort(index, true);
andrewm@41 1064 }
andrewm@41 1065 }
andrewm@41 1066 if(props->containsKey("MIDIInputAuxiliary")) {
andrewm@41 1067 String deviceName = props->getValue("MIDIInputAuxiliary");
andrewm@41 1068 int index = midiInputController_.indexOfDeviceNamed(deviceName);
andrewm@41 1069 // cout << "aux input id " << index << " name " << deviceName << endl;
andrewm@41 1070 if(index >= 0)
andrewm@41 1071 enableMIDIInputPort(index, false);
andrewm@41 1072 }
andrewm@41 1073
andrewm@41 1074 // MIDI output settings are loaded when segments are created
andrewm@41 1075
andrewm@41 1076 // OSC settings
andrewm@41 1077 if(props->containsKey("OSCTransmitEnabled")) {
andrewm@41 1078 bool enable = props->getBoolValue("OSCTransmitEnabled");
andrewm@41 1079 oscTransmitSetEnabled(enable);
andrewm@41 1080 }
andrewm@41 1081 if(props->containsKey("OSCTransmitRawDataEnabled")) {
andrewm@41 1082 bool enable = props->getBoolValue("OSCTransmitRawDataEnabled");
andrewm@41 1083 oscTransmitSetRawDataEnabled(enable);
andrewm@41 1084 }
andrewm@41 1085
andrewm@41 1086 for(int i = 0; i < 16; i++) {
andrewm@41 1087 String keyName = "OSCTransmitHost";
andrewm@41 1088 String host, port;
andrewm@41 1089 int protocol = LO_UDP;
andrewm@41 1090
andrewm@41 1091 keyName += i;
andrewm@41 1092 if(props->containsKey(keyName)) {
andrewm@41 1093 host = props->getValue(keyName);
andrewm@41 1094 }
andrewm@41 1095 else
andrewm@41 1096 continue;
andrewm@41 1097
andrewm@41 1098 keyName = "OSCTransmitPort";
andrewm@41 1099 keyName += i;
andrewm@41 1100 if(props->containsKey(keyName)) {
andrewm@41 1101 port = props->getValue(keyName);
andrewm@41 1102 }
andrewm@41 1103 else
andrewm@41 1104 continue;
andrewm@41 1105
andrewm@41 1106 keyName = "OSCTransmitProtocol";
andrewm@41 1107 keyName += i;
andrewm@41 1108 if(props->containsKey(keyName)) {
andrewm@41 1109 protocol = props->getIntValue(keyName);
andrewm@41 1110 }
andrewm@41 1111 // okay to go ahead without protocol; use default
andrewm@41 1112
andrewm@41 1113 // Check for validity
andrewm@41 1114 if(host != "" && port != "" && (protocol == LO_UDP || protocol == LO_TCP)) {
andrewm@41 1115 oscTransmitter_.addAddress(host.toUTF8(), port.toUTF8(), protocol);
andrewm@41 1116 }
andrewm@41 1117 }
andrewm@41 1118
andrewm@41 1119 if(props->containsKey("OSCReceiveEnabled")) {
andrewm@41 1120 bool enable = props->getBoolValue("OSCReceiveEnabled");
andrewm@41 1121 oscReceiveSetEnabled(enable);
andrewm@41 1122 }
andrewm@41 1123 if(props->containsKey("OSCReceivePort")) {
andrewm@41 1124 int port = props->getIntValue("OSCReceivePort");
andrewm@41 1125 if(port >= 1 && port <= 65535)
andrewm@41 1126 oscReceiveSetPort(port);
andrewm@41 1127 }
andrewm@41 1128
andrewm@41 1129 }
andrewm@41 1130
andrewm@41 1131 // Load the MIDI output device for a given zone
andrewm@41 1132 void MainApplicationController::loadMIDIOutputFromApplicationPreferences(int zone) {
andrewm@41 1133 PropertiesFile *props = applicationProperties_.getUserSettings();
andrewm@41 1134
andrewm@41 1135 String keyName = "MIDIOutputZone";
andrewm@41 1136 keyName += zone;
andrewm@41 1137
andrewm@41 1138 if(props->containsKey(keyName)) {
andrewm@41 1139 String output = props->getValue(keyName);
andrewm@41 1140 if(output.startsWith("__virtual__")) {
andrewm@41 1141 #ifndef JUCE_WINDOWS
andrewm@41 1142 // Open virtual port with the name that follows
andrewm@41 1143 String virtualPortName = output.substring(11); // length of "__virtual__"
andrewm@41 1144 midiOutputController_.enableVirtualPort(zone, virtualPortName.toUTF8());
andrewm@41 1145 #endif
andrewm@41 1146 }
andrewm@41 1147 else {
andrewm@41 1148 String deviceName = props->getValue(keyName);
andrewm@41 1149 int index = midiOutputController_.indexOfDeviceNamed(deviceName);
andrewm@41 1150 // cout << "zone " << zone << " id " << index << " name " << deviceName << endl;
andrewm@41 1151 if(index >= 0)
andrewm@41 1152 enableMIDIOutputPort(zone, index);
andrewm@41 1153 }
andrewm@41 1154 }
andrewm@41 1155 }
andrewm@41 1156
andrewm@17 1157 #ifdef ENABLE_TOUCHKEYS_SENSOR_TEST
andrewm@17 1158 // Start testing the TouchKeys sensors. Returns true on success.
andrewm@17 1159 bool MainApplicationController::touchkeySensorTestStart(const char *path, int firstKey) {
andrewm@17 1160 #ifndef TOUCHKEYS_NO_GUI
andrewm@17 1161 if(path == 0 || firstKey < touchkeyController_.lowestMidiNote())
andrewm@17 1162 return false;
andrewm@17 1163 if(keyboardTesterDisplay_ != 0)
andrewm@17 1164 return true;
andrewm@17 1165
andrewm@17 1166 // First, close the existing device which stops the data autogathering
andrewm@17 1167 closeTouchkeyDevice();
andrewm@17 1168
andrewm@17 1169 // Now reopen the TouchKeys device
andrewm@17 1170 if(!touchkeyController_.openDevice(path)) {
andrewm@17 1171 touchkeyErrorMessage_ = "Failed to open";
andrewm@17 1172 touchkeyErrorOccurred_ = true;
andrewm@17 1173 return false;
andrewm@17 1174 }
andrewm@17 1175
andrewm@17 1176 // Next, see if a real TouchKeys device is present at the other end
andrewm@17 1177 if(!touchkeyDeviceCheckForPresence()) {
andrewm@17 1178 touchkeyErrorMessage_ = "Device not recognized";
andrewm@17 1179 touchkeyErrorOccurred_ = true;
andrewm@17 1180 return false;
andrewm@17 1181 }
andrewm@17 1182
andrewm@17 1183 // Now, create the KeyboardTesterDisplay object which will handle processing
andrewm@17 1184 // raw data and displaying the results. Also a new window to hold it.
andrewm@17 1185 keyboardTesterDisplay_ = new KeyboardTesterDisplay(*this, keyboardController_);
andrewm@17 1186 keyboardTesterDisplay_->setKeyboardRange(touchkeyController_.lowestKeyPresentMidiNote(), touchkeyController_.highestMidiNote());
andrewm@17 1187 keyboardTesterWindow_ = new GraphicsDisplayWindow("TouchKeys Sensor Test", *keyboardTesterDisplay_);
andrewm@17 1188
andrewm@17 1189 // Start raw data gathering from the indicated key (converted to octave/key notation)
andrewm@18 1190 int keyOffset = firstKey - touchkeyController_.lowestMidiNote();
andrewm@17 1191 if(keyOffset < 0) // Shouldn't happen...
andrewm@17 1192 keyOffset = 0;
andrewm@17 1193
andrewm@49 1194 if(!touchkeyController_.startRawDataCollection(keyOffset / 12, keyOffset % 12, 3, 2)) {
andrewm@17 1195 touchkeyErrorMessage_ = "Failed to start";
andrewm@17 1196 touchkeyErrorOccurred_ = true;
andrewm@17 1197 return false;
andrewm@17 1198 }
andrewm@17 1199
andrewm@49 1200 keyboardTesterWindow_->addToDesktop(keyboardTesterWindow_->getDesktopWindowStyleFlags()
andrewm@49 1201 | ComponentPeer::windowHasCloseButton);
andrewm@42 1202 keyboardTesterWindow_->setVisible(true);
andrewm@49 1203 keyboardTesterWindow_->toFront(true);
andrewm@42 1204
andrewm@17 1205 touchkeyErrorMessage_ = "";
andrewm@17 1206 touchkeyErrorOccurred_ = false;
andrewm@17 1207 return true;
andrewm@17 1208 #endif
andrewm@17 1209 }
andrewm@17 1210
andrewm@17 1211 // Stop testing the TouchKeys sensors
andrewm@17 1212 void MainApplicationController::touchkeySensorTestStop() {
andrewm@17 1213 #ifndef TOUCHKEYS_NO_GUI
andrewm@17 1214 if(keyboardTesterDisplay_ == 0)
andrewm@17 1215 return;
andrewm@17 1216
andrewm@17 1217 // Stop raw data gathering first and close device
andrewm@17 1218 touchkeyController_.stopAutoGathering();
andrewm@17 1219 touchkeyController_.closeDevice();
andrewm@17 1220
andrewm@17 1221 // Delete the testing objects
andrewm@17 1222 delete keyboardTesterWindow_;
andrewm@17 1223 delete keyboardTesterDisplay_;
andrewm@17 1224
andrewm@17 1225 keyboardTesterWindow_ = 0;
andrewm@17 1226 keyboardTesterDisplay_ = 0;
andrewm@17 1227
andrewm@17 1228 touchkeyErrorMessage_ = "";
andrewm@17 1229 touchkeyErrorOccurred_ = false;
andrewm@17 1230 #endif
andrewm@17 1231 }
andrewm@17 1232
andrewm@17 1233 // Is the sensor test running?
andrewm@17 1234 bool MainApplicationController::touchkeySensorTestIsRunning() {
andrewm@17 1235 #ifdef TOUCHKEYS_NO_GUI
andrewm@17 1236 return false;
andrewm@17 1237 #else
andrewm@17 1238 return (keyboardTesterDisplay_ != 0);
andrewm@17 1239 #endif
andrewm@17 1240 }
andrewm@17 1241
andrewm@17 1242 // Set the current key that is begin tested
andrewm@17 1243 void MainApplicationController::touchkeySensorTestSetKey(int key) {
andrewm@17 1244 #ifndef TOUCHKEYS_NO_GUI
andrewm@17 1245 if(keyboardTesterDisplay_ == 0 || key < touchkeyController_.lowestMidiNote())
andrewm@17 1246 return;
andrewm@17 1247
andrewm@17 1248 int keyOffset = key - touchkeyController_.lowestMidiNote();
andrewm@17 1249 if(keyOffset < 0) // Shouldn't happen...
andrewm@17 1250 keyOffset = 0;
andrewm@17 1251
andrewm@17 1252 touchkeyController_.rawDataChangeKeyAndMode(keyOffset / 12, keyOffset % 12, 3, 2);
andrewm@17 1253 #endif
andrewm@17 1254 }
andrewm@17 1255
andrewm@17 1256 // Reset the testing state to all sensors off
andrewm@17 1257 void MainApplicationController::touchkeySensorTestResetState() {
andrewm@17 1258 #ifndef TOUCHKEYS_NO_GUI
andrewm@17 1259 if(keyboardTesterDisplay_ == 0)
andrewm@17 1260 return;
andrewm@17 1261 for(int key = 0; key < 128; key++)
andrewm@17 1262 keyboardTesterDisplay_->resetSensorState(key);
andrewm@17 1263 #endif
andrewm@17 1264 }
andrewm@17 1265
andrewm@17 1266 #endif // ENABLE_TOUCHKEYS_SENSOR_TEST
andrewm@17 1267
andrewm@53 1268 #ifdef ENABLE_TOUCHKEYS_FIRMWARE_UPDATE
andrewm@53 1269 // Put TouchKeys controller board into bootloader mode, for receiving firmware updates
andrewm@53 1270 // (supplied by a different utility)
andrewm@53 1271 bool MainApplicationController::touchkeyJumpToBootloader(const char *path) {
andrewm@53 1272 // First, close the existing device which stops the data autogathering
andrewm@53 1273 closeTouchkeyDevice();
andrewm@53 1274
andrewm@53 1275 // Now reopen the TouchKeys device
andrewm@53 1276 if(!touchkeyController_.openDevice(path)) {
andrewm@53 1277 touchkeyErrorMessage_ = "Failed to open";
andrewm@53 1278 touchkeyErrorOccurred_ = true;
andrewm@53 1279 return false;
andrewm@53 1280 }
andrewm@53 1281
andrewm@53 1282 touchkeyController_.jumpToBootloader();
andrewm@53 1283
andrewm@53 1284 // Set an "error" condition to display this message, and because
andrewm@53 1285 // after jumping to bootloader mode, the device will not open properly
andrewm@53 1286 // until it has been reset.
andrewm@53 1287 touchkeyErrorMessage_ = "Firmware update mode";
andrewm@53 1288 touchkeyErrorOccurred_ = true;
andrewm@53 1289 return true;
andrewm@53 1290 }
andrewm@53 1291 #endif // ENABLE_TOUCHKEYS_FIRMWARE_UPDATE
andrewm@53 1292
andrewm@0 1293 // Return the name of a MIDI note given its number
andrewm@0 1294 std::string MainApplicationController::midiNoteName(int noteNumber) {
andrewm@0 1295 if(noteNumber < 0 || noteNumber > 127)
andrewm@0 1296 return "";
andrewm@0 1297 char name[6];
andrewm@20 1298 #ifdef _MSC_VER
andrewm@20 1299 _snprintf_s(name, 6, _TRUNCATE, "%s%d", kNoteNames[noteNumber % 12], (noteNumber / 12) - 1);
andrewm@20 1300 #else
andrewm@0 1301 snprintf(name, 6, "%s%d", kNoteNames[noteNumber % 12], (noteNumber / 12) - 1);
andrewm@20 1302 #endif
andrewm@0 1303
andrewm@0 1304 return name;
andrewm@0 1305 }
andrewm@0 1306
andrewm@0 1307 // Get the number of a MIDI note given its name
andrewm@0 1308 int MainApplicationController::midiNoteNumberForName(std::string const& name) {
andrewm@0 1309 // Any valid note name will have at least two characters
andrewm@0 1310 if(name.length() < 2)
andrewm@0 1311 return -1;
andrewm@0 1312
andrewm@0 1313 // Find the pitch class first, then the octave
andrewm@0 1314 int pitchClass = -1;
andrewm@0 1315 int startIndex = 1;
andrewm@0 1316 if(!name.compare(0, 2, "C#") ||
andrewm@0 1317 !name.compare(0, 2, "c#") ||
andrewm@0 1318 !name.compare(0, 2, "Db") ||
andrewm@0 1319 !name.compare(0, 2, "db")) {
andrewm@0 1320 pitchClass = 1;
andrewm@0 1321 startIndex = 2;
andrewm@0 1322 }
andrewm@0 1323 else if(!name.compare(0, 2, "D#") ||
andrewm@0 1324 !name.compare(0, 2, "d#") ||
andrewm@0 1325 !name.compare(0, 2, "Eb") ||
andrewm@0 1326 !name.compare(0, 2, "eb")) {
andrewm@0 1327 pitchClass = 3;
andrewm@0 1328 startIndex = 2;
andrewm@0 1329 }
andrewm@0 1330 else if(!name.compare(0, 2, "F#") ||
andrewm@0 1331 !name.compare(0, 2, "f#") ||
andrewm@0 1332 !name.compare(0, 2, "Gb") ||
andrewm@0 1333 !name.compare(0, 2, "gb")){
andrewm@0 1334 pitchClass = 6;
andrewm@0 1335 startIndex = 2;
andrewm@0 1336 }
andrewm@0 1337 else if(!name.compare(0, 2, "G#") ||
andrewm@0 1338 !name.compare(0, 2, "g#") ||
andrewm@0 1339 !name.compare(0, 2, "Ab") ||
andrewm@0 1340 !name.compare(0, 2, "ab")){
andrewm@0 1341 pitchClass = 8;
andrewm@0 1342 startIndex = 2;
andrewm@0 1343 }
andrewm@0 1344 else if(!name.compare(0, 2, "A#") ||
andrewm@0 1345 !name.compare(0, 2, "a#") ||
andrewm@0 1346 !name.compare(0, 2, "Bb") ||
andrewm@0 1347 !name.compare(0, 2, "bb")){
andrewm@0 1348 pitchClass = 10;
andrewm@0 1349 startIndex = 2;
andrewm@0 1350 }
andrewm@0 1351 else if(!name.compare(0, 1, "C") ||
andrewm@0 1352 !name.compare(0, 1, "c"))
andrewm@0 1353 pitchClass = 0;
andrewm@0 1354 else if(!name.compare(0, 1, "D") ||
andrewm@0 1355 !name.compare(0, 1, "d"))
andrewm@0 1356 pitchClass = 2;
andrewm@0 1357 else if(!name.compare(0, 1, "E") ||
andrewm@0 1358 !name.compare(0, 1, "e"))
andrewm@0 1359 pitchClass = 4;
andrewm@0 1360 else if(!name.compare(0, 1, "F") ||
andrewm@0 1361 !name.compare(0, 1, "f"))
andrewm@0 1362 pitchClass = 5;
andrewm@0 1363 else if(!name.compare(0, 1, "G") ||
andrewm@0 1364 !name.compare(0, 1, "g"))
andrewm@0 1365 pitchClass = 7;
andrewm@0 1366 else if(!name.compare(0, 1, "A") ||
andrewm@0 1367 !name.compare(0, 1, "a"))
andrewm@0 1368 pitchClass = 9;
andrewm@0 1369 else if(!name.compare(0, 1, "B") ||
andrewm@0 1370 !name.compare(0, 1, "b"))
andrewm@0 1371 pitchClass = 11;
andrewm@0 1372
andrewm@0 1373 if(pitchClass < 0) // No valid note found
andrewm@0 1374 return -1;
andrewm@0 1375
andrewm@0 1376 int octave = atoi(name.substr(startIndex).c_str());
andrewm@0 1377 int noteNumber = (octave + 1) * 12 + pitchClass;
andrewm@0 1378
andrewm@0 1379 if(noteNumber < 0 || noteNumber > 127)
andrewm@0 1380 return -1;
andrewm@0 1381 return noteNumber;
andrewm@49 1382 }
andrewm@49 1383
andrewm@49 1384
andrewm@49 1385 // ***** External OSC Control *****
andrewm@49 1386
andrewm@49 1387 bool MainApplicationOSCController::oscHandlerMethod(const char *path, const char *types, int numValues, lo_arg **values, void *data) {
andrewm@49 1388 if(!strncmp(path, "/control", 8)) {
andrewm@49 1389 // OSC messages that start with /touchkeys/control are used to control the operation of the
andrewm@49 1390 // software and mappings
andrewm@49 1391
andrewm@49 1392 // First check if the message belongs to one of the segments
andrewm@49 1393 if(!strncmp(path, "/control/segment", 16) && strlen(path) > 16) {
andrewm@49 1394 // Pick out which segment based on the following number: e.g. /control/segment0/...
andrewm@49 1395
andrewm@49 1396 std::string subpath(&path[16]);
andrewm@49 1397 int separatorLoc = subpath.find_first_of('/');
andrewm@49 1398 if(separatorLoc == std::string::npos || separatorLoc == subpath.length() - 1) {
andrewm@49 1399 // Malformed input (no slash or it's the last character): ignore
andrewm@49 1400 return false;
andrewm@49 1401 }
andrewm@49 1402 std::stringstream segmentNumberSStream(subpath.substr(0, separatorLoc));
andrewm@49 1403
andrewm@49 1404 int segmentNumber = 0;
andrewm@49 1405 segmentNumberSStream >> segmentNumber;
andrewm@49 1406
andrewm@49 1407 if(segmentNumber < 0) // Unknown segment number
andrewm@49 1408 return false;
andrewm@49 1409
andrewm@49 1410 // Pass this message onto the corresponding segment in MidiInputController
andrewm@49 1411 // If the segment doesn't exist, it will return false. All further handling is
andrewm@49 1412 // done within MidiInputController with its corresponding mutex locked so
andrewm@49 1413 // the segments can't change while this message is processed.
andrewm@49 1414
andrewm@49 1415 subpath = subpath.substr(separatorLoc); // Start at the '/'
andrewm@49 1416
andrewm@49 1417 if(subpath == "/set-midi-out") {
andrewm@49 1418 // Special case for setting the MIDI output, which is done in the main controller
andrewm@49 1419 // rather than in the segment itself
andrewm@49 1420
andrewm@49 1421 if(numValues >= 1) {
andrewm@49 1422 if(types[0] == 'i') {
andrewm@49 1423 MidiKeyboardSegment* segment = controller_.midiInputController_.segment(segmentNumber);
andrewm@49 1424 if(segment == 0)
andrewm@49 1425 oscControlTransmitResult(1); // Failure response
andrewm@49 1426 else {
andrewm@49 1427 if(values[0]->i < 0) {
andrewm@49 1428 // Negative value means disable
andrewm@49 1429 controller_.disableMIDIOutputPort(segment->outputPort());
andrewm@49 1430 }
andrewm@49 1431 else {
andrewm@49 1432 controller_.enableMIDIOutputPort(segment->outputPort(), values[0]->i);
andrewm@49 1433 }
andrewm@49 1434 oscControlTransmitResult(0);
andrewm@49 1435 }
andrewm@49 1436 }
andrewm@49 1437 #ifndef JUCE_WINDOWS
andrewm@49 1438 else if(types[0] == 's') {
andrewm@49 1439 MidiKeyboardSegment* segment = controller_.midiInputController_.segment(segmentNumber);
andrewm@49 1440 if(segment == 0)
andrewm@49 1441 oscControlTransmitResult(1); // Failure response
andrewm@49 1442 else {
andrewm@49 1443 if(!strcmp(&values[0]->s, "virtual")) {
andrewm@49 1444 char st[20];
andrewm@49 1445 snprintf(st, 20, "TouchKeys %d", segment->outputPort());
andrewm@49 1446 controller_.enableMIDIOutputVirtualPort(segment->outputPort(), st);
andrewm@49 1447 oscControlTransmitResult(0);
andrewm@49 1448 }
andrewm@49 1449 }
andrewm@49 1450 }
andrewm@49 1451 #endif
andrewm@49 1452 }
andrewm@49 1453 }
andrewm@49 1454 else {
andrewm@49 1455 // All other segment messages are handled within MidiKeyboardSegment
andrewm@49 1456
andrewm@49 1457 OscMessage* response = controller_.midiInputController_.oscControlMessageForSegment(segmentNumber, subpath.c_str(), types, numValues, values, data);
andrewm@49 1458 if(response != 0) {
andrewm@49 1459 // Add the right prefix to the response. If it is a simple result status,
andrewm@49 1460 // then give it the generic prefix. Otherwise add the zone beforehand
andrewm@49 1461 if(!strcmp(response->path(), "/result"))
andrewm@49 1462 response->prependPath("/touchkeys/control");
andrewm@49 1463 else {
andrewm@49 1464 char prefix[28];
andrewm@49 1465 #ifdef _MSC_VER
andrewm@49 1466 _snprintf_s(prefix, 28, _TRUNCATE, "/touchkeys/control/segment%d", segmentNumber);
andrewm@49 1467 #else
andrewm@49 1468 snprintf(prefix, 28, "/touchkeys/control/segment%d", segmentNumber);
andrewm@49 1469 #endif
andrewm@49 1470 response->prependPath(prefix);
andrewm@49 1471 }
andrewm@49 1472
andrewm@49 1473 // Send the message and free it
andrewm@49 1474 controller_.oscTransmitter_.sendMessage(response->path(), response->type(), response->message());
andrewm@49 1475 delete response;
andrewm@49 1476 }
andrewm@49 1477 }
andrewm@49 1478 }
andrewm@49 1479 else if(!strcmp(path, "/control/preset-load")) {
andrewm@49 1480 // Load preset from file
andrewm@49 1481 // Argument 0 is the path to the file
andrewm@49 1482
andrewm@49 1483 if(numValues > 0) {
andrewm@49 1484 if(types[0] == 's') {
andrewm@49 1485 bool result = controller_.loadPresetFromFile(&values[0]->s);
andrewm@49 1486
andrewm@49 1487 // Send back a message on success/failure
andrewm@49 1488 oscControlTransmitResult(result == true ? 0 : 1);
andrewm@49 1489 return true;
andrewm@49 1490 }
andrewm@49 1491 else if(types[0] == 'i') {
andrewm@49 1492 // TODO: take a second form of the message which has a numerical
andrewm@49 1493 // input for selecting presets
andrewm@49 1494 }
andrewm@49 1495 }
andrewm@49 1496 return false;
andrewm@49 1497 }
andrewm@49 1498 else if(!strcmp(path, "/control/preset-save")) {
andrewm@49 1499 // Save preset to file
andrewm@49 1500
andrewm@49 1501 if(numValues > 0) {
andrewm@49 1502 if(types[0] == 's') {
andrewm@49 1503 bool result = controller_.savePresetToFile(&values[0]->s);
andrewm@49 1504
andrewm@49 1505 // Send back a message on success/failure
andrewm@49 1506 oscControlTransmitResult(result == true ? 0 : 1);
andrewm@49 1507 return true;
andrewm@49 1508 }
andrewm@49 1509 else if(types[0] == 'i') {
andrewm@49 1510 // TODO: take a second form of the message which has a numerical
andrewm@49 1511 // input for selecting presets
andrewm@49 1512 }
andrewm@49 1513 }
andrewm@49 1514 return false;
andrewm@49 1515 }
andrewm@49 1516 else if(!strcmp(path, "/control/preset-clear")) {
andrewm@49 1517 // Clear everything in preset
andrewm@49 1518 controller_.clearPreset();
andrewm@49 1519 oscControlTransmitResult(0);
andrewm@49 1520 return true;
andrewm@49 1521 }
andrewm@49 1522 else if(!strcmp(path, "/control/tk-list-devices")) {
andrewm@49 1523 // Return a list of TouchKeys devices
andrewm@49 1524
andrewm@49 1525 OscMessage *response = OscTransmitter::createMessage("/touchkeys/control/tk-list-devices/result", "i",
andrewm@49 1526 controller_.availableTouchkeyDevices().size(), LO_ARGS_END);
andrewm@49 1527
andrewm@49 1528 vector<std::string> devices = controller_.availableTouchkeyDevices();
andrewm@49 1529 vector<string>::iterator it;
andrewm@49 1530 for(it = devices.begin(); it != devices.end(); ++it) {
andrewm@49 1531 lo_message_add_string(response->message(), it->c_str());
andrewm@49 1532 }
andrewm@49 1533
andrewm@49 1534 controller_.oscTransmitter_.sendMessage(response->path(), response->type(), response->message());
andrewm@49 1535 delete response;
andrewm@49 1536
andrewm@49 1537 return true;
andrewm@49 1538 }
andrewm@49 1539 else if(!strcmp(path, "/control/tk-start")) {
andrewm@49 1540 // Start the TouchKeys device with the given path
andrewm@49 1541 if(numValues > 0) {
andrewm@49 1542 if(types[0] == 's') {
andrewm@49 1543 char *device = &values[0]->s;
andrewm@49 1544 bool result = controller_.touchkeyDeviceStartupSequence(device);
andrewm@49 1545
andrewm@49 1546 oscControlTransmitResult(result == true ? 0 : 1);
andrewm@49 1547 return true;
andrewm@49 1548 }
andrewm@49 1549 }
andrewm@49 1550 }
andrewm@49 1551 else if(!strcmp(path, "/control/tk-stop")) {
andrewm@49 1552 // Stop TouchKeys
andrewm@49 1553 if(controller_.touchkeyDeviceIsOpen()) {
andrewm@49 1554 controller_.closeTouchkeyDevice();
andrewm@49 1555 oscControlTransmitResult(0);
andrewm@49 1556 }
andrewm@49 1557 else {
andrewm@49 1558 // Not running, can't close
andrewm@49 1559 oscControlTransmitResult(1);
andrewm@49 1560 }
andrewm@49 1561 return true;
andrewm@49 1562 }
andrewm@49 1563 else if(!strcmp(path, "/control/tk-set-lowest-midi-note")) {
andrewm@49 1564 // Set TouchKeys octave such that the lowest key is at the
andrewm@49 1565 // given note. Only 'C' notes are valid.
andrewm@49 1566
andrewm@49 1567 if(numValues > 0) {
andrewm@49 1568 if(types[0] == 'i') {
andrewm@49 1569 int note = values[0]->i;
andrewm@49 1570
andrewm@49 1571 if(note % 12 == 0 && note >= 12 && note < 127) {
andrewm@49 1572 controller_.touchkeyDeviceSetLowestMidiNote(note);
andrewm@49 1573 oscControlTransmitResult(0);
andrewm@49 1574 }
andrewm@49 1575 else {
andrewm@49 1576 // Invalid note
andrewm@49 1577 oscControlTransmitResult(1);
andrewm@49 1578 }
andrewm@49 1579 return true;
andrewm@49 1580 }
andrewm@49 1581 }
andrewm@49 1582 }
andrewm@49 1583 else if(!strcmp(path, "/control/tk-autodetect")) {
andrewm@49 1584 // Autodetect lowest TouchKeys octave
andrewm@49 1585 controller_.touchkeyDeviceAutodetectLowestMidiNote();
andrewm@49 1586 oscControlTransmitResult(0);
andrewm@49 1587 return true;
andrewm@49 1588 }
andrewm@49 1589 else if(!strcmp(path, "/control/tk-autodetect-stop")) {
andrewm@49 1590 // Stop autodetecting TouchKeys octave
andrewm@49 1591 controller_.touchkeyDeviceStopAutodetecting();
andrewm@49 1592 oscControlTransmitResult(0);
andrewm@49 1593 return true;
andrewm@49 1594 }
andrewm@49 1595 else if(!strcmp(path, "/control/list-midi-in")) {
andrewm@49 1596 // List available MIDI input devices
andrewm@49 1597 std::vector<std::pair<int, std::string> > midiInputs = controller_.availableMIDIInputDevices();
andrewm@49 1598
andrewm@49 1599 OscMessage *response = OscTransmitter::createMessage("/list-midi-in/result", "i", midiInputs.size(), LO_ARGS_END);
andrewm@49 1600
andrewm@49 1601 std::vector<std::pair<int, std::string> >::iterator it;
andrewm@49 1602 for(it = midiInputs.begin(); it != midiInputs.end(); ++it) {
andrewm@49 1603 lo_message_add_int32(response->message(), it->first);
andrewm@49 1604 lo_message_add_string(response->message(), it->second.c_str());
andrewm@49 1605 }
andrewm@49 1606
andrewm@49 1607 controller_.oscTransmitter_.sendMessage(response->path(), response->type(), response->message());
andrewm@49 1608 delete response;
andrewm@49 1609
andrewm@49 1610 return true;
andrewm@49 1611 }
andrewm@49 1612 else if(!strcmp(path, "/control/list-midi-out")) {
andrewm@49 1613 // List available MIDI output devices
andrewm@49 1614 std::vector<std::pair<int, std::string> > midiOutputs = controller_.availableMIDIOutputDevices();
andrewm@49 1615
andrewm@49 1616 OscMessage *response = OscTransmitter::createMessage("/list-midi-out/result", "i", midiOutputs.size(), LO_ARGS_END);
andrewm@49 1617
andrewm@49 1618 std::vector<std::pair<int, std::string> >::iterator it;
andrewm@49 1619 for(it = midiOutputs.begin(); it != midiOutputs.end(); ++it) {
andrewm@49 1620 lo_message_add_int32(response->message(), it->first);
andrewm@49 1621 lo_message_add_string(response->message(), it->second.c_str());
andrewm@49 1622 }
andrewm@49 1623
andrewm@49 1624 controller_.oscTransmitter_.sendMessage(response->path(), response->type(), response->message());
andrewm@49 1625 delete response;
andrewm@49 1626
andrewm@49 1627 return true;
andrewm@49 1628 }
andrewm@49 1629 else if(!strcmp(path, "/control/set-midi-in-keyboard")) {
andrewm@49 1630 // Set MIDI input device for keyboard
andrewm@49 1631 if(numValues > 0) {
andrewm@49 1632 if(types[0] == 'i') {
andrewm@49 1633 if(controller_.midiTouchkeysStandaloneModeIsEnabled())
andrewm@49 1634 controller_.midiTouchkeysStandaloneModeDisable();
andrewm@49 1635 if(values[0]->i < 0) {
andrewm@49 1636 // Negative means disable
andrewm@49 1637 controller_.disablePrimaryMIDIInputPort();
andrewm@49 1638 }
andrewm@49 1639 else {
andrewm@49 1640 controller_.enableMIDIInputPort(values[0]->i, true);
andrewm@49 1641 }
andrewm@49 1642
andrewm@49 1643 oscControlTransmitResult(0);
andrewm@49 1644 return true;
andrewm@49 1645 }
andrewm@49 1646 else if(types[0] == 's') {
andrewm@49 1647 if(!strncmp(&values[0]->s, "stand", 5)) {
andrewm@49 1648 // Enable TouchKeys standalone mode in place of MIDI input
andrewm@49 1649 controller_.disablePrimaryMIDIInputPort();
andrewm@49 1650 controller_.midiTouchkeysStandaloneModeEnable();
andrewm@49 1651
andrewm@49 1652 oscControlTransmitResult(0);
andrewm@49 1653 return true;
andrewm@49 1654 }
andrewm@49 1655 }
andrewm@49 1656 }
andrewm@49 1657 }
andrewm@49 1658 else if(!strcmp(path, "/control/set-midi-in-aux")) {
andrewm@49 1659 // Set MIDI auxiliary input device
andrewm@49 1660 if(numValues > 0) {
andrewm@49 1661 if(types[0] == 'i') {
andrewm@49 1662 controller_.disableAllMIDIInputPorts(true);
andrewm@49 1663 if(values[0]->i >= 0) {
andrewm@49 1664 // Negative values mean leave the port disabled
andrewm@49 1665 controller_.enableMIDIInputPort(values[0]->i, false);
andrewm@49 1666 }
andrewm@49 1667
andrewm@49 1668 oscControlTransmitResult(0);
andrewm@49 1669 return true;
andrewm@49 1670 }
andrewm@49 1671 }
andrewm@49 1672
andrewm@49 1673 }
andrewm@49 1674 else if(!strcmp(path, "/control/add-segment")) {
andrewm@49 1675 // Add a new keyboard segment
andrewm@49 1676 if(controller_.midiSegmentsCount() >= 8) {
andrewm@49 1677 // Max of 8 segments possible
andrewm@49 1678 oscControlTransmitResult(1);
andrewm@49 1679 }
andrewm@49 1680 else {
andrewm@49 1681 controller_.midiSegmentAdd();
andrewm@49 1682 oscControlTransmitResult(0);
andrewm@49 1683 }
andrewm@49 1684 return true;
andrewm@49 1685 }
andrewm@49 1686 else if(!strcmp(path, "/control/delete-segment")) {
andrewm@49 1687 // Remove a keyboard segment by number
andrewm@49 1688 if(numValues > 0) {
andrewm@49 1689 if(types[0] == 'i') {
andrewm@49 1690 int segmentNumber = values[0]->i;
andrewm@49 1691
andrewm@49 1692 bool result = controller_.midiInputController_.removeSegment(segmentNumber);
andrewm@49 1693 oscControlTransmitResult(result == true ? 0 : 1);
andrewm@49 1694 return true;
andrewm@49 1695 }
andrewm@49 1696 }
andrewm@49 1697 }
andrewm@49 1698 }
andrewm@49 1699
andrewm@49 1700 return false;
andrewm@49 1701 }
andrewm@49 1702
andrewm@49 1703 // Send back an OSC message to indicate the result of a control command
andrewm@49 1704 void MainApplicationOSCController::oscControlTransmitResult(int result) {
andrewm@49 1705 controller_.oscTransmitter_.sendMessage("/touchkeys/control/result", "i", result, LO_ARGS_END);
andrewm@55 1706 }