Fix save/load of MIDI automation state. Fixes #3354.
authorCarl Hetherington <carl@carlh.net>
Mon, 9 Aug 2010 22:23:23 +0000 (22:23 +0000)
committerCarl Hetherington <carl@carlh.net>
Mon, 9 Aug 2010 22:23:23 +0000 (22:23 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@7578 d708f5d6-7413-0410-9779-e7cbd77b26cf

14 files changed:
gtk2_ardour/automation_streamview.cc
gtk2_ardour/generic_pluginui.cc
libs/ardour/ardour/automatable.h
libs/ardour/ardour/automation_list.h
libs/ardour/ardour/midi_model.h
libs/ardour/ardour/midi_source.h
libs/ardour/ardour/plugin_insert.h
libs/ardour/automatable.cc
libs/ardour/automation_list.cc
libs/ardour/midi_model.cc
libs/ardour/midi_region.cc
libs/ardour/midi_source.cc
libs/ardour/plugin_insert.cc
libs/evoral/evoral/ControlSet.hpp

index cdca5ec08ffa0b8aa180c10a6c3a5f9d6dcf50dc..27ee2ca08670d6e4087cf6af1c59751c7521ea99 100644 (file)
@@ -151,9 +151,7 @@ AutomationStreamView::display_region(AutomationRegionView* region_view)
 void
 AutomationStreamView::set_automation_state (AutoState state)
 {
-       /* XXX: not sure if this is right, but for now the automation state is basically held by
-          the regions' AutomationLists.  Each region is always set to have the same AutoState.
-       */
+       /* Setting the automation state for this view sets the state of all regions' lists to the same thing */
        
        if (region_views.empty()) {
                _pending_automation_state = state;
index 04e753bf0cee5836c17b900a817564466af9acc5..40f8e16ef89d89e838daae1a51f095a54dfcdc1d 100644 (file)
@@ -346,7 +346,7 @@ GenericPluginUI::automation_state_changed (ControlUI* cui)
 
        // don't lock to avoid deadlock because we're triggered by
        // AutomationControl::Changed() while the automation lock is taken
-       switch (insert->get_parameter_automation_state (cui->parameter(), false)
+       switch (insert->get_parameter_automation_state (cui->parameter())
                        & (Off|Play|Touch|Write)) {
        case Off:
                cui->automate_button.set_label (_("Manual"));
index 2f35fc6b42c1f97178a160fbcb2a0a2f3fd4ff53..e0fe38e67ed377d22534988e06fb79efdf5fccf1 100644 (file)
@@ -65,7 +65,7 @@ public:
 
        virtual std::string describe_parameter(Evoral::Parameter param);
 
-       AutoState get_parameter_automation_state (Evoral::Parameter param, bool lock = true);
+       AutoState get_parameter_automation_state (Evoral::Parameter param);
        virtual void set_parameter_automation_state (Evoral::Parameter param, AutoState);
 
        AutoStyle get_parameter_automation_style (Evoral::Parameter param);
@@ -95,16 +95,13 @@ public:
 
        int set_automation_state (const XMLNode&, Evoral::Parameter default_param);
        XMLNode& get_automation_state();
-
-       /** Emitted when the automation state of one of our controls changes */
-       PBD::Signal1<void, Evoral::Parameter> AutomationStateChanged;
        
   protected:
        Session& _a_session;
 
        void can_automate(Evoral::Parameter);
 
-       virtual void auto_state_changed (Evoral::Parameter /*which*/) {}
+       virtual void automation_list_automation_state_changed (Evoral::Parameter, AutoState) {}
 
        int load_automation (const std::string& path);
        int old_set_automation_state(const XMLNode&);
@@ -116,7 +113,6 @@ public:
        static nframes_t _automation_interval;
 
 private:
-       void automation_state_changed (Evoral::Parameter const &);
        PBD::ScopedConnectionList _control_connections; ///< connections to our controls' signals
 };
 
index 77b8d49dc71b6e5f9031a69cc277ee095f7b6f7c..acb071cca7b012b3c45c5e113b0f40fe0a989329 100644 (file)
@@ -54,7 +54,7 @@ class AutomationList : public PBD::StatefulDestructible, public Evoral::ControlL
 
        void set_automation_state (AutoState);
        AutoState automation_state() const { return _state; }
-       PBD::Signal0<void> automation_state_changed;
+       PBD::Signal1<void, AutoState> automation_state_changed;
 
        void set_automation_style (AutoStyle m);
        AutoStyle automation_style() const { return _style; }
index e91d6484ea6e923fe6d643e24b5f5f44b7069ad0..e959535a36798f90c9aedfffe2d6806f370d0e0e 100644 (file)
@@ -174,7 +174,9 @@ private:
        friend class DeltaCommand;
 
        void source_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
+       void source_automation_state_changed (Evoral::Parameter, AutoState);
        void control_list_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
+       void automation_list_automation_state_changed (Evoral::Parameter, AutoState);
        
        PBD::ScopedConnectionList _midi_source_connections;
 
index 8d20f9c7b6542181124dbeaff1ee095f3363756c..5afcc202555402e701fabb3c7e59d91e5b19b684 100644 (file)
@@ -122,10 +122,17 @@ class MidiSource : virtual public Source
        void copy_interpolation_from (boost::shared_ptr<MidiSource>);
        void copy_interpolation_from (MidiSource *);
 
+       AutoState automation_state_of (Evoral::Parameter) const;
+       void set_automation_state_of (Evoral::Parameter, AutoState);
+       void copy_automation_state_from (boost::shared_ptr<MidiSource>);
+       void copy_automation_state_from (MidiSource *);
+
        /** Emitted when a different MidiModel is set */
        PBD::Signal0<void> ModelChanged;
        /** Emitted when a parameter's interpolation style is changed */
        PBD::Signal2<void, Evoral::Parameter, Evoral::ControlList::InterpolationStyle> InterpolationChanged;
+       /** Emitted when a parameter's automation state is changed */
+       PBD::Signal2<void, Evoral::Parameter, AutoState> AutomationStateChanged;
 
   protected:
        virtual void flush_midi() = 0;
@@ -159,6 +166,12 @@ class MidiSource : virtual public Source
         */
        typedef std::map<Evoral::Parameter, Evoral::ControlList::InterpolationStyle> InterpolationStyleMap;
        InterpolationStyleMap _interpolation_style;
+
+       /** Map of automation states to use for Parameters; if they are not in this map,
+        *  the correct automation state is Off.
+        */
+       typedef std::map<Evoral::Parameter, AutoState> AutomationStateMap;
+       AutomationStateMap  _automation_state;
 };
 
 }
index c962832eb1864d2ea79532127dd680ab22d9192c..6656f755cf9a1698498c93f008954f379e3ec9e3 100644 (file)
@@ -133,7 +133,7 @@ class PluginInsert : public Processor
        void connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t offset, bool with_auto, nframes_t now = 0);
 
        void set_automatable ();
-       void auto_state_changed (Evoral::Parameter which);
+       void control_list_automation_state_changed (Evoral::Parameter, AutoState);
        void set_parameter_state (const XMLNode& node, int version);
        void set_parameter_state_2X (const XMLNode& node, int version);
 
index c71fabda3794564049a7f28b4e31c87a2a34f81f..d6379eb38e55fb3f349e04606769a55084101290 100644 (file)
@@ -147,19 +147,17 @@ Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
 {
        Evoral::Parameter param = ac->parameter();
 
-       ControlSet::add_control(ac);
-       _can_automate_list.insert(param);
-       auto_state_changed(param); // sync everything up
-
-       /* connect to automation_state_changed so that we can emit a signal when one of our controls'
-          automation state changes
-       */
-       boost::shared_ptr<AutomationControl> c = boost::dynamic_pointer_cast<AutomationControl> (ac);
-       if (c) {
-               c->alist()->automation_state_changed.connect_same_thread (
-                       _control_connections, boost::bind (&Automatable::automation_state_changed, this, c->parameter())
-                       );
-       }
+       boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
+       assert (al);
+       
+       al->automation_state_changed.connect_same_thread (
+               _list_connections, boost::bind (&Automatable::automation_list_automation_state_changed, this, ac->parameter(), _1)
+               );
+
+       ControlSet::add_control (ac);
+       _can_automate_list.insert (param);
+
+       automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
 }
 
 void
@@ -315,21 +313,16 @@ Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState
 }
 
 AutoState
