mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-16 11:46:25 +01:00
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)
This commit is contained in:
parent
d30b901d8c
commit
2a7a64a873
2 changed files with 258 additions and 157 deletions
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -2572,138 +2583,13 @@ Session::midi_output_change_handler (IOChange change, void * /*src*/, boost::wea
|
|||
}
|
||||
|
||||
/* 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.
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/** @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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ChanCount one_added (*t, 1);
|
||||
existing_inputs += one_added;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
|
@ -3402,6 +3286,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
|
|||
ChanCount existing_outputs;
|
||||
uint32_t order = next_control_id();
|
||||
|
||||
|
||||
if (_order_hint > -1) {
|
||||
order = _order_hint;
|
||||
_order_hint = -1;
|
||||
|
|
@ -3415,9 +3300,9 @@ 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) {
|
||||
|
|
@ -3458,9 +3343,10 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue