Fix periodic backup saves
[ardour.git] / libs / ardour / session.cc
index 2c00fbbd800829a80f2987ea72a583e3836b10f4..d61a53cd523dd4913fd16f03319d6852438a655f 100644 (file)
@@ -1,21 +1,31 @@
 /*
-    Copyright (C) 1999-2010 Paul Davis
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
+ * Copyright (C) 1999-2019 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2006-2007 Jesse Chappell <jesse@essej.net>
+ * Copyright (C) 2006-2009 Sampo Savolainen <v2@iki.fi>
+ * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
+ * Copyright (C) 2006-2016 Tim Mayberry <mojofunk@gmail.com>
+ * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
+ * Copyright (C) 2008-2009 Hans Baier <hansfbaier@googlemail.com>
+ * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013-2017 Nick Mainsbridge <mainsbridge@gmail.com>
+ * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
+ * Copyright (C) 2015 GZharun <grygoriiz@wavesglobal.com>
+ * Copyright (C) 2016-2018 Len Ovens <len@ovenwerks.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
 
 #include <stdint.h>
 
@@ -680,6 +690,14 @@ Session::destroy ()
 
        Port::PortDrop (); /* EMIT SIGNAL */
 
+       {
+               Glib::Threads::Mutex::Lock lm (controllables_lock);
+               for (Controllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
+                       (*i)->DropReferences (); /* EMIT SIGNAL */
+               }
+               controllables.clear ();
+       }
+
        /* clear history so that no references to objects are held any more */
 
        _history.clear ();
@@ -864,6 +882,10 @@ Session::destroy ()
 
        DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
 
+#ifndef NDEBUG
+       Controllable::dump_registry ();
+#endif
+
        BOOST_SHOW_POINTERS ();
 }
 
