restore compilability amidst automation state mgmt changes
authorPaul Davis <paul@linuxaudiosystems.com>
Fri, 27 Oct 2006 00:31:38 +0000 (00:31 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Fri, 27 Oct 2006 00:31:38 +0000 (00:31 +0000)
git-svn-id: svn://localhost/ardour2/trunk@1030 d708f5d6-7413-0410-9779-e7cbd77b26cf

gtk2_ardour/audio_regionview.cc
libs/ardour/ardour/panner.h
libs/ardour/automation_event.cc
libs/ardour/io.cc
libs/ardour/panner.cc
libs/ardour/redirect.cc
libs/ardour/route.cc
libs/ardour/send.cc

index b50ea72be2b77e98370e5a6a065927cf75b5067f..c6857000392b561935f48bd4eb8380eaa91d76f3 100644 (file)
@@ -1143,10 +1143,8 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev)
 
        gain_line->view_to_model_y (y);
 
-#ifdef FIX_ME_TO_NOT_USE_STATE_MANAGER
        trackview.session().begin_reversible_command (_("add gain control point"));
-       trackview.session().add_undo (region.envelope().get_memento());
-#endif
+       XMLNode& before = region.envelop().get_state ();
 
        if (!region.envelope_active()) {
                trackview.session().add_undo( bind( mem_fun(region, &AudioRegion::set_envelope_active), false) );
@@ -1156,10 +1154,9 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev)
 
        region.envelope().add (fx, y);
        
-#ifdef FIX_ME_TO_NOT_USE_STATE_MANAGER
-       trackview.session().add_redo_no_execute (region.envelope().get_memento());
-       trackview.session().commit_reversible_command ();
-#endif
+       trackview.session().commit_reversible_command (new MementoCommand<Curve>(region.envelope(), 
+                                                                                before,
+                                                                                region.envelope().get_state());
 }
 
 void
index 0cb0c3dfaad79a6e1ce1bb4483a4368bcd31b2b7..47e30e43d7046143c6975b64606c170ce0e195bd 100644 (file)
@@ -83,16 +83,12 @@ class StreamPanner : public sigc::trackable, public Stateful
 
        virtual Curve& automation() = 0;
 
-       virtual int load (istream&, string path, uint32_t&) = 0;
-
-       virtual int save (ostream&) const = 0;
-
        sigc::signal<void> Changed;      /* for position */
        sigc::signal<void> StateChanged; /* for mute */
 
        int set_state (const XMLNode&);
        virtual XMLNode& state (bool full_state) = 0;
-
+       
        Panner & get_parent() { return parent; }
        
   protected:
@@ -143,8 +139,6 @@ class BaseStereoPanner : public StreamPanner
 
        void distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes);
 
-       int load (istream&, string path, uint32_t&);
-       int save (ostream&) const;
        void snapshot (nframes_t now);
        void transport_stopped (nframes_t frame);
        void set_automation_state (AutoState);
@@ -205,10 +199,7 @@ class Multi2dPanner : public StreamPanner
 
        void distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes);
        void distribute_automated (Sample* src, Sample** obufs, 
-                            nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
-
-       int load (istream&, string path, uint32_t&);
-       int save (ostream&) const;
+                                  nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
 
        static StreamPanner* factory (Panner&);
        static string name;
@@ -239,8 +230,6 @@ class Panner : public std::vector<StreamPanner*>, public Stateful, public sigc::
        Panner (string name, Session&);
        virtual ~Panner ();
 
-       void set_name (string);
-
        bool bypassed() const { return _bypassed; }
        void set_bypassed (bool yn);
 
@@ -260,9 +249,6 @@ class Panner : public std::vector<StreamPanner*>, public Stateful, public sigc::
        AutoStyle automation_style() const;
        bool touching() const;
 
-       int load ();
-       int save () const;
-
        XMLNode& get_state (void);
        XMLNode& state (bool full);
        int      set_state (const XMLNode&);
@@ -302,7 +288,6 @@ class Panner : public std::vector<StreamPanner*>, public Stateful, public sigc::
        
   private:
 
-       string            automation_path;
        Session&         _session;
        uint32_t     current_outs;
        bool             _linked;
index c82a95e05804de3b3783ffb130f9454997572681..50c429ca7009610db1632ba2bc81992344fa12b1 100644 (file)
@@ -1122,6 +1122,10 @@ AutomationList::get_state ()
        XMLNode* node = new XMLNode (X_("events"));
        iterator xx;
 
+       if (events.empty()) {
+               return *node;
+       }
+
        for (xx = events.begin(); xx != events.end(); ++xx) {
                str << (double) (*xx)->when;
                str << ' ';
@@ -1151,35 +1155,39 @@ AutomationList::set_state (const XMLNode& node)
 
                        /* new style */
 
-                       stringstream str (node.content());
-
-                       double x;
-                       double y;
-                       bool ok = true;
-
-                       while (str) {
-                               str >> x;
-                               if (!str) {
-                                       ok = false;
-                                       break;
+                       if (!node.content().empty()) {
+                               
+                               stringstream str (node.content());
+                               
+                               double x;
+                               double y;
+                               bool ok = true;
+                               
+                               
+                               while (str) {
+                                       str >> x;
+                                       if (!str) {
+                                               ok = false;
+                                               break;
+                                       }
+                                       str >> y;
+                                       if (!str) {
+                                               ok = false;
+                                               break;
+                                       }
+                                       add (x, y);
                                }
-                               str >> y;
-                               if (!str) {
-                                       ok = false;
-                                       break;
+                               
+                               if (!ok) {
+                                       clear ();
+                                       error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
                                }
-                               add (x, y);
-                       }
-
-                       if (!ok) {
-                               clear ();
-                               error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
                        }
 
                } else {
 
                        /* old style */
-               
+
                        nframes_t x;
                        double y;
 
index 4de8105a5c2e4bd580ae66375acf34810d37fd1a..38259c97e8e0284ee0e4106d4813ea1ef739f8ef 100644 (file)
@@ -58,9 +58,6 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-
-static float current_automation_version_number = 1.0;
-
 const string IO::state_node_name = "IO";
 bool         IO::connecting_legal = false;
 bool         IO::ports_legal = false;
@@ -136,11 +133,12 @@ IO::IO (Session& s, string name,
                Glib::Mutex::Lock guard (m_meter_signal_lock);
                m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
        }
+
+       _session.add_controllable (&_gain_control);
 }
 
 IO::~IO ()
 {
-
        Glib::Mutex::Lock guard (m_meter_signal_lock);
        
        Glib::Mutex::Lock lm (io_lock);
@@ -1392,7 +1390,6 @@ int
 IO::panners_became_legal ()
 {
        _panner->reset (_noutputs, pans_required());
-       _panner->load (); // automation
        panner_legal_c.disconnect ();
        return 0;
 }
@@ -1527,7 +1524,11 @@ IO::state (bool full_state)
        /* automation */
 
        if (full_state) {
-               node->add_child_nocopy (get_automation_state ());
+
+               XMLNode* autonode = new XMLNode (X_("Automation"));
+               autonode->add_child_nocopy (get_automation_state());
+               node->add_child_nocopy (*autonode);
+
                snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_state());
        } else {
                /* never store anything except Off for automation state in a template */
@@ -1538,56 +1539,10 @@ IO::state (bool full_state)
        snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_style());
        node->add_property ("automation-style", buf);
 
-       /* XXX same for pan etc. */
-
        return *node;
 }
 
-int
-IO::connecting_became_legal ()
-{
-       int ret;
-
-       if (pending_state_node == 0) {
-               fatal << _("IO::connecting_became_legal() called without a pending state node") << endmsg;
-               /*NOTREACHED*/
-               return -1;
-       }
-
-       connection_legal_c.disconnect ();
-
-       ret = make_connections (*pending_state_node);
-
-       if (ports_legal) {
-               delete pending_state_node;
-               pending_state_node = 0;
-       }
-
-       return ret;
-}
-
-int
-IO::ports_became_legal ()
-{
-       int ret;
-
-       if (pending_state_node == 0) {
-               fatal << _("IO::ports_became_legal() called without a pending state node") << endmsg;
-               /*NOTREACHED*/
-               return -1;
-       }
 
-       port_legal_c.disconnect ();
-
-       ret = create_ports (*pending_state_node);
-
-       if (connecting_legal) {
-               delete pending_state_node;
-               pending_state_node = 0;
-       }
-
-       return ret;
-}
 
 int
 IO::set_state (const XMLNode& node)
@@ -1607,7 +1562,7 @@ IO::set_state (const XMLNode& node)
 
        if ((prop = node.property ("name")) != 0) {
                _name = prop->value();
-               _panner->set_name (_name);
+               /* used to set panner name with this, but no more */
        } 
 
        if ((prop = node.property ("id")) != 0) {
@@ -1633,9 +1588,12 @@ IO::set_state (const XMLNode& node)
                        _panner->set_state (**iter);
                }
 
+               if ((*iter)->name() == X_("Automation")) {
+                       set_automation_state (*(*iter));
+               }
+
                if ((*iter)->name() == X_("gaincontrol")) {
                        _gain_control.set_state (**iter);
-                       _session.add_controllable (&_gain_control);
                }
        }
 
@@ -1688,6 +1646,70 @@ IO::set_state (const XMLNode& node)
        return 0;
 }
 
+int
+IO::set_automation_state (const XMLNode& node)
+{
+       /* IO has only one automation sub-node, and it should always be the
+          first one (i.e. derived classes should always call
+          IO::set_automation_state() if they need the IO's automation
+          state. Note that Plugin + Insert Redirects do not need this, so they
+          don't call it at all, though Sends do.
+       */
+
+       return _gain_automation_curve.set_state (*node.children().front());
+}
+
+XMLNode&
+IO::get_automation_state ()
+{
+       return (_gain_automation_curve.get_state ());
+}
+
+int
+IO::connecting_became_legal ()
+{
+       int ret;
+
+       if (pending_state_node == 0) {
+               fatal << _("IO::connecting_became_legal() called without a pending state node") << endmsg;
+               /*NOTREACHED*/
+               return -1;
+       }
+
+       connection_legal_c.disconnect ();
+
+       ret = make_connections (*pending_state_node);
+
+       if (ports_legal) {
+               delete pending_state_node;
+               pending_state_node = 0;
+       }
+
+       return ret;
+}
+int
+IO::ports_became_legal ()
+{
+       int ret;
+
+       if (pending_state_node == 0) {
+               fatal << _("IO::ports_became_legal() called without a pending state node") << endmsg;
+               /*NOTREACHED*/
+               return -1;
+       }
+
+       port_legal_c.disconnect ();
+
+       ret = create_ports (*pending_state_node);
+
+       if (connecting_legal) {
+               delete pending_state_node;
+               pending_state_node = 0;
+       }
+
+       return ret;
+}
+
 int
 IO::create_ports (const XMLNode& node)
 {
index 4b0bd25ba405758fe390126dfdf1b3962482e8d0..6d9faa92b712bd802386508143e63efe9d603e69 100644 (file)
@@ -71,6 +71,8 @@ StreamPanner::StreamPanner (Panner& p)
 {
        _muted = false;
 
+       parent.session().add_controllable (&_control);
+
        x = 0.5;
        y = 0.5;
        z = 0.5;
@@ -229,62 +231,6 @@ BaseStereoPanner::set_automation_state (AutoState state)
        }
 }
 
-int
-BaseStereoPanner::save (ostream& out) const
-{
-       LocaleGuard lg (X_("POSIX"));
-
-       /* force a single format for numeric data to ease session interchange
-          across national boundaries.
-       */
-
-       out << "begin" << endl;
-
-       for (AutomationList::const_iterator i = _automation.const_begin(); i != _automation.const_end(); ++i) {
-               out << '\t' << (nframes_t) floor ((*i)->when) << ' ' << (*i)->value << endl;
-               if (!out) {
-                       error << string_compose (_("error writing pan automation file (%s)"), strerror (errno)) << endmsg;
-                       return -1;
-               }
-       }
-       out << "end" << endl;
-
-       return 0;
-}
-                               
-int
-BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
-{
-       char line[128];
-       LocaleGuard lg (X_("POSIX"));
-       
-       _automation.clear ();
-
-       while (in.getline (line, sizeof (line), '\n')) {
-               nframes_t when;
-               double value;
-
-               ++linecnt;
-
-               if (strcmp (line, "end") == 0) {
-                       break;
-               }
-
-               if (sscanf (line, "%" PRIu32 " %lf", &when, &value) != 2) {
-                       warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg;
-                       continue;
-               }
-
-               _automation.add (when, value, true);
-       }
-
-       /* now that we are done loading */
-
-       _automation.StateChanged (Change (0));
-
-       return 0;
-}
-
 void
 BaseStereoPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes)
 {
@@ -526,12 +472,16 @@ EqualPowerStereoPanner::state (bool full_state)
        snprintf (buf, sizeof (buf), "%.12g", x); 
        root->add_property (X_("x"), buf);
        root->add_property (X_("type"), EqualPowerStereoPanner::name);
+
        if (full_state) {
-               snprintf (buf, sizeof (buf), "0x%x", _automation.automation_state()); 
+               XMLNode* autonode = new XMLNode (X_("Automation"));
+               autonode->add_child_nocopy (_automation.get_state ());
+               root->add_child_nocopy (*autonode);
        } else {
                /* never store automation states other than off in a template */
                snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off); 
        }
+
        root->add_property (X_("automation-state"), buf);
        snprintf (buf, sizeof (buf), "0x%x", _automation.automation_style()); 
        root->add_property (X_("automation-style"), buf);
@@ -555,6 +505,16 @@ EqualPowerStereoPanner::set_state (const XMLNode& node)
                set_position (pos, true);
        } 
 
+       StreamPanner::set_state (node);
+
+       for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
+               if ((*iter)->name() == X_("panner")) {
+                       _control.set_state (**iter);
+               } else if ((*iter)->name() == X_("Automation")) {
+                       _automation.set_state (**iter);
+               }
+       }
+       
        if ((prop = node.property (X_("automation-state")))) {
                sscanf (prop->value().c_str(), "0x%x", &x);
                _automation.set_automation_state ((AutoState) x);
@@ -569,15 +529,6 @@ EqualPowerStereoPanner::set_state (const XMLNode& node)
                _automation.set_automation_style ((AutoStyle) x);
        }
 
-       StreamPanner::set_state (node);
-
-       for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
-               if ((*iter)->name() == X_("panner")) {
-                       _control.set_state (**iter);
-                       parent.session().add_controllable (&_control);
-               }
-       }
-       
        return 0;
 }
 
@@ -740,18 +691,6 @@ Multi2dPanner::factory (Panner& p)
        return new Multi2dPanner (p);
 }
 
