summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2016-04-23 22:11:48 +0200
committerRobin Gareus <robin@gareus.org>2016-04-23 22:11:48 +0200
commit2a7a64a8738413df27e494e67c31695a429f6edc (patch)
tree38919c9b77670ce1c248f3f3e22ea3bcad51f2b4 /libs
parentd30b901d8cff95010b261e8ee07d38220d95b7e8 (diff)
delegate port-connections to low priority thread.
This prevents a deadlock with (some versions) jack: * add new instrument track with instrument * configure processors (takes processor lock) * add I/Os (delivery) -> create ports * auto-connect ports * jack port-connect -> jack graph re-order * Ardour graph-re-order * needs processor lock (to check sends)
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/session.h46
-rw-r--r--libs/ardour/session.cc369
2 files changed, 258 insertions, 157 deletions
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index b02c6619dc..5734ec4c87 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -28,6 +28,7 @@
#include <set>
#include <string>
#include <vector>
+#include <queue>
#include <stdint.h>
#include <boost/dynamic_bitset.hpp>
@@ -1119,7 +1120,9 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
friend class AudioEngine;
void set_block_size (pframes_t nframes);
void set_frame_rate (framecnt_t nframes);
+#ifdef USE_TRACKS_CODE_FEATURES
void reconnect_existing_routes (bool withLock, bool reconnect_master = true, bool reconnect_inputs = true, bool reconnect_outputs = true);
+#endif
protected:
friend class Route;
@@ -1459,6 +1462,45 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
pthread_cond_t _rt_emit_cond;
bool _rt_emit_pending;
+ /* Auto Connect Thread */
+ static void *auto_connect_thread (void *);
+ void auto_connect_thread_run ();
+ void auto_connect_thread_start ();
+ void auto_connect_thread_terminate ();
+
+ pthread_t _auto_connect_thread;
+ bool _ac_thread_active;
+ pthread_mutex_t _auto_connect_mutex;
+ pthread_cond_t _auto_connect_cond;
+
+ struct AutoConnectRequest {
+ public:
+ AutoConnectRequest (boost::shared_ptr <Route> r, bool ci,
+ const ChanCount& is,
+ const ChanCount& os,
+ const ChanCount& io,
+ const ChanCount& oo)
+ : route (boost::weak_ptr<Route> (r))
+ , connect_inputs (ci)
+ , input_start (is)
+ , output_start (os)
+ , input_offset (io)
+ , output_offset (oo)
+ {}
+
+ boost::weak_ptr <Route> route;
+ bool connect_inputs;
+ ChanCount input_start;
+ ChanCount output_start;
+ ChanCount input_offset;
+ ChanCount output_offset;
+ };
+
+ typedef std::queue<AutoConnectRequest> AutoConnectQueue;
+ Glib::Threads::Mutex _auto_connect_queue_lock;
+ AutoConnectQueue _auto_connect_queue;
+
+ void auto_connect (const AutoConnectRequest&);
/* SessionEventManager interface */
@@ -1621,9 +1663,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
bool find_route_name (std::string const &, uint32_t& id, std::string& name, bool);
void count_existing_track_channels (ChanCount& in, ChanCount& out);
- void auto_connect_route (boost::shared_ptr<Route> route, ChanCount& existing_inputs, ChanCount& existing_outputs,
- bool with_lock, bool connect_inputs = true,
- ChanCount input_start = ChanCount (), ChanCount output_start = ChanCount ());
+ void auto_connect_route (boost::shared_ptr<Route>, bool, const ChanCount&, const ChanCount&, const ChanCount& io = ChanCount(), const ChanCount& oo = ChanCount());
void midi_output_change_handler (IOChange change, void* /*src*/, boost::weak_ptr<Route> midi_track);
/* track numbering */
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index b6cc258fa7..b93d685bfc 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -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"
@@ -243,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)
@@ -313,6 +315,9 @@ 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);
@@ -408,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 */
@@ -719,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;
@@ -2559,151 +2570,26 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name
void
Session::midi_output_change_handler (IOChange change, void * /*src*/, boost::weak_ptr<Route> wmt)
{
- 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;
+ boost::shared_ptr<Route> midi_track (wmt.lock());
- auto_connect_route (midi_track, dummy, dummy, false, false, ChanCount(), change.before);
- }
-}
-
-/** @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) {
+ if (!midi_track) {
return;
}
- Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK);
-
- if (with_lock) {
- lm.acquire ();
- }
-
- /* 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.
- */
-
- DEBUG_TRACE (DEBUG::Graph,
- string_compose("Auto-connect: existing in = %1 out = %2\n",
- existing_inputs, existing_outputs));
-
- const bool in_out_physical =
- (Config->get_input_auto_connect() & AutoConnectPhysical)
- && (Config->get_output_auto_connect() & AutoConnectPhysical)
- && connect_inputs;
-
- const ChanCount in_offset = in_out_physical
- ? ChanCount::max(existing_inputs, existing_outputs)
- : existing_inputs;
-
- 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;
-
- _engine.get_physical_outputs (*t, physoutputs);
- _engine.get_physical_inputs (*t, physinputs);
-
- if (!physinputs.empty() && connect_inputs) {
- uint32_t nphysical_in = physinputs.size();
-
- DEBUG_TRACE (DEBUG::Graph,
- string_compose("There are %1 physical inputs of type %2\n",
- nphysical_in, *t));
-
- for (uint32_t i = input_start.get(*t); i < route->n_inputs().get(*t) && i < nphysical_in; ++i) {
- string port;
-
- 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));
-
- if (!port.empty() && route->input()->connect (route->input()->ports().port(*t, i), port, this)) {
- break;
- }
+ if ((change.type & IOChange::ConfigurationChanged) && Config->get_output_auto_connect() != ManualConnect) {
- ChanCount one_added (*t, 1);
- existing_inputs += one_added;
- }
+ if (change.after.n_audio() <= change.before.n_audio()) {
+ return;
}
- 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;
-
- /* 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();
- }
- }
-
- DEBUG_TRACE (DEBUG::Graph,
- string_compose("Connect route %1 OUT to %2\n",
- route->name(), port));
-
- if (!port.empty() && route->output()->connect (route->output()->ports().port(*t, i), port, this)) {
- break;
- }
-
- ChanCount one_added (*t, 1);
- existing_outputs += one_added;
- }
- }
+ /* 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);
}
}
@@ -2865,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);
@@ -3398,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);
@@ -3415,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);
@@ -3454,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()) {
@@ -3489,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 ();
}
@@ -6789,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);
+}