@@ -1030,7 +1052,7 @@ Session::setup_bundles ()
                if (np + 1 < outputs[DataType::AUDIO].size()) {
                        char buf[32];
                        snprintf (buf, sizeof(buf), _("out %" PRIu32 "+%" PRIu32), np + 1, np + 2);
-                        boost::shared_ptr<Bundle> c (new Bundle (buf, true));
+                       boost::shared_ptr<Bundle> c (new Bundle (buf, true));
                        c->add_channel (_("L"), DataType::AUDIO);
                        c->set_port (0, outputs[DataType::AUDIO][np]);
                        c->add_channel (_("R"), DataType::AUDIO);
@@ -1604,11 +1626,6 @@ Session::track_playlist_changed (boost::weak_ptr<Track> wp)
 bool
 Session::record_enabling_legal () const
 {
-       /* this used to be in here, but survey says.... we don't need to restrict it */
-       // if (record_status() == Recording) {
-       //      return false;
-       // }
-
        if (Config->get_all_safe()) {
                return false;
        }
@@ -1829,10 +1846,10 @@ Session::update_skips (Location* loc, bool consolidate)
 
        Locations::LocationList skips;
 
-        if (consolidate) {
-               PBD::Unwinder<bool> uw (_ignore_skips_updates, true);
-               consolidate_skips (loc);
-        }
+       if (consolidate) {
+               PBD::Unwinder<bool> uw (_ignore_skips_updates, true);
+               consolidate_skips (loc);
+       }
 
        sync_locations_to_skips ();
 
@@ -1842,41 +1859,41 @@ Session::update_skips (Location* loc, bool consolidate)
 void
 Session::consolidate_skips (Location* loc)
 {
-        Locations::LocationList all_locations = _locations->list ();
+       Locations::LocationList all_locations = _locations->list ();
 
-        for (Locations::LocationList::iterator l = all_locations.begin(); l != all_locations.end(); ) {
+       for (Locations::LocationList::iterator l = all_locations.begin(); l != all_locations.end(); ) {
 
-                if (!(*l)->is_skip ()) {
-                        ++l;
-                        continue;
-                }
+               if (!(*l)->is_skip ()) {
+                       ++l;
+                       continue;
+               }
 
-                /* don't test against self */
+               /* don't test against self */
 
-                if (*l == loc) {
-                        ++l;
-                        continue;
-                }
+               if (*l == loc) {
+                       ++l;
+                       continue;
+               }
 
-                switch (Evoral::coverage ((*l)->start(), (*l)->end(), loc->start(), loc->end())) {
-                case Evoral::OverlapInternal:
-                case Evoral::OverlapExternal:
-                case Evoral::OverlapStart:
-                case Evoral::OverlapEnd:
-                        /* adjust new location to cover existing one */
-                        loc->set_start (min (loc->start(), (*l)->start()));
-                        loc->set_end (max (loc->end(), (*l)->end()));
-                        /* we don't need this one any more */
-                        _locations->remove (*l);
-                        /* the location has been deleted, so remove reference to it in our local list */
-                        l = all_locations.erase (l);
-                        break;
+               switch (Evoral::coverage ((*l)->start(), (*l)->end(), loc->start(), loc->end())) {
+                       case Evoral::OverlapInternal:
+                       case Evoral::OverlapExternal:
+                       case Evoral::OverlapStart:
+                       case Evoral::OverlapEnd:
+                               /* adjust new location to cover existing one */
+                               loc->set_start (min (loc->start(), (*l)->start()));
+                               loc->set_end (max (loc->end(), (*l)->end()));
+                               /* we don't need this one any more */
+                               _locations->remove (*l);
+                               /* the location has been deleted, so remove reference to it in our local list */
+                               l = all_locations.erase (l);
+                               break;
 
-                case Evoral::OverlapNone:
-                        ++l;
-                        break;
-                }
-        }
+                       case Evoral::OverlapNone:
+                               ++l;
+                               break;
+               }
+       }
 }
 
 void
@@ -1910,52 +1927,52 @@ Session::_sync_locations_to_skips ()
 void
 Session::location_added (Location *location)
 {
-        if (location->is_auto_punch()) {
-                set_auto_punch_location (location);
-        }
+       if (location->is_auto_punch()) {
+               set_auto_punch_location (location);
+       }
 
-        if (location->is_auto_loop()) {
-                set_auto_loop_location (location);
-        }
+       if (location->is_auto_loop()) {
+               set_auto_loop_location (location);
+       }
 
-        if (location->is_session_range()) {
-                /* no need for any signal handling or event setting with the session range,
-                   because we keep a direct reference to it and use its start/end directly.
-                */
-                _session_range_location = location;
-        }
+       if (location->is_session_range()) {
+               /* no need for any signal handling or event setting with the session range,
+                        because we keep a direct reference to it and use its start/end directly.
+                        */
+               _session_range_location = location;
+       }
 
-        if (location->is_mark()) {
-                /* listen for per-location signals that require us to do any * global updates for marks */
+       if (location->is_mark()) {
+               /* listen for per-location signals that require us to do any * global updates for marks */
 
-                location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
-                location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
-                location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
-                location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+               location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+               location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+               location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+               location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
                location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
-        }
+       }
 
        if (location->is_range_marker()) {
-                /* listen for per-location signals that require us to do any * global updates for marks */
+               /* listen for per-location signals that require us to do any * global updates for marks */
 
-                location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
-                location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
-                location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
-                location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+               location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+               location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+               location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+               location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
                location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
-        }
+       }
 
-        if (location->is_skip()) {
-                /* listen for per-location signals that require us to update skip-locate events */
+       if (location->is_skip()) {
+               /* listen for per-location signals that require us to update skip-locate events */
 
-                location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
-                location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
-                location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
-                location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, false));
+               location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
+               location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
+               location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
+               location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, false));
                location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
 
-                update_skips (location, true);
-        }
+               update_skips (location, true);
+       }
 
        set_dirty ();
 }
