Merge with 2.0-ongoing R2885.
[ardour.git] / libs / ardour / session.cc
index d20f0fcf90d50974784b30a7d54edf8da9ff6e24..19d562f84421beb4974ef574828641db9c98cda4 100644 (file)
@@ -62,7 +62,7 @@
 #include <ardour/processor.h>
 #include <ardour/plugin_insert.h>
 #include <ardour/port_insert.h>
-#include <ardour/bundle.h>
+#include <ardour/auto_bundle.h>
 #include <ardour/slave.h>
 #include <ardour/tempo.h>
 #include <ardour/audio_track.h>
@@ -78,6 +78,7 @@
 #include <ardour/region_factory.h>
 #include <ardour/filename_extensions.h>
 #include <ardour/session_directory.h>
+#include <ardour/tape_file_matcher.h>
 
 #ifdef HAVE_LIBLO
 #include <ardour/osc.h>
@@ -96,6 +97,14 @@ static const int CPU_CACHE_ALIGN = 64;
 static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */
 #endif
 
+bool Session::_disable_all_loaded_plugins = false;
+
+Session::compute_peak_t          Session::compute_peak          = 0;
+Session::find_peaks_t            Session::find_peaks            = 0;
+Session::apply_gain_to_buffer_t  Session::apply_gain_to_buffer  = 0;
+Session::mix_buffers_with_gain_t Session::mix_buffers_with_gain = 0;
+Session::mix_buffers_no_gain_t   Session::mix_buffers_no_gain   = 0;
+
 sigc::signal<int> Session::AskAboutPendingState;
 sigc::signal<void> Session::SendFeedback;
 
@@ -104,9 +113,9 @@ sigc::signal<void> Session::StartTimeChanged;
 sigc::signal<void> Session::EndTimeChanged;
 
 Session::Session (AudioEngine &eng,
-                 string fullpath,
-                 string snapshot_name,
-                 string* mix_template)
+                 const string& fullpath,
+                 const string& snapshot_name,
+                 string mix_template)
 
        : _engine (eng),
          _scratch_buffers(new BufferSet()),
