delegate port-connections to low priority thread.
[ardour.git] / libs / ardour / session.cc
index bc17e4eb7fac517f6b38d69010f4670397aba0a1..b93d685bfc74a670bd90b9965fa0a95b8513c858 100644 (file)
@@ -42,6 +42,7 @@
 #include "pbd/error.h"
 #include "pbd/file_utils.h"
 #include "pbd/md5.h"
+#include "pbd/pthread_utils.h"
 #include "pbd/search_path.h"
 #include "pbd/stacktrace.h"
 #include "pbd/stl_delete.h"
@@ -125,11 +126,13 @@ using namespace PBD;
 
 bool Session::_disable_all_loaded_plugins = false;
 bool Session::_bypass_all_loaded_plugins = false;
+guint Session::_name_id_counter = 0;
 
 PBD::Signal1<int,uint32_t> Session::AudioEngineSetupRequired;
 PBD::Signal1<void,std::string> Session::Dialog;
 PBD::Signal0<int> Session::AskAboutPendingState;
 PBD::Signal2<int, framecnt_t, framecnt_t> Session::AskAboutSampleRateMismatch;
+PBD::Signal2<void, framecnt_t, framecnt_t> Session::NotifyAboutSampleRateMismatch;
 PBD::Signal0<void> Session::SendFeedback;
 PBD::Signal3<int,Session*,std::string,DataType> Session::MissingFile;
 
