changeset 28:cfbcd31a54e7

First attempt at "rescan devices". Reloads TouchKeys and MIDI in/out devices. Sets MIDI in/out to Disabled if previous device not found. Does not yet handle order of devices changing. Also fix some bugs: -- Can now disable Standalone mode -- Poly-AT now accounts for transposition -- If TK device stops on error, GUI reflects it -- Possible crash on quit should be fixed (needs more testing)
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Sun, 02 Mar 2014 22:31:54 +0000
parents eef567a60146
children f3efbf2984c3
files Source/GUI/ControlWindowMainComponent.cpp Source/GUI/ControlWindowMainComponent.h Source/GUI/KeyboardZoneComponent.cpp Source/GUI/KeyboardZoneComponent.h Source/GUI/MainWindow.cpp Source/Main.cpp Source/MainApplicationController.cpp Source/MainApplicationController.h Source/TouchKeys/OscMidiConverter.cpp Source/TouchKeys/TouchkeyDevice.cpp Source/TouchKeys/TouchkeyDevice.h
diffstat 11 files changed, 113 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/Source/GUI/ControlWindowMainComponent.cpp	Sun Mar 02 19:25:50 2014 +0000
+++ b/Source/GUI/ControlWindowMainComponent.cpp	Sun Mar 02 22:31:54 2014 +0000
@@ -517,11 +517,20 @@
     midiInputDeviceComboBox->addItem("Disabled", 1);
     midiInputDeviceComboBox->addItem("TouchKeys Standalone", 2);
     counter = kMidiInputDeviceComboBoxOffset;
+    
+    // Check whether the currently selected ID still exists while
+    // we build the list
+    bool lastSelectedDeviceExists = false;
     for(it = devices.begin(); it != devices.end(); ++it) {
         midiInputDeviceComboBox->addItem((*it).second.c_str(), counter);
         midiInputDeviceIDs_.push_back(it->first);
+        if(it->first == lastSelectedMidiInputID_)
+            lastSelectedDeviceExists = true;
         counter++;
     }
+    
+    if(!lastSelectedDeviceExists)
+        controller_->disableAllMIDIInputPorts();
 }
 
 void ControlWindowMainComponent::updateOscHostPort()
