changeset 42:1526d2fbe01e

Updates to multi-finger trigger mapping, and a fix for control-change retransmission on loading presets.
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Thu, 21 Aug 2014 17:02:39 +0100
parents 85577160a0d4
children fa39caec190b
files Builds/Linux/Makefile Builds/Linux32/Makefile Source/MainApplicationController.cpp Source/Mappings/KeyDivision/TouchkeyKeyDivisionMapping.h Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMapping.cpp Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMapping.h Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingFactory.cpp Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingFactory.h Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingShortEditor.cpp Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingShortEditor.h Source/TouchKeys/MidiKeyboardSegment.cpp TouchKeys.jucer
diffstat 12 files changed, 760 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/Builds/Linux/Makefile	Sat Jun 21 23:32:33 2014 +0100
+++ b/Builds/Linux/Makefile	Thu Aug 21 17:02:39 2014 +0100
@@ -65,6 +65,7 @@
   $(OBJDIR)/TouchkeyPitchBendMappingFactory_9fc4ef9e.o \
   $(OBJDIR)/TouchkeyOnsetAngleMapping_a77ca3ca.o \
   $(OBJDIR)/TouchkeyOnsetAngleMappingFactory_6a4803ea.o \
+  $(OBJDIR)/TouchkeyMultiFingerTriggerMappingShortEditor_8604e029.o \
   $(OBJDIR)/TouchkeyMultiFingerTriggerMapping_f7bfe8a.o \
   $(OBJDIR)/TouchkeyMultiFingerTriggerMappingFactory_e811112a.o \
   $(OBJDIR)/TouchkeyKeyDivisionMapping_cea38eb0.o \
@@ -216,6 +217,11 @@
 	@echo "Compiling TouchkeyOnsetAngleMappingFactory.cpp"
 	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
 
+$(OBJDIR)/TouchkeyMultiFingerTriggerMappingShortEditor_8604e029.o: ../../Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingShortEditor.cpp
+	-@mkdir -p $(OBJDIR)
+	@echo "Compiling TouchkeyMultiFingerTriggerMappingShortEditor.cpp"
+	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
+
 $(OBJDIR)/TouchkeyMultiFingerTriggerMapping_f7bfe8a.o: ../../Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMapping.cpp
 	-@mkdir -p $(OBJDIR)
 	@echo "Compiling TouchkeyMultiFingerTriggerMapping.cpp"
--- a/Builds/Linux32/Makefile	Sat Jun 21 23:32:33 2014 +0100
+++ b/Builds/Linux32/Makefile	Thu Aug 21 17:02:39 2014 +0100
@@ -65,6 +65,7 @@
   $(OBJDIR)/TouchkeyPitchBendMappingFactory_9fc4ef9e.o \
   $(OBJDIR)/TouchkeyOnsetAngleMapping_a77ca3ca.o \
   $(OBJDIR)/TouchkeyOnsetAngleMappingFactory_6a4803ea.o \
+  $(OBJDIR)/TouchkeyMultiFingerTriggerMappingShortEditor_8604e029.o \
   $(OBJDIR)/TouchkeyMultiFingerTriggerMapping_f7bfe8a.o \
   $(OBJDIR)/TouchkeyMultiFingerTriggerMappingFactory_e811112a.o \
   $(OBJDIR)/TouchkeyKeyDivisionMapping_cea38eb0.o \
@@ -216,6 +217,11 @@
 	@echo "Compiling TouchkeyOnsetAngleMappingFactory.cpp"
 	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
 
+$(OBJDIR)/TouchkeyMultiFingerTriggerMappingShortEditor_8604e029.o: ../../Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingShortEditor.cpp
+	-@mkdir -p $(OBJDIR)
+	@echo "Compiling TouchkeyMultiFingerTriggerMappingShortEditor.cpp"
+	@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
+
 $(OBJDIR)/TouchkeyMultiFingerTriggerMapping_f7bfe8a.o: ../../Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMapping.cpp
 	-@mkdir -p $(OBJDIR)
 	@echo "Compiling TouchkeyMultiFingerTriggerMapping.cpp"
--- a/Source/MainApplicationController.cpp	Sat Jun 21 23:32:33 2014 +0100
+++ b/Source/MainApplicationController.cpp	Thu Aug 21 17:02:39 2014 +0100
@@ -102,6 +102,7 @@
     if(touchkeySensorTestIsRunning())
         touchkeySensorTestStop();
 #endif
+    removeAllOscListeners();
 }
 
 // Actions here run in the JUCE initialise() method once the application is loaded
@@ -121,10 +122,10 @@
             
             MappingFactory *factory = new TouchkeyVibratoMappingFactory(keyboardController_, *segment);
             if(factory != 0)
-                segment->addMappingFactory(factory);
+                segment->addMappingFactory(factory, true);
             factory = new TouchkeyPitchBendMappingFactory(keyboardController_, *segment);
             if(factory != 0)
-                segment->addMappingFactory(factory);
+                segment->addMappingFactory(factory, true);
         }
     }
     else if(!getPrefsStartupPresetNone()) {
@@ -816,7 +817,7 @@
 
 // Return whethera  given mapping is experimental or not
 bool MainApplicationController::mappingIsExperimental(int index) {
-    if(index > 2)
+    if(index > 2 && index != 4)
         return true;
     return false;
 }
@@ -1163,6 +1164,8 @@
         return false;
     }
     
+    keyboardTesterWindow_->setVisible(true);
+    
     touchkeyErrorMessage_ = "";
     touchkeyErrorOccurred_ = false;
     return true;
--- a/Source/Mappings/KeyDivision/TouchkeyKeyDivisionMapping.h	Sat Jun 21 23:32:33 2014 +0100
+++ b/Source/Mappings/KeyDivision/TouchkeyKeyDivisionMapping.h	Thu Aug 21 17:02:39 2014 +0100
@@ -35,7 +35,7 @@
     friend class TouchkeyKeyDivisionMappingFactory;
 public:
     enum {
-        kDetectionParameterYPosition,
+        kDetectionParameterYPosition = 1,
         kDetectionParameterNumberOfTouches,
         kDetectionParameterYPositionAndNumberOfTouches
     };
--- a/Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMapping.cpp	Sat Jun 21 23:32:33 2014 +0100
+++ b/Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMapping.cpp	Thu Aug 21 17:02:39 2014 +0100
@@ -23,6 +23,7 @@
 */
 
 #include "TouchkeyMultiFingerTriggerMapping.h"
+#include "TouchkeyMultiFingerTriggerMappingFactory.h"
 #include "../../TouchKeys/MidiOutputController.h"
 
 // Class constants
