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