fixes for endemic (compiler?) issues with virtual inheritance of sigc::trackable...
[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 #ifdef STATE_MANAGER
148         for (set<uint32_t>::iterator i = tosave.begin(); i != tosave.end(); ++i) {
149                 automation_list (*i).save_state (_("loaded from disk"));
150         }
151 #endif
152         
153         return 0;
154
155   bad:
156         error << string_compose(_("%1: cannot load automation data from %2"), _name, fullpath) << endmsg;
157         parameter_automation.clear ();
158         return -1;
159 }
160
161 int
162 Redirect::save_automation (string path)
163 {
164         Glib::Mutex::Lock lm (_automation_lock);
165         string fullpath;
166
167         if (parameter_automation.empty()) {
168                 return 1;
169         }
170
171         fullpath = _session.automation_dir();
172         fullpath += path;
173
174         ofstream out (fullpath.c_str());
175
176         if (!out) {
177                 error << string_compose(_("%1: cannot open %2 to store automation data (%3)"), _name, fullpath, strerror (errno)) << endmsg;
178                 return -1;
179         }
180
181         AutomationList::const_iterator i;
182         map<uint32_t,AutomationList*>::iterator li;
183         
184         for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) {
185                 for (i = (*li).second->begin(); i != (*li).second->end(); ++i) {
186                         
187                         out << (*li).first << ' ' << (*i)->when << ' ' << (*i)->value << endl;
188                         
189                         if (!out) {
190                                 break;
191                         }
192                 }
193                 
194                 if (i != (*li).second->end()) {
195                         unlink (fullpath.c_str());
196                         error << string_compose(_("%1: could not save automation state to %2"), _name, fullpath) << endmsg;
197                         return -1;
198                 }
199         }
200
201         if (li != parameter_automation.end()) {
202                 unlink (fullpath.c_str());
203                 error << string_compose(_("%1: could not save automation state to %2"), _name, fullpath) << endmsg;
204                 return -1;
205         }
206
207         return 0;
208 }
209
210 XMLNode&
211 Redirect::get_state (void)
212 {
213         return state (true);
214 }
215
216 XMLNode&
217 Redirect::state (bool full_state)
218 {
219         char buf[64];
220         XMLNode* node = new XMLNode (state_node_name);
221         stringstream sstr;
222
223         node->add_property("active", active() ? "yes" : "no");  
224         node->add_property("placement", placement_as_string (placement()));
225         node->add_child_nocopy (IO::state (full_state));
226
227         if (_extra_xml){
228                 node->add_child_copy (*_extra_xml);
229         }
230         
231         if (full_state) {
232
233                 string path;
234                 string legal_name;
235                 
236                 path = _session.snap_name();
237                 path += "-redirect-";
238                 id().print (buf, sizeof (buf));
239                 path += buf;
240                 path += ".automation";
241                 
242                 /* XXX we didn't ask for a state save, we asked for the current state.
243                    FIX ME!
244                 */
245                 
246                 switch (save_automation (path)) {
247                 case -1:
248                         error << string_compose(_("Could not get state from Redirect (%1).  Problem with save_automation"), _name) << endmsg;
249                         break;
250                         
251                 case 0:
252                         XMLNode *aevents = node->add_child("Automation");
253                         
254                         for (set<uint32_t>::iterator x = visible_parameter_automation.begin(); x != visible_parameter_automation.end(); ++x) {
255                                 if (x != visible_parameter_automation.begin()) {
256                                         sstr << ' ';
257                                 }
258                                 sstr << *x;
259                         }
260                         
261                         aevents->add_property ("path", path);
262                         aevents->add_property ("visible", sstr.str());
263                         break;
264                 }
265         }
266
267         return *node;
268 }
269
270 void
271 Redirect::what_has_automation (set<uint32_t>& s) const
272 {
273         Glib::Mutex::Lock lm (_automation_lock);
274         map<uint32_t,AutomationList*>::const_iterator li;
275         
276         for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) {
277                 s.insert  ((*li).first);
278         }
279 }
280
281 void
282 Redirect::what_has_visible_automation (set<uint32_t>& s) const
283 {
284         Glib::Mutex::Lock lm (_automation_lock);
285         set<uint32_t>::const_iterator li;
286         
287         for (li = visible_parameter_automation.begin(); li != visible_parameter_automation.end(); ++li) {
288                 s.insert  (*li);
289         }
290 }
291
292 int
293 Redirect::set_state (const XMLNode& node)
294 {
295         const XMLProperty *prop;
296
297         if (node.name() != state_node_name) {
298                 error << string_compose(_("incorrect XML node \"%1\" passed to Redirect object"), node.name()) << endmsg;
299                 return -1;
300         }
301
302         XMLNodeList nlist = node.children();
303         XMLNodeIterator niter;
304         bool have_io = false;
305
306         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
307
308                 if ((*niter)->name() == IO::state_node_name) {
309
310                         IO::set_state (**niter);
311                         have_io = true;
312
313                 } else if ((*niter)->name() == "Automation") {
314
315                         XMLProperty *prop;
316                         
317                         if ((prop = (*niter)->property ("path")) != 0) {
318                                 load_automation (prop->value());
319                         } else {
320                                 warning << string_compose(_("%1: Automation node has no path property"), _name) << endmsg;
321                         }
322
323                         if ((prop = (*niter)->property ("visible")) != 0) {
324                                 uint32_t what;
325                                 stringstream sstr;
326
327                                 visible_parameter_automation.clear ();
328                                 
329                                 sstr << prop->value();
330                                 while (1) {
331                                         sstr >> what;
332                                         if (sstr.fail()) {
333                                                 break;
334                                         }
335                                         mark_automation_visible (what, true);
336                                 }
337                         }
338
339                 } else if ((*niter)->name() == "extra") {
340                         _extra_xml = new XMLNode (*(*niter));
341                 }
342         }
343
344         if (!have_io) {
345                 error << _("XML node describing an IO is missing an IO node") << endmsg;
346                 return -1;
347         }
348
349         if ((prop = node.property ("active")) == 0) {
350                 error << _("XML node describing a redirect is missing the `active' field") << endmsg;
351                 return -1;
352         }
353
354         if (_active != (prop->value() == "yes")) {
355                 _active = !_active;
356                 active_changed (this, this); /* EMIT_SIGNAL */
357         }
358         
359         if ((prop = node.property ("placement")) == 0) {
360                 error << _("XML node describing a redirect is missing the `placement' field") << endmsg;
361                 return -1;
362         }
363
364         set_placement (prop->value(), this);
365
366         return 0;
367 }
368
369 AutomationList&
370 Redirect::automation_list (uint32_t parameter)
371 {
372         AutomationList* al = parameter_automation[parameter];
373
374         if (al == 0) {
375                 al = parameter_automation[parameter] = new AutomationList (default_parameter_value (parameter));
376                 /* let derived classes do whatever they need with this */
377                 automation_list_creation_callback (parameter, *al);
378         }
379
380         return *al;
381 }
382
383 string
384 Redirect::describe_parameter (uint32_t which)
385 {
386         /* derived classes will override this */
387         return "";
388 }
389
390 void
391 Redirect::can_automate (uint32_t what)
392 {
393         can_automate_list.insert (what);
394 }
395
396 void
397 Redirect::mark_automation_visible (uint32_t what, bool yn)
398 {
399         if (yn) {
400                 visible_parameter_automation.insert (what);
401         } else {
402                 set<uint32_t>::iterator i;
403
404                 if ((i = visible_parameter_automation.find (what)) != visible_parameter_automation.end()) {
405                         visible_parameter_automation.erase (i);
406                 }
407         }
408 }
409
410 bool
411 Redirect::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_event) const
412 {
413         map<uint32_t,AutomationList*>::const_iterator li;       
414         AutomationList::TimeComparator cmp;
415
416         next_event.when = max_frames;
417         
418         for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) {
419                 
420                 AutomationList::const_iterator i;
421                 const AutomationList& alist (*((*li).second));
422                 ControlEvent cp (now, 0.0f);
423                 
424                 for (i = lower_bound (alist.const_begin(), alist.const_end(), &cp, cmp); i != alist.const_end() && (*i)->when < end; ++i) {
425                         if ((*i)->when > now) {
426                                 break; 
427                         }
428                 }
429                 
430                 if (i != alist.const_end() && (*i)->when < end) {
431                         
432                         if ((*i)->when < next_event.when) {
433                                 next_event.when = (*i)->when;
434                         }
435                 }
436         }
437
438         return next_event.when != max_frames;
439 }
440
441 void
442 Redirect::store_state (RedirectState& state) const
443 {
444         state.active = _active;
445 }
446
447 Change
448 Redirect::restore_state (StateManager::State& state)
449 {
450         RedirectState* rstate = dynamic_cast<RedirectState*> (&state);
451         set_active (rstate->active, this);
452         return Change (0);
453 }
454
455 StateManager::State*
456 Redirect::state_factory (std::string why) const
457 {
458         RedirectState* state = new RedirectState (why);
459
460         store_state (*state);
461
462         return state;
463 }
464
465 void
466 Redirect::set_active (bool yn, void* src)
467 {
468         _active = yn; 
469 #ifdef STATE_MANAGER
470         save_state (_("active_changed"));
471 #endif
472         active_changed (this, src); 
473         _session.set_dirty ();
474 }
475