@@ -30,7 +31,13 @@
 const int TouchkeyMultiFingerTriggerMapping::kDefaultNumTouchesForTrigger = 2;
 const int TouchkeyMultiFingerTriggerMapping::kDefaultNumFramesForTrigger = 2;
 const int TouchkeyMultiFingerTriggerMapping::kDefaultNumConsecutiveTapsForTrigger = 1;
-const timestamp_diff_type TouchkeyMultiFingerTriggerMapping::kDefaultMaxTapSpacing = milliseconds_to_timestamp(500.0);
+const timestamp_diff_type TouchkeyMultiFingerTriggerMapping::kDefaultMaxTapSpacing = milliseconds_to_timestamp(300.0);
+const int TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOnAction = TouchkeyMultiFingerTriggerMapping::kActionNoteOn;
+const int TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOffAction = TouchkeyMultiFingerTriggerMapping::kActionNone;
+const int TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOnNoteNum = -1;
+const int TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOffNoteNum = -1;
+const int TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOnNoteVel =  -1;
+const int TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOffNoteVel = -1;
 
 // Main constructor takes references/pointers from objects which keep track
 // of touch location, continuous key position and the state detected from that
@@ -42,11 +49,31 @@
 : TouchkeyBaseMapping(keyboard, factory, noteNumber, touchBuffer, positionBuffer, positionTracker),
 numTouchesForTrigger_(kDefaultNumTouchesForTrigger), numFramesForTrigger_(kDefaultNumFramesForTrigger),
 numConsecutiveTapsForTrigger_(kDefaultNumConsecutiveTapsForTrigger), maxTapSpacing_(kDefaultMaxTapSpacing),
-needsMidiNoteOn_(true), pastSamples_(kDefaultFilterBufferLength)
+needsMidiNoteOn_(true), triggerOnAction_(kDefaultTriggerOnAction), triggerOffAction_(kDefaultTriggerOffAction),
+triggerOnNoteNum_(kDefaultTriggerOnNoteNum), triggerOffNoteNum_(kDefaultTriggerOffNoteNum),
+triggerOnNoteVel_(kDefaultTriggerOnNoteVel), triggerOffNoteVel_(kDefaultTriggerOffNoteVel),
+pastSamples_(kDefaultFilterBufferLength)
 {
     reset();
 }
 
+// Turn off mapping of data.
+void TouchkeyMultiFingerTriggerMapping::disengage(bool shouldDelete) {
+    // Send note off messages for anything currently on
+    std::set<std::pair<int, int> >::iterator it;
+    int port = static_cast<TouchkeyMultiFingerTriggerMappingFactory*>(factory_)->segment().outputPort();
+    
+    for(it = otherNotesOn_.begin(); it != otherNotesOn_.end(); ++it) {
+        int ch = it->first;
+        int note = it->second;
+        
+        keyboard_.midiOutputController()->sendNoteOn(port, ch, note, 0);
+    }
+    
+    otherNotesOn_.clear();
+    TouchkeyBaseMapping::disengage(shouldDelete);
+}
+
 // Reset state back to defaults
 void TouchkeyMultiFingerTriggerMapping::reset() {
     ScopedLock sl(sampleBufferMutex_);
@@ -68,6 +95,50 @@
     // Message is only sent at release; resend may not apply here.
 }
 
