more loop/transport fixups; make visible PH track transport frame as an experiment...
[ardour.git] / libs / ardour / session.cc
index 29995c66769e1815b67700845fdb16c8ad55eda2..c2141e62e630f39a64a39c4ba18220c4a069aa39 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <glibmm/thread.h>
 #include <glibmm/miscutils.h>
+#include <glibmm/fileutils.h>
 
 #include <pbd/error.h>
 #include <glibmm/thread.h>
@@ -45,6 +46,7 @@
 #include <ardour/audioengine.h>
 #include <ardour/configuration.h>
 #include <ardour/session.h>
+#include <ardour/analyser.h>
 #include <ardour/audio_diskstream.h>
 #include <ardour/utils.h>
 #include <ardour/audioplaylist.h>
@@ -103,13 +105,21 @@ 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<void,std::string> Session::Dialog;
 sigc::signal<int> Session::AskAboutPendingState;
+sigc::signal<int,nframes_t,nframes_t> Session::AskAboutSampleRateMismatch;
 sigc::signal<void> Session::SendFeedback;
 
 sigc::signal<void> Session::SMPTEOffsetChanged;
 sigc::signal<void> Session::StartTimeChanged;
 sigc::signal<void> Session::EndTimeChanged;
 
+sigc::signal<void> Session::AutoBindingOn;
+sigc::signal<void> Session::AutoBindingOff;
+
+
+sigc::signal<void, std::string, std::string> Session::Exported;
+
 int
 Session::find_session (string str, string& path, string& snapshot, bool& isnew)
 {
@@ -266,15 +276,22 @@ Session::Session (AudioEngine &eng,
                  string mix_template)
 
        : _engine (eng),
+         mmc (0),
          _mmc_port (default_mmc_port),
          _mtc_port (default_mtc_port),
          _midi_port (default_midi_port),
          pending_events (2048),
+         state_tree (0),
+         _send_smpte_update (false),
+         midi_thread (pthread_t (0)),
          midi_requests (128), // the size of this should match the midi request pool size
          diskstreams (new DiskstreamList),
          routes (new RouteList),
          auditioner ((Auditioner*) 0),
+         _total_free_4k_blocks (0),
          _click_io ((IO*) 0),
+         click_data (0),
+         click_emphasis_data (0),
          main_outs (0)
 {
        bool new_session;
@@ -283,14 +300,15 @@ Session::Session (AudioEngine &eng,
                throw failed_constructor();
        }
 
-       cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl;
+       info << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl;
 
        n_physical_audio_outputs = _engine.n_physical_audio_outputs();
        n_physical_audio_inputs =  _engine.n_physical_audio_inputs();
 
        first_stage_init (fullpath, snapshot_name);
        
-       new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+       new_session = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+
        if (new_session) {
                if (create (new_session, mix_template, compute_initial_length())) {
                        destroy ();
@@ -328,13 +346,22 @@ Session::Session (AudioEngine &eng,
                  nframes_t initial_length)
 
        : _engine (eng),
+         mmc (0),
          _mmc_port (default_mmc_port),
          _mtc_port (default_mtc_port),
          _midi_port (default_midi_port),
          pending_events (2048),
+         state_tree (0),
+         _send_smpte_update (false),
+         midi_thread (pthread_t (0)),
          midi_requests (16),
          diskstreams (new DiskstreamList),
          routes (new RouteList),
+         auditioner ((Auditioner *) 0),
+         _total_free_4k_blocks (0),
+         _click_io ((IO *) 0),
+         click_data (0),
+         click_emphasis_data (0),
          main_outs (0)
 
 {
@@ -344,7 +371,7 @@ Session::Session (AudioEngine &eng,
                throw failed_constructor();
        }
 
-       cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl;
+       info << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl;
 
        n_physical_audio_outputs = _engine.n_physical_audio_outputs();
        n_physical_audio_inputs = _engine.n_physical_audio_inputs();
@@ -442,19 +469,16 @@ Session::destroy ()
        _history.clear ();
 
        /* clear state tree so that no references to objects are held any more */
-       
-       if (state_tree) {
-               delete state_tree;
-       }
+       delete state_tree;
 
        terminate_butler_thread ();
        terminate_midi_thread ();
        
-       if (click_data && click_data != default_click) {
+       if (click_data != default_click) {
                delete [] click_data;
        }
 
-       if (click_emphasis_data && click_emphasis_data != default_click_emphasis) {
+       if (click_emphasis_data != default_click_emphasis) {
                delete [] click_emphasis_data;
        }
 
@@ -573,14 +597,13 @@ Session::destroy ()
 
                tmp = i;
                ++tmp;
-
+               
                i->second->drop_references ();
-
+               
                i = tmp;
        }
-
        audio_sources.clear ();
-
+       
 #ifdef TRACK_DESTRUCTION
        cerr << "delete mix groups\n";
 #endif /* TRACK_DESTRUCTION */
@@ -623,19 +646,9 @@ Session::destroy ()
                i = tmp;
        }
 
-       if (butler_mixdown_buffer) {
-               delete [] butler_mixdown_buffer;
-       }
-
-       if (butler_gain_buffer) {
-               delete [] butler_gain_buffer;
-       }
-
        Crossfade::set_buffer_size (0);
 
-       if (mmc) {
-               delete mmc;
-       }
+       delete mmc;
 }
 
 void
@@ -659,13 +672,15 @@ Session::set_worst_io_latencies ()
 void
 Session::when_engine_running ()
 {
-       string first_physical_output;
-
        /* we don't want to run execute this again */
 
+       BootMessage (_("Set block size and sample rate"));
+
        set_block_size (_engine.frames_per_cycle());
        set_frame_rate (_engine.frame_rate());
 
+       BootMessage (_("Using configuration"));
+
        Config->map_parameters (mem_fun (*this, &Session::config_changed));
 
        /* every time we reconnect, recompute worst case output latencies */
@@ -703,17 +718,21 @@ Session::when_engine_running ()
 
                } else {
                        
-                       /* default state for Click */
-
-                       first_physical_output = _engine.get_nth_physical_audio_output (0);
+                       /* default state for Click: dual-mono to first 2 physical outputs */
                        
-                       if (first_physical_output.length()) {
-                               if (_click_io->add_output_port (first_physical_output, this)) {
-                                       // relax, even though its an error
-                               } else {
-                                       _clicking = Config->get_clicking ();
+                       for (int physport = 0; physport < 2; ++physport) {
+                               string physical_output = _engine.get_nth_physical_audio_output (physport);
+                       
+                               if (physical_output.length()) {
+                                       if (_click_io->add_output_port (physical_output, this)) {
+                                               // relax, even though its an error
+                                       } 
                                }
                        }
+
+                       if (_click_io->n_outputs() > 0) {
+                               _clicking = Config->get_clicking ();
+                       }
                }
        }
 
@@ -721,6 +740,8 @@ Session::when_engine_running ()
                error << _("cannot setup Click I/O") << endmsg;
        }
 
+       BootMessage (_("Compute I/O Latencies"));
+
        set_worst_io_latencies ();
 
        if (_clicking) {
@@ -731,6 +752,8 @@ Session::when_engine_running ()
           to the physical outputs currently available
        */
 
+       BootMessage (_("Set up standard connections"));
+
        /* ONE: MONO */
 
        for (uint32_t np = 0; np < n_physical_audio_outputs; ++np) {
@@ -835,11 +858,15 @@ Session::when_engine_running ()
                }
                add_connection (c);
        } 
+       
+       BootMessage (_("Setup signal flow and plugins"));
 
        hookup_io ();
 
        /* catch up on send+insert cnts */
 
+       BootMessage (_("Catch up with send/insert state"));
+
        insert_cnt = 0;
        
        for (list<PortInsert*>::iterator i = _port_inserts.begin(); i != _port_inserts.end(); ++i) {
@@ -867,14 +894,17 @@ Session::when_engine_running ()
        
        _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty));
 
-
        /* hook us up to the engine */
 
+       BootMessage (_("Connect to engine"));
+
        _engine.set_session (this);
 
 #ifdef HAVE_LIBLO
        /* and to OSC */
 
+       BootMessage (_("OSC startup"));
+
        osc->set_session (*this);
 #endif
     
@@ -1078,9 +1108,9 @@ Session::auto_loop_changed (Location* location)
 
        if (transport_rolling() && play_loop) {
 
-               //if (_transport_frame < location->start() || _transport_frame > location->end()) {
+               // if (_transport_frame > location->end()) {
 
-               if (_transport_frame > location->end()) {
+               if (_transport_frame < location->start() || _transport_frame > location->end()) {
                        // relocate to beginning of loop
                        clear_events (Event::LocateRoll);
                        
@@ -1103,7 +1133,6 @@ Session::auto_loop_changed (Location* location)
        }       
 
        last_loopend = location->end();
-       
 }
 
 void
@@ -1141,6 +1170,10 @@ Session::set_auto_punch_location (Location* location)
        auto_punch_changed_connection = location->changed.connect (mem_fun (this, &Session::auto_punch_changed));
 
        location->set_auto_punch (true, this);
+
+
+       auto_punch_changed (location);
+
        auto_punch_location_changed (location);
 }
 
@@ -1180,6 +1213,13 @@ Session::set_auto_loop_location (Location* location)
        auto_loop_changed_connection = location->changed.connect (mem_fun (this, &Session::auto_loop_changed));
 
        location->set_auto_loop (true, this);
+
+       /* take care of our stuff first */
+
+       auto_loop_changed (location);
+
+       /* now tell everyone else */
+
        auto_loop_location_changed (location);
 }
 
@@ -1216,6 +1256,12 @@ Session::handle_locations_changed (Locations::LocationList& locations)
                        set_loop = true;
                }
                
+               if (location->is_start()) {
+                       start_location = location;
+               }
+               if (location->is_end()) {
+                       end_location = location;
+               }
        }
 
        if (!set_loop) {
@@ -1292,11 +1338,11 @@ Session::step_back_from_record ()
        if (g_atomic_int_get (&_record_status) == Recording) {
                g_atomic_int_set (&_record_status, Enabled);
 
-               if (Config->get_monitoring_model() == HardwareMonitoring) {
+               if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) {
                        boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
                        
                        for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-                               if (Config->get_auto_input() && (*i)->record_enabled ()) {
+                               if ((*i)->record_enabled ()) {
                                        //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
                                        (*i)->monitor_input (false);   
                                }
@@ -1335,6 +1381,10 @@ Session::audible_frame () const
        nframes_t offset;
        nframes_t tf;
 
+       if (_transport_speed == 0.0f && non_realtime_work_pending()) {
+               return last_stop_frame;
+       }
+
        /* the first of these two possible settings for "offset"
           mean that the audible frame is stationary until 
           audio emerges from the latency compensation
@@ -1363,24 +1413,43 @@ Session::audible_frame () const
        } else {
                tf = _transport_frame;
        }
-
-       if (_transport_speed == 0) {
-               return tf;
-       }
-
-       if (tf < offset) {
-               return 0;
-       }
-
+       
        ret = tf;
 
        if (!non_realtime_work_pending()) {
 
                /* MOVING */
 
-               /* take latency into account */
-               
-               ret -= offset;
+               /* check to see if we have passed the first guaranteed
+                  audible frame past our last stopping position. if not,
+                  the return that last stopping point because in terms
+                  of audible frames, we have not moved yet.
+               */
+
+               if (_transport_speed > 0.0f) {
+
+                       if (!play_loop || !have_looped) {
+                               if (tf < last_stop_frame + offset) {
+                                       return last_stop_frame;
+                                       
+                               }
+                       } 
+                       
+
+                       /* forwards */
+                       ret -= offset;
+
+               } else if (_transport_speed < 0.0f) {
+
+                       /* XXX wot? no backward looping? */
+
+                       if (tf > last_stop_frame - offset) {
+                               return last_stop_frame;
+                       } else {
+                               /* backwards */
+                               ret += offset;
+                       }
+               }
        }
 
        return ret;
@@ -1799,7 +1868,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
 
                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;
+                       error << pfe.what() << endmsg;
 
                        if (track) {
                                /* we need to get rid of this, since the track failed to be created */
@@ -1900,20 +1969,20 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_
                                      << endmsg;
                                goto failure;
                        }
-                       
+                       /*
                        for (uint32_t x = 0; n_physical_audio_inputs && x < bus->n_inputs(); ++x) {
-                               
+                                       
                                port = "";
-
+                               
                                if (Config->get_input_auto_connect() & AutoConnectPhysical) {
-                                               port = physinputs[((n+x)%n_physical_audio_inputs)];
+                                       port = physinputs[((n+x)%n_physical_audio_inputs)];
                                } 
                                
                                if (port.length() && bus->connect_input (bus->input (x), port, this)) {
                                        break;
                                }
                        }
-                       
+                       */
                        for (uint32_t x = 0; n_physical_audio_outputs && x < bus->n_outputs(); ++x) {
                                
                                port = "";
@@ -1944,7 +2013,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_
                }
 
                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;
+                       error << pfe.what() << endmsg;
                        goto failure;
                }
 
@@ -1961,6 +2030,114 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_
 
 }
 
+Session::RouteList
+Session::new_route_from_template (uint32_t how_many, const std::string& template_path)
+{
+       char name[32];
+       RouteList ret;
+       uint32_t control_id;
+       XMLTree tree;
+       uint32_t number = 1;
+
+       if (!tree.read (template_path.c_str())) {
+               return ret;
+       }
+
+       XMLNode* node = tree.root();
+
+       control_id = ntracks() + nbusses() + 1;
+
+       while (how_many) {
+
+               XMLNode node_copy (*node); // make a copy so we can change the name if we need to
+         
+               std::string node_name = IO::name_from_state (*node_copy.children().front());
+
+               /* generate a new name by adding a number to the end of the template name */
+               
+               do {
+                       snprintf (name, sizeof (name), "%s %" PRIu32, node_name.c_str(), number);
+                       
+                       number++;
+                       
+                       if (route_by_name (name) == 0) {
+                               break;
+                       }
+                       
+               } while (number < UINT_MAX);
+               
+               if (number == UINT_MAX) {
+                       fatal << _("Session: UINT_MAX routes? impossible!") << endmsg;
+                               /*NOTREACHED*/
+               }
+               
+               IO::set_name_in_state (*node_copy.children().front(), name);
+
+               Track::zero_diskstream_id_in_xml (node_copy);
+               
+               try {
+                       shared_ptr<Route> route (XMLRouteFactory (node_copy));
+           
+                       if (route == 0) {
+                               error << _("Session: cannot create track/bus from template description") << endmsg;
+                               goto out;
+                       }
+
+                       if (boost::dynamic_pointer_cast<Track>(route)) {
+                               /* force input/output change signals so that the new diskstream
+                                  picks up the configuration of the route. During session
+                                  loading this normally happens in a different way.
+                               */
+                               route->input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), this);
+                               route->output_changed (IOChange (ConfigurationChanged|ConnectionsChanged), this);
+                       }
+
+                       route->set_remote_control_id (control_id);
+                       ++control_id;
+           
+                       ret.push_back (route);
+               }
+         
+               catch (failed_constructor &err) {
+                       error << _("Session: could not create new route from template") << endmsg;
+                       goto out;
+               }
+         
+               catch (AudioEngine::PortRegistrationFailure& pfe) {
+                       error << pfe.what() << endmsg;
+                       goto out;
+               }
+         
+               --how_many;
+       }
+
+  out:
+       if (!ret.empty()) {
+               add_routes (ret, true);
+       }
+
+       return ret;
+}
+
+boost::shared_ptr<Route>
+Session::new_video_track (string name)
+{
+       uint32_t control_id = ntracks() + nbusses() + 1;
+       shared_ptr<Route> new_route (
+               new Route ( *this, name, -1, -1, -1, -1, Route::Flag(0), ARDOUR::DataType::NIL));
+       new_route->set_remote_control_id (control_id);
+
+       RouteList rl;
+       rl.push_back (new_route);
+        {
+               RCUWriter<RouteList> writer (routes);
+               shared_ptr<RouteList> r = writer.get_copy ();
+                r->insert (r->end(), rl.begin(), rl.end());
+               resort_routes_using (r);
+        }
+       return new_route;
+}
+
 void
 Session::add_routes (RouteList& new_routes, bool save)
 {
@@ -2110,6 +2287,8 @@ Session::remove_route (shared_ptr<Route> route)
 
        route->drop_references ();
 
+       sync_order_keys (N_("session"));
+
        /* save the new state of the world */
 
        if (save_state (_current_snapshot_name)) {
@@ -2152,7 +2331,7 @@ Session::route_solo_changed (void* src, boost::weak_ptr<Route> wpr)
                        
                        /* don't mess with busses */
                        
-                       if (dynamic_cast<AudioTrack*>((*i).get()) == 0) {
+                       if (boost::dynamic_pointer_cast<AudioTrack>(*i) == 0) {
                                continue;
                        }
                        
@@ -2160,7 +2339,7 @@ Session::route_solo_changed (void* src, boost::weak_ptr<Route> wpr)
                        
                        /* don't mess with tracks */
                        
-                       if (dynamic_cast<AudioTrack*>((*i).get()) != 0) {
+                       if (boost::dynamic_pointer_cast<AudioTrack>(*i) != 0) {
                                continue;
                        }
                }
@@ -2234,8 +2413,6 @@ Session::update_route_solo_state ()
        bool is_track = false;
        bool signal = false;
 
-       /* caller must hold RouteLock */
-
        /* this is where we actually implement solo by changing
           the solo mute setting of each track.
        */
@@ -2245,7 +2422,7 @@ Session::update_route_solo_state ()
         for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((*i)->soloed()) {
                        mute = true;
-                       if (dynamic_cast<AudioTrack*>((*i).get())) {
+                       if (boost::dynamic_pointer_cast<AudioTrack>(*i)) {
                                is_track = true;
                        }
                        break;
@@ -2290,7 +2467,7 @@ Session::modify_solo_mute (bool is_track, bool mute)
                        
                        /* only alter track solo mute */
                        
-                       if (dynamic_cast<AudioTrack*>((*i).get())) {
+                       if (boost::dynamic_pointer_cast<AudioTrack>(*i)) {
                                if ((*i)->soloed()) {
                                        (*i)->set_solo_mute (!mute);
                                } else {
@@ -2301,8 +2478,8 @@ Session::modify_solo_mute (bool is_track, bool mute)
                } else {
 
                        /* only alter bus solo mute */
-
-                       if (!dynamic_cast<AudioTrack*>((*i).get())) {
+                       
+                       if (!boost::dynamic_pointer_cast<AudioTrack>(*i)) {
 
                                if ((*i)->soloed()) {
 
@@ -2336,7 +2513,24 @@ Session::catch_up_on_solo ()
        */
        update_route_solo_state();
 }      
-               
+
+void
+Session::catch_up_on_solo_mute_override ()
+{
+       if (Config->get_solo_model() != InverseMute) {
+               return;
+       }
+
+       /* this is called whenever the param solo-mute-override is
+          changed.
+       */
+       shared_ptr<RouteList> r = routes.reader ();
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               (*i)->catch_up_on_solo_mute_override ();
+       }
+}      
+
 shared_ptr<Route>
 Session::route_by_name (string name)
 {
@@ -2404,6 +2598,8 @@ Session::get_maximum_extent () const
        boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
 
        for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) {
+               if ((*i)->destructive())  //ignore tape tracks when getting max extents
+                       continue;
                boost::shared_ptr<Playlist> pl = (*i)->playlist();
                if ((me = pl->get_maximum_extent()) > max) {
                        max = me;
@@ -2495,7 +2691,7 @@ Session::new_region_name (string old)
 }
 
 int
-Session::region_name (string& result, string base, bool newlevel) const
+Session::region_name (string& result, string base, bool newlevel)
 {
        char buf[16];
        string subbase;
@@ -2505,15 +2701,11 @@ Session::region_name (string& result, string base, bool newlevel) const
                Glib::Mutex::Lock lm (region_lock);
 
                snprintf (buf, sizeof (buf), "%d", (int)audio_regions.size() + 1);
-
-               
                result = "region.";
                result += buf;
 
        } else {
 
-               /* XXX this is going to be slow. optimize me later */
-               
                if (newlevel) {
                        subbase = base;
                } else {
@@ -2526,43 +2718,38 @@ Session::region_name (string& result, string base, bool newlevel) const
                        subbase = base.substr (0, pos);
 
                }
-
-               bool name_taken = true;
                
                {
                        Glib::Mutex::Lock lm (region_lock);
-                       
-                       for (int n = 1; n < 5000; ++n) {
-                               
-                               result = subbase;
-                               snprintf (buf, sizeof (buf), ".%d", n);
+
+                       map<string,uint32_t>::iterator x;
+
+                       result = subbase;
+
+                       if ((x = region_name_map.find (subbase)) == region_name_map.end()) {
+                               result += ".1";
+                               region_name_map[subbase] = 1;
+                       } else {
+                               x->second++;
+                               snprintf (buf, sizeof (buf), ".%d", x->second);
                                result += buf;
-                               
-                               name_taken = false;
-                               
-                               for (AudioRegionList::const_iterator i = audio_regions.begin(); i != audio_regions.end(); ++i) {
-                                       if (i->second->name() == result) {
-                                               name_taken = true;
-                                               break;
-                                       }
-                               }
-                               
-                               if (!name_taken) {
-                                       break;
-                               }
                        }
                }
-                       
-               if (name_taken) {
-                       fatal << string_compose(_("too many regions with names like %1"), base) << endmsg;
-                       /*NOTREACHED*/
-               }
        }
+
        return 0;
 }      
 
 void
 Session::add_region (boost::shared_ptr<Region> region)
+{
+       vector<boost::shared_ptr<Region> > v;
+       v.push_back (region);
+       add_regions (v);
+}
+               
+void
+Session::add_regions (vector<boost::shared_ptr<Region> >& new_regions)
 {
        boost::shared_ptr<AudioRegion> ar;
        boost::shared_ptr<AudioRegion> oar;
@@ -2571,58 +2758,110 @@ Session::add_region (boost::shared_ptr<Region> region)
        { 
                Glib::Mutex::Lock lm (region_lock);
 
-               if (region == 0) {
-                       error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg;
-               } else if ((ar = boost::dynamic_pointer_cast<AudioRegion> (region)) != 0) {
-
-                       AudioRegionList::iterator x;
-
-                       for (x = audio_regions.begin(); x != audio_regions.end(); ++x) {
+               for (vector<boost::shared_ptr<Region> >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) {
+               
+                       boost::shared_ptr<Region> region = *ii;
+                       
+                       if (region == 0) {
 
-                               oar = boost::dynamic_pointer_cast<AudioRegion> (x->second);
+                               error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg;
 
-                               if (ar->region_list_equivalent (oar)) {
-                                       break;
+                       } else if ((ar = boost::dynamic_pointer_cast<AudioRegion> (region)) != 0) {
+                               
+                               AudioRegionList::iterator x;
+                               
+                               for (x = audio_regions.begin(); x != audio_regions.end(); ++x) {
+                                       
+                                       oar = boost::dynamic_pointer_cast<AudioRegion> (x->second);
+                                       
+                                       if (ar->region_list_equivalent (oar)) {
+                                               break;
+                                       }
                                }
+                               
+                               if (x == audio_regions.end()) {
+                                       
+                                       pair<AudioRegionList::key_type,AudioRegionList::mapped_type> entry;
+                                       
+                                       entry.first = region->id();
+                                       entry.second = ar;
+                                       
+                                       pair<AudioRegionList::iterator,bool> x = audio_regions.insert (entry);
+                                       
+                                       if (!x.second) {
+                                               return;
+                                       }
+                                       
+                                       added = true;
+                               } 
+
+                       } else {
+                               
+                               fatal << _("programming error: ")
+                                     << X_("unknown region type passed to Session::add_region()")
+                                     << endmsg;
+                               /*NOTREACHED*/
+                               
                        }
+               }
+       }
+
+       /* mark dirty because something has changed even if we didn't
+          add the region to the region list.
+       */
+       
+       set_dirty ();
+       
+       if (added) {
 
-                       if (x == audio_regions.end()) {
+               vector<boost::weak_ptr<AudioRegion> > v;
+               boost::shared_ptr<AudioRegion> first_ar;
 
-                               pair<AudioRegionList::key_type,AudioRegionList::mapped_type> entry;
+               for (vector<boost::shared_ptr<Region> >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) {
 
-                               entry.first = region->id();
-                               entry.second = ar;
+                       boost::shared_ptr<Region> region = *ii;
+                       boost::shared_ptr<AudioRegion> ar;
 
-                               pair<AudioRegionList::iterator,bool> x = audio_regions.insert (entry);
+                       if (region == 0) {
 
-                               
-                               if (!x.second) {
-                                       return;
-                               }
+                               error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg;
 
-                               added = true;
-                       } 
+                       } else if ((ar = boost::dynamic_pointer_cast<AudioRegion> (region)) != 0) {
+                               v.push_back (ar);
 
-               } else {
+                               if (!first_ar) {
+                                       first_ar = ar;
+                               }
+                       }
 
-                       fatal << _("programming error: ")
-                             << X_("unknown region type passed to Session::add_region()")
-                             << endmsg;
-                       /*NOTREACHED*/
+                       region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr<Region>(region)));
+                       region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr<Region>(region)));
+
+                       update_region_name_map (region);
+               }
 
+               if (!v.empty()) {
+                       AudioRegionsAdded (v); /* EMIT SIGNAL */
                }
        }
+}
 
-       /* mark dirty because something has changed even if we didn't
-          add the region to the region list.
-       */
-       
-       set_dirty();
+void
+Session::update_region_name_map (boost::shared_ptr<Region> region)
+{
+       string::size_type last_period = region->name().find_last_of ('.');
        
-       if (added) {
-               region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr<Region>(region)));
-               region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr<Region>(region)));
-               AudioRegionAdded (ar); /* EMIT SIGNAL */
+       if (last_period != string::npos && last_period < region->name().length() - 1) {
+               
+               string base = region->name().substr (0, last_period);
+               string number = region->name().substr (last_period+1);
+               map<string,uint32_t>::iterator x;
+               
+               /* note that if there is no number, we get zero from atoi,
+                  which is just fine
+               */
+               
+               region_name_map[base] = atoi (number);
        }
 }
 
@@ -2639,6 +2878,10 @@ Session::region_changed (Change what_changed, boost::weak_ptr<Region> weak_regio
                /* relay hidden changes */
                RegionHiddenChange (region);
        }
+
+       if (what_changed & NameChanged) {
+               update_region_name_map (region);
+       }
 }
 
 void
@@ -2817,6 +3060,9 @@ Session::add_source (boost::shared_ptr<Source> source)
                        set_dirty();
                }
 
+               if (Config->get_auto_analyse_audio()) {
+                       Analyser::queue_source_for_analysis (source, false);
+               }
        } 
 }
 
@@ -2884,13 +3130,7 @@ Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn)
 Glib::ustring
 Session::peak_path (Glib::ustring base) const
 {
-       Glib::ustring res;
-       
-       res = peak_dir ();
-       res += base;
-       res += ".peak";
-
-       return res;
+       return Glib::build_filename(peak_dir (), base + ".peak");
 }
 
 string
@@ -3353,7 +3593,7 @@ void
 Session::set_all_solo (bool yn)
 {
        shared_ptr<RouteList> r = routes.reader ();
-       
+
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if (!(*i)->hidden()) {
                        (*i)->set_solo (yn, this);
@@ -3604,18 +3844,36 @@ void
 Session::tempo_map_changed (Change ignored)
 {
        clear_clicks ();
+       
+       for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+               (*i)->update_after_tempo_map_change ();
+       }
+
+       for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+               (*i)->update_after_tempo_map_change ();
+       }
+
        set_dirty ();
 }
 
 void
 Session::ensure_passthru_buffers (uint32_t howmany)
 {
+       if (current_block_size == 0) {
+               return;
+       }
+
        while (howmany > _passthru_buffers.size()) {
                Sample *p;
 #ifdef NO_POSIX_MEMALIGN
                p =  (Sample *) malloc(current_block_size * sizeof(Sample));
 #else
-               posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample));
+               if (posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample)) != 0) {
+                       fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"),
+                                                current_block_size, sizeof (Sample), strerror (errno))
+                             << endmsg;
+                       /*NOTREACHED*/
+               }
 #endif                 
                _passthru_buffers.push_back (p);
 
