2 Copyright (C) 2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "ardour/automation_list.h"
27 #include "ardour/event_type_map.h"
28 #include "evoral/Curve.hpp"
29 #include "pbd/stacktrace.h"
30 #include "pbd/enumwriter.h"
35 using namespace ARDOUR
;
38 PBD::Signal1
<void,AutomationList
*> AutomationList::AutomationListCreated
;
41 static void dumpit (const AutomationList
& al
, string prefix
= "")
43 cerr
<< prefix
<< &al
<< endl
;
44 for (AutomationList::const_iterator i
= al
.const_begin(); i
!= al
.const_end(); ++i
) {
45 cerr
<< prefix
<< '\t' << (*i
)->when
<< ',' << (*i
)->value
<< endl
;
51 AutomationList::AutomationList (Evoral::Parameter id
)
58 create_curve_if_necessary();
60 assert(_parameter
.type() != NullAutomation
);
61 AutomationListCreated(this);
64 AutomationList::AutomationList (const AutomationList
& other
)
65 : StatefulDestructible()
68 _style
= other
._style
;
69 _state
= other
._state
;
70 _touching
= other
._touching
;
72 create_curve_if_necessary();
74 assert(_parameter
.type() != NullAutomation
);
75 AutomationListCreated(this);
78 AutomationList::AutomationList (const AutomationList
& other
, double start
, double end
)
79 : ControlList(other
, start
, end
)
81 _style
= other
._style
;
82 _state
= other
._state
;
83 _touching
= other
._touching
;
85 create_curve_if_necessary();
87 assert(_parameter
.type() != NullAutomation
);
88 AutomationListCreated(this);
91 /** @param id is used for legacy sessions where the type is not present
92 * in or below the AutomationList node. It is used if @param id is non-null.
94 AutomationList::AutomationList (const XMLNode
& node
, Evoral::Parameter id
)
101 set_state (node
, Stateful::loading_state_version
);
107 create_curve_if_necessary();
109 assert(_parameter
.type() != NullAutomation
);
110 AutomationListCreated(this);
113 AutomationList::~AutomationList()
117 boost::shared_ptr
<Evoral::ControlList
>
118 AutomationList::create(Evoral::Parameter id
)
120 return boost::shared_ptr
<Evoral::ControlList
>(new AutomationList(id
));
124 AutomationList::create_curve_if_necessary()
126 switch (_parameter
.type()) {
129 case FadeInAutomation
:
130 case FadeOutAutomation
:
131 case EnvelopeAutomation
:
140 AutomationList::operator== (const AutomationList
& other
)
142 return _events
== other
._events
;
146 AutomationList::operator= (const AutomationList
& other
)
148 if (this != &other
) {
152 for (const_iterator i
= other
._events
.begin(); i
!= other
._events
.end(); ++i
) {
153 _events
.push_back (new Evoral::ControlEvent (**i
));
156 _min_yval
= other
._min_yval
;
157 _max_yval
= other
._max_yval
;
158 _max_xval
= other
._max_xval
;
159 _default_value
= other
._default_value
;
162 maybe_signal_changed ();
169 AutomationList::maybe_signal_changed ()
171 ControlList::maybe_signal_changed ();
173 if (!ControlList::frozen()) {
174 StateChanged (); /* EMIT SIGNAL */
179 AutomationList::set_automation_state (AutoState s
)
183 automation_state_changed (); /* EMIT SIGNAL */
188 AutomationList::set_automation_style (AutoStyle s
)
192 automation_style_changed (); /* EMIT SIGNAL */
197 AutomationList::start_touch ()
204 AutomationList::stop_touch ()
211 AutomationList::thaw ()
215 if (_changed_when_thawed
) {
216 _changed_when_thawed
= false;
217 StateChanged(); /* EMIT SIGNAL */
222 AutomationList::get_state ()
228 AutomationList::state (bool full
)
230 XMLNode
* root
= new XMLNode (X_("AutomationList"));
232 LocaleGuard
lg (X_("POSIX"));
234 root
->add_property ("automation-id", EventTypeMap::instance().to_symbol(_parameter
));
236 root
->add_property ("id", _id
.to_s());
238 snprintf (buf
, sizeof (buf
), "%.12g", _default_value
);
239 root
->add_property ("default", buf
);
240 snprintf (buf
, sizeof (buf
), "%.12g", _min_yval
);
241 root
->add_property ("min-yval", buf
);
242 snprintf (buf
, sizeof (buf
), "%.12g", _max_yval
);
243 root
->add_property ("max-yval", buf
);
244 snprintf (buf
, sizeof (buf
), "%.12g", _max_xval
);
245 root
->add_property ("max-xval", buf
);
247 root
->add_property ("interpolation-style", enum_2_string (_interpolation
));
250 root
->add_property ("state", auto_state_to_string (_state
));
252 /* never save anything but Off for automation state to a template */
253 root
->add_property ("state", auto_state_to_string (Off
));
256 root
->add_property ("style", auto_style_to_string (_style
));
258 if (!_events
.empty()) {
259 root
->add_child_nocopy (serialize_events());
266 AutomationList::serialize_events ()
268 XMLNode
* node
= new XMLNode (X_("events"));
271 str
.precision(15); //10 digits is enough digits for 24 hours at 96kHz
273 for (iterator xx
= _events
.begin(); xx
!= _events
.end(); ++xx
) {
274 str
<< (double) (*xx
)->when
;
276 str
<<(double) (*xx
)->value
;
280 /* XML is a bit wierd */
282 XMLNode
* content_node
= new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
283 content_node
->set_content (str
.str());
285 node
->add_child_nocopy (*content_node
);
291 AutomationList::deserialize_events (const XMLNode
& node
)
293 if (node
.children().empty()) {
297 XMLNode
* content_node
= node
.children().front();
299 if (content_node
->content().empty()) {
303 ControlList::freeze ();
306 stringstream
str (content_node
->content());
322 fast_simple_add (x
, y
);
327 error
<< _("automation list: cannot load coordinates from XML, all points ignored") << endmsg
;
330 reposition_for_rt_add (0);
331 maybe_signal_changed ();
340 AutomationList::set_state (const XMLNode
& node
, int version
)
342 XMLNodeList nlist
= node
.children();
344 XMLNodeIterator niter
;
345 const XMLProperty
* prop
;
347 if (node
.name() == X_("events")) {
348 /* partial state setting*/
349 return deserialize_events (node
);
352 if (node
.name() == X_("Envelope") || node
.name() == X_("FadeOut") || node
.name() == X_("FadeIn")) {
354 if ((nsos
= node
.child (X_("AutomationList")))) {
355 /* new school in old school clothing */
356 return set_state (*nsos
, version
);
361 const XMLNodeList
& elist
= node
.children();
362 XMLNodeConstIterator i
;
367 ControlList::freeze ();
370 for (i
= elist
.begin(); i
!= elist
.end(); ++i
) {
372 if ((prop
= (*i
)->property ("x")) == 0) {
373 error
<< _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg
;
376 x
= atoi (prop
->value().c_str());
378 if ((prop
= (*i
)->property ("y")) == 0) {
379 error
<< _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg
;
382 y
= atof (prop
->value().c_str());
384 fast_simple_add (x
, y
);
392 if (node
.name() != X_("AutomationList") ) {
393 error
<< string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node
.name()) << endmsg
;
397 if ((prop
= node
.property ("id")) != 0) {
398 _id
= prop
->value ();
399 /* update session AL list */
400 AutomationListCreated(this);
403 if ((prop
= node
.property (X_("automation-id"))) != 0){
404 _parameter
= EventTypeMap::instance().new_parameter(prop
->value());
406 warning
<< "Legacy session: automation list has no automation-id property.";
409 if ((prop
= node
.property (X_("interpolation-style"))) != 0) {
410 _interpolation
= (InterpolationStyle
)string_2_enum(prop
->value(), _interpolation
);
412 _interpolation
= Linear
;
415 if ((prop
= node
.property (X_("default"))) != 0){
416 _default_value
= atof (prop
->value().c_str());
418 _default_value
= 0.0;
421 if ((prop
= node
.property (X_("style"))) != 0) {
422 _style
= string_to_auto_style (prop
->value());
427 if ((prop
= node
.property (X_("state"))) != 0) {
428 _state
= string_to_auto_state (prop
->value());
433 if ((prop
= node
.property (X_("min_yval"))) != 0) {
434 _min_yval
= atof (prop
->value ().c_str());
439 if ((prop
= node
.property (X_("max_yval"))) != 0) {
440 _max_yval
= atof (prop
->value ().c_str());
445 if ((prop
= node
.property (X_("max_xval"))) != 0) {
446 _max_xval
= atof (prop
->value ().c_str());
448 _max_xval
= 0; // means "no limit ;
451 bool have_events
= false;
453 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
454 if ((*niter
)->name() == X_("events")) {
455 deserialize_events (*(*niter
));
461 /* there was no Events child node; clear any current events */
465 reposition_for_rt_add (0);
466 maybe_signal_changed ();