@@ -1991,17 +2008,17 @@ Session::location_removed (Location *location)
 void
 Session::locations_changed ()
 {
-        _locations->apply (*this, &Session::_locations_changed);
+       _locations->apply (*this, &Session::_locations_changed);
 }
 
 void
 Session::_locations_changed (const Locations::LocationList& locations)
 {
-        /* There was some mass-change in the Locations object.
-
-           We might be re-adding a location here but it doesn't actually matter
-           for all the locations that the Session takes an interest in.
-        */
+       /* There was some mass-change in the Locations object.
+        * 
+        * We might be re-adding a location here but it doesn't actually matter
+        * for all the locations that the Session takes an interest in.
+        */
 
        {
                PBD::Unwinder<bool> protect_ignore_skip_updates (_ignore_skips_updates, true);
@@ -2071,10 +2088,6 @@ Session::disable_record (bool rt_context, bool force)
                }
 
                RecordStateChanged (); /* emit signal */
-
-               if (!rt_context) {
-                       remove_pending_capture_state ();
-               }
        }
 }
 
@@ -2250,7 +2263,7 @@ Session::set_block_size (pframes_t nframes)
 
 
 static void
-trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase)
+trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase, bool sends_only)
 {
        boost::shared_ptr<Route> r2;
 
@@ -2277,7 +2290,7 @@ trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase)
                   base as being fed by r2
                */
 
-               rbase->add_fed_by (r2, i->sends_only);
+               rbase->add_fed_by (r2, i->sends_only || sends_only);
 
                if (r2 != rbase) {
 
@@ -2293,7 +2306,7 @@ trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase)
                           all routes that feed r2
                        */
 
-                       trace_terminal (r2, rbase);
+                       trace_terminal (r2, rbase, i->sends_only || sends_only);
                }
 
        }
@@ -2408,7 +2421,7 @@ Session::resort_routes_using (boost::shared_ptr<RouteList> r)
                   or indirectly feeds them.
                */
                for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       trace_terminal (*i, *i);
+                       trace_terminal (*i, *i, false);
                }
 
                *r = *sorted_routes;
@@ -2610,7 +2623,7 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool s
                --how_many;
        }
 
-  failed:
+       failed:
        if (!new_routes.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
@@ -2714,7 +2727,7 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name
                --how_many;
        }
 
