Prevent multiple tempo / meter changes being inserted at the same point
[ardour.git] / libs / surfaces / generic_midi / generic_midi_control_protocol.cc
index 51c1b119eb0fa41aaddc37214cd0ec4c2be9e590..62cc93263befc29d956433458471c5e6c08917f5 100644 (file)
 #include "ardour/session.h"
 #include "ardour/route.h"
 #include "ardour/midi_ui.h"
+#include "ardour/rc_configuration.h"
 
 #include "generic_midi_control_protocol.h"
 #include "midicontrollable.h"
 #include "midifunction.h"
+#include "midiaction.h"
 
 using namespace ARDOUR;
 using namespace PBD;
@@ -61,10 +63,12 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
           the name is defined in ardour.rc which is likely not internationalized.
        */
        
-       _port = mm->port (X_("control"));
+       _port = mm->port (Config->get_midi_port_name());
 
        if (_port == 0) {
-               error << _("no MIDI port named \"control\" exists - generic MIDI control disabled") << endmsg;
+               error << string_compose (_("no MIDI port named \"%1\" exists - generic MIDI control disabled"), 
+                                         Config->get_midi_port_name()) 
+                      << endmsg;
                throw failed_constructor();
        }
 
@@ -82,8 +86,8 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
        Controllable::CreateBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::create_binding, this, _1, _2, _3));
        Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1));
 
-       Session::SendFeedback.connect (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
-       Route::RemoteControlIDChange.connect (*this, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context());
+       Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
+       Route::RemoteControlIDChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context());
 
        reload_maps ();
 }
@@ -190,6 +194,11 @@ GenericMidiControlProtocol::drop_all ()
                delete *i;
        }
        functions.clear ();
+
+       for (MIDIActions::iterator i = actions.begin(); i != actions.end(); ++i) {
+               delete *i;
+       }
+       actions.clear ();
 }
 
 void
@@ -308,7 +317,7 @@ GenericMidiControlProtocol::start_learning (Controllable* c)
        MIDIControllable* mc = 0;
 
        for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
-               if ((*i)->get_controllable()->id() == c->id()) {
+               if ((*i)->get_controllable() && ((*i)->get_controllable()->id() == c->id())) {
                        mc = *i;
                        break;
                }
@@ -388,12 +397,14 @@ GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
        if (control != 0) {
                Glib::Mutex::Lock lm2 (controllables_lock);
                
-               for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
+               for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
                        MIDIControllable* existingBinding = (*iter);
                        
                        if (control == (existingBinding->get_controllable())) {
                                delete existingBinding;
-                               controllables.erase (iter);
+                               iter = controllables.erase (iter);
+                       } else {
+                               ++iter;
                        }
                        
                }
@@ -414,7 +425,7 @@ GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos,
 
                // Remove any old binding for this midi channel/type/value pair
                // Note:  can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
-               for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
+               for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
                        MIDIControllable* existingBinding = (*iter);
                        
                        if ((existingBinding->get_control_channel() & 0xf ) == channel &&
@@ -422,7 +433,9 @@ GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos,
                            (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
                                
                                delete existingBinding;
-                               controllables.erase (iter);
+                               iter = controllables.erase (iter);
+                       } else {
+                               ++iter;
                        }
                        
                }
@@ -633,7 +646,14 @@ GenericMidiControlProtocol::load_bindings (const string& xmlpath)
                                if ((mf = create_function (*child)) != 0) {
                                        functions.push_back (mf);
                                }
-                       }
+
+                       } else if (child->property ("action")) {
+                                MIDIAction* ma;
+
+                               if ((ma = create_action (*child)) != 0) {
+                                       actions.push_back (ma);
+                               }
+                        }
                }
        }
        
@@ -704,15 +724,12 @@ GenericMidiControlProtocol::create_binding (const XMLNode& node)
 
        mc->bind_midi (channel, ev, detail);
 
