* Fix tooltips in the session import dialog
[ardour.git] / libs / ardour / audio_region_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_region_importer.h"
22
23 #include <sstream>
24
25 #include "pbd/failed_constructor.h"
26 #include "pbd/compose.h"
27 #include "pbd/error.h"
28
29 #include "ardour/session.h"
30 #include "ardour/region.h"
31 #include "ardour/source_factory.h"
32 #include "ardour/region_factory.h"
33 #include "ardour/session_directory.h"
34
35 #include "i18n.h"
36
37 using namespace PBD;
38 using namespace ARDOUR;
39
40 /**** Handler ***/
41 AudioRegionImportHandler::AudioRegionImportHandler (XMLTree const & source, Session & session) :
42   ElementImportHandler (source, session)
43 {
44         XMLNode const * root = source.root();
45         XMLNode const * regions;
46         
47         if (!(regions = root->child (X_("Regions")))) {
48                 throw failed_constructor();
49         }
50         
51         create_regions_from_children (*regions, elements);
52 }
53
54 void
55 AudioRegionImportHandler::create_regions_from_children (XMLNode const & node, ElementList & list)
56 {
57         XMLNodeList const & children = node.children();
58         for (XMLNodeList::const_iterator it = children.begin(); it != children.end(); ++it) {
59                 XMLProperty const * type = (*it)->property("type");
60                 if (!(*it)->name().compare ("Region") && (!type || type->value() == "audio") ) {
61                         try {
62                                 list.push_back (ElementPtr ( new AudioRegionImporter (source, session, *this, **it)));
63                         } catch (failed_constructor err) {
64                                 set_dirty();
65                         }
66                 }
67         }
68 }
69
70 string
71 AudioRegionImportHandler::get_info () const
72 {
73         return _("Audio Regions");
74 }
75
76 bool
77 AudioRegionImportHandler::check_source (string const & filename) const
78 {
79         return (sources.find (filename) != sources.end());
80 }
81
82 void
83 AudioRegionImportHandler::add_source (string const & filename, boost::shared_ptr<Source> const & source)
84 {
85         sources.insert (SourcePair (filename, source));
86 }
87
88 boost::shared_ptr<Source> const &
89 AudioRegionImportHandler::get_source (string const & filename) const
90 {
91         return (sources.find (filename))->second;
92 }
93
94 void
95 AudioRegionImportHandler::register_id (PBD::ID & old_id, PBD::ID & new_id)
96 {
97         id_map.insert (IdPair (old_id, new_id));
98 }
99
100 PBD::ID const &
101 AudioRegionImportHandler::get_new_id (PBD::ID & old_id) const
102 {
103         return (id_map.find (old_id))->second;
104 }
105
106 /*** AudioRegionImporter ***/
107 AudioRegionImporter::AudioRegionImporter (XMLTree const & source, Session & session, AudioRegionImportHandler & handler, XMLNode const & node) : 
108   ElementImporter (source, session),
109   xml_region (node),
110   handler (handler),
111   old_id ("0"),
112   region_prepared (false),
113   sources_prepared (false)
114 {
115         if (!parse_xml_region () || !parse_source_xml ()) {
116                 throw failed_constructor();
117         }
118         handler.register_id (old_id, id);
119 }
120
121 AudioRegionImporter::~AudioRegionImporter ()
122 {
123 }
124
125 string
126 AudioRegionImporter::get_info () const
127 {
128         nframes_t length, position;
129         SMPTE::Time length_time, position_time;
130         std::ostringstream oss;
131         
132         // Get sample positions
133         std::istringstream iss_length(xml_region.property ("length")->value());
134         iss_length >> length;
135         std::istringstream iss_position(xml_region.property ("position")->value());
136         iss_position >> position;
137         
138         // Convert to smpte
139         session.sample_to_smpte(length, length_time, true, false);
140         session.sample_to_smpte(position, position_time, true, false);
141         
142         // return info
143         oss << _("Length: ") <<
144           smpte_to_string(length_time) <<
145           _("\nPosition: ") << 
146           smpte_to_string(position_time) <<
147           _("\nChannels: ") <<
148           xml_region.property ("channels")->value();
149
150         
151         return oss.str();
152 }
153
154 bool
155 AudioRegionImporter::_prepare_move ()
156 {
157         return true;
158 }
159
160 void
161 AudioRegionImporter::_cancel_move ()
162 {
163 }
164
165 void
166 AudioRegionImporter::_move ()
167 {
168         if (!region_prepared) {
169                 prepare_region();
170                 if (!region_prepared) {
171                         return;
172                 }
173         }
174         
175         if (broken()) {
176                 return;
177         }
178         
179         session.add_regions (region);
180 }
181
182 bool
183 AudioRegionImporter::parse_xml_region ()
184 {
185         XMLPropertyList const & props = xml_region.properties();
186         bool id_ok = false;
187         bool name_ok = false;
188         
189         for (XMLPropertyList::const_iterator it = props.begin(); it != props.end(); ++it) {
190                 string prop = (*it)->name();
191                 if (!prop.compare ("type") || !prop.compare ("stretch") ||
192                   !prop.compare ("shift") || !prop.compare ("first_edit") ||
193                   !prop.compare ("layer") || !prop.compare ("flags") ||
194                   !prop.compare ("scale-gain") || !prop.compare("channels") ||
195                   !prop.compare ("first-edit") ||
196                   prop.find ("master-source-") == 0 || prop.find ("source-") == 0) {
197                         // All ok
198                 } else if (!prop.compare ("start") || !prop.compare ("length") ||
199                   !prop.compare ("position") || !prop.compare ("ancestral-start") ||
200                   !prop.compare ("ancestral-length") || !prop.compare ("sync-position")) {
201                         // Sample rate conversion
202                         (*it)->set_value (rate_convert_samples ((*it)->value()));
203                 } else if (!prop.compare("id")) {
204                         // get old id and update id
205                         old_id = (*it)->value();
206                         (*it)->set_value (id.to_s());
207                         id_ok = true;
208                 } else if (!prop.compare("name")) {
209                         // rename region if necessary
210                         name = (*it)->value();
211                         name = session.new_region_name (name);
212                         (*it)->set_value (name);
213                         name_ok = true;
214                 } else {
215                         std::cerr << string_compose (X_("AudioRegionImporter (%1): did not recognise XML-property \"%2\""), name, prop) << endmsg;
216                 }
217         }
218         
219         if (!id_ok) {
220                 error << string_compose (X_("AudioRegionImporter (%1): did not find necessary XML-property \"id\""), name) << endmsg;
221                 return false;
222         }
223         
224         if (!name_ok) {
225                 error << X_("AudioRegionImporter: did not find necessary XML-property \"name\"") << endmsg;
226                 return false;
227         }
228         
229         return true;
230 }
231
232 bool
233 AudioRegionImporter::parse_source_xml ()
234 {
235         uint32_t channels;
236         char buf[128];
237         PBD::sys::path source_dir = get_sound_dir (source);
238         PBD::sys::path source_path;
239         XMLNode * source_node;
240         XMLProperty *prop;
241         
242         // Get XML for sources
243         if (!(source_node = source.root()->child (X_("Sources")))) {
244                 return false;
245         }
246         XMLNodeList const & sources = source_node->children();
247         
248         // Get source for each channel
249         if (!(prop = xml_region.property ("channels"))) {
250                 error << string_compose (X_("AudioRegionImporter (%1): did not find necessary XML-property \"channels\""), name) << endmsg;
251                 return false;
252         }
253         
254         channels = atoi (prop->value());
255         for (uint32_t i = 0; i < channels; ++i) {
256                 bool source_found = false;
257                 
258                 // Get id for source-n
259                 snprintf (buf, sizeof(buf), X_("source-%d"), i);
260                 prop = xml_region.property (buf);
261                 if (!prop) {
262                         error << string_compose (X_("AudioRegionImporter (%1): did not find necessary XML-property \"%2\""), name, buf) << endmsg;
263                         return false;
264                 }
265                 string source_id = prop->value();
266                 
267                 // Get source
268                 for (XMLNodeList::const_iterator it = sources.begin(); it != sources.end(); it++) {
269                         prop = (*it)->property ("id");
270                         if (prop && !source_id.compare (prop->value())) {
271                                 source_path = source_dir;
272                                 prop = (*it)->property ("name");
273                                 if (!prop) {
274                                         error << string_compose (X_("AudioRegionImporter (%1): source %2 has no \"name\" property"), name, source_id) << endmsg;
275                                         return false;
276                                 }
277                                 source_path /= prop->value();
278                                 filenames.push_back (source_path.to_string());
279                                 
280                                 source_found = true;
281                                 break;
282                         }
283                 }
284                 
285                 if (!source_found) {
286                         error << string_compose (X_("AudioRegionImporter (%1): could not find all necessary sources"), name) << endmsg;
287                         return false;
288                 }
289         }
290         
291         return true;
292 }
293
294 PBD::sys::path
295 AudioRegionImporter::get_sound_dir (XMLTree const & tree)
296 {
297         PBD::sys::path source_dir = tree.filename();
298         source_dir = source_dir.branch_path();
299         SessionDirectory session_dir(source_dir);
300         source_dir = session_dir.sound_path();
301         
302         return source_dir;
303 }
304
305 void
306 AudioRegionImporter::prepare_region ()
307 {
308         if (region_prepared) {
309                 return;
310         }
311         
312         SourceList source_list;
313         prepare_sources();
314         
315         // Create source list
316         for (std::list<string>::iterator it = filenames.begin(); it != filenames.end(); ++it) {
317                 source_list.push_back (handler.get_source (*it));
318         }
319         
320         // create region and update XML
321         region.push_back (RegionFactory::create (source_list, xml_region));
322         if (*region.begin()) {
323                 xml_region = (*region.begin())->get_state();
324         } else {
325                 error << string_compose (X_("AudioRegionImporter (%1): could not construct Region"), name) << endmsg;
326                 handler.set_errors();
327         }
328
329         region_prepared = true;
330 }
331
332 void
333 AudioRegionImporter::prepare_sources ()
334 {
335         if (sources_prepared) {
336                 return;
337         }
338         
339         status.total = 0;
340         status.replace_existing_source = false;
341         status.done = false;
342         status.cancel = false;
343         status.freeze = false;
344         status.progress = 0.0;
345         status.quality = SrcBest; // TODO other qualities also
346         
347         // Get sources that still need to be imported
348         for (std::list<string>::iterator it = filenames.begin(); it != filenames.end(); ++it) {
349                 if (!handler.check_source (*it)) {
350                         status.paths.push_back (*it);
351                         status.total++;
352                 }
353         }
354         
355         // import files
356         // TODO: threading & exception handling
357         session.import_audiofiles (status);
358         
359         // Add imported sources to handlers map
360         std::vector<Glib::ustring>::iterator file_it = status.paths.begin();
361         for (SourceList::iterator source_it = status.sources.begin(); source_it != status.sources.end(); ++source_it) {
362                 if (*source_it) {
363                         handler.add_source(*file_it, *source_it);
364                 } else {
365                         error << string_compose (X_("AudioRegionImporter (%1): could not import all necessary sources"), name) << endmsg;
366                         handler.set_errors();
367                         set_broken();
368                 }
369                 
370                 ++file_it;
371         }
372         
373         sources_prepared = true;
374 }
375
376 void
377 AudioRegionImporter::add_sources_to_session ()
378 {
379         if (!sources_prepared) {
380                 prepare_sources();
381         }
382         
383         if (broken()) {
384                 return;
385         }
386         
387         for (std::list<string>::iterator it = filenames.begin(); it != filenames.end(); ++it) {
388                 session.add_source (handler.get_source (*it));
389         }
390 }
391
392 XMLNode const & 
393 AudioRegionImporter::get_xml ()
394 {
395         if(!region_prepared) {
396                 prepare_region();
397         }
398         
399         return xml_region;
400 }