#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"
#include "ardour/session.h"
#include "ardour/unknown_processor.h"
#include "ardour/utils.h"
+#include "ardour/vca.h"
#include "i18n.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)
- : SessionObject (sess, name)
+ : Stripable (sess, name)
, Automatable (sess)
, GraphNode (sess._process_graph)
, _active (true)
, _in_sidechain_setup (false)
, _strict_io (false)
, _custom_meter_position_noted (false)
+ , _pinmgr_proxy (0)
{
processor_max_streams.reset();
}
_output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
_output->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::output_port_count_changing, this, _1));
-#if 0 // not used - just yet
- if (!is_master() && !is_monitor() && !is_auditioner()) {
- _delayline.reset (new DelayLine (_session, _name));
- add_processor (_delayline, PreFader);
- }
-#endif
-
- /* add amp processor */
+ /* add the amp/fader processor.
+ * it should be the first processor to be added on every route.
+ */
_gain_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, GainAutomation, shared_from_this ()));
add_control (_gain_control);
_amp->set_display_name (_("Monitor"));
}
+#if 0 // not used - just yet
+ if (!is_master() && !is_monitor() && !is_auditioner()) {
+ _delayline.reset (new DelayLine (_session, _name));
+ add_processor (_delayline, PreFader);
+ }
+#endif
+
/* and input trim */
_trim_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, TrimAutomation, shared_from_this ()));
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);
- }
- }
- }
-
- assert (find (_processors.begin(), _processors.end(), processor) == _processors.end ());
-
- 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, &lm)) {
- pstate.restore ();
- configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
- return -1;
- }
- }
+ ProcessorList pl;
- if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
+ pl.push_back (processor);
+ int rv = add_processors (pl, before, err);
- 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;
}
}
+
+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, &lm)) {
pstate.restore ();
}
}
+ 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) {
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));
node->add_child_nocopy (_pannable->state (full_state));
}
- for (i = _processors.begin(); i != _processors.end(); ++i) {
- if (!full_state) {
- /* template save: do not include internal sends functioning as
- aux sends because the chance of the target ID
- in the session where this template is used
- is not very likely.
-
- similarly, do not save listen sends which connect to
- the monitor section, because these will always be
- added if necessary.
- */
- boost::shared_ptr<InternalSend> is;
+ {
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ if (!full_state) {
+ /* template save: do not include internal sends functioning as
+ aux sends because the chance of the target ID
+ in the session where this template is used
+ is not very likely.
+
+ similarly, do not save listen sends which connect to
+ the monitor section, because these will always be
+ added if necessary.
+ */
+ boost::shared_ptr<InternalSend> is;
- if ((is = boost::dynamic_pointer_cast<InternalSend> (*i)) != 0) {
- if (is->role() == Delivery::Listen) {
- continue;
+ if ((is = boost::dynamic_pointer_cast<InternalSend> (*i)) != 0) {
+ if (is->role() == Delivery::Listen) {
+ continue;
+ }
}
}
+ node->add_child_nocopy((*i)->state (full_state));
}
- node->add_child_nocopy((*i)->state (full_state));
}
if (_extra_xml) {
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;
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
- XMLProperty const * prop = (*niter)->property ("type");
+ XMLProperty* prop = (*niter)->property ("type");
if (prop->value() == "amp") {
_amp->set_state (**niter, Stateful::current_state_version);
return _gain_control;
}
-boost::shared_ptr<GainControl>
+boost::shared_ptr<AutomationControl>
Route::trim_control() const
{
return _trim_control;
}
-boost::shared_ptr<Route::PhaseControllable>
+boost::shared_ptr<AutomationControl>
Route::phase_control() const
{
if (phase_invert().size()) {
/* find the amp */
- ProcessorList::iterator amp = new_processors.begin ();
- while (amp != new_processors.end() && *amp != _amp) {
- ++amp;
- }
+ ProcessorList::iterator amp = find (new_processors.begin(), new_processors.end(), _amp);
- assert (amp != new_processors.end ());
+ if (amp == new_processors.end ()) {
+ error << string_compose (_("Amp/Fader on Route '%1' went AWOL. Re-added."), name()) << endmsg;
+ new_processors.push_front (_amp);
+ amp = find (new_processors.begin(), new_processors.end(), _amp);
+ }
/* and the processor after the amp */
return boost::shared_ptr<AutomationControl>();
#endif
}
+
+bool
+Route::slaved_to (boost::shared_ptr<VCA> vca) const
+{
+ if (!vca || !_gain_control) {
+ return false;
+ }
+
+ return _gain_control->slaved_to (vca);
+}
+
+void
+Route::vca_assign (boost::shared_ptr<VCA> vca)
+{
+ _gain_control->add_master (vca);
+ vca->add_solo_mute_target (shared_from_this());
+}
+
+void
+Route::vca_unassign (boost::shared_ptr<VCA> vca)
+{
+ if (!vca) {
+ /* unassign from all */
+ _gain_control->clear_masters ();
+ /* XXXX need to remove from solo/mute target lists */
+ } else {
+ _gain_control->remove_master (vca);
+ vca->remove_solo_mute_target (shared_from_this());
+ }
+}