handle multiple imports of the same file better (via better source naming); make...
[ardour.git] / gtk2_ardour / editor_audio_import.cc
index 9b36ea69a14c3d2bf95714afa02a3760a1c918ec..dadde038a85b6d9f7e7ee731c83134efccf33118 100644 (file)
@@ -47,6 +47,7 @@
 #include "ardour/region_factory.h"
 #include "ardour/source_factory.h"
 #include "ardour/session.h"
+#include "ardour/smf_source.h"
 #include "pbd/memento_command.h"
 
 #include "ardour_ui.h"
@@ -58,6 +59,7 @@
 #include "session_import_dialog.h"
 #include "utils.h"
 #include "gui_thread.h"
+#include "interthread_progress_window.h"
 
 #include "i18n.h"
 
@@ -67,7 +69,7 @@ using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
 using namespace Editing;
-using Glib::ustring;
+using std::string;
 
 /* Functions supporting the incorporation of external (non-captured) audio material into ardour */
 
@@ -92,7 +94,7 @@ Editor::add_external_audio_action (ImportMode mode_hint)
 void
 Editor::external_audio_dialog ()
 {
-       vector<Glib::ustring> paths;
+       vector<string> paths;
        uint32_t track_cnt;
 
        if (_session == 0) {
@@ -146,12 +148,15 @@ Editor::external_audio_dialog ()
 
                /* lets do it */
 
-               paths = sfbrowser->get_paths ();
-
+               vector<string> upaths = sfbrowser->get_paths ();
+                for (vector<string>::iterator x = upaths.begin(); x != upaths.end(); ++x) {
+                        paths.push_back (*x);
+                }
+                
                ImportPosition pos = sfbrowser->get_position ();
                ImportMode mode = sfbrowser->get_mode ();
                ImportDisposition chns = sfbrowser->get_channel_disposition ();
-               nframes64_t where;
+               framepos_t where;
 
                switch (pos) {
                        case ImportAtEditPoint:
@@ -213,19 +218,16 @@ Editor::check_whether_and_how_to_import(string path, bool all_or_nothing)
        string wave_name (Glib::path_get_basename(path));
 
        SourceMap all_sources = _session->get_sources();
-       bool wave_name_exists = false;
+       bool already_exists = false;
+        uint32_t existing;
 
-       for (SourceMap::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
-               string tmp (Glib::path_get_basename (i->second->path()));
-               if (tmp == wave_name) {
-                       wave_name_exists = true;
-                       break;
-               }
-       }
+        if ((existing = _session->count_sources_by_origin (path)) > 0) {
+                already_exists = true;
+        }
 
        int function = 1;
 
-       if (wave_name_exists) {
+       if (already_exists) {
                string message;
                if (all_or_nothing) {
                        // updating is still disabled
@@ -320,15 +322,21 @@ Editor::get_nth_selected_midi_track (int nth) const
 }
 
 void
-Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mode, SrcQuality quality, nframes64_t& pos)
+Editor::do_import (vector<string> paths, ImportDisposition chns, ImportMode mode, SrcQuality quality, framepos_t& pos)
 {
        boost::shared_ptr<Track> track;
-       vector<ustring> to_import;
+       vector<string> to_import;
        int nth = 0;
+        bool use_timestamp = (pos == -1);
 
-       if (interthread_progress_window == 0) {
-               build_interthread_progress_window ();
-       }
+       current_interthread_info = &import_status;
+       import_status.current = 1;
+       import_status.total = paths.size ();
+       import_status.all_done = false;
+
+       ImportProgressWindow ipw (&import_status, _("Import"), _("Cancel Import"));
+
+       bool ok = true;
 
        if (chns == Editing::ImportMergeFiles) {
 
@@ -337,7 +345,7 @@ Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mod
                */
 
                bool cancel = false;
-               for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) {
+               for (vector<string>::iterator a = paths.begin(); a != paths.end(); ++a) {
                        int check = check_whether_and_how_to_import(*a, false);
                        if (check == 2) {
                                cancel = true;
@@ -345,17 +353,18 @@ Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mod
                        }
                }
 
-               if (!cancel) {
-                       import_sndfiles (paths, mode, quality, pos, 1, 1, track, false, paths.size());
+               if (cancel) {
+                       ok = false;
+               } else {
+                        ipw.show ();
+                       ok = (import_sndfiles (paths, mode, quality, pos, 1, 1, track, false) == 0);
                }
 
        } else {
 
                 bool replace = false;
-               bool ok = true;
-               vector<ustring>::size_type total = paths.size();
 
-               for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) {
+               for (vector<string>::iterator a = paths.begin(); a != paths.end(); ++a) {
 
                         const int check = check_whether_and_how_to_import (*a, true);
                         
@@ -375,6 +384,14 @@ Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mod
                                /* NOTREACHED*/
                        }
 
+                        /* have to reset this for every file we handle */
+                        
+                        if (use_timestamp) {
+                                pos = -1;
+                        }
+
+                        ipw.show ();
+                                
                         switch (chns) {
                         case Editing::ImportDistinctFiles:
                                 
@@ -385,7 +402,7 @@ Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mod
                                         track = get_nth_selected_audio_track (nth++);
                                 }
                        
-                                ok = (import_sndfiles (to_import, mode, quality, pos, 1, -1, track, replace, total) == 0);
+                                ok = (import_sndfiles (to_import, mode, quality, pos, 1, -1, track, replace) == 0);
                                 break;
                                
                         case Editing::ImportDistinctChannels:
@@ -393,7 +410,7 @@ Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mod
                                 to_import.clear ();
                                 to_import.push_back (*a);
                                
-                                ok = (import_sndfiles (to_import, mode, quality, pos, -1, -1, track, replace, total) == 0);
+                                ok = (import_sndfiles (to_import, mode, quality, pos, -1, -1, track, replace) == 0);
                                 break;
                                
                         case Editing::ImportSerializeFiles:
@@ -401,7 +418,7 @@ Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mod
                                 to_import.clear ();
                                 to_import.push_back (*a);
 
-                                ok = (import_sndfiles (to_import, mode, quality, pos, 1, 1, track, replace, total) == 0);
+                                ok = (import_sndfiles (to_import, mode, quality, pos, 1, 1, track, replace) == 0);
                                 break;
 
                         case Editing::ImportMergeFiles:
@@ -411,22 +428,26 @@ Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mod
                 }
        }
 
-       interthread_progress_window->hide_all ();
+       if (ok) {
+               _session->save_state ("");
+       }
+
+       import_status.all_done = true;
 }
 
 void
-Editor::do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode, nframes64_t& pos)
+Editor::do_embed (vector<string> paths, ImportDisposition chns, ImportMode mode, framepos_t& pos)
 {
        boost::shared_ptr<Track> track;
        bool check_sample_rate = true;
        bool ok = false;
-       vector<ustring> to_embed;
+       vector<string> to_embed;
        bool multi = paths.size() > 1;
        int nth = 0;
 
        switch (chns) {
        case Editing::ImportDistinctFiles:
-               for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) {
+               for (vector<string>::iterator a = paths.begin(); a != paths.end(); ++a) {
 
                        to_embed.clear ();
                        to_embed.push_back (*a);
@@ -442,7 +463,7 @@ Editor::do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode
                break;
 
        case Editing::ImportDistinctChannels:
-               for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) {
+               for (vector<string>::iterator a = paths.begin(); a != paths.end(); ++a) {
 
                        to_embed.clear ();
                        to_embed.push_back (*a);
@@ -460,7 +481,7 @@ Editor::do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode
                 break;
 
        case Editing::ImportSerializeFiles:
-               for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) {
+               for (vector<string>::iterator a = paths.begin(); a != paths.end(); ++a) {
 
                        to_embed.clear ();
                        to_embed.push_back (*a);
@@ -481,23 +502,15 @@ Editor::do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode
 }
 
 int
-Editor::import_sndfiles (vector<ustring> paths, ImportMode mode, SrcQuality quality, nframes64_t& pos,
-                        int target_regions, int target_tracks, boost::shared_ptr<Track>& track, bool replace, uint32_t total)
+Editor::import_sndfiles (vector<string> paths, ImportMode mode, SrcQuality quality, framepos_t& pos,
+                        int target_regions, int target_tracks, boost::shared_ptr<Track>& track, bool replace)
 {
-       interthread_progress_window->set_title (string_compose (_("Importing %1"), paths.front()));
-       interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
-       interthread_progress_bar.set_fraction (0.0f);
-       interthread_cancel_label.set_text (_("Cancel Import"));
-       current_interthread_info = &import_status;
-
        import_status.paths = paths;
        import_status.done = false;
        import_status.cancel = false;
        import_status.freeze = false;
-       import_status.done = 0.0;
        import_status.quality = quality;
        import_status.replace_existing_source = replace;
-       import_status.total = total;
 
        import_status.mode = mode;
        import_status.pos = pos;
@@ -505,10 +518,8 @@ Editor::import_sndfiles (vector<ustring> paths, ImportMode mode, SrcQuality qual
        import_status.target_regions = target_regions;
        import_status.track = track;
        import_status.replace = replace;
-       interthread_progress_connection = Glib::signal_timeout().connect
-               (sigc::bind (sigc::mem_fun(*this, &Editor::import_progress_timeout), (gpointer) 0), 500);
-
-       track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
+        
+       set_canvas_cursor (wait_cursor);
        gdk_flush ();
 
        /* start import thread for this spec. this will ultimately call Session::import_audiofiles()
@@ -523,34 +534,33 @@ Editor::import_sndfiles (vector<ustring> paths, ImportMode mode, SrcQuality qual
                gtk_main_iteration ();
        }
 
-       interthread_progress_window->hide ();
        import_status.done = true;
-       interthread_progress_connection.disconnect ();
+
+       int result = -1;
 
        if (!import_status.cancel && !import_status.sources.empty()) {
-               if (add_sources (import_status.paths,
-                                import_status.sources,
-                                import_status.pos,
-                                import_status.mode,
-                                import_status.target_regions,
-                                import_status.target_tracks,
-                                track, false) == 0) {
-                       _session->save_state ("");
-               }
+               result = add_sources (
+                       import_status.paths,
+                       import_status.sources,
+                       import_status.pos,
+                       import_status.mode,
+                       import_status.target_regions,
+                       import_status.target_tracks,
+                       track, false
+                       );
 
                /* update position from results */
 
                pos = import_status.pos;
        }
 
-
-       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
-       return 0;
+       set_canvas_cursor (current_canvas_cursor);
+       return result;
 }
 
 int
-Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile,
-                       bool& check_sample_rate, ImportMode mode, nframes64_t& pos, int target_regions, int target_tracks,
+Editor::embed_sndfiles (vector<string> paths, bool multifile,
+                       bool& check_sample_rate, ImportMode mode, framepos_t& pos, int target_regions, int target_tracks,
                        boost::shared_ptr<Track>& track)
 {
        boost::shared_ptr<AudioFileSource> source;
@@ -558,56 +568,17 @@ Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile,
        string linked_path;
        SoundFileInfo finfo;
        int ret = 0;
-       Glib::ustring path_to_use;
 
-       track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
+       set_canvas_cursor (wait_cursor);
        gdk_flush ();
 
-       for (vector<Glib::ustring>::iterator p = paths.begin(); p != paths.end(); ++p) {
-
-               ustring path = *p;
-
-               if (Config->get_try_link_for_embed()) {
-
-                       /* lets see if we can link it into the session */
-                       
-                       sys::path tmp = _session->session_directory().sound_path() / Glib::path_get_basename(path);
-                       linked_path = tmp.to_string();
-                       
-                       path_to_use = linked_path;
-                       
-                       if (link (path.c_str(), linked_path.c_str()) == 0) {
-                               
-                               /* there are many reasons why link(2) might have failed.
-                                  but if it succeeds, we now have a link in the
-                                  session sound dir that will protect against
-                                  unlinking of the original path. nice.
-                               */
-                               
-                               path = linked_path;
-                               path_to_use = Glib::path_get_basename (path);
-                               
-                       } else {
+       for (vector<string>::iterator p = paths.begin(); p != paths.end(); ++p) {
 
-                               /* one possible reason is that its already linked */
-                               
-                               if (errno == EEXIST) {
-                                       struct stat sb;
-                                       
-                                       if (stat (linked_path.c_str(), &sb) == 0) {
-                                               if (sb.st_nlink > 1) { // its a hard link, assume its the one we want
-                                                       path = linked_path;
-                                                       path_to_use = Glib::path_get_basename (path);
-                                               }
-                                       }
-                               }
-                       }
-               }
+               string path = *p;
+               string error_msg;
 
                /* note that we temporarily truncated _id at the colon */
 
-               string error_msg;
-
                if (!AudioFileSource::get_soundfile_info (path, finfo, error_msg)) {
                        error << string_compose(_("Editor: cannot open file \"%1\", (%2)"), path, error_msg ) << endmsg;
                        goto out;
@@ -671,7 +642,7 @@ Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile,
                        }
                }
 
-               track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
+               set_canvas_cursor (wait_cursor);
 
                for (int n = 0; n < finfo.channels; ++n) {
                        try {
@@ -684,7 +655,7 @@ Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile,
 
                                        source = boost::dynamic_pointer_cast<AudioFileSource> (
                                                SourceFactory::createReadable (DataType::AUDIO, *_session,
-                                                                              path_to_use, n,
+                                                                              path, n,
                                                                               (mode == ImportAsTapeTrack
                                                                                ? Source::Destructive
                                                                                : Source::Flag (0)),
@@ -712,33 +683,26 @@ Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile,
        ret = add_sources (paths, sources, pos, mode, target_regions, target_tracks, track, true);
 
   out:
-       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
+       set_canvas_cursor (current_canvas_cursor);
        return ret;
 }
 
 int
-Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64_t& pos, ImportMode mode,
+Editor::add_sources (vector<string> paths, SourceList& sources, framepos_t& pos, ImportMode mode,
                     int target_regions, int target_tracks, boost::shared_ptr<Track>& track, bool /*add_channel_suffix*/)
 {
        vector<boost::shared_ptr<Region> > regions;
-       ustring region_name;
+       string region_name;
        uint32_t input_chan = 0;
        uint32_t output_chan = 0;
        bool use_timestamp;
 
        use_timestamp = (pos == -1);
 
-       if (use_timestamp) {
-               if (sources[0]->natural_position() != 0) {
-                       pos = sources[0]->natural_position();
-               } else {
-                       pos = get_preferred_edit_position ();
-               }
-       }
-
        // kludge (for MIDI we're abusing "channel" for "track" here)
-       if (paths.front().rfind(".mid") != Glib::ustring::npos)
+       if (SMFSource::safe_midi_file_extension (paths.front())) {
                target_regions = -1;
+       }
 
        if (target_regions == 1) {
 
@@ -746,6 +710,12 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
 
                region_name = region_name_from_path (paths.front(), (sources.size() > 1), false);
 
+                /* we checked in import_sndfiles() that there were not too many */
+
+                while (RegionFactory::region_by_name (region_name)) {
+                        region_name = bump_name_once (region_name, '.');
+                }
+
                PropertyList plist; 
                
                plist.add (ARDOUR::Properties::start, 0);
@@ -815,16 +785,49 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
        }
 
        int n = 0;
+        framepos_t rlen = 0;
 
        for (vector<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); ++r, ++n) {
+                
+                boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*r);
+                
+                if (use_timestamp && ar) {
+                        
+                        /* get timestamp for this region */
+
+                        const boost::shared_ptr<Source> s (ar->sources().front());
+                        const boost::shared_ptr<AudioSource> as = boost::dynamic_pointer_cast<AudioSource> (s);
+                        
+                        assert (as);
+                        
+                        if (as->natural_position() != 0) {
+                                pos = as->natural_position();
+                        } else if (target_tracks == 1) {
+                                /* hmm, no timestamp available, put it after the previous region
+                                 */
+                                if (n == 0) {
+                                        pos = get_preferred_edit_position ();
+                                } else {
+                                        pos += rlen;
+                                }
+                        } else {
+                                pos = get_preferred_edit_position ();
+                        }
+                                
+                }
 
                finish_bringing_in_material (*r, input_chan, output_chan, pos, mode, track);
 
-               if (target_tracks != 1) {
-                       track.reset ();
-               } else {
-                       pos += (*r)->length();
-               }
+                rlen = (*r)->length();
+                
+                if (target_tracks != 1) {
+                        track.reset ();
+                } else { 
+                        if (!use_timestamp || !ar) {
+                                /* line each one up right after the other */
+                                pos += (*r)->length();
+                        }
+                }
        }
 
        /* setup peak file building in another thread */
@@ -837,7 +840,7 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
 }
 
 int
-Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t in_chans, uint32_t out_chans, nframes64_t& pos,
+Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t in_chans, uint32_t out_chans, framepos_t& pos,
                                  ImportMode mode, boost::shared_ptr<Track>& existing_track)
 {
        boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
@@ -863,10 +866,10 @@ Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t
                        }
                }
 
-               boost::shared_ptr<Playlist> playlist = existing_track->diskstream()->playlist();
+               boost::shared_ptr<Playlist> playlist = existing_track->playlist();
                boost::shared_ptr<Region> copy (RegionFactory::create (region, region->properties()));
                begin_reversible_command (_("insert file"));
-                playlist->clear_history ();
+                playlist->clear_changes ();
                playlist->add_region (copy, pos);
                _session->add_command (new StatefulDiffCommand (playlist));
                commit_reversible_command ();
@@ -893,12 +896,12 @@ Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t
 
                                existing_track = mt.front();
                        }
-
+                        
                        existing_track->set_name (region->name());
                }
 
                boost::shared_ptr<Region> copy (RegionFactory::create (region));
-               existing_track->diskstream()->playlist()->add_region (copy, pos);
+               existing_track->playlist()->add_region (copy, pos);
                break;
        }
 
@@ -912,7 +915,7 @@ Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t
                if (!at.empty()) {
                        boost::shared_ptr<Region> copy (RegionFactory::create (region));
                        at.front()->set_name (basename_nosuffix (copy->name()));
-                       at.front()->diskstream()->playlist()->add_region (copy, pos);
+                       at.front()->playlist()->add_region (copy, pos);
                }
                break;
        }
@@ -938,43 +941,3 @@ Editor::import_thread ()
        /*NOTREACHED*/
        return 0;
 }
-
-gint
-Editor::import_progress_timeout (void */*arg*/)
-{
-       bool reset = false;
-
-       if (!interthread_progress_window->is_visible()) {
-               interthread_progress_window->show_all ();
-               reset = true;
-       }
-
-       interthread_progress_label.set_text (import_status.doing_what);
-
-       if (import_status.freeze) {
-               interthread_cancel_button.set_sensitive(false);
-       } else {
-               interthread_cancel_button.set_sensitive(true);
-       }
-
-       if (import_status.doing_what == "building peak files") {
-               interthread_progress_bar.pulse ();
-               return FALSE;
-       } else {
-               float val = import_status.progress;
-               interthread_progress_bar.set_fraction (min (max (0.0f, val), 1.0f));
-       }
-
-       if (reset) {
-
-               /* the window is now visible, speed up the updates */
-
-               interthread_progress_connection.disconnect ();
-               interthread_progress_connection = Glib::signal_timeout().connect
-                       (sigc::bind (sigc::mem_fun(*this, &Editor::import_progress_timeout), (gpointer) 0), 100);
-               return false;
-       } else {
-               return !(import_status.done || import_status.cancel);
-       }
-}
-