fix crash when copy'ing latent plugins
[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 <glibmm/miscutils.h>
26
27 #include "pbd/failed_constructor.h"
28 #include "pbd/compose.h"
29 #include "pbd/error.h"
30
31 #include "ardour/session.h"
32 #include "ardour/region.h"
33 #include "ardour/region_factory.h"
34 #include "ardour/session_directory.h"
35
36 #include "pbd/i18n.h"
37
38 using namespace std;
39 using namespace PBD;
40 using namespace ARDOUR;
41
42 /**** Handler ***/
43 AudioRegionImportHandler::AudioRegionImportHandler (XMLTree const & source, Session & session) :
44   ElementImportHandler (source, session)
45 {
46         XMLNode const * root = source.root();
47         XMLNode const * regions;
48
49         if (!(regions = root->child (X_("Regions")))) {
50                 throw failed_constructor();
51         }
52
53         create_regions_from_children (*regions, elements);
54 }
55
56 void
57 AudioRegionImportHandler::create_regions_from_children (XMLNode const & node, ElementList & list)
58 {
59         XMLNodeList const & children = node.children();
60         for (XMLNodeList::const_iterator it = children.begin(); it != children.end(); ++it) {
61                 XMLProperty const * type = (*it)->property("type");
62                 if (!(*it)->name().compare ("Region") && (!type || type->value() == "audio") ) {
63                         try {
64                                 list.push_back (ElementPtr ( new AudioRegionImporter (source, session, *this, **it)));
65                         } catch (failed_constructor err) {
66                                 set_dirty();
67                         }
68                 }
69         }
70 }
71
72 string
73 AudioRegionImportHandler::get_info () const
74 {
75         return _("Audio Regions");
76 }
77
78 bool
79 AudioRegionImportHandler::check_source (string const & filename) const
80 {
81         return (sources.find (filename) != sources.end());
82 }
83
84 void
85 AudioRegionImportHandler::add_source (string const & filename, boost::shared_ptr<Source> const & source)
86 {
87         sources.insert (SourcePair (filename, source));
88 }
89
90 boost::shared_ptr<Source> const &
91 AudioRegionImportHandler::get_source (string const & filename) const
92 {
93         return (sources.find (filename))->second;
94 }
95
96 void
97 AudioRegionImportHandler::register_id (PBD::ID & old_id, PBD::ID & new_id)
98 {
99         id_map.insert (IdPair (old_id, new_id));
100 }
101
102 PBD::ID const &
103 AudioRegionImportHandler::get_new_id (PBD::ID & old_id) const
104 {
105         return (id_map.find (old_id))->second;
106 }
107
108 /*** AudioRegionImporter ***/
109 AudioRegionImporter::AudioRegionImporter (XMLTree const & source, Session & session, AudioRegionImportHandler & handler, XMLNode const & node) :
110   ElementImporter (source, session),
111   xml_region (node),
112   handler (handler),
113   old_id ("0"),
114   region_prepared (false),
115   sources_prepared (false)
116 {
117         if (!parse_xml_region () || !parse_source_xml ()) {
118                 throw failed_constructor();
119         }
120         handler.register_id (old_id, id);
121 }
122
123 AudioRegionImporter::~AudioRegionImporter ()
124 {
125 }
126
127 string
128 AudioRegionImporter::get_info () const
129 {
130         framecnt_t length, position;
131         Timecode::Time length_time, position_time;
132         std::ostringstream oss;
133
134         // Get sample positions
135         std::istringstream iss_length(xml_region.property ("length")->value());
136         iss_length >> length;
137         std::istringstream iss_position(xml_region.property ("position")->value());
138         iss_position >> position;
139
140         // Convert to timecode
141         session.sample_to_timecode(length, length_time, true, false);
142         session.sample_to_timecode(position, position_time, true, false);
143
144         // return info
145         oss << _("Length: ") <<
146           timecode_to_string(length_time) <<
147           _("\nPosition: ") <<
148           timecode_to_string(position_time) <<
149           _("\nChannels: ") <<
150           xml_region.property ("channels")->value();
151
152
153         return oss.str();
154 }
155
156 bool
157 AudioRegionImporter::_prepare_move ()
158 {
159         return true;
160 }
161
162 void
163 AudioRegionImporter::_cancel_move ()
164 {
165 }
166
167 void
168 AudioRegionImporter::_move ()
169 {
170         if (!region_prepared) {
171                 prepare_region();
172                 if (!region_prepared) {
173                         return;
174                 }
175         }
176
177         if (broken()) {
178                 return;
179         }
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 = RegionFactory::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         std::string source_dir(get_sound_dir (source));
238         XMLNode * source_node;
239         XMLProperty const * prop;
240
241         // Get XML for sources
242         if (!(source_node = source.root()->child (X_("Sources")))) {
243                 return false;
244         }
245         XMLNodeList const & sources = source_node->children();
246
247         // Get source for each channel
248         if (!(prop = xml_region.property ("channels"))) {
249                 error << string_compose (X_("AudioRegionImporter (%1): did not find necessary XML-property \"channels\""), name) << endmsg;
250                 return false;
251         }
252
253         channels = atoi (prop->value().c_str());
254         for (uint32_t i = 0; i < channels; ++i) {
255                 bool source_found = false;
256
257                 // Get id for source-n
258                 snprintf (buf, sizeof(buf), X_("source-%d"), i);
259                 prop = xml_region.property (buf);
260                 if (!prop) {
261                         error << string_compose (X_("AudioRegionImporter (%1): did not find necessary XML-property \"%2\""), name, buf) << endmsg;
262                         return false;
263                 }
264                 string source_id = prop->value();
265
266                 // Get source
267                 for (XMLNodeList::const_iterator it = sources.begin(); it != sources.end(); ++it) {
268                         prop = (*it)->property ("id");
269                         if (prop && !source_id.compare (prop->value())) {
270                                 prop = (*it)->property ("name");
271                                 if (!prop) {
272                                         error << string_compose (X_("AudioRegionImporter (%1): source %2 has no \"name\" property"), name, source_id) << endmsg;
273                                         return false;
274                                 }
275                                 filenames.push_back (Glib::build_filename (source_dir, prop->value()));
276                                 source_found = true;
277                                 break;
278                         }
279                 }
280
281                 if (!source_found) {
282                         error << string_compose (X_("AudioRegionImporter (%1): could not find all necessary sources"), name) << endmsg;
283                         return false;
284                 }
285         }
286
287         return true;
288 }
289
290 std::string
291 AudioRegionImporter::get_sound_dir (XMLTree const & tree)
292 {
293         SessionDirectory session_dir(Glib::path_get_dirname (tree.filename()));
294         return session_dir.sound_path();
295 }
296
297 void
298 AudioRegionImporter::prepare_region ()
299 {
300         if (region_prepared) {
301                 return;
302         }
303
304         SourceList source_list;
305         prepare_sources();
306
307         // Create source list
308         for (std::list<string>::iterator it = filenames.begin(); it != filenames.end(); ++it) {
309                 source_list.push_back (handler.get_source (*it));
310         }
311
312         // create region and update XML
313         boost::shared_ptr<Region> r = RegionFactory::create (source_list, xml_region);
314         if (session.config.get_glue_new_regions_to_bars_and_beats ()) {
315                 r->set_position_lock_style (MusicTime);
316         }
317         region.push_back (r);
318         if (*region.begin()) {
319                 xml_region = (*region.begin())->get_state();
320         } else {
321                 error << string_compose (X_("AudioRegionImporter (%1): could not construct Region"), name) << endmsg;
322                 handler.set_errors();
323         }
324
325         region_prepared = true;
326 }
327
328 void
329 AudioRegionImporter::prepare_sources ()
330 {
331         if (sources_prepared) {
332                 return;
333         }
334
335         status.total = 0;
336         status.replace_existing_source = false;
337         status.done = false;
338         status.cancel = false;
339         status.freeze = false;
340         status.progress = 0.0;
341         status.quality = SrcBest; // TODO other qualities also
342
343         // Get sources that still need to be imported
344         for (std::list<string>::iterator it = filenames.begin(); it != filenames.end(); ++it) {
345                 if (!handler.check_source (*it)) {
346                         status.paths.push_back (*it);
347                         status.total++;
348                 }
349         }
350
351         // import files
352         // TODO: threading & exception handling
353         session.import_files (status);
354
355         // Add imported sources to handlers map
356         std::vector<string>::iterator file_it = status.paths.begin();
357         for (SourceList::iterator source_it = status.sources.begin(); source_it != status.sources.end(); ++source_it) {
358                 if (*source_it) {
359                         handler.add_source(*file_it, *source_it);
360                 } else {
361                         error << string_compose (X_("AudioRegionImporter (%1): could not import all necessary sources"), name) << endmsg;
362                         handler.set_errors();
363                         set_broken();
364                 }
365
366                 ++file_it;
367         }
368
369         sources_prepared = true;
370 }
371
372 void
373 AudioRegionImporter::add_sources_to_session ()
374 {
375         if (!sources_prepared) {
376                 prepare_sources();
377         }
378
379         if (broken()) {
380                 return;
381         }
382
383         for (std::list<string>::iterator it = filenames.begin(); it != filenames.end(); ++it) {
384                 session.add_source (handler.get_source (*it));
385         }
386 }
387
388 XMLNode const &
389 AudioRegionImporter::get_xml ()
390 {
391         if(!region_prepared) {
392                 prepare_region();
393         }
394
395         return xml_region;
396 }