@@ -122,66 +131,32 @@ Session::Session (AudioEngine &eng,
          diskstreams (new DiskstreamList),
          routes (new RouteList),
          auditioner ((Auditioner*) 0),
+         _bundle_xml_node (0),
          _click_io ((IO*) 0),
-         main_outs (0),
-         _automation_interval (0)
+         main_outs (0)
 {
+       bool new_session;
+
        if (!eng.connected()) {
                throw failed_constructor();
        }
+       
+       cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl;
 
        n_physical_outputs = _engine.n_physical_outputs();
        n_physical_inputs =  _engine.n_physical_inputs();
 
        first_stage_init (fullpath, snapshot_name);
 
-       initialize_start_and_end_locations(0, compute_initial_length ());
-
-       if(mix_template) {
-               // try and create a new session directory
-               try
-               {
-                       if(!_session_dir->create()) {
-                               // an existing session.
-                               // throw a_more_meaningful_exception()
-                               destroy ();
-                               throw failed_constructor ();
-                       }
-               }
-               catch(sys::filesystem_error& ex)
-               {
-                       destroy ();
-                       throw failed_constructor ();
-               }
-
-               if(!create_session_file_from_template (*mix_template)) {
+       new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+       if (new_session) {
+               if (create (new_session, mix_template, compute_initial_length())) {
                        destroy ();
                        throw failed_constructor ();
                }
-
-               cerr << "Creating session " << fullpath
-                       <<" using template" << *mix_template
-                       << endl;
-       } else {
-               // must be an existing session
-               try
-               {
-                       // ensure the necessary session subdirectories exist
-                       // in case the directory structure has changed etc.
-                       _session_dir->create();
-               }
-               catch(sys::filesystem_error& ex)
-               {
-                       destroy ();
-                       throw failed_constructor ();
-               }
-
-               cerr << "Loading session " << fullpath
-                       << " using snapshot " << snapshot_name << " (1)"
-                       << endl;
        }
-
-       if (second_stage_init (false)) {
+       
+       if (second_stage_init (new_session)) {
                destroy ();
                throw failed_constructor ();
        }
@@ -223,10 +198,12 @@ Session::Session (AudioEngine &eng,
          _send_smpte_update (false),
          diskstreams (new DiskstreamList),
          routes (new RouteList),
-         main_outs (0),
-         _automation_interval (0)
+         _bundle_xml_node (0),
+         main_outs (0)
 
 {
+       bool new_session;
+
        if (!eng.connected()) {
                throw failed_constructor();
        }
@@ -246,11 +223,13 @@ Session::Session (AudioEngine &eng,
 
        first_stage_init (fullpath, snapshot_name);
 
-       initialize_start_and_end_locations(0, initial_length);
-       
-       if (!_session_dir->create () || !create_session_file ())        {
-               destroy ();
-               throw failed_constructor ();
+       new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+
+       if (new_session) {
+               if (create (new_session, string(), initial_length)) {
+                       destroy ();
+                       throw failed_constructor ();
+               }
        }
 
        {
@@ -277,7 +256,7 @@ Session::Session (AudioEngine &eng,
                }
                
                if (!rl.empty()) {
-                       add_routes (rl);
+                       add_routes (rl, false);
                }
                
        }
@@ -285,22 +264,16 @@ Session::Session (AudioEngine &eng,
        Config->set_input_auto_connect (input_ac);
        Config->set_output_auto_connect (output_ac);
 
-       if (second_stage_init (true)) {
+       if (second_stage_init (new_session)) {
                destroy ();
                throw failed_constructor ();
        }
        
-       store_recent_sessions(_name, _path);
+       store_recent_sessions (_name, _path);
        
-       bool was_dirty = dirty ();
-
        _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
 
        Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed));
-
-       if (was_dirty) {
-               DirtyChanged (); /* EMIT SIGNAL */
-       }
 }
 
 Session::~Session ()
@@ -318,6 +291,7 @@ Session::destroy ()
        remove_pending_capture_state ();
 
        _state_of_the_state = StateOfTheState (CannotSave|Deletion);
+
        _engine.remove_session ();
 
        GoingAway (); /* EMIT SIGNAL */
@@ -486,20 +460,6 @@ Session::destroy ()
                i = tmp;
        }
        
-#ifdef TRACK_DESTRUCTION
-       cerr << "delete bundles\n";
-#endif /* TRACK_DESTRUCTION */
-       for (BundleList::iterator i = _bundles.begin(); i != _bundles.end(); ) {
-               BundleList::iterator tmp;
-
-               tmp = i;
-               ++tmp;
-
-               delete *i;
-
-               i = tmp;
-       }
-
        if (butler_mixdown_buffer) {
                delete [] butler_mixdown_buffer;
        }
@@ -614,22 +574,22 @@ Session::when_engine_running ()
                char buf[32];
                snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1);
 
-               Bundle* c = new OutputBundle (buf, true);
-               c->set_nchannels (1);
-               c->add_port_to_channel (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
+               shared_ptr<AutoBundle> c (new AutoBundle (buf, true));
+               c->set_channels (1);
+               c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
 
-               add_bundle (c);
+               add_bundle (c);
        }
 
        for (uint32_t np = 0; np < n_physical_inputs; ++np) {
                char buf[32];
                snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1);
 
-               Bundle* c = new InputBundle (buf, true);
-               c->set_nchannels (1);
-               c->add_port_to_channel (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
+               shared_ptr<AutoBundle> c (new AutoBundle (buf, false));
+               c->set_channels (1);
+               c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
 
-               add_bundle (c);
+               add_bundle (c);
        }
 
        /* TWO: STEREO */
@@ -638,24 +598,24 @@ Session::when_engine_running ()
                char buf[32];
                snprintf (buf, sizeof (buf), _("out %" PRIu32 "+%" PRIu32), np+1, np+2);
 
-               Bundle* c = new OutputBundle (buf, true);
-               c->set_nchannels (2);
-               c->add_port_to_channel (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
-               c->add_port_to_channel (1, _engine.get_nth_physical_output (DataType::AUDIO, np+1));
+               shared_ptr<AutoBundle> c (new AutoBundle (buf, true));
+               c->set_channels (2);
+               c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
+               c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1));
 
-               add_bundle (c);
+               add_bundle (c);
        }
 
        for (uint32_t np = 0; np < n_physical_inputs; np +=2) {
                char buf[32];
                snprintf (buf, sizeof (buf), _("in %" PRIu32 "+%" PRIu32), np+1, np+2);
 
-               Bundle* c = new InputBundle (buf, true);
-               c->set_nchannels (2);
-               c->add_port_to_channel (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
-               c->add_port_to_channel (1, _engine.get_nth_physical_input (DataType::AUDIO, np+1));
+               shared_ptr<AutoBundle> c (new AutoBundle (buf, false));
+               c->set_channels (2);
+               c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
+               c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1));
 
-               add_bundle (c);
+               add_bundle (c);
        }
 
        /* THREE MASTER */
@@ -700,13 +660,13 @@ Session::when_engine_running ()
                        
                }
 
-               Bundle* c = new OutputBundle (_("Master Out"), true);
+               shared_ptr<AutoBundle> c (new AutoBundle (_("Master Out"), true));
 
-               c->set_nchannels (_master_out->n_inputs().n_total());
-               for (uint32_t n = 0; n < _master_out->n_inputs ().n_total(); ++n) {
-                       c->add_port_to_channel ((int) n, _master_out->input(n)->name());
-               }
-               add_bundle (c);
+               c->set_channels (_master_out->n_inputs().n_total());
+               for (uint32_t n = 0; n < _master_out->n_inputs ().n_total(); ++n) {
+                       c->set_port (n, _master_out->input(n)->name());
+               }
+               add_bundle (c);
        } 
 
        hookup_io ();
@@ -740,6 +700,7 @@ Session::when_engine_running ()
        
        _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty));
 