@@ -165,8 +168,8 @@ Session::Session (AudioEngine &eng,
        , _bounce_processing_active (false)
        , waiting_for_sync_offset (false)
        , _base_frame_rate (0)
-       , _current_frame_rate (0)
        , _nominal_frame_rate (0)
+       , _current_frame_rate (0)
        , transport_sub_state (0)
        , _record_status (Disabled)
        , _transport_frame (0)
@@ -232,6 +235,7 @@ Session::Session (AudioEngine &eng,
        , pending_auto_loop (false)
        , _mempool ("Session", 1048576)
        , lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
+       , _n_lua_scripts (0)
        , _butler (new Butler (*this))
        , _post_transport_work (0)
        ,  cumulative_rf_motion (0)
@@ -240,6 +244,7 @@ Session::Session (AudioEngine &eng,
        , _ignore_skips_updates (false)
        , _rt_thread_active (false)
        , _rt_emit_pending (false)
+       , _ac_thread_active (false)
        , step_speed (0)
        , outbound_mtc_timecode_frame (0)
        , next_quarter_frame_to_send (-1)
@@ -310,6 +315,11 @@ Session::Session (AudioEngine &eng,
        pthread_mutex_init (&_rt_emit_mutex, 0);
        pthread_cond_init (&_rt_emit_cond, 0);
 
+       pthread_mutex_init (&_auto_connect_mutex, 0);
+       pthread_cond_init (&_auto_connect_cond, 0);
+
+       init_name_id_counter (1); // reset for new sessions, start at 1
+
        pre_engine_init (fullpath);
 
        setup_lua ();
@@ -403,6 +413,7 @@ Session::Session (AudioEngine &eng,
        EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1));
 
        emit_thread_start ();
+       auto_connect_thread_start ();
 
        /* hook us up to the engine since we are now completely constructed */
 
@@ -468,6 +479,24 @@ Session::~Session ()
        destroy ();
 }
 
+unsigned int
+Session::next_name_id ()
+{
+       return g_atomic_int_add (&_name_id_counter, 1);
+}
+
+unsigned int
+Session::name_id_counter ()
+{
+       return g_atomic_int_get (&_name_id_counter);
+}
+
+void
+Session::init_name_id_counter (guint n)
+{
+       g_atomic_int_set (&_name_id_counter, n);
+}
+
 int
 Session::ensure_engine (uint32_t desired_sample_rate)
 {
@@ -696,6 +725,11 @@ Session::destroy ()
        pthread_cond_destroy (&_rt_emit_cond);
        pthread_mutex_destroy (&_rt_emit_mutex);
 
+       auto_connect_thread_terminate ();
+
+       pthread_cond_destroy (&_auto_connect_cond);
+       pthread_mutex_destroy (&_auto_connect_mutex);
+
        delete _scene_changer; _scene_changer = 0;
        delete midi_control_ui; midi_control_ui = 0;
 
@@ -1117,7 +1151,7 @@ Session::add_monitor_section ()
        /* if monitor section is not connected, connect it to physical outs
         */
 
-       if (Config->get_auto_connect_standard_busses() && !_monitor_out->output()->connected ()) {
+       if ((Config->get_auto_connect_standard_busses () || Profile->get_mixbus ()) && !_monitor_out->output()->connected ()) {
 
                if (!Config->get_monitor_bus_preferred_bundle().empty()) {
 
@@ -2002,7 +2036,12 @@ Session::set_frame_rate (framecnt_t frames_per_second)
                here.
        */
 
-       _base_frame_rate = frames_per_second;
+       if (_base_frame_rate == 0) {
+               _base_frame_rate = frames_per_second;
+       }
+       else if (_base_frame_rate != frames_per_second && frames_per_second != _nominal_frame_rate) {
+               NotifyAboutSampleRateMismatch (_base_frame_rate, frames_per_second);
+       }
        _nominal_frame_rate = frames_per_second;
 
        sync_time_vars();
@@ -2362,6 +2401,10 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
                                goto failed;
                        }
 
+                       if (Profile->get_mixbus ()) {
+                               track->set_strict_io (true);
+                       }
+
                        track->use_new_diskstream();
 
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
@@ -2434,154 +2477,119 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
        return ret;
 }
 
-void
-Session::midi_output_change_handler (IOChange change, void * /*src*/, boost::weak_ptr<Route> wmt)
+RouteList
+Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr<PluginInfo> instrument)
 {
-        boost::shared_ptr<Route> midi_track (wmt.lock());
-
-        if (!midi_track) {
-                return;
-        }
-
-       if ((change.type & IOChange::ConfigurationChanged) && Config->get_output_auto_connect() != ManualConnect) {
-
-                if (change.after.n_audio() <= change.before.n_audio()) {
-                        return;
-                }
-
-                /* new audio ports: make sure the audio goes somewhere useful,
-                   unless the user has no-auto-connect selected.
-
-                   The existing ChanCounts don't matter for this call as they are only
-                   to do with matching input and output indices, and we are only changing
-                   outputs here.
-                */
-
-                ChanCount dummy;
-
-                auto_connect_route (midi_track, dummy, dummy, false, false, ChanCount(), change.before);
-        }
-}
+       string bus_name;
+       uint32_t bus_id = 0;
+       string port;
+       RouteList ret;
 
-/** @param connect_inputs true to connect inputs as well as outputs, false to connect just outputs.
- *  @param input_start Where to start from when auto-connecting inputs; e.g. if this is 0, auto-connect starting from input 0.
- *  @param output_start As \a input_start, but for outputs.
- */
-void
-Session::auto_connect_route (boost::shared_ptr<Route> route, ChanCount& existing_inputs, ChanCount& existing_outputs,
-                             bool with_lock, bool connect_inputs, ChanCount input_start, ChanCount output_start)
-{
-       if (!IO::connecting_legal) {
-               return;
-       }
+       bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Midi Bus");
 
-       Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK);
+       while (how_many) {
+               if (!find_route_name (name_template.empty () ? _("Midi Bus") : name_template, ++bus_id, bus_name, use_number)) {
+                       error << "cannot find name for new midi bus" << endmsg;
+                       goto failure;
+               }
 
-       if (with_lock) {
-               lm.acquire ();
-       }
+               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
 
-       /* If both inputs and outputs are auto-connected to physical ports,
-          use the max of input and output offsets to ensure auto-connected
-          port numbers always match up (e.g. the first audio input and the
-          first audio output of the route will have the same physical
-          port number).  Otherwise just use the lowest input or output
-          offset possible.
-       */
+                       if (bus->init ()) {
+                               goto failure;
+                       }
 
-       DEBUG_TRACE (DEBUG::Graph,
-                    string_compose("Auto-connect: existing in = %1 out = %2\n",
-                                   existing_inputs, existing_outputs));
+                       if (Profile->get_mixbus ()) {
+                               bus->set_strict_io (true);
+                       }
 
-       const bool in_out_physical =
-               (Config->get_input_auto_connect() & AutoConnectPhysical)
-               && (Config->get_output_auto_connect() & AutoConnectPhysical)
-               && connect_inputs;
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+                       // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route");
+#endif
+                       {
+                               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
-       const ChanCount in_offset = in_out_physical
-               ? ChanCount::max(existing_inputs, existing_outputs)
-                : existing_inputs;
+                               if (bus->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
+                                       error << _("cannot configure new midi bus input") << endmsg;
+                                       goto failure;
+                               }
 
-       const ChanCount out_offset = in_out_physical
-               ? ChanCount::max(existing_inputs, existing_outputs)
-               : existing_outputs;
 
-       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-               vector<string> physinputs;
-               vector<string> physoutputs;
+                               if (bus->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
+                                       error << _("cannot configure new midi bus output") << endmsg;
+                                       goto failure;
+                               }
+                       }
 
-               _engine.get_physical_outputs (*t, physoutputs);
-               _engine.get_physical_inputs (*t, physinputs);
+                       if (route_group) {
+                               route_group->add (bus);
+                       }
+                       if (Config->get_remote_model() == UserOrdered) {
+                               bus->set_remote_control_id (next_control_id());
+                       }
 
-               if (!physinputs.empty() && connect_inputs) {
-                       uint32_t nphysical_in = physinputs.size();
+                       ret.push_back (bus);
+                       RouteAddedOrRemoved (true); /* EMIT SIGNAL */
+                       ARDOUR::GUIIdle ();
+               }
 
-                       DEBUG_TRACE (DEBUG::Graph,
-                                    string_compose("There are %1 physical inputs of type %2\n",
-                                                   nphysical_in, *t));
+               catch (failed_constructor &err) {
+                       error << _("Session: could not create new audio route.") << endmsg;
+                       goto failure;
+               }
 
-                       for (uint32_t i = input_start.get(*t); i < route->n_inputs().get(*t) && i < nphysical_in; ++i) {
-                               string port;
+               catch (AudioEngine::PortRegistrationFailure& pfe) {
+                       error << pfe.what() << endmsg;
+                       goto failure;
+               }
 
-                               if (Config->get_input_auto_connect() & AutoConnectPhysical) {
-                                       DEBUG_TRACE (DEBUG::Graph,
-                                                    string_compose("Get index %1 + %2 % %3 = %4\n",
-                                                                   in_offset.get(*t), i, nphysical_in,
-                                                                   (in_offset.get(*t) + i) % nphysical_in));
-                                       port = physinputs[(in_offset.get(*t) + i) % nphysical_in];
-                               }
 
-                               DEBUG_TRACE (DEBUG::Graph,
-                                            string_compose("Connect route %1 IN to %2\n",
-                                                           route->name(), port));
+               --how_many;
+       }
 
-                               if (!port.empty() && route->input()->connect (route->input()->ports().port(*t, i), port, this)) {
-                                       break;
-                               }
+  failure:
+       if (!ret.empty()) {
+               StateProtector sp (this);
+               add_routes (ret, false, false, false);
 
-                                ChanCount one_added (*t, 1);
-                                existing_inputs += one_added;
+               if (instrument) {
+                       for (RouteList::iterator r = ret.begin(); r != ret.end(); ++r) {
+                               PluginPtr plugin = instrument->load (*this);
+                               boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
+                               (*r)->add_processor (p, PreFader);
                        }
                }
+       }
 
-               if (!physoutputs.empty()) {
-                       uint32_t nphysical_out = physoutputs.size();
-                       for (uint32_t i = output_start.get(*t); i < route->n_outputs().get(*t); ++i) {
-                               string port;
+       return ret;
 
-                               /* Waves Tracks:
-                                * do not create new connections if we reached the limit of physical outputs
-                                * in Multi Out mode
-                                */
+}
 
-                               if (!(Config->get_output_auto_connect() & AutoConnectMaster) &&
-                                   ARDOUR::Profile->get_trx () &&
-                                   existing_outputs.get(*t) == nphysical_out ) {
-                                       break;
-                               }
 
-                               if ((*t) == DataType::MIDI && (Config->get_output_auto_connect() & AutoConnectPhysical)) {
-                                       port = physoutputs[(out_offset.get(*t) + i) % nphysical_out];
-                               } else if ((*t) == DataType::AUDIO && (Config->get_output_auto_connect() & AutoConnectMaster)) {
-                                        /* master bus is audio only */
-                                       if (_master_out && _master_out->n_inputs().get(*t) > 0) {
-                                               port = _master_out->input()->ports().port(*t,
-                                                               i % _master_out->input()->n_ports().get(*t))->name();
-                                       }
-                               }
+void
+Session::midi_output_change_handler (IOChange change, void * /*src*/, boost::weak_ptr<Route> wmt)
+{
+       boost::shared_ptr<Route> midi_track (wmt.lock());
 
-                               DEBUG_TRACE (DEBUG::Graph,
-                                            string_compose("Connect route %1 OUT to %2\n",
-                                                           route->name(), port));
+       if (!midi_track) {
+               return;
+       }
 
-                               if (!port.empty() && route->output()->connect (route->output()->ports().port(*t, i), port, this)) {
-                                       break;
-                               }
+       if ((change.type & IOChange::ConfigurationChanged) && Config->get_output_auto_connect() != ManualConnect) {
 
-                                ChanCount one_added (*t, 1);
-                                existing_outputs += one_added;
-                       }
+               if (change.after.n_audio() <= change.before.n_audio()) {
+                       return;
                }
+
+               /* new audio ports: make sure the audio goes somewhere useful,
+                * unless the user has no-auto-connect selected.
+                *
+                * The existing ChanCounts don't matter for this call as they are only
+                * to do with matching input and output indices, and we are only changing
+                * outputs here.
+                */
+               auto_connect_route (midi_track, false, ChanCount(), change.before);
        }
 }
 
@@ -2743,8 +2751,6 @@ Session::reconnect_existing_routes (bool withLock, bool reconnect_master, bool r
                                        }
                                }
                        }
-
-                       //auto_connect_route (*rIter, inputs, outputs, false, reconnectIputs);
                }
 
                _master_out->output()->disconnect (this);
@@ -2901,6 +2907,11 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
                                goto failed;
                        }
 