-Automatable::get_parameter_automation_state (Evoral::Parameter param, bool lock)
+Automatable::get_parameter_automation_state (Evoral::Parameter param)
 {
        AutoState result = Off;
 
-       if (lock)
-               control_lock().lock();
-
        boost::shared_ptr<Evoral::Control> c = control(param);
        boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
 
-       if (c)
+       if (c) {
                result = l->automation_state();
-
-       if (lock)
-               control_lock().unlock();
+       }
 
        return result;
 }
@@ -479,16 +472,9 @@ Automatable::automation_control (const Evoral::Parameter& id) const
        return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
 }
 
-void
-Automatable::automation_state_changed (Evoral::Parameter const & p)
-{
-       AutomationStateChanged (p); /* EMIT SIGNAL */
-}
-
 void
 Automatable::clear_controls ()
 {
        _control_connections.drop_connections ();
        ControlSet::clear_controls ();
 }
-       
index 17f346db1da3833e5551c3bc4dd61ffcaa521f1c..2270b2eff72905c69fde4660a22a175acc2226ff 100644 (file)
@@ -180,7 +180,7 @@ AutomationList::set_automation_state (AutoState s)
 {
        if (s != _state) {
                _state = s;
-               automation_state_changed (); /* EMIT SIGNAL */
+               automation_state_changed (s); /* EMIT SIGNAL */
        }
 }
 