+
        /* hook us up to the engine */
 
        _engine.set_session (this);
@@ -749,10 +710,7 @@ Session::when_engine_running ()
 
        osc->set_session (*this);
 #endif
-
-       _state_of_the_state = Clean;
-
-       DirtyChanged (); /* EMIT SIGNAL */
+    
 }
 
 void
@@ -764,6 +722,7 @@ Session::hookup_io ()
 
        _state_of_the_state = StateOfTheState (_state_of_the_state | InitialConnecting);
 
+
        if (auditioner == 0) {
                
                /* we delay creating the auditioner till now because
@@ -817,7 +776,13 @@ Session::hookup_io ()
                for (RouteList::iterator x = r->begin(); x != r->end(); ++x) {
                        (*x)->set_control_outs (cports);
                }
-       } 
+       }
+
+       /* load bundles, which we may have postponed earlier on */
+       if (_bundle_xml_node) {
+               load_bundles (*_bundle_xml_node);
+               delete _bundle_xml_node;
+       }       
 
        /* Tell all IO objects to connect themselves together */
 
@@ -833,6 +798,7 @@ Session::hookup_io ()
 
        _state_of_the_state = StateOfTheState (_state_of_the_state & ~InitialConnecting);
 
+
        /* now handle the whole enchilada as if it was one
           graph reorder event.
        */
@@ -1270,8 +1236,10 @@ Session::set_frame_rate (nframes_t frames_per_second)
 
        sync_time_vars();
 
-       _automation_interval = ((nframes_t) ceil ((double) frames_per_second * 0.25));
+       Automatable::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval())));
 
