Several changes to Mackie support. Breaks existing Mackie state on disk but not
authorPaul Davis <paul@linuxaudiosystems.com>
Sun, 11 Oct 2015 15:58:00 +0000 (11:58 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Sun, 11 Oct 2015 16:01:27 +0000 (12:01 -0400)
in any way that causes problems, just loss of pre-existing connectivity.

1. retain state of current device (and serialize to disk) when switching
devices, and restore that state when switching back to it.
2. fix port and surfacenaming.
3. fix bundle assembly so that all ports (for multi-surface combos) work.
4. rationalize master position numbering
5. add small sleep before starting device handshake after reconnection. This
is ugly but seems to be necessary, unfortunately.

libs/surfaces/mackie/mackie_control_protocol.cc
libs/surfaces/mackie/mackie_control_protocol.h
libs/surfaces/mackie/surface.cc
libs/surfaces/mackie/surface_port.cc
libs/surfaces/mackie/surface_port.h

index 1222dc5707e2f669e91e065245c6456d2e617218..6f81b57c5998205819d0d3f1bcd554b9a566dd6b 100644 (file)
@@ -696,15 +696,29 @@ MackieControlProtocol::set_device_info (const string& device_name)
 int
 MackieControlProtocol::set_device (const string& device_name, bool force)
 {
-       cerr << "Set Device\n\n\n\n\n\n";
-
        if (device_name == device_info().name() && !force) {
                /* already using that device, nothing to do */
                return 0;
        }
 
+       /* get state from the current setup, and make sure it is stored in
+          the _surface_states node so that if we switch back to this device,
+          we will have its state available.
+       */
+
+       if (!_surfaces_state) {
+               _surfaces_state = new XMLNode (X_("Surfaces"));
+       }
+
+       {
+               Glib::Threads::Mutex::Lock lm (surfaces_lock);
+
+               for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
+                       update_surface_state (*s);
+               }
+       }
+
        if (set_device_info (device_name)) {
-               cerr << "Unknown device name\n";
                return -1;
        }
 
@@ -750,12 +764,40 @@ MackieControlProtocol::create_surfaces ()
 
        DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces for %2\n", 1 + _device_info.extenders(), _device_info.name()));
 
+       if (!_device_info.uses_ipmidi()) {
+               _input_bundle.reset (new ARDOUR::Bundle (_("Mackie Control In"), true));
+               _output_bundle.reset (new ARDOUR::Bundle (_("Mackie Control Out"), false));
+       } else {
+               _input_bundle.reset ();
+               _output_bundle.reset ();
+
+       }
        for (uint32_t n = 0; n < 1 + _device_info.extenders(); ++n) {
+               bool is_master = false;
+
+               if (_device_info.master_position() == 0) {
+                       /* unspecified master position, use first surface */
+                       if (n == 0) {
+                               is_master = true;
+                               if (_device_info.extenders() == 0) {
+                                       device_name = _device_info.name();
+                               } else {
+                                       device_name = X_("mackie control");
+                               }
+                       }
+               } else if ((n+1) == _device_info.master_position()) {
+                       /* specified master position, uses 1-based counting for user interaction */
+                       is_master = true;
+                       if (_device_info.extenders() == 0) {
+                               device_name = _device_info.name();
+                       } else {
+                               device_name = X_("mackie control");
+                       }
 
-               if (n == 0) {
-                       device_name = _device_info.name();
-               } else {
-                       device_name = string_compose ("%1 #%2", _device_info.name(), n+1);
+               }
+
+               if (!is_master) {
+                       device_name = string_compose (X_("mackie control ext %1"), n+1);
                }
 
                boost::shared_ptr<Surface> surface;
@@ -771,12 +813,11 @@ MackieControlProtocol::create_surfaces ()
                        return -1;
                }
 
-               if (n == _device_info.master_position()) {
+               if (is_master) {
                        _master_surface = surface;
                }
 
                if (_surfaces_state) {
-                       cerr << "Resetting surface state\n";
                        surface->set_state (*_surfaces_state, _surfaces_version);
                }
 
@@ -787,9 +828,6 @@ MackieControlProtocol::create_surfaces ()
 
                if (!_device_info.uses_ipmidi()) {
 
-                       _input_bundle.reset (new ARDOUR::Bundle (_("Mackie Control In"), true));
-                       _output_bundle.reset (new ARDOUR::Bundle (_("Mackie Control Out"), false));
-
                        _input_bundle->add_channel (
                                surface->port().input_port().name(),
                                ARDOUR::DataType::MIDI,
@@ -801,14 +839,6 @@ MackieControlProtocol::create_surfaces ()
                                ARDOUR::DataType::MIDI,
                                session->engine().make_port_name_non_relative (surface->port().output_port().name())
                                );
-
-                       session->BundleAddedOrRemoved ();
-
-               } else {
-                       _input_bundle.reset ((ARDOUR::Bundle*) 0);
-                       _output_bundle.reset ((ARDOUR::Bundle*) 0);
-
-                       session->BundleAddedOrRemoved ();
                }
 
                MIDI::Port& input_port (surface->port().input_port());
@@ -856,6 +886,17 @@ MackieControlProtocol::create_surfaces ()
                }
        }
 
+       if (!_device_info.uses_ipmidi()) {
+               Glib::Threads::Mutex::Lock lm (surfaces_lock);
+               for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
+                       (*s)->port().reconnect ();
+               }
+       }
+
+       session->BundleAddedOrRemoved ();
+
+       assert (_master_surface);
+
        return 0;
 }
 
