Replace thinning static state with parameter.
[ardour.git] / libs / ardour / session_state.cc
index 6a06863e9e16b1ca347cd261df2fe3e374d861ff..8463b92503b84736b4e0d2a1e9b369ecb78b728d 100644 (file)
 #include "pbd/error.h"
 #include "pbd/file_utils.h"
 #include "pbd/pathexpand.h"
-#include "pbd/pathscanner.h"
 #include "pbd/pthread_utils.h"
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
-#include "pbd/clear_dir.h"
 #include "pbd/localtime_r.h"
 
 #include "ardour/amp.h"
@@ -98,6 +96,7 @@
 #include "ardour/midi_track.h"
 #include "ardour/pannable.h"
 #include "ardour/playlist_factory.h"
+#include "ardour/playlist_source.h"
 #include "ardour/port.h"
 #include "ardour/processor.h"
 #include "ardour/proxy_controllable.h"
@@ -317,8 +316,9 @@ Session::post_engine_init ()
                
                initialize_latencies ();
                
+               _locations->added.connect_same_thread (*this, boost::bind (&Session::location_added, this, _1));
+               _locations->removed.connect_same_thread (*this, boost::bind (&Session::location_removed, this, _1));
                _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
-               _locations->added.connect_same_thread (*this, boost::bind (&Session::locations_added, this, _1));
                
        } catch (AudioEngine::PortRegistrationFailure& err) {
                /* handle this one in a different way than all others, so that its clear what happened */
@@ -333,8 +333,8 @@ Session::post_engine_init ()
        // send_full_time_code (0);
        _engine.transport_locate (0);
 
-       _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
-       _mmc->send (MIDI::MachineControlCommand (Timecode::Time ()));
+       send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
+       send_immediate_mmc (MIDI::MachineControlCommand (Timecode::Time ()));
 
        MIDI::Name::MidiPatchManager::instance().set_session (this);
 
@@ -512,7 +512,7 @@ Session::create (const string& session_template, BusProfile* bus_profile)
 
                                /* Copy plugin state files from template to new session */
                                std::string template_plugins = Glib::build_filename (session_template, X_("plugins"));
-                               copy_files (template_plugins, plugins_dir ());
+                               copy_recurse (template_plugins, plugins_dir ());
                                
                                return 0;
 
@@ -542,7 +542,7 @@ Session::create (const string& session_template, BusProfile* bus_profile)
                 ChanCount count(DataType::AUDIO, bus_profile->master_out_channels);
 
                if (bus_profile->master_out_channels) {
-                       boost::shared_ptr<Route> r (new Route (*this, _("master"), Route::MasterOut, DataType::AUDIO));
+                       boost::shared_ptr<Route> r (new Route (*this, _("Master"), Route::MasterOut, DataType::AUDIO));
                         if (r->init ()) {
                                 return -1;
                         }
@@ -667,10 +667,20 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
        XMLTree tree;
        std::string xml_path(_session_dir->root_path());
 
+       /* prevent concurrent saves from different threads */
+
+       Glib::Threads::Mutex::Lock lm (save_state_lock);
+
        if (!_writable || (_state_of_the_state & CannotSave)) {
                return 1;
        }
 
+       if (g_atomic_int_get(&_suspend_save)) {
+               _save_queued = true;
+               return 1;
+       }
+       _save_queued = false;
+
        if (!_engine.connected ()) {
                error << string_compose (_("the %1 audio engine is not connected and state saving would lose all I/O connections. Session not saved"),
                                          PROGRAM_NAME)
@@ -875,6 +885,12 @@ Session::load_options (const XMLNode& node)
        return 0;
 }
 
+bool
+Session::save_default_options ()
+{
+       return config.save_state();
+}
+
 XMLNode&
 Session::get_state()
 {
@@ -1129,7 +1145,7 @@ Session::set_state (const XMLNode& node, int version)
 
        if (node.name() != X_("Session")) {
                fatal << _("programming error: Session: incorrect XML node sent to set_state()") << endmsg;
-               return -1;
+               goto out;
        }
 
        if ((prop = node.property ("name")) != 0) {
@@ -1143,7 +1159,7 @@ Session::set_state (const XMLNode& node, int version)
                if (_nominal_frame_rate != _current_frame_rate) {
                         boost::optional<int> r = AskAboutSampleRateMismatch (_nominal_frame_rate, _current_frame_rate);
                        if (r.get_value_or (0)) {
-                               return -1;
+                               goto out;
                        }
                }
        }
@@ -1218,20 +1234,7 @@ Session::set_state (const XMLNode& node, int version)
                goto out;
        }
 
-       Location* location;
-
-       if ((location = _locations->auto_loop_location()) != 0) {
-               set_auto_loop_location (location);
-       }
-
-       if ((location = _locations->auto_punch_location()) != 0) {
-               set_auto_punch_location (location);
-       }
-
-       if ((location = _locations->session_range_location()) != 0) {
-               delete _session_range_location;
-               _session_range_location = location;
-       }
+       locations_changed ();
 
        if (_session_range_location) {
                AudioFileSource::set_header_position_offset (_session_range_location->start());
@@ -1336,9 +1339,13 @@ Session::set_state (const XMLNode& node, int version)
 
        StateReady (); /* EMIT SIGNAL */
 
+       delete state_tree;
+       state_tree = 0;
        return 0;
 
   out:
+       delete state_tree;
+       state_tree = 0;
        return ret;
 }
 
@@ -1421,7 +1428,13 @@ Session::XMLRouteFactory (const XMLNode& node, int version)
                 ret = track;
 
        } else {
-               boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
+               enum Route::Flag flags = Route::Flag(0);
+               const XMLProperty* prop = node.property("flags");
+               if (prop) {
+                       flags = Route::Flag (string_2_enum (prop->value(), flags));
+               }
+
+               boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
@@ -1493,7 +1506,13 @@ Session::XMLRouteFactory_2X (const XMLNode& node, int version)
                 ret = track;
 
        } else {
-               boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
+               enum Route::Flag flags = Route::Flag(0);
+               const XMLProperty* prop = node.property("flags");
+               if (prop) {
+                       flags = Route::Flag (string_2_enum (prop->value(), flags));
+               }
+
+               boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
@@ -1828,47 +1847,15 @@ 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)
 {
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
-       boost::shared_ptr<Source> source;
+       boost::shared_ptr<Source> source; /* don't need this but it stops some
+                                          * versions of gcc complaining about
+                                          * discarded return values.
+                                          */
 
        nlist = node.children();
 
@@ -1885,9 +1872,15 @@ Session::load_sources (const XMLNode& node)
 
                         int user_choice;
 
+                       if (err.type == DataType::MIDI && Glib::path_is_absolute (err.path)) {
+                               error << string_compose (_("A external MIDI file is missing. %1 cannot currently recover from missing external MIDI files"),
+                                                        PROGRAM_NAME) << endmsg;
+                               return -1;
+                       }
+
                         if (!no_questions_about_missing_files) {
-                                user_choice = MissingFile (this, err.path, err.type).get_value_or (-1);
-                        } else {
+                               user_choice = MissingFile (this, err.path, err.type).get_value_or (-1);
+                       } else {
                                 user_choice = -2;
                         }
 
@@ -1912,8 +1905,35 @@ Session::load_sources (const XMLNode& node)
 
                         case -1:
                         default:
-                                warning << _("A sound file is missing. It will be replaced by silence.") << endmsg;
-                                source = SourceFactory::createSilent (*this, **niter, max_framecnt, _current_frame_rate);
+                               switch (err.type) {
+
+                               case DataType::AUDIO:
+                                       source = SourceFactory::createSilent (*this, **niter, max_framecnt, _current_frame_rate);
+                                       break;
+
+                               case DataType::MIDI:
+                                       /* The MIDI file is actually missing so
+                                        * just create a new one in the same
+                                        * location. Do not announce its
+                                        */
+                                       string fullpath;
+
+                                       if (!Glib::path_is_absolute (err.path)) {
+                                               fullpath = Glib::build_filename (source_search_path (DataType::MIDI).front(), err.path);
+                                       } else {
+                                               /* this should be an unrecoverable error: we would be creating a MIDI file outside
+                                                  the session tree.
+                                               */
+                                               return -1;
+                                       }
+                                       /* Note that we do not announce the source just yet - we need to reset its ID before we do that */
+                                       source = SourceFactory::createWritable (DataType::MIDI, *this, fullpath, false, _current_frame_rate, false, false);
+                                       /* reset ID to match the missing one */
+                                       source->set_id (**niter);
+                                       /* Now we can announce it */
+                                       SourceFactory::SourceCreated (source);
+                                       break;
+                               }
                                 break;
                         }
                }