-int
-Multi2dPanner::load (istream& in, string path, uint32_t& linecnt)
-{
-       return 0;
-}
-
-int
-Multi2dPanner::save (ostream& out) const
-{
-       return 0;
-}
-
 XMLNode&
 Multi2dPanner::get_state (void)
 {
@@ -771,6 +710,8 @@ Multi2dPanner::state (bool full_state)
        root->add_property (X_("y"), buf);
        root->add_property (X_("type"), Multi2dPanner::name);
 
+       /* XXX no meaningful automation yet */
+
        return *root;
 }
 
@@ -807,7 +748,6 @@ Multi2dPanner::set_state (const XMLNode& node)
 Panner::Panner (string name, Session& s)
        : _session (s)
 {
-       set_name (name);
        _linked = false;
        _link_direction = SameDirection;
        _bypassed = false;
@@ -837,17 +777,6 @@ Panner::set_link_direction (LinkDirection ld)
        }
 }
 
-void
-Panner::set_name (string str)
-{
-       automation_path = _session.automation_dir();
-       automation_path += _session.snap_name();
-       automation_path += "-pan-";
-       automation_path += legalize_for_path (str);
-       automation_path += ".automation";
-}
-
-
 void
 Panner::set_bypassed (bool yn)
 {
@@ -864,7 +793,6 @@ Panner::reset (uint32_t nouts, uint32_t npans)
        uint32_t n;
        bool changed = false;
 
-
        if (nouts < 2 || (nouts == outputs.size() && npans == size())) {
                return;
        } 
@@ -1076,102 +1004,6 @@ Panner::clear_automation ()
        _session.set_dirty ();
 }      
 
