dramatic change in logic and naming for operations related to adding a MIDI region...
[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 /** @return true if everything is ok */
227 bool
228 AudioTrackImporter::_prepare_move ()
229 {
230         /* Copy dependent playlists */
231
232         pl_handler.playlists_by_diskstream (old_ds_id, playlists);
233
234         for (PlaylistList::iterator it = playlists.begin(); it != playlists.end(); ++it) {
235                 if (!(*it)->prepare_move ()) {
236                         playlists.clear ();
237                         return false;
238                 }
239                 (*it)->set_diskstream (new_ds_id);
240         }
241
242         /* Rename */
243
244         while (session.route_by_name (name) || !track_handler.check_name (name)) {
245                 std::pair<bool, string> rename_pair = *Rename (_("A playlist with this name already exists, please rename it."), name);
246                 if (!rename_pair.first) {
247                         return false;
248                 }
249                 name = rename_pair.second;
250         }
251         
252         XMLNode* c = xml_track.child ("IO");
253         if (!c) {
254                 error << _("badly-formed XML in imported track") << endmsg;
255                 return false;
256         }
257
258         XMLProperty* p = c->property ("name");
259         if (!p) {
260                 error << _("badly-formed XML in imported track") << endmsg;
261                 return false;
262         }
263         
264         p->set_value (name);
265         
266         track_handler.add_name (name);
267
268         return true;
269 }
270
271 void
272 AudioTrackImporter::_cancel_move ()
273 {
274         track_handler.remove_name (name);
275         playlists.clear ();
276 }
277
278 void
279 AudioTrackImporter::_move ()
280 {
281         /* Add diskstream */
282
283         boost::shared_ptr<XMLSharedNodeList> ds_node_list;
284         string xpath = "/Session/DiskStreams/AudioDiskstream[@id='" + old_ds_id.to_s() + "']";
285         ds_node_list = source.find (xpath);
286
287         if (ds_node_list->size() != 1) {
288                 error << string_compose (_("Error Importing Audio track %1"), name) << endmsg;
289                 return;
290         }
291
292         boost::shared_ptr<XMLNode> ds_node = ds_node_list->front();
293         XMLProperty* p = ds_node->property (X_("id"));
294         assert (p);
295         p->set_value (new_ds_id.to_s());
296
297         boost::shared_ptr<Diskstream> new_ds (new AudioDiskstream (session, *ds_node));
298         new_ds->set_name (name);
299         new_ds->do_refill_with_alloc ();
300         new_ds->set_block_size (session.get_block_size ());
301
302         /* Import playlists */
303
304         for (PlaylistList::const_iterator it = playlists.begin(); it != playlists.end(); ++it) {
305                 (*it)->move ();
306         }
307
308         /* Import track */
309
310         XMLNode routes ("Routes");
311         routes.add_child_copy (xml_track);
312         session.load_routes (routes, 3000);
313 }
314
315 bool
316 AudioTrackImporter::parse_processor (XMLNode & node)
317 {
318         XMLNode * automation = node.child ("Automation");
319         if (automation) {
320                 parse_automation (*automation);
321         }
322
323         return true;
324 }
325
326 bool
327 AudioTrackImporter::parse_controllable (XMLNode & node)
328 {
329         XMLProperty * prop;
330
331         if ((prop = node.property ("id"))) {
332                 PBD::ID new_id;
333                 prop->set_value (new_id.to_s());
334         } else {
335                 return false;
336         }
337
338         return true;
339 }
340
341 bool
342 AudioTrackImporter::parse_automation (XMLNode & node)
343 {
344
345         XMLNodeList const & lists = node.children ("AutomationList");
346         for (XMLNodeList::const_iterator it = lists.begin(); it != lists.end(); ++it) {
347                 XMLProperty * prop;
348
349                 if ((prop = (*it)->property ("id"))) {
350                         PBD::ID id;
351                         prop->set_value (id.to_s());
352                 }
353
354                 if (!(*it)->name().compare ("events")) {
355                         rate_convert_events (**it);
356                 }
357         }
358
359         return true;
360 }
361
362 bool
363 AudioTrackImporter::rate_convert_events (XMLNode & node)
364 {
365         if (node.children().empty()) {
366                 return false;
367         }
368
369         XMLNode* content_node = node.children().front();
370
371         if (content_node->content().empty()) {
372                 return false;
373         }
374
375         std::stringstream str (content_node->content());
376         std::ostringstream new_content;
377
378         framecnt_t x;
379         double y;
380         bool ok = true;
381
382         while (str) {
383                 str >> x;
384                 if (!str) {
385                         break;
386                 }
387                 str >> y;
388                 if (!str) {
389                         ok = false;
390                         break;
391                 }
392
393                 new_content << rate_convert_samples (x) << ' ' << y;
394         }
395
396         if (!ok) {
397                 error << X_("AudioTrackImporter: error in rate converting automation events") << endmsg;
398                 return false;
399         }
400
401         content_node->set_content (new_content.str());
402
403         return true;
404 }