@@ -1935,7 +1955,7 @@ Session::XMLSourceFactory (const XMLNode& node)
        }
 
        catch (failed_constructor& err) {
-               error << string_compose (_("Found a sound file that cannot be used by %1. Talk to the progammers."), PROGRAM_NAME) << endmsg;
+               error << string_compose (_("Found a sound file that cannot be used by %1. Talk to the programmers."), PROGRAM_NAME) << endmsg;
                return boost::shared_ptr<Source>();
        }
 }
@@ -1996,7 +2016,7 @@ Session::save_template (string template_name)
                return -1;
        }
 
-       copy_files (plugins_dir(), template_plugin_state_path);
+       copy_recurse (plugins_dir(), template_plugin_state_path);
 
        return 0;
 }
@@ -2099,7 +2119,7 @@ Session::refresh_disk_space ()
 }
 
 string
-Session::get_best_session_directory_for_new_source ()
+Session::get_best_session_directory_for_new_audio ()
 {
        vector<space_and_path>::iterator i;
        string result = _session_dir->root_path();
@@ -2274,12 +2294,6 @@ Session::load_route_groups (const XMLNode& node, int version)
        return 0;
 }
 
-void
-Session::auto_save()
-{
-       save_state (_current_snapshot_name);
-}
-
 static bool
 state_file_filter (const string &str, void* /*arg*/)
 {
@@ -2287,16 +2301,10 @@ state_file_filter (const string &str, void* /*arg*/)
                str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix)));
 }
 