+                       if (Profile->get_mixbus ()) {
+                               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:
@@ -3010,6 +3021,10 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
                                goto failure;
                        }
 
+                       if (Profile->get_mixbus ()) {
+                               bus->set_strict_io (true);
+                       }
+
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
                        // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route");
 #endif
@@ -3267,16 +3282,17 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output
 void
 Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect)
 {
-        ChanCount existing_inputs;
-        ChanCount existing_outputs;
+       ChanCount existing_inputs;
+       ChanCount existing_outputs;
        uint32_t order = next_control_id();
 
+
        if (_order_hint > -1) {
                order = _order_hint;
                _order_hint = -1;
        }
 
-        count_existing_track_channels (existing_inputs, existing_outputs);
+       count_existing_track_channels (existing_inputs, existing_outputs);
 
        {
                RCUWriter<RouteList> writer (routes);
@@ -3284,10 +3300,10 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                r->insert (r->end(), new_routes.begin(), new_routes.end());
 
                /* 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
-                  toward the end of this method. if we are in the middle of loading,
-                  we will resort when done.
-               */
+                * resort the graph here. if there is a control out, we will resort
+                * toward the end of this method. if we are in the middle of loading,
+                * we will resort when done.
+                */
 
                if (!_monitor_out && IO::connecting_legal) {
                        resort_routes_using (r);
@@ -3323,19 +3339,20 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                        boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (tr);
                        if (mt) {
                                mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1));
-                                mt->output()->changed.connect_same_thread (*this, boost::bind (&Session::midi_output_change_handler, this, _1, _2, boost::weak_ptr<Route>(mt)));
+                               mt->output()->changed.connect_same_thread (*this, boost::bind (&Session::midi_output_change_handler, this, _1, _2, boost::weak_ptr<Route>(mt)));
                        }
                }
 
