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