-struct string_cmp {
-       bool operator()(const string* a, const string* b) {
-               return *a < *b;
-       }
-};
-
-static string*
-remove_end(string* state)
+static string
+remove_end(string state)
 {
-       string statename(*state);
+       string statename(state);
 
        string::size_type start,end;
        if ((start = statename.find_last_of (G_DIR_SEPARATOR)) != string::npos) {
@@ -2307,24 +2315,23 @@ remove_end(string* state)
                end = statename.length();
        }
 
-       return new string(statename.substr (0, end));
+       return string(statename.substr (0, end));
 }
 
-vector<string *> *
+vector<string>
 Session::possible_states (string path)
 {
-       PathScanner scanner;
-       vector<string*>* states = scanner (path, state_file_filter, 0, false, false);
+       vector<string> states;
+       find_files_matching_filter (states, path, state_file_filter, 0, false, false);
 
-       transform(states->begin(), states->end(), states->begin(), remove_end);
+       transform(states.begin(), states.end(), states.begin(), remove_end);
 
-       string_cmp cmp;
-       sort (states->begin(), states->end(), cmp);
+       sort (states.begin(), states.end());
 
        return states;
 }
 
-vector<string *> *
+vector<string>
 Session::possible_states () const
 {
        return possible_states(_path);
@@ -2548,8 +2555,7 @@ Session::find_all_sources (string path, set<string>& result)
 int
 Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_this_snapshot)
 {
-       PathScanner scanner;
-       vector<string*>* state_files;
+       vector<string> state_files;
        string ripped;
        string this_snapshot_path;
 
@@ -2561,9 +2567,9 @@ Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_th
                ripped = ripped.substr (0, ripped.length() - 1);
        }
 