+       clear_clicks ();
+       
        // XXX we need some equivalent to this, somehow
        // SndFileSource::setup_standard_crossfades (frames_per_second);
 
@@ -1495,11 +1463,13 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many)
        char track_name[32];
        uint32_t track_id = 0;
        uint32_t n = 0;
-       uint32_t channels_used = 0;
        string port;
        RouteList new_routes;
        list<boost::shared_ptr<MidiTrack> > ret;
+       //uint32_t control_id;
 
+       // FIXME: need physical I/O and autoconnect stuff for MIDI
+       
        /* count existing midi tracks */
 
        {
@@ -1509,18 +1479,29 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many)
                        if (dynamic_cast<MidiTrack*>((*i).get()) != 0) {
                                if (!(*i)->is_hidden()) {
                                        n++;
-                                       channels_used += (*i)->n_inputs().n_midi();
+                                       //channels_used += (*i)->n_inputs().n_midi();
                                }
                        }
                }
        }
 
+       /*
+       vector<string> physinputs;
+       vector<string> physoutputs;
+       uint32_t nphysical_in;
+       uint32_t nphysical_out;
+
+       _engine.get_physical_outputs (physoutputs);
+       _engine.get_physical_inputs (physinputs);
+       control_id = ntracks() + nbusses() + 1;
+       */
+
        while (how_many) {
 
                /* check for duplicate route names, since we might have pre-existing
-                  routes with this name (e.g. create Midi1, Midi2, delete Midi1,
+                  routes with this name (e.g. create Audio1, Audio2, delete Audio1,
                   save, close,restart,add new route - first named route is now
-                  Midi2)
+                  Audio2)
                */
                
 
@@ -1535,17 +1516,71 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many)
                        
                } while (track_id < (UINT_MAX-1));
 
+               /*
+               if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+                       nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size());
+               } else {
+                       nphysical_in = 0;
+               }
+               
+               if (Config->get_output_auto_connect() & AutoConnectPhysical) {
+                       nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size());
+               } else {
+                       nphysical_out = 0;
+               }
+               */
+
+               shared_ptr<MidiTrack> track;
+               
                try {
-                       shared_ptr<MidiTrack> track (new MidiTrack (*this, track_name, Route::Flag (0), mode));
+                       track = boost::shared_ptr<MidiTrack>((new MidiTrack (*this, track_name, Route::Flag (0), mode)));
                        
-                       if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::MIDI, 1), false, this)) {
+                       if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::AUDIO, 1), false, this)) {
                                error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
+                               goto failed;
+                       }
+
+                       /*
+                       if (nphysical_in) {
+                               for (uint32_t x = 0; x < track->n_inputs().n_midi() && x < nphysical_in; ++x) {
+                                       
+                                       port = "";
+                                       
+                                       if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+                                               port = physinputs[(channels_used+x)%nphysical_in];
+                                       } 
+                                       
+                                       if (port.length() && track->connect_input (track->input (x), port, this)) {
+                                               break;
+                                       }
+                               }
+                       }
+                       
+                       for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) {
+                               
+                               port = "";
+                               
+                               if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) {
+                                       port = physoutputs[(channels_used+x)%nphysical_out];
+                               } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
+                                       if (_master_out) {
+                                               port = _master_out->input (x%_master_out->n_inputs().n_midi())->name();
+                                       }
+                               }
+                               
+                               if (port.length() && track->connect_output (track->output (x), port, this)) {
+                                       break;
+                               }
                        }
                        
                        channels_used += track->n_inputs ().n_midi();
 
+                       */
+
+                       track->midi_diskstream()->non_realtime_input_change();
+                       
                        track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes));
-                       track->set_remote_control_id (ntracks());
+                       //track->set_remote_control_id (control_id);
 
                        new_routes.push_back (track);
                        ret.push_back (track);
