diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2006-10-31 02:40:08 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2006-10-31 02:40:08 +0000 |
commit | 71c94e69438c7c282b2dcac5ead080119944b290 (patch) | |
tree | 17bc6b75ff24c0eca6a7a45043f2c9d11cc2ff0c /libs | |
parent | 74df5d49c8ff42c05d7eb9300c3a9f9a7257e694 (diff) |
massive changes in automation state handling, not entirely complete; some bug fixes for automation line drawing
git-svn-id: svn://localhost/ardour2/trunk@1034 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r-- | libs/ardour/ardour/audio_track.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/automation_event.h | 11 | ||||
-rw-r--r-- | libs/ardour/ardour/curve.h | 1 | ||||
-rw-r--r-- | libs/ardour/ardour/insert.h | 1 | ||||
-rw-r--r-- | libs/ardour/ardour/io.h | 18 | ||||
-rw-r--r-- | libs/ardour/ardour/route.h | 6 | ||||
-rw-r--r-- | libs/ardour/ardour/types.h | 6 | ||||
-rw-r--r-- | libs/ardour/audio_track.cc | 24 | ||||
-rw-r--r-- | libs/ardour/automation_event.cc | 225 | ||||
-rw-r--r-- | libs/ardour/curve.cc | 12 | ||||
-rw-r--r-- | libs/ardour/insert.cc | 17 | ||||
-rw-r--r-- | libs/ardour/io.cc | 76 | ||||
-rw-r--r-- | libs/ardour/panner.cc | 37 | ||||
-rw-r--r-- | libs/ardour/redirect.cc | 56 | ||||
-rw-r--r-- | libs/ardour/route.cc | 37 | ||||
-rw-r--r-- | libs/ardour/session.cc | 10 | ||||
-rw-r--r-- | libs/ardour/session_command.cc | 28 | ||||
-rw-r--r-- | libs/ardour/session_state.cc | 1 | ||||
-rw-r--r-- | libs/ardour/track.cc | 4 | ||||
-rw-r--r-- | libs/ardour/utils.cc | 65 | ||||
-rw-r--r-- | libs/pbd/pbd/xml++.h | 3 | ||||
-rw-r--r-- | libs/pbd/xml++.cc | 29 |
22 files changed, 487 insertions, 182 deletions
diff --git a/libs/ardour/ardour/audio_track.h b/libs/ardour/ardour/audio_track.h index a074732c39..66fcd16a56 100644 --- a/libs/ardour/ardour/audio_track.h +++ b/libs/ardour/ardour/audio_track.h @@ -69,6 +69,8 @@ class AudioTrack : public Track bool meter); uint32_t n_process_buffers (); + + int _set_state (const XMLNode&, bool call_base); private: int set_diskstream (boost::shared_ptr<AudioDiskstream>, void *); diff --git a/libs/ardour/ardour/automation_event.h b/libs/ardour/ardour/automation_event.h index fcc9bb5e91..24d73faaa4 100644 --- a/libs/ardour/ardour/automation_event.h +++ b/libs/ardour/ardour/automation_event.h @@ -60,7 +60,8 @@ class AutomationList : public PBD::StatefulDestructible typedef AutomationEventList::iterator iterator; typedef AutomationEventList::const_iterator const_iterator; - AutomationList(double default_value, bool no_state = false); + AutomationList (double default_value); + AutomationList (const XMLNode&); ~AutomationList(); AutomationList (const AutomationList&); @@ -151,8 +152,10 @@ class AutomationList : public PBD::StatefulDestructible sigc::signal<void,Change> StateChanged; - XMLNode &get_state(void); + XMLNode& get_state(void); int set_state (const XMLNode &s); + XMLNode& state (bool full); + XMLNode& serialize_events (); void set_max_xval (double); double get_max_xval() const { return max_xval; } @@ -204,11 +207,11 @@ class AutomationList : public PBD::StatefulDestructible double min_yval; double max_yval; double default_value; - bool no_state; iterator rt_insertion_point; double rt_pos; + void fast_simple_add (double when, double value); void maybe_signal_changed (); void mark_dirty (); void _x_scale (double factor); @@ -235,6 +238,8 @@ class AutomationList : public PBD::StatefulDestructible virtual ControlEvent* point_factory (const ControlEvent&) const; AutomationList* cut_copy_clear (double, double, int op); + + int deserialize_events (const XMLNode&); }; } // namespace diff --git a/libs/ardour/ardour/curve.h b/libs/ardour/ardour/curve.h index 97601e29ee..df984b74e0 100644 --- a/libs/ardour/ardour/curve.h +++ b/libs/ardour/ardour/curve.h @@ -51,6 +51,7 @@ class Curve : public AutomationList ~Curve (); Curve (const Curve& other); Curve (const Curve& other, double start, double end); + Curve (const XMLNode&); bool rt_safe_get_vector (double x0, double x1, float *arg, int32_t veclen); void get_vector (double x0, double x1, float *arg, int32_t veclen); diff --git a/libs/ardour/ardour/insert.h b/libs/ardour/ardour/insert.h index 885cab4457..61bee6c803 100644 --- a/libs/ardour/ardour/insert.h +++ b/libs/ardour/ardour/insert.h @@ -144,6 +144,7 @@ class PluginInsert : public Insert nframes_t latency(); void transport_stopped (nframes_t now); + void automation_snapshot (nframes_t now); private: diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index b078b3488d..2783ee4140 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -67,9 +67,11 @@ class IO : public PBD::StatefulDestructible IO (Session&, string name, int input_min = -1, int input_max = -1, int output_min = -1, int output_max = -1, - DataType default_type = DataType::AUDIO); + DataType default_type = DataType::AUDIO); -virtual ~IO(); + IO (Session&, const XMLNode&, DataType default_type = DataType::AUDIO); + + virtual ~IO(); int input_minimum() const { return _input_minimum; } int input_maximum() const { return _input_maximum; } @@ -205,6 +207,14 @@ public: /* automation */ + static void set_automation_interval (jack_nframes_t frames) { + _automation_interval = frames; + } + + static jack_nframes_t automation_interval() { + return _automation_interval; + } + void clear_automation (); bool gain_automation_recording() const { @@ -226,6 +236,7 @@ public: sigc::signal<void> gain_automation_style_changed; virtual void transport_stopped (nframes_t now); + void automation_snapshot (nframes_t now); ARDOUR::Curve& gain_automation_curve () { return _gain_automation_curve; } @@ -289,6 +300,9 @@ public: GainControllable _gain_control; + nframes_t last_automation_snapshot; + static nframes_t _automation_interval; + AutoState _gain_automation_state; AutoStyle _gain_automation_style; diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index c8b135fb6b..21638e64c2 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -72,8 +72,7 @@ class Route : public IO Route (Session&, std::string name, int input_min, int input_max, int output_min, int output_max, Flag flags = Flag(0), DataType default_type = DataType::AUDIO); - - Route (Session&, const XMLNode&); + Route (Session&, const XMLNode&, DataType default_type = DataType::AUDIO); virtual ~Route(); std::string comment() { return _comment; } @@ -233,6 +232,7 @@ class Route : public IO return _mute_control; } + void automation_snapshot (nframes_t now); void protect_automation (); void set_remote_control_id (uint32_t id); @@ -315,6 +315,8 @@ class Route : public IO uint32_t pans_required() const; uint32_t n_process_buffers (); + virtual int _set_state (const XMLNode&, bool call_base); + private: void init (); diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index c80944ebd0..32b3231c4e 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -91,11 +91,17 @@ namespace ARDOUR { Play = 0x4 }; + std::string auto_state_to_string (AutoState); + AutoState string_to_auto_state (std::string); + enum AutoStyle { Absolute = 0x1, Trim = 0x2 }; + std::string auto_style_to_string (AutoStyle); + AutoStyle string_to_auto_style (std::string); + enum AlignStyle { CaptureTime, ExistingMaterial diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index 69e183b669..9c7f29dbf2 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -65,7 +65,7 @@ AudioTrack::AudioTrack (Session& sess, string name, Route::Flag flag, TrackMode AudioTrack::AudioTrack (Session& sess, const XMLNode& node) : Track (sess, node) { - set_state (node); + _set_state (node, false); } AudioTrack::~AudioTrack () @@ -188,11 +188,19 @@ AudioTrack::audio_diskstream() const int AudioTrack::set_state (const XMLNode& node) { + return _set_state (node, true); +} + +int +AudioTrack::_set_state (const XMLNode& node, bool call_base) +{ const XMLProperty *prop; XMLNodeConstIterator iter; - if (Route::set_state (node)) { - return -1; + if (call_base) { + if (Route::set_state (node)) { + return -1; + } } if ((prop = node.property (X_("mode"))) != 0) { @@ -507,6 +515,16 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t transport_frame; boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream(); + { + Glib::RWLock::ReaderLock lm (redirect_lock, Glib::TRY_LOCK); + if (lm.locked()) { + // automation snapshot can also be called from the non-rt context + // and it uses the redirect list, so we take the lock out here + automation_snapshot (start_frame); + } + } + + if (n_outputs() == 0 && _redirects.empty()) { return 0; } diff --git a/libs/ardour/automation_event.cc b/libs/ardour/automation_event.cc index f94a1f7cc3..dfa3d078a8 100644 --- a/libs/ardour/automation_event.cc +++ b/libs/ardour/automation_event.cc @@ -47,14 +47,13 @@ static void dumpit (const AutomationList& al, string prefix = "") } #endif -AutomationList::AutomationList (double defval, bool with_state) +AutomationList::AutomationList (double defval) { _frozen = false; changed_when_thawed = false; _state = Off; _style = Absolute; _touching = false; - no_state = with_state; min_yval = FLT_MIN; max_yval = FLT_MAX; max_xval = 0; // means "no limit" @@ -80,7 +79,6 @@ AutomationList::AutomationList (const AutomationList& other) _touching = other._touching; _dirty = false; rt_insertion_point = events.end(); - no_state = other.no_state; lookup_cache.left = -1; lookup_cache.range.first = events.end(); @@ -108,7 +106,6 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl _touching = other._touching; _dirty = false; rt_insertion_point = events.end(); - no_state = other.no_state; lookup_cache.left = -1; lookup_cache.range.first = events.end(); @@ -125,13 +122,34 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl delete section; mark_dirty (); + + AutomationListCreated(this); +} + +AutomationList::AutomationList (const XMLNode& node) +{ + _frozen = false; + changed_when_thawed = false; + _touching = false; + min_yval = FLT_MIN; + max_yval = FLT_MAX; + max_xval = 0; // means "no limit" + _dirty = false; + _state = Off; + _style = Absolute; + rt_insertion_point = events.end(); + lookup_cache.left = -1; + lookup_cache.range.first = events.end(); + + set_state (node); + AutomationListCreated(this); } AutomationList::~AutomationList() { GoingAway (); - + for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) { delete (*x); } @@ -346,12 +364,19 @@ AutomationList::rt_add (double when, double value) maybe_signal_changed (); } +void +AutomationList::fast_simple_add (double when, double value) +{ + /* to be used only for loading pre-sorted data from saved state */ + events.insert (events.end(), point_factory (when, value)); +} + #undef last_rt_insertion_point void AutomationList::add (double when, double value) { - /* this is for graphical editing and loading data from storage */ + /* this is for graphical editing */ { Glib::Mutex::Lock lm (lock); @@ -395,11 +420,6 @@ AutomationList::erase (AutomationList::iterator i) Glib::Mutex::Lock lm (lock); events.erase (i); reposition_for_rt_add (0); - if (!no_state) { -#ifdef STATE_MANAGER - save_state (_("removed event")); -#endif - } mark_dirty (); } maybe_signal_changed (); @@ -1118,66 +1138,179 @@ AutomationList::point_factory (const ControlEvent& other) const XMLNode& AutomationList::get_state () { - stringstream str; - XMLNode* node = new XMLNode (X_("events")); - iterator xx; + return state (true); +} - if (events.empty()) { - return *node; +XMLNode& +AutomationList::state (bool full) +{ + XMLNode* root = new XMLNode (X_("AutomationList")); + char buf[64]; + LocaleGuard lg (X_("POSIX")); + + root->add_property ("id", _id.to_s()); + + snprintf (buf, sizeof (buf), "%.12g", default_value); + root->add_property ("default", buf); + snprintf (buf, sizeof (buf), "%.12g", min_yval); + root->add_property ("min_yval", buf); + snprintf (buf, sizeof (buf), "%.12g", max_yval); + root->add_property ("max_yval", buf); + snprintf (buf, sizeof (buf), "%.12g", max_xval); + root->add_property ("max_xval", buf); + + if (full) { + root->add_property ("state", auto_state_to_string (_state)); + } else { + /* never save anything but Off for automation state to a template */ + root->add_property ("state", auto_state_to_string (Off)); } - for (xx = events.begin(); xx != events.end(); ++xx) { + root->add_property ("style", auto_style_to_string (_style)); + + if (!events.empty()) { + root->add_child_nocopy (serialize_events()); + } + + return *root; +} + +XMLNode& +AutomationList::serialize_events () +{ + XMLNode* node = new XMLNode (X_("events")); + stringstream str; + + for (iterator xx = events.begin(); xx != events.end(); ++xx) { str << (double) (*xx)->when; str << ' '; str <<(double) (*xx)->value; str << '\n'; } - node->add_content (str.str()); + /* XML is a bit wierd */ + + XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */ + content_node->set_content (str.str()); + + node->add_child_nocopy (*content_node); return *node; } int -AutomationList::set_state (const XMLNode& node) +AutomationList::deserialize_events (const XMLNode& node) { - if (node.name() != X_("events")) { - warning << _("automation list: passed XML node not called \"events\" - ignored.") << endmsg; + if (node.children().empty()) { + return -1; + } + + XMLNode* content_node = node.children().front(); + + if (content_node->content().empty()) { return -1; } freeze (); clear (); - - 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); + + stringstream str (content_node->content()); + + double x; + double y; + bool ok = true; + + while (str) { + str >> x; + if (!str) { + break; } - - if (!ok) { - clear (); - error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg; + str >> y; + if (!str) { + ok = false; + break; } + fast_simple_add (x, y); + } + + if (!ok) { + clear (); + error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg; + } else { + mark_dirty (); + reposition_for_rt_add (0); + maybe_signal_changed (); } thaw (); return 0; } +int +AutomationList::set_state (const XMLNode& node) +{ + XMLNodeList nlist = node.children(); + XMLNodeIterator niter; + const XMLProperty* prop; + + if (node.name() == X_("events")) { + /* partial state setting*/ + return deserialize_events (node); + } + + if (node.name() != X_("AutomationList") ) { + error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg; + return -1; + } + + if ((prop = node.property ("id")) != 0) { + _id = prop->value (); + /* update session AL list */ + AutomationListCreated(this); + } + + if ((prop = node.property (X_("default"))) != 0){ + default_value = atof (prop->value()); + } else { + default_value = 0.0; + } + + if ((prop = node.property (X_("style"))) != 0) { + _style = string_to_auto_style (prop->value()); + } else { + _style = Absolute; + } + + if ((prop = node.property (X_("state"))) != 0) { + _state = string_to_auto_state (prop->value()); + } else { + _state = Off; + } + + if ((prop = node.property (X_("min_yval"))) != 0) { + min_yval = atof (prop->value ()); + } else { + min_yval = FLT_MIN; + } + + if ((prop = node.property (X_("max_yval"))) != 0) { + max_yval = atof (prop->value ()); + } else { + max_yval = FLT_MAX; + } + + if ((prop = node.property (X_("max_xval"))) != 0) { + max_xval = atof (prop->value ()); + } else { + max_xval = 0; // means "no limit ; + } + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + if ((*niter)->name() == X_("events")) { + deserialize_events (*(*niter)); + } + } + + return 0; +} + diff --git a/libs/ardour/curve.cc b/libs/ardour/curve.cc index c9c6db7baf..8465094775 100644 --- a/libs/ardour/curve.cc +++ b/libs/ardour/curve.cc @@ -40,14 +40,11 @@ using namespace ARDOUR; using namespace sigc; using namespace PBD; -sigc::signal<void, Curve*> Curve::CurveCreated; - Curve::Curve (double minv, double maxv, double canv, bool nostate) - : AutomationList (canv, nostate) + : AutomationList (canv) { min_yval = minv; max_yval = maxv; - CurveCreated(this); } Curve::Curve (const Curve& other) @@ -55,7 +52,6 @@ Curve::Curve (const Curve& other) { min_yval = other.min_yval; max_yval = other.max_yval; - CurveCreated(this); } Curve::Curve (const Curve& other, double start, double end) @@ -63,7 +59,11 @@ Curve::Curve (const Curve& other, double start, double end) { min_yval = other.min_yval; max_yval = other.max_yval; - CurveCreated(this); +} + +Curve::Curve (const XMLNode& node) + : AutomationList (node) +{ } Curve::~Curve () diff --git a/libs/ardour/insert.cc b/libs/ardour/insert.cc index ee8fdbcec0..77dd67a240 100644 --- a/libs/ardour/insert.cc +++ b/libs/ardour/insert.cc @@ -311,6 +311,23 @@ PluginInsert::connect_and_run (vector<Sample*>& bufs, uint32_t nbufs, nframes_t } void +PluginInsert::automation_snapshot (nframes_t now) +{ + map<uint32_t,AutomationList*>::iterator li; + + for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) { + + AutomationList *alist = ((*li).second); + if (alist != 0 && alist->automation_write ()) { + + float val = _plugins[0]->get_parameter ((*li).first); + alist->rt_add (now, val); + last_automation_snapshot = now; + } + } +} + +void PluginInsert::transport_stopped (nframes_t now) { map<uint32_t,AutomationList*>::iterator li; diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 6cc279a046..02adc3d53d 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -58,6 +58,7 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +nframes_t IO::_automation_interval = 0; const string IO::state_node_name = "IO"; bool IO::connecting_legal = false; bool IO::ports_legal = false; @@ -124,6 +125,8 @@ IO::IO (Session& s, string name, apply_gain_automation = false; _ignore_gain_on_deliver = false; + last_automation_snapshot = 0; + _gain_automation_state = Off; _gain_automation_style = Absolute; @@ -137,6 +140,38 @@ IO::IO (Session& s, string name, _session.add_controllable (&_gain_control); } +IO::IO (Session& s, const XMLNode& node, DataType dt) + : _session (s), + _default_type (dt), + _gain_control (X_("gaincontrol"), *this), + _gain_automation_curve (0, 0, 0) // all reset in set_state() +{ + _panner = 0; + deferred_state = 0; + no_panner_reset = false; + _desired_gain = 1.0; + _gain = 1.0; + _input_connection = 0; + _output_connection = 0; + _ninputs = 0; + _noutputs = 0; + + apply_gain_automation = false; + _ignore_gain_on_deliver = false; + + set_state (node); + + { + // IO::Meter is emitted from another thread so the + // Meter signal must be protected. + 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); @@ -1535,15 +1570,9 @@ IO::state (bool full_state) snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off); } - node->add_property ("automation-state", buf); - snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_style()); - node->add_property ("automation-style", buf); - return *node; } - - int IO::set_state (const XMLNode& node) { @@ -1585,6 +1614,9 @@ IO::set_state (const XMLNode& node) for (iter = node.children().begin(); iter != node.children().end(); ++iter) { if ((*iter)->name() == "Panner") { + if (_panner == 0) { + _panner = new Panner (_name, _session); + } _panner->set_state (**iter); } @@ -1598,20 +1630,6 @@ IO::set_state (const XMLNode& node) } } - if ((prop = node.property ("automation-state")) != 0) { - - long int x; - x = strtol (prop->value().c_str(), 0, 16); - set_gain_automation_state (AutoState (x)); - } - - if ((prop = node.property ("automation-style")) != 0) { - - long int x; - x = strtol (prop->value().c_str(), 0, 16); - set_gain_automation_style (AutoStyle (x)); - } - if (ports_legal) { if (create_ports (node)) { @@ -1644,6 +1662,8 @@ IO::set_state (const XMLNode& node) pending_state_node = new XMLNode (node); } + last_automation_snapshot = 0; + return 0; } @@ -2398,6 +2418,7 @@ IO::set_gain_automation_state (AutoState state) if (state != _gain_automation_curve.automation_state()) { changed = true; + last_automation_snapshot = 0; _gain_automation_curve.set_automation_state (state); if (state != Off) { @@ -2496,6 +2517,21 @@ IO::end_pan_touch (uint32_t which) } void +IO::automation_snapshot (nframes_t now) +{ + if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) { + + if (gain_automation_recording()) { + _gain_automation_curve.rt_add (now, gain()); + } + + _panner->snapshot (now); + + last_automation_snapshot = now; + } +} + +void IO::transport_stopped (nframes_t frame) { _gain_automation_curve.reposition_for_rt_add (frame); diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index faf2e03e2f..306757297d 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -473,20 +473,12 @@ EqualPowerStereoPanner::state (bool full_state) root->add_property (X_("x"), buf); root->add_property (X_("type"), EqualPowerStereoPanner::name); - if (full_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); + XMLNode* autonode = new XMLNode (X_("Automation")); + autonode->add_child_nocopy (_automation.state (full_state)); + root->add_child_nocopy (*autonode); StreamPanner::add_state (*root); + root->add_child_nocopy (_control.get_state ()); return *root; @@ -496,7 +488,6 @@ int EqualPowerStereoPanner::set_state (const XMLNode& node) { const XMLProperty* prop; - int x; float pos; LocaleGuard lg (X_("POSIX")); @@ -508,27 +499,21 @@ EqualPowerStereoPanner::set_state (const XMLNode& node) 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)->children().front())); - } - } - - if ((prop = node.property (X_("automation-state")))) { - sscanf (prop->value().c_str(), "0x%x", &x); - _automation.set_automation_state ((AutoState) x); - if (x != Off) { - set_position (_automation.eval (parent.session().transport_frame())); + if (_automation.automation_state() != Off) { + set_position (_automation.eval (parent.session().transport_frame())); + } } } - if ((prop = node.property (X_("automation-style")))) { - sscanf (prop->value().c_str(), "0x%x", &x); - _automation.set_automation_style ((AutoStyle) x); - } - return 0; } diff --git a/libs/ardour/redirect.cc b/libs/ardour/redirect.cc index 710b00fe18..5623e5d510 100644 --- a/libs/ardour/redirect.cc +++ b/libs/ardour/redirect.cc @@ -108,23 +108,23 @@ Redirect::set_placement (const string& str, void *src) } } +/* NODE STRUCTURE + + <Automation [optionally with visible="...." ]> + <parameter-N> + <AutomationList id=N> + <events> + X1 Y1 + X2 Y2 + .... + </events> + </parameter-N> + <Automation> +*/ + int Redirect::set_automation_state (const XMLNode& node) { - /* NODE STRUCTURE - - <Automation [optionally with visible="...." ]> - <parameter-N> - <events> - X1 Y1 - X2 Y2 - .... - </events> - </parameter-N> - <Automation> - - */ - Glib::Mutex::Lock lm (_automation_lock); parameter_automation.clear (); @@ -157,20 +157,6 @@ Redirect::set_automation_state (const XMLNode& node) XMLNode& Redirect::get_automation_state () { - /* NODE STRUCTURE - - <Automation [optionally with visible="...." ]> - <parameter-N> - <events> - X1 Y1 - X2 Y2 - .... - </events> - </parameter-N> - <Automation> - - */ - Glib::Mutex::Lock lm (_automation_lock); XMLNode* node = new XMLNode (X_("Automation")); string fullpath; @@ -217,20 +203,6 @@ Redirect::state (bool full_state) if (full_state) { - /* NODE STRUCTURE - - <Automation [optionally with visible="...." ]> - <parameter-N> - <events> - X1 Y1 - X2 Y2 - .... - </events> - </parameter-N> - <Automation> - - */ - XMLNode& automation = get_automation_state(); for (set<uint32_t>::iterator x = visible_parameter_automation.begin(); x != visible_parameter_automation.end(); ++x) { diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 5c4be73979..b204ad76dc 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -61,13 +61,13 @@ Route::Route (Session& sess, string name, int input_min, int input_max, int outp init (); } -Route::Route (Session& sess, const XMLNode& node) - : IO (sess, "route"), +Route::Route (Session& sess, const XMLNode& node, DataType default_type) + : IO (sess, *node.child ("IO"), default_type), _solo_control (X_("solo"), *this, ToggleControllable::SoloControl), _mute_control (X_("mute"), *this, ToggleControllable::MuteControl) { init (); - set_state (node); + _set_state (node, false); } void @@ -1482,6 +1482,12 @@ Route::add_redirect_from_xml (const XMLNode& node) int Route::set_state (const XMLNode& node) { + return _set_state (node, true); +} + +int +Route::_set_state (const XMLNode& node, bool call_base) +{ XMLNodeList nlist; XMLNodeConstIterator niter; XMLNode *child; @@ -1604,7 +1610,7 @@ Route::set_state (const XMLNode& node) child = *niter; - if (child->name() == IO::state_node_name) { + if (child->name() == IO::state_node_name && call_base) { IO::set_state (*child); break; @@ -1925,6 +1931,10 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f { Glib::RWLock::ReaderLock lm (redirect_lock); + if (!did_locate) { + automation_snapshot (now); + } + for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { if (Config->get_plugins_stop_with_transport() && can_flush_redirects) { @@ -2024,6 +2034,15 @@ int Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, int declick, bool can_record, bool rec_monitors_input) { + { + Glib::RWLock::ReaderLock lm (redirect_lock, Glib::TRY_LOCK); + if (lm.locked()) { + // automation snapshot can also be called from the non-rt context + // and it uses the redirect list, so we take the lock out here + automation_snapshot (_session.transport_frame()); + } + } + if ((n_outputs() == 0 && _redirects.empty()) || n_inputs() == 0 || !_active) { silence (nframes, offset); return 0; @@ -2160,6 +2179,16 @@ Route::set_latency_delay (nframes_t longest_session_latency) } } +void +Route::automation_snapshot (nframes_t now) +{ + IO::automation_snapshot (now); + + for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + (*i)->automation_snapshot (now); + } +} + Route::ToggleControllable::ToggleControllable (std::string name, Route& s, ToggleType tp) : Controllable (name), route (s), type(tp) { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index fddd289882..98d671281a 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -1321,6 +1321,8 @@ Session::set_frame_rate (nframes_t frames_per_second) sync_time_vars(); + Route::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * 0.25)); + // XXX we need some equivalent to this, somehow // DestructiveFileSource::setup_standard_crossfades (frames_per_second); @@ -3761,13 +3763,7 @@ Session::nbusses () const } void -Session::add_curve(Curve *curve) -{ - curves[curve->id()] = curve; -} - -void Session::add_automation_list(AutomationList *al) { - automation_lists[al->id()] = al; + automation_lists[al->id()] = al; } diff --git a/libs/ardour/session_command.cc b/libs/ardour/session_command.cc index 1492136b88..3d2da887b0 100644 --- a/libs/ardour/session_command.cc +++ b/libs/ardour/session_command.cc @@ -3,6 +3,8 @@ #include <pbd/memento_command.h> #include <ardour/diskstream.h> #include <ardour/playlist.h> +#include <ardour/audioplaylist.h> +#include <ardour/audio_track.h> #include <ardour/tempo.h> #include <ardour/audiosource.h> #include <ardour/audioregion.h> @@ -53,30 +55,26 @@ Command *Session::memento_command_factory(XMLNode *n) return 0; } - /* create command */ - string obj_T = n->children().front()->name(); - if (obj_T == "AudioRegion" || obj_T == "Region") { + string obj_T = n->property ("type_name")->value(); + if (obj_T == typeid (AudioRegion).name() || obj_T == typeid (Region).name()) { if (audio_regions.count(id)) return new MementoCommand<AudioRegion>(*audio_regions[id], before, after); - } else if (obj_T == "AudioSource") { + } else if (obj_T == typeid (AudioSource).name()) { if (audio_sources.count(id)) return new MementoCommand<AudioSource>(*audio_sources[id], before, after); - } else if (obj_T == "Location") { + } else if (obj_T == typeid (Location).name()) { return new MementoCommand<Location>(*_locations.get_location_by_id(id), before, after); - } else if (obj_T == "Locations") { + } else if (obj_T == typeid (Locations).name()) { return new MementoCommand<Locations>(_locations, before, after); - } else if (obj_T == "TempoMap") { + } else if (obj_T == typeid (TempoMap).name()) { return new MementoCommand<TempoMap>(*_tempo_map, before, after); - } else if (obj_T == "Playlist" || obj_T == "AudioPlaylist") { + } else if (obj_T == typeid (Playlist).name() || obj_T == typeid (AudioPlaylist).name()) { if (Playlist *pl = playlist_by_name(child->property("name")->value())) return new MementoCommand<Playlist>(*pl, before, after); - } else if (obj_T == "Route") { // includes AudioTrack + } else if (obj_T == typeid (Route).name() || obj_T == typeid (AudioTrack).name()) { return new MementoCommand<Route>(*route_by_id(id), before, after); - } else if (obj_T == "Curve") { - if (curves.count(id)) - return new MementoCommand<Curve>(*curves[id], before, after); - } else if (obj_T == "AutomationList") { + } else if (obj_T == typeid (Curve).name() || obj_T == typeid (AutomationList).name()) { if (automation_lists.count(id)) return new MementoCommand<AutomationList>(*automation_lists[id], before, after); } else if (registry.count(id)) { // For Editor and AutomationLine which are off-limits here @@ -84,8 +82,8 @@ Command *Session::memento_command_factory(XMLNode *n) } /* we failed */ - error << _("could not reconstitute MementoCommand from XMLNode. id=") << id.to_s() << endmsg; - return 0; + error << string_compose (_("could not reconstitute MementoCommand from XMLNode. object type = %1 id = %2"), obj_T, id.to_s()) << endmsg; + return 0 ; } // solo diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 8a49636941..c69908285c 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -240,7 +240,6 @@ Session::first_stage_init (string fullpath, string snapshot_name) Playlist::PlaylistCreated.connect (mem_fun (*this, &Session::add_playlist)); Redirect::RedirectCreated.connect (mem_fun (*this, &Session::add_redirect)); NamedSelection::NamedSelectionCreated.connect (mem_fun (*this, &Session::add_named_selection)); - Curve::CurveCreated.connect (mem_fun (*this, &Session::add_curve)); AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list)); Controllable::Destroyed.connect (mem_fun (*this, &Session::remove_controllable)); diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 6d959ed88d..2c3d7c9151 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -50,8 +50,8 @@ Track::Track (Session& sess, string name, Route::Flag flag, TrackMode mode, Data } Track::Track (Session& sess, const XMLNode& node, DataType default_type) - : Route (sess, "to be renamed", 0, 0, -1, -1, Route::Flag(0), default_type) - , _rec_enable_control (*this) + : Route (sess, node), + _rec_enable_control (*this) { _freeze_record.state = NoFreeze; _declickable = true; diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index d8642486d9..f55759f812 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -398,3 +398,68 @@ meter_hold_to_float (MeterHold hold) return 200.0f; } } + +AutoState +ARDOUR::string_to_auto_state (std::string str) +{ + if (str == X_("Off")) { + return Off; + } else if (str == X_("Play")) { + return Play; + } else if (str == X_("Write")) { + return Write; + } else if (str == X_("Touch")) { + return Touch; + } + + fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState string: ", str) << endmsg; + /*NOTREACHED*/ +} + +string +ARDOUR::auto_state_to_string (AutoState as) +{ + /* to be used only for XML serialization, no i18n done */ + + switch (as) { + case Off: + return X_("Off"); + break; + case Play: + return X_("Play"); + break; + case Write: + return X_("Write"); + break; + case Touch: + return X_("Touch"); + } +} + +AutoStyle +ARDOUR::string_to_auto_style (std::string str) +{ + if (str == X_("Absolute")) { + return Absolute; + } else if (str == X_("Trim")) { + return Trim; + } + + fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle string: ", str) << endmsg; + /*NOTREACHED*/ +} + +string +ARDOUR::auto_style_to_string (AutoStyle as) +{ + /* to be used only for XML serialization, no i18n done */ + + switch (as) { + case Absolute: + return X_("Absolute"); + break; + case Trim: + return X_("Trim"); + break; + } +} diff --git a/libs/pbd/pbd/xml++.h b/libs/pbd/pbd/xml++.h index 5dcb4f084a..70e231e717 100644 --- a/libs/pbd/pbd/xml++.h +++ b/libs/pbd/pbd/xml++.h @@ -87,9 +87,10 @@ public: const string & set_content (const string &); XMLNode *add_content(const string & = string()); - const XMLNodeList & children (const string & = string()) const; + const XMLNodeList & children (const string& str = string()) const; XMLNode *add_child (const char *); XMLNode *add_child_copy (const XMLNode&); + XMLNode *child (const char*) const; void add_child_nocopy (XMLNode&); const XMLPropertyList & properties() const { return _proplist; }; diff --git a/libs/pbd/xml++.cc b/libs/pbd/xml++.cc index 03fa116279..1b10be89f5 100644 --- a/libs/pbd/xml++.cc +++ b/libs/pbd/xml++.cc @@ -216,13 +216,38 @@ XMLNode::set_content(const string & c) return _content; } +XMLNode* +XMLNode::child (const char *name) const +{ + /* returns first child matching name */ + + static XMLNodeList retval; + XMLNodeConstIterator cur; + + if (name == 0) { + return 0; + } + + retval.erase(retval.begin(), retval.end()); + + for (cur = _children.begin(); cur != _children.end(); ++cur) { + if ((*cur)->name() == name) { + return *cur; + } + } + + return 0; +} + const XMLNodeList & -XMLNode::children(const string & n) const +XMLNode::children(const string& n) const { + /* returns all children matching name */ + static XMLNodeList retval; XMLNodeConstIterator cur; - if (n.length() == 0) { + if (n.empty()) { return _children; } |