#include "pbd/memento_command.h"
#include "pbd/stacktrace.h"
#include "pbd/convert.h"
-#include "pbd/boost_debug.h"
#include "pbd/unwind.h"
#include "ardour/amp.h"
#include "ardour/audio_track.h"
#include "ardour/audio_port.h"
#include "ardour/audioengine.h"
+#include "ardour/boost_debug.h"
#include "ardour/buffer.h"
#include "ardour/buffer_set.h"
#include "ardour/capturing_processor.h"
PBD::Signal0<void> Route::SyncOrderKeys;
PBD::Signal0<void> Route::RemoteControlIDChange;
+PBD::Signal3<int,boost::shared_ptr<Route>, boost::shared_ptr<PluginInsert>, Route::PluginSetupOptions > Route::PluginSetup;
/** Base class for all routable/mixable objects (tracks and busses) */
Route::Route (Session& sess, string name, Flag flg, DataType default_type)
, _in_sidechain_setup (false)
, _strict_io (false)
, _custom_meter_position_noted (false)
+ , _pinmgr_proxy (0)
{
processor_max_streams.reset();
}
DEBUG_TRACE (DEBUG::Processors, string_compose (
"%1 adding processor %2\n", name(), processor->name()));
- if (!AudioEngine::instance()->connected() || !processor) {
- return 1;
- }
-
- if (_strict_io) {
- boost::shared_ptr<PluginInsert> pi;
- if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
- pi->set_strict_io (true);
- }
- }
-
- {
- Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
- Glib::Threads::RWLock::WriterLock lm (_processor_lock);
- ProcessorState pstate (this);
-
- boost::shared_ptr<PluginInsert> pi;
- boost::shared_ptr<PortInsert> porti;
-
- if (processor == _amp) {
- /* Ensure that only one amp is in the list at any time */
- ProcessorList::iterator check = find (_processors.begin(), _processors.end(), processor);
- if (check != _processors.end()) {
- if (before == _amp) {
- /* Already in position; all is well */
- return 0;
- } else {
- _processors.erase (check);
- }
- }
- }
+ ProcessorList pl;
- assert (find (_processors.begin(), _processors.end(), processor) == _processors.end ());
+ pl.push_back (processor);
+ int rv = add_processors (pl, before, err);
- ProcessorList::iterator loc;
- if (before) {
- /* inserting before a processor; find it */
- loc = find (_processors.begin(), _processors.end(), before);
- if (loc == _processors.end ()) {
- /* Not found */
- return 1;
- }
- } else {
- /* inserting at end */
- loc = _processors.end ();
- }
-
- _processors.insert (loc, processor);
- processor->set_owner (this);
-
- // Set up processor list channels. This will set processor->[input|output]_streams(),
- // configure redirect ports properly, etc.
-
- {
- if (configure_processors_unlocked (err)) {
- pstate.restore ();
- configure_processors_unlocked (0); // it worked before we tried to add it ...
- return -1;
- }
- }
-
- if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
-
- if (pi->has_no_inputs ()) {
- /* generator plugin */
- _have_internal_generator = true;
- }
-
- }
-
- if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) {
- processor->activate ();
- }
-
- processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
-
- _output->set_user_latency (0);
+ if (rv) {
+ return rv;
}
- reset_instrument_info ();
- processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
- set_processor_positions ();
-
- boost::shared_ptr<Send> send;
- if ((send = boost::dynamic_pointer_cast<Send> (processor))) {
- send->SelfDestruct.connect_same_thread (*this,
- boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (processor)));
+ if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) {
+ processor->activate ();
}
return 0;
bool
Route::add_processor_from_xml_2X (const XMLNode& node, int version)
{
- const XMLProperty *prop;
+ XMLProperty const * prop;
try {
boost::shared_ptr<Processor> processor;
}
}
+
+inline Route::PluginSetupOptions operator|= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) {
+ return a = static_cast<Route::PluginSetupOptions> (static_cast <int>(a) | static_cast<int> (b));
+}
+
+inline Route::PluginSetupOptions operator&= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) {
+ return a = static_cast<Route::PluginSetupOptions> (static_cast <int>(a) & static_cast<int> (b));
+}
+
int
Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
{
- /* NOTE: this is intended to be used ONLY when copying
- processors from another Route. Hence the subtle
- differences between this and ::add_processor()
- */
-
ProcessorList::iterator loc;
if (before) {
loc = find(_processors.begin(), _processors.end(), before);
+ if (loc == _processors.end ()) {
+ return 1;
+ }
} else {
/* nothing specified - at end */
loc = _processors.end ();
}
- if (!_session.engine().connected()) {
+ if (!AudioEngine::instance()->connected()) {
return 1;
}
return 0;
}
+ ProcessorList to_skip;
+
+ // check if there's an instrument to replace or configure
+ for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
+ boost::shared_ptr<PluginInsert> pi;
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) == 0) {
+ continue;
+ }
+ if (!pi->plugin ()->get_info ()->is_instrument ()) {
+ continue;
+ }
+ boost::shared_ptr<Processor> instrument = the_instrument ();
+ ChanCount in (DataType::MIDI, 1);
+ ChanCount out (DataType::AUDIO, 2); // XXX route's out?!
+
+ PluginSetupOptions flags = None;
+ if (instrument) {
+ flags |= CanReplace;
+ in = instrument->input_streams ();
+ out = instrument->output_streams ();
+ }
+ if (pi->has_output_presets (in, out)) {
+ flags |= MultiOut;
+ }
+
+ pi->set_strict_io (_strict_io);
+
+ PluginSetupOptions mask = None;
+ if (Config->get_ask_replace_instrument ()) {
+ mask |= CanReplace;
+ }
+ if (Config->get_ask_setup_instrument ()) {
+ mask |= MultiOut;
+ }
+
+ flags &= mask;
+
+ if (flags != None) {
+ boost::optional<int> rv = PluginSetup (shared_from_this (), pi, flags); /* EMIT SIGNAL */
+ switch (rv.get_value_or (0)) {
+ case 1:
+ to_skip.push_back (*i); // don't add this one;
+ break;
+ case 2:
+ replace_processor (instrument, *i, err);
+ to_skip.push_back (*i);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
{
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
if (*i == _meter) {
continue;
}
+ ProcessorList::iterator check = find (to_skip.begin(), to_skip.end(), *i);
+ if (check != to_skip.end()) {
+ continue;
+ }
boost::shared_ptr<PluginInsert> pi;
pi->set_strict_io (_strict_io);
}
+ if (*i == _amp) {
+ /* Ensure that only one amp is in the list at any time */
+ ProcessorList::iterator check = find (_processors.begin(), _processors.end(), *i);
+ if (check != _processors.end()) {
+ if (before == _amp) {
+ /* Already in position; all is well */
+ continue;
+ } else {
+ _processors.erase (check);
+ }
+ }
+ }
+
+ assert (find (_processors.begin(), _processors.end(), *i) == _processors.end ());
+
_processors.insert (loc, *i);
(*i)->set_owner (this);
- if ((*i)->active()) {
- (*i)->activate ();
- }
-
- /* Think: does this really need to be called for every processor in the loop? */
{
- if (configure_processors_unlocked (err)) {
+ if (configure_processors_unlocked (err, &lm)) {
pstate.restore ();
- configure_processors_unlocked (0); // it worked before we tried to add it ...
+ configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
return -1;
}
}
+ if ((*i)->active()) {
+ (*i)->activate ();
+ }
+
(*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
+
+ boost::shared_ptr<Send> send;
+ if ((send = boost::dynamic_pointer_cast<Send> (*i))) {
+ send->SelfDestruct.connect_same_thread (*this,
+ boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (*i)));
+ }
}
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
}
_processors = new_list;
- configure_processors_unlocked (&err); // this can't fail
+ configure_processors_unlocked (&err, &lm); // this can't fail
}
processor_max_streams.reset();
return 1;
}
- if (configure_processors_unlocked (err)) {
+ if (configure_processors_unlocked (err, &lm)) {
pstate.restore ();
/* we know this will work, because it worked before :) */
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
return -1;
}
}
}
- if (configure_processors_unlocked (err)) {
+ if (configure_processors_unlocked (err, &lm)) {
pstate.restore ();
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
return -1;
}
_output->set_user_latency (0);
- if (configure_processors_unlocked (err)) {
+ if (configure_processors_unlocked (err, &lm)) {
pstate.restore ();
/* we know this will work, because it worked before :) */
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
return -1;
}
//lx.unlock();
if (!_in_configure_processors) {
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
- return configure_processors_unlocked (err);
+ return configure_processors_unlocked (err, &lm);
}
return 0;
* Return 0 on success, otherwise configuration is impossible.
*/
int
-Route::configure_processors_unlocked (ProcessorStreams* err)
+Route::configure_processors_unlocked (ProcessorStreams* err, Glib::Threads::RWLock::WriterLock* lm)
{
#ifndef PLATFORM_WINDOWS
assert (!AudioEngine::instance()->process_lock().trylock());
processor_out_streams = _input->n_ports();
processor_max_streams.reset();
+ /* processor configure_io() may result in adding ports
+ * e.g. Delivery::configure_io -> ARDOUR::IO::ensure_io ()
+ *
+ * with jack2 adding ports results in a graph-order callback,
+ * which calls Session::resort_routes() and eventually
+ * Route::direct_feeds_according_to_reality()
+ * which takes a ReaderLock (_processor_lock).
+ *
+ * so we can't hold a WriterLock here until jack2 threading
+ * is fixed.
+ *
+ * NB. we still hold the process lock
+ *
+ * (ardour's own engines do call graph-order from the
+ * process-thread and hence do not have this issue; besides
+ * merely adding ports won't trigger a graph-order, only
+ * making connections does)
+ */
+ lm->release ();
+
+ // TODO check for a potential ReaderLock after ReaderLock ??
+ Glib::Threads::RWLock::ReaderLock lr (_processor_lock);
+
list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
if (!(*p)->configure_io(c->first, c->second)) {
DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration failed\n", _name));
_in_configure_processors = false;
+ lr.release ();
+ lm->acquire ();
return -1;
}
processor_max_streams = ChanCount::max(processor_max_streams, c->first);
}
}
+ lr.release ();
+ lm->acquire ();
+
if (_meter) {
_meter->set_max_channels (processor_max_streams);
apply_processor_order (new_order);
- if (configure_processors_unlocked (err)) {
+ if (configure_processors_unlocked (err, &lm)) {
pstate.restore ();
return -1;
}
return false;
}
lx.acquire ();
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
}
if (pi->has_sidechain ()) {
pi->set_preset_out (old);
return false;
}
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
}
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
Route::reset_plugin_insert (boost::shared_ptr<Processor> proc)
{
ChanCount unused;
- return customize_plugin_insert (proc, 0, unused);
+ return customize_plugin_insert (proc, 0, unused, unused);
}
bool
-Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs)
+Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs, ChanCount sinks)
{
boost::shared_ptr<PluginInsert> pi;
if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
- bool old_cust = pi->custom_cfg ();
- uint32_t old_cnt = pi->get_count ();
- ChanCount old_chan = pi->output_streams ();
+ bool old_cust = pi->custom_cfg ();
+ uint32_t old_cnt = pi->get_count ();
+ ChanCount old_chan = pi->output_streams ();
+ ChanCount old_sinks = pi->natural_input_streams ();
if (count == 0) {
pi->set_custom_cfg (false);
pi->set_custom_cfg (true);
pi->set_count (count);
pi->set_outputs (outs);
+ pi->set_sinks (sinks);
}
list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
/* not possible */
pi->set_count (old_cnt);
+ pi->set_sinks (old_sinks);
pi->set_outputs (old_chan);
pi->set_custom_cfg (old_cust);
return false;
}
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
}
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
XMLNode&
Route::state(bool full_state)
{
+ LocaleGuard lg;
if (!_session._template_state_dir.empty()) {
assert (!full_state); // only for templates
foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), _session._template_state_dir));
XMLNodeList nlist;
XMLNodeConstIterator niter;
XMLNode *child;
- const XMLProperty *prop;
+ XMLProperty const * prop;
if (node.name() != "Route"){
error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
if ((prop = node.property (X_("active"))) != 0) {
bool yn = string_is_affirmative (prop->value());
- _active = !yn; // force switch
set_active (yn, this);
}
int
Route::set_state_2X (const XMLNode& node, int version)
{
- LocaleGuard lg (X_("C"));
+ LocaleGuard lg;
XMLNodeList nlist;
XMLNodeConstIterator niter;
XMLNode *child;
- const XMLProperty *prop;
+ XMLProperty const * prop;
/* 2X things which still remain to be handled:
* default-type
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
- XMLProperty* prop = (*niter)->property ("type");
+ XMLProperty const * prop = (*niter)->property ("type");
if (prop->value() == "amp") {
_amp->set_state (**niter, Stateful::current_state_version);
ProcessorList::iterator o;
for (o = _processors.begin(); o != _processors.end(); ++o) {
- XMLProperty* id_prop = (*niter)->property(X_("id"));
+ XMLProperty const * id_prop = (*niter)->property(X_("id"));
if (id_prop && (*o)->id() == id_prop->value()) {
(*o)->set_state (**niter, Stateful::current_state_version);
new_order.push_back (*o);
_processors = new_order;
if (must_configure) {
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
}
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
return true;
}
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
- Glib::Threads::RWLock::ReaderLock lm (_processor_lock); // XXX
for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor>(*r);
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
ProcessorState pstate (this);
- if (configure_processors_unlocked (0)) {
+ if (configure_processors_unlocked (0, &lm)) {
DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
pstate.restore ();
- configure_processors_unlocked (0); // it worked before we tried to add it ...
+ configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
return;
}
}
_capturing_processor.reset (new CapturingProcessor (_session));
_capturing_processor->activate ();
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lw);
}
} else if ((*i)->name() == X_("Processor")) {
- XMLProperty* role = (*i)->property (X_("role"));
+ XMLProperty const * role = (*i)->property (X_("role"));
if (role && role->value() == X_("Main")) {
(*i)->add_property (X_("name"), name);
}
Route::the_instrument_unlocked () const
{
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
- if (boost::dynamic_pointer_cast<PluginInsert>(*i)) {
- if ((*i)->input_streams().n_midi() > 0 &&
- (*i)->output_streams().n_audio() > 0) {
- return (*i);
- }
+ boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*i);
+ if (pi && pi->plugin ()->get_info ()->is_instrument ()) {
+ return (*i);
}
}
return boost::shared_ptr<Processor>();