-       cerr << "New MC with URI " << uri << " on channel " << (int) channel << " detail = " << (int) detail << endl;
-
        return mc;
 }
 
 void
 GenericMidiControlProtocol::reset_controllables ()
 {
-       cerr << "GM::RC\n";
        Glib::Mutex::Lock lm2 (controllables_lock);
        
        for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
@@ -740,8 +757,8 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
        MIDI::channel_t channel = 0;
        string uri;
        MIDI::eventType ev;
-       MIDI::byte* sysex = 0;
-       uint32_t sysex_size = 0;
+       MIDI::byte* data = 0;
+       uint32_t data_size = 0;
 
        if ((prop = node.property (X_("ctl"))) != 0) {
                ev = MIDI::controller;
@@ -749,9 +766,14 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
                ev = MIDI::on;
        } else if ((prop = node.property (X_("pgm"))) != 0) {
                ev = MIDI::program;
-       } else if ((prop = node.property (X_("sysex"))) != 0) {
+       } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
+
+                if (prop->name() == X_("sysex")) {
+                        ev = MIDI::sysex;
+                } else {
+                        ev = MIDI::any;
+                }
 
-               ev = MIDI::sysex;
                int val;
                uint32_t cnt;
 
@@ -769,8 +791,8 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
                        return 0;
                }
 
-               sysex = new MIDI::byte[cnt];
-               sysex_size = cnt;
+               data = new MIDI::byte[cnt];
+               data_size = cnt;
                
                {
                        stringstream ss (prop->value());
@@ -778,16 +800,16 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
                        cnt = 0;
                        
                        while (ss >> val) {
-                               sysex[cnt++] = (MIDI::byte) val;
+                               data[cnt++] = (MIDI::byte) val;
                        }
                }
-               
+
        } else {
                warning << "Binding ignored - unknown type" << endmsg;
                return 0;
        }
 
-       if (sysex_size == 0) {
+       if (data_size == 0) {
                if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
                        return 0;
                }
@@ -812,18 +834,112 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
        
        MIDIFunction* mf = new MIDIFunction (*_port);
        
-       if (mf->init (*this, prop->value(), sysex, sysex_size)) {
+       if (mf->init (*this, prop->value(), data, data_size)) {
                delete mf;
                return 0;
        }
 
-       cerr << "New MF with function = " << prop->value() << " on channel " << (int) channel << " detail = " << (int) detail << endl;
        mf->bind_midi (channel, ev, detail);
 
-
        return mf;
 }
 
+MIDIAction*
+GenericMidiControlProtocol::create_action (const XMLNode& node)
+{
+       const XMLProperty* prop;
+       int intval;
+       MIDI::byte detail = 0;
+       MIDI::channel_t channel = 0;
+       string uri;
+       MIDI::eventType ev;
+       MIDI::byte* data = 0;
+       uint32_t data_size = 0;
+
+       if ((prop = node.property (X_("ctl"))) != 0) {
+               ev = MIDI::controller;
+       } else if ((prop = node.property (X_("note"))) != 0) {
+               ev = MIDI::on;
+       } else if ((prop = node.property (X_("pgm"))) != 0) {
+               ev = MIDI::program;
+       } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
+
+                if (prop->name() == X_("sysex")) {
+                        ev = MIDI::sysex;
+                } else {
+                        ev = MIDI::any;
+                }
+
+               int val;
+               uint32_t cnt;
+
+               {
+                       cnt = 0;
+                       stringstream ss (prop->value());
+                       ss << hex;
+                       
+                       while (ss >> val) {
+                               cnt++;
+                       }
+               }
+
+               if (cnt == 0) {
+                       return 0;
+               }
+
+               data = new MIDI::byte[cnt];
+               data_size = cnt;
+               
+               {
+                       stringstream ss (prop->value());
+                       ss << hex;
+                       cnt = 0;
+                       
+                       while (ss >> val) {
+                               data[cnt++] = (MIDI::byte) val;
+                       }
+               }
+
+       } else {
+               warning << "Binding ignored - unknown type" << endmsg;
+               return 0;
+       }
+
+       if (data_size == 0) {
+               if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
+                       return 0;
+               }
+               
+               detail = (MIDI::byte) intval;
+
+               if ((prop = node.property (X_("channel"))) == 0) {
+                       return 0;
+               }
+       
+               if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
+                       return 0;
+               }
+               channel = (MIDI::channel_t) intval;
+               /* adjust channel to zero-based counting */
+               if (channel > 0) {
+                       channel -= 1;
+               }
+       }
+
+       prop = node.property (X_("action"));
+       
+       MIDIAction* ma = new MIDIAction (*_port);
+        
+       if (ma->init (*this, prop->value(), data, data_size)) {
+               delete ma;
+               return 0;
+       }
+
+       ma->bind_midi (channel, ev, detail);
+
+       return ma;
+}
+
 void
 GenericMidiControlProtocol::set_current_bank (uint32_t b)
 {