@@ -1553,14 +1588,43 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many)
 
                catch (failed_constructor &err) {
                        error << _("Session: could not create new midi track.") << endmsg;
-                       // XXX should we delete the tracks already created? 
-                       ret.clear ();
-                       return ret;
+
+                       if (track) {
+                               /* we need to get rid of this, since the track failed to be created */
+                               /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */
+
+                               { 
+                                       RCUWriter<DiskstreamList> writer (diskstreams);
+                                       boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
+                                       ds->remove (track->midi_diskstream());
+                               }
+                       }
+
+                       goto failed;
                }
-               
+
+               catch (AudioEngine::PortRegistrationFailure& pfe) {
+
+                       error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg;
+
+                       if (track) {
+                               /* we need to get rid of this, since the track failed to be created */
+                               /* XXX arguably, MidiTrack::MidiTrack should not do the Session::add_diskstream() */
+
+                               { 
+                                       RCUWriter<DiskstreamList> writer (diskstreams);
+                                       boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
+                                       ds->remove (track->midi_diskstream());
+                               }
+                       }
+
+                       goto failed;
+               }
+
                --how_many;
        }
 
+  failed:
        if (!new_routes.empty()) {
                add_routes (new_routes, false);
                save_state (_current_snapshot_name);
@@ -1733,8 +1797,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
 
   failed:
        if (!new_routes.empty()) {
-               add_routes (new_routes, false);
-               save_state (_current_snapshot_name);
+               add_routes (new_routes, true);
        }
 
        return ret;
@@ -1868,8 +1931,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_
 
   failure:
        if (!ret.empty()) {
-               add_routes (ret, false);
-               save_state (_current_snapshot_name);
+               add_routes (ret, true);
        }
 
        return ret;
@@ -1901,7 +1963,10 @@ Session::add_routes (RouteList& new_routes, bool save)
                
                if ((*x)->is_control()) {
                        _control_out = (*x);
-               } 
+               }
+
+               add_bundle ((*x)->bundle_for_inputs());
+               add_bundle ((*x)->bundle_for_outputs());
        }
 
        if (_control_out && IO::connecting_legal) {
@@ -2003,13 +2068,14 @@ Session::remove_route (shared_ptr<Route> route)
 
        find_current_end ();
        
+       // We need to disconnect the routes inputs and outputs 
+
+       route->disconnect_inputs (0);
+       route->disconnect_outputs (0);
+       
        update_latency_compensation (false, false);
        set_dirty();
 
-       // We need to disconnect the routes inputs and outputs 
-       route->disconnect_inputs(NULL);
-       route->disconnect_outputs(NULL);
-       
        /* get rid of it from the dead wood collection in the route list manager */
 
        /* XXX i think this is unsafe as it currently stands, but i am not sure. (pd, october 2nd, 2006) */
@@ -2742,8 +2808,6 @@ Session::source_by_id (const PBD::ID& id)
                source = i->second;
        }
 
-       /* XXX search MIDI or other searches here */
-       
        return source;
 }
 
@@ -2765,13 +2829,11 @@ Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn)
        return boost::shared_ptr<Source>();
 }
 
-string
-Session::peak_path_from_audio_path (string audio_path) const
+Glib::ustring
+Session::peak_path (Glib::ustring base) const
 {
        sys::path peakfile_path(_session_dir->peak_path());
-
-       peakfile_path /= basename_nosuffix (audio_path) + peakfile_suffix;
-
+       peakfile_path /= basename_nosuffix (base) + peakfile_suffix;
        return peakfile_path.to_string();
 }
 
@@ -3182,7 +3244,7 @@ Session::midi_path_from_name (string name)
 
        return spath;
 }
