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