-  failure:
+       failure:
        if (!ret.empty()) {
                StateProtector sp (this);
                add_routes (ret, false, false, false, order);
@@ -2946,43 +2959,43 @@ Session::reconnect_existing_routes (bool withLock, bool reconnect_master, bool r
 void
 Session::reconnect_midi_scene_ports(bool inputs)
 {
-    if (inputs ) {
+       if (inputs ) {
 
-        boost::shared_ptr<MidiPort> scene_in_ptr = scene_in();
-        if (scene_in_ptr) {
-            scene_in_ptr->disconnect_all ();
+               boost::shared_ptr<MidiPort> scene_in_ptr = scene_in();
+               if (scene_in_ptr) {
+                       scene_in_ptr->disconnect_all ();
 
-            std::vector<EngineStateController::MidiPortState> midi_port_states;
-            EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
+                       std::vector<EngineStateController::MidiPortState> midi_port_states;
+                       EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
 
-            std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
+                       std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
 
-            for (; state_iter != midi_port_states.end(); ++state_iter) {
-                if (state_iter->active && state_iter->available && state_iter->scene_connected) {
-                    scene_in_ptr->connect (state_iter->name);
-                }
-            }
-        }
+                       for (; state_iter != midi_port_states.end(); ++state_iter) {
+                               if (state_iter->active && state_iter->available && state_iter->scene_connected) {
+                                       scene_in_ptr->connect (state_iter->name);
+                               }
+                       }
+               }
 
-    } else {
+       } else {
 
-        boost::shared_ptr<MidiPort> scene_out_ptr = scene_out();
+               boost::shared_ptr<MidiPort> scene_out_ptr = scene_out();
 
-        if (scene_out_ptr ) {
-            scene_out_ptr->disconnect_all ();
+               if (scene_out_ptr ) {
+                       scene_out_ptr->disconnect_all ();
 
-            std::vector<EngineStateController::MidiPortState> midi_port_states;
-            EngineStateController::instance()->get_physical_midi_output_states (midi_port_states);
+                       std::vector<EngineStateController::MidiPortState> midi_port_states;
+                       EngineStateController::instance()->get_physical_midi_output_states (midi_port_states);
 
-            std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
+                       std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
 
-            for (; state_iter != midi_port_states.end(); ++state_iter) {
-                if (state_iter->active && state_iter->available && state_iter->scene_connected) {
-                    scene_out_ptr->connect (state_iter->name);
-                }
-            }
-        }
-    }
+                       for (; state_iter != midi_port_states.end(); ++state_iter) {
+                               if (state_iter->active && state_iter->available && state_iter->scene_connected) {
+                                       scene_out_ptr->connect (state_iter->name);
+                               }
+                       }
+               }
+       }
 }
 
 void
@@ -3175,7 +3188,7 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r
                --how_many;
        }
 
-  failed:
+       failed:
        if (!new_routes.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
@@ -3262,7 +3275,7 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
                --how_many;
        }
 
-  failure:
+       failure:
        if (!ret.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
@@ -3427,6 +3440,14 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                                                (*i)->remove_property (X_("name"));
                                                (*i)->set_property ("bitslot", bitslot);
                                                (*i)->set_property ("name", name);
+                                               XMLNodeList io_kids = (*i)->children ();
+                                               for (XMLNodeList::iterator j = io_kids.begin(); j != io_kids.end(); ++j) {
+                                                       if ((*j)->name() != X_("IO")) {
+                                                               continue;
+                                                       }
+                                                       (*j)->remove_property (X_("name"));
+                                                       (*j)->set_property ("name", name);
+                                               }
                                        }
                                        else if (type && type->value() == X_("intreturn")) {
                                                (*i)->remove_property (X_("bitslot"));
@@ -3496,7 +3517,7 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                --how_many;
        }
 
-  out:
+       out:
        if (!ret.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
@@ -4152,7 +4173,7 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD
                        }
                        in_signal_flow = true;
                } else {
-                       DEBUG_TRACE (DEBUG::Solo, "\tno feed to\n");
+                       DEBUG_TRACE (DEBUG::Solo, string_compose("\tno feed to %1\n", (*i)->name()) );
                }
 
                if (!in_signal_flow) {
@@ -4774,6 +4795,7 @@ Session::destroy_sources (list<boost::shared_ptr<Source> > srcs)
 
                (*s)->mark_for_remove ();
                (*s)->drop_references ();
+               SourceRemoved(*s);
 
                s = srcs.erase (s);
        }
@@ -4808,6 +4830,25 @@ Session::remove_last_capture ()
        return 0;
 }
 
+void
+Session::get_last_capture_sources (std::list<boost::shared_ptr<Source> >& srcs)
+{
+       boost::shared_ptr<RouteList> rl = routes.reader ();
+       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+               if (!tr) {
+                       continue;
+               }
+
+               list<boost::shared_ptr<Source> >& l = tr->last_capture_sources();
+
+               if (!l.empty()) {
+                       srcs.insert (srcs.end(), l.begin(), l.end());
+                       l.clear ();
+               }
+       }
+}
+
 /* Source Management */
 
 void
@@ -4847,6 +4888,8 @@ Session::add_source (boost::shared_ptr<Source> source)
                }
 
                source->DropReferences.connect_same_thread (*this, boost::bind (&Session::remove_source, this, boost::weak_ptr<Source> (source)));
+
+               SourceAdded(source);
        }
 }
 
@@ -4869,6 +4912,7 @@ Session::remove_source (boost::weak_ptr<Source> src)
 
                if ((i = sources.find (source->id())) != sources.end()) {
                        sources.erase (i);
+                       SourceRemoved(source);
                }
        }
 
@@ -5359,6 +5403,17 @@ Session::create_midi_source_by_stealing_name (boost::shared_ptr<Track> track)
                        DataType::MIDI, *this, path, false, sample_rate()));
 }
 
+bool
+Session::playlist_is_active (boost::shared_ptr<Playlist> playlist)
+{
+       Glib::Threads::Mutex::Lock lm (_playlists->lock);
+       for (SessionPlaylists::List::iterator i = _playlists->playlists.begin(); i != _playlists->playlists.end(); i++) {
+               if ( (*i) == playlist ) {
+                       return true;
+               }
+       }
+       return false;
+}
 
 void
 Session::add_playlist (boost::shared_ptr<Playlist> playlist, bool unused)
