From 74bc0c84686c4a85941b98d17179d3209bf9a2a8 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 2 Jun 2014 11:20:37 -0400 Subject: substantive changes to the logic and safety for naming of (audio/MIDI) sources, especially when created via import --- libs/ardour/ardour/file_source.h | 4 + libs/ardour/ardour/session.h | 10 +- libs/ardour/ardour/smf_source.h | 9 -- libs/ardour/audiofilesource.cc | 1 + libs/ardour/diskstream.cc | 1 - libs/ardour/file_source.cc | 27 +++++- libs/ardour/filter.cc | 5 +- libs/ardour/import.cc | 96 +++++-------------- libs/ardour/midi_diskstream.cc | 4 +- libs/ardour/session.cc | 196 +++++++++++++++++---------------------- libs/ardour/session_state.cc | 37 +------- libs/ardour/smf_source.cc | 31 ------- 12 files changed, 148 insertions(+), 273 deletions(-) (limited to 'libs/ardour') diff --git a/libs/ardour/ardour/file_source.h b/libs/ardour/ardour/file_source.h index 531cdf4d4c..8eebfeac4e 100644 --- a/libs/ardour/ardour/file_source.h +++ b/libs/ardour/ardour/file_source.h @@ -85,6 +85,10 @@ public: void existence_check (); virtual void prevent_deletion (); + /** Rename the file on disk referenced by this source to \param newname + */ + int rename (const std::string& name); + protected: FileSource (Session& session, DataType type, const std::string& path, diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 168137ffc3..b93f932cc0 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -195,10 +195,10 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi std::string peak_path (std::string) const; std::string peak_path_from_audio_path (std::string) const; - std::string new_audio_source_name (const std::string&, uint32_t nchans, uint32_t chan, bool destructive); - std::string new_midi_source_name (const std::string&); - std::string new_source_path_from_name (DataType type, const std::string&); + std::string new_audio_source_path (const std::string&, uint32_t nchans, uint32_t chan, bool destructive, bool take_required); + std::string new_midi_source_path (const std::string&); RouteList new_route_from_template (uint32_t how_many, const std::string& template_path, const std::string& name); + std::vector get_paths_for_new_sources (bool allow_replacing, const std::string& import_file_path, uint32_t channels); void process (pframes_t nframes); @@ -526,8 +526,6 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi boost::shared_ptr find_whole_file_parent (boost::shared_ptr) const; - std::string path_from_region_name (DataType type, std::string name, std::string identifier); - boost::shared_ptr XMLRegionFactory (const XMLNode&, bool full); boost::shared_ptr XMLAudioRegionFactory (const XMLNode&, bool full); boost::shared_ptr XMLMidiRegionFactory (const XMLNode&, bool full); @@ -1433,7 +1431,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi bool no_questions_about_missing_files; - std::string get_best_session_directory_for_new_source (); + std::string get_best_session_directory_for_new_audio (); mutable gint _playback_load; mutable gint _capture_load; diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index a068a3e385..f359100451 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -47,15 +47,6 @@ public: virtual ~SMFSource (); - /** Rename the file on disk referenced by this source to \param newname - * - * This method exists only for MIDI file sources, not for audio, which - * can never be renamed. It exists for MIDI so that we can get - * consistent and sane region/source numbering when regions are added - * manually (which never happens with audio). - */ - int rename (const std::string& name); - bool safe_file_extension (const std::string& path) const { return safe_midi_file_extension(path); } diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 13b03f8f48..37bf502177 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -36,6 +36,7 @@ #include "pbd/stl_delete.h" #include "pbd/strsplit.h" #include "pbd/shortpath.h" +#include "pbd/stacktrace.h" #include "pbd/enumwriter.h" #include diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc index a359f228e8..94b68478d1 100644 --- a/libs/ardour/diskstream.cc +++ b/libs/ardour/diskstream.cc @@ -739,4 +739,3 @@ Diskstream::disengage_record_enable () { g_atomic_int_set (&_record_enabled, 0); } - diff --git a/libs/ardour/file_source.cc b/libs/ardour/file_source.cc index de2783a1ac..d579d11965 100644 --- a/libs/ardour/file_source.cc +++ b/libs/ardour/file_source.cc @@ -214,7 +214,7 @@ FileSource::move_to_trash (const string& trash_dir_name) if (move_dependents_to_trash() != 0) { /* try to back out */ - rename (newpath.c_str(), _path.c_str()); + ::rename (newpath.c_str(), _path.c_str()); return -1; } @@ -581,3 +581,28 @@ FileSource::is_stub () const return true; } +int +FileSource::rename (const string& newpath) +{ + Glib::Threads::Mutex::Lock lm (_lock); + string oldpath = _path; + + // Test whether newpath exists, if yes notify the user but continue. + if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) { + error << string_compose (_("Programming error! %1 tried to rename a file over another file! It's safe to continue working, but please report this to the developers."), PROGRAM_NAME) << endmsg; + return -1; + } + + if (Glib::file_test (oldpath.c_str(), Glib::FILE_TEST_EXISTS)) { + /* rename only needed if file exists on disk */ + if (::rename (oldpath.c_str(), newpath.c_str()) != 0) { + error << string_compose (_("cannot rename file %1 to %2 (%3)"), oldpath, newpath, strerror(errno)) << endmsg; + return -1; + } + } + + _name = Glib::path_get_basename (newpath); + _path = newpath; + + return 0; +} diff --git a/libs/ardour/filter.cc b/libs/ardour/filter.cc index b085ec946b..b723de1e56 100644 --- a/libs/ardour/filter.cc +++ b/libs/ardour/filter.cc @@ -59,10 +59,9 @@ Filter::make_new_sources (boost::shared_ptr region, SourceList& nsrcs, s } } - string path = session.path_from_region_name (region->data_type(), - PBD::basename_nosuffix (names[i]), string ("")); + string path = session.new_audio_source_path (name, region->n_channels(), i, false, false); - if (path.length() == 0) { + if (path.empty()) { error << string_compose (_("filter: error creating name for new file based on %1"), region->name()) << endmsg; return -1; diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index e63143e695..6f7b3d0616 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -116,78 +116,31 @@ open_importable_source (const string& path, framecnt_t samplerate, ARDOUR::SrcQu } } -static std::string -get_non_existent_filename (HeaderFormat hf, DataType type, const bool allow_replacing, const std::string& destdir, const std::string& basename, uint channel, uint channels) -{ - char buf[PATH_MAX+1]; - bool goodfile = false; - string base = basename; - string ext = native_header_format_extension (hf, type); - uint32_t cnt = 1; - - do { - - if (type == DataType::AUDIO && channels == 2) { - if (channel == 0) { - if (cnt == 1) { - snprintf (buf, sizeof(buf), "%s-L%s", base.c_str(), ext.c_str()); - } else { - snprintf (buf, sizeof(buf), "%s-%d-L%s", base.c_str(), cnt, ext.c_str()); - } - } else { - if (cnt == 1) { - snprintf (buf, sizeof(buf), "%s-R%s", base.c_str(), ext.c_str()); - } else { - snprintf (buf, sizeof(buf), "%s-%d-R%s", base.c_str(), cnt, ext.c_str()); - } - } - } else if (channels > 1) { - if (cnt == 1) { - snprintf (buf, sizeof(buf), "%s-c%d%s", base.c_str(), channel, ext.c_str()); - } else { - snprintf (buf, sizeof(buf), "%s-%d-c%d%s", base.c_str(), cnt, channel, ext.c_str()); - } - } else { - if (cnt == 1) { - snprintf (buf, sizeof(buf), "%s%s", base.c_str(), ext.c_str()); - } else { - snprintf (buf, sizeof(buf), "%s-%d%s", base.c_str(), cnt, ext.c_str()); - } - } - - string tempname = destdir + "/" + buf; - - if (!allow_replacing && Glib::file_test (tempname, Glib::FILE_TEST_EXISTS)) { - - cnt++; - - } else { - - goodfile = true; - } - - } while (!goodfile); - - return buf; -} - -static vector -get_paths_for_new_sources (HeaderFormat hf, const bool allow_replacing, const string& import_file_path, const string& session_dir, uint channels) +vector +Session::get_paths_for_new_sources (bool /*allow_replacing*/, const string& import_file_path, uint32_t channels) { vector new_paths; const string basename = basename_nosuffix (import_file_path); - SessionDirectory sdir(session_dir); - for (uint n = 0; n < channels; ++n) { const DataType type = SMFSource::safe_midi_file_extension (import_file_path) ? DataType::MIDI : DataType::AUDIO; + string filepath; + + switch (type) { + case DataType::MIDI: + filepath = new_midi_source_path (basename); + break; + case DataType::AUDIO: + filepath = new_audio_source_path (basename, channels, n, false, false); + break; + } - std::string filepath = (type == DataType::MIDI) - ? sdir.midi_path() : sdir.sound_path(); + if (filepath.empty()) { + error << string_compose (_("Cannot find new filename for imported file %1"), import_file_path) << endmsg; + return vector(); + } - filepath = Glib::build_filename (filepath, - get_non_existent_filename (hf, type, allow_replacing, filepath, basename, n, channels)); new_paths.push_back (filepath); } @@ -274,12 +227,12 @@ write_audio_data_to_new_files (ImportableSource* source, ImportStatus& status, { const framecnt_t nframes = ResampledImportableSource::blocksize; boost::shared_ptr afs; - uint channels = source->channels(); + uint32_t channels = source->channels(); boost::scoped_array data(new float[nframes * channels]); vector > channel_data; - for (uint n = 0; n < channels; ++n) { + for (uint32_t n = 0; n < channels; ++n) { channel_data.push_back(boost::shared_array(new Sample[nframes])); } @@ -323,14 +276,14 @@ write_audio_data_to_new_files (ImportableSource* source, ImportStatus& status, progress_multiplier = 0.5; progress_base = 0.5; } - - uint read_count = 0; + + framecnt_t read_count = 0; while (!status.cancel) { framecnt_t nread, nfread; - uint x; - uint chn; + uint32_t x; + uint32_t chn; if ((nread = source->read (data.get(), nframes)) == 0) { break; @@ -513,10 +466,7 @@ Session::import_files (ImportStatus& status) } } - vector new_paths = get_paths_for_new_sources (config.get_native_file_header_format(), - status.replace_existing_source, *p, - get_best_session_directory_for_new_source (), - channels); + vector new_paths = get_paths_for_new_sources (status.replace_existing_source, *p, channels); Sources newfiles; framepos_t natural_position = source ? source->natural_position() : 0; diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index c38c08d72e..54b060299f 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -1234,9 +1234,9 @@ MidiDiskstream::steal_write_source_name () */ try { - string new_name = _session.new_midi_source_name (name()); + string new_path = _session.new_midi_source_path (name()); - if (_write_source->rename (new_name)) { + if (_write_source->rename (new_path)) { return string(); } } catch (...) { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 1006c0f90b..0e4a10f76b 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -36,6 +36,7 @@ #include +#include "pbd/convert.h" #include "pbd/error.h" #include "pbd/boost_debug.h" #include "pbd/pathscanner.h" @@ -3366,30 +3367,6 @@ Session::count_sources_by_origin (const string& path) return cnt; } -/** Return the full path (in some session directory) for a new within-session source. - * \a name must be a session-unique name that does not contain slashes - * (e.g. as returned by new_*_source_name) - */ -string -Session::new_source_path_from_name (DataType type, const string& name) -{ - assert(name.find("/") == string::npos); - - SessionDirectory sdir(get_best_session_directory_for_new_source()); - - std::string p; - if (type == DataType::AUDIO) { - p = sdir.sound_path(); - } else if (type == DataType::MIDI) { - p = sdir.midi_path(); - } else { - error << "Unknown source type, unable to create file path" << endmsg; - return ""; - } - - return Glib::build_filename (p, name); -} - string Session::peak_path (string base) const { @@ -3398,18 +3375,20 @@ Session::peak_path (string base) const /** Return a unique name based on \a base for a new internal audio source */ string -Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t chan, bool destructive) +Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t chan, bool destructive, bool take_required) { uint32_t cnt; - char buf[PATH_MAX+1]; - const uint32_t limit = 10000; + string possible_name; + const uint32_t limit = 9999; // arbitrary limit on number of files with the same basic name string legalized; string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); + bool some_related_source_name_exists = false; - buf[0] = '\0'; + possible_name[0] = '\0'; legalized = legalize_for_path (base); // Find a "version" of the base name that doesn't exist in any of the possible directories. + for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) { vector::iterator i; @@ -3417,47 +3396,37 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha for (i = session_dirs.begin(); i != session_dirs.end(); ++i) { - if (destructive) { - - if (nchan < 2) { - snprintf (buf, sizeof(buf), "T%04d-%s%s", - cnt, legalized.c_str(), ext.c_str()); - } else if (nchan == 2) { - if (chan == 0) { - snprintf (buf, sizeof(buf), "T%04d-%s%%L%s", - cnt, legalized.c_str(), ext.c_str()); - } else { - snprintf (buf, sizeof(buf), "T%04d-%s%%R%s", - cnt, legalized.c_str(), ext.c_str()); - } - } else if (nchan < 26) { - snprintf (buf, sizeof(buf), "T%04d-%s%%%c%s", - cnt, legalized.c_str(), 'a' + chan, ext.c_str()); - } else { - snprintf (buf, sizeof(buf), "T%04d-%s%s", - cnt, legalized.c_str(), ext.c_str()); - } + ostringstream sstr; + if (destructive) { + sstr << 'T'; + sstr << setfill ('0') << setw (4) << cnt; + sstr << legalized; } else { - - if (nchan < 2) { - snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str()); - } else if (nchan == 2) { - if (chan == 0) { - snprintf (buf, sizeof(buf), "%s-%u%%L%s", legalized.c_str(), cnt, ext.c_str()); - } else { - snprintf (buf, sizeof(buf), "%s-%u%%R%s", legalized.c_str(), cnt, ext.c_str()); - } - } else if (nchan < 26) { - snprintf (buf, sizeof(buf), "%s-%u%%%c%s", legalized.c_str(), cnt, 'a' + chan, ext.c_str()); - } else { - snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str()); + sstr << legalized; + + if (take_required || some_related_source_name_exists) { + sstr << '-'; + sstr << cnt; } } + + if (nchan == 2) { + if (chan == 0) { + sstr << "%L"; + } else { + sstr << "%R"; + } + } else if (nchan > 2 && nchan < 26) { + sstr << '%'; + sstr << 'a' + chan; + } - SessionDirectory sdir((*i).path); + sstr << ext; - string spath = sdir.sound_path(); + possible_name = sstr.str(); + SessionDirectory sdir((*i).path); + const string spath = sdir.sound_path(); /* note that we search *without* the extension so that we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf" @@ -3465,7 +3434,7 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha a file format change. */ - if (matching_unsuffixed_filename_exists_in (spath, buf)) { + if (matching_unsuffixed_filename_exists_in (spath, possible_name)) { existing++; break; } @@ -3479,7 +3448,7 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha * notions of their removability. */ - string possible_path = Glib::build_filename (spath, buf); + string possible_path = Glib::build_filename (spath, possible_name); if (audio_source_by_path_and_channel (possible_path, chan)) { existing++; @@ -3491,6 +3460,8 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha break; } + some_related_source_name_exists = true; + if (cnt > limit) { error << string_compose( _("There are already %1 recordings for %2, which I consider too many."), @@ -3500,32 +3471,31 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha } } - return Glib::path_get_basename (buf); -} + /* We've established that the new name does not exist in any session + * directory, so now find out which one we should use for this new + * audio source. + */ -/** Create a new within-session audio source */ -boost::shared_ptr -Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive) -{ - const string name = new_audio_source_name (n, n_chans, chan, destructive); - const string path = new_source_path_from_name(DataType::AUDIO, name); + SessionDirectory sdir (get_best_session_directory_for_new_audio()); + + std::string s = Glib::build_filename (sdir.sound_path(), possible_name); - return boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate())); + return s; } /** Return a unique name based on \a owner_name for a new internal MIDI source */ string -Session::new_midi_source_name (const string& owner_name) +Session::new_midi_source_path (const string& base) { uint32_t cnt; char buf[PATH_MAX+1]; const uint32_t limit = 10000; string legalized; + string possible_path; string possible_name; buf[0] = '\0'; - legalized = legalize_for_path (owner_name); + legalized = legalize_for_path (base); // Find a "version" of the file name that doesn't exist in any of the possible directories. @@ -3533,7 +3503,7 @@ Session::new_midi_source_name (const string& owner_name) vector::iterator i; uint32_t existing = 0; - + for (i = session_dirs.begin(); i != session_dirs.end(); ++i) { SessionDirectory sdir((*i).path); @@ -3541,7 +3511,7 @@ Session::new_midi_source_name (const string& owner_name) snprintf (buf, sizeof(buf), "%s-%u.mid", legalized.c_str(), cnt); possible_name = buf; - std::string possible_path = Glib::build_filename (sdir.midi_path(), possible_name); + possible_path = Glib::build_filename (sdir.midi_path(), possible_name); if (Glib::file_test (possible_path, Glib::FILE_TEST_EXISTS)) { existing++; @@ -3559,31 +3529,47 @@ Session::new_midi_source_name (const string& owner_name) if (cnt > limit) { error << string_compose( _("There are already %1 recordings for %2, which I consider too many."), - limit, owner_name) << endmsg; + limit, base) << endmsg; destroy (); - throw failed_constructor(); + return 0; } } - return possible_name; + /* No need to "find best location" for software/app-based RAID, because + MIDI is so small that we always put it in the same place. + */ + + return possible_path; } +/** Create a new within-session audio source */ +boost::shared_ptr +Session::create_audio_source_for_session (size_t n_chans, string const & base, uint32_t chan, bool destructive) +{ + const string path = new_audio_source_path (base, n_chans, chan, destructive, true); + + if (!path.empty()) { + return boost::dynamic_pointer_cast ( + SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate())); + } else { + throw failed_constructor (); + } +} + /** Create a new within-session MIDI source */ boost::shared_ptr Session::create_midi_source_for_session (string const & basic_name) { - std::string name; - - if (name.empty()) { - name = new_midi_source_name (basic_name); + const string path = new_midi_source_path (basic_name); + + if (!path.empty()) { + return boost::dynamic_pointer_cast ( + SourceFactory::createWritable ( + DataType::MIDI, *this, path, false, frame_rate())); + } else { + throw failed_constructor (); } - - const string path = new_source_path_from_name (DataType::MIDI, name); - - return boost::dynamic_pointer_cast ( - SourceFactory::createWritable ( - DataType::MIDI, *this, path, false, frame_rate())); } /** Create a new within-session MIDI source */ @@ -3617,7 +3603,7 @@ Session::create_midi_source_by_stealing_name (boost::shared_ptr track) return boost::shared_ptr(); } - const string path = new_source_path_from_name (DataType::MIDI, name); + const string path = new_midi_source_path (name); return boost::dynamic_pointer_cast ( SourceFactory::createWritable ( @@ -4123,18 +4109,13 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, boost::shared_ptr result; boost::shared_ptr playlist; boost::shared_ptr fsource; - uint32_t x; - char buf[PATH_MAX+1]; ChanCount diskstream_channels (track.n_channels()); framepos_t position; framecnt_t this_chunk; framepos_t to_do; BufferSet buffers; - SessionDirectory sdir(get_best_session_directory_for_new_source ()); - const string sound_dir = sdir.sound_path(); framepos_t len = end - start; bool need_block_size_reset = false; - string ext; ChanCount const max_proc = track.max_processor_streams (); if (end <= start) { @@ -4155,29 +4136,22 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, goto out; } - ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); - for (uint32_t chan_n = 0; chan_n < diskstream_channels.n_audio(); ++chan_n) { - for (x = 0; x < 99999; ++x) { - snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 "%s", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1, ext.c_str()); - if (!Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) { - break; - } - } - - if (x == 99999) { - error << string_compose (_("too many bounced versions of playlist \"%1\""), playlist->name()) << endmsg; + string base_name = string_compose ("%1-%2-bounce", playlist->name(), chan_n); + string path = new_audio_source_path (base_name, diskstream_channels.n_audio(), chan_n, false, true); + + if (path.empty()) { goto out; } try { fsource = boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, path, false, frame_rate())); } catch (failed_constructor& err) { - error << string_compose (_("cannot create new audio file \"%1\" for %2"), buf, track.name()) << endmsg; + error << string_compose (_("cannot create new audio file \"%1\" for %2"), path, track.name()) << endmsg; goto out; } diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index ffbe55afbf..c985f8810a 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -1830,41 +1830,6 @@ Session::get_sources_as_xml () return *node; } -string -Session::path_from_region_name (DataType type, string name, string identifier) -{ - char buf[PATH_MAX+1]; - uint32_t n; - SessionDirectory sdir(get_best_session_directory_for_new_source()); - std::string source_dir = ((type == DataType::AUDIO) - ? sdir.sound_path() : sdir.midi_path()); - - string ext = native_header_format_extension (config.get_native_file_header_format(), type); - - for (n = 0; n < 999999; ++n) { - if (identifier.length()) { - snprintf (buf, sizeof(buf), "%s%s%" PRIu32 "%s", name.c_str(), - identifier.c_str(), n, ext.c_str()); - } else { - snprintf (buf, sizeof(buf), "%s-%" PRIu32 "%s", name.c_str(), - n, ext.c_str()); - } - - std::string source_path = Glib::build_filename (source_dir, buf); - - if (!Glib::file_test (source_path, Glib::FILE_TEST_EXISTS)) { - return source_path; - } - } - - error << string_compose (_("cannot create new file from region name \"%1\" with ident = \"%2\": too many existing files with similar names"), - name, identifier) - << endmsg; - - return ""; -} - - int Session::load_sources (const XMLNode& node) { @@ -2053,7 +2018,7 @@ Session::refresh_disk_space () } string -Session::get_best_session_directory_for_new_source () +Session::get_best_session_directory_for_new_audio () { vector::iterator i; string result = _session_dir->root_path(); diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index c3bc78c83d..1cd456ee58 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -718,34 +718,3 @@ SMFSource::prevent_deletion () _flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy)); } -int -SMFSource::rename (const string& newname) -{ - Glib::Threads::Mutex::Lock lm (_lock); - string oldpath = _path; - string newpath = _session.new_source_path_from_name (DataType::MIDI, newname); - - if (newpath.empty()) { - error << string_compose (_("programming error: %1"), "cannot generate a changed file path") << endmsg; - return -1; - } - - // Test whether newpath exists, if yes notify the user but continue. - if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) { - error << string_compose (_("Programming error! %1 tried to rename a file over another file! It's safe to continue working, but please report this to the developers."), PROGRAM_NAME) << endmsg; - return -1; - } - - if (Glib::file_test (oldpath.c_str(), Glib::FILE_TEST_EXISTS)) { - /* rename only needed if file exists on disk */ - if (::rename (oldpath.c_str(), newpath.c_str()) != 0) { - error << string_compose (_("cannot rename file %1 to %2 (%3)"), oldpath, newpath, strerror(errno)) << endmsg; - return -1; - } - } - - _name = Glib::path_get_basename (newpath); - _path = newpath; - - return 0; -} -- cgit v1.2.3 From b660bc8ae92d19aedf0165815432b77a0c6170c4 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 10 Jun 2014 10:07:04 -0400 Subject: fix crash recovery: add new constructors to SndFileSource, AudioFileSource, add a new SourceFactory method and finally tweak AudioDiskstream::use_pending_capture_data() to create both the required whole-file and the in-playlist regions --- libs/ardour/ardour/audiofilesource.h | 6 ++++++ libs/ardour/ardour/sndfilesource.h | 11 ++++++++++- libs/ardour/ardour/source_factory.h | 3 +++ libs/ardour/audio_diskstream.cc | 38 ++++++++++++++++++++++++------------ libs/ardour/audiofilesource.cc | 16 +++++++++++++++ libs/ardour/file_source.cc | 2 +- libs/ardour/sndfilesource.cc | 36 ++++++++++++++++++++++++++++++++++ libs/ardour/source_factory.cc | 33 +++++++++++++++++++++++++++++++ 8 files changed, 131 insertions(+), 14 deletions(-) (limited to 'libs/ardour') diff --git a/libs/ardour/ardour/audiofilesource.h b/libs/ardour/ardour/audiofilesource.h index 9be8d6ce45..7f4b18e404 100644 --- a/libs/ardour/ardour/audiofilesource.h +++ b/libs/ardour/ardour/audiofilesource.h @@ -93,6 +93,12 @@ protected: /** Constructor to be called for existing in-session files */ AudioFileSource (Session&, const XMLNode&, bool must_exist = true); + /** Constructor to be called for crash recovery. Final argument is not + * used but exists to differentiate from the external-to-session + * constructor above. + */ + AudioFileSource (Session&, const std::string& path, Source::Flag flags, bool); + int init (const std::string& idstr, bool must_exist); virtual void set_header_timeline_position () = 0; diff --git a/libs/ardour/ardour/sndfilesource.h b/libs/ardour/ardour/sndfilesource.h index 3f63f1c598..9604d3f232 100644 --- a/libs/ardour/ardour/sndfilesource.h +++ b/libs/ardour/ardour/sndfilesource.h @@ -38,7 +38,16 @@ class SndFileSource : public AudioFileSource { SampleFormat samp_format, HeaderFormat hdr_format, framecnt_t rate, Flag flags = SndFileSource::default_writable_flags); - /** Constructor to be called for existing in-session files */ + /* Constructor to be called for recovering files being used for + * capture. They are in-session, they already exist, they should not + * be writable. They are an odd hybrid (from a constructor point of + * view) of the previous two constructors. + */ + SndFileSource (Session&, const std::string& path, int chn); + + /** Constructor to be called for existing in-session files during + * session loading + */ SndFileSource (Session&, const XMLNode&); ~SndFileSource (); diff --git a/libs/ardour/ardour/source_factory.h b/libs/ardour/ardour/source_factory.h index c94f783b44..ce0f86bb6b 100644 --- a/libs/ardour/ardour/source_factory.h +++ b/libs/ardour/ardour/source_factory.h @@ -57,6 +57,9 @@ class SourceFactory { bool destructive, framecnt_t rate, bool announce = true, bool async = false); + static boost::shared_ptr createForRecovery + (DataType type, Session&, const std::string& path, int chn); + static boost::shared_ptr createFromPlaylist (DataType type, Session& s, boost::shared_ptr p, const PBD::ID& orig, const std::string& name, uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, bool defer_peaks); diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 010e1da21f..7785284dac 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -2181,11 +2181,16 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) continue; } + /* XXX as of June 2014, we always record to mono + files. Since this Source is being created as part of + crash recovery, we know that we need the first + channel (the final argument to the SourceFactory + call below). If we ever support non-mono files for + capture, this will need rethinking. + */ + try { - fs = boost::dynamic_pointer_cast ( - SourceFactory::createWritable ( - DataType::AUDIO, _session, - prop->value(), false, _session.frame_rate())); + fs = boost::dynamic_pointer_cast (SourceFactory::createForRecovery (DataType::AUDIO, _session, prop->value(), 0)); } catch (failed_constructor& err) { @@ -2216,21 +2221,31 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) return -1; } - boost::shared_ptr region; - try { - PropertyList plist; + boost::shared_ptr wf_region; + boost::shared_ptr region; + + /* First create the whole file region */ + PropertyList plist; + plist.add (Properties::start, 0); plist.add (Properties::length, first_fs->length (first_fs->timeline_position())); plist.add (Properties::name, region_name_from_path (first_fs->name(), true)); - region = boost::dynamic_pointer_cast (RegionFactory::create (pending_sources, plist)); + wf_region = boost::dynamic_pointer_cast (RegionFactory::create (pending_sources, plist)); + + wf_region->set_automatic (true); + wf_region->set_whole_file (true); + wf_region->special_set_position (position); - region->set_automatic (true); - region->set_whole_file (true); - region->special_set_position (0); + /* Now create a region that isn't the whole file for adding to + * the playlist */ + + region = boost::dynamic_pointer_cast (RegionFactory::create (pending_sources, plist)); + + _playlist->add_region (region, position); } catch (failed_constructor& err) { @@ -2241,7 +2256,6 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) return -1; } - _playlist->add_region (region, position); return 0; } diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 37bf502177..8c3bf00176 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -115,6 +115,22 @@ AudioFileSource::AudioFileSource (Session& s, const string& path, const string& } } +/** Constructor used for existing internal-to-session files during crash + * recovery. File must exist + */ +AudioFileSource::AudioFileSource (Session& s, const string& path, Source::Flag flags, bool /* ignored-exists-for-prototype differentiation */) + : Source (s, DataType::AUDIO, path, flags) + , AudioSource (s, path) + , FileSource (s, DataType::AUDIO, path, string(), flags) +{ + /* note that origin remains empty */ + + if (init (_path, true)) { + throw failed_constructor (); + } +} + + /** Constructor used for existing internal-to-session files via XML. File must exist. */ AudioFileSource::AudioFileSource (Session& s, const XMLNode& node, bool must_exist) : Source (s, node) diff --git a/libs/ardour/file_source.cc b/libs/ardour/file_source.cc index d579d11965..30ae2178fe 100644 --- a/libs/ardour/file_source.cc +++ b/libs/ardour/file_source.cc @@ -56,7 +56,7 @@ PBD::Signal3 > FileSource:: FileSource::FileSource (Session& session, DataType type, const string& path, const string& origin, Source::Flag flag) : Source(session, type, path, flag) , _path (path) - , _file_is_new (!origin.empty()) // origin empty => new file VS. origin !empty => new file + , _file_is_new (!origin.empty()) // if origin is left unspecified (empty string) then file must exist , _channel (0) , _origin (origin) , _open (false) diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index 6b019f6fd0..5465c5e4a4 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -183,6 +183,34 @@ SndFileSource::SndFileSource (Session& s, const string& path, const string& orig } } +/** Constructor to be called for recovering files being used for + * capture. They are in-session, they already exist, they should not + * be writable. They are an odd hybrid (from a constructor point of + * view) of the previous two constructors. + */ +SndFileSource::SndFileSource (Session& s, const string& path, int chn) + : Source (s, DataType::AUDIO, path, Flag (0)) + /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */ + , AudioFileSource (s, path, Flag (0)) + , _descriptor (0) + , _broadcast_info (0) + , _capture_start (false) + , _capture_end (false) + , file_pos (0) + , xfade_buf (0) +{ + _channel = chn; + + init_sndfile (); + + assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS)); + existence_check (); + + if (open()) { + throw failed_constructor (); + } +} + void SndFileSource::init_sndfile () { @@ -256,6 +284,14 @@ SndFileSource::open () delete _broadcast_info; _broadcast_info = 0; _flags = Flag (_flags & ~Broadcast); + } + + /* Set the broadcast flag if the BWF info is already there. We need + * this when recovering or using existing files. + */ + + if (bwf_info_exists) { + _flags = Flag (_flags | Broadcast); } if (writable()) { diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index 391b205a94..6d2bb80b30 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -341,6 +341,39 @@ SourceFactory::createWritable (DataType type, Session& s, const std::string& pat return boost::shared_ptr (); } +boost::shared_ptr +SourceFactory::createForRecovery (DataType type, Session& s, const std::string& path, int chn) +{ + /* this might throw failed_constructor(), which is OK */ + + if (type == DataType::AUDIO) { + Source* src = new SndFileSource (s, path, chn); + +#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS + // boost_debug_shared_ptr_mark_interesting (src, "Source"); +#endif + boost::shared_ptr ret (src); + + if (setup_peakfile (ret, false)) { + return boost::shared_ptr(); + } + + // no analysis data - this is still basically a new file (we + // crashed while recording. + + // always announce these files + + SourceCreated (ret); + + return ret; + + } else if (type == DataType::MIDI) { + error << _("Recovery attempted on a MIDI file - not implemented") << endmsg; + } + + return boost::shared_ptr (); +} + boost::shared_ptr SourceFactory::createFromPlaylist (DataType type, Session& s, boost::shared_ptr p, const PBD::ID& orig, const std::string& name, uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, bool defer_peaks) -- cgit v1.2.3