remove StateManager code entirely and more debugging output cruft
[ardour.git] / libs / ardour / redirect.cc
1 /*
2     Copyright (C) 2001 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     $Id$
19 */
20
21 #include <fstream>
22 #include <algorithm>
23 #include <string>
24 #include <cerrno>
25 #include <unistd.h>
26 #include <sstream>
27
28 #include <sigc++/bind.h>
29
30 #include <pbd/xml++.h>
31
32 #include <ardour/redirect.h>
33 #include <ardour/session.h>
34 #include <ardour/utils.h>
35 #include <ardour/send.h>
36 #include <ardour/insert.h>
37
38 #include "i18n.h"
39
40 using namespace std;
41 using namespace ARDOUR;
42 using namespace PBD;
43
44 const string Redirect::state_node_name = "Redirect";
45 sigc::signal<void,Redirect*> Redirect::RedirectCreated;
46
47 Redirect::Redirect (Session& s, const string& name, Placement p,
48
49                     int input_min, int input_max, int output_min, int output_max)
50         : IO (s, name, input_min, input_max, output_min, output_max)
51 {
52         _placement = p;
53         _active = false;
54         _sort_key = 0;
55         _gui = 0;
56         _extra_xml = 0;
57 }
58
59 Redirect::~Redirect ()
60 {
61 }
62
63 boost::shared_ptr<Redirect>
64 Redirect::clone (boost::shared_ptr<const Redirect> other)
65 {
66         boost::shared_ptr<const Send> send;
67         boost::shared_ptr<const PortInsert> port_insert;
68         boost::shared_ptr<const PluginInsert> plugin_insert;
69
70         if ((send = boost::dynamic_pointer_cast<const Send>(other)) != 0) {
71                 return boost::shared_ptr<Redirect> (new Send (*send));
72         } else if ((port_insert = boost::dynamic_pointer_cast<const PortInsert>(other)) != 0) {
73                 return boost::shared_ptr<Redirect> (new PortInsert (*port_insert));
74         } else if ((plugin_insert = boost::dynamic_pointer_cast<const PluginInsert>(other)) != 0) {
75                 return boost::shared_ptr<Redirect> (new PluginInsert (*plugin_insert));
76         } else {
77                 fatal << _("programming error: unknown Redirect type in Redirect::Clone!\n")
78                       << endmsg;
79                 /*NOTREACHED*/
80         }
81         return boost::shared_ptr<Redirect>();
82 }
83
84 void
85 Redirect::set_sort_key (uint32_t key)
86 {
87         _sort_key = key;
88 }
89         
90 void
91 Redirect::set_placement (Placement p, void *src)
92 {
93         if (_placement != p) {
94                 _placement = p;
95                  placement_changed (this, src); /* EMIT SIGNAL */
96         }
97 }
98
99 void
100 Redirect::set_placement (const string& str, void *src)
101 {
102         if (str == _("pre")) {
103                 set_placement (PreFader, this);
104         } else if (str == _("post")) {
105                 set_placement (PostFader, this);
106         } else {
107                 error << string_compose(_("Redirect: unknown placement string \"%1\" (ignored)"), str) << endmsg;
108         }
109 }
110
111 int
112 Redirect::load_automation (string path)
113 {
114         string fullpath;
115
116         if (path[0] == '/') { // legacy
117                 fullpath = path;
118         } else {
119                 fullpath = _session.automation_dir();
120                 fullpath += path;
121         }
122         ifstream in (fullpath.c_str());
123
124         if (!in) {
125                 warning << string_compose(_("%1: cannot open %2 to load automation data (%3)"), _name, fullpath, strerror (errno)) << endmsg;
126                 return 1;
127         }
128
129         Glib::Mutex::Lock lm (_automation_lock);
130         set<uint32_t> tosave;
131         parameter_automation.clear ();
132
133         while (in) {
134                 double when;
135                 double value;
136                 uint32_t port;
137
138                 in >> port;     if (!in) break;
139                 in >> when;  if (!in) goto bad;
140                 in >> value; if (!in) goto bad;
141                 
142                 AutomationList& al = automation_list (port);
143                 al.add (when, value);
144                 tosave.insert (port);
145         }
146         
147         return 0;
148
149   bad:
150         error << string_compose(_("%1: cannot load automation data from %2"), _name, fullpath) << endmsg;
151         parameter_automation.clear ();
152         return -1;
153 }
154
155 int
156 Redirect::save_automation (string path)
157 {
158         Glib::Mutex::Lock lm (_automation_lock);
159         string fullpath;
160
161         if (parameter_automation.empty()) {
162                 return 1;
163         }
164
165         fullpath = _session.automation_dir();
166         fullpath += path;
167
168         ofstream out (fullpath.c_str());
169
170         if (!out) {
171                 error << string_compose(_("%1: cannot open %2 to store automation data (%3)"), _name, fullpath, strerror (errno)) << endmsg;
172                 return -1;
173         }
174
175         AutomationList::const_iterator i;
176         map<uint32_t,AutomationList*>::iterator li;
177         
178         for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) {
179                 for (i = (*li).second->begin(); i != (*li).second->end(); ++i) {
180                         
181                         out << (*li).first << ' ' << (*i)->when << ' ' << (*i)->value << endl;
182                         
183                         if (!out) {
184                                 break;
185                         }
186                 }
187                 
188                 if (i != (*li).second->end()) {
189                         unlink (fullpath.c_str());
190                         error << string_compose(_("%1: could not save automation state to %2"), _name, fullpath) << endmsg;
191                         return -1;
192                 }
193         }
194
195         if (li != parameter_automation.end()) {
196                 unlink (fullpath.c_str());
197                 error << string_compose(_("%1: could not save automation state to %2"), _name, fullpath) << endmsg;
198                 return -1;
199         }
200
201         return 0;
202 }
203
204 XMLNode&
205 Redirect::get_state (void)
206 {
207         return state (true);
208 }
209
210 XMLNode&
211 Redirect::state (bool full_state)
212 {
213         char buf[64];
214         XMLNode* node = new XMLNode (state_node_name);
215         stringstream sstr;
216
217         node->add_property("active", active() ? "yes" : "no");  
218         node->add_property("placement", placement_as_string (placement()));
219         node->add_child_nocopy (IO::state (full_state));
220
221         if (_extra_xml){
222                 node->add_child_copy (*_extra_xml);
223         }
224         
225         if (full_state) {
226
227                 string path;
228                 string legal_name;
229                 
230                 path = _session.snap_name();
231                 path += "-redirect-";
232                 id().print (buf, sizeof (buf));
233                 path += buf;
234                 path += ".automation";
235                 
236                 /* XXX we didn't ask for a state save, we asked for the current state.
237                    FIX ME!
238                 */
239                 
240                 switch (save_automation (path)) {
241                 case -1:
242                         error << string_compose(_("Could not get state from Redirect (%1).  Problem with save_automation"), _name) << endmsg;
243                         break;
244                         
245                 case 0:
246                         XMLNode *aevents = node->add_child("Automation");
247                         
248                         for (set<uint32_t>::iterator x = visible_parameter_automation.begin(); x != visible_parameter_automation.end(); ++x) {
249                                 if (x != visible_parameter_automation.begin()) {
250                                         sstr << ' ';
251                                 }
252                                 sstr << *x;
253                         }
254                         
255                         aevents->add_property ("path", path);
256                         aevents->add_property ("visible", sstr.str());
257                         break;
258                 }
259         }
260
261         return *node;
262 }
263
264 void
265 Redirect::what_has_automation (set<uint32_t>& s) const
266 {
267         Glib::Mutex::Lock lm (_automation_lock);
268         map<uint32_t,AutomationList*>::const_iterator li;
269         
270         for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) {
271                 s.insert  ((*li).first);
272         }
273 }
274
275 void
276 Redirect::what_has_visible_automation (set<uint32_t>& s) const
277 {
278         Glib::Mutex::Lock lm (_automation_lock);
279         set<uint32_t>::const_iterator li;
280         
281         for (li = visible_parameter_automation.begin(); li != visible_parameter_automation.end(); ++li) {
282                 s.insert  (*li);
283         }
284 }
285
286 int
287 Redirect::set_state (const XMLNode& node)
288 {
289         const XMLProperty *prop;
290
291         if (node.name() != state_node_name) {
292                 error << string_compose(_("incorrect XML node \"%1\" passed to Redirect object"), node.name()) << endmsg;
293                 return -1;
294         }
295
296         XMLNodeList nlist = node.children();
297         XMLNodeIterator niter;
298         bool have_io = false;
299
300         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
301
302                 if ((*niter)->name() == IO::state_node_name) {
303
304                         IO::set_state (**niter);
305                         have_io = true;
306
307                 } else if ((*niter)->name() == "Automation") {
308
309                         XMLProperty *prop;
310                         
311                         if ((prop = (*niter)->property ("path")) != 0) {
312                                 load_automation (prop->value());
313                         } else {
314                                 warning << string_compose(_("%1: Automation node has no path property"), _name) << endmsg;
315                         }
316
317                         if ((prop = (*niter)->property ("visible")) != 0) {
318                                 uint32_t what;
319                                 stringstream sstr;
320
321                                 visible_parameter_automation.clear ();
322                                 
323                                 sstr << prop->value();
324                                 while (1) {
325                                         sstr >> what;
326                                         if (sstr.fail()) {
327                                                 break;
328                                         }
329                                         mark_automation_visible (what, true);
330                                 }
331                         }
332
333                 } else if ((*niter)->name() == "extra") {
334                         _extra_xml = new XMLNode (*(*niter));
335                 }
336         }
337
338         if (!have_io) {
339                 error << _("XML node describing an IO is missing an IO node") << endmsg;
340                 return -1;
341         }
342
343         if ((prop = node.property ("active")) == 0) {
344                 error << _("XML node describing a redirect is missing the `active' field") << endmsg;
345                 return -1;
346         }
347
348         if (_active != (prop->value() == "yes")) {
349                 _active = !_active;
350                 active_changed (this, this); /* EMIT_SIGNAL */
351         }
352         
353         if ((prop = node.property ("placement")) == 0) {
354                 error << _("XML node describing a redirect is missing the `placement' field") << endmsg;
355                 return -1;
356         }
357
358         set_placement (prop->value(), this);
359
360         return 0;
361 }
362
363 AutomationList&
364 Redirect::automation_list (uint32_t parameter)
365 {
366         AutomationList* al = parameter_automation[parameter];
367
368         if (al == 0) {
369                 al = parameter_automation[parameter] = new AutomationList (default_parameter_value (parameter));
370                 /* let derived classes do whatever they need with this */
371                 automation_list_creation_callback (parameter, *al);
372         }
373
374         return *al;
375 }
376
377 string
378 Redirect::describe_parameter (uint32_t which)
379 {
380         /* derived classes will override this */
381         return "";
382 }
383
384 void
385 Redirect::can_automate (uint32_t what)
386 {
387         can_automate_list.insert (what);
388 }
389
390 void
391 Redirect::mark_automation_visible (uint32_t what, bool yn)
392 {
393         if (yn) {
394                 visible_parameter_automation.insert (what);
395         } else {
396                 set<uint32_t>::iterator i;
397
398                 if ((i = visible_parameter_automation.find (what)) != visible_parameter_automation.end()) {
399                         visible_parameter_automation.erase (i);
400                 }
401         }
402 }
403
404 bool
405 Redirect::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_event) const
406 {
407         map<uint32_t,AutomationList*>::const_iterator li;       
408         AutomationList::TimeComparator cmp;
409
410         next_event.when = max_frames;
411         
412         for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) {
413                 
414                 AutomationList::const_iterator i;
415                 const AutomationList& alist (*((*li).second));
416                 ControlEvent cp (now, 0.0f);
417                 
418                 for (i = lower_bound (alist.const_begin(), alist.const_end(), &cp, cmp); i != alist.const_end() && (*i)->when < end; ++i) {
419                         if ((*i)->when > now) {
420                                 break; 
421                         }
422                 }
423                 
424                 if (i != alist.const_end() && (*i)->when < end) {
425                         
426                         if ((*i)->when < next_event.when) {
427                                 next_event.when = (*i)->when;
428                         }
429                 }
430         }
431
432         return next_event.when != max_frames;
433 }
434
435 void
436 Redirect::set_active (bool yn, void* src)
437 {
438         _active = yn; 
439         active_changed (this, src); 
440         _session.set_dirty ();
441 }
442