@@ -6093,6 +6148,8 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
        string legal_playlist_name;
        string possible_path;
 
+       DataType data_type = track.data_type();
+
        if (end <= start) {
                error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"),
                                         end, start) << endmsg;
@@ -6102,7 +6159,11 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
        diskstream_channels = track.bounce_get_output_streams (diskstream_channels, endpoint,
                        include_endpoint, for_export, for_freeze);
 
-       if (diskstream_channels.n(track.data_type()) < 1) {
+       if (data_type == DataType::MIDI && endpoint && !for_export && !for_freeze && diskstream_channels.n(DataType::AUDIO) > 0) {
+               data_type = DataType::AUDIO;
+       }
+
+       if (diskstream_channels.n(data_type) < 1) {
                error << _("Cannot write a range with no data.") << endmsg;
                return result;
        }
@@ -6127,12 +6188,12 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
                goto out;
        }
 
-       legal_playlist_name = legalize_for_path (playlist->name());
+       legal_playlist_name = "(bounce)" + legalize_for_path (playlist->name());
 
-       for (uint32_t chan_n = 0; chan_n < diskstream_channels.n(track.data_type()); ++chan_n) {
+       for (uint32_t chan_n = 0; chan_n < diskstream_channels.n(data_type); ++chan_n) {
 
                string base_name = string_compose ("%1-%2-bounce", playlist->name(), chan_n);
-               string path = ((track.data_type() == DataType::AUDIO)
+               string path = ((data_type == DataType::AUDIO)
                               ? new_audio_source_path (legal_playlist_name, diskstream_channels.n_audio(), chan_n, false, true)
                               : new_midi_source_path (legal_playlist_name));
 
@@ -6141,7 +6202,7 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
                }
 
                try {
-                       source = SourceFactory::createWritable (track.data_type(), *this, path, false, sample_rate());
+                       source = SourceFactory::createWritable (data_type, *this, path, false, sample_rate());
                }
 
                catch (failed_constructor& err) {
@@ -6216,8 +6277,10 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
                                const MidiBuffer& buf = buffers.get_midi(0);
                                for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) {
                                        Evoral::Event<samplepos_t> ev = *i;
-                                       ev.set_time(ev.time() - position);
-                                       ms->append_event_samples(lock, ev, ms->timeline_position());
+                                       if (!endpoint || for_export) {
+                                               ev.set_time(ev.time() - position);
+                                       }
+                                       ms->append_event_samples(lock, ev, ms->natural_position());
                                }
                        }
                }
@@ -6271,14 +6334,15 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
                PropertyList plist;
 
                plist.add (Properties::start, 0);
-               plist.add (Properties::length, srcs.front()->length(srcs.front()->timeline_position()));
+               plist.add (Properties::whole_file, true);
+               plist.add (Properties::length, srcs.front()->length(srcs.front()->natural_position()));
                plist.add (Properties::name, region_name_from_path (srcs.front()->name(), true));
 
-               result = RegionFactory::create (srcs, plist);
+               result = RegionFactory::create (srcs, plist, true);
 
        }
 
-  out:
+       out:
        if (!result) {
                for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
                        (*src)->mark_for_remove ();
@@ -6414,6 +6478,11 @@ Session::nstripables (bool with_monitor) const
        return rv;
 }
 
+bool
+Session::plot_process_graph (std::string const& file_name) const {
+       return _process_graph ? _process_graph->plot (file_name) : false;
+}
+
 void
 Session::add_automation_list(AutomationList *al)
 {
@@ -6430,7 +6499,7 @@ Session::have_rec_enabled_track () const
 bool
 Session::have_rec_disabled_track () const
 {
-    return g_atomic_int_get (const_cast<gint*>(&_have_rec_disabled_track)) == 1;
+       return g_atomic_int_get (const_cast<gint*>(&_have_rec_disabled_track)) == 1;
 }
 
 /** Update the state of our rec-enabled tracks flag */