index 6922384e71736d4d86aa52ba10c402b8a2912775..39daaacdea27736b63dae295065f18cf9f5103a5 100644 (file)
@@ -1094,14 +1094,18 @@ MidiModel::set_midi_source (MidiSource* s)
        _midi_source->InterpolationChanged.connect_same_thread (
                _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
                );
+
+       _midi_source->AutomationStateChanged.connect_same_thread (
+               _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
+               );
 }
 
 /** The source has signalled that the interpolation style for a parameter has changed.  In order to
  *  keep MidiSource and ControlList interpolation state the same, we pass this change onto the
  *  appropriate ControlList.
  *
- *  The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and the
- *  MidiSource's InterpolationChanged signal is listened to by the GUI.
+ *  The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
+ *  or the other is listened to by the GUI.
  */
 void
 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
@@ -1119,18 +1123,37 @@ MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::Cont
        _midi_source->set_interpolation_of (p, s);
 }
 
+void
+MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
+{
+       Glib::Mutex::Lock lm (_control_lock);
+       boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
+       al->set_automation_state (s);
+}
+
+void
+MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
+{
+       _midi_source->set_automation_state_of (p, s);
+}
+
 boost::shared_ptr<Evoral::Control>
 MidiModel::control_factory (Evoral::Parameter const & p)
 {
        boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
 
-       /* Set up newly created control's lists to the appropriate interpolation state
-          from our source.
+       /* Set up newly created control's lists to the appropriate interpolation and
+          automation state from our source.
        */
 
        assert (_midi_source);
 
        c->list()->set_interpolation (_midi_source->interpolation_of (p));
 
+       boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
+       assert (al);
+
+       al->set_automation_state (_midi_source->automation_state_of (p));
+
        return c;
 }
index c76b6a859ed69286581eb27d801502e4583fe127..91f9ee58ed5ffb2f958ab673ab95e16fc31ff661 100644 (file)
@@ -291,7 +291,7 @@ MidiRegion::model_changed ()
        }
 
        /* watch for changes to controls' AutoState */
-       model()->AutomationStateChanged.connect_same_thread (
+       midi_source()->AutomationStateChanged.connect_same_thread (
                _model_connection, boost::bind (&MidiRegion::model_automation_state_changed, this, _1)
                );
 }
index 5a823d332eb634c4bb91669be2b36493263385be..97fb801a6ca4bad59d66acc3953c689a65c61548 100644 (file)
@@ -102,7 +102,13 @@ MidiSource::get_state ()
                child->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first));
                child->add_property (X_("style"), enum_2_string (i->second));
        }
-                                    
+
+       for (AutomationStateMap::const_iterator i = _automation_state.begin(); i != _automation_state.end(); ++i) {
+               XMLNode* child = node.add_child (X_("AutomationState"));
+               child->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first));
+               child->add_property (X_("state"), enum_2_string (i->second));
+       }
+
        return node;
 }
 
@@ -134,6 +140,25 @@ MidiSource::set_state (const XMLNode& node, int /*version*/)
 
                        Evoral::ControlList::InterpolationStyle s = static_cast<Evoral::ControlList::InterpolationStyle> (string_2_enum (prop->value(), s));
                        set_interpolation_of (p, s);
