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