Can't call the wrong function when there's only one of them: remove ARDOUR::Parameter...
[ardour.git] / libs / ardour / automation_list.cc
1 /*
2     Copyright (C) 2002 Paul Davis 
3
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.
8
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.
13
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.
17
18 */
19
20 #include <set>
21 #include <climits>
22 #include <float.h>
23 #include <cmath>
24 #include <sstream>
25 #include <algorithm>
26 #include <sigc++/bind.h>
27 #include <ardour/automation_list.h>
28 #include <ardour/event_type_map.h>
29 #include <evoral/Curve.hpp>
30 #include <pbd/stacktrace.h>
31 #include <pbd/enumwriter.h>
32
33 #include "i18n.h"
34
35 using namespace std;
36 using namespace ARDOUR;
37 using namespace sigc;
38 using namespace PBD;
39
40 sigc::signal<void,AutomationList *> AutomationList::AutomationListCreated;
41
42 #if 0
43 static void dumpit (const AutomationList& al, string prefix = "")
44 {
45         cerr << prefix << &al << endl;
46         for (AutomationList::const_iterator i = al.const_begin(); i != al.const_end(); ++i) {
47                 cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
48         }
49         cerr << "\n";
50 }
51 #endif
52
53 /* XXX: min_val max_val redundant? (param.min() param.max()) */
54 AutomationList::AutomationList (Evoral::Parameter id)
55         : ControlList(id)
56 {
57         _state = Off;
58         _style = Absolute;
59         _touching = false;
60
61         assert(_parameter.type() != NullAutomation);
62         AutomationListCreated(this);
63 }
64
65 AutomationList::AutomationList (const AutomationList& other)
66         : ControlList(other)
67 {
68         _state = other._state;
69         _touching = other._touching;
70         
71         assert(_parameter.type() != NullAutomation);
72         AutomationListCreated(this);
73 }
74
75 AutomationList::AutomationList (const AutomationList& other, double start, double end)
76         : ControlList(other)
77 {
78         _style = other._style;
79         _state = other._state;
80         _touching = other._touching;
81
82         assert(_parameter.type() != NullAutomation);
83         AutomationListCreated(this);
84 }
85
86 /** \a id is used for legacy sessions where the type is not present
87  * in or below the <AutomationList> node.  It is used if \a id is non-null.
88  */
89 AutomationList::AutomationList (const XMLNode& node, Evoral::Parameter id)
90         : ControlList(id)
91 {
92         _touching = false;
93         _state = Off;
94         _style = Absolute;
95         
96         set_state (node);
97
98         if (id)
99                 _parameter = id;
100
101         assert(_parameter.type() != NullAutomation);
102         AutomationListCreated(this);
103 }
104
105 AutomationList::~AutomationList()
106 {
107         GoingAway ();
108 }
109
110 boost::shared_ptr<Evoral::ControlList>
111 AutomationList::create(Evoral::Parameter id)
112 {
113         return boost::shared_ptr<Evoral::ControlList>(new AutomationList(id));
114 }
115
116 bool
117 AutomationList::operator== (const AutomationList& other)
118 {
119         return _events == other._events;
120 }
121
122 AutomationList&
123 AutomationList::operator= (const AutomationList& other)
124 {
125         if (this != &other) {
126                 
127                 _events.clear ();
128                 
129                 for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) {
130                         _events.push_back (new Evoral::ControlEvent (**i));
131                 }
132                 
133                 _min_yval = other._min_yval;
134                 _max_yval = other._max_yval;
135                 _max_xval = other._max_xval;
136                 _default_value = other._default_value;
137                 
138                 mark_dirty ();
139                 maybe_signal_changed ();
140         }
141
142         return *this;
143 }
144
145 void
146 AutomationList::maybe_signal_changed ()
147 {
148         ControlList::maybe_signal_changed ();
149
150         if (!_frozen) {
151                 StateChanged (); /* EMIT SIGNAL */
152         }
153 }
154
155 void
156 AutomationList::set_automation_state (AutoState s)
157 {
158         if (s != _state) {
159                 _state = s;
160                 automation_state_changed (); /* EMIT SIGNAL */
161         }
162 }
163
164 void
165 AutomationList::set_automation_style (AutoStyle s)
166 {
167         if (s != _style) {
168                 _style = s;
169                 automation_style_changed (); /* EMIT SIGNAL */
170         }
171 }
172
173 void
174 AutomationList::start_touch ()
175 {
176         _touching = true;
177         _new_value = true;
178 }
179
180 void
181 AutomationList::stop_touch ()
182 {
183         _touching = false;
184         _new_value = false;
185 }
186
187 void
188 AutomationList::freeze ()
189 {
190         _frozen++;
191 }
192
193 void
194 AutomationList::thaw ()
195 {
196         ControlList::thaw();
197
198         if (_changed_when_thawed) {
199                 StateChanged(); /* EMIT SIGNAL */
200         }
201 }
202
203 void 
204 AutomationList::mark_dirty () const
205 {
206         ControlList::mark_dirty ();
207         Dirty (); /* EMIT SIGNAL */
208 }
209
210 XMLNode&
211 AutomationList::get_state ()
212 {
213         return state (true);
214 }
215
216 XMLNode&
217 AutomationList::state (bool full)
218 {
219         XMLNode* root = new XMLNode (X_("AutomationList"));
220         char buf[64];
221         LocaleGuard lg (X_("POSIX"));
222
223         root->add_property ("automation-id", EventTypeMap::instance().to_symbol(_parameter));
224
225         root->add_property ("id", _id.to_s());
226
227         snprintf (buf, sizeof (buf), "%.12g", _default_value);
228         root->add_property ("default", buf);
229         snprintf (buf, sizeof (buf), "%.12g", _min_yval);
230         root->add_property ("min_yval", buf);
231         snprintf (buf, sizeof (buf), "%.12g", _max_yval);
232         root->add_property ("max_yval", buf);
233         snprintf (buf, sizeof (buf), "%.12g", _max_xval);
234         root->add_property ("max_xval", buf);
235         
236         root->add_property ("interpolation-style", enum_2_string (_interpolation));
237
238         if (full) {
239                 root->add_property ("state", auto_state_to_string (_state));
240         } else {
241                 /* never save anything but Off for automation state to a template */
242                 root->add_property ("state", auto_state_to_string (Off));
243         }
244
245         root->add_property ("style", auto_style_to_string (_style));
246
247         if (!_events.empty()) {
248                 root->add_child_nocopy (serialize_events());
249         }
250
251         return *root;
252 }
253
254 XMLNode&
255 AutomationList::serialize_events ()
256 {
257         XMLNode* node = new XMLNode (X_("events"));
258         stringstream str;
259
260         for (iterator xx = _events.begin(); xx != _events.end(); ++xx) {
261                 str << (double) (*xx)->when;
262                 str << ' ';
263                 str <<(double) (*xx)->value;
264                 str << '\n';
265         }
266
267         /* XML is a bit wierd */
268
269         XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
270         content_node->set_content (str.str());
271
272         node->add_child_nocopy (*content_node);
273
274         return *node;
275 }
276
277 int
278 AutomationList::deserialize_events (const XMLNode& node)
279 {
280         if (node.children().empty()) {
281                 return -1;
282         }
283
284         XMLNode* content_node = node.children().front();
285
286         if (content_node->content().empty()) {
287                 return -1;
288         }
289
290         freeze ();
291         clear ();
292         
293         stringstream str (content_node->content());
294         
295         double x;
296         double y;
297         bool ok = true;
298         
299         while (str) {
300                 str >> x;
301                 if (!str) {
302                         break;
303                 }
304                 str >> y;
305                 if (!str) {
306                         ok = false;
307                         break;
308                 }
309                 fast_simple_add (x, y);
310         }
311         
312         if (!ok) {
313                 clear ();
314                 error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
315         } else {
316                 mark_dirty ();
317                 reposition_for_rt_add (0);
318                 maybe_signal_changed ();
319         }
320
321         thaw ();
322
323         return 0;
324 }
325
326 int
327 AutomationList::set_state (const XMLNode& node)
328 {
329         XMLNodeList nlist = node.children();
330         XMLNode* nsos;
331         XMLNodeIterator niter;
332         const XMLProperty* prop;
333
334         if (node.name() == X_("events")) {
335                 /* partial state setting*/
336                 return deserialize_events (node);
337         }
338         
339         if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
340
341                 if ((nsos = node.child (X_("AutomationList")))) {
342                         /* new school in old school clothing */
343                         return set_state (*nsos);
344                 }
345
346                 /* old school */
347
348                 const XMLNodeList& elist = node.children();
349                 XMLNodeConstIterator i;
350                 XMLProperty* prop;
351                 nframes_t x;
352                 double y;
353                 
354                 freeze ();
355                 clear ();
356                 
357                 for (i = elist.begin(); i != elist.end(); ++i) {
358                         
359                         if ((prop = (*i)->property ("x")) == 0) {
360                                 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
361                                 continue;
362                         }
363                         x = atoi (prop->value().c_str());
364                         
365                         if ((prop = (*i)->property ("y")) == 0) {
366                                 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
367                                 continue;
368                         }
369                         y = atof (prop->value().c_str());
370                         
371                         fast_simple_add (x, y);
372                 }
373                 
374                 thaw ();
375
376                 return 0;
377         }
378
379         if (node.name() != X_("AutomationList") ) {
380                 error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
381                 return -1;
382         }
383
384         if ((prop = node.property ("id")) != 0) {
385                 _id = prop->value ();
386                 /* update session AL list */
387                 AutomationListCreated(this);
388         }
389         
390         if ((prop = node.property (X_("automation-id"))) != 0){ 
391                 _parameter = EventTypeMap::instance().new_parameter(prop->value());
392         } else {
393                 warning << "Legacy session: automation list has no automation-id property.";
394         }
395         
396         if ((prop = node.property (X_("interpolation-style"))) != 0) {
397                 _interpolation = (InterpolationStyle)string_2_enum(prop->value(), _interpolation);
398         } else {
399                 _interpolation = Linear;
400         }
401         
402         if ((prop = node.property (X_("default"))) != 0){ 
403                 _default_value = atof (prop->value().c_str());
404         } else {
405                 _default_value = 0.0;
406         }
407
408         if ((prop = node.property (X_("style"))) != 0) {
409                 _style = string_to_auto_style (prop->value());
410         } else {
411                 _style = Absolute;
412         }
413
414         if ((prop = node.property (X_("state"))) != 0) {
415                 _state = string_to_auto_state (prop->value());
416         } else {
417                 _state = Off;
418         }
419
420         if ((prop = node.property (X_("min_yval"))) != 0) {
421                 _min_yval = atof (prop->value ().c_str());
422         } else {
423                 _min_yval = FLT_MIN;
424         }
425
426         if ((prop = node.property (X_("max_yval"))) != 0) {
427                 _max_yval = atof (prop->value ().c_str());
428         } else {
429                 _max_yval = FLT_MAX;
430         }
431
432         if ((prop = node.property (X_("max_xval"))) != 0) {
433                 _max_xval = atof (prop->value ().c_str());
434         } else {
435                 _max_xval = 0; // means "no limit ;
436         }
437
438         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
439                 if ((*niter)->name() == X_("events")) {
440                         deserialize_events (*(*niter));
441                 }
442         }
443
444         return 0;
445 }
446