Trim include dependency tree (particularly on evoral/Sequence.hpp).
[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/failed_constructor.h>
28 #include <pbd/convert.h>
29
30 #include <sstream>
31 #include <algorithm>
32
33 #include "i18n.h"
34
35 using namespace PBD;
36 using namespace ARDOUR;
37
38 /*** AudioTrackImportHandler ***/
39
40 AudioTrackImportHandler::AudioTrackImportHandler (XMLTree const & source, Session & session, AudioPlaylistImportHandler & pl_handler) :
41   ElementImportHandler (source, session),
42   pl_handler (pl_handler)
43 {
44         XMLNode const * root = source.root();
45         XMLNode const * routes;
46         
47         if (!(routes = root->child ("Routes"))) {
48                 throw failed_constructor();
49         }
50         
51         XMLNodeList const & route_list = routes->children();
52         for (XMLNodeList::const_iterator it = route_list.begin(); it != route_list.end(); ++it) {
53                 const XMLProperty* type = (*it)->property("default-type");
54                 if ( (!type || type->value() == "audio") &&  ((*it)->property ("diskstream") != 0 || (*it)->property ("diskstream-id") != 0)) {
55                         try {
56                                 elements.push_back (ElementPtr ( new AudioTrackImporter (source, session, *this, **it, pl_handler)));
57                         } catch (failed_constructor err) {
58                                 set_dirty();
59                         }
60                 }
61         }
62 }
63
64 string
65 AudioTrackImportHandler::get_info () const
66 {
67         return _("Audio Tracks");
68 }
69
70
71 /*** AudioTrackImporter ***/
72
73 AudioTrackImporter::AudioTrackImporter (XMLTree const & source,
74                                         Session & session,
75                                         AudioTrackImportHandler & track_handler,
76                                         XMLNode const & node,
77                                         AudioPlaylistImportHandler & pl_handler) :
78   ElementImporter (source, session),
79   track_handler (track_handler),
80   xml_track (node),
81   pl_handler (pl_handler)
82 {
83         XMLProperty * prop;
84
85         if (!parse_route_xml ()) {
86                 throw failed_constructor();
87         }
88         
89         if (!parse_io ()) {
90                 throw failed_constructor();
91         }
92         
93         XMLNodeList const & controllables = node.children ("Controllable");
94         for (XMLNodeList::const_iterator it = controllables.begin(); it != controllables.end(); ++it) {
95                 parse_controllable (**it);
96         }
97         
98         XMLNode * remote_control = xml_track.child ("RemoteControl");
99         if (remote_control && (prop = remote_control->property ("id"))) {
100                 uint32_t control_id = session.ntracks() + session.nbusses() + 1;
101                 prop->set_value (to_string (control_id, std::dec));
102         }
103         
104         xml_track.remove_nodes_and_delete ("Extra");
105 }
106
107 AudioTrackImporter::~AudioTrackImporter ()
108 {
109         playlists.clear ();
110 }
111
112 bool
113 AudioTrackImporter::parse_route_xml ()
114 {
115         bool ds_ok = false;
116
117         // Remove order keys, new ones will be generated
118         xml_track.remove_property ("order-keys");
119
120         XMLPropertyList const & props = xml_track.properties();
121         for (XMLPropertyList::const_iterator it = props.begin(); it != props.end(); ++it) {
122                 string prop = (*it)->name();
123                 if (!prop.compare ("default-type") || !prop.compare ("flags") ||
124                   !prop.compare ("active") || !prop.compare ("muted") ||
125                   !prop.compare ("soloed") || !prop.compare ("phase-invert") ||
126                   !prop.compare ("denormal-protection") || !prop.compare("mute-affects-pre-fader") ||
127                   !prop.compare ("mute-affects-post-fader") || !prop.compare("mute-affects-control-outs") ||
128                   !prop.compare ("mute-affects-main-outs") || !prop.compare("mode")) {
129                         // All ok
130                 } else if (!prop.compare("diskstream-id")) {
131                         old_ds_id = (*it)->value();
132                         (*it)->set_value (new_ds_id.to_s());
133                         ds_ok = true;
134                 } else {
135                         std::cerr << string_compose (X_("AudioTrackImporter: did not recognise XML-property \"%1\""), prop) << endmsg;
136                 }
137         }
138         
139         if (!ds_ok) {
140                 error << X_("AudioTrackImporter: did not find necessary XML-property \"diskstream-id\"") << endmsg;
141                 return false;
142         }
143         
144         return true;
145 }
146
147 bool
148 AudioTrackImporter::parse_io ()
149 {
150         XMLNode * io;
151         bool name_ok = false;
152         bool id_ok = false;
153
154         if (!(io = xml_track.child ("IO"))) {
155                 return false;
156         }
157         
158         XMLPropertyList const & props = io->properties();
159
160         for (XMLPropertyList::const_iterator it = props.begin(); it != props.end(); ++it) {
161                 string prop = (*it)->name();
162                 if (!prop.compare ("gain") || !prop.compare ("iolimits")) {
163                         // All ok
164                 } else if (!prop.compare("name")) {
165                         name = (*it)->value();
166                         name_ok = true;
167                 } else if (!prop.compare("id")) {
168                         PBD::ID id;
169                         (*it)->set_value (id.to_s());
170                         id_ok = true;
171                 } else if (!prop.compare("inputs")) {
172                         // TODO Handle this properly!
173                         /* Input and output ports are counted and added empty, so that no in/output connecting function fails. */
174                         uint32_t num_inputs = std::count ((*it)->value().begin(), (*it)->value().end(), '{');
175                         std::string value;
176                         for (uint32_t i = 0; i < num_inputs; i++) { value += "{}"; }
177                         (*it)->set_value (value);
178                 } else if (!prop.compare("outputs")) {
179                         // TODO See comments above
180                         uint32_t num_outputs = std::count ((*it)->value().begin(), (*it)->value().end(), '{');
181                         std::string value;
182                         for (uint32_t i = 0; i < num_outputs; i++) { value += "{}"; }
183                         (*it)->set_value (value);
184                 } else {
185                         std::cerr << string_compose (X_("AudioTrackImporter: did not recognise XML-property \"%1\""), prop) << endmsg;
186                 }
187         }
188         
189         if (!name_ok) {
190                 error << X_("AudioTrackImporter: did not find necessary XML-property \"name\"") << endmsg;
191                 return false;
192         }
193         
194         if (!id_ok) {
195                 error << X_("AudioTrackImporter: did not find necessary XML-property \"id\"") << endmsg;
196                 return false;
197         }
198         
199         XMLNodeList const & controllables = io->children ("Controllable");
200         for (XMLNodeList::const_iterator it = controllables.begin(); it != controllables.end(); ++it) {
201                 parse_controllable (**it);
202         }
203         
204         XMLNodeList const & processors = io->children ("Processor");
205         for (XMLNodeList::const_iterator it = processors.begin(); it != processors.end(); ++it) {
206                 parse_processor (**it);
207         }
208         
209         XMLNodeList const & automations = io->children ("Automation");
210         for (XMLNodeList::const_iterator it = automations.begin(); it != automations.end(); ++it) {
211                 parse_automation (**it);
212         }
213         
214         return true;
215 }
216
217 string
218 AudioTrackImporter::get_info () const
219 {
220         // TODO
221         return name;
222 }
223
224 bool
225 AudioTrackImporter::_prepare_move ()
226 {
227         /* Copy dependent playlists */
228
229         pl_handler.playlists_by_diskstream (old_ds_id, playlists);
230         
231         for (PlaylistList::iterator it = playlists.begin(); it != playlists.end(); ++it) {
232                 if (!(*it)->prepare_move ()) {
233                         playlists.clear ();
234                         return false;
235                 }
236                 (*it)->set_diskstream (new_ds_id);
237         }
238         
239         /* Rename */
240         
241         while (session.route_by_name (name) || !track_handler.check_name (name)) {
242                 std::pair<bool, string> rename_pair = Rename (_("A playlist with this name already exists, please rename it."), name);
243                 if (!rename_pair.first) {
244                         return false;
245                 }
246                 name = rename_pair.second;
247         }
248         xml_track.child ("IO")->property ("name")->set_value (name);
249         track_handler.add_name (name);
250         
251         return true;
252 }
253
254 void
255 AudioTrackImporter::_cancel_move ()
256 {
257         track_handler.remove_name (name);
258         playlists.clear ();
259 }
260
261 void
262 AudioTrackImporter::_move ()
263 {       
264         /* Add diskstream */
265         
266         boost::shared_ptr<XMLSharedNodeList> ds_node_list;
267         string xpath = "/Session/DiskStreams/AudioDiskstream[@id='" + old_ds_id.to_s() + "']";
268         ds_node_list = source.root()->find (xpath);
269         
270         if (ds_node_list->size() != 1) {
271                 error << string_compose (_("Error Importing Audio track %1"), name) << endmsg;
272                 return;
273         }
274         
275         boost::shared_ptr<XMLNode> ds_node = ds_node_list->front();
276         ds_node->property ("id")->set_value (new_ds_id.to_s());
277         
278         boost::shared_ptr<Diskstream> new_ds (new AudioDiskstream (session, *ds_node));
279         new_ds->set_name (name);
280         session.add_diskstream (new_ds);
281
282         /* Import playlists */
283
284         for (PlaylistList::const_iterator it = playlists.begin(); it != playlists.end(); ++it) {
285                 (*it)->move ();
286         }
287
288         /* Import track */
289
290         XMLNode routes ("Routes");
291         routes.add_child_copy (xml_track);
292         session.load_routes (routes);
293 }
294
295 bool
296 AudioTrackImporter::parse_processor (XMLNode & node)
297 {
298         XMLNode * automation = node.child ("Automation");
299         if (automation) {
300                 parse_automation (*automation);
301         }
302         
303         return true;
304 }
305
306 bool
307 AudioTrackImporter::parse_controllable (XMLNode & node)
308 {
309         XMLProperty * prop;
310         
311         if ((prop = node.property ("id"))) {
312                 PBD::ID new_id;
313                 prop->set_value (new_id.to_s());
314         } else {
315                 return false;
316         }
317
318         return true;
319 }
320
321 bool
322 AudioTrackImporter::parse_automation (XMLNode & node)
323 {
324
325         XMLNodeList const & lists = node.children ("AutomationList");
326         for (XMLNodeList::const_iterator it = lists.begin(); it != lists.end(); ++it) {
327                 XMLProperty * prop;
328                 
329                 if ((prop = (*it)->property ("id"))) {
330                         PBD::ID id;
331                         prop->set_value (id.to_s());
332                 }
333                 
334                 if (!(*it)->name().compare ("events")) {
335                         rate_convert_events (**it);
336                 }
337         }
338
339         return true;
340 }
341
342 bool
343 AudioTrackImporter::rate_convert_events (XMLNode & node)
344 {
345         if (node.children().empty()) {
346                 return false;
347         }
348
349         XMLNode* content_node = node.children().front();
350
351         if (content_node->content().empty()) {
352                 return false;
353         }
354         
355         std::stringstream str (content_node->content());
356         std::ostringstream new_content;
357         
358         nframes_t x;
359         double y;
360         bool ok = true;
361         
362         while (str) {
363                 str >> x;
364                 if (!str) {
365                         break;
366                 }
367                 str >> y;
368                 if (!str) {
369                         ok = false;
370                         break;
371                 }
372                 
373                 new_content << rate_convert_samples (x) << ' ' << y;
374         }
375         
376         if (!ok) {
377                 error << X_("AudioTrackImporter: error in rate converting automation events") << endmsg;
378                 return false;
379         }
380
381         content_node->set_content (new_content.str());
382
383         return true;
384 }