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