-
                if (input_auto_connect || output_auto_connect) {
-                       auto_connect_route (r, existing_inputs, existing_outputs, true, input_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();
                }
 
                /* 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.
-               */
+                        reasonable defaults because they also affect the remote control
+                        ID in most situations.
+                        */
 
                if (!r->has_order_key ()) {
                        if (r->is_auditioner()) {
@@ -3358,7 +3375,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                        if ((*x)->is_monitor()) {
                                /* relax */
                        } else if ((*x)->is_master()) {
-                                       /* relax */
+                               /* relax */
                        } else {
                                (*x)->enable_monitor_send ();
                        }
@@ -3700,7 +3717,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi
         */
 
        RouteGroup* rg = route->route_group ();
-       const bool group_already_accounted_for = route->use_group (group_override, &RouteGroup::is_solo);
+       const bool group_already_accounted_for = (group_override == Controllable::ForGroup);
 
        if (delta == 1 && Config->get_exclusive_solo()) {
 
@@ -5360,7 +5377,7 @@ Session::next_insert_id ()
        /* this doesn't really loop forever. just think about it */
 
        while (true) {
-               for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < insert_bitset.size(); ++n) {
+               for (boost::dynamic_bitset<uint32_t>::size_type n = 1; n < insert_bitset.size(); ++n) {
                        if (!insert_bitset[n]) {
                                insert_bitset[n] = true;
                                return n;
@@ -5380,7 +5397,7 @@ Session::next_send_id ()
        /* this doesn't really loop forever. just think about it */
 
        while (true) {
-               for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < send_bitset.size(); ++n) {
+               for (boost::dynamic_bitset<uint32_t>::size_type n = 1; n < send_bitset.size(); ++n) {
                        if (!send_bitset[n]) {
                                send_bitset[n] = true;
                                return n;
@@ -5400,7 +5417,7 @@ Session::next_aux_send_id ()
        /* this doesn't really loop forever. just think about it */
 
        while (true) {
-               for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < aux_send_bitset.size(); ++n) {
+               for (boost::dynamic_bitset<uint32_t>::size_type n = 1; n < aux_send_bitset.size(); ++n) {
                        if (!aux_send_bitset[n]) {
                                aux_send_bitset[n] = true;
                                return n;
@@ -5420,7 +5437,7 @@ Session::next_return_id ()
        /* this doesn't really loop forever. just think about it */
 
        while (true) {
-               for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < return_bitset.size(); ++n) {
+               for (boost::dynamic_bitset<uint32_t>::size_type n = 1; n < return_bitset.size(); ++n) {
                        if (!return_bitset[n]) {
                                return_bitset[n] = true;
                                return n;
@@ -5811,6 +5828,7 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end,
        }
 
        unblock_processing ();
+       itt.done = true;
 
        return result;
 }
@@ -5851,6 +5869,12 @@ Session::get_scratch_buffers (ChanCount count, bool silence)
        return ProcessThread::get_scratch_buffers (count, silence);
 }
 
+BufferSet&
+Session::get_noinplace_buffers (ChanCount count)
+{
+       return ProcessThread::get_noinplace_buffers (count);
+}
+
 BufferSet&
 Session::get_route_buffers (ChanCount count, bool silence)
 {
@@ -6651,3 +6675,178 @@ Session::clear_object_selection ()
        follow_playhead_priority ();
 #endif
 }
+
+void
+Session::auto_connect_route (boost::shared_ptr<Route> route, bool connect_inputs,
+               const ChanCount& input_start,
+               const ChanCount& output_start,
+               const ChanCount& input_offset,
+               const ChanCount& output_offset)
+{
+       Glib::Threads::Mutex::Lock lx (_auto_connect_queue_lock);
+       _auto_connect_queue.push (AutoConnectRequest (route, connect_inputs,
+                               input_start, output_start,
+                               input_offset, output_offset));
+
+       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)
+{
+       boost::shared_ptr<Route> route = ar.route.lock();
+
+       if (!route) { return; }
+
+       if (!IO::connecting_legal) {
+               return;
+       }
+
+       //why would we need the process lock ??
+       //Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+       /* If both inputs and outputs are auto-connected to physical ports,
+        * use the max of input and output offsets to ensure auto-connected
+        * port numbers always match up (e.g. the first audio input and the
+        * first audio output of the route will have the same physical
+        * port number).  Otherwise just use the lowest input or output
+        * offset possible.
+        */
+
+       const bool in_out_physical =
+               (Config->get_input_auto_connect() & AutoConnectPhysical)
+               && (Config->get_output_auto_connect() & AutoConnectPhysical)
+               && ar.connect_inputs;
+
+       const ChanCount in_offset = in_out_physical
+               ? ChanCount::max(ar.input_offset, ar.output_offset)
+               : ar.input_offset;
+
+       const ChanCount out_offset = in_out_physical
+               ? ChanCount::max(ar.input_offset, ar.output_offset)
+               : ar.output_offset;
+
+       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+               vector<string> physinputs;
+               vector<string> physoutputs;
+
+               _engine.get_physical_outputs (*t, physoutputs);
+               _engine.get_physical_inputs (*t, physinputs);
+
+               if (!physinputs.empty() && ar.connect_inputs) {
+                       uint32_t nphysical_in = physinputs.size();
+
+                       for (uint32_t i = ar.input_start.get(*t); i < route->n_inputs().get(*t) && i < nphysical_in; ++i) {
+                               string port;
+
+                               if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+                                       port = physinputs[(in_offset.get(*t) + i) % nphysical_in];
+                               }
+
+                               if (!port.empty() && route->input()->connect (route->input()->ports().port(*t, i), port, this)) {
+                                       break;
+                               }
+                       }
+               }
+
+               if (!physoutputs.empty()) {
+                       uint32_t nphysical_out = physoutputs.size();
+                       for (uint32_t i = ar.output_start.get(*t); i < route->n_outputs().get(*t); ++i) {
+                               string port;
+
+                               /* Waves Tracks:
+                                * do not create new connections if we reached the limit of physical outputs
+                                * in Multi Out mode
+                                */
+                               if (!(Config->get_output_auto_connect() & AutoConnectMaster) &&
+                                               ARDOUR::Profile->get_trx () &&
+                                               ar.output_offset.get(*t) == nphysical_out ) {
+                                       break;
+                               }
+
+                               if ((*t) == DataType::MIDI && (Config->get_output_auto_connect() & AutoConnectPhysical)) {
+                                       port = physoutputs[(out_offset.get(*t) + i) % nphysical_out];
+                               } else if ((*t) == DataType::AUDIO && (Config->get_output_auto_connect() & AutoConnectMaster)) {
+                                       /* master bus is audio only */
+                                       if (_master_out && _master_out->n_inputs().get(*t) > 0) {
+                                               port = _master_out->input()->ports().port(*t,
+                                                               i % _master_out->input()->n_ports().get(*t))->name();
+                                       }
+                               }
+
+                               if (!port.empty() && route->output()->connect (route->output()->ports().port(*t, i), port, this)) {
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
+void
+Session::auto_connect_thread_start ()
+{
+       if (_ac_thread_active) {
+               return;
+       }
+       _ac_thread_active = true;
+       // clear queue
+       while (!_auto_connect_queue.empty ()) {
+               _auto_connect_queue.pop ();
+       }
+
+       if (pthread_create (&_auto_connect_thread, NULL, auto_connect_thread, this)) {
+               _ac_thread_active = false;
+       }
+}
+
+void
+Session::auto_connect_thread_terminate ()
+{
+       if (!_ac_thread_active) {
+               return;
+       }
+       _ac_thread_active = false;
+
+       if (pthread_mutex_lock (&_auto_connect_mutex) == 0) {
+               pthread_cond_signal (&_auto_connect_cond);
+               pthread_mutex_unlock (&_auto_connect_mutex);
+       }
+
+       void *status;
+       pthread_join (_auto_connect_thread, &status);
+}
+
+void *
+Session::auto_connect_thread (void *arg)
+{
+       Session *s = static_cast<Session *>(arg);
+       s->auto_connect_thread_run ();
+       pthread_exit (0);
+       return 0;
+}
+
+void
+Session::auto_connect_thread_run ()
+{
+       pthread_set_name (X_("autoconnect"));
+       SessionEvent::create_per_thread_pool (X_("autoconnect"), 256);
+       PBD::notify_event_loops_about_thread_creation (pthread_self(), X_("autoconnect"), 256);
+       pthread_mutex_lock (&_auto_connect_mutex);
+       while (_ac_thread_active) {
+
+               while (!_auto_connect_queue.empty ()) {
+                       Glib::Threads::Mutex::Lock lx (_auto_connect_queue_lock);
+                       if (_auto_connect_queue.empty ()) { break; } // re-check with lock
+                       const AutoConnectRequest ar (_auto_connect_queue.front());
+                       _auto_connect_queue.pop ();
+                       lx.release ();
+                       auto_connect (ar);
+               }
+
+               pthread_cond_wait (&_auto_connect_cond, &_auto_connect_mutex);
+       }
+       pthread_mutex_unlock (&_auto_connect_mutex);
+}