fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / audio_track_importer.cc
1 /*
2     Copyright (C) 2008 Paul Davis
3     Author: Sakari Bergen
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include "ardour/audio_track_importer.h"
22
23 #include "ardour/audio_playlist_importer.h"
24 #include "ardour/audio_diskstream.h"
25 #include "ardour/session.h"
26
27 #include "pbd/controllable.h"
28 #include "pbd/convert.h"
29 #include "pbd/failed_constructor.h"
30
31 #include <sstream>
32 #include <algorithm>
33
34 #include "pbd/i18n.h"
35
36 using namespace std;
37 using namespace PBD;
38 using namespace ARDOUR;
39
40 /*** AudioTrackImportHandler ***/
41
42 AudioTrackImportHandler::AudioTrackImportHandler (XMLTree const & source, Session & session, AudioPlaylistImportHandler & pl_handler) :
43   ElementImportHandler (source, session)
44 {
45         XMLNode const * root = source.root();
46         XMLNode const * routes;
47
48         if (!(routes = root->child ("Routes"))) {
49                 throw failed_constructor();
50         }
51
52         XMLNodeList const & route_list = routes->children();
53         for (XMLNodeList::const_iterator it = route_list.begin(); it != route_list.end(); ++it) {
54                 XMLProperty const * type = (*it)->property("default-type");
55                 if ( (!type || type->value() == "audio") &&  ((*it)->property ("diskstream") != 0 || (*it)->property ("diskstream-id") != 0)) {
56                         try {
57                                 elements.push_back (ElementPtr ( new AudioTrackImporter (source, session, *this, **it, pl_handler)));
58                         } catch (failed_constructor err) {
59                                 set_dirty();
60                         }
61                 }
62         }
63 }
64
65 string
66 AudioTrackImportHandler::get_info () const
67 {
68         return _("Audio Tracks");
69 }
70
71
72 /*** AudioTrackImporter ***/
73
74 AudioTrackImporter::AudioTrackImporter (XMLTree const & source,
75                                         Session & session,
76                                         AudioTrackImportHandler & track_handler,
77                                         XMLNode const & node,
78                                         AudioPlaylistImportHandler & pl_handler) :
79   ElementImporter (source, session),
80   track_handler (track_handler),
81   xml_track (node),
82   pl_handler (pl_handler)
83 {
84         XMLProperty * prop;
85
86         if (!parse_route_xml ()) {
87                 throw failed_constructor();
88         }
89
90         if (!parse_io ()) {
91                 throw failed_constructor();
92         }
93
94         XMLNodeList const & controllables = node.children (Controllable::xml_node_name);
95         for (XMLNodeList::const_iterator it = controllables.begin(); it != controllables.end(); ++it) {
96                 parse_controllable (**it);
97         }
98
99         XMLNode * remote_control = xml_track.child ("RemoteControl");
100         if (remote_control && (prop = remote_control->property ("id"))) {
101                 uint32_t control_id = session.ntracks() + session.nbusses() + 1;
102                 prop->set_value (to_string (control_id, std::dec));
103         }
104
105         xml_track.remove_nodes_and_delete ("Extra");
106 }
107
108 AudioTrackImporter::~AudioTrackImporter ()
109 {
110         playlists.clear ();
111 }
112
113 bool
114 AudioTrackImporter::parse_route_xml ()
115 {
116         bool ds_ok = false;
117
118         // Remove order keys, new ones will be generated
119         xml_track.remove_property ("order-keys");
120
121         XMLPropertyList const & props = xml_track.properties();
122         for (XMLPropertyList::const_iterator it = props.begin(); it != props.end(); ++it) {
123                 string prop = (*it)->name();
124                 if (!prop.compare ("default-type") || !prop.compare ("flags") ||
125                   !prop.compare ("active") || !prop.compare ("muted") ||
126                   !prop.compare ("soloed") || !prop.compare ("phase-invert") ||
127                   !prop.compare ("denormal-protection") || !prop.compare("mute-affects-pre-fader") ||
128                   !prop.compare ("mute-affects-post-fader") || !prop.compare("mute-affects-control-outs") ||
129                   !prop.compare ("mute-affects-main-outs") || !prop.compare("mode")) {
130                         // All ok
131                 } else if (!prop.compare("diskstream-id")) {
132                         old_ds_id = (*it)->value();
133                         (*it)->set_value (new_ds_id.to_s());
134                         ds_ok = true;
135                 } else {
136                         std::cerr << string_compose (X_("AudioTrackImporter: did not recognise XML-property \"%1\""), prop) << endmsg;
137                 }
138         }
139
140         if (!ds_ok) {
141                 error << X_("AudioTrackImporter: did not find necessary XML-property \"diskstream-id\"") << endmsg;
142                 return false;
143         }
144
145         return true;
146 }
147
148 bool
149 AudioTrackImporter::parse_io ()
150 {
151         XMLNode * io;
152         bool name_ok = false;
153         bool id_ok = false;
154
155         if (!(io = xml_track.child ("IO"))) {
156                 return false;
157         }
158
159         XMLPropertyList const & props = io->properties();
160
161         for (XMLPropertyList::const_iterator it = props.begin(); it != props.end(); ++it) {
162                 string prop = (*it)->name();
163                 if (!prop.compare ("gain") || !prop.compare ("iolimits")) {
164                         // All ok
165                 } else if (!prop.compare("name")) {
166                         name = (*it)->value();
167                         name_ok = true;
168                 } else if (!prop.compare("id")) {
169                         PBD::ID id;
170                         (*it)->set_value (id.to_s());
171                         id_ok = true;
172                 } else if (!prop.compare("inputs")) {
173                         // TODO Handle this properly!
174                         /* Input and output ports are counted and added empty, so that no in/output connecting function fails. */
175                         uint32_t num_inputs = std::count ((*it)->value().begin(), (*it)->value().end(), '{');
176                         std::string value;
177                         for (uint32_t i = 0; i < num_inputs; i++) { value += "{}"; }
178                         (*it)->set_value (value);
179                 } else if (!prop.compare("outputs")) {
180                         // TODO See comments above
181                         uint32_t num_outputs = std::count ((*it)->value().begin(), (*it)->value().end(), '{');
182                         std::string value;
183                         for (uint32_t i = 0; i < num_outputs; i++) { value += "{}"; }
184                         (*it)->set_value (value);
185                 } else {
186                         std::cerr << string_compose (X_("AudioTrackImporter: did not recognise XML-property \"%1\""), prop) << endmsg;
187                 }
188         }
189
190         if (!name_ok) {
191                 error << X_("AudioTrackImporter: did not find necessary XML-property \"name\"") << endmsg;
192                 return false;
193         }
194
195         if (!id_ok) {
196                 error << X_("AudioTrackImporter: did not find necessary XML-property \"id\"") << endmsg;
197                 return false;
198         }
199
200         XMLNodeList const & controllables = io->children (Controllable::xml_node_name);
201         for (XMLNodeList::const_iterator it = controllables.begin(); it != controllables.end(); ++it) {
202                 parse_controllable (**it);
203         }
204
205         XMLNodeList const & processors = io->children ("Processor");
206         for (XMLNodeList::const_iterator it = processors.begin(); it != processors.end(); ++it) {
207                 parse_processor (**it);
208         }
209
210         XMLNodeList const & automations = io->children ("Automation");
211         for (XMLNodeList::const_iterator it = automations.begin(); it != automations.end(); ++it) {
212                 parse_automation (**it);
213         }
214
215         return true;
216 }
217
218 string
219 AudioTrackImporter::get_info () const
220 {
221         // TODO
222         return name;
223 }
224
225 /** @return true if everything is ok */
226 bool
227 AudioTrackImporter::_prepare_move ()
228 {
229         /* Copy dependent playlists */
230
231         pl_handler.playlists_by_diskstream (old_ds_id, playlists);
232
233         for (PlaylistList::iterator it = playlists.begin(); it != playlists.end(); ++it) {
234                 if (!(*it)->prepare_move ()) {
235                         playlists.clear ();
236                         return false;
237                 }
238                 (*it)->set_diskstream (new_ds_id);
239         }
240
241         /* Rename */
242
243         while (session.route_by_name (name) || !track_handler.check_name (name)) {
244                 std::pair<bool, string> rename_pair = *Rename (_("A playlist with this name already exists, please rename it."), name);
245                 if (!rename_pair.first) {
246                         return false;
247                 }
248                 name = rename_pair.second;
249         }
250
251         XMLNode* c = xml_track.child ("IO");
252         if (!c) {
253                 error << _("badly-formed XML in imported track") << endmsg;
254                 return false;
255         }
256
257         XMLProperty * p = c->property ("name");
258         if (!p) {
259                 error << _("badly-formed XML in imported track") << endmsg;
260                 return false;
261         }
262
263         p->set_value (name);
264
265         track_handler.add_name (name);
266
267         return true;
268 }
269
270 void
271 AudioTrackImporter::_cancel_move ()
272 {
273         track_handler.remove_name (name);
274         playlists.clear ();
275 }
276
277 void
278 AudioTrackImporter::_move ()
279 {
280         /* Add diskstream */
281
282         boost::shared_ptr<XMLSharedNodeList> ds_node_list;
283         string xpath = "/Session/DiskStreams/AudioDiskstream[@id='" + old_ds_id.to_s() + "']";
284         ds_node_list = source.find (xpath);
285
286         if (ds_node_list->size() != 1) {
287                 error << string_compose (_("Error Importing Audio track %1"), name) << endmsg;
288                 return;
289         }
290
291         boost::shared_ptr<XMLNode> ds_node = ds_node_list->front();
292         XMLProperty * p = ds_node->property (X_("id"));
293         assert (p);
294         p->set_value (new_ds_id.to_s());
295
296         boost::shared_ptr<Diskstream> new_ds (new AudioDiskstream (session, *ds_node));
297         new_ds->set_name (name);
298         new_ds->do_refill_with_alloc ();
299         new_ds->set_block_size (session.get_block_size ());
300
301         /* Import playlists */
302
303         for (PlaylistList::const_iterator it = playlists.begin(); it != playlists.end(); ++it) {
304                 (*it)->move ();
305         }
306
307         /* Import track */
308
309         XMLNode routes ("Routes");
310         routes.add_child_copy (xml_track);
311         session.load_routes (routes, 3000);
312 }
313
314 bool
315 AudioTrackImporter::parse_processor (XMLNode & node)
316 {
317         XMLNode * automation = node.child ("Automation");
318         if (automation) {
319                 parse_automation (*automation);
320         }
321
322         return true;
323 }
324
325 bool
326 AudioTrackImporter::parse_controllable (XMLNode & node)
327 {
328         XMLProperty * prop;
329
330         if ((prop = node.property ("id"))) {
331                 PBD::ID new_id;
332                 prop->set_value (new_id.to_s());
333         } else {
334                 return false;
335         }
336
337         return true;
338 }
339
340 bool
341 AudioTrackImporter::parse_automation (XMLNode & node)
342 {
343
344         XMLNodeList const & lists = node.children ("AutomationList");
345         for (XMLNodeList::const_iterator it = lists.begin(); it != lists.end(); ++it) {
346                 XMLProperty * prop;
347
348                 if ((prop = (*it)->property ("id"))) {
349                         PBD::ID id;
350                         prop->set_value (id.to_s());
351                 }
352
353                 if (!(*it)->name().compare ("events")) {
354                         rate_convert_events (**it);
355                 }
356         }
357
358         return true;
359 }
360
361 bool
362 AudioTrackImporter::rate_convert_events (XMLNode & node)
363 {
364         if (node.children().empty()) {
365                 return false;
366         }
367
368         XMLNode* content_node = node.children().front();
369
370         if (content_node->content().empty()) {
371                 return false;
372         }
373
374         std::stringstream str (content_node->content());
375         std::ostringstream new_content;
376
377         framecnt_t x;
378         double y;
379         bool ok = true;
380
381         while (str) {
382                 str >> x;
383                 if (!str) {
384                         break;
385                 }
386                 str >> y;
387                 if (!str) {
388                         ok = false;
389                         break;
390                 }
391
392                 new_content << rate_convert_samples (x) << ' ' << y;
393         }
394
395         if (!ok) {
396                 error << X_("AudioTrackImporter: error in rate converting automation events") << endmsg;
397                 return false;
398         }
399
400         content_node->set_content (new_content.str());
401
402         return true;
403 }