+                       
+               } else if ((*i)->name() == X_("AutomationState")) {
+                       
+                       XMLProperty* prop;
+
+                       if ((prop = (*i)->property (X_("parameter"))) == 0) {
+                               error << _("Missing parameter property on AutomationState") << endmsg;
+                               return -1;
+                       }
+                       
+                       Evoral::Parameter p = EventTypeMap::instance().new_parameter (prop->value());
+
+                       if ((prop = (*i)->property (X_("state"))) == 0) {
+                               error << _("Missing state property on AutomationState") << endmsg;
+                               return -1;
+                       }
+
+                       AutoState s = static_cast<AutoState> (string_2_enum (prop->value(), s));
+                       set_automation_state_of (p, s);
                }
        }
 
@@ -290,6 +315,7 @@ MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
         
         newsrc->set_timeline_position(_timeline_position);
        newsrc->copy_interpolation_from (this);
+       newsrc->copy_automation_state_from (this);
 
         if (_model) {
                 if (begin == Evoral::MinMusicalTime && end == Evoral::MaxMusicalTime) {
@@ -385,6 +411,17 @@ MidiSource::interpolation_of (Evoral::Parameter p) const
        return i->second;
 }
 
+AutoState
+MidiSource::automation_state_of (Evoral::Parameter p) const
+{
+       AutomationStateMap::const_iterator i = _automation_state.find (p);
+       if (i == _automation_state.end()) {
+               return Off;
+       }
+
+       return i->second;
+}
+
 /** Set interpolation style to be used for a given parameter.  This change will be
  *  propagated to anyone who needs to know.
  */
@@ -405,12 +442,35 @@ MidiSource::set_interpolation_of (Evoral::Parameter p, Evoral::ControlList::Inte
        InterpolationChanged (p, s); /* EMIT SIGNAL */
 }
 
+void
+MidiSource::set_automation_state_of (Evoral::Parameter p, AutoState s)
+{
+       if (automation_state_of (p) == s) {
+               return;
+       }
+       
+       if (s == Off) {
+               /* automation state is being set to the default, so we don't need a note in our map */
+               _automation_state.erase (p);
+       } else {
+               _automation_state[p] = s;
+       }
+
+       AutomationStateChanged (p, s); /* EMIT SIGNAL */
+}
+
 void
 MidiSource::copy_interpolation_from (boost::shared_ptr<MidiSource> s)
 {
        copy_interpolation_from (s.get ());
 }
 
+void
+MidiSource::copy_automation_state_from (boost::shared_ptr<MidiSource> s)
+{
+       copy_automation_state_from (s.get ());
+}
+
 void
 MidiSource::copy_interpolation_from (MidiSource* s)
 {
@@ -418,3 +478,11 @@ MidiSource::copy_interpolation_from (MidiSource* s)
 
        /* XXX: should probably emit signals here */
 }
+
+void
+MidiSource::copy_automation_state_from (MidiSource* s)
+{
+       _automation_state = s->_automation_state;
+
+       /* XXX: should probably emit signals here */
+}
index fa481567878ef674cc3b815fc17791f15c0af389..d1f09dd0a8d07dd80d563da63709f6e33c5062a9 100644 (file)
@@ -114,7 +114,7 @@ PluginInsert::~PluginInsert ()
 }
 
 void
-PluginInsert::auto_state_changed (Evoral::Parameter which)
+PluginInsert::control_list_automation_state_changed (Evoral::Parameter which, AutoState s)
 {
        if (which.type() != PluginAutomation)
                return;
@@ -122,7 +122,7 @@ PluginInsert::auto_state_changed (Evoral::Parameter which)
        boost::shared_ptr<AutomationControl> c
                        = boost::dynamic_pointer_cast<AutomationControl>(control (which));
 
-       if (c && ((AutomationList*)c->list().get())->automation_state() != Off) {
+       if (c && s != Off) {
                _plugins[0]->set_parameter (which.id(), c->list()->eval (_session.transport_frame()));
        }
 }
index 090499e83835a731a47b83989796e190e7a369a0..9dd5b94b3be35be4fa3b71fc4a05c78de3c21ca2 100644 (file)
@@ -74,10 +74,11 @@ protected:
        mutable Glib::Mutex _control_lock;
        Controls            _controls;
 
+       PBD::ScopedConnectionList _list_connections;
+       
 private:
 
        PBD::ScopedConnectionList _control_connections;
-       PBD::ScopedConnectionList _list_connections;
 };