possible fix for race between diskstream buffer overwrite and channel setup
[ardour2.git] / libs / ardour / import.cc
blobe05d162a568a6880f188dd36c67b7a51af944039
1 /*
2 Copyright (C) 2000 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #ifdef WAF_BUILD
22 #include "libardour-config.h"
23 #endif
25 #include <cstdio>
26 #include <cstdlib>
27 #include <string>
28 #include <climits>
29 #include <cerrno>
30 #include <unistd.h>
31 #include <sys/stat.h>
32 #include <time.h>
33 #include <stdint.h>
35 #include <sndfile.h>
36 #include <samplerate.h>
38 #include <glibmm.h>
40 #include <boost/scoped_array.hpp>
41 #include <boost/shared_array.hpp>
43 #include "pbd/basename.h"
44 #include "pbd/convert.h"
46 #include "evoral/SMF.hpp"
48 #include "ardour/analyser.h"
49 #include "ardour/ardour.h"
50 #include "ardour/audio_diskstream.h"
51 #include "ardour/audioengine.h"
52 #include "ardour/audioregion.h"
53 #include "ardour/import_status.h"
54 #include "ardour/region_factory.h"
55 #include "ardour/resampled_source.h"
56 #include "ardour/session.h"
57 #include "ardour/session_directory.h"
58 #include "ardour/smf_source.h"
59 #include "ardour/sndfile_helpers.h"
60 #include "ardour/sndfileimportable.h"
61 #include "ardour/sndfilesource.h"
62 #include "ardour/source_factory.h"
63 #include "ardour/tempo.h"
65 #ifdef HAVE_COREAUDIO
66 #include "ardour/caimportable.h"
67 #endif
69 #include "i18n.h"
71 using namespace std;
72 using namespace ARDOUR;
73 using namespace PBD;
75 static boost::shared_ptr<ImportableSource>
76 open_importable_source (const string& path, nframes_t samplerate, ARDOUR::SrcQuality quality)
78 /* try libsndfile first, because it can get BWF info from .wav, which ExtAudioFile cannot.
79 We don't necessarily need that information in an ImportableSource, but it keeps the
80 logic the same as in SourceFactory::create()
83 try {
84 boost::shared_ptr<SndFileImportableSource> source(new SndFileImportableSource(path));
86 if (source->samplerate() == samplerate) {
87 return source;
90 /* rewrap as a resampled source */
92 return boost::shared_ptr<ImportableSource>(new ResampledImportableSource(source, samplerate, quality));
95 catch (...) {
97 #ifdef HAVE_COREAUDIO
99 /* libsndfile failed, see if we can use CoreAudio to handle the IO */
101 CAImportableSource* src = new CAImportableSource(path);
102 boost::shared_ptr<CAImportableSource> source (src);
104 if (source->samplerate() == samplerate) {
105 return source;
108 /* rewrap as a resampled source */
110 return boost::shared_ptr<ImportableSource>(new ResampledImportableSource(source, samplerate, quality));
112 #else
113 throw; // rethrow
114 #endif
119 static std::string
120 get_non_existent_filename (DataType type, const bool allow_replacing, const std::string& destdir, const std::string& basename, uint channel, uint channels)
122 char buf[PATH_MAX+1];
123 bool goodfile = false;
124 string base(basename);
125 const char* ext = (type == DataType::AUDIO) ? "wav" : "mid";
127 do {
129 if (type == DataType::AUDIO && channels == 2) {
130 if (channel == 0) {
131 snprintf (buf, sizeof(buf), "%s-L.wav", base.c_str());
132 } else {
133 snprintf (buf, sizeof(buf), "%s-R.wav", base.c_str());
135 } else if (channels > 1) {
136 snprintf (buf, sizeof(buf), "%s-c%d.%s", base.c_str(), channel, ext);
137 } else {
138 snprintf (buf, sizeof(buf), "%s.%s", base.c_str(), ext);
142 string tempname = destdir + "/" + buf;
143 if (!allow_replacing && Glib::file_test (tempname, Glib::FILE_TEST_EXISTS)) {
145 /* if the file already exists, we must come up with
146 * a new name for it. for now we just keep appending
147 * _ to basename
150 base += "_";
152 } else {
154 goodfile = true;
157 } while ( !goodfile);
159 return buf;
162 static vector<string>
163 get_paths_for_new_sources (const bool allow_replacing, const string& import_file_path, const string& session_dir, uint channels)
165 vector<string> new_paths;
166 const string basename = basename_nosuffix (import_file_path);
168 SessionDirectory sdir(session_dir);
170 for (uint n = 0; n < channels; ++n) {
172 const DataType type = (import_file_path.rfind(".mid") != string::npos)
173 ? DataType::MIDI : DataType::AUDIO;
175 std::string filepath = (type == DataType::MIDI)
176 ? sdir.midi_path().to_string() : sdir.sound_path().to_string();
178 filepath += '/';
179 filepath += get_non_existent_filename (type, allow_replacing, filepath, basename, n, channels);
180 new_paths.push_back (filepath);
183 return new_paths;
186 static bool
187 map_existing_mono_sources (const vector<string>& new_paths, Session& /*sess*/,
188 uint /*samplerate*/, vector<boost::shared_ptr<Source> >& newfiles, Session *session)
190 for (vector<string>::const_iterator i = new_paths.begin();
191 i != new_paths.end(); ++i)
193 boost::shared_ptr<Source> source = session->source_by_path_and_channel(*i, 0);
195 if (source == 0) {
196 error << string_compose(_("Could not find a source for %1 even though we are updating this file!"), (*i)) << endl;
197 return false;
200 newfiles.push_back(boost::dynamic_pointer_cast<Source>(source));
202 return true;
205 static bool
206 create_mono_sources_for_writing (const vector<string>& new_paths, Session& sess,
207 uint samplerate, vector<boost::shared_ptr<Source> >& newfiles,
208 framepos_t timeline_position)
210 for (vector<string>::const_iterator i = new_paths.begin(); i != new_paths.end(); ++i)
212 boost::shared_ptr<Source> source;
216 const DataType type = ((*i).rfind(".mid") != string::npos)
217 ? DataType::MIDI : DataType::AUDIO;
220 source = SourceFactory::createWritable (type, sess,
221 i->c_str(),
222 false, // destructive
223 samplerate);
225 catch (const failed_constructor& err)
227 error << string_compose (_("Unable to create file %1 during import"), *i) << endmsg;
228 return false;
231 newfiles.push_back(boost::dynamic_pointer_cast<Source>(source));
233 /* for audio files, reset the timeline position so that any BWF-ish
234 information in the original files we are importing from is maintained.
237 boost::shared_ptr<AudioFileSource> afs;
238 if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(source)) != 0) {
239 afs->set_timeline_position(timeline_position);
242 return true;
245 static Glib::ustring
246 compose_status_message (const string& path,
247 uint file_samplerate,
248 uint session_samplerate,
249 uint current_file,
250 uint total_files)
252 if (file_samplerate != session_samplerate) {
253 return string_compose (_("resampling %1 from %2kHz to %3kHz\n(%4 of %5)"),
254 Glib::path_get_basename (path),
255 file_samplerate/1000.0f,
256 session_samplerate/1000.0f,
257 current_file, total_files);
260 return string_compose (_("copying %1\n(%2 of %3)"),
261 Glib::path_get_basename (path),
262 current_file, total_files);
265 static void
266 write_audio_data_to_new_files (ImportableSource* source, ImportStatus& status,
267 vector<boost::shared_ptr<Source> >& newfiles)
269 const nframes_t nframes = ResampledImportableSource::blocksize;
270 boost::shared_ptr<AudioFileSource> afs;
271 uint channels = source->channels();
273 boost::scoped_array<float> data(new float[nframes * channels]);
274 vector<boost::shared_array<Sample> > channel_data;
276 for (uint n = 0; n < channels; ++n) {
277 channel_data.push_back(boost::shared_array<Sample>(new Sample[nframes]));
280 uint read_count = 0;
281 status.progress = 0.0f;
283 while (!status.cancel) {
285 nframes_t nread, nfread;
286 uint x;
287 uint chn;
289 if ((nread = source->read (data.get(), nframes)) == 0) {
290 break;
292 nfread = nread / channels;
294 /* de-interleave */
296 for (chn = 0; chn < channels; ++chn) {
298 nframes_t n;
299 for (x = chn, n = 0; n < nfread; x += channels, ++n) {
300 channel_data[chn][n] = (Sample) data[x];
304 /* flush to disk */
306 for (chn = 0; chn < channels; ++chn) {
307 if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(newfiles[chn])) != 0) {
308 afs->write (channel_data[chn].get(), nfread);
312 read_count += nread;
313 status.progress = read_count / (source->ratio () * source->length() * channels);
317 static void
318 write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status,
319 vector<boost::shared_ptr<Source> >& newfiles)
321 uint32_t buf_size = 4;
322 uint8_t* buf = (uint8_t*)malloc(buf_size);
324 status.progress = 0.0f;
326 try {
328 for (unsigned i = 1; i <= source->num_tracks(); ++i) {
329 boost::shared_ptr<SMFSource> smfs = boost::dynamic_pointer_cast<SMFSource>(newfiles[i-1]);
330 smfs->drop_model();
332 source->seek_to_track(i);
334 uint64_t t = 0;
335 uint32_t delta_t = 0;
336 uint32_t size = 0;
337 bool first = true;
339 while (!status.cancel) {
340 size = buf_size;
342 int ret = source->read_event(&delta_t, &size, &buf);
343 if (size > buf_size)
344 buf_size = size;
346 if (ret < 0) { // EOT
347 break;
350 t += delta_t;
352 if (ret == 0) { // Meta
353 continue;
356 if (first) {
357 smfs->mark_streaming_write_started ();
358 first = false;
361 smfs->append_event_unlocked_beats(Evoral::Event<double>(0,
362 (double)t / (double)source->ppqn(),
363 size,
364 buf));
366 if (status.progress < 0.99)
367 status.progress += 0.01;
370 const nframes64_t pos = 0;
371 const double length_beats = ceil(t / (double)source->ppqn());
372 BeatsFramesConverter converter(smfs->session().tempo_map(), pos);
373 smfs->update_length(pos, converter.to(length_beats));
374 smfs->mark_streaming_write_completed ();
376 if (status.cancel) {
377 break;
381 } catch (...) {
382 error << "Corrupt MIDI file " << source->file_path() << endl;
386 static void
387 remove_file_source (boost::shared_ptr<Source> source)
389 ::unlink (source->path().c_str());
392 // This function is still unable to cleanly update an existing source, even though
393 // it is possible to set the ImportStatus flag accordingly. The functinality
394 // is disabled at the GUI until the Source implementations are able to provide
395 // the necessary API.
396 void
397 Session::import_audiofiles (ImportStatus& status)
399 uint32_t cnt = 1;
400 typedef vector<boost::shared_ptr<Source> > Sources;
401 Sources all_new_sources;
402 boost::shared_ptr<AudioFileSource> afs;
403 boost::shared_ptr<SMFSource> smfs;
404 uint channels = 0;
406 status.sources.clear ();
408 for (vector<Glib::ustring>::iterator p = status.paths.begin();
409 p != status.paths.end() && !status.cancel;
410 ++p, ++cnt)
412 boost::shared_ptr<ImportableSource> source;
413 std::auto_ptr<Evoral::SMF> smf_reader;
414 const DataType type = ((*p).rfind(".mid") != string::npos) ?
415 DataType::MIDI : DataType::AUDIO;
417 if (type == DataType::AUDIO) {
418 try {
419 source = open_importable_source (*p, frame_rate(), status.quality);
420 channels = source->channels();
421 } catch (const failed_constructor& err) {
422 error << string_compose(_("Import: cannot open input sound file \"%1\""), (*p)) << endmsg;
423 status.done = status.cancel = true;
424 return;
427 } else {
428 try {
429 smf_reader = std::auto_ptr<Evoral::SMF>(new Evoral::SMF());
430 smf_reader->open(*p);
431 channels = smf_reader->num_tracks();
432 } catch (...) {
433 error << _("Import: error opening MIDI file") << endmsg;
434 status.done = status.cancel = true;
435 return;
439 vector<string> new_paths = get_paths_for_new_sources (status.replace_existing_source, *p,
440 get_best_session_directory_for_new_source (),
441 channels);
442 Sources newfiles;
443 framepos_t natural_position = source ? source->natural_position() : 0;
445 if (status.replace_existing_source) {
446 fatal << "THIS IS NOT IMPLEMENTED YET, IT SHOULD NEVER GET CALLED!!! DYING!" << endmsg;
447 status.cancel = !map_existing_mono_sources (new_paths, *this, frame_rate(), newfiles, this);
448 } else {
449 status.cancel = !create_mono_sources_for_writing (new_paths, *this, frame_rate(), newfiles, natural_position);
452 // copy on cancel/failure so that any files that were created will be removed below
453 std::copy (newfiles.begin(), newfiles.end(), std::back_inserter(all_new_sources));
455 if (status.cancel) {
456 break;
459 for (Sources::iterator i = newfiles.begin(); i != newfiles.end(); ++i) {
460 if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(*i)) != 0) {
461 afs->prepare_for_peakfile_writes ();
465 if (source) { // audio
466 status.doing_what = compose_status_message (*p, source->samplerate(),
467 frame_rate(), cnt, status.total);
468 write_audio_data_to_new_files (source.get(), status, newfiles);
469 } else if (smf_reader.get()) { // midi
470 status.doing_what = string_compose(_("Loading MIDI file %1"), *p);
471 write_midi_data_to_new_files (smf_reader.get(), status, newfiles);
475 if (!status.cancel) {
476 struct tm* now;
477 time_t xnow;
478 time (&xnow);
479 now = localtime (&xnow);
480 status.freeze = true;
482 /* flush the final length(s) to the header(s) */
484 for (Sources::iterator x = all_new_sources.begin(); x != all_new_sources.end(); ) {
485 if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(*x)) != 0) {
486 afs->update_header((*x)->natural_position(), *now, xnow);
487 afs->done_with_peakfile_writes ();
489 /* now that there is data there, requeue the file for analysis */
491 if (Config->get_auto_analyse_audio()) {
492 Analyser::queue_source_for_analysis (boost::static_pointer_cast<Source>(*x), false);
496 /* don't create tracks for empty MIDI sources (channels) */
498 if ((smfs = boost::dynamic_pointer_cast<SMFSource>(*x)) != 0 && smfs->is_empty()) {
499 x = all_new_sources.erase(x);
500 } else {
501 ++x;
505 /* save state so that we don't lose these new Sources */
507 save_state (_name);
509 std::copy (all_new_sources.begin(), all_new_sources.end(), std::back_inserter(status.sources));
510 } else {
511 // this can throw...but it seems very unlikely
512 std::for_each (all_new_sources.begin(), all_new_sources.end(), remove_file_source);
515 status.done = true;