summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2006-10-31 02:40:08 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2006-10-31 02:40:08 +0000
commit71c94e69438c7c282b2dcac5ead080119944b290 (patch)
tree17bc6b75ff24c0eca6a7a45043f2c9d11cc2ff0c /libs
parent74df5d49c8ff42c05d7eb9300c3a9f9a7257e694 (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.h2
-rw-r--r--libs/ardour/ardour/automation_event.h11
-rw-r--r--libs/ardour/ardour/curve.h1
-rw-r--r--libs/ardour/ardour/insert.h1
-rw-r--r--libs/ardour/ardour/io.h18
-rw-r--r--libs/ardour/ardour/route.h6
-rw-r--r--libs/ardour/ardour/types.h6
-rw-r--r--libs/ardour/audio_track.cc24
-rw-r--r--libs/ardour/automation_event.cc225
-rw-r--r--libs/ardour/curve.cc12
-rw-r--r--libs/ardour/insert.cc17
-rw-r--r--libs/ardour/io.cc76
-rw-r--r--libs/ardour/panner.cc37
-rw-r--r--libs/ardour/redirect.cc56
-rw-r--r--libs/ardour/route.cc37
-rw-r--r--libs/ardour/session.cc10
-rw-r--r--libs/ardour/session_command.cc28
-rw-r--r--libs/ardour/session_state.cc1
-rw-r--r--libs/ardour/track.cc4
-rw-r--r--libs/ardour/utils.cc65
-rw-r--r--libs/pbd/pbd/xml++.h3
-rw-r--r--libs/pbd/xml++.cc29
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;
}