-       state_files = scanner (ripped, accept_all_state_files, (void *) 0, true, true);
+       find_files_matching_filter (state_files, ripped, accept_all_state_files, (void *) 0, true, true);
 
-       if (state_files == 0) {
+       if (state_files.empty()) {
                /* impossible! */
                return 0;
        }
@@ -2572,13 +2578,13 @@ Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_th
        this_snapshot_path += legalize_for_path (_current_snapshot_name);
        this_snapshot_path += statefile_suffix;
 
-       for (vector<string*>::iterator i = state_files->begin(); i != state_files->end(); ++i) {
+       for (vector<string>::iterator i = state_files.begin(); i != state_files.end(); ++i) {
 
-               if (exclude_this_snapshot && **i == this_snapshot_path) {
+               if (exclude_this_snapshot && *i == this_snapshot_path) {
                        continue;
                }
 
-               if (find_all_sources (**i, result) < 0) {
+               if (find_all_sources (*i, result) < 0) {
                        return -1;
                }
        }
@@ -2605,6 +2611,7 @@ Session::ask_about_playlist_deletion (boost::shared_ptr<Playlist> p)
 void
 Session::cleanup_regions ()
 {
+       bool removed = false;
        const RegionFactory::RegionMap& regions (RegionFactory::regions());
 
        for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end(); ++i) {
@@ -2612,10 +2619,24 @@ Session::cleanup_regions ()
                uint32_t used = playlists->region_use_count (i->second);
 
                if (used == 0 && !i->second->automatic ()) {
+                       removed = true;
                        RegionFactory::map_remove (i->second);
                }
        }
 
+       if (removed) {
+               // re-check to remove parent references of compound regions
+               for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+                       if (!(i->second->whole_file() && i->second->max_source_level() > 0)) {
+                               continue;
+                       }
+                       assert(boost::dynamic_pointer_cast<PlaylistSource>(i->second->source (0)) != 0);
+                       if (0 == playlists->region_use_count (i->second)) {
+                               RegionFactory::map_remove (i->second);
+                       }
+               }
+       }
+
        /* dump the history list */
        _history.clear ();
 
@@ -2628,13 +2649,9 @@ Session::cleanup_sources (CleanupReport& rep)
        // FIXME: needs adaptation to midi
 
        vector<boost::shared_ptr<Source> > dead_sources;
-       PathScanner scanner;
        string audio_path;
        string midi_path;
-       vector<space_and_path>::iterator i;
-       vector<space_and_path>::iterator nexti;
-       vector<string*>* candidates;
-       vector<string*>* candidates2;
+       vector<string> candidates;
        vector<string> unused;
        set<string> all_sources;
        bool used;
@@ -2642,6 +2659,8 @@ Session::cleanup_sources (CleanupReport& rep)
        int ret = -1;
        string tmppath1;
        string tmppath2;
+       Searchpath asp;
+       Searchpath msp;
 
        _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
 
@@ -2683,54 +2702,23 @@ Session::cleanup_sources (CleanupReport& rep)
 
        /* build a list of all the possible audio directories for the session */
 
-       for (i = session_dirs.begin(); i != session_dirs.end(); ) {
-
-               nexti = i;
-               ++nexti;
-
+       for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
                SessionDirectory sdir ((*i).path);
-               audio_path += sdir.sound_path();
-
-               if (nexti != session_dirs.end()) {
-                       audio_path += G_SEARCHPATH_SEPARATOR;
-               }
-
-               i = nexti;
+               asp += sdir.sound_path();
        }
+       audio_path += asp.to_string();
 
 
        /* build a list of all the possible midi directories for the session */
 
-       for (i = session_dirs.begin(); i != session_dirs.end(); ) {
-
-               nexti = i;
-               ++nexti;
-
+       for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
                SessionDirectory sdir ((*i).path);
-               midi_path += sdir.midi_path();
-
-               if (nexti != session_dirs.end()) {
-                       midi_path += G_SEARCHPATH_SEPARATOR;
-               }
-
-               i = nexti;
+               msp += sdir.midi_path();
        }
+       midi_path += msp.to_string();
 
-       candidates = scanner (audio_path, accept_all_audio_files, (void *) 0, true, true);
-       candidates2 = scanner (midi_path, accept_all_midi_files, (void *) 0, true, true);
-
-        /* merge them */
-
-        if (candidates) {
-                if (candidates2) {
-                        for (vector<string*>::iterator i = candidates2->begin(); i != candidates2->end(); ++i) {
-                                candidates->push_back (*i);
-                        }
-                        delete candidates2;
-                }
-        } else {
-                candidates = candidates2; // might still be null
-        }
+       find_files_matching_filter (candidates, audio_path, accept_all_audio_files, (void *) 0, true, true);
+       find_files_matching_filter (candidates, midi_path, accept_all_midi_files, (void *) 0, true, true);
 
        /* find all sources, but don't use this snapshot because the
           state file on disk still references sources we may have already
@@ -2770,32 +2758,26 @@ Session::cleanup_sources (CleanupReport& rep)
                 i = tmp;
        }
 
-        if (candidates) {
-                for (vector<string*>::iterator x = candidates->begin(); x != candidates->end(); ++x) {
+       for (vector<string>::iterator x = candidates.begin(); x != candidates.end(); ++x) {
 
-                        used = false;
-                        spath = **x;
+               used = false;
+               spath = *x;
 
-                        for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
+               for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
 
-                               tmppath1 = canonical_path (spath);
-                               tmppath2 = canonical_path ((*i));
+                       tmppath1 = canonical_path (spath);
+                       tmppath2 = canonical_path ((*i));
 
-                               if (tmppath1 == tmppath2) {
-                                        used = true;
-                                        break;
-                                }
-                        }
-
-                        if (!used) {
-                                unused.push_back (spath);
-                        }
-
-                        delete *x;
-                }
+                       if (tmppath1 == tmppath2) {
+                               used = true;
+                               break;
+                       }
+               }
 
-                delete candidates;
-        }
+               if (!used) {
+                       unused.push_back (spath);
+               }
+       }
 
        /* now try to move all unused files into the "dead" directory(ies) */
 
@@ -2940,6 +2922,12 @@ Session::cleanup_trash_sources (CleanupReport& rep)
 void
 Session::set_dirty ()
 {
+       /* never mark session dirty during loading */
+
+       if (_state_of_the_state & Loading) {
+               return;
+       }
+
        bool was_dirty = dirty();
 
        _state_of_the_state = StateOfTheState (_state_of_the_state | Dirty);
@@ -3031,7 +3019,7 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
        case ControllableDescriptor::NamedRoute:
        {
                std::string str = desc.top_level_name();
-               if (str == "master") {
+               if (str == "Master" || str == "master") {
                        r = _master_out;
                } else if (str == "control" || str == "listen") {
                        r = _monitor_out;
@@ -3177,6 +3165,11 @@ Session::save_history (string snapshot_name)
                return 0;
        }
 
+       if (!Config->get_save_history() || Config->get_saved_history_depth() < 0 || 
+           (_history.undo_depth() == 0 && _history.redo_depth() == 0)) {
+               return 0;
+       }
+
        if (snapshot_name.empty()) {
                snapshot_name = _current_snapshot_name;
        }
@@ -3193,10 +3186,6 @@ Session::save_history (string snapshot_name)
                }
        }
 
-       if (!Config->get_save_history() || Config->get_saved_history_depth() < 0) {
-               return 0;
-       }
-
        tree.set_root (&_history.get_state (Config->get_saved_history_depth()));
 
        if (!tree.write (xml_path))
@@ -3508,12 +3497,12 @@ Session::config_changed (std::string p, bool ours)
                listen_position_changed ();
        } else if (p == "solo-control-is-listen-control") {
                solo_control_mode_changed ();
+       } else if (p == "solo-mute-gain") {
+               _solo_cut_control->Changed();
        } else if (p == "timecode-offset" || p == "timecode-offset-negative") {
                last_timecode_valid = false;
        } else if (p == "playback-buffer-seconds") {
                AudioSource::allocate_working_buffers (frame_rate());
-       } else if (p == "automation-thinning-factor") {
-               Evoral::ControlList::set_thinning_factor (Config->get_automation_thinning_factor());
        } else if (p == "ltc-source-port") {
                reconnect_ltc_input ();
        } else if (p == "ltc-sink-port") {
@@ -3839,3 +3828,92 @@ Session::get_info_from_path (const string& xmlpath, float& sample_rate, SampleFo
 
        return !(found_sr && found_data_format); // zero if they are both found
 }
+
+typedef std::vector<boost::shared_ptr<FileSource> > SeveralFileSources;
+typedef std::map<std::string,SeveralFileSources> SourcePathMap;
+
+int
+Session::bring_all_sources_into_session (boost::function<void(uint32_t,uint32_t,string)> callback)
+{
+       uint32_t total = 0;
+       uint32_t n = 0;
+       SourcePathMap source_path_map;
+       string new_path;
+       boost::shared_ptr<AudioFileSource> afs;
+       int ret = 0;
+
+       {
+
+               Glib::Threads::Mutex::Lock lm (source_lock);
+               
+               cerr << " total sources = " << sources.size();
+               
+               for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
+                       boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
+                       
+                       if (!fs) {
+                               continue;
+                       }
+                       
+                       if (fs->within_session()) {
+                               cerr << "skip " << fs->name() << endl;
+                               continue;
+                       }
+                       
+                       if (source_path_map.find (fs->path()) != source_path_map.end()) {
+                               source_path_map[fs->path()].push_back (fs);
+                       } else {
+                               SeveralFileSources v;
+                               v.push_back (fs);
+                               source_path_map.insert (make_pair (fs->path(), v));
+                       }
+                       
+                       total++;
+               }
+               
+               cerr << " fsources = " << total << endl;
+               
+               for (SourcePathMap::iterator i = source_path_map.begin(); i != source_path_map.end(); ++i) {
+                       
+                       /* tell caller where we are */
+                       
+                       string old_path = i->first;
+                       
+                       callback (n, total, old_path);
+                       
+                       cerr << old_path << endl;
+                       
+                       new_path.clear ();
+                       
+                       switch (i->second.front()->type()) {
+                       case DataType::AUDIO:
+                               new_path = new_audio_source_path_for_embedded (old_path);
+                               break;
+                               
+                       case DataType::MIDI:
+                               break;
+                       }
+                       
+                       cerr << "Move " << old_path << " => " << new_path << endl;
+                       
+                       if (!copy_file (old_path, new_path)) {
+                               cerr << "failed !\n";
+                               ret = -1;
+                       }
+                       
+                       /* make sure we stop looking in the external
+                          dir/folder. Remember, this is an all-or-nothing
+                          operations, it doesn't merge just some files.
+                       */
+                       remove_dir_from_search_path (Glib::path_get_dirname (old_path), i->second.front()->type());
+
+                       for (SeveralFileSources::iterator f = i->second.begin(); f != i->second.end(); ++f) {
+                               (*f)->set_path (new_path);
+                       }
+               }
+       }
+
+       save_state ("", false, false);
+
+       return ret;
+}