fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / session.cc
index 55cd1f9ae829df570ff3704c46e15c92344140bc..61bfce14b6bfc995f0203b44e42bdf17652e60d8 100644 (file)
@@ -90,9 +90,9 @@
 #include "ardour/recent_sessions.h"
 #include "ardour/region.h"
 #include "ardour/region_factory.h"
+#include "ardour/revision.h"
 #include "ardour/route_graph.h"
 #include "ardour/route_group.h"
-#include "ardour/route_sorters.h"
 #include "ardour/send.h"
 #include "ardour/session.h"
 #include "ardour/session_directory.h"
 
 #include "LuaBridge/LuaBridge.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 #include <glibmm/checksum.h>
 
@@ -178,6 +178,7 @@ Session::Session (AudioEngine &eng,
        , _record_status (Disabled)
        , _transport_frame (0)
        , _session_range_location (0)
+       , _session_range_end_is_free (true)
        , _slave (0)
        , _silent (false)
        , _transport_speed (0)
@@ -212,7 +213,9 @@ Session::Session (AudioEngine &eng,
        , post_export_position (0)
        , _exporting (false)
        , _export_rolling (false)
+       , _realtime_export (false)
        , _export_preroll (0)
+       , _export_latency (0)
        , _pre_export_mmc_enabled (false)
        , _name (snapshot_name)
        , _is_new (true)
@@ -237,7 +240,7 @@ Session::Session (AudioEngine &eng,
        , pending_locate_flush (false)
        , pending_abort (false)
        , pending_auto_loop (false)
-       , _mempool ("Session", 1048576)
+       , _mempool ("Session", 2097152)
        , lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
        , _n_lua_scripts (0)
        , _butler (new Butler (*this))
@@ -249,6 +252,7 @@ Session::Session (AudioEngine &eng,
        , _rt_thread_active (false)
        , _rt_emit_pending (false)
        , _ac_thread_active (false)
+       , _latency_recompute_pending (0)
        , step_speed (0)
        , outbound_mtc_timecode_frame (0)
        , next_quarter_frame_to_send (-1)
@@ -308,7 +312,6 @@ Session::Session (AudioEngine &eng,
        , _step_editors (0)
        , _suspend_timecode_transmission (0)
        ,  _speakers (new Speakers)
-       , _order_hint (-1)
        , ignore_route_processor_changes (false)
        , midi_clock (0)
        , _scene_changer (0)
@@ -318,6 +321,8 @@ Session::Session (AudioEngine &eng,
 {
        uint32_t sr = 0;
 
+       created_with = string_compose ("%1 %2", PROGRAM_NAME, revision);
+
        pthread_mutex_init (&_rt_emit_mutex, 0);
        pthread_cond_init (&_rt_emit_cond, 0);
 
@@ -1149,7 +1154,7 @@ Session::add_monitor_section ()
                return;
        }
 
-       boost::shared_ptr<Route> r (new Route (*this, _("Monitor"), Route::MonitorOut, DataType::AUDIO));
+       boost::shared_ptr<Route> r (new Route (*this, _("Monitor"), PresentationInfo::MonitorOut, DataType::AUDIO));
 
        if (r->init ()) {
                return;
@@ -1167,7 +1172,7 @@ Session::add_monitor_section ()
        }
 
        rl.push_back (r);
-       add_routes (rl, false, false, false);
+       add_routes (rl, false, false, false, 0);
 
        assert (_monitor_out);
 
@@ -1313,8 +1318,12 @@ Session::reset_monitor_section ()
        _master_out->output()->disconnect (this);
        _monitor_out->output()->disconnect (this);
 
-       _monitor_out->input()->ensure_io (_master_out->output()->n_ports(), false, this);
-       _monitor_out->output()->ensure_io (_master_out->output()->n_ports(), false, this);
+       // monitor section follow master bus - except midi
+       ChanCount mon_chn (_master_out->output()->n_ports());
+       mon_chn.set_midi (0);
+
+       _monitor_out->input()->ensure_io (mon_chn, false, this);
+       _monitor_out->output()->ensure_io (mon_chn, false, this);
 
        for (uint32_t n = 0; n < limit; ++n) {
                boost::shared_ptr<AudioPort> p = _monitor_out->input()->ports().nth_audio_port (n);
@@ -1944,7 +1953,7 @@ void
 Session::set_all_tracks_record_enabled (bool enable )
 {
        boost::shared_ptr<RouteList> rl = routes.reader();
-       set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), enable, Controllable::NoGroup);
+       set_controls (route_list_to_control_list (rl, &Stripable::rec_enable_control), enable, Controllable::NoGroup);
 }
 
 void
@@ -2021,59 +2030,55 @@ framepos_t
 Session::audible_frame () const
 {
        framepos_t ret;
-       framepos_t tf;
-       framecnt_t offset;
 
-       offset = worst_playback_latency ();
+       frameoffset_t offset = worst_playback_latency (); // - _engine.samples_since_cycle_start ();
+       offset *= transport_speed ();
 
        if (synced_to_engine()) {
                /* Note: this is basically just sync-to-JACK */
-               tf = _engine.transport_frame();
+               ret = _engine.transport_frame();
        } else {
-               tf = _transport_frame;
+               ret = _transport_frame;
        }
 
-       ret = tf;
-
-       if (!non_realtime_work_pending()) {
-
-               /* MOVING */
+       if (transport_rolling()) {
+               ret -= offset;
 
                /* Check to see if we have passed the first guaranteed
-                  audible frame past our last start position. if not,
-                  return that last start point because in terms
-                  of audible frames, we have not moved yet.
-
-                  `Start position' in this context means the time we last
-                  either started, located, or changed transport direction.
-               */
+                * audible frame past our last start position. if not,
+                * return that last start point because in terms
+                * of audible frames, we have not moved yet.
+                *
+                * `Start position' in this context means the time we last
+                * either started, located, or changed transport direction.
+                */
 
                if (_transport_speed > 0.0f) {
 
                        if (!play_loop || !have_looped) {
-                               if (tf < _last_roll_or_reversal_location + offset) {
+                               if (ret < _last_roll_or_reversal_location) {
                                        return _last_roll_or_reversal_location;
                                }
+                       } else {
+                               // latent loops
+                               Location *location = _locations->auto_loop_location();
+                               frameoffset_t lo = location->start() - ret;
+                               if (lo > 0) {
+                                       ret = location->end () - lo;
+                               }
                        }
 
-
-                       /* forwards */
-                       ret -= offset;
-
                } else if (_transport_speed < 0.0f) {
 
                        /* XXX wot? no backward looping? */
 
-                       if (tf > _last_roll_or_reversal_location - offset) {
+                       if (ret > _last_roll_or_reversal_location) {
                                return _last_roll_or_reversal_location;
-                       } else {
-                               /* backwards */
-                               ret += offset;
                        }
                }
        }
 
-       return ret;
+       return std::max ((framepos_t)0, ret);
 }
 
 void
@@ -2307,8 +2312,7 @@ Session::resort_routes_using (boost::shared_ptr<RouteList> r)
 #ifndef NDEBUG
                DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n");
                for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n",
-                                                                  (*i)->name(), (*i)->order_key ()));
+                       DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 presentation order %2\n", (*i)->name(), (*i)->presentation_info().order()));
                }
 #endif
 
@@ -2426,8 +2430,10 @@ Session::default_track_name_pattern (DataType t)
  *  @param instrument plugin info for the instrument to insert pre-fader, if any
  */
 list<boost::shared_ptr<MidiTrack> >
-Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost::shared_ptr<PluginInfo> instrument,
-                        TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template, Plugin::PresetRecord* pset)
+Session::new_midi_track (const ChanCount& input, const ChanCount& output,
+                         boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
+                         RouteGroup* route_group, uint32_t how_many, string name_template, PresentationInfo::order_t order,
+                         TrackMode mode)
 {
        string track_name;
        uint32_t track_id = 0;
@@ -2447,7 +2453,7 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
                boost::shared_ptr<MidiTrack> track;
 
                try {
-                       track.reset (new MidiTrack (*this, track_name, Route::Flag (0), mode));
+                       track.reset (new MidiTrack (*this, track_name, mode));
 
                        if (track->init ()) {
                                goto failed;
@@ -2482,14 +2488,8 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
 
                        track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
 
-                       if (Config->get_remote_model() == UserOrdered) {
-                               track->set_remote_control_id (next_control_id());
-                       }
-
                        new_routes.push_back (track);
                        ret.push_back (track);
-
-                       RouteAddedOrRemoved (true); /* EMIT SIGNAL */
                }
 
                catch (failed_constructor &err) {
@@ -2510,9 +2510,9 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
        if (!new_routes.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
-                       add_routes (new_routes, false, false, false);
+                       add_routes (new_routes, false, false, false, order);
                } else {
-                       add_routes (new_routes, true, true, false);
+                       add_routes (new_routes, true, true, false, order);
                }
 
                if (instrument) {
@@ -2532,7 +2532,8 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
 }
 
 RouteList
-Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset)
+Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
+                         PresentationInfo::Flag flag, PresentationInfo::order_t order)
 {
        string bus_name;
        uint32_t bus_id = 0;
@@ -2548,7 +2549,7 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name
                }
 
                try {
-                       boost::shared_ptr<Route> bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO)); // XXX Editor::add_routes is not ready for ARDOUR::DataType::MIDI
+                       boost::shared_ptr<Route> bus (new Route (*this, bus_name, flag, DataType::AUDIO)); // XXX Editor::add_routes is not ready for ARDOUR::DataType::MIDI
 
                        if (bus->init ()) {
                                goto failure;
@@ -2578,13 +2579,9 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name
                        if (route_group) {
                                route_group->add (bus);
                        }
-                       if (Config->get_remote_model() == UserOrdered) {
-                               bus->set_remote_control_id (next_control_id());
-                       }
 
+                       bus->add_internal_return ();
                        ret.push_back (bus);
-                       RouteAddedOrRemoved (true); /* EMIT SIGNAL */
-                       ARDOUR::GUIIdle ();
                }
 
                catch (failed_constructor &err) {
@@ -2604,7 +2601,7 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name
   failure:
        if (!ret.empty()) {
                StateProtector sp (this);
-               add_routes (ret, false, false, false);
+               add_routes (ret, false, false, false, order);
 
                if (instrument) {
                        for (RouteList::iterator r = ret.begin(); r != ret.end(); ++r) {
@@ -2931,12 +2928,40 @@ Session::reconnect_mmc_ports(bool inputs)
 
 #endif
 
+void
+Session::ensure_route_presentation_info_gap (PresentationInfo::order_t first_new_order, uint32_t how_many)
+{
+       if (first_new_order == PresentationInfo::max_order) {
+               /* adding at end, no worries */
+               return;
+       }
+
+       /* create a gap in the presentation info to accomodate @param how_many
+        * new objects.
+        */
+       StripableList sl;
+       get_stripables (sl);
+
+       for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) {
+               boost::shared_ptr<Stripable> s (*si);
+
+               if (s->is_monitor() || s->is_auditioner()) {
+                       continue;
+               }
+
+               if (s->presentation_info().order () >= first_new_order) {
+                       s->set_presentation_order (s->presentation_info().order () + how_many);
+               }
+       }
+}
+
 /** Caller must not hold process lock
  *  @param name_template string to use for the start of the name, or "" to use "Audio".
  */
 list< boost::shared_ptr<AudioTrack> >
-Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group,
-                         uint32_t how_many, string name_template)
+Session::new_audio_track (int input_channels, int output_channels, RouteGroup* route_group,
+                          uint32_t how_many, string name_template, PresentationInfo::order_t order,
+                          TrackMode mode)
 {
        string track_name;
        uint32_t track_id = 0;
@@ -2957,7 +2982,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
                boost::shared_ptr<AudioTrack> track;
 
                try {
-                       track.reset (new AudioTrack (*this, track_name, Route::Flag (0), mode));
+                       track.reset (new AudioTrack (*this, track_name, mode));
 
                        if (track->init ()) {
                                goto failed;
@@ -2967,7 +2992,6 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
                                track->set_strict_io (true);
                        }
 
-
                        if (ARDOUR::Profile->get_trx ()) {
                                // TRACKS considers it's not a USE CASE, it's
                                // a piece of behavior of the session model:
@@ -3013,14 +3037,9 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
                        track->non_realtime_input_change();
 
                        track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
-                       if (Config->get_remote_model() == UserOrdered) {
-                               track->set_remote_control_id (next_control_id());
-                       }
 
                        new_routes.push_back (track);
                        ret.push_back (track);
-
-                       RouteAddedOrRemoved (true); /* EMIT SIGNAL */
                }
 
                catch (failed_constructor &err) {
@@ -3041,9 +3060,9 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
        if (!new_routes.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
-                       add_routes (new_routes, false, false, false);
+                       add_routes (new_routes, false, false, false, order);
                } else {
-                       add_routes (new_routes, true, true, false);
+                       add_routes (new_routes, true, true, false, order);
                }
        }
 
@@ -3054,7 +3073,8 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
  *  @param name_template string to use for the start of the name, or "" to use "Bus".
  */
 RouteList
-Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template)
+Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template,
+                          PresentationInfo::Flag flags, PresentationInfo::order_t order)
 {
        string bus_name;
        uint32_t bus_id = 0;
@@ -3070,7 +3090,7 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
                }
 
                try {
-                       boost::shared_ptr<Route> bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO));
+                       boost::shared_ptr<Route> bus (new Route (*this, bus_name, flags, DataType::AUDIO));
 
                        if (bus->init ()) {
                                goto failure;
@@ -3104,20 +3124,11 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
                        if (route_group) {
                                route_group->add (bus);
                        }
-                       if (Config->get_remote_model() == UserOrdered) {
-                               bus->set_remote_control_id (next_control_id());
-                       }
 
                        bus->add_internal_return ();
-
                        ret.push_back (bus);
-
-                       RouteAddedOrRemoved (true); /* EMIT SIGNAL */
-
-                       ARDOUR::GUIIdle ();
                }
 
-
                catch (failed_constructor &err) {
                        error << _("Session: could not create new audio route.") << endmsg;
                        goto failure;
@@ -3136,9 +3147,9 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
        if (!ret.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
-                       add_routes (ret, false, false, false);
+                       add_routes (ret, false, false, false, order);
                } else {
-                       add_routes (ret, false, true, true); // autoconnect // outputs only
+                       add_routes (ret, false, true, true, order); // autoconnect // outputs only
                }
        }
 
@@ -3162,7 +3173,6 @@ RouteList
 Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::string& name_base, PlaylistDisposition pd)
 {
        RouteList ret;
-       uint32_t control_id;
        uint32_t number = 0;
        const uint32_t being_added = how_many;
        /* This will prevent the use of any existing XML-provided PBD::ID
@@ -3171,8 +3181,6 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
        Stateful::ForceIDRegeneration force_ids;
        IO::disable_connecting ();
 
-       control_id = next_control_id ();
-
        while (how_many) {
 
                /* We're going to modify the node contents a bit so take a
@@ -3259,6 +3267,10 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
                                                (*i)->add_property ("bitslot", buf);
                                                (*i)->add_property ("name", name);
                                        }
+                                       else if (type && type->value() == X_("intreturn")) {
+                                               (*i)->remove_property (X_("bitslot"));
+                                               (*i)->add_property ("ignore-bitslot", "1");
+                                       }
                                        else if (type && type->value() == X_("return")) {
                                                // Return::set_state() generates a new one
                                                (*i)->remove_property (X_("bitslot"));
@@ -3293,9 +3305,6 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
                                route->output()->changed (change, this);
                        }
 
-                       route->set_remote_control_id (control_id);
-                       ++control_id;
-
                        boost::shared_ptr<Track> track;
 
                        if ((track = boost::dynamic_pointer_cast<Track> (route))) {
@@ -3312,8 +3321,6 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
                        };
 
                        ret.push_back (route);
-
-                       RouteAddedOrRemoved (true); /* EMIT SIGNAL */
                }
 
                catch (failed_constructor &err) {
@@ -3333,9 +3340,9 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
        if (!ret.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
-                       add_routes (ret, false, false, false);
+                       add_routes (ret, false, false, false, PresentationInfo::max_order);
                } else {
-                       add_routes (ret, true, true, false);
+                       add_routes (ret, true, true, false, PresentationInfo::max_order);
                }
                IO::enable_connecting ();
        }
@@ -3344,11 +3351,11 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
 }
 
 void
-Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, bool save)
+Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, bool save, PresentationInfo::order_t order)
 {
        try {
                PBD::Unwinder<bool> aip (_adding_routes_in_progress, true);
-               add_routes_inner (new_routes, input_auto_connect, output_auto_connect);
+               add_routes_inner (new_routes, input_auto_connect, output_auto_connect, order);
 
        } catch (...) {
                error << _("Adding new tracks/busses failed") << endmsg;
@@ -3365,25 +3372,18 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output
                save_state (_current_snapshot_name);
        }
 
-       reassign_track_numbers();
-
        update_route_record_state ();
 
        RouteAdded (new_routes); /* EMIT SIGNAL */
 }
 
 void
-Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect)
+Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, PresentationInfo::order_t order)
 {
        ChanCount existing_inputs;
        ChanCount existing_outputs;
-       uint32_t order = next_control_id();
-
-
-       if (_order_hint > -1) {
-               order = _order_hint;
-               _order_hint = -1;
-       }
+       uint32_t n_routes;
+       uint32_t added = 0;
 
        count_existing_track_channels (existing_inputs, existing_outputs);
 
@@ -3391,6 +3391,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                RCUWriter<RouteList> writer (routes);
                boost::shared_ptr<RouteList> r = writer.get_copy ();
                r->insert (r->end(), new_routes.begin(), new_routes.end());
+               n_routes = r->size();
 
                /* if there is no control out and we're not in the middle of loading,
                 * resort the graph here. if there is a control out, we will resort
@@ -3403,7 +3404,10 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                }
        }
 
-       for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) {
+       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("ensure order gap starting at %1 for %2\n", order, new_routes.size()));
+       ensure_route_presentation_info_gap (order, new_routes.size());
+
+       for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) {
 
                boost::weak_ptr<Route> wpr (*x);
                boost::shared_ptr<Route> r (*x);
@@ -3414,6 +3418,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
 
                r->output()->changed.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2));
                r->processors_changed.connect_same_thread (*this, boost::bind (&Session::route_processors_changed, this, _1));
+               r->processor_latency_changed.connect_same_thread (*this, boost::bind (&Session::queue_latency_recompute, this));
 
                if (r->is_master()) {
                        _master_out = r;
@@ -3436,28 +3441,45 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                        }
                }
 
-               if (input_auto_connect || output_auto_connect) {
-                       auto_connect_route (r, input_auto_connect, ChanCount (), ChanCount (), existing_inputs, existing_outputs);
-                       existing_inputs += r->n_inputs();
-                       existing_outputs += r->n_outputs();
-               }
+               if (!r->presentation_info().special()) {
 
-               /* order keys are a GUI responsibility but we need to set up
-                        reasonable defaults because they also affect the remote control
-                        ID in most situations.
-                        */
+                       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name()));
+
+                       /* presentation info order may already have been set from XML */
 
-               if (!r->has_order_key ()) {
-                       if (r->is_auditioner()) {
-                               /* use an arbitrarily high value */
-                               r->set_order_key (UINT_MAX);
+                       if (!r->presentation_info().order_set()) {
+
+                               if (order == PresentationInfo::max_order) {
+                                       /* just add to the end */
+                                       r->set_presentation_order (n_routes + added, false);
+                                       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to NR %1 + %2 = %3\n", n_routes, added, n_routes + added));
+                               } else {
+                                       r->set_presentation_order (order + added);
+                                       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to %1 + %2 = %3\n", order, added, order + added));
+                               }
                        } else {
-                               DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("while adding, set %1 to order key %2\n", r->name(), order));
-                               r->set_order_key (order);
-                               order++;
+                               DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order already set to %1\n", r->presentation_info().order()));
                        }
                }
 
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
+               /* clang complains: 'operator<<' should be declared prior to the call site or in an associated namespace of one of its
+                * arguments std::ostream& operator<<(std::ostream& o, ARDOUR::PresentationInfo const& rid)"
+                */
+               DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("added route %1, group order %2 type %3 (summary: %4)\n",
+                                                              r->name(),
+                                                              r->presentation_info().order(),
+                                                              enum_2_string (r->presentation_info().flags()),
+                                                              r->presentation_info()));
+#endif
+
+
+               if (input_auto_connect || output_auto_connect) {
+                       auto_connect_route (r, input_auto_connect, ChanCount (), ChanCount (), existing_inputs, existing_outputs);
+                       existing_inputs += r->n_inputs();
+                       existing_outputs += r->n_outputs();
+               }
+
                ARDOUR::GUIIdle ();
        }
 
@@ -3474,6 +3496,8 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                        }
                }
        }
+
+       reassign_track_numbers ();
 }
 
 void
@@ -3631,7 +3655,6 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
        } // end of RCU Writer scope
 
        update_route_solo_state ();
-       RouteAddedOrRemoved (false); /* EMIT SIGNAL */
        update_latency_compensation ();
        set_dirty();
 
@@ -3668,7 +3691,7 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                return;
        }
 
-       Route::RemoteControlIDChange(); /* EMIT SIGNAL */
+       PresentationInfo::Change(); /* EMIT SIGNAL */
 
        /* save the new state of the world */
 
@@ -3676,7 +3699,6 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                save_history (_current_snapshot_name);
        }
 
-       reassign_track_numbers();
        update_route_record_state ();
 }
 
@@ -3877,6 +3899,8 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD
 
                if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
                        /* route does not get solo propagated to it */
+                       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 excluded from solo because iso = %2 can_solo = %3\n", (*i)->name(), (*i)->solo_isolate_control()->solo_isolated(),
+                                                                 (*i)->can_solo()));
                        continue;
                }
 
@@ -4013,6 +4037,16 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
                                                  something_soloed, listeners, isolated));
 }
 
+void
+Session::get_stripables (StripableList& sl) const
+{
+       boost::shared_ptr<RouteList> r = routes.reader ();
+       sl.insert (sl.end(), r->begin(), r->end());
+
+       VCAList v = _vca_manager->vcas ();
+       sl.insert (sl.end(), v.begin(), v.end());
+}
+
 boost::shared_ptr<RouteList>
 Session::get_routes_with_internal_returns() const
 {
@@ -4028,7 +4062,7 @@ Session::get_routes_with_internal_returns() const
 }
 
 bool
-Session::io_name_is_legal (const std::string& name)
+Session::io_name_is_legal (const std::string& name) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
 
@@ -4137,7 +4171,7 @@ Session::routes_using_input_from (const string& str, RouteList& rl)
 }
 
 boost::shared_ptr<Route>
-Session::route_by_name (string name)
+Session::route_by_name (string name) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
 
@@ -4151,7 +4185,7 @@ Session::route_by_name (string name)
 }
 
 boost::shared_ptr<Route>
-Session::route_by_id (PBD::ID id)
+Session::route_by_id (PBD::ID id) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
 
@@ -4180,7 +4214,7 @@ Session::processor_by_id (PBD::ID id) const
 }
 
 boost::shared_ptr<Track>
-Session::track_by_diskstream_id (PBD::ID id)
+Session::track_by_diskstream_id (PBD::ID id) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
 
@@ -4195,37 +4229,52 @@ Session::track_by_diskstream_id (PBD::ID id)
 }
 
 boost::shared_ptr<Route>
-Session::route_by_remote_id (uint32_t id)
+Session::get_remote_nth_route (PresentationInfo::order_t n) const
 {
-       boost::shared_ptr<RouteList> r = routes.reader ();
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if ((*i)->remote_control_id() == id) {
-                       return *i;
-               }
-       }
-
-       return boost::shared_ptr<Route> ((Route*) 0);
+       return boost::dynamic_pointer_cast<Route> (get_remote_nth_stripable (n, PresentationInfo::Route));
 }
 
-
 boost::shared_ptr<Stripable>
-Session::stripable_by_remote_id (uint32_t id)
+Session::get_remote_nth_stripable (PresentationInfo::order_t n, PresentationInfo::Flag flags) const
 {
-       boost::shared_ptr<RouteList> r = routes.reader ();
+       StripableList sl;
+       PresentationInfo::order_t match_cnt = 0;
 
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if ((*i)->remote_control_id() == id) {
-                       return *i;
+       get_stripables (sl);
+       sl.sort (Stripable::PresentationOrderSorter());
+
+       for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
+
+               if ((*s)->presentation_info().hidden()) {
+                       /* if the caller didn't explicitly ask for hidden
+                          stripables, ignore hidden ones. This matches
+                          the semantics of the pre-PresentationOrder
+                          "get by RID" logic of Ardour 4.x and earlier.
+
+                          XXX at some point we should likely reverse
+                          the logic of the flags, because asking for "the
+                          hidden stripables" is not going to be common,
+                          whereas asking for visible ones is normal.
+                       */
+
+                       if (! (flags & PresentationInfo::Hidden)) {
+                               continue;
+                       }
+               }
+
+               if ((*s)->presentation_info().flag_match (flags)) {
+                       if (match_cnt++ == n) {
+                               return *s;
+                       }
                }
        }
 
-       return boost::shared_ptr<Route> ((Route*) 0);
+       /* there is no nth stripable that matches the given flags */
+       return boost::shared_ptr<Stripable>();
 }
 
-
 boost::shared_ptr<Route>
-Session::route_by_selected_count (uint32_t id)
+Session::route_by_selected_count (uint32_t id) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
 
@@ -4236,6 +4285,19 @@ Session::route_by_selected_count (uint32_t id)
        return boost::shared_ptr<Route> ((Route*) 0);
 }
 
+struct PresentationOrderSorter {
+       bool operator() (boost::shared_ptr<Stripable> a, boost::shared_ptr<Stripable> b) {
+               if (a->presentation_info().special() && !b->presentation_info().special()) {
+                       /* a is not ordered, b is; b comes before a */
+                       return false;
+               } else if (!b->presentation_info().order_set() && a->presentation_info().order_set()) {
+                       /* b is not ordered, a is; a comes before b */
+                       return true;
+               } else {
+                       return a->presentation_info().order() < b->presentation_info().order();
+               }
+       }
+};
 
 void
 Session::reassign_track_numbers ()
@@ -4243,7 +4305,7 @@ Session::reassign_track_numbers ()
        int64_t tn = 0;
        int64_t bn = 0;
        RouteList r (*(routes.reader ()));
-       SignalOrderRouteSorter sorter;
+       PresentationOrderSorter sorter;
        r.sort (sorter);
 
        StateProtector sp (this);
@@ -4270,6 +4332,16 @@ Session::reassign_track_numbers ()
                // trigger GUI re-layout
                config.ParameterChanged("track-name-number");
        }
+
+#ifndef NDEBUG
+       if (DEBUG_ENABLED(DEBUG::OrderKeys)) {
+               boost::shared_ptr<RouteList> rl = routes.reader ();
+               for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+                       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 numbered %2\n", (*i)->name(), (*i)->track_number()));
+               }
+       }
+#endif /* NDEBUG */
+
 }
 
 void
@@ -4335,12 +4407,18 @@ Session::maybe_update_session_range (framepos_t a, framepos_t b)
                        _session_range_location->set_start (a);
                }
 
-               if (b > _session_range_location->end()) {
+               if (_session_range_end_is_free && (b > _session_range_location->end())) {
                        _session_range_location->set_end (b);
                }
        }
 }
 
+void
+Session::set_end_is_free (bool yn)
+{
+       _session_range_end_is_free = yn;
+}
+
 void
 Session::playlist_ranges_moved (list<Evoral::RangeMove<framepos_t> > const & ranges)
 {
@@ -5119,6 +5197,7 @@ Session::try_run_lua (pframes_t nframes)
        Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK);
        if (tm.locked ()) {
                try { (*_lua_run)(nframes); } catch (luabridge::LuaException const& e) { }
+               lua.collect_garbage_step ();
        }
 }
 
@@ -5128,6 +5207,7 @@ Session::setup_lua ()
 #ifndef NDEBUG
        lua.Print.connect (&_lua_print);
 #endif
+       lua.tweak_rt_gc ();
        lua.do_command (
                        "function ArdourSession ()"
                        "  local self = { scripts = {}, instances = {} }"
@@ -5319,7 +5399,7 @@ Session::RoutePublicOrderSorter::operator() (boost::shared_ptr<Route> a, boost::
        if (b->is_monitor()) {
                return false;
        }
-       return a->order_key () < b->order_key ();
+       return a->presentation_info().order() < b->presentation_info().order();
 }
 
 bool
@@ -6136,7 +6216,7 @@ Session::solo_control_mode_changed ()
                   the session's saved solo state). So just explicitly turn
                   them all off.
                */
-               set_controls (route_list_to_control_list (get_routes(), &Route::solo_control), 0.0, Controllable::NoGroup);
+               set_controls (route_list_to_control_list (get_routes(), &Stripable::solo_control), 0.0, Controllable::NoGroup);
        }
 }
 
@@ -6659,75 +6739,25 @@ Session::session_name_is_legal (const string& path)
        return 0;
 }
 
-uint32_t
-Session::next_control_id () const
-{
-       int subtract = 0;
-
-       /* the monitor bus remote ID is in a different
-        * "namespace" than regular routes. its existence doesn't
-        * affect normal (low) numbered routes.
-        */
-
-       if (_monitor_out) {
-               subtract++;
-       }
-
-       /* the same about masterbus in Waves Tracks */
-
-       if (Profile->get_trx() && _master_out) {
-               subtract++;
-       }
-
-       return nroutes() - subtract;
-}
-
 void
-Session::notify_remote_id_change ()
+Session::notify_presentation_info_change ()
 {
        if (deletion_in_progress()) {
                return;
        }
 
-       switch (Config->get_remote_model()) {
-       case MixerOrdered:
-               Route::RemoteControlIDChange (); /* EMIT SIGNAL */
-               break;
-       default:
-               break;
-       }
+       PresentationInfo::Change (); /* EMIT SIGNAL */
+       reassign_track_numbers();
 
 #ifdef USE_TRACKS_CODE_FEATURES
-               /* Waves Tracks: for Waves Tracks session it's required to reconnect their IOs
-                * if track order has been changed by user
-                */
-               reconnect_existing_routes(true, true);
+       /* Waves Tracks: for Waves Tracks session it's required to reconnect their IOs
+        * if track order has been changed by user
+        */
+       reconnect_existing_routes(true, true);
 #endif
 
 }
 
-void
-Session::sync_order_keys ()
-{
-       if (deletion_in_progress()) {
-               return;
-       }
-
-       /* tell everyone that something has happened to the sort keys
-          and let them sync up with the change(s)
-          this will give objects that manage the sort order keys the
-          opportunity to keep them in sync if they wish to.
-       */
-
-       DEBUG_TRACE (DEBUG::OrderKeys, "Sync Order Keys.\n");
-
-       reassign_track_numbers();
-
-       Route::SyncOrderKeys (); /* EMIT SIGNAL */
-
-       DEBUG_TRACE (DEBUG::OrderKeys, "\tsync done\n");
-}
-
 bool
 Session::operation_in_progress (GQuark op) const
 {
@@ -6835,6 +6865,16 @@ Session::auto_connect_route (boost::shared_ptr<Route> route, bool connect_inputs
        }
 }
 
+void
+Session::queue_latency_recompute ()
+{
+       g_atomic_int_inc (&_latency_recompute_pending);
+       if (pthread_mutex_trylock (&_auto_connect_mutex) == 0) {
+               pthread_cond_signal (&_auto_connect_cond);
+               pthread_mutex_unlock (&_auto_connect_mutex);
+       }
+}
+
 void
 Session::auto_connect (const AutoConnectRequest& ar)
 {
@@ -7000,7 +7040,32 @@ Session::auto_connect_thread_run ()
                        }
                }
 
+               if (!actively_recording ()) { // might not be needed,
+                       /* this is only used for updating plugin latencies, the
+                        * graph does not change. so it's safe in general.
+                        * BUT..
+                        * .. update_latency_compensation () entails set_capture_offset()
+                        * which calls Diskstream::set_capture_offset () which
+                        * modifies the capture offset... which can be a proplem
+                        * in "prepare_to_stop"
+                        */
+                       while (g_atomic_int_and (&_latency_recompute_pending, 0)) {
+                               update_latency_compensation ();
+                       }
+               }
+
                pthread_cond_wait (&_auto_connect_cond, &_auto_connect_mutex);
        }
        pthread_mutex_unlock (&_auto_connect_mutex);
 }
+
+void
+Session::cancel_all_solo ()
+{
+       StripableList sl;
+
+       get_stripables (sl);
+
+       set_controls (stripable_list_to_control_list (sl, &Stripable::solo_control), 0.0, Controllable::NoGroup);
+       clear_all_solo_state (routes.reader());
+}