@@ -539,6 +548,14 @@
 void ControlWindowMainComponent::synchronize() {
     if(controller_ == 0)
         return;
+    
+    bool devicesUpdated = false;
+    
+    if(controller_->devicesShouldUpdate() != lastControllerUpdateDeviceCount_) {
+        lastControllerUpdateDeviceCount_ = controller_->devicesShouldUpdate();
+        updateInputDeviceList();
+        devicesUpdated = true;
+    }
 
     // Update TouchKeys status
 #ifdef ENABLE_TOUCHKEYS_SENSOR_TEST
@@ -571,7 +588,7 @@
         if(selectedMidiInputDevices.empty()) {
             midiInputDeviceComboBox->setSelectedId(1, dontSendNotification);
         }
-        else if(selectedMidiInputDevices.front() != lastSelectedMidiInputID_){
+        else if(selectedMidiInputDevices.front() != lastSelectedMidiInputID_ || devicesUpdated){
             // Input has changed from before. Find it in vector
             // If there is more than one selected ID, we will only take the first one for
             // the current UI. This affects the display but not the functionality.
@@ -644,7 +661,7 @@
     // Synchronize every tab component
     for(int tab = 0; tab < keyboardZoneTabbedComponent->getNumTabs(); tab++) {
         KeyboardZoneComponent *component = static_cast<KeyboardZoneComponent*> (keyboardZoneTabbedComponent->getTabContentComponent(tab));
-        component->synchronize();
+        component->synchronize(devicesUpdated);
     }
 
     // Update add/remove buttons
--- a/Source/GUI/ControlWindowMainComponent.h	Sun Mar 02 19:25:50 2014 +0000
+++ b/Source/GUI/ControlWindowMainComponent.h	Sun Mar 02 22:31:54 2014 +0000
@@ -50,6 +50,7 @@
     void setMainApplicationController(MainApplicationController *controller) {
         // Attach the user interface to the controller and vice-versa
         controller_ = controller;
+        lastControllerUpdateDeviceCount_ = controller_->devicesShouldUpdate();
         updateInputDeviceList();
     }
 
@@ -89,6 +90,8 @@
     std::vector<int> midiInputDeviceIDs_;
     int lastSelectedMidiInputID_;
     int lastSegmentUniqueIdentifier_;
+    
+    int lastControllerUpdateDeviceCount_;
     //[/UserVariables]
 
     //==============================================================================
--- a/Source/GUI/KeyboardZoneComponent.cpp	Sun Mar 02 19:25:50 2014 +0000
+++ b/Source/GUI/KeyboardZoneComponent.cpp	Sun Mar 02 22:31:54 2014 +0000
@@ -424,7 +424,12 @@
 {
     if(keyboardSegment_ == 0 || controller_ == 0)
         return;
-
+    
+    if(forceUpdates) {
+        // Update the controls to reflect the current state
+        updateOutputDeviceList();
+    }
+        
     // Update note ranges
     std::pair<int, int> range = keyboardSegment_->noteRange();
     if(!rangeLowComboBox->hasKeyboardFocus(true) || forceUpdates) {
@@ -444,7 +449,7 @@
 
     // Update MIDI output status
     int selectedMidiOutputDevice = controller_->selectedMIDIOutputPort(keyboardSegment_->outputPort());
-    if(selectedMidiOutputDevice != lastSelectedMidiOutputID_) {
+    if(selectedMidiOutputDevice != lastSelectedMidiOutputID_ || forceUpdates) {
         if(selectedMidiOutputDevice == MidiOutputController::kMidiOutputNotOpen)
             midiOutputDeviceComboBox->setSelectedId(1, dontSendNotification);
 #ifndef JUCE_WINDOWS
@@ -573,15 +578,24 @@
     snprintf(virtualPortName, 24, "Virtual Port (%d)", keyboardSegment_->outputPort());
 	midiOutputDeviceComboBox->addItem(virtualPortName, 2);
 #endif
-
+    
+    // Check whether the currently selected ID still exists while
+    // we build the list
+    bool lastSelectedDeviceExists = false;
     int counter = kMidiOutputDeviceComboBoxOffset;
     for(it = devices.begin(); it != devices.end(); ++it) {
         if(it->first < 0)
             continue;
         midiOutputDeviceComboBox->addItem((*it).second.c_str(), counter);
         midiOutputDeviceIDs_.push_back(it->first);
+        if(it->first == lastSelectedMidiOutputID_)
+            lastSelectedDeviceExists = true;
         counter++;
     }
+    
+    if(!lastSelectedDeviceExists) {
+        controller_->disableMIDIOutputPort(keyboardSegment_->outputPort());
+    }
 }
 
 // Create a popup menu containing a list of mapping factories
--- a/Source/GUI/KeyboardZoneComponent.h	Sun Mar 02 19:25:50 2014 +0000
+++ b/Source/GUI/KeyboardZoneComponent.h	Sun Mar 02 22:31:54 2014 +0000
@@ -54,8 +54,6 @@
         controller_ = controller;
         mappingListComponent->setMainApplicationController(controller_);
         if(controller_ != 0) {
-            // Update the controls to reflect the current state
-            updateOutputDeviceList();
             synchronize(true);
         }
     }
@@ -64,8 +62,6 @@
         keyboardSegment_ = segment;
         mappingListComponent->setKeyboardSegment(keyboardSegment_);
         if(controller_ != 0) {
-            // Update the controls to reflect the current state
-            updateOutputDeviceList();
             synchronize(true);
         }
     }
--- a/Source/GUI/MainWindow.cpp	Sun Mar 02 19:25:50 2014 +0000
+++ b/Source/GUI/MainWindow.cpp	Sun Mar 02 22:31:54 2014 +0000
@@ -252,7 +252,7 @@
         case kCommandRescanDevices:
             result.setInfo("Rescan Devices", "Rescans available TouchKeys and MIDI devices", controlCategory, 0);
             result.setTicked(false);
-            result.setActive(false);
+            result.setActive(true);
             result.addDefaultKeypress ('R', ModifierKeys::commandModifier);
             break;
         case kCommandEnableExperimentalMappings:
@@ -290,7 +290,7 @@
         case kCommandNewPreset:            
             break;
         case kCommandRescanDevices:
-            std::cout << "Rescan\n";
+            controller_.tellDevicesToUpdate();
             break;
         case kCommandEnableExperimentalMappings:
             controller_.setExperimentalMappingsEnabled(!controller_.experimentalMappingsEnabled());
--- a/Source/Main.cpp	Sun Mar 02 19:25:50 2014 +0000
+++ b/Source/Main.cpp	Sun Mar 02 22:31:54 2014 +0000
@@ -51,6 +51,9 @@
 
     void shutdown() {
         // Add your application's shutdown code here..
+        if(controller_.touchkeyDeviceIsRunning())
+            controller_.stopTouchkeyDevice();
+        
         mainWindow_ = nullptr; // (deletes our window)
         
         controller_.setKeyboardDisplayWindow(0);    // Delete display window and disconnect from controller
--- a/Source/MainApplicationController.cpp	Sun Mar 02 19:25:50 2014 +0000
+++ b/Source/MainApplicationController.cpp	Sun Mar 02 22:31:54 2014 +0000
@@ -48,6 +48,7 @@
   touchkeyErrorMessage_(""),
   touchkeyAutodetecting_(false),
   touchkeyStandaloneModeEnabled_(false),
+  deviceUpdateCounter_(0),
   oscReceiveEnabled_(false),
   oscReceivePort_(kDefaultOscReceivePort),
   experimentalMappingsEnabled_(false),
@@ -367,7 +368,7 @@
     touchkeyStandaloneModeEnabled_ = false;
     // Go through all segments and disable standalone mode
     for(int i = 0; i < midiInputController_.numSegments(); i++) {
-        midiInputController_.segment(i)->enableTouchkeyStandaloneMode();
+        midiInputController_.segment(i)->disableTouchkeyStandaloneMode();
     }
 }
 
--- a/Source/MainApplicationController.h	Sun Mar 02 19:25:50 2014 +0000
+++ b/Source/MainApplicationController.h	Sun Mar 02 22:31:54 2014 +0000
@@ -211,6 +211,13 @@
     void midiTouchkeysStandaloneModeDisable();
     bool midiTouchkeysStandaloneModeIsEnabled() { return touchkeyStandaloneModeEnabled_; }
     
+    // *** Update sync methods ***
+    // The controller maintains a variable that tells when the devices should be updated
+    // by the control window component. Whenever it changes value, the devices should be rescanned.
+    
+    int devicesShouldUpdate() { return deviceUpdateCounter_; }
+    void tellDevicesToUpdate() { deviceUpdateCounter_++; }
+    
     // *** OSC device methods ***
     
     bool oscTransmitEnabled() {
@@ -357,6 +364,8 @@
     std::string touchkeyErrorMessage_;
     bool touchkeyAutodetecting_;
     bool touchkeyStandaloneModeEnabled_;
+    int deviceUpdateCounter_;               // Unique number that increments every time devices should
+                                            // be rescanned
     
     // OSC information
     bool oscReceiveEnabled_;
--- a/Source/TouchKeys/OscMidiConverter.cpp	Sun Mar 02 19:25:50 2014 +0000
+++ b/Source/TouchKeys/OscMidiConverter.cpp	Sun Mar 02 22:31:54 2014 +0000
@@ -490,7 +490,8 @@
         midiOutputController_->sendAftertouchChannel(port, channel, roundedControlValue);
     }
     else if(controller_ == MidiKeyboardSegment::kControlPolyphonicAftertouch && note >= 0) {
-        midiOutputController_->sendAftertouchPoly(port, channel, note, roundedControlValue);
+        midiOutputController_->sendAftertouchPoly(port, channel, note + keyboardSegment_.outputTransposition(),
+                                                    roundedControlValue);
     }
     else if(controllerIs14Bit_) {
         // LSB for controllers 0-31 are found on controllers 32-63
--- a/Source/TouchKeys/TouchkeyDevice.cpp	Sun Mar 02 19:25:50 2014 +0000
+++ b/Source/TouchKeys/TouchkeyDevice.cpp	Sun Mar 02 22:31:54 2014 +0000
@@ -39,7 +39,7 @@
 ioThread_(boost::bind(&TouchkeyDevice::runLoop, this, _1), "TouchKeyDevice::ioThread"),
 rawDataThread_(boost::bind(&TouchkeyDevice::rawDataRunLoop, this, _1), "TouchKeyDevice::rawDataThread"),
 autoGathering_(false), shouldStop_(false), sendRawOscMessages_(false),
-verbose_(1), numOctaves_(0), lowestMidiNote_(48), lowestKeyPresentMidiNote_(48),
+verbose_(0), numOctaves_(0), lowestMidiNote_(48), lowestKeyPresentMidiNote_(48),
 updatedLowestMidiNote_(48), deviceSoftwareVersion_(-1), deviceHardwareVersion_(-1),
 expectedLengthWhite_(kTransmissionLengthWhiteNewHardware),
 expectedLengthBlack_(kTransmissionLengthBlackNewHardware), deviceHasRGBLEDs_(false),
@@ -130,39 +130,39 @@
 	// Open the device
 #ifdef _MSC_VER
 	// Open the serial port
-	serialHandle_ = CreateFile(inputDevicePath, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
-	if(serialHandle_ == INVALID_HANDLE_VALUE) {
-		Logger::writeToLog("Unable to open serial port " + String(inputDevicePath));
-		return false;
-	}
-
-	// Set some serial parameters, though they don't actually affect the operation
-	// of the port since it is all native USB
-	DCB serialParams = { 0 };
-	serialParams.DCBlength = sizeof(serialParams);
-
-	if(!BuildCommDCBA("baud=1000000 data=8 parity=N stop=1 dtr=on rts=on", &serialParams)) {
-		Logger::writeToLog("Unable to create port settings\n");
-		CloseHandle(serialHandle_);
-		serialHandle_ = INVALID_HANDLE_VALUE;
-		return false;
-	}
-
-	if(!SetCommState(serialHandle_, &serialParams)) {
-		Logger::writeToLog("Unable to set comm state\n");
-		CloseHandle(serialHandle_);
-		serialHandle_ = INVALID_HANDLE_VALUE;
-		return false;
-	}
-
-	// Set timeouts
-	COMMTIMEOUTS timeout = { 0 };
-	timeout.ReadIntervalTimeout = MAXDWORD;
-	timeout.ReadTotalTimeoutConstant = 0;
-	timeout.ReadTotalTimeoutMultiplier = 0;
-	timeout.WriteTotalTimeoutConstant = 0;
-	timeout.WriteTotalTimeoutMultiplier = 0;
-
+	serialHandle_ = CreateFile(inputDevicePath, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+	if(serialHandle_ == INVALID_HANDLE_VALUE) {
+		Logger::writeToLog("Unable to open serial port " + String(inputDevicePath));
+		return false;
+	}
+
+	// Set some serial parameters, though they don't actually affect the operation
+	// of the port since it is all native USB
+	DCB serialParams = { 0 };
+	serialParams.DCBlength = sizeof(serialParams);
+
+	if(!BuildCommDCBA("baud=1000000 data=8 parity=N stop=1 dtr=on rts=on", &serialParams)) {
+		Logger::writeToLog("Unable to create port settings\n");
+		CloseHandle(serialHandle_);
+		serialHandle_ = INVALID_HANDLE_VALUE;
+		return false;
+	}
+
+	if(!SetCommState(serialHandle_, &serialParams)) {
+		Logger::writeToLog("Unable to set comm state\n");
+		CloseHandle(serialHandle_);
+		serialHandle_ = INVALID_HANDLE_VALUE;
+		return false;
+	}
+
+	// Set timeouts
+	COMMTIMEOUTS timeout = { 0 };
+	timeout.ReadIntervalTimeout = MAXDWORD;
+	timeout.ReadTotalTimeoutConstant = 0;
+	timeout.ReadTotalTimeoutMultiplier = 0;
+	timeout.WriteTotalTimeoutConstant = 0;
+	timeout.WriteTotalTimeoutMultiplier = 0;
+
 	if(!SetCommTimeouts(serialHandle_, &timeout)) {
 		Logger::writeToLog("Unable to set timeouts\n");
 		CloseHandle(serialHandle_);
@@ -444,18 +444,20 @@
 }
 
 // Stop the run loop if applicable
-void TouchkeyDevice::stopAutoGathering() {
+void TouchkeyDevice::stopAutoGathering(bool writeStopCommandToDevice) {
     // Check if actually running
 	if(!autoGathering_ || !isOpen())
 		return;
     // Stop any calibration in progress
     calibrationAbort();	
     
-    // Tell device to stop scanning
-	if(deviceWrite((char*)kCommandStopScanning, 5) < 0) {
-        if(verbose_ >= 1)
-            cout << "ERROR: unable to write stopAutoGather command.  errno = " << errno << endl;
-	}
+    if(writeStopCommandToDevice) {
+        // Tell device to stop scanning
+        if(deviceWrite((char*)kCommandStopScanning, 5) < 0) {
+            if(verbose_ >= 1)
+                cout << "ERROR: unable to write stopAutoGather command.  errno = " << errno << endl;
+        }
+    }
 	
     // Setting this to true tells the run loop to exit what it's doing
 	shouldStop_ = true;
@@ -466,12 +468,15 @@
 	
     // Wait for run loop thread to finish. Set a timeout in case there's
     // some sort of device hangup
-    if(ioThread_.isThreadRunning())
-        ioThread_.stopThread(3000);
-    if(ledThread_.isThreadRunning())
-        ledThread_.stopThread(3000);
-    if(rawDataThread_.isThreadRunning())
-        rawDataThread_.stopThread(3000);
+    if(ioThread_.getThreadId() != Thread::getCurrentThreadId())
+        if(ioThread_.isThreadRunning())
+            ioThread_.stopThread(3000);
+    if(ledThread_.getThreadId() != Thread::getCurrentThreadId())
+        if(ledThread_.isThreadRunning())
+            ledThread_.stopThread(3000);
+    if(rawDataThread_.getThreadId() != Thread::getCurrentThreadId())
+        if(rawDataThread_.isThreadRunning())
+            rawDataThread_.stopThread(3000);
 	
     // Stop any currently playing notes
 	keyboard_.sendMessage("/touchkeys/allnotesoff", "", LO_ARGS_END);
@@ -1285,7 +1290,8 @@
 			if(errno != EAGAIN) {	// EAGAIN just means no data was available
                 if(verbose_ >= 1)
                     cout << "Unable to read from device (error " << errno << ").  Aborting.\n";
-				shouldStop_ = true;
+                stopAutoGathering(false);
+				//shouldStop_ = true;
 			}
 			
 #ifdef _MSC_VER
@@ -1422,7 +1428,8 @@
 			if(errno != EAGAIN) {	// EAGAIN just means no data was available
                 if(verbose_ >= 1)
                     cout << "Unable to read from device (error " << errno << ").  Aborting.\n";
-				shouldStop_ = true;
+                stopAutoGathering(false);
+				//shouldStop_ = true;
 			}
 			
 #ifdef _MSC_VER
--- a/Source/TouchKeys/TouchkeyDevice.h	Sun Mar 02 19:25:50 2014 +0000
+++ b/Source/TouchKeys/TouchkeyDevice.h	Sun Mar 02 22:31:54 2014 +0000
@@ -232,7 +232,7 @@
 	// Start or stop the processing.  startAutoGathering() returns
 	// true on success.
 	bool startAutoGathering();
-	void stopAutoGathering();	
+	void stopAutoGathering(bool writeStopCommandToDevice = true);
 	
 	// Status query methods
 	bool isOpen();