-
+       
 boost::shared_ptr<MidiSource>
 Session::create_midi_source_for_session (MidiDiskstream& ds)
 {
@@ -3380,27 +3442,13 @@ Session::remove_empty_sounds ()
        get_files_in_directory (_session_dir->sound_path(), audio_filenames);
        
        Glib::Mutex::Lock lm (source_lock);
-       
-       regex_t compiled_tape_track_pattern;
-       int err;
 
-       if ((err = regcomp (&compiled_tape_track_pattern, "/T[0-9][0-9][0-9][0-9]-", REG_EXTENDED|REG_NOSUB))) {
+       TapeFileMatcher tape_file_matcher;
 
-               char msg[256];
-               
-               regerror (err, &compiled_tape_track_pattern, msg, sizeof (msg));
-               
-               error << string_compose (_("Cannot compile tape track regexp for use (%1)"), msg) << endmsg;
-               return;
-       }
+       remove_if (audio_filenames.begin(), audio_filenames.end(),
+                       sigc::mem_fun (tape_file_matcher, &TapeFileMatcher::matches));
 
        for (vector<string>::iterator i = audio_filenames.begin(); i != audio_filenames.end(); ++i) {
-               
-               // never remove files that appear to be a tape track
-
-               if (regexec (&compiled_tape_track_pattern, i->c_str(), 0, 0, 0) == 0) {
-                       continue;
-               }
 
                sys::path audio_file_path (_session_dir->sound_path());
 
@@ -3408,10 +3456,16 @@ Session::remove_empty_sounds ()
                        
                if (AudioFileSource::is_empty (*this, audio_file_path.to_string())) {
 
-                       unlink (audio_file_path.to_string().c_str());
-                       
-                       string peak_path = peak_path_from_audio_path (audio_file_path.to_string());
-                       unlink (peak_path.c_str());
+                       try
+                       {
+                               sys::remove (audio_file_path);
+                               const string peakfile = peak_path (audio_file_path.to_string());
+                               sys::remove (peakfile);
+                       }
+                       catch (const sys::filesystem_error& err)
+                       {
+                               error << err.what() << endmsg; 
+                       }
                }
        }
 }
@@ -3616,7 +3670,7 @@ Session::available_capture_duration ()
 }
 
 void
-Session::add_bundle (ARDOUR::Bundle* bundle)
+Session::add_bundle (shared_ptr<Bundle> bundle)
 {
        {
                Glib::Mutex::Lock guard (bundle_lock);
@@ -3629,7 +3683,7 @@ Session::add_bundle (ARDOUR::Bundle* bundle)
 }
 
 void
-Session::remove_bundle (ARDOUR::Bundle* bundle)
+Session::remove_bundle (shared_ptr<Bundle> bundle)
 {
        bool removed = false;
 
@@ -3650,7 +3704,7 @@ Session::remove_bundle (ARDOUR::Bundle* bundle)
        set_dirty();
 }
 
-ARDOUR::Bundle *
+shared_ptr<Bundle>
 Session::bundle_by_name (string name) const
 {
        Glib::Mutex::Lock lm (bundle_lock);
@@ -3661,7 +3715,7 @@ Session::bundle_by_name (string name) const
                }
        }
 
-       return 0;
+       return boost::shared_ptr<Bundle> ();
 }
 
 void
@@ -4112,3 +4166,29 @@ Session::compute_initial_length ()
        return _engine.frame_rate() * 60 * 5;
 }
 
+void
+Session::sync_order_keys ()
+{
+       if (!Config->get_sync_all_route_ordering()) {
+               /* leave order keys as they are */
+               return;
+       }
+
+       boost::shared_ptr<RouteList> r = routes.reader ();
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               (*i)->sync_order_keys ();
+       }
+
+       Route::SyncOrderKeys (); // EMIT SIGNAL
+}
+
+void
+Session::foreach_bundle (sigc::slot<void, boost::shared_ptr<Bundle> > sl)
+{
+       Glib::Mutex::Lock lm (bundle_lock);
+       for (BundleList::iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
+               sl (*i);
+       }
+}
+