tentative fix for losing (empty) MIDI files. Incomplete because testing shows issues...
[ardour.git] / libs / ardour / session.cc
index cf1487c28c414b325925c087d837e69fd70e0ff1..2d26cb73b45a803e9e918c484077dcb1aee31064 100644 (file)
@@ -36,6 +36,7 @@
 
 #include <boost/algorithm/string/erase.hpp>
 
+#include "pbd/convert.h"
 #include "pbd/error.h"
 #include "pbd/boost_debug.h"
 #include "pbd/pathscanner.h"
@@ -3305,7 +3306,7 @@ Session::source_by_id (const PBD::ID& id)
 }
 
 boost::shared_ptr<AudioFileSource>
-Session::source_by_path_and_channel (const string& path, uint16_t chn) const
+Session::audio_source_by_path_and_channel (const string& path, uint16_t chn) const
 {
        /* Restricted to audio files because only audio sources have channel
           as a property.
@@ -3326,7 +3327,7 @@ Session::source_by_path_and_channel (const string& path, uint16_t chn) const
 }
 
 boost::shared_ptr<MidiSource>
-Session::source_by_path (const std::string& path) const
+Session::midi_source_by_path (const std::string& path) const
 {
        /* Restricted to MIDI files because audio sources require a channel
           for unique identification, in addition to a path.
@@ -3366,136 +3367,6 @@ Session::count_sources_by_origin (const string& path)
        return cnt;
 }
 
-string
-Session::generate_new_source_path_from_name (string path, string oldname, string newname, bool destructive)
-{
-       string look_for;
-       string old_basename = PBD::basename_nosuffix (oldname);
-       string new_legalized = legalize_for_path (newname);
-
-       /* note: we know (or assume) the old path is already valid */
-
-       if (destructive) {
-
-               /* destructive file sources have a name of the form:
-
-                   /path/to/Tnnnn-NAME(%[LR])?.wav
-
-                   the task here is to replace NAME with the new name.
-               */
-
-               string dir;
-               string prefix;
-               string::size_type dash;
-
-               dir = Glib::path_get_dirname (path);
-               path = Glib::path_get_basename (path);
-
-               /* '-' is not a legal character for the NAME part of the path */
-
-               if ((dash = path.find_last_of ('-')) == string::npos) {
-                       return "";
-               }
-
-               prefix = path.substr (0, dash);
-
-               path += prefix;
-               path += '-';
-               path += new_legalized;
-               path += native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
-               path = Glib::build_filename (dir, path);
-
-       } else {
-
-               /* non-destructive file sources have a name of the form:
-
-                   /path/to/NAME-nnnnn(%[LR])?.ext
-
-                   the task here is to replace NAME with the new name.
-               */
-
-               string dir;
-               string suffix;
-               string::size_type dash;
-               string::size_type postfix;
-
-               dir = Glib::path_get_dirname (path);
-               path = Glib::path_get_basename (path);
-
-               /* '-' is not a legal character for the NAME part of the path */
-
-               if ((dash = path.find_last_of ('-')) == string::npos) {
-                       return "";
-               }
-
-               suffix = path.substr (dash+1);
-
-               // Suffix is now everything after the dash. Now we need to eliminate
-               // the nnnnn part, which is done by either finding a '%' or a '.'
-
-               postfix = suffix.find_last_of ("%");
-               if (postfix == string::npos) {
-                       postfix = suffix.find_last_of ('.');
-               }
-
-               if (postfix != string::npos) {
-                       suffix = suffix.substr (postfix);
-               } else {
-                       error << "Logic error in Session::change_source_path_by_name(), please report" << endl;
-                       return "";
-               }
-
-               const uint32_t limit = 10000;
-               char buf[PATH_MAX+1];
-
-               for (uint32_t cnt = 1; cnt <= limit; ++cnt) {
-
-                       snprintf (buf, sizeof(buf), "%s-%u%s", newname.c_str(), cnt, suffix.c_str());
-
-                       if (!matching_unsuffixed_filename_exists_in (dir, buf)) {
-                               path = Glib::build_filename (dir, buf);
-                               if (!source_by_path (path)) {
-                                       break;
-                               }
-                       }
-
-                       path = "";
-               }
-
-               if (path.empty()) {
-                       fatal << string_compose (_("FATAL ERROR! Could not find a suitable version of %1 for a rename"),
-                                                newname) << endl;
-                       /*NOTREACHED*/
-               }
-       }
-
-       return path;
-}
-
-/** 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
 {
@@ -3504,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<space_and_path>::iterator i;
@@ -3523,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"
@@ -3571,7 +3434,23 @@ 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;
+                       }
+
+                       /* it is possible that we have the path already
+                        * assigned to a source that has not yet been written
+                        * (ie. the write source for a diskstream). we have to
+                        * check this in order to make sure that our candidate
+                        * path isn't used again, because that can lead to
+                        * two Sources point to the same file with different
+                        * notions of their removability.
+                        */
+
+                       string possible_path = Glib::build_filename (spath, possible_name);
+
+                       if (audio_source_by_path_and_channel (possible_path, chan)) {
                                existing++;
                                break;
                        }
@@ -3581,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."),
@@ -3590,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<AudioFileSource>
-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<AudioFileSource> (
-               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.
 
@@ -3623,7 +3503,7 @@ Session::new_midi_source_name (const string& owner_name)
 
                vector<space_and_path>::iterator i;
                uint32_t existing = 0;
-
+               
                for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
 
                        SessionDirectory sdir((*i).path);
@@ -3631,13 +3511,13 @@ 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++;
                        }
 
-                       if (source_by_path (possible_path)) {
+                       if (midi_source_by_path (possible_path)) {
                                existing++;
                        }
                }
@@ -3649,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<AudioFileSource>
+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<AudioFileSource> (
+                       SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
+       } else {
+               throw failed_constructor ();
+       }
+}
+
 /** Create a new within-session MIDI source */
 boost::shared_ptr<MidiSource>
 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<SMFSource> (
+                       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<SMFSource> (
-               SourceFactory::createWritable (
-                       DataType::MIDI, *this, path, false, frame_rate()));
 }
 
 /** Create a new within-session MIDI source */
@@ -3707,7 +3603,11 @@ Session::create_midi_source_by_stealing_name (boost::shared_ptr<Track> track)
                return boost::shared_ptr<MidiSource>();
        }
 
-       const string path = new_source_path_from_name (DataType::MIDI, name);
+       /* MIDI files are small, just put them in the first location of the
+          session source search path.
+       */
+
+       const string path = Glib::build_filename (source_search_path (DataType::MIDI).front(), name);
 
        return boost::dynamic_pointer_cast<SMFSource> (
                SourceFactory::createWritable (
@@ -4213,18 +4113,13 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
        boost::shared_ptr<Region> result;
        boost::shared_ptr<Playlist> playlist;
        boost::shared_ptr<AudioFileSource> 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) {
@@ -4245,29 +4140,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<AudioFileSource> (
-                               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;
                }