@@ -870,6 +911,18 @@ MackieControlProtocol::close()
        clear_surfaces();
 }
 
+/** Ensure that the _surfaces_state XML node contains an up-to-date
+ *  copy of the state node for @param surface. If _surfaces_state already
+ *  contains a state node for @param surface, it will deleted and replaced.
+ */
+void
+MackieControlProtocol::update_surface_state (boost::shared_ptr<Surface> surface)
+{
+       assert (_surfaces_state);
+       _surfaces_state->remove_nodes_and_delete (X_("name"), surface->name());
+       _surfaces_state->add_child_nocopy (surface->get_state());
+}
+
 XMLNode&
 MackieControlProtocol::get_state()
 {
@@ -889,12 +942,19 @@ MackieControlProtocol::get_state()
        node.add_property (X_("device-profile"), _device_profile.name());
        node.add_property (X_("device-name"), _device_info.name());
 
-       XMLNode* snode = new XMLNode (X_("Surfaces"));
-       for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
-               snode->add_child_nocopy ((*s)->get_state());
+       if (!_surfaces_state) {
+               _surfaces_state = new XMLNode (X_("Surfaces"));
+       }
+
+       {
+               Glib::Threads::Mutex::Lock lm (surfaces_lock);
+               for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
+                       update_surface_state (*s);
+               }
        }
 
-       node.add_child_nocopy (*snode);
+       /* force a copy of the _surfaces_state node, because we want to retain ownership */
+       node.add_child_copy (*_surfaces_state);
 
        DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::get_state done\n");
 
index 091fa3898fa6a142d0131057ffa089d7a367d65d..18069dc591b2e056fd08616a97b2d30c50ed85db 100644 (file)
@@ -352,6 +352,7 @@ class MackieControlProtocol
        int ipmidi_restart ();
         void initialize ();
         int set_device_info (const std::string& device_name);
+       void update_surface_state (boost::shared_ptr<Mackie::Surface>);
 
        /* MIDI port connection management */
 
index 3b42aa1e8ffe4bed04004ab15f3253e156fc3c91..005f1fadb2816255318eee12338fb3e56a2e2963 100644 (file)
@@ -229,7 +229,6 @@ Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, b
                */
 
                g_usleep (100000);