-int
-Panner::save () const
-{
-       ofstream out (automation_path.c_str());
-       
-       if (!out) {
-               error << string_compose (_("cannot open pan automation file \"%1\" for saving (%2)"), automation_path, strerror (errno))
-                     << endmsg;
-               return -1;
-       }
-
-       out << X_("version ") << current_automation_version_number << endl;
-
-       for (vector<StreamPanner*>::const_iterator i = begin(); i != end(); ++i) {
-               if ((*i)->save (out)) {
-                       return -1;
-               }
-       }
-
-       return 0;
-}
-
-int
-Panner::load ()
-{
-       char line[128];
-       uint32_t linecnt = 0;
-       float version;
-       iterator sp;
-       LocaleGuard lg (X_("POSIX"));
-
-       if (automation_path.length() == 0) {
-               return 0;
-       }
-       
-       if (access (automation_path.c_str(), F_OK)) {
-               return 0;
-       }
-
-       ifstream in (automation_path.c_str());
-
-       if (!in) {
-               error << string_compose (_("cannot open pan automation file %1 (%2)"),
-                                 automation_path, strerror (errno))
-                     << endmsg;
-               return -1;
-       }
-
-       sp = begin();
-
-       while (in.getline (line, sizeof(line), '\n')) {
-
-               if (++linecnt == 1) {
-                       if (memcmp (line, X_("version"), 7) == 0) {
-                               if (sscanf (line, "version %f", &version) != 1) {
-                                       error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg;
-                                       return -1;
-                               }
-                       } else {
-                               error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"), 
-                                                automation_path, line) << endmsg;
-                               return -1;
-                       }
-
-                       if (version != current_automation_version_number) {
-                               error << string_compose(_("mismatched pan automation event file version (%1)"), version) << endmsg;
-                               return -1;
-                       }
-
-                       continue;
-               }
-
-               if (strlen (line) == 0 || line[0] == '#') {
-                       continue;
-               }
-
-               if (strcmp (line, "begin") == 0) {
-                       
-                       if (sp == end()) {
-                               error << string_compose (_("too many panner states found in pan automation file %1"),
-                                                 automation_path)
-                                     << endmsg;
-                               return -1;
-                       }
-
-                       if ((*sp)->load (in, automation_path, linecnt)) {
-                               return -1;
-                       }
-                       
-                       ++sp;
-               }
-       }
-
-       return 0;
-}
-
 struct PanPlugins {
     string name;
     uint32_t nouts;
@@ -1216,10 +1048,8 @@ Panner::state (bool full)
                root->add_child_nocopy (*onode);
        }
 
-       if (full) {
-               if (save () == 0) {
-                       root->add_property (X_("automation"), Glib::path_get_basename (automation_path));
-               }
+       for (vector<StreamPanner*>::const_iterator i = begin(); i != end(); ++i) {
+               root->add_child_nocopy ((*i)->state (full));
        }
 
        return *root;
@@ -1310,16 +1140,6 @@ Panner::set_state (const XMLNode& node)
                }       
        }
 
-       /* don't try to load automation if it wasn't marked as existing */
-
-       if ((prop = node.property (X_("automation")))) {
-
-               /* automation path is relative */
-               
-               automation_path = _session.automation_dir();
-               automation_path += prop->value ();
-       } 
-
        return 0;
 }
 
index f414860ec837f9beb7c93f929a367fe3bc9e99c1..710b00fe186b0e31423616109f6e29d9786ed165 100644 (file)
@@ -248,27 +248,6 @@ Redirect::state (bool full_state)
        return *node;
 }
 
