+
+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::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)
+{
+ boost::shared_ptr<Route> route = ar.route.lock();
+
+ if (!route) { return; }
+
+ if (!IO::connecting_legal) {
+ return;
+ }
+
+ /* 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;
+ }
+
+ while (!_auto_connect_queue.empty ()) {
+ _auto_connect_queue.pop ();
+ }
+
+ _ac_thread_active = true;
+ 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;
+
+ {
+ Glib::Threads::Mutex::Lock lx (_auto_connect_queue_lock);
+ while (!_auto_connect_queue.empty ()) {
+ _auto_connect_queue.pop ();
+ }
+ }
+
+ 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"), 1024);
+ PBD::notify_event_loops_about_thread_creation (pthread_self(), X_("autoconnect"), 1024);
+ pthread_mutex_lock (&_auto_connect_mutex);
+ while (_ac_thread_active) {
+
+ if (!_auto_connect_queue.empty ()) {
+ // Why would we need the process lock ??
+ // A: if ports are added while we're connecting, the backend's iterator may be invalidated:
+ // graph_order_callback() -> resort_routes() -> direct_feeds_according_to_reality () -> backend::connected_to()
+ // All ardour-internal backends use a std::vector xxxAudioBackend::find_port()
+ // We have control over those, but what does jack do?
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+ Glib::Threads::Mutex::Lock lx (_auto_connect_queue_lock);
+ while (!_auto_connect_queue.empty ()) {
+ const AutoConnectRequest ar (_auto_connect_queue.front());
+ _auto_connect_queue.pop ();
+ lx.release ();
+ auto_connect (ar);
+ lx.acquire ();
+ }
+ }
+
+ 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());
+}