-
                connected ();
 
        } else {
@@ -243,21 +242,29 @@ Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, b
 XMLNode&
 Surface::get_state()
 {
-       char buf[64];
-       snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
-       XMLNode* node = new XMLNode (buf);
-
+       XMLNode* node = new XMLNode (X_("Surface"));
+       node->add_property (X_("name"), _name);
        node->add_child_nocopy (_port->get_state());
-
        return *node;
 }
 
 int
 Surface::set_state (const XMLNode& node, int version)
 {
-       char buf[64];
-       snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
-       XMLNode* mynode = node.child (buf);
+       /* Look for a node named after this surface */
+
+       XMLNodeList const& children = node.children();
+       XMLNode* mynode = 0;
+
+       for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
+               XMLProperty const* prop = (*c)->property (X_("name"));
+               if (prop) {
+                       if (prop->value() == _name) {
+                               mynode = *c;
+                               break;
+                       }
+               }
+       }
 
        if (!mynode) {
                return 0;
index 50bb206cdf2ab3247c85da1833fe1f0288e86f78..6f243a54449fe06085049d2479d12ec9a9229d7b 100644 (file)
@@ -58,8 +58,24 @@ SurfacePort::SurfacePort (Surface& s)
 
        } else {
 
-               _async_in  = AudioEngine::instance()->register_input_port (DataType::MIDI, string_compose (_("%1 in"),  _surface->name()), true);
-               _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, string_compose (_("%1 out"),  _surface->name()), true);
+               string in_name;
+               string out_name;
+
+               if (_surface->mcp().device_info().extenders() > 0) {
+                       if (_surface->number() + 1 == _surface->mcp().device_info().master_position()) {
+                               in_name = X_("mackie control in");
+                               out_name = X_("mackie control out");
+                       } else {
+                               in_name = string_compose (X_("mackie control in ext %1"), _surface->number());
+                               out_name = string_compose (X_("mackie control out ext %1"), _surface->number());
+                       }
+               } else {
+                       in_name = X_("mackie control in");
+                       out_name = X_("mackie control out");
+               }
+
+               _async_in  = AudioEngine::instance()->register_input_port (DataType::MIDI, in_name, true);
+               _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, out_name, true);
 
                if (_async_in == 0 || _async_out == 0) {
                        throw failed_constructor();
@@ -121,29 +137,33 @@ SurfacePort::set_state (const XMLNode& node, int version)
        if (dynamic_cast<MIDI::IPMIDIPort*>(_input_port)) {
                return 0;
        }
-       // the rest should not be run if the device-name changes outside of a session load.
-       if ( _surface->mcp().session_load()) {
 
-               XMLNode* child;
+       XMLNode* child;
 
-               if ((child = node.child (X_("Input"))) != 0) {
-                       XMLNode* portnode = child->child (Port::state_node_name.c_str());
-                       if (portnode) {
-                               _async_in->set_state (*portnode, version);
-                       }
+       if ((child = node.child (X_("Input"))) != 0) {
+               XMLNode* portnode = child->child (Port::state_node_name.c_str());
+               if (portnode) {
+                       _async_in->set_state (*portnode, version);
                }
+       }
 
-               if ((child = node.child (X_("Output"))) != 0) {
-                       XMLNode* portnode = child->child (Port::state_node_name.c_str());
-                       if (portnode) {
-                               _async_out->set_state (*portnode, version);
-                       }
+       if ((child = node.child (X_("Output"))) != 0) {
+               XMLNode* portnode = child->child (Port::state_node_name.c_str());
+               if (portnode) {
+                       _async_out->set_state (*portnode, version);
                }
        }
 
        return 0;
 }
 
+void
+SurfacePort::reconnect ()
+{
+       _async_out->reconnect ();
+       _async_in->reconnect ();
+}
+
 std::string
 SurfacePort::input_name () const
 {
@@ -211,4 +231,3 @@ Mackie::operator <<  (ostream & os, const SurfacePort & port)
        os << " }";
        return os;
 }
-
index 9795a8d5cd5637c0b3d59d06236d9b9568432fde..bdc089985d1e1b61360854f72bd3d539cf5ebd45 100644 (file)
@@ -68,6 +68,8 @@ class SurfacePort
        std::string input_name() const;
        std::string output_name() const;
 
+       void reconnect ();
+
        XMLNode& get_state ();
        int set_state (const XMLNode&, int version);