-void
-Redirect::what_has_automation (set<uint32_t>& s) const
-{
-       Glib::Mutex::Lock lm (_automation_lock);
-       map<uint32_t,AutomationList*>::const_iterator li;
-       
-       for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) {
-               s.insert  ((*li).first);
-       }
-}
-
-void
-Redirect::what_has_visible_automation (set<uint32_t>& s) const
-{
-       Glib::Mutex::Lock lm (_automation_lock);
-       set<uint32_t>::const_iterator li;
-       
-       for (li = visible_parameter_automation.begin(); li != visible_parameter_automation.end(); ++li) {
-               s.insert  (*li);
-       }
-}
 
 int
 Redirect::set_state (const XMLNode& node)
@@ -291,7 +270,7 @@ Redirect::set_state (const XMLNode& node)
                        IO::set_state (**niter);
                        have_io = true;
 
-               } else if ((*niter)->name() == "Automation") {
+               } else if ((*niter)->name() == X_("Automation")) {
 
                        XMLProperty *prop;
                        
@@ -347,6 +326,27 @@ Redirect::set_state (const XMLNode& node)
        return 0;
 }
 
+void
+Redirect::what_has_automation (set<uint32_t>& s) const
+{
+       Glib::Mutex::Lock lm (_automation_lock);
+       map<uint32_t,AutomationList*>::const_iterator li;
+       
+       for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) {
+               s.insert  ((*li).first);
+       }
+}
+
+void
+Redirect::what_has_visible_automation (set<uint32_t>& s) const
+{
+       Glib::Mutex::Lock lm (_automation_lock);
+       set<uint32_t>::const_iterator li;
+       
+       for (li = visible_parameter_automation.begin(); li != visible_parameter_automation.end(); ++li) {
+               s.insert  (*li);
+       }
+}
 AutomationList&
 Redirect::automation_list (uint32_t parameter)
 {
index 535508bca64f8128448059b29c00c98d8ba8d506..5c4be739793295de55f988afbb97f1eda77103fa 100644 (file)
@@ -1335,7 +1335,6 @@ XMLNode&
 Route::state(bool full_state)
 {
        XMLNode *node = new XMLNode("Route");
-       XMLNode *aevents;
        RedirectList:: iterator i;
        char buf[32];
 
@@ -1638,22 +1637,6 @@ Route::set_state (const XMLNode& node)
                                add_redirect_from_xml (*child);
                        }
 
-               } else if (child->name() == "Automation") {
-
-                       XMLPropertyList plist;
-                       XMLPropertyConstIterator piter;
-                       XMLProperty *prop;
-                       
-                       plist = child->properties();
-                       for (piter = plist.begin(); piter != plist.end(); ++piter) {
-                               prop = *piter;
-                               if (prop->name() == "path") {
-                                       warning << string_compose (_("old automation data found for %1, ignored"), _name) << endmsg;
-                               } else {
-                                       set_automation_state (*(*piter));
-                               }
-                       }
-
                } else if (child->name() == "ControlOuts") {
                        
                        string coutname = _name;
index 51d41a93b1d921332d542ee4d74c83d8bfea9d61..21069e3899cd5d4c0b43e1925712093dcf6467b3 100644 (file)
@@ -76,7 +76,7 @@ XMLNode&
 Send::state(bool full)
 {
        XMLNode *node = new XMLNode("Send");
-       node->add_child_nocopy (Redirect::state(full));
+       node->add_child_nocopy (Redirect::state (full));
        return *node;
 }
 
@@ -85,11 +85,15 @@ Send::set_state(const XMLNode& node)
 {
        XMLNodeList nlist = node.children();
        XMLNodeIterator niter;
-       
+
+       /* Send has regular IO automation (gain, pan) */
+
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                if ((*niter)->name() == Redirect::state_node_name) {
                        Redirect::set_state (**niter);
                        break;
+               } else if ((*niter)->name() == X_("Automation")) {
+                       IO::set_automation_state (*(*niter));
                }
        }