+void TouchkeyMultiFingerTriggerMapping::setTouchesForTrigger(int touches) {
+    numTouchesForTrigger_ = touches;
+}
+
+void TouchkeyMultiFingerTriggerMapping::setFramesForTrigger(int frames) {
+    numFramesForTrigger_ = frames;
+}
+
+void TouchkeyMultiFingerTriggerMapping::setConsecutiveTapsForTrigger(int taps) {
+    numConsecutiveTapsForTrigger_ = taps;
+}
+
+void TouchkeyMultiFingerTriggerMapping::setMaxTimeBetweenTapsForTrigger(timestamp_diff_type timeDiff) {
+    maxTapSpacing_ = timeDiff;
+}
+
+void TouchkeyMultiFingerTriggerMapping::setNeedsMidiNoteOn(bool needsMidi) {
+    needsMidiNoteOn_ = needsMidi;
+}
+
+void TouchkeyMultiFingerTriggerMapping::setTriggerOnAction(int action) {
+    triggerOnAction_ = action;
+}
+
+void TouchkeyMultiFingerTriggerMapping::setTriggerOffAction(int action) {
+    triggerOffAction_ = action;
+}
+
+void TouchkeyMultiFingerTriggerMapping::setTriggerOnNoteNumber(int note) {
+    triggerOnNoteNum_ = note;
+}
+
+void TouchkeyMultiFingerTriggerMapping::setTriggerOffNoteNumber(int note) {
+    triggerOffNoteNum_ = note;
+}
+
+void TouchkeyMultiFingerTriggerMapping::setTriggerOnNoteVelocity(int velocity) {
+    triggerOnNoteVel_ = velocity;
+}
+
+void TouchkeyMultiFingerTriggerMapping::setTriggerOffNoteVelocity(int velocity) {
+    triggerOffNoteVel_ = velocity;
+}
+
 // This method receives data from the touch buffer or possibly the continuous key angle (not used here)
 void TouchkeyMultiFingerTriggerMapping::triggerReceived(TriggerSource* who, timestamp_type timestamp) {
     if(needsMidiNoteOn_ && !noteIsOn_) {
@@ -106,8 +177,6 @@
                     else
                         tapsCount_ = 1;
                     
-                    std::cout << "Tap " << tapsCount_ << std::endl;
-                    
                     // Check if the right number of taps has elapsed
                     if(tapsCount_ >= numConsecutiveTapsForTrigger_ && !hasTriggered_) {
                         hasTriggered_ = true;
@@ -173,54 +242,87 @@
 }
 
 void TouchkeyMultiFingerTriggerMapping::generateTriggerOn(timestamp_type timestamp, timestamp_diff_type timeBetweenTaps, float distanceBetweenPoints) {
-    std::cout << "Trigger distance = " << distanceBetweenPoints << " timing = " << timeBetweenTaps << std::endl;
-    // KLUDGE
     if(!suspended_) {
-#if 0
-        if(distanceBetweenPoints > 0.35) {
-            //keyboard_.sendMessage("/touchkeys/pitchbend", "if", noteNumber_, 2.0, LO_ARGS_END);
+        if(triggerOnAction_ == kActionNoteOn ||
+           triggerOnAction_ == kActionNoteOff) {
+            // Send a MIDI note on message with given note number and velocity
+            int port = static_cast<TouchkeyMultiFingerTriggerMappingFactory*>(factory_)->segment().outputPort();
             int ch = keyboard_.key(noteNumber_)->midiChannel();
-            int vel = keyboard_.key(noteNumber_)->midiVelocity();
-            keyboard_.midiOutputController()->sendNoteOn(0, ch, noteNumber_ + 14, vel);
-            //keyboard_.midiOutputController()->sendNoteOff(0, ch, noteNumber_ + 12, vel);
+            int vel = triggerOnNoteVel_;
+            int note = triggerOnNoteNum_;
+            if(note < 0)    // note < 0 means current note
+                note = noteNumber_;
+            if(note < 128) {
+                if(triggerOnAction_ == kActionNoteOn) {
+                    // Can't send notes above 127...
+                    if(vel < 0)     // vel < 0 means same as current
+                        vel = keyboard_.key(noteNumber_)->midiVelocity();
+                    if(vel > 127)
+                        vel = 127;
+                    
+                    // Register that this note has been turned on
+                    if(note != noteNumber_)
+                        otherNotesOn_.insert(std::pair<int,int>(ch, note));
+                }
+                else {
+                    // Note off
+                    vel = 0;
+                    if(note != noteNumber_) {
+                        // Unregister this note if we are turning it off
+                        if(otherNotesOn_.count(std::pair<int,int>(ch, note)) > 0) {
+                            otherNotesOn_.erase(std::pair<int,int>(ch, note));
+                        }
+                    }
+                }
+                
+                keyboard_.midiOutputController()->sendNoteOn(port, ch, note, vel);
+            }
         }
-        else {
-            //keyboard_.sendMessage("/touchkeys/pitchbend", "if", noteNumber_, 1.0, LO_ARGS_END);
-            int ch = keyboard_.key(noteNumber_)->midiChannel();
-            int vel = keyboard_.key(noteNumber_)->midiVelocity();
-            keyboard_.midiOutputController()->sendNoteOn(0, ch, noteNumber_ + 13, vel);
-            //keyboard_.midiOutputController()->sendNoteOff(0, ch, noteNumber_ + 12, vel);
-        }
-#elif 0
-        int ch = keyboard_.key(noteNumber_)->midiChannel();
-        keyboard_.midiOutputController()->sendControlChange(0, ch, 73, 127);
-#else
-        keyboard_.midiOutputController()->sendNoteOn(0, keyboard_.key(noteNumber_)->midiChannel(), noteNumber_, 127);
-#endif
     }
 }
 
 void TouchkeyMultiFingerTriggerMapping::generateTriggerOff(timestamp_type timestamp) {
-    std::cout << "Trigger off\n";
     if(!suspended_) {
-#if 0
-        //eyboard_.sendMessage("/touchkeys/pitchbend", "if", noteNumber_, 0.0, LO_ARGS_END);
-        int ch = keyboard_.key(noteNumber_)->midiChannel();
-        int vel = keyboard_.key(noteNumber_)->midiVelocity();
-        keyboard_.midiOutputController()->sendNoteOn(0, ch, noteNumber_ + 12, vel);
-        //keyboard_.midiOutputController()->sendNoteOff(0, ch, noteNumber_ + 13, vel);
-        //keyboard_.midiOutputController()->sendNoteOff(0, ch, noteNumber_ + 14, vel);
-#elif 0
-        int ch = keyboard_.key(noteNumber_)->midiChannel();
-        keyboard_.midiOutputController()->sendControlChange(0, ch, 73, 0);
-#else
-        keyboard_.midiOutputController()->sendNoteOn(0, keyboard_.key(noteNumber_)->midiChannel(), noteNumber_, 127);
-#endif
+        if(triggerOffAction_ == kActionNoteOn ||
+           triggerOffAction_ == kActionNoteOff) {
+            // Send a MIDI note on message with given note number and velocity
+            int port = static_cast<TouchkeyMultiFingerTriggerMappingFactory*>(factory_)->segment().outputPort();
+            int ch = keyboard_.key(noteNumber_)->midiChannel();
+            int vel = triggerOffNoteVel_;
+            int note = triggerOffNoteNum_;
+            if(note < 0)    // note < 0 means current note
+                note = noteNumber_;
+            if(note < 128) {
+                if(triggerOffAction_ == kActionNoteOn) {
+                    // Can't send notes above 127...
+                    if(vel < 0)     // vel < 0 means same as current
+                        vel = keyboard_.key(noteNumber_)->midiVelocity();
+                    if(vel > 127)
+                        vel = 127;
+                    
+                    // Register that this note has been turned on
+                    if(note != noteNumber_)
+                        otherNotesOn_.insert(std::pair<int,int>(ch, note));
+                }
+                else {
+                    // Note off
+                    vel = 0;
+                    if(note != noteNumber_) {
+                        // Unregister this note if we are turning it off
+                        if(otherNotesOn_.count(std::pair<int,int>(ch, note)) > 0) {
+                            otherNotesOn_.erase(std::pair<int,int>(ch, note));
+                        }
+                    }
+                }
+                
+                keyboard_.midiOutputController()->sendNoteOn(port, ch, note, vel);
+            }
+        }
     }
 }
 
 // MIDI note-off message received
 void TouchkeyMultiFingerTriggerMapping::midiNoteOffReceived(int channel) {
-    int ch = keyboard_.key(noteNumber_)->midiChannel();
-    keyboard_.midiOutputController()->sendControlChange(0, ch, 73, 0);
+    // int ch = keyboard_.key(noteNumber_)->midiChannel();
+    // keyboard_.midiOutputController()->sendControlChange(0, ch, 73, 0);
 }
--- a/Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMapping.h	Sat Jun 21 23:32:33 2014 +0100
+++ b/Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMapping.h	Thu Aug 21 17:02:39 2014 +0100
@@ -26,11 +26,23 @@
 #define __TouchKeys__TouchkeyMultiFingerTriggerMapping__
 
 #include "../TouchkeyBaseMapping.h"
+#include <set>
+
+class TouchkeyMultiFingerTriggerMappingFactory;
 
 // This class handles the detection of finger motion specifically at
 // note release, which can be used to trigger specific release effects.
 
 class TouchkeyMultiFingerTriggerMapping : public TouchkeyBaseMapping {
+    friend class TouchkeyMultiFingerTriggerMappingFactory;
+public:
+    enum {
+        kActionNone = 1,
+        kActionNoteOn,
+        kActionNoteOff,
+        kActionMax
+    };
+    
 private:
     // Default values
     /*constexpr static const int kDefaultFilterBufferLength = 30;
@@ -43,7 +55,13 @@
     static const int kDefaultNumFramesForTrigger;
     static const int kDefaultNumConsecutiveTapsForTrigger;
     static const timestamp_diff_type kDefaultMaxTapSpacing;
-
+    static const int kDefaultTriggerOnAction;
+    static const int kDefaultTriggerOffAction;
+    static const int kDefaultTriggerOnNoteNum;
+    static const int kDefaultTriggerOffNoteNum;
+    static const int kDefaultTriggerOnNoteVel;
+    static const int kDefaultTriggerOffNoteVel;
+    
 public:
 	// ***** Constructors *****
 	
@@ -53,12 +71,28 @@
 	
     // ***** Modifiers *****
     
+    // Disable mappings from being sent
+    void disengage(bool shouldDelete = false);
+    
     // Reset the state back initial values
     void reset();
     
     // Resend the current state of all parameters
     void resend();
     
+    // Parameters for multi-finger trigger
+    void setTouchesForTrigger(int touches);
+    void setFramesForTrigger(int frames);
+    void setConsecutiveTapsForTrigger(int taps);
+    void setMaxTimeBetweenTapsForTrigger(timestamp_diff_type timeDiff);
+    void setNeedsMidiNoteOn(bool needsMidi);
+    void setTriggerOnAction(int action);
+    void setTriggerOffAction(int action);
+    void setTriggerOnNoteNumber(int note);
+    void setTriggerOffNoteNumber(int note);
+    void setTriggerOnNoteVelocity(int velocity);
+    void setTriggerOffNoteVelocity(int velocity);
+    
 	// ***** Evaluators *****
     
     // This method receives triggers whenever events occur in the touch data or the
@@ -89,6 +123,9 @@
     int numConsecutiveTapsForTrigger_;          // How many taps with this number of touches are needed to trigger
     timestamp_diff_type maxTapSpacing_;         // How far apart the taps can come and be considered a multi-tap gesture
     bool needsMidiNoteOn_;                      // Whether the MIDI note has to be on for this gesture to trigger
+    int triggerOnAction_, triggerOffAction_;    // Actions to take on trigger on/off
+    int triggerOnNoteNum_, triggerOffNoteNum_;  // Which notes to send if a note is being sent
+    int triggerOnNoteVel_, triggerOffNoteVel_;  // Velocity to send if a note is being sent
     
     int lastNumActiveTouches_;                  // How many touches were active before
     float lastActiveTouchLocations_[3];         // Where (Y coord.) the active touches were last frame
@@ -98,6 +135,8 @@
     timestamp_type lastTapStartTimestamp_;      // When the last tap ended
     bool hasTriggered_;                         // Whether we've generated a trigger
     
+    std::set<std::pair<int, int> > otherNotesOn_; // Which other notes are on as a result of triggers?
+    
     Node<KeyTouchFrame> pastSamples_;           // Locations of touch
     CriticalSection sampleBufferMutex_;         // Mutex to protect threaded access to sample buffer
 };
--- a/Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingFactory.cpp	Sat Jun 21 23:32:33 2014 +0100
+++ b/Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingFactory.cpp	Thu Aug 21 17:02:39 2014 +0100
@@ -23,6 +23,89 @@
 */
 
 #include "TouchkeyMultiFingerTriggerMappingFactory.h"
+#include "TouchkeyMultiFingerTriggerMappingShortEditor.h"
+
+TouchkeyMultiFingerTriggerMappingFactory::TouchkeyMultiFingerTriggerMappingFactory(PianoKeyboard &keyboard, MidiKeyboardSegment& segment)
+: TouchkeyBaseMappingFactory<TouchkeyMultiFingerTriggerMapping>(keyboard, segment),
+numTouchesForTrigger_(TouchkeyMultiFingerTriggerMapping::kDefaultNumTouchesForTrigger),
+numFramesForTrigger_(TouchkeyMultiFingerTriggerMapping::kDefaultNumFramesForTrigger),
+numConsecutiveTapsForTrigger_(TouchkeyMultiFingerTriggerMapping::kDefaultNumConsecutiveTapsForTrigger),
+maxTapSpacing_(TouchkeyMultiFingerTriggerMapping::kDefaultMaxTapSpacing),
+needsMidiNoteOn_(true),
+triggerOnAction_(TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOnAction),
+triggerOffAction_(TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOffAction),
+triggerOnNoteNum_(TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOnNoteNum),
+triggerOffNoteNum_(TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOffNoteNum),
+triggerOnNoteVel_(TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOnNoteVel),
+triggerOffNoteVel_(TouchkeyMultiFingerTriggerMapping::kDefaultTriggerOffNoteVel)
+{
+    
+}
+
+void TouchkeyMultiFingerTriggerMappingFactory::setTouchesForTrigger(int touches) {
+    if(touches < 1)
+        touches = 1;
+    if(touches > 3)
+        touches = 3;
+    numTouchesForTrigger_ = touches;
+}
+
+void TouchkeyMultiFingerTriggerMappingFactory::setFramesForTrigger(int frames) {
+    if(frames < 1)
+        frames = 1;
+    numFramesForTrigger_ = frames;
+}
+
+void TouchkeyMultiFingerTriggerMappingFactory::setConsecutiveTapsForTrigger(int taps) {
+    if(taps < 1)
+        taps = 1;
+    numConsecutiveTapsForTrigger_ = taps;
+}
+
+void TouchkeyMultiFingerTriggerMappingFactory::setMaxTimeBetweenTapsForTrigger(timestamp_diff_type timeDiff) {
+    if(timeDiff < 0)
+        timeDiff = 0;
+    maxTapSpacing_ = timeDiff;
+}
+
+void TouchkeyMultiFingerTriggerMappingFactory::setNeedsMidiNoteOn(bool needsMidi) {
+    needsMidiNoteOn_ = needsMidi;
+}
+
+void TouchkeyMultiFingerTriggerMappingFactory::setTriggerOnAction(int action) {
+    if(action > 0 && action < TouchkeyMultiFingerTriggerMapping::kActionMax)
+        triggerOnAction_ = action;
+}
+
+void TouchkeyMultiFingerTriggerMappingFactory::setTriggerOffAction(int action) {
+    if(action > 0 && action < TouchkeyMultiFingerTriggerMapping::kActionMax)
+        triggerOffAction_ = action;
+}
+
+void TouchkeyMultiFingerTriggerMappingFactory::setTriggerOnNoteNumber(int note) {
+    triggerOnNoteNum_ = note;
+}
+
+void TouchkeyMultiFingerTriggerMappingFactory::setTriggerOffNoteNumber(int note) {
+    triggerOffNoteNum_ = note;
+}
+
+void TouchkeyMultiFingerTriggerMappingFactory::setTriggerOnNoteVelocity(int velocity) {
+    if(velocity > 127)
+        velocity = 127;
+    triggerOnNoteVel_ = velocity;
+}
+
+void TouchkeyMultiFingerTriggerMappingFactory::setTriggerOffNoteVelocity(int velocity) {
+    if(velocity > 127)
+        velocity = 127;
+    triggerOffNoteVel_ = velocity;
+}
+
+// ***** GUI Support *****
+MappingEditorComponent* TouchkeyMultiFingerTriggerMappingFactory::createBasicEditor() {
+    return new TouchkeyMultiFingerTriggerMappingShortEditor(*this);
+}
 
 // ****** Preset Save/Load ******
 XmlElement* TouchkeyMultiFingerTriggerMappingFactory::getPreset() {
@@ -30,7 +113,17 @@
     
     storeCommonProperties(properties);
     
-    // No properties for now
+    properties.setValue("numTouchesForTrigger", numTouchesForTrigger_);
+    properties.setValue("numFramesForTrigger", numFramesForTrigger_);
+    properties.setValue("numConsecutiveTapsForTrigger", numConsecutiveTapsForTrigger_);
+    properties.setValue("maxTapSpacing", maxTapSpacing_);
+    properties.setValue("needsMidiNoteOn", needsMidiNoteOn_);
+    properties.setValue("triggerOnAction", triggerOnAction_);
+    properties.setValue("triggerOffAction", triggerOffAction_);
+    properties.setValue("triggerOnNoteNum", triggerOnNoteNum_);
+    properties.setValue("triggerOffNoteNum", triggerOffNoteNum_);
+    properties.setValue("triggerOnNoteVel", triggerOnNoteVel_);
+    properties.setValue("triggerOffNoteVel", triggerOffNoteVel_);
     
     XmlElement* preset = properties.createXml("MappingFactory");
     preset->setAttribute("type", "MultiFingerTrigger");
@@ -48,7 +141,46 @@
     if(!loadCommonProperties(properties))
         return false;
     
-    // Nothing specific to do for now
+    // Load specific properties
+    if(properties.containsKey("numTouchesForTrigger"))
+        numTouchesForTrigger_ = properties.getIntValue("numTouchesForTrigger");
+    if(properties.containsKey("numFramesForTrigger"))
+        numFramesForTrigger_ = properties.getIntValue("numFramesForTrigger");
+    if(properties.containsKey("numConsecutiveTapsForTrigger"))
+        numConsecutiveTapsForTrigger_ = properties.getIntValue("numConsecutiveTapsForTrigger");
+    if(properties.containsKey("maxTapSpacing"))
+        maxTapSpacing_ = properties.getDoubleValue("maxTapSpacing");
+    if(properties.containsKey("needsMidiNoteOn"))
+        needsMidiNoteOn_ = properties.getBoolValue("needsMidiNoteOn");
+    if(properties.containsKey("triggerOnAction"))
+        triggerOnAction_ = properties.getBoolValue("triggerOnAction");
+    if(properties.containsKey("triggerOffAction"))
+        triggerOffAction_ = properties.getBoolValue("triggerOffAction");
+    if(properties.containsKey("triggerOnNoteNum"))
+        triggerOnNoteNum_ = properties.getBoolValue("triggerOnNoteNum");
+    if(properties.containsKey("triggerOffNoteNum"))
+        triggerOffNoteNum_ = properties.getBoolValue("triggerOffNoteNum");
+    if(properties.containsKey("triggerOnNoteVel"))
+        triggerOnNoteVel_ = properties.getBoolValue("triggerOnNoteVel");
+    if(properties.containsKey("triggerOffNoteVel"))
+        triggerOffNoteVel_ = properties.getBoolValue("triggerOffNoteVel");
     
     return true;
 }
+
+// ***** Private Methods *****
+
+// Set the initial parameters for a new mapping
+void TouchkeyMultiFingerTriggerMappingFactory::initializeMappingParameters(int noteNumber, TouchkeyMultiFingerTriggerMapping *mapping) {
+    mapping->setTouchesForTrigger(numTouchesForTrigger_);
+    mapping->setFramesForTrigger(numFramesForTrigger_);
+    mapping->setConsecutiveTapsForTrigger(numConsecutiveTapsForTrigger_);
+    mapping->setMaxTimeBetweenTapsForTrigger(maxTapSpacing_);
+    mapping->setNeedsMidiNoteOn(needsMidiNoteOn_);
+    mapping->setTriggerOnAction(triggerOnAction_);
+    mapping->setTriggerOffAction(triggerOffAction_);
+    mapping->setTriggerOnNoteNumber(triggerOnNoteNum_);
+    mapping->setTriggerOffNoteNumber(triggerOffNoteNum_);
+    mapping->setTriggerOnNoteVelocity(triggerOnNoteVel_);
+    mapping->setTriggerOffNoteVelocity(triggerOffNoteVel_);
+}
--- a/Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingFactory.h	Sat Jun 21 23:32:33 2014 +0100
+++ b/Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingFactory.h	Thu Aug 21 17:02:39 2014 +0100
@@ -36,8 +36,7 @@
     // ***** Constructor *****
     
 	// Default constructor, containing a reference to the PianoKeyboard class.
-    TouchkeyMultiFingerTriggerMappingFactory(PianoKeyboard &keyboard, MidiKeyboardSegment& segment)
-    : TouchkeyBaseMappingFactory<TouchkeyMultiFingerTriggerMapping>(keyboard, segment) {}
+    TouchkeyMultiFingerTriggerMappingFactory(PianoKeyboard &keyboard, MidiKeyboardSegment& segment);
 	
     // ***** Destructor *****
     
@@ -47,9 +46,56 @@
     
     virtual const std::string factoryTypeName() { return "Multi-Finger\nTrigger"; }
     
+    // ***** Class-Specific Methods *****
+    
+    // Parameters for multi-finger trigger
+    int getTouchesForTrigger() { return numTouchesForTrigger_; }
+    int getFramesForTrigger() { return numFramesForTrigger_; }
+    int getConsecutiveTapsForTrigger() { return numConsecutiveTapsForTrigger_; }
+    timestamp_diff_type getMaxTimeBetweenTapsForTrigger() { return maxTapSpacing_; }
+    bool getNeedsMidiNoteOn() { return needsMidiNoteOn_; }
+    int getTriggerOnAction() { return triggerOnAction_; }
+    int getTriggerOffAction()  { return triggerOffAction_; }
+    int getTriggerOnNoteNumber() { return triggerOnNoteNum_; }
+    int getTriggerOffNoteNumber()  { return triggerOffNoteNum_; }
+    int getTriggerOnNoteVelocity()  { return triggerOnNoteVel_; }
+    int getTriggerOffNoteVelocity() { return triggerOffNoteVel_; }
+    
+    void setTouchesForTrigger(int touches);
+    void setFramesForTrigger(int frames);
+    void setConsecutiveTapsForTrigger(int taps);
+    void setMaxTimeBetweenTapsForTrigger(timestamp_diff_type timeDiff);
+    void setNeedsMidiNoteOn(bool needsMidi);
+    void setTriggerOnAction(int action);
+    void setTriggerOffAction(int action);
+    void setTriggerOnNoteNumber(int note);
+    void setTriggerOffNoteNumber(int note);
+    void setTriggerOnNoteVelocity(int velocity);
+    void setTriggerOffNoteVelocity(int velocity);
+    
+    // ***** GUI Support *****
+    bool hasBasicEditor() { return true; }
+    MappingEditorComponent* createBasicEditor();
+    bool hasExtendedEditor() { return false; }
+    MappingEditorComponent* createExtendedEditor() { return nullptr; }
+    
     // ****** Preset Save/Load ******
     XmlElement* getPreset();
     bool loadPreset(XmlElement const* preset);
+    
+private:
+    // ***** Private Methods *****
+    void initializeMappingParameters(int noteNumber, TouchkeyMultiFingerTriggerMapping *mapping);
+
+    // Parameters
+    int numTouchesForTrigger_;                  // How many touches are needed for a trigger
+    int numFramesForTrigger_;                   // How many consecutive frames with these touches are needed to trigger
+    int numConsecutiveTapsForTrigger_;          // How many taps with this number of touches are needed to trigger
+    timestamp_diff_type maxTapSpacing_;         // How far apart the taps can come and be considered a multi-tap gesture
+    bool needsMidiNoteOn_;                      // Whether the MIDI note has to be on for this gesture to trigger
+    int triggerOnAction_, triggerOffAction_;    // Actions to take on trigger on/off
+    int triggerOnNoteNum_, triggerOffNoteNum_;  // Which notes to send if a note is being sent
+    int triggerOnNoteVel_, triggerOffNoteVel_;  // Velocity to send if a note is being sent
 };
 
 #endif /* defined(__TouchKeys__TouchkeyMultiFingerTriggerMappingFactory__) */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingShortEditor.cpp	Thu Aug 21 17:02:39 2014 +0100
@@ -0,0 +1,275 @@
+/*
+  ==============================================================================
+
+  This is an automatically generated GUI class created by the Introjucer!
+
+  Be careful when adding custom code to these files, as only the code within
+  the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
+  and re-saved.
+
+  Created with Introjucer version: 3.1.0
+
+  ------------------------------------------------------------------------------
+
+  The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
+  Copyright 2004-13 by Raw Material Software Ltd.
+
+  ==============================================================================
+*/
+
+//[Headers] You can add your own extra header files here...
+//[/Headers]
+
+#include "TouchkeyMultiFingerTriggerMappingShortEditor.h"
+
+
+//[MiscUserDefs] You can add your own user definitions and misc code here...
+const int TouchkeyMultiFingerTriggerMappingShortEditor::kNoteSame = 256;
+const int TouchkeyMultiFingerTriggerMappingShortEditor::kNoteOffset = 1;
+//[/MiscUserDefs]
+
+//==============================================================================
+TouchkeyMultiFingerTriggerMappingShortEditor::TouchkeyMultiFingerTriggerMappingShortEditor (TouchkeyMultiFingerTriggerMappingFactory& factory)
+    : factory_(factory)
+{
+    addAndMakeVisible (controlLabel = new Label ("control label",
+                                                 "Touches:"));
+    controlLabel->setFont (Font (15.00f, Font::plain));
+    controlLabel->setJustificationType (Justification::centredRight);
+    controlLabel->setEditable (false, false, false);
+    controlLabel->setColour (TextEditor::textColourId, Colours::black);
+    controlLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
+
+    addAndMakeVisible (touchesComboBox = new ComboBox ("control combo box"));
+    touchesComboBox->setEditableText (false);
+    touchesComboBox->setJustificationType (Justification::centredLeft);
+    touchesComboBox->setTextWhenNothingSelected (String::empty);
+    touchesComboBox->setTextWhenNoChoicesAvailable ("(no choices)");
+    touchesComboBox->addListener (this);
+
+    addAndMakeVisible (controlLabel2 = new Label ("control label",
+                                                  "Repeat Taps:"));
+    controlLabel2->setFont (Font (15.00f, Font::plain));
+    controlLabel2->setJustificationType (Justification::centredLeft);
+    controlLabel2->setEditable (false, false, false);
+    controlLabel2->setColour (TextEditor::textColourId, Colours::black);
+    controlLabel2->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
+
+    addAndMakeVisible (tapsComboBox = new ComboBox ("control combo box"));
+    tapsComboBox->setEditableText (false);
+    tapsComboBox->setJustificationType (Justification::centredLeft);
+    tapsComboBox->setTextWhenNothingSelected (String::empty);
+    tapsComboBox->setTextWhenNoChoicesAvailable ("(no choices)");
+    tapsComboBox->addListener (this);
+
+    addAndMakeVisible (controlLabel3 = new Label ("control label",
+                                                  "Note:"));
+    controlLabel3->setFont (Font (15.00f, Font::plain));
+    controlLabel3->setJustificationType (Justification::centredRight);
+    controlLabel3->setEditable (false, false, false);
+    controlLabel3->setColour (TextEditor::textColourId, Colours::black);
+    controlLabel3->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
+
+    addAndMakeVisible (noteComboBox = new ComboBox ("control combo box"));
+    noteComboBox->setEditableText (false);
+    noteComboBox->setJustificationType (Justification::centredLeft);
+    noteComboBox->setTextWhenNothingSelected (String::empty);
+    noteComboBox->setTextWhenNoChoicesAvailable ("(no choices)");
+    noteComboBox->addListener (this);
+
+    addAndMakeVisible (sendOnReleaseButton = new ToggleButton ("new toggle button"));
+    sendOnReleaseButton->setButtonText ("Also send on release");
+    sendOnReleaseButton->addListener (this);
+
+
+    //[UserPreSize]
+    for(int i = 1; i <= 3; i++) {
+        touchesComboBox->addItem(String(i), i);
+    }
+    for(int i = 1; i <= 5; i++) {
+        tapsComboBox->addItem(String(i), i);
+    }
+    noteComboBox->addItem("Same", kNoteSame);
+    for(int i = 0; i <= 127; i++) {
+        noteComboBox->addItem(String(i), i + kNoteOffset);
+    }
+    //[/UserPreSize]
+
+    setSize (328, 71);
+
+
+    //[Constructor] You can add your own custom stuff here..
+    //[/Constructor]
+}
+
+TouchkeyMultiFingerTriggerMappingShortEditor::~TouchkeyMultiFingerTriggerMappingShortEditor()
+{
+    //[Destructor_pre]. You can add your own custom destruction code here..
+    //[/Destructor_pre]
+
+    controlLabel = nullptr;
+    touchesComboBox = nullptr;
+    controlLabel2 = nullptr;
+    tapsComboBox = nullptr;
+    controlLabel3 = nullptr;
+    noteComboBox = nullptr;
+    sendOnReleaseButton = nullptr;
+
+
+    //[Destructor]. You can add your own custom destruction code here..
+    //[/Destructor]
+}
+
+//==============================================================================
+void TouchkeyMultiFingerTriggerMappingShortEditor::paint (Graphics& g)
+{
+    //[UserPrePaint] Add your own custom painting code here..
+    //[/UserPrePaint]
+
+    g.fillAll (Colours::white);
+
+    //[UserPaint] Add your own custom painting code here..
+    //[/UserPaint]
+}
+
+void TouchkeyMultiFingerTriggerMappingShortEditor::resized()
+{
+    controlLabel->setBounds (8, 8, 64, 24);
+    touchesComboBox->setBounds (72, 8, 80, 24);
+    controlLabel2->setBounds (160, 8, 80, 24);
+    tapsComboBox->setBounds (240, 8, 80, 24);
+    controlLabel3->setBounds (8, 40, 64, 24);
+    noteComboBox->setBounds (72, 40, 80, 24);
+    sendOnReleaseButton->setBounds (168, 40, 152, 24);
+    //[UserResized] Add your own custom resize handling here..
+    //[/UserResized]
+}
+
+void TouchkeyMultiFingerTriggerMappingShortEditor::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
+{
+    //[UsercomboBoxChanged_Pre]
+    //[/UsercomboBoxChanged_Pre]
+
+    if (comboBoxThatHasChanged == touchesComboBox)
+    {
+        //[UserComboBoxCode_touchesComboBox] -- add your combo box handling code here..
+        factory_.setTouchesForTrigger(touchesComboBox->getSelectedId());
+        //[/UserComboBoxCode_touchesComboBox]
+    }
+    else if (comboBoxThatHasChanged == tapsComboBox)
+    {
+        //[UserComboBoxCode_tapsComboBox] -- add your combo box handling code here..
+        factory_.setConsecutiveTapsForTrigger(tapsComboBox->getSelectedId());
+        //[/UserComboBoxCode_tapsComboBox]
+    }
+    else if (comboBoxThatHasChanged == noteComboBox)
+    {
+        //[UserComboBoxCode_noteComboBox] -- add your combo box handling code here..
+        int note = noteComboBox->getSelectedId();
+        if(note == kNoteSame)
+            note = -1;
+        else
+            note -= kNoteOffset;
+        factory_.setTriggerOnNoteNumber(note);
+        factory_.setTriggerOffNoteNumber(note);
+        //[/UserComboBoxCode_noteComboBox]
+    }
+
+    //[UsercomboBoxChanged_Post]
+    //[/UsercomboBoxChanged_Post]
+}
+
+void TouchkeyMultiFingerTriggerMappingShortEditor::buttonClicked (Button* buttonThatWasClicked)
+{
+    //[UserbuttonClicked_Pre]
+    //[/UserbuttonClicked_Pre]
+
+    if (buttonThatWasClicked == sendOnReleaseButton)
+    {
+        //[UserButtonCode_sendOnReleaseButton] -- add your button handler code here..
+        if(sendOnReleaseButton->getToggleState()) {
+            factory_.setTriggerOffAction(TouchkeyMultiFingerTriggerMapping::kActionNoteOn);
+        }
+        else {
+            factory_.setTriggerOffAction(TouchkeyMultiFingerTriggerMapping::kActionNone);
+        }
+        //[/UserButtonCode_sendOnReleaseButton]
+    }
+
+    //[UserbuttonClicked_Post]
+    //[/UserbuttonClicked_Post]
+}
+
+
+
+//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
+void TouchkeyMultiFingerTriggerMappingShortEditor::synchronize() {
+    touchesComboBox->setSelectedId(factory_.getTouchesForTrigger(), dontSendNotification);
+    tapsComboBox->setSelectedId(factory_.getConsecutiveTapsForTrigger(), dontSendNotification);
+    
+    int note = factory_.getTriggerOnNoteNumber();
+    if(note < 0)
+        noteComboBox->setSelectedId(kNoteSame, dontSendNotification);
+    else
+        noteComboBox->setSelectedId(note + kNoteOffset, dontSendNotification);
+    
+    if(factory_.getTriggerOffAction() == TouchkeyMultiFingerTriggerMapping::kActionNoteOn)
+        sendOnReleaseButton->setToggleState(true, dontSendNotification);
+    else
+        sendOnReleaseButton->setToggleState(false, dontSendNotification);
+}
+//[/MiscUserCode]
+
+
+//==============================================================================
+#if 0
+/*  -- Introjucer information section --
+
+    This is where the Introjucer stores the metadata that describe this GUI layout, so
+    make changes in here at your peril!
+
+BEGIN_JUCER_METADATA
+
+<JUCER_COMPONENT documentType="Component" className="TouchkeyMultiFingerTriggerMappingShortEditor"
+                 componentName="" parentClasses="public MappingEditorComponent, public TextEditor::Listener"
+                 constructorParams="TouchkeyMultiFingerTriggerMappingFactory&amp; factory"
+                 variableInitialisers="factory_(factory)" snapPixels="8" snapActive="1"
+                 snapShown="1" overlayOpacity="0.330" fixedSize="1" initialWidth="328"
+                 initialHeight="71">
+  <BACKGROUND backgroundColour="ffffffff"/>
+  <LABEL name="control label" id="f953b12999632418" memberName="controlLabel"
+         virtualName="" explicitFocusOrder="0" pos="8 8 64 24" edTextCol="ff000000"
+         edBkgCol="0" labelText="Touches:" editableSingleClick="0" editableDoubleClick="0"
+         focusDiscardsChanges="0" fontname="Default font" fontsize="15"
+         bold="0" italic="0" justification="34"/>
+  <COMBOBOX name="control combo box" id="f1c84bb5fd2730fb" memberName="touchesComboBox"
+            virtualName="" explicitFocusOrder="0" pos="72 8 80 24" editable="0"
+            layout="33" items="" textWhenNonSelected="" textWhenNoItems="(no choices)"/>
+  <LABEL name="control label" id="e3b829a3e4774248" memberName="controlLabel2"
+         virtualName="" explicitFocusOrder="0" pos="160 8 80 24" edTextCol="ff000000"
+         edBkgCol="0" labelText="Repeat Taps:" editableSingleClick="0"
+         editableDoubleClick="0" focusDiscardsChanges="0" fontname="Default font"
+         fontsize="15" bold="0" italic="0" justification="33"/>
+  <COMBOBOX name="control combo box" id="26848818ea1ea5ea" memberName="tapsComboBox"
+            virtualName="" explicitFocusOrder="0" pos="240 8 80 24" editable="0"
+            layout="33" items="" textWhenNonSelected="" textWhenNoItems="(no choices)"/>
+  <LABEL name="control label" id="858bbbef4bfb2c55" memberName="controlLabel3"
+         virtualName="" explicitFocusOrder="0" pos="8 40 64 24" edTextCol="ff000000"
+         edBkgCol="0" labelText="Note:" editableSingleClick="0" editableDoubleClick="0"
+         focusDiscardsChanges="0" fontname="Default font" fontsize="15"
+         bold="0" italic="0" justification="34"/>
+  <COMBOBOX name="control combo box" id="cb809b358724b54b" memberName="noteComboBox"
+            virtualName="" explicitFocusOrder="0" pos="72 40 80 24" editable="0"
+            layout="33" items="" textWhenNonSelected="" textWhenNoItems="(no choices)"/>
+  <TOGGLEBUTTON name="new toggle button" id="f75c92be72563883" memberName="sendOnReleaseButton"
+                virtualName="" explicitFocusOrder="0" pos="168 40 152 24" buttonText="Also send on release"
+                connectedEdges="0" needsCallback="1" radioGroupId="0" state="0"/>
+</JUCER_COMPONENT>
+
+END_JUCER_METADATA
+*/
+#endif
+
+
+//[EndFile] You can add extra defines here...
+//[/EndFile]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingShortEditor.h	Thu Aug 21 17:02:39 2014 +0100
@@ -0,0 +1,86 @@
+/*
+  ==============================================================================
+
+  This is an automatically generated GUI class created by the Introjucer!
+
+  Be careful when adding custom code to these files, as only the code within
+  the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
+  and re-saved.
+
+  Created with Introjucer version: 3.1.0
+
+  ------------------------------------------------------------------------------
+
+  The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
+  Copyright 2004-13 by Raw Material Software Ltd.
+
+  ==============================================================================
+*/
+
+#ifndef __JUCE_HEADER_575F04D6673EEA8A__
+#define __JUCE_HEADER_575F04D6673EEA8A__
+
+//[Headers]     -- You can add your own extra header files here --
+#include "JuceHeader.h"
+#include "TouchkeyMultiFingerTriggerMappingFactory.h"
+//[/Headers]
+
+
+
+//==============================================================================
+/**
+                                                                    //[Comments]
+    An auto-generated component, created by the Introjucer.
+
+    Describe your class and how it works here!
+                                                                    //[/Comments]
+*/
+class TouchkeyMultiFingerTriggerMappingShortEditor  : public MappingEditorComponent,
+                                                      public TextEditor::Listener,
+                                                      public ComboBoxListener,
+                                                      public ButtonListener
+{
+private:
+    static const int kNoteSame;
+    static const int kNoteOffset;
+    
+public:
+    //==============================================================================
+    TouchkeyMultiFingerTriggerMappingShortEditor (TouchkeyMultiFingerTriggerMappingFactory& factory);
+    ~TouchkeyMultiFingerTriggerMappingShortEditor();
+
+    //==============================================================================
+    //[UserMethods]     -- You can add your own custom methods in this section.
+    void synchronize();
+    //[/UserMethods]
+
+    void paint (Graphics& g);
+    void resized();
+    void comboBoxChanged (ComboBox* comboBoxThatHasChanged);
+    void buttonClicked (Button* buttonThatWasClicked);
+
+
+
+private:
+    //[UserVariables]   -- You can add your own custom variables in this section.
+    TouchkeyMultiFingerTriggerMappingFactory& factory_;
+    //[/UserVariables]
+
+    //==============================================================================
+    ScopedPointer<Label> controlLabel;
+    ScopedPointer<ComboBox> touchesComboBox;
+    ScopedPointer<Label> controlLabel2;
+    ScopedPointer<ComboBox> tapsComboBox;
+    ScopedPointer<Label> controlLabel3;
+    ScopedPointer<ComboBox> noteComboBox;
+    ScopedPointer<ToggleButton> sendOnReleaseButton;
+
+
+    //==============================================================================
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TouchkeyMultiFingerTriggerMappingShortEditor)
+};
+
+//[EndFile] You can add extra defines here...
+//[/EndFile]
+
+#endif   // __JUCE_HEADER_575F04D6673EEA8A__
--- a/Source/TouchKeys/MidiKeyboardSegment.cpp	Sat Jun 21 23:32:33 2014 +0100
+++ b/Source/TouchKeys/MidiKeyboardSegment.cpp	Thu Aug 21 17:02:39 2014 +0100
@@ -608,7 +608,17 @@
     outputPortNumber_ = properties.getIntValue("outputPort");
     if(!properties.containsKey("mode"))
         return false;
-    mode_ = properties.getIntValue("mode");
+    int mode = properties.getIntValue("mode");
+    // Setting the mode affects a few other variables so use the
+    // functions rather than setting mode_ directly
+    if(mode == ModePassThrough)
+        setModePassThrough();
+    else if(mode == ModeMonophonic)
+        setModeMonophonic();
+    else if(mode == ModePolyphonic)
+        setModePolyphonic();
+    else // Off or unknown
+        setModeOff();
     if(!properties.containsKey("channelMask"))
         return false;
     channelMask_ = properties.getIntValue("channelMask");
--- a/TouchKeys.jucer	Sat Jun 21 23:32:33 2014 +0100
+++ b/TouchKeys.jucer	Thu Aug 21 17:02:39 2014 +0100
@@ -90,6 +90,10 @@
                 resource="0" file="Source/Mappings/OnsetAngle/TouchkeyOnsetAngleMappingFactory.h"/>
         </GROUP>
         <GROUP id="{03E557AC-E333-5406-F3E0-F4A2F6D0E90B}" name="MultiFingerTrigger">
+          <FILE id="eBw76c" name="TouchkeyMultiFingerTriggerMappingShortEditor.cpp"
+                compile="1" resource="0" file="Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingShortEditor.cpp"/>
+          <FILE id="otOw9f" name="TouchkeyMultiFingerTriggerMappingShortEditor.h"
+                compile="0" resource="0" file="Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMappingShortEditor.h"/>
           <FILE id="DKcOhm" name="TouchkeyMultiFingerTriggerMapping.cpp" compile="1"
                 resource="0" file="Source/Mappings/MultiFingerTrigger/TouchkeyMultiFingerTriggerMapping.cpp"/>
           <FILE id="rYM1Os" name="TouchkeyMultiFingerTriggerMapping.h" compile="0"