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