@@ -3624,7 +3882,12 @@ Session::ensure_passthru_buffers (uint32_t howmany)
 #ifdef NO_POSIX_MEMALIGN
                p =  (Sample *) malloc(current_block_size * sizeof(Sample));
 #else
-               posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * 4);
+               if (posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample)) != 0) {
+                       fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"),
+                                                current_block_size, sizeof (Sample), strerror (errno))
+                             << endmsg;
+                       /*NOTREACHED*/
+               }
 #endif                 
                memset (p, 0, sizeof (Sample) * current_block_size);
                _silent_buffers.push_back (p);
@@ -3837,11 +4100,11 @@ Session::freeze (InterThreadInfo& itt)
        return 0;
 }
 
-int
-Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t len,     
-                              bool overwrite, vector<boost::shared_ptr<AudioSource> >& srcs, InterThreadInfo& itt)
+boost::shared_ptr<Region>
+Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t end,     
+                              bool overwrite, vector<boost::shared_ptr<AudioSource> >& srcs, InterThreadInfo& itt, bool enable_processing)
 {
-       int ret = -1;
+       boost::shared_ptr<Region> result;
        boost::shared_ptr<Playlist> playlist;
        boost::shared_ptr<AudioFileSource> fsource;
        uint32_t x;
@@ -3851,12 +4114,21 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le
        nframes_t position;
        nframes_t this_chunk;
        nframes_t to_do;
+       nframes_t len = end - start;
        vector<Sample*> buffers;
 
+       if (end <= start) {
+               error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"),
+                                        end, start) << endmsg;
+               return result;
+       }
+
        // any bigger than this seems to cause stack overflows in called functions
        const nframes_t chunk_size = (128 * 1024)/4;
 
-       g_atomic_int_set (&processing_prohibited, 1);
+       // block all process callback handling
+
+       block_processing ();
        
        /* call tree *MUST* hold route_lock */
        
@@ -3925,7 +4197,7 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le
                
                this_chunk = min (to_do, chunk_size);
                
-               if (track.export_stuff (buffers, nchans, start, this_chunk)) {
+               if (track.export_stuff (buffers, nchans, start, this_chunk, enable_processing)) {
                        goto out;
                }
 
@@ -3965,21 +4237,19 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le
                
                /* construct a region to represent the bounced material */
 
-               boost::shared_ptr<Region> aregion = RegionFactory::create (srcs, 0, srcs.front()->length(), 
-                                                                          region_name_from_path (srcs.front()->name(), true));
-
-               ret = 0;
+               result = RegionFactory::create (srcs, 0, srcs.front()->length(), 
+                                               region_name_from_path (srcs.front()->name(), true));
        }
                
   out:
-       if (ret) {
+       if (!result) {
                for (vector<boost::shared_ptr<AudioSource> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
                        boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
 
                        if (afs) {
                                afs->mark_for_remove ();
                        }
-
+                       
                        (*src)->drop_references ();
                }
 
@@ -3990,22 +4260,50 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le
        }
 
        for (vector<Sample*>::iterator i = buffers.begin(); i != buffers.end(); ++i) {
-               free(*i);
+               free (*i);
        }
 
-       g_atomic_int_set (&processing_prohibited, 0);
+       unblock_processing ();
 
        itt.done = true;
 
-       return ret;
+       return result;
 }
 
 vector<Sample*>&
 Session::get_silent_buffers (uint32_t howmany)
 {
+       if (howmany > _silent_buffers.size()) {
+
+               error << string_compose (_("Programming error: get_silent_buffers() called for %1 buffers but only %2 exist"),
+                                        howmany, _silent_buffers.size()) << endmsg;
+
+               if (howmany > 1000) {
+                       cerr << "ABSURD: more than 1000 silent buffers requested!\n";
+                       abort ();
+               }
+               
+               while (howmany > _silent_buffers.size()) {
+                       Sample *p = 0;
+                       
+#ifdef NO_POSIX_MEMALIGN
+                       p =  (Sample *) malloc(current_block_size * sizeof(Sample));
+#else
+                       if (posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample)) != 0) {
+                               fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"),
+                                                        current_block_size, sizeof (Sample), strerror (errno))
+                                     << endmsg;
+                               /*NOTREACHED*/
+                       }
+#endif                 
+                       _silent_buffers.push_back (p);
+               }
+       }
+
        for (uint32_t i = 0; i < howmany; ++i) {
                memset (_silent_buffers[i], 0, sizeof (Sample) * current_block_size);
        }
+
        return _silent_buffers;
 }
 
@@ -4052,7 +4350,7 @@ Session::compute_initial_length ()
 }
 
 void
-Session::sync_order_keys ()
+Session::sync_order_keys (const char* base)
 {
        if (!Config->get_sync_all_route_ordering()) {
                /* leave order keys as they are */
@@ -4062,8 +4360,8 @@ Session::sync_order_keys ()
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               (*i)->sync_order_keys ();
+               (*i)->sync_order_keys (base);
        }
 
-       Route::SyncOrderKeys (); // EMIT SIGNAL
+       Route::SyncOrderKeys (base); // EMIT SIGNAL
 }